├── .eslintrc.json ├── .gitattributes ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.yml │ ├── feature_request.yml │ └── question.yml └── workflows │ ├── deploy.yml │ └── main.yml ├── .gitignore ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── ci ├── install-rust.sh └── make-release-asset.sh ├── examples ├── nop-preprocessor.rs └── remove-emphasis │ ├── .gitignore │ ├── book.toml │ ├── mdbook-remove-emphasis │ ├── Cargo.toml │ └── src │ │ └── main.rs │ ├── src │ ├── SUMMARY.md │ └── chapter_1.md │ └── test.rs ├── guide ├── book.toml └── src │ ├── 404.md │ ├── README.md │ ├── SUMMARY.md │ ├── cli │ ├── README.md │ ├── arg-watcher.md │ ├── build.md │ ├── clean.md │ ├── completions.md │ ├── init.md │ ├── serve.md │ ├── test.md │ └── watch.md │ ├── continuous-integration.md │ ├── for_developers │ ├── README.md │ ├── backends.md │ ├── mdbook-wordcount │ │ ├── Cargo.toml │ │ └── src │ │ │ └── main.rs │ └── preprocessors.md │ ├── format │ ├── README.md │ ├── configuration │ │ ├── README.md │ │ ├── environment-variables.md │ │ ├── general.md │ │ ├── preprocessors.md │ │ └── renderers.md │ ├── example.rs │ ├── images │ │ └── rust-logo-blk.svg │ ├── markdown.md │ ├── mathjax.md │ ├── mdbook.md │ ├── summary.md │ └── theme │ │ ├── README.md │ │ ├── editor.md │ │ ├── index-hbs.md │ │ └── syntax-highlighting.md │ ├── guide │ ├── README.md │ ├── creating.md │ ├── installation.md │ └── reading.md │ └── misc │ └── contributors.md ├── package.json ├── release.toml ├── src ├── book │ ├── book.rs │ ├── init.rs │ ├── mod.rs │ └── summary.rs ├── cmd │ ├── build.rs │ ├── clean.rs │ ├── command_prelude.rs │ ├── init.rs │ ├── mod.rs │ ├── serve.rs │ ├── test.rs │ ├── watch.rs │ └── watch │ │ ├── native.rs │ │ └── poller.rs ├── config.rs ├── front-end │ ├── css │ │ ├── ayu-highlight.css │ │ ├── chrome.css │ │ ├── font-awesome.min.css │ │ ├── general.css │ │ ├── highlight.css │ │ ├── print.css │ │ ├── tomorrow-night.css │ │ └── variables.css │ ├── fonts │ │ ├── FontAwesome.otf │ │ ├── OPEN-SANS-LICENSE.txt │ │ ├── SOURCE-CODE-PRO-LICENSE.txt │ │ ├── fontawesome-webfont.eot │ │ ├── fontawesome-webfont.svg │ │ ├── fontawesome-webfont.ttf │ │ ├── fontawesome-webfont.woff │ │ ├── fontawesome-webfont.woff2 │ │ ├── fonts.css │ │ ├── mod.rs │ │ ├── open-sans-v17-all-charsets-300.woff2 │ │ ├── open-sans-v17-all-charsets-300italic.woff2 │ │ ├── open-sans-v17-all-charsets-600.woff2 │ │ ├── open-sans-v17-all-charsets-600italic.woff2 │ │ ├── open-sans-v17-all-charsets-700.woff2 │ │ ├── open-sans-v17-all-charsets-700italic.woff2 │ │ ├── open-sans-v17-all-charsets-800.woff2 │ │ ├── open-sans-v17-all-charsets-800italic.woff2 │ │ ├── open-sans-v17-all-charsets-italic.woff2 │ │ ├── open-sans-v17-all-charsets-regular.woff2 │ │ └── source-code-pro-v11-all-charsets-500.woff2 │ ├── images │ │ ├── favicon.png │ │ └── favicon.svg │ ├── js │ │ ├── book.js │ │ ├── clipboard.min.js │ │ └── highlight.js │ ├── mod.rs │ ├── playground_editor │ │ ├── ace.js │ │ ├── editor.js │ │ ├── mod.rs │ │ ├── mode-rust.js │ │ ├── theme-dawn.js │ │ └── theme-tomorrow_night.js │ ├── searcher │ │ ├── elasticlunr.min.js │ │ ├── mark.min.js │ │ ├── mod.rs │ │ └── searcher.js │ └── templates │ │ ├── head.hbs │ │ ├── header.hbs │ │ ├── index.hbs │ │ ├── redirect.hbs │ │ ├── toc.html.hbs │ │ └── toc.js.hbs ├── lib.rs ├── main.rs ├── preprocess │ ├── cmd.rs │ ├── index.rs │ ├── links.rs │ └── mod.rs ├── renderer │ ├── html_handlebars │ │ ├── hbs_renderer.rs │ │ ├── helpers │ │ │ ├── mod.rs │ │ │ ├── navigation.rs │ │ │ ├── resources.rs │ │ │ ├── theme.rs │ │ │ └── toc.rs │ │ ├── mod.rs │ │ ├── search.rs │ │ └── static_files.rs │ ├── markdown_renderer.rs │ └── mod.rs └── utils │ ├── fs.rs │ ├── mod.rs │ ├── string.rs │ └── toml_ext.rs ├── test_book ├── book.toml └── src │ ├── README.md │ ├── SUMMARY.md │ ├── individual │ ├── README.md │ ├── blockquote.md │ ├── code.md │ ├── emphasis.md │ ├── heading.md │ ├── image.md │ ├── linebreak.md │ ├── link_hr.md │ ├── list.md │ ├── mathjax.md │ ├── mixed.md │ ├── paragraph.md │ ├── strikethrough.md │ ├── table.md │ └── task.md │ ├── languages │ ├── README.md │ └── highlight.md │ ├── prefix.md │ ├── rust │ ├── README.md │ └── rust_codeblock.md │ └── suffix.md ├── tests ├── gui │ ├── help.goml │ ├── move-between-pages.goml │ ├── runner.rs │ ├── search.goml │ ├── sidebar-active.goml │ ├── sidebar-nojs.goml │ └── sidebar.goml └── testsuite │ ├── README.md │ ├── book_test.rs │ ├── build.rs │ ├── build │ ├── basic_build │ │ ├── book.toml │ │ └── src │ │ │ ├── SUMMARY.md │ │ │ └── chapter_1.md │ ├── create_missing │ │ ├── book.toml │ │ └── src │ │ │ └── SUMMARY.md │ ├── missing_file │ │ ├── book.toml │ │ └── src │ │ │ └── SUMMARY.md │ └── no_reserved_filename │ │ ├── book.toml │ │ └── src │ │ ├── SUMMARY.md │ │ └── print.md │ ├── cli.rs │ ├── cli │ ├── help.term.svg │ └── no_args.term.svg │ ├── includes.rs │ ├── includes │ └── all_includes │ │ ├── book.toml │ │ └── src │ │ ├── SUMMARY.md │ │ ├── anchors.md │ │ ├── example.rs │ │ ├── includes.md │ │ ├── nested-test-with-anchors.rs │ │ ├── partially-included-test-with-anchors.rs │ │ ├── partially-included-test.rs │ │ ├── playground.md │ │ ├── recursive.md │ │ ├── relative │ │ └── includes.md │ │ ├── rustdoc.md │ │ └── sample.md │ ├── index.rs │ ├── index │ └── basic_readme │ │ ├── book.toml │ │ └── src │ │ ├── README.md │ │ ├── SUMMARY.md │ │ ├── first │ │ └── README │ │ └── second │ │ └── Readme.md │ ├── init.rs │ ├── init │ └── init_from_summary │ │ └── src │ │ └── SUMMARY.md │ ├── main.rs │ ├── markdown.rs │ ├── markdown │ ├── custom_header_attributes │ │ ├── book.toml │ │ └── src │ │ │ ├── SUMMARY.md │ │ │ └── custom_header_attributes.md │ ├── footnotes │ │ ├── expected │ │ │ └── footnotes.html │ │ └── src │ │ │ ├── SUMMARY.md │ │ │ └── footnotes.md │ ├── smart_punctuation │ │ └── src │ │ │ ├── SUMMARY.md │ │ │ └── smart_punctuation.md │ ├── strikethrough │ │ └── src │ │ │ ├── SUMMARY.md │ │ │ └── strikethrough.md │ ├── tables │ │ └── src │ │ │ ├── SUMMARY.md │ │ │ └── tables.md │ └── tasklists │ │ └── src │ │ ├── SUMMARY.md │ │ └── tasklists.md │ ├── playground.rs │ ├── playground │ ├── disabled_playground │ │ ├── book.toml │ │ └── src │ │ │ ├── SUMMARY.md │ │ │ └── index.md │ └── playground_on_rust_code │ │ ├── book.toml │ │ └── src │ │ ├── SUMMARY.md │ │ └── index.md │ ├── preprocessor.rs │ ├── preprocessor │ ├── failing_preprocessor │ │ ├── book.toml │ │ └── src │ │ │ └── SUMMARY.md │ └── nop_preprocessor │ │ ├── book.toml │ │ └── src │ │ └── SUMMARY.md │ ├── print.rs │ ├── print │ ├── noindex │ │ └── src │ │ │ └── SUMMARY.md │ └── relative_links │ │ ├── book.toml │ │ └── src │ │ ├── SUMMARY.md │ │ ├── first │ │ ├── index.md │ │ └── nested.md │ │ └── second │ │ └── nested.md │ ├── redirects.rs │ ├── redirects │ └── redirects_are_emitted_correctly │ │ ├── book.toml │ │ ├── expected │ │ ├── nested │ │ │ └── page.html │ │ └── overview.html │ │ └── src │ │ ├── SUMMARY.md │ │ └── chapter_1.md │ ├── renderer.rs │ ├── renderer │ ├── backends_receive_render_context_via_stdin │ │ ├── book.toml │ │ └── src │ │ │ ├── SUMMARY.md │ │ │ └── chapter_1.md │ ├── missing_optional_not_fatal │ │ ├── book.toml │ │ └── src │ │ │ └── SUMMARY.md │ ├── missing_renderer │ │ ├── book.toml │ │ └── src │ │ │ └── SUMMARY.md │ └── renderer_with_arguments │ │ ├── book.toml │ │ └── src │ │ └── SUMMARY.md │ ├── rendering.rs │ ├── rendering │ ├── edit_url_template │ │ ├── book.toml │ │ └── src │ │ │ └── SUMMARY.md │ ├── edit_url_template_explicit_src │ │ ├── book.toml │ │ └── src2 │ │ │ └── SUMMARY.md │ └── first_chapter_is_copied_as_index_even_if_not_first_elem │ │ └── src │ │ └── SUMMARY.md │ ├── search.rs │ ├── search │ ├── chapter_settings_validation_error │ │ ├── book.toml │ │ └── src │ │ │ └── SUMMARY.md │ ├── disable_search_chapter │ │ ├── book.toml │ │ └── src │ │ │ ├── SUMMARY.md │ │ │ ├── first │ │ │ ├── disable_me.md │ │ │ └── keep_me.md │ │ │ ├── second.md │ │ │ └── second │ │ │ └── nested.md │ └── reasonable_search_index │ │ ├── expected_index.js │ │ └── src │ │ ├── SUMMARY.md │ │ ├── first │ │ ├── duplicate-headers.md │ │ ├── heading-attributes.md │ │ ├── includes.md │ │ ├── index.md │ │ ├── no-headers.md │ │ └── unicode.md │ │ └── intro.md │ ├── test.rs │ ├── test │ ├── failing_tests │ │ ├── book.toml │ │ └── src │ │ │ ├── SUMMARY.md │ │ │ ├── failing.md │ │ │ ├── failing_include.md │ │ │ └── test1.rs │ └── passing_tests │ │ ├── book.toml │ │ └── src │ │ ├── SUMMARY.md │ │ ├── passing1.md │ │ ├── passing2.md │ │ ├── test1.rs │ │ ├── test2.rs │ │ └── test3.rs │ ├── theme.rs │ ├── theme │ ├── copy_fonts_false_no_theme │ │ ├── book.toml │ │ └── src │ │ │ └── SUMMARY.md │ ├── copy_fonts_false_with_empty_fonts_css │ │ ├── book.toml │ │ ├── src │ │ │ └── SUMMARY.md │ │ └── theme │ │ │ └── fonts │ │ │ └── fonts.css │ ├── copy_fonts_false_with_fonts_css │ │ ├── book.toml │ │ ├── src │ │ │ └── SUMMARY.md │ │ └── theme │ │ │ └── fonts │ │ │ ├── fonts.css │ │ │ └── myfont.woff │ ├── empty_theme │ │ ├── book.toml │ │ └── src │ │ │ ├── SUMMARY.md │ │ │ └── chapter_1.md │ ├── fonts_css │ │ ├── src │ │ │ └── SUMMARY.md │ │ └── theme │ │ │ └── fonts │ │ │ ├── fonts.css │ │ │ └── myfont.woff │ ├── missing_theme │ │ ├── book.toml │ │ └── src │ │ │ ├── SUMMARY.md │ │ │ └── chapter_1.md │ └── override_index │ │ ├── src │ │ └── SUMMARY.md │ │ └── theme │ │ └── index.hbs │ ├── toc.rs │ └── toc │ ├── basic_toc │ ├── book.toml │ └── src │ │ ├── README.md │ │ ├── SUMMARY.md │ │ ├── deep │ │ ├── a │ │ │ ├── b │ │ │ │ └── index.md │ │ │ └── index.md │ │ └── index.md │ │ ├── nested │ │ ├── index.md │ │ └── two.md │ │ ├── prefix1.md │ │ ├── prefix2.md │ │ ├── suffix1.md │ │ └── suffix2.md │ └── summary_with_markdown_formatting │ └── src │ └── SUMMARY.md └── triagebot.toml /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "node": true, 5 | "es6": true 6 | }, 7 | "extends": "eslint:recommended", 8 | "globals": { 9 | "module": "readonly", 10 | "require": "readonly" 11 | }, 12 | "parserOptions": { 13 | "ecmaVersion": 2021, 14 | "requireConfigFile": false, 15 | "sourceType": "module" 16 | }, 17 | "ignorePatterns": ["**min.js", "**/highlight.js", "**/playground_editor/*"], 18 | "rules": { 19 | "indent": [ 20 | "error", 21 | 4 22 | ], 23 | "linebreak-style": [ 24 | "error", 25 | "unix" 26 | ], 27 | "quotes": [ 28 | "error", 29 | "single" 30 | ], 31 | "semi": [ 32 | "error", 33 | "always" 34 | ], 35 | "brace-style": [ 36 | "error", 37 | "1tbs", 38 | { "allowSingleLine": false } 39 | ], 40 | "curly": "error", 41 | "no-trailing-spaces": "error", 42 | "no-multi-spaces": "error", 43 | "keyword-spacing": [ 44 | "error", 45 | { "before": true, "after": true } 46 | ], 47 | "comma-spacing": [ 48 | "error", 49 | { "before": false, "after": true } 50 | ], 51 | "arrow-spacing": [ 52 | "error", 53 | { "before": true, "after": true } 54 | ], 55 | "key-spacing": [ 56 | "error", 57 | { "beforeColon": false, "afterColon": true, "mode": "strict" } 58 | ], 59 | "func-call-spacing": ["error", "never"], 60 | "space-infix-ops": "error", 61 | "space-before-function-paren": ["error", "never"], 62 | "space-before-blocks": "error", 63 | "no-console": [ 64 | "error", 65 | { "allow": ["warn", "error"] } 66 | ], 67 | "comma-dangle": ["error", "always-multiline"], 68 | "comma-style": ["error", "last"], 69 | "max-len": ["error", { "code": 100, "tabWidth": 2 }], 70 | "eol-last": ["error", "always"], 71 | "no-extra-parens": "error", 72 | "arrow-parens": ["error", "as-needed"], 73 | "no-unused-vars": [ 74 | "error", 75 | { 76 | "argsIgnorePattern": "^_", 77 | "varsIgnorePattern": "^_" 78 | } 79 | ], 80 | "prefer-const": ["error"], 81 | "no-var": "error", 82 | "eqeqeq": "error" 83 | }, 84 | "overrides": [ 85 | { 86 | "files": [ 87 | "tests/**/*.js" 88 | ], 89 | "env": { 90 | "jest": true, 91 | "node": true 92 | } 93 | } 94 | ] 95 | } 96 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | [attr]rust text eol=lf whitespace=tab-in-indent,trailing-space,tabwidth=4 2 | 3 | * text=auto eol=lf 4 | *.rs rust 5 | *.woff binary 6 | *.ttf binary 7 | *.otf binary 8 | *.png binary 9 | *.eot binary 10 | *.woff2 binary 11 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.yml: -------------------------------------------------------------------------------- 1 | name: Bug Report 2 | description: Create a report to help us improve 3 | labels: ["C-bug"] 4 | body: 5 | - type: markdown 6 | attributes: 7 | value: Thanks for filing a 🐛 bug report 😄! 8 | - type: textarea 9 | id: problem 10 | attributes: 11 | label: Problem 12 | description: > 13 | Please provide a clear and concise description of what the bug is, 14 | including what currently happens and what you expected to happen. 15 | validations: 16 | required: true 17 | - type: textarea 18 | id: steps 19 | attributes: 20 | label: Steps 21 | description: Please list the steps to reproduce the bug. 22 | placeholder: | 23 | 1. 24 | 2. 25 | 3. 26 | - type: textarea 27 | id: possible-solutions 28 | attributes: 29 | label: Possible Solution(s) 30 | description: > 31 | Not obligatory, but suggest a fix/reason for the bug, 32 | or ideas how to implement the addition or change. 33 | - type: textarea 34 | id: notes 35 | attributes: 36 | label: Notes 37 | description: Provide any additional notes that might be helpful. 38 | - type: textarea 39 | id: version 40 | attributes: 41 | label: Version 42 | description: > 43 | Please paste the output of running `mdbook --version` or which version 44 | of the library you are using. 45 | render: text 46 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.yml: -------------------------------------------------------------------------------- 1 | name: Enhancement 2 | description: Suggest an idea for enhancing mdBook 3 | labels: ["C-enhancement"] 4 | body: 5 | - type: markdown 6 | attributes: 7 | value: | 8 | Thanks for filing a 🙋 feature request 😄! 9 | - type: textarea 10 | id: problem 11 | attributes: 12 | label: Problem 13 | description: > 14 | Please provide a clear description of your use case and the problem 15 | this feature request is trying to solve. 16 | validations: 17 | required: true 18 | - type: textarea 19 | id: solution 20 | attributes: 21 | label: Proposed Solution 22 | description: > 23 | Please provide a clear and concise description of what you want to happen. 24 | - type: textarea 25 | id: notes 26 | attributes: 27 | label: Notes 28 | description: Provide any additional context or information that might be helpful. 29 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/question.yml: -------------------------------------------------------------------------------- 1 | name: Question 2 | description: Have a question on how to use mdBook? 3 | labels: ["C-question"] 4 | body: 5 | - type: markdown 6 | attributes: 7 | value: | 8 | Got a question on how to do something with mdBook? 9 | - type: textarea 10 | id: question 11 | attributes: 12 | label: Question 13 | description: > 14 | Enter your question here. Please try to provide as much detail as possible. 15 | validations: 16 | required: true 17 | - type: textarea 18 | id: version 19 | attributes: 20 | label: Version 21 | description: > 22 | Please paste the output of running `mdbook --version` or which version 23 | of the library you are using. 24 | render: text 25 | -------------------------------------------------------------------------------- /.github/workflows/deploy.yml: -------------------------------------------------------------------------------- 1 | name: Deploy 2 | on: 3 | release: 4 | types: [created] 5 | 6 | defaults: 7 | run: 8 | shell: bash 9 | 10 | permissions: 11 | contents: write 12 | 13 | jobs: 14 | release: 15 | runs-on: ${{ matrix.os }} 16 | strategy: 17 | matrix: 18 | include: 19 | - target: aarch64-unknown-linux-musl 20 | os: ubuntu-22.04 21 | - target: x86_64-unknown-linux-gnu 22 | os: ubuntu-22.04 23 | - target: x86_64-unknown-linux-musl 24 | os: ubuntu-22.04 25 | - target: x86_64-apple-darwin 26 | os: macos-latest 27 | - target: aarch64-apple-darwin 28 | os: macos-latest 29 | - target: x86_64-pc-windows-msvc 30 | os: windows-latest 31 | name: Deploy ${{ matrix.target }} 32 | steps: 33 | - uses: actions/checkout@v4 34 | - name: Install Rust 35 | run: ci/install-rust.sh stable ${{ matrix.target }} 36 | - name: Build asset 37 | run: ci/make-release-asset.sh ${{ matrix.os }} ${{ matrix.target }} 38 | - name: Update release with new asset 39 | env: 40 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 41 | run: gh release upload $MDBOOK_TAG $MDBOOK_ASSET 42 | pages: 43 | name: GitHub Pages 44 | runs-on: ubuntu-latest 45 | steps: 46 | - uses: actions/checkout@v4 47 | - name: Install Rust (rustup) 48 | run: rustup update stable --no-self-update && rustup default stable 49 | - name: Build book 50 | run: cargo run -- build guide 51 | - name: Deploy the User Guide to GitHub Pages using the gh-pages branch 52 | env: 53 | GITHUB_DEPLOY_KEY: ${{ secrets.GITHUB_DEPLOY_KEY }} 54 | run: | 55 | touch guide/book/.nojekyll 56 | curl -LsSf https://raw.githubusercontent.com/rust-lang/simpleinfra/master/setup-deploy-keys/src/deploy.rs | rustc - -o /tmp/deploy 57 | cd guide/book 58 | /tmp/deploy 59 | publish: 60 | name: Publish to crates.io 61 | runs-on: ubuntu-latest 62 | steps: 63 | - uses: actions/checkout@v4 64 | - name: Install Rust (rustup) 65 | run: rustup update stable --no-self-update && rustup default stable 66 | - name: Publish 67 | env: 68 | CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} 69 | run: cargo publish --no-verify 70 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | 3 | # MacOS temp file 4 | .DS_Store 5 | 6 | book-test 7 | guide/book 8 | 9 | .vscode 10 | tests/dummy_book/book/ 11 | test_book/book/ 12 | tests/testsuite/*/*/book/ 13 | 14 | # Ignore Jetbrains specific files. 15 | .idea/ 16 | 17 | # Ignore Vim temporary and swap files. 18 | *.sw? 19 | *~ 20 | 21 | # GUI tests 22 | node_modules 23 | package-lock.json 24 | package.json 25 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # The Rust Code of Conduct 2 | 3 | The Code of Conduct for this repository [can be found online](https://www.rust-lang.org/conduct.html). 4 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [".", "examples/remove-emphasis/mdbook-remove-emphasis"] 3 | 4 | [workspace.lints.clippy] 5 | all = { level = "allow", priority = -2 } 6 | correctness = { level = "warn", priority = -1 } 7 | complexity = { level = "warn", priority = -1 } 8 | needless-lifetimes = "allow" # Remove once 1.87 is stable, https://github.com/rust-lang/rust-clippy/issues/13514 9 | 10 | [package] 11 | name = "mdbook" 12 | version = "0.4.51" 13 | authors = [ 14 | "Mathieu David ", 15 | "Michael-F-Bryan ", 16 | "Matt Ickstadt " 17 | ] 18 | documentation = "https://rust-lang.github.io/mdBook/index.html" 19 | edition = "2021" 20 | exclude = ["/guide/*"] 21 | keywords = ["book", "gitbook", "rustbook", "markdown"] 22 | license = "MPL-2.0" 23 | readme = "README.md" 24 | repository = "https://github.com/rust-lang/mdBook" 25 | description = "Creates a book from markdown files" 26 | rust-version = "1.82" # Keep in sync with installation.md and .github/workflows/main.yml 27 | 28 | [dependencies] 29 | anyhow = "1.0.71" 30 | chrono = { version = "0.4.24", default-features = false, features = ["clock"] } 31 | clap = { version = "4.3.12", features = ["cargo", "wrap_help"] } 32 | clap_complete = "4.3.2" 33 | env_logger = "0.11.1" 34 | handlebars = "6.0" 35 | hex = "0.4.3" 36 | log = "0.4.17" 37 | memchr = "2.5.0" 38 | opener = "0.8.1" 39 | pulldown-cmark = { version = "0.10.0", default-features = false, features = ["html"] } # Do not update, part of the public api. 40 | regex = "1.8.1" 41 | serde = { version = "1.0.163", features = ["derive"] } 42 | serde_json = "1.0.96" 43 | sha2 = "0.10.8" 44 | shlex = "1.3.0" 45 | tempfile = "3.4.0" 46 | toml = "0.5.11" # Do not update, see https://github.com/rust-lang/mdBook/issues/2037 47 | topological-sort = "0.2.2" 48 | 49 | # Watch feature 50 | notify = { version = "8.0.0", optional = true } 51 | notify-debouncer-mini = { version = "0.6.0", optional = true } 52 | ignore = { version = "0.4.20", optional = true } 53 | pathdiff = { version = "0.2.1", optional = true } 54 | walkdir = { version = "2.3.3", optional = true } 55 | 56 | # Serve feature 57 | futures-util = { version = "0.3.28", optional = true } 58 | tokio = { version = "1.43.1", features = ["macros", "rt-multi-thread"], optional = true } 59 | warp = { version = "0.3.6", default-features = false, features = ["websocket"], optional = true } 60 | 61 | # Search feature 62 | elasticlunr-rs = { version = "3.0.2", optional = true } 63 | ammonia = { version = "4.0.0", optional = true } 64 | 65 | [dev-dependencies] 66 | select = "0.6.0" 67 | semver = "1.0.17" 68 | snapbox = { version = "0.6.21", features = ["diff", "dir", "term-svg", "regex", "json"] } 69 | pretty_assertions = "1.3.0" 70 | walkdir = "2.3.3" 71 | 72 | [features] 73 | default = ["watch", "serve", "search"] 74 | watch = ["dep:notify", "dep:notify-debouncer-mini", "dep:ignore", "dep:pathdiff", "dep:walkdir"] 75 | serve = ["dep:futures-util", "dep:tokio", "dep:warp"] 76 | search = ["dep:elasticlunr-rs", "dep:ammonia"] 77 | 78 | [[bin]] 79 | doc = false 80 | name = "mdbook" 81 | 82 | [[example]] 83 | name = "nop-preprocessor" 84 | test = true 85 | 86 | [[example]] 87 | name = "remove-emphasis" 88 | path = "examples/remove-emphasis/test.rs" 89 | crate-type = ["lib"] 90 | test = true 91 | 92 | [[test]] 93 | harness = false 94 | test = false 95 | name = "gui" 96 | path = "tests/gui/runner.rs" 97 | crate-type = ["bin"] 98 | 99 | [lints] 100 | workspace = true 101 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # mdBook 2 | 3 | [![CI Status](https://github.com/rust-lang/mdBook/actions/workflows/main.yml/badge.svg)](https://github.com/rust-lang/mdBook/actions/workflows/main.yml) 4 | [![crates.io](https://img.shields.io/crates/v/mdbook.svg)](https://crates.io/crates/mdbook) 5 | [![LICENSE](https://img.shields.io/github/license/rust-lang/mdBook.svg)](LICENSE) 6 | 7 | mdBook is a utility to create modern online books from Markdown files. 8 | 9 | Check out the **[User Guide]** for a list of features and installation and usage information. 10 | The User Guide also serves as a demonstration to showcase what a book looks like. 11 | 12 | If you are interested in contributing to the development of mdBook, check out the [Contribution Guide]. 13 | 14 | ## License 15 | 16 | All the code in this repository is released under the ***Mozilla Public License v2.0***, for more information take a look at the [LICENSE] file. 17 | 18 | [User Guide]: https://rust-lang.github.io/mdBook/ 19 | [contribution guide]: https://github.com/rust-lang/mdBook/blob/master/CONTRIBUTING.md 20 | [LICENSE]: https://github.com/rust-lang/mdBook/blob/master/LICENSE 21 | -------------------------------------------------------------------------------- /ci/install-rust.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Install/update rust. 3 | # The first argument should be the toolchain to install. 4 | 5 | set -ex 6 | if [ -z "$1" ] 7 | then 8 | echo "First parameter must be toolchain to install." 9 | exit 1 10 | fi 11 | TOOLCHAIN="$1" 12 | 13 | rustup set profile minimal 14 | rustup component remove --toolchain=$TOOLCHAIN rust-docs || echo "already removed" 15 | rustup update --no-self-update $TOOLCHAIN 16 | if [ -n "$2" ] 17 | then 18 | TARGET="$2" 19 | HOST=$(rustc -Vv | grep ^host: | sed -e "s/host: //g") 20 | if [ "$HOST" != "$TARGET" ] 21 | then 22 | rustup component add llvm-tools-preview --toolchain=$TOOLCHAIN 23 | rustup component add rust-std-$TARGET --toolchain=$TOOLCHAIN 24 | fi 25 | if [[ $TARGET == *"musl" ]] 26 | then 27 | # This is needed by libdbus-sys. 28 | sudo apt update -y && sudo apt install musl-dev musl-tools -y 29 | fi 30 | if [[ $TARGET == "aarch64-unknown-linux-musl" ]] 31 | then 32 | echo CARGO_TARGET_AARCH64_UNKNOWN_LINUX_MUSL_LINKER=rust-lld >> $GITHUB_ENV 33 | # This `CC` is some nonsense needed for libdbus-sys (via opener). 34 | # I don't know if this is really the right thing to do, but it seems to work. 35 | sudo apt install gcc-aarch64-linux-gnu -y 36 | echo CC=aarch64-linux-gnu-gcc >> $GITHUB_ENV 37 | fi 38 | fi 39 | 40 | rustup default $TOOLCHAIN 41 | rustup -V 42 | rustc -Vv 43 | cargo -V 44 | -------------------------------------------------------------------------------- /ci/make-release-asset.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Builds the release and creates an archive and optionally deploys to GitHub. 3 | set -ex 4 | 5 | if [[ -z "$GITHUB_REF" ]] 6 | then 7 | echo "GITHUB_REF must be set" 8 | exit 1 9 | fi 10 | # Strip mdbook-refs/tags/ from the start of the ref. 11 | TAG=${GITHUB_REF#*/tags/} 12 | 13 | host=$(rustc -Vv | grep ^host: | sed -e "s/host: //g") 14 | target=$2 15 | export CARGO_PROFILE_RELEASE_LTO=true 16 | cargo build --locked --bin mdbook --release --target $target 17 | cd target/$target/release 18 | case $1 in 19 | ubuntu*) 20 | asset="mdbook-$TAG-$target.tar.gz" 21 | tar czf ../../$asset mdbook 22 | ;; 23 | macos*) 24 | asset="mdbook-$TAG-$target.tar.gz" 25 | # There is a bug with BSD tar on macOS where the first 8MB of the file are 26 | # sometimes all NUL bytes. See https://github.com/actions/cache/issues/403 27 | # and https://github.com/rust-lang/cargo/issues/8603 for some more 28 | # information. An alternative solution here is to install GNU tar, but 29 | # flushing the disk cache seems to work, too. 30 | sudo /usr/sbin/purge 31 | tar czf ../../$asset mdbook 32 | ;; 33 | windows*) 34 | asset="mdbook-$TAG-$target.zip" 35 | 7z a ../../$asset mdbook.exe 36 | ;; 37 | *) 38 | echo "OS should be first parameter, was: $1" 39 | ;; 40 | esac 41 | cd ../.. 42 | 43 | if [[ -z "$GITHUB_ENV" ]] 44 | then 45 | echo "GITHUB_ENV not set, run: gh release upload $TAG target/$asset" 46 | else 47 | echo "MDBOOK_TAG=$TAG" >> $GITHUB_ENV 48 | echo "MDBOOK_ASSET=target/$asset" >> $GITHUB_ENV 49 | fi 50 | -------------------------------------------------------------------------------- /examples/remove-emphasis/.gitignore: -------------------------------------------------------------------------------- 1 | book 2 | -------------------------------------------------------------------------------- /examples/remove-emphasis/book.toml: -------------------------------------------------------------------------------- 1 | [book] 2 | title = "remove-emphasis" 3 | 4 | [preprocessor.remove-emphasis] 5 | command = "cargo run --manifest-path=mdbook-remove-emphasis/Cargo.toml --locked" 6 | -------------------------------------------------------------------------------- /examples/remove-emphasis/mdbook-remove-emphasis/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "mdbook-remove-emphasis" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | mdbook = { version = "0.4.40", path = "../../.." } 8 | pulldown-cmark = { version = "0.12.2", default-features = false } 9 | pulldown-cmark-to-cmark = "18.0.0" 10 | serde_json = "1.0.132" 11 | -------------------------------------------------------------------------------- /examples/remove-emphasis/mdbook-remove-emphasis/src/main.rs: -------------------------------------------------------------------------------- 1 | //! This is a demonstration of an mdBook preprocessor which parses markdown 2 | //! and removes any instances of emphasis. 3 | 4 | use mdbook::book::{Book, Chapter}; 5 | use mdbook::errors::Error; 6 | use mdbook::preprocess::{CmdPreprocessor, Preprocessor, PreprocessorContext}; 7 | use mdbook::BookItem; 8 | use pulldown_cmark::{Event, Parser, Tag, TagEnd}; 9 | use std::io; 10 | 11 | fn main() { 12 | let mut args = std::env::args().skip(1); 13 | match args.next().as_deref() { 14 | Some("supports") => { 15 | // Supports all renderers. 16 | return; 17 | } 18 | Some(arg) => { 19 | eprintln!("unknown argument: {arg}"); 20 | std::process::exit(1); 21 | } 22 | None => {} 23 | } 24 | 25 | if let Err(e) = handle_preprocessing() { 26 | eprintln!("{e}"); 27 | std::process::exit(1); 28 | } 29 | } 30 | 31 | struct RemoveEmphasis; 32 | 33 | impl Preprocessor for RemoveEmphasis { 34 | fn name(&self) -> &str { 35 | "remove-emphasis" 36 | } 37 | 38 | fn run(&self, _ctx: &PreprocessorContext, mut book: Book) -> Result { 39 | let mut total = 0; 40 | book.for_each_mut(|item| { 41 | let BookItem::Chapter(ch) = item else { 42 | return; 43 | }; 44 | if ch.is_draft_chapter() { 45 | return; 46 | } 47 | match remove_emphasis(&mut total, ch) { 48 | Ok(s) => ch.content = s, 49 | Err(e) => eprintln!("failed to process chapter: {e:?}"), 50 | } 51 | }); 52 | eprintln!("removed {total} emphasis"); 53 | Ok(book) 54 | } 55 | } 56 | 57 | // ANCHOR: remove_emphasis 58 | fn remove_emphasis(num_removed_items: &mut usize, chapter: &mut Chapter) -> Result { 59 | let mut buf = String::with_capacity(chapter.content.len()); 60 | 61 | let events = Parser::new(&chapter.content).filter(|e| match e { 62 | Event::Start(Tag::Emphasis) | Event::Start(Tag::Strong) => { 63 | *num_removed_items += 1; 64 | false 65 | } 66 | Event::End(TagEnd::Emphasis) | Event::End(TagEnd::Strong) => false, 67 | _ => true, 68 | }); 69 | 70 | Ok(pulldown_cmark_to_cmark::cmark(events, &mut buf).map(|_| buf)?) 71 | } 72 | // ANCHOR_END: remove_emphasis 73 | 74 | pub fn handle_preprocessing() -> Result<(), Error> { 75 | let pre = RemoveEmphasis; 76 | let (ctx, book) = CmdPreprocessor::parse_input(io::stdin())?; 77 | 78 | let processed_book = pre.run(&ctx, book)?; 79 | serde_json::to_writer(io::stdout(), &processed_book)?; 80 | 81 | Ok(()) 82 | } 83 | -------------------------------------------------------------------------------- /examples/remove-emphasis/src/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | - [Chapter 1](./chapter_1.md) 4 | -------------------------------------------------------------------------------- /examples/remove-emphasis/src/chapter_1.md: -------------------------------------------------------------------------------- 1 | # Chapter 1 2 | 3 | This has *light emphasis* and **bold emphasis**. 4 | -------------------------------------------------------------------------------- /examples/remove-emphasis/test.rs: -------------------------------------------------------------------------------- 1 | #[test] 2 | fn remove_emphasis_works() { 3 | // Tests that the remove-emphasis example works as expected. 4 | 5 | // Workaround for https://github.com/rust-lang/mdBook/issues/1424 6 | std::env::set_current_dir("examples/remove-emphasis").unwrap(); 7 | let book = mdbook::MDBook::load(".").unwrap(); 8 | book.build().unwrap(); 9 | let ch1 = std::fs::read_to_string("book/chapter_1.html").unwrap(); 10 | assert!(ch1.contains("This has light emphasis and bold emphasis.")); 11 | } 12 | -------------------------------------------------------------------------------- /guide/book.toml: -------------------------------------------------------------------------------- 1 | [book] 2 | title = "mdBook Documentation" 3 | description = "Create book from markdown files. Like Gitbook but implemented in Rust" 4 | authors = ["Mathieu David", "Michael-F-Bryan"] 5 | language = "en" 6 | 7 | [rust] 8 | edition = "2018" 9 | 10 | [output.html] 11 | smart-punctuation = true 12 | mathjax-support = true 13 | site-url = "/mdBook/" 14 | git-repository-url = "https://github.com/rust-lang/mdBook/tree/master/guide" 15 | edit-url-template = "https://github.com/rust-lang/mdBook/edit/master/guide/{path}" 16 | hash-files = true 17 | 18 | [output.html.playground] 19 | editable = true 20 | line-numbers = true 21 | 22 | [output.html.code.hidelines] 23 | python = "~" 24 | 25 | [output.html.search] 26 | limit-results = 20 27 | use-boolean-and = true 28 | boost-title = 2 29 | boost-hierarchy = 2 30 | boost-paragraph = 1 31 | expand = true 32 | heading-split-level = 2 33 | 34 | [output.html.redirect] 35 | "/format/config.html" = "configuration/index.html" 36 | -------------------------------------------------------------------------------- /guide/src/404.md: -------------------------------------------------------------------------------- 1 | # Document not found (404) 2 | 3 | This URL is invalid, sorry. Try the search instead! -------------------------------------------------------------------------------- /guide/src/README.md: -------------------------------------------------------------------------------- 1 | # Introduction 2 | 3 | **mdBook** is a command line tool to create books with Markdown. 4 | It is ideal for creating product or API documentation, tutorials, course materials or anything that requires a clean, 5 | easily navigable and customizable presentation. 6 | 7 | * Lightweight [Markdown] syntax helps you focus more on your content 8 | * Integrated [search] support 9 | * Color [syntax highlighting] for code blocks for many different languages 10 | * [Theme] files allow customizing the formatting of the output 11 | * [Preprocessors] can provide extensions for custom syntax and modifying content 12 | * [Backends] can render the output to multiple formats 13 | * Written in [Rust] for speed, safety, and simplicity 14 | * Automated testing of [Rust code samples] 15 | 16 | This guide is an example of what mdBook produces. 17 | mdBook is used by the Rust programming language project, and [The Rust Programming Language][trpl] book is another fine example of mdBook in action. 18 | 19 | [Markdown]: format/markdown.md 20 | [search]: guide/reading.md#search 21 | [syntax highlighting]: format/theme/syntax-highlighting.md 22 | [theme]: format/theme/index.html 23 | [preprocessors]: format/configuration/preprocessors.md 24 | [backends]: format/configuration/renderers.md 25 | [Rust]: https://www.rust-lang.org/ 26 | [trpl]: https://doc.rust-lang.org/book/ 27 | [Rust code samples]: cli/test.md 28 | 29 | ## Contributing 30 | 31 | mdBook is free and open source. You can find the source code on 32 | [GitHub](https://github.com/rust-lang/mdBook) and issues and feature requests can be posted on 33 | the [GitHub issue tracker](https://github.com/rust-lang/mdBook/issues). mdBook relies on the community to fix bugs and 34 | add features: if you'd like to contribute, please read 35 | the [CONTRIBUTING](https://github.com/rust-lang/mdBook/blob/master/CONTRIBUTING.md) guide and consider opening 36 | a [pull request](https://github.com/rust-lang/mdBook/pulls). 37 | 38 | ## License 39 | 40 | The mdBook source and documentation are released under 41 | the [Mozilla Public License v2.0](https://www.mozilla.org/MPL/2.0/). 42 | -------------------------------------------------------------------------------- /guide/src/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | [Introduction](README.md) 4 | 5 | # User Guide 6 | 7 | - [Installation](guide/installation.md) 8 | - [Reading Books](guide/reading.md) 9 | - [Creating a Book](guide/creating.md) 10 | 11 | # Reference Guide 12 | 13 | - [Command Line Tool](cli/README.md) 14 | - [init](cli/init.md) 15 | - [build](cli/build.md) 16 | - [watch](cli/watch.md) 17 | - [serve](cli/serve.md) 18 | - [test](cli/test.md) 19 | - [clean](cli/clean.md) 20 | - [completions](cli/completions.md) 21 | - [Format](format/README.md) 22 | - [SUMMARY.md](format/summary.md) 23 | - [Draft chapter]() 24 | - [Configuration](format/configuration/README.md) 25 | - [General](format/configuration/general.md) 26 | - [Preprocessors](format/configuration/preprocessors.md) 27 | - [Renderers](format/configuration/renderers.md) 28 | - [Environment Variables](format/configuration/environment-variables.md) 29 | - [Theme](format/theme/README.md) 30 | - [index.hbs](format/theme/index-hbs.md) 31 | - [Syntax highlighting](format/theme/syntax-highlighting.md) 32 | - [Editor](format/theme/editor.md) 33 | - [MathJax Support](format/mathjax.md) 34 | - [mdBook-specific features](format/mdbook.md) 35 | - [Markdown](format/markdown.md) 36 | - [Continuous Integration](continuous-integration.md) 37 | - [For Developers](for_developers/README.md) 38 | - [Preprocessors](for_developers/preprocessors.md) 39 | - [Alternative Backends](for_developers/backends.md) 40 | 41 | ----------- 42 | 43 | [Contributors](misc/contributors.md) 44 | -------------------------------------------------------------------------------- /guide/src/cli/README.md: -------------------------------------------------------------------------------- 1 | # Command Line Tool 2 | 3 | The `mdbook` command-line tool is used to create and build books. 4 | After you have [installed](../guide/installation.md) `mdbook`, you can run the `mdbook help` command in your terminal to view the available commands. 5 | 6 | This following sections provide in-depth information on the different commands available. 7 | 8 | * [`mdbook init `](init.md) --- Creates a new book with minimal boilerplate to start with. 9 | * [`mdbook build`](build.md) --- Renders the book. 10 | * [`mdbook watch`](watch.md) --- Rebuilds the book any time a source file changes. 11 | * [`mdbook serve`](serve.md) --- Runs a web server to view the book, and rebuilds on changes. 12 | * [`mdbook test`](test.md) --- Tests Rust code samples. 13 | * [`mdbook clean`](clean.md) --- Deletes the rendered output. 14 | * [`mdbook completions`](completions.md) --- Support for shell auto-completion. 15 | -------------------------------------------------------------------------------- /guide/src/cli/arg-watcher.md: -------------------------------------------------------------------------------- 1 | #### `--watcher` 2 | 3 | There are different backends used to determine when a file has changed. 4 | 5 | * `poll` (default) --- Checks for file modifications by scanning the filesystem every second. 6 | * `native` --- Uses the native operating system facilities to receive notifications when files change. 7 | This can have less constant overhead, but may not be as reliable as the `poll` based watcher. See these issues for more information: [#383](https://github.com/rust-lang/mdBook/issues/383) [#1441](https://github.com/rust-lang/mdBook/issues/1441) [#1707](https://github.com/rust-lang/mdBook/issues/1707) [#2035](https://github.com/rust-lang/mdBook/issues/2035) [#2102](https://github.com/rust-lang/mdBook/issues/2102) 8 | -------------------------------------------------------------------------------- /guide/src/cli/build.md: -------------------------------------------------------------------------------- 1 | # The build command 2 | 3 | The build command is used to render your book: 4 | 5 | ```bash 6 | mdbook build 7 | ``` 8 | 9 | It will try to parse your `SUMMARY.md` file to understand the structure of your 10 | book and fetch the corresponding files. Note that this will also create files 11 | mentioned in `SUMMARY.md` which are not yet present. 12 | 13 | The rendered output will maintain the same directory structure as the source for 14 | convenience. Large books will therefore remain structured when rendered. 15 | 16 | #### Specify a directory 17 | 18 | The `build` command can take a directory as an argument to use as the book's 19 | root instead of the current working directory. 20 | 21 | ```bash 22 | mdbook build path/to/book 23 | ``` 24 | 25 | #### `--open` 26 | 27 | When you use the `--open` (`-o`) flag, mdbook will open the rendered book in 28 | your default web browser after building it. 29 | 30 | #### `--dest-dir` 31 | 32 | The `--dest-dir` (`-d`) option allows you to change the output directory for the 33 | book. Relative paths are interpreted relative to the book's root directory. If 34 | not specified it will default to the value of the `build.build-dir` key in 35 | `book.toml`, or to `./book`. 36 | 37 | ------------------- 38 | 39 | ***Note:*** *The build command copies all files (excluding files with `.md` extension) from the source directory 40 | into the build directory.* 41 | -------------------------------------------------------------------------------- /guide/src/cli/clean.md: -------------------------------------------------------------------------------- 1 | # The clean command 2 | 3 | The clean command is used to delete the generated book and any other build 4 | artifacts. 5 | 6 | ```bash 7 | mdbook clean 8 | ``` 9 | 10 | #### Specify a directory 11 | 12 | The `clean` command can take a directory as an argument to use as the book's 13 | root instead of the current working directory. 14 | 15 | ```bash 16 | mdbook clean path/to/book 17 | ``` 18 | 19 | #### `--dest-dir` 20 | 21 | The `--dest-dir` (`-d`) option allows you to override the book's output 22 | directory, which will be deleted by this command. Relative paths are interpreted 23 | relative to the book's root directory. If not specified it will default to the 24 | value of the `build.build-dir` key in `book.toml`, or to `./book`. 25 | 26 | ```bash 27 | mdbook clean --dest-dir=path/to/book 28 | ``` 29 | 30 | `path/to/book` could be absolute or relative. 31 | -------------------------------------------------------------------------------- /guide/src/cli/completions.md: -------------------------------------------------------------------------------- 1 | # The completions command 2 | 3 | The completions command is used to generate auto-completions for some common shells. 4 | This means when you type `mdbook` in your shell, you can then press your shell's auto-complete key (usually the Tab key) and it may display what the valid options are, or finish partial input. 5 | 6 | The completions first need to be installed for your shell: 7 | 8 | ```bash 9 | # bash 10 | mdbook completions bash > ~/.local/share/bash-completion/completions/mdbook 11 | # oh-my-zsh 12 | mdbook completions zsh > ~/.oh-my-zsh/completions/_mdbook 13 | autoload -U compinit && compinit 14 | ``` 15 | 16 | The command prints a completion script for the given shell. 17 | Run `mdbook completions --help` for a list of supported shells. 18 | 19 | Where to place the completions depend on which shell you are using and your operating system. 20 | Consult your shell's documentation for more information one where to place the script. 21 | -------------------------------------------------------------------------------- /guide/src/cli/init.md: -------------------------------------------------------------------------------- 1 | # The init command 2 | 3 | There is some minimal boilerplate that is the same for every new book. It's for 4 | this purpose that mdBook includes an `init` command. 5 | 6 | The `init` command is used like this: 7 | 8 | ```bash 9 | mdbook init 10 | ``` 11 | 12 | When using the `init` command for the first time, a couple of files will be set 13 | up for you: 14 | ```bash 15 | book-test/ 16 | ├── book 17 | └── src 18 | ├── chapter_1.md 19 | └── SUMMARY.md 20 | ``` 21 | 22 | - The `src` directory is where you write your book in markdown. It contains all 23 | the source files, configuration files, etc. 24 | 25 | - The `book` directory is where your book is rendered. All the output is ready 26 | to be uploaded to a server to be seen by your audience. 27 | 28 | - The `SUMMARY.md` is the skeleton of your 29 | book, and is discussed in more detail [in another 30 | chapter](../format/summary.md). 31 | 32 | #### Tip: Generate chapters from SUMMARY.md 33 | 34 | When a `SUMMARY.md` file already exists, the `init` command will first parse it 35 | and generate the missing files according to the paths used in the `SUMMARY.md`. 36 | This allows you to think and create the whole structure of your book and then 37 | let mdBook generate it for you. 38 | 39 | #### Specify a directory 40 | 41 | The `init` command can take a directory as an argument to use as the book's root 42 | instead of the current working directory. 43 | 44 | ```bash 45 | mdbook init path/to/book 46 | ``` 47 | 48 | #### `--theme` 49 | 50 | When you use the `--theme` flag, the default theme will be copied into a 51 | directory called `theme` in your source directory so that you can modify it. 52 | 53 | The theme is selectively overwritten, this means that if you don't want to 54 | overwrite a specific file, just delete it and the default file will be used. 55 | 56 | #### `--title` 57 | 58 | Specify a title for the book. If not supplied, an interactive prompt will ask for 59 | a title. 60 | 61 | ```bash 62 | mdbook init --title="my amazing book" 63 | ``` 64 | 65 | #### `--ignore` 66 | 67 | Create a `.gitignore` file configured to ignore the `book` directory created when [building] a book. 68 | If not supplied, an interactive prompt will ask whether it should be created. 69 | 70 | ```bash 71 | mdbook init --ignore=none 72 | ``` 73 | 74 | ```bash 75 | mdbook init --ignore=git 76 | ``` 77 | 78 | [building]: build.md 79 | 80 | #### `--force` 81 | 82 | Skip the prompts to create a `.gitignore` and for the title for the book. 83 | -------------------------------------------------------------------------------- /guide/src/cli/serve.md: -------------------------------------------------------------------------------- 1 | # The serve command 2 | 3 | The serve command is used to preview a book by serving it via HTTP at 4 | `localhost:3000` by default: 5 | 6 | ```bash 7 | mdbook serve 8 | ``` 9 | 10 | The `serve` command watches the book's `src` directory for 11 | changes, rebuilding the book and refreshing clients for each change; this includes 12 | re-creating deleted files still mentioned in `SUMMARY.md`! A websocket 13 | connection is used to trigger the client-side refresh. 14 | 15 | ***Note:*** *The `serve` command is for testing a book's HTML output, and is not 16 | intended to be a complete HTTP server for a website.* 17 | 18 | #### Specify a directory 19 | 20 | The `serve` command can take a directory as an argument to use as the book's 21 | root instead of the current working directory. 22 | 23 | ```bash 24 | mdbook serve path/to/book 25 | ``` 26 | 27 | ### Server options 28 | 29 | The `serve` hostname defaults to `localhost`, and the port defaults to `3000`. Either option can be specified on the command line: 30 | 31 | ```bash 32 | mdbook serve path/to/book -p 8000 -n 127.0.0.1 33 | ``` 34 | 35 | #### `--open` 36 | 37 | When you use the `--open` (`-o`) flag, mdbook will open the book in your 38 | default web browser after starting the server. 39 | 40 | #### `--dest-dir` 41 | 42 | The `--dest-dir` (`-d`) option allows you to change the output directory for the 43 | book. Relative paths are interpreted relative to the book's root directory. If 44 | not specified it will default to the value of the `build.build-dir` key in 45 | `book.toml`, or to `./book`. 46 | 47 | {{#include arg-watcher.md}} 48 | 49 | #### Specify exclude patterns 50 | 51 | The `serve` command will not automatically trigger a build for files listed in 52 | the `.gitignore` file in the book root directory. The `.gitignore` file may 53 | contain file patterns described in the [gitignore 54 | documentation](https://git-scm.com/docs/gitignore). This can be useful for 55 | ignoring temporary files created by some editors. 56 | 57 | ***Note:*** *Only the `.gitignore` from the book root directory is used. Global 58 | `$HOME/.gitignore` or `.gitignore` files in parent directories are not used.* 59 | -------------------------------------------------------------------------------- /guide/src/cli/test.md: -------------------------------------------------------------------------------- 1 | # The test command 2 | 3 | When writing a book, you sometimes need to automate some tests. For example, 4 | [The Rust Programming Book](https://doc.rust-lang.org/stable/book/) uses a lot 5 | of code examples that could get outdated. Therefore it is very important for 6 | them to be able to automatically test these code examples. 7 | 8 | mdBook supports a `test` command that will run all available tests in a book. At 9 | the moment, only Rust tests are supported. 10 | 11 | #### Disable tests on a code block 12 | 13 | rustdoc doesn't test code blocks which contain the `ignore` attribute: 14 | 15 | ```rust,ignore 16 | fn main() {} 17 | ``` 18 | 19 | rustdoc also doesn't test code blocks which specify a language other than Rust: 20 | 21 | ```markdown 22 | **Foo**: _bar_ 23 | ``` 24 | 25 | rustdoc *does* test code blocks which have no language specified: 26 | 27 | ``` 28 | This is going to cause an error! 29 | ``` 30 | 31 | #### Specify a directory 32 | 33 | The `test` command can take a directory as an argument to use as the book's root 34 | instead of the current working directory. 35 | 36 | ```bash 37 | mdbook test path/to/book 38 | ``` 39 | 40 | #### `--library-path` 41 | 42 | The `--library-path` (`-L`) option allows you to add directories to the library 43 | search path used by `rustdoc` when it builds and tests the examples. Multiple 44 | directories can be specified with multiple options (`-L foo -L bar`) or with a 45 | comma-delimited list (`-L foo,bar`). The path should point to the Cargo 46 | [build cache](https://doc.rust-lang.org/cargo/guide/build-cache.html) `deps` directory that 47 | contains the build output of your project. For example, if your Rust project's book is in a directory 48 | named `my-book`, the following command would include the crate's dependencies when running `test`: 49 | 50 | ```shell 51 | mdbook test my-book -L target/debug/deps/ 52 | ``` 53 | 54 | See the `rustdoc` command-line [documentation](https://doc.rust-lang.org/rustdoc/command-line-arguments.html#-l--library-path-where-to-look-for-dependencies) 55 | for more information. 56 | 57 | #### `--dest-dir` 58 | 59 | The `--dest-dir` (`-d`) option allows you to change the output directory for the 60 | book. Relative paths are interpreted relative to the book's root directory. If 61 | not specified it will default to the value of the `build.build-dir` key in 62 | `book.toml`, or to `./book`. 63 | 64 | #### `--chapter` 65 | 66 | The `--chapter` (`-c`) option allows you to test a specific chapter of the 67 | book using the chapter name or the relative path to the chapter. 68 | -------------------------------------------------------------------------------- /guide/src/cli/watch.md: -------------------------------------------------------------------------------- 1 | # The watch command 2 | 3 | The `watch` command is useful when you want your book to be rendered on every 4 | file change. You could repeatedly issue `mdbook build` every time a file is 5 | changed. But using `mdbook watch` once will watch your files and will trigger a 6 | build automatically whenever you modify a file; this includes re-creating 7 | deleted files still mentioned in `SUMMARY.md`! 8 | 9 | #### Specify a directory 10 | 11 | The `watch` command can take a directory as an argument to use as the book's 12 | root instead of the current working directory. 13 | 14 | ```bash 15 | mdbook watch path/to/book 16 | ``` 17 | 18 | #### `--open` 19 | 20 | When you use the `--open` (`-o`) option, mdbook will open the rendered book in 21 | your default web browser. 22 | 23 | #### `--dest-dir` 24 | 25 | The `--dest-dir` (`-d`) option allows you to change the output directory for the 26 | book. Relative paths are interpreted relative to the book's root directory. If 27 | not specified it will default to the value of the `build.build-dir` key in 28 | `book.toml`, or to `./book`. 29 | 30 | {{#include arg-watcher.md}} 31 | 32 | #### Specify exclude patterns 33 | 34 | The `watch` command will not automatically trigger a build for files listed in 35 | the `.gitignore` file in the book root directory. The `.gitignore` file may 36 | contain file patterns described in the [gitignore 37 | documentation](https://git-scm.com/docs/gitignore). This can be useful for 38 | ignoring temporary files created by some editors. 39 | 40 | _Note: Only `.gitignore` from book root directory is used. Global 41 | `$HOME/.gitignore` or `.gitignore` files in parent directories are not used._ 42 | -------------------------------------------------------------------------------- /guide/src/for_developers/README.md: -------------------------------------------------------------------------------- 1 | # For Developers 2 | 3 | While `mdbook` is mainly used as a command line tool, you can also import the 4 | underlying library directly and use that to manage a book. It also has a fairly 5 | flexible plugin mechanism, allowing you to create your own custom tooling and 6 | consumers (often referred to as *backends*) if you need to do some analysis of 7 | the book or render it in a different format. 8 | 9 | The *For Developers* chapters are here to show you the more advanced usage of 10 | `mdbook`. 11 | 12 | The two main ways a developer can hook into the book's build process is via, 13 | 14 | - [Preprocessors](preprocessors.md) 15 | - [Alternative Backends](backends.md) 16 | 17 | 18 | ## The Build Process 19 | 20 | The process of rendering a book project goes through several steps. 21 | 22 | 1. Load the book 23 | - Parse the `book.toml`, falling back to the default `Config` if it doesn't 24 | exist 25 | - Load the book chapters into memory 26 | - Discover which preprocessors/backends should be used 27 | 2. For each backend: 28 | 1. Run all the preprocessors. 29 | 2. Call the backend to render the processed result. 30 | 31 | 32 | ## Using `mdbook` as a Library 33 | 34 | The `mdbook` binary is just a wrapper around the `mdbook` crate, exposing its 35 | functionality as a command-line program. As such it is quite easy to create your 36 | own programs which use `mdbook` internally, adding your own functionality (e.g. 37 | a custom preprocessor) or tweaking the build process. 38 | 39 | The easiest way to find out how to use the `mdbook` crate is by looking at the 40 | [API Docs]. The top level documentation explains how one would use the 41 | [`MDBook`] type to load and build a book, while the [config] module gives a good 42 | explanation on the configuration system. 43 | 44 | 45 | [`MDBook`]: https://docs.rs/mdbook/*/mdbook/book/struct.MDBook.html 46 | [API Docs]: https://docs.rs/mdbook/*/mdbook/ 47 | [config]: https://docs.rs/mdbook/*/mdbook/config/index.html 48 | -------------------------------------------------------------------------------- /guide/src/for_developers/mdbook-wordcount/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "mdbook-wordcount" 3 | version = "0.1.0" 4 | authors = ["Michael Bryan "] 5 | 6 | [dependencies] 7 | mdbook = { path = "../../../..", version = "*" } 8 | serde = "1.0" 9 | serde_derive = "1.0" 10 | -------------------------------------------------------------------------------- /guide/src/for_developers/mdbook-wordcount/src/main.rs: -------------------------------------------------------------------------------- 1 | extern crate mdbook; 2 | extern crate serde; 3 | #[macro_use] 4 | extern crate serde_derive; 5 | 6 | use std::process; 7 | use std::fs::{self, File}; 8 | use std::io::{self, Write}; 9 | use mdbook::renderer::RenderContext; 10 | use mdbook::book::{BookItem, Chapter}; 11 | 12 | fn main() { 13 | let mut stdin = io::stdin(); 14 | let ctx = RenderContext::from_json(&mut stdin).unwrap(); 15 | let cfg: WordcountConfig = ctx.config 16 | .get_deserialized("output.wordcount") 17 | .unwrap_or_default(); 18 | 19 | let _ = fs::create_dir_all(&ctx.destination); 20 | let mut f = File::create(ctx.destination.join("wordcounts.txt")).unwrap(); 21 | 22 | for item in ctx.book.iter() { 23 | if let BookItem::Chapter(ref ch) = *item { 24 | if cfg.ignores.contains(&ch.name) { 25 | continue; 26 | } 27 | 28 | let num_words = count_words(ch); 29 | println!("{}: {}", ch.name, num_words); 30 | writeln!(f, "{}: {}", ch.name, num_words).unwrap(); 31 | 32 | if cfg.deny_odds && num_words % 2 == 1 { 33 | eprintln!("{} has an odd number of words!", ch.name); 34 | process::exit(1); 35 | } 36 | } 37 | } 38 | } 39 | 40 | fn count_words(ch: &Chapter) -> usize { 41 | ch.content.split_whitespace().count() 42 | } 43 | 44 | #[derive(Debug, Default, Serialize, Deserialize)] 45 | #[serde(default, rename_all = "kebab-case")] 46 | pub struct WordcountConfig { 47 | pub ignores: Vec, 48 | pub deny_odds: bool, 49 | } 50 | -------------------------------------------------------------------------------- /guide/src/format/README.md: -------------------------------------------------------------------------------- 1 | # Format 2 | 3 | In this section you will learn how to: 4 | 5 | - Structure your book correctly 6 | - Format your `SUMMARY.md` file 7 | - Configure your book using `book.toml` 8 | - Customize your theme 9 | -------------------------------------------------------------------------------- /guide/src/format/configuration/README.md: -------------------------------------------------------------------------------- 1 | # Configuration 2 | 3 | This section details the configuration options available in the ***book.toml***: 4 | - **[General]** configuration including the `book`, `rust`, `build` sections 5 | - **[Preprocessor]** configuration for default and custom book preprocessors 6 | - **[Renderer]** configuration for the HTML, Markdown and custom renderers 7 | - **[Environment Variable]** configuration for overriding configuration options in your environment 8 | 9 | [General]: general.md 10 | [Preprocessor]: preprocessors.md 11 | [Renderer]: renderers.md 12 | [Environment Variable]: environment-variables.md -------------------------------------------------------------------------------- /guide/src/format/configuration/environment-variables.md: -------------------------------------------------------------------------------- 1 | # Environment Variables 2 | 3 | All configuration values can be overridden from the command line by setting the 4 | corresponding environment variable. Because many operating systems restrict 5 | environment variables to be alphanumeric characters or `_`, the configuration 6 | key needs to be formatted slightly differently to the normal `foo.bar.baz` form. 7 | 8 | Variables starting with `MDBOOK_` are used for configuration. The key is created 9 | by removing the `MDBOOK_` prefix and turning the resulting string into 10 | `kebab-case`. Double underscores (`__`) separate nested keys, while a single 11 | underscore (`_`) is replaced with a dash (`-`). 12 | 13 | For example: 14 | 15 | - `MDBOOK_foo` -> `foo` 16 | - `MDBOOK_FOO` -> `foo` 17 | - `MDBOOK_FOO__BAR` -> `foo.bar` 18 | - `MDBOOK_FOO_BAR` -> `foo-bar` 19 | - `MDBOOK_FOO_bar__baz` -> `foo-bar.baz` 20 | 21 | So by setting the `MDBOOK_BOOK__TITLE` environment variable you can override the 22 | book's title without needing to touch your `book.toml`. 23 | 24 | > **Note:** To facilitate setting more complex config items, the value of an 25 | > environment variable is first parsed as JSON, falling back to a string if the 26 | > parse fails. 27 | > 28 | > This means, if you so desired, you could override all book metadata when 29 | > building the book with something like 30 | > 31 | > ```shell 32 | > $ export MDBOOK_BOOK='{"title": "My Awesome Book", "authors": ["Michael-F-Bryan"]}' 33 | > $ mdbook build 34 | > ``` 35 | 36 | The latter case may be useful in situations where `mdbook` is invoked from a 37 | script or CI, where it sometimes isn't possible to update the `book.toml` before 38 | building. 39 | -------------------------------------------------------------------------------- /guide/src/format/configuration/preprocessors.md: -------------------------------------------------------------------------------- 1 | # Configuring Preprocessors 2 | 3 | Preprocessors are extensions that can modify the raw Markdown source before it gets sent to the renderer. 4 | 5 | The following preprocessors are built-in and included by default: 6 | 7 | - `links`: Expands the `{{ #playground }}`, `{{ #include }}`, and `{{ #rustdoc_include }}` handlebars 8 | helpers in a chapter to include the contents of a file. 9 | See [Including files] for more. 10 | - `index`: Convert all chapter files named `README.md` into `index.md`. That is 11 | to say, all `README.md` would be rendered to an index file `index.html` in the 12 | rendered book. 13 | 14 | The built-in preprocessors can be disabled with the [`build.use-default-preprocessors`] config option. 15 | 16 | The community has developed several preprocessors. 17 | See the [Third Party Plugins] wiki page for a list of available preprocessors. 18 | 19 | For information on how to create a new preprocessor, see the [Preprocessors for Developers] chapter. 20 | 21 | [Including files]: ../mdbook.md#including-files 22 | [`build.use-default-preprocessors`]: general.md#build-options 23 | [Third Party Plugins]: https://github.com/rust-lang/mdBook/wiki/Third-party-plugins 24 | [Preprocessors for Developers]: ../../for_developers/preprocessors.md 25 | 26 | ## Custom Preprocessor Configuration 27 | 28 | Preprocessors can be added by including a `preprocessor` table in `book.toml` with the name of the preprocessor. 29 | For example, if you have a preprocessor called `mdbook-example`, then you can include it with: 30 | 31 | ```toml 32 | [preprocessor.example] 33 | ``` 34 | 35 | With this table, mdBook will execute the `mdbook-example` preprocessor. 36 | 37 | This table can include additional key-value pairs that are specific to the preprocessor. 38 | For example, if our example preprocessor needed some extra configuration options: 39 | 40 | ```toml 41 | [preprocessor.example] 42 | some-extra-feature = true 43 | ``` 44 | 45 | ## Locking a Preprocessor dependency to a renderer 46 | 47 | You can explicitly specify that a preprocessor should run for a renderer by 48 | binding the two together. 49 | 50 | ```toml 51 | [preprocessor.example] 52 | renderers = ["html"] # example preprocessor only runs with the HTML renderer 53 | ``` 54 | 55 | ## Provide Your Own Command 56 | 57 | By default when you add a `[preprocessor.foo]` table to your `book.toml` file, 58 | `mdbook` will try to invoke the `mdbook-foo` executable. If you want to use a 59 | different program name or pass in command-line arguments, this behaviour can 60 | be overridden by adding a `command` field. 61 | 62 | ```toml 63 | [preprocessor.random] 64 | command = "python random.py" 65 | ``` 66 | 67 | ## Require A Certain Order 68 | 69 | The order in which preprocessors are run can be controlled with the `before` and `after` fields. 70 | 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: 71 | 72 | ```toml 73 | [preprocessor.linenos] 74 | after = [ "links" ] 75 | ``` 76 | 77 | or 78 | 79 | ```toml 80 | [preprocessor.links] 81 | before = [ "linenos" ] 82 | ``` 83 | 84 | It would also be possible, though redundant, to specify both of the above in the same config file. 85 | 86 | Preprocessors having the same priority specified through `before` and `after` are sorted by name. 87 | Any infinite loops will be detected and produce an error. 88 | -------------------------------------------------------------------------------- /guide/src/format/example.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | println!("Hello World!"); 3 | # 4 | # // You can even hide lines! :D 5 | # println!("I am hidden! Expand the code snippet to see me"); 6 | } 7 | -------------------------------------------------------------------------------- /guide/src/format/images/rust-logo-blk.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /guide/src/format/mathjax.md: -------------------------------------------------------------------------------- 1 | # MathJax Support 2 | 3 | mdBook has optional support for math equations through 4 | [MathJax](https://www.mathjax.org/). 5 | 6 | To enable MathJax, you need to add the `mathjax-support` key to your `book.toml` 7 | under the `output.html` section. 8 | 9 | ```toml 10 | [output.html] 11 | mathjax-support = true 12 | ``` 13 | 14 | >**Note:** The usual delimiters MathJax uses are not yet supported. You can't 15 | currently use `$$ ... $$` as delimiters and the `\[ ... \]` delimiters need an 16 | extra backslash to work. Hopefully this limitation will be lifted soon. 17 | 18 | >**Note:** When you use double backslashes in MathJax blocks (for example in 19 | > commands such as `\begin{cases} \frac 1 2 \\ \frac 3 4 \end{cases}`) you need 20 | > to add _two extra_ backslashes (e.g., `\begin{cases} \frac 1 2 \\\\ \frac 3 4 21 | > \end{cases}`). 22 | 23 | 24 | ### Inline equations 25 | Inline equations are delimited by `\\(` and `\\)`. So for example, to render the 26 | following inline equation \\( \int x dx = \frac{x^2}{2} + C \\) you would write 27 | the following: 28 | ``` 29 | \\( \int x dx = \frac{x^2}{2} + C \\) 30 | ``` 31 | 32 | ### Block equations 33 | Block equations are delimited by `\\[` and `\\]`. To render the following 34 | equation 35 | 36 | \\[ \mu = \frac{1}{N} \sum_{i=0} x_i \\] 37 | 38 | 39 | you would write: 40 | 41 | ```bash 42 | \\[ \mu = \frac{1}{N} \sum_{i=0} x_i \\] 43 | ``` 44 | -------------------------------------------------------------------------------- /guide/src/format/theme/README.md: -------------------------------------------------------------------------------- 1 | # Theme 2 | 3 | The default renderer uses a [handlebars](https://handlebarsjs.com) template to 4 | render your markdown files and comes with a default theme included in the mdBook 5 | binary. 6 | 7 | The theme is totally customizable, you can selectively replace every file from 8 | the theme by your own by adding a `theme` directory next to `src` folder in your 9 | project root. Create a new file with the name of the file you want to override 10 | and now that file will be used instead of the default file. 11 | 12 | Here are the files you can override: 13 | 14 | - **_index.hbs_** is the handlebars template. 15 | - **_head.hbs_** is appended to the HTML `` section. 16 | - **_header.hbs_** content is appended on top of every book page. 17 | - **_css/_** contains the CSS files for styling the book. 18 | - **_css/chrome.css_** is for UI elements. 19 | - **_css/general.css_** is the base styles. 20 | - **_css/print.css_** is the style for printer output. 21 | - **_css/variables.css_** contains variables used in other CSS files. 22 | - **_book.js_** is mostly used to add client side functionality, like hiding / 23 | un-hiding the sidebar, changing the theme, ... 24 | - **_highlight.js_** is the JavaScript that is used to highlight code snippets, 25 | you should not need to modify this. 26 | - **_highlight.css_** is the theme used for the code highlighting. 27 | - **_favicon.svg_** and **_favicon.png_** the favicon that will be used. The SVG 28 | version is used by [newer browsers]. 29 | - **fonts/fonts.css** contains the definition of which fonts to load. 30 | Custom fonts can be included in the `fonts` directory. 31 | 32 | Generally, when you want to tweak the theme, you don't need to override all the 33 | files. If you only need changes in the stylesheet, there is no point in 34 | overriding all the other files. Because custom files take precedence over 35 | built-in ones, they will not get updated with new fixes / features. 36 | 37 | **Note:** When you override a file, it is possible that you break some 38 | functionality. Therefore I recommend to use the file from the default theme as 39 | template and only add / modify what you need. You can copy the default theme 40 | into your source directory automatically by using `mdbook init --theme` and just 41 | remove the files you don't want to override. 42 | 43 | `mdbook init --theme` will not create every file listed above. 44 | Some files, such as `head.hbs`, do not have built-in equivalents. 45 | Just create the file if you need it. 46 | 47 | If you completely replace all built-in themes, be sure to also set 48 | [`output.html.preferred-dark-theme`] in the config, which defaults to the 49 | built-in `navy` theme. 50 | 51 | [`output.html.preferred-dark-theme`]: ../configuration/renderers.md#html-renderer-options 52 | [newer browsers]: https://caniuse.com/#feat=link-icon-svg 53 | -------------------------------------------------------------------------------- /guide/src/format/theme/editor.md: -------------------------------------------------------------------------------- 1 | # Editor 2 | 3 | In addition to providing runnable code playgrounds, mdBook optionally allows them 4 | to be editable. In order to enable editable code blocks, the following needs to 5 | be added to the ***book.toml***: 6 | 7 | ```toml 8 | [output.html.playground] 9 | editable = true 10 | ``` 11 | 12 | After enabling editable code blocks, the `editable` attribute must be added to a 13 | code block to make it editable: 14 | 15 | ~~~markdown 16 | ```rust,editable 17 | fn main() { 18 | let number = 5; 19 | print!("{}", number); 20 | } 21 | ``` 22 | ~~~ 23 | 24 | The above will result in this editable playground: 25 | 26 | ```rust,editable 27 | fn main() { 28 | let number = 5; 29 | print!("{}", number); 30 | } 31 | ``` 32 | 33 | Note the new `Undo Changes` button in the editable playgrounds. 34 | 35 | ## Customizing the Editor 36 | 37 | By default, the editor is the [Ace](https://ace.c9.io/) editor, but, if desired, 38 | the functionality may be overridden by providing a different folder: 39 | 40 | ```toml 41 | [output.html.playground] 42 | editable = true 43 | editor = "/path/to/editor" 44 | ``` 45 | 46 | Note that for the editor changes to function correctly, the `book.js` inside of 47 | the `theme` folder will need to be overridden as it has some couplings with the 48 | default Ace editor. 49 | -------------------------------------------------------------------------------- /guide/src/format/theme/index-hbs.md: -------------------------------------------------------------------------------- 1 | # index.hbs 2 | 3 | `index.hbs` is the handlebars template that is used to render the book. The 4 | markdown files are processed to html and then injected in that template. 5 | 6 | If you want to change the layout or style of your book, chances are that you 7 | will have to modify this template a little bit. Here is what you need to know. 8 | 9 | ## Data 10 | 11 | A lot of data is exposed to the handlebars template with the "context". In the 12 | handlebars template you can access this information by using 13 | 14 | ```handlebars 15 | {{name_of_property}} 16 | ``` 17 | 18 | Here is a list of the properties that are exposed: 19 | 20 | - ***language*** Language of the book in the form `en`, as specified in `book.toml` (if not specified, defaults to `en`). To use in \ for example. 22 | - ***title*** Title used for the current page. This is identical to `{{ chapter_title }} - {{ book_title }}` unless `book_title` is not set in which case it just defaults to the `chapter_title`. 23 | - ***book_title*** Title of the book, as specified in `book.toml` 24 | - ***chapter_title*** Title of the current chapter, as listed in `SUMMARY.md` 25 | 26 | - ***path*** Relative path to the original markdown file from the source 27 | directory 28 | - ***content*** This is the rendered markdown. 29 | - ***path_to_root*** This is a path containing exclusively `../`'s that points 30 | to the root of the book from the current file. Since the original directory 31 | structure is maintained, it is useful to prepend relative links with this 32 | `path_to_root`. 33 | 34 | - ***chapters*** Is an array of dictionaries of the form 35 | ```json 36 | {"section": "1.2.1", "name": "name of this chapter", "path": "dir/markdown.md"} 37 | ``` 38 | containing all the chapters of the book. It is used for example to construct 39 | the table of contents (sidebar). 40 | 41 | ## Handlebars Helpers 42 | 43 | In addition to the properties you can access, there are some handlebars helpers 44 | at your disposal. 45 | 46 | ### 1. toc 47 | 48 | The toc helper is used like this 49 | 50 | ```handlebars 51 | {{#toc}}{{/toc}} 52 | ``` 53 | 54 | and outputs something that looks like this, depending on the structure of your 55 | book 56 | 57 | ```html 58 | 66 | ``` 67 | 68 | If you would like to make a toc with another structure, you have access to the 69 | chapters property containing all the data. The only limitation at the moment 70 | is that you would have to do it with JavaScript instead of with a handlebars 71 | helper. 72 | 73 | ```html 74 | 78 | ``` 79 | 80 | ### 2. previous / next 81 | 82 | The previous and next helpers expose a `link` and `title` property to the 83 | previous and next chapters. 84 | 85 | They are used like this 86 | 87 | ```handlebars 88 | {{#previous}} 89 | 92 | {{/previous}} 93 | ``` 94 | 95 | The inner html will only be rendered if the previous / next chapter exists. 96 | Of course the inner html can be changed to your liking. 97 | 98 | ------ 99 | 100 | *If you would like other properties or helpers exposed, please [create a new 101 | issue](https://github.com/rust-lang/mdBook/issues)* 102 | 103 | ### 3. resource 104 | 105 | The path to a static file. 106 | It implicitly includes `path_to_root`, 107 | and accounts for files that are renamed with a hash in their filename. 108 | 109 | ```handlebars 110 | 111 | ``` 112 | -------------------------------------------------------------------------------- /guide/src/format/theme/syntax-highlighting.md: -------------------------------------------------------------------------------- 1 | # Syntax Highlighting 2 | 3 | mdBook uses [Highlight.js](https://highlightjs.org) with a custom theme 4 | for syntax highlighting. 5 | 6 | Automatic language detection has been turned off, so you will probably want to 7 | specify the programming language you use like this: 8 | 9 | ~~~markdown 10 | ```rust 11 | fn main() { 12 | // Some code 13 | } 14 | ``` 15 | ~~~ 16 | 17 | ## Supported languages 18 | 19 | These languages are supported by default, but you can add more by supplying 20 | your own `highlight.js` file: 21 | 22 | - apache 23 | - armasm 24 | - bash 25 | - c 26 | - coffeescript 27 | - cpp 28 | - csharp 29 | - css 30 | - d 31 | - diff 32 | - go 33 | - handlebars 34 | - haskell 35 | - http 36 | - ini 37 | - java 38 | - javascript 39 | - json 40 | - julia 41 | - kotlin 42 | - less 43 | - lua 44 | - makefile 45 | - markdown 46 | - nginx 47 | - nim 48 | - nix 49 | - objectivec 50 | - perl 51 | - php 52 | - plaintext 53 | - properties 54 | - python 55 | - r 56 | - ruby 57 | - rust 58 | - scala 59 | - scss 60 | - shell 61 | - sql 62 | - swift 63 | - typescript 64 | - x86asm 65 | - xml 66 | - yaml 67 | 68 | ## Custom theme 69 | Like the rest of the theme, the files used for syntax highlighting can be 70 | overridden with your own. 71 | 72 | - ***highlight.js*** normally you shouldn't have to overwrite this file, unless 73 | you want to use a more recent version. 74 | - ***highlight.css*** theme used by highlight.js for syntax highlighting. 75 | 76 | If you want to use another theme for `highlight.js` download it from their 77 | website, or make it yourself, rename it to `highlight.css` and put it in 78 | the `theme` folder of your book. 79 | 80 | Now your theme will be used instead of the default theme. 81 | 82 | ## Improve default theme 83 | 84 | If you think the default theme doesn't look quite right for a specific language, 85 | or could be improved, feel free to [submit a new 86 | issue](https://github.com/rust-lang/mdBook/issues) explaining what you 87 | have in mind and I will take a look at it. 88 | 89 | You could also create a pull-request with the proposed improvements. 90 | 91 | Overall the theme should be light and sober, without too many flashy colors. 92 | -------------------------------------------------------------------------------- /guide/src/guide/README.md: -------------------------------------------------------------------------------- 1 | # User Guide 2 | 3 | This user guide provides an introduction to basic concepts of using mdBook. 4 | 5 | - [Installation](installation.md) 6 | - [Reading Books](reading.md) 7 | - [Creating a Book](creating.md) 8 | -------------------------------------------------------------------------------- /guide/src/guide/installation.md: -------------------------------------------------------------------------------- 1 | # Installation 2 | 3 | There are multiple ways to install the mdBook CLI tool. 4 | Choose any one of the methods below that best suit your needs. 5 | If you are installing mdBook for automatic deployment, check out the [continuous integration] chapter for more examples on how to install. 6 | 7 | [continuous integration]: ../continuous-integration.md 8 | 9 | ## Pre-compiled binaries 10 | 11 | Executable binaries are available for download on the [GitHub Releases page][releases]. 12 | Download the binary for your platform (Windows, macOS, or Linux) and extract the archive. 13 | The archive contains an `mdbook` executable which you can run to build your books. 14 | 15 | To make it easier to run, put the path to the binary into your `PATH`. 16 | 17 | [releases]: https://github.com/rust-lang/mdBook/releases 18 | 19 | ## Build from source using Rust 20 | 21 | To build the `mdbook` executable from source, you will first need to install Rust and Cargo. 22 | Follow the instructions on the [Rust installation page]. 23 | mdBook currently requires at least Rust version 1.82. 24 | 25 | Once you have installed Rust, the following command can be used to build and install mdBook: 26 | 27 | ```sh 28 | cargo install mdbook 29 | ``` 30 | 31 | This will automatically download mdBook from [crates.io], build it, and install it in Cargo's global binary directory (`~/.cargo/bin/` by default). 32 | 33 | You can run `cargo install mdbook` again whenever you want to update to a new version. 34 | That command will check if there is a newer version, and re-install mdBook if a newer version is found. 35 | 36 | To uninstall, run the command `cargo uninstall mdbook`. 37 | 38 | [Rust installation page]: https://www.rust-lang.org/tools/install 39 | [crates.io]: https://crates.io/ 40 | 41 | ### Installing the latest master version 42 | 43 | The version published to crates.io will ever so slightly be behind the version hosted on GitHub. 44 | If you need the latest version you can build the git version of mdBook yourself. 45 | Cargo makes this ***super easy***! 46 | 47 | ```sh 48 | cargo install --git https://github.com/rust-lang/mdBook.git mdbook 49 | ``` 50 | 51 | Again, make sure to add the Cargo bin directory to your `PATH`. 52 | 53 | ## Modifying and contributing 54 | 55 | If you are interested in making modifications to mdBook itself, check out the [Contributing Guide] for more information. 56 | 57 | [Contributing Guide]: https://github.com/rust-lang/mdBook/blob/master/CONTRIBUTING.md 58 | -------------------------------------------------------------------------------- /guide/src/guide/reading.md: -------------------------------------------------------------------------------- 1 | # Reading Books 2 | 3 | This chapter gives an introduction on how to interact with a book produced by mdBook. 4 | This assumes you are reading an HTML book. 5 | The options and formatting will be different for other output formats such as PDF. 6 | 7 | A book is organized into *chapters*. 8 | Each chapter is a separate page. 9 | Chapters can be nested into a hierarchy of sub-chapters. 10 | Typically, each chapter will be organized into a series of *headings* to subdivide a chapter. 11 | 12 | ## Navigation 13 | 14 | There are several methods for navigating through the chapters of a book. 15 | 16 | The **sidebar** on the left provides a list of all chapters. 17 | Clicking on any of the chapter titles will load that page. 18 | 19 | The sidebar may not automatically appear if the window is too narrow, particularly on mobile displays. 20 | In that situation, the menu icon (three horizontal bars) at the top-left of the page can be pressed to open and close the sidebar. 21 | 22 | The **arrow buttons** at the bottom of the page can be used to navigate to the previous or the next chapter. 23 | 24 | The **left and right arrow keys** on the keyboard can be used to navigate to the previous or the next chapter. 25 | 26 | ## Top menu bar 27 | 28 | The menu bar at the top of the page provides some icons for interacting with the book. 29 | The icons displayed will depend on the settings of how the book was generated. 30 | 31 | | Icon | Description | 32 | |------|-------------| 33 | | | Opens and closes the chapter listing sidebar. | 34 | | | Opens a picker to choose a different color theme. | 35 | | | Opens a search bar for searching within the book. | 36 | | | Instructs the web browser to print the entire book. | 37 | | | Opens a link to the website that hosts the source code of the book. | 38 | | | Opens a page to directly edit the source of the page you are currently reading. | 39 | 40 | Tapping the menu bar will scroll the page to the top. 41 | 42 | ## Search 43 | 44 | Each book has a built-in search system. 45 | Pressing the search icon () in the menu bar, or pressing the / or S key on the keyboard will open an input box for entering search terms. 46 | Typing some terms will show matching chapters and sections in real time. 47 | 48 | Clicking any of the results will jump to that section. 49 | The up and down arrow keys can be used to navigate the results, and enter will open the highlighted section. 50 | 51 | After loading a search result, the matching search terms will be highlighted in the text. 52 | Clicking a highlighted word or pressing the Escape key will remove the highlighting. 53 | 54 | ## Code blocks 55 | 56 | mdBook books are often used for programming projects, and thus support highlighting code blocks and samples. 57 | Code blocks may contain several different icons for interacting with them: 58 | 59 | | Icon | Description | 60 | |------|-------------| 61 | | | Copies the code block into your local clipboard, to allow pasting into another application. | 62 | | | For Rust code examples, this will execute the sample code and display the compiler output just below the example (see [playground]). | 63 | | | For Rust code examples, this will toggle visibility of "hidden" lines. Sometimes, larger examples will hide lines which are not particularly relevant to what is being illustrated (see [hiding code lines]). | 64 | | | For [editable code examples][editor], this will undo any changes you have made. | 65 | 66 | Here's an example: 67 | 68 | ```rust 69 | println!("Hello, World!"); 70 | ``` 71 | 72 | [editor]: ../format/theme/editor.md 73 | [playground]: ../format/mdbook.md#rust-playground 74 | [hiding code lines]: ../format/mdbook.md#hiding-code-lines 75 | -------------------------------------------------------------------------------- /guide/src/misc/contributors.md: -------------------------------------------------------------------------------- 1 | # Contributors 2 | 3 | Here is a list of the contributors who have helped improving mdBook. Big 4 | shout-out to them! 5 | 6 | - [mdinger](https://github.com/mdinger) 7 | - Kevin ([kbknapp](https://github.com/kbknapp)) 8 | - Steve Klabnik ([steveklabnik](https://github.com/steveklabnik)) 9 | - Adam Solove ([asolove](https://github.com/asolove)) 10 | - Wayne Nilsen ([waynenilsen](https://github.com/waynenilsen)) 11 | - [funnkill](https://github.com/funkill) 12 | - Fu Gangqiang ([FuGangqiang](https://github.com/FuGangqiang)) 13 | - [Michael-F-Bryan](https://github.com/Michael-F-Bryan) 14 | - Chris Spiegel ([cspiegel](https://github.com/cspiegel)) 15 | - [projektir](https://github.com/projektir) 16 | - [Phaiax](https://github.com/Phaiax) 17 | - Matt Ickstadt ([mattico](https://github.com/mattico)) 18 | - Weihang Lo ([weihanglo](https://github.com/weihanglo)) 19 | - Avision Ho ([avisionh](https://github.com/avisionh)) 20 | - Vivek Akupatni ([apatniv](https://github.com/apatniv)) 21 | - Eric Huss ([ehuss](https://github.com/ehuss)) 22 | - Josh Rotenberg ([joshrotenberg](https://github.com/joshrotenberg)) 23 | 24 | If you feel you're missing from this list, feel free to add yourself in a PR. 25 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "browser-ui-test": "^0.20.6", 4 | "eslint": "^8.57.1" 5 | }, 6 | "scripts": { 7 | "lint": "eslint src/front-end/*js src/front-end/**/*js", 8 | "lint-fix": "eslint --fix src/front-end/*js src/front-end/**/*js" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /release.toml: -------------------------------------------------------------------------------- 1 | sign-commit = true 2 | push-remote = "origin" 3 | tag-prefix = "v" 4 | -------------------------------------------------------------------------------- /src/cmd/build.rs: -------------------------------------------------------------------------------- 1 | use super::command_prelude::*; 2 | use crate::{get_book_dir, open}; 3 | use mdbook::errors::Result; 4 | use mdbook::MDBook; 5 | use std::path::PathBuf; 6 | 7 | // Create clap subcommand arguments 8 | pub fn make_subcommand() -> Command { 9 | Command::new("build") 10 | .about("Builds a book from its markdown files") 11 | .arg_dest_dir() 12 | .arg_root_dir() 13 | .arg_open() 14 | } 15 | 16 | // Build command implementation 17 | pub fn execute(args: &ArgMatches) -> Result<()> { 18 | let book_dir = get_book_dir(args); 19 | let mut book = MDBook::load(book_dir)?; 20 | 21 | if let Some(dest_dir) = args.get_one::("dest-dir") { 22 | book.config.build.build_dir = dest_dir.into(); 23 | } 24 | 25 | book.build()?; 26 | 27 | if args.get_flag("open") { 28 | // FIXME: What's the right behaviour if we don't use the HTML renderer? 29 | let path = book.build_dir_for("html").join("index.html"); 30 | if !path.exists() { 31 | error!("No chapter available to open"); 32 | std::process::exit(1) 33 | } 34 | open(path); 35 | } 36 | 37 | Ok(()) 38 | } 39 | -------------------------------------------------------------------------------- /src/cmd/command_prelude.rs: -------------------------------------------------------------------------------- 1 | //! Helpers for building the command-line arguments for commands. 2 | 3 | pub use clap::{arg, Arg, ArgMatches, Command}; 4 | use std::path::PathBuf; 5 | 6 | pub trait CommandExt: Sized { 7 | fn _arg(self, arg: Arg) -> Self; 8 | 9 | fn arg_dest_dir(self) -> Self { 10 | self._arg( 11 | Arg::new("dest-dir") 12 | .short('d') 13 | .long("dest-dir") 14 | .value_name("dest-dir") 15 | .value_parser(clap::value_parser!(PathBuf)) 16 | .help( 17 | "Output directory for the book\n\ 18 | Relative paths are interpreted relative to the book's root directory.\n\ 19 | If omitted, mdBook uses build.build-dir from book.toml \ 20 | or defaults to `./book`.", 21 | ), 22 | ) 23 | } 24 | 25 | fn arg_root_dir(self) -> Self { 26 | self._arg( 27 | Arg::new("dir") 28 | .help( 29 | "Root directory for the book\n\ 30 | (Defaults to the current directory when omitted)", 31 | ) 32 | .value_parser(clap::value_parser!(PathBuf)), 33 | ) 34 | } 35 | 36 | fn arg_open(self) -> Self { 37 | self._arg(arg!(-o --open "Opens the compiled book in a web browser")) 38 | } 39 | 40 | #[cfg(any(feature = "watch", feature = "serve"))] 41 | fn arg_watcher(self) -> Self { 42 | #[cfg(feature = "watch")] 43 | return self._arg( 44 | Arg::new("watcher") 45 | .long("watcher") 46 | .value_parser(["poll", "native"]) 47 | .default_value("poll") 48 | .help("The filesystem watching technique"), 49 | ); 50 | #[cfg(not(feature = "watch"))] 51 | return self; 52 | } 53 | } 54 | 55 | impl CommandExt for Command { 56 | fn _arg(self, arg: Arg) -> Self { 57 | self.arg(arg) 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/cmd/mod.rs: -------------------------------------------------------------------------------- 1 | //! Subcommand modules for the `mdbook` binary. 2 | 3 | pub mod build; 4 | pub mod clean; 5 | pub mod command_prelude; 6 | pub mod init; 7 | #[cfg(feature = "serve")] 8 | pub mod serve; 9 | pub mod test; 10 | #[cfg(feature = "watch")] 11 | pub mod watch; 12 | -------------------------------------------------------------------------------- /src/cmd/test.rs: -------------------------------------------------------------------------------- 1 | use super::command_prelude::*; 2 | use crate::get_book_dir; 3 | use clap::builder::NonEmptyStringValueParser; 4 | use clap::ArgAction; 5 | use mdbook::errors::Result; 6 | use mdbook::MDBook; 7 | use std::path::PathBuf; 8 | 9 | // Create clap subcommand arguments 10 | pub fn make_subcommand() -> Command { 11 | Command::new("test") 12 | .about("Tests that a book's Rust code samples compile") 13 | // FIXME: --dest-dir is unused by the test command, it should be removed 14 | .arg_dest_dir() 15 | .arg_root_dir() 16 | .arg( 17 | Arg::new("chapter") 18 | .short('c') 19 | .long("chapter") 20 | .value_name("chapter"), 21 | ) 22 | .arg( 23 | Arg::new("library-path") 24 | .short('L') 25 | .long("library-path") 26 | .value_name("dir") 27 | .value_delimiter(',') 28 | .value_parser(NonEmptyStringValueParser::new()) 29 | .action(ArgAction::Append) 30 | .help( 31 | "A comma-separated list of directories to add to the crate \ 32 | search path when building tests", 33 | ), 34 | ) 35 | } 36 | 37 | // test command implementation 38 | pub fn execute(args: &ArgMatches) -> Result<()> { 39 | let library_paths: Vec<&str> = args 40 | .get_many("library-path") 41 | .map(|it| it.map(String::as_str).collect()) 42 | .unwrap_or_default(); 43 | 44 | let chapter: Option<&str> = args.get_one::("chapter").map(|s| s.as_str()); 45 | 46 | let book_dir = get_book_dir(args); 47 | let mut book = MDBook::load(book_dir)?; 48 | 49 | if let Some(dest_dir) = args.get_one::("dest-dir") { 50 | book.config.build.build_dir = dest_dir.to_path_buf(); 51 | } 52 | match chapter { 53 | Some(_) => book.test_chapter(library_paths, chapter), 54 | None => book.test(library_paths), 55 | }?; 56 | 57 | Ok(()) 58 | } 59 | -------------------------------------------------------------------------------- /src/cmd/watch.rs: -------------------------------------------------------------------------------- 1 | use super::command_prelude::*; 2 | use crate::{get_book_dir, open}; 3 | use mdbook::errors::Result; 4 | use mdbook::MDBook; 5 | use std::path::{Path, PathBuf}; 6 | 7 | mod native; 8 | mod poller; 9 | 10 | // Create clap subcommand arguments 11 | pub fn make_subcommand() -> Command { 12 | Command::new("watch") 13 | .about("Watches a book's files and rebuilds it on changes") 14 | .arg_dest_dir() 15 | .arg_root_dir() 16 | .arg_open() 17 | .arg_watcher() 18 | } 19 | 20 | pub enum WatcherKind { 21 | Poll, 22 | Native, 23 | } 24 | 25 | impl WatcherKind { 26 | pub fn from_str(s: &str) -> WatcherKind { 27 | match s { 28 | "poll" => WatcherKind::Poll, 29 | "native" => WatcherKind::Native, 30 | _ => panic!("unsupported watcher {s}"), 31 | } 32 | } 33 | } 34 | 35 | // Watch command implementation 36 | pub fn execute(args: &ArgMatches) -> Result<()> { 37 | let book_dir = get_book_dir(args); 38 | let mut book = MDBook::load(&book_dir)?; 39 | 40 | let update_config = |book: &mut MDBook| { 41 | if let Some(dest_dir) = args.get_one::("dest-dir") { 42 | book.config.build.build_dir = dest_dir.into(); 43 | } 44 | }; 45 | update_config(&mut book); 46 | 47 | if args.get_flag("open") { 48 | book.build()?; 49 | let path = book.build_dir_for("html").join("index.html"); 50 | if !path.exists() { 51 | error!("No chapter available to open"); 52 | std::process::exit(1) 53 | } 54 | open(path); 55 | } 56 | 57 | let watcher = WatcherKind::from_str(args.get_one::("watcher").unwrap()); 58 | rebuild_on_change(watcher, &book_dir, &update_config, &|| {}); 59 | 60 | Ok(()) 61 | } 62 | 63 | pub fn rebuild_on_change( 64 | kind: WatcherKind, 65 | book_dir: &Path, 66 | update_config: &dyn Fn(&mut MDBook), 67 | post_build: &dyn Fn(), 68 | ) { 69 | match kind { 70 | WatcherKind::Poll => self::poller::rebuild_on_change(book_dir, update_config, post_build), 71 | WatcherKind::Native => self::native::rebuild_on_change(book_dir, update_config, post_build), 72 | } 73 | } 74 | 75 | fn find_gitignore(book_root: &Path) -> Option { 76 | book_root 77 | .ancestors() 78 | .map(|p| p.join(".gitignore")) 79 | .find(|p| p.exists()) 80 | } 81 | -------------------------------------------------------------------------------- /src/front-end/css/ayu-highlight.css: -------------------------------------------------------------------------------- 1 | /* 2 | Based off of the Ayu theme 3 | Original by Dempfi (https://github.com/dempfi/ayu) 4 | */ 5 | 6 | .hljs { 7 | display: block; 8 | overflow-x: auto; 9 | background: #191f26; 10 | color: #e6e1cf; 11 | } 12 | 13 | .hljs-comment, 14 | .hljs-quote { 15 | color: #5c6773; 16 | font-style: italic; 17 | } 18 | 19 | .hljs-variable, 20 | .hljs-template-variable, 21 | .hljs-attribute, 22 | .hljs-attr, 23 | .hljs-regexp, 24 | .hljs-link, 25 | .hljs-selector-id, 26 | .hljs-selector-class { 27 | color: #ff7733; 28 | } 29 | 30 | .hljs-number, 31 | .hljs-meta, 32 | .hljs-builtin-name, 33 | .hljs-literal, 34 | .hljs-type, 35 | .hljs-params { 36 | color: #ffee99; 37 | } 38 | 39 | .hljs-string, 40 | .hljs-bullet { 41 | color: #b8cc52; 42 | } 43 | 44 | .hljs-title, 45 | .hljs-built_in, 46 | .hljs-section { 47 | color: #ffb454; 48 | } 49 | 50 | .hljs-keyword, 51 | .hljs-selector-tag, 52 | .hljs-symbol { 53 | color: #ff7733; 54 | } 55 | 56 | .hljs-name { 57 | color: #36a3d9; 58 | } 59 | 60 | .hljs-tag { 61 | color: #00568d; 62 | } 63 | 64 | .hljs-emphasis { 65 | font-style: italic; 66 | } 67 | 68 | .hljs-strong { 69 | font-weight: bold; 70 | } 71 | 72 | .hljs-addition { 73 | color: #91b362; 74 | } 75 | 76 | .hljs-deletion { 77 | color: #d96c75; 78 | } 79 | -------------------------------------------------------------------------------- /src/front-end/css/highlight.css: -------------------------------------------------------------------------------- 1 | /* 2 | * An increased contrast highlighting scheme loosely based on the 3 | * "Base16 Atelier Dune Light" theme by Bram de Haan 4 | * (http://atelierbram.github.io/syntax-highlighting/atelier-schemes/dune) 5 | * Original Base16 color scheme by Chris Kempson 6 | * (https://github.com/chriskempson/base16) 7 | */ 8 | 9 | /* Comment */ 10 | .hljs-comment, 11 | .hljs-quote { 12 | color: #575757; 13 | } 14 | 15 | /* Red */ 16 | .hljs-variable, 17 | .hljs-template-variable, 18 | .hljs-attribute, 19 | .hljs-attr, 20 | .hljs-tag, 21 | .hljs-name, 22 | .hljs-regexp, 23 | .hljs-link, 24 | .hljs-name, 25 | .hljs-selector-id, 26 | .hljs-selector-class { 27 | color: #d70025; 28 | } 29 | 30 | /* Orange */ 31 | .hljs-number, 32 | .hljs-meta, 33 | .hljs-built_in, 34 | .hljs-builtin-name, 35 | .hljs-literal, 36 | .hljs-type, 37 | .hljs-params { 38 | color: #b21e00; 39 | } 40 | 41 | /* Green */ 42 | .hljs-string, 43 | .hljs-symbol, 44 | .hljs-bullet { 45 | color: #008200; 46 | } 47 | 48 | /* Blue */ 49 | .hljs-title, 50 | .hljs-section { 51 | color: #0030f2; 52 | } 53 | 54 | /* Purple */ 55 | .hljs-keyword, 56 | .hljs-selector-tag { 57 | color: #9d00ec; 58 | } 59 | 60 | .hljs { 61 | display: block; 62 | overflow-x: auto; 63 | background: #f6f7f6; 64 | color: #000; 65 | } 66 | 67 | .hljs-emphasis { 68 | font-style: italic; 69 | } 70 | 71 | .hljs-strong { 72 | font-weight: bold; 73 | } 74 | 75 | .hljs-addition { 76 | color: #22863a; 77 | background-color: #f0fff4; 78 | } 79 | 80 | .hljs-deletion { 81 | color: #b31d28; 82 | background-color: #ffeef0; 83 | } 84 | -------------------------------------------------------------------------------- /src/front-end/css/print.css: -------------------------------------------------------------------------------- 1 | 2 | #sidebar, 3 | #menu-bar, 4 | .nav-chapters, 5 | .mobile-nav-chapters { 6 | display: none; 7 | } 8 | 9 | #page-wrapper.page-wrapper { 10 | transform: none !important; 11 | margin-inline-start: 0px; 12 | overflow-y: initial; 13 | } 14 | 15 | #content { 16 | max-width: none; 17 | margin: 0; 18 | padding: 0; 19 | } 20 | 21 | .page { 22 | overflow-y: initial; 23 | } 24 | 25 | code { 26 | direction: ltr !important; 27 | } 28 | 29 | pre > .buttons { 30 | z-index: 2; 31 | } 32 | 33 | a, a:visited, a:active, a:hover { 34 | color: #4183c4; 35 | text-decoration: none; 36 | } 37 | 38 | h1, h2, h3, h4, h5, h6 { 39 | page-break-inside: avoid; 40 | page-break-after: avoid; 41 | } 42 | 43 | pre, code { 44 | page-break-inside: avoid; 45 | white-space: pre-wrap; 46 | } 47 | 48 | .fa { 49 | display: none !important; 50 | } 51 | -------------------------------------------------------------------------------- /src/front-end/css/tomorrow-night.css: -------------------------------------------------------------------------------- 1 | /* Tomorrow Night Theme */ 2 | /* https://github.com/jmblog/color-themes-for-highlightjs */ 3 | /* Original theme - https://github.com/chriskempson/tomorrow-theme */ 4 | /* https://github.com/jmblog/color-themes-for-highlightjs */ 5 | 6 | /* Tomorrow Comment */ 7 | .hljs-comment { 8 | color: #969896; 9 | } 10 | 11 | /* Tomorrow Red */ 12 | .hljs-variable, 13 | .hljs-attribute, 14 | .hljs-attr, 15 | .hljs-tag, 16 | .hljs-regexp, 17 | .ruby .hljs-constant, 18 | .xml .hljs-tag .hljs-title, 19 | .xml .hljs-pi, 20 | .xml .hljs-doctype, 21 | .html .hljs-doctype, 22 | .css .hljs-id, 23 | .css .hljs-class, 24 | .css .hljs-pseudo { 25 | color: #cc6666; 26 | } 27 | 28 | /* Tomorrow Orange */ 29 | .hljs-number, 30 | .hljs-preprocessor, 31 | .hljs-pragma, 32 | .hljs-built_in, 33 | .hljs-literal, 34 | .hljs-params, 35 | .hljs-constant { 36 | color: #de935f; 37 | } 38 | 39 | /* Tomorrow Yellow */ 40 | .ruby .hljs-class .hljs-title, 41 | .css .hljs-rule .hljs-attribute { 42 | color: #f0c674; 43 | } 44 | 45 | /* Tomorrow Green */ 46 | .hljs-string, 47 | .hljs-value, 48 | .hljs-inheritance, 49 | .hljs-header, 50 | .hljs-name, 51 | .ruby .hljs-symbol, 52 | .xml .hljs-cdata { 53 | color: #b5bd68; 54 | } 55 | 56 | /* Tomorrow Aqua */ 57 | .hljs-title, 58 | .hljs-section, 59 | .css .hljs-hexcolor { 60 | color: #8abeb7; 61 | } 62 | 63 | /* Tomorrow Blue */ 64 | .hljs-function, 65 | .python .hljs-decorator, 66 | .python .hljs-title, 67 | .ruby .hljs-function .hljs-title, 68 | .ruby .hljs-title .hljs-keyword, 69 | .perl .hljs-sub, 70 | .javascript .hljs-title, 71 | .coffeescript .hljs-title { 72 | color: #81a2be; 73 | } 74 | 75 | /* Tomorrow Purple */ 76 | .hljs-keyword, 77 | .javascript .hljs-function { 78 | color: #b294bb; 79 | } 80 | 81 | .hljs { 82 | display: block; 83 | overflow-x: auto; 84 | background: #1d1f21; 85 | color: #c5c8c6; 86 | } 87 | 88 | .coffeescript .javascript, 89 | .javascript .xml, 90 | .tex .hljs-formula, 91 | .xml .javascript, 92 | .xml .vbscript, 93 | .xml .css, 94 | .xml .hljs-cdata { 95 | opacity: 0.5; 96 | } 97 | 98 | .hljs-addition { 99 | color: #718c00; 100 | } 101 | 102 | .hljs-deletion { 103 | color: #c82829; 104 | } 105 | -------------------------------------------------------------------------------- /src/front-end/fonts/FontAwesome.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rust-lang/mdBook/1b046e5a907d70e4204461c30daa7b3ca112d519/src/front-end/fonts/FontAwesome.otf -------------------------------------------------------------------------------- /src/front-end/fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rust-lang/mdBook/1b046e5a907d70e4204461c30daa7b3ca112d519/src/front-end/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /src/front-end/fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rust-lang/mdBook/1b046e5a907d70e4204461c30daa7b3ca112d519/src/front-end/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /src/front-end/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rust-lang/mdBook/1b046e5a907d70e4204461c30daa7b3ca112d519/src/front-end/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /src/front-end/fonts/fontawesome-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rust-lang/mdBook/1b046e5a907d70e4204461c30daa7b3ca112d519/src/front-end/fonts/fontawesome-webfont.woff2 -------------------------------------------------------------------------------- /src/front-end/fonts/mod.rs: -------------------------------------------------------------------------------- 1 | pub static CSS: &[u8] = include_bytes!("fonts.css"); 2 | // An array of (file_name, file_contents) pairs 3 | pub static LICENSES: [(&str, &[u8]); 2] = [ 4 | ( 5 | "fonts/OPEN-SANS-LICENSE.txt", 6 | include_bytes!("OPEN-SANS-LICENSE.txt"), 7 | ), 8 | ( 9 | "fonts/SOURCE-CODE-PRO-LICENSE.txt", 10 | include_bytes!("SOURCE-CODE-PRO-LICENSE.txt"), 11 | ), 12 | ]; 13 | // An array of (file_name, file_contents) pairs 14 | pub static OPEN_SANS: [(&str, &[u8]); 10] = [ 15 | ( 16 | "fonts/open-sans-v17-all-charsets-300.woff2", 17 | include_bytes!("open-sans-v17-all-charsets-300.woff2"), 18 | ), 19 | ( 20 | "fonts/open-sans-v17-all-charsets-300italic.woff2", 21 | include_bytes!("open-sans-v17-all-charsets-300italic.woff2"), 22 | ), 23 | ( 24 | "fonts/open-sans-v17-all-charsets-regular.woff2", 25 | include_bytes!("open-sans-v17-all-charsets-regular.woff2"), 26 | ), 27 | ( 28 | "fonts/open-sans-v17-all-charsets-italic.woff2", 29 | include_bytes!("open-sans-v17-all-charsets-italic.woff2"), 30 | ), 31 | ( 32 | "fonts/open-sans-v17-all-charsets-600.woff2", 33 | include_bytes!("open-sans-v17-all-charsets-600.woff2"), 34 | ), 35 | ( 36 | "fonts/open-sans-v17-all-charsets-600italic.woff2", 37 | include_bytes!("open-sans-v17-all-charsets-600italic.woff2"), 38 | ), 39 | ( 40 | "fonts/open-sans-v17-all-charsets-700.woff2", 41 | include_bytes!("open-sans-v17-all-charsets-700.woff2"), 42 | ), 43 | ( 44 | "fonts/open-sans-v17-all-charsets-700italic.woff2", 45 | include_bytes!("open-sans-v17-all-charsets-700italic.woff2"), 46 | ), 47 | ( 48 | "fonts/open-sans-v17-all-charsets-800.woff2", 49 | include_bytes!("open-sans-v17-all-charsets-800.woff2"), 50 | ), 51 | ( 52 | "fonts/open-sans-v17-all-charsets-800italic.woff2", 53 | include_bytes!("open-sans-v17-all-charsets-800italic.woff2"), 54 | ), 55 | ]; 56 | 57 | // A (file_name, file_contents) pair 58 | pub static SOURCE_CODE_PRO: (&str, &[u8]) = ( 59 | "fonts/source-code-pro-v11-all-charsets-500.woff2", 60 | include_bytes!("source-code-pro-v11-all-charsets-500.woff2"), 61 | ); 62 | -------------------------------------------------------------------------------- /src/front-end/fonts/open-sans-v17-all-charsets-300.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rust-lang/mdBook/1b046e5a907d70e4204461c30daa7b3ca112d519/src/front-end/fonts/open-sans-v17-all-charsets-300.woff2 -------------------------------------------------------------------------------- /src/front-end/fonts/open-sans-v17-all-charsets-300italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rust-lang/mdBook/1b046e5a907d70e4204461c30daa7b3ca112d519/src/front-end/fonts/open-sans-v17-all-charsets-300italic.woff2 -------------------------------------------------------------------------------- /src/front-end/fonts/open-sans-v17-all-charsets-600.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rust-lang/mdBook/1b046e5a907d70e4204461c30daa7b3ca112d519/src/front-end/fonts/open-sans-v17-all-charsets-600.woff2 -------------------------------------------------------------------------------- /src/front-end/fonts/open-sans-v17-all-charsets-600italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rust-lang/mdBook/1b046e5a907d70e4204461c30daa7b3ca112d519/src/front-end/fonts/open-sans-v17-all-charsets-600italic.woff2 -------------------------------------------------------------------------------- /src/front-end/fonts/open-sans-v17-all-charsets-700.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rust-lang/mdBook/1b046e5a907d70e4204461c30daa7b3ca112d519/src/front-end/fonts/open-sans-v17-all-charsets-700.woff2 -------------------------------------------------------------------------------- /src/front-end/fonts/open-sans-v17-all-charsets-700italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rust-lang/mdBook/1b046e5a907d70e4204461c30daa7b3ca112d519/src/front-end/fonts/open-sans-v17-all-charsets-700italic.woff2 -------------------------------------------------------------------------------- /src/front-end/fonts/open-sans-v17-all-charsets-800.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rust-lang/mdBook/1b046e5a907d70e4204461c30daa7b3ca112d519/src/front-end/fonts/open-sans-v17-all-charsets-800.woff2 -------------------------------------------------------------------------------- /src/front-end/fonts/open-sans-v17-all-charsets-800italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rust-lang/mdBook/1b046e5a907d70e4204461c30daa7b3ca112d519/src/front-end/fonts/open-sans-v17-all-charsets-800italic.woff2 -------------------------------------------------------------------------------- /src/front-end/fonts/open-sans-v17-all-charsets-italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rust-lang/mdBook/1b046e5a907d70e4204461c30daa7b3ca112d519/src/front-end/fonts/open-sans-v17-all-charsets-italic.woff2 -------------------------------------------------------------------------------- /src/front-end/fonts/open-sans-v17-all-charsets-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rust-lang/mdBook/1b046e5a907d70e4204461c30daa7b3ca112d519/src/front-end/fonts/open-sans-v17-all-charsets-regular.woff2 -------------------------------------------------------------------------------- /src/front-end/fonts/source-code-pro-v11-all-charsets-500.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rust-lang/mdBook/1b046e5a907d70e4204461c30daa7b3ca112d519/src/front-end/fonts/source-code-pro-v11-all-charsets-500.woff2 -------------------------------------------------------------------------------- /src/front-end/images/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rust-lang/mdBook/1b046e5a907d70e4204461c30daa7b3ca112d519/src/front-end/images/favicon.png -------------------------------------------------------------------------------- /src/front-end/images/favicon.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/front-end/playground_editor/editor.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | window.editors = []; 3 | (function(editors) { 4 | if (typeof(ace) === 'undefined' || !ace) { 5 | return; 6 | } 7 | 8 | Array.from(document.querySelectorAll('.editable')).forEach(function(editable) { 9 | let display_line_numbers = window.playground_line_numbers || false; 10 | 11 | let editor = ace.edit(editable); 12 | editor.setOptions({ 13 | highlightActiveLine: false, 14 | showPrintMargin: false, 15 | showLineNumbers: display_line_numbers, 16 | showGutter: display_line_numbers, 17 | maxLines: Infinity, 18 | fontSize: "0.875em" // please adjust the font size of the code in general.css 19 | }); 20 | 21 | editor.$blockScrolling = Infinity; 22 | 23 | editor.getSession().setMode("ace/mode/rust"); 24 | 25 | editor.originalCode = editor.getValue(); 26 | 27 | editors.push(editor); 28 | }); 29 | })(window.editors); 30 | -------------------------------------------------------------------------------- /src/front-end/playground_editor/mod.rs: -------------------------------------------------------------------------------- 1 | //! Theme dependencies for the playground editor. 2 | 3 | pub static JS: &[u8] = include_bytes!("editor.js"); 4 | pub static ACE_JS: &[u8] = include_bytes!("ace.js"); 5 | pub static MODE_RUST_JS: &[u8] = include_bytes!("mode-rust.js"); 6 | pub static THEME_DAWN_JS: &[u8] = include_bytes!("theme-dawn.js"); 7 | pub static THEME_TOMORROW_NIGHT_JS: &[u8] = include_bytes!("theme-tomorrow_night.js"); 8 | -------------------------------------------------------------------------------- /src/front-end/playground_editor/theme-dawn.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/theme/dawn",["require","exports","module","ace/lib/dom"],function(e,t,n){t.isDark=!1,t.cssClass="ace-dawn",t.cssText=".ace-dawn .ace_gutter {background: #ebebeb;color: #333}.ace-dawn .ace_print-margin {width: 1px;background: #e8e8e8}.ace-dawn {background-color: #F9F9F9;color: #080808}.ace-dawn .ace_cursor {color: #000000}.ace-dawn .ace_marker-layer .ace_selection {background: rgba(39, 95, 255, 0.30)}.ace-dawn.ace_multiselect .ace_selection.ace_start {box-shadow: 0 0 3px 0px #F9F9F9;}.ace-dawn .ace_marker-layer .ace_step {background: rgb(255, 255, 0)}.ace-dawn .ace_marker-layer .ace_bracket {margin: -1px 0 0 -1px;border: 1px solid rgba(75, 75, 126, 0.50)}.ace-dawn .ace_marker-layer .ace_active-line {background: rgba(36, 99, 180, 0.12)}.ace-dawn .ace_gutter-active-line {background-color : #dcdcdc}.ace-dawn .ace_marker-layer .ace_selected-word {border: 1px solid rgba(39, 95, 255, 0.30)}.ace-dawn .ace_invisible {color: rgba(75, 75, 126, 0.50)}.ace-dawn .ace_keyword,.ace-dawn .ace_meta {color: #794938}.ace-dawn .ace_constant,.ace-dawn .ace_constant.ace_character,.ace-dawn .ace_constant.ace_character.ace_escape,.ace-dawn .ace_constant.ace_other {color: #811F24}.ace-dawn .ace_invalid.ace_illegal {text-decoration: underline;font-style: italic;color: #F8F8F8;background-color: #B52A1D}.ace-dawn .ace_invalid.ace_deprecated {text-decoration: underline;font-style: italic;color: #B52A1D}.ace-dawn .ace_support {color: #691C97}.ace-dawn .ace_support.ace_constant {color: #B4371F}.ace-dawn .ace_fold {background-color: #794938;border-color: #080808}.ace-dawn .ace_list,.ace-dawn .ace_markup.ace_list,.ace-dawn .ace_support.ace_function {color: #693A17}.ace-dawn .ace_storage {font-style: italic;color: #A71D5D}.ace-dawn .ace_string {color: #0B6125}.ace-dawn .ace_string.ace_regexp {color: #CF5628}.ace-dawn .ace_comment {font-style: italic;color: #5A525F}.ace-dawn .ace_heading,.ace-dawn .ace_markup.ace_heading {color: #19356D}.ace-dawn .ace_variable {color: #234A97}.ace-dawn .ace_indent-guide {background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAEklEQVQImWNgYGBgYLh/5+x/AAizA4hxNNsZAAAAAElFTkSuQmCC) right repeat-y}";var r=e("../lib/dom");r.importCssString(t.cssText,t.cssClass)}); (function() { 2 | ace.require(["ace/theme/dawn"], function(m) { 3 | if (typeof module == "object" && typeof exports == "object" && module) { 4 | module.exports = m; 5 | } 6 | }); 7 | })(); 8 | -------------------------------------------------------------------------------- /src/front-end/playground_editor/theme-tomorrow_night.js: -------------------------------------------------------------------------------- 1 | ace.define("ace/theme/tomorrow_night",["require","exports","module","ace/lib/dom"],function(e,t,n){t.isDark=!0,t.cssClass="ace-tomorrow-night",t.cssText=".ace-tomorrow-night .ace_gutter {background: #25282c;color: #C5C8C6}.ace-tomorrow-night .ace_print-margin {width: 1px;background: #25282c}.ace-tomorrow-night {background-color: #1D1F21;color: #C5C8C6}.ace-tomorrow-night .ace_cursor {color: #AEAFAD}.ace-tomorrow-night .ace_marker-layer .ace_selection {background: #373B41}.ace-tomorrow-night.ace_multiselect .ace_selection.ace_start {box-shadow: 0 0 3px 0px #1D1F21;}.ace-tomorrow-night .ace_marker-layer .ace_step {background: rgb(102, 82, 0)}.ace-tomorrow-night .ace_marker-layer .ace_bracket {margin: -1px 0 0 -1px;border: 1px solid #4B4E55}.ace-tomorrow-night .ace_marker-layer .ace_active-line {background: #282A2E}.ace-tomorrow-night .ace_gutter-active-line {background-color: #282A2E}.ace-tomorrow-night .ace_marker-layer .ace_selected-word {border: 1px solid #373B41}.ace-tomorrow-night .ace_invisible {color: #4B4E55}.ace-tomorrow-night .ace_keyword,.ace-tomorrow-night .ace_meta,.ace-tomorrow-night .ace_storage,.ace-tomorrow-night .ace_storage.ace_type,.ace-tomorrow-night .ace_support.ace_type {color: #B294BB}.ace-tomorrow-night .ace_keyword.ace_operator {color: #8ABEB7}.ace-tomorrow-night .ace_constant.ace_character,.ace-tomorrow-night .ace_constant.ace_language,.ace-tomorrow-night .ace_constant.ace_numeric,.ace-tomorrow-night .ace_keyword.ace_other.ace_unit,.ace-tomorrow-night .ace_support.ace_constant,.ace-tomorrow-night .ace_variable.ace_parameter {color: #DE935F}.ace-tomorrow-night .ace_constant.ace_other {color: #CED1CF}.ace-tomorrow-night .ace_invalid {color: #CED2CF;background-color: #DF5F5F}.ace-tomorrow-night .ace_invalid.ace_deprecated {color: #CED2CF;background-color: #B798BF}.ace-tomorrow-night .ace_fold {background-color: #81A2BE;border-color: #C5C8C6}.ace-tomorrow-night .ace_entity.ace_name.ace_function,.ace-tomorrow-night .ace_support.ace_function,.ace-tomorrow-night .ace_variable {color: #81A2BE}.ace-tomorrow-night .ace_support.ace_class,.ace-tomorrow-night .ace_support.ace_type {color: #F0C674}.ace-tomorrow-night .ace_heading,.ace-tomorrow-night .ace_markup.ace_heading,.ace-tomorrow-night .ace_string {color: #B5BD68}.ace-tomorrow-night .ace_entity.ace_name.ace_tag,.ace-tomorrow-night .ace_entity.ace_other.ace_attribute-name,.ace-tomorrow-night .ace_meta.ace_tag,.ace-tomorrow-night .ace_string.ace_regexp,.ace-tomorrow-night .ace_variable {color: #CC6666}.ace-tomorrow-night .ace_comment {color: #969896}.ace-tomorrow-night .ace_indent-guide {background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAEklEQVQImWNgYGBgYHB3d/8PAAOIAdULw8qMAAAAAElFTkSuQmCC) right repeat-y}";var r=e("../lib/dom");r.importCssString(t.cssText,t.cssClass)}); (function() { 2 | ace.require(["ace/theme/tomorrow_night"], function(m) { 3 | if (typeof module == "object" && typeof exports == "object" && module) { 4 | module.exports = m; 5 | } 6 | }); 7 | })(); 8 | -------------------------------------------------------------------------------- /src/front-end/searcher/mod.rs: -------------------------------------------------------------------------------- 1 | //! Theme dependencies for in-browser search. Not included in mdbook when 2 | //! the "search" cargo feature is disabled. 3 | 4 | pub static JS: &[u8] = include_bytes!("searcher.js"); 5 | pub static MARK_JS: &[u8] = include_bytes!("mark.min.js"); 6 | pub static ELASTICLUNR_JS: &[u8] = include_bytes!("elasticlunr.min.js"); 7 | -------------------------------------------------------------------------------- /src/front-end/templates/head.hbs: -------------------------------------------------------------------------------- 1 | {{!-- Put your head HTML text here --}} 2 | -------------------------------------------------------------------------------- /src/front-end/templates/header.hbs: -------------------------------------------------------------------------------- 1 | {{!-- Put your header HTML text here --}} -------------------------------------------------------------------------------- /src/front-end/templates/redirect.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Redirecting... 6 | 7 | 8 | 9 | 10 |

Redirecting to... {{url}}.

11 | 12 | 13 | -------------------------------------------------------------------------------- /src/front-end/templates/toc.html.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 15 | 16 | 17 | {{#if base_url}} 18 | 19 | {{/if}} 20 | 21 | {{> head}} 22 | 23 | 24 | 25 | 26 | 27 | {{#if print_enable}} 28 | 29 | {{/if}} 30 | 31 | 32 | {{#if copy_fonts}} 33 | 34 | {{/if}} 35 | 36 | {{#each additional_css}} 37 | 38 | {{/each}} 39 | 40 | 41 | {{#toc}}{{/toc}} 42 | 43 | 44 | -------------------------------------------------------------------------------- /src/front-end/templates/toc.js.hbs: -------------------------------------------------------------------------------- 1 | // Populate the sidebar 2 | // 3 | // This is a script, and not included directly in the page, to control the total size of the book. 4 | // The TOC contains an entry for each page, so if each page includes a copy of the TOC, 5 | // the total size of the page becomes O(n**2). 6 | class MDBookSidebarScrollbox extends HTMLElement { 7 | constructor() { 8 | super(); 9 | } 10 | connectedCallback() { 11 | this.innerHTML = '{{#toc}}{{/toc}}'; 12 | // Set the current, active page, and reveal it if it's hidden 13 | let current_page = document.location.href.toString().split("#")[0].split("?")[0]; 14 | if (current_page.endsWith("/")) { 15 | current_page += "index.html"; 16 | } 17 | var links = Array.prototype.slice.call(this.querySelectorAll("a")); 18 | var l = links.length; 19 | for (var i = 0; i < l; ++i) { 20 | var link = links[i]; 21 | var href = link.getAttribute("href"); 22 | if (href && !href.startsWith("#") && !/^(?:[a-z+]+:)?\/\//.test(href)) { 23 | link.href = path_to_root + href; 24 | } 25 | // The "index" page is supposed to alias the first chapter in the book. 26 | if (link.href === current_page || (i === 0 && path_to_root === "" && current_page.endsWith("/index.html"))) { 27 | link.classList.add("active"); 28 | var parent = link.parentElement; 29 | if (parent && parent.classList.contains("chapter-item")) { 30 | parent.classList.add("expanded"); 31 | } 32 | while (parent) { 33 | if (parent.tagName === "LI" && parent.previousElementSibling) { 34 | if (parent.previousElementSibling.classList.contains("chapter-item")) { 35 | parent.previousElementSibling.classList.add("expanded"); 36 | } 37 | } 38 | parent = parent.parentElement; 39 | } 40 | } 41 | } 42 | // Track and set sidebar scroll position 43 | this.addEventListener('click', function(e) { 44 | if (e.target.tagName === 'A') { 45 | sessionStorage.setItem('sidebar-scroll', this.scrollTop); 46 | } 47 | }, { passive: true }); 48 | var sidebarScrollTop = sessionStorage.getItem('sidebar-scroll'); 49 | sessionStorage.removeItem('sidebar-scroll'); 50 | if (sidebarScrollTop) { 51 | // preserve sidebar scroll position when navigating via links within sidebar 52 | this.scrollTop = sidebarScrollTop; 53 | } else { 54 | // scroll sidebar to current active section when navigating via "next/previous chapter" buttons 55 | var activeSection = document.querySelector('#sidebar .active'); 56 | if (activeSection) { 57 | activeSection.scrollIntoView({ block: 'center' }); 58 | } 59 | } 60 | // Toggle buttons 61 | var sidebarAnchorToggles = document.querySelectorAll('#sidebar a.toggle'); 62 | function toggleSection(ev) { 63 | ev.currentTarget.parentElement.classList.toggle('expanded'); 64 | } 65 | Array.from(sidebarAnchorToggles).forEach(function (el) { 66 | el.addEventListener('click', toggleSection); 67 | }); 68 | } 69 | } 70 | window.customElements.define("mdbook-sidebar-scrollbox", MDBookSidebarScrollbox); 71 | -------------------------------------------------------------------------------- /src/preprocess/index.rs: -------------------------------------------------------------------------------- 1 | use regex::Regex; 2 | use std::{path::Path, sync::LazyLock}; 3 | 4 | use super::{Preprocessor, PreprocessorContext}; 5 | use crate::book::{Book, BookItem}; 6 | use crate::errors::*; 7 | use log::warn; 8 | 9 | /// A preprocessor for converting file name `README.md` to `index.md` since 10 | /// `README.md` is the de facto index file in markdown-based documentation. 11 | #[derive(Default)] 12 | pub struct IndexPreprocessor; 13 | 14 | impl IndexPreprocessor { 15 | pub(crate) const NAME: &'static str = "index"; 16 | 17 | /// Create a new `IndexPreprocessor`. 18 | pub fn new() -> Self { 19 | IndexPreprocessor 20 | } 21 | } 22 | 23 | impl Preprocessor for IndexPreprocessor { 24 | fn name(&self) -> &str { 25 | Self::NAME 26 | } 27 | 28 | fn run(&self, ctx: &PreprocessorContext, mut book: Book) -> Result { 29 | let source_dir = ctx.root.join(&ctx.config.book.src); 30 | book.for_each_mut(|section: &mut BookItem| { 31 | if let BookItem::Chapter(ref mut ch) = *section { 32 | if let Some(ref mut path) = ch.path { 33 | if is_readme_file(&path) { 34 | let mut index_md = source_dir.join(path.with_file_name("index.md")); 35 | if index_md.exists() { 36 | warn_readme_name_conflict(&path, &&mut index_md); 37 | } 38 | 39 | path.set_file_name("index.md"); 40 | } 41 | } 42 | } 43 | }); 44 | 45 | Ok(book) 46 | } 47 | } 48 | 49 | fn warn_readme_name_conflict>(readme_path: P, index_path: P) { 50 | let file_name = readme_path.as_ref().file_name().unwrap_or_default(); 51 | let parent_dir = index_path 52 | .as_ref() 53 | .parent() 54 | .unwrap_or_else(|| index_path.as_ref()); 55 | warn!( 56 | "It seems that there are both {:?} and index.md under \"{}\".", 57 | file_name, 58 | parent_dir.display() 59 | ); 60 | warn!( 61 | "mdbook converts {:?} into index.html by default. It may cause", 62 | file_name 63 | ); 64 | warn!("unexpected behavior if putting both files under the same directory."); 65 | warn!("To solve the warning, try to rearrange the book structure or disable"); 66 | warn!("\"index\" preprocessor to stop the conversion."); 67 | } 68 | 69 | fn is_readme_file>(path: P) -> bool { 70 | static RE: LazyLock = LazyLock::new(|| Regex::new(r"(?i)^readme$").unwrap()); 71 | 72 | RE.is_match( 73 | path.as_ref() 74 | .file_stem() 75 | .and_then(std::ffi::OsStr::to_str) 76 | .unwrap_or_default(), 77 | ) 78 | } 79 | 80 | #[cfg(test)] 81 | mod tests { 82 | use super::*; 83 | 84 | #[test] 85 | fn file_stem_exactly_matches_readme_case_insensitively() { 86 | let path = "path/to/Readme.md"; 87 | assert!(is_readme_file(path)); 88 | 89 | let path = "path/to/README.md"; 90 | assert!(is_readme_file(path)); 91 | 92 | let path = "path/to/rEaDmE.md"; 93 | assert!(is_readme_file(path)); 94 | 95 | let path = "path/to/README.markdown"; 96 | assert!(is_readme_file(path)); 97 | 98 | let path = "path/to/README"; 99 | assert!(is_readme_file(path)); 100 | 101 | let path = "path/to/README-README.md"; 102 | assert!(!is_readme_file(path)); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/preprocess/mod.rs: -------------------------------------------------------------------------------- 1 | //! Book preprocessing. 2 | 3 | pub use self::cmd::CmdPreprocessor; 4 | pub use self::index::IndexPreprocessor; 5 | pub use self::links::LinkPreprocessor; 6 | 7 | mod cmd; 8 | mod index; 9 | mod links; 10 | 11 | use crate::book::Book; 12 | use crate::config::Config; 13 | use crate::errors::*; 14 | 15 | use serde::{Deserialize, Serialize}; 16 | use std::cell::RefCell; 17 | use std::collections::HashMap; 18 | use std::path::PathBuf; 19 | 20 | /// Extra information for a `Preprocessor` to give them more context when 21 | /// processing a book. 22 | #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] 23 | pub struct PreprocessorContext { 24 | /// The location of the book directory on disk. 25 | pub root: PathBuf, 26 | /// The book configuration (`book.toml`). 27 | pub config: Config, 28 | /// The `Renderer` this preprocessor is being used with. 29 | pub renderer: String, 30 | /// The calling `mdbook` version. 31 | pub mdbook_version: String, 32 | #[serde(skip)] 33 | pub(crate) chapter_titles: RefCell>, 34 | #[serde(skip)] 35 | __non_exhaustive: (), 36 | } 37 | 38 | impl PreprocessorContext { 39 | /// Create a new `PreprocessorContext`. 40 | pub(crate) fn new(root: PathBuf, config: Config, renderer: String) -> Self { 41 | PreprocessorContext { 42 | root, 43 | config, 44 | renderer, 45 | mdbook_version: crate::MDBOOK_VERSION.to_string(), 46 | chapter_titles: RefCell::new(HashMap::new()), 47 | __non_exhaustive: (), 48 | } 49 | } 50 | } 51 | 52 | /// An operation which is run immediately after loading a book into memory and 53 | /// before it gets rendered. 54 | pub trait Preprocessor { 55 | /// Get the `Preprocessor`'s name. 56 | fn name(&self) -> &str; 57 | 58 | /// Run this `Preprocessor`, allowing it to update the book before it is 59 | /// given to a renderer. 60 | fn run(&self, ctx: &PreprocessorContext, book: Book) -> Result; 61 | 62 | /// A hint to `MDBook` whether this preprocessor is compatible with a 63 | /// particular renderer. 64 | /// 65 | /// By default, always returns `true`. 66 | fn supports_renderer(&self, _renderer: &str) -> bool { 67 | true 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/renderer/html_handlebars/helpers/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod navigation; 2 | pub mod resources; 3 | pub mod theme; 4 | pub mod toc; 5 | -------------------------------------------------------------------------------- /src/renderer/html_handlebars/helpers/resources.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | use crate::utils; 4 | 5 | use handlebars::{ 6 | Context, Handlebars, Helper, HelperDef, Output, RenderContext, RenderError, RenderErrorReason, 7 | }; 8 | 9 | // Handlebars helper to find filenames with hashes in them 10 | #[derive(Clone)] 11 | pub struct ResourceHelper { 12 | pub hash_map: HashMap, 13 | } 14 | 15 | impl HelperDef for ResourceHelper { 16 | fn call<'reg: 'rc, 'rc>( 17 | &self, 18 | h: &Helper<'rc>, 19 | _r: &'reg Handlebars<'_>, 20 | ctx: &'rc Context, 21 | rc: &mut RenderContext<'reg, 'rc>, 22 | out: &mut dyn Output, 23 | ) -> Result<(), RenderError> { 24 | let param = h.param(0).and_then(|v| v.value().as_str()).ok_or_else(|| { 25 | RenderErrorReason::Other( 26 | "Param 0 with String type is required for theme_option helper.".to_owned(), 27 | ) 28 | })?; 29 | 30 | let base_path = rc 31 | .evaluate(ctx, "@root/path")? 32 | .as_json() 33 | .as_str() 34 | .ok_or_else(|| { 35 | RenderErrorReason::Other("Type error for `path`, string expected".to_owned()) 36 | })? 37 | .replace("\"", ""); 38 | 39 | let path_to_root = utils::fs::path_to_root(&base_path); 40 | 41 | out.write(&path_to_root)?; 42 | out.write(self.hash_map.get(param).map(|p| &p[..]).unwrap_or(¶m))?; 43 | Ok(()) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/renderer/html_handlebars/helpers/theme.rs: -------------------------------------------------------------------------------- 1 | use handlebars::{ 2 | Context, Handlebars, Helper, Output, RenderContext, RenderError, RenderErrorReason, 3 | }; 4 | use log::trace; 5 | 6 | pub fn theme_option( 7 | h: &Helper<'_>, 8 | _r: &Handlebars<'_>, 9 | ctx: &Context, 10 | rc: &mut RenderContext<'_, '_>, 11 | out: &mut dyn Output, 12 | ) -> Result<(), RenderError> { 13 | trace!("theme_option (handlebars helper)"); 14 | 15 | let param = h.param(0).and_then(|v| v.value().as_str()).ok_or_else(|| { 16 | RenderErrorReason::ParamTypeMismatchForName( 17 | "theme_option", 18 | "0".to_owned(), 19 | "string".to_owned(), 20 | ) 21 | })?; 22 | 23 | let default_theme = rc.evaluate(ctx, "@root/default_theme")?; 24 | let default_theme_name = default_theme.as_json().as_str().ok_or_else(|| { 25 | RenderErrorReason::ParamTypeMismatchForName( 26 | "theme_option", 27 | "default_theme".to_owned(), 28 | "string".to_owned(), 29 | ) 30 | })?; 31 | 32 | out.write(param)?; 33 | if param.to_lowercase() == default_theme_name.to_lowercase() { 34 | out.write(" (default)")?; 35 | } 36 | 37 | Ok(()) 38 | } 39 | -------------------------------------------------------------------------------- /src/renderer/html_handlebars/mod.rs: -------------------------------------------------------------------------------- 1 | pub use self::hbs_renderer::HtmlHandlebars; 2 | pub use self::static_files::StaticFiles; 3 | 4 | mod hbs_renderer; 5 | mod helpers; 6 | mod static_files; 7 | 8 | #[cfg(feature = "search")] 9 | mod search; 10 | -------------------------------------------------------------------------------- /src/renderer/markdown_renderer.rs: -------------------------------------------------------------------------------- 1 | use crate::book::BookItem; 2 | use crate::errors::*; 3 | use crate::renderer::{RenderContext, Renderer}; 4 | use crate::utils; 5 | use log::trace; 6 | use std::fs; 7 | 8 | #[derive(Default)] 9 | /// A renderer to output the Markdown after the preprocessors have run. Mostly useful 10 | /// when debugging preprocessors. 11 | pub struct MarkdownRenderer; 12 | 13 | impl MarkdownRenderer { 14 | /// Create a new `MarkdownRenderer` instance. 15 | pub fn new() -> Self { 16 | MarkdownRenderer 17 | } 18 | } 19 | 20 | impl Renderer for MarkdownRenderer { 21 | fn name(&self) -> &str { 22 | "markdown" 23 | } 24 | 25 | fn render(&self, ctx: &RenderContext) -> Result<()> { 26 | let destination = &ctx.destination; 27 | let book = &ctx.book; 28 | 29 | if destination.exists() { 30 | utils::fs::remove_dir_content(destination) 31 | .with_context(|| "Unable to remove stale Markdown output")?; 32 | } 33 | 34 | trace!("markdown render"); 35 | for item in book.iter() { 36 | if let BookItem::Chapter(ref ch) = *item { 37 | if !ch.is_draft_chapter() { 38 | utils::fs::write_file( 39 | &ctx.destination, 40 | ch.path.as_ref().expect("Checked path exists before"), 41 | ch.content.as_bytes(), 42 | )?; 43 | } 44 | } 45 | } 46 | 47 | fs::create_dir_all(destination) 48 | .with_context(|| "Unexpected error when constructing destination path")?; 49 | 50 | Ok(()) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/utils/toml_ext.rs: -------------------------------------------------------------------------------- 1 | use toml::value::{Table, Value}; 2 | 3 | pub(crate) trait TomlExt { 4 | fn read(&self, key: &str) -> Option<&Value>; 5 | fn read_mut(&mut self, key: &str) -> Option<&mut Value>; 6 | fn insert(&mut self, key: &str, value: Value); 7 | fn delete(&mut self, key: &str) -> Option; 8 | } 9 | 10 | impl TomlExt for Value { 11 | fn read(&self, key: &str) -> Option<&Value> { 12 | if let Some((head, tail)) = split(key) { 13 | self.get(head)?.read(tail) 14 | } else { 15 | self.get(key) 16 | } 17 | } 18 | 19 | fn read_mut(&mut self, key: &str) -> Option<&mut Value> { 20 | if let Some((head, tail)) = split(key) { 21 | self.get_mut(head)?.read_mut(tail) 22 | } else { 23 | self.get_mut(key) 24 | } 25 | } 26 | 27 | fn insert(&mut self, key: &str, value: Value) { 28 | if !self.is_table() { 29 | *self = Value::Table(Table::new()); 30 | } 31 | 32 | let table = self.as_table_mut().expect("unreachable"); 33 | 34 | if let Some((head, tail)) = split(key) { 35 | table 36 | .entry(head) 37 | .or_insert_with(|| Value::Table(Table::new())) 38 | .insert(tail, value); 39 | } else { 40 | table.insert(key.to_string(), value); 41 | } 42 | } 43 | 44 | fn delete(&mut self, key: &str) -> Option { 45 | if let Some((head, tail)) = split(key) { 46 | self.get_mut(head)?.delete(tail) 47 | } else if let Some(table) = self.as_table_mut() { 48 | table.remove(key) 49 | } else { 50 | None 51 | } 52 | } 53 | } 54 | 55 | fn split(key: &str) -> Option<(&str, &str)> { 56 | let ix = key.find('.')?; 57 | 58 | let (head, tail) = key.split_at(ix); 59 | // splitting will leave the "." 60 | let tail = &tail[1..]; 61 | 62 | Some((head, tail)) 63 | } 64 | 65 | #[cfg(test)] 66 | mod tests { 67 | use super::*; 68 | use std::str::FromStr; 69 | 70 | #[test] 71 | fn read_simple_table() { 72 | let src = "[table]"; 73 | let value = Value::from_str(src).unwrap(); 74 | 75 | let got = value.read("table").unwrap(); 76 | 77 | assert!(got.is_table()); 78 | } 79 | 80 | #[test] 81 | fn read_nested_item() { 82 | let src = "[table]\nnested=true"; 83 | let value = Value::from_str(src).unwrap(); 84 | 85 | let got = value.read("table.nested").unwrap(); 86 | 87 | assert_eq!(got, &Value::Boolean(true)); 88 | } 89 | 90 | #[test] 91 | fn insert_item_at_top_level() { 92 | let mut value = Value::Table(Table::default()); 93 | let item = Value::Boolean(true); 94 | 95 | value.insert("first", item.clone()); 96 | 97 | assert_eq!(value.get("first").unwrap(), &item); 98 | } 99 | 100 | #[test] 101 | fn insert_nested_item() { 102 | let mut value = Value::Table(Table::default()); 103 | let item = Value::Boolean(true); 104 | 105 | value.insert("first.second", item.clone()); 106 | 107 | let inserted = value.read("first.second").unwrap(); 108 | assert_eq!(inserted, &item); 109 | } 110 | 111 | #[test] 112 | fn delete_a_top_level_item() { 113 | let src = "top = true"; 114 | let mut value = Value::from_str(src).unwrap(); 115 | 116 | let got = value.delete("top").unwrap(); 117 | 118 | assert_eq!(got, Value::Boolean(true)); 119 | } 120 | 121 | #[test] 122 | fn delete_a_nested_item() { 123 | let src = "[table]\n nested = true"; 124 | let mut value = Value::from_str(src).unwrap(); 125 | 126 | let got = value.delete("table.nested").unwrap(); 127 | 128 | assert_eq!(got, Value::Boolean(true)); 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /test_book/book.toml: -------------------------------------------------------------------------------- 1 | [book] 2 | title = "mdBook test book" 3 | description = "A demo book to test and validate changes" 4 | authors = ["YJDoc2"] 5 | language = "en" 6 | 7 | [rust] 8 | edition = "2018" 9 | 10 | [output.html] 11 | mathjax-support = true 12 | hash-files = true 13 | 14 | [output.html.playground] 15 | editable = true 16 | line-numbers = true 17 | 18 | [output.html.search] 19 | limit-results = 20 20 | use-boolean-and = true 21 | boost-title = 2 22 | boost-hierarchy = 2 23 | boost-paragraph = 1 24 | expand = true 25 | heading-split-level = 2 26 | 27 | [output.html.redirect] 28 | "/format/config.html" = "configuration/index.html" 29 | -------------------------------------------------------------------------------- /test_book/src/README.md: -------------------------------------------------------------------------------- 1 | # Demo Book 2 | 3 | This is a simple demo book, which is intended to be used for verifying and validating style changes in mdBook. 4 | This contains dummy examples of various markdown elements and code languages, so that one can check changes made in mdBook styles. 5 | 6 | This rough outline is : 7 | 8 | - individual : contains basic markdown elements such as headings, paragraphs, links etc. 9 | - languages : contains a `hello world` in each of supported language to see changes in syntax highlighting 10 | - rust : contains language examples specific to rust, such as play pen, runnable examples etc. 11 | 12 | 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. 13 | -------------------------------------------------------------------------------- /test_book/src/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | [Prefix Chapter](prefix.md) 4 | 5 | --- 6 | 7 | - [Introduction](README.md) 8 | - [Draft Chapter]() 9 | 10 | # Actual Markdown Tag Examples 11 | 12 | - [Markdown Individual tags](individual/README.md) 13 | - [Heading](individual/heading.md) 14 | - [Paragraphs](individual/paragraph.md) 15 | - [Line Break](individual/linebreak.md) 16 | - [Emphasis](individual/emphasis.md) 17 | - [Blockquote](individual/blockquote.md) 18 | - [List](individual/list.md) 19 | - [Code](individual/code.md) 20 | - [Image](individual/image.md) 21 | - [Links and Horizontal Rule](individual/link_hr.md) 22 | - [Tables](individual/table.md) 23 | - [Tasks](individual/task.md) 24 | - [Strikethrough](individual/strikethrough.md) 25 | - [MathJax](individual/mathjax.md) 26 | - [Mixed](individual/mixed.md) 27 | - [Languages](languages/README.md) 28 | - [Syntax Highlight](languages/highlight.md) 29 | - [Rust Specific](rust/README.md) 30 | - [Rust Codeblocks](rust/rust_codeblock.md) 31 | 32 | --- 33 | 34 | [Suffix Chapter](suffix.md) 35 | -------------------------------------------------------------------------------- /test_book/src/individual/README.md: -------------------------------------------------------------------------------- 1 | # Individual Common mark tags 2 | 3 | This contains following tags: 4 | 5 | - Headings 6 | - Paragraphs 7 | - Line breaks 8 | - Emphasis 9 | - Blockquotes 10 | - Lists 11 | - Code blocks 12 | - Images 13 | - Links and Horizontal rules 14 | - Github tables 15 | - Github Task Lists 16 | - Strikethrough 17 | - Mixed 18 | -------------------------------------------------------------------------------- /test_book/src/individual/blockquote.md: -------------------------------------------------------------------------------- 1 | # Blockquote 2 | 3 | > This is a quoted sentence. 4 | 5 | > This is a quoted paragraph 6 | > 7 | > separated lines 8 | > here 9 | 10 | > Nested 11 | > 12 | > > Quoted 13 | > > Paragraph 14 | 15 | > ### And now, 16 | > 17 | > **Let us _introduce_** 18 | > All kinds of 19 | > 20 | > - tags 21 | > - etc 22 | > - stuff 23 | > 24 | > 1. In 25 | > 2. The 26 | > 3. blockquote 27 | > 28 | > > cause we can 29 | > > 30 | > > > Cause we can 31 | -------------------------------------------------------------------------------- /test_book/src/individual/code.md: -------------------------------------------------------------------------------- 1 | # Code 2 | 3 | This section only does simple code blocks and inline code, detailed syntax highlight and stuff is in the languages section 4 | 5 | --- 6 | 7 | ``` 8 | This is a codeblock 9 | ``` 10 | 11 | --- 12 | 13 | This line contains `inline code` mixed with some other stuff. (LTR) 14 | 15 | ושורה זו מכילה `inline code` אבל עם טקסט בשפה שנכתבת מימין לשמאל. (RTL) 16 | 17 | --- 18 | 19 | ```` 20 | escaping ``` in ```, fun, isn't is? 21 | ```` 22 | 23 | --- 24 | 25 | ```bash,editable 26 | This is an editable codeblock 27 | ``` 28 | 29 | --- 30 | 31 | ```rust 32 | // This links to a playpen 33 | ``` 34 | -------------------------------------------------------------------------------- /test_book/src/individual/emphasis.md: -------------------------------------------------------------------------------- 1 | # Emphasis 2 | 3 | This has **bold text** in between normal. 4 | 5 | This has _italic text_ in between normal. 6 | 7 | A **line** having _both_, bold and italic text. 8 | 9 | **A bold line _having_ italic text** 10 | 11 | _An Italic line having **bold** text_ 12 | 13 | Now this is going **_out of hands_**. 14 | -------------------------------------------------------------------------------- /test_book/src/individual/heading.md: -------------------------------------------------------------------------------- 1 | # Chapter Heading 2 | 3 | --- 4 | 5 | # Really Big Heading 6 | 7 | ## Big Heading 8 | 9 | ### Normal-ish Heading 10 | 11 | #### Small Heading...? 12 | 13 | ##### Really Small Heading 14 | 15 | ###### Is it even a heading anymore - heading 16 | 17 | ## Custom id {#example-id} 18 | 19 | ## Custom class {.class1 .class2} 20 | 21 | ## Both id and class {#example-id2 .class1 .class2} 22 | -------------------------------------------------------------------------------- /test_book/src/individual/image.md: -------------------------------------------------------------------------------- 1 | # Images 2 | 3 | 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) 4 | 5 | ## A 16x16 image 6 | 7 | ![16x16 rust-lang logo](https://rust-lang.org/logos/rust-logo-16x16.png) 8 | 9 | ## A 32x32 image 10 | 11 | ![32x32 rust-lang logo](https://rust-lang.org/logos/rust-logo-32x32-blk.png) 12 | 13 | ## A 256x256 image 14 | 15 | ![256x256 rust-lang logo](https://rust-lang.org/logos/rust-logo-256x256.png) 16 | 17 | ## A 512x512 image 18 | 19 | ![512x512 rust-lang logo](https://rust-lang.org/logos/rust-logo-512x512-blk.png) 20 | 21 | ## A large image 22 | 23 | ![2018 rust-conf art](https://raw.githubusercontent.com/rust-lang/rust-artwork/master/2018-RustConf/lucy-mountain-climber.png) 24 | 25 | ## A SVG image 26 | 27 | ![2018 rust-conf art svg](https://raw.githubusercontent.com/rust-lang/rust-artwork/461afe27d8e02451cf9f46e507f2c2a71d2b276b/2018-RustConf/lucy-mountain-climber.svg) 28 | -------------------------------------------------------------------------------- /test_book/src/individual/linebreak.md: -------------------------------------------------------------------------------- 1 | # Line breaks 2 | 3 | This is a long 4 | line with a couple of 5 | line breaks in
6 | between : both with two 7 | spaces and return,
8 | and with HTML tags. 9 | -------------------------------------------------------------------------------- /test_book/src/individual/link_hr.md: -------------------------------------------------------------------------------- 1 | # Links and Horizontal Rule 2 | 3 | This is followed by a Horizontal rule 4 | 5 | --- 6 | 7 | And this is preceded by a horizontal rule. 8 | 9 | [This](www.rust-lang.org) should link to rust-lang website 10 | [So should this][rl]. 11 | **[This][rl]** is a strong link. 12 | _[This][rl]_ is italic. 13 | **_[This][rl]_** is both. 14 | 15 | [rl]: www.rust-lang.org 16 | -------------------------------------------------------------------------------- /test_book/src/individual/list.md: -------------------------------------------------------------------------------- 1 | # Lists 2 | 3 | 1. A 4 | 2. Normal 5 | 3. Ordered 6 | 4. List 7 | 8 | --- 9 | 10 | 1. A 11 | 1. Nested 12 | 2. List 13 | 2. But 14 | 3. Still 15 | 4. Normal 16 | 17 | --- 18 | 19 | - An 20 | - Unordered 21 | - Normal 22 | - List 23 | 24 | --- 25 | 26 | - Nested 27 | - Unordered 28 | - List 29 | 30 | --- 31 | 32 | - This 33 | 1. Is 34 | 2. Normal 35 | - ?! 36 | -------------------------------------------------------------------------------- /test_book/src/individual/mathjax.md: -------------------------------------------------------------------------------- 1 | # MathJax 2 | 3 | Fourier Transform 4 | 5 | \\[ 6 | \begin{aligned} 7 | f(x) &= \int_{-\infty}^{\infty}F(s)(-1)^{ 2xs}ds \\\\ 8 | F(s) &= \int_{-\infty}^{\infty}f(x)(-1)^{-2xs}dx 9 | \end{aligned} 10 | \\] 11 | 12 | The kernel can also be written as \\(e^{2i\pi xs}\\) which is more frequently used in literature. 13 | 14 | > Proof that \\(e^{ix} = \cos x + i\sin x\\) a.k.a Euler's Formula: 15 | > 16 | > \\( 17 | \begin{aligned} 18 | e^x &= \sum_{n=0}^\infty \frac{x^n}{n!} \implies e^{ix} = \sum_{n=0}^\infty \frac{(ix)^n}{n!} \\\\ 19 | \cos x &= \sum_{m=0}^\infty \frac{(-1)^m x^{2m}}{(2m)!} = \sum_{m=0}^\infty \frac{(ix)^{2m}}{(2m)!} \\\\ 20 | \sin x &= \sum_{s=0}^\infty \frac{(-1)^s x^{2s+1}}{(2s+1)!} = \sum_{s=0}^\infty \frac{(ix)^{2s+1}}{i(2s+1)!} \\\\ 21 | \cos x + i\sin x &= \sum_{l=0}^\infty \frac{(ix)^{2l}}{(2l)!} + \sum_{s=0}^\infty \frac{(ix)^{2s+1}}{(2s+1)!} = \sum_{n=0}^\infty \frac{(ix)^{n}}{n!} \\\\ 22 | &= e^{ix} 23 | \end{aligned} 24 | \\) 25 | > 26 | 27 | 28 | Pauli Matrices 29 | 30 | \\[ 31 | \begin{aligned} 32 | \sigma_x &= \begin{pmatrix} 33 | 1 & 0 \\\\ 0 & 1 34 | \end{pmatrix} \\\\ 35 | \sigma_y &= \begin{pmatrix} 36 | 0 & -i \\\\ i & 0 37 | \end{pmatrix} \\\\ 38 | \sigma_z &= \begin{pmatrix} 39 | 1 & 0 \\\\ 0 & -1 40 | \end{pmatrix} 41 | \end{aligned} 42 | \\] -------------------------------------------------------------------------------- /test_book/src/individual/mixed.md: -------------------------------------------------------------------------------- 1 | # Mixed 2 | 3 | This contains all tags randomly mixed together, to make sure style changes in one does not affect others. 4 | 5 | ### A heading 6 | 7 | **Quite a Strong statement , to make** 8 | 9 | ~~No, cross that~~ 10 | 11 | > Whose **quote** is this 12 | > 13 | > > And ~~this~~ 14 | > > 15 | > > > - and 16 | > > > - this 17 | > > > - also 18 | 19 | ``` 20 | You encountered a wild codepen 21 | ``` 22 | 23 | ```rust,editable 24 | // The codepen is editable and runnable 25 | fn main(){ 26 | println!("Hello world!"); 27 | } 28 | ``` 29 | 30 | Ctrl + S saves a file. 31 | 32 | A random image sprinkled in between 33 | 34 | ![16x16 rust-lang logo](https://rust-lang.org/logos/rust-logo-16x16.png) 35 | 36 | --- 37 | 38 | - ~~An unordered list~~ 39 | - **Hello** 40 | - _World_ 41 | - What 42 | 1. Should 43 | 2. be 44 | 3. `put` 45 | 4. here? 46 | 5. **Ctrl + S saves a file.** 47 | 48 | | col1 | col2 | col 3 | col 4 | col 5 | col 6 | 49 | | ---- | ---- | ----- | ----- | ----- | ----- | 50 | | val1 | val2 | val3 | val5 | val4 | val6 | 51 | 52 | | col1 | col2 | col 3 | An Questionable table header | col 5 | col 6 | 53 | | ---- | ---- | ----- | ---------------------------- | ----- | ---------------------------------------- | 54 | | val1 | val2 | val3 | val5 | val4 | An equally Questionable long table value | 55 | 56 | ### Things to do 57 | 58 | - [x] Add individual tags 59 | - [ ] Add language examples 60 | - [ ] Add rust specific examples 61 | 62 | And another image 63 | 64 | ![2018 rust-conf art svg](https://raw.githubusercontent.com/rust-lang/rust-artwork/461afe27d8e02451cf9f46e507f2c2a71d2b276b/2018-RustConf/lucy-mountain-climber.svg) 65 | -------------------------------------------------------------------------------- /test_book/src/individual/strikethrough.md: -------------------------------------------------------------------------------- 1 | # Strikethrough 2 | 3 | ~Single strike~ 4 | 5 | ~~This is Striked~~ 6 | 7 | ~~This is **strong**, _italic_ , **_both_** and striked~~ 8 | -------------------------------------------------------------------------------- /test_book/src/individual/table.md: -------------------------------------------------------------------------------- 1 | # Tables 2 | 3 | | col1 | col2 | 4 | | ---- | ---- | 5 | 6 | --- 7 | 8 | | col1 | col2 | 9 | | ---- | ---- | 10 | | val1 | val2 | 11 | 12 | --- 13 | 14 | | col1 | col2 | col 3 | col 4 | col 5 | col 6 | 15 | | ---- | ---- | ----- | ----- | ----- | ----- | 16 | | val1 | val2 | val3 | val5 | val4 | val6 | 17 | | val1 | val2 | val3 | val5 | val4 | val6 | 18 | | val1 | val2 | val3 | val5 | val4 | val6 | 19 | | val1 | val2 | val3 | val5 | val4 | val6 | 20 | 21 | --- 22 | 23 | | col1 | col2 | col 3 | col 4 | col 5 | col 6 | 24 | | -------------------------------------------------------------------------------------------------------------- | ---- | -------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- | ----- | -------------------------------------------------------------------------------------------------------------- | 25 | | 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 | 26 | | val1 | val2 | val3 | val5 | val4 | val6 | 27 | | 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. | 28 | | 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 | 29 | -------------------------------------------------------------------------------- /test_book/src/individual/task.md: -------------------------------------------------------------------------------- 1 | # Tasks 2 | 3 | - [ ] Task 1 4 | - [ ] Task 2 5 | - [x] Completed Task 1 6 | - [x] Completed Task 2 7 | 8 | --- 9 | 10 | - [ ] **Important Task** 11 | - [x] _Completed Important task_ 12 | -------------------------------------------------------------------------------- /test_book/src/languages/README.md: -------------------------------------------------------------------------------- 1 | # Syntax Highlighting 2 | 3 | This Currently contains following languages 4 | 5 | - apache 6 | - armasm 7 | - bash 8 | - c 9 | - coffeescript 10 | - cpp 11 | - csharp 12 | - css 13 | - d 14 | - diff 15 | - go 16 | - handlebars 17 | - haskell 18 | - http 19 | - ini 20 | - java 21 | - javascript 22 | - json 23 | - julia 24 | - kotlin 25 | - less 26 | - lua 27 | - makefile 28 | - markdown 29 | - nginx 30 | - nim 31 | - nix 32 | - objectivec 33 | - perl 34 | - php 35 | - plaintext 36 | - properties 37 | - python 38 | - r 39 | - ruby 40 | - rust 41 | - scala 42 | - scss 43 | - shell 44 | - sql 45 | - swift 46 | - typescript 47 | - x86asm 48 | - xml 49 | - yaml 50 | -------------------------------------------------------------------------------- /test_book/src/prefix.md: -------------------------------------------------------------------------------- 1 | # Prefix Chapter 2 | 3 | This is to verify the placement and style of prefix chapter in book index. 4 | -------------------------------------------------------------------------------- /test_book/src/rust/README.md: -------------------------------------------------------------------------------- 1 | # Rust specific code examples 2 | -------------------------------------------------------------------------------- /test_book/src/rust/rust_codeblock.md: -------------------------------------------------------------------------------- 1 | ## Rust codeblocks 2 | 3 | This contains various examples of codeblocks, specific to rust 4 | 5 | ## Simple 6 | 7 | ```rust 8 | fn main(){ 9 | println!("Hello world!"); 10 | } 11 | ``` 12 | 13 | ## With Hidden lines 14 | 15 | ```rust 16 | # fn main(){ 17 | println!("Hello world!"); 18 | # } 19 | ``` 20 | 21 | ## Editable 22 | 23 | ```rust,editable 24 | fn main(){ 25 | println!("Hello world!"); 26 | } 27 | ``` 28 | -------------------------------------------------------------------------------- /test_book/src/suffix.md: -------------------------------------------------------------------------------- 1 | # Suffix Chapter 2 | 3 | This is to verify the placement and style of suffix chapter in book index. 4 | -------------------------------------------------------------------------------- /tests/gui/help.goml: -------------------------------------------------------------------------------- 1 | // This GUI test checks help popup. 2 | 3 | go-to: |DOC_PATH| + "index.html" 4 | assert-css: ("#mdbook-help-container", {"display": "none"}) 5 | press-key: '?' 6 | wait-for-css: ("#mdbook-help-container", {"display": "flex"}) 7 | press-key: 'Escape' 8 | wait-for-css: ("#mdbook-help-container", {"display": "none"}) 9 | press-key: '?' 10 | wait-for-css: ("#mdbook-help-container", {"display": "flex"}) 11 | // Click inside does nothing. 12 | click: "#mdbook-help-popup" 13 | wait-for-css: ("#mdbook-help-container", {"display": "flex"}) 14 | // Click outside dismisses. 15 | click: "*" 16 | wait-for-css: ("#mdbook-help-container", {"display": "none"}) 17 | -------------------------------------------------------------------------------- /tests/gui/move-between-pages.goml: -------------------------------------------------------------------------------- 1 | // This tests pressing the left and right arrows moving to previous and next page. 2 | 3 | go-to: |DOC_PATH| + "index.html" 4 | 5 | // default page is the first numbered page 6 | assert-text: ("title", "Introduction - mdBook test book") 7 | 8 | // Moving left we get to the prefix page 9 | press-key: 'ArrowLeft' 10 | assert-text: ("title", "Prefix Chapter - mdBook test book") 11 | 12 | // Trying to move to the left beyond the prefix pages - nothing changes 13 | press-key: 'ArrowLeft' 14 | assert-text: ("title", "Prefix Chapter - mdBook test book") 15 | 16 | // Back to the main page 17 | press-key: 'ArrowRight' 18 | assert-text: ("title", "Introduction - mdBook test book") 19 | 20 | press-key: 'ArrowRight' 21 | assert-text: ("title", "Markdown Individual tags - mdBook test book") 22 | 23 | press-key: 'ArrowRight' 24 | assert-text: ("title", "Heading - mdBook test book") 25 | 26 | // Last numbered page 27 | go-to: "../rust/rust_codeblock.html" 28 | assert-text: ("title", "Rust Codeblocks - mdBook test book") 29 | 30 | // Go to the suffix chapter 31 | press-key: 'ArrowRight' 32 | assert-text: ("title", "Suffix Chapter - mdBook test book") 33 | 34 | // Try to go beyond the last page 35 | press-key: 'ArrowRight' 36 | assert-text: ("title", "Suffix Chapter - mdBook test book") 37 | -------------------------------------------------------------------------------- /tests/gui/search.goml: -------------------------------------------------------------------------------- 1 | // This tests basic search behavior. 2 | 3 | go-to: |DOC_PATH| + "index.html" 4 | 5 | define-function: ( 6 | "open-search", 7 | [], 8 | block { 9 | assert-css: ("#search-wrapper", {"display": "none"}) 10 | press-key: 's' 11 | wait-for-css-false: ("#search-wrapper", {"display": "none"}) 12 | } 13 | ) 14 | 15 | call-function: ("open-search", {}) 16 | assert-text: ("#searchresults-header", "") 17 | write: "strikethrough" 18 | wait-for-text: ("#searchresults-header", "2 search results for 'strikethrough':") 19 | // Close the search display 20 | press-key: 'Escape' 21 | wait-for-css: ("#search-wrapper", {"display": "none"}) 22 | // Reopening the search should show the last value 23 | call-function: ("open-search", {}) 24 | assert-text: ("#searchresults-header", "2 search results for 'strikethrough':") 25 | // Navigate to a sub-chapter 26 | go-to: "./individual/strikethrough.html" 27 | assert-text: ("#searchresults-header", "") 28 | call-function: ("open-search", {}) 29 | write: "strikethrough" 30 | wait-for-text: ("#searchresults-header", "2 search results for 'strikethrough':") 31 | -------------------------------------------------------------------------------- /tests/gui/sidebar-active.goml: -------------------------------------------------------------------------------- 1 | // This GUI test checks the active page sidebar highlight. 2 | 3 | go-to: |DOC_PATH| + "index.html" 4 | 5 | assert-text: ("mdbook-sidebar-scrollbox a.active", "Prefix Chapter") 6 | 7 | go-to: |DOC_PATH| + "individual/index.html" 8 | 9 | assert-text: ("mdbook-sidebar-scrollbox a.active", "3. Markdown Individual tags") 10 | 11 | go-to: |DOC_PATH| + "index.html?highlight=test" 12 | 13 | assert-text: ("mdbook-sidebar-scrollbox a.active", "Prefix Chapter") 14 | 15 | go-to: |DOC_PATH| + "individual/index.html?highlight=test" 16 | 17 | assert-text: ("mdbook-sidebar-scrollbox a.active", "3. Markdown Individual tags") 18 | -------------------------------------------------------------------------------- /tests/gui/sidebar-nojs.goml: -------------------------------------------------------------------------------- 1 | // This GUI test checks that the sidebar takes the whole height when it's inside 2 | // an iframe (because of JS disabled). 3 | // Regression test for . 4 | 5 | // We disable javascript 6 | javascript: false 7 | go-to: |DOC_PATH| + "index.html" 8 | store-value: (height, 1000) 9 | set-window-size: (1000, |height|) 10 | 11 | within-iframe: (".sidebar-iframe-outer", block { 12 | assert-size: (" body", {"height": |height|}) 13 | }) 14 | -------------------------------------------------------------------------------- /tests/gui/sidebar.goml: -------------------------------------------------------------------------------- 1 | // This GUI test checks sidebar hide/show and also its behaviour on smaller 2 | // width. 3 | 4 | go-to: |DOC_PATH| + "index.html" 5 | set-window-size: (1100, 600) 6 | // Need to reload for the new size to be taken account by the JS. 7 | reload: 8 | 9 | store-value: (content_indent, 308) 10 | 11 | define-function: ( 12 | "hide-sidebar", 13 | [], 14 | block { 15 | // The content should be "moved" to the right because of the sidebar. 16 | assert-css: ("#sidebar", {"transform": "none"}) 17 | assert-position: ("#page-wrapper", {"x": |content_indent|}) 18 | 19 | // We now hide the sidebar. 20 | click: "#sidebar-toggle" 21 | wait-for: "body.sidebar-hidden" 22 | // `transform` is 0.3s so we need to wait a bit (0.5s) to ensure the animation is done. 23 | wait-for: 5000 24 | assert-css-false: ("#sidebar", {"transform": "none"}) 25 | // The page content should now be on the left. 26 | assert-position: ("#page-wrapper", {"x": 0}) 27 | }, 28 | ) 29 | 30 | define-function: ( 31 | "show-sidebar", 32 | [], 33 | block { 34 | // The page content should be on the left and the sidebar "moved out". 35 | assert-css: ("#sidebar", {"transform": "matrix(1, 0, 0, 1, -308, 0)"}) 36 | assert-position: ("#page-wrapper", {"x": 0}) 37 | 38 | // We expand the sidebar. 39 | click: "#sidebar-toggle" 40 | wait-for: "body.sidebar-visible" 41 | // `transform` is 0.3s so we need to wait a bit (0.5s) to ensure the animation is done. 42 | wait-for: 5000 43 | assert-css-false: ("#sidebar", {"transform": "matrix(1, 0, 0, 1, -308, 0)"}) 44 | // The page content should be moved to the right. 45 | assert-position: ("#page-wrapper", {"x": |content_indent|}) 46 | }, 47 | ) 48 | 49 | call-function: ("hide-sidebar", {}) 50 | call-function: ("show-sidebar", {}) 51 | 52 | // We now test on smaller width to ensure that the sidebar is collapsed by default. 53 | set-window-size: (900, 600) 54 | reload: 55 | call-function: ("show-sidebar", {}) 56 | call-function: ("hide-sidebar", {}) 57 | -------------------------------------------------------------------------------- /tests/testsuite/README.md: -------------------------------------------------------------------------------- 1 | # Testsuite 2 | 3 | ## Introduction 4 | 5 | This is the main testsuite for exercising all functionality of mdBook. 6 | 7 | Tests should be organized into modules based around major features. Tests should use `BookTest` to drive the test. `BookTest` will set up a temp directory, and provides a variety of methods to help create a build books. 8 | 9 | ## Basic structure of a test 10 | 11 | Using `BookTest`, you typically use it to copy a directory into a temp directory, and then run mdbook commands in that temp directory. You can run the `mdbook` executable, or use the mdbook API to perform whatever tasks you need. Running the executable has the benefit of being able to validate the console output. 12 | 13 | See `build::basic_build` for a simple test example. I recommend reviewing the methods on `BookTest` to learn more, and reviewing some of the existing tests to get a feel for how they are structured. 14 | 15 | For example, let's say you are creating a new theme test. In the `testsuite/theme` directory, create a new directory with the book source that you want to exercise. At a minimum, this needs a `src/SUMMARY.md`, but often you'll also want `book.toml`. Then, in `testsuite/theme.rs`, add a test with `BookTest::from_dir("theme/mytest")`, and then use the methods to perform whatever actions you want. 16 | 17 | `BookTest` is designed to be able to chain a series of actions. For example, you can do something like: 18 | 19 | ```rust 20 | BookTest::from_dir("theme/mytest") 21 | .build() 22 | .check_main_file("book/index.html", str![["file contents"]]) 23 | .change_file("src/index.md", "new contents") 24 | .build() 25 | .check_main_file("book/index.html", str![["new contents"]]); 26 | ``` 27 | 28 | ## Snapbox 29 | 30 | The testsuite uses [`snapbox`] to drive most of the tests. This library provides the ability to compare strings using a variety of methods. These strings are written in the source code using either the [`str!`] or [`file!`] macros. 31 | 32 | The magic is that you can set the `SNAPSHOTS=overwrite` environment variable, and snapbox will automatically update the strings contents of `str!`, or the file contents of `file!`. This makes it easier to update tests. Snapbox provides nice diffing output, and quite a few other features. 33 | 34 | Expected contents can have wildcards like `...` (matches any lines) or `[..]` (matches any characters on a line). See [snapbox filters] for more info and other filters. 35 | 36 | Typically when writing a test, I'll just start with an empty `str!` or `file!`, and let snapbox fill it in. Then I review the contents to make sure they are what I expect. 37 | 38 | Note that there is some normalization applied to the strings. See `book_test::assert` for how some of these normalizations happen. 39 | 40 | [`snapbox`]: https://docs.rs/snapbox/latest/snapbox/ 41 | [`str!`]: https://docs.rs/snapbox/latest/snapbox/macro.str.html 42 | [`file!`]: https://docs.rs/snapbox/latest/snapbox/macro.file.html 43 | [snapbox filters]: https://docs.rs/snapbox/latest/snapbox/assert/struct.Assert.html#method.eq 44 | -------------------------------------------------------------------------------- /tests/testsuite/build.rs: -------------------------------------------------------------------------------- 1 | //! General build tests. 2 | //! 3 | //! More specific tests should usually go into a module based on the feature. 4 | //! This module should just have general build tests, or misc small things. 5 | 6 | use crate::prelude::*; 7 | 8 | // Simple smoke test that building works. 9 | #[test] 10 | fn basic_build() { 11 | BookTest::from_dir("build/basic_build").run("build", |cmd| { 12 | cmd.expect_stderr(str![[r#" 13 | [TIMESTAMP] [INFO] (mdbook::book): Book building has started 14 | [TIMESTAMP] [INFO] (mdbook::book): Running the html backend 15 | 16 | "#]]); 17 | }); 18 | } 19 | 20 | // Ensure building fails if `create-missing` is false and one of the files does 21 | // not exist. 22 | #[test] 23 | fn failure_on_missing_file() { 24 | BookTest::from_dir("build/missing_file").run("build", |cmd| { 25 | cmd.expect_failure().expect_stderr(str![[r#" 26 | [TIMESTAMP] [ERROR] (mdbook::utils): Error: Chapter file not found, ./chapter_1.md 27 | [TIMESTAMP] [ERROR] (mdbook::utils): [TAB]Caused By: [NOT_FOUND] 28 | 29 | "#]]); 30 | }); 31 | } 32 | 33 | // Ensure a missing file is created if `create-missing` is true. 34 | #[test] 35 | fn create_missing() { 36 | let test = BookTest::from_dir("build/create_missing"); 37 | assert!(test.dir.join("src/SUMMARY.md").exists()); 38 | assert!(!test.dir.join("src/chapter_1.md").exists()); 39 | test.load_book(); 40 | assert!(test.dir.join("src/chapter_1.md").exists()); 41 | } 42 | 43 | // Checks that it fails if the summary has a reserved filename. 44 | #[test] 45 | fn no_reserved_filename() { 46 | BookTest::from_dir("build/no_reserved_filename").run("build", |cmd| { 47 | cmd.expect_failure().expect_stderr(str![[r#" 48 | [TIMESTAMP] [INFO] (mdbook::book): Book building has started 49 | [TIMESTAMP] [INFO] (mdbook::book): Running the html backend 50 | [TIMESTAMP] [ERROR] (mdbook::utils): Error: Rendering failed 51 | [TIMESTAMP] [ERROR] (mdbook::utils): [TAB]Caused By: print.md is reserved for internal use 52 | 53 | "#]]); 54 | }); 55 | } 56 | 57 | // Build without book.toml should be OK. 58 | #[test] 59 | fn book_toml_isnt_required() { 60 | let mut test = BookTest::init(|_| {}); 61 | std::fs::remove_file(test.dir.join("book.toml")).unwrap(); 62 | test.build(); 63 | test.check_main_file( 64 | "book/chapter_1.html", 65 | str![[r##"

Chapter 1

"##]], 66 | ); 67 | } 68 | -------------------------------------------------------------------------------- /tests/testsuite/build/basic_build/book.toml: -------------------------------------------------------------------------------- 1 | [book] 2 | title = "basic_build" 3 | -------------------------------------------------------------------------------- /tests/testsuite/build/basic_build/src/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | - [Chapter 1](./chapter_1.md) 4 | -------------------------------------------------------------------------------- /tests/testsuite/build/basic_build/src/chapter_1.md: -------------------------------------------------------------------------------- 1 | # Chapter 1 2 | -------------------------------------------------------------------------------- /tests/testsuite/build/create_missing/book.toml: -------------------------------------------------------------------------------- 1 | [book] 2 | title = "create_missing" 3 | -------------------------------------------------------------------------------- /tests/testsuite/build/create_missing/src/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | - [Chapter 1](./chapter_1.md) 4 | -------------------------------------------------------------------------------- /tests/testsuite/build/missing_file/book.toml: -------------------------------------------------------------------------------- 1 | [book] 2 | title = "missing_file" 3 | 4 | [build] 5 | create-missing = false 6 | -------------------------------------------------------------------------------- /tests/testsuite/build/missing_file/src/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | - [Chapter 1](./chapter_1.md) 4 | -------------------------------------------------------------------------------- /tests/testsuite/build/no_reserved_filename/book.toml: -------------------------------------------------------------------------------- 1 | [book] 2 | title = "no_reserved_filename" 3 | -------------------------------------------------------------------------------- /tests/testsuite/build/no_reserved_filename/src/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | - [Print](print.md) 4 | -------------------------------------------------------------------------------- /tests/testsuite/build/no_reserved_filename/src/print.md: -------------------------------------------------------------------------------- 1 | # Print 2 | -------------------------------------------------------------------------------- /tests/testsuite/cli.rs: -------------------------------------------------------------------------------- 1 | //! Basic tests for mdbook's CLI. 2 | 3 | use crate::prelude::*; 4 | use snapbox::file; 5 | 6 | // Test with no args. 7 | #[test] 8 | #[cfg_attr( 9 | not(all(feature = "watch", feature = "serve")), 10 | ignore = "needs all features" 11 | )] 12 | fn no_args() { 13 | BookTest::empty().run("", |cmd| { 14 | cmd.expect_code(2) 15 | .expect_stdout(str![[""]]) 16 | .expect_stderr(file!["cli/no_args.term.svg"]); 17 | }); 18 | } 19 | 20 | // Help command. 21 | #[test] 22 | #[cfg_attr( 23 | not(all(feature = "watch", feature = "serve")), 24 | ignore = "needs all features" 25 | )] 26 | fn help() { 27 | BookTest::empty() 28 | .run("help", |cmd| { 29 | cmd.expect_stdout(file!["cli/help.term.svg"]) 30 | .expect_stderr(str![[""]]); 31 | }) 32 | .run("--help", |cmd| { 33 | cmd.expect_stdout(file!["cli/help.term.svg"]) 34 | .expect_stderr(str![[""]]); 35 | }); 36 | } 37 | -------------------------------------------------------------------------------- /tests/testsuite/cli/help.term.svg: -------------------------------------------------------------------------------- 1 | 2 | 15 | 16 | 17 | 18 | 19 | Creates a book from markdown files 20 | 21 | 22 | 23 | Usage: mdbook[EXE] [COMMAND] 24 | 25 | 26 | 27 | Commands: 28 | 29 | init Creates the boilerplate structure and files for a new book 30 | 31 | build Builds a book from its markdown files 32 | 33 | test Tests that a book's Rust code samples compile 34 | 35 | clean Deletes a built book 36 | 37 | completions Generate shell completions for your shell to stdout 38 | 39 | watch Watches a book's files and rebuilds it on changes 40 | 41 | serve Serves a book at http://localhost:3000, and rebuilds it on changes 42 | 43 | help Print this message or the help of the given subcommand(s) 44 | 45 | 46 | 47 | Options: 48 | 49 | -h, --help Print help 50 | 51 | -V, --version Print version 52 | 53 | 54 | 55 | For more information about a specific command, try `mdbook <command> --help` 56 | 57 | The source code for mdBook is available at: https://github.com/rust-lang/mdBook 58 | 59 | 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /tests/testsuite/cli/no_args.term.svg: -------------------------------------------------------------------------------- 1 | 2 | 15 | 16 | 17 | 18 | 19 | Creates a book from markdown files 20 | 21 | 22 | 23 | Usage: mdbook[EXE] [COMMAND] 24 | 25 | 26 | 27 | Commands: 28 | 29 | init Creates the boilerplate structure and files for a new book 30 | 31 | build Builds a book from its markdown files 32 | 33 | test Tests that a book's Rust code samples compile 34 | 35 | clean Deletes a built book 36 | 37 | completions Generate shell completions for your shell to stdout 38 | 39 | watch Watches a book's files and rebuilds it on changes 40 | 41 | serve Serves a book at http://localhost:3000, and rebuilds it on changes 42 | 43 | help Print this message or the help of the given subcommand(s) 44 | 45 | 46 | 47 | Options: 48 | 49 | -h, --help Print help 50 | 51 | -V, --version Print version 52 | 53 | 54 | 55 | For more information about a specific command, try `mdbook <command> --help` 56 | 57 | The source code for mdBook is available at: https://github.com/rust-lang/mdBook 58 | 59 | 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /tests/testsuite/includes/all_includes/book.toml: -------------------------------------------------------------------------------- 1 | [book] 2 | authors = ["Eric Huss"] 3 | language = "en" 4 | multilingual = false 5 | src = "src" 6 | title = "all_includes" 7 | -------------------------------------------------------------------------------- /tests/testsuite/includes/all_includes/src/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | - [Basic Includes](./includes.md) 4 | - [Relative Includes](./relative/includes.md) 5 | - [Recursive Includes](./recursive.md) 6 | - [Include Anchors](./anchors.md) 7 | - [Rustdoc Includes](./rustdoc.md) 8 | - [Playground Includes](./playground.md) 9 | -------------------------------------------------------------------------------- /tests/testsuite/includes/all_includes/src/anchors.md: -------------------------------------------------------------------------------- 1 | # Include Anchors 2 | 3 | ```rust 4 | {{#include nested-test-with-anchors.rs:myanchor}} 5 | ``` 6 | -------------------------------------------------------------------------------- /tests/testsuite/includes/all_includes/src/example.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | println!("Hello World!"); 3 | # 4 | # // You can even hide lines! :D 5 | # println!("I am hidden! Expand the code snippet to see me"); 6 | } 7 | -------------------------------------------------------------------------------- /tests/testsuite/includes/all_includes/src/includes.md: -------------------------------------------------------------------------------- 1 | # Basic Includes 2 | 3 | {{#include sample.md}} 4 | 5 | -------------------------------------------------------------------------------- /tests/testsuite/includes/all_includes/src/nested-test-with-anchors.rs: -------------------------------------------------------------------------------- 1 | // This is a test of includes with anchors. 2 | 3 | // ANCHOR: myanchor 4 | // ANCHOR: unendinganchor 5 | let x = 1; 6 | // ANCHOR_END: myanchor 7 | -------------------------------------------------------------------------------- /tests/testsuite/includes/all_includes/src/partially-included-test-with-anchors.rs: -------------------------------------------------------------------------------- 1 | fn some_other_function() { 2 | // ANCHOR: unused-anchor-that-should-be-stripped 3 | println!("unused anchor"); 4 | // ANCHOR_END: unused-anchor-that-should-be-stripped 5 | } 6 | 7 | // ANCHOR: rustdoc-include-anchor 8 | fn main() { 9 | some_other_function(); 10 | } 11 | // ANCHOR_END: rustdoc-include-anchor 12 | -------------------------------------------------------------------------------- /tests/testsuite/includes/all_includes/src/partially-included-test.rs: -------------------------------------------------------------------------------- 1 | fn some_function() { 2 | println!("some function"); 3 | } 4 | 5 | fn main() { 6 | some_function(); 7 | } 8 | -------------------------------------------------------------------------------- /tests/testsuite/includes/all_includes/src/playground.md: -------------------------------------------------------------------------------- 1 | # Playground Includes 2 | 3 | {{#playground example.rs}} 4 | -------------------------------------------------------------------------------- /tests/testsuite/includes/all_includes/src/recursive.md: -------------------------------------------------------------------------------- 1 | Around the world, around the world 2 | {{#include recursive.md}} 3 | -------------------------------------------------------------------------------- /tests/testsuite/includes/all_includes/src/relative/includes.md: -------------------------------------------------------------------------------- 1 | # Relative Includes 2 | 3 | {{#include ../sample.md}} 4 | -------------------------------------------------------------------------------- /tests/testsuite/includes/all_includes/src/rustdoc.md: -------------------------------------------------------------------------------- 1 | # Rustdoc Includes 2 | 3 | ## Rustdoc include adds the rest of the file as hidden 4 | 5 | ```rust 6 | {{#rustdoc_include partially-included-test.rs:5:7}} 7 | ``` 8 | 9 | ## Rustdoc include works with anchors too 10 | 11 | ```rust 12 | {{#rustdoc_include partially-included-test-with-anchors.rs:rustdoc-include-anchor}} 13 | ``` 14 | -------------------------------------------------------------------------------- /tests/testsuite/includes/all_includes/src/sample.md: -------------------------------------------------------------------------------- 1 | ## Sample 2 | 3 | This is a sample include. 4 | -------------------------------------------------------------------------------- /tests/testsuite/index.rs: -------------------------------------------------------------------------------- 1 | //! Tests for the index preprocessor. 2 | 3 | use crate::prelude::*; 4 | 5 | // Checks basic README to index.html conversion. 6 | #[test] 7 | fn readme_to_index() { 8 | let mut test = BookTest::from_dir("index/basic_readme"); 9 | test.check_main_file( 10 | "book/index.html", 11 | str![[r##"

Intro

"##]], 12 | ) 13 | .check_main_file( 14 | "book/first/index.html", 15 | str![[r##"

First

"##]], 16 | ) 17 | .check_main_file( 18 | "book/second/index.html", 19 | str![[r##"

Second

"##]], 20 | ) 21 | .check_toc_js(str![[r#" 22 |
    23 |
  1. 24 | Intro 25 |
  2. 26 |
  3. 27 | 28 | First 29 |
  4. 30 |
  5. 31 | 32 | Second 33 |
  6. 34 |
35 | "#]]); 36 | assert!(test.dir.join("book/index.html").exists()); 37 | assert!(!test.dir.join("book/README.html").exists()); 38 | } 39 | -------------------------------------------------------------------------------- /tests/testsuite/index/basic_readme/book.toml: -------------------------------------------------------------------------------- 1 | [book] 2 | title = "basic_readme" 3 | -------------------------------------------------------------------------------- /tests/testsuite/index/basic_readme/src/README.md: -------------------------------------------------------------------------------- 1 | # Intro 2 | -------------------------------------------------------------------------------- /tests/testsuite/index/basic_readme/src/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | [Intro](./README.md) 4 | 5 | - [First](first/README) 6 | - [Second](second/Readme.md) 7 | -------------------------------------------------------------------------------- /tests/testsuite/index/basic_readme/src/first/README: -------------------------------------------------------------------------------- 1 | # First 2 | -------------------------------------------------------------------------------- /tests/testsuite/index/basic_readme/src/second/Readme.md: -------------------------------------------------------------------------------- 1 | # Second 2 | -------------------------------------------------------------------------------- /tests/testsuite/init/init_from_summary/src/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | [intro](intro.md) 4 | 5 | - [First chapter](first.md) 6 | 7 | [outro](outro.md) 8 | 9 | -------------------------------------------------------------------------------- /tests/testsuite/main.rs: -------------------------------------------------------------------------------- 1 | //! Main testsuite for exercising all functionality of mdBook. 2 | //! 3 | //! See README.md for documentation. 4 | 5 | mod book_test; 6 | mod build; 7 | mod cli; 8 | mod includes; 9 | mod index; 10 | mod init; 11 | mod markdown; 12 | mod playground; 13 | mod preprocessor; 14 | mod print; 15 | mod redirects; 16 | mod renderer; 17 | mod rendering; 18 | #[cfg(feature = "search")] 19 | mod search; 20 | mod test; 21 | mod theme; 22 | mod toc; 23 | 24 | mod prelude { 25 | pub use crate::book_test::BookTest; 26 | pub use snapbox::str; 27 | } 28 | -------------------------------------------------------------------------------- /tests/testsuite/markdown/custom_header_attributes/book.toml: -------------------------------------------------------------------------------- 1 | [book] 2 | title = "custom_header_attributes" 3 | -------------------------------------------------------------------------------- /tests/testsuite/markdown/custom_header_attributes/src/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | - [Heading Attributes](./custom_header_attributes.md) 4 | -------------------------------------------------------------------------------- /tests/testsuite/markdown/custom_header_attributes/src/custom_header_attributes.md: -------------------------------------------------------------------------------- 1 | # Heading Attributes {#attrs} 2 | 3 | ## Heading with classes {.class1 .class2} 4 | 5 | ## Heading with id and classes {#both .class1 .class2} 6 | -------------------------------------------------------------------------------- /tests/testsuite/markdown/footnotes/expected/footnotes.html: -------------------------------------------------------------------------------- 1 |

Footnote tests

2 |

Footnote example1, or with a word2.

3 |

There are multiple references to word2.

4 |

Footnote without a paragraph3

5 |

Footnote with multiple paragraphs4

6 |

Footnote name with wacky characters5

7 |

Testing when referring to something earlier.6

8 |

Footnote that is defined multiple times.7

9 |

And another8 that references the duplicate again.7

10 |
11 |
  1. 12 |

    This is a footnote. ↩2

    13 |
  2. 14 |
  3. 15 |

    A longer footnote. 16 | With multiple lines. Link to other. 17 | With a reference inside.1 ↩2

    18 |
  4. 19 |
  5. 20 |
      21 |
    1. Item one 22 |
        23 |
      1. Sub-item
      2. 24 |
      25 |
    2. 26 |
    3. Item two
    4. 27 |
    28 |
  6. 29 |
  7. 30 |

    One

    31 |

    Two

    32 |

    Three

    33 |
  8. 34 |
  9. 35 |

    Testing footnote id with special characters.

    36 |
  10. 37 |
  11. 38 |

    This is defined before it is referred to.

    39 |
  12. 40 |
  13. 41 |

    This is the first definition of the footnote with tag multiple-definitions ↩2

    42 |
  14. 43 |
  15. 44 |

    Footnote between duplicates.

    45 |
  16. 46 |
-------------------------------------------------------------------------------- /tests/testsuite/markdown/footnotes/src/SUMMARY.md: -------------------------------------------------------------------------------- 1 | - [Footnotes](footnotes.md) 2 | -------------------------------------------------------------------------------- /tests/testsuite/markdown/footnotes/src/footnotes.md: -------------------------------------------------------------------------------- 1 | # Footnote tests 2 | 3 | Footnote example[^1], or with a word[^word]. 4 | 5 | [^1]: This is a footnote. 6 | 7 | [^word]: A longer footnote. 8 | With multiple lines. [Link to other](other.md). 9 | With a reference inside.[^1] 10 | 11 | There are multiple references to word[^word]. 12 | 13 | Footnote without a paragraph[^para] 14 | 15 | [^para]: 16 | 1. Item one 17 | 1. Sub-item 18 | 2. Item two 19 | 20 | Footnote with multiple paragraphs[^multiple] 21 | 22 | [^define-before-use]: This is defined before it is referred to. 23 | 24 | [^multiple]: 25 | One 26 | 27 | Two 28 | 29 | Three 30 | 31 | [^unused]: This footnote is defined by not used. 32 | 33 | Footnote name with wacky characters[^"wacky"] 34 | 35 | [^"wacky"]: Testing footnote id with special characters. 36 | 37 | Testing when referring to something earlier.[^define-before-use] 38 | 39 | Footnote that is defined multiple times.[^multiple-definitions] 40 | 41 | [^multiple-definitions]: This is the first definition of the footnote with tag multiple-definitions 42 | 43 | And another[^in-between] that references the duplicate again.[^multiple-definitions] 44 | 45 | [^in-between]: Footnote between duplicates. 46 | 47 | [^multiple-definitions]: This is the second definition of the footnote with tag multiple-definitions 48 | -------------------------------------------------------------------------------- /tests/testsuite/markdown/smart_punctuation/src/SUMMARY.md: -------------------------------------------------------------------------------- 1 | - [Smart Punctuation](smart_punctuation.md) 2 | -------------------------------------------------------------------------------- /tests/testsuite/markdown/smart_punctuation/src/smart_punctuation.md: -------------------------------------------------------------------------------- 1 | # Smart Punctuation 2 | 3 | - En dash: -- 4 | - Em dash: --- 5 | - Ellipsis: ... 6 | - Double quote: "quote" 7 | - Single quote: 'quote' 8 | -------------------------------------------------------------------------------- /tests/testsuite/markdown/strikethrough/src/SUMMARY.md: -------------------------------------------------------------------------------- 1 | - [Strikethrough](strikethrough.md) 2 | -------------------------------------------------------------------------------- /tests/testsuite/markdown/strikethrough/src/strikethrough.md: -------------------------------------------------------------------------------- 1 | # Strikethrough 2 | 3 | ~~strikethrough example~~ 4 | 5 | -------------------------------------------------------------------------------- /tests/testsuite/markdown/tables/src/SUMMARY.md: -------------------------------------------------------------------------------- 1 | - [Tables](tables.md) 2 | -------------------------------------------------------------------------------- /tests/testsuite/markdown/tables/src/tables.md: -------------------------------------------------------------------------------- 1 | # Tables 2 | 3 | | foo | bar | 4 | | --- | --- | 5 | | baz | bim | 6 | | Backslash in code | `\` | 7 | | Double back in code | `\\` | 8 | | Pipe in code | `\|` | 9 | | Pipe in code2 | `test \| inside` | 10 | -------------------------------------------------------------------------------- /tests/testsuite/markdown/tasklists/src/SUMMARY.md: -------------------------------------------------------------------------------- 1 | - [Tasklists](tasklists.md) 2 | -------------------------------------------------------------------------------- /tests/testsuite/markdown/tasklists/src/tasklists.md: -------------------------------------------------------------------------------- 1 | ## Tasklisks 2 | 3 | - [X] Apples 4 | - [X] Broccoli 5 | - [ ] Carrots 6 | -------------------------------------------------------------------------------- /tests/testsuite/playground.rs: -------------------------------------------------------------------------------- 1 | //! Tests for Rust playground support. 2 | 3 | use crate::prelude::*; 4 | 5 | // Verifies that a rust codeblock gets the playground class. 6 | #[test] 7 | fn playground_on_rust_code() { 8 | BookTest::from_dir("playground/playground_on_rust_code").check_main_file( 9 | "book/index.html", 10 | str![[r##" 11 |

Rust Sample

12 |
#![allow(unused)]
13 | fn main() {
14 | let x = 1;
15 | }
16 | "##]], 17 | ); 18 | } 19 | 20 | // When the playground is disabled, there should be no playground class. 21 | #[test] 22 | fn disabled_playground() { 23 | BookTest::from_dir("playground/disabled_playground").check_main_file( 24 | "book/index.html", 25 | str![[r##" 26 |

Rust Sample

27 |
let x = 1;
28 | "##]], 29 | ); 30 | } 31 | -------------------------------------------------------------------------------- /tests/testsuite/playground/disabled_playground/book.toml: -------------------------------------------------------------------------------- 1 | [book] 2 | title = "playground_on_rust_code" 3 | 4 | [output.html.playground] 5 | runnable = false 6 | -------------------------------------------------------------------------------- /tests/testsuite/playground/disabled_playground/src/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | - [Rust Playground](./index.md) 4 | -------------------------------------------------------------------------------- /tests/testsuite/playground/disabled_playground/src/index.md: -------------------------------------------------------------------------------- 1 | # Rust Sample 2 | 3 | ```rust 4 | let x = 1; 5 | ``` 6 | -------------------------------------------------------------------------------- /tests/testsuite/playground/playground_on_rust_code/book.toml: -------------------------------------------------------------------------------- 1 | [book] 2 | title = "playground_on_rust_code" 3 | -------------------------------------------------------------------------------- /tests/testsuite/playground/playground_on_rust_code/src/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | - [Rust Playground](./index.md) 4 | -------------------------------------------------------------------------------- /tests/testsuite/playground/playground_on_rust_code/src/index.md: -------------------------------------------------------------------------------- 1 | # Rust Sample 2 | 3 | ```rust 4 | let x = 1; 5 | ``` 6 | -------------------------------------------------------------------------------- /tests/testsuite/preprocessor.rs: -------------------------------------------------------------------------------- 1 | //! Tests for custom preprocessors. 2 | 3 | use crate::prelude::*; 4 | use mdbook::book::Book; 5 | use mdbook::errors::Result; 6 | use mdbook::preprocess::{CmdPreprocessor, Preprocessor, PreprocessorContext}; 7 | use std::sync::{Arc, Mutex}; 8 | 9 | struct Spy(Arc>); 10 | 11 | #[derive(Debug, Default)] 12 | struct Inner { 13 | run_count: usize, 14 | rendered_with: Vec, 15 | } 16 | 17 | impl Preprocessor for Spy { 18 | fn name(&self) -> &str { 19 | "dummy" 20 | } 21 | 22 | fn run(&self, ctx: &PreprocessorContext, book: Book) -> Result { 23 | let mut inner = self.0.lock().unwrap(); 24 | inner.run_count += 1; 25 | inner.rendered_with.push(ctx.renderer.clone()); 26 | Ok(book) 27 | } 28 | } 29 | 30 | // Test that preprocessor gets run. 31 | #[test] 32 | fn runs_preprocessors() { 33 | let test = BookTest::init(|_| {}); 34 | let spy: Arc> = Default::default(); 35 | let mut book = test.load_book(); 36 | book.with_preprocessor(Spy(Arc::clone(&spy))); 37 | book.build().unwrap(); 38 | 39 | let inner = spy.lock().unwrap(); 40 | assert_eq!(inner.run_count, 1); 41 | assert_eq!(inner.rendered_with, ["html"]); 42 | } 43 | 44 | // No-op preprocessor works. 45 | #[test] 46 | fn nop_preprocessor() { 47 | BookTest::from_dir("preprocessor/nop_preprocessor").run("build", |cmd| { 48 | cmd.expect_stdout(str![[""]]).expect_stderr(str![[r#" 49 | [TIMESTAMP] [INFO] (mdbook::book): Book building has started 50 | [TIMESTAMP] [INFO] (mdbook::book): Running the html backend 51 | 52 | "#]]); 53 | }); 54 | } 55 | 56 | // Failing preprocessor generates an error. 57 | #[test] 58 | fn failing_preprocessor() { 59 | BookTest::from_dir("preprocessor/failing_preprocessor") 60 | .run("build", |cmd| { 61 | cmd.expect_failure() 62 | .expect_stdout(str![[""]]) 63 | .expect_stderr(str![[r#" 64 | [TIMESTAMP] [INFO] (mdbook::book): Book building has started 65 | Boom!!1! 66 | [TIMESTAMP] [ERROR] (mdbook::utils): Error: The "nop-preprocessor" preprocessor exited unsuccessfully with [EXIT_STATUS]: 1 status 67 | 68 | "#]]); 69 | }); 70 | } 71 | 72 | fn example() -> CmdPreprocessor { 73 | CmdPreprocessor::new( 74 | "nop-preprocessor".to_string(), 75 | "cargo run --quiet --example nop-preprocessor --".to_string(), 76 | ) 77 | } 78 | 79 | #[test] 80 | fn example_supports_whatever() { 81 | let cmd = example(); 82 | 83 | let got = cmd.supports_renderer("whatever"); 84 | 85 | assert_eq!(got, true); 86 | } 87 | 88 | #[test] 89 | fn example_doesnt_support_not_supported() { 90 | let cmd = example(); 91 | 92 | let got = cmd.supports_renderer("not-supported"); 93 | 94 | assert_eq!(got, false); 95 | } 96 | -------------------------------------------------------------------------------- /tests/testsuite/preprocessor/failing_preprocessor/book.toml: -------------------------------------------------------------------------------- 1 | [preprocessor.nop-preprocessor] 2 | command = "cargo run --quiet --example nop-preprocessor --" 3 | blow-up = true 4 | 5 | -------------------------------------------------------------------------------- /tests/testsuite/preprocessor/failing_preprocessor/src/SUMMARY.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rust-lang/mdBook/1b046e5a907d70e4204461c30daa7b3ca112d519/tests/testsuite/preprocessor/failing_preprocessor/src/SUMMARY.md -------------------------------------------------------------------------------- /tests/testsuite/preprocessor/nop_preprocessor/book.toml: -------------------------------------------------------------------------------- 1 | [preprocessor.nop-preprocessor] 2 | command = "cargo run --quiet --example nop-preprocessor --" 3 | -------------------------------------------------------------------------------- /tests/testsuite/preprocessor/nop_preprocessor/src/SUMMARY.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rust-lang/mdBook/1b046e5a907d70e4204461c30daa7b3ca112d519/tests/testsuite/preprocessor/nop_preprocessor/src/SUMMARY.md -------------------------------------------------------------------------------- /tests/testsuite/print.rs: -------------------------------------------------------------------------------- 1 | //! Tests for print page. 2 | 3 | use crate::prelude::*; 4 | 5 | // Tests relative links from the print page. 6 | #[test] 7 | fn relative_links() { 8 | BookTest::from_dir("print/relative_links") 9 | .check_main_file("book/print.html", 10 | str![[r##" 11 |

First Chapter

12 |

First Nested

13 |

Testing relative links for the print page

14 |

When we link to the first section, it should work on 15 | both the print page and the non-print page.

16 |

A fragment link should work.

17 |

Link outside.

18 |

Some image

19 |

HTML Link

20 | raw html 21 |

Some section

22 | "##]]); 23 | } 24 | 25 | // Checks that print.html is noindex. 26 | #[test] 27 | fn noindex() { 28 | let robots = r#""#; 29 | BookTest::from_dir("print/noindex") 30 | .check_file_contains("book/print.html", robots) 31 | .check_file_doesnt_contain("book/index.html", robots); 32 | } 33 | -------------------------------------------------------------------------------- /tests/testsuite/print/noindex/src/SUMMARY.md: -------------------------------------------------------------------------------- 1 | - [Intro](index.md) 2 | -------------------------------------------------------------------------------- /tests/testsuite/print/relative_links/book.toml: -------------------------------------------------------------------------------- 1 | [book] 2 | title = "relative_links" 3 | -------------------------------------------------------------------------------- /tests/testsuite/print/relative_links/src/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | - [First Chapter](first/index.md) 4 | - [First Nested](first/nested.md) 5 | - [Second Chapter](second/nested.md) 6 | -------------------------------------------------------------------------------- /tests/testsuite/print/relative_links/src/first/index.md: -------------------------------------------------------------------------------- 1 | # First Chapter 2 | -------------------------------------------------------------------------------- /tests/testsuite/print/relative_links/src/first/nested.md: -------------------------------------------------------------------------------- 1 | # First Nested 2 | -------------------------------------------------------------------------------- /tests/testsuite/print/relative_links/src/second/nested.md: -------------------------------------------------------------------------------- 1 | # Testing relative links for the print page 2 | 3 | When we link to [the first section](../first/nested.md), it should work on 4 | both the print page and the non-print page. 5 | 6 | A [fragment link](#some-section) should work. 7 | 8 | Link [outside](../../std/foo/bar.html). 9 | 10 | ![Some image](../images/picture.png) 11 | 12 | HTML Link 13 | 14 | raw html 15 | 16 | ## Some section 17 | -------------------------------------------------------------------------------- /tests/testsuite/redirects.rs: -------------------------------------------------------------------------------- 1 | //! Tests for the HTML redirect feature. 2 | 3 | use crate::prelude::*; 4 | use snapbox::file; 5 | 6 | // Basic check of redirects. 7 | #[test] 8 | fn redirects_are_emitted_correctly() { 9 | BookTest::from_dir("redirects/redirects_are_emitted_correctly") 10 | .check_file( 11 | "book/overview.html", 12 | file!["redirects/redirects_are_emitted_correctly/expected/overview.html"], 13 | ) 14 | .check_file( 15 | "book/nested/page.html", 16 | file!["redirects/redirects_are_emitted_correctly/expected/nested/page.html"], 17 | ); 18 | } 19 | -------------------------------------------------------------------------------- /tests/testsuite/redirects/redirects_are_emitted_correctly/book.toml: -------------------------------------------------------------------------------- 1 | [book] 2 | title = "redirects_are_emitted_correctly" 3 | 4 | [output.html.redirect] 5 | "/overview.html" = "index.html" 6 | "/nested/page.html" = "https://rust-lang.org/" 7 | -------------------------------------------------------------------------------- /tests/testsuite/redirects/redirects_are_emitted_correctly/expected/nested/page.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Redirecting... 6 | 7 | 8 | 9 | 10 |

Redirecting to... https://rust-lang.org/.

11 | 12 | 13 | -------------------------------------------------------------------------------- /tests/testsuite/redirects/redirects_are_emitted_correctly/expected/overview.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Redirecting... 6 | 7 | 8 | 9 | 10 |

Redirecting to... index.html.

11 | 12 | 13 | -------------------------------------------------------------------------------- /tests/testsuite/redirects/redirects_are_emitted_correctly/src/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Redirects 2 | 3 | - [Chapter 1](chapter_1.md) 4 | -------------------------------------------------------------------------------- /tests/testsuite/redirects/redirects_are_emitted_correctly/src/chapter_1.md: -------------------------------------------------------------------------------- 1 | # Chapter 1 2 | -------------------------------------------------------------------------------- /tests/testsuite/renderer/backends_receive_render_context_via_stdin/book.toml: -------------------------------------------------------------------------------- 1 | [output.cat-to-file] 2 | command = "./cat-to-file" 3 | 4 | -------------------------------------------------------------------------------- /tests/testsuite/renderer/backends_receive_render_context_via_stdin/src/SUMMARY.md: -------------------------------------------------------------------------------- 1 | - [Chapter 1](./chapter_1.md) 2 | -------------------------------------------------------------------------------- /tests/testsuite/renderer/backends_receive_render_context_via_stdin/src/chapter_1.md: -------------------------------------------------------------------------------- 1 | # Chapter 1 2 | -------------------------------------------------------------------------------- /tests/testsuite/renderer/missing_optional_not_fatal/book.toml: -------------------------------------------------------------------------------- 1 | [output.missing] 2 | command = "trduyvbhijnorgevfuhn" 3 | optional = true 4 | 5 | -------------------------------------------------------------------------------- /tests/testsuite/renderer/missing_optional_not_fatal/src/SUMMARY.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rust-lang/mdBook/1b046e5a907d70e4204461c30daa7b3ca112d519/tests/testsuite/renderer/missing_optional_not_fatal/src/SUMMARY.md -------------------------------------------------------------------------------- /tests/testsuite/renderer/missing_renderer/book.toml: -------------------------------------------------------------------------------- 1 | [output.missing] 2 | command = "trduyvbhijnorgevfuhn" 3 | 4 | -------------------------------------------------------------------------------- /tests/testsuite/renderer/missing_renderer/src/SUMMARY.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rust-lang/mdBook/1b046e5a907d70e4204461c30daa7b3ca112d519/tests/testsuite/renderer/missing_renderer/src/SUMMARY.md -------------------------------------------------------------------------------- /tests/testsuite/renderer/renderer_with_arguments/book.toml: -------------------------------------------------------------------------------- 1 | [output.arguments] 2 | command = "./arguments arg1 arg2" 3 | 4 | -------------------------------------------------------------------------------- /tests/testsuite/renderer/renderer_with_arguments/src/SUMMARY.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rust-lang/mdBook/1b046e5a907d70e4204461c30daa7b3ca112d519/tests/testsuite/renderer/renderer_with_arguments/src/SUMMARY.md -------------------------------------------------------------------------------- /tests/testsuite/rendering.rs: -------------------------------------------------------------------------------- 1 | //! Tests for HTML rendering. 2 | 3 | use crate::prelude::*; 4 | 5 | // Checks that edit-url-template works. 6 | #[test] 7 | fn edit_url_template() { 8 | BookTest::from_dir("rendering/edit_url_template").check_file_contains( 9 | "book/index.html", 10 | "", 12 | ); 13 | } 14 | 15 | // Checks that an alternate `src` setting works with the edit url template. 16 | #[test] 17 | fn edit_url_template_explicit_src() { 18 | BookTest::from_dir("rendering/edit_url_template_explicit_src").check_file_contains( 19 | "book/index.html", 20 | "", 22 | ); 23 | } 24 | 25 | // Checks that index.html is generated correctly, even when the first few 26 | // chapters are drafts. 27 | #[test] 28 | fn first_chapter_is_copied_as_index_even_if_not_first_elem() { 29 | BookTest::from_dir("rendering/first_chapter_is_copied_as_index_even_if_not_first_elem") 30 | // These two files should be equal. 31 | .check_main_file( 32 | "book/chapter_1.html", 33 | str![[ 34 | r##"

Chapter 1

"## 35 | ]], 36 | ) 37 | .check_main_file( 38 | "book/index.html", 39 | str![[ 40 | r##"

Chapter 1

"## 41 | ]], 42 | ); 43 | } 44 | -------------------------------------------------------------------------------- /tests/testsuite/rendering/edit_url_template/book.toml: -------------------------------------------------------------------------------- 1 | [book] 2 | title = "edit_url_template" 3 | 4 | [output.html] 5 | edit-url-template = "https://github.com/rust-lang/mdBook/edit/master/guide/{path}" 6 | -------------------------------------------------------------------------------- /tests/testsuite/rendering/edit_url_template/src/SUMMARY.md: -------------------------------------------------------------------------------- 1 | - [Intro](README.md) 2 | -------------------------------------------------------------------------------- /tests/testsuite/rendering/edit_url_template_explicit_src/book.toml: -------------------------------------------------------------------------------- 1 | [book] 2 | title = "edit_url_template" 3 | src = "src2" 4 | 5 | [output.html] 6 | edit-url-template = "https://github.com/rust-lang/mdBook/edit/master/guide/{path}" 7 | -------------------------------------------------------------------------------- /tests/testsuite/rendering/edit_url_template_explicit_src/src2/SUMMARY.md: -------------------------------------------------------------------------------- 1 | - [Intro](README.md) 2 | -------------------------------------------------------------------------------- /tests/testsuite/rendering/first_chapter_is_copied_as_index_even_if_not_first_elem/src/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | --- 4 | 5 | - [None of these should be treated as the "index chapter"]() 6 | 7 | # Part 1 8 | 9 | - [Not this either]() 10 | - [Chapter 1](./chapter_1.md) 11 | - [And not this]() 12 | -------------------------------------------------------------------------------- /tests/testsuite/search/chapter_settings_validation_error/book.toml: -------------------------------------------------------------------------------- 1 | [book] 2 | title = "Search Test" 3 | 4 | [output.html.search.chapter] 5 | "does-not-exist" = { enable = false } 6 | -------------------------------------------------------------------------------- /tests/testsuite/search/chapter_settings_validation_error/src/SUMMARY.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rust-lang/mdBook/1b046e5a907d70e4204461c30daa7b3ca112d519/tests/testsuite/search/chapter_settings_validation_error/src/SUMMARY.md -------------------------------------------------------------------------------- /tests/testsuite/search/disable_search_chapter/book.toml: -------------------------------------------------------------------------------- 1 | [book] 2 | title = "disable_search_chapter" 3 | 4 | [output.html.search.chapter] 5 | "second" = { enable = false } 6 | "first/disable_me.md" = { enable = false } 7 | -------------------------------------------------------------------------------- /tests/testsuite/search/disable_search_chapter/src/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | - [Keep Me](first/keep_me.md) 4 | - [Disable Me](first/disable_me.md) 5 | - [Second](second.md) 6 | - [Second Nested](second/nested.md) 7 | -------------------------------------------------------------------------------- /tests/testsuite/search/disable_search_chapter/src/first/disable_me.md: -------------------------------------------------------------------------------- 1 | # Disable Me 2 | -------------------------------------------------------------------------------- /tests/testsuite/search/disable_search_chapter/src/first/keep_me.md: -------------------------------------------------------------------------------- 1 | # Keep Me 2 | -------------------------------------------------------------------------------- /tests/testsuite/search/disable_search_chapter/src/second.md: -------------------------------------------------------------------------------- 1 | # Second 2 | -------------------------------------------------------------------------------- /tests/testsuite/search/disable_search_chapter/src/second/nested.md: -------------------------------------------------------------------------------- 1 | # Second Nested 2 | -------------------------------------------------------------------------------- /tests/testsuite/search/reasonable_search_index/src/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | [Introduction](intro.md) 4 | 5 | - [First Chapter](first/index.md) 6 | - [Includes](first/includes.md) 7 | - [Unicode](first/unicode.md) 8 | - [No Headers](first/no-headers.md) 9 | - [Duplicate Headers](first/duplicate-headers.md) 10 | - [Heading Attributes](first/heading-attributes.md) 11 | -------------------------------------------------------------------------------- /tests/testsuite/search/reasonable_search_index/src/first/duplicate-headers.md: -------------------------------------------------------------------------------- 1 | # Duplicate headers 2 | 3 | This page validates behaviour of duplicate headers. 4 | 5 | # Header Text 6 | 7 | # Header Text 8 | 9 | # header-text 10 | -------------------------------------------------------------------------------- /tests/testsuite/search/reasonable_search_index/src/first/heading-attributes.md: -------------------------------------------------------------------------------- 1 | # Heading Attributes {#attrs} 2 | 3 | ## Heading with classes {.class1 .class2} 4 | 5 | ## Heading with id and classes {#both .class1 .class2} 6 | -------------------------------------------------------------------------------- /tests/testsuite/search/reasonable_search_index/src/first/includes.md: -------------------------------------------------------------------------------- 1 | # Includes 2 | 3 | {{#include ../SUMMARY.md::}} 4 | -------------------------------------------------------------------------------- /tests/testsuite/search/reasonable_search_index/src/first/index.md: -------------------------------------------------------------------------------- 1 | # First Chapter 2 | 3 | more text. 4 | 5 | ## Some Section 6 | -------------------------------------------------------------------------------- /tests/testsuite/search/reasonable_search_index/src/first/no-headers.md: -------------------------------------------------------------------------------- 1 | Capybara capybara capybara. 2 | 3 | Capybara capybara capybara. 4 | 5 | ThisLongWordIsIncludedSoWeCanCheckThatSufficientlyLongWordsAreOmittedFromTheSearchIndex. 6 | -------------------------------------------------------------------------------- /tests/testsuite/search/reasonable_search_index/src/first/unicode.md: -------------------------------------------------------------------------------- 1 | # Unicode stress tests 2 | 3 | Please be careful editing, this contains carefully crafted characters. 4 | 5 | Two byte character: spatiëring 6 | 7 | Combining character: spatiëring 8 | 9 | Three byte character: 书こんにちは 10 | 11 | Four byte character: 𐌀‮𐌁‮𐌂‮𐌃‮𐌄‮𐌅‮𐌆‮𐌇‮𐌈‬ 12 | 13 | Right-to-left: مرحبا 14 | 15 | Emoticons: 🔊 😍 💜 1️⃣ 16 | 17 | right-to-left mark: hello באמת!‏ 18 | 19 | 20 | Zalgo: ǫ̛̖̱̗̝͈̋͒͋̏ͥͫ̒̆ͩ̏͌̾͊͐ͪ̾̚ 21 | 22 | -------------------------------------------------------------------------------- /tests/testsuite/search/reasonable_search_index/src/intro.md: -------------------------------------------------------------------------------- 1 | # Introduction 2 | 3 | Here's some interesting text... 4 | 5 | ## Sneaky 6 | 7 |

8 | 9 | I put <HTML> in here!
10 |

11 | 18 | 25 | 26 | Sneaky inline event . 27 | 28 | But regular inline is indexed. 29 | -------------------------------------------------------------------------------- /tests/testsuite/test.rs: -------------------------------------------------------------------------------- 1 | //! Tests for the `mdbook test` command. 2 | 3 | use crate::prelude::*; 4 | 5 | // Simple test for passing tests. 6 | #[test] 7 | fn passing_tests() { 8 | BookTest::from_dir("test/passing_tests").run("test", |cmd| { 9 | cmd.expect_stdout(str![[""]]).expect_stderr(str![[r#" 10 | [TIMESTAMP] [INFO] (mdbook::book): Testing chapter 'Intro': "intro.md" 11 | [TIMESTAMP] [INFO] (mdbook::book): Testing chapter 'Passing 1': "passing1.md" 12 | [TIMESTAMP] [INFO] (mdbook::book): Testing chapter 'Passing 2': "passing2.md" 13 | 14 | "#]]); 15 | }); 16 | } 17 | 18 | // Test for a test failure 19 | #[test] 20 | fn failing_tests() { 21 | BookTest::from_dir("test/failing_tests").run("test", |cmd| { 22 | cmd.expect_code(101) 23 | .expect_stdout(str![[""]]) 24 | // This redacts a large number of lines that come from rustdoc and 25 | // libtest. If the output from those ever changes, then it would not 26 | // make it possible to test against different versions of Rust. This 27 | // still includes a little bit of output, so if that is a problem, 28 | // add more redactions. 29 | .expect_stderr(str![[r#" 30 | [TIMESTAMP] [INFO] (mdbook::book): Testing chapter 'Failing Tests': "failing.md" 31 | [TIMESTAMP] [ERROR] (mdbook::book): rustdoc returned an error: 32 | 33 | --- stdout 34 | 35 | ... 36 | test failing.md - Failing_Tests (line 3) ... FAILED 37 | ... 38 | thread 'main' panicked at failing.md:3:1: 39 | fail 40 | ... 41 | [TIMESTAMP] [INFO] (mdbook::book): Testing chapter 'Failing Include': "failing_include.md" 42 | [TIMESTAMP] [ERROR] (mdbook::book): rustdoc returned an error: 43 | 44 | --- stdout 45 | ... 46 | test failing_include.md - Failing_Include (line 3) ... FAILED 47 | ... 48 | thread 'main' panicked at failing_include.md:3:1: 49 | failing! 50 | ... 51 | [TIMESTAMP] [ERROR] (mdbook::utils): Error: One or more tests failed 52 | 53 | "#]]); 54 | }); 55 | } 56 | 57 | // Test with a specific chapter. 58 | #[test] 59 | fn test_individual_chapter() { 60 | let mut test = BookTest::from_dir("test/passing_tests"); 61 | test.run("test -c", |cmd| { 62 | cmd.args(&["Passing 1"]) 63 | .expect_stdout(str![[""]]) 64 | .expect_stderr(str![[r#" 65 | [TIMESTAMP] [INFO] (mdbook::book): Testing chapter 'Passing 1': "passing1.md" 66 | 67 | "#]]); 68 | }) 69 | // Can also be a source path. 70 | .run("test -c passing2.md", |cmd| { 71 | cmd.expect_stdout(str![[""]]).expect_stderr(str![[r#" 72 | [TIMESTAMP] [INFO] (mdbook::book): Testing chapter 'Passing 2': "passing2.md" 73 | 74 | "#]]); 75 | }); 76 | } 77 | 78 | // Unknown chapter name. 79 | #[test] 80 | fn chapter_not_found() { 81 | BookTest::from_dir("test/passing_tests").run("test -c bogus", |cmd| { 82 | cmd.expect_failure() 83 | .expect_stdout(str![[""]]) 84 | .expect_stderr(str![[r#" 85 | [TIMESTAMP] [ERROR] (mdbook::utils): Error: Chapter not found: bogus 86 | 87 | "#]]); 88 | }); 89 | } 90 | -------------------------------------------------------------------------------- /tests/testsuite/test/failing_tests/book.toml: -------------------------------------------------------------------------------- 1 | [book] 2 | title = "failing_tests" 3 | -------------------------------------------------------------------------------- /tests/testsuite/test/failing_tests/src/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | - [Failing Tests](./failing.md) 4 | - [Failing Include](./failing_include.md) 5 | -------------------------------------------------------------------------------- /tests/testsuite/test/failing_tests/src/failing.md: -------------------------------------------------------------------------------- 1 | # Failing Tests 2 | 3 | ```rust 4 | panic!("fail"); 5 | ``` 6 | -------------------------------------------------------------------------------- /tests/testsuite/test/failing_tests/src/failing_include.md: -------------------------------------------------------------------------------- 1 | # Failing Include 2 | 3 | ```rust 4 | {{#include test1.rs:FAILING}} 5 | ``` 6 | -------------------------------------------------------------------------------- /tests/testsuite/test/failing_tests/src/test1.rs: -------------------------------------------------------------------------------- 1 | fn test2() { 2 | println!("test2"); 3 | } 4 | 5 | // ANCHOR: PASSING 6 | println!("passing!"); 7 | // ANCHOR_END: PASSING 8 | 9 | // ANCHOR: FAILING 10 | panic!("failing!"); 11 | // ANCHOR_END: FAILING 12 | -------------------------------------------------------------------------------- /tests/testsuite/test/passing_tests/book.toml: -------------------------------------------------------------------------------- 1 | [book] 2 | title = "passing_tests" 3 | -------------------------------------------------------------------------------- /tests/testsuite/test/passing_tests/src/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | [Intro](./intro.md) 4 | 5 | - [Passing 1](./passing1.md) 6 | - [Passing 2](./passing2.md) 7 | -------------------------------------------------------------------------------- /tests/testsuite/test/passing_tests/src/passing1.md: -------------------------------------------------------------------------------- 1 | # Passing Tests 1 2 | 3 | ```rust 4 | assert!(true); 5 | ``` 6 | 7 | ```rust 8 | println!("hello!"); 9 | ``` 10 | 11 | ## Also check includes 12 | 13 | ```rust 14 | {{#include test1.rs}} 15 | ``` 16 | 17 | ```rust 18 | {{#include test2.rs:2}} 19 | ``` 20 | 21 | ```rust 22 | {{#include test2.rs:PASSING}} 23 | ``` 24 | 25 | ```rust 26 | {{#rustdoc_include test3.rs:2}} 27 | ``` 28 | 29 | {{#playground test1.rs}} 30 | -------------------------------------------------------------------------------- /tests/testsuite/test/passing_tests/src/passing2.md: -------------------------------------------------------------------------------- 1 | # Passing Tests 2 2 | 3 | ```rust 4 | println!("also passing"); 5 | ``` 6 | -------------------------------------------------------------------------------- /tests/testsuite/test/passing_tests/src/test1.rs: -------------------------------------------------------------------------------- 1 | println!("test1"); 2 | -------------------------------------------------------------------------------- /tests/testsuite/test/passing_tests/src/test2.rs: -------------------------------------------------------------------------------- 1 | fn test2() { 2 | println!("test2"); 3 | } 4 | 5 | // ANCHOR: PASSING 6 | println!("passing!"); 7 | // ANCHOR_END: PASSING 8 | 9 | // ANCHOR: FAILING 10 | panic!("failing!"); 11 | // ANCHOR_END: FAILING 12 | -------------------------------------------------------------------------------- /tests/testsuite/test/passing_tests/src/test3.rs: -------------------------------------------------------------------------------- 1 | println!("test3"); 2 | -------------------------------------------------------------------------------- /tests/testsuite/theme/copy_fonts_false_no_theme/book.toml: -------------------------------------------------------------------------------- 1 | [output.html] 2 | copy-fonts = false 3 | -------------------------------------------------------------------------------- /tests/testsuite/theme/copy_fonts_false_no_theme/src/SUMMARY.md: -------------------------------------------------------------------------------- 1 | - [Intro](index.md) 2 | -------------------------------------------------------------------------------- /tests/testsuite/theme/copy_fonts_false_with_empty_fonts_css/book.toml: -------------------------------------------------------------------------------- 1 | [output.html] 2 | copy-fonts = false 3 | -------------------------------------------------------------------------------- /tests/testsuite/theme/copy_fonts_false_with_empty_fonts_css/src/SUMMARY.md: -------------------------------------------------------------------------------- 1 | - [Intro](index.md) 2 | -------------------------------------------------------------------------------- /tests/testsuite/theme/copy_fonts_false_with_empty_fonts_css/theme/fonts/fonts.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rust-lang/mdBook/1b046e5a907d70e4204461c30daa7b3ca112d519/tests/testsuite/theme/copy_fonts_false_with_empty_fonts_css/theme/fonts/fonts.css -------------------------------------------------------------------------------- /tests/testsuite/theme/copy_fonts_false_with_fonts_css/book.toml: -------------------------------------------------------------------------------- 1 | [output.html] 2 | copy-fonts = false 3 | -------------------------------------------------------------------------------- /tests/testsuite/theme/copy_fonts_false_with_fonts_css/src/SUMMARY.md: -------------------------------------------------------------------------------- 1 | - [Intro](index.md) 2 | -------------------------------------------------------------------------------- /tests/testsuite/theme/copy_fonts_false_with_fonts_css/theme/fonts/fonts.css: -------------------------------------------------------------------------------- 1 | /*custom*/ 2 | -------------------------------------------------------------------------------- /tests/testsuite/theme/copy_fonts_false_with_fonts_css/theme/fonts/myfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rust-lang/mdBook/1b046e5a907d70e4204461c30daa7b3ca112d519/tests/testsuite/theme/copy_fonts_false_with_fonts_css/theme/fonts/myfont.woff -------------------------------------------------------------------------------- /tests/testsuite/theme/empty_theme/book.toml: -------------------------------------------------------------------------------- 1 | [book] 2 | title = "empty_theme" 3 | 4 | [output.html] 5 | theme = "./theme" 6 | -------------------------------------------------------------------------------- /tests/testsuite/theme/empty_theme/src/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | - [Chapter 1](./chapter_1.md) 4 | -------------------------------------------------------------------------------- /tests/testsuite/theme/empty_theme/src/chapter_1.md: -------------------------------------------------------------------------------- 1 | # Chapter 1 2 | -------------------------------------------------------------------------------- /tests/testsuite/theme/fonts_css/src/SUMMARY.md: -------------------------------------------------------------------------------- 1 | - [With Fonts](index.md) 2 | -------------------------------------------------------------------------------- /tests/testsuite/theme/fonts_css/theme/fonts/fonts.css: -------------------------------------------------------------------------------- 1 | /*custom*/ 2 | -------------------------------------------------------------------------------- /tests/testsuite/theme/fonts_css/theme/fonts/myfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rust-lang/mdBook/1b046e5a907d70e4204461c30daa7b3ca112d519/tests/testsuite/theme/fonts_css/theme/fonts/myfont.woff -------------------------------------------------------------------------------- /tests/testsuite/theme/missing_theme/book.toml: -------------------------------------------------------------------------------- 1 | [book] 2 | title = "missing_theme" 3 | 4 | [output.html] 5 | theme = "./non-existent-directory" 6 | -------------------------------------------------------------------------------- /tests/testsuite/theme/missing_theme/src/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | - [Chapter 1](./chapter_1.md) 4 | -------------------------------------------------------------------------------- /tests/testsuite/theme/missing_theme/src/chapter_1.md: -------------------------------------------------------------------------------- 1 | # Chapter 1 2 | -------------------------------------------------------------------------------- /tests/testsuite/theme/override_index/src/SUMMARY.md: -------------------------------------------------------------------------------- 1 | - [Intro](index.md) 2 | -------------------------------------------------------------------------------- /tests/testsuite/theme/override_index/theme/index.hbs: -------------------------------------------------------------------------------- 1 | This is a modified index.hbs! 2 | -------------------------------------------------------------------------------- /tests/testsuite/toc/basic_toc/book.toml: -------------------------------------------------------------------------------- 1 | [book] 2 | title = "basic_toc" 3 | -------------------------------------------------------------------------------- /tests/testsuite/toc/basic_toc/src/README.md: -------------------------------------------------------------------------------- 1 | # With Readme 2 | -------------------------------------------------------------------------------- /tests/testsuite/toc/basic_toc/src/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | [Prefix 1](prefix1.md) 4 | [Prefix 2](prefix2.md) 5 | 6 | - [With Readme](README.md) 7 | - [Nested Index](nested/index.md) 8 | - [Nested two](nested/two.md) 9 | - [Draft]() 10 | 11 | --- 12 | 13 | # Deep Nest 14 | 15 | - [Deep Nest 1](deep/index.md) 16 | - [Deep Nest 2](deep/a/index.md) 17 | - [Deep Nest 3](deep/a/b/index.md) 18 | [Deep Nest 4](deep/a/b/c/index.md) 19 | 20 | --- 21 | 22 | [Suffix 1](suffix1.md) 23 | [Suffix 2](suffix2.md) 24 | -------------------------------------------------------------------------------- /tests/testsuite/toc/basic_toc/src/deep/a/b/index.md: -------------------------------------------------------------------------------- 1 | # Deep Nest 3 2 | -------------------------------------------------------------------------------- /tests/testsuite/toc/basic_toc/src/deep/a/index.md: -------------------------------------------------------------------------------- 1 | # Deep Nest 2 2 | -------------------------------------------------------------------------------- /tests/testsuite/toc/basic_toc/src/deep/index.md: -------------------------------------------------------------------------------- 1 | # Deep Nest 1 2 | -------------------------------------------------------------------------------- /tests/testsuite/toc/basic_toc/src/nested/index.md: -------------------------------------------------------------------------------- 1 | # Nested Index 2 | -------------------------------------------------------------------------------- /tests/testsuite/toc/basic_toc/src/nested/two.md: -------------------------------------------------------------------------------- 1 | # Nested two 2 | -------------------------------------------------------------------------------- /tests/testsuite/toc/basic_toc/src/prefix1.md: -------------------------------------------------------------------------------- 1 | # Prefix 1 2 | -------------------------------------------------------------------------------- /tests/testsuite/toc/basic_toc/src/prefix2.md: -------------------------------------------------------------------------------- 1 | # Prefix 2 2 | -------------------------------------------------------------------------------- /tests/testsuite/toc/basic_toc/src/suffix1.md: -------------------------------------------------------------------------------- 1 | # Suffix 1 2 | -------------------------------------------------------------------------------- /tests/testsuite/toc/basic_toc/src/suffix2.md: -------------------------------------------------------------------------------- 1 | # Suffix 2 2 | -------------------------------------------------------------------------------- /tests/testsuite/toc/summary_with_markdown_formatting/src/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Summary formatting tests 2 | 3 | - [*Italic* `code` \*escape\* \`escape2\`](formatted-summary.md) 4 | - [Soft 5 | line break](soft.md) 6 | - [\](escaped-tag.md) 7 | -------------------------------------------------------------------------------- /triagebot.toml: -------------------------------------------------------------------------------- 1 | # This will allow users to self assign, and/or drop assignment 2 | [assign] 3 | 4 | # Allows @rustbot ready, review, author, or blocked 5 | [shortcut] 6 | 7 | [relabel] 8 | allow-unauthenticated = [ 9 | # For Issue areas 10 | "A-*", 11 | # Categories 12 | "C-*", 13 | # Commands 14 | "Command-*", 15 | # Status 16 | "S-*", 17 | "regression", 18 | "Breaking Change", 19 | "msrv-bump", 20 | ] 21 | 22 | [autolabel."S-waiting-on-review"] 23 | new_pr = true 24 | 25 | [merge-conflicts] 26 | remove = [] 27 | add = ["S-waiting-on-author"] 28 | unless = ["S-blocked", "S-waiting-on-review"] 29 | 30 | [review-submitted] 31 | reviewed_label = "S-waiting-on-author" 32 | review_labels = ["S-waiting-on-review"] 33 | 34 | [review-requested] 35 | remove_labels = ["S-waiting-on-author"] 36 | add_labels = ["S-waiting-on-review"] 37 | --------------------------------------------------------------------------------