[
  {
    "path": ".cargo/config.toml",
    "content": "[alias]\nxtask = \"run --manifest-path=crates/xtask/Cargo.toml --\"\n"
  },
  {
    "path": ".git-blame-ignore-revs",
    "content": "# Use `git config blame.ignorerevsfile .git-blame-ignore-revs` to make `git blame` ignore the following commits.\n\n# rustfmt\nad0794a0bd692e4f2ff23b85e361889620e93f51\n# rustfmt and use_try_shorthand\n75bbd55128083897d40c3f5265cc5b1f10314ddb\n# rustfmt\n382fc4139b96bde3c4b8875b499c720eabc89c6a\n# rustfmt\n154e0fb3080c6ffc225b0d47b5d835e589789892\n# rustfmt\n5835da243244bfc5c95c6c6db96f453da4bb5740\n# rustfmt\nfd9d27e082f5e9eea50e4fa9fa3a22060d02c66b\n# rustfmt\n1d69ccae4854f13552d452d0bffef95cbff70364\n# rustfmt\n3688f73052454bf510a5acc85cf55aae450c6e46\n# rustfmt\n742dbbc91700dce1b7d910bca6b3e10a5ae46b86\n# rustfmt 1.38\nb88839cc25a6fd1c782101e94318959e8079bb20\n# rustfmt 1.40\n2f59943c04f0aa204a9238d6a699ba9cc06c88d9\n# Rustfmt for 2024\nc7b67e363bb9ce3383636ee615e8e761bf185b33\n"
  },
  {
    "path": ".gitattributes",
    "content": "[attr]rust text eol=lf whitespace=tab-in-indent,trailing-space,tabwidth=4\n\n* text=auto eol=lf\n*.rs rust\n*.woff binary\n*.ttf binary\n*.otf binary\n*.png binary\n*.eot binary\n*.woff2 binary\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.yml",
    "content": "name: Bug Report\ndescription: Create a report to help us improve\nlabels: [\"C-bug\"]\nbody:\n  - type: markdown\n    attributes:\n      value: Thanks for filing a 🐛 bug report 😄!\n  - type: textarea\n    id: problem\n    attributes:\n      label: Problem\n      description: >\n        Please provide a clear and concise description of what the bug is,\n        including what currently happens and what you expected to happen.\n    validations:\n      required: true\n  - type: textarea\n    id: steps\n    attributes:\n      label: Steps\n      description: Please list the steps to reproduce the bug.\n      placeholder: |\n        1.\n        2.\n        3.\n  - type: textarea\n    id: possible-solutions\n    attributes:\n      label: Possible Solution(s)\n      description: >\n        Not obligatory, but suggest a fix/reason for the bug,\n        or ideas how to implement the addition or change.\n  - type: textarea\n    id: notes\n    attributes:\n      label: Notes\n      description: Provide any additional notes that might be helpful.\n  - type: textarea\n    id: version\n    attributes:\n      label: Version\n      description: >\n        Please paste the output of running `mdbook --version` or which version\n        of the library you are using.\n      render: text\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.yml",
    "content": "name: Enhancement\ndescription: Suggest an idea for enhancing mdBook\nlabels: [\"C-enhancement\"]\nbody:\n  - type: markdown\n    attributes:\n      value: |\n        Thanks for filing a 🙋 feature request 😄!\n  - type: textarea\n    id: problem\n    attributes:\n      label: Problem\n      description: >\n        Please provide a clear description of your use case and the problem\n        this feature request is trying to solve.\n    validations:\n      required: true\n  - type: textarea\n    id: solution\n    attributes:\n      label: Proposed Solution\n      description: >\n        Please provide a clear and concise description of what you want to happen.\n  - type: textarea\n    id: notes\n    attributes:\n      label: Notes\n      description: Provide any additional context or information that might be helpful.\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/question.yml",
    "content": "name: Question\ndescription: Have a question on how to use mdBook?\nlabels: [\"C-question\"]\nbody:\n  - type: markdown\n    attributes:\n      value: |\n        Got a question on how to do something with mdBook?\n  - type: textarea\n    id: question\n    attributes:\n      label: Question\n      description: >\n        Enter your question here. Please try to provide as much detail as possible.\n    validations:\n      required: true\n  - type: textarea\n    id: version\n    attributes:\n      label: Version\n      description: >\n        Please paste the output of running `mdbook --version` or which version\n        of the library you are using.\n      render: text\n"
  },
  {
    "path": ".github/renovate.json5",
    "content": "{\n    schedule: ['before 5am on the first day of the month'],\n    // Raise from default of 2 to reduce trickle.\n    prHourlyLimit: 6,\n    dependencyDashboard: true,\n    // Creates PRs if this renovate config file needs updating.\n    configMigration: true,\n    ignorePaths: [\n        'guide/src/for_developers/mdbook-wordcount/',\n    ],\n    customManagers: [\n        // Custom manager to extract the version of cargo-semver-checks from the workflow.\n        {\n            customType: 'regex',\n            managerFilePatterns: [\n               '/^.github.workflows.main.yml$/',\n            ],\n            matchStrings: [\n                'cargo-semver-checks.releases.download.v(?<currentValue>\\\\d+\\\\.\\\\d+(\\\\.\\\\d+)?)',\n            ],\n            depNameTemplate: 'cargo-semver-checks',\n            packageNameTemplate: 'obi1kenobi/cargo-semver-checks',\n            datasourceTemplate: 'github-releases',\n        },\n    ],\n    packageRules: [\n        // The next two rules disable compatible dependency updates. I wasn't\n        // able to get Renovate to be able to update Cargo.toml for compatible\n        // updates only, update all transitive dependencies, and do that all\n        // in a single PR. Instead, the `update-dependencies.sh` will handle\n        // that.\n        {\n            matchManagers: ['cargo'],\n            matchUpdateTypes: ['patch'],\n            enabled: false,\n        },\n        {\n            matchManagers: ['cargo'],\n            matchCurrentVersion: '>=1.0.0',\n            matchUpdateTypes: ['minor'],\n            enabled: false,\n        },\n        // Allow minor updates for pre-1.0 dependencies (semver-breaking)\n        {\n            matchManagers: ['cargo'],\n            matchCurrentVersion: '<1.0.0',\n            matchUpdateTypes: ['minor'],\n        },\n        // Allow major updates for stable dependencies (semver-breaking)\n        {\n            matchManagers: ['cargo'],\n            matchCurrentVersion: '>=1.0.0',\n            matchUpdateTypes: ['major'],\n        },\n        // Update cargo-semver-checks when a new version is available.\n        {\n            commitMessageTopic: 'cargo-semver-checks',\n            matchManagers: [\n                'custom.regex',\n            ],\n            matchDepNames: [\n                'cargo-semver-checks',\n            ],\n            extractVersion: '^v(?<version>\\\\d+\\\\.\\\\d+\\\\.\\\\d+)',\n            schedule: [\n                '* * * * *',\n            ],\n            internalChecksFilter: 'strict',\n        },\n    ]\n}\n"
  },
  {
    "path": ".github/workflows/deploy.yml",
    "content": "name: Deploy\non:\n  release:\n    types: [created]\n\ndefaults:\n  run:\n    shell: bash\n\npermissions:\n  contents: write\n\njobs:\n  release:\n    runs-on: ${{ matrix.os }}\n    strategy:\n      matrix:\n        include:\n          - target: aarch64-unknown-linux-musl\n            os: ubuntu-22.04\n          - target: x86_64-unknown-linux-gnu\n            os: ubuntu-22.04\n          - target: x86_64-unknown-linux-musl\n            os: ubuntu-22.04\n          - target: x86_64-apple-darwin\n            os: macos-latest\n          - target: aarch64-apple-darwin\n            os: macos-latest\n          - target: x86_64-pc-windows-msvc\n            os: windows-latest\n    name: Deploy ${{ matrix.target }}\n    steps:\n    - uses: actions/checkout@v5\n    - name: Install Rust\n      run: ci/install-rust.sh stable ${{ matrix.target }}\n    - name: Build asset\n      run: ci/make-release-asset.sh ${{ matrix.os }} ${{ matrix.target }}\n    - name: Update release with new asset\n      env:\n        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n      run: gh release upload $MDBOOK_TAG $MDBOOK_ASSET\n  pages:\n    name: GitHub Pages\n    runs-on: ubuntu-latest\n    steps:\n    - uses: actions/checkout@v5\n    - name: Install Rust (rustup)\n      run: rustup update stable --no-self-update && rustup default stable\n    - name: Deploy the User Guide to GitHub Pages using the gh-pages branch\n      run: ci/publish-guide.sh\n  publish:\n    name: Publish to crates.io\n    runs-on: ubuntu-latest\n    permissions:\n      # Required for OIDC token exchange\n      id-token: write\n    environment: publish\n    steps:\n      - uses: actions/checkout@v5\n      - name: Install Rust (rustup)\n        run: rustup update stable --no-self-update && rustup default stable\n      - name: Authenticate with crates.io\n        id: auth\n        uses: rust-lang/crates-io-auth-action@v1\n      - name: Publish\n        env:\n          CARGO_REGISTRY_TOKEN: ${{ steps.auth.outputs.token }}\n        run: cargo publish --workspace --no-verify\n"
  },
  {
    "path": ".github/workflows/main.yml",
    "content": "name: CI\non:\n  pull_request:\n  merge_group:\n\njobs:\n  test:\n    runs-on: ${{ matrix.os }}\n    strategy:\n      matrix:\n        include:\n          - name: stable linux\n            os: ubuntu-latest\n            rust: stable\n            target: x86_64-unknown-linux-gnu\n          - name: beta linux\n            os: ubuntu-latest\n            rust: beta\n            target: x86_64-unknown-linux-gnu\n          - name: nightly linux\n            os: ubuntu-latest\n            rust: nightly\n            target: x86_64-unknown-linux-gnu\n          - name: stable x86_64-unknown-linux-musl\n            os: ubuntu-22.04\n            rust: stable\n            target: x86_64-unknown-linux-musl\n          - name: stable x86_64 macos\n            os: macos-latest\n            rust: stable\n            target: x86_64-apple-darwin\n          - name: stable aarch64 macos\n            os: macos-latest\n            rust: stable\n            target: aarch64-apple-darwin\n          - name: stable windows-msvc\n            os: windows-latest\n            rust: stable\n            target: x86_64-pc-windows-msvc\n          - name: msrv\n            os: ubuntu-22.04\n            # sync MSRV with docs: guide/src/guide/installation.md and Cargo.toml\n            rust: 1.88.0\n            target: x86_64-unknown-linux-gnu\n    name: ${{ matrix.name }}\n    steps:\n    - uses: actions/checkout@v5\n    - name: Install Rust\n      run: bash ci/install-rust.sh ${{ matrix.rust }} ${{ matrix.target }}\n    - name: Build and run tests\n      run: cargo test --workspace --locked --target ${{ matrix.target }}\n    - name: Test no default\n      run: cargo test --workspace --no-default-features --target ${{ matrix.target }}\n\n  aarch64-cross-builds:\n    runs-on: ubuntu-22.04\n    steps:\n    - uses: actions/checkout@v5\n    - name: Install Rust\n      run: bash ci/install-rust.sh stable aarch64-unknown-linux-musl\n    - name: Build\n      run: cargo build --locked --target aarch64-unknown-linux-musl\n\n  rustfmt:\n    name: Rustfmt\n    runs-on: ubuntu-latest\n    steps:\n    - uses: actions/checkout@v5\n    - name: Install Rust\n      run: rustup update stable && rustup default stable && rustup component add rustfmt\n    - run: cargo fmt --check\n\n  gui:\n    name: GUI tests\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v5\n      - name: Install Rust\n        run: bash ci/install-rust.sh stable x86_64-unknown-linux-gnu\n      - name: Install npm\n        uses: actions/setup-node@v6\n        with:\n          node-version: 24\n      - name: Install browser-ui-test\n        run: npm install\n      - name: Run eslint\n        run: npm run lint\n      - name: Build and run tests (+ GUI)\n        run: cargo test --locked --target x86_64-unknown-linux-gnu --test gui\n\n  # Ensure there are no clippy warnings\n  clippy:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v5\n      - name: Install Rust\n        run: bash ci/install-rust.sh stable x86_64-unknown-linux-gnu\n      - run: rustup component add clippy\n      - run: cargo clippy --workspace --all-targets --no-deps -- -D warnings\n\n  docs:\n    name: Check API docs\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v5\n      - name: Install Rust\n        run: bash ci/install-rust.sh stable x86_64-unknown-linux-gnu\n      - name: Ensure intradoc links are valid\n        run: cargo doc --workspace --document-private-items --no-deps\n        env:\n          RUSTDOCFLAGS: -D warnings\n\n  check-version-bump:\n    name: Check version bump\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v5\n      - run: rustup update stable && rustup default stable\n      - name: Install cargo-semver-checks\n        run: |\n          mkdir installed-bins\n          curl -Lf https://github.com/obi1kenobi/cargo-semver-checks/releases/download/v0.47.0/cargo-semver-checks-x86_64-unknown-linux-gnu.tar.gz \\\n            | tar -xz --directory=./installed-bins\n          echo `pwd`/installed-bins >> $GITHUB_PATH\n      - run: cargo semver-checks --workspace\n\n  # The success job is here to consolidate the total success/failure state of\n  # all other jobs. This job is then included in the GitHub branch protection\n  # rule which prevents merges unless all other jobs are passing. This makes\n  # it easier to manage the list of jobs via this yml file and to prevent\n  # accidentally adding new jobs without also updating the branch protections.\n  success:\n    name: Success gate\n    if: always()\n    needs:\n      - test\n      - rustfmt\n      - aarch64-cross-builds\n      - gui\n      - clippy\n      - docs\n      - check-version-bump\n    runs-on: ubuntu-latest\n    steps:\n      - run: jq --exit-status 'all(.result == \"success\")' <<< '${{ toJson(needs) }}'\n      - name: Done\n        run: exit 0\n"
  },
  {
    "path": ".github/workflows/update-dependencies.yml",
    "content": "name: Update dependencies\non:\n  schedule:\n    - cron: '0 0 1 * *'\n  workflow_dispatch:\n\njobs:\n  update:\n    name: Update dependencies\n    runs-on: ubuntu-latest\n    if: github.repository == 'rust-lang/mdBook'\n    steps:\n      - uses: actions/checkout@v5\n      - name: Install Rust\n        run: bash ci/install-rust.sh stable x86_64-unknown-linux-gnu\n      - name: Install cargo-edit\n        run: cargo install cargo-edit --locked\n      - name: Update dependencies\n        run: ci/update-dependencies.sh\n        env:\n          GH_TOKEN: ${{ github.token }}\n"
  },
  {
    "path": ".gitignore",
    "content": "target\n\n# MacOS temp file\n.DS_Store\n\nbook-test\nguide/book\n\n.vscode\ntests/dummy_book/book/\ntests/gui/books/*/book/\ntests/testsuite/*/*/book/\n\n# Ignore Jetbrains specific files.\n.idea/\n\n# Ignore Vim temporary and swap files.\n*.sw?\n*~\n\n# GUI tests\nnode_modules\npackage-lock.json\npackage.json\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "# Changelog\n\n## mdBook 0.5.2\n[v0.5.1...v0.5.2](https://github.com/rust-lang/mdBook/compare/v0.5.1...v0.5.2)\n\n### Changed\n\n- Updated Rust crate html5ever to 0.36.0.\n  [#2970](https://github.com/rust-lang/mdBook/pull/2970)\n- Updated cargo dependencies.\n  [#2969](https://github.com/rust-lang/mdBook/pull/2969)\n\n### Fixed\n\n- Fixed repeated error message when HTML config is invalid in `mdbook serve`.\n  [#2983](https://github.com/rust-lang/mdBook/pull/2983)\n- Fixed sidebar scroll position when heading nav is involved.\n  [#2982](https://github.com/rust-lang/mdBook/pull/2982)\n- Fixed color for rustdoc error messages.\n  [#2981](https://github.com/rust-lang/mdBook/pull/2981)\n- Fixed usage of custom preprocessors with `MDBook::test`.\n  [#2980](https://github.com/rust-lang/mdBook/pull/2980)\n\n## mdBook 0.5.1\n[v0.5.0...v0.5.1](https://github.com/rust-lang/mdBook/compare/v0.5.0...v0.5.1)\n\n### Changed\n- Changed the scrollbar background to be transparent.\n  [#2932](https://github.com/rust-lang/mdBook/pull/2932)\n- Ignore invalid top-level environment variable config keys. This allows setting things like `MDBOOK_VERSION` to not cause an error.\n  [#2952](https://github.com/rust-lang/mdBook/pull/2952)\n\n### Fixed\n- Fixed the sidebar heading nav to have the correct nesting levels.\n  [#2953](https://github.com/rust-lang/mdBook/pull/2953)\n- Various Font Awesome fixes and improvements.\n  [#2951](https://github.com/rust-lang/mdBook/pull/2951)\n\n## mdBook 0.5.0\n[v0.4.52...v0.5.0](https://github.com/rust-lang/mdBook/compare/v0.4.52...v0.5.0)\n\nThe 0.5.0 release is the next major release of mdBook, containing over 130 PRs since 0.4.52! The primary focus for this release has been an evolution of the Rust APIs to make it easier to maintain, to evolve in a backwards-compatible fashion, to clean up some things that have accumulated over time, and to significantly improve the performance and compile-times.\n\nThis release also includes many new features described below.\n\nWe have prepared a [0.5 Migration Guide](#05-migration-guide) to help existing authors switch from 0.4.\n\nThe final 0.5.0 release only contains the following changes since [0.5.0-beta.2](#mdbook-050-beta2):\n\n- Added error handling to environment config handling. This checks that environment variables starting with `MDBOOK_` are correctly specified instead of silently ignoring. This also fixed being able to replace entire top-level tables like `MDBOOK_OUTPUT`.\n  [#2942](https://github.com/rust-lang/mdBook/pull/2942)\n\n## 0.5 Migration Guide\n\nThe 0.5 release contains several breaking changes from the 0.4 release. Preprocessors and renderers will need to be migrated to continue to work with this release. After updating your configuration, it is recommended to carefully compare and review how your book renders to ensure everything is working correctly.\n\nIf you have overridden any of the theme files, you will likely need to update them to match the current version.\n\nSee the entries below for [mdBook 0.5.0-alpha.1](#mdbook-050-alpha1), [mdBook 0.5.0-beta.1](#mdbook-050-beta1), and [mdBook 0.5.0-beta.2](#mdbook-050-beta2) for a more complete list of changes and fixes.\n\nThe following is a summary of the changes that may require your attention when updating to 0.5:\n\n### Major additions\n\n- Added sidebar heading navigation. This includes the `output.html.sidebar-header-nav` option to disable it.\n  [#2822](https://github.com/rust-lang/mdBook/pull/2822)\n- Added support for definition lists. These are enabled by default, with the option `output.html.definition-lists` to disable it. See [docs](https://rust-lang.github.io/mdBook/format/markdown.html#definition-lists) for more.\n  [#2847](https://github.com/rust-lang/mdBook/pull/2847)\n- Added support for admonitions. These are enabled by default, with the option `output.html.admonitions` to disable it. See [docs](https://rust-lang.github.io/mdBook/format/markdown.html#admonitions) for more.\n  [#2851](https://github.com/rust-lang/mdBook/pull/2851)\n- Links on the print page now link to elements on the print page instead of linking out to the individual chapters.\n  [#2844](https://github.com/rust-lang/mdBook/pull/2844)\n\n### Config changes\n\n- Unknown fields in config are now an error.\n  [#2787](https://github.com/rust-lang/mdBook/pull/2787)\n  [#2801](https://github.com/rust-lang/mdBook/pull/2801)\n- Removed `curly-quotes`, use `output.html.smart-punctuation` instead.\n  [#2788](https://github.com/rust-lang/mdBook/pull/2788)\n- Removed `output.html.copy-fonts`. The default fonts are now always copied unless you override the `theme/fonts/fonts.css` file.\n  [#2790](https://github.com/rust-lang/mdBook/pull/2790)\n- If the `command` path for a renderer or preprocessor is relative, it is now always relative to the book root.\n  [#2792](https://github.com/rust-lang/mdBook/pull/2792)\n  [#2796](https://github.com/rust-lang/mdBook/pull/2796)\n- Added the `optional` field for preprocessors. The default is `false`, so this also means it is an error by default if the preprocessor is missing.\n  [#2797](https://github.com/rust-lang/mdBook/pull/2797)\n- `output.html.smart-punctuation` is now `true` by default.\n  [#2810](https://github.com/rust-lang/mdBook/pull/2810)\n- `output.html.hash-files` is now `true` by default.\n  [#2820](https://github.com/rust-lang/mdBook/pull/2820)\n- Removed support for google-analytics. Use a theme extension (like `head.hbs`) if you need to continue to support this.\n  [#2776](https://github.com/rust-lang/mdBook/pull/2776)\n- Removed the `book.multilingual` field. This was never used.\n  [#2775](https://github.com/rust-lang/mdBook/pull/2775)\n- Removed the very old legacy config support. Warnings have been displayed in previous versions on how to migrate.\n  [#2783](https://github.com/rust-lang/mdBook/pull/2783)\n- Top-level config values set from the environment like `MDBOOK_BOOK` now *replace* the contents of the top-level table instead of merging into it.\n  [#2942](https://github.com/rust-lang/mdBook/pull/2942)\n- Invalid environment variables are now rejected. Previously unknown keys like `MDBOOK_FOO` would be ignored, or keys or invalid values inside objects like the `[book]` table would be ignored.\n  [#2942](https://github.com/rust-lang/mdBook/pull/2942)\n\n### Theme changes\n\n- Replaced the `{{#previous}}` and `{{#next}}` handlebars helpers with simple objects that contain the previous and next values.\n  [#2794](https://github.com/rust-lang/mdBook/pull/2794)\n- Removed the `{{theme_option}}` handlebars helper. It has not been used for a while.\n  [#2795](https://github.com/rust-lang/mdBook/pull/2795)\n\n### Rendering changes\n\n- Updated to a newer version of `pulldown-cmark`. This brings a large number of fixes to markdown processing.\n  [#2401](https://github.com/rust-lang/mdBook/pull/2401)\n- The font-awesome font is no longer loaded as a font. Instead, the corresponding SVG is embedded in the output for the corresponding `<i>` tags. Additionally, a handlebars helper has been added for the `hbs` files. This also updates the version from 4.7.0 to 6.2.0, which means some of the icon names and styles have changed. Most of the free icons are in the \"solid\" set. See the [free icon set](https://fontawesome.com/v6/search) for the available icons.\n  [#1330](https://github.com/rust-lang/mdBook/pull/1330)\n- Changed all internal HTML IDs to have an `mdbook-` prefix. This helps avoid namespace conflicts with header IDs.\n  [#2808](https://github.com/rust-lang/mdBook/pull/2808)\n- There is a new internal HTML rendering pipeline. This is primarily intended to give mdBook more flexibility in generating its HTML output. This resulted in some small changes to the HTML structure. HTML parsing may now be more strict than before.\n  [#2844](https://github.com/rust-lang/mdBook/pull/2844)\n- Links on the print page now link to elements on the print page instead of linking out to the individual chapters.\n  [#2844](https://github.com/rust-lang/mdBook/pull/2844)\n- Added support for definition lists. These are enabled by default, with the option `output.html.definition-lists` to disable it.\n  [#2847](https://github.com/rust-lang/mdBook/pull/2847)\n- Added support for admonitions. These are enabled by default, with the option `output.html.admonitions` to disable it.\n  [#2851](https://github.com/rust-lang/mdBook/pull/2851)\n- Header ID generation has some minor changes to bring the ID generation closer to other tools and sites:\n  - IDs now use Unicode lowercase instead of ASCII lowercase.\n    [#2922](https://github.com/rust-lang/mdBook/pull/2922)\n  - Headers that start or end with HTML characters like `<`, `&`, or `>` now replace those characters in the link ID with `-` instead of being stripped.\n    [#2844](https://github.com/rust-lang/mdBook/pull/2844)\n- Headers are no longer modified if the tag is manually written HTML.\n  [#2913](https://github.com/rust-lang/mdBook/pull/2913)\n\n### CLI changes\n\n- Removed the `--dest-dir` option to `mdbook test`. It was unused since `mdbook test` does not generate output.\n  [#2805](https://github.com/rust-lang/mdBook/pull/2805)\n- Changed CLI `--dest-dir` to be relative to the current directory, not the book root.\n  [#2806](https://github.com/rust-lang/mdBook/pull/2806)\n\n### Rust API\n\n- The Rust API has been split into several crates ([#2766](https://github.com/rust-lang/mdBook/pull/2766)). In summary, the different crates are:\n  - `mdbook` — The CLI binary.\n  - [`mdbook-driver`](https://docs.rs/mdbook-driver/latest/mdbook_driver/) — The high-level library for running mdBook, primarily through the `MDBook` type. If you are driving mdBook programmatically, this is the crate you want.\n  - [`mdbook-preprocessor`](https://docs.rs/mdbook-preprocessor/latest/mdbook_preprocessor/) — Support for implementing preprocessors. If you have a preprocessor, then this is the crate you should depend on.\n  - [`mdbook-renderer`](https://docs.rs/mdbook-renderer/latest/mdbook_renderer/) — Support for implementing renderers. If you have a custom renderer, this is the crate you should depend on.\n  - [`mdbook-markdown`](https://docs.rs/mdbook-markdown/latest/mdbook_markdown/) — The Markdown renderer. If you are processing markdown, this is the crate you should depend on. This is essentially a thin wrapper around `pulldown-cmark`, and re-exports that crate so that you can ensure the version stays in sync with mdBook.\n  - [`mdbook-summary`](https://docs.rs/mdbook-summary/latest/mdbook_summary/) — The `SUMMARY.md` parser.\n  - [`mdbook-html`](https://docs.rs/mdbook-html/latest/mdbook_html/) — The HTML renderer.\n  - [`mdbook-core`](https://docs.rs/mdbook-core/latest/mdbook_core/) — An internal library that is used by the other crates for shared types. You should not depend on this crate directly since types from this crate are re-exported from the other crates as appropriate.\n- Changes to `Config`:\n  - [`Config::get`](https://docs.rs/mdbook-core/latest/mdbook_core/config/struct.Config.html#method.get) is now generic over the return value, using `serde` to deserialize the value. It also returns a `Result` to handle deserialization errors. [#2773](https://github.com/rust-lang/mdBook/pull/2773)\n  - [`Config::set`](https://docs.rs/mdbook-core/latest/mdbook_core/config/struct.Config.html#method.set) now validates that the config keys and values are valid.\n    [#2942](https://github.com/rust-lang/mdBook/pull/2942)\n  - [`Config::update_from_env`](https://docs.rs/mdbook-core/latest/mdbook_core/config/struct.Config.html#method.update_from_env) now returns a `Result` to indicate any errors.\n    [#2942](https://github.com/rust-lang/mdBook/pull/2942)\n  - Removed `Config::get_deserialized`. Use `Config::get` instead.\n  - Removed `Config::get_deserialized_opt`. Use `Config::get` instead.\n  - Removed `Config::get_mut`. Use `Config::set` instead.\n  - Removed deprecated `Config::get_deserialized_opt`. Use `Config::get` instead.\n  - Removed `Config::get_renderer`. Use `Config::get` instead.\n  - Removed `Config::get_preprocessor`. Use `Config::get` instead.\n- Public types have been switch to use the `#[non_exhaustive]` attribute to help allow them to change in a backwards-compatible way.\n  [#2779](https://github.com/rust-lang/mdBook/pull/2779)\n  [#2823](https://github.com/rust-lang/mdBook/pull/2823)\n- Changed `MDBook` `with_renderer`/`with_preprocessor` to overwrite the entry if an extension of the same name is already loaded. This allows the caller to replace an entry.\n  [#2802](https://github.com/rust-lang/mdBook/pull/2802)\n- Added `MarkdownOptions` struct to specify settings for markdown rendering for `mdbook_markdown::new_cmark_parser`.\n  [#2809](https://github.cocm/rust-lang/mdBook/pull/2809)\n- Renamed `Book::sections` to `Book::items`.\n  [#2813](https://github.com/rust-lang/mdBook/pull/2813)\n- `mdbook::book::load_book` is now private. Instead, use one of the `MDBook` load functions like `MDBook::load_with_config`.\n- Removed `HtmlConfig::smart_punctuation` method, use the field of the same name.\n- `CmdPreprocessor::parse_input` moved to `mdbook_preprocessor::parse_input`.\n- `Preprocessor::supports_renderer` now returns a `Result<bool>` instead of `bool` to be able to handle errors.\n- Most of the types from the `theme` module are now private. The `Theme` struct is still exposed for working with themes.\n- Various functions in the `utils::fs` module have been removed, renamed, or reworked.\n- Most of the functions in the `utils` module have been moved, removed, or made private.\n\n## mdBook 0.5.0-beta.2\n[v0.5.0-beta.1...v0.5.0-beta.2](https://github.com/rust-lang/mdBook/compare/v0.5.0-beta.1...v0.5.0-beta.2)\n\n### Added\n\n- Added a warning when a Font Awesome icon is missing.\n  [#2915](https://github.com/rust-lang/mdBook/pull/2915)\n- Added some trace logging for event processing.\n  [#2911](https://github.com/rust-lang/mdBook/pull/2911)\n- Added `Config::contains_key`.\n  [#2910](https://github.com/rust-lang/mdBook/pull/2910)\n\n### Changed\n\n- Heading IDs are now lowercase.\n  [#2922](https://github.com/rust-lang/mdBook/pull/2922)\n- Updated cargo dependencies.\n  [#2916](https://github.com/rust-lang/mdBook/pull/2916)\n- Removed italics for in quotes/comments in code blocks with the `ayu` theme.\n  [#2904](https://github.com/rust-lang/mdBook/pull/2904)\n- Exposed \"search\" feature from mdbook-driver.\n  [#2907](https://github.com/rust-lang/mdBook/pull/2907)\n\n### Fixed\n\n- Fixed rust fenced code blocks with an indent.\n  [#2905](https://github.com/rust-lang/mdBook/pull/2905)\n- Headers and `dt` tags are no longer modified if the tag is manually written HTML.\n  [#2913](https://github.com/rust-lang/mdBook/pull/2913)\n- Fixed print page links for internal links to non-chapters.\n  [#2914](https://github.com/rust-lang/mdBook/pull/2914)\n- Better handling for unbalanced HTML tags.\n  [#2924](https://github.com/rust-lang/mdBook/pull/2924)\n- Handle unclosed HTML tags inside a markdown element.\n  [#2927](https://github.com/rust-lang/mdBook/pull/2927)\n- Fixed missing font-awesome icons in the guide.\n  [#2926](https://github.com/rust-lang/mdBook/pull/2926)\n- Hide the sidebar resize indicator when JS isn't available.\n  [#2923](https://github.com/rust-lang/mdBook/pull/2923)\n\n## mdBook 0.5.0-beta.1\n[v0.5.0-alpha.1...v0.5.0-beta.1](https://github.com/rust-lang/mdBook/compare/v0.5.0-alpha.1...v0.5.0-beta.1)\n\n### Changed\n\n- Reworked the look of the header navigation.\n  [#2898](https://github.com/rust-lang/mdBook/pull/2898)\n- Update cargo dependencies.\n  [#2896](https://github.com/rust-lang/mdBook/pull/2896)\n- Improved the heading nav debug.\n  [#2892](https://github.com/rust-lang/mdBook/pull/2892)\n\n### Fixed\n\n- Fixed error message for config.get deserialization error.\n  [#2902](https://github.com/rust-lang/mdBook/pull/2902)\n- Filter `<mark>` tags from sidebar heading nav.\n  [#2899](https://github.com/rust-lang/mdBook/pull/2899)\n- Avoid divide-by-zero in heading nav computation\n  [#2891](https://github.com/rust-lang/mdBook/pull/2891)\n- Fixed heading nav with folded chapters.\n  [#2893](https://github.com/rust-lang/mdBook/pull/2893)\n\n## mdBook 0.5.0-alpha.1\n[v0.4.52...v0.5.0-alpha.1](https://github.com/rust-lang/mdBook/compare/v0.4.52...v0.5.0-alpha.1)\n\n### Added\n\n- The location of the generated HTML book is now displayed on the console.\n  [#2729](https://github.com/rust-lang/mdBook/pull/2729)\n- ❗ Added the `optional` field for preprocessors. The default is `false`, so this also changes it so that it is an error if the preprocessor is missing.\n  [#2797](https://github.com/rust-lang/mdBook/pull/2797)\n- ❗ Added `MarkdownOptions` struct to specify settings for markdown rendering.\n  [#2809](https://github.cocm/rust-lang/mdBook/pull/2809)\n- Added sidebar heading navigation. This includes the `output.html.sidebar-header-nav` option to disable it.\n  [#2822](https://github.com/rust-lang/mdBook/pull/2822)\n- Added the mdbook version to the guide.\n  [#2826](https://github.com/rust-lang/mdBook/pull/2826)\n- Added `Book::chapters` and `Book::for_each_chapter_mut` to more conveniently iterate over chapters (instead of all items).\n  [#2838](https://github.com/rust-lang/mdBook/pull/2838)\n- ❗ Added support for definition lists. These are enabled by default, with the option `output.html.definition-lists` to disable it.\n  [#2847](https://github.com/rust-lang/mdBook/pull/2847)\n- ❗ Added support for admonitions. These are enabled by default, with the option `output.html.admonitions` to disable it.\n  [#2851](https://github.com/rust-lang/mdBook/pull/2851)\n\n### Changed\n\n- ❗ The `mdbook` crate has been split into multiple crates.\n  [#2766](https://github.com/rust-lang/mdBook/pull/2766)\n- The minimum Rust version has been updated to 1.88.\n  [#2844](https://github.com/rust-lang/mdBook/pull/2844)\n- ❗ `pulldown-cmark` has been upgraded to 0.13.0, bringing a large number of fixes to markdown processing.\n  [#2401](https://github.com/rust-lang/mdBook/pull/2401)\n- ❗ Switched public types to `non_exhaustive` to help allow them to change in a backwards-compatible way.\n  [#2779](https://github.com/rust-lang/mdBook/pull/2779)\n  [#2823](https://github.com/rust-lang/mdBook/pull/2823)\n- ❗ Unknown fields in config are now an error.\n  [#2787](https://github.com/rust-lang/mdBook/pull/2787)\n  [#2801](https://github.com/rust-lang/mdBook/pull/2801)\n- ❗ Changed `id_from_content` to be private.\n  [#2791](https://github.com/rust-lang/mdBook/pull/2791)\n- ❗ Changed preprocessor `command` to use paths relative to the book root.\n  [#2796](https://github.com/rust-lang/mdBook/pull/2796)\n- ❗ Replaced the `{{#previous}}` and `{{#next}}` handelbars navigation helpers with objects.\n  [#2794](https://github.com/rust-lang/mdBook/pull/2794)\n- ❗ Use embedded SVG instead of fonts for icons, font-awesome 6.2.\n  [#1330](https://github.com/rust-lang/mdBook/pull/1330)\n- The `book.src` field is no longer serialized if it is the default of \"src\".\n  [#2800](https://github.com/rust-lang/mdBook/pull/2800)\n- ❗ Changed `MDBook` `with_renderer`/`with_preprocessor` to overwrite the entry if an extension of the same name is already loaded.\n  [#2802](https://github.com/rust-lang/mdBook/pull/2802)\n- ❗ Changed CLI `--dest-dir` to be relative to the current directory, not the book root.\n  [#2806](https://github.com/rust-lang/mdBook/pull/2806)\n- ❗ Changed all internal HTML IDs to have an `mdbook-` prefix. This helps avoid namespace conflicts with header IDs.\n  [#2808](https://github.com/rust-lang/mdBook/pull/2808)\n- ❗ `output.html.smart-punctuation` is now `true` by default.\n  [#2810](https://github.com/rust-lang/mdBook/pull/2810)\n- ❗ Renamed `Book::sections` to `Book::items`.\n  [#2813](https://github.com/rust-lang/mdBook/pull/2813)\n- ❗ `output.html.hash-files` is now `true` by default.\n  [#2820](https://github.com/rust-lang/mdBook/pull/2820)\n- Switched from `log` to `tracing`.\n  [#2829](https://github.com/rust-lang/mdBook/pull/2829)\n- ❗ Rewrote the HTML rendering pipeline.\n  [#2844](https://github.com/rust-lang/mdBook/pull/2844)\n- ❗ Links on the print page now link to elements on the print page instead of linking out to the individual chapters.\n  [#2844](https://github.com/rust-lang/mdBook/pull/2844)\n- ❗ Moved theme copy to the Theme type and reduced visibility.\n  [#2857](https://github.com/rust-lang/mdBook/pull/2857)\n- ❗ Cleaned up some fs-related utilities.\n  [#2856](https://github.com/rust-lang/mdBook/pull/2856)\n- ❗ Moved `get_404_output_file` to `HtmlConfig`.\n  [#2855](https://github.com/rust-lang/mdBook/pull/2855)\n- ❗ Moved `take_lines` functions to `mdbook-driver` and made private.\n  [#2854](https://github.com/rust-lang/mdBook/pull/2854)\n- Updated dependencies.\n  [#2793](https://github.com/rust-lang/mdBook/pull/2793)\n  [#2869](https://github.com/rust-lang/mdBook/pull/2869)\n\n### Removed\n\n- ❗ Removed `toml` as a public dependency.\n  [#2773](https://github.com/rust-lang/mdBook/pull/2773)\n- ❗ Removed the `book.multilingual` field. This was never used.\n  [#2775](https://github.com/rust-lang/mdBook/pull/2775)\n- ❗ Removed support for google-analytics.\n  [#2776](https://github.com/rust-lang/mdBook/pull/2776)\n- ❗ Removed the very old legacy config support.\n  [#2783](https://github.com/rust-lang/mdBook/pull/2783)\n- ❗ Removed `curly-quotes`, use `output.html.smart-punctuation` instead.\n  [#2788](https://github.com/rust-lang/mdBook/pull/2788)\n- Removed old warning about `book.json`.\n  [#2789](https://github.com/rust-lang/mdBook/pull/2789)\n- ❗ Removed `output.html.copy-fonts`. The default fonts are now always copied unless you override the `theme/fonts/fonts.css` file.\n  [#2790](https://github.com/rust-lang/mdBook/pull/2790)\n- ❗ Removed legacy relative renderer command paths. Relative renderer command paths now must always be relative to the book root.\n  [#2792](https://github.com/rust-lang/mdBook/pull/2792)\n- ❗ Removed the `{{theme_option}}` handlebars helper. It has not been used for a while.\n  [#2795](https://github.com/rust-lang/mdBook/pull/2795)\n- ❗ Removed the `--dest-dir` option to `mdbook test`.\n  [#2805](https://github.com/rust-lang/mdBook/pull/2805)\n\n### Fixed\n\n- Fixed handling of multiple footnotes in a row.\n  [#2807](https://github.com/rust-lang/mdBook/pull/2807)\n- Fixed ID collisions when the numeric suffix gets used.\n  [#2846](https://github.com/rust-lang/mdBook/pull/2846)\n- Fixed missing css vars for no-js dark mode.\n  [#2850](https://github.com/rust-lang/mdBook/pull/2850)\n\n## mdBook 0.4.52\n[v0.4.51...v0.4.52](https://github.com/rust-lang/mdBook/compare/v0.4.51...v0.4.52)\n\n**Note:** If you have a custom `index.hbs` theme file, it is recommended that you update it to the latest version to pick up the fixes in this release.\n\n### Added\n- Added the ability to redirect `#` HTML fragments using the existing `output.html.redirect` table.\n  [#2747](https://github.com/rust-lang/mdBook/pull/2747)\n- Added the `rel=\"edit\"` attribute to the edit page button.\n  [#2702](https://github.com/rust-lang/mdBook/pull/2702)\n\n### Changed\n- The search index is now only loaded when the search input is opened instead of always being loaded.\n  [#2553](https://github.com/rust-lang/mdBook/pull/2553)\n  [#2735](https://github.com/rust-lang/mdBook/pull/2735)\n- The `mdbook serve` command has switched its underlying server library from warp to axum.\n  [#2748](https://github.com/rust-lang/mdBook/pull/2748)\n- Updated dependencies.\n  [#2752](https://github.com/rust-lang/mdBook/pull/2752)\n\n### Fixed\n- The sidebar is now set to `display:none` when it is hidden in order to prevent the browser's search from thinking the sidebar's text is visible.\n  [#2725](https://github.com/rust-lang/mdBook/pull/2725)\n- Fixed search index URL not updating correctly when `hash-files` is enabled.\n  [#2742](https://github.com/rust-lang/mdBook/pull/2742)\n  [#2746](https://github.com/rust-lang/mdBook/pull/2746)\n- Fixed several sidebar animation bugs, particularly when manually resizing.\n  [#2750](https://github.com/rust-lang/mdBook/pull/2750)\n\n## mdBook 0.4.51\n[v0.4.50...v0.4.51](https://github.com/rust-lang/mdBook/compare/v0.4.50...v0.4.51)\n\n### Fixed\n- Fixed regression that broke the `S` search hotkey.\n  [#2713](https://github.com/rust-lang/mdBook/pull/2713)\n\n## mdBook 0.4.50\n[v0.4.49...v0.4.50](https://github.com/rust-lang/mdBook/compare/v0.4.49...v0.4.50)\n\n### Added\n\n- Added a keyboard shortcut help popup when pressing `?`.\n  [#2608](https://github.com/rust-lang/mdBook/pull/2608)\n\n### Changed\n\n- Changed the look of the sidebar resize handle to match the new rustdoc format.\n  [#2691](https://github.com/rust-lang/mdBook/pull/2691)\n- `/` can now be used to open the search bar.\n  [#2698](https://github.com/rust-lang/mdBook/pull/2698)\n- Pressing enter from the search bar will navigate to the first entry.\n  [#2698](https://github.com/rust-lang/mdBook/pull/2698)\n- Updated `opener` to drop some dependencies.\n  [#2709](https://github.com/rust-lang/mdBook/pull/2709)\n- Updated dependencies, MSRV raised to 1.82.\n  [#2711](https://github.com/rust-lang/mdBook/pull/2711)\n\n### Fixed\n\n- Fixed uncaught exception when pressing down when there are no search results.\n  [#2698](https://github.com/rust-lang/mdBook/pull/2698)\n- Fixed syntax highlighting of Rust code in the ACE editor.\n  [#2710](https://github.com/rust-lang/mdBook/pull/2710)\n\n## mdBook 0.4.49\n[v0.4.48...v0.4.49](https://github.com/rust-lang/mdBook/compare/v0.4.48...v0.4.49)\n\n### Added\n\n- Added a warning on unused fields in the root of `book.toml`.\n  [#2622](https://github.com/rust-lang/mdBook/pull/2622)\n\n### Changed\n\n- Updated dependencies.\n  [#2650](https://github.com/rust-lang/mdBook/pull/2650)\n  [#2688](https://github.com/rust-lang/mdBook/pull/2688)\n- Updated minimum Rust version to 1.81.\n  [#2688](https://github.com/rust-lang/mdBook/pull/2688)\n- The unused `book.multilingual` field is no longer serialized, or shown in `mdbook init`.\n  [#2689](https://github.com/rust-lang/mdBook/pull/2689)\n- Speed up search index loading by using `JSON.parse` instead of parsing JavaScript.\n  [#2633](https://github.com/rust-lang/mdBook/pull/2633)\n\n### Fixed\n\n- Search highlighting will not try to highlight in SVG `<text>` elements because it breaks the element.\n  [#2668](https://github.com/rust-lang/mdBook/pull/2668)\n- Fixed scrolling of the sidebar when a search highlight term is in the URL.\n  [#2675](https://github.com/rust-lang/mdBook/pull/2675)\n- Fixed issues when multiple footnote definitions use the same ID. Now, only one definition is used, and a warning is displayed.\n  [#2681](https://github.com/rust-lang/mdBook/pull/2681)\n- The sidebar is now restricted to 80% of the viewport width to make it possible to collapse it when the viewport is very narrow.\n  [#2679](https://github.com/rust-lang/mdBook/pull/2679)\n\n## mdBook 0.4.48\n[v0.4.47...v0.4.48](https://github.com/rust-lang/mdBook/compare/v0.4.47...v0.4.48)\n\n### Added\n\n- Footnotes now have back-reference links. These links bring the reader back to the original location. As part of this change, footnotes are now only rendered at the bottom of the page. This also includes some styling updates and fixes for footnote rendering.\n  [#2626](https://github.com/rust-lang/mdBook/pull/2626)\n- Added an \"Auto\" theme selection option which will default to the system-preferred mode. This will also automatically switch when the system changes the preferred mode.\n  [#2576](https://github.com/rust-lang/mdBook/pull/2576)\n\n### Changed\n\n- The `searchindex.json` file has been removed; only the `searchindex.js` file will be generated.\n  [#2552](https://github.com/rust-lang/mdBook/pull/2552)\n- Updated Javascript code to use eslint.\n  [#2554](https://github.com/rust-lang/mdBook/pull/2554)\n- An error is generated if there are duplicate files in `SUMMARY.md`.\n  [#2613](https://github.com/rust-lang/mdBook/pull/2613)\n\n## mdBook 0.4.47\n[v0.4.46...v0.4.47](https://github.com/rust-lang/mdBook/compare/v0.4.46...v0.4.47)\n\n### Fixed\n\n- Fixed search not showing up in sub-directories.\n  [#2586](https://github.com/rust-lang/mdBook/pull/2586)\n\n## mdBook 0.4.46\n[v0.4.45...v0.4.46](https://github.com/rust-lang/mdBook/compare/v0.4.45...v0.4.46)\n\n### Changed\n\n- The `output.html.hash-files` config option has been added to add hashes to static filenames to bust any caches when a book is updated. `{{resource}}` template tags have been added so that links can be properly generated to those files.\n  [#1368](https://github.com/rust-lang/mdBook/pull/1368)\n\n### Fixed\n\n- Playground links for Rust 2024 now set the edition correctly.\n  [#2557](https://github.com/rust-lang/mdBook/pull/2557)\n\n## mdBook 0.4.45\n[v0.4.44...v0.4.45](https://github.com/rust-lang/mdBook/compare/v0.4.44...v0.4.45)\n\n### Changed\n\n- Added context to error message when rustdoc is not found.\n  [#2545](https://github.com/rust-lang/mdBook/pull/2545)\n- Slightly changed the styling rules around margins of footnotes.\n  [#2524](https://github.com/rust-lang/mdBook/pull/2524)\n\n### Fixed\n\n- Fixed an issue where it would panic if a source_path is not set.\n  [#2550](https://github.com/rust-lang/mdBook/pull/2550)\n\n## mdBook 0.4.44\n[v0.4.43...v0.4.44](https://github.com/rust-lang/mdBook/compare/v0.4.43...v0.4.44)\n\n### Added\n\n- Added pre-built aarch64-apple-darwin binaries to the releases.\n  [#2500](https://github.com/rust-lang/mdBook/pull/2500)\n- `mdbook clean` now shows a summary of what it did.\n  [#2458](https://github.com/rust-lang/mdBook/pull/2458)\n- Added the `output.html.search.chapter` config setting to disable search indexing of individual chapters.\n  [#2533](https://github.com/rust-lang/mdBook/pull/2533)\n\n### Fixed\n\n- Fixed auto-scrolling the side-bar when loading a page with a `#` fragment URL.\n  [#2517](https://github.com/rust-lang/mdBook/pull/2517)\n- Fixed display of sidebar when javascript is disabled.\n  [#2529](https://github.com/rust-lang/mdBook/pull/2529)\n- Fixed the sidebar visibility getting out of sync with the button.\n  [#2532](https://github.com/rust-lang/mdBook/pull/2532)\n\n### Changed\n\n- ❗ Rust code block hidden lines now follow the same logic as rustdoc. This requires a space after the `#` symbol.\n  [#2530](https://github.com/rust-lang/mdBook/pull/2530)\n- ❗ Updated the Linux pre-built binaries which requires a newer version of glibc (2.34).\n  [#2523](https://github.com/rust-lang/mdBook/pull/2523)\n- Updated dependencies\n  [#2538](https://github.com/rust-lang/mdBook/pull/2538)\n  [#2539](https://github.com/rust-lang/mdBook/pull/2539)\n\n## mdBook 0.4.43\n[v0.4.42...v0.4.43](https://github.com/rust-lang/mdBook/compare/v0.4.42...v0.4.43)\n\n### Fixed\n\n- Fixed setting the title in `mdbook init` when no git user is configured.\n  [#2486](https://github.com/rust-lang/mdBook/pull/2486)\n\n### Changed\n\n- The Rust 2024 edition no longer needs `-Zunstable-options`.\n  [#2495](https://github.com/rust-lang/mdBook/pull/2495)\n\n## mdBook 0.4.42\n[v0.4.41...v0.4.42](https://github.com/rust-lang/mdBook/compare/v0.4.41...v0.4.42)\n\n### Fixed\n\n- Fixed chapter list folding.\n  [#2473](https://github.com/rust-lang/mdBook/pull/2473)\n\n## mdBook 0.4.41\n[v0.4.40...v0.4.41](https://github.com/rust-lang/mdBook/compare/v0.4.40...v0.4.41)\n\n**Note:** If you have a custom `index.hbs` theme file, you will need to update it to the latest version.\n\n### Added\n\n- Added preliminary support for Rust 2024 edition.\n  [#2398](https://github.com/rust-lang/mdBook/pull/2398)\n- Added a full example of the remove-emphasis preprocessor.\n  [#2464](https://github.com/rust-lang/mdBook/pull/2464)\n\n### Changed\n\n- Adjusted styling of clipboard/play icons.\n  [#2421](https://github.com/rust-lang/mdBook/pull/2421)\n- Updated to handlebars v6.\n  [#2416](https://github.com/rust-lang/mdBook/pull/2416)\n- Attr and section rules now have specific code highlighting.\n  [#2448](https://github.com/rust-lang/mdBook/pull/2448)\n- The sidebar is now loaded from a common file, significantly reducing the book size when there are many chapters.\n  [#2414](https://github.com/rust-lang/mdBook/pull/2414)\n- Updated dependencies.\n  [#2470](https://github.com/rust-lang/mdBook/pull/2470)\n\n### Fixed\n\n- Improved theme support when JavaScript is disabled.\n  [#2454](https://github.com/rust-lang/mdBook/pull/2454)\n- Fixed broken themes when localStorage has an invalid theme id.\n  [#2463](https://github.com/rust-lang/mdBook/pull/2463)\n- Adjusted the line-height of superscripts (and footnotes) to avoid adding extra space between lines.\n  [#2465](https://github.com/rust-lang/mdBook/pull/2465)\n\n## mdBook 0.4.40\n[v0.4.39...v0.4.40](https://github.com/rust-lang/mdBook/compare/v0.4.39...v0.4.40)\n\n### Fixed\n\n- Reverted the update to pulldown-cmark which broke the semver API.\n  [#2388](https://github.com/rust-lang/mdBook/pull/2388)\n\n## mdBook 0.4.39\n[v0.4.38...v0.4.39](https://github.com/rust-lang/mdBook/compare/v0.4.38...v0.4.39)\n\n### Fixed\n\n- Fixed the automatic deploy broken in the previous release.\n  [#2383](https://github.com/rust-lang/mdBook/pull/2383)\n\n## mdBook 0.4.38\n[v0.4.37...v0.4.38](https://github.com/rust-lang/mdBook/compare/v0.4.37...v0.4.38)\n\n### Added\n\n- Added `nix` to the default set of languages supported for syntax highlighting.\n  [#2262](https://github.com/rust-lang/mdBook/pull/2262)\n\n### Changed\n\n- The `output.html.curly-quotes` option has been renamed to `output.html.smart-punctuation` to better reflect what it does. The old option `curly-quotes` is kept for compatibility, but may be removed in the future.\n  [#2327](https://github.com/rust-lang/mdBook/pull/2327)\n- The file-watcher used in `mdbook serve` and `mdbook watch` now uses a poll-based watcher instead of the native operating system notifications. This should fix issues on various systems and environments, and more accurately detect when files change. The native watcher can still be used with the `--watcher native` CLI option.\n  [#2325](https://github.com/rust-lang/mdBook/pull/2325)\n- `mdbook test` output now includes color, and shows relative paths to the source.\n  [#2259](https://github.com/rust-lang/mdBook/pull/2259)\n- Updated dependencies, MSRV raised to 1.74\n  [#2350](https://github.com/rust-lang/mdBook/pull/2350)\n  [#2351](https://github.com/rust-lang/mdBook/pull/2351)\n  [#2378](https://github.com/rust-lang/mdBook/pull/2378)\n  [#2381](https://github.com/rust-lang/mdBook/pull/2381)\n\n### Fixed\n\n- Reduced memory allocation when copying files.\n  [#2355](https://github.com/rust-lang/mdBook/pull/2355)\n- Fixed the horizontal divider in `SUMMARY.md` from being indented into the previous nested section.\n  [#2364](https://github.com/rust-lang/mdBook/pull/2364)\n- Removed unnecessary `@import` in the CSS.\n  [#2260](https://github.com/rust-lang/mdBook/pull/2260)\n\n## mdBook 0.4.37\n[v0.4.36...v0.4.37](https://github.com/rust-lang/mdBook/compare/v0.4.36...v0.4.37)\n\n### Changed\n- ❗️ Updated the markdown parser. This brings in many changes to more closely follow the CommonMark spec. This may cause some small rendering changes. It is recommended to compare the output of the old and new version to check for changes. See <https://github.com/raphlinus/pulldown-cmark/releases/tag/v0.10.0> for more information.\n  [#2308](https://github.com/rust-lang/mdBook/pull/2308)\n- The warning about the legacy `src/theme` directory has been removed.\n  [#2263](https://github.com/rust-lang/mdBook/pull/2263)\n- Updated dependencies. MSRV raised to 1.71.0.\n  [#2283](https://github.com/rust-lang/mdBook/pull/2283)\n  [#2293](https://github.com/rust-lang/mdBook/pull/2293)\n  [#2297](https://github.com/rust-lang/mdBook/pull/2297)\n  [#2310](https://github.com/rust-lang/mdBook/pull/2310)\n  [#2309](https://github.com/rust-lang/mdBook/pull/2309)\n- Some internal performance/memory improvements.\n  [#2273](https://github.com/rust-lang/mdBook/pull/2273)\n  [#2290](https://github.com/rust-lang/mdBook/pull/2290)\n- Made the `pathdiff` dependency optional based on the `watch` feature.\n  [#2291](https://github.com/rust-lang/mdBook/pull/2291)\n\n### Fixed\n- The `s` shortcut key handler should not trigger when focus is in an HTML form.\n  [#2311](https://github.com/rust-lang/mdBook/pull/2311)\n\n## mdBook 0.4.36\n[v0.4.35...v0.4.36](https://github.com/rust-lang/mdBook/compare/v0.4.35...v0.4.36)\n\n### Added\n- Added Nim to the default highlighted languages.\n  [#2232](https://github.com/rust-lang/mdBook/pull/2232)\n- Added a small indicator for the sidebar resize handle.\n  [#2209](https://github.com/rust-lang/mdBook/pull/2209)\n\n### Changed\n- Updated dependencies. MSRV raised to 1.70.0.\n  [#2173](https://github.com/rust-lang/mdBook/pull/2173)\n  [#2250](https://github.com/rust-lang/mdBook/pull/2250)\n  [#2252](https://github.com/rust-lang/mdBook/pull/2252)\n\n### Fixed\n- Fixed blank column in print page when the sidebar was visible.\n  [#2235](https://github.com/rust-lang/mdBook/pull/2235)\n- Fixed indentation of code blocks when Javascript is disabled.\n  [#2162](https://github.com/rust-lang/mdBook/pull/2162)\n- Fixed a panic when `mdbook serve` or `mdbook watch` were given certain kinds of paths.\n  [#2229](https://github.com/rust-lang/mdBook/pull/2229)\n\n## mdBook 0.4.35\n[v0.4.34...v0.4.35](https://github.com/rust-lang/mdBook/compare/v0.4.34...v0.4.35)\n\n### Added\n- Added the `book.text-direction` setting for explicit support for right-to-left languages.\n  [#1641](https://github.com/rust-lang/mdBook/pull/1641)\n- Added `rel=prefetch` to the \"next\" links to potentially improve browser performance.\n  [#2168](https://github.com/rust-lang/mdBook/pull/2168)\n- Added a `.warning` CSS class which is styled for displaying warning blocks.\n  [#2187](https://github.com/rust-lang/mdBook/pull/2187)\n\n### Changed\n- Better support of the sidebar when JavaScript is disabled.\n  [#2175](https://github.com/rust-lang/mdBook/pull/2175)\n\n## mdBook 0.4.34\n[v0.4.33...v0.4.34](https://github.com/rust-lang/mdBook/compare/v0.4.33...v0.4.34)\n\n### Fixed\n- Fixed file change watcher failing on macOS with a large number of files.\n  [#2157](https://github.com/rust-lang/mdBook/pull/2157)\n\n## mdBook 0.4.33\n[v0.4.32...v0.4.33](https://github.com/rust-lang/mdBook/compare/v0.4.32...v0.4.33)\n\n### Added\n- The `color-scheme` CSS property is now set based on the light/dark theme, which applies some slight color differences in browser elements like scroll bars on some browsers.\n  [#2134](https://github.com/rust-lang/mdBook/pull/2134)\n\n### Fixed\n- Fixed watching of extra-watch-dirs when not running in the book root directory.\n  [#2146](https://github.com/rust-lang/mdBook/pull/2146)\n- Reverted the dependency update to the `toml` crate (again!). This was an unintentional breaking change in 0.4.32.\n  [#2021](https://github.com/rust-lang/mdBook/pull/2021)\n- Changed macOS change notifications to use the kqueue implementation which should fix some issues with repeated rebuilds when a file changed.\n  [#2152](https://github.com/rust-lang/mdBook/pull/2152)\n- Don't set a background color in the print page for code blocks in a header.\n  [#2150](https://github.com/rust-lang/mdBook/pull/2150)\n\n## mdBook 0.4.32\n[v0.4.31...v0.4.32](https://github.com/rust-lang/mdBook/compare/v0.4.31...v0.4.32)\n\n### Fixed\n- Fixed theme-color meta tag not syncing with the theme.\n  [#2118](https://github.com/rust-lang/mdBook/pull/2118)\n\n### Changed\n- Updated all dependencies.\n  [#2121](https://github.com/rust-lang/mdBook/pull/2121)\n  [#2122](https://github.com/rust-lang/mdBook/pull/2122)\n  [#2123](https://github.com/rust-lang/mdBook/pull/2123)\n  [#2124](https://github.com/rust-lang/mdBook/pull/2124)\n  [#2125](https://github.com/rust-lang/mdBook/pull/2125)\n  [#2126](https://github.com/rust-lang/mdBook/pull/2126)\n\n## mdBook 0.4.31\n[v0.4.30...v0.4.31](https://github.com/rust-lang/mdBook/compare/v0.4.30...v0.4.31)\n\n### Fixed\n- Fixed menu border render flash during page navigation.\n  [#2101](https://github.com/rust-lang/mdBook/pull/2101)\n- Fixed flicker setting sidebar scroll position.\n  [#2104](https://github.com/rust-lang/mdBook/pull/2104)\n- Fixed compile error with proc-macro2 on latest Rust nightly.\n  [#2109](https://github.com/rust-lang/mdBook/pull/2109)\n\n## mdBook 0.4.30\n[v0.4.29...v0.4.30](https://github.com/rust-lang/mdBook/compare/v0.4.29...v0.4.30)\n\n### Added\n- Added support for heading attributes.\n  Attributes are specified in curly braces just after the heading text.\n  An HTML ID can be specified with `#` and classes with `.`.\n  For example: `## My heading {#custom-id .class1 .class2}`\n  [#2013](https://github.com/rust-lang/mdBook/pull/2013)\n- Added support for hidden code lines for languages other than Rust.\n  The `output.html.code.hidelines` table allows you to define the prefix character that will be used to hide code lines based on the language.\n  [#2093](https://github.com/rust-lang/mdBook/pull/2093)\n\n### Fixed\n- Fixed a few minor markdown rendering issues.\n  [#2092](https://github.com/rust-lang/mdBook/pull/2092)\n\n## mdBook 0.4.29\n[v0.4.28...v0.4.29](https://github.com/rust-lang/mdBook/compare/v0.4.28...v0.4.29)\n\n### Changed\n- Built-in fonts are no longer copied when `fonts/fonts.css` is overridden in the theme directory.\n  Additionally, the warning about `copy-fonts` has been removed if `fonts/fonts.css` is specified.\n  [#2080](https://github.com/rust-lang/mdBook/pull/2080)\n- `mdbook init --force` now skips all interactive prompts as intended.\n  [#2057](https://github.com/rust-lang/mdBook/pull/2057)\n- Updated dependencies\n  [#2063](https://github.com/rust-lang/mdBook/pull/2063)\n  [#2086](https://github.com/rust-lang/mdBook/pull/2086)\n  [#2082](https://github.com/rust-lang/mdBook/pull/2082)\n  [#2084](https://github.com/rust-lang/mdBook/pull/2084)\n  [#2085](https://github.com/rust-lang/mdBook/pull/2085)\n\n### Fixed\n- Switched from the `gitignore` library to `ignore`. This should bring some improvements with gitignore handling.\n  [#2076](https://github.com/rust-lang/mdBook/pull/2076)\n\n## mdBook 0.4.28\n[v0.4.27...v0.4.28](https://github.com/rust-lang/mdBook/compare/v0.4.27...v0.4.28)\n\n### Changed\n- The sidebar is now shown on wide screens when localstorage is disabled.\n  [#2017](https://github.com/rust-lang/mdBook/pull/2017)\n- Preprocessors are now run with `mdbook test`.\n  [#1986](https://github.com/rust-lang/mdBook/pull/1986)\n\n### Fixed\n- Fixed regression in 0.4.26 that prevented the title bar from scrolling properly on smaller screens.\n  [#2039](https://github.com/rust-lang/mdBook/pull/2039)\n\n## mdBook 0.4.27\n[v0.4.26...v0.4.27](https://github.com/rust-lang/mdBook/compare/v0.4.26...v0.4.27)\n\n### Changed\n- Reverted the dependency update to the `toml` crate. This was an unintentional breaking change in 0.4.26.\n  [#2021](https://github.com/rust-lang/mdBook/pull/2021)\n\n## mdBook 0.4.26\n[v0.4.25...v0.4.26](https://github.com/rust-lang/mdBook/compare/v0.4.25...v0.4.26)\n\n**The 0.4.26 release has been yanked due to an unintentional breaking change.**\n\n### Changed\n- Removed custom scrollbars for webkit browsers\n  [#1961](https://github.com/rust-lang/mdBook/pull/1961)\n- Updated some dependencies\n  [#1998](https://github.com/rust-lang/mdBook/pull/1998)\n  [#2009](https://github.com/rust-lang/mdBook/pull/2009)\n  [#2011](https://github.com/rust-lang/mdBook/pull/2011)\n- Fonts are now part of the theme.\n  The `output.html.copy-fonts` option has been deprecated.\n  To define custom fonts, be sure to define `theme/fonts.css`.\n  [#1987](https://github.com/rust-lang/mdBook/pull/1987)\n\n### Fixed\n- Fixed overflow viewport issue with mobile Safari\n  [#1994](https://github.com/rust-lang/mdBook/pull/1994)\n\n## mdBook 0.4.25\n[e14d381...1ba74a3](https://github.com/rust-lang/mdBook/compare/e14d381...1ba74a3)\n\n### Fixed\n- Fixed a regression where `mdbook test -L deps path-to-book` would not work.\n  [#1959](https://github.com/rust-lang/mdBook/pull/1959)\n\n## mdBook 0.4.24\n[eb77083...8767ebf](https://github.com/rust-lang/mdBook/compare/eb77083...8767ebf)\n\n### Fixed\n- The precompiled linux-gnu mdbook binary available on [GitHub Releases](https://github.com/rust-lang/mdBook/releases) inadvertently switched to a newer version of glibc. This release goes back to an older version that should be more compatible on older versions of Linux.\n  [#1955](https://github.com/rust-lang/mdBook/pull/1955)\n\n## mdBook 0.4.23\n[678b469...68a75da](https://github.com/rust-lang/mdBook/compare/678b469...68a75da)\n\n### Changed\n- Updated all dependencies\n  [#1951](https://github.com/rust-lang/mdBook/pull/1951)\n  [#1952](https://github.com/rust-lang/mdBook/pull/1952)\n  [#1844](https://github.com/rust-lang/mdBook/pull/1844)\n- Updated minimum Rust version to 1.60.\n  [#1951](https://github.com/rust-lang/mdBook/pull/1951)\n\n### Fixed\n- Fixed a regression where playground code was missing hidden lines, preventing it from compiling correctly.\n  [#1950](https://github.com/rust-lang/mdBook/pull/1950)\n\n## mdBook 0.4.22\n[40c06f5...4844f72](https://github.com/rust-lang/mdBook/compare/40c06f5...4844f72)\n\n### Added\n- Added a `--chapter` option to `mdbook test` to specify a specific chapter to test.\n  [#1741](https://github.com/rust-lang/mdBook/pull/1741)\n- Added CSS styling for `<kbd>` tags.\n  [#1906](https://github.com/rust-lang/mdBook/pull/1906)\n- Added pre-compiled binaries for `x86_64-unknown-linux-musl` and `aarch64-unknown-linux-musl` (see [Releases](https://github.com/rust-lang/mdBook/releases)).\n  [#1862](https://github.com/rust-lang/mdBook/pull/1862)\n- Added `build.extra-watch-dirs` which is an array of additional directories to watch for changes when running `mdbook serve`.\n  [#1884](https://github.com/rust-lang/mdBook/pull/1884)\n\n### Changed\n- Removed the `type=\"text/javascript\"` attribute from `<script>` tags.\n  [#1881](https://github.com/rust-lang/mdBook/pull/1881)\n- Switched to building with Rust Edition 2021.\n  This raises the minimum supported Rust version to 1.56.\n  [#1887](https://github.com/rust-lang/mdBook/pull/1887)\n- When hidden code is hidden, the hidden parts are no longer copied to the clipboard via the copy button.\n  [#1911](https://github.com/rust-lang/mdBook/pull/1911)\n- Various HTML changes and fixes to be more compliant with HTML5.\n  [#1924](https://github.com/rust-lang/mdBook/pull/1924)\n- The theme picker now shows which theme is currently selected.\n  [#1935](https://github.com/rust-lang/mdBook/pull/1935)\n\n### Fixed\n- Avoid blank line at the end of an ACE code block\n  [#1836](https://github.com/rust-lang/mdBook/pull/1836)\n\n\n## mdBook 0.4.21\n[92afe9b...8f01d02](https://github.com/rust-lang/mdBook/compare/92afe9b...8f01d02)\n\n### Fixed\n- Fixed an issue where mdBook would fail to compile with Rust nightly-2022-07-22.\n  [#1861](https://github.com/rust-lang/mdBook/pull/1861)\n\n## mdBook 0.4.20\n[53055e0...da166e0](https://github.com/rust-lang/mdBook/compare/53055e0...da166e0)\n\n### Fixed\n- Fixed a regression in 0.4.19 where inline code would have excessive padding\n  in some situations such as headings.\n  [#1855](https://github.com/rust-lang/mdBook/pull/1855)\n\n## mdBook 0.4.19\n[ae275ad...53055e0](https://github.com/rust-lang/mdBook/compare/ae275ad...53055e0)\n\n### Added\n- The `serve` command now supports HEAD requests.\n  [#1825](https://github.com/rust-lang/mdBook/pull/1825)\n\n### Changed\n- An error is now generated when a custom theme directory does not exist.\n  [#1791](https://github.com/rust-lang/mdBook/pull/1791)\n- Very wide tables now have independent horizontal scrolling so that scrolling\n  to see the rest of the table will not scroll the entire page.\n  [#1617](https://github.com/rust-lang/mdBook/pull/1617)\n- The buttons on code blocks are now only shown when the mouse cursor hovers\n  over them (or tapped on mobile). There is also some extra spacing to reduce\n  the overlap with the code.\n  [#1806](https://github.com/rust-lang/mdBook/pull/1806)\n- The first chapter always generates an `index.html` file. Previously it would\n  only generate the index file for prefix chapters.\n  [#1829](https://github.com/rust-lang/mdBook/pull/1829)\n\n### Fixed\n- `mdbook serve --open` now properly handles the case if the first chapter is a draft.\n  [#1714](https://github.com/rust-lang/mdBook/pull/1714)\n  [#1830](https://github.com/rust-lang/mdBook/pull/1830)\n- Very long words (over 80 characters) are no longer indexed to avoid a stack overflow.\n  [#1833](https://github.com/rust-lang/mdBook/pull/1833)\n\n## mdBook 0.4.18\n[981b79b...ae275ad](https://github.com/rust-lang/mdBook/compare/981b79b...ae275ad)\n\n### Fixed\n- Fixed rendering of SUMMARY links that contain markdown escapes or other\n  markdown elements.\n  [#1785](https://github.com/rust-lang/mdBook/pull/1785)\n\n## mdBook 0.4.17\n[a5fddfa...981b79b](https://github.com/rust-lang/mdBook/compare/a5fddfa...981b79b)\n\n### Fixed\n- Fixed parsing of `output.html.print` configuration table.\n  [#1775](https://github.com/rust-lang/mdBook/pull/1775)\n\n## mdBook 0.4.16\n[68a5c09...a5fddfa](https://github.com/rust-lang/mdBook/compare/68a5c09...a5fddfa)\n\n### Added\n- Added `output.html.print.page-break` config option to control whether or not\n  there is a page break between chapters in the print output.\n  [#1728](https://github.com/rust-lang/mdBook/pull/1728)\n- Added `output.html.playground.runnable` config option to globally disable\n  the run button in code blocks.\n  [#1546](https://github.com/rust-lang/mdBook/pull/1546)\n\n### Changed\n- The `mdbook serve` live reload websocket now uses the protocol, host, and\n  port of the current page, allowing access through a proxy.\n  [#1771](https://github.com/rust-lang/mdBook/pull/1771)\n- The 404 not-found page now includes the books title in the HTML title tag.\n  [#1693](https://github.com/rust-lang/mdBook/pull/1693)\n- Migrated to clap 3.0 which handles CLI option parsing.\n  [#1731](https://github.com/rust-lang/mdBook/pull/1731)\n\n### Fixed\n- Minor fixes to the markdown parser.\n  [#1729](https://github.com/rust-lang/mdBook/pull/1729)\n- Fixed incorrect parsing in `SUMMARY.md` when it didn't start with a title.\n  [#1744](https://github.com/rust-lang/mdBook/pull/1744)\n- Fixed duplicate anchor IDs for links in search results.\n  [#1749](https://github.com/rust-lang/mdBook/pull/1749)\n\n## mdBook 0.4.15\n[5eb7d46...68a5c09](https://github.com/rust-lang/mdBook/compare/5eb7d46...68a5c09)\n\n### Changed\n- Major update to expand the documentation located at <https://rust-lang.github.io/mdBook/>.\n  [#1709](https://github.com/rust-lang/mdBook/pull/1709)\n  [#1710](https://github.com/rust-lang/mdBook/pull/1710)\n- Updated the markdown parser with various fixes for common-mark compliance.\n  [#1712](https://github.com/rust-lang/mdBook/pull/1712)\n\n## mdBook 0.4.14\n[ffa8284...c9b6be8](https://github.com/rust-lang/mdBook/compare/ffa8284...c9b6be8)\n\n### Added\n- The 2021 Rust edition option has been stabilized.\n  [#1642](https://github.com/rust-lang/mdBook/pull/1642)\n\n### Changed\n- Header anchors no longer include any HTML tags. Previously only a small\n  subset were excluded.\n  [#1683](https://github.com/rust-lang/mdBook/pull/1683)\n- Deprecated the google-analytics option. Books using this option should place\n  the appropriate code in the `theme/head.hbs` file instead.\n  [#1675](https://github.com/rust-lang/mdBook/pull/1675)\n\n### Fixed\n- Updated the markdown parser which brings in a few small fixes and removes\n  the custom smart quote handling.\n  [#1668](https://github.com/rust-lang/mdBook/pull/1668)\n- Fixed iOS Safari enlarging text when going into landscape mode.\n  [#1685](https://github.com/rust-lang/mdBook/pull/1685)\n\n## mdBook 0.4.13\n[e6629cd...f55028b](https://github.com/rust-lang/mdBook/compare/e6629cd...f55028b)\n\n### Added\n\n- Added the ability to specify the preprocessor order.\n  [#1607](https://github.com/rust-lang/mdBook/pull/1607)\n\n### Fixed\n\n- Include chapters with no headers in the search index\n  [#1637](https://github.com/rust-lang/mdBook/pull/1637)\n- Switched to the `opener` crate for opening a web browser, which should fix\n  some issues with blocking.\n  [#1656](https://github.com/rust-lang/mdBook/pull/1656)\n- Fixed clicking the border of the theme switcher breaking the theme selection.\n  [#1651](https://github.com/rust-lang/mdBook/pull/1651)\n\n## mdBook 0.4.12\n[14add9c...8b4e488](https://github.com/rust-lang/mdBook/compare/14add9c...8b4e488)\n\n### Changed\n- Reverted the change to update to highlight.js 11, as it broke hidden code lines.\n  [#1597](https://github.com/rust-lang/mdBook/pull/1621)\n \n## mdBook 0.4.11\n[e440094...2cf00d0](https://github.com/rust-lang/mdBook/compare/e440094...2cf00d0)\n\n### Added\n- Added support for Rust 2021 edition.\n  [#1596](https://github.com/rust-lang/mdBook/pull/1596)\n- Added `mdbook completions` subcommand which provides shell completions.\n  [#1425](https://github.com/rust-lang/mdBook/pull/1425)\n- Added `--title` and `--ignore` flags to `mdbook init` to avoid the\n  interactive input.\n  [#1559](https://github.com/rust-lang/mdBook/pull/1559)\n\n### Changed\n- If running a Rust example does not have any output, it now displays the text\n  \"No output\" instead of not showing anything.\n  [#1599](https://github.com/rust-lang/mdBook/pull/1599)\n- Code block language tags can now be separated by space or tab (along with\n  commas) to match the behavior of other sites like GitHub and rustdoc.\n  [#1469](https://github.com/rust-lang/mdBook/pull/1469)\n- Updated `warp` (the web server) to the latest version.\n  This also updates the minimum supported Rust version to 1.46.\n  [#1612](https://github.com/rust-lang/mdBook/pull/1612)\n- Updated to highlight.js 11. This has various highlighting improvements.\n  [#1597](https://github.com/rust-lang/mdBook/pull/1597)\n\n### Fixed\n- Inline code blocks inside a header are no longer highlighted when\n  `output.html.playground.editable` is `true`.\n  [#1613](https://github.com/rust-lang/mdBook/pull/1613)\n\n## mdBook 0.4.10\n[2f7293a...dc2062a](https://github.com/rust-lang/mdBook/compare/2f7293a...dc2062a)\n\n### Changed\n- Reverted breaking change in 0.4.9 that removed the `__non_exhaustive` marker\n  on the `Book` struct.\n  [#1572](https://github.com/rust-lang/mdBook/pull/1572)\n- Updated handlebars to 4.0.\n  [#1550](https://github.com/rust-lang/mdBook/pull/1550)\n- Removed the `chapter_begin` id on the print page's chapter separators.\n  [#1541](https://github.com/rust-lang/mdBook/pull/1541)\n\n## mdBook 0.4.9\n[7e01cf9...d325c60](https://github.com/rust-lang/mdBook/compare/7e01cf9...d325c60)\n\n### Changed\n- Updated all dependencies and raised the minimum Rust version to 1.42.\n  [#1528](https://github.com/rust-lang/mdBook/pull/1528)\n- Added more detail to error message when a preprocessor fails.\n  [#1526](https://github.com/rust-lang/mdBook/pull/1526)\n- Set max-width of HTML video tags to 100% to match img tags.\n  [#1542](https://github.com/rust-lang/mdBook/pull/1542)\n\n### Fixed\n- Type errors when parsing `book.toml` are no longer ignored.\n  [#1539](https://github.com/rust-lang/mdBook/pull/1539)\n- Better handling if `mdbook serve` fails to start the http server.\n  [#1555](https://github.com/rust-lang/mdBook/pull/1555)\n- Fixed the path for `edit-url-template` if the book used a source directory\n  other than `src`.\n  [#1554](https://github.com/rust-lang/mdBook/pull/1554)\n\n## mdBook 0.4.8\n[fcceee4...b592b10](https://github.com/rust-lang/mdBook/compare/fcceee4...b592b10)\n\n### Added\n- Added the option `output.html.edit-url-template` which can be a URL which is\n  linked on each page to direct the user to a site (such as GitHub) where the\n  user can directly suggest an edit for the page they are currently reading.\n  [#1506](https://github.com/rust-lang/mdBook/pull/1506)\n\n### Changed\n- Printed output now includes a page break between chapters.\n  [#1485](https://github.com/rust-lang/mdBook/pull/1485)\n\n### Fixed\n- HTML, such as HTML comments, is now ignored if it appears above the title line\n  in `SUMMARY.md`.\n  [#1437](https://github.com/rust-lang/mdBook/pull/1437)\n\n## mdBook 0.4.7\n[9a9eb01...c83bbd6](https://github.com/rust-lang/mdBook/compare/9a9eb01...c83bbd6)\n\n### Changed\n- Updated shlex parser to fix a minor parsing issue (used by the\n  preprocessor/backend custom command config).\n  [#1471](https://github.com/rust-lang/mdBook/pull/1471)\n- Enhanced text contrast of `light` theme to improve accessibility.\n  [#1470](https://github.com/rust-lang/mdBook/pull/1470)\n\n### Fixed\n- Fixed some issues with fragment scrolling and linking.\n  [#1463](https://github.com/rust-lang/mdBook/pull/1463)\n\n## mdBook 0.4.6\n[eaa6914...1a0c296](https://github.com/rust-lang/mdBook/compare/eaa6914...1a0c296)\n\n### Changed\n- The chapter name is now included in the search breadcrumbs.\n  [#1389](https://github.com/rust-lang/mdBook/pull/1389)\n- Pressing Escape will remove the `?highlight` argument from the URL.\n  [#1427](https://github.com/rust-lang/mdBook/pull/1427)\n- `mdbook init --theme` will now place the theme in the root of the book\n  directory instead of in the `src` directory.\n  [#1432](https://github.com/rust-lang/mdBook/pull/1432)\n- A custom renderer that sets the `command` to a relative path now interprets\n  the relative path relative to the book root. Previously it was inconsistent\n  based on the platform (either relative to the current directory, or relative\n  to the renderer output directory). Paths relative to the output directory\n  are still supported with a deprecation warning.\n  [#1418](https://github.com/rust-lang/mdBook/pull/1418)\n- The `theme` directory in the config is now interpreted as relative to the\n  book root, instead of the current directory.\n  [#1405](https://github.com/rust-lang/mdBook/pull/1405)\n- Handle UTF-8 BOM for chapter sources.\n  [#1285](https://github.com/rust-lang/mdBook/pull/1285)\n- Removed extra whitespace added to `{{#playground}}` snippets.\n  [#1375](https://github.com/rust-lang/mdBook/pull/1375)\n\n### Fixed\n- Clicking on a search result with multiple search words will now correctly\n  highlight all of the words.\n  [#1426](https://github.com/rust-lang/mdBook/pull/1426)\n- Properly handle `<` and `>` characters in the table of contents.\n  [#1376](https://github.com/rust-lang/mdBook/pull/1376)\n- Fixed to properly serialize the `build` table in the config, which prevented\n  setting it in the API.\n  [#1378](https://github.com/rust-lang/mdBook/pull/1378)\n\n## mdBook 0.4.5\n[eaa6914...f66df09](https://github.com/rust-lang/mdBook/compare/eaa6914...f66df09)\n\n### Fixed\n\n- Fixed XSS in the search page.\n  [CVE-2020-26297](https://groups.google.com/g/rustlang-security-announcements/c/3-sO6of29O0)\n  [648c9ae](https://github.com/rust-lang/mdBook/commit/648c9ae772bec83f0a5954d17b4287d5bb1d6606)\n\n## mdBook 0.4.4\n[4df9ec9...01836ba](https://github.com/rust-lang/mdBook/compare/4df9ec9...01836ba)\n\n### Added\n- Added the `output.html.print.enable` configuration value to disable the\n  \"print\" page.\n  [#1169](https://github.com/rust-lang/mdBook/pull/1169)\n- Added a list of supported languages for syntax-highlighting to the\n  documentation.\n  [#1345](https://github.com/rust-lang/mdBook/pull/1345)\n\n### Fixed\n- Now supports symbolic links for files in the `src` directory.\n  [#1323](https://github.com/rust-lang/mdBook/pull/1323)\n\n## mdBook 0.4.3\n[9278b83...4df9ec9](https://github.com/rust-lang/mdBook/compare/9278b83...4df9ec9)\n\n### Added\n- Added `output.html.cname` option to emit a `CNAME` file which is used by\n  GitHub Pages to know which domain is being used.\n  [#1311](https://github.com/rust-lang/mdBook/pull/1311)\n\n### Changed\n- `mdbook test` no longer stops on the first test failure, but instead will\n  run all the tests.\n  [#1313](https://github.com/rust-lang/mdBook/pull/1313)\n- Removed the `local` font source for Source Code Pro, as the locally\n  installed font may not render properly on FireFox on macOS.\n  [#1307](https://github.com/rust-lang/mdBook/pull/1307)\n\n### Fixed\n- Added newline to end of `.nojekyll` file.\n  [#1310](https://github.com/rust-lang/mdBook/pull/1310)\n- Fixed missing space before draft chapter titles.\n  [#1309](https://github.com/rust-lang/mdBook/pull/1309)\n\n## mdBook 0.4.2\n[649f355...9278b83](https://github.com/rust-lang/mdBook/compare/649f355...9278b83)\n\n### Changed\n- The \"show hidden lines\" icon has changed from the \"expand\" icon to an \"eye\".\n  [#1281](https://github.com/rust-lang/mdBook/pull/1281)\n- Updated highlight.js. This adds several languages: c, c-like (effectively\n  cpp), csharp (replaces cs), kotlin, less, lua, php-template, plaintext,\n  python-repl, r, scss, typescript.\n  [#1277](https://github.com/rust-lang/mdBook/pull/1277)\n\n### Fixed\n- Fixed SUMMARY links that contained newlines.\n  [#1291](https://github.com/rust-lang/mdBook/pull/1291)\n- Fixed SUMMARY links that contain `%20` spaces.\n  [#1293](https://github.com/rust-lang/mdBook/pull/1293)\n- Fixed favicon so that if only the png or svg is overridden, the other is not\n  automatically included in the `<link>` tag.\n  [#1272](https://github.com/rust-lang/mdBook/pull/1272)\n\n## mdBook 0.4.1\n[d4df7e7...649f355](https://github.com/rust-lang/mdBook/compare/d4df7e7...649f355)\n\n### Changed\n- Removed several outdated dev-dependencies.\n  [#1267](https://github.com/rust-lang/mdBook/pull/1267)\n\n### Fixed\n- Fixed sidebar scrolling if the book includes part titles.\n  [#1265](https://github.com/rust-lang/mdBook/pull/1265)\n- Don't include the default favicon if only one of the PNG or SVG is overridden.\n  [#1266](https://github.com/rust-lang/mdBook/pull/1266)\n\n## mdBook 0.4.0\n[99ecd4f...d4df7e7](https://github.com/rust-lang/mdBook/compare/99ecd4f...d4df7e7)\n\n### Breaking Changes\n- Several of the changes in the release have altered the public API of the\n  mdbook library.\n- Many dependencies have been updated or replaced.\n  This also removes the `--websocket-hostname` and `--websocket-port` from\n  the `serve` command.\n  [#1211](https://github.com/rust-lang/mdBook/pull/1211)\n- A new \"404\" page is now automatically rendered. This requires knowledge of\n  the base URL of your site to work properly. If you decide to use this as\n  your 404 page, you should set the `site-url` setting in the book\n  configuration so mdbook can generate the links correctly. Alternatively you\n  can disable the 404 page generation, or set up your own 404 handling in your\n  web server.\n  [#1221](https://github.com/rust-lang/mdBook/pull/1221)\n- The `debug` and `output` features have been removed as they were unused.\n  [#1211](https://github.com/rust-lang/mdBook/pull/1211)\n- If you are using customized themes, you may want to consider setting the\n  `preferred-dark-theme` config setting, as it now defaults to \"navy\".\n  [#1199](https://github.com/rust-lang/mdBook/pull/1199)\n- \"Playpen\" has been renamed to \"playground\". This is generally backwards\n  compatible for users, but `{{#playpen}}` will now display warnings. This may\n  impact books that have modified the \"playpen\" elements in the theme.\n  [#1241](https://github.com/rust-lang/mdBook/pull/1241)\n- If a renderer is not installed, it is now treated as an error. If you want\n  the old behavior of ignoring missing renderers, set the `optional` setting\n  for that renderer.\n  [#1122](https://github.com/rust-lang/mdBook/pull/1122)\n- If you have a custom favicon, you may need to look into adding an SVG\n  version, otherwise the default SVG icon will be displayed.\n  [#1230](https://github.com/rust-lang/mdBook/pull/1230)\n\n### Added\n- Added a new `[rust]` configuration section to `book.toml`, which allows\n  setting the default edition with `edition = \"2018\"`.\n  [#1163](https://github.com/rust-lang/mdBook/pull/1163)\n- Renderers can now be marked as `optional`, so that they will be ignored if\n  the renderer is not installed.\n  [#1122](https://github.com/rust-lang/mdBook/pull/1122)\n- Added `head.hbs` to allow adding content to the `<head>` section in HTML.\n  [#1206](https://github.com/rust-lang/mdBook/pull/1206)\n- Added \"draft chapters\". These are chapters listed without a link to indicate\n  content yet to be written.\n  [#1153](https://github.com/rust-lang/mdBook/pull/1153)\n- Added \"parts\" to split a book into different sections. Headers can be added\n  to `SUMMARY.md` to signify different sections.\n  [#1171](https://github.com/rust-lang/mdBook/pull/1171)\n- Added generation of a \"404\" page for handling missing pages and broken links.\n  [#1221](https://github.com/rust-lang/mdBook/pull/1221)\n- Added configuration section for specifying URL redirects.\n  [#1237](https://github.com/rust-lang/mdBook/pull/1237)\n- Added an SVG favicon that works with light and dark colors schemes.\n  [#1230](https://github.com/rust-lang/mdBook/pull/1230)\n\n### Changed\n- Changed default Rust attribute of `allow(unused_variables)` to `allow(unused)`.\n  [#1195](https://github.com/rust-lang/mdBook/pull/1195)\n- Fonts are now served locally instead of from the Google Fonts CDN. The\n  `copy-fonts` option was added to disable this if you want to supply your own\n  fonts.\n  [#1188](https://github.com/rust-lang/mdBook/pull/1188)\n- Switched the built-in webserver for the `serve` command to a new\n  implementation. This results in some internal differences in how websockets\n  are handled, which removes the separate websocket options. This should also\n  make it easier to serve multiple books at once.\n  [#1211](https://github.com/rust-lang/mdBook/pull/1211)\n- The default dark theme is now \"navy\".\n  [#1199](https://github.com/rust-lang/mdBook/pull/1199)\n- \"Playpen\" has been renamed to \"playground\", matching the actual name of the\n  service which was renamed many years ago.\n  [#1241](https://github.com/rust-lang/mdBook/pull/1241)\n\n### Fixed\n- Links with the `+` symbol should now work.\n  [#1208](https://github.com/rust-lang/mdBook/pull/1208)\n- The `MDBOOK_BOOK` environment variable now correctly allows overriding the\n  entire book configuration.\n  [#1207](https://github.com/rust-lang/mdBook/pull/1207)\n- The sidebar can no longer be dragged outside of the window.\n  [#1229](https://github.com/rust-lang/mdBook/pull/1229)\n- Hide the Rust Playground \"play\" button for `no_run` code samples.\n  [#1249](https://github.com/rust-lang/mdBook/pull/1249)\n- Fixed the `--dest-dir` command-line option for the `serve` and `watch`\n  commands.\n  [#1228](https://github.com/rust-lang/mdBook/pull/1228)\n- Hotkey handlers are now disabled in `text` input fields (for example, typing\n  `S` in a custom text input field).\n  [#1244](https://github.com/rust-lang/mdBook/pull/1244)\n\n## mdBook 0.3.7\n[88684d8...99ecd4f](https://github.com/rust-lang/mdBook/compare/88684d8...99ecd4f)\n\n### Changed\n- Code spans in headers are no longer highlighted as code.\n  [#1162](https://github.com/rust-lang/mdBook/pull/1162)\n- The sidebar will now scroll the activate page to the middle instead of the top.\n  [#1161](https://github.com/rust-lang/mdBook/pull/1161)\n- Reverted change to reject build output within the `src` directory, and\n  instead add a check that prevents infinite copies.\n  [#1181](https://github.com/rust-lang/mdBook/pull/1181)\n  [#1026](https://github.com/rust-lang/mdBook/pull/1026)\n\n### Fixed\n- Fixed sidebar line-height jumping for collapsed chapters.\n  [#1182](https://github.com/rust-lang/mdBook/pull/1182)\n- Fixed theme selector focus.\n  [#1170](https://github.com/rust-lang/mdBook/pull/1170)\n\n## mdBook 0.3.6\n[efdb832...88684d8](https://github.com/rust-lang/mdBook/compare/efdb832...88684d8)\n\n### Added\n- `MDBook::execute_build_process` is now publicly accessible in the API so\n  that plugins can more easily initiate the build process.\n  [#1099](https://github.com/rust-lang/mdBook/pull/1099)\n\n### Changed\n- Use a different color for Ayu theme's highlighting for Rust attributes (uses\n  a bright color instead of the comment color).\n  [#1133](https://github.com/rust-lang/mdBook/pull/1133)\n- Adjusted spacing of sidebar entries.\n  [#1137](https://github.com/rust-lang/mdBook/pull/1137)\n- Slightly adjusted line-height of `<p>`, `<ul>`, and `<ol>`.\n  [#1136](https://github.com/rust-lang/mdBook/pull/1136)\n- Handlebars updated to 3.0.\n  [#1130](https://github.com/rust-lang/mdBook/pull/1130)\n\n### Fixed\n- Fix an issue with sidebar scroll position on reload.\n  [#1108](https://github.com/rust-lang/mdBook/pull/1108)\n- `mdbook serve` will retain the current scroll position when the page is reloaded.\n  [#1097](https://github.com/rust-lang/mdBook/pull/1097)\n- Fixed the page name if the book didn't have a title to not be prefixed with ` - `.\n  [#1145](https://github.com/rust-lang/mdBook/pull/1145)\n- HTML attributes `rel=next` and `rel=previous` are now supported in \"wide\"\n  mode (previously they were only set in narrow mode).\n  [#1150](https://github.com/rust-lang/mdBook/pull/1150)\n- Prevent recursive copies when the destination directory is contained in the\n  source directory.\n  [#1135](https://github.com/rust-lang/mdBook/pull/1135)\n- Adjusted the menu bar animation to not immediately obscure the top content.\n  [#989](https://github.com/rust-lang/mdBook/pull/989)\n- Fix for comments in SUMMARY.md that appear between items.\n  [#1167](https://github.com/rust-lang/mdBook/pull/1167)\n\n## mdBook 0.3.5\n[6e0d0fa...efdb832](https://github.com/rust-lang/mdBook/compare/6e0d0fa...efdb832)\n\n### Changed\n- The `default-theme` config setting is now case-insensitive.\n  [#1079](https://github.com/rust-lang/mdBook/pull/1079)\n\n### Fixed\n- Fixed `#` hidden Rust code lines not rendering properly.\n  [#1088](https://github.com/rust-lang/mdBook/pull/1088)\n- Updated pulldown-cmark to 0.6.1, fixing several issues.\n  [#1021](https://github.com/rust-lang/mdBook/pull/1021)\n\n## mdBook 0.3.4\n[e5f77aa...6e0d0fa](https://github.com/rust-lang/mdBook/compare/e5f77aa...6e0d0fa)\n\n### Changed\n- Switch to relative `rem` font sizes from `px`.\n  [#894](https://github.com/rust-lang/mdBook/pull/894)\n- Migrated repository to https://github.com/rust-lang/mdBook/\n  [#1083](https://github.com/rust-lang/mdBook/pull/1083)\n\n## mdBook 0.3.3\n[2b649fe...e5f77aa](https://github.com/rust-lang/mdBook/compare/2b649fe...e5f77aa)\n\n### Changed\n- Improvements to the automatic dark theme selection.\n  [#1069](https://github.com/rust-lang/mdBook/pull/1069)\n- Fragment links now prevent scrolling the header behind the menu bar.\n  [#1077](https://github.com/rust-lang/mdBook/pull/1077)\n\n### Fixed\n- Fixed error when building a book that has a spacer immediately after the\n  first chapter.\n  [#1075](https://github.com/rust-lang/mdBook/pull/1075)\n\n## mdBook 0.3.2\n[9cd47eb...2b649fe](https://github.com/rust-lang/mdBook/compare/9cd47eb...2b649fe)\n\n### Added\n- Added a markdown renderer, which is off by default. This may be useful for\n  debugging preprocessors.\n  [#1018](https://github.com/rust-lang/mdBook/pull/1018)\n- Code samples may now include line numbers with the\n  `output.html.playpen.line-numbers` configuration value.\n  [#1035](https://github.com/rust-lang/mdBook/pull/1035)\n- The `watch` and `serve` commands will now ignore files listed in\n  `.gitignore`.\n  [#1044](https://github.com/rust-lang/mdBook/pull/1044)\n- Added automatic dark-theme detection based on the CSS `prefers-color-scheme`\n  feature. This may be enabled by setting `output.html.preferred-dark-theme`\n  to your preferred dark theme.\n  [#1037](https://github.com/rust-lang/mdBook/pull/1037)\n- Added `rustdoc_include` preprocessor. This makes it easier to include\n  portions of an external Rust source file. The rest of the file is hidden,\n  but the user may expand it to see the entire file, and will continue to work\n  with `mdbook test`.\n  [#1003](https://github.com/rust-lang/mdBook/pull/1003)\n- Added Ctrl-Enter shortcut to the playpen editor to automatically run the\n  sample.\n  [#1066](https://github.com/rust-lang/mdBook/pull/1066)\n- Added `output.html.playpen.copyable` configuration option to disable\n  the copy button.\n  [#1050](https://github.com/rust-lang/mdBook/pull/1050)\n- Added ability to dynamically expand and fold sections within the sidebar.\n  See the `output.html.fold` configuration to enable this feature.\n  [#1027](https://github.com/rust-lang/mdBook/pull/1027)\n\n### Changed\n- Use standard `scrollbar-color` CSS along with webkit extension\n  [#816](https://github.com/rust-lang/mdBook/pull/816)\n- The renderer build directory is no longer deleted before the renderer is\n  run. This allows a backend to cache results between runs.\n  [#985](https://github.com/rust-lang/mdBook/pull/985)\n- Next/prev links now highlight on hover to indicate it is clickable.\n  [#994](https://github.com/rust-lang/mdBook/pull/994)\n- Increase padding of table headers.\n  [#824](https://github.com/rust-lang/mdBook/pull/824)\n- Errors in `[output.html]` config are no longer ignored.\n  [#1033](https://github.com/rust-lang/mdBook/pull/1033)\n- Updated highlight.js for syntax highlighting updates (primarily to add\n  async/await to Rust highlighting).\n  [#1041](https://github.com/rust-lang/mdBook/pull/1041)\n- Raised minimum supported rust version to 1.35.\n  [#1003](https://github.com/rust-lang/mdBook/pull/1003)\n- Hidden code lines are no longer dynamically removed via JavaScript, but\n  instead managed with CSS.\n  [#846](https://github.com/rust-lang/mdBook/pull/846)\n  [#1065](https://github.com/rust-lang/mdBook/pull/1065)\n- Changed the default font set for the ACE editor, giving preference to\n  \"Source Code Pro\".\n  [#1062](https://github.com/rust-lang/mdBook/pull/1062)\n- Windows 32-bit releases are no longer published.\n  [#1071](https://github.com/rust-lang/mdBook/pull/1071)\n\n### Fixed\n- Fixed sidebar auto-scrolling.\n  [#1052](https://github.com/rust-lang/mdBook/pull/1052)\n- Fixed error message when running `clean` multiple times.\n  [#1055](https://github.com/rust-lang/mdBook/pull/1055)\n- Actually fix the \"next\" link on index.html. The previous fix didn't work.\n  [#1005](https://github.com/rust-lang/mdBook/pull/1005)\n- Stop using `inline-block` for `inline code`, fixing selection highlighting\n  and some rendering issues.\n  [#1058](https://github.com/rust-lang/mdBook/pull/1058)\n- Fix header auto-hide on browsers with momentum scrolling that allows\n  negative `scrollTop`.\n  [#1070](https://github.com/rust-lang/mdBook/pull/1070)\n\n## mdBook 0.3.1\n[69a08ef...9cd47eb](https://github.com/rust-lang/mdBook/compare/69a08ef...9cd47eb)\n\n### Added\n- 🔥 Added ability to include files using anchor points instead of line numbers.\n  [#851](https://github.com/rust-lang/mdBook/pull/851)\n- Added `language` configuration value to set the language of the book, which\n  will affect things like the `<html lang=\"en\">` tag.\n  [#941](https://github.com/rust-lang/mdBook/pull/941)\n\n### Changed\n- Updated to handlebars 2.0.\n  [#977](https://github.com/rust-lang/mdBook/pull/977)\n\n### Fixed\n- Fixed memory leak warning.\n  [#967](https://github.com/rust-lang/mdBook/pull/967)\n- Fix more print.html links.\n  [#963](https://github.com/rust-lang/mdBook/pull/963)\n- Fixed crash on some unicode input.\n  [#978](https://github.com/rust-lang/mdBook/pull/978)\n\n## mdBook 0.3.0\n[6cbc41d...69a08ef](https://github.com/rust-lang/mdBook/compare/6cbc41d...69a08ef)\n\n### Added\n- Added ability to resize the sidebar.\n  [#849](https://github.com/rust-lang/mdBook/pull/849)\n- Added `load_with_config_and_summary` function to `MDBook` to be able to\n  build a book with a custom `Summary`.\n  [#883](https://github.com/rust-lang/mdBook/pull/883)\n- Set `noindex` on `print.html` page to prevent robots from indexing it.\n  [#844](https://github.com/rust-lang/mdBook/pull/844)\n- Added support for ~~strikethrough~~ and GitHub-style tasklists.\n  [#952](https://github.com/rust-lang/mdBook/pull/952)\n\n### Changed\n- Command-line help output is now colored.\n  [#861](https://github.com/rust-lang/mdBook/pull/861)\n- The build directory is now deleted before rendering starts, instead of after\n  if finishes.\n  [#878](https://github.com/rust-lang/mdBook/pull/878)\n- Removed dependency on `same-file` crate.\n  [#903](https://github.com/rust-lang/mdBook/pull/903)\n- 💥 Renamed `with_preprecessor` to `with_preprocessor`.\n  [#906](https://github.com/rust-lang/mdBook/pull/906)\n- Updated ACE editor to 1.4.4, should remove a JavaScript console warning.\n  [#935](https://github.com/rust-lang/mdBook/pull/935)\n- Dependencies have been updated.\n  [#934](https://github.com/rust-lang/mdBook/pull/934)\n  [#945](https://github.com/rust-lang/mdBook/pull/945)\n- Highlight.js has been updated. This fixes some TOML highlighting, and adds\n  Julia support.\n  [#942](https://github.com/rust-lang/mdBook/pull/942)\n- 🔥 Updated to pulldown-cmark 0.5. This may have significant changes to the\n  formatting of existing books, as the newer version has more accurate\n  interpretation of the CommonMark spec and a large number of bug fixes and\n  changes.\n  [#898](https://github.com/rust-lang/mdBook/pull/898)\n- The `diff` language should now highlight correctly.\n  [#943](https://github.com/rust-lang/mdBook/pull/943)\n- Make the blank region of a header not clickable.\n  [#948](https://github.com/rust-lang/mdBook/pull/948)\n- Rustdoc tests now use the preprocessed content instead of the raw,\n  unpreprocessed content.\n  [#891](https://github.com/rust-lang/mdBook/pull/891)\n\n### Fixed\n- Fixed file change detection so that `mdbook serve` only reloads once when\n  multiple files are changed at once.\n  [#870](https://github.com/rust-lang/mdBook/pull/870)\n- Fixed on-hover color highlighting for links in sidebar.\n  [#834](https://github.com/rust-lang/mdBook/pull/834)\n- Fixed loss of focus when clicking the \"Copy\" button in code blocks.\n  [#867](https://github.com/rust-lang/mdBook/pull/867)\n- Fixed incorrectly stripping the path for `additional-js` files.\n  [#796](https://github.com/rust-lang/mdBook/pull/796)\n- Fixed color of `code spans` that are links.\n  [#905](https://github.com/rust-lang/mdBook/pull/905)\n- Fixed \"next\" navigation on index.html.\n  [#916](https://github.com/rust-lang/mdBook/pull/916)\n- Fixed keyboard chapter navigation for `file` urls.\n  [#915](https://github.com/rust-lang/mdBook/pull/915)\n- Fixed bad wrapping for inline code on some browsers.\n  [#818](https://github.com/rust-lang/mdBook/pull/818)\n- Properly load an existing `SUMMARY.md` in `mdbook init`.\n  [#841](https://github.com/rust-lang/mdBook/pull/841)\n- Fixed some broken links in `print.html`.\n  [#871](https://github.com/rust-lang/mdBook/pull/871)\n- The Rust Playground link now supports the 2018 edition.\n  [#946](https://github.com/rust-lang/mdBook/pull/946)\n\n## mdBook 0.2.3 (2018-01-18)\n[2c20c99...6cbc41d](https://github.com/rust-lang/mdBook/compare/2c20c99...6cbc41d)\n\n### Added\n- Added an optional button to the top of the page which will link to a git\n  repository. Use the `git-repository-url` and `git-repository-icon` options\n  in the `[output.html]` section to enable it and set its appearance.\n  [#802](https://github.com/rust-lang/mdBook/pull/802)\n- Added a `default-theme` option to the `[output.html]` section.\n  [#804](https://github.com/rust-lang/mdBook/pull/804)\n\n### Changed\n- 💥 Header ID anchors no longer add an arbitrary `a` character for headers\n  that start with a non-ascii-alphabetic character.\n  [#788](https://github.com/rust-lang/mdBook/pull/788)\n\n### Fixed\n- Fix websocket hostname usage\n  [#865](https://github.com/rust-lang/mdBook/pull/865)\n- Fixing links in print.html\n  [#866](https://github.com/rust-lang/mdBook/pull/866)\n\n## mdBook 0.2.2 (2018-10-19)\n[7e2e095...2c20c99](https://github.com/rust-lang/mdBook/compare/7e2e095...2c20c99)\n\n### Added\n- 🎉 Process-based custom preprocessors. See [the\n  docs](https://rust-lang.github.io/mdBook/for_developers/preprocessors.html)\n  for more.\n  [#792](https://github.com/rust-lang/mdBook/pull/792)\n\n- 🎉 Configurable preprocessors.\n\n  Added `build.use-default-preprocessors` boolean TOML key to allow disabling\n  the built-in `links` and `index` preprocessors.\n\n  Added `[preprocessor]` TOML tables to configure each preprocessor.\n\n  Specifying `[preprocessor.links]` or `[preprocessor.index]` will enable the\n  respective built-in preprocessor if `build.use-default-preprocessors` is\n  `false`.\n\n  Added `fn supports_renderer(&self, renderer: &str) -> bool` to the\n  `Preprocessor` trait to specify if the preprocessor supports the given\n  renderer. The default implementation always returns `true`.\n\n  `Preprocessor::run` now takes a book by value instead of a mutable\n  reference. It should return a `Book` value with the intended modifications.\n\n  Added `PreprocessorContext::renderer` to indicate the renderer being used.\n\n  [#658](https://github.com/rust-lang/mdBook/pull/658)\n  [#787](https://github.com/rust-lang/mdBook/pull/787)\n\n### Fixed\n- Fix paths to additional CSS and JavaScript files\n  [#777](https://github.com/rust-lang/mdBook/pull/777)\n- Ensure section numbers are correctly incremented after a horizontal\n  separator\n  [#790](https://github.com/rust-lang/mdBook/pull/790)\n\n## mdBook 0.2.1 (2018-08-22)\n[91ffca1...7e2e095](https://github.com/rust-lang/mdBook/compare/91ffca1...7e2e095)\n\n### Changed\n- Update to handlebars-rs 1.0\n  [#761](https://github.com/rust-lang/mdBook/pull/761)\n\n### Fixed\n- Fix table colors, broken by Stylus -> CSS transition\n  [#765](https://github.com/rust-lang/mdBook/pull/765)\n\n## mdBook 0.2.0 (2018-08-02)\n\n### Changed\n- 💥 This release changes how links are handled in mdBook. Previously, relative\n  links were interpreted relative to the book's root. In `0.2.0`+ links are\n  relative to the page they are in, and use the `.md` extension. This has [several\n  advantages](https://github.com/rust-lang/mdBook/pull/603#issue-166701447),\n  such as making links work in other markdown viewers like GitHub. You will\n  likely have to change links in your book to accommodate this change. For\n  example, a book with this layout:\n\n  ```\n  chapter_1/\n      section_1.md\n      section_2.md\n  SUMMARY.md\n  ```\n\n  Previously a link in `section_1.md` to `section_2.md` would look like this:\n  ```markdown\n  [section_2](chapter_1/section_2.html)\n  ```\n\n  Now it must be changed to this:\n  ```markdown\n  [section_2](section_2.md)\n  ```\n\n- 💥 `mdbook test --library-path` now accepts a comma-delimited list of\n  arguments rather than taking all following arguments. This makes it easier\n  to handle the trailing book directory argument without always needing to put\n  ` -- ` before it. Multiple instances of the option continue to be accepted:\n  `mdbook test -L foo -L bar`.\n\n- 💥 `mdbook serve` has some of its options renamed for clarity. See `mdbook\n  help serve` for details.\n\n- Embedded rust playpens now use the \"stable\" playground API.\n  [#754](https://github.com/rust-lang/mdBook/pull/754)\n\n### Fixed\n- Escaped includes (`\\{{#include file.rs}}`) will now render correctly.\n  [f30ce01](https://github.com/rust-lang/mdBook/commit/f30ce0184d71e342141145472bf816419d30a2c5)\n- `index.html` will now render correctly when the book's first section is\n  inside a subdirectory.\n  [#756](https://github.com/rust-lang/mdBook/pull/756)\n"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "content": "# The Rust Code of Conduct\n\nThe Code of Conduct for this repository [can be found online](https://www.rust-lang.org/conduct.html).\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# Contributing\n\nWelcome stranger!\n\nIf you have come here to learn how to contribute to mdBook, we have some tips for you!\n\nFirst of all, don't hesitate to ask questions!\nUse the [issue tracker](https://github.com/rust-lang/mdBook/issues), no question is too simple.\n\n## Issue assignment\n\n**:warning: Important :warning:**\n\nBefore working on pull request, please ping us on the corresponding issue.\nThe current PR backlog is beyond what we can process at this time.\nOnly issues that have an [`E-Help-wanted`](https://github.com/rust-lang/mdBook/labels/E-Help-wanted) or [`Feature accepted`](https://github.com/rust-lang/mdBook/labels/Feature%20accepted) label will likely receive reviews.\nIf there isn't already an open issue for what you want to work on, please open one first to see if it is something we would be available to review.\n\n## Issues to work on\n\nIf you are starting out, you might be interested in the\n[E-Easy issues](https://github.com/rust-lang/mdBook/issues?q=is%3Aopen+is%3Aissue+label%3AE-Easy).\nThose are issues that are considered more straightforward for beginners to Rust or the codebase itself.\nThese issues can be a good launching pad for more involved issues.\nEasy tasks for a first time contribution include documentation improvements, new tests, examples, updating dependencies, etc.\n\nIf you come from a web development background, you might be interested in issues related to web technologies tagged\n[A-JavaScript](https://github.com/rust-lang/mdBook/issues?q=is%3Aopen+is%3Aissue+label%3AA-JavaScript),\n[A-Style](https://github.com/rust-lang/mdBook/issues?q=is%3Aopen+is%3Aissue+label%3AA-Style),\n[A-HTML](https://github.com/rust-lang/mdBook/issues?q=is%3Aopen+is%3Aissue+label%3AA-HTML) or\n[A-Mobile](https://github.com/rust-lang/mdBook/issues?q=is%3Aopen+is%3Aissue+label%3AA-Mobile).\n\nWhen you decide you want to work on a specific issue, and it isn't already assigned to someone else, assign the issue to yourself by leaving a comment with the text `@rustbot claim`.\nAgain, do not hesitate to ask questions. We will gladly mentor anyone that want to tackle an issue.\n\nIssues on the issue tracker are categorized with the following labels:\n\n- **A**-prefixed labels state which area of the project an issue relates to.\n- **E**-prefixed labels show an estimate of the experience necessary to fix the issue.\n- **M**-prefixed labels are meta-issues regarding the management of the mdBook project itself\n- **S**-prefixed labels show the status of the issue\n- **C**-prefixed labels show the category of issue\n\n## Building mdBook\n\nmdBook builds on stable Rust, if you want to build mdBook from source, here are the steps to follow:\n\n1. Navigate to the directory of your choice\n0. Clone this repository with git.\n\n   ```\n   git clone https://github.com/rust-lang/mdBook.git\n   ```\n0. Navigate into the newly created `mdBook` directory\n0. Run `cargo build`\n\nThe resulting binary can be found in `mdBook/target/debug/` under the name `mdbook` or `mdbook.exe`.\n\n## Code quality\n\nWe love code quality and Rust has some excellent tools to assist you with contributions.\n\n### Formatting code with rustfmt\n\nBefore you make your Pull Request to the project, please run it through the `rustfmt` utility.\nThis will ensure we have good quality source code that is better for us all to maintain.\n\n[rustfmt](https://github.com/rust-lang/rustfmt) has a lot more information on the project.\nThe quick guide is\n\n1. Install it (`rustfmt` is usually installed by default via [rustup](https://rustup.rs/)):\n    ```\n    rustup component add rustfmt\n    ```\n1. You can now run `rustfmt` on a single file simply by...\n    ```\n    rustfmt src/path/to/your/file.rs\n    ```\n   ... or you can format the entire project with\n   ```\n   cargo fmt\n   ```\n   When run through `cargo` it will format all bin and lib files in the current package.\n\nFor more information, such as running it from your favourite editor, please see the `rustfmt` project. [rustfmt](https://github.com/rust-lang/rustfmt)\n\n### Finding issues with clippy\n\n[Clippy](https://doc.rust-lang.org/clippy/) is a code analyser/linter detecting mistakes, and therefore helps to improve your code.\nLike formatting your code with `rustfmt`, running clippy regularly and before your Pull Request will help us maintain awesome code.\n\n1. To install\n    ```\n    rustup component add clippy\n    ```\n2. Running clippy\n    ```\n    cargo clippy\n    ```\n\n## Change requirements\n\nPlease consider the following when making a change:\n\n* Almost all changes that modify the Rust code must be accompanied with a test.\n\n* Almost all features and changes must update the documentation.\n  mdBook has the [mdBook Guide](https://rust-lang.github.io/mdBook/) whose source is at <https://github.com/rust-lang/mdBook/tree/master/guide>.\n\n* Almost all Rust items should be documented with doc comments.\n  See the [Rustdoc Book](https://doc.rust-lang.org/rustdoc/) for more information on writing doc comments.\n\n* Breaking the API can only be done in major SemVer releases.\n  These are done very infrequently, so it is preferred to avoid these when possible.\n  See [SemVer Compatibility](https://doc.rust-lang.org/cargo/reference/semver.html) for more information on what a SemVer breaking change is.\n\n  (Note: At this time, some SemVer breaking changes are inevitable due to the current code structure.\n  An example is adding new fields to the config structures.\n  These are intended to be fixed in the next major release.)\n\n* Similarly, the CLI interface is considered to be stable.\n  Care should be taken to avoid breaking existing workflows.\n\n* Check out the [Rust API Guidelines](https://rust-lang.github.io/api-guidelines/) for guidelines on designing the API.\n\n## Tests\n\nThe main test harness is described in the [testsuite documentation](tests/testsuite/README.md). There are several different commands to run different kinds of tests:\n\n- `cargo test --workspace` — This runs all of the unit and integration tests, except for the GUI tests.\n- `cargo test --test gui` — This runs the [GUI test harness](#browser-compatibility-and-testing). This does not get run automatically due to its extra requirements.\n- `npm run lint` — [Checks the `.js` files](#checking-changes-in-js-files)\n- `cargo test --workspace --no-default-features` — Testing without default features helps check that all feature checks are implemented correctly.\n- `cargo clippy --workspace --all-targets --no-deps -- -D warnings` — This makes sure that there are no clippy warnings.\n- `RUSTDOCFLAGS=\"-D warnings\" cargo doc --workspace --document-private-items --no-deps` — This verifies that there aren't any rustdoc warnings.\n- `cargo fmt --check` — Verifies that everything is formatted correctly.\n- `cargo +stable semver-checks` — Verifies that no SemVer breaking changes have been made. You must install [`cargo-semver-checks`](https://crates.io/crates/cargo-semver-checks) first.\n\nTo help simplify running all these commands, you can run the following cargo command:\n\n```sh\ncargo xtask test-all\n```\n\nIt is useful to run all tests before submitting a PR. While developing I recommend to run some subset of that command based on what you are working on. There are individual arguments for each one. For example:\n\n```sh\ncargo xtask test-workspace clippy doc eslint fmt gui semver-checks\n```\n\nWhile developing, remove any of those arguments that are not relevant to what you are changing, or are really slow.\n\n## Making a pull-request\n\nWhen you feel comfortable that your changes could be integrated into mdBook, you can create a pull-request on GitHub.\nOne of the core maintainers will then approve the changes or request some changes before it gets merged.\n\nThat's it, happy contributions! :tada: :tada: :tada:\n\n## Browser compatibility and testing\n\nCurrently we don't have a strict browser compatibility matrix due to our limited resources.\nWe generally strive to keep mdBook compatible with a relatively recent browser on all of the most major platforms.\nThat is, supporting Chrome, Safari, Firefox, Edge on Windows, macOS, Linux, iOS, and Android.\nIf possible, do your best to avoid breaking older browser releases.\n\nGUI tests are checked with the GUI testsuite. To run it, you need to install `npm` first. Then run:\n\n```\ncargo test --test gui\n```\n\nIf you want to only run some tests, you can filter them by passing (part of) their name:\n\n```\ncargo test --test gui -- search\n```\n\nThe first time, it'll fail and ask you to install the `browser-ui-test` package. Install it with the provided\ncommand then re-run the tests.\n\nIf you want to disable the headless mode, use the `--disable-headless-test` option:\n\n```\ncargo test --test gui -- --disable-headless-test\n```\n\nThe GUI tests are in the directory `tests/gui` in text files with the `.goml` extension. The books that the tests use are located in the `tests/gui/books` directory. These tests are run using a `node.js` framework called `browser-ui-test`. You can find documentation for this language on its [repository](https://github.com/GuillaumeGomez/browser-UI-test/blob/master/goml-script.md).\n\n### Checking changes in `.js` files\n\nThe `.js` files source code is checked using [`eslint`](https://eslint.org/). This is a linter (just like `clippy` in Rust)\nfor the Javascript language. You can install it with `npm` by running the following command:\n\n```\nnpm install\n```\n\nThen you can run it using:\n\n```\nnpm run lint\n```\n\n## Updating highlight.js\n\nThe following are instructions for updating [highlight.js](https://highlightjs.org/).\n\n1. Clone the repository at <https://github.com/highlightjs/highlight.js>\n1. Check out a tagged release (like `10.1.1`).\n1. Run `npm install`\n1. Run `node tools/build.js :common apache armasm coffeescript d handlebars haskell http julia nginx nim nix properties r scala x86asm yaml`\n1. Compare the language list that it spits out to the one in [`syntax-highlighting.md`](https://github.com/camelid/mdBook/blob/master/guide/src/format/theme/syntax-highlighting.md). If any are missing, add them to the list and rebuild (and update these docs). If any are added to the common set, add them to `syntax-highlighting.md`.\n1. Copy `build/highlight.min.js` to mdbook's directory [`highlight.js`](https://github.com/rust-lang/mdBook/blob/master/src/theme/highlight.js).\n1. Be sure to check the highlight.js [CHANGES](https://github.com/highlightjs/highlight.js/blob/main/CHANGES.md) for any breaking changes. Breaking changes that would affect users will need to wait until the next major release.\n1. Build mdbook with the new file and build some books with the new version and compare the output with a variety of languages to see if anything changes. The [syntax GUI test](https://github.com/rust-lang/mdBook/tree/master/tests/gui/books/highlighting) contains a chapter with many languages to examine. Update the test (`highlighting.goml`) to add any new languages.\n\n## Publishing new releases\n\nInstructions for mdBook maintainers to publish a new release:\n\n1. Create a PR that bumps the version and updates the changelog:\n    1. `git fetch upstream`\n    2. `git checkout -B bump-version upstream/master && git branch --set-upstream-to=origin/bump-version`\n    3. `cargo xtask bump <BUMP>`\n       - This will update the version of all the crates.\n       - `cargo set-version` must first be installed with `cargo install cargo-edit`.\n       - Replace `<BUMP>` with the kind of bump (patch, alpha, etc.)\n    4. `cargo xtask changelog`\n       - This will update `CHANGELOG.md` to add a list of all changes at the top. You will need to move those into the appropriate categories. Most changes that are generally not relevant to a user should be removed. Rewrite the descriptions so that a user can reasonably figure out what it means.\n    5. `git add --update  .`\n    6. `git commit`\n    7. `git push`\n2. After the PR has been merged, create a release in GitHub. This can either be done in the GitHub web UI, or on the command-line:\n   ```bash\n   MDBOOK_VERS=\"`cargo read-manifest | jq -r .version`\" ; \\\n    gh release create -R rust-lang/mdbook v$MDBOOK_VERS \\\n        --title v$MDBOOK_VERS \\\n        --notes \"See https://github.com/rust-lang/mdBook/blob/master/CHANGELOG.md#mdbook-${MDBOOK_VERS//.} for a complete list of changes.\"\n   ```\n"
  },
  {
    "path": "Cargo.toml",
    "content": "[workspace]\nmembers = [\n    \".\",\n    \"crates/*\",\n    \"examples/remove-emphasis/mdbook-remove-emphasis\", \"guide/guide-helper\",\n]\n\n[workspace.lints.clippy]\nall = { level = \"allow\", priority = -2 }\ncorrectness = { level = \"warn\", priority = -1 }\ncomplexity = { level = \"warn\", priority = -1 }\nexhaustive_enums = \"warn\"\nexhaustive_structs = \"warn\"\nmanual_non_exhaustive = \"warn\"\n\n[workspace.lints.rust]\nmissing_docs = \"warn\"\nrust_2018_idioms = \"warn\"\nunreachable_pub = \"warn\"\n\n[workspace.package]\nedition = \"2024\"\nlicense = \"MPL-2.0\"\nrepository = \"https://github.com/rust-lang/mdBook\"\nrust-version = \"1.88.0\" # Keep in sync with installation.md and .github/workflows/main.yml\n\n[workspace.dependencies]\nanyhow = \"1.0.102\"\naxum = \"0.8.8\"\nclap = { version = \"4.5.60\", features = [\"cargo\", \"wrap_help\"] }\nclap_complete = \"4.5.66\"\nego-tree = \"0.11.0\"\nelasticlunr-rs = \"3.0.2\"\nfont-awesome-as-a-crate = \"0.3.0\"\nfutures-util = \"0.3.32\"\nglob = \"0.3.3\"\nhandlebars = \"6.4.0\"\nhex = \"0.4.3\"\nhtml5ever = \"0.38.0\"\nindexmap = \"2.13.0\"\nignore = \"0.4.25\"\nmdbook-core = { path = \"crates/mdbook-core\", version = \"0.5.2\" }\nmdbook-driver = { path = \"crates/mdbook-driver\", version = \"0.5.2\" }\nmdbook-html = { path = \"crates/mdbook-html\", version = \"0.5.2\" }\nmdbook-markdown = { path = \"crates/mdbook-markdown\", version = \"0.5.2\" }\nmdbook-preprocessor = { path = \"crates/mdbook-preprocessor\", version = \"0.5.2\" }\nmdbook-renderer = { path = \"crates/mdbook-renderer\", version = \"0.5.2\" }\nmdbook-summary = { path = \"crates/mdbook-summary\", version = \"0.5.2\" }\nmemchr = \"2.8.0\"\nnotify = \"8.2.0\"\nnotify-debouncer-mini = \"0.7.0\"\nopener = \"0.8.4\"\npathdiff = \"0.2.3\"\npulldown-cmark = { version = \"0.13.1\", default-features = false, features = [\"html\"] } # Do not update, part of the public api.\nregex = \"1.12.3\"\nselect = \"0.6.1\"\nsemver = \"1.0.27\"\nserde = { version = \"1.0.228\", features = [\"derive\"] }\nserde_json = \"1.0.149\"\nsha2 = \"0.10.9\"\nshlex = \"1.3.0\"\nsnapbox = \"1.0.0\"\ntempfile = \"3.26.0\"\ntokio = \"1.49.0\"\ntoml = \"1.0.3\"\ntopological-sort = \"0.2.2\"\ntower-http = \"0.6.8\"\ntracing = \"0.1.44\"\ntracing-subscriber = { version = \"0.3.22\", features = [\"env-filter\"] }\nwalkdir = \"2.5.0\"\n\n[package]\nname = \"mdbook\"\nversion = \"0.5.2\"\nauthors = [\n    \"Mathieu David <mathieudavid@mathieudavid.org>\",\n    \"Michael-F-Bryan <michaelfbryan@gmail.com>\",\n    \"Matt Ickstadt <mattico8@gmail.com>\"\n]\ndocumentation = \"https://rust-lang.github.io/mdBook/index.html\"\nedition.workspace = true\nexclude = [\"/guide/*\"]\nkeywords = [\"book\", \"gitbook\", \"rustbook\", \"markdown\"]\nlicense.workspace = true\nreadme = \"README.md\"\nrepository.workspace = true\ndescription = \"Creates a book from markdown files\"\nrust-version.workspace = true\n\n[dependencies]\nanyhow.workspace = true\nclap.workspace = true\nclap_complete.workspace = true\nmdbook-core.workspace = true\nmdbook-driver.workspace = true\nmdbook-html.workspace = true\nopener.workspace = true\ntracing.workspace = true\ntracing-subscriber.workspace = true\n\n# Watch feature\nignore = { workspace = true, optional = true }\nnotify = { workspace = true, optional = true }\nnotify-debouncer-mini = { workspace = true, optional = true }\npathdiff = { workspace = true, optional = true }\nwalkdir = { workspace = true, optional = true }\n\n# Serve feature\naxum = { workspace = true, features = [\"ws\"], optional = true }\nfutures-util = { workspace = true, optional = true }\ntokio = { workspace = true, features = [\"macros\", \"rt-multi-thread\"], optional = true }\ntower-http = { workspace = true, features = [\"fs\", \"trace\"], optional = true }\n\n[dev-dependencies]\nglob.workspace = true\nmdbook-preprocessor.workspace = true\nmdbook-renderer.workspace = true\nregex.workspace = true\nselect.workspace = true\nsemver.workspace = true\nserde_json.workspace = true\nsnapbox = { workspace = true, features = [\"diff\", \"dir\", \"term-svg\", \"regex\", \"json\"] }\ntempfile.workspace = true\nwalkdir.workspace = true\n\n[features]\ndefault = [\"watch\", \"serve\", \"search\"]\nwatch = [\"dep:notify\", \"dep:notify-debouncer-mini\", \"dep:ignore\", \"dep:pathdiff\", \"dep:walkdir\"]\nserve = [\"dep:futures-util\", \"dep:tokio\", \"dep:axum\", \"dep:tower-http\"]\nsearch = [\"mdbook-html/search\"]\n\n[[bin]]\ndoc = false\nname = \"mdbook\"\n\n[[example]]\nname = \"nop-preprocessor\"\ntest = true\n\n[[example]]\nname = \"remove-emphasis\"\npath = \"examples/remove-emphasis/test.rs\"\ncrate-type = [\"lib\"]\ntest = true\n\n[[test]]\nharness = false\ntest = false\nname = \"gui\"\npath = \"tests/gui/runner.rs\"\ncrate-type = [\"bin\"]\n\n[lints]\nworkspace = true\n"
  },
  {
    "path": "LICENSE",
    "content": "Mozilla Public License, version 2.0\n\n1. Definitions\n\n1.1. \"Contributor\"\n\n     means each individual or legal entity that creates, contributes to the\n     creation of, or owns Covered Software.\n\n1.2. \"Contributor Version\"\n\n     means the combination of the Contributions of others (if any) used by a\n     Contributor and that particular Contributor's Contribution.\n\n1.3. \"Contribution\"\n\n     means Covered Software of a particular Contributor.\n\n1.4. \"Covered Software\"\n\n     means Source Code Form to which the initial Contributor has attached the\n     notice in Exhibit A, the Executable Form of such Source Code Form, and\n     Modifications of such Source Code Form, in each case including portions\n     thereof.\n\n1.5. \"Incompatible With Secondary Licenses\"\n     means\n\n     a. that the initial Contributor has attached the notice described in\n        Exhibit B to the Covered Software; or\n\n     b. that the Covered Software was made available under the terms of\n        version 1.1 or earlier of the License, but not also under the terms of\n        a Secondary License.\n\n1.6. \"Executable Form\"\n\n     means any form of the work other than Source Code Form.\n\n1.7. \"Larger Work\"\n\n     means a work that combines Covered Software with other material, in a\n     separate file or files, that is not Covered Software.\n\n1.8. \"License\"\n\n     means this document.\n\n1.9. \"Licensable\"\n\n     means having the right to grant, to the maximum extent possible, whether\n     at the time of the initial grant or subsequently, any and all of the\n     rights conveyed by this License.\n\n1.10. \"Modifications\"\n\n     means any of the following:\n\n     a. any file in Source Code Form that results from an addition to,\n        deletion from, or modification of the contents of Covered Software; or\n\n     b. any new file in Source Code Form that contains any Covered Software.\n\n1.11. \"Patent Claims\" of a Contributor\n\n      means any patent claim(s), including without limitation, method,\n      process, and apparatus claims, in any patent Licensable by such\n      Contributor that would be infringed, but for the grant of the License,\n      by the making, using, selling, offering for sale, having made, import,\n      or transfer of either its Contributions or its Contributor Version.\n\n1.12. \"Secondary License\"\n\n      means either the GNU General Public License, Version 2.0, the GNU Lesser\n      General Public License, Version 2.1, the GNU Affero General Public\n      License, Version 3.0, or any later versions of those licenses.\n\n1.13. \"Source Code Form\"\n\n      means the form of the work preferred for making modifications.\n\n1.14. \"You\" (or \"Your\")\n\n      means an individual or a legal entity exercising rights under this\n      License. For legal entities, \"You\" includes any entity that controls, is\n      controlled by, or is under common control with You. For purposes of this\n      definition, \"control\" means (a) the power, direct or indirect, to cause\n      the direction or management of such entity, whether by contract or\n      otherwise, or (b) ownership of more than fifty percent (50%) of the\n      outstanding shares or beneficial ownership of such entity.\n\n\n2. License Grants and Conditions\n\n2.1. Grants\n\n     Each Contributor hereby grants You a world-wide, royalty-free,\n     non-exclusive license:\n\n     a. under intellectual property rights (other than patent or trademark)\n        Licensable by such Contributor to use, reproduce, make available,\n        modify, display, perform, distribute, and otherwise exploit its\n        Contributions, either on an unmodified basis, with Modifications, or\n        as part of a Larger Work; and\n\n     b. under Patent Claims of such Contributor to make, use, sell, offer for\n        sale, have made, import, and otherwise transfer either its\n        Contributions or its Contributor Version.\n\n2.2. Effective Date\n\n     The licenses granted in Section 2.1 with respect to any Contribution\n     become effective for each Contribution on the date the Contributor first\n     distributes such Contribution.\n\n2.3. Limitations on Grant Scope\n\n     The licenses granted in this Section 2 are the only rights granted under\n     this License. No additional rights or licenses will be implied from the\n     distribution or licensing of Covered Software under this License.\n     Notwithstanding Section 2.1(b) above, no patent license is granted by a\n     Contributor:\n\n     a. for any code that a Contributor has removed from Covered Software; or\n\n     b. for infringements caused by: (i) Your and any other third party's\n        modifications of Covered Software, or (ii) the combination of its\n        Contributions with other software (except as part of its Contributor\n        Version); or\n\n     c. under Patent Claims infringed by Covered Software in the absence of\n        its Contributions.\n\n     This License does not grant any rights in the trademarks, service marks,\n     or logos of any Contributor (except as may be necessary to comply with\n     the notice requirements in Section 3.4).\n\n2.4. Subsequent Licenses\n\n     No Contributor makes additional grants as a result of Your choice to\n     distribute the Covered Software under a subsequent version of this\n     License (see Section 10.2) or under the terms of a Secondary License (if\n     permitted under the terms of Section 3.3).\n\n2.5. Representation\n\n     Each Contributor represents that the Contributor believes its\n     Contributions are its original creation(s) or it has sufficient rights to\n     grant the rights to its Contributions conveyed by this License.\n\n2.6. Fair Use\n\n     This License is not intended to limit any rights You have under\n     applicable copyright doctrines of fair use, fair dealing, or other\n     equivalents.\n\n2.7. Conditions\n\n     Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in\n     Section 2.1.\n\n\n3. Responsibilities\n\n3.1. Distribution of Source Form\n\n     All distribution of Covered Software in Source Code Form, including any\n     Modifications that You create or to which You contribute, must be under\n     the terms of this License. You must inform recipients that the Source\n     Code Form of the Covered Software is governed by the terms of this\n     License, and how they can obtain a copy of this License. You may not\n     attempt to alter or restrict the recipients' rights in the Source Code\n     Form.\n\n3.2. Distribution of Executable Form\n\n     If You distribute Covered Software in Executable Form then:\n\n     a. such Covered Software must also be made available in Source Code Form,\n        as described in Section 3.1, and You must inform recipients of the\n        Executable Form how they can obtain a copy of such Source Code Form by\n        reasonable means in a timely manner, at a charge no more than the cost\n        of distribution to the recipient; and\n\n     b. You may distribute such Executable Form under the terms of this\n        License, or sublicense it under different terms, provided that the\n        license for the Executable Form does not attempt to limit or alter the\n        recipients' rights in the Source Code Form under this License.\n\n3.3. Distribution of a Larger Work\n\n     You may create and distribute a Larger Work under terms of Your choice,\n     provided that You also comply with the requirements of this License for\n     the Covered Software. If the Larger Work is a combination of Covered\n     Software with a work governed by one or more Secondary Licenses, and the\n     Covered Software is not Incompatible With Secondary Licenses, this\n     License permits You to additionally distribute such Covered Software\n     under the terms of such Secondary License(s), so that the recipient of\n     the Larger Work may, at their option, further distribute the Covered\n     Software under the terms of either this License or such Secondary\n     License(s).\n\n3.4. Notices\n\n     You may not remove or alter the substance of any license notices\n     (including copyright notices, patent notices, disclaimers of warranty, or\n     limitations of liability) contained within the Source Code Form of the\n     Covered Software, except that You may alter any license notices to the\n     extent required to remedy known factual inaccuracies.\n\n3.5. Application of Additional Terms\n\n     You may choose to offer, and to charge a fee for, warranty, support,\n     indemnity or liability obligations to one or more recipients of Covered\n     Software. However, You may do so only on Your own behalf, and not on\n     behalf of any Contributor. You must make it absolutely clear that any\n     such warranty, support, indemnity, or liability obligation is offered by\n     You alone, and You hereby agree to indemnify every Contributor for any\n     liability incurred by such Contributor as a result of warranty, support,\n     indemnity or liability terms You offer. You may include additional\n     disclaimers of warranty and limitations of liability specific to any\n     jurisdiction.\n\n4. Inability to Comply Due to Statute or Regulation\n\n   If it is impossible for You to comply with any of the terms of this License\n   with respect to some or all of the Covered Software due to statute,\n   judicial order, or regulation then You must: (a) comply with the terms of\n   this License to the maximum extent possible; and (b) describe the\n   limitations and the code they affect. Such description must be placed in a\n   text file included with all distributions of the Covered Software under\n   this License. Except to the extent prohibited by statute or regulation,\n   such description must be sufficiently detailed for a recipient of ordinary\n   skill to be able to understand it.\n\n5. Termination\n\n5.1. The rights granted under this License will terminate automatically if You\n     fail to comply with any of its terms. However, if You become compliant,\n     then the rights granted under this License from a particular Contributor\n     are reinstated (a) provisionally, unless and until such Contributor\n     explicitly and finally terminates Your grants, and (b) on an ongoing\n     basis, if such Contributor fails to notify You of the non-compliance by\n     some reasonable means prior to 60 days after You have come back into\n     compliance. Moreover, Your grants from a particular Contributor are\n     reinstated on an ongoing basis if such Contributor notifies You of the\n     non-compliance by some reasonable means, this is the first time You have\n     received notice of non-compliance with this License from such\n     Contributor, and You become compliant prior to 30 days after Your receipt\n     of the notice.\n\n5.2. If You initiate litigation against any entity by asserting a patent\n     infringement claim (excluding declaratory judgment actions,\n     counter-claims, and cross-claims) alleging that a Contributor Version\n     directly or indirectly infringes any patent, then the rights granted to\n     You by any and all Contributors for the Covered Software under Section\n     2.1 of this License shall terminate.\n\n5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user\n     license agreements (excluding distributors and resellers) which have been\n     validly granted by You or Your distributors under this License prior to\n     termination shall survive termination.\n\n6. Disclaimer of Warranty\n\n   Covered Software is provided under this License on an \"as is\" basis,\n   without warranty of any kind, either expressed, implied, or statutory,\n   including, without limitation, warranties that the Covered Software is free\n   of defects, merchantable, fit for a particular purpose or non-infringing.\n   The entire risk as to the quality and performance of the Covered Software\n   is with You. Should any Covered Software prove defective in any respect,\n   You (not any Contributor) assume the cost of any necessary servicing,\n   repair, or correction. This disclaimer of warranty constitutes an essential\n   part of this License. No use of  any Covered Software is authorized under\n   this License except under this disclaimer.\n\n7. Limitation of Liability\n\n   Under no circumstances and under no legal theory, whether tort (including\n   negligence), contract, or otherwise, shall any Contributor, or anyone who\n   distributes Covered Software as permitted above, be liable to You for any\n   direct, indirect, special, incidental, or consequential damages of any\n   character including, without limitation, damages for lost profits, loss of\n   goodwill, work stoppage, computer failure or malfunction, or any and all\n   other commercial damages or losses, even if such party shall have been\n   informed of the possibility of such damages. This limitation of liability\n   shall not apply to liability for death or personal injury resulting from\n   such party's negligence to the extent applicable law prohibits such\n   limitation. Some jurisdictions do not allow the exclusion or limitation of\n   incidental or consequential damages, so this exclusion and limitation may\n   not apply to You.\n\n8. Litigation\n\n   Any litigation relating to this License may be brought only in the courts\n   of a jurisdiction where the defendant maintains its principal place of\n   business and such litigation shall be governed by laws of that\n   jurisdiction, without reference to its conflict-of-law provisions. Nothing\n   in this Section shall prevent a party's ability to bring cross-claims or\n   counter-claims.\n\n9. Miscellaneous\n\n   This License represents the complete agreement concerning the subject\n   matter hereof. If any provision of this License is held to be\n   unenforceable, such provision shall be reformed only to the extent\n   necessary to make it enforceable. Any law or regulation which provides that\n   the language of a contract shall be construed against the drafter shall not\n   be used to construe this License against a Contributor.\n\n\n10. Versions of the License\n\n10.1. New Versions\n\n      Mozilla Foundation is the license steward. Except as provided in Section\n      10.3, no one other than the license steward has the right to modify or\n      publish new versions of this License. Each version will be given a\n      distinguishing version number.\n\n10.2. Effect of New Versions\n\n      You may distribute the Covered Software under the terms of the version\n      of the License under which You originally received the Covered Software,\n      or under the terms of any subsequent version published by the license\n      steward.\n\n10.3. Modified Versions\n\n      If you create software not governed by this License, and you want to\n      create a new license for such software, you may create and use a\n      modified version of this License if you rename the license and remove\n      any references to the name of the license steward (except to note that\n      such modified license differs from this License).\n\n10.4. Distributing Source Code Form that is Incompatible With Secondary\n      Licenses If You choose to distribute Source Code Form that is\n      Incompatible With Secondary Licenses under the terms of this version of\n      the License, the notice described in Exhibit B of this License must be\n      attached.\n\nExhibit A - Source Code Form License Notice\n\n      This Source Code Form is subject to the\n      terms of the Mozilla Public License, v.\n      2.0. If a copy of the MPL was not\n      distributed with this file, You can\n      obtain one at\n      http://mozilla.org/MPL/2.0/.\n\nIf it is not possible or desirable to put the notice in a particular file,\nthen You may include the notice in a location (such as a LICENSE file in a\nrelevant directory) where a recipient would be likely to look for such a\nnotice.\n\nYou may add additional accurate notices of copyright ownership.\n\nExhibit B - \"Incompatible With Secondary Licenses\" Notice\n\n      This Source Code Form is \"Incompatible\n      With Secondary Licenses\", as defined by\n      the Mozilla Public License, v. 2.0.\n"
  },
  {
    "path": "README.md",
    "content": "# mdBook\n\n[![CI Status](https://github.com/rust-lang/mdBook/actions/workflows/main.yml/badge.svg)](https://github.com/rust-lang/mdBook/actions/workflows/main.yml)\n[![crates.io](https://img.shields.io/crates/v/mdbook.svg)](https://crates.io/crates/mdbook)\n[![LICENSE](https://img.shields.io/github/license/rust-lang/mdBook.svg)](LICENSE)\n\nmdBook is a utility to create modern online books from Markdown files.\n\nCheck out the **[User Guide]** for a list of features and installation and usage information.\nThe User Guide also serves as a demonstration to showcase what a book looks like.\n\nIf you are interested in contributing to the development of mdBook, check out the [Contribution Guide].\n\n## License\n\nAll the code in this repository is released under the ***Mozilla Public License v2.0***, for more information take a look at the [LICENSE] file.\n\n[User Guide]: https://rust-lang.github.io/mdBook/\n[contribution guide]: https://github.com/rust-lang/mdBook/blob/master/CONTRIBUTING.md\n[LICENSE]: https://github.com/rust-lang/mdBook/blob/master/LICENSE\n"
  },
  {
    "path": "ci/install-rust.sh",
    "content": "#!/usr/bin/env bash\n# Install/update rust.\n# The first argument should be the toolchain to install.\n\nset -ex\nif [ -z \"$1\" ]\nthen\n    echo \"First parameter must be toolchain to install.\"\n    exit 1\nfi\nTOOLCHAIN=\"$1\"\n\nrustup set profile minimal\nrustup component remove --toolchain=$TOOLCHAIN rust-docs || echo \"already removed\"\nrustup update --no-self-update $TOOLCHAIN\nif [ -n \"$2\" ]\nthen\n    TARGET=\"$2\"\n    HOST=$(rustc -Vv | grep ^host: | sed -e \"s/host: //g\")\n    if [ \"$HOST\" != \"$TARGET\" ]\n    then\n        rustup component add llvm-tools-preview --toolchain=$TOOLCHAIN\n        rustup component add rust-std-$TARGET --toolchain=$TOOLCHAIN\n    fi\n    if [[ $TARGET == *\"musl\" ]]\n    then\n        # This is needed by libdbus-sys.\n        sudo apt update -y && sudo apt install musl-dev musl-tools -y\n    fi\n    if [[ $TARGET == \"aarch64-unknown-linux-musl\" ]]\n    then\n        echo CARGO_TARGET_AARCH64_UNKNOWN_LINUX_MUSL_LINKER=rust-lld >> $GITHUB_ENV\n        # This `CC` is some nonsense needed for libdbus-sys (via opener).\n        # I don't know if this is really the right thing to do, but it seems to work.\n        sudo apt install gcc-aarch64-linux-gnu -y\n        echo CC=aarch64-linux-gnu-gcc >> $GITHUB_ENV\n    fi\nfi\n\nrustup default $TOOLCHAIN\nrustup -V\nrustc -Vv\ncargo -V\n"
  },
  {
    "path": "ci/make-release-asset.sh",
    "content": "#!/usr/bin/env bash\n# Builds the release and creates an archive and optionally deploys to GitHub.\nset -ex\n\nif [[ -z \"$GITHUB_REF\" ]]\nthen\n  echo \"GITHUB_REF must be set\"\n  exit 1\nfi\n# Strip mdbook-refs/tags/ from the start of the ref.\nTAG=${GITHUB_REF#*/tags/}\n\nhost=$(rustc -Vv | grep ^host: | sed -e \"s/host: //g\")\ntarget=$2\nexport CARGO_PROFILE_RELEASE_LTO=true\ncargo build --locked --bin mdbook --release --target $target\ncd target/$target/release\ncase $1 in\n  ubuntu*)\n    asset=\"mdbook-$TAG-$target.tar.gz\"\n    tar czf ../../$asset mdbook\n    ;;\n  macos*)\n    asset=\"mdbook-$TAG-$target.tar.gz\"\n    # There is a bug with BSD tar on macOS where the first 8MB of the file are\n    # sometimes all NUL bytes. See https://github.com/actions/cache/issues/403\n    # and https://github.com/rust-lang/cargo/issues/8603 for some more\n    # information. An alternative solution here is to install GNU tar, but\n    # flushing the disk cache seems to work, too.\n    sudo /usr/sbin/purge\n    tar czf ../../$asset mdbook\n    ;;\n  windows*)\n    asset=\"mdbook-$TAG-$target.zip\"\n    7z a ../../$asset mdbook.exe\n    ;;\n  *)\n    echo \"OS should be first parameter, was: $1\"\n    ;;\nesac\ncd ../..\n\nif [[ -z \"$GITHUB_ENV\" ]]\nthen\n  echo \"GITHUB_ENV not set, run: gh release upload $TAG target/$asset\"\nelse\n  echo \"MDBOOK_TAG=$TAG\" >> $GITHUB_ENV\n  echo \"MDBOOK_ASSET=target/$asset\" >> $GITHUB_ENV\nfi\n"
  },
  {
    "path": "ci/publish-guide.sh",
    "content": "#!/usr/bin/env bash\n# This publishes the user guide to GitHub Pages.\n#\n# If this is a pre-release, then it goes in a separate directory called \"pre-release\".\n# Commits are amended to avoid keeping history which can balloon the repo size.\nset -ex\n\ncargo run --no-default-features -F search -- build guide\n\nVERSION=$(cargo metadata --format-version 1 --no-deps | jq '.packages[] | select(.name == \"mdbook\") | .version')\n\nif [[ \"$VERSION\" == *-* ]]; then\n    PRERELEASE=true\nelse\n    PRERELEASE=false\nfi\n\ngit fetch origin gh-pages\ngit worktree add gh-pages gh-pages\ngit config user.name \"Deploy from CI\"\ngit config user.email \"\"\ncd gh-pages\nif [[ \"$PRERELEASE\" == \"true\" ]]\nthen\n    rm -rf pre-release\n    mv ../guide/book pre-release\n    git add pre-release\n    git commit --amend -m \"Deploy $GITHUB_SHA pre-release to gh-pages\"\nelse\n    # Delete everything except pre-release and .git.\n    find . -mindepth 1 -maxdepth 1 -not -name \"pre-release\" -not -name \".git\" -exec rm -rf {} +\n    # Copy the guide here.\n    find ../guide/book/ -mindepth 1 -maxdepth 1 -exec mv {} . \\;\n    git add .\n    git commit --amend -m \"Deploy $GITHUB_SHA to gh-pages\"\nfi\n\ngit push --force origin +gh-pages\n"
  },
  {
    "path": "ci/update-dependencies.sh",
    "content": "#!/usr/bin/env bash\n# Updates all compatible Cargo dependencies.\n#\n# I wasn't able to get Renovate to update compatible dependencies in a way\n# that I like, so this script takes care of it. This uses `cargo upgrade` to\n# ensure that `Cargo.toml` also gets updated. This also makes sure that all\n# transitive dependencies are updated.\n\nset -ex\n\ngit fetch origin update-dependencies\nif git checkout update-dependencies\nthen\n    git reset --hard origin/master\nelse\n    git checkout -b update-dependencies\nfi\n\ncat > commit-message << 'EOF'\nUpdate cargo dependencies\n\n```\nEOF\ncargo upgrade >> commit-message\necho '```' >> commit-message\nif git diff --quiet\nthen\n    echo \"No changes detected, exiting.\"\n    exit 0\nfi\n# Also update any transitive dependencies.\ncargo update\n\ngit config user.name \"github-actions[bot]\"\ngit config user.email \"github-actions[bot]@users.noreply.github.com\"\n\ngit add Cargo.toml Cargo.lock\ngit commit -F commit-message\n\ngit push --force origin update-dependencies\n\ngh pr create --fill \\\n    --head update-dependencies \\\n    --base master\n"
  },
  {
    "path": "crates/mdbook-compare/Cargo.toml",
    "content": "[package]\nname = \"mdbook-compare\"\npublish = false\nedition.workspace = true\nlicense.workspace = true\nrepository.workspace = true\nrust-version.workspace = true\n\n[dependencies]\n\n[lints]\nworkspace = true\n"
  },
  {
    "path": "crates/mdbook-compare/README.md",
    "content": "# mdbook-compare\n\nThis is a simple utility to compare the output of two different versions of mdbook.\n\nTo use this:\n\n1. Install [`tidy`](https://www.html-tidy.org/).\n2. Install or build the initial version of mdbook that you want to compare.\n3. Install or build the new version of mdbook that you want to compare.\n4. Run `mdbook-compare` with the arguments to the mdbook executables and the books to build.\n\n```sh\ncargo run --manifest-path /path/to/mdBook/Cargo.toml -p mdbook-compare -- \\\n    /path/to/orig/mdbook /path/to/my-book /path/to/new/mdbook /path/to/my-book\n```\n\nIt takes two separate paths for the book to use for \"before\" and \"after\" in case you need to customize the book to run on older versions. If you don't need that, then you can use the same directory for both the before and after.\n\n`mdbook-compare` will do the following:\n\n1. Clean up any book directories.\n2. Build the book with the first mdbook.\n3. Build the book with the second mdbook.\n4. The output of those two commands are stored in directories called `compare1` and `compare2`.\n5. The HTML in those directories is normalized using `tidy`.\n6. Runs `git diff` to compare the output.\n"
  },
  {
    "path": "crates/mdbook-compare/src/main.rs",
    "content": "//! Utility to compare the output of two different versions of mdbook.\n\nuse std::path::Path;\nuse std::process::Command;\n\nmacro_rules! error {\n    ($msg:literal $($arg:tt)*) => {\n        eprint!(\"error: \");\n        eprintln!($msg $($arg)*);\n        std::process::exit(1);\n    };\n}\n\nfn main() {\n    let mut args = std::env::args().skip(1);\n    let (Some(mdbook1), Some(book1), Some(mdbook2), Some(book2)) =\n        (args.next(), args.next(), args.next(), args.next())\n    else {\n        eprintln!(\"error: Expected four arguments: <exe1> <dir1> <exe2> <dir2>\");\n        std::process::exit(1);\n    };\n    let mdbook1 = Path::new(&mdbook1);\n    let mdbook2 = Path::new(&mdbook2);\n    let book1 = Path::new(&book1);\n    let book2 = Path::new(&book2);\n    let compare1 = Path::new(\"compare1\");\n    let compare2 = Path::new(\"compare2\");\n    clean(compare1);\n    clean(compare2);\n    clean(&book1.join(\"book\"));\n    clean(&book2.join(\"book\"));\n    build(mdbook1, book1);\n    std::fs::rename(book1.join(\"book\"), compare1).unwrap();\n    build(mdbook2, book2);\n    std::fs::rename(book2.join(\"book\"), compare2).unwrap();\n    diff(compare1, compare2);\n}\n\nfn clean(path: &Path) {\n    if path.exists() {\n        println!(\"removing {path:?}\");\n        std::fs::remove_dir_all(path).unwrap();\n    }\n}\n\nfn build(mdbook: &Path, book: &Path) {\n    println!(\"running `{mdbook:?} build` in `{book:?}`\");\n    let status = Command::new(mdbook)\n        .arg(\"build\")\n        .current_dir(book)\n        .status()\n        .unwrap_or_else(|e| {\n            error!(\"expected {mdbook:?} executable to exist: {e}\");\n        });\n    if !status.success() {\n        error!(\"process {mdbook:?} failed\");\n    }\n    process(&book.join(\"book\"));\n}\n\nfn process(path: &Path) {\n    for entry in std::fs::read_dir(path).unwrap() {\n        let entry = entry.unwrap();\n        let path = entry.path();\n        if path.is_dir() {\n            process(&path);\n        } else {\n            if path.extension().is_some_and(|ext| ext == \"html\") {\n                tidy(&path);\n                process_html(&path);\n            } else {\n                std::fs::remove_file(path).unwrap();\n            }\n        }\n    }\n}\n\nfn process_html(path: &Path) {\n    let content = std::fs::read_to_string(path).unwrap();\n    let Some(start_index) = content.find(\"<main>\") else {\n        return;\n    };\n    let end_index = content.rfind(\"</main>\").unwrap();\n    let new_content = &content[start_index..end_index + 8];\n    std::fs::write(path, new_content).unwrap();\n}\n\nfn tidy(path: &Path) {\n    // quiet, no wrap, modify in place\n    let args = \"-q -w 0 -m --custom-tags yes --drop-empty-elements no\";\n    println!(\"running `tidy {args}` in `{path:?}`\");\n    let status = Command::new(\"tidy\")\n        .args(args.split(' '))\n        .arg(path)\n        .status()\n        .expect(\"tidy should be installed\");\n    if !status.success() {\n        // Exit code 1 is a warning.\n        if status.code() != Some(1) {\n            error!(\"tidy failed: {status}\");\n        }\n    }\n}\n\nfn diff(a: &Path, b: &Path) {\n    let args = \"diff --no-index\";\n    println!(\"running `git {args} {a:?} {b:?}`\");\n    Command::new(\"git\")\n        .args(args.split(' '))\n        .args([a, b])\n        .status()\n        .unwrap();\n}\n"
  },
  {
    "path": "crates/mdbook-core/Cargo.toml",
    "content": "[package]\nname = \"mdbook-core\"\nversion = \"0.5.2\"\ndescription = \"The base support library for mdbook, intended for internal use only\"\nedition.workspace = true\nlicense.workspace = true\nrepository.workspace = true\nrust-version.workspace = true\n\n[dependencies]\nanyhow.workspace = true\nregex.workspace = true\nserde.workspace = true\nserde_json.workspace = true\ntoml.workspace = true\ntracing.workspace = true\n\n[dev-dependencies]\ntempfile.workspace = true\n\n[lints]\nworkspace = true\n"
  },
  {
    "path": "crates/mdbook-core/README.md",
    "content": "# mdbook-core\n\n[![Documentation](https://img.shields.io/docsrs/mdbook-core)](https://docs.rs/mdbook-core)\n[![crates.io](https://img.shields.io/crates/v/mdbook-core.svg)](https://crates.io/crates/mdbook-core)\n[![Changelog](https://img.shields.io/badge/CHANGELOG-Latest-green)](https://github.com/rust-lang/mdBook/blob/master/CHANGELOG.md)\n\nThis is the base support library for [mdBook](https://rust-lang.github.io/mdBook/). It is intended for internal use only. Other mdBook crates depend on this for any types that are shared across the crates.\n\n> This crate is maintained by the mdBook team, primarily for use by mdBook and not intended for external use (except as a transitive dependency). This crate may make major changes to its APIs or be deprecated without warning.\n\n## License\n\n[Mozilla Public License, version 2.0](https://github.com/rust-lang/mdBook/blob/master/LICENSE)\n"
  },
  {
    "path": "crates/mdbook-core/src/book/tests.rs",
    "content": "use super::*;\n\n#[test]\nfn section_number_has_correct_dotted_representation() {\n    let inputs = vec![\n        (vec![0], \"0.\"),\n        (vec![1, 3], \"1.3.\"),\n        (vec![1, 2, 3], \"1.2.3.\"),\n    ];\n\n    for (input, should_be) in inputs {\n        let section_number = SectionNumber(input).to_string();\n        assert_eq!(section_number, should_be);\n    }\n}\n\n#[test]\nfn book_iter_iterates_over_sequential_items() {\n    let items = vec![\n        BookItem::Chapter(Chapter {\n            name: String::from(\"Chapter 1\"),\n            content: String::from(\"# Chapter 1\"),\n            ..Default::default()\n        }),\n        BookItem::Separator,\n    ];\n    let book = Book::new_with_items(items);\n\n    let should_be: Vec<_> = book.items.iter().collect();\n\n    let got: Vec<_> = book.iter().collect();\n\n    assert_eq!(got, should_be);\n}\n\n#[test]\nfn for_each_mut_visits_all_items() {\n    let items = vec![\n        BookItem::Chapter(Chapter {\n            name: String::from(\"Chapter 1\"),\n            content: String::from(\"# Chapter 1\"),\n            number: None,\n            path: Some(PathBuf::from(\"Chapter_1/index.md\")),\n            source_path: Some(PathBuf::from(\"Chapter_1/index.md\")),\n            parent_names: Vec::new(),\n            sub_items: vec![\n                BookItem::Chapter(Chapter::new(\n                    \"Hello World\",\n                    String::new(),\n                    \"Chapter_1/hello.md\",\n                    Vec::new(),\n                )),\n                BookItem::Separator,\n                BookItem::Chapter(Chapter::new(\n                    \"Goodbye World\",\n                    String::new(),\n                    \"Chapter_1/goodbye.md\",\n                    Vec::new(),\n                )),\n            ],\n        }),\n        BookItem::Separator,\n    ];\n    let mut book = Book::new_with_items(items);\n\n    let num_items = book.iter().count();\n    let mut visited = 0;\n\n    book.for_each_mut(|_| visited += 1);\n\n    assert_eq!(visited, num_items);\n}\n\n#[test]\nfn iterate_over_nested_book_items() {\n    let items = vec![\n        BookItem::Chapter(Chapter {\n            name: String::from(\"Chapter 1\"),\n            content: String::from(\"# Chapter 1\"),\n            number: None,\n            path: Some(PathBuf::from(\"Chapter_1/index.md\")),\n            source_path: Some(PathBuf::from(\"Chapter_1/index.md\")),\n            parent_names: Vec::new(),\n            sub_items: vec![\n                BookItem::Chapter(Chapter::new(\n                    \"Hello World\",\n                    String::new(),\n                    \"Chapter_1/hello.md\",\n                    Vec::new(),\n                )),\n                BookItem::Separator,\n                BookItem::Chapter(Chapter::new(\n                    \"Goodbye World\",\n                    String::new(),\n                    \"Chapter_1/goodbye.md\",\n                    Vec::new(),\n                )),\n            ],\n        }),\n        BookItem::Separator,\n    ];\n    let book = Book::new_with_items(items);\n\n    let got: Vec<_> = book.iter().collect();\n\n    assert_eq!(got.len(), 5);\n\n    // checking the chapter names are in the order should be sufficient here...\n    let chapter_names: Vec<String> = got\n        .into_iter()\n        .filter_map(|i| match *i {\n            BookItem::Chapter(ref ch) => Some(ch.name.clone()),\n            _ => None,\n        })\n        .collect();\n    let should_be: Vec<_> = vec![\n        String::from(\"Chapter 1\"),\n        String::from(\"Hello World\"),\n        String::from(\"Goodbye World\"),\n    ];\n\n    assert_eq!(chapter_names, should_be);\n}\n"
  },
  {
    "path": "crates/mdbook-core/src/book.rs",
    "content": "//! A tree structure representing a book.\n\nuse serde::{Deserialize, Serialize};\nuse std::collections::VecDeque;\nuse std::fmt::{self, Display, Formatter};\nuse std::ops::{Deref, DerefMut};\nuse std::path::PathBuf;\n\n#[cfg(test)]\nmod tests;\n\n/// A tree structure representing a book.\n///\n/// A book is just a collection of [`BookItems`] which are accessible by\n/// either iterating (immutably) over the book with [`iter()`], or recursively\n/// applying a closure to each item to mutate the chapters, using\n/// [`for_each_mut()`].\n///\n/// [`iter()`]: #method.iter\n/// [`for_each_mut()`]: #method.for_each_mut\n#[allow(\n    clippy::exhaustive_structs,\n    reason = \"This cannot be extended without breaking preprocessors.\"\n)]\n#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]\npub struct Book {\n    /// The items in this book.\n    pub items: Vec<BookItem>,\n}\n\nimpl Book {\n    /// Create an empty book.\n    pub fn new() -> Self {\n        Default::default()\n    }\n\n    /// Creates a new book with the given items.\n    pub fn new_with_items(items: Vec<BookItem>) -> Book {\n        Book { items }\n    }\n\n    /// Get a depth-first iterator over the items in the book.\n    pub fn iter(&self) -> BookItems<'_> {\n        BookItems {\n            items: self.items.iter().collect(),\n        }\n    }\n\n    /// A depth-first iterator over each [`Chapter`], skipping draft chapters.\n    pub fn chapters(&self) -> impl Iterator<Item = &Chapter> {\n        self.iter().filter_map(|item| match item {\n            BookItem::Chapter(ch) if !ch.is_draft_chapter() => Some(ch),\n            _ => None,\n        })\n    }\n\n    /// Recursively apply a closure to each item in the book, allowing you to\n    /// mutate them.\n    ///\n    /// # Note\n    ///\n    /// Unlike the `iter()` method, this requires a closure instead of returning\n    /// an iterator. This is because using iterators can possibly allow you\n    /// to have iterator invalidation errors.\n    pub fn for_each_mut<F>(&mut self, mut func: F)\n    where\n        F: FnMut(&mut BookItem),\n    {\n        for_each_mut(&mut func, &mut self.items);\n    }\n\n    /// Recursively apply a closure to each non-draft chapter in the book,\n    /// allowing you to mutate them.\n    pub fn for_each_chapter_mut<F>(&mut self, mut func: F)\n    where\n        F: FnMut(&mut Chapter),\n    {\n        for_each_mut(\n            &mut |item| {\n                let BookItem::Chapter(ch) = item else {\n                    return;\n                };\n                if ch.is_draft_chapter() {\n                    return;\n                }\n                func(ch)\n            },\n            &mut self.items,\n        );\n    }\n\n    /// Append a `BookItem` to the `Book`.\n    pub fn push_item<I: Into<BookItem>>(&mut self, item: I) -> &mut Self {\n        self.items.push(item.into());\n        self\n    }\n}\n\nfn for_each_mut<'a, F, I>(func: &mut F, items: I)\nwhere\n    F: FnMut(&mut BookItem),\n    I: IntoIterator<Item = &'a mut BookItem>,\n{\n    for item in items {\n        if let BookItem::Chapter(ch) = item {\n            for_each_mut(func, &mut ch.sub_items);\n        }\n\n        func(item);\n    }\n}\n\n/// Enum representing any type of item which can be added to a book.\n#[allow(\n    clippy::exhaustive_enums,\n    reason = \"This cannot be extended without breaking preprocessors.\"\n)]\n#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]\npub enum BookItem {\n    /// A nested chapter.\n    Chapter(Chapter),\n    /// A section separator.\n    Separator,\n    /// A part title.\n    PartTitle(String),\n}\n\nimpl From<Chapter> for BookItem {\n    fn from(other: Chapter) -> BookItem {\n        BookItem::Chapter(other)\n    }\n}\n\n/// The representation of a \"chapter\", usually mapping to a single file on\n/// disk however it may contain multiple sub-chapters.\n#[allow(\n    clippy::exhaustive_structs,\n    reason = \"This cannot be extended without breaking preprocessors.\"\n)]\n#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]\npub struct Chapter {\n    /// The chapter's name.\n    pub name: String,\n    /// The chapter's contents.\n    pub content: String,\n    /// The chapter's section number, if it has one.\n    pub number: Option<SectionNumber>,\n    /// Nested items.\n    pub sub_items: Vec<BookItem>,\n    /// The chapter's location, relative to the `SUMMARY.md` file.\n    ///\n    /// **Note**: After the index preprocessor runs, any README files will be\n    /// modified to be `index.md`. If you need access to the actual filename\n    /// on disk, use [`Chapter::source_path`] instead.\n    ///\n    /// This is `None` for a draft chapter.\n    pub path: Option<PathBuf>,\n    /// The chapter's source file, relative to the `SUMMARY.md` file.\n    ///\n    /// **Note**: Beware that README files will internally be treated as\n    /// `index.md` via the [`Chapter::path`] field. The `source_path` field\n    /// exists if you need access to the true file path.\n    ///\n    /// This is `None` for a draft chapter, or a synthetically generated\n    /// chapter that has no file on disk.\n    pub source_path: Option<PathBuf>,\n    /// An ordered list of the names of each chapter above this one in the hierarchy.\n    pub parent_names: Vec<String>,\n}\n\nimpl Chapter {\n    /// Create a new chapter with the provided content.\n    pub fn new<P: Into<PathBuf>>(\n        name: &str,\n        content: String,\n        p: P,\n        parent_names: Vec<String>,\n    ) -> Chapter {\n        let path: PathBuf = p.into();\n        Chapter {\n            name: name.to_string(),\n            content,\n            path: Some(path.clone()),\n            source_path: Some(path),\n            parent_names,\n            ..Default::default()\n        }\n    }\n\n    /// Create a new draft chapter that is not attached to a source markdown file (and thus\n    /// has no content).\n    pub fn new_draft(name: &str, parent_names: Vec<String>) -> Self {\n        Chapter {\n            name: name.to_string(),\n            content: String::new(),\n            path: None,\n            source_path: None,\n            parent_names,\n            ..Default::default()\n        }\n    }\n\n    /// Check if the chapter is a draft chapter, meaning it has no path to a source markdown file.\n    pub fn is_draft_chapter(&self) -> bool {\n        self.path.is_none()\n    }\n}\n\nimpl Display for Chapter {\n    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {\n        if let Some(ref section_number) = self.number {\n            write!(f, \"{section_number} \")?;\n        }\n\n        write!(f, \"{}\", self.name)\n    }\n}\n\n/// A section number like \"1.2.3\", basically just a newtype'd `Vec<u32>` with\n/// a pretty `Display` impl.\n#[derive(Debug, PartialEq, Clone, Default, Serialize, Deserialize)]\npub struct SectionNumber(Vec<u32>);\n\nimpl SectionNumber {\n    /// Creates a new [`SectionNumber`].\n    pub fn new(numbers: impl Into<Vec<u32>>) -> SectionNumber {\n        SectionNumber(numbers.into())\n    }\n}\n\nimpl Display for SectionNumber {\n    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {\n        if self.0.is_empty() {\n            write!(f, \"0\")\n        } else {\n            for item in &self.0 {\n                write!(f, \"{item}.\")?;\n            }\n            Ok(())\n        }\n    }\n}\n\nimpl Deref for SectionNumber {\n    type Target = Vec<u32>;\n    fn deref(&self) -> &Self::Target {\n        &self.0\n    }\n}\n\nimpl DerefMut for SectionNumber {\n    fn deref_mut(&mut self) -> &mut Self::Target {\n        &mut self.0\n    }\n}\n\nimpl FromIterator<u32> for SectionNumber {\n    fn from_iter<I: IntoIterator<Item = u32>>(it: I) -> Self {\n        SectionNumber(it.into_iter().collect())\n    }\n}\n\n/// A depth-first iterator over the items in a book.\n///\n/// # Note\n///\n/// This struct shouldn't be created directly, instead prefer the\n/// [`Book::iter()`] method.\npub struct BookItems<'a> {\n    items: VecDeque<&'a BookItem>,\n}\n\nimpl<'a> Iterator for BookItems<'a> {\n    type Item = &'a BookItem;\n\n    fn next(&mut self) -> Option<Self::Item> {\n        let item = self.items.pop_front();\n\n        if let Some(BookItem::Chapter(ch)) = item {\n            // if we wanted a breadth-first iterator we'd `extend()` here\n            for sub_item in ch.sub_items.iter().rev() {\n                self.items.push_front(sub_item);\n            }\n        }\n\n        item\n    }\n}\n"
  },
  {
    "path": "crates/mdbook-core/src/config.rs",
    "content": "//! Mdbook's configuration system.\n//!\n//! The main entrypoint of the `config` module is the `Config` struct. This acts\n//! essentially as a bag of configuration information, with a couple\n//! pre-determined tables ([`BookConfig`] and [`BuildConfig`]) as well as support\n//! for arbitrary data which is exposed to plugins and alternative backends.\n//!\n//!\n//! # Examples\n//!\n//! ```rust\n//! # use anyhow::Result;\n//! use std::path::PathBuf;\n//! use std::str::FromStr;\n//! use mdbook_core::config::Config;\n//!\n//! # fn run() -> Result<()> {\n//! let src = r#\"\n//! [book]\n//! title = \"My Book\"\n//! authors = [\"Michael-F-Bryan\"]\n//!\n//! [preprocessor.my-preprocessor]\n//! bar = 123\n//! \"#;\n//!\n//! // load the `Config` from a toml string\n//! let mut cfg = Config::from_str(src)?;\n//!\n//! // retrieve a nested value\n//! let bar = cfg.get::<i32>(\"preprocessor.my-preprocessor.bar\")?;\n//! assert_eq!(bar, Some(123));\n//!\n//! // Set the `output.html.theme` directory\n//! assert!(cfg.get::<toml::Value>(\"output.html\")?.is_none());\n//! cfg.set(\"output.html.theme\", \"./themes\");\n//!\n//! // then load it again, automatically deserializing to a `PathBuf`.\n//! let got = cfg.get(\"output.html.theme\")?;\n//! assert_eq!(got, Some(PathBuf::from(\"./themes\")));\n//! # Ok(())\n//! # }\n//! # run().unwrap()\n//! ```\n\nuse crate::static_regex;\nuse crate::utils::{TomlExt, fs, log_backtrace};\nuse anyhow::{Context, Error, Result, bail};\nuse serde::{Deserialize, Serialize};\nuse std::collections::{BTreeMap, HashMap};\nuse std::env;\nuse std::path::{Path, PathBuf};\nuse std::str::FromStr;\nuse toml::Value;\nuse toml::value::Table;\nuse tracing::{debug, trace};\n\n/// The overall configuration object for MDBook, essentially an in-memory\n/// representation of `book.toml`.\n#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]\n#[serde(default, deny_unknown_fields)]\n#[non_exhaustive]\npub struct Config {\n    /// Metadata about the book.\n    pub book: BookConfig,\n    /// Information about the build environment.\n    #[serde(skip_serializing_if = \"is_default\")]\n    pub build: BuildConfig,\n    /// Information about Rust language support.\n    #[serde(skip_serializing_if = \"is_default\")]\n    pub rust: RustConfig,\n    /// The renderer configurations.\n    #[serde(skip_serializing_if = \"toml_is_empty\")]\n    output: Value,\n    /// The preprocessor configurations.\n    #[serde(skip_serializing_if = \"toml_is_empty\")]\n    preprocessor: Value,\n}\n\n/// Helper for serde serialization.\nfn is_default<T: Default + PartialEq>(t: &T) -> bool {\n    t == &T::default()\n}\n\n/// Helper for serde serialization.\nfn toml_is_empty(table: &Value) -> bool {\n    table.as_table().unwrap().is_empty()\n}\n\nimpl FromStr for Config {\n    type Err = Error;\n\n    /// Load a `Config` from some string.\n    fn from_str(src: &str) -> Result<Self> {\n        toml::from_str(src).with_context(|| \"Invalid configuration file\")\n    }\n}\n\nimpl Default for Config {\n    fn default() -> Config {\n        Config {\n            book: BookConfig::default(),\n            build: BuildConfig::default(),\n            rust: RustConfig::default(),\n            output: Value::Table(Table::default()),\n            preprocessor: Value::Table(Table::default()),\n        }\n    }\n}\n\nimpl Config {\n    /// Load the configuration file from disk.\n    pub fn from_disk<P: AsRef<Path>>(config_file: P) -> Result<Config> {\n        let cfg = fs::read_to_string(config_file)?;\n        Config::from_str(&cfg)\n    }\n\n    /// Updates the `Config` from the available environment variables.\n    ///\n    /// Variables starting with `MDBOOK_` are used for configuration. The key is\n    /// created by removing the `MDBOOK_` prefix and turning the resulting\n    /// string into `kebab-case`. Double underscores (`__`) separate nested\n    /// keys, while a single underscore (`_`) is replaced with a dash (`-`).\n    ///\n    /// For example:\n    ///\n    /// - `MDBOOK_book` -> `book`\n    /// - `MDBOOK_BOOK` -> `book`\n    /// - `MDBOOK_BOOK__TITLE` -> `book.title`\n    /// - `MDBOOK_BOOK__TEXT_DIRECTION` -> `book.text-direction`\n    ///\n    /// So by setting the `MDBOOK_BOOK__TITLE` environment variable you can\n    /// override the book's title without needing to touch your `book.toml`.\n    ///\n    /// > **Note:** To facilitate setting more complex config items, the value\n    /// > of an environment variable is first parsed as JSON, falling back to a\n    /// > string if the parse fails.\n    /// >\n    /// > This means, if you so desired, you could override all book metadata\n    /// > when building the book with something like\n    /// >\n    /// > ```text\n    /// > $ export MDBOOK_BOOK='{\"title\": \"My Awesome Book\", \"authors\": [\"Michael-F-Bryan\"]}'\n    /// > $ mdbook build\n    /// > ```\n    ///\n    /// The latter case may be useful in situations where `mdbook` is invoked\n    /// from a script or CI, where it sometimes isn't possible to update the\n    /// `book.toml` before building.\n    pub fn update_from_env(&mut self) -> Result<()> {\n        debug!(\"Updating the config from environment variables\");\n\n        static_regex!(\n            VALID_KEY,\n            r\"^(:?book|build|rust|output|preprocessor)(:?$|\\.)\"\n        );\n\n        let overrides =\n            env::vars().filter_map(|(key, value)| parse_env(&key).map(|index| (index, value)));\n\n        for (key, value) in overrides {\n            trace!(\"{} => {}\", key, value);\n            if !VALID_KEY.is_match(&key) {\n                // Ignore environment variables for other top-level things.\n                // This allows users to set things like `MDBOOK_VERSION` or\n                // `MDBOOK_DOWNLOAD_URL` for their own scripts and not\n                // interfere with how the config is loaded.\n                continue;\n            }\n            let parsed_value = serde_json::from_str(&value)\n                .unwrap_or_else(|_| serde_json::Value::String(value.to_string()));\n\n            self.set(key, parsed_value)?;\n        }\n        Ok(())\n    }\n\n    /// Get a value from the configuration.\n    ///\n    /// This fetches a value from the book configuration. The key can have\n    /// dotted indices to access nested items (e.g. `output.html.playground`\n    /// will fetch the \"playground\" out of the html output table).\n    ///\n    /// This can only access the `output` and `preprocessor` tables.\n    ///\n    /// Returns `Ok(None)` if the field is not set.\n    ///\n    /// Returns `Err` if it fails to deserialize.\n    pub fn get<'de, T: Deserialize<'de>>(&self, name: &str) -> Result<Option<T>> {\n        let (key, table) = if let Some(key) = name.strip_prefix(\"output.\") {\n            (key, &self.output)\n        } else if let Some(key) = name.strip_prefix(\"preprocessor.\") {\n            (key, &self.preprocessor)\n        } else {\n            bail!(\n                \"unable to get `{name}`, only `output` and `preprocessor` table entries are allowed\"\n            );\n        };\n        table\n            .read(key)\n            .map(|value| {\n                value\n                    .clone()\n                    .try_into()\n                    .with_context(|| format!(\"Failed to deserialize `{name}`\"))\n            })\n            .transpose()\n    }\n\n    /// Returns whether the config contains the given dotted key name.\n    ///\n    /// The key can have dotted indices to access nested items (e.g.\n    /// `preprocessor.foo.bar` will check if that key is set in the config).\n    ///\n    /// This can only access the `output` and `preprocessor` tables.\n    pub fn contains_key(&self, name: &str) -> bool {\n        if let Some(key) = name.strip_prefix(\"output.\") {\n            self.output.read(key)\n        } else if let Some(key) = name.strip_prefix(\"preprocessor.\") {\n            self.preprocessor.read(key)\n        } else {\n            panic!(\"invalid key `{name}`\");\n        }\n        .is_some()\n    }\n\n    /// Returns the configuration for all preprocessors.\n    pub fn preprocessors<'de, T: Deserialize<'de>>(&self) -> Result<BTreeMap<String, T>> {\n        self.preprocessor\n            .clone()\n            .try_into()\n            .with_context(|| \"Failed to read preprocessors\")\n    }\n\n    /// Returns the configuration for all renderers.\n    pub fn outputs<'de, T: Deserialize<'de>>(&self) -> Result<BTreeMap<String, T>> {\n        self.output\n            .clone()\n            .try_into()\n            .with_context(|| \"Failed to read renderers\")\n    }\n\n    /// Convenience method for getting the html renderer's configuration.\n    ///\n    /// # Note\n    ///\n    /// This is for compatibility only. It will be removed completely once the\n    /// HTML renderer is refactored to be less coupled to `mdbook` internals.\n    #[doc(hidden)]\n    pub fn html_config(&self) -> Option<HtmlConfig> {\n        match self.get(\"output.html\") {\n            Ok(Some(config)) => Some(config),\n            Ok(None) => None,\n            Err(e) => {\n                log_backtrace(&e);\n                None\n            }\n        }\n    }\n\n    /// Set a config key, clobbering any existing values along the way.\n    ///\n    /// The key can have dotted indices for nested items (e.g.\n    /// `output.html.playground` will set the \"playground\" in the html output\n    /// table).\n    ///\n    /// # Errors\n    ///\n    /// This will fail if:\n    ///\n    /// - The value cannot be represented as TOML.\n    /// - The value is not a correct type.\n    /// - The key is an unknown configuration option.\n    pub fn set<S: Serialize, I: AsRef<str>>(&mut self, index: I, value: S) -> Result<()> {\n        let index = index.as_ref();\n\n        let value = Value::try_from(value)\n            .with_context(|| \"Unable to represent the item as a JSON Value\")?;\n\n        if index == \"book\" {\n            self.book = value.try_into()?;\n        } else if index == \"build\" {\n            self.build = value.try_into()?;\n        } else if index == \"rust\" {\n            self.rust = value.try_into()?;\n        } else if index == \"output\" {\n            self.output = value;\n        } else if index == \"preprocessor\" {\n            self.preprocessor = value;\n        } else if let Some(key) = index.strip_prefix(\"book.\") {\n            self.book.update_value(key, value)?;\n        } else if let Some(key) = index.strip_prefix(\"build.\") {\n            self.build.update_value(key, value)?;\n        } else if let Some(key) = index.strip_prefix(\"rust.\") {\n            self.rust.update_value(key, value)?;\n        } else if let Some(key) = index.strip_prefix(\"output.\") {\n            self.output.update_value(key, value)?;\n        } else if let Some(key) = index.strip_prefix(\"preprocessor.\") {\n            self.preprocessor.update_value(key, value)?;\n        } else {\n            bail!(\"invalid key `{index}`\");\n        }\n\n        Ok(())\n    }\n}\n\nfn parse_env(key: &str) -> Option<String> {\n    key.strip_prefix(\"MDBOOK_\")\n        .map(|key| key.to_lowercase().replace(\"__\", \".\").replace('_', \"-\"))\n}\n\n/// Configuration options which are specific to the book and required for\n/// loading it from disk.\n#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]\n#[serde(default, rename_all = \"kebab-case\", deny_unknown_fields)]\n#[non_exhaustive]\npub struct BookConfig {\n    /// The book's title.\n    pub title: Option<String>,\n    /// The book's authors.\n    pub authors: Vec<String>,\n    /// An optional description for the book.\n    pub description: Option<String>,\n    /// Location of the book source relative to the book's root directory.\n    #[serde(skip_serializing_if = \"is_default_src\")]\n    pub src: PathBuf,\n    /// The main language of the book.\n    pub language: Option<String>,\n    /// The direction of text in the book: Left-to-right (LTR) or Right-to-left (RTL).\n    /// When not specified, the text direction is derived from [`BookConfig::language`].\n    pub text_direction: Option<TextDirection>,\n}\n\n/// Helper for serde serialization.\nfn is_default_src(src: &PathBuf) -> bool {\n    src == Path::new(\"src\")\n}\n\nimpl Default for BookConfig {\n    fn default() -> BookConfig {\n        BookConfig {\n            title: None,\n            authors: Vec::new(),\n            description: None,\n            src: PathBuf::from(\"src\"),\n            language: Some(String::from(\"en\")),\n            text_direction: None,\n        }\n    }\n}\n\nimpl BookConfig {\n    /// Gets the realized text direction, either from [`BookConfig::text_direction`]\n    /// or derived from [`BookConfig::language`], to be used by templating engines.\n    pub fn realized_text_direction(&self) -> TextDirection {\n        if let Some(direction) = self.text_direction {\n            direction\n        } else {\n            TextDirection::from_lang_code(self.language.as_deref().unwrap_or_default())\n        }\n    }\n}\n\n/// Text direction to use for HTML output\n#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]\n#[non_exhaustive]\npub enum TextDirection {\n    /// Left to right.\n    #[serde(rename = \"ltr\")]\n    LeftToRight,\n    /// Right to left\n    #[serde(rename = \"rtl\")]\n    RightToLeft,\n}\n\nimpl TextDirection {\n    /// Gets the text direction from language code\n    pub fn from_lang_code(code: &str) -> Self {\n        match code {\n            // list sourced from here: https://github.com/abarrak/rtl/blob/master/lib/rtl/core.rb#L16\n            \"ar\" | \"ara\" | \"arc\" | \"ae\" | \"ave\" | \"egy\" | \"he\" | \"heb\" | \"nqo\" | \"pal\" | \"phn\"\n            | \"sam\" | \"syc\" | \"syr\" | \"fa\" | \"per\" | \"fas\" | \"ku\" | \"kur\" | \"ur\" | \"urd\"\n            | \"pus\" | \"ps\" | \"yi\" | \"yid\" => TextDirection::RightToLeft,\n            _ => TextDirection::LeftToRight,\n        }\n    }\n}\n\n/// Configuration for the build procedure.\n#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]\n#[serde(default, rename_all = \"kebab-case\", deny_unknown_fields)]\n#[non_exhaustive]\npub struct BuildConfig {\n    /// Where to put built artifacts relative to the book's root directory.\n    pub build_dir: PathBuf,\n    /// Should non-existent markdown files specified in `SUMMARY.md` be created\n    /// if they don't exist?\n    pub create_missing: bool,\n    /// Should the default preprocessors always be used when they are\n    /// compatible with the renderer?\n    pub use_default_preprocessors: bool,\n    /// Extra directories to trigger rebuild when watching/serving\n    pub extra_watch_dirs: Vec<PathBuf>,\n}\n\nimpl Default for BuildConfig {\n    fn default() -> BuildConfig {\n        BuildConfig {\n            build_dir: PathBuf::from(\"book\"),\n            create_missing: true,\n            use_default_preprocessors: true,\n            extra_watch_dirs: Vec::new(),\n        }\n    }\n}\n\n/// Configuration for the Rust compiler(e.g., for playground)\n#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]\n#[serde(default, rename_all = \"kebab-case\", deny_unknown_fields)]\n#[non_exhaustive]\npub struct RustConfig {\n    /// Rust edition used in playground\n    pub edition: Option<RustEdition>,\n}\n\n/// Rust edition to use for the code.\n#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]\n#[non_exhaustive]\npub enum RustEdition {\n    /// The 2024 edition of Rust\n    #[serde(rename = \"2024\")]\n    E2024,\n    /// The 2021 edition of Rust\n    #[serde(rename = \"2021\")]\n    E2021,\n    /// The 2018 edition of Rust\n    #[serde(rename = \"2018\")]\n    E2018,\n    /// The 2015 edition of Rust\n    #[serde(rename = \"2015\")]\n    E2015,\n}\n\n/// Configuration for the HTML renderer.\n#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]\n#[serde(default, rename_all = \"kebab-case\", deny_unknown_fields)]\n#[non_exhaustive]\npub struct HtmlConfig {\n    /// The theme directory, if specified.\n    pub theme: Option<PathBuf>,\n    /// The default theme to use, defaults to 'light'\n    pub default_theme: Option<String>,\n    /// The theme to use if the browser requests the dark version of the site.\n    /// Defaults to 'navy'.\n    pub preferred_dark_theme: Option<String>,\n    /// Supports smart quotes, apostrophes, ellipsis, en-dash, and em-dash.\n    pub smart_punctuation: bool,\n    /// Support for definition lists.\n    pub definition_lists: bool,\n    /// Support for admonitions.\n    pub admonitions: bool,\n    /// Should mathjax be enabled?\n    pub mathjax_support: bool,\n    /// Additional CSS stylesheets to include in the rendered page's `<head>`.\n    pub additional_css: Vec<PathBuf>,\n    /// Additional JS scripts to include at the bottom of the rendered page's\n    /// `<body>`.\n    pub additional_js: Vec<PathBuf>,\n    /// Fold settings.\n    pub fold: Fold,\n    /// Playground settings.\n    #[serde(alias = \"playpen\")]\n    pub playground: Playground,\n    /// Code settings.\n    pub code: Code,\n    /// Print settings.\n    pub print: Print,\n    /// Don't render section labels.\n    pub no_section_label: bool,\n    /// Search settings. If `None`, the default will be used.\n    pub search: Option<Search>,\n    /// Git repository url. If `None`, the git button will not be shown.\n    pub git_repository_url: Option<String>,\n    /// FontAwesome icon class to use for the Git repository link.\n    /// Defaults to `fa-github` if `None`.\n    pub git_repository_icon: Option<String>,\n    /// Input path for the 404 file, defaults to 404.md, set to \"\" to disable 404 file output\n    pub input_404: Option<String>,\n    /// Absolute url to site, used to emit correct paths for the 404 page, which might be accessed in a deeply nested directory\n    pub site_url: Option<String>,\n    /// The DNS subdomain or apex domain at which your book will be hosted. This\n    /// string will be written to a file named CNAME in the root of your site,\n    /// as required by GitHub Pages (see [*Managing a custom domain for your\n    /// GitHub Pages site*][custom domain]).\n    ///\n    /// [custom domain]: https://docs.github.com/en/github/working-with-github-pages/managing-a-custom-domain-for-your-github-pages-site\n    pub cname: Option<String>,\n    /// Edit url template, when set shows a \"Suggest an edit\" button for\n    /// directly jumping to editing the currently viewed page.\n    /// Contains {path} that is replaced with chapter source file path\n    pub edit_url_template: Option<String>,\n    /// Endpoint of websocket, for livereload usage. Value loaded from .toml\n    /// file is ignored, because our code overrides this field with an\n    /// internal value (`LIVE_RELOAD_ENDPOINT)\n    ///\n    /// This config item *should not be edited* by the end user.\n    #[doc(hidden)]\n    pub live_reload_endpoint: Option<String>,\n    /// The mapping from old pages to new pages/URLs to use when generating\n    /// redirects.\n    pub redirect: HashMap<String, String>,\n    /// If this option is turned on, \"cache bust\" static files by adding\n    /// hashes to their file names.\n    ///\n    /// The default is `true`.\n    pub hash_files: bool,\n    /// If enabled, the sidebar includes navigation for headers on the current\n    /// page. Default is `true`.\n    pub sidebar_header_nav: bool,\n}\n\nimpl Default for HtmlConfig {\n    fn default() -> HtmlConfig {\n        HtmlConfig {\n            theme: None,\n            default_theme: None,\n            preferred_dark_theme: None,\n            smart_punctuation: true,\n            definition_lists: true,\n            admonitions: true,\n            mathjax_support: false,\n            additional_css: Vec::new(),\n            additional_js: Vec::new(),\n            fold: Fold::default(),\n            playground: Playground::default(),\n            code: Code::default(),\n            print: Print::default(),\n            no_section_label: false,\n            search: None,\n            git_repository_url: None,\n            git_repository_icon: None,\n            input_404: None,\n            site_url: None,\n            cname: None,\n            edit_url_template: None,\n            live_reload_endpoint: None,\n            redirect: HashMap::new(),\n            hash_files: true,\n            sidebar_header_nav: true,\n        }\n    }\n}\n\nimpl HtmlConfig {\n    /// Returns the directory of theme from the provided root directory. If the\n    /// directory is not present it will append the default directory of \"theme\"\n    pub fn theme_dir(&self, root: &Path) -> PathBuf {\n        match self.theme {\n            Some(ref d) => root.join(d),\n            None => root.join(\"theme\"),\n        }\n    }\n\n    /// Returns the name of the file used for HTTP 404 \"not found\" with the `.html` extension.\n    pub fn get_404_output_file(&self) -> String {\n        self.input_404\n            .as_ref()\n            .unwrap_or(&\"404.md\".to_string())\n            .replace(\".md\", \".html\")\n    }\n}\n\n/// Configuration for how to render the print icon, print.html, and print.css.\n#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]\n#[serde(default, rename_all = \"kebab-case\", deny_unknown_fields)]\n#[non_exhaustive]\npub struct Print {\n    /// Whether print support is enabled.\n    pub enable: bool,\n    /// Insert page breaks between chapters. Default: `true`.\n    pub page_break: bool,\n}\n\nimpl Default for Print {\n    fn default() -> Self {\n        Self {\n            enable: true,\n            page_break: true,\n        }\n    }\n}\n\n/// Configuration for how to fold chapters of sidebar.\n#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]\n#[serde(default, rename_all = \"kebab-case\", deny_unknown_fields)]\n#[non_exhaustive]\npub struct Fold {\n    /// When off, all folds are open. Default: `false`.\n    pub enable: bool,\n    /// The higher the more folded regions are open. When level is 0, all folds\n    /// are closed.\n    /// Default: `0`.\n    pub level: u8,\n}\n\n/// Configuration for tweaking how the HTML renderer handles the playground.\n#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]\n#[serde(default, rename_all = \"kebab-case\", deny_unknown_fields)]\n#[non_exhaustive]\npub struct Playground {\n    /// Should playground snippets be editable? Default: `false`.\n    pub editable: bool,\n    /// Display the copy button. Default: `true`.\n    pub copyable: bool,\n    /// Copy JavaScript files for the editor to the output directory?\n    /// Default: `true`.\n    pub copy_js: bool,\n    /// Display line numbers on playground snippets. Default: `false`.\n    pub line_numbers: bool,\n    /// Display the run button. Default: `true`\n    pub runnable: bool,\n}\n\nimpl Default for Playground {\n    fn default() -> Playground {\n        Playground {\n            editable: false,\n            copyable: true,\n            copy_js: true,\n            line_numbers: false,\n            runnable: true,\n        }\n    }\n}\n\n/// Configuration for tweaking how the HTML renderer handles code blocks.\n#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]\n#[serde(default, rename_all = \"kebab-case\", deny_unknown_fields)]\n#[non_exhaustive]\npub struct Code {\n    /// A prefix string to hide lines per language (one or more chars).\n    pub hidelines: HashMap<String, String>,\n}\n\n/// Configuration of the search functionality of the HTML renderer.\n#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]\n#[serde(default, rename_all = \"kebab-case\", deny_unknown_fields)]\n#[non_exhaustive]\npub struct Search {\n    /// Enable the search feature. Default: `true`.\n    pub enable: bool,\n    /// Maximum number of visible results. Default: `30`.\n    pub limit_results: u32,\n    /// The number of words used for a search result teaser. Default: `30`.\n    pub teaser_word_count: u32,\n    /// Define the logical link between multiple search words.\n    /// If true, all search words must appear in each result. Default: `false`.\n    pub use_boolean_and: bool,\n    /// Boost factor for the search result score if a search word appears in the header.\n    /// Default: `2`.\n    pub boost_title: u8,\n    /// Boost factor for the search result score if a search word appears in the hierarchy.\n    /// The hierarchy contains all titles of the parent documents and all parent headings.\n    /// Default: `1`.\n    pub boost_hierarchy: u8,\n    /// Boost factor for the search result score if a search word appears in the text.\n    /// Default: `1`.\n    pub boost_paragraph: u8,\n    /// True if the searchword `micro` should match `microwave`. Default: `true`.\n    pub expand: bool,\n    /// Documents are split into smaller parts, separated by headings. This defines, until which\n    /// level of heading documents should be split. Default: `3`. (`### This is a level 3 heading`)\n    pub heading_split_level: u8,\n    /// Copy JavaScript files for the search functionality to the output directory?\n    /// Default: `true`.\n    pub copy_js: bool,\n    /// Specifies search settings for the given path.\n    ///\n    /// The path can be for a specific chapter, or a directory. This will\n    /// merge recursively, with more specific paths taking precedence.\n    pub chapter: HashMap<String, SearchChapterSettings>,\n}\n\nimpl Default for Search {\n    fn default() -> Search {\n        // Please update the documentation of `Search` when changing values!\n        Search {\n            enable: true,\n            limit_results: 30,\n            teaser_word_count: 30,\n            use_boolean_and: false,\n            boost_title: 2,\n            boost_hierarchy: 1,\n            boost_paragraph: 1,\n            expand: true,\n            heading_split_level: 3,\n            copy_js: true,\n            chapter: HashMap::new(),\n        }\n    }\n}\n\n/// Search options for chapters (or paths).\n#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]\n#[serde(default, rename_all = \"kebab-case\", deny_unknown_fields)]\n#[non_exhaustive]\npub struct SearchChapterSettings {\n    /// Whether or not indexing is enabled, default `true`.\n    pub enable: Option<bool>,\n}\n\n/// Allows you to \"update\" any arbitrary field in a struct by round-tripping via\n/// a `toml::Value`.\n///\n/// This is definitely not the most performant way to do things, which means you\n/// should probably keep it away from tight loops...\ntrait Updateable<'de>: Serialize + Deserialize<'de> {\n    fn update_value<S: Serialize>(&mut self, key: &str, value: S) -> Result<()> {\n        let mut raw = Value::try_from(&self).expect(\"unreachable\");\n        let value = Value::try_from(value)?;\n        raw.insert(key, value);\n        let updated = raw.try_into()?;\n        *self = updated;\n        Ok(())\n    }\n}\n\nimpl<'de, T> Updateable<'de> for T where T: Serialize + Deserialize<'de> {}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    const COMPLEX_CONFIG: &str = r#\"\n        [book]\n        title = \"Some Book\"\n        authors = [\"Michael-F-Bryan <michaelfbryan@gmail.com>\"]\n        description = \"A completely useless book\"\n        src = \"source\"\n        language = \"ja\"\n\n        [build]\n        build-dir = \"outputs\"\n        create-missing = false\n        use-default-preprocessors = true\n\n        [output.html]\n        theme = \"./themedir\"\n        default-theme = \"rust\"\n        smart-punctuation = true\n        additional-css = [\"./foo/bar/baz.css\"]\n        git-repository-url = \"https://foo.com/\"\n        git-repository-icon = \"fa-code-fork\"\n\n        [output.html.playground]\n        editable = true\n\n        [output.html.redirect]\n        \"index.html\" = \"overview.html\"\n        \"nexted/page.md\" = \"https://rust-lang.org/\"\n\n        [preprocessor.first]\n\n        [preprocessor.second]\n        \"#;\n\n    #[test]\n    fn load_a_complex_config_file() {\n        let src = COMPLEX_CONFIG;\n\n        let book_should_be = BookConfig {\n            title: Some(String::from(\"Some Book\")),\n            authors: vec![String::from(\"Michael-F-Bryan <michaelfbryan@gmail.com>\")],\n            description: Some(String::from(\"A completely useless book\")),\n            src: PathBuf::from(\"source\"),\n            language: Some(String::from(\"ja\")),\n            text_direction: None,\n        };\n        let build_should_be = BuildConfig {\n            build_dir: PathBuf::from(\"outputs\"),\n            create_missing: false,\n            use_default_preprocessors: true,\n            extra_watch_dirs: Vec::new(),\n        };\n        let rust_should_be = RustConfig { edition: None };\n        let playground_should_be = Playground {\n            editable: true,\n            copyable: true,\n            copy_js: true,\n            line_numbers: false,\n            runnable: true,\n        };\n        let html_should_be = HtmlConfig {\n            smart_punctuation: true,\n            additional_css: vec![PathBuf::from(\"./foo/bar/baz.css\")],\n            theme: Some(PathBuf::from(\"./themedir\")),\n            default_theme: Some(String::from(\"rust\")),\n            playground: playground_should_be,\n            git_repository_url: Some(String::from(\"https://foo.com/\")),\n            git_repository_icon: Some(String::from(\"fa-code-fork\")),\n            redirect: vec![\n                (String::from(\"index.html\"), String::from(\"overview.html\")),\n                (\n                    String::from(\"nexted/page.md\"),\n                    String::from(\"https://rust-lang.org/\"),\n                ),\n            ]\n            .into_iter()\n            .collect(),\n            ..Default::default()\n        };\n\n        let got = Config::from_str(src).unwrap();\n\n        assert_eq!(got.book, book_should_be);\n        assert_eq!(got.build, build_should_be);\n        assert_eq!(got.rust, rust_should_be);\n        assert_eq!(got.html_config().unwrap(), html_should_be);\n    }\n\n    #[test]\n    fn disable_runnable() {\n        let src = r#\"\n        [book]\n        title = \"Some Book\"\n        description = \"book book book\"\n        authors = [\"Shogo Takata\"]\n\n        [output.html.playground]\n        runnable = false\n        \"#;\n\n        let got = Config::from_str(src).unwrap();\n        assert!(!got.html_config().unwrap().playground.runnable);\n    }\n\n    #[test]\n    fn edition_2015() {\n        let src = r#\"\n        [book]\n        title = \"mdBook Documentation\"\n        description = \"Create book from markdown files. Like Gitbook but implemented in Rust\"\n        authors = [\"Mathieu David\"]\n        src = \"./source\"\n        [rust]\n        edition = \"2015\"\n        \"#;\n\n        let book_should_be = BookConfig {\n            title: Some(String::from(\"mdBook Documentation\")),\n            description: Some(String::from(\n                \"Create book from markdown files. Like Gitbook but implemented in Rust\",\n            )),\n            authors: vec![String::from(\"Mathieu David\")],\n            src: PathBuf::from(\"./source\"),\n            ..Default::default()\n        };\n\n        let got = Config::from_str(src).unwrap();\n        assert_eq!(got.book, book_should_be);\n\n        let rust_should_be = RustConfig {\n            edition: Some(RustEdition::E2015),\n        };\n        let got = Config::from_str(src).unwrap();\n        assert_eq!(got.rust, rust_should_be);\n    }\n\n    #[test]\n    fn edition_2018() {\n        let src = r#\"\n        [book]\n        title = \"mdBook Documentation\"\n        description = \"Create book from markdown files. Like Gitbook but implemented in Rust\"\n        authors = [\"Mathieu David\"]\n        src = \"./source\"\n        [rust]\n        edition = \"2018\"\n        \"#;\n\n        let rust_should_be = RustConfig {\n            edition: Some(RustEdition::E2018),\n        };\n\n        let got = Config::from_str(src).unwrap();\n        assert_eq!(got.rust, rust_should_be);\n    }\n\n    #[test]\n    fn edition_2021() {\n        let src = r#\"\n        [book]\n        title = \"mdBook Documentation\"\n        description = \"Create book from markdown files. Like Gitbook but implemented in Rust\"\n        authors = [\"Mathieu David\"]\n        src = \"./source\"\n        [rust]\n        edition = \"2021\"\n        \"#;\n\n        let rust_should_be = RustConfig {\n            edition: Some(RustEdition::E2021),\n        };\n\n        let got = Config::from_str(src).unwrap();\n        assert_eq!(got.rust, rust_should_be);\n    }\n\n    #[test]\n    fn load_arbitrary_output_type() {\n        #[derive(Debug, Deserialize, PartialEq)]\n        struct RandomOutput {\n            foo: u32,\n            bar: String,\n            baz: Vec<bool>,\n        }\n\n        let src = r#\"\n        [output.random]\n        foo = 5\n        bar = \"Hello World\"\n        baz = [true, true, false]\n        \"#;\n\n        let should_be = RandomOutput {\n            foo: 5,\n            bar: String::from(\"Hello World\"),\n            baz: vec![true, true, false],\n        };\n\n        let cfg = Config::from_str(src).unwrap();\n        let got: RandomOutput = cfg.get(\"output.random\").unwrap().unwrap();\n\n        assert_eq!(got, should_be);\n\n        let got_baz: Vec<bool> = cfg.get(\"output.random.baz\").unwrap().unwrap();\n        let baz_should_be = vec![true, true, false];\n\n        assert_eq!(got_baz, baz_should_be);\n    }\n\n    #[test]\n    fn set_special_tables() {\n        let mut cfg = Config::default();\n        assert_eq!(cfg.book.title, None);\n        cfg.set(\"book.title\", \"my title\").unwrap();\n        assert_eq!(cfg.book.title, Some(\"my title\".to_string()));\n\n        assert_eq!(&cfg.build.build_dir, Path::new(\"book\"));\n        cfg.set(\"build.build-dir\", \"some-directory\").unwrap();\n        assert_eq!(&cfg.build.build_dir, Path::new(\"some-directory\"));\n\n        assert_eq!(cfg.rust.edition, None);\n        cfg.set(\"rust.edition\", \"2024\").unwrap();\n        assert_eq!(cfg.rust.edition, Some(RustEdition::E2024));\n\n        cfg.set(\"output.foo.value\", \"123\").unwrap();\n        let got: String = cfg.get(\"output.foo.value\").unwrap().unwrap();\n        assert_eq!(got, \"123\");\n\n        cfg.set(\"preprocessor.bar.value\", \"456\").unwrap();\n        let got: String = cfg.get(\"preprocessor.bar.value\").unwrap().unwrap();\n        assert_eq!(got, \"456\");\n    }\n\n    #[test]\n    fn set_invalid_keys() {\n        let mut cfg = Config::default();\n        let err = cfg.set(\"foo\", \"test\").unwrap_err();\n        assert!(err.to_string().contains(\"invalid key `foo`\"));\n    }\n\n    #[test]\n    fn parse_env_vars() {\n        let inputs = vec![\n            (\"FOO\", None),\n            (\"MDBOOK_foo\", Some(\"foo\")),\n            (\"MDBOOK_FOO__bar__baz\", Some(\"foo.bar.baz\")),\n            (\"MDBOOK_FOO_bar__baz\", Some(\"foo-bar.baz\")),\n        ];\n\n        for (src, should_be) in inputs {\n            let got = parse_env(src);\n            let should_be = should_be.map(ToString::to_string);\n\n            assert_eq!(got, should_be);\n        }\n    }\n\n    #[test]\n    fn file_404_default() {\n        let src = r#\"\n        [output.html]\n        \"#;\n\n        let got = Config::from_str(src).unwrap();\n        let html_config = got.html_config().unwrap();\n        assert_eq!(html_config.input_404, None);\n        assert_eq!(html_config.get_404_output_file(), \"404.html\");\n    }\n\n    #[test]\n    fn file_404_custom() {\n        let src = r#\"\n        [output.html]\n        input-404= \"missing.md\"\n        \"#;\n\n        let got = Config::from_str(src).unwrap();\n        let html_config = got.html_config().unwrap();\n        assert_eq!(html_config.input_404, Some(\"missing.md\".to_string()));\n        assert_eq!(html_config.get_404_output_file(), \"missing.html\");\n    }\n\n    #[test]\n    fn text_direction_ltr() {\n        let src = r#\"\n        [book]\n        text-direction = \"ltr\"\n        \"#;\n\n        let got = Config::from_str(src).unwrap();\n        assert_eq!(got.book.text_direction, Some(TextDirection::LeftToRight));\n    }\n\n    #[test]\n    fn text_direction_rtl() {\n        let src = r#\"\n        [book]\n        text-direction = \"rtl\"\n        \"#;\n\n        let got = Config::from_str(src).unwrap();\n        assert_eq!(got.book.text_direction, Some(TextDirection::RightToLeft));\n    }\n\n    #[test]\n    fn text_direction_none() {\n        let src = r#\"\n        [book]\n        \"#;\n\n        let got = Config::from_str(src).unwrap();\n        assert_eq!(got.book.text_direction, None);\n    }\n\n    #[test]\n    fn test_text_direction() {\n        let mut cfg = BookConfig::default();\n\n        // test deriving the text direction from language codes\n        cfg.language = Some(\"ar\".into());\n        assert_eq!(cfg.realized_text_direction(), TextDirection::RightToLeft);\n\n        cfg.language = Some(\"he\".into());\n        assert_eq!(cfg.realized_text_direction(), TextDirection::RightToLeft);\n\n        cfg.language = Some(\"en\".into());\n        assert_eq!(cfg.realized_text_direction(), TextDirection::LeftToRight);\n\n        cfg.language = Some(\"ja\".into());\n        assert_eq!(cfg.realized_text_direction(), TextDirection::LeftToRight);\n\n        // test forced direction\n        cfg.language = Some(\"ar\".into());\n        cfg.text_direction = Some(TextDirection::LeftToRight);\n        assert_eq!(cfg.realized_text_direction(), TextDirection::LeftToRight);\n\n        cfg.language = Some(\"ar\".into());\n        cfg.text_direction = Some(TextDirection::RightToLeft);\n        assert_eq!(cfg.realized_text_direction(), TextDirection::RightToLeft);\n\n        cfg.language = Some(\"en\".into());\n        cfg.text_direction = Some(TextDirection::LeftToRight);\n        assert_eq!(cfg.realized_text_direction(), TextDirection::LeftToRight);\n\n        cfg.language = Some(\"en\".into());\n        cfg.text_direction = Some(TextDirection::RightToLeft);\n        assert_eq!(cfg.realized_text_direction(), TextDirection::RightToLeft);\n    }\n\n    #[test]\n    #[should_panic(expected = \"Invalid configuration file\")]\n    fn invalid_language_type_error() {\n        let src = r#\"\n        [book]\n        title = \"mdBook Documentation\"\n        language = [\"en\", \"pt-br\"]\n        description = \"Create book from markdown files. Like Gitbook but implemented in Rust\"\n        authors = [\"Mathieu David\"]\n        src = \"./source\"\n        \"#;\n\n        Config::from_str(src).unwrap();\n    }\n\n    #[test]\n    #[should_panic(expected = \"Invalid configuration file\")]\n    fn invalid_title_type() {\n        let src = r#\"\n        [book]\n        title = 20\n        language = \"en\"\n        description = \"Create book from markdown files. Like Gitbook but implemented in Rust\"\n        authors = [\"Mathieu David\"]\n        src = \"./source\"\n        \"#;\n\n        Config::from_str(src).unwrap();\n    }\n\n    #[test]\n    #[should_panic(expected = \"Invalid configuration file\")]\n    fn invalid_build_dir_type() {\n        let src = r#\"\n        [build]\n        build-dir = 99\n        create-missing = false\n        \"#;\n\n        Config::from_str(src).unwrap();\n    }\n\n    #[test]\n    #[should_panic(expected = \"Invalid configuration file\")]\n    fn invalid_rust_edition() {\n        let src = r#\"\n        [rust]\n        edition = \"1999\"\n        \"#;\n\n        Config::from_str(src).unwrap();\n    }\n\n    #[test]\n    #[should_panic(\n        expected = \"unknown variant `1999`, expected one of `2024`, `2021`, `2018`, `2015`\\n\"\n    )]\n    fn invalid_rust_edition_expected() {\n        let src = r#\"\n        [rust]\n        edition = \"1999\"\n        \"#;\n\n        Config::from_str(src).unwrap();\n    }\n\n    #[test]\n    fn print_config() {\n        let src = r#\"\n        [output.html.print]\n        enable = false\n        \"#;\n        let got = Config::from_str(src).unwrap();\n        let html_config = got.html_config().unwrap();\n        assert!(!html_config.print.enable);\n        assert!(html_config.print.page_break);\n        let src = r#\"\n        [output.html.print]\n        page-break = false\n        \"#;\n        let got = Config::from_str(src).unwrap();\n        let html_config = got.html_config().unwrap();\n        assert!(html_config.print.enable);\n        assert!(!html_config.print.page_break);\n    }\n\n    #[test]\n    fn test_json_direction() {\n        use serde_json::json;\n        assert_eq!(json!(TextDirection::RightToLeft), json!(\"rtl\"));\n        assert_eq!(json!(TextDirection::LeftToRight), json!(\"ltr\"));\n    }\n\n    #[test]\n    fn get_deserialize_error() {\n        let src = r#\"\n        [preprocessor.foo]\n        x = 123\n        \"#;\n        let cfg = Config::from_str(src).unwrap();\n        let err = cfg.get::<String>(\"preprocessor.foo.x\").unwrap_err();\n        assert_eq!(\n            err.to_string(),\n            \"Failed to deserialize `preprocessor.foo.x`\"\n        );\n    }\n\n    #[test]\n    fn contains_key() {\n        let src = r#\"\n        [preprocessor.foo]\n        x = 123\n        [output.foo.sub]\n        y = 'x'\n        \"#;\n        let cfg = Config::from_str(src).unwrap();\n        assert!(cfg.contains_key(\"preprocessor.foo\"));\n        assert!(cfg.contains_key(\"preprocessor.foo.x\"));\n        assert!(!cfg.contains_key(\"preprocessor.bar\"));\n        assert!(!cfg.contains_key(\"preprocessor.foo.y\"));\n        assert!(cfg.contains_key(\"output.foo\"));\n        assert!(cfg.contains_key(\"output.foo.sub\"));\n        assert!(cfg.contains_key(\"output.foo.sub.y\"));\n        assert!(!cfg.contains_key(\"output.bar\"));\n        assert!(!cfg.contains_key(\"output.foo.sub.z\"));\n    }\n}\n"
  },
  {
    "path": "crates/mdbook-core/src/lib.rs",
    "content": "//! The base support library for mdbook, intended for internal use only.\n\n/// The current version of `mdbook`.\n///\n/// This is provided as a way for custom preprocessors and renderers to do\n/// compatibility checks.\npub const MDBOOK_VERSION: &str = env!(\"CARGO_PKG_VERSION\");\n\npub mod book;\npub mod config;\npub mod utils;\n\n/// The error types used in mdbook.\npub mod errors {\n    pub use anyhow::{Error, Result};\n}\n"
  },
  {
    "path": "crates/mdbook-core/src/utils/fs.rs",
    "content": "//! Filesystem utilities and helpers.\n\nuse anyhow::{Context, Result};\nuse std::fs;\nuse std::path::{Component, Path, PathBuf};\nuse tracing::debug;\n\n/// Reads a file into a string.\n///\n/// Equivalent to [`std::fs::read_to_string`] with better error messages.\npub fn read_to_string<P: AsRef<Path>>(path: P) -> Result<String> {\n    let path = path.as_ref();\n    fs::read_to_string(path).with_context(|| format!(\"failed to read `{}`\", path.display()))\n}\n\n/// Writes a file to disk.\n///\n/// Equivalent to [`std::fs::write`] with better error messages. This will\n/// also create the parent directory if it doesn't exist.\npub fn write<P: AsRef<Path>, C: AsRef<[u8]>>(path: P, contents: C) -> Result<()> {\n    let path = path.as_ref();\n    debug!(\"Writing `{}`\", path.display());\n    if let Some(parent) = path.parent() {\n        create_dir_all(parent)?;\n    }\n    fs::write(path, contents.as_ref())\n        .with_context(|| format!(\"failed to write `{}`\", path.display()))\n}\n\n/// Equivalent to [`std::fs::create_dir_all`] with better error messages.\npub fn create_dir_all(p: impl AsRef<Path>) -> Result<()> {\n    let p = p.as_ref();\n    fs::create_dir_all(p)\n        .with_context(|| format!(\"failed to create directory `{}`\", p.display()))?;\n    Ok(())\n}\n\n/// Takes a path and returns a path containing just enough `../` to point to\n/// the root of the given path.\n///\n/// This is mostly interesting for a relative path to point back to the\n/// directory from where the path starts.\n///\n/// ```rust\n/// # use std::path::Path;\n/// # use mdbook_core::utils::fs::path_to_root;\n/// let path = Path::new(\"some/relative/path\");\n/// assert_eq!(path_to_root(path), \"../../\");\n/// ```\n///\n/// **note:** it's not very fool-proof, if you find a situation where\n/// it doesn't return the correct path.\n/// Consider [submitting a new issue](https://github.com/rust-lang/mdBook/issues)\n/// or a [pull-request](https://github.com/rust-lang/mdBook/pulls) to improve it.\npub fn path_to_root<P: Into<PathBuf>>(path: P) -> String {\n    // Remove filename and add \"../\" for every directory\n\n    path.into()\n        .parent()\n        .expect(\"\")\n        .components()\n        .fold(String::new(), |mut s, c| {\n            match c {\n                Component::Normal(_) => s.push_str(\"../\"),\n                _ => {\n                    debug!(\"Other path component... {:?}\", c);\n                }\n            }\n            s\n        })\n}\n\n/// Removes all the content of a directory but not the directory itself.\npub fn remove_dir_content(dir: &Path) -> Result<()> {\n    for item in fs::read_dir(dir)\n        .with_context(|| format!(\"failed to read directory `{}`\", dir.display()))?\n        .flatten()\n    {\n        let item = item.path();\n        if item.is_dir() {\n            fs::remove_dir_all(&item)\n                .with_context(|| format!(\"failed to remove `{}`\", item.display()))?;\n        } else {\n            fs::remove_file(&item)\n                .with_context(|| format!(\"failed to remove `{}`\", item.display()))?;\n        }\n    }\n    Ok(())\n}\n\n/// Copies all files of a directory to another one except the files\n/// with the extensions given in the `ext_blacklist` array\npub fn copy_files_except_ext(\n    from: &Path,\n    to: &Path,\n    recursive: bool,\n    avoid_dir: Option<&PathBuf>,\n    ext_blacklist: &[&str],\n) -> Result<()> {\n    debug!(\n        \"Copying all files from {} to {} (blacklist: {:?}), avoiding {:?}\",\n        from.display(),\n        to.display(),\n        ext_blacklist,\n        avoid_dir\n    );\n\n    // Check that from and to are different\n    if from == to {\n        return Ok(());\n    }\n\n    for entry in fs::read_dir(from)? {\n        let entry = entry?.path();\n        let metadata = entry\n            .metadata()\n            .with_context(|| format!(\"Failed to read {entry:?}\"))?;\n\n        let entry_file_name = entry.file_name().unwrap();\n        let target_file_path = to.join(entry_file_name);\n\n        // If the entry is a dir and the recursive option is enabled, call itself\n        if metadata.is_dir() && recursive {\n            if entry == to.as_os_str() {\n                continue;\n            }\n\n            if let Some(avoid) = avoid_dir {\n                if entry == *avoid {\n                    continue;\n                }\n            }\n\n            // check if output dir already exists\n            if !target_file_path.exists() {\n                fs::create_dir(&target_file_path)?;\n            }\n\n            copy_files_except_ext(&entry, &target_file_path, true, avoid_dir, ext_blacklist)?;\n        } else if metadata.is_file() {\n            // Check if it is in the blacklist\n            if let Some(ext) = entry.extension() {\n                if ext_blacklist.contains(&ext.to_str().unwrap()) {\n                    continue;\n                }\n            }\n            debug!(\"Copying {entry:?} to {target_file_path:?}\");\n            copy(&entry, &target_file_path)?;\n        }\n    }\n    Ok(())\n}\n\n/// Copies a file.\nfn copy<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> Result<()> {\n    let from = from.as_ref();\n    let to = to.as_ref();\n    return copy_inner(from, to)\n        .with_context(|| format!(\"failed to copy `{}` to `{}`\", from.display(), to.display()));\n\n    // This is a workaround for an issue with the macOS file watcher.\n    // Rust's `std::fs::copy` function uses `fclonefileat`, which creates\n    // clones on APFS. Unfortunately fs events seem to trigger on both\n    // sides of the clone, and there doesn't seem to be a way to differentiate\n    // which side it is.\n    // https://github.com/notify-rs/notify/issues/465#issuecomment-1657261035\n    // contains more information.\n    //\n    // This is essentially a copy of the simple copy code path in Rust's\n    // standard library.\n    #[cfg(target_os = \"macos\")]\n    fn copy_inner(from: &Path, to: &Path) -> Result<()> {\n        use std::fs::OpenOptions;\n        use std::os::unix::fs::{OpenOptionsExt, PermissionsExt};\n\n        let mut reader = std::fs::File::open(from)?;\n        let metadata = reader.metadata()?;\n        if !metadata.is_file() {\n            anyhow::bail!(\n                \"expected a file, `{}` appears to be {:?}\",\n                from.display(),\n                metadata.file_type()\n            );\n        }\n        let perm = metadata.permissions();\n        let mut writer = OpenOptions::new()\n            .mode(perm.mode())\n            .write(true)\n            .create(true)\n            .truncate(true)\n            .open(to)?;\n        let writer_metadata = writer.metadata()?;\n        if writer_metadata.is_file() {\n            // Set the correct file permissions, in case the file already existed.\n            // Don't set the permissions on already existing non-files like\n            // pipes/FIFOs or device nodes.\n            writer.set_permissions(perm)?;\n        }\n        std::io::copy(&mut reader, &mut writer)?;\n        Ok(())\n    }\n\n    #[cfg(not(target_os = \"macos\"))]\n    fn copy_inner(from: &Path, to: &Path) -> Result<()> {\n        fs::copy(from, to)?;\n        Ok(())\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use std::io::Result;\n    use std::path::Path;\n\n    #[cfg(target_os = \"windows\")]\n    fn symlink<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> Result<()> {\n        std::os::windows::fs::symlink_file(src, dst)\n    }\n\n    #[cfg(not(target_os = \"windows\"))]\n    fn symlink<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> Result<()> {\n        std::os::unix::fs::symlink(src, dst)\n    }\n\n    #[test]\n    fn copy_files_except_ext_test() {\n        let tmp = match tempfile::TempDir::new() {\n            Ok(t) => t,\n            Err(e) => panic!(\"Could not create a temp dir: {e}\"),\n        };\n\n        // Create a couple of files\n        write(tmp.path().join(\"file.txt\"), \"\").unwrap();\n        write(tmp.path().join(\"file.md\"), \"\").unwrap();\n        write(tmp.path().join(\"file.png\"), \"\").unwrap();\n        write(tmp.path().join(\"sub_dir/file.png\"), \"\").unwrap();\n        write(tmp.path().join(\"sub_dir_exists/file.txt\"), \"\").unwrap();\n        if let Err(err) = symlink(tmp.path().join(\"file.png\"), tmp.path().join(\"symlink.png\")) {\n            panic!(\"Could not symlink file.png: {err}\");\n        }\n\n        // Create output dir\n        create_dir_all(tmp.path().join(\"output\")).unwrap();\n        create_dir_all(tmp.path().join(\"output/sub_dir_exists\")).unwrap();\n\n        if let Err(e) =\n            copy_files_except_ext(tmp.path(), &tmp.path().join(\"output\"), true, None, &[\"md\"])\n        {\n            panic!(\"Error while executing the function:\\n{e:?}\");\n        }\n\n        // Check if the correct files where created\n        if !tmp.path().join(\"output/file.txt\").exists() {\n            panic!(\"output/file.txt should exist\")\n        }\n        if tmp.path().join(\"output/file.md\").exists() {\n            panic!(\"output/file.md should not exist\")\n        }\n        if !tmp.path().join(\"output/file.png\").exists() {\n            panic!(\"output/file.png should exist\")\n        }\n        if !tmp.path().join(\"output/sub_dir/file.png\").exists() {\n            panic!(\"output/sub_dir/file.png should exist\")\n        }\n        if !tmp.path().join(\"output/sub_dir_exists/file.txt\").exists() {\n            panic!(\"output/sub_dir/file.png should exist\")\n        }\n        if !tmp.path().join(\"output/symlink.png\").exists() {\n            panic!(\"output/symlink.png should exist\")\n        }\n    }\n}\n"
  },
  {
    "path": "crates/mdbook-core/src/utils/html.rs",
    "content": "//! Utilities for dealing with HTML.\n\nuse std::borrow::Cow;\n\n/// Escape characters to make it safe for an HTML string.\npub fn escape_html_attribute(text: &str) -> Cow<'_, str> {\n    let needs_escape: &[char] = &['<', '>', '\\'', '\"', '\\\\', '&'];\n    let mut s = text;\n    let mut output = String::new();\n    while let Some(next) = s.find(needs_escape) {\n        output.push_str(&s[..next]);\n        match s.as_bytes()[next] {\n            b'<' => output.push_str(\"&lt;\"),\n            b'>' => output.push_str(\"&gt;\"),\n            b'\\'' => output.push_str(\"&#39;\"),\n            b'\"' => output.push_str(\"&quot;\"),\n            b'\\\\' => output.push_str(\"&#92;\"),\n            b'&' => output.push_str(\"&amp;\"),\n            _ => unreachable!(),\n        }\n        s = &s[next + 1..];\n    }\n    if output.is_empty() {\n        Cow::Borrowed(text)\n    } else {\n        output.push_str(s);\n        Cow::Owned(output)\n    }\n}\n\n/// Escape `<`, `>`, and '&' for HTML.\npub fn escape_html(text: &str) -> Cow<'_, str> {\n    let needs_escape: &[char] = &['<', '>', '&'];\n    let mut s = text;\n    let mut output = String::new();\n    while let Some(next) = s.find(needs_escape) {\n        output.push_str(&s[..next]);\n        match s.as_bytes()[next] {\n            b'<' => output.push_str(\"&lt;\"),\n            b'>' => output.push_str(\"&gt;\"),\n            b'&' => output.push_str(\"&amp;\"),\n            _ => unreachable!(),\n        }\n        s = &s[next + 1..];\n    }\n    if output.is_empty() {\n        Cow::Borrowed(text)\n    } else {\n        output.push_str(s);\n        Cow::Owned(output)\n    }\n}\n\n#[test]\nfn attributes_are_escaped() {\n    assert_eq!(escape_html_attribute(\"\"), \"\");\n    assert_eq!(escape_html_attribute(\"<\"), \"&lt;\");\n    assert_eq!(escape_html_attribute(\">\"), \"&gt;\");\n    assert_eq!(escape_html_attribute(\"<>\"), \"&lt;&gt;\");\n    assert_eq!(escape_html_attribute(\"<test>\"), \"&lt;test&gt;\");\n    assert_eq!(escape_html_attribute(\"a<test>b\"), \"a&lt;test&gt;b\");\n    assert_eq!(escape_html_attribute(\"'\"), \"&#39;\");\n    assert_eq!(escape_html_attribute(\"\\\\\"), \"&#92;\");\n    assert_eq!(escape_html_attribute(\"&\"), \"&amp;\");\n}\n\n#[test]\nfn html_is_escaped() {\n    assert_eq!(escape_html(\"\"), \"\");\n    assert_eq!(escape_html(\"<\"), \"&lt;\");\n    assert_eq!(escape_html(\">\"), \"&gt;\");\n    assert_eq!(escape_html(\"&\"), \"&amp;\");\n    assert_eq!(escape_html(\"<>\"), \"&lt;&gt;\");\n    assert_eq!(escape_html(\"<test>\"), \"&lt;test&gt;\");\n    assert_eq!(escape_html(\"a<test>b\"), \"a&lt;test&gt;b\");\n    assert_eq!(escape_html(\"'\"), \"'\");\n    assert_eq!(escape_html(\"\\\\\"), \"\\\\\");\n}\n"
  },
  {
    "path": "crates/mdbook-core/src/utils/mod.rs",
    "content": "//! Various helpers and utilities.\n\nuse anyhow::Error;\nuse std::fmt::Write;\nuse tracing::error;\n\npub mod fs;\nmod html;\nmod toml_ext;\n\npub(crate) use self::toml_ext::TomlExt;\n\npub use self::html::{escape_html, escape_html_attribute};\n\n/// Defines a `static` with a [`regex::Regex`].\n#[macro_export]\nmacro_rules! static_regex {\n    ($name:ident, $regex:literal) => {\n        static $name: std::sync::LazyLock<regex::Regex> =\n            std::sync::LazyLock::new(|| regex::Regex::new($regex).unwrap());\n    };\n    ($name:ident, bytes, $regex:literal) => {\n        static $name: std::sync::LazyLock<regex::bytes::Regex> =\n            std::sync::LazyLock::new(|| regex::bytes::Regex::new($regex).unwrap());\n    };\n}\n\n/// Prints a \"backtrace\" of some `Error`.\npub fn log_backtrace(e: &Error) {\n    let mut message = format!(\"{e}\");\n\n    for cause in e.chain().skip(1) {\n        write!(message, \"\\n\\tCaused by: {cause}\").unwrap();\n    }\n\n    error!(\"{message}\");\n}\n"
  },
  {
    "path": "crates/mdbook-core/src/utils/toml_ext.rs",
    "content": "//! Helper for working with toml types.\n\nuse toml::value::{Table, Value};\n\n/// Helper for working with toml types.\npub(crate) trait TomlExt {\n    /// Read a dotted key.\n    fn read(&self, key: &str) -> Option<&Value>;\n    /// Insert with a dotted key.\n    fn insert(&mut self, key: &str, value: Value);\n}\n\nimpl TomlExt for Value {\n    fn read(&self, key: &str) -> Option<&Value> {\n        if let Some((head, tail)) = split(key) {\n            self.get(head)?.read(tail)\n        } else {\n            self.get(key)\n        }\n    }\n\n    fn insert(&mut self, key: &str, value: Value) {\n        if !self.is_table() {\n            *self = Value::Table(Table::new());\n        }\n\n        let table = self.as_table_mut().expect(\"unreachable\");\n\n        if let Some((head, tail)) = split(key) {\n            table\n                .entry(head)\n                .or_insert_with(|| Value::Table(Table::new()))\n                .insert(tail, value);\n        } else {\n            table.insert(key.to_string(), value);\n        }\n    }\n}\n\nfn split(key: &str) -> Option<(&str, &str)> {\n    let ix = key.find('.')?;\n\n    let (head, tail) = key.split_at(ix);\n    // splitting will leave the \".\"\n    let tail = &tail[1..];\n\n    Some((head, tail))\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn read_simple_table() {\n        let src = \"[table]\";\n        let value: Value = toml::from_str(src).unwrap();\n\n        let got = value.read(\"table\").unwrap();\n\n        assert!(got.is_table());\n    }\n\n    #[test]\n    fn read_nested_item() {\n        let src = \"[table]\\nnested=true\";\n        let value: Value = toml::from_str(src).unwrap();\n\n        let got = value.read(\"table.nested\").unwrap();\n\n        assert_eq!(got, &Value::Boolean(true));\n    }\n\n    #[test]\n    fn insert_item_at_top_level() {\n        let mut value = Value::Table(Table::default());\n        let item = Value::Boolean(true);\n\n        value.insert(\"first\", item.clone());\n\n        assert_eq!(value.get(\"first\").unwrap(), &item);\n    }\n\n    #[test]\n    fn insert_nested_item() {\n        let mut value = Value::Table(Table::default());\n        let item = Value::Boolean(true);\n\n        value.insert(\"first.second\", item.clone());\n\n        let inserted = value.read(\"first.second\").unwrap();\n        assert_eq!(inserted, &item);\n    }\n}\n"
  },
  {
    "path": "crates/mdbook-driver/Cargo.toml",
    "content": "[package]\nname = \"mdbook-driver\"\nversion = \"0.5.2\"\ndescription = \"High-level library for running mdBook\"\nedition.workspace = true\nlicense.workspace = true\nrepository.workspace = true\nrust-version.workspace = true\n\n[dependencies]\nanyhow.workspace = true\nindexmap.workspace = true\nmdbook-core.workspace = true\nmdbook-html.workspace = true\nmdbook-markdown.workspace = true\nmdbook-preprocessor.workspace = true\nmdbook-renderer.workspace = true\nmdbook-summary.workspace = true\nregex.workspace = true\nserde.workspace = true\nserde_json.workspace = true\nshlex.workspace = true\ntempfile.workspace = true\ntoml.workspace = true\ntopological-sort.workspace = true\ntracing.workspace = true\n\n[lints]\nworkspace = true\n\n[features]\nsearch = [\"mdbook-html/search\"]\n"
  },
  {
    "path": "crates/mdbook-driver/README.md",
    "content": "# mdbook-driver\n\n[![Documentation](https://img.shields.io/docsrs/mdbook-driver)](https://docs.rs/mdbook-driver)\n[![crates.io](https://img.shields.io/crates/v/mdbook-driver.svg)](https://crates.io/crates/mdbook-driver)\n[![Changelog](https://img.shields.io/badge/CHANGELOG-Latest-green)](https://github.com/rust-lang/mdBook/blob/master/CHANGELOG.md)\n\nThis is the high-level Rust library for running [mdBook](https://rust-lang.github.io/mdBook/). New books can be created using [`BookBuilder`](https://docs.rs/mdbook-driver/latest/mdbook_driver/init/struct.BookBuilder.html). The primary type [`MDBook`](https://docs.rs/mdbook-driver/latest/mdbook_driver/struct.MDBook.html) can be used to manage and render books.\n\n> This crate is maintained by the mdBook team for use by the wider ecosystem. This crate follows [semver compatibility](https://doc.rust-lang.org/cargo/reference/semver.html) for its APIs.\n\n## License\n\n[Mozilla Public License, version 2.0](https://github.com/rust-lang/mdBook/blob/master/LICENSE)\n"
  },
  {
    "path": "crates/mdbook-driver/src/builtin_preprocessors/cmd.rs",
    "content": "use anyhow::{Context, Result, ensure};\nuse mdbook_core::book::Book;\nuse mdbook_preprocessor::{Preprocessor, PreprocessorContext};\nuse std::io::Write;\nuse std::path::PathBuf;\nuse std::process::{Child, Stdio};\nuse tracing::{debug, trace, warn};\n\n/// A custom preprocessor which will shell out to a 3rd-party program.\n///\n/// See <https://rust-lang.github.io/mdBook/for_developers/preprocessors.html>\n/// for a description of the preprocessor protocol.\n#[derive(Debug, Clone, PartialEq)]\npub struct CmdPreprocessor {\n    name: String,\n    cmd: String,\n    root: PathBuf,\n    optional: bool,\n}\n\nimpl CmdPreprocessor {\n    /// Create a new `CmdPreprocessor`.\n    pub fn new(name: String, cmd: String, root: PathBuf, optional: bool) -> CmdPreprocessor {\n        CmdPreprocessor {\n            name,\n            cmd,\n            root,\n            optional,\n        }\n    }\n\n    fn write_input_to_child(&self, child: &mut Child, book: &Book, ctx: &PreprocessorContext) {\n        let stdin = child.stdin.take().expect(\"Child has stdin\");\n\n        if let Err(e) = self.write_input(stdin, book, ctx) {\n            // Looks like the backend hung up before we could finish\n            // sending it the render context. Log the error and keep going\n            warn!(\"Error writing the RenderContext to the backend, {}\", e);\n        }\n    }\n\n    fn write_input<W: Write>(\n        &self,\n        writer: W,\n        book: &Book,\n        ctx: &PreprocessorContext,\n    ) -> Result<()> {\n        serde_json::to_writer(writer, &(ctx, book)).map_err(Into::into)\n    }\n\n    /// The command this `Preprocessor` will invoke.\n    pub fn cmd(&self) -> &str {\n        &self.cmd\n    }\n}\n\nimpl Preprocessor for CmdPreprocessor {\n    fn name(&self) -> &str {\n        &self.name\n    }\n\n    fn run(&self, ctx: &PreprocessorContext, book: Book) -> Result<Book> {\n        let mut cmd = crate::compose_command(&self.cmd, &ctx.root)?;\n\n        let mut child = match cmd\n            .stdin(Stdio::piped())\n            .stdout(Stdio::piped())\n            .stderr(Stdio::inherit())\n            .current_dir(&self.root)\n            .spawn()\n        {\n            Ok(c) => c,\n            Err(e) => {\n                crate::handle_command_error(\n                    e,\n                    self.optional,\n                    \"preprocessor\",\n                    \"preprocessor\",\n                    &self.name,\n                    &self.cmd,\n                )?;\n                // This should normally not be reached, since the validation\n                // for NotFound should have already happened when running the\n                // \"supports\" command.\n                return Ok(book);\n            }\n        };\n\n        self.write_input_to_child(&mut child, &book, ctx);\n\n        let output = child.wait_with_output().with_context(|| {\n            format!(\n                \"Error waiting for the \\\"{}\\\" preprocessor to complete\",\n                self.name\n            )\n        })?;\n\n        trace!(\"{} exited with output: {:?}\", self.cmd, output);\n        ensure!(\n            output.status.success(),\n            format!(\n                \"The \\\"{}\\\" preprocessor exited unsuccessfully with {} status\",\n                self.name, output.status\n            )\n        );\n\n        serde_json::from_slice(&output.stdout).with_context(|| {\n            format!(\n                \"Unable to parse the preprocessed book from \\\"{}\\\" processor\",\n                self.name\n            )\n        })\n    }\n\n    fn supports_renderer(&self, renderer: &str) -> Result<bool> {\n        debug!(\n            \"Checking if the \\\"{}\\\" preprocessor supports \\\"{}\\\"\",\n            self.name(),\n            renderer\n        );\n\n        let mut cmd = crate::compose_command(&self.cmd, &self.root)?;\n\n        match cmd\n            .arg(\"supports\")\n            .arg(renderer)\n            .stdin(Stdio::null())\n            .stdout(Stdio::inherit())\n            .stderr(Stdio::inherit())\n            .current_dir(&self.root)\n            .status()\n        {\n            Ok(status) => Ok(status.code() == Some(0)),\n            Err(e) => {\n                crate::handle_command_error(\n                    e,\n                    self.optional,\n                    \"preprocessor\",\n                    \"preprocessor\",\n                    &self.name,\n                    &self.cmd,\n                )?;\n                Ok(false)\n            }\n        }\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use crate::MDBook;\n    use std::path::Path;\n\n    fn guide() -> MDBook {\n        let example = Path::new(env!(\"CARGO_MANIFEST_DIR\")).join(\"../../guide\");\n        MDBook::load(example).unwrap()\n    }\n\n    #[test]\n    fn round_trip_write_and_parse_input() {\n        let md = guide();\n        let cmd = CmdPreprocessor::new(\n            \"test\".to_string(),\n            \"test\".to_string(),\n            md.root.clone(),\n            false,\n        );\n        let ctx = PreprocessorContext::new(\n            md.root.clone(),\n            md.config.clone(),\n            \"some-renderer\".to_string(),\n        );\n\n        let mut buffer = Vec::new();\n        cmd.write_input(&mut buffer, &md.book, &ctx).unwrap();\n\n        let (got_ctx, got_book) = mdbook_preprocessor::parse_input(buffer.as_slice()).unwrap();\n\n        assert_eq!(got_book, md.book);\n        assert_eq!(got_ctx, ctx);\n    }\n}\n"
  },
  {
    "path": "crates/mdbook-driver/src/builtin_preprocessors/index.rs",
    "content": "use anyhow::Result;\nuse mdbook_core::book::{Book, BookItem};\nuse mdbook_core::static_regex;\nuse mdbook_preprocessor::{Preprocessor, PreprocessorContext};\nuse std::path::Path;\nuse tracing::warn;\n\n/// A preprocessor for converting file name `README.md` to `index.md` since\n/// `README.md` is the de facto index file in markdown-based documentation.\n#[derive(Default)]\n#[non_exhaustive]\npub struct IndexPreprocessor;\n\nimpl IndexPreprocessor {\n    /// Name of this preprocessor.\n    pub const NAME: &'static str = \"index\";\n\n    /// Create a new `IndexPreprocessor`.\n    pub fn new() -> Self {\n        IndexPreprocessor\n    }\n}\n\nimpl Preprocessor for IndexPreprocessor {\n    fn name(&self) -> &str {\n        Self::NAME\n    }\n\n    fn run(&self, ctx: &PreprocessorContext, mut book: Book) -> Result<Book> {\n        let source_dir = ctx.root.join(&ctx.config.book.src);\n        book.for_each_mut(|section: &mut BookItem| {\n            if let BookItem::Chapter(ref mut ch) = *section {\n                if let Some(ref mut path) = ch.path {\n                    if is_readme_file(&path) {\n                        let mut index_md = source_dir.join(path.with_file_name(\"index.md\"));\n                        if index_md.exists() {\n                            warn_readme_name_conflict(&path, &&mut index_md);\n                        }\n\n                        path.set_file_name(\"index.md\");\n                    }\n                }\n            }\n        });\n\n        Ok(book)\n    }\n}\n\nfn warn_readme_name_conflict<P: AsRef<Path>>(readme_path: P, index_path: P) {\n    let file_name = readme_path.as_ref().file_name().unwrap_or_default();\n    let parent_dir = index_path\n        .as_ref()\n        .parent()\n        .unwrap_or_else(|| index_path.as_ref());\n    warn!(\n        \"It seems that there are both {:?} and index.md under \\\"{}\\\".\",\n        file_name,\n        parent_dir.display()\n    );\n    warn!(\n        \"mdbook converts {:?} into index.html by default. It may cause\",\n        file_name\n    );\n    warn!(\"unexpected behavior if putting both files under the same directory.\");\n    warn!(\"To solve the warning, try to rearrange the book structure or disable\");\n    warn!(\"\\\"index\\\" preprocessor to stop the conversion.\");\n}\n\nfn is_readme_file<P: AsRef<Path>>(path: P) -> bool {\n    static_regex!(README, r\"(?i)^readme$\");\n\n    README.is_match(\n        path.as_ref()\n            .file_stem()\n            .and_then(std::ffi::OsStr::to_str)\n            .unwrap_or_default(),\n    )\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn file_stem_exactly_matches_readme_case_insensitively() {\n        let path = \"path/to/Readme.md\";\n        assert!(is_readme_file(path));\n\n        let path = \"path/to/README.md\";\n        assert!(is_readme_file(path));\n\n        let path = \"path/to/rEaDmE.md\";\n        assert!(is_readme_file(path));\n\n        let path = \"path/to/README.markdown\";\n        assert!(is_readme_file(path));\n\n        let path = \"path/to/README\";\n        assert!(is_readme_file(path));\n\n        let path = \"path/to/README-README.md\";\n        assert!(!is_readme_file(path));\n    }\n}\n"
  },
  {
    "path": "crates/mdbook-driver/src/builtin_preprocessors/links/take_lines.rs",
    "content": "use mdbook_core::static_regex;\nuse std::ops::Bound::{Excluded, Included, Unbounded};\nuse std::ops::RangeBounds;\n\n/// Take a range of lines from a string.\npub(super) fn take_lines<R: RangeBounds<usize>>(s: &str, range: R) -> String {\n    let start = match range.start_bound() {\n        Excluded(&n) => n + 1,\n        Included(&n) => n,\n        Unbounded => 0,\n    };\n    let lines = s.lines().skip(start);\n    match range.end_bound() {\n        Excluded(end) => lines\n            .take(end.saturating_sub(start))\n            .collect::<Vec<_>>()\n            .join(\"\\n\"),\n        Included(end) => lines\n            .take((end + 1).saturating_sub(start))\n            .collect::<Vec<_>>()\n            .join(\"\\n\"),\n        Unbounded => lines.collect::<Vec<_>>().join(\"\\n\"),\n    }\n}\n\nstatic_regex!(ANCHOR_START, r\"ANCHOR:\\s*(?P<anchor_name>[\\w_-]+)\");\nstatic_regex!(ANCHOR_END, r\"ANCHOR_END:\\s*(?P<anchor_name>[\\w_-]+)\");\n\n/// Take anchored lines from a string.\n/// Lines containing anchor are ignored.\npub(super) fn take_anchored_lines(s: &str, anchor: &str) -> String {\n    let mut retained = Vec::<&str>::new();\n    let mut anchor_found = false;\n\n    for l in s.lines() {\n        if anchor_found {\n            match ANCHOR_END.captures(l) {\n                Some(cap) => {\n                    if &cap[\"anchor_name\"] == anchor {\n                        break;\n                    }\n                }\n                None => {\n                    if !ANCHOR_START.is_match(l) {\n                        retained.push(l);\n                    }\n                }\n            }\n        } else if let Some(cap) = ANCHOR_START.captures(l) {\n            if &cap[\"anchor_name\"] == anchor {\n                anchor_found = true;\n            }\n        }\n    }\n\n    retained.join(\"\\n\")\n}\n\n/// Keep lines contained within the range specified as-is.\n/// For any lines not in the range, include them but use `#` at the beginning. This will hide the\n/// lines from initial display but include them when expanding the code snippet or testing with\n/// rustdoc.\npub(super) fn take_rustdoc_include_lines<R: RangeBounds<usize>>(s: &str, range: R) -> String {\n    let mut output = String::with_capacity(s.len());\n\n    for (index, line) in s.lines().enumerate() {\n        if !range.contains(&index) {\n            output.push_str(\"# \");\n        }\n        output.push_str(line);\n        output.push('\\n');\n    }\n    output.pop();\n    output\n}\n\n/// Keep lines between the anchor comments specified as-is.\n/// For any lines not between the anchors, include them but use `#` at the beginning. This will\n/// hide the lines from initial display but include them when expanding the code snippet or testing\n/// with rustdoc.\npub(super) fn take_rustdoc_include_anchored_lines(s: &str, anchor: &str) -> String {\n    let mut output = String::with_capacity(s.len());\n    let mut within_anchored_section = false;\n\n    for l in s.lines() {\n        if within_anchored_section {\n            match ANCHOR_END.captures(l) {\n                Some(cap) => {\n                    if &cap[\"anchor_name\"] == anchor {\n                        within_anchored_section = false;\n                    }\n                }\n                None => {\n                    if !ANCHOR_START.is_match(l) {\n                        output.push_str(l);\n                        output.push('\\n');\n                    }\n                }\n            }\n        } else if let Some(cap) = ANCHOR_START.captures(l) {\n            if &cap[\"anchor_name\"] == anchor {\n                within_anchored_section = true;\n            }\n        } else if !ANCHOR_END.is_match(l) {\n            output.push_str(\"# \");\n            output.push_str(l);\n            output.push('\\n');\n        }\n    }\n\n    output.pop();\n    output\n}\n\n#[cfg(test)]\nmod tests {\n    use super::{\n        take_anchored_lines, take_lines, take_rustdoc_include_anchored_lines,\n        take_rustdoc_include_lines,\n    };\n\n    #[test]\n    #[allow(clippy::reversed_empty_ranges)] // Intentionally checking that those are correctly handled\n    fn take_lines_test() {\n        let s = \"Lorem\\nipsum\\ndolor\\nsit\\namet\";\n        assert_eq!(take_lines(s, 1..3), \"ipsum\\ndolor\");\n        assert_eq!(take_lines(s, 3..), \"sit\\namet\");\n        assert_eq!(take_lines(s, ..3), \"Lorem\\nipsum\\ndolor\");\n        assert_eq!(take_lines(s, ..), s);\n        // corner cases\n        assert_eq!(take_lines(s, 4..3), \"\");\n        assert_eq!(take_lines(s, ..100), s);\n    }\n\n    #[test]\n    fn take_anchored_lines_test() {\n        let s = \"Lorem\\nipsum\\ndolor\\nsit\\namet\";\n        assert_eq!(take_anchored_lines(s, \"test\"), \"\");\n\n        let s = \"Lorem\\nipsum\\ndolor\\nANCHOR_END: test\\nsit\\namet\";\n        assert_eq!(take_anchored_lines(s, \"test\"), \"\");\n\n        let s = \"Lorem\\nipsum\\nANCHOR: test\\ndolor\\nsit\\namet\";\n        assert_eq!(take_anchored_lines(s, \"test\"), \"dolor\\nsit\\namet\");\n        assert_eq!(take_anchored_lines(s, \"something\"), \"\");\n\n        let s = \"Lorem\\nipsum\\nANCHOR: test\\ndolor\\nsit\\namet\\nANCHOR_END: test\\nlorem\\nipsum\";\n        assert_eq!(take_anchored_lines(s, \"test\"), \"dolor\\nsit\\namet\");\n        assert_eq!(take_anchored_lines(s, \"something\"), \"\");\n\n        let s = \"Lorem\\nANCHOR: test\\nipsum\\nANCHOR: test\\ndolor\\nsit\\namet\\nANCHOR_END: test\\nlorem\\nipsum\";\n        assert_eq!(take_anchored_lines(s, \"test\"), \"ipsum\\ndolor\\nsit\\namet\");\n        assert_eq!(take_anchored_lines(s, \"something\"), \"\");\n\n        let s = \"Lorem\\nANCHOR:    test2\\nipsum\\nANCHOR: test\\ndolor\\nsit\\namet\\nANCHOR_END: test\\nlorem\\nANCHOR_END:test2\\nipsum\";\n        assert_eq!(\n            take_anchored_lines(s, \"test2\"),\n            \"ipsum\\ndolor\\nsit\\namet\\nlorem\"\n        );\n        assert_eq!(take_anchored_lines(s, \"test\"), \"dolor\\nsit\\namet\");\n        assert_eq!(take_anchored_lines(s, \"something\"), \"\");\n    }\n\n    #[test]\n    #[allow(clippy::reversed_empty_ranges)] // Intentionally checking that those are correctly handled\n    fn take_rustdoc_include_lines_test() {\n        let s = \"Lorem\\nipsum\\ndolor\\nsit\\namet\";\n        assert_eq!(\n            take_rustdoc_include_lines(s, 1..3),\n            \"# Lorem\\nipsum\\ndolor\\n# sit\\n# amet\"\n        );\n        assert_eq!(\n            take_rustdoc_include_lines(s, 3..),\n            \"# Lorem\\n# ipsum\\n# dolor\\nsit\\namet\"\n        );\n        assert_eq!(\n            take_rustdoc_include_lines(s, ..3),\n            \"Lorem\\nipsum\\ndolor\\n# sit\\n# amet\"\n        );\n        assert_eq!(take_rustdoc_include_lines(s, ..), s);\n        // corner cases\n        assert_eq!(\n            take_rustdoc_include_lines(s, 4..3),\n            \"# Lorem\\n# ipsum\\n# dolor\\n# sit\\n# amet\"\n        );\n        assert_eq!(take_rustdoc_include_lines(s, ..100), s);\n    }\n\n    #[test]\n    fn take_rustdoc_include_anchored_lines_test() {\n        let s = \"Lorem\\nipsum\\ndolor\\nsit\\namet\";\n        assert_eq!(\n            take_rustdoc_include_anchored_lines(s, \"test\"),\n            \"# Lorem\\n# ipsum\\n# dolor\\n# sit\\n# amet\"\n        );\n\n        let s = \"Lorem\\nipsum\\ndolor\\nANCHOR_END: test\\nsit\\namet\";\n        assert_eq!(\n            take_rustdoc_include_anchored_lines(s, \"test\"),\n            \"# Lorem\\n# ipsum\\n# dolor\\n# sit\\n# amet\"\n        );\n\n        let s = \"Lorem\\nipsum\\nANCHOR: test\\ndolor\\nsit\\namet\";\n        assert_eq!(\n            take_rustdoc_include_anchored_lines(s, \"test\"),\n            \"# Lorem\\n# ipsum\\ndolor\\nsit\\namet\"\n        );\n        assert_eq!(\n            take_rustdoc_include_anchored_lines(s, \"something\"),\n            \"# Lorem\\n# ipsum\\n# dolor\\n# sit\\n# amet\"\n        );\n\n        let s = \"Lorem\\nipsum\\nANCHOR: test\\ndolor\\nsit\\namet\\nANCHOR_END: test\\nlorem\\nipsum\";\n        assert_eq!(\n            take_rustdoc_include_anchored_lines(s, \"test\"),\n            \"# Lorem\\n# ipsum\\ndolor\\nsit\\namet\\n# lorem\\n# ipsum\"\n        );\n        assert_eq!(\n            take_rustdoc_include_anchored_lines(s, \"something\"),\n            \"# Lorem\\n# ipsum\\n# dolor\\n# sit\\n# amet\\n# lorem\\n# ipsum\"\n        );\n\n        let s = \"Lorem\\nANCHOR: test\\nipsum\\nANCHOR: test\\ndolor\\nsit\\namet\\nANCHOR_END: test\\nlorem\\nipsum\";\n        assert_eq!(\n            take_rustdoc_include_anchored_lines(s, \"test\"),\n            \"# Lorem\\nipsum\\ndolor\\nsit\\namet\\n# lorem\\n# ipsum\"\n        );\n        assert_eq!(\n            take_rustdoc_include_anchored_lines(s, \"something\"),\n            \"# Lorem\\n# ipsum\\n# dolor\\n# sit\\n# amet\\n# lorem\\n# ipsum\"\n        );\n\n        let s = \"Lorem\\nANCHOR:    test2\\nipsum\\nANCHOR: test\\ndolor\\nsit\\namet\\nANCHOR_END: test\\nlorem\\nANCHOR_END:test2\\nipsum\";\n        assert_eq!(\n            take_rustdoc_include_anchored_lines(s, \"test2\"),\n            \"# Lorem\\nipsum\\ndolor\\nsit\\namet\\nlorem\\n# ipsum\"\n        );\n        assert_eq!(\n            take_rustdoc_include_anchored_lines(s, \"test\"),\n            \"# Lorem\\n# ipsum\\ndolor\\nsit\\namet\\n# lorem\\n# ipsum\"\n        );\n        assert_eq!(\n            take_rustdoc_include_anchored_lines(s, \"something\"),\n            \"# Lorem\\n# ipsum\\n# dolor\\n# sit\\n# amet\\n# lorem\\n# ipsum\"\n        );\n\n        let s = \"Lorem\\nANCHOR: test\\nipsum\\nANCHOR_END: test\\ndolor\\nANCHOR: test\\nsit\\nANCHOR_END: test\\namet\";\n        assert_eq!(\n            take_rustdoc_include_anchored_lines(s, \"test\"),\n            \"# Lorem\\nipsum\\n# dolor\\nsit\\n# amet\"\n        );\n    }\n}\n"
  },
  {
    "path": "crates/mdbook-driver/src/builtin_preprocessors/links.rs",
    "content": "use self::take_lines::{\n    take_anchored_lines, take_lines, take_rustdoc_include_anchored_lines,\n    take_rustdoc_include_lines,\n};\nuse anyhow::{Context, Result};\nuse mdbook_core::book::{Book, BookItem};\nuse mdbook_core::static_regex;\nuse mdbook_core::utils::fs;\nuse mdbook_preprocessor::{Preprocessor, PreprocessorContext};\nuse regex::{CaptureMatches, Captures};\nuse std::ops::{Bound, Range, RangeBounds, RangeFrom, RangeFull, RangeTo};\nuse std::path::{Path, PathBuf};\nuse tracing::{error, warn};\n\nmod take_lines;\n\nconst ESCAPE_CHAR: char = '\\\\';\nconst MAX_LINK_NESTED_DEPTH: usize = 10;\n\n/// A preprocessor for expanding helpers in a chapter. Supported helpers are:\n///\n/// - `{{# include}}` - Insert an external file of any type. Include the whole file, only particular\n///   lines, or only between the specified anchors.\n/// - `{{# rustdoc_include}}` - Insert an external Rust file, showing the particular lines\n///   specified or the lines between specified anchors, and include the rest of the file behind `#`.\n///   This hides the lines from initial display but shows them when the reader expands the code\n///   block and provides them to Rustdoc for testing.\n/// - `{{# playground}}` - Insert runnable Rust files\n/// - `{{# title}}` - Override \\<title\\> of a webpage.\n#[derive(Default)]\n#[non_exhaustive]\npub struct LinkPreprocessor;\n\nimpl LinkPreprocessor {\n    /// Name of this preprocessor.\n    pub const NAME: &'static str = \"links\";\n\n    /// Create a new `LinkPreprocessor`.\n    pub fn new() -> Self {\n        LinkPreprocessor\n    }\n}\n\nimpl Preprocessor for LinkPreprocessor {\n    fn name(&self) -> &str {\n        Self::NAME\n    }\n\n    fn run(&self, ctx: &PreprocessorContext, mut book: Book) -> Result<Book> {\n        let src_dir = ctx.root.join(&ctx.config.book.src);\n\n        book.for_each_mut(|section: &mut BookItem| {\n            if let BookItem::Chapter(ref mut ch) = *section {\n                if let Some(ref chapter_path) = ch.path {\n                    let base = chapter_path\n                        .parent()\n                        .map(|dir| src_dir.join(dir))\n                        .expect(\"All book items have a parent\");\n\n                    let mut chapter_title = ch.name.clone();\n                    let content =\n                        replace_all(&ch.content, base, chapter_path, 0, &mut chapter_title);\n                    ch.content = content;\n                    if chapter_title != ch.name {\n                        ctx.chapter_titles\n                            .borrow_mut()\n                            .insert(chapter_path.clone(), chapter_title);\n                    }\n                }\n            }\n        });\n\n        Ok(book)\n    }\n}\n\nfn replace_all<P1, P2>(\n    s: &str,\n    path: P1,\n    source: P2,\n    depth: usize,\n    chapter_title: &mut String,\n) -> String\nwhere\n    P1: AsRef<Path>,\n    P2: AsRef<Path>,\n{\n    // When replacing one thing in a string by something with a different length,\n    // the indices after that will not correspond,\n    // we therefore have to store the difference to correct this\n    let path = path.as_ref();\n    let source = source.as_ref();\n    let mut previous_end_index = 0;\n    let mut replaced = String::new();\n\n    for link in find_links(s) {\n        replaced.push_str(&s[previous_end_index..link.start_index]);\n\n        match link.render_with_path(path, chapter_title) {\n            Ok(new_content) => {\n                if depth < MAX_LINK_NESTED_DEPTH {\n                    if let Some(rel_path) = link.link_type.relative_path(path) {\n                        replaced.push_str(&replace_all(\n                            &new_content,\n                            rel_path,\n                            source,\n                            depth + 1,\n                            chapter_title,\n                        ));\n                    } else {\n                        replaced.push_str(&new_content);\n                    }\n                } else {\n                    error!(\n                        \"Stack depth exceeded in {}. Check for cyclic includes\",\n                        source.display()\n                    );\n                }\n                previous_end_index = link.end_index;\n            }\n            Err(e) => {\n                error!(\"Error updating \\\"{}\\\", {}\", link.link_text, e);\n                for cause in e.chain().skip(1) {\n                    warn!(\"Caused By: {}\", cause);\n                }\n\n                // This should make sure we include the raw `{{# ... }}` snippet\n                // in the page content if there are any errors.\n                previous_end_index = link.start_index;\n            }\n        }\n    }\n\n    replaced.push_str(&s[previous_end_index..]);\n    replaced\n}\n\n#[derive(PartialEq, Debug, Clone)]\nenum LinkType<'a> {\n    Escaped,\n    Include(PathBuf, RangeOrAnchor),\n    Playground(PathBuf, Vec<&'a str>),\n    RustdocInclude(PathBuf, RangeOrAnchor),\n    Title(&'a str),\n}\n\n#[derive(PartialEq, Debug, Clone)]\nenum RangeOrAnchor {\n    Range(LineRange),\n    Anchor(String),\n}\n\n// A range of lines specified with some include directive.\n#[derive(PartialEq, Debug, Clone)]\nenum LineRange {\n    Range(Range<usize>),\n    RangeFrom(RangeFrom<usize>),\n    RangeTo(RangeTo<usize>),\n    RangeFull(RangeFull),\n}\n\nimpl RangeBounds<usize> for LineRange {\n    fn start_bound(&self) -> Bound<&usize> {\n        match self {\n            LineRange::Range(r) => r.start_bound(),\n            LineRange::RangeFrom(r) => r.start_bound(),\n            LineRange::RangeTo(r) => r.start_bound(),\n            LineRange::RangeFull(r) => r.start_bound(),\n        }\n    }\n\n    fn end_bound(&self) -> Bound<&usize> {\n        match self {\n            LineRange::Range(r) => r.end_bound(),\n            LineRange::RangeFrom(r) => r.end_bound(),\n            LineRange::RangeTo(r) => r.end_bound(),\n            LineRange::RangeFull(r) => r.end_bound(),\n        }\n    }\n}\n\nimpl From<Range<usize>> for LineRange {\n    fn from(r: Range<usize>) -> LineRange {\n        LineRange::Range(r)\n    }\n}\n\nimpl From<RangeFrom<usize>> for LineRange {\n    fn from(r: RangeFrom<usize>) -> LineRange {\n        LineRange::RangeFrom(r)\n    }\n}\n\nimpl From<RangeTo<usize>> for LineRange {\n    fn from(r: RangeTo<usize>) -> LineRange {\n        LineRange::RangeTo(r)\n    }\n}\n\nimpl From<RangeFull> for LineRange {\n    fn from(r: RangeFull) -> LineRange {\n        LineRange::RangeFull(r)\n    }\n}\n\nimpl<'a> LinkType<'a> {\n    fn relative_path<P: AsRef<Path>>(self, base: P) -> Option<PathBuf> {\n        let base = base.as_ref();\n        match self {\n            LinkType::Escaped => None,\n            LinkType::Include(p, _) => Some(return_relative_path(base, &p)),\n            LinkType::Playground(p, _) => Some(return_relative_path(base, &p)),\n            LinkType::RustdocInclude(p, _) => Some(return_relative_path(base, &p)),\n            LinkType::Title(_) => None,\n        }\n    }\n}\nfn return_relative_path<P: AsRef<Path>>(base: P, relative: P) -> PathBuf {\n    base.as_ref()\n        .join(relative)\n        .parent()\n        .expect(\"Included file should not be /\")\n        .to_path_buf()\n}\n\nfn parse_range_or_anchor(parts: Option<&str>) -> RangeOrAnchor {\n    let mut parts = parts.unwrap_or(\"\").splitn(3, ':').fuse();\n\n    let next_element = parts.next();\n    let start = if let Some(value) = next_element.and_then(|s| s.parse::<usize>().ok()) {\n        // subtract 1 since line numbers usually begin with 1\n        Some(value.saturating_sub(1))\n    } else if let Some(\"\") = next_element {\n        None\n    } else if let Some(anchor) = next_element {\n        return RangeOrAnchor::Anchor(String::from(anchor));\n    } else {\n        None\n    };\n\n    let end = parts.next();\n    // If `end` is empty string or any other value that can't be parsed as a usize, treat this\n    // include as a range with only a start bound. However, if end isn't specified, include only\n    // the single line specified by `start`.\n    let end = end.map(|s| s.parse::<usize>());\n\n    match (start, end) {\n        (Some(start), Some(Ok(end))) => RangeOrAnchor::Range(LineRange::from(start..end)),\n        (Some(start), Some(Err(_))) => RangeOrAnchor::Range(LineRange::from(start..)),\n        (Some(start), None) => RangeOrAnchor::Range(LineRange::from(start..start + 1)),\n        (None, Some(Ok(end))) => RangeOrAnchor::Range(LineRange::from(..end)),\n        (None, None) | (None, Some(Err(_))) => RangeOrAnchor::Range(LineRange::from(RangeFull)),\n    }\n}\n\nfn parse_include_path(path: &str) -> LinkType<'static> {\n    let mut parts = path.splitn(2, ':');\n\n    let path = parts.next().unwrap().into();\n    let range_or_anchor = parse_range_or_anchor(parts.next());\n\n    LinkType::Include(path, range_or_anchor)\n}\n\nfn parse_rustdoc_include_path(path: &str) -> LinkType<'static> {\n    let mut parts = path.splitn(2, ':');\n\n    let path = parts.next().unwrap().into();\n    let range_or_anchor = parse_range_or_anchor(parts.next());\n\n    LinkType::RustdocInclude(path, range_or_anchor)\n}\n\n#[derive(PartialEq, Debug, Clone)]\nstruct Link<'a> {\n    start_index: usize,\n    end_index: usize,\n    link_type: LinkType<'a>,\n    link_text: &'a str,\n}\n\nimpl<'a> Link<'a> {\n    fn from_capture(cap: Captures<'a>) -> Option<Link<'a>> {\n        let link_type = match (cap.get(0), cap.get(1), cap.get(2)) {\n            (_, Some(typ), Some(title)) if typ.as_str() == \"title\" => {\n                Some(LinkType::Title(title.as_str()))\n            }\n            (_, Some(typ), Some(rest)) => {\n                let mut path_props = rest.as_str().split_whitespace();\n                let file_arg = path_props.next();\n                let props: Vec<&str> = path_props.collect();\n\n                match (typ.as_str(), file_arg) {\n                    (\"include\", Some(pth)) => Some(parse_include_path(pth)),\n                    (\"playground\", Some(pth)) => Some(LinkType::Playground(pth.into(), props)),\n                    (\"playpen\", Some(pth)) => {\n                        warn!(\n                            \"the {{{{#playpen}}}} expression has been \\\n                            renamed to {{{{#playground}}}}, \\\n                            please update your book to use the new name\"\n                        );\n                        Some(LinkType::Playground(pth.into(), props))\n                    }\n                    (\"rustdoc_include\", Some(pth)) => Some(parse_rustdoc_include_path(pth)),\n                    _ => None,\n                }\n            }\n            (Some(mat), None, None) if mat.as_str().starts_with(ESCAPE_CHAR) => {\n                Some(LinkType::Escaped)\n            }\n            _ => None,\n        };\n\n        link_type.and_then(|lnk_type| {\n            cap.get(0).map(|mat| Link {\n                start_index: mat.start(),\n                end_index: mat.end(),\n                link_type: lnk_type,\n                link_text: mat.as_str(),\n            })\n        })\n    }\n\n    fn render_with_path<P: AsRef<Path>>(\n        &self,\n        base: P,\n        chapter_title: &mut String,\n    ) -> Result<String> {\n        let base = base.as_ref();\n        match self.link_type {\n            // omit the escape char\n            LinkType::Escaped => Ok(self.link_text[1..].to_owned()),\n            LinkType::Include(ref pat, ref range_or_anchor) => {\n                let target = base.join(pat);\n\n                fs::read_to_string(&target)\n                    .map(|s| match range_or_anchor {\n                        RangeOrAnchor::Range(range) => take_lines(&s, range.clone()),\n                        RangeOrAnchor::Anchor(anchor) => take_anchored_lines(&s, anchor),\n                    })\n                    .with_context(|| {\n                        format!(\n                            \"Could not read file for link {} ({})\",\n                            self.link_text,\n                            target.display(),\n                        )\n                    })\n            }\n            LinkType::RustdocInclude(ref pat, ref range_or_anchor) => {\n                let target = base.join(pat);\n\n                fs::read_to_string(&target)\n                    .map(|s| match range_or_anchor {\n                        RangeOrAnchor::Range(range) => {\n                            take_rustdoc_include_lines(&s, range.clone())\n                        }\n                        RangeOrAnchor::Anchor(anchor) => {\n                            take_rustdoc_include_anchored_lines(&s, anchor)\n                        }\n                    })\n                    .with_context(|| {\n                        format!(\n                            \"Could not read file for link {} ({})\",\n                            self.link_text,\n                            target.display(),\n                        )\n                    })\n            }\n            LinkType::Playground(ref pat, ref attrs) => {\n                let target = base.join(pat);\n\n                let mut contents = fs::read_to_string(&target).with_context(|| {\n                    format!(\n                        \"Could not read file for link {} ({})\",\n                        self.link_text,\n                        target.display()\n                    )\n                })?;\n                let ftype = if !attrs.is_empty() { \"rust,\" } else { \"rust\" };\n                if !contents.ends_with('\\n') {\n                    contents.push('\\n');\n                }\n                Ok(format!(\n                    \"```{}{}\\n{}```\\n\",\n                    ftype,\n                    attrs.join(\",\"),\n                    contents\n                ))\n            }\n            LinkType::Title(title) => {\n                *chapter_title = title.to_owned();\n                Ok(String::new())\n            }\n        }\n    }\n}\n\nstruct LinkIter<'a>(CaptureMatches<'a, 'a>);\n\nimpl<'a> Iterator for LinkIter<'a> {\n    type Item = Link<'a>;\n    fn next(&mut self) -> Option<Link<'a>> {\n        for cap in &mut self.0 {\n            if let Some(inc) = Link::from_capture(cap) {\n                return Some(inc);\n            }\n        }\n        None\n    }\n}\n\nfn find_links(contents: &str) -> LinkIter<'_> {\n    static_regex!(\n        LINK,\n        r\"(?x)              # insignificant whitespace mode\n        \\\\\\{\\{\\#.*\\}\\}      # match escaped link\n        |                   # or\n        \\{\\{\\s*             # link opening parens and whitespace\n        \\#([a-zA-Z0-9_]+)   # link type\n        \\s+                 # separating whitespace\n        ([^}]+)             # link target path and space separated properties\n        \\}\\}                # link closing parens\"\n    );\n\n    LinkIter(LINK.captures_iter(contents))\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn test_replace_all_escaped() {\n        let start = r\"\n        Some text over here.\n        ```hbs\n        \\{{#include file.rs}} << an escaped link!\n        ```\";\n        let end = r\"\n        Some text over here.\n        ```hbs\n        {{#include file.rs}} << an escaped link!\n        ```\";\n        let mut chapter_title = \"test_replace_all_escaped\".to_owned();\n        assert_eq!(replace_all(start, \"\", \"\", 0, &mut chapter_title), end);\n    }\n\n    #[test]\n    fn test_set_chapter_title() {\n        let start = r\"{{#title My Title}}\n        # My Chapter\n        \";\n        let end = r\"\n        # My Chapter\n        \";\n        let mut chapter_title = \"test_set_chapter_title\".to_owned();\n        assert_eq!(replace_all(start, \"\", \"\", 0, &mut chapter_title), end);\n        assert_eq!(chapter_title, \"My Title\");\n    }\n\n    #[test]\n    fn test_find_links_no_link() {\n        let s = \"Some random text without link...\";\n        assert!(find_links(s).collect::<Vec<_>>() == vec![]);\n    }\n\n    #[test]\n    fn test_find_links_partial_link() {\n        let s = \"Some random text with {{#playground...\";\n        assert!(find_links(s).collect::<Vec<_>>() == vec![]);\n        let s = \"Some random text with {{#include...\";\n        assert!(find_links(s).collect::<Vec<_>>() == vec![]);\n        let s = \"Some random text with \\\\{{#include...\";\n        assert!(find_links(s).collect::<Vec<_>>() == vec![]);\n    }\n\n    #[test]\n    fn test_find_links_empty_link() {\n        let s = \"Some random text with {{#playground}} and {{#playground   }} {{}} {{#}}...\";\n        assert!(find_links(s).collect::<Vec<_>>() == vec![]);\n    }\n\n    #[test]\n    fn test_find_links_unknown_link_type() {\n        let s = \"Some random text with {{#playgroundz ar.rs}} and {{#incn}} {{baz}} {{#bar}}...\";\n        assert!(find_links(s).collect::<Vec<_>>() == vec![]);\n    }\n\n    #[test]\n    fn test_find_links_simple_link() {\n        let s = \"Some random text with {{#playground file.rs}} and {{#playground test.rs }}...\";\n\n        let res = find_links(s).collect::<Vec<_>>();\n        println!(\"\\nOUTPUT: {res:?}\\n\");\n\n        assert_eq!(\n            res,\n            vec![\n                Link {\n                    start_index: 22,\n                    end_index: 45,\n                    link_type: LinkType::Playground(PathBuf::from(\"file.rs\"), vec![]),\n                    link_text: \"{{#playground file.rs}}\",\n                },\n                Link {\n                    start_index: 50,\n                    end_index: 74,\n                    link_type: LinkType::Playground(PathBuf::from(\"test.rs\"), vec![]),\n                    link_text: \"{{#playground test.rs }}\",\n                },\n            ]\n        );\n    }\n\n    #[test]\n    fn test_find_links_with_special_characters() {\n        let s = \"Some random text with {{#playground foo-bar\\\\baz/_c++.rs}}...\";\n\n        let res = find_links(s).collect::<Vec<_>>();\n        println!(\"\\nOUTPUT: {res:?}\\n\");\n\n        assert_eq!(\n            res,\n            vec![Link {\n                start_index: 22,\n                end_index: 57,\n                link_type: LinkType::Playground(PathBuf::from(\"foo-bar\\\\baz/_c++.rs\"), vec![]),\n                link_text: \"{{#playground foo-bar\\\\baz/_c++.rs}}\",\n            },]\n        );\n    }\n\n    #[test]\n    fn test_find_links_with_range() {\n        let s = \"Some random text with {{#include file.rs:10:20}}...\";\n        let res = find_links(s).collect::<Vec<_>>();\n        println!(\"\\nOUTPUT: {res:?}\\n\");\n        assert_eq!(\n            res,\n            vec![Link {\n                start_index: 22,\n                end_index: 48,\n                link_type: LinkType::Include(\n                    PathBuf::from(\"file.rs\"),\n                    RangeOrAnchor::Range(LineRange::from(9..20))\n                ),\n                link_text: \"{{#include file.rs:10:20}}\",\n            }]\n        );\n    }\n\n    #[test]\n    fn test_find_links_with_line_number() {\n        let s = \"Some random text with {{#include file.rs:10}}...\";\n        let res = find_links(s).collect::<Vec<_>>();\n        println!(\"\\nOUTPUT: {res:?}\\n\");\n        assert_eq!(\n            res,\n            vec![Link {\n                start_index: 22,\n                end_index: 45,\n                link_type: LinkType::Include(\n                    PathBuf::from(\"file.rs\"),\n                    RangeOrAnchor::Range(LineRange::from(9..10))\n                ),\n                link_text: \"{{#include file.rs:10}}\",\n            }]\n        );\n    }\n\n    #[test]\n    fn test_find_links_with_from_range() {\n        let s = \"Some random text with {{#include file.rs:10:}}...\";\n        let res = find_links(s).collect::<Vec<_>>();\n        println!(\"\\nOUTPUT: {res:?}\\n\");\n        assert_eq!(\n            res,\n            vec![Link {\n                start_index: 22,\n                end_index: 46,\n                link_type: LinkType::Include(\n                    PathBuf::from(\"file.rs\"),\n                    RangeOrAnchor::Range(LineRange::from(9..))\n                ),\n                link_text: \"{{#include file.rs:10:}}\",\n            }]\n        );\n    }\n\n    #[test]\n    fn test_find_links_with_to_range() {\n        let s = \"Some random text with {{#include file.rs::20}}...\";\n        let res = find_links(s).collect::<Vec<_>>();\n        println!(\"\\nOUTPUT: {res:?}\\n\");\n        assert_eq!(\n            res,\n            vec![Link {\n                start_index: 22,\n                end_index: 46,\n                link_type: LinkType::Include(\n                    PathBuf::from(\"file.rs\"),\n                    RangeOrAnchor::Range(LineRange::from(..20))\n                ),\n                link_text: \"{{#include file.rs::20}}\",\n            }]\n        );\n    }\n\n    #[test]\n    fn test_find_links_with_full_range() {\n        let s = \"Some random text with {{#include file.rs::}}...\";\n        let res = find_links(s).collect::<Vec<_>>();\n        println!(\"\\nOUTPUT: {res:?}\\n\");\n        assert_eq!(\n            res,\n            vec![Link {\n                start_index: 22,\n                end_index: 44,\n                link_type: LinkType::Include(\n                    PathBuf::from(\"file.rs\"),\n                    RangeOrAnchor::Range(LineRange::from(..))\n                ),\n                link_text: \"{{#include file.rs::}}\",\n            }]\n        );\n    }\n\n    #[test]\n    fn test_find_links_with_no_range_specified() {\n        let s = \"Some random text with {{#include file.rs}}...\";\n        let res = find_links(s).collect::<Vec<_>>();\n        println!(\"\\nOUTPUT: {res:?}\\n\");\n        assert_eq!(\n            res,\n            vec![Link {\n                start_index: 22,\n                end_index: 42,\n                link_type: LinkType::Include(\n                    PathBuf::from(\"file.rs\"),\n                    RangeOrAnchor::Range(LineRange::from(..))\n                ),\n                link_text: \"{{#include file.rs}}\",\n            }]\n        );\n    }\n\n    #[test]\n    fn test_find_links_with_anchor() {\n        let s = \"Some random text with {{#include file.rs:anchor}}...\";\n        let res = find_links(s).collect::<Vec<_>>();\n        println!(\"\\nOUTPUT: {res:?}\\n\");\n        assert_eq!(\n            res,\n            vec![Link {\n                start_index: 22,\n                end_index: 49,\n                link_type: LinkType::Include(\n                    PathBuf::from(\"file.rs\"),\n                    RangeOrAnchor::Anchor(String::from(\"anchor\"))\n                ),\n                link_text: \"{{#include file.rs:anchor}}\",\n            }]\n        );\n    }\n\n    #[test]\n    fn test_find_links_escaped_link() {\n        let s = \"Some random text with escaped playground \\\\{{#playground file.rs editable}} ...\";\n\n        let res = find_links(s).collect::<Vec<_>>();\n        println!(\"\\nOUTPUT: {res:?}\\n\");\n\n        assert_eq!(\n            res,\n            vec![Link {\n                start_index: 41,\n                end_index: 74,\n                link_type: LinkType::Escaped,\n                link_text: \"\\\\{{#playground file.rs editable}}\",\n            }]\n        );\n    }\n\n    #[test]\n    fn test_find_playgrounds_with_properties() {\n        let s = \"Some random text with escaped playground {{#playground file.rs editable }} and some \\\n                 more\\n text {{#playground my.rs editable no_run should_panic}} ...\";\n\n        let res = find_links(s).collect::<Vec<_>>();\n        println!(\"\\nOUTPUT: {res:?}\\n\");\n        assert_eq!(\n            res,\n            vec![\n                Link {\n                    start_index: 41,\n                    end_index: 74,\n                    link_type: LinkType::Playground(PathBuf::from(\"file.rs\"), vec![\"editable\"]),\n                    link_text: \"{{#playground file.rs editable }}\",\n                },\n                Link {\n                    start_index: 95,\n                    end_index: 145,\n                    link_type: LinkType::Playground(\n                        PathBuf::from(\"my.rs\"),\n                        vec![\"editable\", \"no_run\", \"should_panic\"],\n                    ),\n                    link_text: \"{{#playground my.rs editable no_run should_panic}}\",\n                },\n            ]\n        );\n    }\n\n    #[test]\n    fn test_find_all_link_types() {\n        let s = \"Some random text with escaped playground {{#include file.rs}} and \\\\{{#contents are \\\n                 insignifficant in escaped link}} some more\\n text  {{#playground my.rs editable \\\n                 no_run should_panic}} ...\";\n\n        let res = find_links(s).collect::<Vec<_>>();\n        println!(\"\\nOUTPUT: {res:?}\\n\");\n        assert_eq!(res.len(), 3);\n        assert_eq!(\n            res[0],\n            Link {\n                start_index: 41,\n                end_index: 61,\n                link_type: LinkType::Include(\n                    PathBuf::from(\"file.rs\"),\n                    RangeOrAnchor::Range(LineRange::from(..))\n                ),\n                link_text: \"{{#include file.rs}}\",\n            }\n        );\n        assert_eq!(\n            res[1],\n            Link {\n                start_index: 66,\n                end_index: 115,\n                link_type: LinkType::Escaped,\n                link_text: \"\\\\{{#contents are insignifficant in escaped link}}\",\n            }\n        );\n        assert_eq!(\n            res[2],\n            Link {\n                start_index: 133,\n                end_index: 183,\n                link_type: LinkType::Playground(\n                    PathBuf::from(\"my.rs\"),\n                    vec![\"editable\", \"no_run\", \"should_panic\"]\n                ),\n                link_text: \"{{#playground my.rs editable no_run should_panic}}\",\n            }\n        );\n    }\n\n    #[test]\n    fn parse_without_colon_includes_all() {\n        let link_type = parse_include_path(\"arbitrary\");\n        assert_eq!(\n            link_type,\n            LinkType::Include(\n                PathBuf::from(\"arbitrary\"),\n                RangeOrAnchor::Range(LineRange::from(RangeFull))\n            )\n        );\n    }\n\n    #[test]\n    fn parse_with_nothing_after_colon_includes_all() {\n        let link_type = parse_include_path(\"arbitrary:\");\n        assert_eq!(\n            link_type,\n            LinkType::Include(\n                PathBuf::from(\"arbitrary\"),\n                RangeOrAnchor::Range(LineRange::from(RangeFull))\n            )\n        );\n    }\n\n    #[test]\n    fn parse_with_two_colons_includes_all() {\n        let link_type = parse_include_path(\"arbitrary::\");\n        assert_eq!(\n            link_type,\n            LinkType::Include(\n                PathBuf::from(\"arbitrary\"),\n                RangeOrAnchor::Range(LineRange::from(RangeFull))\n            )\n        );\n    }\n\n    #[test]\n    fn parse_with_garbage_after_two_colons_includes_all() {\n        let link_type = parse_include_path(\"arbitrary::NaN\");\n        assert_eq!(\n            link_type,\n            LinkType::Include(\n                PathBuf::from(\"arbitrary\"),\n                RangeOrAnchor::Range(LineRange::from(RangeFull))\n            )\n        );\n    }\n\n    #[test]\n    fn parse_with_one_number_after_colon_only_that_line() {\n        let link_type = parse_include_path(\"arbitrary:5\");\n        assert_eq!(\n            link_type,\n            LinkType::Include(\n                PathBuf::from(\"arbitrary\"),\n                RangeOrAnchor::Range(LineRange::from(4..5))\n            )\n        );\n    }\n\n    #[test]\n    fn parse_with_one_based_start_becomes_zero_based() {\n        let link_type = parse_include_path(\"arbitrary:1\");\n        assert_eq!(\n            link_type,\n            LinkType::Include(\n                PathBuf::from(\"arbitrary\"),\n                RangeOrAnchor::Range(LineRange::from(0..1))\n            )\n        );\n    }\n\n    #[test]\n    fn parse_with_zero_based_start_stays_zero_based_but_is_probably_an_error() {\n        let link_type = parse_include_path(\"arbitrary:0\");\n        assert_eq!(\n            link_type,\n            LinkType::Include(\n                PathBuf::from(\"arbitrary\"),\n                RangeOrAnchor::Range(LineRange::from(0..1))\n            )\n        );\n    }\n\n    #[test]\n    fn parse_start_only_range() {\n        let link_type = parse_include_path(\"arbitrary:5:\");\n        assert_eq!(\n            link_type,\n            LinkType::Include(\n                PathBuf::from(\"arbitrary\"),\n                RangeOrAnchor::Range(LineRange::from(4..))\n            )\n        );\n    }\n\n    #[test]\n    fn parse_start_with_garbage_interpreted_as_start_only_range() {\n        let link_type = parse_include_path(\"arbitrary:5:NaN\");\n        assert_eq!(\n            link_type,\n            LinkType::Include(\n                PathBuf::from(\"arbitrary\"),\n                RangeOrAnchor::Range(LineRange::from(4..))\n            )\n        );\n    }\n\n    #[test]\n    fn parse_end_only_range() {\n        let link_type = parse_include_path(\"arbitrary::5\");\n        assert_eq!(\n            link_type,\n            LinkType::Include(\n                PathBuf::from(\"arbitrary\"),\n                RangeOrAnchor::Range(LineRange::from(..5))\n            )\n        );\n    }\n\n    #[test]\n    fn parse_start_and_end_range() {\n        let link_type = parse_include_path(\"arbitrary:5:10\");\n        assert_eq!(\n            link_type,\n            LinkType::Include(\n                PathBuf::from(\"arbitrary\"),\n                RangeOrAnchor::Range(LineRange::from(4..10))\n            )\n        );\n    }\n\n    #[test]\n    fn parse_with_negative_interpreted_as_anchor() {\n        let link_type = parse_include_path(\"arbitrary:-5\");\n        assert_eq!(\n            link_type,\n            LinkType::Include(\n                PathBuf::from(\"arbitrary\"),\n                RangeOrAnchor::Anchor(\"-5\".to_string())\n            )\n        );\n    }\n\n    #[test]\n    fn parse_with_floating_point_interpreted_as_anchor() {\n        let link_type = parse_include_path(\"arbitrary:-5.7\");\n        assert_eq!(\n            link_type,\n            LinkType::Include(\n                PathBuf::from(\"arbitrary\"),\n                RangeOrAnchor::Anchor(\"-5.7\".to_string())\n            )\n        );\n    }\n\n    #[test]\n    fn parse_with_anchor_followed_by_colon() {\n        let link_type = parse_include_path(\"arbitrary:some-anchor:this-gets-ignored\");\n        assert_eq!(\n            link_type,\n            LinkType::Include(\n                PathBuf::from(\"arbitrary\"),\n                RangeOrAnchor::Anchor(\"some-anchor\".to_string())\n            )\n        );\n    }\n\n    #[test]\n    fn parse_with_more_than_three_colons_ignores_everything_after_third_colon() {\n        let link_type = parse_include_path(\"arbitrary:5:10:17:anything:\");\n        assert_eq!(\n            link_type,\n            LinkType::Include(\n                PathBuf::from(\"arbitrary\"),\n                RangeOrAnchor::Range(LineRange::from(4..10))\n            )\n        );\n    }\n}\n"
  },
  {
    "path": "crates/mdbook-driver/src/builtin_preprocessors/mod.rs",
    "content": "//! Built-in preprocessors.\n\npub use self::cmd::CmdPreprocessor;\npub use self::index::IndexPreprocessor;\npub use self::links::LinkPreprocessor;\n\nmod cmd;\nmod index;\nmod links;\n"
  },
  {
    "path": "crates/mdbook-driver/src/builtin_renderers/markdown_renderer.rs",
    "content": "use anyhow::{Context, Result};\nuse mdbook_core::utils::fs;\nuse mdbook_renderer::{RenderContext, Renderer};\nuse tracing::trace;\n\n/// A renderer to output the Markdown after the preprocessors have run. Mostly useful\n/// when debugging preprocessors.\n#[derive(Default)]\n#[non_exhaustive]\npub struct MarkdownRenderer;\n\nimpl MarkdownRenderer {\n    /// Create a new `MarkdownRenderer` instance.\n    pub fn new() -> Self {\n        MarkdownRenderer\n    }\n}\n\nimpl Renderer for MarkdownRenderer {\n    fn name(&self) -> &str {\n        \"markdown\"\n    }\n\n    fn render(&self, ctx: &RenderContext) -> Result<()> {\n        let destination = &ctx.destination;\n        let book = &ctx.book;\n\n        if destination.exists() {\n            fs::remove_dir_content(destination)\n                .with_context(|| \"Unable to remove stale Markdown output\")?;\n        }\n\n        trace!(\"markdown render\");\n        for ch in book.chapters() {\n            let path = ctx\n                .destination\n                .join(ch.path.as_ref().expect(\"Checked path exists before\"));\n            fs::write(path, &ch.content)?;\n        }\n\n        fs::create_dir_all(destination)\n            .with_context(|| \"Unexpected error when constructing destination path\")?;\n\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "crates/mdbook-driver/src/builtin_renderers/mod.rs",
    "content": "//! Built-in renderers.\n//!\n//! The HTML renderer can be found in the [`mdbook_html`] crate.\n\nuse anyhow::{Context, Result, bail};\nuse mdbook_core::utils::fs;\nuse mdbook_renderer::{RenderContext, Renderer};\nuse std::process::Stdio;\nuse tracing::{error, info, trace, warn};\n\npub use self::markdown_renderer::MarkdownRenderer;\n\nmod markdown_renderer;\n\n/// A generic renderer which will shell out to an arbitrary executable.\n///\n/// See <https://rust-lang.github.io/mdBook/for_developers/backends.html>\n/// for a description of the renderer protocol.\n#[derive(Debug, Clone, PartialEq)]\npub struct CmdRenderer {\n    name: String,\n    cmd: String,\n}\n\nimpl CmdRenderer {\n    /// Create a new `CmdRenderer` which will invoke the provided `cmd` string.\n    pub fn new(name: String, cmd: String) -> CmdRenderer {\n        CmdRenderer { name, cmd }\n    }\n}\n\nimpl Renderer for CmdRenderer {\n    fn name(&self) -> &str {\n        &self.name\n    }\n\n    fn render(&self, ctx: &RenderContext) -> Result<()> {\n        info!(\"Invoking the \\\"{}\\\" renderer\", self.name);\n\n        let optional_key = format!(\"output.{}.optional\", self.name);\n        let optional = match ctx.config.get(&optional_key) {\n            Ok(Some(value)) => value,\n            Err(e) => bail!(\"expected bool for `{optional_key}`: {e}\"),\n            Ok(None) => false,\n        };\n\n        let _ = fs::create_dir_all(&ctx.destination);\n\n        let mut cmd = crate::compose_command(&self.cmd, &ctx.root)?;\n        let mut child = match cmd\n            .stdin(Stdio::piped())\n            .stdout(Stdio::inherit())\n            .stderr(Stdio::inherit())\n            .current_dir(&ctx.destination)\n            .spawn()\n        {\n            Ok(c) => c,\n            Err(e) => {\n                return crate::handle_command_error(\n                    e, optional, \"output\", \"backend\", &self.name, &self.cmd,\n                );\n            }\n        };\n\n        let mut stdin = child.stdin.take().expect(\"Child has stdin\");\n        if let Err(e) = serde_json::to_writer(&mut stdin, &ctx) {\n            // Looks like the backend hung up before we could finish\n            // sending it the render context. Log the error and keep going\n            warn!(\"Error writing the RenderContext to the backend, {}\", e);\n        }\n\n        // explicitly close the `stdin` file handle\n        drop(stdin);\n\n        let status = child\n            .wait()\n            .with_context(|| \"Error waiting for the backend to complete\")?;\n\n        trace!(\"{} exited with output: {:?}\", self.cmd, status);\n\n        if !status.success() {\n            error!(\"Renderer exited with non-zero return code.\");\n            bail!(\"The \\\"{}\\\" renderer failed\", self.name);\n        } else {\n            Ok(())\n        }\n    }\n}\n"
  },
  {
    "path": "crates/mdbook-driver/src/init.rs",
    "content": "//! Support for initializing a new book.\n\nuse super::MDBook;\nuse anyhow::{Context, Result};\nuse mdbook_core::config::Config;\nuse mdbook_core::utils::fs;\nuse mdbook_html::theme::Theme;\nuse std::path::PathBuf;\nuse tracing::{debug, error, info, trace};\n\n/// A helper for setting up a new book and its directory structure.\n#[derive(Debug, Clone, PartialEq)]\npub struct BookBuilder {\n    root: PathBuf,\n    create_gitignore: bool,\n    config: Config,\n    copy_theme: bool,\n}\n\nimpl BookBuilder {\n    /// Create a new `BookBuilder` which will generate a book in the provided\n    /// root directory.\n    pub fn new<P: Into<PathBuf>>(root: P) -> BookBuilder {\n        BookBuilder {\n            root: root.into(),\n            create_gitignore: false,\n            config: Config::default(),\n            copy_theme: false,\n        }\n    }\n\n    /// Set the [`Config`] to be used.\n    pub fn with_config(&mut self, cfg: Config) -> &mut BookBuilder {\n        self.config = cfg;\n        self\n    }\n\n    /// Get the config used by the `BookBuilder`.\n    pub fn config(&self) -> &Config {\n        &self.config\n    }\n\n    /// Should the theme be copied into the generated book (so users can tweak\n    /// it)?\n    pub fn copy_theme(&mut self, copy: bool) -> &mut BookBuilder {\n        self.copy_theme = copy;\n        self\n    }\n\n    /// Should we create a `.gitignore` file?\n    pub fn create_gitignore(&mut self, create: bool) -> &mut BookBuilder {\n        self.create_gitignore = create;\n        self\n    }\n\n    /// Generate the actual book. This will:\n    ///\n    /// - Create the directory structure.\n    /// - Stub out some dummy chapters and the `SUMMARY.md`.\n    /// - Create a `.gitignore` (if applicable)\n    /// - Create a themes directory and populate it (if applicable)\n    /// - Generate a `book.toml` file,\n    /// - Then load the book so we can build it or run tests.\n    pub fn build(&self) -> Result<MDBook> {\n        info!(\"Creating a new book with stub content\");\n\n        self.create_directory_structure()\n            .with_context(|| \"Unable to create directory structure\")?;\n\n        self.create_stub_files()\n            .with_context(|| \"Unable to create stub files\")?;\n\n        if self.create_gitignore {\n            self.build_gitignore()\n                .with_context(|| \"Unable to create .gitignore\")?;\n        }\n\n        if self.copy_theme {\n            self.copy_across_theme()\n                .with_context(|| \"Unable to copy across the theme\")?;\n        }\n\n        self.write_book_toml()?;\n\n        match MDBook::load(&self.root) {\n            Ok(book) => Ok(book),\n            Err(e) => {\n                error!(\"{}\", e);\n\n                panic!(\n                    \"The BookBuilder should always create a valid book. If you are seeing this it \\\n                     is a bug and should be reported.\"\n                );\n            }\n        }\n    }\n\n    fn write_book_toml(&self) -> Result<()> {\n        debug!(\"Writing book.toml\");\n        let book_toml = self.root.join(\"book.toml\");\n        let cfg =\n            toml::to_string(&self.config).with_context(|| \"Unable to serialize the config\")?;\n\n        fs::write(&book_toml, cfg)?;\n        Ok(())\n    }\n\n    fn copy_across_theme(&self) -> Result<()> {\n        debug!(\"Copying theme\");\n\n        let html_config = self.config.html_config().unwrap_or_default();\n        Theme::copy_theme(&html_config, &self.root)?;\n        Ok(())\n    }\n\n    fn build_gitignore(&self) -> Result<()> {\n        fs::write(\n            self.root.join(\".gitignore\"),\n            format!(\"{}\", self.config.build.build_dir.display()),\n        )?;\n        Ok(())\n    }\n\n    fn create_stub_files(&self) -> Result<()> {\n        debug!(\"Creating example book contents\");\n        let src_dir = self.root.join(&self.config.book.src);\n\n        let summary = src_dir.join(\"SUMMARY.md\");\n        if !summary.exists() {\n            trace!(\"No summary found creating stub summary and chapter_1.md.\");\n            fs::write(\n                summary,\n                \"# Summary\\n\\\n                 \\n\\\n                 - [Chapter 1](./chapter_1.md)\\n\",\n            )?;\n\n            fs::write(src_dir.join(\"chapter_1.md\"), \"# Chapter 1\\n\")?;\n        } else {\n            trace!(\"Existing summary found, no need to create stub files.\");\n        }\n        Ok(())\n    }\n\n    fn create_directory_structure(&self) -> Result<()> {\n        debug!(\"Creating directory tree\");\n        fs::create_dir_all(&self.root)?;\n\n        let src = self.root.join(&self.config.book.src);\n        fs::create_dir_all(src)?;\n\n        let build = self.root.join(&self.config.build.build_dir);\n        fs::create_dir_all(build)?;\n\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "crates/mdbook-driver/src/lib.rs",
    "content": "//! High-level library for running mdBook.\n//!\n//! This is the high-level library for running\n//! [mdBook](https://rust-lang.github.io/mdBook/). There are several\n//! reasons for using the programmatic API (over the CLI):\n//!\n//! - Integrate mdBook in a current project.\n//! - Extend the capabilities of mdBook.\n//! - Do some processing or test before building your book.\n//! - Accessing the public API to help create a new Renderer.\n//!\n//! ## Additional crates\n//!\n//! In addition to `mdbook-driver`, there are several other crates available\n//! for using and extending mdBook:\n//!\n//! - [`mdbook_preprocessor`]: Provides support for implementing preprocessors.\n//! - [`mdbook_renderer`]: Provides support for implementing renderers.\n//! - [`mdbook_markdown`]: The Markdown renderer.\n//! - [`mdbook_summary`]: The `SUMMARY.md` parser.\n//! - [`mdbook_html`]: The HTML renderer.\n//! - [`mdbook_core`]: An internal library that is used by the other crates\n//!   for shared types. Types from this crate are rexported from the other\n//!   crates as appropriate.\n//!\n//! ## Cargo features\n//!\n//! The following cargo features are available:\n//!\n//! - `search`: Enables the search index in the HTML renderer.\n//!\n//! ## Examples\n//!\n//! If creating a new book from scratch, you'll want to get a [`init::BookBuilder`] via\n//! the [`MDBook::init()`] method.\n//!\n//! ```rust,no_run\n//! use mdbook_driver::MDBook;\n//! use mdbook_driver::config::Config;\n//!\n//! let root_dir = \"/path/to/book/root\";\n//!\n//! // create a default config and change a couple things\n//! let mut cfg = Config::default();\n//! cfg.book.title = Some(\"My Book\".to_string());\n//! cfg.book.authors.push(\"Michael-F-Bryan\".to_string());\n//!\n//! MDBook::init(root_dir)\n//!     .create_gitignore(true)\n//!     .with_config(cfg)\n//!     .build()\n//!     .expect(\"Book generation failed\");\n//! ```\n//!\n//! You can also load an existing book and build it.\n//!\n//! ```rust,no_run\n//! use mdbook_driver::MDBook;\n//!\n//! let root_dir = \"/path/to/book/root\";\n//!\n//! let mut md = MDBook::load(root_dir)\n//!     .expect(\"Unable to load the book\");\n//! md.build().expect(\"Building failed\");\n//! ```\n\npub mod builtin_preprocessors;\npub mod builtin_renderers;\npub mod init;\nmod load;\nmod mdbook;\n\nuse anyhow::{Context, Result, bail};\npub use mdbook::MDBook;\npub use mdbook_core::{book, config, errors};\nuse shlex::Shlex;\nuse std::path::{Path, PathBuf};\nuse std::process::Command;\nuse tracing::{error, warn};\n\n/// Creates a [`Command`] for command renderers and preprocessors.\nfn compose_command(cmd: &str, root: &Path) -> Result<Command> {\n    let mut words = Shlex::new(cmd);\n    let exe = match words.next() {\n        Some(e) => PathBuf::from(e),\n        None => bail!(\"Command string was empty\"),\n    };\n\n    let exe = if exe.components().count() == 1 {\n        // Search PATH for the executable.\n        exe\n    } else {\n        // Relative path is relative to book root.\n        root.join(&exe)\n    };\n\n    let mut cmd = Command::new(exe);\n\n    for arg in words {\n        cmd.arg(arg);\n    }\n\n    Ok(cmd)\n}\n\n/// Handles a failure for a preprocessor or renderer.\nfn handle_command_error(\n    error: std::io::Error,\n    optional: bool,\n    key: &str,\n    what: &str,\n    name: &str,\n    cmd: &str,\n) -> Result<()> {\n    if let std::io::ErrorKind::NotFound = error.kind() {\n        if optional {\n            warn!(\n                \"The command `{cmd}` for {what} `{name}` was not found, \\\n                 but is marked as optional.\",\n            );\n            return Ok(());\n        } else {\n            error!(\n                \"The command `{cmd}` wasn't found, is the `{name}` {what} installed? \\\n                If you want to ignore this error when the `{name}` {what} is not installed, \\\n                set `optional = true` in the `[{key}.{name}]` section of the book.toml configuration file.\",\n            );\n        }\n    }\n    Err(error).with_context(|| format!(\"Unable to run the {what} `{name}`\"))?\n}\n"
  },
  {
    "path": "crates/mdbook-driver/src/load.rs",
    "content": "use anyhow::{Context, Result};\nuse mdbook_core::book::{Book, BookItem, Chapter};\nuse mdbook_core::config::BuildConfig;\nuse mdbook_core::utils::{escape_html, fs};\nuse mdbook_summary::{Link, Summary, SummaryItem, parse_summary};\nuse std::path::Path;\nuse tracing::debug;\n\n/// Load a book into memory from its `src/` directory.\npub(crate) fn load_book<P: AsRef<Path>>(src_dir: P, cfg: &BuildConfig) -> Result<Book> {\n    let src_dir = src_dir.as_ref();\n    let summary_md = src_dir.join(\"SUMMARY.md\");\n\n    let summary_content = fs::read_to_string(&summary_md)?;\n    let summary = parse_summary(&summary_content)\n        .with_context(|| format!(\"Summary parsing failed for file={summary_md:?}\"))?;\n\n    if cfg.create_missing {\n        create_missing(src_dir, &summary).with_context(|| \"Unable to create missing chapters\")?;\n    }\n\n    load_book_from_disk(&summary, src_dir)\n}\n\nfn create_missing(src_dir: &Path, summary: &Summary) -> Result<()> {\n    let mut items: Vec<_> = summary\n        .prefix_chapters\n        .iter()\n        .chain(summary.numbered_chapters.iter())\n        .chain(summary.suffix_chapters.iter())\n        .collect();\n\n    while let Some(next) = items.pop() {\n        if let SummaryItem::Link(ref link) = *next {\n            if let Some(ref location) = link.location {\n                let filename = src_dir.join(location);\n                if !filename.exists() {\n                    if let Some(parent) = filename.parent() {\n                        if !parent.exists() {\n                            fs::create_dir_all(parent)?;\n                        }\n                    }\n                    debug!(\"Creating missing file {}\", filename.display());\n                    let title = escape_html(&link.name);\n                    fs::write(&filename, format!(\"# {title}\\n\"))?;\n                }\n            }\n\n            items.extend(&link.nested_items);\n        }\n    }\n\n    Ok(())\n}\n\n/// Use the provided `Summary` to load a `Book` from disk.\n///\n/// You need to pass in the book's source directory because all the links in\n/// `SUMMARY.md` give the chapter locations relative to it.\npub(crate) fn load_book_from_disk<P: AsRef<Path>>(summary: &Summary, src_dir: P) -> Result<Book> {\n    debug!(\"Loading the book from disk\");\n    let src_dir = src_dir.as_ref();\n\n    let prefix = summary.prefix_chapters.iter();\n    let numbered = summary.numbered_chapters.iter();\n    let suffix = summary.suffix_chapters.iter();\n\n    let summary_items = prefix.chain(numbered).chain(suffix);\n\n    let mut chapters = Vec::new();\n\n    for summary_item in summary_items {\n        let chapter = load_summary_item(summary_item, src_dir, Vec::new())?;\n        chapters.push(chapter);\n    }\n\n    Ok(Book::new_with_items(chapters))\n}\n\nfn load_summary_item<P: AsRef<Path> + Clone>(\n    item: &SummaryItem,\n    src_dir: P,\n    parent_names: Vec<String>,\n) -> Result<BookItem> {\n    match item {\n        SummaryItem::Separator => Ok(BookItem::Separator),\n        SummaryItem::Link(link) => load_chapter(link, src_dir, parent_names).map(BookItem::Chapter),\n        SummaryItem::PartTitle(title) => Ok(BookItem::PartTitle(title.clone())),\n        _ => panic!(\"SummaryItem {item:?} not covered\"),\n    }\n}\n\nfn load_chapter<P: AsRef<Path>>(\n    link: &Link,\n    src_dir: P,\n    parent_names: Vec<String>,\n) -> Result<Chapter> {\n    let src_dir = src_dir.as_ref();\n\n    let mut ch = if let Some(ref link_location) = link.location {\n        debug!(\"Loading {} ({})\", link.name, link_location.display());\n\n        let location = if link_location.is_absolute() {\n            link_location.clone()\n        } else {\n            src_dir.join(link_location)\n        };\n\n        let mut content = std::fs::read_to_string(&location)\n            .with_context(|| format!(\"failed to read chapter `{}`\", link_location.display()))?;\n\n        if content.as_bytes().starts_with(b\"\\xef\\xbb\\xbf\") {\n            content.replace_range(..3, \"\");\n        }\n\n        let stripped = location\n            .strip_prefix(src_dir)\n            .expect(\"Chapters are always inside a book\");\n\n        Chapter::new(&link.name, content, stripped, parent_names.clone())\n    } else {\n        Chapter::new_draft(&link.name, parent_names.clone())\n    };\n\n    let mut sub_item_parents = parent_names;\n\n    ch.number = link.number.clone();\n\n    sub_item_parents.push(link.name.clone());\n    let sub_items = link\n        .nested_items\n        .iter()\n        .map(|i| load_summary_item(i, src_dir, sub_item_parents.clone()))\n        .collect::<Result<Vec<_>>>()?;\n\n    ch.sub_items = sub_items;\n\n    Ok(ch)\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use mdbook_core::book::SectionNumber;\n    use std::path::PathBuf;\n    use tempfile::{Builder as TempFileBuilder, TempDir};\n\n    const DUMMY_SRC: &str = \"\n# Dummy Chapter\n\nthis is some dummy text.\n\nAnd here is some \\\n                                     more text.\n\";\n\n    /// Create a dummy `Link` in a temporary directory.\n    fn dummy_link() -> (Link, TempDir) {\n        let temp = TempFileBuilder::new().prefix(\"book\").tempdir().unwrap();\n\n        let chapter_path = temp.path().join(\"chapter_1.md\");\n        fs::write(&chapter_path, DUMMY_SRC).unwrap();\n\n        let link = Link::new(\"Chapter 1\", chapter_path);\n\n        (link, temp)\n    }\n\n    /// Create a nested `Link` written to a temporary directory.\n    fn nested_links() -> (Link, TempDir) {\n        let (mut root, temp_dir) = dummy_link();\n\n        let second_path = temp_dir.path().join(\"second.md\");\n        fs::write(&second_path, \"Hello World!\").unwrap();\n\n        let mut second = Link::new(\"Nested Chapter 1\", &second_path);\n        second.number = Some(SectionNumber::new([1, 2]));\n\n        root.nested_items.push(second.clone().into());\n        root.nested_items.push(SummaryItem::Separator);\n        root.nested_items.push(second.into());\n\n        (root, temp_dir)\n    }\n\n    #[test]\n    fn load_a_single_chapter_from_disk() {\n        let (link, temp_dir) = dummy_link();\n        let should_be = Chapter::new(\n            \"Chapter 1\",\n            DUMMY_SRC.to_string(),\n            \"chapter_1.md\",\n            Vec::new(),\n        );\n\n        let got = load_chapter(&link, temp_dir.path(), Vec::new()).unwrap();\n        assert_eq!(got, should_be);\n    }\n\n    #[test]\n    fn load_a_single_chapter_with_utf8_bom_from_disk() {\n        let temp_dir = TempFileBuilder::new().prefix(\"book\").tempdir().unwrap();\n\n        let chapter_path = temp_dir.path().join(\"chapter_1.md\");\n        fs::write(&chapter_path, format!(\"\\u{feff}{DUMMY_SRC}\")).unwrap();\n\n        let link = Link::new(\"Chapter 1\", chapter_path);\n\n        let should_be = Chapter::new(\n            \"Chapter 1\",\n            DUMMY_SRC.to_string(),\n            \"chapter_1.md\",\n            Vec::new(),\n        );\n\n        let got = load_chapter(&link, temp_dir.path(), Vec::new()).unwrap();\n        assert_eq!(got, should_be);\n    }\n\n    #[test]\n    fn cant_load_a_nonexistent_chapter() {\n        let link = Link::new(\"Chapter 1\", \"/foo/bar/baz.md\");\n\n        let got = load_chapter(&link, \"\", Vec::new());\n        assert!(got.is_err());\n    }\n\n    #[test]\n    fn load_recursive_link_with_separators() {\n        let (root, temp) = nested_links();\n\n        let mut nested = Chapter::new(\n            \"Nested Chapter 1\",\n            String::from(\"Hello World!\"),\n            \"second.md\",\n            vec![String::from(\"Chapter 1\")],\n        );\n        nested.number = Some(SectionNumber::new([1, 2]));\n        let mut chapter =\n            Chapter::new(\"Chapter 1\", String::from(DUMMY_SRC), \"chapter_1.md\", vec![]);\n        chapter.sub_items = vec![\n            BookItem::Chapter(nested.clone()),\n            BookItem::Separator,\n            BookItem::Chapter(nested),\n        ];\n        let should_be = BookItem::Chapter(chapter);\n\n        let got = load_summary_item(&SummaryItem::Link(root), temp.path(), Vec::new()).unwrap();\n        assert_eq!(got, should_be);\n    }\n\n    #[test]\n    fn load_a_book_with_a_single_chapter() {\n        let (link, temp) = dummy_link();\n        let mut summary = Summary::default();\n        summary.numbered_chapters = vec![SummaryItem::Link(link)];\n        let chapter = Chapter::new(\n            \"Chapter 1\",\n            String::from(DUMMY_SRC),\n            PathBuf::from(\"chapter_1.md\"),\n            vec![],\n        );\n        let items = vec![BookItem::Chapter(chapter)];\n        let should_be = Book::new_with_items(items);\n\n        let got = load_book_from_disk(&summary, temp.path()).unwrap();\n\n        assert_eq!(got, should_be);\n    }\n\n    #[test]\n    fn cant_load_chapters_with_an_empty_path() {\n        let (_, temp) = dummy_link();\n        let mut summary = Summary::default();\n        let link = Link::new(\"Empty\", \"\");\n        summary.numbered_chapters = vec![SummaryItem::Link(link)];\n        let got = load_book_from_disk(&summary, temp.path());\n        assert!(got.is_err());\n    }\n\n    #[test]\n    fn cant_load_chapters_when_the_link_is_a_directory() {\n        let (_, temp) = dummy_link();\n        let dir = temp.path().join(\"nested\");\n        fs::create_dir_all(&dir).unwrap();\n\n        let mut summary = Summary::default();\n        let link = Link::new(\"nested\", dir);\n        summary.numbered_chapters = vec![SummaryItem::Link(link)];\n\n        let got = load_book_from_disk(&summary, temp.path());\n        assert!(got.is_err());\n    }\n\n    #[test]\n    fn cant_open_summary_md() {\n        let cfg = BuildConfig::default();\n        let temp_dir = TempFileBuilder::new().prefix(\"book\").tempdir().unwrap();\n\n        let got = load_book(&temp_dir, &cfg);\n        assert!(got.is_err());\n        let error_message = got.err().unwrap().to_string();\n        let expected = format!(\n            r#\"failed to read `{}`\"#,\n            temp_dir.path().join(\"SUMMARY.md\").display()\n        );\n        assert_eq!(error_message, expected);\n    }\n}\n"
  },
  {
    "path": "crates/mdbook-driver/src/mdbook/tests.rs",
    "content": "use super::*;\nuse std::str::FromStr;\nuse toml::value::{Table, Value};\n\n#[test]\nfn config_defaults_to_html_renderer_if_empty() {\n    let cfg = Config::default();\n\n    // make sure we haven't got anything in the `output` table\n    assert!(cfg.outputs::<toml::Value>().unwrap().is_empty());\n\n    let got = determine_renderers(&cfg).unwrap();\n\n    assert_eq!(got.len(), 1);\n    assert_eq!(got[0].name(), \"html\");\n}\n\n#[test]\nfn add_a_random_renderer_to_the_config() {\n    let mut cfg = Config::default();\n    cfg.set(\"output.random\", Table::new()).unwrap();\n\n    let got = determine_renderers(&cfg).unwrap();\n\n    assert_eq!(got.len(), 1);\n    assert_eq!(got[0].name(), \"random\");\n}\n\n#[test]\nfn add_a_random_renderer_with_custom_command_to_the_config() {\n    let mut cfg = Config::default();\n\n    let mut table = Table::new();\n    table.insert(\"command\".to_string(), Value::String(\"false\".to_string()));\n    cfg.set(\"output.random\", table).unwrap();\n\n    let got = determine_renderers(&cfg).unwrap();\n\n    assert_eq!(got.len(), 1);\n    assert_eq!(got[0].name(), \"random\");\n}\n\n#[test]\nfn config_defaults_to_link_and_index_preprocessor_if_not_set() {\n    let cfg = Config::default();\n\n    // make sure we haven't got anything in the `preprocessor` table\n    assert!(cfg.preprocessors::<toml::Value>().unwrap().is_empty());\n\n    let got = determine_preprocessors(&cfg, Path::new(\"\")).unwrap();\n\n    let names: Vec<_> = got.values().map(|p| p.name()).collect();\n    assert_eq!(names, [\"index\", \"links\"]);\n}\n\n#[test]\nfn use_default_preprocessors_works() {\n    let mut cfg = Config::default();\n    cfg.build.use_default_preprocessors = false;\n\n    let got = determine_preprocessors(&cfg, Path::new(\"\")).unwrap();\n\n    assert_eq!(got.len(), 0);\n}\n\n#[test]\nfn can_determine_third_party_preprocessors() {\n    let cfg_str = r#\"\n        [book]\n        title = \"Some Book\"\n\n        [preprocessor.random]\n\n        [build]\n        build-dir = \"outputs\"\n        create-missing = false\n        \"#;\n\n    let cfg = Config::from_str(cfg_str).unwrap();\n\n    // make sure the `preprocessor.random` table exists\n    assert!(cfg.get::<Value>(\"preprocessor.random\").unwrap().is_some());\n\n    let got = determine_preprocessors(&cfg, Path::new(\"\")).unwrap();\n\n    assert!(got.contains_key(\"random\"));\n}\n\n#[test]\nfn preprocessors_can_provide_their_own_commands() {\n    let cfg_str = r#\"\n        [preprocessor.random]\n        command = \"python random.py\"\n        \"#;\n\n    let cfg = Config::from_str(cfg_str).unwrap();\n\n    // make sure the `preprocessor.random` table exists\n    let random = cfg\n        .get::<OutputConfig>(\"preprocessor.random\")\n        .unwrap()\n        .unwrap();\n    assert_eq!(random.command, Some(\"python random.py\".to_string()));\n}\n\n#[test]\nfn preprocessor_before_must_be_array() {\n    let cfg_str = r#\"\n        [preprocessor.random]\n        before = 0\n        \"#;\n\n    let cfg = Config::from_str(cfg_str).unwrap();\n\n    assert!(determine_preprocessors(&cfg, Path::new(\"\")).is_err());\n}\n\n#[test]\nfn preprocessor_after_must_be_array() {\n    let cfg_str = r#\"\n        [preprocessor.random]\n        after = 0\n        \"#;\n\n    let cfg = Config::from_str(cfg_str).unwrap();\n\n    assert!(determine_preprocessors(&cfg, Path::new(\"\")).is_err());\n}\n\n#[test]\nfn preprocessor_order_is_honored() {\n    let cfg_str = r#\"\n        [preprocessor.random]\n        before = [ \"last\" ]\n        after = [ \"index\" ]\n\n        [preprocessor.last]\n        after = [ \"links\", \"index\" ]\n        \"#;\n\n    let cfg = Config::from_str(cfg_str).unwrap();\n\n    let preprocessors = determine_preprocessors(&cfg, Path::new(\"\")).unwrap();\n    let index = |name| preprocessors.get_index_of(name).unwrap();\n    let assert_before = |before, after| {\n        if index(before) >= index(after) {\n            eprintln!(\"Preprocessor order:\");\n            for preprocessor in preprocessors.keys() {\n                eprintln!(\"  {}\", preprocessor);\n            }\n            panic!(\"{before} should come before {after}\");\n        }\n    };\n\n    assert_before(\"index\", \"random\");\n    assert_before(\"index\", \"last\");\n    assert_before(\"random\", \"last\");\n    assert_before(\"links\", \"last\");\n}\n\n#[test]\nfn cyclic_dependencies_are_detected() {\n    let cfg_str = r#\"\n        [preprocessor.links]\n        before = [ \"index\" ]\n\n        [preprocessor.index]\n        before = [ \"links\" ]\n        \"#;\n\n    let cfg = Config::from_str(cfg_str).unwrap();\n\n    assert!(determine_preprocessors(&cfg, Path::new(\"\")).is_err());\n}\n\n#[test]\nfn dependencies_dont_register_undefined_preprocessors() {\n    let cfg_str = r#\"\n        [preprocessor.links]\n        before = [ \"random\" ]\n        \"#;\n\n    let cfg = Config::from_str(cfg_str).unwrap();\n\n    let preprocessors = determine_preprocessors(&cfg, Path::new(\"\")).unwrap();\n\n    // Does not contain \"random\"\n    assert_eq!(preprocessors.keys().collect::<Vec<_>>(), [\"index\", \"links\"]);\n}\n\n#[test]\nfn dependencies_dont_register_builtin_preprocessors_if_disabled() {\n    let cfg_str = r#\"\n        [preprocessor.random]\n        before = [ \"links\" ]\n\n        [build]\n        use-default-preprocessors = false\n        \"#;\n\n    let cfg = Config::from_str(cfg_str).unwrap();\n\n    let preprocessors = determine_preprocessors(&cfg, Path::new(\"\")).unwrap();\n\n    // Does not contain \"links\"\n    assert_eq!(preprocessors.keys().collect::<Vec<_>>(), [\"random\"]);\n}\n\n#[test]\nfn config_respects_preprocessor_selection() {\n    let cfg_str = r#\"\n        [preprocessor.links]\n        renderers = [\"html\"]\n        \"#;\n\n    let cfg = Config::from_str(cfg_str).unwrap();\n\n    let html_renderer = HtmlHandlebars::default();\n    let pre = LinkPreprocessor::new();\n\n    let should_run = preprocessor_should_run(&pre, &html_renderer, &cfg).unwrap();\n    assert!(should_run);\n}\n\nstruct BoolPreprocessor(bool);\nimpl Preprocessor for BoolPreprocessor {\n    fn name(&self) -> &str {\n        \"bool-preprocessor\"\n    }\n\n    fn run(&self, _ctx: &PreprocessorContext, _book: Book) -> Result<Book> {\n        unimplemented!()\n    }\n\n    fn supports_renderer(&self, _renderer: &str) -> Result<bool> {\n        Ok(self.0)\n    }\n}\n\n#[test]\nfn preprocessor_should_run_falls_back_to_supports_renderer_method() {\n    let cfg = Config::default();\n    let html = HtmlHandlebars::new();\n\n    let should_be = true;\n    let got = preprocessor_should_run(&BoolPreprocessor(should_be), &html, &cfg).unwrap();\n    assert_eq!(got, should_be);\n\n    let should_be = false;\n    let got = preprocessor_should_run(&BoolPreprocessor(should_be), &html, &cfg).unwrap();\n    assert_eq!(got, should_be);\n}\n\n// Default is to sort preprocessors alphabetically.\n#[test]\nfn preprocessor_sorted_by_name() {\n    let cfg_str = r#\"\n        [preprocessor.xyz]\n        [preprocessor.abc]\n        \"#;\n\n    let cfg = Config::from_str(cfg_str).unwrap();\n\n    let got = determine_preprocessors(&cfg, Path::new(\"\")).unwrap();\n\n    let names: Vec<_> = got.values().map(|p| p.name()).collect();\n    assert_eq!(names, [\"abc\", \"index\", \"links\", \"xyz\"]);\n}\n\n// Default is to sort renderers alphabetically.\n#[test]\nfn renderers_sorted_by_name() {\n    let cfg_str = r#\"\n        [output.xyz]\n        [output.abc]\n        \"#;\n\n    let cfg = Config::from_str(cfg_str).unwrap();\n\n    let got = determine_renderers(&cfg).unwrap();\n\n    let names: Vec<_> = got.values().map(|p| p.name()).collect();\n    assert_eq!(names, [\"abc\", \"xyz\"]);\n}\n"
  },
  {
    "path": "crates/mdbook-driver/src/mdbook.rs",
    "content": "//! The high-level interface for loading and rendering books.\n\nuse crate::builtin_preprocessors::{CmdPreprocessor, IndexPreprocessor, LinkPreprocessor};\nuse crate::builtin_renderers::{CmdRenderer, MarkdownRenderer};\nuse crate::init::BookBuilder;\nuse crate::load::{load_book, load_book_from_disk};\nuse anyhow::{Context, Error, Result, bail};\nuse indexmap::IndexMap;\nuse mdbook_core::book::{Book, BookItem, BookItems};\nuse mdbook_core::config::{Config, RustEdition};\nuse mdbook_core::utils::fs;\nuse mdbook_html::HtmlHandlebars;\nuse mdbook_preprocessor::{Preprocessor, PreprocessorContext};\nuse mdbook_renderer::{RenderContext, Renderer};\nuse mdbook_summary::Summary;\nuse serde::Deserialize;\nuse std::ffi::OsString;\nuse std::io::IsTerminal;\nuse std::path::{Path, PathBuf};\nuse std::process::Command;\nuse tempfile::Builder as TempFileBuilder;\nuse topological_sort::TopologicalSort;\nuse tracing::{debug, info, trace, warn};\n\n#[cfg(test)]\nmod tests;\n\n/// The object used to manage and build a book.\npub struct MDBook {\n    /// The book's root directory.\n    pub root: PathBuf,\n\n    /// The configuration used to tweak now a book is built.\n    pub config: Config,\n\n    /// A representation of the book's contents in memory.\n    pub book: Book,\n\n    /// Renderers to execute.\n    renderers: IndexMap<String, Box<dyn Renderer>>,\n\n    /// Pre-processors to be run on the book.\n    preprocessors: IndexMap<String, Box<dyn Preprocessor>>,\n}\n\nimpl MDBook {\n    /// Load a book from its root directory on disk.\n    pub fn load<P: Into<PathBuf>>(book_root: P) -> Result<MDBook> {\n        let book_root = book_root.into();\n        let config_location = book_root.join(\"book.toml\");\n\n        let mut config = if config_location.exists() {\n            debug!(\"Loading config from {}\", config_location.display());\n            Config::from_disk(&config_location)?\n        } else {\n            Config::default()\n        };\n\n        config.update_from_env()?;\n\n        if tracing::enabled!(tracing::Level::TRACE) {\n            for line in format!(\"Config: {config:#?}\").lines() {\n                trace!(\"{}\", line);\n            }\n        }\n\n        MDBook::load_with_config(book_root, config)\n    }\n\n    /// Load a book from its root directory using a custom `Config`.\n    pub fn load_with_config<P: Into<PathBuf>>(book_root: P, config: Config) -> Result<MDBook> {\n        let root = book_root.into();\n\n        let src_dir = root.join(&config.book.src);\n        let book = load_book(src_dir, &config.build)?;\n\n        let renderers = determine_renderers(&config)?;\n        let preprocessors = determine_preprocessors(&config, &root)?;\n\n        Ok(MDBook {\n            root,\n            config,\n            book,\n            renderers,\n            preprocessors,\n        })\n    }\n\n    /// Load a book from its root directory using a custom `Config` and a custom summary.\n    pub fn load_with_config_and_summary<P: Into<PathBuf>>(\n        book_root: P,\n        config: Config,\n        summary: Summary,\n    ) -> Result<MDBook> {\n        let root = book_root.into();\n\n        let src_dir = root.join(&config.book.src);\n        let book = load_book_from_disk(&summary, src_dir)?;\n\n        let renderers = determine_renderers(&config)?;\n        let preprocessors = determine_preprocessors(&config, &root)?;\n\n        Ok(MDBook {\n            root,\n            config,\n            book,\n            renderers,\n            preprocessors,\n        })\n    }\n\n    /// Returns a flat depth-first iterator over the [`BookItem`]s of the book.\n    ///\n    /// ```no_run\n    /// # use mdbook_driver::MDBook;\n    /// # use mdbook_driver::book::BookItem;\n    /// # let book = MDBook::load(\"mybook\").unwrap();\n    /// for item in book.iter() {\n    ///     match *item {\n    ///         BookItem::Chapter(ref chapter) => {},\n    ///         BookItem::Separator => {},\n    ///         BookItem::PartTitle(ref title) => {}\n    ///         _ => {}\n    ///     }\n    /// }\n    ///\n    /// // would print something like this:\n    /// // 1. Chapter 1\n    /// // 1.1 Sub Chapter\n    /// // 1.2 Sub Chapter\n    /// // 2. Chapter 2\n    /// //\n    /// // etc.\n    /// ```\n    pub fn iter(&self) -> BookItems<'_> {\n        self.book.iter()\n    }\n\n    /// `init()` gives you a `BookBuilder` which you can use to setup a new book\n    /// and its accompanying directory structure.\n    ///\n    /// The `BookBuilder` creates some boilerplate files and directories to get\n    /// you started with your book.\n    ///\n    /// ```text\n    /// book-test/\n    /// ├── book\n    /// └── src\n    ///     ├── chapter_1.md\n    ///     └── SUMMARY.md\n    /// ```\n    ///\n    /// It uses the path provided as the root directory for your book, then adds\n    /// in a `src/` directory containing a `SUMMARY.md` and `chapter_1.md` file\n    /// to get you started.\n    pub fn init<P: Into<PathBuf>>(book_root: P) -> BookBuilder {\n        BookBuilder::new(book_root)\n    }\n\n    /// Tells the renderer to build our book and put it in the build directory.\n    pub fn build(&self) -> Result<()> {\n        info!(\"Book building has started\");\n\n        for renderer in self.renderers.values() {\n            self.execute_build_process(&**renderer)?;\n        }\n\n        Ok(())\n    }\n\n    /// Run preprocessors and return the final book.\n    pub fn preprocess_book(&self, renderer: &dyn Renderer) -> Result<(Book, PreprocessorContext)> {\n        let preprocess_ctx = PreprocessorContext::new(\n            self.root.clone(),\n            self.config.clone(),\n            renderer.name().to_string(),\n        );\n        let mut preprocessed_book = self.book.clone();\n        for preprocessor in self.preprocessors.values() {\n            if preprocessor_should_run(&**preprocessor, renderer, &self.config)? {\n                debug!(\"Running the {} preprocessor.\", preprocessor.name());\n                preprocessed_book = preprocessor.run(&preprocess_ctx, preprocessed_book)?;\n            }\n        }\n        Ok((preprocessed_book, preprocess_ctx))\n    }\n\n    /// Run the entire build process for a particular [`Renderer`].\n    pub fn execute_build_process(&self, renderer: &dyn Renderer) -> Result<()> {\n        let (preprocessed_book, preprocess_ctx) = self.preprocess_book(renderer)?;\n\n        let name = renderer.name();\n        let build_dir = self.build_dir_for(name);\n\n        let mut render_context = RenderContext::new(\n            self.root.clone(),\n            preprocessed_book,\n            self.config.clone(),\n            build_dir,\n        );\n        render_context\n            .chapter_titles\n            .extend(preprocess_ctx.chapter_titles.borrow_mut().drain());\n\n        info!(\"Running the {} backend\", renderer.name());\n        renderer\n            .render(&render_context)\n            .with_context(|| \"Rendering failed\")\n    }\n\n    /// You can change the default renderer to another one by using this method.\n    /// The only requirement is that your renderer implement the [`Renderer`]\n    /// trait.\n    pub fn with_renderer<R: Renderer + 'static>(&mut self, renderer: R) -> &mut Self {\n        self.renderers\n            .insert(renderer.name().to_string(), Box::new(renderer));\n        self\n    }\n\n    /// Register a [`Preprocessor`] to be used when rendering the book.\n    pub fn with_preprocessor<P: Preprocessor + 'static>(&mut self, preprocessor: P) -> &mut Self {\n        self.preprocessors\n            .insert(preprocessor.name().to_string(), Box::new(preprocessor));\n        self\n    }\n\n    /// Run `rustdoc` tests on the book, linking against the provided libraries.\n    pub fn test(&mut self, library_paths: Vec<&str>) -> Result<()> {\n        // test_chapter with chapter:None will run all tests.\n        self.test_chapter(library_paths, None)\n    }\n\n    /// Run `rustdoc` tests on a specific chapter of the book, linking against the provided libraries.\n    /// If `chapter` is `None`, all tests will be run.\n    pub fn test_chapter(&mut self, library_paths: Vec<&str>, chapter: Option<&str>) -> Result<()> {\n        let cwd = std::env::current_dir()?;\n        let library_args: Vec<OsString> = library_paths\n            .into_iter()\n            .flat_map(|path| {\n                let path = Path::new(path);\n                let path = if path.is_relative() {\n                    cwd.join(path).into_os_string()\n                } else {\n                    path.to_path_buf().into_os_string()\n                };\n                [OsString::from(\"-L\"), path]\n            })\n            .collect();\n\n        let temp_dir = TempFileBuilder::new().prefix(\"mdbook-\").tempdir()?;\n\n        let mut chapter_found = false;\n\n        struct TestRenderer;\n        impl Renderer for TestRenderer {\n            // FIXME: Is \"test\" the proper renderer name to use here?\n            fn name(&self) -> &str {\n                \"test\"\n            }\n\n            fn render(&self, _: &RenderContext) -> Result<()> {\n                Ok(())\n            }\n        }\n\n        let (book, _) = self.preprocess_book(&TestRenderer)?;\n\n        let color_output = std::io::stderr().is_terminal();\n        let mut failed = false;\n        for item in book.iter() {\n            if let BookItem::Chapter(ref ch) = *item {\n                let chapter_path = match ch.path {\n                    Some(ref path) if !path.as_os_str().is_empty() => path,\n                    _ => continue,\n                };\n\n                if let Some(chapter) = chapter {\n                    if ch.name != chapter && chapter_path.to_str() != Some(chapter) {\n                        if chapter == \"?\" {\n                            info!(\"Skipping chapter '{}'...\", ch.name);\n                        }\n                        continue;\n                    }\n                }\n                chapter_found = true;\n                info!(\"Testing chapter '{}': {:?}\", ch.name, chapter_path);\n\n                // write preprocessed file to tempdir\n                let path = temp_dir.path().join(chapter_path);\n                fs::write(&path, &ch.content)?;\n\n                let mut cmd = Command::new(\"rustdoc\");\n                cmd.current_dir(temp_dir.path())\n                    .arg(chapter_path)\n                    .arg(\"--test\")\n                    .args(&library_args);\n\n                if let Some(edition) = self.config.rust.edition {\n                    match edition {\n                        RustEdition::E2015 => {\n                            cmd.args([\"--edition\", \"2015\"]);\n                        }\n                        RustEdition::E2018 => {\n                            cmd.args([\"--edition\", \"2018\"]);\n                        }\n                        RustEdition::E2021 => {\n                            cmd.args([\"--edition\", \"2021\"]);\n                        }\n                        RustEdition::E2024 => {\n                            cmd.args([\"--edition\", \"2024\"]);\n                        }\n                        _ => panic!(\"RustEdition {edition:?} not covered\"),\n                    }\n                }\n\n                if color_output {\n                    cmd.args([\"--color\", \"always\"]);\n                }\n\n                debug!(\"running {:?}\", cmd);\n                let output = cmd\n                    .output()\n                    .with_context(|| \"failed to execute `rustdoc`\")?;\n\n                if !output.status.success() {\n                    failed = true;\n                    eprintln!(\n                        \"ERROR rustdoc returned an error:\\n\\\n                        \\n--- stdout\\n{}\\n--- stderr\\n{}\",\n                        String::from_utf8_lossy(&output.stdout),\n                        String::from_utf8_lossy(&output.stderr)\n                    );\n                }\n            }\n        }\n        if failed {\n            bail!(\"One or more tests failed\");\n        }\n        if let Some(chapter) = chapter {\n            if !chapter_found {\n                bail!(\"Chapter not found: {}\", chapter);\n            }\n        }\n        Ok(())\n    }\n\n    /// The logic for determining where a backend should put its build\n    /// artefacts.\n    ///\n    /// If there is only 1 renderer, put it in the directory pointed to by the\n    /// `build.build_dir` key in [`Config`]. If there is more than one then the\n    /// renderer gets its own directory within the main build dir.\n    ///\n    /// i.e. If there were only one renderer (in this case, the HTML renderer):\n    ///\n    /// - build/\n    ///   - index.html\n    ///   - ...\n    ///\n    /// Otherwise if there are multiple:\n    ///\n    /// - build/\n    ///   - epub/\n    ///     - my_awesome_book.epub\n    ///   - html/\n    ///     - index.html\n    ///     - ...\n    ///   - latex/\n    ///     - my_awesome_book.tex\n    ///\n    pub fn build_dir_for(&self, backend_name: &str) -> PathBuf {\n        let build_dir = self.root.join(&self.config.build.build_dir);\n\n        if self.renderers.len() <= 1 {\n            build_dir\n        } else {\n            build_dir.join(backend_name)\n        }\n    }\n\n    /// Get the directory containing this book's source files.\n    pub fn source_dir(&self) -> PathBuf {\n        self.root.join(&self.config.book.src)\n    }\n\n    /// Get the directory containing the theme resources for the book.\n    pub fn theme_dir(&self) -> PathBuf {\n        self.config\n            .html_config()\n            .unwrap_or_default()\n            .theme_dir(&self.root)\n    }\n}\n\n/// An `output` table.\n#[derive(Deserialize)]\nstruct OutputConfig {\n    command: Option<String>,\n}\n\n/// Look at the `Config` and try to figure out what renderers to use.\nfn determine_renderers(config: &Config) -> Result<IndexMap<String, Box<dyn Renderer>>> {\n    let mut renderers = IndexMap::new();\n\n    let outputs = config.outputs::<OutputConfig>()?;\n    renderers.extend(outputs.into_iter().map(|(key, table)| {\n        let renderer = if key == \"html\" {\n            Box::new(HtmlHandlebars::new()) as Box<dyn Renderer>\n        } else if key == \"markdown\" {\n            Box::new(MarkdownRenderer::new()) as Box<dyn Renderer>\n        } else {\n            let command = table.command.unwrap_or_else(|| format!(\"mdbook-{key}\"));\n            Box::new(CmdRenderer::new(key.clone(), command))\n        };\n        (key, renderer)\n    }));\n\n    // if we couldn't find anything, add the HTML renderer as a default\n    if renderers.is_empty() {\n        renderers.insert(\"html\".to_string(), Box::new(HtmlHandlebars::new()));\n    }\n\n    Ok(renderers)\n}\n\nconst DEFAULT_PREPROCESSORS: &[&str] = &[\"links\", \"index\"];\n\nfn is_default_preprocessor(pre: &dyn Preprocessor) -> bool {\n    let name = pre.name();\n    name == LinkPreprocessor::NAME || name == IndexPreprocessor::NAME\n}\n\n/// A `preprocessor` table.\n#[derive(Deserialize)]\nstruct PreprocessorConfig {\n    command: Option<String>,\n    #[serde(default)]\n    before: Vec<String>,\n    #[serde(default)]\n    after: Vec<String>,\n    #[serde(default)]\n    optional: bool,\n}\n\n/// Look at the `MDBook` and try to figure out what preprocessors to run.\nfn determine_preprocessors(\n    config: &Config,\n    root: &Path,\n) -> Result<IndexMap<String, Box<dyn Preprocessor>>> {\n    // Collect the names of all preprocessors intended to be run, and the order\n    // in which they should be run.\n    let mut preprocessor_names = TopologicalSort::<String>::new();\n\n    if config.build.use_default_preprocessors {\n        for name in DEFAULT_PREPROCESSORS {\n            preprocessor_names.insert(name.to_string());\n        }\n    }\n\n    let preprocessor_table = config.preprocessors::<PreprocessorConfig>()?;\n\n    for (name, table) in preprocessor_table.iter() {\n        preprocessor_names.insert(name.to_string());\n\n        let exists = |name| {\n            (config.build.use_default_preprocessors && DEFAULT_PREPROCESSORS.contains(&name))\n                || preprocessor_table.contains_key(name)\n        };\n\n        for after in &table.before {\n            if !exists(&after) {\n                // Only warn so that preprocessors can be toggled on and off (e.g. for\n                // troubleshooting) without having to worry about order too much.\n                warn!(\n                    \"preprocessor.{}.after contains \\\"{}\\\", which was not found\",\n                    name, after\n                );\n            } else {\n                preprocessor_names.add_dependency(name, after);\n            }\n        }\n\n        for before in &table.after {\n            if !exists(&before) {\n                // See equivalent warning above for rationale\n                warn!(\n                    \"preprocessor.{}.before contains \\\"{}\\\", which was not found\",\n                    name, before\n                );\n            } else {\n                preprocessor_names.add_dependency(before, name);\n            }\n        }\n    }\n\n    // Now that all links have been established, queue preprocessors in a suitable order\n    let mut preprocessors = IndexMap::with_capacity(preprocessor_names.len());\n    // `pop_all()` returns an empty vector when no more items are not being depended upon\n    for mut names in std::iter::repeat_with(|| preprocessor_names.pop_all())\n        .take_while(|names| !names.is_empty())\n    {\n        // The `topological_sort` crate does not guarantee a stable order for ties, even across\n        // runs of the same program. Thus, we break ties manually by sorting.\n        // Careful: `str`'s default sorting, which we are implicitly invoking here, uses code point\n        // values ([1]), which may not be an alphabetical sort.\n        // As mentioned in [1], doing so depends on locale, which is not desirable for deciding\n        // preprocessor execution order.\n        // [1]: https://doc.rust-lang.org/stable/std/cmp/trait.Ord.html#impl-Ord-14\n        names.sort();\n        for name in names {\n            let preprocessor: Box<dyn Preprocessor> = match name.as_str() {\n                \"links\" => Box::new(LinkPreprocessor::new()),\n                \"index\" => Box::new(IndexPreprocessor::new()),\n                _ => {\n                    // The only way to request a custom preprocessor is through the `preprocessor`\n                    // table, so it must exist, be a table, and contain the key.\n                    let table = &preprocessor_table[&name];\n                    let command = table\n                        .command\n                        .to_owned()\n                        .unwrap_or_else(|| format!(\"mdbook-{name}\"));\n                    Box::new(CmdPreprocessor::new(\n                        name.clone(),\n                        command,\n                        root.to_owned(),\n                        table.optional,\n                    ))\n                }\n            };\n            preprocessors.insert(name, preprocessor);\n        }\n    }\n\n    // \"If `pop_all` returns an empty vector and `len` is not 0, there are cyclic dependencies.\"\n    // Normally, `len() == 0` is equivalent to `is_empty()`, so we'll use that.\n    if preprocessor_names.is_empty() {\n        Ok(preprocessors)\n    } else {\n        Err(Error::msg(\"Cyclic dependency detected in preprocessors\"))\n    }\n}\n\n/// Check whether we should run a particular `Preprocessor` in combination\n/// with the renderer, falling back to `Preprocessor::supports_renderer()`\n/// method if the user doesn't say anything.\n///\n/// The `build.use-default-preprocessors` config option can be used to ensure\n/// default preprocessors always run if they support the renderer.\nfn preprocessor_should_run(\n    preprocessor: &dyn Preprocessor,\n    renderer: &dyn Renderer,\n    cfg: &Config,\n) -> Result<bool> {\n    // default preprocessors should be run by default (if supported)\n    if cfg.build.use_default_preprocessors && is_default_preprocessor(preprocessor) {\n        return preprocessor.supports_renderer(renderer.name());\n    }\n\n    let key = format!(\"preprocessor.{}.renderers\", preprocessor.name());\n    let renderer_name = renderer.name();\n\n    match cfg.get::<Vec<String>>(&key) {\n        Ok(Some(explicit_renderers)) => {\n            Ok(explicit_renderers.iter().any(|name| name == renderer_name))\n        }\n        Ok(None) => preprocessor.supports_renderer(renderer_name),\n        Err(e) => bail!(\"failed to get `{key}`: {e}\"),\n    }\n}\n"
  },
  {
    "path": "crates/mdbook-html/Cargo.toml",
    "content": "[package]\nname = \"mdbook-html\"\nversion = \"0.5.2\"\ndescription = \"mdBook HTML renderer\"\nedition.workspace = true\nlicense.workspace = true\nrepository.workspace = true\nrust-version.workspace = true\n\n[dependencies]\nanyhow.workspace = true\nego-tree.workspace = true\nelasticlunr-rs = { workspace = true, optional = true }\nfont-awesome-as-a-crate.workspace = true\nhandlebars.workspace = true\nhex.workspace = true\nhtml5ever.workspace = true\nindexmap.workspace = true\nmdbook-core.workspace = true\nmdbook-markdown.workspace = true\nmdbook-renderer.workspace = true\npulldown-cmark.workspace = true\nregex.workspace = true\nserde.workspace = true\nserde_json.workspace = true\nsha2.workspace = true\ntracing.workspace = true\n\n[dev-dependencies]\ntempfile.workspace = true\ntoml.workspace = true\n\n[lints]\nworkspace = true\n\n[features]\nsearch = [\"dep:elasticlunr-rs\"]\n"
  },
  {
    "path": "crates/mdbook-html/README.md",
    "content": "# mdbook-html\n\n[![Documentation](https://img.shields.io/docsrs/mdbook-html)](https://docs.rs/mdbook-html)\n[![crates.io](https://img.shields.io/crates/v/mdbook-html.svg)](https://crates.io/crates/mdbook-html)\n[![Changelog](https://img.shields.io/badge/CHANGELOG-Latest-green)](https://github.com/rust-lang/mdBook/blob/master/CHANGELOG.md)\n\nThis is the HTML renderer for [mdBook](https://rust-lang.github.io/mdBook/). This is intended for internal use only. It is automatically included by [`mdbook-driver`](https://crates.io/crates/mdbook-driver) to render books to HTML.\n\n> This crate is maintained by the mdBook team, primarily for use by mdBook and not intended for external use (except as a transitive dependency). This crate may make major changes to its APIs or be deprecated without warning.\n\n## License\n\n[Mozilla Public License, version 2.0](https://github.com/rust-lang/mdBook/blob/master/LICENSE)\n"
  },
  {
    "path": "crates/mdbook-html/front-end/css/ayu-highlight.css",
    "content": "/*\nBased off of the Ayu theme\nOriginal by Dempfi (https://github.com/dempfi/ayu)\n*/\n\n.hljs {\n  display: block;\n  overflow-x: auto;\n  background: #191f26;\n  color: #e6e1cf;\n}\n\n.hljs-comment,\n.hljs-quote {\n  color: #5c6773;\n}\n\n.hljs-variable,\n.hljs-template-variable,\n.hljs-attribute,\n.hljs-attr,\n.hljs-regexp,\n.hljs-link,\n.hljs-selector-id,\n.hljs-selector-class {\n  color: #ff7733;\n}\n\n.hljs-number,\n.hljs-meta,\n.hljs-builtin-name,\n.hljs-literal,\n.hljs-type,\n.hljs-params {\n  color: #ffee99;\n}\n\n.hljs-string,\n.hljs-bullet {\n  color: #b8cc52;\n}\n\n.hljs-title,\n.hljs-built_in,\n.hljs-section {\n  color: #ffb454;\n}\n\n.hljs-keyword,\n.hljs-selector-tag,\n.hljs-symbol {\n  color: #ff7733;\n}\n\n.hljs-name {\n    color: #36a3d9;\n}\n\n.hljs-tag {\n    color: #00568d;\n}\n\n.hljs-emphasis {\n  font-style: italic;\n}\n\n.hljs-strong {\n  font-weight: bold;\n}\n\n.hljs-addition {\n  color: #91b362;\n}\n\n.hljs-deletion {\n  color: #d96c75;\n}\n"
  },
  {
    "path": "crates/mdbook-html/front-end/css/chrome.css",
    "content": "/* CSS for UI elements (a.k.a. chrome) */\n\nhtml {\n    scrollbar-color: var(--scrollbar) transparent;\n}\n#mdbook-searchresults a,\n.content a:link,\na:visited,\na > .hljs {\n    color: var(--links);\n}\n\n/*\n    mdbook-body-container is necessary because mobile browsers don't seem to like\n    overflow-x on the body tag when there is a <meta name=\"viewport\"> tag.\n*/\n#mdbook-body-container {\n    /*\n        This is used when the sidebar pushes the body content off the side of\n        the screen on small screens. Without it, dragging on mobile Safari\n        will want to reposition the viewport in a weird way.\n    */\n    overflow-x: clip;\n}\n\n/* Menu Bar */\n\n#mdbook-menu-bar,\n#mdbook-menu-bar-hover-placeholder {\n    z-index: 101;\n    margin: auto calc(0px - var(--page-padding));\n}\n#mdbook-menu-bar {\n    position: relative;\n    display: flex;\n    flex-wrap: wrap;\n    background-color: var(--bg);\n    border-block-end-color: var(--bg);\n    border-block-end-width: 1px;\n    border-block-end-style: solid;\n}\n#mdbook-menu-bar.sticky,\n#mdbook-menu-bar-hover-placeholder:hover + #mdbook-menu-bar,\n#mdbook-menu-bar:hover,\nhtml.sidebar-visible #mdbook-menu-bar {\n    position: -webkit-sticky;\n    position: sticky;\n    top: 0 !important;\n}\n#mdbook-menu-bar-hover-placeholder {\n    position: sticky;\n    position: -webkit-sticky;\n    top: 0;\n    height: var(--menu-bar-height);\n}\n#mdbook-menu-bar.bordered {\n    border-block-end-color: var(--table-border-color);\n}\n#mdbook-menu-bar .fa-svg, #mdbook-menu-bar .icon-button {\n    position: relative;\n    padding: 0 8px;\n    z-index: 10;\n    line-height: var(--menu-bar-height);\n    cursor: pointer;\n    transition: color 0.5s;\n}\n@media only screen and (max-width: 420px) {\n    #mdbook-menu-bar .fa-svg, #mdbook-menu-bar .icon-button {\n        padding: 0 5px;\n    }\n}\n\n.icon-button {\n    border: none;\n    background: none;\n    padding: 0;\n    color: inherit;\n}\n.icon-button .fa-svg {\n    margin: 0;\n}\n\n.right-buttons {\n    margin: 0 15px;\n}\n.right-buttons a {\n    text-decoration: none;\n}\n\n.left-buttons {\n    display: flex;\n    margin: 0 5px;\n}\nhtml:not(.js) .left-buttons button {\n    display: none;\n}\n\n.menu-title {\n    display: inline-block;\n    font-weight: 200;\n    font-size: 2.4rem;\n    line-height: var(--menu-bar-height);\n    text-align: center;\n    margin: 0;\n    flex: 1;\n    white-space: nowrap;\n    overflow: hidden;\n    text-overflow: ellipsis;\n}\n.menu-title {\n    cursor: pointer;\n}\n\n.menu-bar,\n.menu-bar:visited,\n.nav-chapters,\n.nav-chapters:visited,\n.mobile-nav-chapters,\n.mobile-nav-chapters:visited,\n.menu-bar .icon-button,\n.menu-bar a .fa-svg {\n    color: var(--icons);\n}\n\n.menu-bar .fa-svg:hover,\n.menu-bar .icon-button:hover,\n.nav-chapters:hover,\n.mobile-nav-chapters .fa-svg:hover {\n    color: var(--icons-hover);\n}\n\n/* Nav Icons */\n\n.nav-chapters {\n    font-size: 2.5em;\n    text-align: center;\n    text-decoration: none;\n\n    position: fixed;\n    top: 0;\n    bottom: 0;\n    margin: 0;\n    max-width: 150px;\n    min-width: 90px;\n\n    display: flex;\n    justify-content: center;\n    align-content: center;\n    flex-direction: column;\n\n    transition: color 0.5s, background-color 0.5s;\n}\n\n.nav-chapters:hover {\n    text-decoration: none;\n    background-color: var(--theme-hover);\n    transition: background-color 0.15s, color 0.15s;\n}\n\n.nav-wrapper {\n    margin-block-start: 50px;\n    display: none;\n}\n\n.mobile-nav-chapters {\n    font-size: 2.5em;\n    text-align: center;\n    text-decoration: none;\n    width: 90px;\n    border-radius: 5px;\n    background-color: var(--sidebar-bg);\n}\n\n/* Only Firefox supports flow-relative values */\n.previous { float: left; }\n[dir=rtl] .previous { float: right; }\n\n/* Only Firefox supports flow-relative values */\n.next {\n    float: right;\n    right: var(--page-padding);\n}\n[dir=rtl] .next {\n    float: left;\n    right: unset;\n    left: var(--page-padding);\n}\n\n@media only screen and (max-width: 1080px) {\n    .nav-wide-wrapper { display: none; }\n    .nav-wrapper { display: block; }\n}\n\n/* sidebar-visible */\n@media only screen and (max-width: 1380px) {\n    #mdbook-sidebar-toggle-anchor:checked ~ .page-wrapper .nav-wide-wrapper { display: none; }\n    #mdbook-sidebar-toggle-anchor:checked ~ .page-wrapper .nav-wrapper { display: block; }\n}\n\n/* Inline code */\n\n:not(pre) > .hljs {\n    display: inline;\n    padding: 0.1em 0.3em;\n    border-radius: 3px;\n}\n\n:not(pre):not(a) > .hljs {\n    color: var(--inline-code-color);\n    overflow-x: initial;\n}\n\na:hover > .hljs {\n    text-decoration: underline;\n}\n\npre {\n    position: relative;\n}\npre > .buttons {\n    position: absolute;\n    z-index: 100;\n    right: 0px;\n    top: 2px;\n    margin: 0px;\n    padding: 2px 0px;\n\n    color: var(--sidebar-fg);\n    cursor: pointer;\n    visibility: hidden;\n    opacity: 0;\n    transition: visibility 0.1s linear, opacity 0.1s linear;\n}\npre:hover > .buttons {\n    visibility: visible;\n    opacity: 1\n}\npre > .buttons :hover {\n    color: var(--sidebar-active);\n    border-color: var(--icons-hover);\n    background-color: var(--theme-hover);\n}\npre > .buttons button {\n    cursor: inherit;\n    margin: 0px 5px;\n    padding: 2px 3px 0px 4px;\n    font-size: 23px;\n\n    border-style: solid;\n    border-width: 1px;\n    border-radius: 4px;\n    border-color: var(--icons);\n    background-color: var(--theme-popup-bg);\n    transition: 100ms;\n    transition-property: color,border-color,background-color;\n    color: var(--icons);\n}\n\npre > .buttons button.clip-button {\n    padding: 2px 4px 0px 6px;\n}\npre > .buttons button.clip-button::before {\n    /* clipboard image from octicons (https://github.com/primer/octicons/tree/v2.0.0) MIT license\n     */\n    content: url('data:image/svg+xml,<svg width=\"21\" height=\"20\" viewBox=\"0 0 24 25\" \\\nxmlns=\"http://www.w3.org/2000/svg\" aria-label=\"Copy to clipboard\">\\\n<path d=\"M18 20h2v3c0 1-1 2-2 2H2c-.998 0-2-1-2-2V5c0-.911.755-1.667 1.667-1.667h5A3.323 3.323 0 \\\n0110 0a3.323 3.323 0 013.333 3.333h5C19.245 3.333 20 4.09 20 5v8.333h-2V9H2v14h16v-3zM3 \\\n7h14c0-.911-.793-1.667-1.75-1.667H13.5c-.957 0-1.75-.755-1.75-1.666C11.75 2.755 10.957 2 10 \\\n2s-1.75.755-1.75 1.667c0 .911-.793 1.666-1.75 1.666H4.75C3.793 5.333 3 6.09 3 7z\"/>\\\n<path d=\"M4 19h6v2H4zM12 11H4v2h8zM4 17h4v-2H4zM15 15v-3l-4.5 4.5L15 21v-3l8.027-.032L23 15z\"/>\\\n</svg>');\n    filter: var(--copy-button-filter);\n}\npre > .buttons button.clip-button:hover::before {\n    filter: var(--copy-button-filter-hover);\n}\n\n@media (pointer: coarse) {\n    pre > .buttons button {\n        /* On mobile, make it easier to tap buttons. */\n        padding: 0.3rem 1rem;\n    }\n\n    .sidebar-resize-indicator {\n        /* Hide resize indicator on devices with limited accuracy */\n        display: none;\n    }\n}\npre > code {\n    display: block;\n    padding: 1rem;\n}\n\n/* FIXME: ACE editors overlap their buttons because ACE does absolute\n   positioning within the code block which breaks padding. The only solution I\n   can think of is to move the padding to the outer pre tag (or insert a div\n   wrapper), but that would require fixing a whole bunch of CSS rules.\n*/\n.hljs.ace_editor {\n  padding: 0rem 0rem;\n}\n\npre > .result {\n    margin-block-start: 10px;\n}\n\n/* Search */\n\n#mdbook-searchresults a {\n    text-decoration: none;\n}\n\nmark {\n    border-radius: 2px;\n    padding-block-start: 0;\n    padding-block-end: 1px;\n    padding-inline-start: 3px;\n    padding-inline-end: 3px;\n    margin-block-start: 0;\n    margin-block-end: -1px;\n    margin-inline-start: -3px;\n    margin-inline-end: -3px;\n    background-color: var(--search-mark-bg);\n    transition: background-color 300ms linear;\n    cursor: pointer;\n}\n\nmark.fade-out {\n    background-color: rgba(0,0,0,0) !important;\n    cursor: auto;\n}\n\n.searchbar-outer {\n    margin-inline-start: auto;\n    margin-inline-end: auto;\n    max-width: var(--content-max-width);\n}\n\n#mdbook-searchbar-outer.searching #mdbook-searchbar {\n    padding-right: 30px;\n}\n#mdbook-searchbar-outer .spinner-wrapper {\n    display: none;\n}\n#mdbook-searchbar-outer.searching .spinner-wrapper {\n    display: block;\n}\n\n.search-wrapper {\n    position: relative;\n}\n\n.spinner-wrapper {\n    --spinner-margin: 2px;\n    position: absolute;\n    margin-block-start: calc(var(--searchbar-margin-block-start) + var(--spinner-margin));\n    right: var(--spinner-margin);\n    top: 0;\n    bottom: var(--spinner-margin);\n    padding: 6px;\n    background-color: var(--bg);\n}\n\n#fa-spin {\n    animation: rotating 2s linear infinite;\n    display: inline-block;\n}\n\n@keyframes rotating {\n    from {\n        transform: rotate(0deg);\n    }\n    to {\n        transform: rotate(360deg);\n    }\n}\n\n#mdbook-searchbar {\n    width: 100%;\n    margin-block-start: var(--searchbar-margin-block-start);\n    margin-block-end: 0;\n    margin-inline-start: auto;\n    margin-inline-end: auto;\n    padding: 10px 16px;\n    transition: box-shadow 300ms ease-in-out;\n    border: 1px solid var(--searchbar-border-color);\n    border-radius: 3px;\n    background-color: var(--searchbar-bg);\n    color: var(--searchbar-fg);\n}\n#mdbook-searchbar:focus,\n#mdbook-searchbar.active {\n    box-shadow: 0 0 3px var(--searchbar-shadow-color);\n}\n\n.searchresults-header {\n    font-weight: bold;\n    font-size: 1em;\n    padding-block-start: 18px;\n    padding-block-end: 0;\n    padding-inline-start: 5px;\n    padding-inline-end: 0;\n    color: var(--searchresults-header-fg);\n}\n\n.searchresults-outer {\n    margin-inline-start: auto;\n    margin-inline-end: auto;\n    max-width: var(--content-max-width);\n    border-block-end: 1px dashed var(--searchresults-border-color);\n}\n\nul#mdbook-searchresults {\n    list-style: none;\n    padding-inline-start: 20px;\n}\nul#mdbook-searchresults li {\n    margin: 10px 0px;\n    padding: 2px;\n    border-radius: 2px;\n}\nul#mdbook-searchresults li.focus {\n    background-color: var(--searchresults-li-bg);\n}\nul#mdbook-searchresults span.teaser {\n    display: block;\n    clear: both;\n    margin-block-start: 5px;\n    margin-block-end: 0;\n    margin-inline-start: 20px;\n    margin-inline-end: 0;\n    font-size: 0.8em;\n}\nul#mdbook-searchresults span.teaser em {\n    font-weight: bold;\n    font-style: normal;\n}\n\n/* Sidebar */\n\n.sidebar {\n    position: fixed;\n    left: 0;\n    top: 0;\n    bottom: 0;\n    width: var(--sidebar-width);\n    font-size: 0.875em;\n    box-sizing: border-box;\n    -webkit-overflow-scrolling: touch;\n    overscroll-behavior-y: contain;\n    background-color: var(--sidebar-bg);\n    color: var(--sidebar-fg);\n}\n.sidebar-iframe-inner {\n    --padding: 10px;\n\n    background-color: var(--sidebar-bg);\n    padding: var(--padding);\n    margin: 0;\n    font-size: 1.4rem;\n    color: var(--sidebar-fg);\n    min-height: calc(100vh - var(--padding) * 2);\n}\n.sidebar-iframe-outer {\n    border: none;\n    height: 100%;\n    position: absolute;\n    top: 0;\n    bottom: 0;\n    left: 0;\n    right: 0;\n}\n[dir=rtl] .sidebar { left: unset; right: 0; }\n.sidebar-resizing {\n    -moz-user-select: none;\n    -webkit-user-select: none;\n    -ms-user-select: none;\n    user-select: none;\n}\nhtml:not(.sidebar-resizing) .sidebar {\n    transition: transform 0.3s; /* Animation: slide away */\n}\n.sidebar code {\n    line-height: 2em;\n}\n.sidebar .sidebar-scrollbox {\n    overflow-y: auto;\n    position: absolute;\n    top: 0;\n    bottom: 0;\n    left: 0;\n    right: 0;\n    padding: 10px 10px;\n}\n.sidebar .sidebar-resize-handle {\n    position: absolute;\n    cursor: col-resize;\n    width: 0;\n    right: calc(var(--sidebar-resize-indicator-width) * -1);\n    top: 0;\n    bottom: 0;\n    display: flex;\n    align-items: center;\n}\n\n.sidebar-resize-handle .sidebar-resize-indicator {\n    width: 100%;\n    height: 16px;\n    color: var(--icons);\n    margin-inline-start: var(--sidebar-resize-indicator-space);\n    display: flex;\n    align-items: center;\n    justify-content: flex-start;\n}\n.sidebar-resize-handle .sidebar-resize-indicator::before {\n    content: \"\";\n    width: 2px;\n    height: 12px;\n    border-left: dotted 2px currentColor;\n}\n.sidebar-resize-handle .sidebar-resize-indicator::after {\n    content: \"\";\n    width: 2px;\n    height: 16px;\n    border-left: dotted 2px currentColor;\n}\n\n[dir=rtl] .sidebar .sidebar-resize-handle {\n    left: calc(var(--sidebar-resize-indicator-width) * -1);\n    right: unset;\n}\n.js .sidebar .sidebar-resize-handle {\n    cursor: col-resize;\n    width: calc(var(--sidebar-resize-indicator-width) - var(--sidebar-resize-indicator-space));\n}\n\nhtml:not(.js) .sidebar-resize-handle {\n    display: none;\n}\n\n/* sidebar-hidden */\n#mdbook-sidebar-toggle-anchor:not(:checked) ~ .sidebar {\n    transform: translateX(calc(0px - var(--sidebar-width) - var(--sidebar-resize-indicator-width)));\n}\n[dir=rtl] #mdbook-sidebar-toggle-anchor:not(:checked) ~ .sidebar {\n    transform: translateX(calc(var(--sidebar-width) + var(--sidebar-resize-indicator-width)));\n}\n.sidebar::-webkit-scrollbar {\n    background: var(--sidebar-bg);\n}\n.sidebar::-webkit-scrollbar-thumb {\n    background: var(--scrollbar);\n}\n\n/* sidebar-visible */\n#mdbook-sidebar-toggle-anchor:checked ~ .page-wrapper {\n    transform: translateX(calc(var(--sidebar-width) + var(--sidebar-resize-indicator-width)));\n}\n[dir=rtl] #mdbook-sidebar-toggle-anchor:checked ~ .page-wrapper {\n    transform: translateX(calc(0px - var(--sidebar-width) - var(--sidebar-resize-indicator-width)));\n}\n@media only screen and (min-width: 620px) {\n    #mdbook-sidebar-toggle-anchor:checked ~ .page-wrapper {\n        transform: none;\n        margin-inline-start: calc(var(--sidebar-width) + var(--sidebar-resize-indicator-width));\n    }\n    [dir=rtl] #mdbook-sidebar-toggle-anchor:checked ~ .page-wrapper {\n        transform: none;\n    }\n}\n\n.chapter {\n    list-style: none outside none;\n    padding-inline-start: 0;\n    line-height: 2.2em;\n}\n\n.chapter li {\n    color: var(--sidebar-non-existant);\n}\n\n/* This is a span wrapping the chapter link and the fold chevron. */\n.chapter-link-wrapper {\n    /* Used to position the chevron to the right, allowing the text to wrap before it. */\n    display: flex;\n}\n\n.chapter li a {\n    /* Remove underlines. */\n    text-decoration: none;\n    color: var(--sidebar-fg);\n}\n\n.chapter li a:hover {\n    color: var(--sidebar-active);\n}\n\n.chapter li a.active {\n    color: var(--sidebar-active);\n}\n\n/* This is the toggle chevron. */\n.chapter-fold-toggle {\n    cursor: pointer;\n    /* Positions the chevron to the side. */\n    margin-inline-start: auto;\n    padding: 0 10px;\n    user-select: none;\n    opacity: 0.68;\n}\n\n.chapter-fold-toggle div {\n    transition: transform 0.5s;\n}\n\n/* collapse the section */\n.chapter li:not(.expanded) > ol {\n    display: none;\n}\n\n.chapter li.chapter-item {\n    line-height: 1.5em;\n    margin-block-start: 0.6em;\n}\n\n/* When expanded, rotate the chevron to point down. */\n.chapter li.expanded > span > .chapter-fold-toggle div {\n    transform: rotate(90deg);\n}\n\n.chapter a.current-header {\n    color: var(--sidebar-active);\n}\n\n.on-this-page {\n    margin-left: 22px;\n    border-inline-start: 4px solid var(--sidebar-header-border-color);\n    padding-left: 8px;\n}\n\n.on-this-page > ol {\n    padding-left: 0;\n}\n\n/* Horizontal line in chapter list. */\n.spacer {\n    width: 100%;\n    height: 3px;\n    margin: 5px 0px;\n}\n.chapter .spacer {\n    background-color: var(--sidebar-spacer);\n}\n\n/* On touch devices, add more vertical spacing to make it easier to tap links. */\n@media (-moz-touch-enabled: 1), (pointer: coarse) {\n    .chapter li a { padding: 5px 0; }\n    .spacer { margin: 10px 0; }\n}\n\n.section {\n    list-style: none outside none;\n    padding-inline-start: 20px;\n    line-height: 1.9em;\n}\n\n/* Theme Menu Popup */\n\n.theme-popup {\n    position: absolute;\n    left: 10px;\n    top: var(--menu-bar-height);\n    z-index: 1000;\n    border-radius: 4px;\n    font-size: 0.7em;\n    color: var(--fg);\n    background: var(--theme-popup-bg);\n    border: 1px solid var(--theme-popup-border);\n    margin: 0;\n    padding: 0;\n    list-style: none;\n    display: none;\n    /* Don't let the children's background extend past the rounded corners. */\n    overflow: hidden;\n}\n[dir=rtl] .theme-popup { left: unset;  right: 10px; }\n.theme-popup .default {\n    color: var(--icons);\n}\n.theme-popup .theme {\n    width: 100%;\n    border: 0;\n    margin: 0;\n    padding: 2px 20px;\n    line-height: 25px;\n    white-space: nowrap;\n    text-align: start;\n    cursor: pointer;\n    color: inherit;\n    background: inherit;\n    font-size: inherit;\n}\n.theme-popup .theme:hover {\n    background-color: var(--theme-hover);\n}\n\n.theme-selected::before {\n    display: inline-block;\n    content: \"✓\";\n    margin-inline-start: -14px;\n    width: 14px;\n}\n\n/* The container for the help popup that covers the whole window. */\n#mdbook-help-container {\n    /* Position and size for the whole window. */\n    position: fixed;\n    top: 0;\n    left: 0;\n    right: 0;\n    bottom: 0;\n    /* This uses flex layout (which is set in book.js), and centers the popup\n       in the window.*/\n    display: none;\n    align-items: center;\n    justify-content: center;\n    z-index: 1000;\n    /* Dim out the book while the popup is visible. */\n    background: var(--overlay-bg);\n}\n\n/* The popup help box. */\n#mdbook-help-popup {\n    box-shadow: 0 4px 24px rgba(0,0,0,0.15);\n    min-width: 300px;\n    max-width: 500px;\n    width: 100%;\n    box-sizing: border-box;\n    display: flex;\n    flex-direction: column;\n    align-items: center;\n    background-color: var(--bg);\n    color: var(--fg);\n    border-width: 1px;\n    border-color: var(--theme-popup-border);\n    border-style: solid;\n    border-radius: 8px;\n    padding: 10px;\n}\n\n.mdbook-help-title {\n    text-align: center;\n    /* mdbook's margin for h2 is way too large. */\n    margin: 10px;\n}\n"
  },
  {
    "path": "crates/mdbook-html/front-end/css/general.css",
    "content": "/* Base styles and content styles */\n\n:root {\n    /* Browser default font-size is 16px, this way 1 rem = 10px */\n    font-size: 62.5%;\n    color-scheme: var(--color-scheme);\n}\n\nhtml {\n    font-family: \"Open Sans\", sans-serif;\n    color: var(--fg);\n    background-color: var(--bg);\n    text-size-adjust: none;\n    -webkit-text-size-adjust: none;\n}\n\nbody {\n    margin: 0;\n    font-size: 1.6rem;\n    overflow-x: hidden;\n}\n\ncode {\n    font-family: var(--mono-font) !important;\n    font-size: var(--code-font-size);\n    direction: ltr !important;\n}\n\n/* make long words/inline code not x overflow */\nmain {\n    overflow-wrap: break-word;\n}\n\n/* make wide tables scroll if they overflow */\n.table-wrapper {\n    overflow-x: auto;\n}\n\n/* Don't change font size in headers. */\nh1 code, h2 code, h3 code, h4 code, h5 code, h6 code {\n    font-size: unset;\n}\n\n.left { float: left; }\n.right { float: right; }\n.boring { opacity: 0.6; }\n.hide-boring .boring { display: none; }\n.hidden { display: none !important; }\n\nh2, h3 { margin-block-start: 2.5em; }\nh4, h5 { margin-block-start: 2em; }\n\n.header + .header h3,\n.header + .header h4,\n.header + .header h5 {\n    margin-block-start: 1em;\n}\n\nh1:target::before,\nh2:target::before,\nh3:target::before,\nh4:target::before,\nh5:target::before,\nh6:target::before,\ndt:target::before {\n    display: inline-block;\n    content: \"»\";\n    margin-inline-start: -30px;\n    width: 30px;\n}\n\n/* This is broken on Safari as of version 14, but is fixed\n   in Safari Technology Preview 117 which I think will be Safari 14.2.\n   https://bugs.webkit.org/show_bug.cgi?id=218076\n*/\n:target {\n    /* Safari does not support logical properties */\n    scroll-margin-top: calc(var(--menu-bar-height) + 0.5em);\n}\n\n.page {\n    outline: 0;\n    padding: 0 var(--page-padding);\n    margin-block-start: calc(0px - var(--menu-bar-height)); /* Compensate for the #mdbook-menu-bar-hover-placeholder */\n}\n.page-wrapper {\n    box-sizing: border-box;\n    background-color: var(--bg);\n}\nhtml:not(.js) .page-wrapper,\n.js:not(.sidebar-resizing) .page-wrapper {\n    transition: margin-left 0.3s ease, transform 0.3s ease; /* Animation: slide away */\n}\n[dir=rtl]:not(.js) .page-wrapper,\n[dir=rtl].js:not(.sidebar-resizing) .page-wrapper {\n    transition: margin-right 0.3s ease, transform 0.3s ease; /* Animation: slide away */\n}\n\n.content {\n    overflow-y: auto;\n    padding: 0 5px 50px 5px;\n}\n.content main {\n    margin-inline-start: auto;\n    margin-inline-end: auto;\n    max-width: var(--content-max-width);\n}\n.content p { line-height: 1.45em; }\n.content ol { line-height: 1.45em; }\n.content ul { line-height: 1.45em; }\n.content a { text-decoration: none; }\n.content a:hover { text-decoration: underline; }\n.content img, .content video { max-width: 100%; }\n.content .header:link,\n.content .header:visited {\n    color: var(--fg);\n}\n.content .header:link,\n.content .header:visited:hover {\n    text-decoration: none;\n}\n\ntable {\n    margin: 0 auto;\n    border-collapse: collapse;\n}\ntable td {\n    padding: 3px 20px;\n    border: 1px var(--table-border-color) solid;\n}\ntable thead {\n    background: var(--table-header-bg);\n}\ntable thead td {\n    font-weight: 700;\n    border: none;\n}\ntable thead th {\n    padding: 3px 20px;\n}\ntable thead tr {\n    border: 1px var(--table-header-bg) solid;\n}\n/* Alternate background colors for rows */\ntable tbody tr:nth-child(2n) {\n    background: var(--table-alternate-bg);\n}\n\n\nblockquote {\n    margin: 20px 0;\n    padding: 0 20px;\n    color: var(--fg);\n    background-color: var(--quote-bg);\n    border-block-start: .1em solid var(--quote-border);\n    border-block-end: .1em solid var(--quote-border);\n}\n\n/* TODO: Remove .warning in a future version of mdbook, it is replaced by\nblockquote tags. */\n.warning {\n    margin: 20px;\n    padding: 0 20px;\n    border-inline-start: 2px solid var(--warning-border);\n}\n\n.warning:before {\n    position: absolute;\n    width: 3rem;\n    height: 3rem;\n    margin-inline-start: calc(-1.5rem - 21px);\n    content: \"ⓘ\";\n    text-align: center;\n    background-color: var(--bg);\n    color: var(--warning-border);\n    font-weight: bold;\n    font-size: 2rem;\n}\n\nblockquote .warning:before {\n    background-color: var(--quote-bg);\n}\n\nkbd {\n    background-color: var(--table-border-color);\n    border-radius: 4px;\n    border: solid 1px var(--theme-popup-border);\n    box-shadow: inset 0 -1px 0 var(--theme-hover);\n    display: inline-block;\n    font-size: var(--code-font-size);\n    font-family: var(--mono-font);\n    line-height: 10px;\n    padding: 4px 5px;\n    vertical-align: middle;\n}\n\nsup {\n    /* Set the line-height for superscript and footnote references so that there\n       isn't an awkward space appearing above lines that contain the footnote.\n\n       See https://github.com/rust-lang/mdBook/pull/2443#discussion_r1813773583\n       for an explanation.\n    */\n    line-height: 0;\n}\n\n.footnote-definition {\n    font-size: 0.9em;\n}\n/* The default spacing for a list is a little too large. */\n.footnote-definition ul,\n.footnote-definition ol {\n    padding-left: 20px;\n}\n.footnote-definition > li {\n    /* Required to position the ::before target */\n    position: relative;\n}\n.footnote-definition > li:target {\n    scroll-margin-top: 50vh;\n}\n.footnote-reference:target {\n    scroll-margin-top: 50vh;\n}\n/* Draws a border around the footnote (including the marker) when it is selected.\n   TODO: If there are multiple linkbacks, highlight which one you just came\n   from so you know which one to click.\n*/\n.footnote-definition > li:target::before {\n    border: 2px solid var(--footnote-highlight);\n    border-radius: 6px;\n    position: absolute;\n    top: -8px;\n    right: -8px;\n    bottom: -8px;\n    left: -32px;\n    pointer-events: none;\n    content: \"\";\n}\n/* Pulses the footnote reference so you can quickly see where you left off reading.\n   This could use some improvement.\n*/\n@media not (prefers-reduced-motion) {\n    .footnote-reference:target  {\n        animation: fn-highlight 0.8s;\n        border-radius: 2px;\n    }\n\n    @keyframes fn-highlight {\n        from {\n            background-color: var(--footnote-highlight);\n        }\n    }\n}\n\n.tooltiptext {\n    position: absolute;\n    visibility: hidden;\n    color: #fff;\n    background-color: #333;\n    transform: translateX(-50%); /* Center by moving tooltip 50% of its width left */\n    left: -8px; /* Half of the width of the icon */\n    top: -35px;\n    font-size: 0.8em;\n    text-align: center;\n    border-radius: 6px;\n    padding: 5px 8px;\n    margin: 5px;\n    z-index: 1000;\n}\n.tooltipped .tooltiptext {\n    visibility: visible;\n}\n\n.chapter li.part-title {\n    color: var(--sidebar-fg);\n    margin: 5px 0px;\n    font-weight: bold;\n}\n\n.result-no-output {\n    font-style: italic;\n}\n\n.fa-svg svg {\n    width: 1em;\n    height: 1em;\n    fill: currentColor;\n    margin-bottom: -0.1em;\n}\n\ndt {\n    font-weight: bold;\n    margin-top: 0.5em;\n    margin-bottom: 0.1em;\n}\n\n/* This uses a CSS counter to add numbers to definitions, but only if there is\n more than one definition. */\ndl, dt {\n    counter-reset: dd-counter;\n}\n\n/* When there is more than one definition, increment the counter. The first\nselector selects the first definition, and the second one selects definitions\n2 and beyond.*/\ndd:has(+ dd), dd + dd {\n    counter-increment: dd-counter;\n    /* Use flex display to help with positioning the numbers when there is a p\n    tag inside the definition. */\n    display: flex;\n    align-items: flex-start;\n}\n\n/* Shows the counter for definitions. The first selector selects the first\ndefinition, and the second one selections definitions 2 and beyond.*/\ndd:has(+ dd)::before, dd + dd::before  {\n    content: counter(dd-counter) \". \";\n    font-weight: 600;\n    display: inline-block;\n    margin-right: 0.5em;\n}\n\ndd > p {\n    /* For loose definitions that have a p tag inside, don't add a bunch of\n    space before the definition. */\n    margin-top: 0;\n}\n\n/* Remove some excess space from the bottom. */\n.blockquote-tag p:last-child {\n    margin-bottom: 2px;\n}\n\n.blockquote-tag {\n    /* Add some padding to make the vertical bar a little taller than the text.*/\n    padding: 2px 0px 2px 20px;\n    /* Add a solid color bar on the left side. */\n    border-inline-start-style: solid;\n    border-inline-start-width: 4px;\n    /* Disable the background color from normal blockquotes . */\n    background-color: inherit;\n    /* Disable border blocks from blockquotes. */\n    border-block-start: none;\n    border-block-end: none;\n}\n\n.blockquote-tag-title svg {\n    fill: currentColor;\n    /* Add space between the icon and the title. */\n    margin-right: 8px;\n}\n\n.blockquote-tag-note {\n    border-inline-start-color: var(--blockquote-note-color);\n}\n\n.blockquote-tag-tip {\n    border-inline-start-color: var(--blockquote-tip-color);\n}\n\n.blockquote-tag-important {\n    border-inline-start-color: var(--blockquote-important-color);\n}\n\n.blockquote-tag-warning {\n    border-inline-start-color: var(--blockquote-warning-color);\n}\n\n.blockquote-tag-caution {\n    border-inline-start-color: var(--blockquote-caution-color);\n}\n\n.blockquote-tag-note > .blockquote-tag-title {\n    color: var(--blockquote-note-color);\n}\n\n.blockquote-tag-tip > .blockquote-tag-title {\n    color: var(--blockquote-tip-color);\n}\n\n.blockquote-tag-important > .blockquote-tag-title {\n    color: var(--blockquote-important-color);\n}\n\n.blockquote-tag-warning > .blockquote-tag-title {\n    color: var(--blockquote-warning-color);\n}\n\n.blockquote-tag-caution > .blockquote-tag-title {\n    color: var(--blockquote-caution-color);\n}\n\n.blockquote-tag-title {\n    /* Slightly increase the weight for more emphasis. */\n    font-weight: 600;\n    /* Vertically center the icon with the text. */\n    display: flex;\n    align-items: center;\n    /* Remove default large margins for a more compact display. */\n    margin: 2px 0 8px 0;\n}\n\n.blockquote-tag-title .fa-svg {\n    fill: currentColor;\n    /* Add some space between the icon and the text. */\n    margin-right: 8px;\n}\n"
  },
  {
    "path": "crates/mdbook-html/front-end/css/highlight.css",
    "content": "/*\n * An increased contrast highlighting scheme loosely based on the\n * \"Base16 Atelier Dune Light\" theme by Bram de Haan\n * (http://atelierbram.github.io/syntax-highlighting/atelier-schemes/dune)\n * Original Base16 color scheme by Chris Kempson\n * (https://github.com/chriskempson/base16)\n */\n\n/* Comment */\n.hljs-comment,\n.hljs-quote {\n  color: #575757;\n}\n\n/* Red */\n.hljs-variable,\n.hljs-template-variable,\n.hljs-attribute,\n.hljs-attr,\n.hljs-tag,\n.hljs-name,\n.hljs-regexp,\n.hljs-link,\n.hljs-name,\n.hljs-selector-id,\n.hljs-selector-class {\n  color: #d70025;\n}\n\n/* Orange */\n.hljs-number,\n.hljs-meta,\n.hljs-built_in,\n.hljs-builtin-name,\n.hljs-literal,\n.hljs-type,\n.hljs-params {\n  color: #b21e00;\n}\n\n/* Green */\n.hljs-string,\n.hljs-symbol,\n.hljs-bullet {\n  color: #008200;\n}\n\n/* Blue */\n.hljs-title,\n.hljs-section {\n  color: #0030f2;\n}\n\n/* Purple */\n.hljs-keyword,\n.hljs-selector-tag {\n  color: #9d00ec;\n}\n\n.hljs {\n  display: block;\n  overflow-x: auto;\n  background: #f6f7f6;\n  color: #000;\n}\n\n.hljs-emphasis {\n  font-style: italic;\n}\n\n.hljs-strong {\n  font-weight: bold;\n}\n\n.hljs-addition {\n  color: #22863a;\n  background-color: #f0fff4;\n}\n\n.hljs-deletion {\n  color: #b31d28;\n  background-color: #ffeef0;\n}\n"
  },
  {
    "path": "crates/mdbook-html/front-end/css/print.css",
    "content": "\n#mdbook-sidebar,\n#mdbook-menu-bar,\n.nav-chapters,\n.mobile-nav-chapters {\n    display: none;\n}\n\n#mdbook-page-wrapper.page-wrapper {\n    transform: none !important;\n    margin-inline-start: 0px;\n    overflow-y: initial;\n}\n\n#mdbook-content {\n    max-width: none;\n    margin: 0;\n    padding: 0;\n}\n\n.page {\n    overflow-y: initial;\n}\n\ncode {\n    direction: ltr !important;\n}\n\npre > .buttons {\n    z-index: 2;\n}\n\na, a:visited, a:active, a:hover {\n    color: #4183c4;\n    text-decoration: none;\n}\n\nh1, h2, h3, h4, h5, h6 {\n    page-break-inside: avoid;\n    page-break-after: avoid;\n}\n\npre, code {\n    page-break-inside: avoid;\n    white-space: pre-wrap;\n}\n\n.fa {\n    display: none !important;\n}\n"
  },
  {
    "path": "crates/mdbook-html/front-end/css/tomorrow-night.css",
    "content": "/* Tomorrow Night Theme */\n/* https://github.com/jmblog/color-themes-for-highlightjs */\n/* Original theme - https://github.com/chriskempson/tomorrow-theme */\n/* https://github.com/jmblog/color-themes-for-highlightjs */\n\n/* Tomorrow Comment */\n.hljs-comment {\n  color: #969896;\n}\n\n/* Tomorrow Red */\n.hljs-variable,\n.hljs-attribute,\n.hljs-attr,\n.hljs-tag,\n.hljs-regexp,\n.ruby .hljs-constant,\n.xml .hljs-tag .hljs-title,\n.xml .hljs-pi,\n.xml .hljs-doctype,\n.html .hljs-doctype,\n.css .hljs-id,\n.css .hljs-class,\n.css .hljs-pseudo {\n  color: #cc6666;\n}\n\n/* Tomorrow Orange */\n.hljs-number,\n.hljs-preprocessor,\n.hljs-pragma,\n.hljs-built_in,\n.hljs-literal,\n.hljs-params,\n.hljs-constant {\n  color: #de935f;\n}\n\n/* Tomorrow Yellow */\n.ruby .hljs-class .hljs-title,\n.css .hljs-rule .hljs-attribute {\n  color: #f0c674;\n}\n\n/* Tomorrow Green */\n.hljs-string,\n.hljs-value,\n.hljs-inheritance,\n.hljs-header,\n.hljs-name,\n.ruby .hljs-symbol,\n.xml .hljs-cdata {\n  color: #b5bd68;\n}\n\n/* Tomorrow Aqua */\n.hljs-title,\n.hljs-section,\n.css .hljs-hexcolor {\n  color: #8abeb7;\n}\n\n/* Tomorrow Blue */\n.hljs-function,\n.python .hljs-decorator,\n.python .hljs-title,\n.ruby .hljs-function .hljs-title,\n.ruby .hljs-title .hljs-keyword,\n.perl .hljs-sub,\n.javascript .hljs-title,\n.coffeescript .hljs-title {\n  color: #81a2be;\n}\n\n/* Tomorrow Purple */\n.hljs-keyword,\n.javascript .hljs-function {\n  color: #b294bb;\n}\n\n.hljs {\n  display: block;\n  overflow-x: auto;\n  background: #1d1f21;\n  color: #c5c8c6;\n}\n\n.coffeescript .javascript,\n.javascript .xml,\n.tex .hljs-formula,\n.xml .javascript,\n.xml .vbscript,\n.xml .css,\n.xml .hljs-cdata {\n  opacity: 0.5;\n}\n\n.hljs-addition {\n  color: #718c00;\n}\n\n.hljs-deletion {\n  color: #c82829;\n}\n"
  },
  {
    "path": "crates/mdbook-html/front-end/css/variables.css",
    "content": "\n/* Globals */\n\n:root {\n    --sidebar-target-width: 300px;\n    --sidebar-width: min(var(--sidebar-target-width), 80vw);\n    --sidebar-resize-indicator-width: 8px;\n    --sidebar-resize-indicator-space: 2px;\n    --page-padding: 15px;\n    --content-max-width: 750px;\n    --menu-bar-height: 50px;\n    --mono-font: \"Source Code Pro\", Consolas, \"Ubuntu Mono\", Menlo, \"DejaVu Sans Mono\", monospace, monospace;\n    --code-font-size: 0.875em; /* please adjust the ace font size accordingly in editor.js */\n    --searchbar-margin-block-start: 5px;\n}\n\n/* Themes */\n\n.ayu {\n    --bg: hsl(210, 25%, 8%);\n    --fg: #c5c5c5;\n\n    --sidebar-bg: #14191f;\n    --sidebar-fg: #c8c9db;\n    --sidebar-non-existant: #5c6773;\n    --sidebar-active: #ffb454;\n    --sidebar-spacer: #2d334f;\n\n    --scrollbar: var(--sidebar-fg);\n\n    --icons: #737480;\n    --icons-hover: #b7b9cc;\n\n    --links: #0096cf;\n\n    --inline-code-color: #ffb454;\n\n    --theme-popup-bg: #14191f;\n    --theme-popup-border: #5c6773;\n    --theme-hover: #191f26;\n\n    --quote-bg: hsl(226, 15%, 17%);\n    --quote-border: hsl(226, 15%, 22%);\n\n    --warning-border: #ff8e00;\n\n    --table-border-color: hsl(210, 25%, 13%);\n    --table-header-bg: hsl(210, 25%, 28%);\n    --table-alternate-bg: hsl(210, 25%, 11%);\n\n    --searchbar-border-color: #848484;\n    --searchbar-bg: #424242;\n    --searchbar-fg: #fff;\n    --searchbar-shadow-color: #d4c89f;\n    --searchresults-header-fg: #666;\n    --searchresults-border-color: #888;\n    --searchresults-li-bg: #252932;\n    --search-mark-bg: #e3b171;\n\n    --color-scheme: dark;\n\n    /* Same as `--icons` */\n    --copy-button-filter: invert(45%) sepia(6%) saturate(621%) hue-rotate(198deg) brightness(99%) contrast(85%);\n    /* Same as `--sidebar-active` */\n    --copy-button-filter-hover: invert(68%) sepia(55%) saturate(531%) hue-rotate(341deg) brightness(104%) contrast(101%);\n\n    --footnote-highlight: #2668a6;\n\n    --overlay-bg: rgba(33, 40, 48, 0.4);\n\n    --blockquote-note-color: #74b9ff;\n    --blockquote-tip-color: #09ca09;\n    --blockquote-important-color: #d3abff;\n    --blockquote-warning-color: #f0b72f;\n    --blockquote-caution-color: #f21424;\n\n    --sidebar-header-border-color: #c18639;\n}\n\n.coal {\n    --bg: hsl(200, 7%, 8%);\n    --fg: #98a3ad;\n\n    --sidebar-bg: #292c2f;\n    --sidebar-fg: #a1adb8;\n    --sidebar-non-existant: #505254;\n    --sidebar-active: #3473ad;\n    --sidebar-spacer: #393939;\n\n    --scrollbar: var(--sidebar-fg);\n\n    --icons: #43484d;\n    --icons-hover: #b3c0cc;\n\n    --links: #2b79a2;\n\n    --inline-code-color: #c5c8c6;\n\n    --theme-popup-bg: #141617;\n    --theme-popup-border: #43484d;\n    --theme-hover: #1f2124;\n\n    --quote-bg: hsl(234, 21%, 18%);\n    --quote-border: hsl(234, 21%, 23%);\n\n    --warning-border: #ff8e00;\n\n    --table-border-color: hsl(200, 7%, 13%);\n    --table-header-bg: hsl(200, 7%, 28%);\n    --table-alternate-bg: hsl(200, 7%, 11%);\n\n    --searchbar-border-color: #aaa;\n    --searchbar-bg: #b7b7b7;\n    --searchbar-fg: #000;\n    --searchbar-shadow-color: #aaa;\n    --searchresults-header-fg: #666;\n    --searchresults-border-color: #98a3ad;\n    --searchresults-li-bg: #2b2b2f;\n    --search-mark-bg: #355c7d;\n\n    --color-scheme: dark;\n\n    /* Same as `--icons` */\n    --copy-button-filter: invert(26%) sepia(8%) saturate(575%) hue-rotate(169deg) brightness(87%) contrast(82%);\n    /* Same as `--sidebar-active` */\n    --copy-button-filter-hover: invert(36%) sepia(70%) saturate(503%) hue-rotate(167deg) brightness(98%) contrast(89%);\n\n    --footnote-highlight: #4079ae;\n\n    --overlay-bg: rgba(33, 40, 48, 0.4);\n\n    --blockquote-note-color: #4493f8;\n    --blockquote-tip-color: #08ae08;\n    --blockquote-important-color: #ab7df8;\n    --blockquote-warning-color: #d29922;\n    --blockquote-caution-color: #d91b29;\n\n    --sidebar-header-border-color: #3473ad;\n}\n\n.light, html:not(.js) {\n    --bg: hsl(0, 0%, 100%);\n    --fg: hsl(0, 0%, 0%);\n\n    --sidebar-bg: #fafafa;\n    --sidebar-fg: hsl(0, 0%, 0%);\n    --sidebar-non-existant: #aaaaaa;\n    --sidebar-active: #1f1fff;\n    --sidebar-spacer: #f4f4f4;\n\n    --scrollbar: #8F8F8F;\n\n    --icons: #747474;\n    --icons-hover: #000000;\n\n    --links: #20609f;\n\n    --inline-code-color: #301900;\n\n    --theme-popup-bg: #fafafa;\n    --theme-popup-border: #cccccc;\n    --theme-hover: #e6e6e6;\n\n    --quote-bg: hsl(197, 37%, 96%);\n    --quote-border: hsl(197, 37%, 91%);\n\n    --warning-border: #ff8e00;\n\n    --table-border-color: hsl(0, 0%, 95%);\n    --table-header-bg: hsl(0, 0%, 80%);\n    --table-alternate-bg: hsl(0, 0%, 97%);\n\n    --searchbar-border-color: #aaa;\n    --searchbar-bg: #fafafa;\n    --searchbar-fg: #000;\n    --searchbar-shadow-color: #aaa;\n    --searchresults-header-fg: #666;\n    --searchresults-border-color: #888;\n    --searchresults-li-bg: #e4f2fe;\n    --search-mark-bg: #a2cff5;\n\n    --color-scheme: light;\n\n    /* Same as `--icons` */\n    --copy-button-filter: invert(45.49%);\n    /* Same as `--sidebar-active` */\n    --copy-button-filter-hover: invert(14%) sepia(93%) saturate(4250%) hue-rotate(243deg) brightness(99%) contrast(130%);\n\n    --footnote-highlight: #7e7eff;\n\n    --overlay-bg: rgba(200, 200, 205, 0.4);\n\n    --blockquote-note-color: #0969da;\n    --blockquote-tip-color: #008000;\n    --blockquote-important-color: #8250df;\n    --blockquote-warning-color: #9a6700;\n    --blockquote-caution-color: #b52731;\n\n    --sidebar-header-border-color: #6e6edb;\n}\n\n.navy {\n    --bg: hsl(226, 23%, 11%);\n    --fg: #bcbdd0;\n\n    --sidebar-bg: #282d3f;\n    --sidebar-fg: #c8c9db;\n    --sidebar-non-existant: #505274;\n    --sidebar-active: #2b79a2;\n    --sidebar-spacer: #2d334f;\n\n    --scrollbar: var(--sidebar-fg);\n\n    --icons: #737480;\n    --icons-hover: #b7b9cc;\n\n    --links: #2b79a2;\n\n    --inline-code-color: #c5c8c6;\n\n    --theme-popup-bg: #161923;\n    --theme-popup-border: #737480;\n    --theme-hover: #282e40;\n\n    --quote-bg: hsl(226, 15%, 17%);\n    --quote-border: hsl(226, 15%, 22%);\n\n    --warning-border: #ff8e00;\n\n    --table-border-color: hsl(226, 23%, 16%);\n    --table-header-bg: hsl(226, 23%, 31%);\n    --table-alternate-bg: hsl(226, 23%, 14%);\n\n    --searchbar-border-color: #aaa;\n    --searchbar-bg: #aeaec6;\n    --searchbar-fg: #000;\n    --searchbar-shadow-color: #aaa;\n    --searchresults-header-fg: #5f5f71;\n    --searchresults-border-color: #5c5c68;\n    --searchresults-li-bg: #242430;\n    --search-mark-bg: #a2cff5;\n\n    --color-scheme: dark;\n\n    /* Same as `--icons` */\n    --copy-button-filter: invert(51%) sepia(10%) saturate(393%) hue-rotate(198deg) brightness(86%) contrast(87%);\n    /* Same as `--sidebar-active` */\n    --copy-button-filter-hover: invert(46%) sepia(20%) saturate(1537%) hue-rotate(156deg) brightness(85%) contrast(90%);\n\n    --footnote-highlight: #4079ae;\n\n    --overlay-bg: rgba(33, 40, 48, 0.4);\n\n    --blockquote-note-color: #4493f8;\n    --blockquote-tip-color: #09ca09;\n    --blockquote-important-color: #ab7df8;\n    --blockquote-warning-color: #d29922;\n    --blockquote-caution-color: #f21424;\n\n    --sidebar-header-border-color: #2f6ab5;\n}\n\n.rust {\n    --bg: hsl(60, 9%, 87%);\n    --fg: #262625;\n\n    --sidebar-bg: #3b2e2a;\n    --sidebar-fg: #c8c9db;\n    --sidebar-non-existant: #505254;\n    --sidebar-active: #e69f67;\n    --sidebar-spacer: #45373a;\n\n    --scrollbar: var(--sidebar-fg);\n\n    --icons: #737480;\n    --icons-hover: #262625;\n\n    --links: #2b79a2;\n\n    --inline-code-color: #6e6b5e;\n\n    --theme-popup-bg: #e1e1db;\n    --theme-popup-border: #b38f6b;\n    --theme-hover: #99908a;\n\n    --quote-bg: hsl(60, 5%, 75%);\n    --quote-border: hsl(60, 5%, 70%);\n\n    --warning-border: #ff8e00;\n\n    --table-border-color: hsl(60, 9%, 82%);\n    --table-header-bg: #b3a497;\n    --table-alternate-bg: hsl(60, 9%, 84%);\n\n    --searchbar-border-color: #aaa;\n    --searchbar-bg: #fafafa;\n    --searchbar-fg: #000;\n    --searchbar-shadow-color: #aaa;\n    --searchresults-header-fg: #666;\n    --searchresults-border-color: #888;\n    --searchresults-li-bg: #dec2a2;\n    --search-mark-bg: #e69f67;\n\n    /* Same as `--icons` */\n    --copy-button-filter: invert(51%) sepia(10%) saturate(393%) hue-rotate(198deg) brightness(86%) contrast(87%);\n    /* Same as `--sidebar-active` */\n    --copy-button-filter-hover: invert(77%) sepia(16%) saturate(1798%) hue-rotate(328deg) brightness(98%) contrast(83%);\n\n    --footnote-highlight: #d3a17a;\n\n    --overlay-bg: rgba(150, 150, 150, 0.25);\n\n    --blockquote-note-color: #023b95;\n    --blockquote-tip-color: #007700;\n    --blockquote-important-color: #8250df;\n    --blockquote-warning-color: #603700;\n    --blockquote-caution-color: #aa1721;\n\n    --sidebar-header-border-color: #8c391f;\n}\n\n@media (prefers-color-scheme: dark) {\n    html:not(.js) {\n        --bg: hsl(200, 7%, 8%);\n        --fg: #98a3ad;\n\n        --sidebar-bg: #292c2f;\n        --sidebar-fg: #a1adb8;\n        --sidebar-non-existant: #505254;\n        --sidebar-active: #3473ad;\n        --sidebar-spacer: #393939;\n\n        --scrollbar: var(--sidebar-fg);\n\n        --icons: #43484d;\n        --icons-hover: #b3c0cc;\n\n        --links: #2b79a2;\n\n        --inline-code-color: #c5c8c6;\n\n        --theme-popup-bg: #141617;\n        --theme-popup-border: #43484d;\n        --theme-hover: #1f2124;\n\n        --quote-bg: hsl(234, 21%, 18%);\n        --quote-border: hsl(234, 21%, 23%);\n\n        --warning-border: #ff8e00;\n\n        --table-border-color: hsl(200, 7%, 13%);\n        --table-header-bg: hsl(200, 7%, 28%);\n        --table-alternate-bg: hsl(200, 7%, 11%);\n\n        --searchbar-border-color: #aaa;\n        --searchbar-bg: #b7b7b7;\n        --searchbar-fg: #000;\n        --searchbar-shadow-color: #aaa;\n        --searchresults-header-fg: #666;\n        --searchresults-border-color: #98a3ad;\n        --searchresults-li-bg: #2b2b2f;\n        --search-mark-bg: #355c7d;\n\n        --color-scheme: dark;\n\n        /* Same as `--icons` */\n        --copy-button-filter: invert(26%) sepia(8%) saturate(575%) hue-rotate(169deg) brightness(87%) contrast(82%);\n        /* Same as `--sidebar-active` */\n        --copy-button-filter-hover: invert(36%) sepia(70%) saturate(503%) hue-rotate(167deg) brightness(98%) contrast(89%);\n\n        --footnote-highlight: #4079ae;\n\n        --overlay-bg: rgba(33, 40, 48, 0.4);\n\n        --blockquote-note-color: #4493f8;\n        --blockquote-tip-color: #08ae08;\n        --blockquote-important-color: #ab7df8;\n        --blockquote-warning-color: #d29922;\n        --blockquote-caution-color: #d91b29;\n\n        --sidebar-header-border-color: #3473ad;\n    }\n}\n"
  },
  {
    "path": "crates/mdbook-html/front-end/fonts/OPEN-SANS-LICENSE.txt",
    "content": "\n                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright [yyyy] [name of copyright owner]\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "crates/mdbook-html/front-end/fonts/SOURCE-CODE-PRO-LICENSE.txt",
    "content": "Copyright 2010, 2012 Adobe Systems Incorporated (http://www.adobe.com/), with Reserved Font Name 'Source'. All Rights Reserved. Source is a trademark of Adobe Systems Incorporated in the United States and/or other countries.\n\nThis Font Software is licensed under the SIL Open Font License, Version 1.1.\nThis license is copied below, and is also available with a FAQ at:\nhttp://scripts.sil.org/OFL\n\n\n-----------------------------------------------------------\nSIL OPEN FONT LICENSE Version 1.1 - 26 February 2007\n-----------------------------------------------------------\n\nPREAMBLE\nThe goals of the Open Font License (OFL) are to stimulate worldwide\ndevelopment of collaborative font projects, to support the font creation\nefforts of academic and linguistic communities, and to provide a free and\nopen framework in which fonts may be shared and improved in partnership\nwith others.\n\nThe OFL allows the licensed fonts to be used, studied, modified and\nredistributed freely as long as they are not sold by themselves. The\nfonts, including any derivative works, can be bundled, embedded, \nredistributed and/or sold with any software provided that any reserved\nnames are not used by derivative works. The fonts and derivatives,\nhowever, cannot be released under any other type of license. The\nrequirement for fonts to remain under this license does not apply\nto any document created using the fonts or their derivatives.\n\nDEFINITIONS\n\"Font Software\" refers to the set of files released by the Copyright\nHolder(s) under this license and clearly marked as such. This may\ninclude source files, build scripts and documentation.\n\n\"Reserved Font Name\" refers to any names specified as such after the\ncopyright statement(s).\n\n\"Original Version\" refers to the collection of Font Software components as\ndistributed by the Copyright Holder(s).\n\n\"Modified Version\" refers to any derivative made by adding to, deleting,\nor substituting -- in part or in whole -- any of the components of the\nOriginal Version, by changing formats or by porting the Font Software to a\nnew environment.\n\n\"Author\" refers to any designer, engineer, programmer, technical\nwriter or other person who contributed to the Font Software.\n\nPERMISSION & CONDITIONS\nPermission is hereby granted, free of charge, to any person obtaining\na copy of the Font Software, to use, study, copy, merge, embed, modify,\nredistribute, and sell modified and unmodified copies of the Font\nSoftware, subject to the following conditions:\n\n1) Neither the Font Software nor any of its individual components,\nin Original or Modified Versions, may be sold by itself.\n\n2) Original or Modified Versions of the Font Software may be bundled,\nredistributed and/or sold with any software, provided that each copy\ncontains the above copyright notice and this license. These can be\nincluded either as stand-alone text files, human-readable headers or\nin the appropriate machine-readable metadata fields within text or\nbinary files as long as those fields can be easily viewed by the user.\n\n3) No Modified Version of the Font Software may use the Reserved Font\nName(s) unless explicit written permission is granted by the corresponding\nCopyright Holder. This restriction only applies to the primary font name as\npresented to the users.\n\n4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font\nSoftware shall not be used to promote, endorse or advertise any\nModified Version, except to acknowledge the contribution(s) of the\nCopyright Holder(s) and the Author(s) or with their explicit written\npermission.\n\n5) The Font Software, modified or unmodified, in part or in whole,\nmust be distributed entirely under this license, and must not be\ndistributed under any other license. The requirement for fonts to\nremain under this license does not apply to any document created\nusing the Font Software.\n\nTERMINATION\nThis license becomes null and void if any of the above conditions are\nnot met.\n\nDISCLAIMER\nTHE FONT SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT\nOF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE\nCOPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,\nINCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL\nDAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\nFROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM\nOTHER DEALINGS IN THE FONT SOFTWARE.\n"
  },
  {
    "path": "crates/mdbook-html/front-end/fonts/fonts.css",
    "content": "/* Open Sans is licensed under the Apache License, Version 2.0. See http://www.apache.org/licenses/LICENSE-2.0 */\n/* Source Code Pro is under the Open Font License. See https://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=OFL */\n\n/* open-sans-300 - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */\n@font-face {\n  font-family: 'Open Sans';\n  font-style: normal;\n  font-weight: 300;\n  src: local('Open Sans Light'), local('OpenSans-Light'),\n       url('{{ resource \"fonts/open-sans-v17-all-charsets-300.woff2\" }}') format('woff2');\n}\n\n/* open-sans-300italic - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */\n@font-face {\n  font-family: 'Open Sans';\n  font-style: italic;\n  font-weight: 300;\n  src: local('Open Sans Light Italic'), local('OpenSans-LightItalic'),\n       url('{{ resource \"fonts/open-sans-v17-all-charsets-300italic.woff2\" }}') format('woff2');\n}\n\n/* open-sans-regular - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */\n@font-face {\n  font-family: 'Open Sans';\n  font-style: normal;\n  font-weight: 400;\n  src: local('Open Sans Regular'), local('OpenSans-Regular'),\n       url('{{ resource \"fonts/open-sans-v17-all-charsets-regular.woff2\" }}') format('woff2');\n}\n\n/* open-sans-italic - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */\n@font-face {\n  font-family: 'Open Sans';\n  font-style: italic;\n  font-weight: 400;\n  src: local('Open Sans Italic'), local('OpenSans-Italic'),\n       url('{{ resource \"fonts/open-sans-v17-all-charsets-italic.woff2\" }}') format('woff2');\n}\n\n/* open-sans-600 - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */\n@font-face {\n  font-family: 'Open Sans';\n  font-style: normal;\n  font-weight: 600;\n  src: local('Open Sans SemiBold'), local('OpenSans-SemiBold'),\n       url('{{ resource \"fonts/open-sans-v17-all-charsets-600.woff2\" }}') format('woff2');\n}\n\n/* open-sans-600italic - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */\n@font-face {\n  font-family: 'Open Sans';\n  font-style: italic;\n  font-weight: 600;\n  src: local('Open Sans SemiBold Italic'), local('OpenSans-SemiBoldItalic'),\n       url('{{ resource \"fonts/open-sans-v17-all-charsets-600italic.woff2\" }}') format('woff2');\n}\n\n/* open-sans-700 - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */\n@font-face {\n  font-family: 'Open Sans';\n  font-style: normal;\n  font-weight: 700;\n  src: local('Open Sans Bold'), local('OpenSans-Bold'),\n       url('{{ resource \"fonts/open-sans-v17-all-charsets-700.woff2\" }}') format('woff2');\n}\n\n/* open-sans-700italic - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */\n@font-face {\n  font-family: 'Open Sans';\n  font-style: italic;\n  font-weight: 700;\n  src: local('Open Sans Bold Italic'), local('OpenSans-BoldItalic'),\n       url('{{ resource \"fonts/open-sans-v17-all-charsets-700italic.woff2\" }}') format('woff2');\n}\n\n/* open-sans-800 - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */\n@font-face {\n  font-family: 'Open Sans';\n  font-style: normal;\n  font-weight: 800;\n  src: local('Open Sans ExtraBold'), local('OpenSans-ExtraBold'),\n       url('{{ resource \"fonts/open-sans-v17-all-charsets-800.woff2\" }}') format('woff2');\n}\n\n/* open-sans-800italic - latin_vietnamese_latin-ext_greek-ext_greek_cyrillic-ext_cyrillic */\n@font-face {\n  font-family: 'Open Sans';\n  font-style: italic;\n  font-weight: 800;\n  src: local('Open Sans ExtraBold Italic'), local('OpenSans-ExtraBoldItalic'),\n       url('{{ resource \"fonts/open-sans-v17-all-charsets-800italic.woff2\" }}') format('woff2');\n}\n\n/* source-code-pro-500 - latin_vietnamese_latin-ext_greek_cyrillic-ext_cyrillic */\n@font-face {\n  font-family: 'Source Code Pro';\n  font-style: normal;\n  font-weight: 500;\n  src: url('{{ resource \"fonts/source-code-pro-v11-all-charsets-500.woff2\" }}') format('woff2');\n}\n"
  },
  {
    "path": "crates/mdbook-html/front-end/js/book.js",
    "content": "'use strict';\n\n/* global default_theme, default_dark_theme, default_light_theme, hljs, ClipboardJS */\n\n// Fix back button cache problem\nwindow.onunload = function() { };\n\n// Global variable, shared between modules\nfunction playground_text(playground, hidden = true) {\n    const code_block = playground.querySelector('code');\n\n    if (window.ace && code_block.classList.contains('editable')) {\n        const editor = window.ace.edit(code_block);\n        return editor.getValue();\n    } else if (hidden) {\n        return code_block.textContent;\n    } else {\n        return code_block.innerText;\n    }\n}\n\n(function codeSnippets() {\n    function fetch_with_timeout(url, options, timeout = 6000) {\n        return Promise.race([\n            fetch(url, options),\n            new Promise((_, reject) => setTimeout(() => reject(new Error('timeout')), timeout)),\n        ]);\n    }\n\n    const playgrounds = Array.from(document.querySelectorAll('.playground'));\n    if (playgrounds.length > 0) {\n        fetch_with_timeout('https://play.rust-lang.org/meta/crates', {\n            headers: {\n                'Content-Type': 'application/json',\n            },\n            method: 'POST',\n            mode: 'cors',\n        })\n            .then(response => response.json())\n            .then(response => {\n            // get list of crates available in the rust playground\n                const playground_crates = response.crates.map(item => item['id']);\n                playgrounds.forEach(block => handle_crate_list_update(block, playground_crates));\n            });\n    }\n\n    function handle_crate_list_update(playground_block, playground_crates) {\n        // update the play buttons after receiving the response\n        update_play_button(playground_block, playground_crates);\n\n        // and install on change listener to dynamically update ACE editors\n        if (window.ace) {\n            const code_block = playground_block.querySelector('code');\n            if (code_block.classList.contains('editable')) {\n                const editor = window.ace.edit(code_block);\n                editor.addEventListener('change', () => {\n                    update_play_button(playground_block, playground_crates);\n                });\n                // add Ctrl-Enter command to execute rust code\n                editor.commands.addCommand({\n                    name: 'run',\n                    bindKey: {\n                        win: 'Ctrl-Enter',\n                        mac: 'Ctrl-Enter',\n                    },\n                    exec: _editor => run_rust_code(playground_block),\n                });\n            }\n        }\n    }\n\n    // updates the visibility of play button based on `no_run` class and\n    // used crates vs ones available on https://play.rust-lang.org\n    function update_play_button(pre_block, playground_crates) {\n        const play_button = pre_block.querySelector('.play-button');\n\n        // skip if code is `no_run`\n        if (pre_block.querySelector('code').classList.contains('no_run')) {\n            play_button.classList.add('hidden');\n            return;\n        }\n\n        // get list of `extern crate`'s from snippet\n        const txt = playground_text(pre_block);\n        const re = /extern\\s+crate\\s+([a-zA-Z_0-9]+)\\s*;/g;\n        const snippet_crates = [];\n        let item;\n        while (item = re.exec(txt)) {\n            snippet_crates.push(item[1]);\n        }\n\n        // check if all used crates are available on play.rust-lang.org\n        const all_available = snippet_crates.every(function(elem) {\n            return playground_crates.indexOf(elem) > -1;\n        });\n\n        if (all_available) {\n            play_button.classList.remove('hidden');\n            play_button.hidden = false;\n        } else {\n            play_button.classList.add('hidden');\n        }\n    }\n\n    function run_rust_code(code_block) {\n        let result_block = code_block.querySelector('.result');\n        if (!result_block) {\n            result_block = document.createElement('code');\n            result_block.className = 'result hljs language-bash';\n\n            code_block.append(result_block);\n        }\n\n        const text = playground_text(code_block);\n        const classes = code_block.querySelector('code').classList;\n        let edition = '2015';\n        classes.forEach(className => {\n            if (className.startsWith('edition')) {\n                edition = className.slice(7);\n            }\n        });\n        const params = {\n            version: 'stable',\n            optimize: '0',\n            code: text,\n            edition: edition,\n        };\n\n        if (text.indexOf('#![feature') !== -1) {\n            params.version = 'nightly';\n        }\n\n        result_block.innerText = 'Running...';\n\n        fetch_with_timeout('https://play.rust-lang.org/evaluate.json', {\n            headers: {\n                'Content-Type': 'application/json',\n            },\n            method: 'POST',\n            mode: 'cors',\n            body: JSON.stringify(params),\n        })\n            .then(response => response.json())\n            .then(response => {\n                if (response.result.trim() === '') {\n                    result_block.innerText = 'No output';\n                    result_block.classList.add('result-no-output');\n                } else {\n                    result_block.innerText = response.result;\n                    result_block.classList.remove('result-no-output');\n                }\n            })\n            .catch(error => result_block.innerText = 'Playground Communication: ' + error.message);\n    }\n\n    // Syntax highlighting Configuration\n    hljs.configure({\n        tabReplace: '    ', // 4 spaces\n        languages: [], // Languages used for auto-detection\n    });\n\n    const code_nodes = Array\n        .from(document.querySelectorAll('code'))\n        // Don't highlight `inline code` blocks in headers.\n        .filter(function(node) {\n            return !node.parentElement.classList.contains('header');\n        });\n\n    if (window.ace) {\n        // language-rust class needs to be removed for editable\n        // blocks or highlightjs will capture events\n        code_nodes\n            .filter(function(node) {\n                return node.classList.contains('editable');\n            })\n            .forEach(function(block) {\n                block.classList.remove('language-rust');\n            });\n\n        code_nodes\n            .filter(function(node) {\n                return !node.classList.contains('editable');\n            })\n            .forEach(function(block) {\n                hljs.highlightBlock(block);\n            });\n    } else {\n        code_nodes.forEach(function(block) {\n            hljs.highlightBlock(block);\n        });\n    }\n\n    // Adding the hljs class gives code blocks the color css\n    // even if highlighting doesn't apply\n    code_nodes.forEach(function(block) {\n        block.classList.add('hljs');\n    });\n\n    Array.from(document.querySelectorAll('code.hljs')).forEach(function(block) {\n\n        const lines = Array.from(block.querySelectorAll('.boring'));\n        // If no lines were hidden, return\n        if (!lines.length) {\n            return;\n        }\n        block.classList.add('hide-boring');\n\n        const buttons = document.createElement('div');\n        buttons.className = 'buttons';\n        buttons.innerHTML = '<button title=\"Show hidden lines\" \\\naria-label=\"Show hidden lines\"></button>';\n        buttons.firstChild.innerHTML = document.getElementById('fa-eye').innerHTML;\n\n        // add expand button\n        const pre_block = block.parentNode;\n        pre_block.insertBefore(buttons, pre_block.firstChild);\n\n        buttons.firstChild.addEventListener('click', function(e) {\n            if (this.title === 'Show hidden lines') {\n                this.innerHTML = document.getElementById('fa-eye-slash').innerHTML;\n                this.title = 'Hide lines';\n                this.setAttribute('aria-label', e.target.title);\n\n                block.classList.remove('hide-boring');\n            } else if (this.title === 'Hide lines') {\n                this.innerHTML = document.getElementById('fa-eye').innerHTML;\n                this.title = 'Show hidden lines';\n                this.setAttribute('aria-label', e.target.title);\n\n                block.classList.add('hide-boring');\n            }\n        });\n    });\n\n    if (window.playground_copyable) {\n        Array.from(document.querySelectorAll('pre code')).forEach(function(block) {\n            const pre_block = block.parentNode;\n            if (!pre_block.classList.contains('playground')) {\n                let buttons = pre_block.querySelector('.buttons');\n                if (!buttons) {\n                    buttons = document.createElement('div');\n                    buttons.className = 'buttons';\n                    pre_block.insertBefore(buttons, pre_block.firstChild);\n                }\n\n                const clipButton = document.createElement('button');\n                clipButton.className = 'clip-button';\n                clipButton.title = 'Copy to clipboard';\n                clipButton.setAttribute('aria-label', clipButton.title);\n                clipButton.innerHTML = '<i class=\"tooltiptext\"></i>';\n\n                buttons.insertBefore(clipButton, buttons.firstChild);\n            }\n        });\n    }\n\n    // Process playground code blocks\n    Array.from(document.querySelectorAll('.playground')).forEach(function(pre_block) {\n        // Add play button\n        let buttons = pre_block.querySelector('.buttons');\n        if (!buttons) {\n            buttons = document.createElement('div');\n            buttons.className = 'buttons';\n            pre_block.insertBefore(buttons, pre_block.firstChild);\n        }\n\n        const runCodeButton = document.createElement('button');\n        runCodeButton.className = 'play-button';\n        runCodeButton.hidden = true;\n        runCodeButton.title = 'Run this code';\n        runCodeButton.setAttribute('aria-label', runCodeButton.title);\n        runCodeButton.innerHTML = document.getElementById('fa-play').innerHTML;\n\n        buttons.insertBefore(runCodeButton, buttons.firstChild);\n        runCodeButton.addEventListener('click', () => {\n            run_rust_code(pre_block);\n        });\n\n        if (window.playground_copyable) {\n            const copyCodeClipboardButton = document.createElement('button');\n            copyCodeClipboardButton.className = 'clip-button';\n            copyCodeClipboardButton.innerHTML = '<i class=\"tooltiptext\"></i>';\n            copyCodeClipboardButton.title = 'Copy to clipboard';\n            copyCodeClipboardButton.setAttribute('aria-label', copyCodeClipboardButton.title);\n\n            buttons.insertBefore(copyCodeClipboardButton, buttons.firstChild);\n        }\n\n        const code_block = pre_block.querySelector('code');\n        if (window.ace && code_block.classList.contains('editable')) {\n            const undoChangesButton = document.createElement('button');\n            undoChangesButton.className = 'reset-button';\n            undoChangesButton.title = 'Undo changes';\n            undoChangesButton.setAttribute('aria-label', undoChangesButton.title);\n            undoChangesButton.innerHTML +=\n                document.getElementById('fa-clock-rotate-left').innerHTML;\n\n            buttons.insertBefore(undoChangesButton, buttons.firstChild);\n\n            undoChangesButton.addEventListener('click', function() {\n                const editor = window.ace.edit(code_block);\n                editor.setValue(editor.originalCode);\n                editor.clearSelection();\n            });\n        }\n    });\n})();\n\n(function themes() {\n    const html = document.querySelector('html');\n    const themeToggleButton = document.getElementById('mdbook-theme-toggle');\n    const themePopup = document.getElementById('mdbook-theme-list');\n    const themeColorMetaTag = document.querySelector('meta[name=\"theme-color\"]');\n    const themeIds = [];\n    themePopup.querySelectorAll('button.theme').forEach(function(el) {\n        themeIds.push(el.id);\n    });\n    const stylesheets = {\n        ayuHighlight: document.querySelector('#mdbook-ayu-highlight-css'),\n        tomorrowNight: document.querySelector('#mdbook-tomorrow-night-css'),\n        highlight: document.querySelector('#mdbook-highlight-css'),\n    };\n\n    function showThemes() {\n        themePopup.style.display = 'block';\n        themeToggleButton.setAttribute('aria-expanded', true);\n        themePopup.querySelector('button#mdbook-theme-' + get_theme()).focus();\n    }\n\n    function updateThemeSelected() {\n        themePopup.querySelectorAll('.theme-selected').forEach(function(el) {\n            el.classList.remove('theme-selected');\n        });\n        const selected = get_saved_theme() ?? 'default_theme';\n        let element = themePopup.querySelector('button#mdbook-theme-' + selected);\n        if (element === null) {\n            // Fall back in case there is no \"Default\" item.\n            element = themePopup.querySelector('button#mdbook-theme-' + get_theme());\n        }\n        element.classList.add('theme-selected');\n    }\n\n    function hideThemes() {\n        themePopup.style.display = 'none';\n        themeToggleButton.setAttribute('aria-expanded', false);\n        themeToggleButton.focus();\n    }\n\n    function get_saved_theme() {\n        let theme = null;\n        try {\n            theme = localStorage.getItem('mdbook-theme');\n        } catch {\n            // ignore error.\n        }\n        return theme;\n    }\n\n    function delete_saved_theme() {\n        localStorage.removeItem('mdbook-theme');\n    }\n\n    function get_theme() {\n        const theme = get_saved_theme();\n        if (theme === null || theme === undefined || !themeIds.includes('mdbook-theme-' + theme)) {\n            if (typeof default_dark_theme === 'undefined') {\n                // A customized index.hbs might not define this, so fall back to\n                // old behavior of determining the default on page load.\n                return default_theme;\n            }\n            return window.matchMedia('(prefers-color-scheme: dark)').matches\n                ? default_dark_theme\n                : default_light_theme;\n        } else {\n            return theme;\n        }\n    }\n\n    let previousTheme = default_theme;\n    function set_theme(theme, store = true) {\n        let ace_theme;\n\n        if (theme === 'coal' || theme === 'navy') {\n            stylesheets.ayuHighlight.disabled = true;\n            stylesheets.tomorrowNight.disabled = false;\n            stylesheets.highlight.disabled = true;\n\n            ace_theme = 'ace/theme/tomorrow_night';\n        } else if (theme === 'ayu') {\n            stylesheets.ayuHighlight.disabled = false;\n            stylesheets.tomorrowNight.disabled = true;\n            stylesheets.highlight.disabled = true;\n            ace_theme = 'ace/theme/tomorrow_night';\n        } else {\n            stylesheets.ayuHighlight.disabled = true;\n            stylesheets.tomorrowNight.disabled = true;\n            stylesheets.highlight.disabled = false;\n            ace_theme = 'ace/theme/dawn';\n        }\n\n        setTimeout(function() {\n            themeColorMetaTag.content = getComputedStyle(document.documentElement).backgroundColor;\n        }, 1);\n\n        if (window.ace && window.editors) {\n            window.editors.forEach(function(editor) {\n                editor.setTheme(ace_theme);\n            });\n        }\n\n        if (store) {\n            try {\n                localStorage.setItem('mdbook-theme', theme);\n            } catch {\n                // ignore error.\n            }\n        }\n\n        html.classList.remove(previousTheme);\n        html.classList.add(theme);\n        previousTheme = theme;\n        updateThemeSelected();\n    }\n\n    const query = window.matchMedia('(prefers-color-scheme: dark)');\n    query.onchange = function() {\n        set_theme(get_theme(), false);\n    };\n\n    // Set theme.\n    set_theme(get_theme(), false);\n\n    themeToggleButton.addEventListener('click', function() {\n        if (themePopup.style.display === 'block') {\n            hideThemes();\n        } else {\n            showThemes();\n        }\n    });\n\n    themePopup.addEventListener('click', function(e) {\n        let theme;\n        if (e.target.className === 'theme') {\n            theme = e.target.id;\n        } else if (e.target.parentElement.className === 'theme') {\n            theme = e.target.parentElement.id;\n        } else {\n            return;\n        }\n        theme = theme.replace(/^mdbook-theme-/, '');\n\n        if (theme === 'default_theme' || theme === null) {\n            delete_saved_theme();\n            set_theme(get_theme(), false);\n        } else {\n            set_theme(theme);\n        }\n    });\n\n    themePopup.addEventListener('focusout', function(e) {\n        // e.relatedTarget is null in Safari and Firefox on macOS (see workaround below)\n        if (!!e.relatedTarget &&\n            !themeToggleButton.contains(e.relatedTarget) &&\n            !themePopup.contains(e.relatedTarget)\n        ) {\n            hideThemes();\n        }\n    });\n\n    // Should not be needed, but it works around an issue on macOS & iOS:\n    // https://github.com/rust-lang/mdBook/issues/628\n    document.addEventListener('click', function(e) {\n        if (themePopup.style.display === 'block' &&\n            !themeToggleButton.contains(e.target) &&\n            !themePopup.contains(e.target)\n        ) {\n            hideThemes();\n        }\n    });\n\n    document.addEventListener('keydown', function(e) {\n        if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey) {\n            return;\n        }\n        if (!themePopup.contains(e.target)) {\n            return;\n        }\n\n        let li;\n        switch (e.key) {\n        case 'Escape':\n            e.preventDefault();\n            hideThemes();\n            break;\n        case 'ArrowUp':\n            e.preventDefault();\n            li = document.activeElement.parentElement;\n            if (li && li.previousElementSibling) {\n                li.previousElementSibling.querySelector('button').focus();\n            }\n            break;\n        case 'ArrowDown':\n            e.preventDefault();\n            li = document.activeElement.parentElement;\n            if (li && li.nextElementSibling) {\n                li.nextElementSibling.querySelector('button').focus();\n            }\n            break;\n        case 'Home':\n            e.preventDefault();\n            themePopup.querySelector('li:first-child button').focus();\n            break;\n        case 'End':\n            e.preventDefault();\n            themePopup.querySelector('li:last-child button').focus();\n            break;\n        }\n    });\n})();\n\n(function sidebar() {\n    const sidebar = document.getElementById('mdbook-sidebar');\n    const sidebarLinks = document.querySelectorAll('#mdbook-sidebar a');\n    const sidebarToggleButton = document.getElementById('mdbook-sidebar-toggle');\n    const sidebarResizeHandle = document.getElementById('mdbook-sidebar-resize-handle');\n    const sidebarCheckbox = document.getElementById('mdbook-sidebar-toggle-anchor');\n    let firstContact = null;\n\n\n    /* Because we cannot change the `display` using only CSS after/before the transition, we\n       need JS to do it. We change the display to prevent the browsers search to find text inside\n       the collapsed sidebar. */\n    if (!document.documentElement.classList.contains('sidebar-visible')) {\n        sidebar.style.display = 'none';\n    }\n    sidebar.addEventListener('transitionend', () => {\n        /* We only change the display to \"none\" if we're collapsing the sidebar. */\n        if (!sidebarCheckbox.checked) {\n            sidebar.style.display = 'none';\n        }\n    });\n    sidebarToggleButton.addEventListener('click', () => {\n        /* To allow the sidebar expansion animation, we first need to put back the display. */\n        if (!sidebarCheckbox.checked) {\n            sidebar.style.display = '';\n            // Workaround for Safari skipping the animation when changing\n            // `display` and a transform in the same event loop. This forces a\n            // reflow after updating the display.\n            sidebar.offsetHeight;\n        }\n    });\n\n    function showSidebar() {\n        document.documentElement.classList.add('sidebar-visible');\n        Array.from(sidebarLinks).forEach(function(link) {\n            link.setAttribute('tabIndex', 0);\n        });\n        sidebarToggleButton.setAttribute('aria-expanded', true);\n        sidebar.setAttribute('aria-hidden', false);\n        try {\n            localStorage.setItem('mdbook-sidebar', 'visible');\n        } catch {\n            // Ignore error.\n        }\n    }\n\n    function hideSidebar() {\n        document.documentElement.classList.remove('sidebar-visible');\n        Array.from(sidebarLinks).forEach(function(link) {\n            link.setAttribute('tabIndex', -1);\n        });\n        sidebarToggleButton.setAttribute('aria-expanded', false);\n        sidebar.setAttribute('aria-hidden', true);\n        try {\n            localStorage.setItem('mdbook-sidebar', 'hidden');\n        } catch {\n            // Ignore error.\n        }\n    }\n\n    // Toggle sidebar\n    sidebarCheckbox.addEventListener('change', function sidebarToggle() {\n        if (sidebarCheckbox.checked) {\n            const current_width = parseInt(\n                document.documentElement.style.getPropertyValue('--sidebar-target-width'), 10);\n            if (current_width < 150) {\n                document.documentElement.style.setProperty('--sidebar-target-width', '150px');\n            }\n            showSidebar();\n        } else {\n            hideSidebar();\n        }\n    });\n\n    sidebarResizeHandle.addEventListener('mousedown', initResize, false);\n\n    function initResize() {\n        window.addEventListener('mousemove', resize, false);\n        window.addEventListener('mouseup', stopResize, false);\n        document.documentElement.classList.add('sidebar-resizing');\n    }\n    function resize(e) {\n        let pos = e.clientX - sidebar.offsetLeft;\n        if (pos < 20) {\n            hideSidebar();\n        } else {\n            if (!document.documentElement.classList.contains('sidebar-visible')) {\n                showSidebar();\n            }\n            pos = Math.min(pos, window.innerWidth - 100);\n            document.documentElement.style.setProperty('--sidebar-target-width', pos + 'px');\n        }\n    }\n    //on mouseup remove windows functions mousemove & mouseup\n    function stopResize() {\n        document.documentElement.classList.remove('sidebar-resizing');\n        window.removeEventListener('mousemove', resize, false);\n        window.removeEventListener('mouseup', stopResize, false);\n    }\n\n    document.addEventListener('touchstart', function(e) {\n        firstContact = {\n            x: e.touches[0].clientX,\n            time: Date.now(),\n        };\n    }, { passive: true });\n\n    document.addEventListener('touchmove', function(e) {\n        if (!firstContact) {\n            return;\n        }\n\n        const curX = e.touches[0].clientX;\n        const xDiff = curX - firstContact.x,\n            tDiff = Date.now() - firstContact.time;\n\n        if (tDiff < 250 && Math.abs(xDiff) >= 150) {\n            if (xDiff >= 0 && firstContact.x < Math.min(document.body.clientWidth * 0.25, 300)) {\n                showSidebar();\n            } else if (xDiff < 0 && curX < 300) {\n                hideSidebar();\n            }\n\n            firstContact = null;\n        }\n    }, { passive: true });\n})();\n\n(function chapterNavigation() {\n    document.addEventListener('keydown', function(e) {\n        if (e.altKey || e.ctrlKey || e.metaKey) {\n            return;\n        }\n        if (window.search && window.search.hasFocus()) {\n            return;\n        }\n        const html = document.querySelector('html');\n\n        function next() {\n            const nextButton = document.querySelector('.nav-chapters.next');\n            if (nextButton) {\n                window.location.href = nextButton.href;\n            }\n        }\n        function prev() {\n            const previousButton = document.querySelector('.nav-chapters.previous');\n            if (previousButton) {\n                window.location.href = previousButton.href;\n            }\n        }\n        function showHelp() {\n            const container = document.getElementById('mdbook-help-container');\n            const overlay = document.getElementById('mdbook-help-popup');\n            container.style.display = 'flex';\n\n            // Clicking outside the popup will dismiss it.\n            const mouseHandler = event => {\n                if (overlay.contains(event.target)) {\n                    return;\n                }\n                if (event.button !== 0) {\n                    return;\n                }\n                event.preventDefault();\n                event.stopPropagation();\n                document.removeEventListener('mousedown', mouseHandler);\n                hideHelp();\n            };\n\n            // Pressing esc will dismiss the popup.\n            const escapeKeyHandler = event => {\n                if (event.key === 'Escape') {\n                    event.preventDefault();\n                    event.stopPropagation();\n                    document.removeEventListener('keydown', escapeKeyHandler, true);\n                    hideHelp();\n                }\n            };\n            document.addEventListener('keydown', escapeKeyHandler, true);\n            document.getElementById('mdbook-help-container')\n                .addEventListener('mousedown', mouseHandler);\n        }\n        function hideHelp() {\n            document.getElementById('mdbook-help-container').style.display = 'none';\n        }\n\n        // Usually needs the Shift key to be pressed\n        switch (e.key) {\n        case '?':\n            e.preventDefault();\n            showHelp();\n            break;\n        }\n\n        // Rest of the keys are only active when the Shift key is not pressed\n        if (e.shiftKey) {\n            return;\n        }\n\n        switch (e.key) {\n        case 'ArrowRight':\n            e.preventDefault();\n            if (html.dir === 'rtl') {\n                prev();\n            } else {\n                next();\n            }\n            break;\n        case 'ArrowLeft':\n            e.preventDefault();\n            if (html.dir === 'rtl') {\n                next();\n            } else {\n                prev();\n            }\n            break;\n        }\n    });\n})();\n\n(function clipboard() {\n    const clipButtons = document.querySelectorAll('.clip-button');\n\n    function hideTooltip(elem) {\n        elem.firstChild.innerText = '';\n        elem.className = 'clip-button';\n    }\n\n    function showTooltip(elem, msg) {\n        elem.firstChild.innerText = msg;\n        elem.className = 'clip-button tooltipped';\n    }\n\n    const clipboardSnippets = new ClipboardJS('.clip-button', {\n        text: function(trigger) {\n            hideTooltip(trigger);\n            const playground = trigger.closest('pre');\n            return playground_text(playground, false);\n        },\n    });\n\n    Array.from(clipButtons).forEach(function(clipButton) {\n        clipButton.addEventListener('mouseout', function(e) {\n            hideTooltip(e.currentTarget);\n        });\n    });\n\n    clipboardSnippets.on('success', function(e) {\n        e.clearSelection();\n        showTooltip(e.trigger, 'Copied!');\n    });\n\n    clipboardSnippets.on('error', function(e) {\n        showTooltip(e.trigger, 'Clipboard error!');\n    });\n})();\n\n(function scrollToTop() {\n    const menuTitle = document.querySelector('.menu-title');\n\n    menuTitle.addEventListener('click', function() {\n        document.scrollingElement.scrollTo({ top: 0, behavior: 'smooth' });\n    });\n})();\n\n(function controllMenu() {\n    const menu = document.getElementById('mdbook-menu-bar');\n\n    (function controllPosition() {\n        let scrollTop = document.scrollingElement.scrollTop;\n        let prevScrollTop = scrollTop;\n        const minMenuY = -menu.clientHeight - 50;\n        // When the script loads, the page can be at any scroll (e.g. if you refresh it).\n        menu.style.top = scrollTop + 'px';\n        // Same as parseInt(menu.style.top.slice(0, -2), but faster\n        let topCache = menu.style.top.slice(0, -2);\n        menu.classList.remove('sticky');\n        let stickyCache = false; // Same as menu.classList.contains('sticky'), but faster\n        document.addEventListener('scroll', function() {\n            scrollTop = Math.max(document.scrollingElement.scrollTop, 0);\n            // `null` means that it doesn't need to be updated\n            let nextSticky = null;\n            let nextTop = null;\n            const scrollDown = scrollTop > prevScrollTop;\n            const menuPosAbsoluteY = topCache - scrollTop;\n            if (scrollDown) {\n                nextSticky = false;\n                if (menuPosAbsoluteY > 0) {\n                    nextTop = prevScrollTop;\n                }\n            } else {\n                if (menuPosAbsoluteY > 0) {\n                    nextSticky = true;\n                } else if (menuPosAbsoluteY < minMenuY) {\n                    nextTop = prevScrollTop + minMenuY;\n                }\n            }\n            if (nextSticky === true && stickyCache === false) {\n                menu.classList.add('sticky');\n                stickyCache = true;\n            } else if (nextSticky === false && stickyCache === true) {\n                menu.classList.remove('sticky');\n                stickyCache = false;\n            }\n            if (nextTop !== null) {\n                menu.style.top = nextTop + 'px';\n                topCache = nextTop;\n            }\n            prevScrollTop = scrollTop;\n        }, { passive: true });\n    })();\n    (function controllBorder() {\n        function updateBorder() {\n            if (menu.offsetTop === 0) {\n                menu.classList.remove('bordered');\n            } else {\n                menu.classList.add('bordered');\n            }\n        }\n        updateBorder();\n        document.addEventListener('scroll', updateBorder, { passive: true });\n    })();\n})();\n"
  },
  {
    "path": "crates/mdbook-html/front-end/js/highlight.js",
    "content": "/*\n  Highlight.js 10.1.1 (93fd0d73)\n  License: BSD-3-Clause\n  Copyright (c) 2006-2020, Ivan Sagalaev\n*/\nvar hljs=function(){\"use strict\";function e(n){Object.freeze(n);var t=\"function\"==typeof n;return Object.getOwnPropertyNames(n).forEach((function(r){!Object.hasOwnProperty.call(n,r)||null===n[r]||\"object\"!=typeof n[r]&&\"function\"!=typeof n[r]||t&&(\"caller\"===r||\"callee\"===r||\"arguments\"===r)||Object.isFrozen(n[r])||e(n[r])})),n}class n{constructor(e){void 0===e.data&&(e.data={}),this.data=e.data}ignoreMatch(){this.ignore=!0}}function t(e){return e.replace(/&/g,\"&amp;\").replace(/</g,\"&lt;\").replace(/>/g,\"&gt;\").replace(/\"/g,\"&quot;\").replace(/'/g,\"&#x27;\")}function r(e,...n){var t={};for(const n in e)t[n]=e[n];return n.forEach((function(e){for(const n in e)t[n]=e[n]})),t}function a(e){return e.nodeName.toLowerCase()}var i=Object.freeze({__proto__:null,escapeHTML:t,inherit:r,nodeStream:function(e){var n=[];return function e(t,r){for(var i=t.firstChild;i;i=i.nextSibling)3===i.nodeType?r+=i.nodeValue.length:1===i.nodeType&&(n.push({event:\"start\",offset:r,node:i}),r=e(i,r),a(i).match(/br|hr|img|input/)||n.push({event:\"stop\",offset:r,node:i}));return r}(e,0),n},mergeStreams:function(e,n,r){var i=0,s=\"\",o=[];function l(){return e.length&&n.length?e[0].offset!==n[0].offset?e[0].offset<n[0].offset?e:n:\"start\"===n[0].event?e:n:e.length?e:n}function c(e){s+=\"<\"+a(e)+[].map.call(e.attributes,(function(e){return\" \"+e.nodeName+'=\"'+t(e.value)+'\"'})).join(\"\")+\">\"}function u(e){s+=\"</\"+a(e)+\">\"}function d(e){(\"start\"===e.event?c:u)(e.node)}for(;e.length||n.length;){var g=l();if(s+=t(r.substring(i,g[0].offset)),i=g[0].offset,g===e){o.reverse().forEach(u);do{d(g.splice(0,1)[0]),g=l()}while(g===e&&g.length&&g[0].offset===i);o.reverse().forEach(c)}else\"start\"===g[0].event?o.push(g[0].node):o.pop(),d(g.splice(0,1)[0])}return s+t(r.substr(i))}});const s=\"</span>\",o=e=>!!e.kind;class l{constructor(e,n){this.buffer=\"\",this.classPrefix=n.classPrefix,e.walk(this)}addText(e){this.buffer+=t(e)}openNode(e){if(!o(e))return;let n=e.kind;e.sublanguage||(n=`${this.classPrefix}${n}`),this.span(n)}closeNode(e){o(e)&&(this.buffer+=s)}value(){return this.buffer}span(e){this.buffer+=`<span class=\"${e}\">`}}class c{constructor(){this.rootNode={children:[]},this.stack=[this.rootNode]}get top(){return this.stack[this.stack.length-1]}get root(){return this.rootNode}add(e){this.top.children.push(e)}openNode(e){const n={kind:e,children:[]};this.add(n),this.stack.push(n)}closeNode(){if(this.stack.length>1)return this.stack.pop()}closeAllNodes(){for(;this.closeNode(););}toJSON(){return JSON.stringify(this.rootNode,null,4)}walk(e){return this.constructor._walk(e,this.rootNode)}static _walk(e,n){return\"string\"==typeof n?e.addText(n):n.children&&(e.openNode(n),n.children.forEach(n=>this._walk(e,n)),e.closeNode(n)),e}static _collapse(e){\"string\"!=typeof e&&e.children&&(e.children.every(e=>\"string\"==typeof e)?e.children=[e.children.join(\"\")]:e.children.forEach(e=>{c._collapse(e)}))}}class u extends c{constructor(e){super(),this.options=e}addKeyword(e,n){\"\"!==e&&(this.openNode(n),this.addText(e),this.closeNode())}addText(e){\"\"!==e&&this.add(e)}addSublanguage(e,n){const t=e.root;t.kind=n,t.sublanguage=!0,this.add(t)}toHTML(){return new l(this,this.options).value()}finalize(){return!0}}function d(e){return e?\"string\"==typeof e?e:e.source:null}const g=\"(-?)(\\\\b0[xX][a-fA-F0-9]+|(\\\\b\\\\d+(\\\\.\\\\d*)?|\\\\.\\\\d+)([eE][-+]?\\\\d+)?)\",h={begin:\"\\\\\\\\[\\\\s\\\\S]\",relevance:0},f={className:\"string\",begin:\"'\",end:\"'\",illegal:\"\\\\n\",contains:[h]},p={className:\"string\",begin:'\"',end:'\"',illegal:\"\\\\n\",contains:[h]},b={begin:/\\b(a|an|the|are|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such|will|you|your|they|like|more)\\b/},m=function(e,n,t={}){var a=r({className:\"comment\",begin:e,end:n,contains:[]},t);return a.contains.push(b),a.contains.push({className:\"doctag\",begin:\"(?:TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):\",relevance:0}),a},v=m(\"//\",\"$\"),x=m(\"/\\\\*\",\"\\\\*/\"),E=m(\"#\",\"$\");var _=Object.freeze({__proto__:null,IDENT_RE:\"[a-zA-Z]\\\\w*\",UNDERSCORE_IDENT_RE:\"[a-zA-Z_]\\\\w*\",NUMBER_RE:\"\\\\b\\\\d+(\\\\.\\\\d+)?\",C_NUMBER_RE:g,BINARY_NUMBER_RE:\"\\\\b(0b[01]+)\",RE_STARTERS_RE:\"!|!=|!==|%|%=|&|&&|&=|\\\\*|\\\\*=|\\\\+|\\\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\\\?|\\\\[|\\\\{|\\\\(|\\\\^|\\\\^=|\\\\||\\\\|=|\\\\|\\\\||~\",SHEBANG:(e={})=>{const n=/^#![ ]*\\//;return e.binary&&(e.begin=function(...e){return e.map(e=>d(e)).join(\"\")}(n,/.*\\b/,e.binary,/\\b.*/)),r({className:\"meta\",begin:n,end:/$/,relevance:0,\"on:begin\":(e,n)=>{0!==e.index&&n.ignoreMatch()}},e)},BACKSLASH_ESCAPE:h,APOS_STRING_MODE:f,QUOTE_STRING_MODE:p,PHRASAL_WORDS_MODE:b,COMMENT:m,C_LINE_COMMENT_MODE:v,C_BLOCK_COMMENT_MODE:x,HASH_COMMENT_MODE:E,NUMBER_MODE:{className:\"number\",begin:\"\\\\b\\\\d+(\\\\.\\\\d+)?\",relevance:0},C_NUMBER_MODE:{className:\"number\",begin:g,relevance:0},BINARY_NUMBER_MODE:{className:\"number\",begin:\"\\\\b(0b[01]+)\",relevance:0},CSS_NUMBER_MODE:{className:\"number\",begin:\"\\\\b\\\\d+(\\\\.\\\\d+)?(%|em|ex|ch|rem|vw|vh|vmin|vmax|cm|mm|in|pt|pc|px|deg|grad|rad|turn|s|ms|Hz|kHz|dpi|dpcm|dppx)?\",relevance:0},REGEXP_MODE:{begin:/(?=\\/[^/\\n]*\\/)/,contains:[{className:\"regexp\",begin:/\\//,end:/\\/[gimuy]*/,illegal:/\\n/,contains:[h,{begin:/\\[/,end:/\\]/,relevance:0,contains:[h]}]}]},TITLE_MODE:{className:\"title\",begin:\"[a-zA-Z]\\\\w*\",relevance:0},UNDERSCORE_TITLE_MODE:{className:\"title\",begin:\"[a-zA-Z_]\\\\w*\",relevance:0},METHOD_GUARD:{begin:\"\\\\.\\\\s*[a-zA-Z_]\\\\w*\",relevance:0},END_SAME_AS_BEGIN:function(e){return Object.assign(e,{\"on:begin\":(e,n)=>{n.data._beginMatch=e[1]},\"on:end\":(e,n)=>{n.data._beginMatch!==e[1]&&n.ignoreMatch()}})}}),N=\"of and for in not or if then\".split(\" \");function w(e,n){return n?+n:function(e){return N.includes(e.toLowerCase())}(e)?0:1}const R=t,y=r,{nodeStream:k,mergeStreams:O}=i,M=Symbol(\"nomatch\");return function(t){var a=[],i={},s={},o=[],l=!0,c=/(^(<[^>]+>|\\t|)+|\\n)/gm,g=\"Could not find the language '{}', did you forget to load/include a language module?\";const h={disableAutodetect:!0,name:\"Plain text\",contains:[]};var f={noHighlightRe:/^(no-?highlight)$/i,languageDetectRe:/\\blang(?:uage)?-([\\w-]+)\\b/i,classPrefix:\"hljs-\",tabReplace:null,useBR:!1,languages:null,__emitter:u};function p(e){return f.noHighlightRe.test(e)}function b(e,n,t,r){var a={code:n,language:e};S(\"before:highlight\",a);var i=a.result?a.result:m(a.language,a.code,t,r);return i.code=a.code,S(\"after:highlight\",i),i}function m(e,t,a,s){var o=t;function c(e,n){var t=E.case_insensitive?n[0].toLowerCase():n[0];return Object.prototype.hasOwnProperty.call(e.keywords,t)&&e.keywords[t]}function u(){null!=y.subLanguage?function(){if(\"\"!==A){var e=null;if(\"string\"==typeof y.subLanguage){if(!i[y.subLanguage])return void O.addText(A);e=m(y.subLanguage,A,!0,k[y.subLanguage]),k[y.subLanguage]=e.top}else e=v(A,y.subLanguage.length?y.subLanguage:null);y.relevance>0&&(I+=e.relevance),O.addSublanguage(e.emitter,e.language)}}():function(){if(!y.keywords)return void O.addText(A);let e=0;y.keywordPatternRe.lastIndex=0;let n=y.keywordPatternRe.exec(A),t=\"\";for(;n;){t+=A.substring(e,n.index);const r=c(y,n);if(r){const[e,a]=r;O.addText(t),t=\"\",I+=a,O.addKeyword(n[0],e)}else t+=n[0];e=y.keywordPatternRe.lastIndex,n=y.keywordPatternRe.exec(A)}t+=A.substr(e),O.addText(t)}(),A=\"\"}function h(e){return e.className&&O.openNode(e.className),y=Object.create(e,{parent:{value:y}})}function p(e){return 0===y.matcher.regexIndex?(A+=e[0],1):(L=!0,0)}var b={};function x(t,r){var i=r&&r[0];if(A+=t,null==i)return u(),0;if(\"begin\"===b.type&&\"end\"===r.type&&b.index===r.index&&\"\"===i){if(A+=o.slice(r.index,r.index+1),!l){const n=Error(\"0 width match regex\");throw n.languageName=e,n.badRule=b.rule,n}return 1}if(b=r,\"begin\"===r.type)return function(e){var t=e[0],r=e.rule;const a=new n(r),i=[r.__beforeBegin,r[\"on:begin\"]];for(const n of i)if(n&&(n(e,a),a.ignore))return p(t);return r&&r.endSameAsBegin&&(r.endRe=RegExp(t.replace(/[-/\\\\^$*+?.()|[\\]{}]/g,\"\\\\$&\"),\"m\")),r.skip?A+=t:(r.excludeBegin&&(A+=t),u(),r.returnBegin||r.excludeBegin||(A=t)),h(r),r.returnBegin?0:t.length}(r);if(\"illegal\"===r.type&&!a){const e=Error('Illegal lexeme \"'+i+'\" for mode \"'+(y.className||\"<unnamed>\")+'\"');throw e.mode=y,e}if(\"end\"===r.type){var s=function(e){var t=e[0],r=o.substr(e.index),a=function e(t,r,a){let i=function(e,n){var t=e&&e.exec(n);return t&&0===t.index}(t.endRe,a);if(i){if(t[\"on:end\"]){const e=new n(t);t[\"on:end\"](r,e),e.ignore&&(i=!1)}if(i){for(;t.endsParent&&t.parent;)t=t.parent;return t}}if(t.endsWithParent)return e(t.parent,r,a)}(y,e,r);if(!a)return M;var i=y;i.skip?A+=t:(i.returnEnd||i.excludeEnd||(A+=t),u(),i.excludeEnd&&(A=t));do{y.className&&O.closeNode(),y.skip||y.subLanguage||(I+=y.relevance),y=y.parent}while(y!==a.parent);return a.starts&&(a.endSameAsBegin&&(a.starts.endRe=a.endRe),h(a.starts)),i.returnEnd?0:t.length}(r);if(s!==M)return s}if(\"illegal\"===r.type&&\"\"===i)return 1;if(B>1e5&&B>3*r.index)throw Error(\"potential infinite loop, way more iterations than matches\");return A+=i,i.length}var E=T(e);if(!E)throw console.error(g.replace(\"{}\",e)),Error('Unknown language: \"'+e+'\"');var _=function(e){function n(n,t){return RegExp(d(n),\"m\"+(e.case_insensitive?\"i\":\"\")+(t?\"g\":\"\"))}class t{constructor(){this.matchIndexes={},this.regexes=[],this.matchAt=1,this.position=0}addRule(e,n){n.position=this.position++,this.matchIndexes[this.matchAt]=n,this.regexes.push([n,e]),this.matchAt+=function(e){return RegExp(e.toString()+\"|\").exec(\"\").length-1}(e)+1}compile(){0===this.regexes.length&&(this.exec=()=>null);const e=this.regexes.map(e=>e[1]);this.matcherRe=n(function(e,n=\"|\"){for(var t=/\\[(?:[^\\\\\\]]|\\\\.)*\\]|\\(\\??|\\\\([1-9][0-9]*)|\\\\./,r=0,a=\"\",i=0;i<e.length;i++){var s=r+=1,o=d(e[i]);for(i>0&&(a+=n),a+=\"(\";o.length>0;){var l=t.exec(o);if(null==l){a+=o;break}a+=o.substring(0,l.index),o=o.substring(l.index+l[0].length),\"\\\\\"===l[0][0]&&l[1]?a+=\"\\\\\"+(+l[1]+s):(a+=l[0],\"(\"===l[0]&&r++)}a+=\")\"}return a}(e),!0),this.lastIndex=0}exec(e){this.matcherRe.lastIndex=this.lastIndex;const n=this.matcherRe.exec(e);if(!n)return null;const t=n.findIndex((e,n)=>n>0&&void 0!==e),r=this.matchIndexes[t];return n.splice(0,t),Object.assign(n,r)}}class a{constructor(){this.rules=[],this.multiRegexes=[],this.count=0,this.lastIndex=0,this.regexIndex=0}getMatcher(e){if(this.multiRegexes[e])return this.multiRegexes[e];const n=new t;return this.rules.slice(e).forEach(([e,t])=>n.addRule(e,t)),n.compile(),this.multiRegexes[e]=n,n}considerAll(){this.regexIndex=0}addRule(e,n){this.rules.push([e,n]),\"begin\"===n.type&&this.count++}exec(e){const n=this.getMatcher(this.regexIndex);n.lastIndex=this.lastIndex;const t=n.exec(e);return t&&(this.regexIndex+=t.position+1,this.regexIndex===this.count&&(this.regexIndex=0)),t}}function i(e,n){const t=e.input[e.index-1],r=e.input[e.index+e[0].length];\".\"!==t&&\".\"!==r||n.ignoreMatch()}if(e.contains&&e.contains.includes(\"self\"))throw Error(\"ERR: contains `self` is not supported at the top-level of a language.  See documentation.\");return function t(s,o){const l=s;if(s.compiled)return l;s.compiled=!0,s.__beforeBegin=null,s.keywords=s.keywords||s.beginKeywords;let c=null;if(\"object\"==typeof s.keywords&&(c=s.keywords.$pattern,delete s.keywords.$pattern),s.keywords&&(s.keywords=function(e,n){var t={};return\"string\"==typeof e?r(\"keyword\",e):Object.keys(e).forEach((function(n){r(n,e[n])})),t;function r(e,r){n&&(r=r.toLowerCase()),r.split(\" \").forEach((function(n){var r=n.split(\"|\");t[r[0]]=[e,w(r[0],r[1])]}))}}(s.keywords,e.case_insensitive)),s.lexemes&&c)throw Error(\"ERR: Prefer `keywords.$pattern` to `mode.lexemes`, BOTH are not allowed. (see mode reference) \");return l.keywordPatternRe=n(s.lexemes||c||/\\w+/,!0),o&&(s.beginKeywords&&(s.begin=\"\\\\b(\"+s.beginKeywords.split(\" \").join(\"|\")+\")(?=\\\\b|\\\\s)\",s.__beforeBegin=i),s.begin||(s.begin=/\\B|\\b/),l.beginRe=n(s.begin),s.endSameAsBegin&&(s.end=s.begin),s.end||s.endsWithParent||(s.end=/\\B|\\b/),s.end&&(l.endRe=n(s.end)),l.terminator_end=d(s.end)||\"\",s.endsWithParent&&o.terminator_end&&(l.terminator_end+=(s.end?\"|\":\"\")+o.terminator_end)),s.illegal&&(l.illegalRe=n(s.illegal)),void 0===s.relevance&&(s.relevance=1),s.contains||(s.contains=[]),s.contains=[].concat(...s.contains.map((function(e){return function(e){return e.variants&&!e.cached_variants&&(e.cached_variants=e.variants.map((function(n){return r(e,{variants:null},n)}))),e.cached_variants?e.cached_variants:function e(n){return!!n&&(n.endsWithParent||e(n.starts))}(e)?r(e,{starts:e.starts?r(e.starts):null}):Object.isFrozen(e)?r(e):e}(\"self\"===e?s:e)}))),s.contains.forEach((function(e){t(e,l)})),s.starts&&t(s.starts,o),l.matcher=function(e){const n=new a;return e.contains.forEach(e=>n.addRule(e.begin,{rule:e,type:\"begin\"})),e.terminator_end&&n.addRule(e.terminator_end,{type:\"end\"}),e.illegal&&n.addRule(e.illegal,{type:\"illegal\"}),n}(l),l}(e)}(E),N=\"\",y=s||_,k={},O=new f.__emitter(f);!function(){for(var e=[],n=y;n!==E;n=n.parent)n.className&&e.unshift(n.className);e.forEach(e=>O.openNode(e))}();var A=\"\",I=0,S=0,B=0,L=!1;try{for(y.matcher.considerAll();;){B++,L?L=!1:(y.matcher.lastIndex=S,y.matcher.considerAll());const e=y.matcher.exec(o);if(!e)break;const n=x(o.substring(S,e.index),e);S=e.index+n}return x(o.substr(S)),O.closeAllNodes(),O.finalize(),N=O.toHTML(),{relevance:I,value:N,language:e,illegal:!1,emitter:O,top:y}}catch(n){if(n.message&&n.message.includes(\"Illegal\"))return{illegal:!0,illegalBy:{msg:n.message,context:o.slice(S-100,S+100),mode:n.mode},sofar:N,relevance:0,value:R(o),emitter:O};if(l)return{illegal:!1,relevance:0,value:R(o),emitter:O,language:e,top:y,errorRaised:n};throw n}}function v(e,n){n=n||f.languages||Object.keys(i);var t=function(e){const n={relevance:0,emitter:new f.__emitter(f),value:R(e),illegal:!1,top:h};return n.emitter.addText(e),n}(e),r=t;return n.filter(T).filter(I).forEach((function(n){var a=m(n,e,!1);a.language=n,a.relevance>r.relevance&&(r=a),a.relevance>t.relevance&&(r=t,t=a)})),r.language&&(t.second_best=r),t}function x(e){return f.tabReplace||f.useBR?e.replace(c,e=>\"\\n\"===e?f.useBR?\"<br>\":e:f.tabReplace?e.replace(/\\t/g,f.tabReplace):e):e}function E(e){let n=null;const t=function(e){var n=e.className+\" \";n+=e.parentNode?e.parentNode.className:\"\";const t=f.languageDetectRe.exec(n);if(t){var r=T(t[1]);return r||(console.warn(g.replace(\"{}\",t[1])),console.warn(\"Falling back to no-highlight mode for this block.\",e)),r?t[1]:\"no-highlight\"}return n.split(/\\s+/).find(e=>p(e)||T(e))}(e);if(p(t))return;S(\"before:highlightBlock\",{block:e,language:t}),f.useBR?(n=document.createElement(\"div\")).innerHTML=e.innerHTML.replace(/\\n/g,\"\").replace(/<br[ /]*>/g,\"\\n\"):n=e;const r=n.textContent,a=t?b(t,r,!0):v(r),i=k(n);if(i.length){const e=document.createElement(\"div\");e.innerHTML=a.value,a.value=O(i,k(e),r)}a.value=x(a.value),S(\"after:highlightBlock\",{block:e,result:a}),e.innerHTML=a.value,e.className=function(e,n,t){var r=n?s[n]:t,a=[e.trim()];return e.match(/\\bhljs\\b/)||a.push(\"hljs\"),e.includes(r)||a.push(r),a.join(\" \").trim()}(e.className,t,a.language),e.result={language:a.language,re:a.relevance,relavance:a.relevance},a.second_best&&(e.second_best={language:a.second_best.language,re:a.second_best.relevance,relavance:a.second_best.relevance})}const N=()=>{if(!N.called){N.called=!0;var e=document.querySelectorAll(\"pre code\");a.forEach.call(e,E)}};function T(e){return e=(e||\"\").toLowerCase(),i[e]||i[s[e]]}function A(e,{languageName:n}){\"string\"==typeof e&&(e=[e]),e.forEach(e=>{s[e]=n})}function I(e){var n=T(e);return n&&!n.disableAutodetect}function S(e,n){var t=e;o.forEach((function(e){e[t]&&e[t](n)}))}Object.assign(t,{highlight:b,highlightAuto:v,fixMarkup:x,highlightBlock:E,configure:function(e){f=y(f,e)},initHighlighting:N,initHighlightingOnLoad:function(){window.addEventListener(\"DOMContentLoaded\",N,!1)},registerLanguage:function(e,n){var r=null;try{r=n(t)}catch(n){if(console.error(\"Language definition for '{}' could not be registered.\".replace(\"{}\",e)),!l)throw n;console.error(n),r=h}r.name||(r.name=e),i[e]=r,r.rawDefinition=n.bind(null,t),r.aliases&&A(r.aliases,{languageName:e})},listLanguages:function(){return Object.keys(i)},getLanguage:T,registerAliases:A,requireLanguage:function(e){var n=T(e);if(n)return n;throw Error(\"The '{}' language is required, but not loaded.\".replace(\"{}\",e))},autoDetection:I,inherit:y,addPlugin:function(e){o.push(e)}}),t.debugMode=function(){l=!1},t.safeMode=function(){l=!0},t.versionString=\"10.1.1\";for(const n in _)\"object\"==typeof _[n]&&e(_[n]);return Object.assign(t,_),t}({})}();\"object\"==typeof exports&&\"undefined\"!=typeof module&&(module.exports=hljs);\nhljs.registerLanguage(\"apache\",function(){\"use strict\";return function(e){var n={className:\"number\",begin:\"\\\\d{1,3}\\\\.\\\\d{1,3}\\\\.\\\\d{1,3}\\\\.\\\\d{1,3}(:\\\\d{1,5})?\"};return{name:\"Apache config\",aliases:[\"apacheconf\"],case_insensitive:!0,contains:[e.HASH_COMMENT_MODE,{className:\"section\",begin:\"</?\",end:\">\",contains:[n,{className:\"number\",begin:\":\\\\d{1,5}\"},e.inherit(e.QUOTE_STRING_MODE,{relevance:0})]},{className:\"attribute\",begin:/\\w+/,relevance:0,keywords:{nomarkup:\"order deny allow setenv rewriterule rewriteengine rewritecond documentroot sethandler errordocument loadmodule options header listen serverroot servername\"},starts:{end:/$/,relevance:0,keywords:{literal:\"on off all deny allow\"},contains:[{className:\"meta\",begin:\"\\\\s\\\\[\",end:\"\\\\]$\"},{className:\"variable\",begin:\"[\\\\$%]\\\\{\",end:\"\\\\}\",contains:[\"self\",{className:\"number\",begin:\"[\\\\$%]\\\\d+\"}]},n,{className:\"number\",begin:\"\\\\d+\"},e.QUOTE_STRING_MODE]}}],illegal:/\\S/}}}());\nhljs.registerLanguage(\"bash\",function(){\"use strict\";return function(e){const s={};Object.assign(s,{className:\"variable\",variants:[{begin:/\\$[\\w\\d#@][\\w\\d_]*/},{begin:/\\$\\{/,end:/\\}/,contains:[{begin:/:-/,contains:[s]}]}]});const t={className:\"subst\",begin:/\\$\\(/,end:/\\)/,contains:[e.BACKSLASH_ESCAPE]},n={className:\"string\",begin:/\"/,end:/\"/,contains:[e.BACKSLASH_ESCAPE,s,t]};t.contains.push(n);const a={begin:/\\$\\(\\(/,end:/\\)\\)/,contains:[{begin:/\\d+#[0-9a-f]+/,className:\"number\"},e.NUMBER_MODE,s]},i=e.SHEBANG({binary:\"(fish|bash|zsh|sh|csh|ksh|tcsh|dash|scsh)\",relevance:10}),c={className:\"function\",begin:/\\w[\\w\\d_]*\\s*\\(\\s*\\)\\s*\\{/,returnBegin:!0,contains:[e.inherit(e.TITLE_MODE,{begin:/\\w[\\w\\d_]*/})],relevance:0};return{name:\"Bash\",aliases:[\"sh\",\"zsh\"],keywords:{$pattern:/\\b-?[a-z\\._]+\\b/,keyword:\"if then else elif fi for while in do done case esac function\",literal:\"true false\",built_in:\"break cd continue eval exec exit export getopts hash pwd readonly return shift test times trap umask unset alias bind builtin caller command declare echo enable help let local logout mapfile printf read readarray source type typeset ulimit unalias set shopt autoload bg bindkey bye cap chdir clone comparguments compcall compctl compdescribe compfiles compgroups compquote comptags comptry compvalues dirs disable disown echotc echoti emulate fc fg float functions getcap getln history integer jobs kill limit log noglob popd print pushd pushln rehash sched setcap setopt stat suspend ttyctl unfunction unhash unlimit unsetopt vared wait whence where which zcompile zformat zftp zle zmodload zparseopts zprof zpty zregexparse zsocket zstyle ztcp\",_:\"-ne -eq -lt -gt -f -d -e -s -l -a\"},contains:[i,e.SHEBANG(),c,a,e.HASH_COMMENT_MODE,n,{className:\"\",begin:/\\\\\"/},{className:\"string\",begin:/'/,end:/'/},s]}}}());\nhljs.registerLanguage(\"c-like\",function(){\"use strict\";return function(e){function t(e){return\"(?:\"+e+\")?\"}var n=\"(decltype\\\\(auto\\\\)|\"+t(\"[a-zA-Z_]\\\\w*::\")+\"[a-zA-Z_]\\\\w*\"+t(\"<.*?>\")+\")\",r={className:\"keyword\",begin:\"\\\\b[a-z\\\\d_]*_t\\\\b\"},a={className:\"string\",variants:[{begin:'(u8?|U|L)?\"',end:'\"',illegal:\"\\\\n\",contains:[e.BACKSLASH_ESCAPE]},{begin:\"(u8?|U|L)?'(\\\\\\\\(x[0-9A-Fa-f]{2}|u[0-9A-Fa-f]{4,8}|[0-7]{3}|\\\\S)|.)\",end:\"'\",illegal:\".\"},e.END_SAME_AS_BEGIN({begin:/(?:u8?|U|L)?R\"([^()\\\\ ]{0,16})\\(/,end:/\\)([^()\\\\ ]{0,16})\"/})]},i={className:\"number\",variants:[{begin:\"\\\\b(0b[01']+)\"},{begin:\"(-?)\\\\b([\\\\d']+(\\\\.[\\\\d']*)?|\\\\.[\\\\d']+)(u|U|l|L|ul|UL|f|F|b|B)\"},{begin:\"(-?)(\\\\b0[xX][a-fA-F0-9']+|(\\\\b[\\\\d']+(\\\\.[\\\\d']*)?|\\\\.[\\\\d']+)([eE][-+]?[\\\\d']+)?)\"}],relevance:0},s={className:\"meta\",begin:/#\\s*[a-z]+\\b/,end:/$/,keywords:{\"meta-keyword\":\"if else elif endif define undef warning error line pragma _Pragma ifdef ifndef include\"},contains:[{begin:/\\\\\\n/,relevance:0},e.inherit(a,{className:\"meta-string\"}),{className:\"meta-string\",begin:/<.*?>/,end:/$/,illegal:\"\\\\n\"},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},o={className:\"title\",begin:t(\"[a-zA-Z_]\\\\w*::\")+e.IDENT_RE,relevance:0},c=t(\"[a-zA-Z_]\\\\w*::\")+e.IDENT_RE+\"\\\\s*\\\\(\",l={keyword:\"int float while private char char8_t char16_t char32_t catch import module export virtual operator sizeof dynamic_cast|10 typedef const_cast|10 const for static_cast|10 union namespace unsigned long volatile static protected bool template mutable if public friend do goto auto void enum else break extern using asm case typeid wchar_t short reinterpret_cast|10 default double register explicit signed typename try this switch continue inline delete alignas alignof constexpr consteval constinit decltype concept co_await co_return co_yield requires noexcept static_assert thread_local restrict final override atomic_bool atomic_char atomic_schar atomic_uchar atomic_short atomic_ushort atomic_int atomic_uint atomic_long atomic_ulong atomic_llong atomic_ullong new throw return and and_eq bitand bitor compl not not_eq or or_eq xor xor_eq\",built_in:\"std string wstring cin cout cerr clog stdin stdout stderr stringstream istringstream ostringstream auto_ptr deque list queue stack vector map set pair bitset multiset multimap unordered_set unordered_map unordered_multiset unordered_multimap priority_queue make_pair array shared_ptr abort terminate abs acos asin atan2 atan calloc ceil cosh cos exit exp fabs floor fmod fprintf fputs free frexp fscanf future isalnum isalpha iscntrl isdigit isgraph islower isprint ispunct isspace isupper isxdigit tolower toupper labs ldexp log10 log malloc realloc memchr memcmp memcpy memset modf pow printf putchar puts scanf sinh sin snprintf sprintf sqrt sscanf strcat strchr strcmp strcpy strcspn strlen strncat strncmp strncpy strpbrk strrchr strspn strstr tanh tan vfprintf vprintf vsprintf endl initializer_list unique_ptr _Bool complex _Complex imaginary _Imaginary\",literal:\"true false nullptr NULL\"},d=[r,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,i,a],_={variants:[{begin:/=/,end:/;/},{begin:/\\(/,end:/\\)/},{beginKeywords:\"new throw return else\",end:/;/}],keywords:l,contains:d.concat([{begin:/\\(/,end:/\\)/,keywords:l,contains:d.concat([\"self\"]),relevance:0}]),relevance:0},u={className:\"function\",begin:\"(\"+n+\"[\\\\*&\\\\s]+)+\"+c,returnBegin:!0,end:/[{;=]/,excludeEnd:!0,keywords:l,illegal:/[^\\w\\s\\*&:<>]/,contains:[{begin:\"decltype\\\\(auto\\\\)\",keywords:l,relevance:0},{begin:c,returnBegin:!0,contains:[o],relevance:0},{className:\"params\",begin:/\\(/,end:/\\)/,keywords:l,relevance:0,contains:[e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,a,i,r,{begin:/\\(/,end:/\\)/,keywords:l,relevance:0,contains:[\"self\",e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,a,i,r]}]},r,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,s]};return{aliases:[\"c\",\"cc\",\"h\",\"c++\",\"h++\",\"hpp\",\"hh\",\"hxx\",\"cxx\"],keywords:l,disableAutodetect:!0,illegal:\"</\",contains:[].concat(_,u,d,[s,{begin:\"\\\\b(deque|list|queue|priority_queue|pair|stack|vector|map|set|bitset|multiset|multimap|unordered_map|unordered_set|unordered_multiset|unordered_multimap|array)\\\\s*<\",end:\">\",keywords:l,contains:[\"self\",r]},{begin:e.IDENT_RE+\"::\",keywords:l},{className:\"class\",beginKeywords:\"class struct\",end:/[{;:]/,contains:[{begin:/</,end:/>/,contains:[\"self\"]},e.TITLE_MODE]}]),exports:{preprocessor:s,strings:a,keywords:l}}}}());\nhljs.registerLanguage(\"c\",function(){\"use strict\";return function(e){var n=e.getLanguage(\"c-like\").rawDefinition();return n.name=\"C\",n.aliases=[\"c\",\"h\"],n}}());\nhljs.registerLanguage(\"coffeescript\",function(){\"use strict\";const e=[\"as\",\"in\",\"of\",\"if\",\"for\",\"while\",\"finally\",\"var\",\"new\",\"function\",\"do\",\"return\",\"void\",\"else\",\"break\",\"catch\",\"instanceof\",\"with\",\"throw\",\"case\",\"default\",\"try\",\"switch\",\"continue\",\"typeof\",\"delete\",\"let\",\"yield\",\"const\",\"class\",\"debugger\",\"async\",\"await\",\"static\",\"import\",\"from\",\"export\",\"extends\"],n=[\"true\",\"false\",\"null\",\"undefined\",\"NaN\",\"Infinity\"],a=[].concat([\"setInterval\",\"setTimeout\",\"clearInterval\",\"clearTimeout\",\"require\",\"exports\",\"eval\",\"isFinite\",\"isNaN\",\"parseFloat\",\"parseInt\",\"decodeURI\",\"decodeURIComponent\",\"encodeURI\",\"encodeURIComponent\",\"escape\",\"unescape\"],[\"arguments\",\"this\",\"super\",\"console\",\"window\",\"document\",\"localStorage\",\"module\",\"global\"],[\"Intl\",\"DataView\",\"Number\",\"Math\",\"Date\",\"String\",\"RegExp\",\"Object\",\"Function\",\"Boolean\",\"Error\",\"Symbol\",\"Set\",\"Map\",\"WeakSet\",\"WeakMap\",\"Proxy\",\"Reflect\",\"JSON\",\"Promise\",\"Float64Array\",\"Int16Array\",\"Int32Array\",\"Int8Array\",\"Uint16Array\",\"Uint32Array\",\"Float32Array\",\"Array\",\"Uint8Array\",\"Uint8ClampedArray\",\"ArrayBuffer\"],[\"EvalError\",\"InternalError\",\"RangeError\",\"ReferenceError\",\"SyntaxError\",\"TypeError\",\"URIError\"]);return function(r){var t={keyword:e.concat([\"then\",\"unless\",\"until\",\"loop\",\"by\",\"when\",\"and\",\"or\",\"is\",\"isnt\",\"not\"]).filter((e=>n=>!e.includes(n))([\"var\",\"const\",\"let\",\"function\",\"static\"])).join(\" \"),literal:n.concat([\"yes\",\"no\",\"on\",\"off\"]).join(\" \"),built_in:a.concat([\"npm\",\"print\"]).join(\" \")},i=\"[A-Za-z$_][0-9A-Za-z$_]*\",s={className:\"subst\",begin:/#\\{/,end:/}/,keywords:t},o=[r.BINARY_NUMBER_MODE,r.inherit(r.C_NUMBER_MODE,{starts:{end:\"(\\\\s*/)?\",relevance:0}}),{className:\"string\",variants:[{begin:/'''/,end:/'''/,contains:[r.BACKSLASH_ESCAPE]},{begin:/'/,end:/'/,contains:[r.BACKSLASH_ESCAPE]},{begin:/\"\"\"/,end:/\"\"\"/,contains:[r.BACKSLASH_ESCAPE,s]},{begin:/\"/,end:/\"/,contains:[r.BACKSLASH_ESCAPE,s]}]},{className:\"regexp\",variants:[{begin:\"///\",end:\"///\",contains:[s,r.HASH_COMMENT_MODE]},{begin:\"//[gim]{0,3}(?=\\\\W)\",relevance:0},{begin:/\\/(?![ *]).*?(?![\\\\]).\\/[gim]{0,3}(?=\\W)/}]},{begin:\"@\"+i},{subLanguage:\"javascript\",excludeBegin:!0,excludeEnd:!0,variants:[{begin:\"```\",end:\"```\"},{begin:\"`\",end:\"`\"}]}];s.contains=o;var c=r.inherit(r.TITLE_MODE,{begin:i}),l={className:\"params\",begin:\"\\\\([^\\\\(]\",returnBegin:!0,contains:[{begin:/\\(/,end:/\\)/,keywords:t,contains:[\"self\"].concat(o)}]};return{name:\"CoffeeScript\",aliases:[\"coffee\",\"cson\",\"iced\"],keywords:t,illegal:/\\/\\*/,contains:o.concat([r.COMMENT(\"###\",\"###\"),r.HASH_COMMENT_MODE,{className:\"function\",begin:\"^\\\\s*\"+i+\"\\\\s*=\\\\s*(\\\\(.*\\\\))?\\\\s*\\\\B[-=]>\",end:\"[-=]>\",returnBegin:!0,contains:[c,l]},{begin:/[:\\(,=]\\s*/,relevance:0,contains:[{className:\"function\",begin:\"(\\\\(.*\\\\))?\\\\s*\\\\B[-=]>\",end:\"[-=]>\",returnBegin:!0,contains:[l]}]},{className:\"class\",beginKeywords:\"class\",end:\"$\",illegal:/[:=\"\\[\\]]/,contains:[{beginKeywords:\"extends\",endsWithParent:!0,illegal:/[:=\"\\[\\]]/,contains:[c]},c]},{begin:i+\":\",end:\":\",returnBegin:!0,returnEnd:!0,relevance:0}])}}}());\nhljs.registerLanguage(\"cpp\",function(){\"use strict\";return function(e){var t=e.getLanguage(\"c-like\").rawDefinition();return t.disableAutodetect=!1,t.name=\"C++\",t.aliases=[\"cc\",\"c++\",\"h++\",\"hpp\",\"hh\",\"hxx\",\"cxx\"],t}}());\nhljs.registerLanguage(\"csharp\",function(){\"use strict\";return function(e){var n={keyword:\"abstract as base bool break byte case catch char checked const continue decimal default delegate do double enum event explicit extern finally fixed float for foreach goto if implicit in int interface internal is lock long object operator out override params private protected public readonly ref sbyte sealed short sizeof stackalloc static string struct switch this try typeof uint ulong unchecked unsafe ushort using virtual void volatile while add alias ascending async await by descending dynamic equals from get global group into join let nameof on orderby partial remove select set value var when where yield\",literal:\"null false true\"},i=e.inherit(e.TITLE_MODE,{begin:\"[a-zA-Z](\\\\.?\\\\w)*\"}),a={className:\"number\",variants:[{begin:\"\\\\b(0b[01']+)\"},{begin:\"(-?)\\\\b([\\\\d']+(\\\\.[\\\\d']*)?|\\\\.[\\\\d']+)(u|U|l|L|ul|UL|f|F|b|B)\"},{begin:\"(-?)(\\\\b0[xX][a-fA-F0-9']+|(\\\\b[\\\\d']+(\\\\.[\\\\d']*)?|\\\\.[\\\\d']+)([eE][-+]?[\\\\d']+)?)\"}],relevance:0},s={className:\"string\",begin:'@\"',end:'\"',contains:[{begin:'\"\"'}]},t=e.inherit(s,{illegal:/\\n/}),l={className:\"subst\",begin:\"{\",end:\"}\",keywords:n},r=e.inherit(l,{illegal:/\\n/}),c={className:\"string\",begin:/\\$\"/,end:'\"',illegal:/\\n/,contains:[{begin:\"{{\"},{begin:\"}}\"},e.BACKSLASH_ESCAPE,r]},o={className:\"string\",begin:/\\$@\"/,end:'\"',contains:[{begin:\"{{\"},{begin:\"}}\"},{begin:'\"\"'},l]},g=e.inherit(o,{illegal:/\\n/,contains:[{begin:\"{{\"},{begin:\"}}\"},{begin:'\"\"'},r]});l.contains=[o,c,s,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,a,e.C_BLOCK_COMMENT_MODE],r.contains=[g,c,t,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,a,e.inherit(e.C_BLOCK_COMMENT_MODE,{illegal:/\\n/})];var d={variants:[o,c,s,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE]},E={begin:\"<\",end:\">\",contains:[{beginKeywords:\"in out\"},i]},_=e.IDENT_RE+\"(<\"+e.IDENT_RE+\"(\\\\s*,\\\\s*\"+e.IDENT_RE+\")*>)?(\\\\[\\\\])?\",b={begin:\"@\"+e.IDENT_RE,relevance:0};return{name:\"C#\",aliases:[\"cs\",\"c#\"],keywords:n,illegal:/::/,contains:[e.COMMENT(\"///\",\"$\",{returnBegin:!0,contains:[{className:\"doctag\",variants:[{begin:\"///\",relevance:0},{begin:\"\\x3c!--|--\\x3e\"},{begin:\"</?\",end:\">\"}]}]}),e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,{className:\"meta\",begin:\"#\",end:\"$\",keywords:{\"meta-keyword\":\"if else elif endif define undef warning error line region endregion pragma checksum\"}},d,a,{beginKeywords:\"class interface\",end:/[{;=]/,illegal:/[^\\s:,]/,contains:[{beginKeywords:\"where class\"},i,E,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{beginKeywords:\"namespace\",end:/[{;=]/,illegal:/[^\\s:]/,contains:[i,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{className:\"meta\",begin:\"^\\\\s*\\\\[\",excludeBegin:!0,end:\"\\\\]\",excludeEnd:!0,contains:[{className:\"meta-string\",begin:/\"/,end:/\"/}]},{beginKeywords:\"new return throw await else\",relevance:0},{className:\"function\",begin:\"(\"+_+\"\\\\s+)+\"+e.IDENT_RE+\"\\\\s*(\\\\<.+\\\\>)?\\\\s*\\\\(\",returnBegin:!0,end:/\\s*[{;=]/,excludeEnd:!0,keywords:n,contains:[{begin:e.IDENT_RE+\"\\\\s*(\\\\<.+\\\\>)?\\\\s*\\\\(\",returnBegin:!0,contains:[e.TITLE_MODE,E],relevance:0},{className:\"params\",begin:/\\(/,end:/\\)/,excludeBegin:!0,excludeEnd:!0,keywords:n,relevance:0,contains:[d,a,e.C_BLOCK_COMMENT_MODE]},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},b]}}}());\nhljs.registerLanguage(\"css\",function(){\"use strict\";return function(e){var n={begin:/(?:[A-Z\\_\\.\\-]+|--[a-zA-Z0-9_-]+)\\s*:/,returnBegin:!0,end:\";\",endsWithParent:!0,contains:[{className:\"attribute\",begin:/\\S/,end:\":\",excludeEnd:!0,starts:{endsWithParent:!0,excludeEnd:!0,contains:[{begin:/[\\w-]+\\(/,returnBegin:!0,contains:[{className:\"built_in\",begin:/[\\w-]+/},{begin:/\\(/,end:/\\)/,contains:[e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,e.CSS_NUMBER_MODE]}]},e.CSS_NUMBER_MODE,e.QUOTE_STRING_MODE,e.APOS_STRING_MODE,e.C_BLOCK_COMMENT_MODE,{className:\"number\",begin:\"#[0-9A-Fa-f]+\"},{className:\"meta\",begin:\"!important\"}]}}]};return{name:\"CSS\",case_insensitive:!0,illegal:/[=\\/|'\\$]/,contains:[e.C_BLOCK_COMMENT_MODE,{className:\"selector-id\",begin:/#[A-Za-z0-9_-]+/},{className:\"selector-class\",begin:/\\.[A-Za-z0-9_-]+/},{className:\"selector-attr\",begin:/\\[/,end:/\\]/,illegal:\"$\",contains:[e.APOS_STRING_MODE,e.QUOTE_STRING_MODE]},{className:\"selector-pseudo\",begin:/:(:)?[a-zA-Z0-9\\_\\-\\+\\(\\)\"'.]+/},{begin:\"@(page|font-face)\",lexemes:\"@[a-z-]+\",keywords:\"@page @font-face\"},{begin:\"@\",end:\"[{;]\",illegal:/:/,returnBegin:!0,contains:[{className:\"keyword\",begin:/@\\-?\\w[\\w]*(\\-\\w+)*/},{begin:/\\s/,endsWithParent:!0,excludeEnd:!0,relevance:0,keywords:\"and or not only\",contains:[{begin:/[a-z-]+:/,className:\"attribute\"},e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,e.CSS_NUMBER_MODE]}]},{className:\"selector-tag\",begin:\"[a-zA-Z-][a-zA-Z0-9_-]*\",relevance:0},{begin:\"{\",end:\"}\",illegal:/\\S/,contains:[e.C_BLOCK_COMMENT_MODE,n]}]}}}());\nhljs.registerLanguage(\"diff\",function(){\"use strict\";return function(e){return{name:\"Diff\",aliases:[\"patch\"],contains:[{className:\"meta\",relevance:10,variants:[{begin:/^@@ +\\-\\d+,\\d+ +\\+\\d+,\\d+ +@@$/},{begin:/^\\*\\*\\* +\\d+,\\d+ +\\*\\*\\*\\*$/},{begin:/^\\-\\-\\- +\\d+,\\d+ +\\-\\-\\-\\-$/}]},{className:\"comment\",variants:[{begin:/Index: /,end:/$/},{begin:/={3,}/,end:/$/},{begin:/^\\-{3}/,end:/$/},{begin:/^\\*{3} /,end:/$/},{begin:/^\\+{3}/,end:/$/},{begin:/^\\*{15}$/}]},{className:\"addition\",begin:\"^\\\\+\",end:\"$\"},{className:\"deletion\",begin:\"^\\\\-\",end:\"$\"},{className:\"addition\",begin:\"^\\\\!\",end:\"$\"}]}}}());\nhljs.registerLanguage(\"go\",function(){\"use strict\";return function(e){var n={keyword:\"break default func interface select case map struct chan else goto package switch const fallthrough if range type continue for import return var go defer bool byte complex64 complex128 float32 float64 int8 int16 int32 int64 string uint8 uint16 uint32 uint64 int uint uintptr rune\",literal:\"true false iota nil\",built_in:\"append cap close complex copy imag len make new panic print println real recover delete\"};return{name:\"Go\",aliases:[\"golang\"],keywords:n,illegal:\"</\",contains:[e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,{className:\"string\",variants:[e.QUOTE_STRING_MODE,e.APOS_STRING_MODE,{begin:\"`\",end:\"`\"}]},{className:\"number\",variants:[{begin:e.C_NUMBER_RE+\"[i]\",relevance:1},e.C_NUMBER_MODE]},{begin:/:=/},{className:\"function\",beginKeywords:\"func\",end:\"\\\\s*(\\\\{|$)\",excludeEnd:!0,contains:[e.TITLE_MODE,{className:\"params\",begin:/\\(/,end:/\\)/,keywords:n,illegal:/[\"']/}]}]}}}());\nhljs.registerLanguage(\"http\",function(){\"use strict\";return function(e){var n=\"HTTP/[0-9\\\\.]+\";return{name:\"HTTP\",aliases:[\"https\"],illegal:\"\\\\S\",contains:[{begin:\"^\"+n,end:\"$\",contains:[{className:\"number\",begin:\"\\\\b\\\\d{3}\\\\b\"}]},{begin:\"^[A-Z]+ (.*?) \"+n+\"$\",returnBegin:!0,end:\"$\",contains:[{className:\"string\",begin:\" \",end:\" \",excludeBegin:!0,excludeEnd:!0},{begin:n},{className:\"keyword\",begin:\"[A-Z]+\"}]},{className:\"attribute\",begin:\"^\\\\w\",end:\": \",excludeEnd:!0,illegal:\"\\\\n|\\\\s|=\",starts:{end:\"$\",relevance:0}},{begin:\"\\\\n\\\\n\",starts:{subLanguage:[],endsWithParent:!0}}]}}}());\nhljs.registerLanguage(\"ini\",function(){\"use strict\";function e(e){return e?\"string\"==typeof e?e:e.source:null}function n(...n){return n.map(n=>e(n)).join(\"\")}return function(a){var s={className:\"number\",relevance:0,variants:[{begin:/([\\+\\-]+)?[\\d]+_[\\d_]+/},{begin:a.NUMBER_RE}]},i=a.COMMENT();i.variants=[{begin:/;/,end:/$/},{begin:/#/,end:/$/}];var t={className:\"variable\",variants:[{begin:/\\$[\\w\\d\"][\\w\\d_]*/},{begin:/\\$\\{(.*?)}/}]},r={className:\"literal\",begin:/\\bon|off|true|false|yes|no\\b/},l={className:\"string\",contains:[a.BACKSLASH_ESCAPE],variants:[{begin:\"'''\",end:\"'''\",relevance:10},{begin:'\"\"\"',end:'\"\"\"',relevance:10},{begin:'\"',end:'\"'},{begin:\"'\",end:\"'\"}]},c={begin:/\\[/,end:/\\]/,contains:[i,r,t,l,s,\"self\"],relevance:0},g=\"(\"+[/[A-Za-z0-9_-]+/,/\"(\\\\\"|[^\"])*\"/,/'[^']*'/].map(n=>e(n)).join(\"|\")+\")\";return{name:\"TOML, also INI\",aliases:[\"toml\"],case_insensitive:!0,illegal:/\\S/,contains:[i,{className:\"section\",begin:/\\[+/,end:/\\]+/},{begin:n(g,\"(\\\\s*\\\\.\\\\s*\",g,\")*\",n(\"(?=\",/\\s*=\\s*[^#\\s]/,\")\")),className:\"attr\",starts:{end:/$/,contains:[i,c,r,t,l,s]}}]}}}());\nhljs.registerLanguage(\"java\",function(){\"use strict\";function e(e){return e?\"string\"==typeof e?e:e.source:null}function n(e){return a(\"(\",e,\")?\")}function a(...n){return n.map(n=>e(n)).join(\"\")}function s(...n){return\"(\"+n.map(n=>e(n)).join(\"|\")+\")\"}return function(e){var t=\"false synchronized int abstract float private char boolean var static null if const for true while long strictfp finally protected import native final void enum else break transient catch instanceof byte super volatile case assert short package default double public try this switch continue throws protected public private module requires exports do\",i={className:\"meta\",begin:\"@[À-ʸa-zA-Z_$][À-ʸa-zA-Z_$0-9]*\",contains:[{begin:/\\(/,end:/\\)/,contains:[\"self\"]}]},r=e=>a(\"[\",e,\"]+([\",e,\"_]*[\",e,\"]+)?\"),c={className:\"number\",variants:[{begin:`\\\\b(0[bB]${r(\"01\")})[lL]?`},{begin:`\\\\b(0${r(\"0-7\")})[dDfFlL]?`},{begin:a(/\\b0[xX]/,s(a(r(\"a-fA-F0-9\"),/\\./,r(\"a-fA-F0-9\")),a(r(\"a-fA-F0-9\"),/\\.?/),a(/\\./,r(\"a-fA-F0-9\"))),/([pP][+-]?(\\d+))?/,/[fFdDlL]?/)},{begin:a(/\\b/,s(a(/\\d*\\./,r(\"\\\\d\")),r(\"\\\\d\")),/[eE][+-]?[\\d]+[dDfF]?/)},{begin:a(/\\b/,r(/\\d/),n(/\\.?/),n(r(/\\d/)),/[dDfFlL]?/)}],relevance:0};return{name:\"Java\",aliases:[\"jsp\"],keywords:t,illegal:/<\\/|#/,contains:[e.COMMENT(\"/\\\\*\\\\*\",\"\\\\*/\",{relevance:0,contains:[{begin:/\\w+@/,relevance:0},{className:\"doctag\",begin:\"@[A-Za-z]+\"}]}),e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,{className:\"class\",beginKeywords:\"class interface\",end:/[{;=]/,excludeEnd:!0,keywords:\"class interface\",illegal:/[:\"\\[\\]]/,contains:[{beginKeywords:\"extends implements\"},e.UNDERSCORE_TITLE_MODE]},{beginKeywords:\"new throw return else\",relevance:0},{className:\"function\",begin:\"([À-ʸa-zA-Z_$][À-ʸa-zA-Z_$0-9]*(<[À-ʸa-zA-Z_$][À-ʸa-zA-Z_$0-9]*(\\\\s*,\\\\s*[À-ʸa-zA-Z_$][À-ʸa-zA-Z_$0-9]*)*>)?\\\\s+)+\"+e.UNDERSCORE_IDENT_RE+\"\\\\s*\\\\(\",returnBegin:!0,end:/[{;=]/,excludeEnd:!0,keywords:t,contains:[{begin:e.UNDERSCORE_IDENT_RE+\"\\\\s*\\\\(\",returnBegin:!0,relevance:0,contains:[e.UNDERSCORE_TITLE_MODE]},{className:\"params\",begin:/\\(/,end:/\\)/,keywords:t,relevance:0,contains:[i,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,e.C_NUMBER_MODE,e.C_BLOCK_COMMENT_MODE]},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},c,i]}}}());\nhljs.registerLanguage(\"javascript\",function(){\"use strict\";const e=[\"as\",\"in\",\"of\",\"if\",\"for\",\"while\",\"finally\",\"var\",\"new\",\"function\",\"do\",\"return\",\"void\",\"else\",\"break\",\"catch\",\"instanceof\",\"with\",\"throw\",\"case\",\"default\",\"try\",\"switch\",\"continue\",\"typeof\",\"delete\",\"let\",\"yield\",\"const\",\"class\",\"debugger\",\"async\",\"await\",\"static\",\"import\",\"from\",\"export\",\"extends\"],n=[\"true\",\"false\",\"null\",\"undefined\",\"NaN\",\"Infinity\"],a=[].concat([\"setInterval\",\"setTimeout\",\"clearInterval\",\"clearTimeout\",\"require\",\"exports\",\"eval\",\"isFinite\",\"isNaN\",\"parseFloat\",\"parseInt\",\"decodeURI\",\"decodeURIComponent\",\"encodeURI\",\"encodeURIComponent\",\"escape\",\"unescape\"],[\"arguments\",\"this\",\"super\",\"console\",\"window\",\"document\",\"localStorage\",\"module\",\"global\"],[\"Intl\",\"DataView\",\"Number\",\"Math\",\"Date\",\"String\",\"RegExp\",\"Object\",\"Function\",\"Boolean\",\"Error\",\"Symbol\",\"Set\",\"Map\",\"WeakSet\",\"WeakMap\",\"Proxy\",\"Reflect\",\"JSON\",\"Promise\",\"Float64Array\",\"Int16Array\",\"Int32Array\",\"Int8Array\",\"Uint16Array\",\"Uint32Array\",\"Float32Array\",\"Array\",\"Uint8Array\",\"Uint8ClampedArray\",\"ArrayBuffer\"],[\"EvalError\",\"InternalError\",\"RangeError\",\"ReferenceError\",\"SyntaxError\",\"TypeError\",\"URIError\"]);function s(e){return r(\"(?=\",e,\")\")}function r(...e){return e.map(e=>(function(e){return e?\"string\"==typeof e?e:e.source:null})(e)).join(\"\")}return function(t){var i=\"[A-Za-z$_][0-9A-Za-z$_]*\",c={begin:/<[A-Za-z0-9\\\\._:-]+/,end:/\\/[A-Za-z0-9\\\\._:-]+>|\\/>/},o={$pattern:\"[A-Za-z$_][0-9A-Za-z$_]*\",keyword:e.join(\" \"),literal:n.join(\" \"),built_in:a.join(\" \")},l={className:\"number\",variants:[{begin:\"\\\\b(0[bB][01]+)n?\"},{begin:\"\\\\b(0[oO][0-7]+)n?\"},{begin:t.C_NUMBER_RE+\"n?\"}],relevance:0},E={className:\"subst\",begin:\"\\\\$\\\\{\",end:\"\\\\}\",keywords:o,contains:[]},d={begin:\"html`\",end:\"\",starts:{end:\"`\",returnEnd:!1,contains:[t.BACKSLASH_ESCAPE,E],subLanguage:\"xml\"}},g={begin:\"css`\",end:\"\",starts:{end:\"`\",returnEnd:!1,contains:[t.BACKSLASH_ESCAPE,E],subLanguage:\"css\"}},u={className:\"string\",begin:\"`\",end:\"`\",contains:[t.BACKSLASH_ESCAPE,E]};E.contains=[t.APOS_STRING_MODE,t.QUOTE_STRING_MODE,d,g,u,l,t.REGEXP_MODE];var b=E.contains.concat([{begin:/\\(/,end:/\\)/,contains:[\"self\"].concat(E.contains,[t.C_BLOCK_COMMENT_MODE,t.C_LINE_COMMENT_MODE])},t.C_BLOCK_COMMENT_MODE,t.C_LINE_COMMENT_MODE]),_={className:\"params\",begin:/\\(/,end:/\\)/,excludeBegin:!0,excludeEnd:!0,contains:b};return{name:\"JavaScript\",aliases:[\"js\",\"jsx\",\"mjs\",\"cjs\"],keywords:o,contains:[t.SHEBANG({binary:\"node\",relevance:5}),{className:\"meta\",relevance:10,begin:/^\\s*['\"]use (strict|asm)['\"]/},t.APOS_STRING_MODE,t.QUOTE_STRING_MODE,d,g,u,t.C_LINE_COMMENT_MODE,t.COMMENT(\"/\\\\*\\\\*\",\"\\\\*/\",{relevance:0,contains:[{className:\"doctag\",begin:\"@[A-Za-z]+\",contains:[{className:\"type\",begin:\"\\\\{\",end:\"\\\\}\",relevance:0},{className:\"variable\",begin:i+\"(?=\\\\s*(-)|$)\",endsParent:!0,relevance:0},{begin:/(?=[^\\n])\\s/,relevance:0}]}]}),t.C_BLOCK_COMMENT_MODE,l,{begin:r(/[{,\\n]\\s*/,s(r(/(((\\/\\/.*)|(\\/\\*(.|\\n)*\\*\\/))\\s*)*/,i+\"\\\\s*:\"))),relevance:0,contains:[{className:\"attr\",begin:i+s(\"\\\\s*:\"),relevance:0}]},{begin:\"(\"+t.RE_STARTERS_RE+\"|\\\\b(case|return|throw)\\\\b)\\\\s*\",keywords:\"return throw case\",contains:[t.C_LINE_COMMENT_MODE,t.C_BLOCK_COMMENT_MODE,t.REGEXP_MODE,{className:\"function\",begin:\"(\\\\([^(]*(\\\\([^(]*(\\\\([^(]*\\\\))?\\\\))?\\\\)|\"+t.UNDERSCORE_IDENT_RE+\")\\\\s*=>\",returnBegin:!0,end:\"\\\\s*=>\",contains:[{className:\"params\",variants:[{begin:t.UNDERSCORE_IDENT_RE},{className:null,begin:/\\(\\s*\\)/,skip:!0},{begin:/\\(/,end:/\\)/,excludeBegin:!0,excludeEnd:!0,keywords:o,contains:b}]}]},{begin:/,/,relevance:0},{className:\"\",begin:/\\s/,end:/\\s*/,skip:!0},{variants:[{begin:\"<>\",end:\"</>\"},{begin:c.begin,end:c.end}],subLanguage:\"xml\",contains:[{begin:c.begin,end:c.end,skip:!0,contains:[\"self\"]}]}],relevance:0},{className:\"function\",beginKeywords:\"function\",end:/\\{/,excludeEnd:!0,contains:[t.inherit(t.TITLE_MODE,{begin:i}),_],illegal:/\\[|%/},{begin:/\\$[(.]/},t.METHOD_GUARD,{className:\"class\",beginKeywords:\"class\",end:/[{;=]/,excludeEnd:!0,illegal:/[:\"\\[\\]]/,contains:[{beginKeywords:\"extends\"},t.UNDERSCORE_TITLE_MODE]},{beginKeywords:\"constructor\",end:/\\{/,excludeEnd:!0},{begin:\"(get|set)\\\\s+(?=\"+i+\"\\\\()\",end:/{/,keywords:\"get set\",contains:[t.inherit(t.TITLE_MODE,{begin:i}),{begin:/\\(\\)/},_]}],illegal:/#(?!!)/}}}());\nhljs.registerLanguage(\"json\",function(){\"use strict\";return function(n){var e={literal:\"true false null\"},i=[n.C_LINE_COMMENT_MODE,n.C_BLOCK_COMMENT_MODE],t=[n.QUOTE_STRING_MODE,n.C_NUMBER_MODE],a={end:\",\",endsWithParent:!0,excludeEnd:!0,contains:t,keywords:e},l={begin:\"{\",end:\"}\",contains:[{className:\"attr\",begin:/\"/,end:/\"/,contains:[n.BACKSLASH_ESCAPE],illegal:\"\\\\n\"},n.inherit(a,{begin:/:/})].concat(i),illegal:\"\\\\S\"},s={begin:\"\\\\[\",end:\"\\\\]\",contains:[n.inherit(a)],illegal:\"\\\\S\"};return t.push(l,s),i.forEach((function(n){t.push(n)})),{name:\"JSON\",contains:t,keywords:e,illegal:\"\\\\S\"}}}());\nhljs.registerLanguage(\"kotlin\",function(){\"use strict\";return function(e){var n={keyword:\"abstract as val var vararg get set class object open private protected public noinline crossinline dynamic final enum if else do while for when throw try catch finally import package is in fun override companion reified inline lateinit init interface annotation data sealed internal infix operator out by constructor super tailrec where const inner suspend typealias external expect actual trait volatile transient native default\",built_in:\"Byte Short Char Int Long Boolean Float Double Void Unit Nothing\",literal:\"true false null\"},a={className:\"symbol\",begin:e.UNDERSCORE_IDENT_RE+\"@\"},i={className:\"subst\",begin:\"\\\\${\",end:\"}\",contains:[e.C_NUMBER_MODE]},s={className:\"variable\",begin:\"\\\\$\"+e.UNDERSCORE_IDENT_RE},t={className:\"string\",variants:[{begin:'\"\"\"',end:'\"\"\"(?=[^\"])',contains:[s,i]},{begin:\"'\",end:\"'\",illegal:/\\n/,contains:[e.BACKSLASH_ESCAPE]},{begin:'\"',end:'\"',illegal:/\\n/,contains:[e.BACKSLASH_ESCAPE,s,i]}]};i.contains.push(t);var r={className:\"meta\",begin:\"@(?:file|property|field|get|set|receiver|param|setparam|delegate)\\\\s*:(?:\\\\s*\"+e.UNDERSCORE_IDENT_RE+\")?\"},l={className:\"meta\",begin:\"@\"+e.UNDERSCORE_IDENT_RE,contains:[{begin:/\\(/,end:/\\)/,contains:[e.inherit(t,{className:\"meta-string\"})]}]},c=e.COMMENT(\"/\\\\*\",\"\\\\*/\",{contains:[e.C_BLOCK_COMMENT_MODE]}),o={variants:[{className:\"type\",begin:e.UNDERSCORE_IDENT_RE},{begin:/\\(/,end:/\\)/,contains:[]}]},d=o;return d.variants[1].contains=[o],o.variants[1].contains=[d],{name:\"Kotlin\",aliases:[\"kt\"],keywords:n,contains:[e.COMMENT(\"/\\\\*\\\\*\",\"\\\\*/\",{relevance:0,contains:[{className:\"doctag\",begin:\"@[A-Za-z]+\"}]}),e.C_LINE_COMMENT_MODE,c,{className:\"keyword\",begin:/\\b(break|continue|return|this)\\b/,starts:{contains:[{className:\"symbol\",begin:/@\\w+/}]}},a,r,l,{className:\"function\",beginKeywords:\"fun\",end:\"[(]|$\",returnBegin:!0,excludeEnd:!0,keywords:n,illegal:/fun\\s+(<.*>)?[^\\s\\(]+(\\s+[^\\s\\(]+)\\s*=/,relevance:5,contains:[{begin:e.UNDERSCORE_IDENT_RE+\"\\\\s*\\\\(\",returnBegin:!0,relevance:0,contains:[e.UNDERSCORE_TITLE_MODE]},{className:\"type\",begin:/</,end:/>/,keywords:\"reified\",relevance:0},{className:\"params\",begin:/\\(/,end:/\\)/,endsParent:!0,keywords:n,relevance:0,contains:[{begin:/:/,end:/[=,\\/]/,endsWithParent:!0,contains:[o,e.C_LINE_COMMENT_MODE,c],relevance:0},e.C_LINE_COMMENT_MODE,c,r,l,t,e.C_NUMBER_MODE]},c]},{className:\"class\",beginKeywords:\"class interface trait\",end:/[:\\{(]|$/,excludeEnd:!0,illegal:\"extends implements\",contains:[{beginKeywords:\"public protected internal private constructor\"},e.UNDERSCORE_TITLE_MODE,{className:\"type\",begin:/</,end:/>/,excludeBegin:!0,excludeEnd:!0,relevance:0},{className:\"type\",begin:/[,:]\\s*/,end:/[<\\(,]|$/,excludeBegin:!0,returnEnd:!0},r,l]},t,{className:\"meta\",begin:\"^#!/usr/bin/env\",end:\"$\",illegal:\"\\n\"},{className:\"number\",begin:\"\\\\b(0[bB]([01]+[01_]+[01]+|[01]+)|0[xX]([a-fA-F0-9]+[a-fA-F0-9_]+[a-fA-F0-9]+|[a-fA-F0-9]+)|(([\\\\d]+[\\\\d_]+[\\\\d]+|[\\\\d]+)(\\\\.([\\\\d]+[\\\\d_]+[\\\\d]+|[\\\\d]+))?|\\\\.([\\\\d]+[\\\\d_]+[\\\\d]+|[\\\\d]+))([eE][-+]?\\\\d+)?)[lLfF]?\",relevance:0}]}}}());\nhljs.registerLanguage(\"less\",function(){\"use strict\";return function(e){var n=\"([\\\\w-]+|@{[\\\\w-]+})\",a=[],s=[],t=function(e){return{className:\"string\",begin:\"~?\"+e+\".*?\"+e}},r=function(e,n,a){return{className:e,begin:n,relevance:a}},i={begin:\"\\\\(\",end:\"\\\\)\",contains:s,relevance:0};s.push(e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,t(\"'\"),t('\"'),e.CSS_NUMBER_MODE,{begin:\"(url|data-uri)\\\\(\",starts:{className:\"string\",end:\"[\\\\)\\\\n]\",excludeEnd:!0}},r(\"number\",\"#[0-9A-Fa-f]+\\\\b\"),i,r(\"variable\",\"@@?[\\\\w-]+\",10),r(\"variable\",\"@{[\\\\w-]+}\"),r(\"built_in\",\"~?`[^`]*?`\"),{className:\"attribute\",begin:\"[\\\\w-]+\\\\s*:\",end:\":\",returnBegin:!0,excludeEnd:!0},{className:\"meta\",begin:\"!important\"});var c=s.concat({begin:\"{\",end:\"}\",contains:a}),l={beginKeywords:\"when\",endsWithParent:!0,contains:[{beginKeywords:\"and not\"}].concat(s)},o={begin:n+\"\\\\s*:\",returnBegin:!0,end:\"[;}]\",relevance:0,contains:[{className:\"attribute\",begin:n,end:\":\",excludeEnd:!0,starts:{endsWithParent:!0,illegal:\"[<=$]\",relevance:0,contains:s}}]},g={className:\"keyword\",begin:\"@(import|media|charset|font-face|(-[a-z]+-)?keyframes|supports|document|namespace|page|viewport|host)\\\\b\",starts:{end:\"[;{}]\",returnEnd:!0,contains:s,relevance:0}},d={className:\"variable\",variants:[{begin:\"@[\\\\w-]+\\\\s*:\",relevance:15},{begin:\"@[\\\\w-]+\"}],starts:{end:\"[;}]\",returnEnd:!0,contains:c}},b={variants:[{begin:\"[\\\\.#:&\\\\[>]\",end:\"[;{}]\"},{begin:n,end:\"{\"}],returnBegin:!0,returnEnd:!0,illegal:\"[<='$\\\"]\",relevance:0,contains:[e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,l,r(\"keyword\",\"all\\\\b\"),r(\"variable\",\"@{[\\\\w-]+}\"),r(\"selector-tag\",n+\"%?\",0),r(\"selector-id\",\"#\"+n),r(\"selector-class\",\"\\\\.\"+n,0),r(\"selector-tag\",\"&\",0),{className:\"selector-attr\",begin:\"\\\\[\",end:\"\\\\]\"},{className:\"selector-pseudo\",begin:/:(:)?[a-zA-Z0-9\\_\\-\\+\\(\\)\"'.]+/},{begin:\"\\\\(\",end:\"\\\\)\",contains:c},{begin:\"!important\"}]};return a.push(e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,g,d,o,b),{name:\"Less\",case_insensitive:!0,illegal:\"[=>'/<($\\\"]\",contains:a}}}());\nhljs.registerLanguage(\"lua\",function(){\"use strict\";return function(e){var t={begin:\"\\\\[=*\\\\[\",end:\"\\\\]=*\\\\]\",contains:[\"self\"]},a=[e.COMMENT(\"--(?!\\\\[=*\\\\[)\",\"$\"),e.COMMENT(\"--\\\\[=*\\\\[\",\"\\\\]=*\\\\]\",{contains:[t],relevance:10})];return{name:\"Lua\",keywords:{$pattern:e.UNDERSCORE_IDENT_RE,literal:\"true false nil\",keyword:\"and break do else elseif end for goto if in local not or repeat return then until while\",built_in:\"_G _ENV _VERSION __index __newindex __mode __call __metatable __tostring __len __gc __add __sub __mul __div __mod __pow __concat __unm __eq __lt __le assert collectgarbage dofile error getfenv getmetatable ipairs load loadfile loadstring module next pairs pcall print rawequal rawget rawset require select setfenv setmetatable tonumber tostring type unpack xpcall arg self coroutine resume yield status wrap create running debug getupvalue debug sethook getmetatable gethook setmetatable setlocal traceback setfenv getinfo setupvalue getlocal getregistry getfenv io lines write close flush open output type read stderr stdin input stdout popen tmpfile math log max acos huge ldexp pi cos tanh pow deg tan cosh sinh random randomseed frexp ceil floor rad abs sqrt modf asin min mod fmod log10 atan2 exp sin atan os exit setlocale date getenv difftime remove time clock tmpname rename execute package preload loadlib loaded loaders cpath config path seeall string sub upper len gfind rep find match char dump gmatch reverse byte format gsub lower table setn insert getn foreachi maxn foreach concat sort remove\"},contains:a.concat([{className:\"function\",beginKeywords:\"function\",end:\"\\\\)\",contains:[e.inherit(e.TITLE_MODE,{begin:\"([_a-zA-Z]\\\\w*\\\\.)*([_a-zA-Z]\\\\w*:)?[_a-zA-Z]\\\\w*\"}),{className:\"params\",begin:\"\\\\(\",endsWithParent:!0,contains:a}].concat(a)},e.C_NUMBER_MODE,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,{className:\"string\",begin:\"\\\\[=*\\\\[\",end:\"\\\\]=*\\\\]\",contains:[t],relevance:5}])}}}());\nhljs.registerLanguage(\"makefile\",function(){\"use strict\";return function(e){var i={className:\"variable\",variants:[{begin:\"\\\\$\\\\(\"+e.UNDERSCORE_IDENT_RE+\"\\\\)\",contains:[e.BACKSLASH_ESCAPE]},{begin:/\\$[@%<?\\^\\+\\*]/}]},n={className:\"string\",begin:/\"/,end:/\"/,contains:[e.BACKSLASH_ESCAPE,i]},a={className:\"variable\",begin:/\\$\\([\\w-]+\\s/,end:/\\)/,keywords:{built_in:\"subst patsubst strip findstring filter filter-out sort word wordlist firstword lastword dir notdir suffix basename addsuffix addprefix join wildcard realpath abspath error warning shell origin flavor foreach if or and call eval file value\"},contains:[i]},r={begin:\"^\"+e.UNDERSCORE_IDENT_RE+\"\\\\s*(?=[:+?]?=)\"},s={className:\"section\",begin:/^[^\\s]+:/,end:/$/,contains:[i]};return{name:\"Makefile\",aliases:[\"mk\",\"mak\"],keywords:{$pattern:/[\\w-]+/,keyword:\"define endef undefine ifdef ifndef ifeq ifneq else endif include -include sinclude override export unexport private vpath\"},contains:[e.HASH_COMMENT_MODE,i,n,a,r,{className:\"meta\",begin:/^\\.PHONY:/,end:/$/,keywords:{$pattern:/[\\.\\w]+/,\"meta-keyword\":\".PHONY\"}},s]}}}());\nhljs.registerLanguage(\"xml\",function(){\"use strict\";return function(e){var n={className:\"symbol\",begin:\"&[a-z]+;|&#[0-9]+;|&#x[a-f0-9]+;\"},a={begin:\"\\\\s\",contains:[{className:\"meta-keyword\",begin:\"#?[a-z_][a-z1-9_-]+\",illegal:\"\\\\n\"}]},s=e.inherit(a,{begin:\"\\\\(\",end:\"\\\\)\"}),t=e.inherit(e.APOS_STRING_MODE,{className:\"meta-string\"}),i=e.inherit(e.QUOTE_STRING_MODE,{className:\"meta-string\"}),c={endsWithParent:!0,illegal:/</,relevance:0,contains:[{className:\"attr\",begin:\"[A-Za-z0-9\\\\._:-]+\",relevance:0},{begin:/=\\s*/,relevance:0,contains:[{className:\"string\",endsParent:!0,variants:[{begin:/\"/,end:/\"/,contains:[n]},{begin:/'/,end:/'/,contains:[n]},{begin:/[^\\s\"'=<>`]+/}]}]}]};return{name:\"HTML, XML\",aliases:[\"html\",\"xhtml\",\"rss\",\"atom\",\"xjb\",\"xsd\",\"xsl\",\"plist\",\"wsf\",\"svg\"],case_insensitive:!0,contains:[{className:\"meta\",begin:\"<![a-z]\",end:\">\",relevance:10,contains:[a,i,t,s,{begin:\"\\\\[\",end:\"\\\\]\",contains:[{className:\"meta\",begin:\"<![a-z]\",end:\">\",contains:[a,s,i,t]}]}]},e.COMMENT(\"\\x3c!--\",\"--\\x3e\",{relevance:10}),{begin:\"<\\\\!\\\\[CDATA\\\\[\",end:\"\\\\]\\\\]>\",relevance:10},n,{className:\"meta\",begin:/<\\?xml/,end:/\\?>/,relevance:10},{className:\"tag\",begin:\"<style(?=\\\\s|>)\",end:\">\",keywords:{name:\"style\"},contains:[c],starts:{end:\"</style>\",returnEnd:!0,subLanguage:[\"css\",\"xml\"]}},{className:\"tag\",begin:\"<script(?=\\\\s|>)\",end:\">\",keywords:{name:\"script\"},contains:[c],starts:{end:\"<\\/script>\",returnEnd:!0,subLanguage:[\"javascript\",\"handlebars\",\"xml\"]}},{className:\"tag\",begin:\"</?\",end:\"/?>\",contains:[{className:\"name\",begin:/[^\\/><\\s]+/,relevance:0},c]}]}}}());\nhljs.registerLanguage(\"markdown\",function(){\"use strict\";return function(n){const e={begin:\"<\",end:\">\",subLanguage:\"xml\",relevance:0},a={begin:\"\\\\[.+?\\\\][\\\\(\\\\[].*?[\\\\)\\\\]]\",returnBegin:!0,contains:[{className:\"string\",begin:\"\\\\[\",end:\"\\\\]\",excludeBegin:!0,returnEnd:!0,relevance:0},{className:\"link\",begin:\"\\\\]\\\\(\",end:\"\\\\)\",excludeBegin:!0,excludeEnd:!0},{className:\"symbol\",begin:\"\\\\]\\\\[\",end:\"\\\\]\",excludeBegin:!0,excludeEnd:!0}],relevance:10},i={className:\"strong\",contains:[],variants:[{begin:/_{2}/,end:/_{2}/},{begin:/\\*{2}/,end:/\\*{2}/}]},s={className:\"emphasis\",contains:[],variants:[{begin:/\\*(?!\\*)/,end:/\\*/},{begin:/_(?!_)/,end:/_/,relevance:0}]};i.contains.push(s),s.contains.push(i);var c=[e,a];return i.contains=i.contains.concat(c),s.contains=s.contains.concat(c),{name:\"Markdown\",aliases:[\"md\",\"mkdown\",\"mkd\"],contains:[{className:\"section\",variants:[{begin:\"^#{1,6}\",end:\"$\",contains:c=c.concat(i,s)},{begin:\"(?=^.+?\\\\n[=-]{2,}$)\",contains:[{begin:\"^[=-]*$\"},{begin:\"^\",end:\"\\\\n\",contains:c}]}]},e,{className:\"bullet\",begin:\"^[ \\t]*([*+-]|(\\\\d+\\\\.))(?=\\\\s+)\",end:\"\\\\s+\",excludeEnd:!0},i,s,{className:\"quote\",begin:\"^>\\\\s+\",contains:c,end:\"$\"},{className:\"code\",variants:[{begin:\"(`{3,})(.|\\\\n)*?\\\\1`*[ ]*\"},{begin:\"(~{3,})(.|\\\\n)*?\\\\1~*[ ]*\"},{begin:\"```\",end:\"```+[ ]*$\"},{begin:\"~~~\",end:\"~~~+[ ]*$\"},{begin:\"`.+?`\"},{begin:\"(?=^( {4}|\\\\t))\",contains:[{begin:\"^( {4}|\\\\t)\",end:\"(\\\\n)$\"}],relevance:0}]},{begin:\"^[-\\\\*]{3,}\",end:\"$\"},a,{begin:/^\\[[^\\n]+\\]:/,returnBegin:!0,contains:[{className:\"symbol\",begin:/\\[/,end:/\\]/,excludeBegin:!0,excludeEnd:!0},{className:\"link\",begin:/:\\s*/,end:/$/,excludeBegin:!0}]}]}}}());\nhljs.registerLanguage(\"nginx\",function(){\"use strict\";return function(e){var n={className:\"variable\",variants:[{begin:/\\$\\d+/},{begin:/\\$\\{/,end:/}/},{begin:\"[\\\\$\\\\@]\"+e.UNDERSCORE_IDENT_RE}]},a={endsWithParent:!0,keywords:{$pattern:\"[a-z/_]+\",literal:\"on off yes no true false none blocked debug info notice warn error crit select break last permanent redirect kqueue rtsig epoll poll /dev/poll\"},relevance:0,illegal:\"=>\",contains:[e.HASH_COMMENT_MODE,{className:\"string\",contains:[e.BACKSLASH_ESCAPE,n],variants:[{begin:/\"/,end:/\"/},{begin:/'/,end:/'/}]},{begin:\"([a-z]+):/\",end:\"\\\\s\",endsWithParent:!0,excludeEnd:!0,contains:[n]},{className:\"regexp\",contains:[e.BACKSLASH_ESCAPE,n],variants:[{begin:\"\\\\s\\\\^\",end:\"\\\\s|{|;\",returnEnd:!0},{begin:\"~\\\\*?\\\\s+\",end:\"\\\\s|{|;\",returnEnd:!0},{begin:\"\\\\*(\\\\.[a-z\\\\-]+)+\"},{begin:\"([a-z\\\\-]+\\\\.)+\\\\*\"}]},{className:\"number\",begin:\"\\\\b\\\\d{1,3}\\\\.\\\\d{1,3}\\\\.\\\\d{1,3}\\\\.\\\\d{1,3}(:\\\\d{1,5})?\\\\b\"},{className:\"number\",begin:\"\\\\b\\\\d+[kKmMgGdshdwy]*\\\\b\",relevance:0},n]};return{name:\"Nginx config\",aliases:[\"nginxconf\"],contains:[e.HASH_COMMENT_MODE,{begin:e.UNDERSCORE_IDENT_RE+\"\\\\s+{\",returnBegin:!0,end:\"{\",contains:[{className:\"section\",begin:e.UNDERSCORE_IDENT_RE}],relevance:0},{begin:e.UNDERSCORE_IDENT_RE+\"\\\\s\",end:\";|{\",returnBegin:!0,contains:[{className:\"attribute\",begin:e.UNDERSCORE_IDENT_RE,starts:a}],relevance:0}],illegal:\"[^\\\\s\\\\}]\"}}}());\nhljs.registerLanguage(\"objectivec\",function(){\"use strict\";return function(e){var n=/[a-zA-Z@][a-zA-Z0-9_]*/,_={$pattern:n,keyword:\"@interface @class @protocol @implementation\"};return{name:\"Objective-C\",aliases:[\"mm\",\"objc\",\"obj-c\"],keywords:{$pattern:n,keyword:\"int float while char export sizeof typedef const struct for union unsigned long volatile static bool mutable if do return goto void enum else break extern asm case short default double register explicit signed typename this switch continue wchar_t inline readonly assign readwrite self @synchronized id typeof nonatomic super unichar IBOutlet IBAction strong weak copy in out inout bycopy byref oneway __strong __weak __block __autoreleasing @private @protected @public @try @property @end @throw @catch @finally @autoreleasepool @synthesize @dynamic @selector @optional @required @encode @package @import @defs @compatibility_alias __bridge __bridge_transfer __bridge_retained __bridge_retain __covariant __contravariant __kindof _Nonnull _Nullable _Null_unspecified __FUNCTION__ __PRETTY_FUNCTION__ __attribute__ getter setter retain unsafe_unretained nonnull nullable null_unspecified null_resettable class instancetype NS_DESIGNATED_INITIALIZER NS_UNAVAILABLE NS_REQUIRES_SUPER NS_RETURNS_INNER_POINTER NS_INLINE NS_AVAILABLE NS_DEPRECATED NS_ENUM NS_OPTIONS NS_SWIFT_UNAVAILABLE NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_END NS_REFINED_FOR_SWIFT NS_SWIFT_NAME NS_SWIFT_NOTHROW NS_DURING NS_HANDLER NS_ENDHANDLER NS_VALUERETURN NS_VOIDRETURN\",literal:\"false true FALSE TRUE nil YES NO NULL\",built_in:\"BOOL dispatch_once_t dispatch_queue_t dispatch_sync dispatch_async dispatch_once\"},illegal:\"</\",contains:[{className:\"built_in\",begin:\"\\\\b(AV|CA|CF|CG|CI|CL|CM|CN|CT|MK|MP|MTK|MTL|NS|SCN|SK|UI|WK|XC)\\\\w+\"},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,e.C_NUMBER_MODE,e.QUOTE_STRING_MODE,e.APOS_STRING_MODE,{className:\"string\",variants:[{begin:'@\"',end:'\"',illegal:\"\\\\n\",contains:[e.BACKSLASH_ESCAPE]}]},{className:\"meta\",begin:/#\\s*[a-z]+\\b/,end:/$/,keywords:{\"meta-keyword\":\"if else elif endif define undef warning error line pragma ifdef ifndef include\"},contains:[{begin:/\\\\\\n/,relevance:0},e.inherit(e.QUOTE_STRING_MODE,{className:\"meta-string\"}),{className:\"meta-string\",begin:/<.*?>/,end:/$/,illegal:\"\\\\n\"},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{className:\"class\",begin:\"(\"+_.keyword.split(\" \").join(\"|\")+\")\\\\b\",end:\"({|$)\",excludeEnd:!0,keywords:_,contains:[e.UNDERSCORE_TITLE_MODE]},{begin:\"\\\\.\"+e.UNDERSCORE_IDENT_RE,relevance:0}]}}}());\nhljs.registerLanguage(\"perl\",function(){\"use strict\";return function(e){var n={$pattern:/[\\w.]+/,keyword:\"getpwent getservent quotemeta msgrcv scalar kill dbmclose undef lc ma syswrite tr send umask sysopen shmwrite vec qx utime local oct semctl localtime readpipe do return format read sprintf dbmopen pop getpgrp not getpwnam rewinddir qq fileno qw endprotoent wait sethostent bless s|0 opendir continue each sleep endgrent shutdown dump chomp connect getsockname die socketpair close flock exists index shmget sub for endpwent redo lstat msgctl setpgrp abs exit select print ref gethostbyaddr unshift fcntl syscall goto getnetbyaddr join gmtime symlink semget splice x|0 getpeername recv log setsockopt cos last reverse gethostbyname getgrnam study formline endhostent times chop length gethostent getnetent pack getprotoent getservbyname rand mkdir pos chmod y|0 substr endnetent printf next open msgsnd readdir use unlink getsockopt getpriority rindex wantarray hex system getservbyport endservent int chr untie rmdir prototype tell listen fork shmread ucfirst setprotoent else sysseek link getgrgid shmctl waitpid unpack getnetbyname reset chdir grep split require caller lcfirst until warn while values shift telldir getpwuid my getprotobynumber delete and sort uc defined srand accept package seekdir getprotobyname semop our rename seek if q|0 chroot sysread setpwent no crypt getc chown sqrt write setnetent setpriority foreach tie sin msgget map stat getlogin unless elsif truncate exec keys glob tied closedir ioctl socket readlink eval xor readline binmode setservent eof ord bind alarm pipe atan2 getgrent exp time push setgrent gt lt or ne m|0 break given say state when\"},t={className:\"subst\",begin:\"[$@]\\\\{\",end:\"\\\\}\",keywords:n},s={begin:\"->{\",end:\"}\"},r={variants:[{begin:/\\$\\d/},{begin:/[\\$%@](\\^\\w\\b|#\\w+(::\\w+)*|{\\w+}|\\w+(::\\w*)*)/},{begin:/[\\$%@][^\\s\\w{]/,relevance:0}]},i=[e.BACKSLASH_ESCAPE,t,r],a=[r,e.HASH_COMMENT_MODE,e.COMMENT(\"^\\\\=\\\\w\",\"\\\\=cut\",{endsWithParent:!0}),s,{className:\"string\",contains:i,variants:[{begin:\"q[qwxr]?\\\\s*\\\\(\",end:\"\\\\)\",relevance:5},{begin:\"q[qwxr]?\\\\s*\\\\[\",end:\"\\\\]\",relevance:5},{begin:\"q[qwxr]?\\\\s*\\\\{\",end:\"\\\\}\",relevance:5},{begin:\"q[qwxr]?\\\\s*\\\\|\",end:\"\\\\|\",relevance:5},{begin:\"q[qwxr]?\\\\s*\\\\<\",end:\"\\\\>\",relevance:5},{begin:\"qw\\\\s+q\",end:\"q\",relevance:5},{begin:\"'\",end:\"'\",contains:[e.BACKSLASH_ESCAPE]},{begin:'\"',end:'\"'},{begin:\"`\",end:\"`\",contains:[e.BACKSLASH_ESCAPE]},{begin:\"{\\\\w+}\",contains:[],relevance:0},{begin:\"-?\\\\w+\\\\s*\\\\=\\\\>\",contains:[],relevance:0}]},{className:\"number\",begin:\"(\\\\b0[0-7_]+)|(\\\\b0x[0-9a-fA-F_]+)|(\\\\b[1-9][0-9_]*(\\\\.[0-9_]+)?)|[0_]\\\\b\",relevance:0},{begin:\"(\\\\/\\\\/|\"+e.RE_STARTERS_RE+\"|\\\\b(split|return|print|reverse|grep)\\\\b)\\\\s*\",keywords:\"split return print reverse grep\",relevance:0,contains:[e.HASH_COMMENT_MODE,{className:\"regexp\",begin:\"(s|tr|y)/(\\\\\\\\.|[^/])*/(\\\\\\\\.|[^/])*/[a-z]*\",relevance:10},{className:\"regexp\",begin:\"(m|qr)?/\",end:\"/[a-z]*\",contains:[e.BACKSLASH_ESCAPE],relevance:0}]},{className:\"function\",beginKeywords:\"sub\",end:\"(\\\\s*\\\\(.*?\\\\))?[;{]\",excludeEnd:!0,relevance:5,contains:[e.TITLE_MODE]},{begin:\"-\\\\w\\\\b\",relevance:0},{begin:\"^__DATA__$\",end:\"^__END__$\",subLanguage:\"mojolicious\",contains:[{begin:\"^@@.*\",end:\"$\",className:\"comment\"}]}];return t.contains=a,s.contains=a,{name:\"Perl\",aliases:[\"pl\",\"pm\"],keywords:n,contains:a}}}());\nhljs.registerLanguage(\"php\",function(){\"use strict\";return function(e){var r={begin:\"\\\\$+[a-zA-Z_-ÿ][a-zA-Z0-9_-ÿ]*\"},t={className:\"meta\",variants:[{begin:/<\\?php/,relevance:10},{begin:/<\\?[=]?/},{begin:/\\?>/}]},a={className:\"string\",contains:[e.BACKSLASH_ESCAPE,t],variants:[{begin:'b\"',end:'\"'},{begin:\"b'\",end:\"'\"},e.inherit(e.APOS_STRING_MODE,{illegal:null}),e.inherit(e.QUOTE_STRING_MODE,{illegal:null})]},n={variants:[e.BINARY_NUMBER_MODE,e.C_NUMBER_MODE]},i={keyword:\"__CLASS__ __DIR__ __FILE__ __FUNCTION__ __LINE__ __METHOD__ __NAMESPACE__ __TRAIT__ die echo exit include include_once print require require_once array abstract and as binary bool boolean break callable case catch class clone const continue declare default do double else elseif empty enddeclare endfor endforeach endif endswitch endwhile eval extends final finally float for foreach from global goto if implements instanceof insteadof int integer interface isset iterable list new object or private protected public real return string switch throw trait try unset use var void while xor yield\",literal:\"false null true\",built_in:\"Error|0 AppendIterator ArgumentCountError ArithmeticError ArrayIterator ArrayObject AssertionError BadFunctionCallException BadMethodCallException CachingIterator CallbackFilterIterator CompileError Countable DirectoryIterator DivisionByZeroError DomainException EmptyIterator ErrorException Exception FilesystemIterator FilterIterator GlobIterator InfiniteIterator InvalidArgumentException IteratorIterator LengthException LimitIterator LogicException MultipleIterator NoRewindIterator OutOfBoundsException OutOfRangeException OuterIterator OverflowException ParentIterator ParseError RangeException RecursiveArrayIterator RecursiveCachingIterator RecursiveCallbackFilterIterator RecursiveDirectoryIterator RecursiveFilterIterator RecursiveIterator RecursiveIteratorIterator RecursiveRegexIterator RecursiveTreeIterator RegexIterator RuntimeException SeekableIterator SplDoublyLinkedList SplFileInfo SplFileObject SplFixedArray SplHeap SplMaxHeap SplMinHeap SplObjectStorage SplObserver SplObserver SplPriorityQueue SplQueue SplStack SplSubject SplSubject SplTempFileObject TypeError UnderflowException UnexpectedValueException ArrayAccess Closure Generator Iterator IteratorAggregate Serializable Throwable Traversable WeakReference Directory __PHP_Incomplete_Class parent php_user_filter self static stdClass\"};return{aliases:[\"php\",\"php3\",\"php4\",\"php5\",\"php6\",\"php7\"],case_insensitive:!0,keywords:i,contains:[e.HASH_COMMENT_MODE,e.COMMENT(\"//\",\"$\",{contains:[t]}),e.COMMENT(\"/\\\\*\",\"\\\\*/\",{contains:[{className:\"doctag\",begin:\"@[A-Za-z]+\"}]}),e.COMMENT(\"__halt_compiler.+?;\",!1,{endsWithParent:!0,keywords:\"__halt_compiler\"}),{className:\"string\",begin:/<<<['\"]?\\w+['\"]?$/,end:/^\\w+;?$/,contains:[e.BACKSLASH_ESCAPE,{className:\"subst\",variants:[{begin:/\\$\\w+/},{begin:/\\{\\$/,end:/\\}/}]}]},t,{className:\"keyword\",begin:/\\$this\\b/},r,{begin:/(::|->)+[a-zA-Z_\\x7f-\\xff][a-zA-Z0-9_\\x7f-\\xff]*/},{className:\"function\",beginKeywords:\"fn function\",end:/[;{]/,excludeEnd:!0,illegal:\"[$%\\\\[]\",contains:[e.UNDERSCORE_TITLE_MODE,{className:\"params\",begin:\"\\\\(\",end:\"\\\\)\",excludeBegin:!0,excludeEnd:!0,keywords:i,contains:[\"self\",r,e.C_BLOCK_COMMENT_MODE,a,n]}]},{className:\"class\",beginKeywords:\"class interface\",end:\"{\",excludeEnd:!0,illegal:/[:\\(\\$\"]/,contains:[{beginKeywords:\"extends implements\"},e.UNDERSCORE_TITLE_MODE]},{beginKeywords:\"namespace\",end:\";\",illegal:/[\\.']/,contains:[e.UNDERSCORE_TITLE_MODE]},{beginKeywords:\"use\",end:\";\",contains:[e.UNDERSCORE_TITLE_MODE]},{begin:\"=>\"},a,n]}}}());\nhljs.registerLanguage(\"php-template\",function(){\"use strict\";return function(n){return{name:\"PHP template\",subLanguage:\"xml\",contains:[{begin:/<\\?(php|=)?/,end:/\\?>/,subLanguage:\"php\",contains:[{begin:\"/\\\\*\",end:\"\\\\*/\",skip:!0},{begin:'b\"',end:'\"',skip:!0},{begin:\"b'\",end:\"'\",skip:!0},n.inherit(n.APOS_STRING_MODE,{illegal:null,className:null,contains:null,skip:!0}),n.inherit(n.QUOTE_STRING_MODE,{illegal:null,className:null,contains:null,skip:!0})]}]}}}());\nhljs.registerLanguage(\"plaintext\",function(){\"use strict\";return function(t){return{name:\"Plain text\",aliases:[\"text\",\"txt\"],disableAutodetect:!0}}}());\nhljs.registerLanguage(\"properties\",function(){\"use strict\";return function(e){var n=\"[ \\\\t\\\\f]*\",t=\"(\"+n+\"[:=]\"+n+\"|[ \\\\t\\\\f]+)\",a=\"([^\\\\\\\\:= \\\\t\\\\f\\\\n]|\\\\\\\\.)+\",s={end:t,relevance:0,starts:{className:\"string\",end:/$/,relevance:0,contains:[{begin:\"\\\\\\\\\\\\n\"}]}};return{name:\".properties\",case_insensitive:!0,illegal:/\\S/,contains:[e.COMMENT(\"^\\\\s*[!#]\",\"$\"),{begin:\"([^\\\\\\\\\\\\W:= \\\\t\\\\f\\\\n]|\\\\\\\\.)+\"+t,returnBegin:!0,contains:[{className:\"attr\",begin:\"([^\\\\\\\\\\\\W:= \\\\t\\\\f\\\\n]|\\\\\\\\.)+\",endsParent:!0,relevance:0}],starts:s},{begin:a+t,returnBegin:!0,relevance:0,contains:[{className:\"meta\",begin:a,endsParent:!0,relevance:0}],starts:s},{className:\"attr\",relevance:0,begin:a+n+\"$\"}]}}}());\nhljs.registerLanguage(\"python\",function(){\"use strict\";return function(e){var n={keyword:\"and elif is global as in if from raise for except finally print import pass return exec else break not with class assert yield try while continue del or def lambda async await nonlocal|10\",built_in:\"Ellipsis NotImplemented\",literal:\"False None True\"},a={className:\"meta\",begin:/^(>>>|\\.\\.\\.) /},i={className:\"subst\",begin:/\\{/,end:/\\}/,keywords:n,illegal:/#/},s={begin:/\\{\\{/,relevance:0},r={className:\"string\",contains:[e.BACKSLASH_ESCAPE],variants:[{begin:/(u|b)?r?'''/,end:/'''/,contains:[e.BACKSLASH_ESCAPE,a],relevance:10},{begin:/(u|b)?r?\"\"\"/,end:/\"\"\"/,contains:[e.BACKSLASH_ESCAPE,a],relevance:10},{begin:/(fr|rf|f)'''/,end:/'''/,contains:[e.BACKSLASH_ESCAPE,a,s,i]},{begin:/(fr|rf|f)\"\"\"/,end:/\"\"\"/,contains:[e.BACKSLASH_ESCAPE,a,s,i]},{begin:/(u|r|ur)'/,end:/'/,relevance:10},{begin:/(u|r|ur)\"/,end:/\"/,relevance:10},{begin:/(b|br)'/,end:/'/},{begin:/(b|br)\"/,end:/\"/},{begin:/(fr|rf|f)'/,end:/'/,contains:[e.BACKSLASH_ESCAPE,s,i]},{begin:/(fr|rf|f)\"/,end:/\"/,contains:[e.BACKSLASH_ESCAPE,s,i]},e.APOS_STRING_MODE,e.QUOTE_STRING_MODE]},l={className:\"number\",relevance:0,variants:[{begin:e.BINARY_NUMBER_RE+\"[lLjJ]?\"},{begin:\"\\\\b(0o[0-7]+)[lLjJ]?\"},{begin:e.C_NUMBER_RE+\"[lLjJ]?\"}]},t={className:\"params\",variants:[{begin:/\\(\\s*\\)/,skip:!0,className:null},{begin:/\\(/,end:/\\)/,excludeBegin:!0,excludeEnd:!0,contains:[\"self\",a,l,r,e.HASH_COMMENT_MODE]}]};return i.contains=[r,l,a],{name:\"Python\",aliases:[\"py\",\"gyp\",\"ipython\"],keywords:n,illegal:/(<\\/|->|\\?)|=>/,contains:[a,l,{beginKeywords:\"if\",relevance:0},r,e.HASH_COMMENT_MODE,{variants:[{className:\"function\",beginKeywords:\"def\"},{className:\"class\",beginKeywords:\"class\"}],end:/:/,illegal:/[${=;\\n,]/,contains:[e.UNDERSCORE_TITLE_MODE,t,{begin:/->/,endsWithParent:!0,keywords:\"None\"}]},{className:\"meta\",begin:/^[\\t ]*@/,end:/$/},{begin:/\\b(print|exec)\\(/}]}}}());\nhljs.registerLanguage(\"python-repl\",function(){\"use strict\";return function(n){return{aliases:[\"pycon\"],contains:[{className:\"meta\",starts:{end:/ |$/,starts:{end:\"$\",subLanguage:\"python\"}},variants:[{begin:/^>>>(?=[ ]|$)/},{begin:/^\\.\\.\\.(?=[ ]|$)/}]}]}}}());\nhljs.registerLanguage(\"ruby\",function(){\"use strict\";return function(e){var n=\"[a-zA-Z_]\\\\w*[!?=]?|[-+~]\\\\@|<<|>>|=~|===?|<=>|[<>]=?|\\\\*\\\\*|[-/+%^&*~`|]|\\\\[\\\\]=?\",a={keyword:\"and then defined module in return redo if BEGIN retry end for self when next until do begin unless END rescue else break undef not super class case require yield alias while ensure elsif or include attr_reader attr_writer attr_accessor\",literal:\"true false nil\"},s={className:\"doctag\",begin:\"@[A-Za-z]+\"},i={begin:\"#<\",end:\">\"},r=[e.COMMENT(\"#\",\"$\",{contains:[s]}),e.COMMENT(\"^\\\\=begin\",\"^\\\\=end\",{contains:[s],relevance:10}),e.COMMENT(\"^__END__\",\"\\\\n$\")],c={className:\"subst\",begin:\"#\\\\{\",end:\"}\",keywords:a},t={className:\"string\",contains:[e.BACKSLASH_ESCAPE,c],variants:[{begin:/'/,end:/'/},{begin:/\"/,end:/\"/},{begin:/`/,end:/`/},{begin:\"%[qQwWx]?\\\\(\",end:\"\\\\)\"},{begin:\"%[qQwWx]?\\\\[\",end:\"\\\\]\"},{begin:\"%[qQwWx]?{\",end:\"}\"},{begin:\"%[qQwWx]?<\",end:\">\"},{begin:\"%[qQwWx]?/\",end:\"/\"},{begin:\"%[qQwWx]?%\",end:\"%\"},{begin:\"%[qQwWx]?-\",end:\"-\"},{begin:\"%[qQwWx]?\\\\|\",end:\"\\\\|\"},{begin:/\\B\\?(\\\\\\d{1,3}|\\\\x[A-Fa-f0-9]{1,2}|\\\\u[A-Fa-f0-9]{4}|\\\\?\\S)\\b/},{begin:/<<[-~]?'?(\\w+)(?:.|\\n)*?\\n\\s*\\1\\b/,returnBegin:!0,contains:[{begin:/<<[-~]?'?/},e.END_SAME_AS_BEGIN({begin:/(\\w+)/,end:/(\\w+)/,contains:[e.BACKSLASH_ESCAPE,c]})]}]},b={className:\"params\",begin:\"\\\\(\",end:\"\\\\)\",endsParent:!0,keywords:a},d=[t,i,{className:\"class\",beginKeywords:\"class module\",end:\"$|;\",illegal:/=/,contains:[e.inherit(e.TITLE_MODE,{begin:\"[A-Za-z_]\\\\w*(::\\\\w+)*(\\\\?|\\\\!)?\"}),{begin:\"<\\\\s*\",contains:[{begin:\"(\"+e.IDENT_RE+\"::)?\"+e.IDENT_RE}]}].concat(r)},{className:\"function\",beginKeywords:\"def\",end:\"$|;\",contains:[e.inherit(e.TITLE_MODE,{begin:n}),b].concat(r)},{begin:e.IDENT_RE+\"::\"},{className:\"symbol\",begin:e.UNDERSCORE_IDENT_RE+\"(\\\\!|\\\\?)?:\",relevance:0},{className:\"symbol\",begin:\":(?!\\\\s)\",contains:[t,{begin:n}],relevance:0},{className:\"number\",begin:\"(\\\\b0[0-7_]+)|(\\\\b0x[0-9a-fA-F_]+)|(\\\\b[1-9][0-9_]*(\\\\.[0-9_]+)?)|[0_]\\\\b\",relevance:0},{begin:\"(\\\\$\\\\W)|((\\\\$|\\\\@\\\\@?)(\\\\w+))\"},{className:\"params\",begin:/\\|/,end:/\\|/,keywords:a},{begin:\"(\"+e.RE_STARTERS_RE+\"|unless)\\\\s*\",keywords:\"unless\",contains:[i,{className:\"regexp\",contains:[e.BACKSLASH_ESCAPE,c],illegal:/\\n/,variants:[{begin:\"/\",end:\"/[a-z]*\"},{begin:\"%r{\",end:\"}[a-z]*\"},{begin:\"%r\\\\(\",end:\"\\\\)[a-z]*\"},{begin:\"%r!\",end:\"![a-z]*\"},{begin:\"%r\\\\[\",end:\"\\\\][a-z]*\"}]}].concat(r),relevance:0}].concat(r);c.contains=d,b.contains=d;var g=[{begin:/^\\s*=>/,starts:{end:\"$\",contains:d}},{className:\"meta\",begin:\"^([>?]>|[\\\\w#]+\\\\(\\\\w+\\\\):\\\\d+:\\\\d+>|(\\\\w+-)?\\\\d+\\\\.\\\\d+\\\\.\\\\d(p\\\\d+)?[^>]+>)\",starts:{end:\"$\",contains:d}}];return{name:\"Ruby\",aliases:[\"rb\",\"gemspec\",\"podspec\",\"thor\",\"irb\"],keywords:a,illegal:/\\/\\*/,contains:r.concat(g).concat(d)}}}());\nhljs.registerLanguage(\"rust\",function(){\"use strict\";return function(e){var n=\"([ui](8|16|32|64|128|size)|f(32|64))?\",t=\"drop i8 i16 i32 i64 i128 isize u8 u16 u32 u64 u128 usize f32 f64 str char bool Box Option Result String Vec Copy Send Sized Sync Drop Fn FnMut FnOnce ToOwned Clone Debug PartialEq PartialOrd Eq Ord AsRef AsMut Into From Default Iterator Extend IntoIterator DoubleEndedIterator ExactSizeIterator SliceConcatExt ToString assert! assert_eq! bitflags! bytes! cfg! col! concat! concat_idents! debug_assert! debug_assert_eq! env! panic! file! format! format_args! include_bin! include_str! line! local_data_key! module_path! option_env! print! println! select! stringify! try! unimplemented! unreachable! vec! write! writeln! macro_rules! assert_ne! debug_assert_ne!\";return{name:\"Rust\",aliases:[\"rs\"],keywords:{$pattern:e.IDENT_RE+\"!?\",keyword:\"abstract as async await become box break const continue crate do dyn else enum extern false final fn for if impl in let loop macro match mod move mut override priv pub ref return self Self static struct super trait true try type typeof unsafe unsized use virtual where while yield\",literal:\"true false Some None Ok Err\",built_in:t},illegal:\"</\",contains:[e.C_LINE_COMMENT_MODE,e.COMMENT(\"/\\\\*\",\"\\\\*/\",{contains:[\"self\"]}),e.inherit(e.QUOTE_STRING_MODE,{begin:/b?\"/,illegal:null}),{className:\"string\",variants:[{begin:/r(#*)\"(.|\\n)*?\"\\1(?!#)/},{begin:/b?'\\\\?(x\\w{2}|u\\w{4}|U\\w{8}|.)'/}]},{className:\"symbol\",begin:/'[a-zA-Z_][a-zA-Z0-9_]*/},{className:\"number\",variants:[{begin:\"\\\\b0b([01_]+)\"+n},{begin:\"\\\\b0o([0-7_]+)\"+n},{begin:\"\\\\b0x([A-Fa-f0-9_]+)\"+n},{begin:\"\\\\b(\\\\d[\\\\d_]*(\\\\.[0-9_]+)?([eE][+-]?[0-9_]+)?)\"+n}],relevance:0},{className:\"function\",beginKeywords:\"fn\",end:\"(\\\\(|<)\",excludeEnd:!0,contains:[e.UNDERSCORE_TITLE_MODE]},{className:\"meta\",begin:\"#\\\\!?\\\\[\",end:\"\\\\]\",contains:[{className:\"meta-string\",begin:/\"/,end:/\"/}]},{className:\"class\",beginKeywords:\"type\",end:\";\",contains:[e.inherit(e.UNDERSCORE_TITLE_MODE,{endsParent:!0})],illegal:\"\\\\S\"},{className:\"class\",beginKeywords:\"trait enum struct union\",end:\"{\",contains:[e.inherit(e.UNDERSCORE_TITLE_MODE,{endsParent:!0})],illegal:\"[\\\\w\\\\d]\"},{begin:e.IDENT_RE+\"::\",keywords:{built_in:t}},{begin:\"->\"}]}}}());\nhljs.registerLanguage(\"scss\",function(){\"use strict\";return function(e){var t={className:\"variable\",begin:\"(\\\\$[a-zA-Z-][a-zA-Z0-9_-]*)\\\\b\"},i={className:\"number\",begin:\"#[0-9A-Fa-f]+\"};return e.CSS_NUMBER_MODE,e.QUOTE_STRING_MODE,e.APOS_STRING_MODE,e.C_BLOCK_COMMENT_MODE,{name:\"SCSS\",case_insensitive:!0,illegal:\"[=/|']\",contains:[e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,{className:\"selector-id\",begin:\"\\\\#[A-Za-z0-9_-]+\",relevance:0},{className:\"selector-class\",begin:\"\\\\.[A-Za-z0-9_-]+\",relevance:0},{className:\"selector-attr\",begin:\"\\\\[\",end:\"\\\\]\",illegal:\"$\"},{className:\"selector-tag\",begin:\"\\\\b(a|abbr|acronym|address|area|article|aside|audio|b|base|big|blockquote|body|br|button|canvas|caption|cite|code|col|colgroup|command|datalist|dd|del|details|dfn|div|dl|dt|em|embed|fieldset|figcaption|figure|footer|form|frame|frameset|(h[1-6])|head|header|hgroup|hr|html|i|iframe|img|input|ins|kbd|keygen|label|legend|li|link|map|mark|meta|meter|nav|noframes|noscript|object|ol|optgroup|option|output|p|param|pre|progress|q|rp|rt|ruby|samp|script|section|select|small|span|strike|strong|style|sub|sup|table|tbody|td|textarea|tfoot|th|thead|time|title|tr|tt|ul|var|video)\\\\b\",relevance:0},{className:\"selector-pseudo\",begin:\":(visited|valid|root|right|required|read-write|read-only|out-range|optional|only-of-type|only-child|nth-of-type|nth-last-of-type|nth-last-child|nth-child|not|link|left|last-of-type|last-child|lang|invalid|indeterminate|in-range|hover|focus|first-of-type|first-line|first-letter|first-child|first|enabled|empty|disabled|default|checked|before|after|active)\"},{className:\"selector-pseudo\",begin:\"::(after|before|choices|first-letter|first-line|repeat-index|repeat-item|selection|value)\"},t,{className:\"attribute\",begin:\"\\\\b(src|z-index|word-wrap|word-spacing|word-break|width|widows|white-space|visibility|vertical-align|unicode-bidi|transition-timing-function|transition-property|transition-duration|transition-delay|transition|transform-style|transform-origin|transform|top|text-underline-position|text-transform|text-shadow|text-rendering|text-overflow|text-indent|text-decoration-style|text-decoration-line|text-decoration-color|text-decoration|text-align-last|text-align|tab-size|table-layout|right|resize|quotes|position|pointer-events|perspective-origin|perspective|page-break-inside|page-break-before|page-break-after|padding-top|padding-right|padding-left|padding-bottom|padding|overflow-y|overflow-x|overflow-wrap|overflow|outline-width|outline-style|outline-offset|outline-color|outline|orphans|order|opacity|object-position|object-fit|normal|none|nav-up|nav-right|nav-left|nav-index|nav-down|min-width|min-height|max-width|max-height|mask|marks|margin-top|margin-right|margin-left|margin-bottom|margin|list-style-type|list-style-position|list-style-image|list-style|line-height|letter-spacing|left|justify-content|initial|inherit|ime-mode|image-orientation|image-resolution|image-rendering|icon|hyphens|height|font-weight|font-variant-ligatures|font-variant|font-style|font-stretch|font-size-adjust|font-size|font-language-override|font-kerning|font-feature-settings|font-family|font|float|flex-wrap|flex-shrink|flex-grow|flex-flow|flex-direction|flex-basis|flex|filter|empty-cells|display|direction|cursor|counter-reset|counter-increment|content|column-width|column-span|column-rule-width|column-rule-style|column-rule-color|column-rule|column-gap|column-fill|column-count|columns|color|clip-path|clip|clear|caption-side|break-inside|break-before|break-after|box-sizing|box-shadow|box-decoration-break|bottom|border-width|border-top-width|border-top-style|border-top-right-radius|border-top-left-radius|border-top-color|border-top|border-style|border-spacing|border-right-width|border-right-style|border-right-color|border-right|border-radius|border-left-width|border-left-style|border-left-color|border-left|border-image-width|border-image-source|border-image-slice|border-image-repeat|border-image-outset|border-image|border-color|border-collapse|border-bottom-width|border-bottom-style|border-bottom-right-radius|border-bottom-left-radius|border-bottom-color|border-bottom|border|background-size|background-repeat|background-position|background-origin|background-image|background-color|background-clip|background-attachment|background-blend-mode|background|backface-visibility|auto|animation-timing-function|animation-play-state|animation-name|animation-iteration-count|animation-fill-mode|animation-duration|animation-direction|animation-delay|animation|align-self|align-items|align-content)\\\\b\",illegal:\"[^\\\\s]\"},{begin:\"\\\\b(whitespace|wait|w-resize|visible|vertical-text|vertical-ideographic|uppercase|upper-roman|upper-alpha|underline|transparent|top|thin|thick|text|text-top|text-bottom|tb-rl|table-header-group|table-footer-group|sw-resize|super|strict|static|square|solid|small-caps|separate|se-resize|scroll|s-resize|rtl|row-resize|ridge|right|repeat|repeat-y|repeat-x|relative|progress|pointer|overline|outside|outset|oblique|nowrap|not-allowed|normal|none|nw-resize|no-repeat|no-drop|newspaper|ne-resize|n-resize|move|middle|medium|ltr|lr-tb|lowercase|lower-roman|lower-alpha|loose|list-item|line|line-through|line-edge|lighter|left|keep-all|justify|italic|inter-word|inter-ideograph|inside|inset|inline|inline-block|inherit|inactive|ideograph-space|ideograph-parenthesis|ideograph-numeric|ideograph-alpha|horizontal|hidden|help|hand|groove|fixed|ellipsis|e-resize|double|dotted|distribute|distribute-space|distribute-letter|distribute-all-lines|disc|disabled|default|decimal|dashed|crosshair|collapse|col-resize|circle|char|center|capitalize|break-word|break-all|bottom|both|bolder|bold|block|bidi-override|below|baseline|auto|always|all-scroll|absolute|table|table-cell)\\\\b\"},{begin:\":\",end:\";\",contains:[t,i,e.CSS_NUMBER_MODE,e.QUOTE_STRING_MODE,e.APOS_STRING_MODE,{className:\"meta\",begin:\"!important\"}]},{begin:\"@(page|font-face)\",lexemes:\"@[a-z-]+\",keywords:\"@page @font-face\"},{begin:\"@\",end:\"[{;]\",returnBegin:!0,keywords:\"and or not only\",contains:[{begin:\"@[a-z-]+\",className:\"keyword\"},t,e.QUOTE_STRING_MODE,e.APOS_STRING_MODE,i,e.CSS_NUMBER_MODE]}]}}}());\nhljs.registerLanguage(\"shell\",function(){\"use strict\";return function(s){return{name:\"Shell Session\",aliases:[\"console\"],contains:[{className:\"meta\",begin:\"^\\\\s{0,3}[/\\\\w\\\\d\\\\[\\\\]()@-]*[>%$#]\",starts:{end:\"$\",subLanguage:\"bash\"}}]}}}());\nhljs.registerLanguage(\"sql\",function(){\"use strict\";return function(e){var t=e.COMMENT(\"--\",\"$\");return{name:\"SQL\",case_insensitive:!0,illegal:/[<>{}*]/,contains:[{beginKeywords:\"begin end start commit rollback savepoint lock alter create drop rename call delete do handler insert load replace select truncate update set show pragma grant merge describe use explain help declare prepare execute deallocate release unlock purge reset change stop analyze cache flush optimize repair kill install uninstall checksum restore check backup revoke comment values with\",end:/;/,endsWithParent:!0,keywords:{$pattern:/[\\w\\.]+/,keyword:\"as abort abs absolute acc acce accep accept access accessed accessible account acos action activate add addtime admin administer advanced advise aes_decrypt aes_encrypt after agent aggregate ali alia alias all allocate allow alter always analyze ancillary and anti any anydata anydataset anyschema anytype apply archive archived archivelog are as asc ascii asin assembly assertion associate asynchronous at atan atn2 attr attri attrib attribu attribut attribute attributes audit authenticated authentication authid authors auto autoallocate autodblink autoextend automatic availability avg backup badfile basicfile before begin beginning benchmark between bfile bfile_base big bigfile bin binary_double binary_float binlog bit_and bit_count bit_length bit_or bit_xor bitmap blob_base block blocksize body both bound bucket buffer_cache buffer_pool build bulk by byte byteordermark bytes cache caching call calling cancel capacity cascade cascaded case cast catalog category ceil ceiling chain change changed char_base char_length character_length characters characterset charindex charset charsetform charsetid check checksum checksum_agg child choose chr chunk class cleanup clear client clob clob_base clone close cluster_id cluster_probability cluster_set clustering coalesce coercibility col collate collation collect colu colum column column_value columns columns_updated comment commit compact compatibility compiled complete composite_limit compound compress compute concat concat_ws concurrent confirm conn connec connect connect_by_iscycle connect_by_isleaf connect_by_root connect_time connection consider consistent constant constraint constraints constructor container content contents context contributors controlfile conv convert convert_tz corr corr_k corr_s corresponding corruption cos cost count count_big counted covar_pop covar_samp cpu_per_call cpu_per_session crc32 create creation critical cross cube cume_dist curdate current current_date current_time current_timestamp current_user cursor curtime customdatum cycle data database databases datafile datafiles datalength date_add date_cache date_format date_sub dateadd datediff datefromparts datename datepart datetime2fromparts day day_to_second dayname dayofmonth dayofweek dayofyear days db_role_change dbtimezone ddl deallocate declare decode decompose decrement decrypt deduplicate def defa defau defaul default defaults deferred defi defin define degrees delayed delegate delete delete_all delimited demand dense_rank depth dequeue des_decrypt des_encrypt des_key_file desc descr descri describ describe descriptor deterministic diagnostics difference dimension direct_load directory disable disable_all disallow disassociate discardfile disconnect diskgroup distinct distinctrow distribute distributed div do document domain dotnet double downgrade drop dumpfile duplicate duration each edition editionable editions element ellipsis else elsif elt empty enable enable_all enclosed encode encoding encrypt end end-exec endian enforced engine engines enqueue enterprise entityescaping eomonth error errors escaped evalname evaluate event eventdata events except exception exceptions exchange exclude excluding execu execut execute exempt exists exit exp expire explain explode export export_set extended extent external external_1 external_2 externally extract failed failed_login_attempts failover failure far fast feature_set feature_value fetch field fields file file_name_convert filesystem_like_logging final finish first first_value fixed flash_cache flashback floor flush following follows for forall force foreign form forma format found found_rows freelist freelists freepools fresh from from_base64 from_days ftp full function general generated get get_format get_lock getdate getutcdate global global_name globally go goto grant grants greatest group group_concat group_id grouping grouping_id groups gtid_subtract guarantee guard handler hash hashkeys having hea head headi headin heading heap help hex hierarchy high high_priority hosts hour hours http id ident_current ident_incr ident_seed identified identity idle_time if ifnull ignore iif ilike ilm immediate import in include including increment index indexes indexing indextype indicator indices inet6_aton inet6_ntoa inet_aton inet_ntoa infile initial initialized initially initrans inmemory inner innodb input insert install instance instantiable instr interface interleaved intersect into invalidate invisible is is_free_lock is_ipv4 is_ipv4_compat is_not is_not_null is_used_lock isdate isnull isolation iterate java join json json_exists keep keep_duplicates key keys kill language large last last_day last_insert_id last_value lateral lax lcase lead leading least leaves left len lenght length less level levels library like like2 like4 likec limit lines link list listagg little ln load load_file lob lobs local localtime localtimestamp locate locator lock locked log log10 log2 logfile logfiles logging logical logical_reads_per_call logoff logon logs long loop low low_priority lower lpad lrtrim ltrim main make_set makedate maketime managed management manual map mapping mask master master_pos_wait match matched materialized max maxextents maximize maxinstances maxlen maxlogfiles maxloghistory maxlogmembers maxsize maxtrans md5 measures median medium member memcompress memory merge microsecond mid migration min minextents minimum mining minus minute minutes minvalue missing mod mode model modification modify module monitoring month months mount move movement multiset mutex name name_const names nan national native natural nav nchar nclob nested never new newline next nextval no no_write_to_binlog noarchivelog noaudit nobadfile nocheck nocompress nocopy nocycle nodelay nodiscardfile noentityescaping noguarantee nokeep nologfile nomapping nomaxvalue nominimize nominvalue nomonitoring none noneditionable nonschema noorder nopr nopro noprom nopromp noprompt norely noresetlogs noreverse normal norowdependencies noschemacheck noswitch not nothing notice notnull notrim novalidate now nowait nth_value nullif nulls num numb numbe nvarchar nvarchar2 object ocicoll ocidate ocidatetime ociduration ociinterval ociloblocator ocinumber ociref ocirefcursor ocirowid ocistring ocitype oct octet_length of off offline offset oid oidindex old on online only opaque open operations operator optimal optimize option optionally or oracle oracle_date oradata ord ordaudio orddicom orddoc order ordimage ordinality ordvideo organization orlany orlvary out outer outfile outline output over overflow overriding package pad parallel parallel_enable parameters parent parse partial partition partitions pascal passing password password_grace_time password_lock_time password_reuse_max password_reuse_time password_verify_function patch path patindex pctincrease pctthreshold pctused pctversion percent percent_rank percentile_cont percentile_disc performance period period_add period_diff permanent physical pi pipe pipelined pivot pluggable plugin policy position post_transaction pow power pragma prebuilt precedes preceding precision prediction prediction_cost prediction_details prediction_probability prediction_set prepare present preserve prior priority private private_sga privileges procedural procedure procedure_analyze processlist profiles project prompt protection public publishingservername purge quarter query quick quiesce quota quotename radians raise rand range rank raw read reads readsize rebuild record records recover recovery recursive recycle redo reduced ref reference referenced references referencing refresh regexp_like register regr_avgx regr_avgy regr_count regr_intercept regr_r2 regr_slope regr_sxx regr_sxy reject rekey relational relative relaylog release release_lock relies_on relocate rely rem remainder rename repair repeat replace replicate replication required reset resetlogs resize resource respect restore restricted result result_cache resumable resume retention return returning returns reuse reverse revoke right rlike role roles rollback rolling rollup round row row_count rowdependencies rowid rownum rows rtrim rules safe salt sample save savepoint sb1 sb2 sb4 scan schema schemacheck scn scope scroll sdo_georaster sdo_topo_geometry search sec_to_time second seconds section securefile security seed segment select self semi sequence sequential serializable server servererror session session_user sessions_per_user set sets settings sha sha1 sha2 share shared shared_pool short show shrink shutdown si_averagecolor si_colorhistogram si_featurelist si_positionalcolor si_stillimage si_texture siblings sid sign sin size size_t sizes skip slave sleep smalldatetimefromparts smallfile snapshot some soname sort soundex source space sparse spfile split sql sql_big_result sql_buffer_result sql_cache sql_calc_found_rows sql_small_result sql_variant_property sqlcode sqldata sqlerror sqlname sqlstate sqrt square standalone standby start starting startup statement static statistics stats_binomial_test stats_crosstab stats_ks_test stats_mode stats_mw_test stats_one_way_anova stats_t_test_ stats_t_test_indep stats_t_test_one stats_t_test_paired stats_wsr_test status std stddev stddev_pop stddev_samp stdev stop storage store stored str str_to_date straight_join strcmp strict string struct stuff style subdate subpartition subpartitions substitutable substr substring subtime subtring_index subtype success sum suspend switch switchoffset switchover sync synchronous synonym sys sys_xmlagg sysasm sysaux sysdate sysdatetimeoffset sysdba sysoper system system_user sysutcdatetime table tables tablespace tablesample tan tdo template temporary terminated tertiary_weights test than then thread through tier ties time time_format time_zone timediff timefromparts timeout timestamp timestampadd timestampdiff timezone_abbr timezone_minute timezone_region to to_base64 to_date to_days to_seconds todatetimeoffset trace tracking transaction transactional translate translation treat trigger trigger_nestlevel triggers trim truncate try_cast try_convert try_parse type ub1 ub2 ub4 ucase unarchived unbounded uncompress under undo unhex unicode uniform uninstall union unique unix_timestamp unknown unlimited unlock unnest unpivot unrecoverable unsafe unsigned until untrusted unusable unused update updated upgrade upped upper upsert url urowid usable usage use use_stored_outlines user user_data user_resources users using utc_date utc_timestamp uuid uuid_short validate validate_password_strength validation valist value values var var_samp varcharc vari varia variab variabl variable variables variance varp varraw varrawc varray verify version versions view virtual visible void wait wallet warning warnings week weekday weekofyear wellformed when whene whenev wheneve whenever where while whitespace window with within without work wrapped xdb xml xmlagg xmlattributes xmlcast xmlcolattval xmlelement xmlexists xmlforest xmlindex xmlnamespaces xmlpi xmlquery xmlroot xmlschema xmlserialize xmltable xmltype xor year year_to_month years yearweek\",literal:\"true false null unknown\",built_in:\"array bigint binary bit blob bool boolean char character date dec decimal float int int8 integer interval number numeric real record serial serial8 smallint text time timestamp tinyint varchar varchar2 varying void\"},contains:[{className:\"string\",begin:\"'\",end:\"'\",contains:[{begin:\"''\"}]},{className:\"string\",begin:'\"',end:'\"',contains:[{begin:'\"\"'}]},{className:\"string\",begin:\"`\",end:\"`\"},e.C_NUMBER_MODE,e.C_BLOCK_COMMENT_MODE,t,e.HASH_COMMENT_MODE]},e.C_BLOCK_COMMENT_MODE,t,e.HASH_COMMENT_MODE]}}}());\nhljs.registerLanguage(\"swift\",function(){\"use strict\";return function(e){var i={keyword:\"#available #colorLiteral #column #else #elseif #endif #file #fileLiteral #function #if #imageLiteral #line #selector #sourceLocation _ __COLUMN__ __FILE__ __FUNCTION__ __LINE__ Any as as! as? associatedtype associativity break case catch class continue convenience default defer deinit didSet do dynamic dynamicType else enum extension fallthrough false fileprivate final for func get guard if import in indirect infix init inout internal is lazy left let mutating nil none nonmutating open operator optional override postfix precedence prefix private protocol Protocol public repeat required rethrows return right self Self set static struct subscript super switch throw throws true try try! try? Type typealias unowned var weak where while willSet\",literal:\"true false nil\",built_in:\"abs advance alignof alignofValue anyGenerator assert assertionFailure bridgeFromObjectiveC bridgeFromObjectiveCUnconditional bridgeToObjectiveC bridgeToObjectiveCUnconditional c compactMap contains count countElements countLeadingZeros debugPrint debugPrintln distance dropFirst dropLast dump encodeBitsAsWords enumerate equal fatalError filter find getBridgedObjectiveCType getVaList indices insertionSort isBridgedToObjectiveC isBridgedVerbatimToObjectiveC isUniquelyReferenced isUniquelyReferencedNonObjC join lazy lexicographicalCompare map max maxElement min minElement numericCast overlaps partition posix precondition preconditionFailure print println quickSort readLine reduce reflect reinterpretCast reverse roundUpToAlignment sizeof sizeofValue sort split startsWith stride strideof strideofValue swap toString transcode underestimateCount unsafeAddressOf unsafeBitCast unsafeDowncast unsafeUnwrap unsafeReflect withExtendedLifetime withObjectAtPlusZero withUnsafePointer withUnsafePointerToObject withUnsafeMutablePointer withUnsafeMutablePointers withUnsafePointer withUnsafePointers withVaList zip\"},n=e.COMMENT(\"/\\\\*\",\"\\\\*/\",{contains:[\"self\"]}),t={className:\"subst\",begin:/\\\\\\(/,end:\"\\\\)\",keywords:i,contains:[]},a={className:\"string\",contains:[e.BACKSLASH_ESCAPE,t],variants:[{begin:/\"\"\"/,end:/\"\"\"/},{begin:/\"/,end:/\"/}]},r={className:\"number\",begin:\"\\\\b([\\\\d_]+(\\\\.[\\\\deE_]+)?|0x[a-fA-F0-9_]+(\\\\.[a-fA-F0-9p_]+)?|0b[01_]+|0o[0-7_]+)\\\\b\",relevance:0};return t.contains=[r],{name:\"Swift\",keywords:i,contains:[a,e.C_LINE_COMMENT_MODE,n,{className:\"type\",begin:\"\\\\b[A-Z][\\\\wÀ-ʸ']*[!?]\"},{className:\"type\",begin:\"\\\\b[A-Z][\\\\wÀ-ʸ']*\",relevance:0},r,{className:\"function\",beginKeywords:\"func\",end:\"{\",excludeEnd:!0,contains:[e.inherit(e.TITLE_MODE,{begin:/[A-Za-z$_][0-9A-Za-z$_]*/}),{begin:/</,end:/>/},{className:\"params\",begin:/\\(/,end:/\\)/,endsParent:!0,keywords:i,contains:[\"self\",r,a,e.C_BLOCK_COMMENT_MODE,{begin:\":\"}],illegal:/[\"']/}],illegal:/\\[|%/},{className:\"class\",beginKeywords:\"struct protocol class extension enum\",keywords:i,end:\"\\\\{\",excludeEnd:!0,contains:[e.inherit(e.TITLE_MODE,{begin:/[A-Za-z$_][\\u00C0-\\u02B80-9A-Za-z$_]*/})]},{className:\"meta\",begin:\"(@discardableResult|@warn_unused_result|@exported|@lazy|@noescape|@NSCopying|@NSManaged|@objc|@objcMembers|@convention|@required|@noreturn|@IBAction|@IBDesignable|@IBInspectable|@IBOutlet|@infix|@prefix|@postfix|@autoclosure|@testable|@available|@nonobjc|@NSApplicationMain|@UIApplicationMain|@dynamicMemberLookup|@propertyWrapper)\\\\b\"},{beginKeywords:\"import\",end:/$/,contains:[e.C_LINE_COMMENT_MODE,n]}]}}}());\nhljs.registerLanguage(\"typescript\",function(){\"use strict\";const e=[\"as\",\"in\",\"of\",\"if\",\"for\",\"while\",\"finally\",\"var\",\"new\",\"function\",\"do\",\"return\",\"void\",\"else\",\"break\",\"catch\",\"instanceof\",\"with\",\"throw\",\"case\",\"default\",\"try\",\"switch\",\"continue\",\"typeof\",\"delete\",\"let\",\"yield\",\"const\",\"class\",\"debugger\",\"async\",\"await\",\"static\",\"import\",\"from\",\"export\",\"extends\"],n=[\"true\",\"false\",\"null\",\"undefined\",\"NaN\",\"Infinity\"],a=[].concat([\"setInterval\",\"setTimeout\",\"clearInterval\",\"clearTimeout\",\"require\",\"exports\",\"eval\",\"isFinite\",\"isNaN\",\"parseFloat\",\"parseInt\",\"decodeURI\",\"decodeURIComponent\",\"encodeURI\",\"encodeURIComponent\",\"escape\",\"unescape\"],[\"arguments\",\"this\",\"super\",\"console\",\"window\",\"document\",\"localStorage\",\"module\",\"global\"],[\"Intl\",\"DataView\",\"Number\",\"Math\",\"Date\",\"String\",\"RegExp\",\"Object\",\"Function\",\"Boolean\",\"Error\",\"Symbol\",\"Set\",\"Map\",\"WeakSet\",\"WeakMap\",\"Proxy\",\"Reflect\",\"JSON\",\"Promise\",\"Float64Array\",\"Int16Array\",\"Int32Array\",\"Int8Array\",\"Uint16Array\",\"Uint32Array\",\"Float32Array\",\"Array\",\"Uint8Array\",\"Uint8ClampedArray\",\"ArrayBuffer\"],[\"EvalError\",\"InternalError\",\"RangeError\",\"ReferenceError\",\"SyntaxError\",\"TypeError\",\"URIError\"]);return function(r){var t={$pattern:\"[A-Za-z$_][0-9A-Za-z$_]*\",keyword:e.concat([\"type\",\"namespace\",\"typedef\",\"interface\",\"public\",\"private\",\"protected\",\"implements\",\"declare\",\"abstract\",\"readonly\"]).join(\" \"),literal:n.join(\" \"),built_in:a.concat([\"any\",\"void\",\"number\",\"boolean\",\"string\",\"object\",\"never\",\"enum\"]).join(\" \")},s={className:\"meta\",begin:\"@[A-Za-z$_][0-9A-Za-z$_]*\"},i={className:\"number\",variants:[{begin:\"\\\\b(0[bB][01]+)n?\"},{begin:\"\\\\b(0[oO][0-7]+)n?\"},{begin:r.C_NUMBER_RE+\"n?\"}],relevance:0},o={className:\"subst\",begin:\"\\\\$\\\\{\",end:\"\\\\}\",keywords:t,contains:[]},c={begin:\"html`\",end:\"\",starts:{end:\"`\",returnEnd:!1,contains:[r.BACKSLASH_ESCAPE,o],subLanguage:\"xml\"}},l={begin:\"css`\",end:\"\",starts:{end:\"`\",returnEnd:!1,contains:[r.BACKSLASH_ESCAPE,o],subLanguage:\"css\"}},E={className:\"string\",begin:\"`\",end:\"`\",contains:[r.BACKSLASH_ESCAPE,o]};o.contains=[r.APOS_STRING_MODE,r.QUOTE_STRING_MODE,c,l,E,i,r.REGEXP_MODE];var d={begin:\"\\\\(\",end:/\\)/,keywords:t,contains:[\"self\",r.QUOTE_STRING_MODE,r.APOS_STRING_MODE,r.NUMBER_MODE]},u={className:\"params\",begin:/\\(/,end:/\\)/,excludeBegin:!0,excludeEnd:!0,keywords:t,contains:[r.C_LINE_COMMENT_MODE,r.C_BLOCK_COMMENT_MODE,s,d]};return{name:\"TypeScript\",aliases:[\"ts\"],keywords:t,contains:[r.SHEBANG(),{className:\"meta\",begin:/^\\s*['\"]use strict['\"]/},r.APOS_STRING_MODE,r.QUOTE_STRING_MODE,c,l,E,r.C_LINE_COMMENT_MODE,r.C_BLOCK_COMMENT_MODE,i,{begin:\"(\"+r.RE_STARTERS_RE+\"|\\\\b(case|return|throw)\\\\b)\\\\s*\",keywords:\"return throw case\",contains:[r.C_LINE_COMMENT_MODE,r.C_BLOCK_COMMENT_MODE,r.REGEXP_MODE,{className:\"function\",begin:\"(\\\\([^(]*(\\\\([^(]*(\\\\([^(]*\\\\))?\\\\))?\\\\)|\"+r.UNDERSCORE_IDENT_RE+\")\\\\s*=>\",returnBegin:!0,end:\"\\\\s*=>\",contains:[{className:\"params\",variants:[{begin:r.UNDERSCORE_IDENT_RE},{className:null,begin:/\\(\\s*\\)/,skip:!0},{begin:/\\(/,end:/\\)/,excludeBegin:!0,excludeEnd:!0,keywords:t,contains:d.contains}]}]}],relevance:0},{className:\"function\",beginKeywords:\"function\",end:/[\\{;]/,excludeEnd:!0,keywords:t,contains:[\"self\",r.inherit(r.TITLE_MODE,{begin:\"[A-Za-z$_][0-9A-Za-z$_]*\"}),u],illegal:/%/,relevance:0},{beginKeywords:\"constructor\",end:/[\\{;]/,excludeEnd:!0,contains:[\"self\",u]},{begin:/module\\./,keywords:{built_in:\"module\"},relevance:0},{beginKeywords:\"module\",end:/\\{/,excludeEnd:!0},{beginKeywords:\"interface\",end:/\\{/,excludeEnd:!0,keywords:\"interface extends\"},{begin:/\\$[(.]/},{begin:\"\\\\.\"+r.IDENT_RE,relevance:0},s,d]}}}());\nhljs.registerLanguage(\"yaml\",function(){\"use strict\";return function(e){var n=\"true false yes no null\",a=\"[\\\\w#;/?:@&=+$,.~*\\\\'()[\\\\]]+\",s={className:\"string\",relevance:0,variants:[{begin:/'/,end:/'/},{begin:/\"/,end:/\"/},{begin:/\\S+/}],contains:[e.BACKSLASH_ESCAPE,{className:\"template-variable\",variants:[{begin:\"{{\",end:\"}}\"},{begin:\"%{\",end:\"}\"}]}]},i=e.inherit(s,{variants:[{begin:/'/,end:/'/},{begin:/\"/,end:/\"/},{begin:/[^\\s,{}[\\]]+/}]}),l={end:\",\",endsWithParent:!0,excludeEnd:!0,contains:[],keywords:n,relevance:0},t={begin:\"{\",end:\"}\",contains:[l],illegal:\"\\\\n\",relevance:0},g={begin:\"\\\\[\",end:\"\\\\]\",contains:[l],illegal:\"\\\\n\",relevance:0},b=[{className:\"attr\",variants:[{begin:\"\\\\w[\\\\w :\\\\/.-]*:(?=[ \\t]|$)\"},{begin:'\"\\\\w[\\\\w :\\\\/.-]*\":(?=[ \\t]|$)'},{begin:\"'\\\\w[\\\\w :\\\\/.-]*':(?=[ \\t]|$)\"}]},{className:\"meta\",begin:\"^---s*$\",relevance:10},{className:\"string\",begin:\"[\\\\|>]([0-9]?[+-])?[ ]*\\\\n( *)[\\\\S ]+\\\\n(\\\\2[\\\\S ]+\\\\n?)*\"},{begin:\"<%[%=-]?\",end:\"[%-]?%>\",subLanguage:\"ruby\",excludeBegin:!0,excludeEnd:!0,relevance:0},{className:\"type\",begin:\"!\\\\w+!\"+a},{className:\"type\",begin:\"!<\"+a+\">\"},{className:\"type\",begin:\"!\"+a},{className:\"type\",begin:\"!!\"+a},{className:\"meta\",begin:\"&\"+e.UNDERSCORE_IDENT_RE+\"$\"},{className:\"meta\",begin:\"\\\\*\"+e.UNDERSCORE_IDENT_RE+\"$\"},{className:\"bullet\",begin:\"\\\\-(?=[ ]|$)\",relevance:0},e.HASH_COMMENT_MODE,{beginKeywords:n,keywords:{literal:n}},{className:\"number\",begin:\"\\\\b[0-9]{4}(-[0-9][0-9]){0,2}([Tt \\\\t][0-9][0-9]?(:[0-9][0-9]){2})?(\\\\.[0-9]*)?([ \\\\t])*(Z|[-+][0-9][0-9]?(:[0-9][0-9])?)?\\\\b\"},{className:\"number\",begin:e.C_NUMBER_RE+\"\\\\b\"},t,g,s],c=[...b];return c.pop(),c.push(i),l.contains=c,{name:\"YAML\",case_insensitive:!0,aliases:[\"yml\",\"YAML\"],contains:b}}}());\nhljs.registerLanguage(\"armasm\",function(){\"use strict\";return function(s){const e={variants:[s.COMMENT(\"^[ \\\\t]*(?=#)\",\"$\",{relevance:0,excludeBegin:!0}),s.COMMENT(\"[;@]\",\"$\",{relevance:0}),s.C_LINE_COMMENT_MODE,s.C_BLOCK_COMMENT_MODE]};return{name:\"ARM Assembly\",case_insensitive:!0,aliases:[\"arm\"],keywords:{$pattern:\"\\\\.?\"+s.IDENT_RE,meta:\".2byte .4byte .align .ascii .asciz .balign .byte .code .data .else .end .endif .endm .endr .equ .err .exitm .extern .global .hword .if .ifdef .ifndef .include .irp .long .macro .rept .req .section .set .skip .space .text .word .arm .thumb .code16 .code32 .force_thumb .thumb_func .ltorg ALIAS ALIGN ARM AREA ASSERT ATTR CN CODE CODE16 CODE32 COMMON CP DATA DCB DCD DCDU DCDO DCFD DCFDU DCI DCQ DCQU DCW DCWU DN ELIF ELSE END ENDFUNC ENDIF ENDP ENTRY EQU EXPORT EXPORTAS EXTERN FIELD FILL FUNCTION GBLA GBLL GBLS GET GLOBAL IF IMPORT INCBIN INCLUDE INFO KEEP LCLA LCLL LCLS LTORG MACRO MAP MEND MEXIT NOFP OPT PRESERVE8 PROC QN READONLY RELOC REQUIRE REQUIRE8 RLIST FN ROUT SETA SETL SETS SN SPACE SUBT THUMB THUMBX TTL WHILE WEND \",built_in:\"r0 r1 r2 r3 r4 r5 r6 r7 r8 r9 r10 r11 r12 r13 r14 r15 pc lr sp ip sl sb fp a1 a2 a3 a4 v1 v2 v3 v4 v5 v6 v7 v8 f0 f1 f2 f3 f4 f5 f6 f7 p0 p1 p2 p3 p4 p5 p6 p7 p8 p9 p10 p11 p12 p13 p14 p15 c0 c1 c2 c3 c4 c5 c6 c7 c8 c9 c10 c11 c12 c13 c14 c15 q0 q1 q2 q3 q4 q5 q6 q7 q8 q9 q10 q11 q12 q13 q14 q15 cpsr_c cpsr_x cpsr_s cpsr_f cpsr_cx cpsr_cxs cpsr_xs cpsr_xsf cpsr_sf cpsr_cxsf spsr_c spsr_x spsr_s spsr_f spsr_cx spsr_cxs spsr_xs spsr_xsf spsr_sf spsr_cxsf s0 s1 s2 s3 s4 s5 s6 s7 s8 s9 s10 s11 s12 s13 s14 s15 s16 s17 s18 s19 s20 s21 s22 s23 s24 s25 s26 s27 s28 s29 s30 s31 d0 d1 d2 d3 d4 d5 d6 d7 d8 d9 d10 d11 d12 d13 d14 d15 d16 d17 d18 d19 d20 d21 d22 d23 d24 d25 d26 d27 d28 d29 d30 d31 {PC} {VAR} {TRUE} {FALSE} {OPT} {CONFIG} {ENDIAN} {CODESIZE} {CPU} {FPU} {ARCHITECTURE} {PCSTOREOFFSET} {ARMASM_VERSION} {INTER} {ROPI} {RWPI} {SWST} {NOSWST} . @\"},contains:[{className:\"keyword\",begin:\"\\\\b(adc|(qd?|sh?|u[qh]?)?add(8|16)?|usada?8|(q|sh?|u[qh]?)?(as|sa)x|and|adrl?|sbc|rs[bc]|asr|b[lx]?|blx|bxj|cbn?z|tb[bh]|bic|bfc|bfi|[su]bfx|bkpt|cdp2?|clz|clrex|cmp|cmn|cpsi[ed]|cps|setend|dbg|dmb|dsb|eor|isb|it[te]{0,3}|lsl|lsr|ror|rrx|ldm(([id][ab])|f[ds])?|ldr((s|ex)?[bhd])?|movt?|mvn|mra|mar|mul|[us]mull|smul[bwt][bt]|smu[as]d|smmul|smmla|mla|umlaal|smlal?([wbt][bt]|d)|mls|smlsl?[ds]|smc|svc|sev|mia([bt]{2}|ph)?|mrr?c2?|mcrr2?|mrs|msr|orr|orn|pkh(tb|bt)|rbit|rev(16|sh)?|sel|[su]sat(16)?|nop|pop|push|rfe([id][ab])?|stm([id][ab])?|str(ex)?[bhd]?|(qd?)?sub|(sh?|q|u[qh]?)?sub(8|16)|[su]xt(a?h|a?b(16)?)|srs([id][ab])?|swpb?|swi|smi|tst|teq|wfe|wfi|yield)(eq|ne|cs|cc|mi|pl|vs|vc|hi|ls|ge|lt|gt|le|al|hs|lo)?[sptrx]?(?=\\\\s)\"},e,s.QUOTE_STRING_MODE,{className:\"string\",begin:\"'\",end:\"[^\\\\\\\\]'\",relevance:0},{className:\"title\",begin:\"\\\\|\",end:\"\\\\|\",illegal:\"\\\\n\",relevance:0},{className:\"number\",variants:[{begin:\"[#$=]?0x[0-9a-f]+\"},{begin:\"[#$=]?0b[01]+\"},{begin:\"[#$=]\\\\d+\"},{begin:\"\\\\b\\\\d+\"}],relevance:0},{className:\"symbol\",variants:[{begin:\"^[ \\\\t]*[a-z_\\\\.\\\\$][a-z0-9_\\\\.\\\\$]+:\"},{begin:\"^[a-z_\\\\.\\\\$][a-z0-9_\\\\.\\\\$]+\"},{begin:\"[=#]\\\\w+\"}],relevance:0}]}}}());\nhljs.registerLanguage(\"d\",function(){\"use strict\";return function(e){var a={$pattern:e.UNDERSCORE_IDENT_RE,keyword:\"abstract alias align asm assert auto body break byte case cast catch class const continue debug default delete deprecated do else enum export extern final finally for foreach foreach_reverse|10 goto if immutable import in inout int interface invariant is lazy macro mixin module new nothrow out override package pragma private protected public pure ref return scope shared static struct super switch synchronized template this throw try typedef typeid typeof union unittest version void volatile while with __FILE__ __LINE__ __gshared|10 __thread __traits __DATE__ __EOF__ __TIME__ __TIMESTAMP__ __VENDOR__ __VERSION__\",built_in:\"bool cdouble cent cfloat char creal dchar delegate double dstring float function idouble ifloat ireal long real short string ubyte ucent uint ulong ushort wchar wstring\",literal:\"false null true\"},d=\"((0|[1-9][\\\\d_]*)|0[bB][01_]+|0[xX]([\\\\da-fA-F][\\\\da-fA-F_]*|_[\\\\da-fA-F][\\\\da-fA-F_]*))\",n=\"\\\\\\\\(['\\\"\\\\?\\\\\\\\abfnrtv]|u[\\\\dA-Fa-f]{4}|[0-7]{1,3}|x[\\\\dA-Fa-f]{2}|U[\\\\dA-Fa-f]{8})|&[a-zA-Z\\\\d]{2,};\",t={className:\"number\",begin:\"\\\\b\"+d+\"(L|u|U|Lu|LU|uL|UL)?\",relevance:0},_={className:\"number\",begin:\"\\\\b(((0[xX](([\\\\da-fA-F][\\\\da-fA-F_]*|_[\\\\da-fA-F][\\\\da-fA-F_]*)\\\\.([\\\\da-fA-F][\\\\da-fA-F_]*|_[\\\\da-fA-F][\\\\da-fA-F_]*)|\\\\.?([\\\\da-fA-F][\\\\da-fA-F_]*|_[\\\\da-fA-F][\\\\da-fA-F_]*))[pP][+-]?(0|[1-9][\\\\d_]*|\\\\d[\\\\d_]*|[\\\\d_]+?\\\\d))|((0|[1-9][\\\\d_]*|\\\\d[\\\\d_]*|[\\\\d_]+?\\\\d)(\\\\.\\\\d*|([eE][+-]?(0|[1-9][\\\\d_]*|\\\\d[\\\\d_]*|[\\\\d_]+?\\\\d)))|\\\\d+\\\\.(0|[1-9][\\\\d_]*|\\\\d[\\\\d_]*|[\\\\d_]+?\\\\d)(0|[1-9][\\\\d_]*|\\\\d[\\\\d_]*|[\\\\d_]+?\\\\d)|\\\\.(0|[1-9][\\\\d_]*)([eE][+-]?(0|[1-9][\\\\d_]*|\\\\d[\\\\d_]*|[\\\\d_]+?\\\\d))?))([fF]|L|i|[fF]i|Li)?|\"+d+\"(i|[fF]i|Li))\",relevance:0},r={className:\"string\",begin:\"'(\"+n+\"|.)\",end:\"'\",illegal:\".\"},i={className:\"string\",begin:'\"',contains:[{begin:n,relevance:0}],end:'\"[cwd]?'},s=e.COMMENT(\"\\\\/\\\\+\",\"\\\\+\\\\/\",{contains:[\"self\"],relevance:10});return{name:\"D\",keywords:a,contains:[e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,s,{className:\"string\",begin:'x\"[\\\\da-fA-F\\\\s\\\\n\\\\r]*\"[cwd]?',relevance:10},i,{className:\"string\",begin:'[rq]\"',end:'\"[cwd]?',relevance:5},{className:\"string\",begin:\"`\",end:\"`[cwd]?\"},{className:\"string\",begin:'q\"\\\\{',end:'\\\\}\"'},_,t,r,{className:\"meta\",begin:\"^#!\",end:\"$\",relevance:5},{className:\"meta\",begin:\"#(line)\",end:\"$\",relevance:5},{className:\"keyword\",begin:\"@[a-zA-Z_][a-zA-Z_\\\\d]*\"}]}}}());\nhljs.registerLanguage(\"handlebars\",function(){\"use strict\";function e(...e){return e.map(e=>(function(e){return e?\"string\"==typeof e?e:e.source:null})(e)).join(\"\")}return function(n){const a={\"builtin-name\":\"action bindattr collection component concat debugger each each-in get hash if in input link-to loc log lookup mut outlet partial query-params render template textarea unbound unless view with yield\"},t=/\\[.*?\\]/,s=/[^\\s!\"#%&'()*+,.\\/;<=>@\\[\\\\\\]^`{|}~]+/,i=e(\"(\",/'.*?'/,\"|\",/\".*?\"/,\"|\",t,\"|\",s,\"|\",/\\.|\\//,\")+\"),r=e(\"(\",t,\"|\",s,\")(?==)\"),l={begin:i,lexemes:/[\\w.\\/]+/},c=n.inherit(l,{keywords:{literal:\"true false undefined null\"}}),o={begin:/\\(/,end:/\\)/},m={className:\"attr\",begin:r,relevance:0,starts:{begin:/=/,end:/=/,starts:{contains:[n.NUMBER_MODE,n.QUOTE_STRING_MODE,n.APOS_STRING_MODE,c,o]}}},d={contains:[n.NUMBER_MODE,n.QUOTE_STRING_MODE,n.APOS_STRING_MODE,{begin:/as\\s+\\|/,keywords:{keyword:\"as\"},end:/\\|/,contains:[{begin:/\\w+/}]},m,c,o],returnEnd:!0},g=n.inherit(l,{className:\"name\",keywords:a,starts:n.inherit(d,{end:/\\)/})});o.contains=[g];const u=n.inherit(l,{keywords:a,className:\"name\",starts:n.inherit(d,{end:/}}/})}),b=n.inherit(l,{keywords:a,className:\"name\"}),h=n.inherit(l,{className:\"name\",keywords:a,starts:n.inherit(d,{end:/}}/})});return{name:\"Handlebars\",aliases:[\"hbs\",\"html.hbs\",\"html.handlebars\",\"htmlbars\"],case_insensitive:!0,subLanguage:\"xml\",contains:[{begin:/\\\\\\{\\{/,skip:!0},{begin:/\\\\\\\\(?=\\{\\{)/,skip:!0},n.COMMENT(/\\{\\{!--/,/--\\}\\}/),n.COMMENT(/\\{\\{!/,/\\}\\}/),{className:\"template-tag\",begin:/\\{\\{\\{\\{(?!\\/)/,end:/\\}\\}\\}\\}/,contains:[u],starts:{end:/\\{\\{\\{\\{\\//,returnEnd:!0,subLanguage:\"xml\"}},{className:\"template-tag\",begin:/\\{\\{\\{\\{\\//,end:/\\}\\}\\}\\}/,contains:[b]},{className:\"template-tag\",begin:/\\{\\{#/,end:/\\}\\}/,contains:[u]},{className:\"template-tag\",begin:/\\{\\{(?=else\\}\\})/,end:/\\}\\}/,keywords:\"else\"},{className:\"template-tag\",begin:/\\{\\{\\//,end:/\\}\\}/,contains:[b]},{className:\"template-variable\",begin:/\\{\\{\\{/,end:/\\}\\}\\}/,contains:[h]},{className:\"template-variable\",begin:/\\{\\{/,end:/\\}\\}/,contains:[h]}]}}}());\nhljs.registerLanguage(\"haskell\",function(){\"use strict\";return function(e){var n={variants:[e.COMMENT(\"--\",\"$\"),e.COMMENT(\"{-\",\"-}\",{contains:[\"self\"]})]},i={className:\"meta\",begin:\"{-#\",end:\"#-}\"},a={className:\"meta\",begin:\"^#\",end:\"$\"},s={className:\"type\",begin:\"\\\\b[A-Z][\\\\w']*\",relevance:0},l={begin:\"\\\\(\",end:\"\\\\)\",illegal:'\"',contains:[i,a,{className:\"type\",begin:\"\\\\b[A-Z][\\\\w]*(\\\\((\\\\.\\\\.|,|\\\\w+)\\\\))?\"},e.inherit(e.TITLE_MODE,{begin:\"[_a-z][\\\\w']*\"}),n]};return{name:\"Haskell\",aliases:[\"hs\"],keywords:\"let in if then else case of where do module import hiding qualified type data newtype deriving class instance as default infix infixl infixr foreign export ccall stdcall cplusplus jvm dotnet safe unsafe family forall mdo proc rec\",contains:[{beginKeywords:\"module\",end:\"where\",keywords:\"module where\",contains:[l,n],illegal:\"\\\\W\\\\.|;\"},{begin:\"\\\\bimport\\\\b\",end:\"$\",keywords:\"import qualified as hiding\",contains:[l,n],illegal:\"\\\\W\\\\.|;\"},{className:\"class\",begin:\"^(\\\\s*)?(class|instance)\\\\b\",end:\"where\",keywords:\"class family instance where\",contains:[s,l,n]},{className:\"class\",begin:\"\\\\b(data|(new)?type)\\\\b\",end:\"$\",keywords:\"data family type newtype deriving\",contains:[i,s,l,{begin:\"{\",end:\"}\",contains:l.contains},n]},{beginKeywords:\"default\",end:\"$\",contains:[s,l,n]},{beginKeywords:\"infix infixl infixr\",end:\"$\",contains:[e.C_NUMBER_MODE,n]},{begin:\"\\\\bforeign\\\\b\",end:\"$\",keywords:\"foreign import export ccall stdcall cplusplus jvm dotnet safe unsafe\",contains:[s,e.QUOTE_STRING_MODE,n]},{className:\"meta\",begin:\"#!\\\\/usr\\\\/bin\\\\/env runhaskell\",end:\"$\"},i,a,e.QUOTE_STRING_MODE,e.C_NUMBER_MODE,s,e.inherit(e.TITLE_MODE,{begin:\"^[_a-z][\\\\w']*\"}),n,{begin:\"->|<-\"}]}}}());\nhljs.registerLanguage(\"julia\",function(){\"use strict\";return function(e){var r=\"[A-Za-z_\\\\u00A1-\\\\uFFFF][A-Za-z_0-9\\\\u00A1-\\\\uFFFF]*\",t={$pattern:r,keyword:\"in isa where baremodule begin break catch ccall const continue do else elseif end export false finally for function global if import importall let local macro module quote return true try using while type immutable abstract bitstype typealias \",literal:\"true false ARGS C_NULL DevNull ENDIAN_BOM ENV I Inf Inf16 Inf32 Inf64 InsertionSort JULIA_HOME LOAD_PATH MergeSort NaN NaN16 NaN32 NaN64 PROGRAM_FILE QuickSort RoundDown RoundFromZero RoundNearest RoundNearestTiesAway RoundNearestTiesUp RoundToZero RoundUp STDERR STDIN STDOUT VERSION catalan e|0 eu|0 eulergamma golden im nothing pi γ π φ \",built_in:\"ANY AbstractArray AbstractChannel AbstractFloat AbstractMatrix AbstractRNG AbstractSerializer AbstractSet AbstractSparseArray AbstractSparseMatrix AbstractSparseVector AbstractString AbstractUnitRange AbstractVecOrMat AbstractVector Any ArgumentError Array AssertionError Associative Base64DecodePipe Base64EncodePipe Bidiagonal BigFloat BigInt BitArray BitMatrix BitVector Bool BoundsError BufferStream CachingPool CapturedException CartesianIndex CartesianRange Cchar Cdouble Cfloat Channel Char Cint Cintmax_t Clong Clonglong ClusterManager Cmd CodeInfo Colon Complex Complex128 Complex32 Complex64 CompositeException Condition ConjArray ConjMatrix ConjVector Cptrdiff_t Cshort Csize_t Cssize_t Cstring Cuchar Cuint Cuintmax_t Culong Culonglong Cushort Cwchar_t Cwstring DataType Date DateFormat DateTime DenseArray DenseMatrix DenseVecOrMat DenseVector Diagonal Dict DimensionMismatch Dims DirectIndexString Display DivideError DomainError EOFError EachLine Enum Enumerate ErrorException Exception ExponentialBackOff Expr Factorization FileMonitor Float16 Float32 Float64 Function Future GlobalRef GotoNode HTML Hermitian IO IOBuffer IOContext IOStream IPAddr IPv4 IPv6 IndexCartesian IndexLinear IndexStyle InexactError InitError Int Int128 Int16 Int32 Int64 Int8 IntSet Integer InterruptException InvalidStateException Irrational KeyError LabelNode LinSpace LineNumberNode LoadError LowerTriangular MIME Matrix MersenneTwister Method MethodError MethodTable Module NTuple NewvarNode NullException Nullable Number ObjectIdDict OrdinalRange OutOfMemoryError OverflowError Pair ParseError PartialQuickSort PermutedDimsArray Pipe PollingFileWatcher ProcessExitedException Ptr QuoteNode RandomDevice Range RangeIndex Rational RawFD ReadOnlyMemoryError Real ReentrantLock Ref Regex RegexMatch RemoteChannel RemoteException RevString RoundingMode RowVector SSAValue SegmentationFault SerializationState Set SharedArray SharedMatrix SharedVector Signed SimpleVector Slot SlotNumber SparseMatrixCSC SparseVector StackFrame StackOverflowError StackTrace StepRange StepRangeLen StridedArray StridedMatrix StridedVecOrMat StridedVector String SubArray SubString SymTridiagonal Symbol Symmetric SystemError TCPSocket Task Text TextDisplay Timer Tridiagonal Tuple Type TypeError TypeMapEntry TypeMapLevel TypeName TypeVar TypedSlot UDPSocket UInt UInt128 UInt16 UInt32 UInt64 UInt8 UndefRefError UndefVarError UnicodeError UniformScaling Union UnionAll UnitRange Unsigned UpperTriangular Val Vararg VecElement VecOrMat Vector VersionNumber Void WeakKeyDict WeakRef WorkerConfig WorkerPool \"},a={keywords:t,illegal:/<\\//},n={className:\"subst\",begin:/\\$\\(/,end:/\\)/,keywords:t},o={className:\"variable\",begin:\"\\\\$\"+r},i={className:\"string\",contains:[e.BACKSLASH_ESCAPE,n,o],variants:[{begin:/\\w*\"\"\"/,end:/\"\"\"\\w*/,relevance:10},{begin:/\\w*\"/,end:/\"\\w*/}]},l={className:\"string\",contains:[e.BACKSLASH_ESCAPE,n,o],begin:\"`\",end:\"`\"},s={className:\"meta\",begin:\"@\"+r};return a.name=\"Julia\",a.contains=[{className:\"number\",begin:/(\\b0x[\\d_]*(\\.[\\d_]*)?|0x\\.\\d[\\d_]*)p[-+]?\\d+|\\b0[box][a-fA-F0-9][a-fA-F0-9_]*|(\\b\\d[\\d_]*(\\.[\\d_]*)?|\\.\\d[\\d_]*)([eEfF][-+]?\\d+)?/,relevance:0},{className:\"string\",begin:/'(.|\\\\[xXuU][a-zA-Z0-9]+)'/},i,l,s,{className:\"comment\",variants:[{begin:\"#=\",end:\"=#\",relevance:10},{begin:\"#\",end:\"$\"}]},e.HASH_COMMENT_MODE,{className:\"keyword\",begin:\"\\\\b(((abstract|primitive)\\\\s+)type|(mutable\\\\s+)?struct)\\\\b\"},{begin:/<:/}],n.contains=a.contains,a}}());\nhljs.registerLanguage(\"nim\",function(){\"use strict\";return function(e){return{name:\"Nim\",aliases:[\"nim\"],keywords:{keyword:\"addr and as asm bind block break case cast const continue converter discard distinct div do elif else end enum except export finally for from func generic if import in include interface is isnot iterator let macro method mixin mod nil not notin object of or out proc ptr raise ref return shl shr static template try tuple type using var when while with without xor yield\",literal:\"shared guarded stdin stdout stderr result true false\",built_in:\"int int8 int16 int32 int64 uint uint8 uint16 uint32 uint64 float float32 float64 bool char string cstring pointer expr stmt void auto any range array openarray varargs seq set clong culong cchar cschar cshort cint csize clonglong cfloat cdouble clongdouble cuchar cushort cuint culonglong cstringarray semistatic\"},contains:[{className:\"meta\",begin:/{\\./,end:/\\.}/,relevance:10},{className:\"string\",begin:/[a-zA-Z]\\w*\"/,end:/\"/,contains:[{begin:/\"\"/}]},{className:\"string\",begin:/([a-zA-Z]\\w*)?\"\"\"/,end:/\"\"\"/},e.QUOTE_STRING_MODE,{className:\"type\",begin:/\\b[A-Z]\\w+\\b/,relevance:0},{className:\"number\",relevance:0,variants:[{begin:/\\b(0[xX][0-9a-fA-F][_0-9a-fA-F]*)('?[iIuU](8|16|32|64))?/},{begin:/\\b(0o[0-7][_0-7]*)('?[iIuUfF](8|16|32|64))?/},{begin:/\\b(0(b|B)[01][_01]*)('?[iIuUfF](8|16|32|64))?/},{begin:/\\b(\\d[_\\d]*)('?[iIuUfF](8|16|32|64))?/}]},e.HASH_COMMENT_MODE]}}}());\nhljs.registerLanguage(\"nix\",function(){\"use strict\";return function(e){var n={keyword:\"rec with let in inherit assert if else then\",literal:\"true false or and null\",built_in:\"import abort baseNameOf dirOf isNull builtins map removeAttrs throw toString derivation\"},i={className:\"subst\",begin:/\\$\\{/,end:/}/,keywords:n},t={className:\"string\",contains:[i],variants:[{begin:\"''\",end:\"''\"},{begin:'\"',end:'\"'}]},s=[e.NUMBER_MODE,e.HASH_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,t,{begin:/[a-zA-Z0-9-_]+(\\s*=)/,returnBegin:!0,relevance:0,contains:[{className:\"attr\",begin:/\\S+/}]}];return i.contains=s,{name:\"Nix\",aliases:[\"nixos\"],keywords:n,contains:s}}}());\nhljs.registerLanguage(\"r\",function(){\"use strict\";return function(e){var n=\"([a-zA-Z]|\\\\.[a-zA-Z.])[a-zA-Z0-9._]*\";return{name:\"R\",contains:[e.HASH_COMMENT_MODE,{begin:n,keywords:{$pattern:n,keyword:\"function if in break next repeat else for return switch while try tryCatch stop warning require library attach detach source setMethod setGeneric setGroupGeneric setClass ...\",literal:\"NULL NA TRUE FALSE T F Inf NaN NA_integer_|10 NA_real_|10 NA_character_|10 NA_complex_|10\"},relevance:0},{className:\"number\",begin:\"0[xX][0-9a-fA-F]+[Li]?\\\\b\",relevance:0},{className:\"number\",begin:\"\\\\d+(?:[eE][+\\\\-]?\\\\d*)?L\\\\b\",relevance:0},{className:\"number\",begin:\"\\\\d+\\\\.(?!\\\\d)(?:i\\\\b)?\",relevance:0},{className:\"number\",begin:\"\\\\d+(?:\\\\.\\\\d*)?(?:[eE][+\\\\-]?\\\\d*)?i?\\\\b\",relevance:0},{className:\"number\",begin:\"\\\\.\\\\d+(?:[eE][+\\\\-]?\\\\d*)?i?\\\\b\",relevance:0},{begin:\"`\",end:\"`\",relevance:0},{className:\"string\",contains:[e.BACKSLASH_ESCAPE],variants:[{begin:'\"',end:'\"'},{begin:\"'\",end:\"'\"}]}]}}}());\nhljs.registerLanguage(\"scala\",function(){\"use strict\";return function(e){var n={className:\"subst\",variants:[{begin:\"\\\\$[A-Za-z0-9_]+\"},{begin:\"\\\\${\",end:\"}\"}]},a={className:\"string\",variants:[{begin:'\"',end:'\"',illegal:\"\\\\n\",contains:[e.BACKSLASH_ESCAPE]},{begin:'\"\"\"',end:'\"\"\"',relevance:10},{begin:'[a-z]+\"',end:'\"',illegal:\"\\\\n\",contains:[e.BACKSLASH_ESCAPE,n]},{className:\"string\",begin:'[a-z]+\"\"\"',end:'\"\"\"',contains:[n],relevance:10}]},s={className:\"type\",begin:\"\\\\b[A-Z][A-Za-z0-9_]*\",relevance:0},t={className:\"title\",begin:/[^0-9\\n\\t \"'(),.`{}\\[\\]:;][^\\n\\t \"'(),.`{}\\[\\]:;]+|[^0-9\\n\\t \"'(),.`{}\\[\\]:;=]/,relevance:0},i={className:\"class\",beginKeywords:\"class object trait type\",end:/[:={\\[\\n;]/,excludeEnd:!0,contains:[{beginKeywords:\"extends with\",relevance:10},{begin:/\\[/,end:/\\]/,excludeBegin:!0,excludeEnd:!0,relevance:0,contains:[s]},{className:\"params\",begin:/\\(/,end:/\\)/,excludeBegin:!0,excludeEnd:!0,relevance:0,contains:[s]},t]},l={className:\"function\",beginKeywords:\"def\",end:/[:={\\[(\\n;]/,excludeEnd:!0,contains:[t]};return{name:\"Scala\",keywords:{literal:\"true false null\",keyword:\"type yield lazy override def with val var sealed abstract private trait object if forSome for while throw finally protected extends import final return else break new catch super class case package default try this match continue throws implicit\"},contains:[e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,a,{className:\"symbol\",begin:\"'\\\\w[\\\\w\\\\d_]*(?!')\"},s,l,i,e.C_NUMBER_MODE,{className:\"meta\",begin:\"@[A-Za-z]+\"}]}}}());\nhljs.registerLanguage(\"x86asm\",function(){\"use strict\";return function(s){return{name:\"Intel x86 Assembly\",case_insensitive:!0,keywords:{$pattern:\"[.%]?\"+s.IDENT_RE,keyword:\"lock rep repe repz repne repnz xaquire xrelease bnd nobnd aaa aad aam aas adc add and arpl bb0_reset bb1_reset bound bsf bsr bswap bt btc btr bts call cbw cdq cdqe clc cld cli clts cmc cmp cmpsb cmpsd cmpsq cmpsw cmpxchg cmpxchg486 cmpxchg8b cmpxchg16b cpuid cpu_read cpu_write cqo cwd cwde daa das dec div dmint emms enter equ f2xm1 fabs fadd faddp fbld fbstp fchs fclex fcmovb fcmovbe fcmove fcmovnb fcmovnbe fcmovne fcmovnu fcmovu fcom fcomi fcomip fcomp fcompp fcos fdecstp fdisi fdiv fdivp fdivr fdivrp femms feni ffree ffreep fiadd ficom ficomp fidiv fidivr fild fimul fincstp finit fist fistp fisttp fisub fisubr fld fld1 fldcw fldenv fldl2e fldl2t fldlg2 fldln2 fldpi fldz fmul fmulp fnclex fndisi fneni fninit fnop fnsave fnstcw fnstenv fnstsw fpatan fprem fprem1 fptan frndint frstor fsave fscale fsetpm fsin fsincos fsqrt fst fstcw fstenv fstp fstsw fsub fsubp fsubr fsubrp ftst fucom fucomi fucomip fucomp fucompp fxam fxch fxtract fyl2x fyl2xp1 hlt ibts icebp idiv imul in inc incbin insb insd insw int int01 int1 int03 int3 into invd invpcid invlpg invlpga iret iretd iretq iretw jcxz jecxz jrcxz jmp jmpe lahf lar lds lea leave les lfence lfs lgdt lgs lidt lldt lmsw loadall loadall286 lodsb lodsd lodsq lodsw loop loope loopne loopnz loopz lsl lss ltr mfence monitor mov movd movq movsb movsd movsq movsw movsx movsxd movzx mul mwait neg nop not or out outsb outsd outsw packssdw packsswb packuswb paddb paddd paddsb paddsiw paddsw paddusb paddusw paddw pand pandn pause paveb pavgusb pcmpeqb pcmpeqd pcmpeqw pcmpgtb pcmpgtd pcmpgtw pdistib pf2id pfacc pfadd pfcmpeq pfcmpge pfcmpgt pfmax pfmin pfmul pfrcp pfrcpit1 pfrcpit2 pfrsqit1 pfrsqrt pfsub pfsubr pi2fd pmachriw pmaddwd pmagw pmulhriw pmulhrwa pmulhrwc pmulhw pmullw pmvgezb pmvlzb pmvnzb pmvzb pop popa popad popaw popf popfd popfq popfw por prefetch prefetchw pslld psllq psllw psrad psraw psrld psrlq psrlw psubb psubd psubsb psubsiw psubsw psubusb psubusw psubw punpckhbw punpckhdq punpckhwd punpcklbw punpckldq punpcklwd push pusha pushad pushaw pushf pushfd pushfq pushfw pxor rcl rcr rdshr rdmsr rdpmc rdtsc rdtscp ret retf retn rol ror rdm rsdc rsldt rsm rsts sahf sal salc sar sbb scasb scasd scasq scasw sfence sgdt shl shld shr shrd sidt sldt skinit smi smint smintold smsw stc std sti stosb stosd stosq stosw str sub svdc svldt svts swapgs syscall sysenter sysexit sysret test ud0 ud1 ud2b ud2 ud2a umov verr verw fwait wbinvd wrshr wrmsr xadd xbts xchg xlatb xlat xor cmove cmovz cmovne cmovnz cmova cmovnbe cmovae cmovnb cmovb cmovnae cmovbe cmovna cmovg cmovnle cmovge cmovnl cmovl cmovnge cmovle cmovng cmovc cmovnc cmovo cmovno cmovs cmovns cmovp cmovpe cmovnp cmovpo je jz jne jnz ja jnbe jae jnb jb jnae jbe jna jg jnle jge jnl jl jnge jle jng jc jnc jo jno js jns jpo jnp jpe jp sete setz setne setnz seta setnbe setae setnb setnc setb setnae setcset setbe setna setg setnle setge setnl setl setnge setle setng sets setns seto setno setpe setp setpo setnp addps addss andnps andps cmpeqps cmpeqss cmpleps cmpless cmpltps cmpltss cmpneqps cmpneqss cmpnleps cmpnless cmpnltps cmpnltss cmpordps cmpordss cmpunordps cmpunordss cmpps cmpss comiss cvtpi2ps cvtps2pi cvtsi2ss cvtss2si cvttps2pi cvttss2si divps divss ldmxcsr maxps maxss minps minss movaps movhps movlhps movlps movhlps movmskps movntps movss movups mulps mulss orps rcpps rcpss rsqrtps rsqrtss shufps sqrtps sqrtss stmxcsr subps subss ucomiss unpckhps unpcklps xorps fxrstor fxrstor64 fxsave fxsave64 xgetbv xsetbv xsave xsave64 xsaveopt xsaveopt64 xrstor xrstor64 prefetchnta prefetcht0 prefetcht1 prefetcht2 maskmovq movntq pavgb pavgw pextrw pinsrw pmaxsw pmaxub pminsw pminub pmovmskb pmulhuw psadbw pshufw pf2iw pfnacc pfpnacc pi2fw pswapd maskmovdqu clflush movntdq movnti movntpd movdqa movdqu movdq2q movq2dq paddq pmuludq pshufd pshufhw pshuflw pslldq psrldq psubq punpckhqdq punpcklqdq addpd addsd andnpd andpd cmpeqpd cmpeqsd cmplepd cmplesd cmpltpd cmpltsd cmpneqpd cmpneqsd cmpnlepd cmpnlesd cmpnltpd cmpnltsd cmpordpd cmpordsd cmpunordpd cmpunordsd cmppd comisd cvtdq2pd cvtdq2ps cvtpd2dq cvtpd2pi cvtpd2ps cvtpi2pd cvtps2dq cvtps2pd cvtsd2si cvtsd2ss cvtsi2sd cvtss2sd cvttpd2pi cvttpd2dq cvttps2dq cvttsd2si divpd divsd maxpd maxsd minpd minsd movapd movhpd movlpd movmskpd movupd mulpd mulsd orpd shufpd sqrtpd sqrtsd subpd subsd ucomisd unpckhpd unpcklpd xorpd addsubpd addsubps haddpd haddps hsubpd hsubps lddqu movddup movshdup movsldup clgi stgi vmcall vmclear vmfunc vmlaunch vmload vmmcall vmptrld vmptrst vmread vmresume vmrun vmsave vmwrite vmxoff vmxon invept invvpid pabsb pabsw pabsd palignr phaddw phaddd phaddsw phsubw phsubd phsubsw pmaddubsw pmulhrsw pshufb psignb psignw psignd extrq insertq movntsd movntss lzcnt blendpd blendps blendvpd blendvps dppd dpps extractps insertps movntdqa mpsadbw packusdw pblendvb pblendw pcmpeqq pextrb pextrd pextrq phminposuw pinsrb pinsrd pinsrq pmaxsb pmaxsd pmaxud pmaxuw pminsb pminsd pminud pminuw pmovsxbw pmovsxbd pmovsxbq pmovsxwd pmovsxwq pmovsxdq pmovzxbw pmovzxbd pmovzxbq pmovzxwd pmovzxwq pmovzxdq pmuldq pmulld ptest roundpd roundps roundsd roundss crc32 pcmpestri pcmpestrm pcmpistri pcmpistrm pcmpgtq popcnt getsec pfrcpv pfrsqrtv movbe aesenc aesenclast aesdec aesdeclast aesimc aeskeygenassist vaesenc vaesenclast vaesdec vaesdeclast vaesimc vaeskeygenassist vaddpd vaddps vaddsd vaddss vaddsubpd vaddsubps vandpd vandps vandnpd vandnps vblendpd vblendps vblendvpd vblendvps vbroadcastss vbroadcastsd vbroadcastf128 vcmpeq_ospd vcmpeqpd vcmplt_ospd vcmpltpd vcmple_ospd vcmplepd vcmpunord_qpd vcmpunordpd vcmpneq_uqpd vcmpneqpd vcmpnlt_uspd vcmpnltpd vcmpnle_uspd vcmpnlepd vcmpord_qpd vcmpordpd vcmpeq_uqpd vcmpnge_uspd vcmpngepd vcmpngt_uspd vcmpngtpd vcmpfalse_oqpd vcmpfalsepd vcmpneq_oqpd vcmpge_ospd vcmpgepd vcmpgt_ospd vcmpgtpd vcmptrue_uqpd vcmptruepd vcmplt_oqpd vcmple_oqpd vcmpunord_spd vcmpneq_uspd vcmpnlt_uqpd vcmpnle_uqpd vcmpord_spd vcmpeq_uspd vcmpnge_uqpd vcmpngt_uqpd vcmpfalse_ospd vcmpneq_ospd vcmpge_oqpd vcmpgt_oqpd vcmptrue_uspd vcmppd vcmpeq_osps vcmpeqps vcmplt_osps vcmpltps vcmple_osps vcmpleps vcmpunord_qps vcmpunordps vcmpneq_uqps vcmpneqps vcmpnlt_usps vcmpnltps vcmpnle_usps vcmpnleps vcmpord_qps vcmpordps vcmpeq_uqps vcmpnge_usps vcmpngeps vcmpngt_usps vcmpngtps vcmpfalse_oqps vcmpfalseps vcmpneq_oqps vcmpge_osps vcmpgeps vcmpgt_osps vcmpgtps vcmptrue_uqps vcmptrueps vcmplt_oqps vcmple_oqps vcmpunord_sps vcmpneq_usps vcmpnlt_uqps vcmpnle_uqps vcmpord_sps vcmpeq_usps vcmpnge_uqps vcmpngt_uqps vcmpfalse_osps vcmpneq_osps vcmpge_oqps vcmpgt_oqps vcmptrue_usps vcmpps vcmpeq_ossd vcmpeqsd vcmplt_ossd vcmpltsd vcmple_ossd vcmplesd vcmpunord_qsd vcmpunordsd vcmpneq_uqsd vcmpneqsd vcmpnlt_ussd vcmpnltsd vcmpnle_ussd vcmpnlesd vcmpord_qsd vcmpordsd vcmpeq_uqsd vcmpnge_ussd vcmpngesd vcmpngt_ussd vcmpngtsd vcmpfalse_oqsd vcmpfalsesd vcmpneq_oqsd vcmpge_ossd vcmpgesd vcmpgt_ossd vcmpgtsd vcmptrue_uqsd vcmptruesd vcmplt_oqsd vcmple_oqsd vcmpunord_ssd vcmpneq_ussd vcmpnlt_uqsd vcmpnle_uqsd vcmpord_ssd vcmpeq_ussd vcmpnge_uqsd vcmpngt_uqsd vcmpfalse_ossd vcmpneq_ossd vcmpge_oqsd vcmpgt_oqsd vcmptrue_ussd vcmpsd vcmpeq_osss vcmpeqss vcmplt_osss vcmpltss vcmple_osss vcmpless vcmpunord_qss vcmpunordss vcmpneq_uqss vcmpneqss vcmpnlt_usss vcmpnltss vcmpnle_usss vcmpnless vcmpord_qss vcmpordss vcmpeq_uqss vcmpnge_usss vcmpngess vcmpngt_usss vcmpngtss vcmpfalse_oqss vcmpfalsess vcmpneq_oqss vcmpge_osss vcmpgess vcmpgt_osss vcmpgtss vcmptrue_uqss vcmptruess vcmplt_oqss vcmple_oqss vcmpunord_sss vcmpneq_usss vcmpnlt_uqss vcmpnle_uqss vcmpord_sss vcmpeq_usss vcmpnge_uqss vcmpngt_uqss vcmpfalse_osss vcmpneq_osss vcmpge_oqss vcmpgt_oqss vcmptrue_usss vcmpss vcomisd vcomiss vcvtdq2pd vcvtdq2ps vcvtpd2dq vcvtpd2ps vcvtps2dq vcvtps2pd vcvtsd2si vcvtsd2ss vcvtsi2sd vcvtsi2ss vcvtss2sd vcvtss2si vcvttpd2dq vcvttps2dq vcvttsd2si vcvttss2si vdivpd vdivps vdivsd vdivss vdppd vdpps vextractf128 vextractps vhaddpd vhaddps vhsubpd vhsubps vinsertf128 vinsertps vlddqu vldqqu vldmxcsr vmaskmovdqu vmaskmovps vmaskmovpd vmaxpd vmaxps vmaxsd vmaxss vminpd vminps vminsd vminss vmovapd vmovaps vmovd vmovq vmovddup vmovdqa vmovqqa vmovdqu vmovqqu vmovhlps vmovhpd vmovhps vmovlhps vmovlpd vmovlps vmovmskpd vmovmskps vmovntdq vmovntqq vmovntdqa vmovntpd vmovntps vmovsd vmovshdup vmovsldup vmovss vmovupd vmovups vmpsadbw vmulpd vmulps vmulsd vmulss vorpd vorps vpabsb vpabsw vpabsd vpacksswb vpackssdw vpackuswb vpackusdw vpaddb vpaddw vpaddd vpaddq vpaddsb vpaddsw vpaddusb vpaddusw vpalignr vpand vpandn vpavgb vpavgw vpblendvb vpblendw vpcmpestri vpcmpestrm vpcmpistri vpcmpistrm vpcmpeqb vpcmpeqw vpcmpeqd vpcmpeqq vpcmpgtb vpcmpgtw vpcmpgtd vpcmpgtq vpermilpd vpermilps vperm2f128 vpextrb vpextrw vpextrd vpextrq vphaddw vphaddd vphaddsw vphminposuw vphsubw vphsubd vphsubsw vpinsrb vpinsrw vpinsrd vpinsrq vpmaddwd vpmaddubsw vpmaxsb vpmaxsw vpmaxsd vpmaxub vpmaxuw vpmaxud vpminsb vpminsw vpminsd vpminub vpminuw vpminud vpmovmskb vpmovsxbw vpmovsxbd vpmovsxbq vpmovsxwd vpmovsxwq vpmovsxdq vpmovzxbw vpmovzxbd vpmovzxbq vpmovzxwd vpmovzxwq vpmovzxdq vpmulhuw vpmulhrsw vpmulhw vpmullw vpmulld vpmuludq vpmuldq vpor vpsadbw vpshufb vpshufd vpshufhw vpshuflw vpsignb vpsignw vpsignd vpslldq vpsrldq vpsllw vpslld vpsllq vpsraw vpsrad vpsrlw vpsrld vpsrlq vptest vpsubb vpsubw vpsubd vpsubq vpsubsb vpsubsw vpsubusb vpsubusw vpunpckhbw vpunpckhwd vpunpckhdq vpunpckhqdq vpunpcklbw vpunpcklwd vpunpckldq vpunpcklqdq vpxor vrcpps vrcpss vrsqrtps vrsqrtss vroundpd vroundps vroundsd vroundss vshufpd vshufps vsqrtpd vsqrtps vsqrtsd vsqrtss vstmxcsr vsubpd vsubps vsubsd vsubss vtestps vtestpd vucomisd vucomiss vunpckhpd vunpckhps vunpcklpd vunpcklps vxorpd vxorps vzeroall vzeroupper pclmullqlqdq pclmulhqlqdq pclmullqhqdq pclmulhqhqdq pclmulqdq vpclmullqlqdq vpclmulhqlqdq vpclmullqhqdq vpclmulhqhqdq vpclmulqdq vfmadd132ps vfmadd132pd vfmadd312ps vfmadd312pd vfmadd213ps vfmadd213pd vfmadd123ps vfmadd123pd vfmadd231ps vfmadd231pd vfmadd321ps vfmadd321pd vfmaddsub132ps vfmaddsub132pd vfmaddsub312ps vfmaddsub312pd vfmaddsub213ps vfmaddsub213pd vfmaddsub123ps vfmaddsub123pd vfmaddsub231ps vfmaddsub231pd vfmaddsub321ps vfmaddsub321pd vfmsub132ps vfmsub132pd vfmsub312ps vfmsub312pd vfmsub213ps vfmsub213pd vfmsub123ps vfmsub123pd vfmsub231ps vfmsub231pd vfmsub321ps vfmsub321pd vfmsubadd132ps vfmsubadd132pd vfmsubadd312ps vfmsubadd312pd vfmsubadd213ps vfmsubadd213pd vfmsubadd123ps vfmsubadd123pd vfmsubadd231ps vfmsubadd231pd vfmsubadd321ps vfmsubadd321pd vfnmadd132ps vfnmadd132pd vfnmadd312ps vfnmadd312pd vfnmadd213ps vfnmadd213pd vfnmadd123ps vfnmadd123pd vfnmadd231ps vfnmadd231pd vfnmadd321ps vfnmadd321pd vfnmsub132ps vfnmsub132pd vfnmsub312ps vfnmsub312pd vfnmsub213ps vfnmsub213pd vfnmsub123ps vfnmsub123pd vfnmsub231ps vfnmsub231pd vfnmsub321ps vfnmsub321pd vfmadd132ss vfmadd132sd vfmadd312ss vfmadd312sd vfmadd213ss vfmadd213sd vfmadd123ss vfmadd123sd vfmadd231ss vfmadd231sd vfmadd321ss vfmadd321sd vfmsub132ss vfmsub132sd vfmsub312ss vfmsub312sd vfmsub213ss vfmsub213sd vfmsub123ss vfmsub123sd vfmsub231ss vfmsub231sd vfmsub321ss vfmsub321sd vfnmadd132ss vfnmadd132sd vfnmadd312ss vfnmadd312sd vfnmadd213ss vfnmadd213sd vfnmadd123ss vfnmadd123sd vfnmadd231ss vfnmadd231sd vfnmadd321ss vfnmadd321sd vfnmsub132ss vfnmsub132sd vfnmsub312ss vfnmsub312sd vfnmsub213ss vfnmsub213sd vfnmsub123ss vfnmsub123sd vfnmsub231ss vfnmsub231sd vfnmsub321ss vfnmsub321sd rdfsbase rdgsbase rdrand wrfsbase wrgsbase vcvtph2ps vcvtps2ph adcx adox rdseed clac stac xstore xcryptecb xcryptcbc xcryptctr xcryptcfb xcryptofb montmul xsha1 xsha256 llwpcb slwpcb lwpval lwpins vfmaddpd vfmaddps vfmaddsd vfmaddss vfmaddsubpd vfmaddsubps vfmsubaddpd vfmsubaddps vfmsubpd vfmsubps vfmsubsd vfmsubss vfnmaddpd vfnmaddps vfnmaddsd vfnmaddss vfnmsubpd vfnmsubps vfnmsubsd vfnmsubss vfrczpd vfrczps vfrczsd vfrczss vpcmov vpcomb vpcomd vpcomq vpcomub vpcomud vpcomuq vpcomuw vpcomw vphaddbd vphaddbq vphaddbw vphadddq vphaddubd vphaddubq vphaddubw vphaddudq vphadduwd vphadduwq vphaddwd vphaddwq vphsubbw vphsubdq vphsubwd vpmacsdd vpmacsdqh vpmacsdql vpmacssdd vpmacssdqh vpmacssdql vpmacsswd vpmacssww vpmacswd vpmacsww vpmadcsswd vpmadcswd vpperm vprotb vprotd vprotq vprotw vpshab vpshad vpshaq vpshaw vpshlb vpshld vpshlq vpshlw vbroadcasti128 vpblendd vpbroadcastb vpbroadcastw vpbroadcastd vpbroadcastq vpermd vpermpd vpermps vpermq vperm2i128 vextracti128 vinserti128 vpmaskmovd vpmaskmovq vpsllvd vpsllvq vpsravd vpsrlvd vpsrlvq vgatherdpd vgatherqpd vgatherdps vgatherqps vpgatherdd vpgatherqd vpgatherdq vpgatherqq xabort xbegin xend xtest andn bextr blci blcic blsi blsic blcfill blsfill blcmsk blsmsk blsr blcs bzhi mulx pdep pext rorx sarx shlx shrx tzcnt tzmsk t1mskc valignd valignq vblendmpd vblendmps vbroadcastf32x4 vbroadcastf64x4 vbroadcasti32x4 vbroadcasti64x4 vcompresspd vcompressps vcvtpd2udq vcvtps2udq vcvtsd2usi vcvtss2usi vcvttpd2udq vcvttps2udq vcvttsd2usi vcvttss2usi vcvtudq2pd vcvtudq2ps vcvtusi2sd vcvtusi2ss vexpandpd vexpandps vextractf32x4 vextractf64x4 vextracti32x4 vextracti64x4 vfixupimmpd vfixupimmps vfixupimmsd vfixupimmss vgetexppd vgetexpps vgetexpsd vgetexpss vgetmantpd vgetmantps vgetmantsd vgetmantss vinsertf32x4 vinsertf64x4 vinserti32x4 vinserti64x4 vmovdqa32 vmovdqa64 vmovdqu32 vmovdqu64 vpabsq vpandd vpandnd vpandnq vpandq vpblendmd vpblendmq vpcmpltd vpcmpled vpcmpneqd vpcmpnltd vpcmpnled vpcmpd vpcmpltq vpcmpleq vpcmpneqq vpcmpnltq vpcmpnleq vpcmpq vpcmpequd vpcmpltud vpcmpleud vpcmpnequd vpcmpnltud vpcmpnleud vpcmpud vpcmpequq vpcmpltuq vpcmpleuq vpcmpnequq vpcmpnltuq vpcmpnleuq vpcmpuq vpcompressd vpcompressq vpermi2d vpermi2pd vpermi2ps vpermi2q vpermt2d vpermt2pd vpermt2ps vpermt2q vpexpandd vpexpandq vpmaxsq vpmaxuq vpminsq vpminuq vpmovdb vpmovdw vpmovqb vpmovqd vpmovqw vpmovsdb vpmovsdw vpmovsqb vpmovsqd vpmovsqw vpmovusdb vpmovusdw vpmovusqb vpmovusqd vpmovusqw vpord vporq vprold vprolq vprolvd vprolvq vprord vprorq vprorvd vprorvq vpscatterdd vpscatterdq vpscatterqd vpscatterqq vpsraq vpsravq vpternlogd vpternlogq vptestmd vptestmq vptestnmd vptestnmq vpxord vpxorq vrcp14pd vrcp14ps vrcp14sd vrcp14ss vrndscalepd vrndscaleps vrndscalesd vrndscaless vrsqrt14pd vrsqrt14ps vrsqrt14sd vrsqrt14ss vscalefpd vscalefps vscalefsd vscalefss vscatterdpd vscatterdps vscatterqpd vscatterqps vshuff32x4 vshuff64x2 vshufi32x4 vshufi64x2 kandnw kandw kmovw knotw kortestw korw kshiftlw kshiftrw kunpckbw kxnorw kxorw vpbroadcastmb2q vpbroadcastmw2d vpconflictd vpconflictq vplzcntd vplzcntq vexp2pd vexp2ps vrcp28pd vrcp28ps vrcp28sd vrcp28ss vrsqrt28pd vrsqrt28ps vrsqrt28sd vrsqrt28ss vgatherpf0dpd vgatherpf0dps vgatherpf0qpd vgatherpf0qps vgatherpf1dpd vgatherpf1dps vgatherpf1qpd vgatherpf1qps vscatterpf0dpd vscatterpf0dps vscatterpf0qpd vscatterpf0qps vscatterpf1dpd vscatterpf1dps vscatterpf1qpd vscatterpf1qps prefetchwt1 bndmk bndcl bndcu bndcn bndmov bndldx bndstx sha1rnds4 sha1nexte sha1msg1 sha1msg2 sha256rnds2 sha256msg1 sha256msg2 hint_nop0 hint_nop1 hint_nop2 hint_nop3 hint_nop4 hint_nop5 hint_nop6 hint_nop7 hint_nop8 hint_nop9 hint_nop10 hint_nop11 hint_nop12 hint_nop13 hint_nop14 hint_nop15 hint_nop16 hint_nop17 hint_nop18 hint_nop19 hint_nop20 hint_nop21 hint_nop22 hint_nop23 hint_nop24 hint_nop25 hint_nop26 hint_nop27 hint_nop28 hint_nop29 hint_nop30 hint_nop31 hint_nop32 hint_nop33 hint_nop34 hint_nop35 hint_nop36 hint_nop37 hint_nop38 hint_nop39 hint_nop40 hint_nop41 hint_nop42 hint_nop43 hint_nop44 hint_nop45 hint_nop46 hint_nop47 hint_nop48 hint_nop49 hint_nop50 hint_nop51 hint_nop52 hint_nop53 hint_nop54 hint_nop55 hint_nop56 hint_nop57 hint_nop58 hint_nop59 hint_nop60 hint_nop61 hint_nop62 hint_nop63\",built_in:\"ip eip rip al ah bl bh cl ch dl dh sil dil bpl spl r8b r9b r10b r11b r12b r13b r14b r15b ax bx cx dx si di bp sp r8w r9w r10w r11w r12w r13w r14w r15w eax ebx ecx edx esi edi ebp esp eip r8d r9d r10d r11d r12d r13d r14d r15d rax rbx rcx rdx rsi rdi rbp rsp r8 r9 r10 r11 r12 r13 r14 r15 cs ds es fs gs ss st st0 st1 st2 st3 st4 st5 st6 st7 mm0 mm1 mm2 mm3 mm4 mm5 mm6 mm7 xmm0  xmm1  xmm2  xmm3  xmm4  xmm5  xmm6  xmm7  xmm8  xmm9 xmm10  xmm11 xmm12 xmm13 xmm14 xmm15 xmm16 xmm17 xmm18 xmm19 xmm20 xmm21 xmm22 xmm23 xmm24 xmm25 xmm26 xmm27 xmm28 xmm29 xmm30 xmm31 ymm0  ymm1  ymm2  ymm3  ymm4  ymm5  ymm6  ymm7  ymm8  ymm9 ymm10  ymm11 ymm12 ymm13 ymm14 ymm15 ymm16 ymm17 ymm18 ymm19 ymm20 ymm21 ymm22 ymm23 ymm24 ymm25 ymm26 ymm27 ymm28 ymm29 ymm30 ymm31 zmm0  zmm1  zmm2  zmm3  zmm4  zmm5  zmm6  zmm7  zmm8  zmm9 zmm10  zmm11 zmm12 zmm13 zmm14 zmm15 zmm16 zmm17 zmm18 zmm19 zmm20 zmm21 zmm22 zmm23 zmm24 zmm25 zmm26 zmm27 zmm28 zmm29 zmm30 zmm31 k0 k1 k2 k3 k4 k5 k6 k7 bnd0 bnd1 bnd2 bnd3 cr0 cr1 cr2 cr3 cr4 cr8 dr0 dr1 dr2 dr3 dr8 tr3 tr4 tr5 tr6 tr7 r0 r1 r2 r3 r4 r5 r6 r7 r0b r1b r2b r3b r4b r5b r6b r7b r0w r1w r2w r3w r4w r5w r6w r7w r0d r1d r2d r3d r4d r5d r6d r7d r0h r1h r2h r3h r0l r1l r2l r3l r4l r5l r6l r7l r8l r9l r10l r11l r12l r13l r14l r15l db dw dd dq dt ddq do dy dz resb resw resd resq rest resdq reso resy resz incbin equ times byte word dword qword nosplit rel abs seg wrt strict near far a32 ptr\",meta:\"%define %xdefine %+ %undef %defstr %deftok %assign %strcat %strlen %substr %rotate %elif %else %endif %if %ifmacro %ifctx %ifidn %ifidni %ifid %ifnum %ifstr %iftoken %ifempty %ifenv %error %warning %fatal %rep %endrep %include %push %pop %repl %pathsearch %depend %use %arg %stacksize %local %line %comment %endcomment .nolist __FILE__ __LINE__ __SECT__  __BITS__ __OUTPUT_FORMAT__ __DATE__ __TIME__ __DATE_NUM__ __TIME_NUM__ __UTC_DATE__ __UTC_TIME__ __UTC_DATE_NUM__ __UTC_TIME_NUM__  __PASS__ struc endstruc istruc at iend align alignb sectalign daz nodaz up down zero default option assume public bits use16 use32 use64 default section segment absolute extern global common cpu float __utf16__ __utf16le__ __utf16be__ __utf32__ __utf32le__ __utf32be__ __float8__ __float16__ __float32__ __float64__ __float80m__ __float80e__ __float128l__ __float128h__ __Infinity__ __QNaN__ __SNaN__ Inf NaN QNaN SNaN float8 float16 float32 float64 float80m float80e float128l float128h __FLOAT_DAZ__ __FLOAT_ROUND__ __FLOAT__\"},contains:[s.COMMENT(\";\",\"$\",{relevance:0}),{className:\"number\",variants:[{begin:\"\\\\b(?:([0-9][0-9_]*)?\\\\.[0-9_]*(?:[eE][+-]?[0-9_]+)?|(0[Xx])?[0-9][0-9_]*\\\\.?[0-9_]*(?:[pP](?:[+-]?[0-9_]+)?)?)\\\\b\",relevance:0},{begin:\"\\\\$[0-9][0-9A-Fa-f]*\",relevance:0},{begin:\"\\\\b(?:[0-9A-Fa-f][0-9A-Fa-f_]*[Hh]|[0-9][0-9_]*[DdTt]?|[0-7][0-7_]*[QqOo]|[0-1][0-1_]*[BbYy])\\\\b\"},{begin:\"\\\\b(?:0[Xx][0-9A-Fa-f_]+|0[DdTt][0-9_]+|0[QqOo][0-7_]+|0[BbYy][0-1_]+)\\\\b\"}]},s.QUOTE_STRING_MODE,{className:\"string\",variants:[{begin:\"'\",end:\"[^\\\\\\\\]'\"},{begin:\"`\",end:\"[^\\\\\\\\]`\"}],relevance:0},{className:\"symbol\",variants:[{begin:\"^\\\\s*[A-Za-z._?][A-Za-z0-9_$#@~.?]*(:|\\\\s+label)\"},{begin:\"^\\\\s*%%[A-Za-z0-9_$#@~.?]*:\"}],relevance:0},{className:\"subst\",begin:\"%[0-9]+\",relevance:0},{className:\"subst\",begin:\"%!S+\",relevance:0},{className:\"meta\",begin:/^\\s*\\.[\\w_-]+/}]}}}());"
  },
  {
    "path": "crates/mdbook-html/front-end/playground_editor/ace.js",
    "content": "/*\nCopyright (c) 2010, Ajax.org B.V.\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n    * Redistributions of source code must retain the above copyright\n      notice, this list of conditions and the following disclaimer.\n    * Redistributions in binary form must reproduce the above copyright\n      notice, this list of conditions and the following disclaimer in the\n      documentation and/or other materials provided with the distribution.\n    * Neither the name of Ajax.org B.V. nor the\n      names of its contributors may be used to endorse or promote products\n      derived from this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\nANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\nWARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY\nDIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\nLOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\nON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\nSOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n*/\n\n(function(){function o(n){var i=e;n&&(e[n]||(e[n]={}),i=e[n]);if(!i.define||!i.define.packaged)t.original=i.define,i.define=t,i.define.packaged=!0;if(!i.require||!i.require.packaged)r.original=i.require,i.require=r,i.require.packaged=!0}var ACE_NAMESPACE = \"ace\",e=function(){return this}();!e&&typeof window!=\"undefined\"&&(e=window);if(!ACE_NAMESPACE&&typeof requirejs!=\"undefined\")return;var t=function(e,n,r){if(typeof e!=\"string\"){t.original?t.original.apply(this,arguments):(console.error(\"dropping module because define wasn't a string.\"),console.trace());return}arguments.length==2&&(r=n),t.modules[e]||(t.payloads[e]=r,t.modules[e]=null)};t.modules={},t.payloads={};var n=function(e,t,n){if(typeof t==\"string\"){var i=s(e,t);if(i!=undefined)return n&&n(),i}else if(Object.prototype.toString.call(t)===\"[object Array]\"){var o=[];for(var u=0,a=t.length;u<a;++u){var f=s(e,t[u]);if(f==undefined&&r.original)return;o.push(f)}return n&&n.apply(null,o)||!0}},r=function(e,t){var i=n(\"\",e,t);return i==undefined&&r.original?r.original.apply(this,arguments):i},i=function(e,t){if(t.indexOf(\"!\")!==-1){var n=t.split(\"!\");return i(e,n[0])+\"!\"+i(e,n[1])}if(t.charAt(0)==\".\"){var r=e.split(\"/\").slice(0,-1).join(\"/\");t=r+\"/\"+t;while(t.indexOf(\".\")!==-1&&s!=t){var s=t;t=t.replace(/\\/\\.\\//,\"/\").replace(/[^\\/]+\\/\\.\\.\\//,\"\")}}return t},s=function(e,r){r=i(e,r);var s=t.modules[r];if(!s){s=t.payloads[r];if(typeof s==\"function\"){var o={},u={id:r,uri:\"\",exports:o,packaged:!0},a=function(e,t){return n(r,e,t)},f=s(a,o,u);o=f||u.exports,t.modules[r]=o,delete t.payloads[r]}s=t.modules[r]=o||s}return s};o(ACE_NAMESPACE)})(),ace.define(\"ace/lib/regexp\",[\"require\",\"exports\",\"module\"],function(e,t,n){\"use strict\";function o(e){return(e.global?\"g\":\"\")+(e.ignoreCase?\"i\":\"\")+(e.multiline?\"m\":\"\")+(e.extended?\"x\":\"\")+(e.sticky?\"y\":\"\")}function u(e,t,n){if(Array.prototype.indexOf)return e.indexOf(t,n);for(var r=n||0;r<e.length;r++)if(e[r]===t)return r;return-1}var r={exec:RegExp.prototype.exec,test:RegExp.prototype.test,match:String.prototype.match,replace:String.prototype.replace,split:String.prototype.split},i=r.exec.call(/()??/,\"\")[1]===undefined,s=function(){var e=/^/g;return r.test.call(e,\"\"),!e.lastIndex}();if(s&&i)return;RegExp.prototype.exec=function(e){var t=r.exec.apply(this,arguments),n,a;if(typeof e==\"string\"&&t){!i&&t.length>1&&u(t,\"\")>-1&&(a=RegExp(this.source,r.replace.call(o(this),\"g\",\"\")),r.replace.call(e.slice(t.index),a,function(){for(var e=1;e<arguments.length-2;e++)arguments[e]===undefined&&(t[e]=undefined)}));if(this._xregexp&&this._xregexp.captureNames)for(var f=1;f<t.length;f++)n=this._xregexp.captureNames[f-1],n&&(t[n]=t[f]);!s&&this.global&&!t[0].length&&this.lastIndex>t.index&&this.lastIndex--}return t},s||(RegExp.prototype.test=function(e){var t=r.exec.call(this,e);return t&&this.global&&!t[0].length&&this.lastIndex>t.index&&this.lastIndex--,!!t})}),ace.define(\"ace/lib/es5-shim\",[\"require\",\"exports\",\"module\"],function(e,t,n){function r(){}function w(e){try{return Object.defineProperty(e,\"sentinel\",{}),\"sentinel\"in e}catch(t){}}function H(e){return e=+e,e!==e?e=0:e!==0&&e!==1/0&&e!==-1/0&&(e=(e>0||-1)*Math.floor(Math.abs(e))),e}function B(e){var t=typeof e;return e===null||t===\"undefined\"||t===\"boolean\"||t===\"number\"||t===\"string\"}function j(e){var t,n,r;if(B(e))return e;n=e.valueOf;if(typeof n==\"function\"){t=n.call(e);if(B(t))return t}r=e.toString;if(typeof r==\"function\"){t=r.call(e);if(B(t))return t}throw new TypeError}Function.prototype.bind||(Function.prototype.bind=function(t){var n=this;if(typeof n!=\"function\")throw new TypeError(\"Function.prototype.bind called on incompatible \"+n);var i=u.call(arguments,1),s=function(){if(this instanceof s){var e=n.apply(this,i.concat(u.call(arguments)));return Object(e)===e?e:this}return n.apply(t,i.concat(u.call(arguments)))};return n.prototype&&(r.prototype=n.prototype,s.prototype=new r,r.prototype=null),s});var i=Function.prototype.call,s=Array.prototype,o=Object.prototype,u=s.slice,a=i.bind(o.toString),f=i.bind(o.hasOwnProperty),l,c,h,p,d;if(d=f(o,\"__defineGetter__\"))l=i.bind(o.__defineGetter__),c=i.bind(o.__defineSetter__),h=i.bind(o.__lookupGetter__),p=i.bind(o.__lookupSetter__);if([1,2].splice(0).length!=2)if(!function(){function e(e){var t=new Array(e+2);return t[0]=t[1]=0,t}var t=[],n;t.splice.apply(t,e(20)),t.splice.apply(t,e(26)),n=t.length,t.splice(5,0,\"XXX\"),n+1==t.length;if(n+1==t.length)return!0}())Array.prototype.splice=function(e,t){var n=this.length;e>0?e>n&&(e=n):e==void 0?e=0:e<0&&(e=Math.max(n+e,0)),e+t<n||(t=n-e);var r=this.slice(e,e+t),i=u.call(arguments,2),s=i.length;if(e===n)s&&this.push.apply(this,i);else{var o=Math.min(t,n-e),a=e+o,f=a+s-o,l=n-a,c=n-o;if(f<a)for(var h=0;h<l;++h)this[f+h]=this[a+h];else if(f>a)for(h=l;h--;)this[f+h]=this[a+h];if(s&&e===c)this.length=c,this.push.apply(this,i);else{this.length=c+s;for(h=0;h<s;++h)this[e+h]=i[h]}}return r};else{var v=Array.prototype.splice;Array.prototype.splice=function(e,t){return arguments.length?v.apply(this,[e===void 0?0:e,t===void 0?this.length-e:t].concat(u.call(arguments,2))):[]}}Array.isArray||(Array.isArray=function(t){return a(t)==\"[object Array]\"});var m=Object(\"a\"),g=m[0]!=\"a\"||!(0 in m);Array.prototype.forEach||(Array.prototype.forEach=function(t){var n=F(this),r=g&&a(this)==\"[object String]\"?this.split(\"\"):n,i=arguments[1],s=-1,o=r.length>>>0;if(a(t)!=\"[object Function]\")throw new TypeError;while(++s<o)s in r&&t.call(i,r[s],s,n)}),Array.prototype.map||(Array.prototype.map=function(t){var n=F(this),r=g&&a(this)==\"[object String]\"?this.split(\"\"):n,i=r.length>>>0,s=Array(i),o=arguments[1];if(a(t)!=\"[object Function]\")throw new TypeError(t+\" is not a function\");for(var u=0;u<i;u++)u in r&&(s[u]=t.call(o,r[u],u,n));return s}),Array.prototype.filter||(Array.prototype.filter=function(t){var n=F(this),r=g&&a(this)==\"[object String]\"?this.split(\"\"):n,i=r.length>>>0,s=[],o,u=arguments[1];if(a(t)!=\"[object Function]\")throw new TypeError(t+\" is not a function\");for(var f=0;f<i;f++)f in r&&(o=r[f],t.call(u,o,f,n)&&s.push(o));return s}),Array.prototype.every||(Array.prototype.every=function(t){var n=F(this),r=g&&a(this)==\"[object String]\"?this.split(\"\"):n,i=r.length>>>0,s=arguments[1];if(a(t)!=\"[object Function]\")throw new TypeError(t+\" is not a function\");for(var o=0;o<i;o++)if(o in r&&!t.call(s,r[o],o,n))return!1;return!0}),Array.prototype.some||(Array.prototype.some=function(t){var n=F(this),r=g&&a(this)==\"[object String]\"?this.split(\"\"):n,i=r.length>>>0,s=arguments[1];if(a(t)!=\"[object Function]\")throw new TypeError(t+\" is not a function\");for(var o=0;o<i;o++)if(o in r&&t.call(s,r[o],o,n))return!0;return!1}),Array.prototype.reduce||(Array.prototype.reduce=function(t){var n=F(this),r=g&&a(this)==\"[object String]\"?this.split(\"\"):n,i=r.length>>>0;if(a(t)!=\"[object Function]\")throw new TypeError(t+\" is not a function\");if(!i&&arguments.length==1)throw new TypeError(\"reduce of empty array with no initial value\");var s=0,o;if(arguments.length>=2)o=arguments[1];else do{if(s in r){o=r[s++];break}if(++s>=i)throw new TypeError(\"reduce of empty array with no initial value\")}while(!0);for(;s<i;s++)s in r&&(o=t.call(void 0,o,r[s],s,n));return o}),Array.prototype.reduceRight||(Array.prototype.reduceRight=function(t){var n=F(this),r=g&&a(this)==\"[object String]\"?this.split(\"\"):n,i=r.length>>>0;if(a(t)!=\"[object Function]\")throw new TypeError(t+\" is not a function\");if(!i&&arguments.length==1)throw new TypeError(\"reduceRight of empty array with no initial value\");var s,o=i-1;if(arguments.length>=2)s=arguments[1];else do{if(o in r){s=r[o--];break}if(--o<0)throw new TypeError(\"reduceRight of empty array with no initial value\")}while(!0);do o in this&&(s=t.call(void 0,s,r[o],o,n));while(o--);return s});if(!Array.prototype.indexOf||[0,1].indexOf(1,2)!=-1)Array.prototype.indexOf=function(t){var n=g&&a(this)==\"[object String]\"?this.split(\"\"):F(this),r=n.length>>>0;if(!r)return-1;var i=0;arguments.length>1&&(i=H(arguments[1])),i=i>=0?i:Math.max(0,r+i);for(;i<r;i++)if(i in n&&n[i]===t)return i;return-1};if(!Array.prototype.lastIndexOf||[0,1].lastIndexOf(0,-3)!=-1)Array.prototype.lastIndexOf=function(t){var n=g&&a(this)==\"[object String]\"?this.split(\"\"):F(this),r=n.length>>>0;if(!r)return-1;var i=r-1;arguments.length>1&&(i=Math.min(i,H(arguments[1]))),i=i>=0?i:r-Math.abs(i);for(;i>=0;i--)if(i in n&&t===n[i])return i;return-1};Object.getPrototypeOf||(Object.getPrototypeOf=function(t){return t.__proto__||(t.constructor?t.constructor.prototype:o)});if(!Object.getOwnPropertyDescriptor){var y=\"Object.getOwnPropertyDescriptor called on a non-object: \";Object.getOwnPropertyDescriptor=function(t,n){if(typeof t!=\"object\"&&typeof t!=\"function\"||t===null)throw new TypeError(y+t);if(!f(t,n))return;var r,i,s;r={enumerable:!0,configurable:!0};if(d){var u=t.__proto__;t.__proto__=o;var i=h(t,n),s=p(t,n);t.__proto__=u;if(i||s)return i&&(r.get=i),s&&(r.set=s),r}return r.value=t[n],r}}Object.getOwnPropertyNames||(Object.getOwnPropertyNames=function(t){return Object.keys(t)});if(!Object.create){var b;Object.prototype.__proto__===null?b=function(){return{__proto__:null}}:b=function(){var e={};for(var t in e)e[t]=null;return e.constructor=e.hasOwnProperty=e.propertyIsEnumerable=e.isPrototypeOf=e.toLocaleString=e.toString=e.valueOf=e.__proto__=null,e},Object.create=function(t,n){var r;if(t===null)r=b();else{if(typeof t!=\"object\")throw new TypeError(\"typeof prototype[\"+typeof t+\"] != 'object'\");var i=function(){};i.prototype=t,r=new i,r.__proto__=t}return n!==void 0&&Object.defineProperties(r,n),r}}if(Object.defineProperty){var E=w({}),S=typeof document==\"undefined\"||w(document.createElement(\"div\"));if(!E||!S)var x=Object.defineProperty}if(!Object.defineProperty||x){var T=\"Property description must be an object: \",N=\"Object.defineProperty called on non-object: \",C=\"getters & setters can not be defined on this javascript engine\";Object.defineProperty=function(t,n,r){if(typeof t!=\"object\"&&typeof t!=\"function\"||t===null)throw new TypeError(N+t);if(typeof r!=\"object\"&&typeof r!=\"function\"||r===null)throw new TypeError(T+r);if(x)try{return x.call(Object,t,n,r)}catch(i){}if(f(r,\"value\"))if(d&&(h(t,n)||p(t,n))){var s=t.__proto__;t.__proto__=o,delete t[n],t[n]=r.value,t.__proto__=s}else t[n]=r.value;else{if(!d)throw new TypeError(C);f(r,\"get\")&&l(t,n,r.get),f(r,\"set\")&&c(t,n,r.set)}return t}}Object.defineProperties||(Object.defineProperties=function(t,n){for(var r in n)f(n,r)&&Object.defineProperty(t,r,n[r]);return t}),Object.seal||(Object.seal=function(t){return t}),Object.freeze||(Object.freeze=function(t){return t});try{Object.freeze(function(){})}catch(k){Object.freeze=function(t){return function(n){return typeof n==\"function\"?n:t(n)}}(Object.freeze)}Object.preventExtensions||(Object.preventExtensions=function(t){return t}),Object.isSealed||(Object.isSealed=function(t){return!1}),Object.isFrozen||(Object.isFrozen=function(t){return!1}),Object.isExtensible||(Object.isExtensible=function(t){if(Object(t)===t)throw new TypeError;var n=\"\";while(f(t,n))n+=\"?\";t[n]=!0;var r=f(t,n);return delete t[n],r});if(!Object.keys){var L=!0,A=[\"toString\",\"toLocaleString\",\"valueOf\",\"hasOwnProperty\",\"isPrototypeOf\",\"propertyIsEnumerable\",\"constructor\"],O=A.length;for(var M in{toString:null})L=!1;Object.keys=function I(e){if(typeof e!=\"object\"&&typeof e!=\"function\"||e===null)throw new TypeError(\"Object.keys called on a non-object\");var I=[];for(var t in e)f(e,t)&&I.push(t);if(L)for(var n=0,r=O;n<r;n++){var i=A[n];f(e,i)&&I.push(i)}return I}}Date.now||(Date.now=function(){return(new Date).getTime()});var _=\"\t\\n\\x0b\\f\\r \\u00a0\\u1680\\u180e\\u2000\\u2001\\u2002\\u2003\\u2004\\u2005\\u2006\\u2007\\u2008\\u2009\\u200a\\u202f\\u205f\\u3000\\u2028\\u2029\\ufeff\";if(!String.prototype.trim||_.trim()){_=\"[\"+_+\"]\";var D=new RegExp(\"^\"+_+_+\"*\"),P=new RegExp(_+_+\"*$\");String.prototype.trim=function(){return String(this).replace(D,\"\").replace(P,\"\")}}var F=function(e){if(e==null)throw new TypeError(\"can't convert \"+e+\" to object\");return Object(e)}}),ace.define(\"ace/lib/fixoldbrowsers\",[\"require\",\"exports\",\"module\",\"ace/lib/regexp\",\"ace/lib/es5-shim\"],function(e,t,n){\"use strict\";e(\"./regexp\"),e(\"./es5-shim\"),typeof Element!=\"undefined\"&&!Element.prototype.remove&&Object.defineProperty(Element.prototype,\"remove\",{enumerable:!1,writable:!0,configurable:!0,value:function(){this.parentNode&&this.parentNode.removeChild(this)}})}),ace.define(\"ace/lib/useragent\",[\"require\",\"exports\",\"module\"],function(e,t,n){\"use strict\";t.OS={LINUX:\"LINUX\",MAC:\"MAC\",WINDOWS:\"WINDOWS\"},t.getOS=function(){return t.isMac?t.OS.MAC:t.isLinux?t.OS.LINUX:t.OS.WINDOWS};if(typeof navigator!=\"object\")return;var r=(navigator.platform.match(/mac|win|linux/i)||[\"other\"])[0].toLowerCase(),i=navigator.userAgent;t.isWin=r==\"win\",t.isMac=r==\"mac\",t.isLinux=r==\"linux\",t.isIE=navigator.appName==\"Microsoft Internet Explorer\"||navigator.appName.indexOf(\"MSAppHost\")>=0?parseFloat((i.match(/(?:MSIE |Trident\\/[0-9]+[\\.0-9]+;.*rv:)([0-9]+[\\.0-9]+)/)||[])[1]):parseFloat((i.match(/(?:Trident\\/[0-9]+[\\.0-9]+;.*rv:)([0-9]+[\\.0-9]+)/)||[])[1]),t.isOldIE=t.isIE&&t.isIE<9,t.isGecko=t.isMozilla=i.match(/ Gecko\\/\\d+/),t.isOpera=window.opera&&Object.prototype.toString.call(window.opera)==\"[object Opera]\",t.isWebKit=parseFloat(i.split(\"WebKit/\")[1])||undefined,t.isChrome=parseFloat(i.split(\" Chrome/\")[1])||undefined,t.isEdge=parseFloat(i.split(\" Edge/\")[1])||undefined,t.isAIR=i.indexOf(\"AdobeAIR\")>=0,t.isIPad=i.indexOf(\"iPad\")>=0,t.isAndroid=i.indexOf(\"Android\")>=0,t.isChromeOS=i.indexOf(\" CrOS \")>=0,t.isIOS=/iPad|iPhone|iPod/.test(i)&&!window.MSStream,t.isIOS&&(t.isMac=!0),t.isMobile=t.isIPad||t.isAndroid}),ace.define(\"ace/lib/dom\",[\"require\",\"exports\",\"module\",\"ace/lib/useragent\"],function(e,t,n){\"use strict\";var r=e(\"./useragent\"),i=\"http://www.w3.org/1999/xhtml\";t.buildDom=function o(e,t,n){if(typeof e==\"string\"&&e){var r=document.createTextNode(e);return t&&t.appendChild(r),r}if(!Array.isArray(e))return e;if(typeof e[0]!=\"string\"||!e[0]){var i=[];for(var s=0;s<e.length;s++){var u=o(e[s],t,n);u&&i.push(u)}return i}var a=document.createElement(e[0]),f=e[1],l=1;f&&typeof f==\"object\"&&!Array.isArray(f)&&(l=2);for(var s=l;s<e.length;s++)o(e[s],a,n);return l==2&&Object.keys(f).forEach(function(e){var t=f[e];e===\"class\"?a.className=Array.isArray(t)?t.join(\" \"):t:typeof t==\"function\"||e==\"value\"?a[e]=t:e===\"ref\"?n&&(n[t]=a):t!=null&&a.setAttribute(e,t)}),t&&t.appendChild(a),a},t.getDocumentHead=function(e){return e||(e=document),e.head||e.getElementsByTagName(\"head\")[0]||e.documentElement},t.createElement=function(e,t){return document.createElementNS?document.createElementNS(t||i,e):document.createElement(e)},t.removeChildren=function(e){e.innerHTML=\"\"},t.createTextNode=function(e,t){var n=t?t.ownerDocument:document;return n.createTextNode(e)},t.createFragment=function(e){var t=e?e.ownerDocument:document;return t.createDocumentFragment()},t.hasCssClass=function(e,t){var n=(e.className+\"\").split(/\\s+/g);return n.indexOf(t)!==-1},t.addCssClass=function(e,n){t.hasCssClass(e,n)||(e.className+=\" \"+n)},t.removeCssClass=function(e,t){var n=e.className.split(/\\s+/g);for(;;){var r=n.indexOf(t);if(r==-1)break;n.splice(r,1)}e.className=n.join(\" \")},t.toggleCssClass=function(e,t){var n=e.className.split(/\\s+/g),r=!0;for(;;){var i=n.indexOf(t);if(i==-1)break;r=!1,n.splice(i,1)}return r&&n.push(t),e.className=n.join(\" \"),r},t.setCssClass=function(e,n,r){r?t.addCssClass(e,n):t.removeCssClass(e,n)},t.hasCssString=function(e,t){var n=0,r;t=t||document;if(r=t.querySelectorAll(\"style\"))while(n<r.length)if(r[n++].id===e)return!0},t.importCssString=function(n,r,i){var s=i;if(!i||!i.getRootNode)s=document;else{s=i.getRootNode();if(!s||s==i)s=document}var o=s.ownerDocument||s;if(r&&t.hasCssString(r,s))return null;r&&(n+=\"\\n/*# sourceURL=ace/css/\"+r+\" */\");var u=t.createElement(\"style\");u.appendChild(o.createTextNode(n)),r&&(u.id=r),s==o&&(s=t.getDocumentHead(o)),s.insertBefore(u,s.firstChild)},t.importCssStylsheet=function(e,n){t.buildDom([\"link\",{rel:\"stylesheet\",href:e}],t.getDocumentHead(n))},t.scrollbarWidth=function(e){var n=t.createElement(\"ace_inner\");n.style.width=\"100%\",n.style.minWidth=\"0px\",n.style.height=\"200px\",n.style.display=\"block\";var r=t.createElement(\"ace_outer\"),i=r.style;i.position=\"absolute\",i.left=\"-10000px\",i.overflow=\"hidden\",i.width=\"200px\",i.minWidth=\"0px\",i.height=\"150px\",i.display=\"block\",r.appendChild(n);var s=e.documentElement;s.appendChild(r);var o=n.offsetWidth;i.overflow=\"scroll\";var u=n.offsetWidth;return o==u&&(u=r.clientWidth),s.removeChild(r),o-u},typeof document==\"undefined\"&&(t.importCssString=function(){}),t.computedStyle=function(e,t){return window.getComputedStyle(e,\"\")||{}},t.setStyle=function(e,t,n){e[t]!==n&&(e[t]=n)},t.HAS_CSS_ANIMATION=!1,t.HAS_CSS_TRANSFORMS=!1,t.HI_DPI=r.isWin?typeof window!=\"undefined\"&&window.devicePixelRatio>=1.5:!0;if(typeof document!=\"undefined\"){var s=document.createElement(\"div\");t.HI_DPI&&s.style.transform!==undefined&&(t.HAS_CSS_TRANSFORMS=!0),!r.isEdge&&typeof s.style.animationName!=\"undefined\"&&(t.HAS_CSS_ANIMATION=!0),s=null}t.HAS_CSS_TRANSFORMS?t.translate=function(e,t,n){e.style.transform=\"translate(\"+Math.round(t)+\"px, \"+Math.round(n)+\"px)\"}:t.translate=function(e,t,n){e.style.top=Math.round(n)+\"px\",e.style.left=Math.round(t)+\"px\"}}),ace.define(\"ace/lib/oop\",[\"require\",\"exports\",\"module\"],function(e,t,n){\"use strict\";t.inherits=function(e,t){e.super_=t,e.prototype=Object.create(t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}})},t.mixin=function(e,t){for(var n in t)e[n]=t[n];return e},t.implement=function(e,n){t.mixin(e,n)}}),ace.define(\"ace/lib/keys\",[\"require\",\"exports\",\"module\",\"ace/lib/oop\"],function(e,t,n){\"use strict\";var r=e(\"./oop\"),i=function(){var e={MODIFIER_KEYS:{16:\"Shift\",17:\"Ctrl\",18:\"Alt\",224:\"Meta\"},KEY_MODS:{ctrl:1,alt:2,option:2,shift:4,\"super\":8,meta:8,command:8,cmd:8},FUNCTION_KEYS:{8:\"Backspace\",9:\"Tab\",13:\"Return\",19:\"Pause\",27:\"Esc\",32:\"Space\",33:\"PageUp\",34:\"PageDown\",35:\"End\",36:\"Home\",37:\"Left\",38:\"Up\",39:\"Right\",40:\"Down\",44:\"Print\",45:\"Insert\",46:\"Delete\",96:\"Numpad0\",97:\"Numpad1\",98:\"Numpad2\",99:\"Numpad3\",100:\"Numpad4\",101:\"Numpad5\",102:\"Numpad6\",103:\"Numpad7\",104:\"Numpad8\",105:\"Numpad9\",\"-13\":\"NumpadEnter\",112:\"F1\",113:\"F2\",114:\"F3\",115:\"F4\",116:\"F5\",117:\"F6\",118:\"F7\",119:\"F8\",120:\"F9\",121:\"F10\",122:\"F11\",123:\"F12\",144:\"Numlock\",145:\"Scrolllock\"},PRINTABLE_KEYS:{32:\" \",48:\"0\",49:\"1\",50:\"2\",51:\"3\",52:\"4\",53:\"5\",54:\"6\",55:\"7\",56:\"8\",57:\"9\",59:\";\",61:\"=\",65:\"a\",66:\"b\",67:\"c\",68:\"d\",69:\"e\",70:\"f\",71:\"g\",72:\"h\",73:\"i\",74:\"j\",75:\"k\",76:\"l\",77:\"m\",78:\"n\",79:\"o\",80:\"p\",81:\"q\",82:\"r\",83:\"s\",84:\"t\",85:\"u\",86:\"v\",87:\"w\",88:\"x\",89:\"y\",90:\"z\",107:\"+\",109:\"-\",110:\".\",186:\";\",187:\"=\",188:\",\",189:\"-\",190:\".\",191:\"/\",192:\"`\",219:\"[\",220:\"\\\\\",221:\"]\",222:\"'\",111:\"/\",106:\"*\"}},t,n;for(n in e.FUNCTION_KEYS)t=e.FUNCTION_KEYS[n].toLowerCase(),e[t]=parseInt(n,10);for(n in e.PRINTABLE_KEYS)t=e.PRINTABLE_KEYS[n].toLowerCase(),e[t]=parseInt(n,10);return r.mixin(e,e.MODIFIER_KEYS),r.mixin(e,e.PRINTABLE_KEYS),r.mixin(e,e.FUNCTION_KEYS),e.enter=e[\"return\"],e.escape=e.esc,e.del=e[\"delete\"],e[173]=\"-\",function(){var t=[\"cmd\",\"ctrl\",\"alt\",\"shift\"];for(var n=Math.pow(2,t.length);n--;)e.KEY_MODS[n]=t.filter(function(t){return n&e.KEY_MODS[t]}).join(\"-\")+\"-\"}(),e.KEY_MODS[0]=\"\",e.KEY_MODS[-1]=\"input-\",e}();r.mixin(t,i),t.keyCodeToString=function(e){var t=i[e];return typeof t!=\"string\"&&(t=String.fromCharCode(e)),t.toLowerCase()}}),ace.define(\"ace/lib/event\",[\"require\",\"exports\",\"module\",\"ace/lib/keys\",\"ace/lib/useragent\"],function(e,t,n){\"use strict\";function a(e,t,n){var a=u(t);if(!i.isMac&&s){t.getModifierState&&(t.getModifierState(\"OS\")||t.getModifierState(\"Win\"))&&(a|=8);if(s.altGr){if((3&a)==3)return;s.altGr=0}if(n===18||n===17){var f=\"location\"in t?t.location:t.keyLocation;if(n===17&&f===1)s[n]==1&&(o=t.timeStamp);else if(n===18&&a===3&&f===2){var l=t.timeStamp-o;l<50&&(s.altGr=!0)}}}n in r.MODIFIER_KEYS&&(n=-1),a&8&&n>=91&&n<=93&&(n=-1);if(!a&&n===13){var f=\"location\"in t?t.location:t.keyLocation;if(f===3){e(t,a,-n);if(t.defaultPrevented)return}}if(i.isChromeOS&&a&8){e(t,a,n);if(t.defaultPrevented)return;a&=-9}return!!a||n in r.FUNCTION_KEYS||n in r.PRINTABLE_KEYS?e(t,a,n):!1}function f(){s=Object.create(null)}var r=e(\"./keys\"),i=e(\"./useragent\"),s=null,o=0;t.addListener=function(e,t,n){if(e.addEventListener)return e.addEventListener(t,n,!1);if(e.attachEvent){var r=function(){n.call(e,window.event)};n._wrapper=r,e.attachEvent(\"on\"+t,r)}},t.removeListener=function(e,t,n){if(e.removeEventListener)return e.removeEventListener(t,n,!1);e.detachEvent&&e.detachEvent(\"on\"+t,n._wrapper||n)},t.stopEvent=function(e){return t.stopPropagation(e),t.preventDefault(e),!1},t.stopPropagation=function(e){e.stopPropagation?e.stopPropagation():e.cancelBubble=!0},t.preventDefault=function(e){e.preventDefault?e.preventDefault():e.returnValue=!1},t.getButton=function(e){return e.type==\"dblclick\"?0:e.type==\"contextmenu\"||i.isMac&&e.ctrlKey&&!e.altKey&&!e.shiftKey?2:e.preventDefault?e.button:{1:0,2:2,4:1}[e.button]},t.capture=function(e,n,r){function i(e){n&&n(e),r&&r(e),t.removeListener(document,\"mousemove\",n,!0),t.removeListener(document,\"mouseup\",i,!0),t.removeListener(document,\"dragstart\",i,!0)}return t.addListener(document,\"mousemove\",n,!0),t.addListener(document,\"mouseup\",i,!0),t.addListener(document,\"dragstart\",i,!0),i},t.addTouchMoveListener=function(e,n){var r,i;t.addListener(e,\"touchstart\",function(e){var t=e.touches,n=t[0];r=n.clientX,i=n.clientY}),t.addListener(e,\"touchmove\",function(e){var t=e.touches;if(t.length>1)return;var s=t[0];e.wheelX=r-s.clientX,e.wheelY=i-s.clientY,r=s.clientX,i=s.clientY,n(e)})},t.addMouseWheelListener=function(e,n){\"onmousewheel\"in e?t.addListener(e,\"mousewheel\",function(e){var t=8;e.wheelDeltaX!==undefined?(e.wheelX=-e.wheelDeltaX/t,e.wheelY=-e.wheelDeltaY/t):(e.wheelX=0,e.wheelY=-e.wheelDelta/t),n(e)}):\"onwheel\"in e?t.addListener(e,\"wheel\",function(e){var t=.35;switch(e.deltaMode){case e.DOM_DELTA_PIXEL:e.wheelX=e.deltaX*t||0,e.wheelY=e.deltaY*t||0;break;case e.DOM_DELTA_LINE:case e.DOM_DELTA_PAGE:e.wheelX=(e.deltaX||0)*5,e.wheelY=(e.deltaY||0)*5}n(e)}):t.addListener(e,\"DOMMouseScroll\",function(e){e.axis&&e.axis==e.HORIZONTAL_AXIS?(e.wheelX=(e.detail||0)*5,e.wheelY=0):(e.wheelX=0,e.wheelY=(e.detail||0)*5),n(e)})},t.addMultiMouseDownListener=function(e,n,r,s){function c(e){t.getButton(e)!==0?o=0:e.detail>1?(o++,o>4&&(o=1)):o=1;if(i.isIE){var c=Math.abs(e.clientX-u)>5||Math.abs(e.clientY-a)>5;if(!f||c)o=1;f&&clearTimeout(f),f=setTimeout(function(){f=null},n[o-1]||600),o==1&&(u=e.clientX,a=e.clientY)}e._clicks=o,r[s](\"mousedown\",e);if(o>4)o=0;else if(o>1)return r[s](l[o],e)}function h(e){o=2,f&&clearTimeout(f),f=setTimeout(function(){f=null},n[o-1]||600),r[s](\"mousedown\",e),r[s](l[o],e)}var o=0,u,a,f,l={2:\"dblclick\",3:\"tripleclick\",4:\"quadclick\"};Array.isArray(e)||(e=[e]),e.forEach(function(e){t.addListener(e,\"mousedown\",c),i.isOldIE&&t.addListener(e,\"dblclick\",h)})};var u=!i.isMac||!i.isOpera||\"KeyboardEvent\"in window?function(e){return 0|(e.ctrlKey?1:0)|(e.altKey?2:0)|(e.shiftKey?4:0)|(e.metaKey?8:0)}:function(e){return 0|(e.metaKey?1:0)|(e.altKey?2:0)|(e.shiftKey?4:0)|(e.ctrlKey?8:0)};t.getModifierString=function(e){return r.KEY_MODS[u(e)]},t.addCommandKeyListener=function(e,n){var r=t.addListener;if(i.isOldGecko||i.isOpera&&!(\"KeyboardEvent\"in window)){var o=null;r(e,\"keydown\",function(e){o=e.keyCode}),r(e,\"keypress\",function(e){return a(n,e,o)})}else{var u=null;r(e,\"keydown\",function(e){s[e.keyCode]=(s[e.keyCode]||0)+1;var t=a(n,e,e.keyCode);return u=e.defaultPrevented,t}),r(e,\"keypress\",function(e){u&&(e.ctrlKey||e.altKey||e.shiftKey||e.metaKey)&&(t.stopEvent(e),u=null)}),r(e,\"keyup\",function(e){s[e.keyCode]=null}),s||(f(),r(window,\"focus\",f))}};if(typeof window==\"object\"&&window.postMessage&&!i.isOldIE){var l=1;t.nextTick=function(e,n){n=n||window;var r=\"zero-timeout-message-\"+l++,i=function(s){s.data==r&&(t.stopPropagation(s),t.removeListener(n,\"message\",i),e())};t.addListener(n,\"message\",i),n.postMessage(r,\"*\")}}t.$idleBlocked=!1,t.onIdle=function(e,n){return setTimeout(function r(){t.$idleBlocked?setTimeout(r,100):e()},n)},t.$idleBlockId=null,t.blockIdle=function(e){t.$idleBlockId&&clearTimeout(t.$idleBlockId),t.$idleBlocked=!0,t.$idleBlockId=setTimeout(function(){t.$idleBlocked=!1},e||100)},t.nextFrame=typeof window==\"object\"&&(window.requestAnimationFrame||window.mozRequestAnimationFrame||window.webkitRequestAnimationFrame||window.msRequestAnimationFrame||window.oRequestAnimationFrame),t.nextFrame?t.nextFrame=t.nextFrame.bind(window):t.nextFrame=function(e){setTimeout(e,17)}}),ace.define(\"ace/range\",[\"require\",\"exports\",\"module\"],function(e,t,n){\"use strict\";var r=function(e,t){return e.row-t.row||e.column-t.column},i=function(e,t,n,r){this.start={row:e,column:t},this.end={row:n,column:r}};(function(){this.isEqual=function(e){return this.start.row===e.start.row&&this.end.row===e.end.row&&this.start.column===e.start.column&&this.end.column===e.end.column},this.toString=function(){return\"Range: [\"+this.start.row+\"/\"+this.start.column+\"] -> [\"+this.end.row+\"/\"+this.end.column+\"]\"},this.contains=function(e,t){return this.compare(e,t)==0},this.compareRange=function(e){var t,n=e.end,r=e.start;return t=this.compare(n.row,n.column),t==1?(t=this.compare(r.row,r.column),t==1?2:t==0?1:0):t==-1?-2:(t=this.compare(r.row,r.column),t==-1?-1:t==1?42:0)},this.comparePoint=function(e){return this.compare(e.row,e.column)},this.containsRange=function(e){return this.comparePoint(e.start)==0&&this.comparePoint(e.end)==0},this.intersects=function(e){var t=this.compareRange(e);return t==-1||t==0||t==1},this.isEnd=function(e,t){return this.end.row==e&&this.end.column==t},this.isStart=function(e,t){return this.start.row==e&&this.start.column==t},this.setStart=function(e,t){typeof e==\"object\"?(this.start.column=e.column,this.start.row=e.row):(this.start.row=e,this.start.column=t)},this.setEnd=function(e,t){typeof e==\"object\"?(this.end.column=e.column,this.end.row=e.row):(this.end.row=e,this.end.column=t)},this.inside=function(e,t){return this.compare(e,t)==0?this.isEnd(e,t)||this.isStart(e,t)?!1:!0:!1},this.insideStart=function(e,t){return this.compare(e,t)==0?this.isEnd(e,t)?!1:!0:!1},this.insideEnd=function(e,t){return this.compare(e,t)==0?this.isStart(e,t)?!1:!0:!1},this.compare=function(e,t){return!this.isMultiLine()&&e===this.start.row?t<this.start.column?-1:t>this.end.column?1:0:e<this.start.row?-1:e>this.end.row?1:this.start.row===e?t>=this.start.column?0:-1:this.end.row===e?t<=this.end.column?0:1:0},this.compareStart=function(e,t){return this.start.row==e&&this.start.column==t?-1:this.compare(e,t)},this.compareEnd=function(e,t){return this.end.row==e&&this.end.column==t?1:this.compare(e,t)},this.compareInside=function(e,t){return this.end.row==e&&this.end.column==t?1:this.start.row==e&&this.start.column==t?-1:this.compare(e,t)},this.clipRows=function(e,t){if(this.end.row>t)var n={row:t+1,column:0};else if(this.end.row<e)var n={row:e,column:0};if(this.start.row>t)var r={row:t+1,column:0};else if(this.start.row<e)var r={row:e,column:0};return i.fromPoints(r||this.start,n||this.end)},this.extend=function(e,t){var n=this.compare(e,t);if(n==0)return this;if(n==-1)var r={row:e,column:t};else var s={row:e,column:t};return i.fromPoints(r||this.start,s||this.end)},this.isEmpty=function(){return this.start.row===this.end.row&&this.start.column===this.end.column},this.isMultiLine=function(){return this.start.row!==this.end.row},this.clone=function(){return i.fromPoints(this.start,this.end)},this.collapseRows=function(){return this.end.column==0?new i(this.start.row,0,Math.max(this.start.row,this.end.row-1),0):new i(this.start.row,0,this.end.row,0)},this.toScreenRange=function(e){var t=e.documentToScreenPosition(this.start),n=e.documentToScreenPosition(this.end);return new i(t.row,t.column,n.row,n.column)},this.moveBy=function(e,t){this.start.row+=e,this.start.column+=t,this.end.row+=e,this.end.column+=t}}).call(i.prototype),i.fromPoints=function(e,t){return new i(e.row,e.column,t.row,t.column)},i.comparePoints=r,i.comparePoints=function(e,t){return e.row-t.row||e.column-t.column},t.Range=i}),ace.define(\"ace/lib/lang\",[\"require\",\"exports\",\"module\"],function(e,t,n){\"use strict\";t.last=function(e){return e[e.length-1]},t.stringReverse=function(e){return e.split(\"\").reverse().join(\"\")},t.stringRepeat=function(e,t){var n=\"\";while(t>0){t&1&&(n+=e);if(t>>=1)e+=e}return n};var r=/^\\s\\s*/,i=/\\s\\s*$/;t.stringTrimLeft=function(e){return e.replace(r,\"\")},t.stringTrimRight=function(e){return e.replace(i,\"\")},t.copyObject=function(e){var t={};for(var n in e)t[n]=e[n];return t},t.copyArray=function(e){var t=[];for(var n=0,r=e.length;n<r;n++)e[n]&&typeof e[n]==\"object\"?t[n]=this.copyObject(e[n]):t[n]=e[n];return t},t.deepCopy=function s(e){if(typeof e!=\"object\"||!e)return e;var t;if(Array.isArray(e)){t=[];for(var n=0;n<e.length;n++)t[n]=s(e[n]);return t}if(Object.prototype.toString.call(e)!==\"[object Object]\")return e;t={};for(var n in e)t[n]=s(e[n]);return t},t.arrayToMap=function(e){var t={};for(var n=0;n<e.length;n++)t[e[n]]=1;return t},t.createMap=function(e){var t=Object.create(null);for(var n in e)t[n]=e[n];return t},t.arrayRemove=function(e,t){for(var n=0;n<=e.length;n++)t===e[n]&&e.splice(n,1)},t.escapeRegExp=function(e){return e.replace(/([.*+?^${}()|[\\]\\/\\\\])/g,\"\\\\$1\")},t.escapeHTML=function(e){return(\"\"+e).replace(/&/g,\"&#38;\").replace(/\"/g,\"&#34;\").replace(/'/g,\"&#39;\").replace(/</g,\"&#60;\")},t.getMatchOffsets=function(e,t){var n=[];return e.replace(t,function(e){n.push({offset:arguments[arguments.length-2],length:e.length})}),n},t.deferredCall=function(e){var t=null,n=function(){t=null,e()},r=function(e){return r.cancel(),t=setTimeout(n,e||0),r};return r.schedule=r,r.call=function(){return this.cancel(),e(),r},r.cancel=function(){return clearTimeout(t),t=null,r},r.isPending=function(){return t},r},t.delayedCall=function(e,t){var n=null,r=function(){n=null,e()},i=function(e){n==null&&(n=setTimeout(r,e||t))};return i.delay=function(e){n&&clearTimeout(n),n=setTimeout(r,e||t)},i.schedule=i,i.call=function(){this.cancel(),e()},i.cancel=function(){n&&clearTimeout(n),n=null},i.isPending=function(){return n},i}}),ace.define(\"ace/keyboard/textinput\",[\"require\",\"exports\",\"module\",\"ace/lib/event\",\"ace/lib/useragent\",\"ace/lib/dom\",\"ace/lib/lang\",\"ace/lib/keys\"],function(e,t,n){\"use strict\";var r=e(\"../lib/event\"),i=e(\"../lib/useragent\"),s=e(\"../lib/dom\"),o=e(\"../lib/lang\"),u=i.isChrome<18,a=i.isIE,f=i.isChrome>63,l=400,c=e(\"../lib/keys\"),h=c.KEY_MODS,p=i.isIOS,d=p?/\\s/:/\\n/,v=function(e,t){function W(){x=!0,n.blur(),n.focus(),x=!1}function V(e){e.keyCode==27&&n.value.length<n.selectionStart&&(g||(T=n.value),N=C=-1,A()),X()}function J(){clearTimeout($),$=setTimeout(function(){b&&(n.style.cssText=b,b=\"\"),t.renderer.$keepTextAreaAtCursor==null&&(t.renderer.$keepTextAreaAtCursor=!0,t.renderer.$moveTextAreaToCursor())},0)}function Q(e,t,n){var r=null,i=!1;n.addEventListener(\"keydown\",function(e){r&&clearTimeout(r),i=!0},!0),n.addEventListener(\"keyup\",function(e){r=setTimeout(function(){i=!1},100)},!0);var s=function(e){if(document.activeElement!==n)return;if(i||g)return;if(v)return;var r=n.selectionStart,s=n.selectionEnd,o=null,u=0;console.log(r,s);if(r==0)o=c.up;else if(r==1)o=c.home;else if(s>C&&T[s]==\"\\n\")o=c.end;else if(r<N&&T[r-1]==\" \")o=c.left,u=h.option;else if(r<N||r==N&&C!=N&&r==s)o=c.left;else if(s>C&&T.slice(0,s).split(\"\\n\").length>2)o=c.down;else if(s>C&&T[s-1]==\" \")o=c.right,u=h.option;else if(s>C||s==C&&C!=N&&r==s)o=c.right;r!==s&&(u|=h.shift),o&&(t.onCommandKey(null,u,o),N=r,C=s,A(\"\"))};document.addEventListener(\"selectionchange\",s),t.on(\"destroy\",function(){document.removeEventListener(\"selectionchange\",s)})}var n=s.createElement(\"textarea\");n.className=\"ace_text-input\",n.setAttribute(\"wrap\",\"off\"),n.setAttribute(\"autocorrect\",\"off\"),n.setAttribute(\"autocapitalize\",\"off\"),n.setAttribute(\"spellcheck\",!1),n.style.opacity=\"0\",e.insertBefore(n,e.firstChild);var v=!1,m=!1,g=!1,y=!1,b=\"\",w=!0,E=!1;i.isMobile||(n.style.fontSize=\"1px\");var S=!1,x=!1,T=\"\",N=0,C=0;try{var k=document.activeElement===n}catch(L){}r.addListener(n,\"blur\",function(e){if(x)return;t.onBlur(e),k=!1}),r.addListener(n,\"focus\",function(e){if(x)return;k=!0;if(i.isEdge)try{if(!document.hasFocus())return}catch(e){}t.onFocus(e),i.isEdge?setTimeout(A):A()}),this.$focusScroll=!1,this.focus=function(){if(b||f||this.$focusScroll==\"browser\")return n.focus({preventScroll:!0});var e=n.style.top;n.style.position=\"fixed\",n.style.top=\"0px\";try{var t=n.getBoundingClientRect().top!=0}catch(r){return}var i=[];if(t){var s=n.parentElement;while(s&&s.nodeType==1)i.push(s),s.setAttribute(\"ace_nocontext\",!0),!s.parentElement&&s.getRootNode?s=s.getRootNode().host:s=s.parentElement}n.focus({preventScroll:!0}),t&&i.forEach(function(e){e.removeAttribute(\"ace_nocontext\")}),setTimeout(function(){n.style.position=\"\",n.style.top==\"0px\"&&(n.style.top=e)},0)},this.blur=function(){n.blur()},this.isFocused=function(){return k},t.on(\"beforeEndOperation\",function(){if(t.curOp&&t.curOp.command.name==\"insertstring\")return;g&&(T=n.value=\"\",z()),A()});var A=p?function(e){if(!k||v&&!e||y)return;e||(e=\"\");var r=\"\\n ab\"+e+\"cde fg\\n\";r!=n.value&&(n.value=T=r);var i=4,s=4+(e.length||(t.selection.isEmpty()?0:1));(N!=i||C!=s)&&n.setSelectionRange(i,s),N=i,C=s}:function(){if(g||y)return;if(!k&&!D)return;g=!0;var e=t.selection,r=e.getRange(),i=e.cursor.row,s=r.start.column,o=r.end.column,u=t.session.getLine(i);if(r.start.row!=i){var a=t.session.getLine(i-1);s=r.start.row<i-1?0:s,o+=a.length+1,u=a+\"\\n\"+u}else if(r.end.row!=i){var f=t.session.getLine(i+1);o=r.end.row>i+1?f.length:o,o+=u.length+1,u=u+\"\\n\"+f}u.length>l&&(s<l&&o<l?u=u.slice(0,l):(u=\"\\n\",s=0,o=1));var c=u+\"\\n\\n\";c!=T&&(n.value=T=c,N=C=c.length),D&&(N=n.selectionStart,C=n.selectionEnd);if(C!=o||N!=s||n.selectionEnd!=C)try{n.setSelectionRange(s,o),N=s,C=o}catch(h){}g=!1};k&&t.onFocus();var O=function(e){return e.selectionStart===0&&e.selectionEnd>=T.length&&e.value===T&&T&&e.selectionEnd!==C},M=function(e){if(g)return;v?v=!1:O(n)&&(t.selectAll(),A())},_=null;this.setInputHandler=function(e){_=e},this.getInputHandler=function(){return _};var D=!1,P=function(e,r){D&&(D=!1);if(m)return A(),e&&t.onPaste(e),m=!1,\"\";var i=n.selectionStart,s=n.selectionEnd,o=N,u=T.length-C,a=e,f=e.length-i,l=e.length-s,c=0;while(o>0&&T[c]==e[c])c++,o--;a=a.slice(c),c=1;while(u>0&&T.length-c>N-1&&T[T.length-c]==e[e.length-c])c++,u--;return f-=c-1,l-=c-1,a=a.slice(0,a.length-c+1),!r&&f==a.length&&!o&&!u&&!l?\"\":(y=!0,a&&!o&&!u&&!f&&!l||S?t.onTextInput(a):t.onTextInput(a,{extendLeft:o,extendRight:u,restoreStart:f,restoreEnd:l}),y=!1,T=e,N=i,C=s,a)},H=function(e){if(g)return U();var t=n.value,r=P(t,!0);(t.length>l+100||d.test(r))&&A()},B=function(e,t,n){var r=e.clipboardData||window.clipboardData;if(!r||u)return;var i=a||n?\"Text\":\"text/plain\";try{return t?r.setData(i,t)!==!1:r.getData(i)}catch(e){if(!n)return B(e,t,!0)}},j=function(e,i){var s=t.getCopyText();if(!s)return r.preventDefault(e);B(e,s)?(p&&(A(s),v=s,setTimeout(function(){v=!1},10)),i?t.onCut():t.onCopy(),r.preventDefault(e)):(v=!0,n.value=s,n.select(),setTimeout(function(){v=!1,A(),i?t.onCut():t.onCopy()}))},F=function(e){j(e,!0)},I=function(e){j(e,!1)},q=function(e){var s=B(e);typeof s==\"string\"?(s&&t.onPaste(s,e),i.isIE&&setTimeout(A),r.preventDefault(e)):(n.value=\"\",m=!0)};r.addCommandKeyListener(n,t.onCommandKey.bind(t)),r.addListener(n,\"select\",M),r.addListener(n,\"input\",H),r.addListener(n,\"cut\",F),r.addListener(n,\"copy\",I),r.addListener(n,\"paste\",q),(!(\"oncut\"in n)||!(\"oncopy\"in n)||!(\"onpaste\"in n))&&r.addListener(e,\"keydown\",function(e){if(i.isMac&&!e.metaKey||!e.ctrlKey)return;switch(e.keyCode){case 67:I(e);break;case 86:q(e);break;case 88:F(e)}});var R=function(e){if(g||!t.onCompositionStart||t.$readOnly)return;g={};if(S)return;setTimeout(U,0),t.on(\"mousedown\",W);var r=t.getSelectionRange();r.end.row=r.start.row,r.end.column=r.start.column,g.markerRange=r,g.selectionStart=N,t.onCompositionStart(g),g.useTextareaForIME?(n.value=\"\",T=\"\",N=0,C=0):(n.msGetInputContext&&(g.context=n.msGetInputContext()),n.getInputContext&&(g.context=n.getInputContext()))},U=function(){if(!g||!t.onCompositionUpdate||t.$readOnly)return;if(S)return W();if(g.useTextareaForIME)t.onCompositionUpdate(n.value);else{var e=n.value;P(e),g.markerRange&&(g.context&&(g.markerRange.start.column=g.selectionStart=g.context.compositionStartOffset),g.markerRange.end.column=g.markerRange.start.column+C-g.selectionStart)}},z=function(e){if(!t.onCompositionEnd||t.$readOnly)return;g=!1,t.onCompositionEnd(),t.off(\"mousedown\",W),e&&H()},X=o.delayedCall(U,50).schedule.bind(null,null);r.addListener(n,\"compositionstart\",R),r.addListener(n,\"compositionupdate\",U),r.addListener(n,\"keyup\",V),r.addListener(n,\"keydown\",X),r.addListener(n,\"compositionend\",z),this.getElement=function(){return n},this.setCommandMode=function(e){S=e,n.readOnly=!1},this.setReadOnly=function(e){S||(n.readOnly=e)},this.setCopyWithEmptySelection=function(e){E=e},this.onContextMenu=function(e){D=!0,A(),t._emit(\"nativecontextmenu\",{target:t,domEvent:e}),this.moveToMouse(e,!0)},this.moveToMouse=function(e,o){b||(b=n.style.cssText),n.style.cssText=(o?\"z-index:100000;\":\"\")+(i.isIE?\"opacity:0.1;\":\"\")+\"text-indent: -\"+(N+C)*t.renderer.characterWidth*.5+\"px;\";var u=t.container.getBoundingClientRect(),a=s.computedStyle(t.container),f=u.top+(parseInt(a.borderTopWidth)||0),l=u.left+(parseInt(u.borderLeftWidth)||0),c=u.bottom-f-n.clientHeight-2,h=function(e){n.style.left=e.clientX-l-2+\"px\",n.style.top=Math.min(e.clientY-f-2,c)+\"px\"};h(e);if(e.type!=\"mousedown\")return;t.renderer.$keepTextAreaAtCursor&&(t.renderer.$keepTextAreaAtCursor=null),clearTimeout($),i.isWin&&r.capture(t.container,h,J)},this.onContextMenuClose=J;var $,K=function(e){t.textInput.onContextMenu(e),J()};r.addListener(n,\"mouseup\",K),r.addListener(n,\"mousedown\",function(e){e.preventDefault(),J()}),r.addListener(t.renderer.scroller,\"contextmenu\",K),r.addListener(n,\"contextmenu\",K),p&&Q(e,t,n)};t.TextInput=v}),ace.define(\"ace/mouse/default_handlers\",[\"require\",\"exports\",\"module\",\"ace/lib/useragent\"],function(e,t,n){\"use strict\";function o(e){e.$clickSelection=null;var t=e.editor;t.setDefaultHandler(\"mousedown\",this.onMouseDown.bind(e)),t.setDefaultHandler(\"dblclick\",this.onDoubleClick.bind(e)),t.setDefaultHandler(\"tripleclick\",this.onTripleClick.bind(e)),t.setDefaultHandler(\"quadclick\",this.onQuadClick.bind(e)),t.setDefaultHandler(\"mousewheel\",this.onMouseWheel.bind(e)),t.setDefaultHandler(\"touchmove\",this.onTouchMove.bind(e));var n=[\"select\",\"startSelect\",\"selectEnd\",\"selectAllEnd\",\"selectByWordsEnd\",\"selectByLinesEnd\",\"dragWait\",\"dragWaitEnd\",\"focusWait\"];n.forEach(function(t){e[t]=this[t]},this),e.selectByLines=this.extendSelectionBy.bind(e,\"getLineRange\"),e.selectByWords=this.extendSelectionBy.bind(e,\"getWordRange\")}function u(e,t,n,r){return Math.sqrt(Math.pow(n-e,2)+Math.pow(r-t,2))}function a(e,t){if(e.start.row==e.end.row)var n=2*t.column-e.start.column-e.end.column;else if(e.start.row==e.end.row-1&&!e.start.column&&!e.end.column)var n=t.column-4;else var n=2*t.row-e.start.row-e.end.row;return n<0?{cursor:e.start,anchor:e.end}:{cursor:e.end,anchor:e.start}}var r=e(\"../lib/useragent\"),i=0,s=550;(function(){this.onMouseDown=function(e){var t=e.inSelection(),n=e.getDocumentPosition();this.mousedownEvent=e;var i=this.editor,s=e.getButton();if(s!==0){var o=i.getSelectionRange(),u=o.isEmpty();(u||s==1)&&i.selection.moveToPosition(n),s==2&&(i.textInput.onContextMenu(e.domEvent),r.isMozilla||e.preventDefault());return}this.mousedownEvent.time=Date.now();if(t&&!i.isFocused()){i.focus();if(this.$focusTimeout&&!this.$clickSelection&&!i.inMultiSelectMode){this.setState(\"focusWait\"),this.captureMouse(e);return}}return this.captureMouse(e),this.startSelect(n,e.domEvent._clicks>1),e.preventDefault()},this.startSelect=function(e,t){e=e||this.editor.renderer.screenToTextCoordinates(this.x,this.y);var n=this.editor;if(!this.mousedownEvent)return;this.mousedownEvent.getShiftKey()?n.selection.selectToPosition(e):t||n.selection.moveToPosition(e),t||this.select(),n.renderer.scroller.setCapture&&n.renderer.scroller.setCapture(),n.setStyle(\"ace_selecting\"),this.setState(\"select\")},this.select=function(){var e,t=this.editor,n=t.renderer.screenToTextCoordinates(this.x,this.y);if(this.$clickSelection){var r=this.$clickSelection.comparePoint(n);if(r==-1)e=this.$clickSelection.end;else if(r==1)e=this.$clickSelection.start;else{var i=a(this.$clickSelection,n);n=i.cursor,e=i.anchor}t.selection.setSelectionAnchor(e.row,e.column)}t.selection.selectToPosition(n),t.renderer.scrollCursorIntoView()},this.extendSelectionBy=function(e){var t,n=this.editor,r=n.renderer.screenToTextCoordinates(this.x,this.y),i=n.selection[e](r.row,r.column);if(this.$clickSelection){var s=this.$clickSelection.comparePoint(i.start),o=this.$clickSelection.comparePoint(i.end);if(s==-1&&o<=0){t=this.$clickSelection.end;if(i.end.row!=r.row||i.end.column!=r.column)r=i.start}else if(o==1&&s>=0){t=this.$clickSelection.start;if(i.start.row!=r.row||i.start.column!=r.column)r=i.end}else if(s==-1&&o==1)r=i.end,t=i.start;else{var u=a(this.$clickSelection,r);r=u.cursor,t=u.anchor}n.selection.setSelectionAnchor(t.row,t.column)}n.selection.selectToPosition(r),n.renderer.scrollCursorIntoView()},this.selectEnd=this.selectAllEnd=this.selectByWordsEnd=this.selectByLinesEnd=function(){this.$clickSelection=null,this.editor.unsetStyle(\"ace_selecting\"),this.editor.renderer.scroller.releaseCapture&&this.editor.renderer.scroller.releaseCapture()},this.focusWait=function(){var e=u(this.mousedownEvent.x,this.mousedownEvent.y,this.x,this.y),t=Date.now();(e>i||t-this.mousedownEvent.time>this.$focusTimeout)&&this.startSelect(this.mousedownEvent.getDocumentPosition())},this.onDoubleClick=function(e){var t=e.getDocumentPosition(),n=this.editor,r=n.session,i=r.getBracketRange(t);i?(i.isEmpty()&&(i.start.column--,i.end.column++),this.setState(\"select\")):(i=n.selection.getWordRange(t.row,t.column),this.setState(\"selectByWords\")),this.$clickSelection=i,this.select()},this.onTripleClick=function(e){var t=e.getDocumentPosition(),n=this.editor;this.setState(\"selectByLines\");var r=n.getSelectionRange();r.isMultiLine()&&r.contains(t.row,t.column)?(this.$clickSelection=n.selection.getLineRange(r.start.row),this.$clickSelection.end=n.selection.getLineRange(r.end.row).end):this.$clickSelection=n.selection.getLineRange(t.row),this.select()},this.onQuadClick=function(e){var t=this.editor;t.selectAll(),this.$clickSelection=t.getSelectionRange(),this.setState(\"selectAll\")},this.onMouseWheel=function(e){if(e.getAccelKey())return;e.getShiftKey()&&e.wheelY&&!e.wheelX&&(e.wheelX=e.wheelY,e.wheelY=0);var t=this.editor;this.$lastScroll||(this.$lastScroll={t:0,vx:0,vy:0,allowed:0});var n=this.$lastScroll,r=e.domEvent.timeStamp,i=r-n.t,o=i?e.wheelX/i:n.vx,u=i?e.wheelY/i:n.vy;i<s&&(o=(o+n.vx)/2,u=(u+n.vy)/2);var a=Math.abs(o/u),f=!1;a>=1&&t.renderer.isScrollableBy(e.wheelX*e.speed,0)&&(f=!0),a<=1&&t.renderer.isScrollableBy(0,e.wheelY*e.speed)&&(f=!0);if(f)n.allowed=r;else if(r-n.allowed<s){var l=Math.abs(o)<=1.5*Math.abs(n.vx)&&Math.abs(u)<=1.5*Math.abs(n.vy);l?(f=!0,n.allowed=r):n.allowed=0}n.t=r,n.vx=o,n.vy=u;if(f)return t.renderer.scrollBy(e.wheelX*e.speed,e.wheelY*e.speed),e.stop()},this.onTouchMove=function(e){this.editor._emit(\"mousewheel\",e)}}).call(o.prototype),t.DefaultHandlers=o}),ace.define(\"ace/tooltip\",[\"require\",\"exports\",\"module\",\"ace/lib/oop\",\"ace/lib/dom\"],function(e,t,n){\"use strict\";function s(e){this.isOpen=!1,this.$element=null,this.$parentNode=e}var r=e(\"./lib/oop\"),i=e(\"./lib/dom\");(function(){this.$init=function(){return this.$element=i.createElement(\"div\"),this.$element.className=\"ace_tooltip\",this.$element.style.display=\"none\",this.$parentNode.appendChild(this.$element),this.$element},this.getElement=function(){return this.$element||this.$init()},this.setText=function(e){this.getElement().textContent=e},this.setHtml=function(e){this.getElement().innerHTML=e},this.setPosition=function(e,t){this.getElement().style.left=e+\"px\",this.getElement().style.top=t+\"px\"},this.setClassName=function(e){i.addCssClass(this.getElement(),e)},this.show=function(e,t,n){e!=null&&this.setText(e),t!=null&&n!=null&&this.setPosition(t,n),this.isOpen||(this.getElement().style.display=\"block\",this.isOpen=!0)},this.hide=function(){this.isOpen&&(this.getElement().style.display=\"none\",this.isOpen=!1)},this.getHeight=function(){return this.getElement().offsetHeight},this.getWidth=function(){return this.getElement().offsetWidth},this.destroy=function(){this.isOpen=!1,this.$element&&this.$element.parentNode&&this.$element.parentNode.removeChild(this.$element)}}).call(s.prototype),t.Tooltip=s}),ace.define(\"ace/mouse/default_gutter_handler\",[\"require\",\"exports\",\"module\",\"ace/lib/dom\",\"ace/lib/oop\",\"ace/lib/event\",\"ace/tooltip\"],function(e,t,n){\"use strict\";function u(e){function l(){var r=u.getDocumentPosition().row,s=n.$annotations[r];if(!s)return c();var o=t.session.getLength();if(r==o){var a=t.renderer.pixelToScreenCoordinates(0,u.y).row,l=u.$pos;if(a>t.session.documentToScreenRow(l.row,l.column))return c()}if(f==s)return;f=s.text.join(\"<br/>\"),i.setHtml(f),i.show(),t._signal(\"showGutterTooltip\",i),t.on(\"mousewheel\",c);if(e.$tooltipFollowsMouse)h(u);else{var p=u.domEvent.target,d=p.getBoundingClientRect(),v=i.getElement().style;v.left=d.right+\"px\",v.top=d.bottom+\"px\"}}function c(){o&&(o=clearTimeout(o)),f&&(i.hide(),f=null,t._signal(\"hideGutterTooltip\",i),t.removeEventListener(\"mousewheel\",c))}function h(e){i.setPosition(e.x,e.y)}var t=e.editor,n=t.renderer.$gutterLayer,i=new a(t.container);e.editor.setDefaultHandler(\"guttermousedown\",function(r){if(!t.isFocused()||r.getButton()!=0)return;var i=n.getRegion(r);if(i==\"foldWidgets\")return;var s=r.getDocumentPosition().row,o=t.session.selection;if(r.getShiftKey())o.selectTo(s,0);else{if(r.domEvent.detail==2)return t.selectAll(),r.preventDefault();e.$clickSelection=t.selection.getLineRange(s)}return e.setState(\"selectByLines\"),e.captureMouse(r),r.preventDefault()});var o,u,f;e.editor.setDefaultHandler(\"guttermousemove\",function(t){var n=t.domEvent.target||t.domEvent.srcElement;if(r.hasCssClass(n,\"ace_fold-widget\"))return c();f&&e.$tooltipFollowsMouse&&h(t),u=t;if(o)return;o=setTimeout(function(){o=null,u&&!e.isMousePressed?l():c()},50)}),s.addListener(t.renderer.$gutter,\"mouseout\",function(e){u=null;if(!f||o)return;o=setTimeout(function(){o=null,c()},50)}),t.on(\"changeSession\",c)}function a(e){o.call(this,e)}var r=e(\"../lib/dom\"),i=e(\"../lib/oop\"),s=e(\"../lib/event\"),o=e(\"../tooltip\").Tooltip;i.inherits(a,o),function(){this.setPosition=function(e,t){var n=window.innerWidth||document.documentElement.clientWidth,r=window.innerHeight||document.documentElement.clientHeight,i=this.getWidth(),s=this.getHeight();e+=15,t+=15,e+i>n&&(e-=e+i-n),t+s>r&&(t-=20+s),o.prototype.setPosition.call(this,e,t)}}.call(a.prototype),t.GutterHandler=u}),ace.define(\"ace/mouse/mouse_event\",[\"require\",\"exports\",\"module\",\"ace/lib/event\",\"ace/lib/useragent\"],function(e,t,n){\"use strict\";var r=e(\"../lib/event\"),i=e(\"../lib/useragent\"),s=t.MouseEvent=function(e,t){this.domEvent=e,this.editor=t,this.x=this.clientX=e.clientX,this.y=this.clientY=e.clientY,this.$pos=null,this.$inSelection=null,this.propagationStopped=!1,this.defaultPrevented=!1};(function(){this.stopPropagation=function(){r.stopPropagation(this.domEvent),this.propagationStopped=!0},this.preventDefault=function(){r.preventDefault(this.domEvent),this.defaultPrevented=!0},this.stop=function(){this.stopPropagation(),this.preventDefault()},this.getDocumentPosition=function(){return this.$pos?this.$pos:(this.$pos=this.editor.renderer.screenToTextCoordinates(this.clientX,this.clientY),this.$pos)},this.inSelection=function(){if(this.$inSelection!==null)return this.$inSelection;var e=this.editor,t=e.getSelectionRange();if(t.isEmpty())this.$inSelection=!1;else{var n=this.getDocumentPosition();this.$inSelection=t.contains(n.row,n.column)}return this.$inSelection},this.getButton=function(){return r.getButton(this.domEvent)},this.getShiftKey=function(){return this.domEvent.shiftKey},this.getAccelKey=i.isMac?function(){return this.domEvent.metaKey}:function(){return this.domEvent.ctrlKey}}).call(s.prototype)}),ace.define(\"ace/mouse/dragdrop_handler\",[\"require\",\"exports\",\"module\",\"ace/lib/dom\",\"ace/lib/event\",\"ace/lib/useragent\"],function(e,t,n){\"use strict\";function f(e){function T(e,n){var r=Date.now(),i=!n||e.row!=n.row,s=!n||e.column!=n.column;if(!S||i||s)t.moveCursorToPosition(e),S=r,x={x:p,y:d};else{var o=l(x.x,x.y,p,d);o>a?S=null:r-S>=u&&(t.renderer.scrollCursorIntoView(),S=null)}}function N(e,n){var r=Date.now(),i=t.renderer.layerConfig.lineHeight,s=t.renderer.layerConfig.characterWidth,u=t.renderer.scroller.getBoundingClientRect(),a={x:{left:p-u.left,right:u.right-p},y:{top:d-u.top,bottom:u.bottom-d}},f=Math.min(a.x.left,a.x.right),l=Math.min(a.y.top,a.y.bottom),c={row:e.row,column:e.column};f/s<=2&&(c.column+=a.x.left<a.x.right?-3:2),l/i<=1&&(c.row+=a.y.top<a.y.bottom?-1:1);var h=e.row!=c.row,v=e.column!=c.column,m=!n||e.row!=n.row;h||v&&!m?E?r-E>=o&&t.renderer.scrollCursorIntoView(c):E=r:E=null}function C(){var e=g;g=t.renderer.screenToTextCoordinates(p,d),T(g,e),N(g,e)}function k(){m=t.selection.toOrientedRange(),h=t.session.addMarker(m,\"ace_selection\",t.getSelectionStyle()),t.clearSelection(),t.isFocused()&&t.renderer.$cursorLayer.setBlinking(!1),clearInterval(v),C(),v=setInterval(C,20),y=0,i.addListener(document,\"mousemove\",O)}function L(){clearInterval(v),t.session.removeMarker(h),h=null,t.selection.fromOrientedRange(m),t.isFocused()&&!w&&t.renderer.$cursorLayer.setBlinking(!t.getReadOnly()),m=null,g=null,y=0,E=null,S=null,i.removeListener(document,\"mousemove\",O)}function O(){A==null&&(A=setTimeout(function(){A!=null&&h&&L()},20))}function M(e){var t=e.types;return!t||Array.prototype.some.call(t,function(e){return e==\"text/plain\"||e==\"Text\"})}function _(e){var t=[\"copy\",\"copymove\",\"all\",\"uninitialized\"],n=[\"move\",\"copymove\",\"linkmove\",\"all\",\"uninitialized\"],r=s.isMac?e.altKey:e.ctrlKey,i=\"uninitialized\";try{i=e.dataTransfer.effectAllowed.toLowerCase()}catch(e){}var o=\"none\";return r&&t.indexOf(i)>=0?o=\"copy\":n.indexOf(i)>=0?o=\"move\":t.indexOf(i)>=0&&(o=\"copy\"),o}var t=e.editor,n=r.createElement(\"img\");n.src=\"data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\",s.isOpera&&(n.style.cssText=\"width:1px;height:1px;position:fixed;top:0;left:0;z-index:2147483647;opacity:0;\");var f=[\"dragWait\",\"dragWaitEnd\",\"startDrag\",\"dragReadyEnd\",\"onMouseDrag\"];f.forEach(function(t){e[t]=this[t]},this),t.addEventListener(\"mousedown\",this.onMouseDown.bind(e));var c=t.container,h,p,d,v,m,g,y=0,b,w,E,S,x;this.onDragStart=function(e){if(this.cancelDrag||!c.draggable){var r=this;return setTimeout(function(){r.startSelect(),r.captureMouse(e)},0),e.preventDefault()}m=t.getSelectionRange();var i=e.dataTransfer;i.effectAllowed=t.getReadOnly()?\"copy\":\"copyMove\",s.isOpera&&(t.container.appendChild(n),n.scrollTop=0),i.setDragImage&&i.setDragImage(n,0,0),s.isOpera&&t.container.removeChild(n),i.clearData(),i.setData(\"Text\",t.session.getTextRange()),w=!0,this.setState(\"drag\")},this.onDragEnd=function(e){c.draggable=!1,w=!1,this.setState(null);if(!t.getReadOnly()){var n=e.dataTransfer.dropEffect;!b&&n==\"move\"&&t.session.remove(t.getSelectionRange()),t.renderer.$cursorLayer.setBlinking(!0)}this.editor.unsetStyle(\"ace_dragging\"),this.editor.renderer.setCursorStyle(\"\")},this.onDragEnter=function(e){if(t.getReadOnly()||!M(e.dataTransfer))return;return p=e.clientX,d=e.clientY,h||k(),y++,e.dataTransfer.dropEffect=b=_(e),i.preventDefault(e)},this.onDragOver=function(e){if(t.getReadOnly()||!M(e.dataTransfer))return;return p=e.clientX,d=e.clientY,h||(k(),y++),A!==null&&(A=null),e.dataTransfer.dropEffect=b=_(e),i.preventDefault(e)},this.onDragLeave=function(e){y--;if(y<=0&&h)return L(),b=null,i.preventDefault(e)},this.onDrop=function(e){if(!g)return;var n=e.dataTransfer;if(w)switch(b){case\"move\":m.contains(g.row,g.column)?m={start:g,end:g}:m=t.moveText(m,g);break;case\"copy\":m=t.moveText(m,g,!0)}else{var r=n.getData(\"Text\");m={start:g,end:t.session.insert(g,r)},t.focus(),b=null}return L(),i.preventDefault(e)},i.addListener(c,\"dragstart\",this.onDragStart.bind(e)),i.addListener(c,\"dragend\",this.onDragEnd.bind(e)),i.addListener(c,\"dragenter\",this.onDragEnter.bind(e)),i.addListener(c,\"dragover\",this.onDragOver.bind(e)),i.addListener(c,\"dragleave\",this.onDragLeave.bind(e)),i.addListener(c,\"drop\",this.onDrop.bind(e));var A=null}function l(e,t,n,r){return Math.sqrt(Math.pow(n-e,2)+Math.pow(r-t,2))}var r=e(\"../lib/dom\"),i=e(\"../lib/event\"),s=e(\"../lib/useragent\"),o=200,u=200,a=5;(function(){this.dragWait=function(){var e=Date.now()-this.mousedownEvent.time;e>this.editor.getDragDelay()&&this.startDrag()},this.dragWaitEnd=function(){var e=this.editor.container;e.draggable=!1,this.startSelect(this.mousedownEvent.getDocumentPosition()),this.selectEnd()},this.dragReadyEnd=function(e){this.editor.renderer.$cursorLayer.setBlinking(!this.editor.getReadOnly()),this.editor.unsetStyle(\"ace_dragging\"),this.editor.renderer.setCursorStyle(\"\"),this.dragWaitEnd()},this.startDrag=function(){this.cancelDrag=!1;var e=this.editor,t=e.container;t.draggable=!0,e.renderer.$cursorLayer.setBlinking(!1),e.setStyle(\"ace_dragging\");var n=s.isWin?\"default\":\"move\";e.renderer.setCursorStyle(n),this.setState(\"dragReady\")},this.onMouseDrag=function(e){var t=this.editor.container;if(s.isIE&&this.state==\"dragReady\"){var n=l(this.mousedownEvent.x,this.mousedownEvent.y,this.x,this.y);n>3&&t.dragDrop()}if(this.state===\"dragWait\"){var n=l(this.mousedownEvent.x,this.mousedownEvent.y,this.x,this.y);n>0&&(t.draggable=!1,this.startSelect(this.mousedownEvent.getDocumentPosition()))}},this.onMouseDown=function(e){if(!this.$dragEnabled)return;this.mousedownEvent=e;var t=this.editor,n=e.inSelection(),r=e.getButton(),i=e.domEvent.detail||1;if(i===1&&r===0&&n){if(e.editor.inMultiSelectMode&&(e.getAccelKey()||e.getShiftKey()))return;this.mousedownEvent.time=Date.now();var o=e.domEvent.target||e.domEvent.srcElement;\"unselectable\"in o&&(o.unselectable=\"on\");if(t.getDragDelay()){if(s.isWebKit){this.cancelDrag=!0;var u=t.container;u.draggable=!0}this.setState(\"dragWait\")}else this.startDrag();this.captureMouse(e,this.onMouseDrag.bind(this)),e.defaultPrevented=!0}}}).call(f.prototype),t.DragdropHandler=f}),ace.define(\"ace/lib/net\",[\"require\",\"exports\",\"module\",\"ace/lib/dom\"],function(e,t,n){\"use strict\";var r=e(\"./dom\");t.get=function(e,t){var n=new XMLHttpRequest;n.open(\"GET\",e,!0),n.onreadystatechange=function(){n.readyState===4&&t(n.responseText)},n.send(null)},t.loadScript=function(e,t){var n=r.getDocumentHead(),i=document.createElement(\"script\");i.src=e,n.appendChild(i),i.onload=i.onreadystatechange=function(e,n){if(n||!i.readyState||i.readyState==\"loaded\"||i.readyState==\"complete\")i=i.onload=i.onreadystatechange=null,n||t()}},t.qualifyURL=function(e){var t=document.createElement(\"a\");return t.href=e,t.href}}),ace.define(\"ace/lib/event_emitter\",[\"require\",\"exports\",\"module\"],function(e,t,n){\"use strict\";var r={},i=function(){this.propagationStopped=!0},s=function(){this.defaultPrevented=!0};r._emit=r._dispatchEvent=function(e,t){this._eventRegistry||(this._eventRegistry={}),this._defaultHandlers||(this._defaultHandlers={});var n=this._eventRegistry[e]||[],r=this._defaultHandlers[e];if(!n.length&&!r)return;if(typeof t!=\"object\"||!t)t={};t.type||(t.type=e),t.stopPropagation||(t.stopPropagation=i),t.preventDefault||(t.preventDefault=s),n=n.slice();for(var o=0;o<n.length;o++){n[o](t,this);if(t.propagationStopped)break}if(r&&!t.defaultPrevented)return r(t,this)},r._signal=function(e,t){var n=(this._eventRegistry||{})[e];if(!n)return;n=n.slice();for(var r=0;r<n.length;r++)n[r](t,this)},r.once=function(e,t){var n=this;this.addEventListener(e,function r(){n.removeEventListener(e,r),t.apply(null,arguments)});if(!t)return new Promise(function(e){t=e})},r.setDefaultHandler=function(e,t){var n=this._defaultHandlers;n||(n=this._defaultHandlers={_disabled_:{}});if(n[e]){var r=n[e],i=n._disabled_[e];i||(n._disabled_[e]=i=[]),i.push(r);var s=i.indexOf(t);s!=-1&&i.splice(s,1)}n[e]=t},r.removeDefaultHandler=function(e,t){var n=this._defaultHandlers;if(!n)return;var r=n._disabled_[e];if(n[e]==t)r&&this.setDefaultHandler(e,r.pop());else if(r){var i=r.indexOf(t);i!=-1&&r.splice(i,1)}},r.on=r.addEventListener=function(e,t,n){this._eventRegistry=this._eventRegistry||{};var r=this._eventRegistry[e];return r||(r=this._eventRegistry[e]=[]),r.indexOf(t)==-1&&r[n?\"unshift\":\"push\"](t),t},r.off=r.removeListener=r.removeEventListener=function(e,t){this._eventRegistry=this._eventRegistry||{};var n=this._eventRegistry[e];if(!n)return;var r=n.indexOf(t);r!==-1&&n.splice(r,1)},r.removeAllListeners=function(e){this._eventRegistry&&(this._eventRegistry[e]=[])},t.EventEmitter=r}),ace.define(\"ace/lib/app_config\",[\"require\",\"exports\",\"module\",\"ace/lib/oop\",\"ace/lib/event_emitter\"],function(e,t,n){\"no use strict\";function o(e){typeof console!=\"undefined\"&&console.warn&&console.warn.apply(console,arguments)}function u(e,t){var n=new Error(e);n.data=t,typeof console==\"object\"&&console.error&&console.error(n),setTimeout(function(){throw n})}var r=e(\"./oop\"),i=e(\"./event_emitter\").EventEmitter,s={setOptions:function(e){Object.keys(e).forEach(function(t){this.setOption(t,e[t])},this)},getOptions:function(e){var t={};if(!e){var n=this.$options;e=Object.keys(n).filter(function(e){return!n[e].hidden})}else Array.isArray(e)||(t=e,e=Object.keys(t));return e.forEach(function(e){t[e]=this.getOption(e)},this),t},setOption:function(e,t){if(this[\"$\"+e]===t)return;var n=this.$options[e];if(!n)return o('misspelled option \"'+e+'\"');if(n.forwardTo)return this[n.forwardTo]&&this[n.forwardTo].setOption(e,t);n.handlesSet||(this[\"$\"+e]=t),n&&n.set&&n.set.call(this,t)},getOption:function(e){var t=this.$options[e];return t?t.forwardTo?this[t.forwardTo]&&this[t.forwardTo].getOption(e):t&&t.get?t.get.call(this):this[\"$\"+e]:o('misspelled option \"'+e+'\"')}},a=function(){this.$defaultOptions={}};(function(){r.implement(this,i),this.defineOptions=function(e,t,n){return e.$options||(this.$defaultOptions[t]=e.$options={}),Object.keys(n).forEach(function(t){var r=n[t];typeof r==\"string\"&&(r={forwardTo:r}),r.name||(r.name=t),e.$options[r.name]=r,\"initialValue\"in r&&(e[\"$\"+r.name]=r.initialValue)}),r.implement(e,s),this},this.resetOptions=function(e){Object.keys(e.$options).forEach(function(t){var n=e.$options[t];\"value\"in n&&e.setOption(t,n.value)})},this.setDefaultValue=function(e,t,n){var r=this.$defaultOptions[e]||(this.$defaultOptions[e]={});r[t]&&(r.forwardTo?this.setDefaultValue(r.forwardTo,t,n):r[t].value=n)},this.setDefaultValues=function(e,t){Object.keys(t).forEach(function(n){this.setDefaultValue(e,n,t[n])},this)},this.warn=o,this.reportError=u}).call(a.prototype),t.AppConfig=a}),ace.define(\"ace/config\",[\"require\",\"exports\",\"module\",\"ace/lib/lang\",\"ace/lib/oop\",\"ace/lib/net\",\"ace/lib/app_config\"],function(e,t,n){\"no use strict\";function l(r){if(!u||!u.document)return;a.packaged=r||e.packaged||n.packaged||u.define&&define.packaged;var i={},s=\"\",o=document.currentScript||document._currentScript,f=o&&o.ownerDocument||document,l=f.getElementsByTagName(\"script\");for(var h=0;h<l.length;h++){var p=l[h],d=p.src||p.getAttribute(\"src\");if(!d)continue;var v=p.attributes;for(var m=0,g=v.length;m<g;m++){var y=v[m];y.name.indexOf(\"data-ace-\")===0&&(i[c(y.name.replace(/^data-ace-/,\"\"))]=y.value)}var b=d.match(/^(.*)\\/ace(\\-\\w+)?\\.js(\\?|$)/);b&&(s=b[1])}s&&(i.base=i.base||s,i.packaged=!0),i.basePath=i.base,i.workerPath=i.workerPath||i.base,i.modePath=i.modePath||i.base,i.themePath=i.themePath||i.base,delete i.base;for(var w in i)typeof i[w]!=\"undefined\"&&t.set(w,i[w])}function c(e){return e.replace(/-(.)/g,function(e,t){return t.toUpperCase()})}var r=e(\"./lib/lang\"),i=e(\"./lib/oop\"),s=e(\"./lib/net\"),o=e(\"./lib/app_config\").AppConfig;n.exports=t=new o;var u=function(){return this||typeof window!=\"undefined\"&&window}(),a={packaged:!1,workerPath:null,modePath:null,themePath:null,basePath:\"\",suffix:\".js\",$moduleUrls:{},loadWorkerFromBlob:!0};t.get=function(e){if(!a.hasOwnProperty(e))throw new Error(\"Unknown config key: \"+e);return a[e]},t.set=function(e,t){if(!a.hasOwnProperty(e))throw new Error(\"Unknown config key: \"+e);a[e]=t},t.all=function(){return r.copyObject(a)},t.$modes={},t.moduleUrl=function(e,t){if(a.$moduleUrls[e])return a.$moduleUrls[e];var n=e.split(\"/\");t=t||n[n.length-2]||\"\";var r=t==\"snippets\"?\"/\":\"-\",i=n[n.length-1];if(t==\"worker\"&&r==\"-\"){var s=new RegExp(\"^\"+t+\"[\\\\-_]|[\\\\-_]\"+t+\"$\",\"g\");i=i.replace(s,\"\")}(!i||i==t)&&n.length>1&&(i=n[n.length-2]);var o=a[t+\"Path\"];return o==null?o=a.basePath:r==\"/\"&&(t=r=\"\"),o&&o.slice(-1)!=\"/\"&&(o+=\"/\"),o+t+r+i+this.get(\"suffix\")},t.setModuleUrl=function(e,t){return a.$moduleUrls[e]=t},t.$loading={},t.loadModule=function(n,r){var i,o;Array.isArray(n)&&(o=n[0],n=n[1]);try{i=e(n)}catch(u){}if(i&&!t.$loading[n])return r&&r(i);t.$loading[n]||(t.$loading[n]=[]),t.$loading[n].push(r);if(t.$loading[n].length>1)return;var a=function(){e([n],function(e){t._emit(\"load.module\",{name:n,module:e});var r=t.$loading[n];t.$loading[n]=null,r.forEach(function(t){t&&t(e)})})};if(!t.get(\"packaged\"))return a();s.loadScript(t.moduleUrl(n,o),a),f()};var f=function(){!a.basePath&&!a.workerPath&&!a.modePath&&!a.themePath&&!Object.keys(a.$moduleUrls).length&&(console.error(\"Unable to infer path to ace from script src,\",\"use ace.config.set('basePath', 'path') to enable dynamic loading of modes and themes\",\"or with webpack use ace/webpack-resolver\"),f=function(){})};t.init=l}),ace.define(\"ace/mouse/mouse_handler\",[\"require\",\"exports\",\"module\",\"ace/lib/event\",\"ace/lib/useragent\",\"ace/mouse/default_handlers\",\"ace/mouse/default_gutter_handler\",\"ace/mouse/mouse_event\",\"ace/mouse/dragdrop_handler\",\"ace/config\"],function(e,t,n){\"use strict\";var r=e(\"../lib/event\"),i=e(\"../lib/useragent\"),s=e(\"./default_handlers\").DefaultHandlers,o=e(\"./default_gutter_handler\").GutterHandler,u=e(\"./mouse_event\").MouseEvent,a=e(\"./dragdrop_handler\").DragdropHandler,f=e(\"../config\"),l=function(e){var t=this;this.editor=e,new s(this),new o(this),new a(this);var n=function(t){var n=!document.hasFocus||!document.hasFocus()||!e.isFocused()&&document.activeElement==(e.textInput&&e.textInput.getElement());n&&window.focus(),e.focus()},u=e.renderer.getMouseEventTarget();r.addListener(u,\"click\",this.onMouseEvent.bind(this,\"click\")),r.addListener(u,\"mousemove\",this.onMouseMove.bind(this,\"mousemove\")),r.addMultiMouseDownListener([u,e.renderer.scrollBarV&&e.renderer.scrollBarV.inner,e.renderer.scrollBarH&&e.renderer.scrollBarH.inner,e.textInput&&e.textInput.getElement()].filter(Boolean),[400,300,250],this,\"onMouseEvent\"),r.addMouseWheelListener(e.container,this.onMouseWheel.bind(this,\"mousewheel\")),r.addTouchMoveListener(e.container,this.onTouchMove.bind(this,\"touchmove\"));var f=e.renderer.$gutter;r.addListener(f,\"mousedown\",this.onMouseEvent.bind(this,\"guttermousedown\")),r.addListener(f,\"click\",this.onMouseEvent.bind(this,\"gutterclick\")),r.addListener(f,\"dblclick\",this.onMouseEvent.bind(this,\"gutterdblclick\")),r.addListener(f,\"mousemove\",this.onMouseEvent.bind(this,\"guttermousemove\")),r.addListener(u,\"mousedown\",n),r.addListener(f,\"mousedown\",n),i.isIE&&e.renderer.scrollBarV&&(r.addListener(e.renderer.scrollBarV.element,\"mousedown\",n),r.addListener(e.renderer.scrollBarH.element,\"mousedown\",n)),e.on(\"mousemove\",function(n){if(t.state||t.$dragDelay||!t.$dragEnabled)return;var r=e.renderer.screenToTextCoordinates(n.x,n.y),i=e.session.selection.getRange(),s=e.renderer;!i.isEmpty()&&i.insideStart(r.row,r.column)?s.setCursorStyle(\"default\"):s.setCursorStyle(\"\")})};(function(){this.onMouseEvent=function(e,t){this.editor._emit(e,new u(t,this.editor))},this.onMouseMove=function(e,t){var n=this.editor._eventRegistry&&this.editor._eventRegistry.mousemove;if(!n||!n.length)return;this.editor._emit(e,new u(t,this.editor))},this.onMouseWheel=function(e,t){var n=new u(t,this.editor);n.speed=this.$scrollSpeed*2,n.wheelX=t.wheelX,n.wheelY=t.wheelY,this.editor._emit(e,n)},this.onTouchMove=function(e,t){var n=new u(t,this.editor);n.speed=1,n.wheelX=t.wheelX,n.wheelY=t.wheelY,this.editor._emit(e,n)},this.setState=function(e){this.state=e},this.captureMouse=function(e,t){this.x=e.x,this.y=e.y,this.isMousePressed=!0;var n=this.editor,s=this.editor.renderer;s.$keepTextAreaAtCursor&&(s.$keepTextAreaAtCursor=null);var o=this,a=function(e){if(!e)return;if(i.isWebKit&&!e.which&&o.releaseMouse)return o.releaseMouse();o.x=e.clientX,o.y=e.clientY,t&&t(e),o.mouseEvent=new u(e,o.editor),o.$mouseMoved=!0},f=function(e){n.off(\"beforeEndOperation\",c),clearInterval(h),l(),o[o.state+\"End\"]&&o[o.state+\"End\"](e),o.state=\"\",s.$keepTextAreaAtCursor==null&&(s.$keepTextAreaAtCursor=!0,s.$moveTextAreaToCursor()),o.isMousePressed=!1,o.$onCaptureMouseMove=o.releaseMouse=null,e&&o.onMouseEvent(\"mouseup\",e),n.endOperation()},l=function(){o[o.state]&&o[o.state](),o.$mouseMoved=!1};if(i.isOldIE&&e.domEvent.type==\"dblclick\")return setTimeout(function(){f(e)});var c=function(e){if(!o.releaseMouse)return;n.curOp.command.name&&n.curOp.selectionChanged&&(o[o.state+\"End\"]&&o[o.state+\"End\"](),o.state=\"\",o.releaseMouse())};n.on(\"beforeEndOperation\",c),n.startOperation({command:{name:\"mouse\"}}),o.$onCaptureMouseMove=a,o.releaseMouse=r.capture(this.editor.container,a,f);var h=setInterval(l,20)},this.releaseMouse=null,this.cancelContextMenu=function(){var e=function(t){if(t&&t.domEvent&&t.domEvent.type!=\"contextmenu\")return;this.editor.off(\"nativecontextmenu\",e),t&&t.domEvent&&r.stopEvent(t.domEvent)}.bind(this);setTimeout(e,10),this.editor.on(\"nativecontextmenu\",e)}}).call(l.prototype),f.defineOptions(l.prototype,\"mouseHandler\",{scrollSpeed:{initialValue:2},dragDelay:{initialValue:i.isMac?150:0},dragEnabled:{initialValue:!0},focusTimeout:{initialValue:0},tooltipFollowsMouse:{initialValue:!0}}),t.MouseHandler=l}),ace.define(\"ace/mouse/fold_handler\",[\"require\",\"exports\",\"module\",\"ace/lib/dom\"],function(e,t,n){\"use strict\";function i(e){e.on(\"click\",function(t){var n=t.getDocumentPosition(),i=e.session,s=i.getFoldAt(n.row,n.column,1);s&&(t.getAccelKey()?i.removeFold(s):i.expandFold(s),t.stop());var o=t.domEvent&&t.domEvent.target;o&&r.hasCssClass(o,\"ace_inline_button\")&&r.hasCssClass(o,\"ace_toggle_wrap\")&&(i.setOption(\"wrap\",!0),e.renderer.scrollCursorIntoView())}),e.on(\"gutterclick\",function(t){var n=e.renderer.$gutterLayer.getRegion(t);if(n==\"foldWidgets\"){var r=t.getDocumentPosition().row,i=e.session;i.foldWidgets&&i.foldWidgets[r]&&e.session.onFoldWidgetClick(r,t),e.isFocused()||e.focus(),t.stop()}}),e.on(\"gutterdblclick\",function(t){var n=e.renderer.$gutterLayer.getRegion(t);if(n==\"foldWidgets\"){var r=t.getDocumentPosition().row,i=e.session,s=i.getParentFoldRangeData(r,!0),o=s.range||s.firstRange;if(o){r=o.start.row;var u=i.getFoldAt(r,i.getLine(r).length,1);u?i.removeFold(u):(i.addFold(\"...\",o),e.renderer.scrollCursorIntoView({row:o.start.row,column:0}))}t.stop()}})}var r=e(\"../lib/dom\");t.FoldHandler=i}),ace.define(\"ace/keyboard/keybinding\",[\"require\",\"exports\",\"module\",\"ace/lib/keys\",\"ace/lib/event\"],function(e,t,n){\"use strict\";var r=e(\"../lib/keys\"),i=e(\"../lib/event\"),s=function(e){this.$editor=e,this.$data={editor:e},this.$handlers=[],this.setDefaultHandler(e.commands)};(function(){this.setDefaultHandler=function(e){this.removeKeyboardHandler(this.$defaultHandler),this.$defaultHandler=e,this.addKeyboardHandler(e,0)},this.setKeyboardHandler=function(e){var t=this.$handlers;if(t[t.length-1]==e)return;while(t[t.length-1]&&t[t.length-1]!=this.$defaultHandler)this.removeKeyboardHandler(t[t.length-1]);this.addKeyboardHandler(e,1)},this.addKeyboardHandler=function(e,t){if(!e)return;typeof e==\"function\"&&!e.handleKeyboard&&(e.handleKeyboard=e);var n=this.$handlers.indexOf(e);n!=-1&&this.$handlers.splice(n,1),t==undefined?this.$handlers.push(e):this.$handlers.splice(t,0,e),n==-1&&e.attach&&e.attach(this.$editor)},this.removeKeyboardHandler=function(e){var t=this.$handlers.indexOf(e);return t==-1?!1:(this.$handlers.splice(t,1),e.detach&&e.detach(this.$editor),!0)},this.getKeyboardHandler=function(){return this.$handlers[this.$handlers.length-1]},this.getStatusText=function(){var e=this.$data,t=e.editor;return this.$handlers.map(function(n){return n.getStatusText&&n.getStatusText(t,e)||\"\"}).filter(Boolean).join(\" \")},this.$callKeyboardHandlers=function(e,t,n,r){var s,o=!1,u=this.$editor.commands;for(var a=this.$handlers.length;a--;){s=this.$handlers[a].handleKeyboard(this.$data,e,t,n,r);if(!s||!s.command)continue;s.command==\"null\"?o=!0:o=u.exec(s.command,this.$editor,s.args,r),o&&r&&e!=-1&&s.passEvent!=1&&s.command.passEvent!=1&&i.stopEvent(r);if(o)break}return!o&&e==-1&&(s={command:\"insertstring\"},o=u.exec(\"insertstring\",this.$editor,t)),o&&this.$editor._signal&&this.$editor._signal(\"keyboardActivity\",s),o},this.onCommandKey=function(e,t,n){var i=r.keyCodeToString(n);this.$callKeyboardHandlers(t,i,n,e)},this.onTextInput=function(e){this.$callKeyboardHandlers(-1,e)}}).call(s.prototype),t.KeyBinding=s}),ace.define(\"ace/lib/bidiutil\",[\"require\",\"exports\",\"module\"],function(e,t,n){\"use strict\";function F(e,t,n,r){var i=s?d:p,c=null,h=null,v=null,m=0,g=null,y=null,b=-1,w=null,E=null,T=[];if(!r)for(w=0,r=[];w<n;w++)r[w]=R(e[w]);o=s,u=!1,a=!1,f=!1,l=!1;for(E=0;E<n;E++){c=m,T[E]=h=q(e,r,T,E),m=i[c][h],g=m&240,m&=15,t[E]=v=i[m][5];if(g>0)if(g==16){for(w=b;w<E;w++)t[w]=1;b=-1}else b=-1;y=i[m][6];if(y)b==-1&&(b=E);else if(b>-1){for(w=b;w<E;w++)t[w]=v;b=-1}r[E]==S&&(t[E]=0),o|=v}if(l)for(w=0;w<n;w++)if(r[w]==x){t[w]=s;for(var C=w-1;C>=0;C--){if(r[C]!=N)break;t[C]=s}}}function I(e,t,n){if(o<e)return;if(e==1&&s==m&&!f){n.reverse();return}var r=n.length,i=0,u,a,l,c;while(i<r){if(t[i]>=e){u=i+1;while(u<r&&t[u]>=e)u++;for(a=i,l=u-1;a<l;a++,l--)c=n[a],n[a]=n[l],n[l]=c;i=u}i++}}function q(e,t,n,r){var i=t[r],o,c,h,p;switch(i){case g:case y:u=!1;case E:case w:return i;case b:return u?w:b;case T:return u=!0,a=!0,y;case N:return E;case C:if(r<1||r+1>=t.length||(o=n[r-1])!=b&&o!=w||(c=t[r+1])!=b&&c!=w)return E;return u&&(c=w),c==o?c:E;case k:o=r>0?n[r-1]:S;if(o==b&&r+1<t.length&&t[r+1]==b)return b;return E;case L:if(r>0&&n[r-1]==b)return b;if(u)return E;p=r+1,h=t.length;while(p<h&&t[p]==L)p++;if(p<h&&t[p]==b)return b;return E;case A:h=t.length,p=r+1;while(p<h&&t[p]==A)p++;if(p<h){var d=e[r],v=d>=1425&&d<=2303||d==64286;o=t[p];if(v&&(o==y||o==T))return y}if(r<1||(o=t[r-1])==S)return E;return n[r-1];case S:return u=!1,f=!0,s;case x:return l=!0,E;case O:case M:case D:case P:case _:u=!1;case H:return E}}function R(e){var t=e.charCodeAt(0),n=t>>8;return n==0?t>191?g:B[t]:n==5?/[\\u0591-\\u05f4]/.test(e)?y:g:n==6?/[\\u0610-\\u061a\\u064b-\\u065f\\u06d6-\\u06e4\\u06e7-\\u06ed]/.test(e)?A:/[\\u0660-\\u0669\\u066b-\\u066c]/.test(e)?w:t==1642?L:/[\\u06f0-\\u06f9]/.test(e)?b:T:n==32&&t<=8287?j[t&255]:n==254?t>=65136?T:E:E}function U(e){return e>=\"\\u064b\"&&e<=\"\\u0655\"}var r=[\"\\u0621\",\"\\u0641\"],i=[\"\\u063a\",\"\\u064a\"],s=0,o=0,u=!1,a=!1,f=!1,l=!1,c=!1,h=!1,p=[[0,3,0,1,0,0,0],[0,3,0,1,2,2,0],[0,3,0,17,2,0,1],[0,3,5,5,4,1,0],[0,3,21,21,4,0,1],[0,3,5,5,4,2,0]],d=[[2,0,1,1,0,1,0],[2,0,1,1,0,2,0],[2,0,2,1,3,2,0],[2,0,2,33,3,1,1]],v=0,m=1,g=0,y=1,b=2,w=3,E=4,S=5,x=6,T=7,N=8,C=9,k=10,L=11,A=12,O=13,M=14,_=15,D=16,P=17,H=18,B=[H,H,H,H,H,H,H,H,H,x,S,x,N,S,H,H,H,H,H,H,H,H,H,H,H,H,H,H,S,S,S,x,N,E,E,L,L,L,E,E,E,E,E,k,C,k,C,C,b,b,b,b,b,b,b,b,b,b,C,E,E,E,E,E,E,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,E,E,E,E,E,E,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,g,E,E,E,E,H,H,H,H,H,H,S,H,H,H,H,H,H,H,H,H,H,H,H,H,H,H,H,H,H,H,H,H,H,H,H,H,H,C,E,L,L,L,L,E,E,E,E,g,E,E,H,E,E,L,L,b,b,E,g,E,E,E,b,g,E,E,E,E,E],j=[N,N,N,N,N,N,N,N,N,N,N,H,H,H,g,y,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,N,S,O,M,_,D,P,C,L,L,L,L,L,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,C,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,E,N];t.L=g,t.R=y,t.EN=b,t.ON_R=3,t.AN=4,t.R_H=5,t.B=6,t.RLE=7,t.DOT=\"\\u00b7\",t.doBidiReorder=function(e,n,r){if(e.length<2)return{};var i=e.split(\"\"),o=new Array(i.length),u=new Array(i.length),a=[];s=r?m:v,F(i,a,i.length,n);for(var f=0;f<o.length;o[f]=f,f++);I(2,a,o),I(1,a,o);for(var f=0;f<o.length-1;f++)n[f]===w?a[f]=t.AN:a[f]===y&&(n[f]>T&&n[f]<O||n[f]===E||n[f]===H)?a[f]=t.ON_R:f>0&&i[f-1]===\"\\u0644\"&&/\\u0622|\\u0623|\\u0625|\\u0627/.test(i[f])&&(a[f-1]=a[f]=t.R_H,f++);i[i.length-1]===t.DOT&&(a[i.length-1]=t.B),i[0]===\"\\u202b\"&&(a[0]=t.RLE);for(var f=0;f<o.length;f++)u[f]=a[o[f]];return{logicalFromVisual:o,bidiLevels:u}},t.hasBidiCharacters=function(e,t){var n=!1;for(var r=0;r<e.length;r++)t[r]=R(e.charAt(r)),!n&&(t[r]==y||t[r]==T||t[r]==w)&&(n=!0);return n},t.getVisualFromLogicalIdx=function(e,t){for(var n=0;n<t.logicalFromVisual.length;n++)if(t.logicalFromVisual[n]==e)return n;return 0}}),ace.define(\"ace/bidihandler\",[\"require\",\"exports\",\"module\",\"ace/lib/bidiutil\",\"ace/lib/lang\"],function(e,t,n){\"use strict\";var r=e(\"./lib/bidiutil\"),i=e(\"./lib/lang\"),s=/[\\u0590-\\u05f4\\u0600-\\u06ff\\u0700-\\u08ac\\u202B]/,o=function(e){this.session=e,this.bidiMap={},this.currentRow=null,this.bidiUtil=r,this.charWidths=[],this.EOL=\"\\u00ac\",this.showInvisibles=!0,this.isRtlDir=!1,this.$isRtl=!1,this.line=\"\",this.wrapIndent=0,this.EOF=\"\\u00b6\",this.RLE=\"\\u202b\",this.contentWidth=0,this.fontMetrics=null,this.rtlLineOffset=0,this.wrapOffset=0,this.isMoveLeftOperation=!1,this.seenBidi=s.test(e.getValue())};(function(){this.isBidiRow=function(e,t,n){return this.seenBidi?(e!==this.currentRow&&(this.currentRow=e,this.updateRowLine(t,n),this.updateBidiMap()),this.bidiMap.bidiLevels):!1},this.onChange=function(e){this.seenBidi?this.currentRow=null:e.action==\"insert\"&&s.test(e.lines.join(\"\\n\"))&&(this.seenBidi=!0,this.currentRow=null)},this.getDocumentRow=function(){var e=0,t=this.session.$screenRowCache;if(t.length){var n=this.session.$getRowCacheIndex(t,this.currentRow);n>=0&&(e=this.session.$docRowCache[n])}return e},this.getSplitIndex=function(){var e=0,t=this.session.$screenRowCache;if(t.length){var n,r=this.session.$getRowCacheIndex(t,this.currentRow);while(this.currentRow-e>0){n=this.session.$getRowCacheIndex(t,this.currentRow-e-1);if(n!==r)break;r=n,e++}}else e=this.currentRow;return e},this.updateRowLine=function(e,t){e===undefined&&(e=this.getDocumentRow());var n=e===this.session.getLength()-1,s=n?this.EOF:this.EOL;this.wrapIndent=0,this.line=this.session.getLine(e),this.isRtlDir=this.$isRtl||this.line.charAt(0)===this.RLE;if(this.session.$useWrapMode){var o=this.session.$wrapData[e];o&&(t===undefined&&(t=this.getSplitIndex()),t>0&&o.length?(this.wrapIndent=o.indent,this.wrapOffset=this.wrapIndent*this.charWidths[r.L],this.line=t<o.length?this.line.substring(o[t-1],o[t]):this.line.substring(o[o.length-1])):this.line=this.line.substring(0,o[t])),t==o.length&&(this.line+=this.showInvisibles?s:r.DOT)}else this.line+=this.showInvisibles?s:r.DOT;var u=this.session,a=0,f;this.line=this.line.replace(/\\t|[\\u1100-\\u2029, \\u202F-\\uFFE6]/g,function(e,t){return e===\"\t\"||u.isFullWidth(e.charCodeAt(0))?(f=e===\"\t\"?u.getScreenTabSize(t+a):2,a+=f-1,i.stringRepeat(r.DOT,f)):e}),this.isRtlDir&&(this.fontMetrics.$main.textContent=this.line.charAt(this.line.length-1)==r.DOT?this.line.substr(0,this.line.length-1):this.line,this.rtlLineOffset=this.contentWidth-this.fontMetrics.$main.getBoundingClientRect().width)},this.updateBidiMap=function(){var e=[];r.hasBidiCharacters(this.line,e)||this.isRtlDir?this.bidiMap=r.doBidiReorder(this.line,e,this.isRtlDir):this.bidiMap={}},this.markAsDirty=function(){this.currentRow=null},this.updateCharacterWidths=function(e){if(this.characterWidth===e.$characterSize.width)return;this.fontMetrics=e;var t=this.characterWidth=e.$characterSize.width,n=e.$measureCharWidth(\"\\u05d4\");this.charWidths[r.L]=this.charWidths[r.EN]=this.charWidths[r.ON_R]=t,this.charWidths[r.R]=this.charWidths[r.AN]=n,this.charWidths[r.R_H]=n*.45,this.charWidths[r.B]=this.charWidths[r.RLE]=0,this.currentRow=null},this.setShowInvisibles=function(e){this.showInvisibles=e,this.currentRow=null},this.setEolChar=function(e){this.EOL=e},this.setContentWidth=function(e){this.contentWidth=e},this.isRtlLine=function(e){return this.$isRtl?!0:e!=undefined?this.session.getLine(e).charAt(0)==this.RLE:this.isRtlDir},this.setRtlDirection=function(e,t){var n=e.getCursorPosition();for(var r=e.selection.getSelectionAnchor().row;r<=n.row;r++)!t&&e.session.getLine(r).charAt(0)===e.session.$bidiHandler.RLE?e.session.doc.removeInLine(r,0,1):t&&e.session.getLine(r).charAt(0)!==e.session.$bidiHandler.RLE&&e.session.doc.insert({column:0,row:r},e.session.$bidiHandler.RLE)},this.getPosLeft=function(e){e-=this.wrapIndent;var t=this.line.charAt(0)===this.RLE?1:0,n=e>t?this.session.getOverwrite()?e:e-1:t,i=r.getVisualFromLogicalIdx(n,this.bidiMap),s=this.bidiMap.bidiLevels,o=0;!this.session.getOverwrite()&&e<=t&&s[i]%2!==0&&i++;for(var u=0;u<i;u++)o+=this.charWidths[s[u]];return!this.session.getOverwrite()&&e>t&&s[i]%2===0&&(o+=this.charWidths[s[i]]),this.wrapIndent&&(o+=this.isRtlDir?-1*this.wrapOffset:this.wrapOffset),this.isRtlDir&&(o+=this.rtlLineOffset),o},this.getSelections=function(e,t){var n=this.bidiMap,r=n.bidiLevels,i,s=[],o=0,u=Math.min(e,t)-this.wrapIndent,a=Math.max(e,t)-this.wrapIndent,f=!1,l=!1,c=0;this.wrapIndent&&(o+=this.isRtlDir?-1*this.wrapOffset:this.wrapOffset);for(var h,p=0;p<r.length;p++)h=n.logicalFromVisual[p],i=r[p],f=h>=u&&h<a,f&&!l?c=o:!f&&l&&s.push({left:c,width:o-c}),o+=this.charWidths[i],l=f;f&&p===r.length&&s.push({left:c,width:o-c});if(this.isRtlDir)for(var d=0;d<s.length;d++)s[d].left+=this.rtlLineOffset;return s},this.offsetToCol=function(e){this.isRtlDir&&(e-=this.rtlLineOffset);var t=0,e=Math.max(e,0),n=0,r=0,i=this.bidiMap.bidiLevels,s=this.charWidths[i[r]];this.wrapIndent&&(e-=this.isRtlDir?-1*this.wrapOffset:this.wrapOffset);while(e>n+s/2){n+=s;if(r===i.length-1){s=0;break}s=this.charWidths[i[++r]]}return r>0&&i[r-1]%2!==0&&i[r]%2===0?(e<n&&r--,t=this.bidiMap.logicalFromVisual[r]):r>0&&i[r-1]%2===0&&i[r]%2!==0?t=1+(e>n?this.bidiMap.logicalFromVisual[r]:this.bidiMap.logicalFromVisual[r-1]):this.isRtlDir&&r===i.length-1&&s===0&&i[r-1]%2===0||!this.isRtlDir&&r===0&&i[r]%2!==0?t=1+this.bidiMap.logicalFromVisual[r]:(r>0&&i[r-1]%2!==0&&s!==0&&r--,t=this.bidiMap.logicalFromVisual[r]),t===0&&this.isRtlDir&&t++,t+this.wrapIndent}}).call(o.prototype),t.BidiHandler=o}),ace.define(\"ace/selection\",[\"require\",\"exports\",\"module\",\"ace/lib/oop\",\"ace/lib/lang\",\"ace/lib/event_emitter\",\"ace/range\"],function(e,t,n){\"use strict\";var r=e(\"./lib/oop\"),i=e(\"./lib/lang\"),s=e(\"./lib/event_emitter\").EventEmitter,o=e(\"./range\").Range,u=function(e){this.session=e,this.doc=e.getDocument(),this.clearSelection(),this.cursor=this.lead=this.doc.createAnchor(0,0),this.anchor=this.doc.createAnchor(0,0),this.$silent=!1;var t=this;this.cursor.on(\"change\",function(e){t.$cursorChanged=!0,t.$silent||t._emit(\"changeCursor\"),!t.$isEmpty&&!t.$silent&&t._emit(\"changeSelection\"),!t.$keepDesiredColumnOnChange&&e.old.column!=e.value.column&&(t.$desiredColumn=null)}),this.anchor.on(\"change\",function(){t.$anchorChanged=!0,!t.$isEmpty&&!t.$silent&&t._emit(\"changeSelection\")})};(function(){r.implement(this,s),this.isEmpty=function(){return this.$isEmpty||this.anchor.row==this.lead.row&&this.anchor.column==this.lead.column},this.isMultiLine=function(){return!this.$isEmpty&&this.anchor.row!=this.cursor.row},this.getCursor=function(){return this.lead.getPosition()},this.setSelectionAnchor=function(e,t){this.$isEmpty=!1,this.anchor.setPosition(e,t)},this.getAnchor=this.getSelectionAnchor=function(){return this.$isEmpty?this.getSelectionLead():this.anchor.getPosition()},this.getSelectionLead=function(){return this.lead.getPosition()},this.isBackwards=function(){var e=this.anchor,t=this.lead;return e.row>t.row||e.row==t.row&&e.column>t.column},this.getRange=function(){var e=this.anchor,t=this.lead;return this.$isEmpty?o.fromPoints(t,t):this.isBackwards()?o.fromPoints(t,e):o.fromPoints(e,t)},this.clearSelection=function(){this.$isEmpty||(this.$isEmpty=!0,this._emit(\"changeSelection\"))},this.selectAll=function(){this.$setSelection(0,0,Number.MAX_VALUE,Number.MAX_VALUE)},this.setRange=this.setSelectionRange=function(e,t){var n=t?e.end:e.start,r=t?e.start:e.end;this.$setSelection(n.row,n.column,r.row,r.column)},this.$setSelection=function(e,t,n,r){var i=this.$isEmpty,s=this.inMultiSelectMode;this.$silent=!0,this.$cursorChanged=this.$anchorChanged=!1,this.anchor.setPosition(e,t),this.cursor.setPosition(n,r),this.$isEmpty=!o.comparePoints(this.anchor,this.cursor),this.$silent=!1,this.$cursorChanged&&this._emit(\"changeCursor\"),(this.$cursorChanged||this.$anchorChanged||i!=this.$isEmpty||s)&&this._emit(\"changeSelection\")},this.$moveSelection=function(e){var t=this.lead;this.$isEmpty&&this.setSelectionAnchor(t.row,t.column),e.call(this)},this.selectTo=function(e,t){this.$moveSelection(function(){this.moveCursorTo(e,t)})},this.selectToPosition=function(e){this.$moveSelection(function(){this.moveCursorToPosition(e)})},this.moveTo=function(e,t){this.clearSelection(),this.moveCursorTo(e,t)},this.moveToPosition=function(e){this.clearSelection(),this.moveCursorToPosition(e)},this.selectUp=function(){this.$moveSelection(this.moveCursorUp)},this.selectDown=function(){this.$moveSelection(this.moveCursorDown)},this.selectRight=function(){this.$moveSelection(this.moveCursorRight)},this.selectLeft=function(){this.$moveSelection(this.moveCursorLeft)},this.selectLineStart=function(){this.$moveSelection(this.moveCursorLineStart)},this.selectLineEnd=function(){this.$moveSelection(this.moveCursorLineEnd)},this.selectFileEnd=function(){this.$moveSelection(this.moveCursorFileEnd)},this.selectFileStart=function(){this.$moveSelection(this.moveCursorFileStart)},this.selectWordRight=function(){this.$moveSelection(this.moveCursorWordRight)},this.selectWordLeft=function(){this.$moveSelection(this.moveCursorWordLeft)},this.getWordRange=function(e,t){if(typeof t==\"undefined\"){var n=e||this.lead;e=n.row,t=n.column}return this.session.getWordRange(e,t)},this.selectWord=function(){this.setSelectionRange(this.getWordRange())},this.selectAWord=function(){var e=this.getCursor(),t=this.session.getAWordRange(e.row,e.column);this.setSelectionRange(t)},this.getLineRange=function(e,t){var n=typeof e==\"number\"?e:this.lead.row,r,i=this.session.getFoldLine(n);return i?(n=i.start.row,r=i.end.row):r=n,t===!0?new o(n,0,r,this.session.getLine(r).length):new o(n,0,r+1,0)},this.selectLine=function(){this.setSelectionRange(this.getLineRange())},this.moveCursorUp=function(){this.moveCursorBy(-1,0)},this.moveCursorDown=function(){this.moveCursorBy(1,0)},this.wouldMoveIntoSoftTab=function(e,t,n){var r=e.column,i=e.column+t;return n<0&&(r=e.column-t,i=e.column),this.session.isTabStop(e)&&this.doc.getLine(e.row).slice(r,i).split(\" \").length-1==t},this.moveCursorLeft=function(){var e=this.lead.getPosition(),t;if(t=this.session.getFoldAt(e.row,e.column,-1))this.moveCursorTo(t.start.row,t.start.column);else if(e.column===0)e.row>0&&this.moveCursorTo(e.row-1,this.doc.getLine(e.row-1).length);else{var n=this.session.getTabSize();this.wouldMoveIntoSoftTab(e,n,-1)&&!this.session.getNavigateWithinSoftTabs()?this.moveCursorBy(0,-n):this.moveCursorBy(0,-1)}},this.moveCursorRight=function(){var e=this.lead.getPosition(),t;if(t=this.session.getFoldAt(e.row,e.column,1))this.moveCursorTo(t.end.row,t.end.column);else if(this.lead.column==this.doc.getLine(this.lead.row).length)this.lead.row<this.doc.getLength()-1&&this.moveCursorTo(this.lead.row+1,0);else{var n=this.session.getTabSize(),e=this.lead;this.wouldMoveIntoSoftTab(e,n,1)&&!this.session.getNavigateWithinSoftTabs()?this.moveCursorBy(0,n):this.moveCursorBy(0,1)}},this.moveCursorLineStart=function(){var e=this.lead.row,t=this.lead.column,n=this.session.documentToScreenRow(e,t),r=this.session.screenToDocumentPosition(n,0),i=this.session.getDisplayLine(e,null,r.row,r.column),s=i.match(/^\\s*/);s[0].length!=t&&!this.session.$useEmacsStyleLineStart&&(r.column+=s[0].length),this.moveCursorToPosition(r)},this.moveCursorLineEnd=function(){var e=this.lead,t=this.session.getDocumentLastRowColumnPosition(e.row,e.column);if(this.lead.column==t.column){var n=this.session.getLine(t.row);if(t.column==n.length){var r=n.search(/\\s+$/);r>0&&(t.column=r)}}this.moveCursorTo(t.row,t.column)},this.moveCursorFileEnd=function(){var e=this.doc.getLength()-1,t=this.doc.getLine(e).length;this.moveCursorTo(e,t)},this.moveCursorFileStart=function(){this.moveCursorTo(0,0)},this.moveCursorLongWordRight=function(){var e=this.lead.row,t=this.lead.column,n=this.doc.getLine(e),r=n.substring(t);this.session.nonTokenRe.lastIndex=0,this.session.tokenRe.lastIndex=0;var i=this.session.getFoldAt(e,t,1);if(i){this.moveCursorTo(i.end.row,i.end.column);return}this.session.nonTokenRe.exec(r)&&(t+=this.session.nonTokenRe.lastIndex,this.session.nonTokenRe.lastIndex=0,r=n.substring(t));if(t>=n.length){this.moveCursorTo(e,n.length),this.moveCursorRight(),e<this.doc.getLength()-1&&this.moveCursorWordRight();return}this.session.tokenRe.exec(r)&&(t+=this.session.tokenRe.lastIndex,this.session.tokenRe.lastIndex=0),this.moveCursorTo(e,t)},this.moveCursorLongWordLeft=function(){var e=this.lead.row,t=this.lead.column,n;if(n=this.session.getFoldAt(e,t,-1)){this.moveCursorTo(n.start.row,n.start.column);return}var r=this.session.getFoldStringAt(e,t,-1);r==null&&(r=this.doc.getLine(e).substring(0,t));var s=i.stringReverse(r);this.session.nonTokenRe.lastIndex=0,this.session.tokenRe.lastIndex=0,this.session.nonTokenRe.exec(s)&&(t-=this.session.nonTokenRe.lastIndex,s=s.slice(this.session.nonTokenRe.lastIndex),this.session.nonTokenRe.lastIndex=0);if(t<=0){this.moveCursorTo(e,0),this.moveCursorLeft(),e>0&&this.moveCursorWordLeft();return}this.session.tokenRe.exec(s)&&(t-=this.session.tokenRe.lastIndex,this.session.tokenRe.lastIndex=0),this.moveCursorTo(e,t)},this.$shortWordEndIndex=function(e){var t=0,n,r=/\\s/,i=this.session.tokenRe;i.lastIndex=0;if(this.session.tokenRe.exec(e))t=this.session.tokenRe.lastIndex;else{while((n=e[t])&&r.test(n))t++;if(t<1){i.lastIndex=0;while((n=e[t])&&!i.test(n)){i.lastIndex=0,t++;if(r.test(n)){if(t>2){t--;break}while((n=e[t])&&r.test(n))t++;if(t>2)break}}}}return i.lastIndex=0,t},this.moveCursorShortWordRight=function(){var e=this.lead.row,t=this.lead.column,n=this.doc.getLine(e),r=n.substring(t),i=this.session.getFoldAt(e,t,1);if(i)return this.moveCursorTo(i.end.row,i.end.column);if(t==n.length){var s=this.doc.getLength();do e++,r=this.doc.getLine(e);while(e<s&&/^\\s*$/.test(r));/^\\s+/.test(r)||(r=\"\"),t=0}var o=this.$shortWordEndIndex(r);this.moveCursorTo(e,t+o)},this.moveCursorShortWordLeft=function(){var e=this.lead.row,t=this.lead.column,n;if(n=this.session.getFoldAt(e,t,-1))return this.moveCursorTo(n.start.row,n.start.column);var r=this.session.getLine(e).substring(0,t);if(t===0){do e--,r=this.doc.getLine(e);while(e>0&&/^\\s*$/.test(r));t=r.length,/\\s+$/.test(r)||(r=\"\")}var s=i.stringReverse(r),o=this.$shortWordEndIndex(s);return this.moveCursorTo(e,t-o)},this.moveCursorWordRight=function(){this.session.$selectLongWords?this.moveCursorLongWordRight():this.moveCursorShortWordRight()},this.moveCursorWordLeft=function(){this.session.$selectLongWords?this.moveCursorLongWordLeft():this.moveCursorShortWordLeft()},this.moveCursorBy=function(e,t){var n=this.session.documentToScreenPosition(this.lead.row,this.lead.column),r;t===0&&(e!==0&&(this.session.$bidiHandler.isBidiRow(n.row,this.lead.row)?(r=this.session.$bidiHandler.getPosLeft(n.column),n.column=Math.round(r/this.session.$bidiHandler.charWidths[0])):r=n.column*this.session.$bidiHandler.charWidths[0]),this.$desiredColumn?n.column=this.$desiredColumn:this.$desiredColumn=n.column);var i=this.session.screenToDocumentPosition(n.row+e,n.column,r);e!==0&&t===0&&i.row===this.lead.row&&i.column===this.lead.column&&this.session.lineWidgets&&this.session.lineWidgets[i.row]&&(i.row>0||e>0)&&i.row++,this.moveCursorTo(i.row,i.column+t,t===0)},this.moveCursorToPosition=function(e){this.moveCursorTo(e.row,e.column)},this.moveCursorTo=function(e,t,n){var r=this.session.getFoldAt(e,t,1);r&&(e=r.start.row,t=r.start.column),this.$keepDesiredColumnOnChange=!0;var i=this.session.getLine(e);/[\\uDC00-\\uDFFF]/.test(i.charAt(t))&&i.charAt(t-1)&&(this.lead.row==e&&this.lead.column==t+1?t-=1:t+=1),this.lead.setPosition(e,t),this.$keepDesiredColumnOnChange=!1,n||(this.$desiredColumn=null)},this.moveCursorToScreen=function(e,t,n){var r=this.session.screenToDocumentPosition(e,t);this.moveCursorTo(r.row,r.column,n)},this.detach=function(){this.lead.detach(),this.anchor.detach(),this.session=this.doc=null},this.fromOrientedRange=function(e){this.setSelectionRange(e,e.cursor==e.start),this.$desiredColumn=e.desiredColumn||this.$desiredColumn},this.toOrientedRange=function(e){var t=this.getRange();return e?(e.start.column=t.start.column,e.start.row=t.start.row,e.end.column=t.end.column,e.end.row=t.end.row):e=t,e.cursor=this.isBackwards()?e.start:e.end,e.desiredColumn=this.$desiredColumn,e},this.getRangeOfMovements=function(e){var t=this.getCursor();try{e(this);var n=this.getCursor();return o.fromPoints(t,n)}catch(r){return o.fromPoints(t,t)}finally{this.moveCursorToPosition(t)}},this.toJSON=function(){if(this.rangeCount)var e=this.ranges.map(function(e){var t=e.clone();return t.isBackwards=e.cursor==e.start,t});else{var e=this.getRange();e.isBackwards=this.isBackwards()}return e},this.fromJSON=function(e){if(e.start==undefined){if(this.rangeList&&e.length>1){this.toSingleRange(e[0]);for(var t=e.length;t--;){var n=o.fromPoints(e[t].start,e[t].end);e[t].isBackwards&&(n.cursor=n.start),this.addRange(n,!0)}return}e=e[0]}this.rangeList&&this.toSingleRange(e),this.setSelectionRange(e,e.isBackwards)},this.isEqual=function(e){if((e.length||this.rangeCount)&&e.length!=this.rangeCount)return!1;if(!e.length||!this.ranges)return this.getRange().isEqual(e);for(var t=this.ranges.length;t--;)if(!this.ranges[t].isEqual(e[t]))return!1;return!0}}).call(u.prototype),t.Selection=u}),ace.define(\"ace/tokenizer\",[\"require\",\"exports\",\"module\",\"ace/config\"],function(e,t,n){\"use strict\";var r=e(\"./config\"),i=2e3,s=function(e){this.states=e,this.regExps={},this.matchMappings={};for(var t in this.states){var n=this.states[t],r=[],i=0,s=this.matchMappings[t]={defaultToken:\"text\"},o=\"g\",u=[];for(var a=0;a<n.length;a++){var f=n[a];f.defaultToken&&(s.defaultToken=f.defaultToken),f.caseInsensitive&&(o=\"gi\");if(f.regex==null)continue;f.regex instanceof RegExp&&(f.regex=f.regex.toString().slice(1,-1));var l=f.regex,c=(new RegExp(\"(?:(\"+l+\")|(.))\")).exec(\"a\").length-2;Array.isArray(f.token)?f.token.length==1||c==1?f.token=f.token[0]:c-1!=f.token.length?(this.reportError(\"number of classes and regexp groups doesn't match\",{rule:f,groupCount:c-1}),f.token=f.token[0]):(f.tokenArray=f.token,f.token=null,f.onMatch=this.$arrayTokens):typeof f.token==\"function\"&&!f.onMatch&&(c>1?f.onMatch=this.$applyToken:f.onMatch=f.token),c>1&&(/\\\\\\d/.test(f.regex)?l=f.regex.replace(/\\\\([0-9]+)/g,function(e,t){return\"\\\\\"+(parseInt(t,10)+i+1)}):(c=1,l=this.removeCapturingGroups(f.regex)),!f.splitRegex&&typeof f.token!=\"string\"&&u.push(f)),s[i]=a,i+=c,r.push(l),f.onMatch||(f.onMatch=null)}r.length||(s[0]=0,r.push(\"$\")),u.forEach(function(e){e.splitRegex=this.createSplitterRegexp(e.regex,o)},this),this.regExps[t]=new RegExp(\"(\"+r.join(\")|(\")+\")|($)\",o)}};(function(){this.$setMaxTokenCount=function(e){i=e|0},this.$applyToken=function(e){var t=this.splitRegex.exec(e).slice(1),n=this.token.apply(this,t);if(typeof n==\"string\")return[{type:n,value:e}];var r=[];for(var i=0,s=n.length;i<s;i++)t[i]&&(r[r.length]={type:n[i],value:t[i]});return r},this.$arrayTokens=function(e){if(!e)return[];var t=this.splitRegex.exec(e);if(!t)return\"text\";var n=[],r=this.tokenArray;for(var i=0,s=r.length;i<s;i++)t[i+1]&&(n[n.length]={type:r[i],value:t[i+1]});return n},this.removeCapturingGroups=function(e){var t=e.replace(/\\\\.|\\[(?:\\\\.|[^\\\\\\]])*|\\(\\?[:=!]|(\\()/g,function(e,t){return t?\"(?:\":e});return t},this.createSplitterRegexp=function(e,t){if(e.indexOf(\"(?=\")!=-1){var n=0,r=!1,i={};e.replace(/(\\\\.)|(\\((?:\\?[=!])?)|(\\))|([\\[\\]])/g,function(e,t,s,o,u,a){return r?r=u!=\"]\":u?r=!0:o?(n==i.stack&&(i.end=a+1,i.stack=-1),n--):s&&(n++,s.length!=1&&(i.stack=n,i.start=a)),e}),i.end!=null&&/^\\)*$/.test(e.substr(i.end))&&(e=e.substring(0,i.start)+e.substr(i.end))}return e.charAt(0)!=\"^\"&&(e=\"^\"+e),e.charAt(e.length-1)!=\"$\"&&(e+=\"$\"),new RegExp(e,(t||\"\").replace(\"g\",\"\"))},this.getLineTokens=function(e,t){if(t&&typeof t!=\"string\"){var n=t.slice(0);t=n[0],t===\"#tmp\"&&(n.shift(),t=n.shift())}else var n=[];var r=t||\"start\",s=this.states[r];s||(r=\"start\",s=this.states[r]);var o=this.matchMappings[r],u=this.regExps[r];u.lastIndex=0;var a,f=[],l=0,c=0,h={type:null,value:\"\"};while(a=u.exec(e)){var p=o.defaultToken,d=null,v=a[0],m=u.lastIndex;if(m-v.length>l){var g=e.substring(l,m-v.length);h.type==p?h.value+=g:(h.type&&f.push(h),h={type:p,value:g})}for(var y=0;y<a.length-2;y++){if(a[y+1]===undefined)continue;d=s[o[y]],d.onMatch?p=d.onMatch(v,r,n,e):p=d.token,d.next&&(typeof d.next==\"string\"?r=d.next:r=d.next(r,n),s=this.states[r],s||(this.reportError(\"state doesn't exist\",r),r=\"start\",s=this.states[r]),o=this.matchMappings[r],l=m,u=this.regExps[r],u.lastIndex=m),d.consumeLineEnd&&(l=m);break}if(v)if(typeof p==\"string\")!!d&&d.merge===!1||h.type!==p?(h.type&&f.push(h),h={type:p,value:v}):h.value+=v;else if(p){h.type&&f.push(h),h={type:null,value:\"\"};for(var y=0;y<p.length;y++)f.push(p[y])}if(l==e.length)break;l=m;if(c++>i){c>2*e.length&&this.reportError(\"infinite loop with in ace tokenizer\",{startState:t,line:e});while(l<e.length)h.type&&f.push(h),h={value:e.substring(l,l+=2e3),type:\"overflow\"};r=\"start\",n=[];break}}return h.type&&f.push(h),n.length>1&&n[0]!==r&&n.unshift(\"#tmp\",r),{tokens:f,state:n.length?n:r}},this.reportError=r.reportError}).call(s.prototype),t.Tokenizer=s}),ace.define(\"ace/mode/text_highlight_rules\",[\"require\",\"exports\",\"module\",\"ace/lib/lang\"],function(e,t,n){\"use strict\";var r=e(\"../lib/lang\"),i=function(){this.$rules={start:[{token:\"empty_line\",regex:\"^$\"},{defaultToken:\"text\"}]}};(function(){this.addRules=function(e,t){if(!t){for(var n in e)this.$rules[n]=e[n];return}for(var n in e){var r=e[n];for(var i=0;i<r.length;i++){var s=r[i];if(s.next||s.onMatch)typeof s.next==\"string\"&&s.next.indexOf(t)!==0&&(s.next=t+s.next),s.nextState&&s.nextState.indexOf(t)!==0&&(s.nextState=t+s.nextState)}this.$rules[t+n]=r}},this.getRules=function(){return this.$rules},this.embedRules=function(e,t,n,i,s){var o=typeof e==\"function\"?(new e).getRules():e;if(i)for(var u=0;u<i.length;u++)i[u]=t+i[u];else{i=[];for(var a in o)i.push(t+a)}this.addRules(o,t);if(n){var f=Array.prototype[s?\"push\":\"unshift\"];for(var u=0;u<i.length;u++)f.apply(this.$rules[i[u]],r.deepCopy(n))}this.$embeds||(this.$embeds=[]),this.$embeds.push(t)},this.getEmbeds=function(){return this.$embeds};var e=function(e,t){return(e!=\"start\"||t.length)&&t.unshift(this.nextState,e),this.nextState},t=function(e,t){return t.shift(),t.shift()||\"start\"};this.normalizeRules=function(){function i(s){var o=r[s];o.processed=!0;for(var u=0;u<o.length;u++){var a=o[u],f=null;Array.isArray(a)&&(f=a,a={}),!a.regex&&a.start&&(a.regex=a.start,a.next||(a.next=[]),a.next.push({defaultToken:a.token},{token:a.token+\".end\",regex:a.end||a.start,next:\"pop\"}),a.token=a.token+\".start\",a.push=!0);var l=a.next||a.push;if(l&&Array.isArray(l)){var c=a.stateName;c||(c=a.token,typeof c!=\"string\"&&(c=c[0]||\"\"),r[c]&&(c+=n++)),r[c]=l,a.next=c,i(c)}else l==\"pop\"&&(a.next=t);a.push&&(a.nextState=a.next||a.push,a.next=e,delete a.push);if(a.rules)for(var h in a.rules)r[h]?r[h].push&&r[h].push.apply(r[h],a.rules[h]):r[h]=a.rules[h];var p=typeof a==\"string\"?a:a.include;p&&(Array.isArray(p)?f=p.map(function(e){return r[e]}):f=r[p]);if(f){var d=[u,1].concat(f);a.noEscape&&(d=d.filter(function(e){return!e.next})),o.splice.apply(o,d),u--}a.keywordMap&&(a.token=this.createKeywordMapper(a.keywordMap,a.defaultToken||\"text\",a.caseInsensitive),delete a.defaultToken)}}var n=0,r=this.$rules;Object.keys(r).forEach(i,this)},this.createKeywordMapper=function(e,t,n,r){var i=Object.create(null);return Object.keys(e).forEach(function(t){var s=e[t];n&&(s=s.toLowerCase());var o=s.split(r||\"|\");for(var u=o.length;u--;)i[o[u]]=t}),Object.getPrototypeOf(i)&&(i.__proto__=null),this.$keywordList=Object.keys(i),e=null,n?function(e){return i[e.toLowerCase()]||t}:function(e){return i[e]||t}},this.getKeywords=function(){return this.$keywords}}).call(i.prototype),t.TextHighlightRules=i}),ace.define(\"ace/mode/behaviour\",[\"require\",\"exports\",\"module\"],function(e,t,n){\"use strict\";var r=function(){this.$behaviours={}};(function(){this.add=function(e,t,n){switch(undefined){case this.$behaviours:this.$behaviours={};case this.$behaviours[e]:this.$behaviours[e]={}}this.$behaviours[e][t]=n},this.addBehaviours=function(e){for(var t in e)for(var n in e[t])this.add(t,n,e[t][n])},this.remove=function(e){this.$behaviours&&this.$behaviours[e]&&delete this.$behaviours[e]},this.inherit=function(e,t){if(typeof e==\"function\")var n=(new e).getBehaviours(t);else var n=e.getBehaviours(t);this.addBehaviours(n)},this.getBehaviours=function(e){if(!e)return this.$behaviours;var t={};for(var n=0;n<e.length;n++)this.$behaviours[e[n]]&&(t[e[n]]=this.$behaviours[e[n]]);return t}}).call(r.prototype),t.Behaviour=r}),ace.define(\"ace/token_iterator\",[\"require\",\"exports\",\"module\",\"ace/range\"],function(e,t,n){\"use strict\";var r=e(\"./range\").Range,i=function(e,t,n){this.$session=e,this.$row=t,this.$rowTokens=e.getTokens(t);var r=e.getTokenAt(t,n);this.$tokenIndex=r?r.index:-1};(function(){this.stepBackward=function(){this.$tokenIndex-=1;while(this.$tokenIndex<0){this.$row-=1;if(this.$row<0)return this.$row=0,null;this.$rowTokens=this.$session.getTokens(this.$row),this.$tokenIndex=this.$rowTokens.length-1}return this.$rowTokens[this.$tokenIndex]},this.stepForward=function(){this.$tokenIndex+=1;var e;while(this.$tokenIndex>=this.$rowTokens.length){this.$row+=1,e||(e=this.$session.getLength());if(this.$row>=e)return this.$row=e-1,null;this.$rowTokens=this.$session.getTokens(this.$row),this.$tokenIndex=0}return this.$rowTokens[this.$tokenIndex]},this.getCurrentToken=function(){return this.$rowTokens[this.$tokenIndex]},this.getCurrentTokenRow=function(){return this.$row},this.getCurrentTokenColumn=function(){var e=this.$rowTokens,t=this.$tokenIndex,n=e[t].start;if(n!==undefined)return n;n=0;while(t>0)t-=1,n+=e[t].value.length;return n},this.getCurrentTokenPosition=function(){return{row:this.$row,column:this.getCurrentTokenColumn()}},this.getCurrentTokenRange=function(){var e=this.$rowTokens[this.$tokenIndex],t=this.getCurrentTokenColumn();return new r(this.$row,t,this.$row,t+e.value.length)}}).call(i.prototype),t.TokenIterator=i}),ace.define(\"ace/mode/behaviour/cstyle\",[\"require\",\"exports\",\"module\",\"ace/lib/oop\",\"ace/mode/behaviour\",\"ace/token_iterator\",\"ace/lib/lang\"],function(e,t,n){\"use strict\";var r=e(\"../../lib/oop\"),i=e(\"../behaviour\").Behaviour,s=e(\"../../token_iterator\").TokenIterator,o=e(\"../../lib/lang\"),u=[\"text\",\"paren.rparen\",\"punctuation.operator\"],a=[\"text\",\"paren.rparen\",\"punctuation.operator\",\"comment\"],f,l={},c={'\"':'\"',\"'\":\"'\"},h=function(e){var t=-1;e.multiSelect&&(t=e.selection.index,l.rangeCount!=e.multiSelect.rangeCount&&(l={rangeCount:e.multiSelect.rangeCount}));if(l[t])return f=l[t];f=l[t]={autoInsertedBrackets:0,autoInsertedRow:-1,autoInsertedLineEnd:\"\",maybeInsertedBrackets:0,maybeInsertedRow:-1,maybeInsertedLineStart:\"\",maybeInsertedLineEnd:\"\"}},p=function(e,t,n,r){var i=e.end.row-e.start.row;return{text:n+t+r,selection:[0,e.start.column+1,i,e.end.column+(i?0:1)]}},d=function(e){this.add(\"braces\",\"insertion\",function(t,n,r,i,s){var u=r.getCursorPosition(),a=i.doc.getLine(u.row);if(s==\"{\"){h(r);var l=r.getSelectionRange(),c=i.doc.getTextRange(l);if(c!==\"\"&&c!==\"{\"&&r.getWrapBehavioursEnabled())return p(l,c,\"{\",\"}\");if(d.isSaneInsertion(r,i))return/[\\]\\}\\)]/.test(a[u.column])||r.inMultiSelectMode||e&&e.braces?(d.recordAutoInsert(r,i,\"}\"),{text:\"{}\",selection:[1,1]}):(d.recordMaybeInsert(r,i,\"{\"),{text:\"{\",selection:[1,1]})}else if(s==\"}\"){h(r);var v=a.substring(u.column,u.column+1);if(v==\"}\"){var m=i.$findOpeningBracket(\"}\",{column:u.column+1,row:u.row});if(m!==null&&d.isAutoInsertedClosing(u,a,s))return d.popAutoInsertedClosing(),{text:\"\",selection:[1,1]}}}else{if(s==\"\\n\"||s==\"\\r\\n\"){h(r);var g=\"\";d.isMaybeInsertedClosing(u,a)&&(g=o.stringRepeat(\"}\",f.maybeInsertedBrackets),d.clearMaybeInsertedClosing());var v=a.substring(u.column,u.column+1);if(v===\"}\"){var y=i.findMatchingBracket({row:u.row,column:u.column+1},\"}\");if(!y)return null;var b=this.$getIndent(i.getLine(y.row))}else{if(!g){d.clearMaybeInsertedClosing();return}var b=this.$getIndent(a)}var w=b+i.getTabString();return{text:\"\\n\"+w+\"\\n\"+b+g,selection:[1,w.length,1,w.length]}}d.clearMaybeInsertedClosing()}}),this.add(\"braces\",\"deletion\",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&s==\"{\"){h(n);var o=r.doc.getLine(i.start.row),u=o.substring(i.end.column,i.end.column+1);if(u==\"}\")return i.end.column++,i;f.maybeInsertedBrackets--}}),this.add(\"parens\",\"insertion\",function(e,t,n,r,i){if(i==\"(\"){h(n);var s=n.getSelectionRange(),o=r.doc.getTextRange(s);if(o!==\"\"&&n.getWrapBehavioursEnabled())return p(s,o,\"(\",\")\");if(d.isSaneInsertion(n,r))return d.recordAutoInsert(n,r,\")\"),{text:\"()\",selection:[1,1]}}else if(i==\")\"){h(n);var u=n.getCursorPosition(),a=r.doc.getLine(u.row),f=a.substring(u.column,u.column+1);if(f==\")\"){var l=r.$findOpeningBracket(\")\",{column:u.column+1,row:u.row});if(l!==null&&d.isAutoInsertedClosing(u,a,i))return d.popAutoInsertedClosing(),{text:\"\",selection:[1,1]}}}}),this.add(\"parens\",\"deletion\",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&s==\"(\"){h(n);var o=r.doc.getLine(i.start.row),u=o.substring(i.start.column+1,i.start.column+2);if(u==\")\")return i.end.column++,i}}),this.add(\"brackets\",\"insertion\",function(e,t,n,r,i){if(i==\"[\"){h(n);var s=n.getSelectionRange(),o=r.doc.getTextRange(s);if(o!==\"\"&&n.getWrapBehavioursEnabled())return p(s,o,\"[\",\"]\");if(d.isSaneInsertion(n,r))return d.recordAutoInsert(n,r,\"]\"),{text:\"[]\",selection:[1,1]}}else if(i==\"]\"){h(n);var u=n.getCursorPosition(),a=r.doc.getLine(u.row),f=a.substring(u.column,u.column+1);if(f==\"]\"){var l=r.$findOpeningBracket(\"]\",{column:u.column+1,row:u.row});if(l!==null&&d.isAutoInsertedClosing(u,a,i))return d.popAutoInsertedClosing(),{text:\"\",selection:[1,1]}}}}),this.add(\"brackets\",\"deletion\",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&s==\"[\"){h(n);var o=r.doc.getLine(i.start.row),u=o.substring(i.start.column+1,i.start.column+2);if(u==\"]\")return i.end.column++,i}}),this.add(\"string_dquotes\",\"insertion\",function(e,t,n,r,i){var s=r.$mode.$quotes||c;if(i.length==1&&s[i]){if(this.lineCommentStart&&this.lineCommentStart.indexOf(i)!=-1)return;h(n);var o=i,u=n.getSelectionRange(),a=r.doc.getTextRange(u);if(a!==\"\"&&(a.length!=1||!s[a])&&n.getWrapBehavioursEnabled())return p(u,a,o,o);if(!a){var f=n.getCursorPosition(),l=r.doc.getLine(f.row),d=l.substring(f.column-1,f.column),v=l.substring(f.column,f.column+1),m=r.getTokenAt(f.row,f.column),g=r.getTokenAt(f.row,f.column+1);if(d==\"\\\\\"&&m&&/escape/.test(m.type))return null;var y=m&&/string|escape/.test(m.type),b=!g||/string|escape/.test(g.type),w;if(v==o)w=y!==b,w&&/string\\.end/.test(g.type)&&(w=!1);else{if(y&&!b)return null;if(y&&b)return null;var E=r.$mode.tokenRe;E.lastIndex=0;var S=E.test(d);E.lastIndex=0;var x=E.test(d);if(S||x)return null;if(v&&!/[\\s;,.})\\]\\\\]/.test(v))return null;w=!0}return{text:w?o+o:\"\",selection:[1,1]}}}}),this.add(\"string_dquotes\",\"deletion\",function(e,t,n,r,i){var s=r.$mode.$quotes||c,o=r.doc.getTextRange(i);if(!i.isMultiLine()&&s.hasOwnProperty(o)){h(n);var u=r.doc.getLine(i.start.row),a=u.substring(i.start.column+1,i.start.column+2);if(a==o)return i.end.column++,i}})};d.isSaneInsertion=function(e,t){var n=e.getCursorPosition(),r=new s(t,n.row,n.column);if(!this.$matchTokenType(r.getCurrentToken()||\"text\",u)){var i=new s(t,n.row,n.column+1);if(!this.$matchTokenType(i.getCurrentToken()||\"text\",u))return!1}return r.stepForward(),r.getCurrentTokenRow()!==n.row||this.$matchTokenType(r.getCurrentToken()||\"text\",a)},d.$matchTokenType=function(e,t){return t.indexOf(e.type||e)>-1},d.recordAutoInsert=function(e,t,n){var r=e.getCursorPosition(),i=t.doc.getLine(r.row);this.isAutoInsertedClosing(r,i,f.autoInsertedLineEnd[0])||(f.autoInsertedBrackets=0),f.autoInsertedRow=r.row,f.autoInsertedLineEnd=n+i.substr(r.column),f.autoInsertedBrackets++},d.recordMaybeInsert=function(e,t,n){var r=e.getCursorPosition(),i=t.doc.getLine(r.row);this.isMaybeInsertedClosing(r,i)||(f.maybeInsertedBrackets=0),f.maybeInsertedRow=r.row,f.maybeInsertedLineStart=i.substr(0,r.column)+n,f.maybeInsertedLineEnd=i.substr(r.column),f.maybeInsertedBrackets++},d.isAutoInsertedClosing=function(e,t,n){return f.autoInsertedBrackets>0&&e.row===f.autoInsertedRow&&n===f.autoInsertedLineEnd[0]&&t.substr(e.column)===f.autoInsertedLineEnd},d.isMaybeInsertedClosing=function(e,t){return f.maybeInsertedBrackets>0&&e.row===f.maybeInsertedRow&&t.substr(e.column)===f.maybeInsertedLineEnd&&t.substr(0,e.column)==f.maybeInsertedLineStart},d.popAutoInsertedClosing=function(){f.autoInsertedLineEnd=f.autoInsertedLineEnd.substr(1),f.autoInsertedBrackets--},d.clearMaybeInsertedClosing=function(){f&&(f.maybeInsertedBrackets=0,f.maybeInsertedRow=-1)},r.inherits(d,i),t.CstyleBehaviour=d}),ace.define(\"ace/unicode\",[\"require\",\"exports\",\"module\"],function(e,t,n){\"use strict\";var r=[48,9,8,25,5,0,2,25,48,0,11,0,5,0,6,22,2,30,2,457,5,11,15,4,8,0,2,0,18,116,2,1,3,3,9,0,2,2,2,0,2,19,2,82,2,138,2,4,3,155,12,37,3,0,8,38,10,44,2,0,2,1,2,1,2,0,9,26,6,2,30,10,7,61,2,9,5,101,2,7,3,9,2,18,3,0,17,58,3,100,15,53,5,0,6,45,211,57,3,18,2,5,3,11,3,9,2,1,7,6,2,2,2,7,3,1,3,21,2,6,2,0,4,3,3,8,3,1,3,3,9,0,5,1,2,4,3,11,16,2,2,5,5,1,3,21,2,6,2,1,2,1,2,1,3,0,2,4,5,1,3,2,4,0,8,3,2,0,8,15,12,2,2,8,2,2,2,21,2,6,2,1,2,4,3,9,2,2,2,2,3,0,16,3,3,9,18,2,2,7,3,1,3,21,2,6,2,1,2,4,3,8,3,1,3,2,9,1,5,1,2,4,3,9,2,0,17,1,2,5,4,2,2,3,4,1,2,0,2,1,4,1,4,2,4,11,5,4,4,2,2,3,3,0,7,0,15,9,18,2,2,7,2,2,2,22,2,9,2,4,4,7,2,2,2,3,8,1,2,1,7,3,3,9,19,1,2,7,2,2,2,22,2,9,2,4,3,8,2,2,2,3,8,1,8,0,2,3,3,9,19,1,2,7,2,2,2,22,2,15,4,7,2,2,2,3,10,0,9,3,3,9,11,5,3,1,2,17,4,23,2,8,2,0,3,6,4,0,5,5,2,0,2,7,19,1,14,57,6,14,2,9,40,1,2,0,3,1,2,0,3,0,7,3,2,6,2,2,2,0,2,0,3,1,2,12,2,2,3,4,2,0,2,5,3,9,3,1,35,0,24,1,7,9,12,0,2,0,2,0,5,9,2,35,5,19,2,5,5,7,2,35,10,0,58,73,7,77,3,37,11,42,2,0,4,328,2,3,3,6,2,0,2,3,3,40,2,3,3,32,2,3,3,6,2,0,2,3,3,14,2,56,2,3,3,66,5,0,33,15,17,84,13,619,3,16,2,25,6,74,22,12,2,6,12,20,12,19,13,12,2,2,2,1,13,51,3,29,4,0,5,1,3,9,34,2,3,9,7,87,9,42,6,69,11,28,4,11,5,11,11,39,3,4,12,43,5,25,7,10,38,27,5,62,2,28,3,10,7,9,14,0,89,75,5,9,18,8,13,42,4,11,71,55,9,9,4,48,83,2,2,30,14,230,23,280,3,5,3,37,3,5,3,7,2,0,2,0,2,0,2,30,3,52,2,6,2,0,4,2,2,6,4,3,3,5,5,12,6,2,2,6,67,1,20,0,29,0,14,0,17,4,60,12,5,0,4,11,18,0,5,0,3,9,2,0,4,4,7,0,2,0,2,0,2,3,2,10,3,3,6,4,5,0,53,1,2684,46,2,46,2,132,7,6,15,37,11,53,10,0,17,22,10,6,2,6,2,6,2,6,2,6,2,6,2,6,2,6,2,31,48,0,470,1,36,5,2,4,6,1,5,85,3,1,3,2,2,89,2,3,6,40,4,93,18,23,57,15,513,6581,75,20939,53,1164,68,45,3,268,4,27,21,31,3,13,13,1,2,24,9,69,11,1,38,8,3,102,3,1,111,44,25,51,13,68,12,9,7,23,4,0,5,45,3,35,13,28,4,64,15,10,39,54,10,13,3,9,7,22,4,1,5,66,25,2,227,42,2,1,3,9,7,11171,13,22,5,48,8453,301,3,61,3,105,39,6,13,4,6,11,2,12,2,4,2,0,2,1,2,1,2,107,34,362,19,63,3,53,41,11,5,15,17,6,13,1,25,2,33,4,2,134,20,9,8,25,5,0,2,25,12,88,4,5,3,5,3,5,3,2],i=0,s=[];for(var o=0;o<r.length;o+=2)s.push(i+=r[o]),r[o+1]&&s.push(45,i+=r[o+1]);t.wordChars=String.fromCharCode.apply(null,s)}),ace.define(\"ace/mode/text\",[\"require\",\"exports\",\"module\",\"ace/config\",\"ace/tokenizer\",\"ace/mode/text_highlight_rules\",\"ace/mode/behaviour/cstyle\",\"ace/unicode\",\"ace/lib/lang\",\"ace/token_iterator\",\"ace/range\"],function(e,t,n){\"use strict\";var r=e(\"../config\"),i=e(\"../tokenizer\").Tokenizer,s=e(\"./text_highlight_rules\").TextHighlightRules,o=e(\"./behaviour/cstyle\").CstyleBehaviour,u=e(\"../unicode\"),a=e(\"../lib/lang\"),f=e(\"../token_iterator\").TokenIterator,l=e(\"../range\").Range,c=function(){this.HighlightRules=s};(function(){this.$defaultBehaviour=new o,this.tokenRe=new RegExp(\"^[\"+u.wordChars+\"\\\\$_]+\",\"g\"),this.nonTokenRe=new RegExp(\"^(?:[^\"+u.wordChars+\"\\\\$_]|\\\\s])+\",\"g\"),this.getTokenizer=function(){return this.$tokenizer||(this.$highlightRules=this.$highlightRules||new this.HighlightRules(this.$highlightRuleConfig),this.$tokenizer=new i(this.$highlightRules.getRules())),this.$tokenizer},this.lineCommentStart=\"\",this.blockComment=\"\",this.toggleCommentLines=function(e,t,n,r){function w(e){for(var t=n;t<=r;t++)e(i.getLine(t),t)}var i=t.doc,s=!0,o=!0,u=Infinity,f=t.getTabSize(),l=!1;if(!this.lineCommentStart){if(!this.blockComment)return!1;var c=this.blockComment.start,h=this.blockComment.end,p=new RegExp(\"^(\\\\s*)(?:\"+a.escapeRegExp(c)+\")\"),d=new RegExp(\"(?:\"+a.escapeRegExp(h)+\")\\\\s*$\"),v=function(e,t){if(g(e,t))return;if(!s||/\\S/.test(e))i.insertInLine({row:t,column:e.length},h),i.insertInLine({row:t,column:u},c)},m=function(e,t){var n;(n=e.match(d))&&i.removeInLine(t,e.length-n[0].length,e.length),(n=e.match(p))&&i.removeInLine(t,n[1].length,n[0].length)},g=function(e,n){if(p.test(e))return!0;var r=t.getTokens(n);for(var i=0;i<r.length;i++)if(r[i].type===\"comment\")return!0}}else{if(Array.isArray(this.lineCommentStart))var p=this.lineCommentStart.map(a.escapeRegExp).join(\"|\"),c=this.lineCommentStart[0];else var p=a.escapeRegExp(this.lineCommentStart),c=this.lineCommentStart;p=new RegExp(\"^(\\\\s*)(?:\"+p+\") ?\"),l=t.getUseSoftTabs();var m=function(e,t){var n=e.match(p);if(!n)return;var r=n[1].length,s=n[0].length;!b(e,r,s)&&n[0][s-1]==\" \"&&s--,i.removeInLine(t,r,s)},y=c+\" \",v=function(e,t){if(!s||/\\S/.test(e))b(e,u,u)?i.insertInLine({row:t,column:u},y):i.insertInLine({row:t,column:u},c)},g=function(e,t){return p.test(e)},b=function(e,t,n){var r=0;while(t--&&e.charAt(t)==\" \")r++;if(r%f!=0)return!1;var r=0;while(e.charAt(n++)==\" \")r++;return f>2?r%f!=f-1:r%f==0}}var E=Infinity;w(function(e,t){var n=e.search(/\\S/);n!==-1?(n<u&&(u=n),o&&!g(e,t)&&(o=!1)):E>e.length&&(E=e.length)}),u==Infinity&&(u=E,s=!1,o=!1),l&&u%f!=0&&(u=Math.floor(u/f)*f),w(o?m:v)},this.toggleBlockComment=function(e,t,n,r){var i=this.blockComment;if(!i)return;!i.start&&i[0]&&(i=i[0]);var s=new f(t,r.row,r.column),o=s.getCurrentToken(),u=t.selection,a=t.selection.toOrientedRange(),c,h;if(o&&/comment/.test(o.type)){var p,d;while(o&&/comment/.test(o.type)){var v=o.value.indexOf(i.start);if(v!=-1){var m=s.getCurrentTokenRow(),g=s.getCurrentTokenColumn()+v;p=new l(m,g,m,g+i.start.length);break}o=s.stepBackward()}var s=new f(t,r.row,r.column),o=s.getCurrentToken();while(o&&/comment/.test(o.type)){var v=o.value.indexOf(i.end);if(v!=-1){var m=s.getCurrentTokenRow(),g=s.getCurrentTokenColumn()+v;d=new l(m,g,m,g+i.end.length);break}o=s.stepForward()}d&&t.remove(d),p&&(t.remove(p),c=p.start.row,h=-i.start.length)}else h=i.start.length,c=n.start.row,t.insert(n.end,i.end),t.insert(n.start,i.start);a.start.row==c&&(a.start.column+=h),a.end.row==c&&(a.end.column+=h),t.selection.fromOrientedRange(a)},this.getNextLineIndent=function(e,t,n){return this.$getIndent(t)},this.checkOutdent=function(e,t,n){return!1},this.autoOutdent=function(e,t,n){},this.$getIndent=function(e){return e.match(/^\\s*/)[0]},this.createWorker=function(e){return null},this.createModeDelegates=function(e){this.$embeds=[],this.$modes={};for(var t in e)if(e[t]){var n=e[t],i=n.prototype.$id,s=r.$modes[i];s||(r.$modes[i]=s=new n),r.$modes[t]||(r.$modes[t]=s),this.$embeds.push(t),this.$modes[t]=s}var o=[\"toggleBlockComment\",\"toggleCommentLines\",\"getNextLineIndent\",\"checkOutdent\",\"autoOutdent\",\"transformAction\",\"getCompletions\"];for(var t=0;t<o.length;t++)(function(e){var n=o[t],r=e[n];e[o[t]]=function(){return this.$delegator(n,arguments,r)}})(this)},this.$delegator=function(e,t,n){var r=t[0]||\"start\";if(typeof r!=\"string\"){if(Array.isArray(r[2])){var i=r[2][r[2].length-1],s=this.$modes[i];if(s)return s[e].apply(s,[r[1]].concat([].slice.call(t,1)))}r=r[0]||\"start\"}for(var o=0;o<this.$embeds.length;o++){if(!this.$modes[this.$embeds[o]])continue;var u=r.split(this.$embeds[o]);if(!u[0]&&u[1]){t[0]=u[1];var s=this.$modes[this.$embeds[o]];return s[e].apply(s,t)}}var a=n.apply(this,t);return n?a:undefined},this.transformAction=function(e,t,n,r,i){if(this.$behaviour){var s=this.$behaviour.getBehaviours();for(var o in s)if(s[o][t]){var u=s[o][t].apply(this,arguments);if(u)return u}}},this.getKeywords=function(e){if(!this.completionKeywords){var t=this.$tokenizer.rules,n=[];for(var r in t){var i=t[r];for(var s=0,o=i.length;s<o;s++)if(typeof i[s].token==\"string\")/keyword|support|storage/.test(i[s].token)&&n.push(i[s].regex);else if(typeof i[s].token==\"object\")for(var u=0,a=i[s].token.length;u<a;u++)if(/keyword|support|storage/.test(i[s].token[u])){var r=i[s].regex.match(/\\(.+?\\)/g)[u];n.push(r.substr(1,r.length-2))}}this.completionKeywords=n}return e?n.concat(this.$keywordList||[]):this.$keywordList},this.$createKeywordList=function(){return this.$highlightRules||this.getTokenizer(),this.$keywordList=this.$highlightRules.$keywordList||[]},this.getCompletions=function(e,t,n,r){var i=this.$keywordList||this.$createKeywordList();return i.map(function(e){return{name:e,value:e,score:0,meta:\"keyword\"}})},this.$id=\"ace/mode/text\"}).call(c.prototype),t.Mode=c}),ace.define(\"ace/apply_delta\",[\"require\",\"exports\",\"module\"],function(e,t,n){\"use strict\";function r(e,t){throw console.log(\"Invalid Delta:\",e),\"Invalid Delta: \"+t}function i(e,t){return t.row>=0&&t.row<e.length&&t.column>=0&&t.column<=e[t.row].length}function s(e,t){t.action!=\"insert\"&&t.action!=\"remove\"&&r(t,\"delta.action must be 'insert' or 'remove'\"),t.lines instanceof Array||r(t,\"delta.lines must be an Array\"),(!t.start||!t.end)&&r(t,\"delta.start/end must be an present\");var n=t.start;i(e,t.start)||r(t,\"delta.start must be contained in document\");var s=t.end;t.action==\"remove\"&&!i(e,s)&&r(t,\"delta.end must contained in document for 'remove' actions\");var o=s.row-n.row,u=s.column-(o==0?n.column:0);(o!=t.lines.length-1||t.lines[o].length!=u)&&r(t,\"delta.range must match delta lines\")}t.applyDelta=function(e,t,n){var r=t.start.row,i=t.start.column,s=e[r]||\"\";switch(t.action){case\"insert\":var o=t.lines;if(o.length===1)e[r]=s.substring(0,i)+t.lines[0]+s.substring(i);else{var u=[r,1].concat(t.lines);e.splice.apply(e,u),e[r]=s.substring(0,i)+e[r],e[r+t.lines.length-1]+=s.substring(i)}break;case\"remove\":var a=t.end.column,f=t.end.row;r===f?e[r]=s.substring(0,i)+s.substring(a):e.splice(r,f-r+1,s.substring(0,i)+e[f].substring(a))}}}),ace.define(\"ace/anchor\",[\"require\",\"exports\",\"module\",\"ace/lib/oop\",\"ace/lib/event_emitter\"],function(e,t,n){\"use strict\";var r=e(\"./lib/oop\"),i=e(\"./lib/event_emitter\").EventEmitter,s=t.Anchor=function(e,t,n){this.$onChange=this.onChange.bind(this),this.attach(e),typeof n==\"undefined\"?this.setPosition(t.row,t.column):this.setPosition(t,n)};(function(){function e(e,t,n){var r=n?e.column<=t.column:e.column<t.column;return e.row<t.row||e.row==t.row&&r}function t(t,n,r){var i=t.action==\"insert\",s=(i?1:-1)*(t.end.row-t.start.row),o=(i?1:-1)*(t.end.column-t.start.column),u=t.start,a=i?u:t.end;return e(n,u,r)?{row:n.row,column:n.column}:e(a,n,!r)?{row:n.row+s,column:n.column+(n.row==a.row?o:0)}:{row:u.row,column:u.column}}r.implement(this,i),this.getPosition=function(){return this.$clipPositionToDocument(this.row,this.column)},this.getDocument=function(){return this.document},this.$insertRight=!1,this.onChange=function(e){if(e.start.row==e.end.row&&e.start.row!=this.row)return;if(e.start.row>this.row)return;var n=t(e,{row:this.row,column:this.column},this.$insertRight);this.setPosition(n.row,n.column,!0)},this.setPosition=function(e,t,n){var r;n?r={row:e,column:t}:r=this.$clipPositionToDocument(e,t);if(this.row==r.row&&this.column==r.column)return;var i={row:this.row,column:this.column};this.row=r.row,this.column=r.column,this._signal(\"change\",{old:i,value:r})},this.detach=function(){this.document.removeEventListener(\"change\",this.$onChange)},this.attach=function(e){this.document=e||this.document,this.document.on(\"change\",this.$onChange)},this.$clipPositionToDocument=function(e,t){var n={};return e>=this.document.getLength()?(n.row=Math.max(0,this.document.getLength()-1),n.column=this.document.getLine(n.row).length):e<0?(n.row=0,n.column=0):(n.row=e,n.column=Math.min(this.document.getLine(n.row).length,Math.max(0,t))),t<0&&(n.column=0),n}}).call(s.prototype)}),ace.define(\"ace/document\",[\"require\",\"exports\",\"module\",\"ace/lib/oop\",\"ace/apply_delta\",\"ace/lib/event_emitter\",\"ace/range\",\"ace/anchor\"],function(e,t,n){\"use strict\";var r=e(\"./lib/oop\"),i=e(\"./apply_delta\").applyDelta,s=e(\"./lib/event_emitter\").EventEmitter,o=e(\"./range\").Range,u=e(\"./anchor\").Anchor,a=function(e){this.$lines=[\"\"],e.length===0?this.$lines=[\"\"]:Array.isArray(e)?this.insertMergedLines({row:0,column:0},e):this.insert({row:0,column:0},e)};(function(){r.implement(this,s),this.setValue=function(e){var t=this.getLength()-1;this.remove(new o(0,0,t,this.getLine(t).length)),this.insert({row:0,column:0},e)},this.getValue=function(){return this.getAllLines().join(this.getNewLineCharacter())},this.createAnchor=function(e,t){return new u(this,e,t)},\"aaa\".split(/a/).length===0?this.$split=function(e){return e.replace(/\\r\\n|\\r/g,\"\\n\").split(\"\\n\")}:this.$split=function(e){return e.split(/\\r\\n|\\r|\\n/)},this.$detectNewLine=function(e){var t=e.match(/^.*?(\\r\\n|\\r|\\n)/m);this.$autoNewLine=t?t[1]:\"\\n\",this._signal(\"changeNewLineMode\")},this.getNewLineCharacter=function(){switch(this.$newLineMode){case\"windows\":return\"\\r\\n\";case\"unix\":return\"\\n\";default:return this.$autoNewLine||\"\\n\"}},this.$autoNewLine=\"\",this.$newLineMode=\"auto\",this.setNewLineMode=function(e){if(this.$newLineMode===e)return;this.$newLineMode=e,this._signal(\"changeNewLineMode\")},this.getNewLineMode=function(){return this.$newLineMode},this.isNewLine=function(e){return e==\"\\r\\n\"||e==\"\\r\"||e==\"\\n\"},this.getLine=function(e){return this.$lines[e]||\"\"},this.getLines=function(e,t){return this.$lines.slice(e,t+1)},this.getAllLines=function(){return this.getLines(0,this.getLength())},this.getLength=function(){return this.$lines.length},this.getTextRange=function(e){return this.getLinesForRange(e).join(this.getNewLineCharacter())},this.getLinesForRange=function(e){var t;if(e.start.row===e.end.row)t=[this.getLine(e.start.row).substring(e.start.column,e.end.column)];else{t=this.getLines(e.start.row,e.end.row),t[0]=(t[0]||\"\").substring(e.start.column);var n=t.length-1;e.end.row-e.start.row==n&&(t[n]=t[n].substring(0,e.end.column))}return t},this.insertLines=function(e,t){return console.warn(\"Use of document.insertLines is deprecated. Use the insertFullLines method instead.\"),this.insertFullLines(e,t)},this.removeLines=function(e,t){return console.warn(\"Use of document.removeLines is deprecated. Use the removeFullLines method instead.\"),this.removeFullLines(e,t)},this.insertNewLine=function(e){return console.warn(\"Use of document.insertNewLine is deprecated. Use insertMergedLines(position, ['', '']) instead.\"),this.insertMergedLines(e,[\"\",\"\"])},this.insert=function(e,t){return this.getLength()<=1&&this.$detectNewLine(t),this.insertMergedLines(e,this.$split(t))},this.insertInLine=function(e,t){var n=this.clippedPos(e.row,e.column),r=this.pos(e.row,e.column+t.length);return this.applyDelta({start:n,end:r,action:\"insert\",lines:[t]},!0),this.clonePos(r)},this.clippedPos=function(e,t){var n=this.getLength();e===undefined?e=n:e<0?e=0:e>=n&&(e=n-1,t=undefined);var r=this.getLine(e);return t==undefined&&(t=r.length),t=Math.min(Math.max(t,0),r.length),{row:e,column:t}},this.clonePos=function(e){return{row:e.row,column:e.column}},this.pos=function(e,t){return{row:e,column:t}},this.$clipPosition=function(e){var t=this.getLength();return e.row>=t?(e.row=Math.max(0,t-1),e.column=this.getLine(t-1).length):(e.row=Math.max(0,e.row),e.column=Math.min(Math.max(e.column,0),this.getLine(e.row).length)),e},this.insertFullLines=function(e,t){e=Math.min(Math.max(e,0),this.getLength());var n=0;e<this.getLength()?(t=t.concat([\"\"]),n=0):(t=[\"\"].concat(t),e--,n=this.$lines[e].length),this.insertMergedLines({row:e,column:n},t)},this.insertMergedLines=function(e,t){var n=this.clippedPos(e.row,e.column),r={row:n.row+t.length-1,column:(t.length==1?n.column:0)+t[t.length-1].length};return this.applyDelta({start:n,end:r,action:\"insert\",lines:t}),this.clonePos(r)},this.remove=function(e){var t=this.clippedPos(e.start.row,e.start.column),n=this.clippedPos(e.end.row,e.end.column);return this.applyDelta({start:t,end:n,action:\"remove\",lines:this.getLinesForRange({start:t,end:n})}),this.clonePos(t)},this.removeInLine=function(e,t,n){var r=this.clippedPos(e,t),i=this.clippedPos(e,n);return this.applyDelta({start:r,end:i,action:\"remove\",lines:this.getLinesForRange({start:r,end:i})},!0),this.clonePos(r)},this.removeFullLines=function(e,t){e=Math.min(Math.max(0,e),this.getLength()-1),t=Math.min(Math.max(0,t),this.getLength()-1);var n=t==this.getLength()-1&&e>0,r=t<this.getLength()-1,i=n?e-1:e,s=n?this.getLine(i).length:0,u=r?t+1:t,a=r?0:this.getLine(u).length,f=new o(i,s,u,a),l=this.$lines.slice(e,t+1);return this.applyDelta({start:f.start,end:f.end,action:\"remove\",lines:this.getLinesForRange(f)}),l},this.removeNewLine=function(e){e<this.getLength()-1&&e>=0&&this.applyDelta({start:this.pos(e,this.getLine(e).length),end:this.pos(e+1,0),action:\"remove\",lines:[\"\",\"\"]})},this.replace=function(e,t){e instanceof o||(e=o.fromPoints(e.start,e.end));if(t.length===0&&e.isEmpty())return e.start;if(t==this.getTextRange(e))return e.end;this.remove(e);var n;return t?n=this.insert(e.start,t):n=e.start,n},this.applyDeltas=function(e){for(var t=0;t<e.length;t++)this.applyDelta(e[t])},this.revertDeltas=function(e){for(var t=e.length-1;t>=0;t--)this.revertDelta(e[t])},this.applyDelta=function(e,t){var n=e.action==\"insert\";if(n?e.lines.length<=1&&!e.lines[0]:!o.comparePoints(e.start,e.end))return;n&&e.lines.length>2e4?this.$splitAndapplyLargeDelta(e,2e4):(i(this.$lines,e,t),this._signal(\"change\",e))},this.$splitAndapplyLargeDelta=function(e,t){var n=e.lines,r=n.length-t+1,i=e.start.row,s=e.start.column;for(var o=0,u=0;o<r;o=u){u+=t-1;var a=n.slice(o,u);a.push(\"\"),this.applyDelta({start:this.pos(i+o,s),end:this.pos(i+u,s=0),action:e.action,lines:a},!0)}e.lines=n.slice(o),e.start.row=i+o,e.start.column=s,this.applyDelta(e,!0)},this.revertDelta=function(e){this.applyDelta({start:this.clonePos(e.start),end:this.clonePos(e.end),action:e.action==\"insert\"?\"remove\":\"insert\",lines:e.lines.slice()})},this.indexToPosition=function(e,t){var n=this.$lines||this.getAllLines(),r=this.getNewLineCharacter().length;for(var i=t||0,s=n.length;i<s;i++){e-=n[i].length+r;if(e<0)return{row:i,column:e+n[i].length+r}}return{row:s-1,column:e+n[s-1].length+r}},this.positionToIndex=function(e,t){var n=this.$lines||this.getAllLines(),r=this.getNewLineCharacter().length,i=0,s=Math.min(e.row,n.length);for(var o=t||0;o<s;++o)i+=n[o].length+r;return i+e.column}}).call(a.prototype),t.Document=a}),ace.define(\"ace/background_tokenizer\",[\"require\",\"exports\",\"module\",\"ace/lib/oop\",\"ace/lib/event_emitter\"],function(e,t,n){\"use strict\";var r=e(\"./lib/oop\"),i=e(\"./lib/event_emitter\").EventEmitter,s=function(e,t){this.running=!1,this.lines=[],this.states=[],this.currentLine=0,this.tokenizer=e;var n=this;this.$worker=function(){if(!n.running)return;var e=new Date,t=n.currentLine,r=-1,i=n.doc,s=t;while(n.lines[t])t++;var o=i.getLength(),u=0;n.running=!1;while(t<o){n.$tokenizeRow(t),r=t;do t++;while(n.lines[t]);u++;if(u%5===0&&new Date-e>20){n.running=setTimeout(n.$worker,20);break}}n.currentLine=t,r==-1&&(r=t),s<=r&&n.fireUpdateEvent(s,r)}};(function(){r.implement(this,i),this.setTokenizer=function(e){this.tokenizer=e,this.lines=[],this.states=[],this.start(0)},this.setDocument=function(e){this.doc=e,this.lines=[],this.states=[],this.stop()},this.fireUpdateEvent=function(e,t){var n={first:e,last:t};this._signal(\"update\",{data:n})},this.start=function(e){this.currentLine=Math.min(e||0,this.currentLine,this.doc.getLength()),this.lines.splice(this.currentLine,this.lines.length),this.states.splice(this.currentLine,this.states.length),this.stop(),this.running=setTimeout(this.$worker,700)},this.scheduleStart=function(){this.running||(this.running=setTimeout(this.$worker,700))},this.$updateOnChange=function(e){var t=e.start.row,n=e.end.row-t;if(n===0)this.lines[t]=null;else if(e.action==\"remove\")this.lines.splice(t,n+1,null),this.states.splice(t,n+1,null);else{var r=Array(n+1);r.unshift(t,1),this.lines.splice.apply(this.lines,r),this.states.splice.apply(this.states,r)}this.currentLine=Math.min(t,this.currentLine,this.doc.getLength()),this.stop()},this.stop=function(){this.running&&clearTimeout(this.running),this.running=!1},this.getTokens=function(e){return this.lines[e]||this.$tokenizeRow(e)},this.getState=function(e){return this.currentLine==e&&this.$tokenizeRow(e),this.states[e]||\"start\"},this.$tokenizeRow=function(e){var t=this.doc.getLine(e),n=this.states[e-1],r=this.tokenizer.getLineTokens(t,n,e);return this.states[e]+\"\"!=r.state+\"\"?(this.states[e]=r.state,this.lines[e+1]=null,this.currentLine>e+1&&(this.currentLine=e+1)):this.currentLine==e&&(this.currentLine=e+1),this.lines[e]=r.tokens}}).call(s.prototype),t.BackgroundTokenizer=s}),ace.define(\"ace/search_highlight\",[\"require\",\"exports\",\"module\",\"ace/lib/lang\",\"ace/lib/oop\",\"ace/range\"],function(e,t,n){\"use strict\";var r=e(\"./lib/lang\"),i=e(\"./lib/oop\"),s=e(\"./range\").Range,o=function(e,t,n){this.setRegexp(e),this.clazz=t,this.type=n||\"text\"};(function(){this.MAX_RANGES=500,this.setRegexp=function(e){if(this.regExp+\"\"==e+\"\")return;this.regExp=e,this.cache=[]},this.update=function(e,t,n,i){if(!this.regExp)return;var o=i.firstRow,u=i.lastRow;for(var a=o;a<=u;a++){var f=this.cache[a];f==null&&(f=r.getMatchOffsets(n.getLine(a),this.regExp),f.length>this.MAX_RANGES&&(f=f.slice(0,this.MAX_RANGES)),f=f.map(function(e){return new s(a,e.offset,a,e.offset+e.length)}),this.cache[a]=f.length?f:\"\");for(var l=f.length;l--;)t.drawSingleLineMarker(e,f[l].toScreenRange(n),this.clazz,i)}}}).call(o.prototype),t.SearchHighlight=o}),ace.define(\"ace/edit_session/fold_line\",[\"require\",\"exports\",\"module\",\"ace/range\"],function(e,t,n){\"use strict\";function i(e,t){this.foldData=e,Array.isArray(t)?this.folds=t:t=this.folds=[t];var n=t[t.length-1];this.range=new r(t[0].start.row,t[0].start.column,n.end.row,n.end.column),this.start=this.range.start,this.end=this.range.end,this.folds.forEach(function(e){e.setFoldLine(this)},this)}var r=e(\"../range\").Range;(function(){this.shiftRow=function(e){this.start.row+=e,this.end.row+=e,this.folds.forEach(function(t){t.start.row+=e,t.end.row+=e})},this.addFold=function(e){if(e.sameRow){if(e.start.row<this.startRow||e.endRow>this.endRow)throw new Error(\"Can't add a fold to this FoldLine as it has no connection\");this.folds.push(e),this.folds.sort(function(e,t){return-e.range.compareEnd(t.start.row,t.start.column)}),this.range.compareEnd(e.start.row,e.start.column)>0?(this.end.row=e.end.row,this.end.column=e.end.column):this.range.compareStart(e.end.row,e.end.column)<0&&(this.start.row=e.start.row,this.start.column=e.start.column)}else if(e.start.row==this.end.row)this.folds.push(e),this.end.row=e.end.row,this.end.column=e.end.column;else{if(e.end.row!=this.start.row)throw new Error(\"Trying to add fold to FoldRow that doesn't have a matching row\");this.folds.unshift(e),this.start.row=e.start.row,this.start.column=e.start.column}e.foldLine=this},this.containsRow=function(e){return e>=this.start.row&&e<=this.end.row},this.walk=function(e,t,n){var r=0,i=this.folds,s,o,u,a=!0;t==null&&(t=this.end.row,n=this.end.column);for(var f=0;f<i.length;f++){s=i[f],o=s.range.compareStart(t,n);if(o==-1){e(null,t,n,r,a);return}u=e(null,s.start.row,s.start.column,r,a),u=!u&&e(s.placeholder,s.start.row,s.start.column,r);if(u||o===0)return;a=!s.sameRow,r=s.end.column}e(null,t,n,r,a)},this.getNextFoldTo=function(e,t){var n,r;for(var i=0;i<this.folds.length;i++){n=this.folds[i],r=n.range.compareEnd(e,t);if(r==-1)return{fold:n,kind:\"after\"};if(r===0)return{fold:n,kind:\"inside\"}}return null},this.addRemoveChars=function(e,t,n){var r=this.getNextFoldTo(e,t),i,s;if(r){i=r.fold;if(r.kind==\"inside\"&&i.start.column!=t&&i.start.row!=e)window.console&&window.console.log(e,t,i);else if(i.start.row==e){s=this.folds;var o=s.indexOf(i);o===0&&(this.start.column+=n);for(o;o<s.length;o++){i=s[o],i.start.column+=n;if(!i.sameRow)return;i.end.column+=n}this.end.column+=n}}},this.split=function(e,t){var n=this.getNextFoldTo(e,t);if(!n||n.kind==\"inside\")return null;var r=n.fold,s=this.folds,o=this.foldData,u=s.indexOf(r),a=s[u-1];this.end.row=a.end.row,this.end.column=a.end.column,s=s.splice(u,s.length-u);var f=new i(o,s);return o.splice(o.indexOf(this)+1,0,f),f},this.merge=function(e){var t=e.folds;for(var n=0;n<t.length;n++)this.addFold(t[n]);var r=this.foldData;r.splice(r.indexOf(e),1)},this.toString=function(){var e=[this.range.toString()+\": [\"];return this.folds.forEach(function(t){e.push(\"  \"+t.toString())}),e.push(\"]\"),e.join(\"\\n\")},this.idxToPosition=function(e){var t=0;for(var n=0;n<this.folds.length;n++){var r=this.folds[n];e-=r.start.column-t;if(e<0)return{row:r.start.row,column:r.start.column+e};e-=r.placeholder.length;if(e<0)return r.start;t=r.end.column}return{row:this.end.row,column:this.end.column+e}}}).call(i.prototype),t.FoldLine=i}),ace.define(\"ace/range_list\",[\"require\",\"exports\",\"module\",\"ace/range\"],function(e,t,n){\"use strict\";var r=e(\"./range\").Range,i=r.comparePoints,s=function(){this.ranges=[]};(function(){this.comparePoints=i,this.pointIndex=function(e,t,n){var r=this.ranges;for(var s=n||0;s<r.length;s++){var o=r[s],u=i(e,o.end);if(u>0)continue;var a=i(e,o.start);return u===0?t&&a!==0?-s-2:s:a>0||a===0&&!t?s:-s-1}return-s-1},this.add=function(e){var t=!e.isEmpty(),n=this.pointIndex(e.start,t);n<0&&(n=-n-1);var r=this.pointIndex(e.end,t,n);return r<0?r=-r-1:r++,this.ranges.splice(n,r-n,e)},this.addList=function(e){var t=[];for(var n=e.length;n--;)t.push.apply(t,this.add(e[n]));return t},this.substractPoint=function(e){var t=this.pointIndex(e);if(t>=0)return this.ranges.splice(t,1)},this.merge=function(){var e=[],t=this.ranges;t=t.sort(function(e,t){return i(e.start,t.start)});var n=t[0],r;for(var s=1;s<t.length;s++){r=n,n=t[s];var o=i(r.end,n.start);if(o<0)continue;if(o==0&&!r.isEmpty()&&!n.isEmpty())continue;i(r.end,n.end)<0&&(r.end.row=n.end.row,r.end.column=n.end.column),t.splice(s,1),e.push(n),n=r,s--}return this.ranges=t,e},this.contains=function(e,t){return this.pointIndex({row:e,column:t})>=0},this.containsPoint=function(e){return this.pointIndex(e)>=0},this.rangeAtPoint=function(e){var t=this.pointIndex(e);if(t>=0)return this.ranges[t]},this.clipRows=function(e,t){var n=this.ranges;if(n[0].start.row>t||n[n.length-1].start.row<e)return[];var r=this.pointIndex({row:e,column:0});r<0&&(r=-r-1);var i=this.pointIndex({row:t,column:0},r);i<0&&(i=-i-1);var s=[];for(var o=r;o<i;o++)s.push(n[o]);return s},this.removeAll=function(){return this.ranges.splice(0,this.ranges.length)},this.attach=function(e){this.session&&this.detach(),this.session=e,this.onChange=this.$onChange.bind(this),this.session.on(\"change\",this.onChange)},this.detach=function(){if(!this.session)return;this.session.removeListener(\"change\",this.onChange),this.session=null},this.$onChange=function(e){var t=e.start,n=e.end,r=t.row,i=n.row,s=this.ranges;for(var o=0,u=s.length;o<u;o++){var a=s[o];if(a.end.row>=r)break}if(e.action==\"insert\"){var f=i-r,l=-t.column+n.column;for(;o<u;o++){var a=s[o];if(a.start.row>r)break;a.start.row==r&&a.start.column>=t.column&&(a.start.column!=t.column||!this.$insertRight)&&(a.start.column+=l,a.start.row+=f);if(a.end.row==r&&a.end.column>=t.column){if(a.end.column==t.column&&this.$insertRight)continue;a.end.column==t.column&&l>0&&o<u-1&&a.end.column>a.start.column&&a.end.column==s[o+1].start.column&&(a.end.column-=l),a.end.column+=l,a.end.row+=f}}}else{var f=r-i,l=t.column-n.column;for(;o<u;o++){var a=s[o];if(a.start.row>i)break;if(a.end.row<i&&(r<a.end.row||r==a.end.row&&t.column<a.end.column))a.end.row=r,a.end.column=t.column;else if(a.end.row==i)if(a.end.column<=n.column){if(f||a.end.column>t.column)a.end.column=t.column,a.end.row=t.row}else a.end.column+=l,a.end.row+=f;else a.end.row>i&&(a.end.row+=f);if(a.start.row<i&&(r<a.start.row||r==a.start.row&&t.column<a.start.column))a.start.row=r,a.start.column=t.column;else if(a.start.row==i)if(a.start.column<=n.column){if(f||a.start.column>t.column)a.start.column=t.column,a.start.row=t.row}else a.start.column+=l,a.start.row+=f;else a.start.row>i&&(a.start.row+=f)}}if(f!=0&&o<u)for(;o<u;o++){var a=s[o];a.start.row+=f,a.end.row+=f}}}).call(s.prototype),t.RangeList=s}),ace.define(\"ace/edit_session/fold\",[\"require\",\"exports\",\"module\",\"ace/range\",\"ace/range_list\",\"ace/lib/oop\"],function(e,t,n){\"use strict\";function u(e,t){e.row-=t.row,e.row==0&&(e.column-=t.column)}function a(e,t){u(e.start,t),u(e.end,t)}function f(e,t){e.row==0&&(e.column+=t.column),e.row+=t.row}function l(e,t){f(e.start,t),f(e.end,t)}var r=e(\"../range\").Range,i=e(\"../range_list\").RangeList,s=e(\"../lib/oop\"),o=t.Fold=function(e,t){this.foldLine=null,this.placeholder=t,this.range=e,this.start=e.start,this.end=e.end,this.sameRow=e.start.row==e.end.row,this.subFolds=this.ranges=[]};s.inherits(o,i),function(){this.toString=function(){return'\"'+this.placeholder+'\" '+this.range.toString()},this.setFoldLine=function(e){this.foldLine=e,this.subFolds.forEach(function(t){t.setFoldLine(e)})},this.clone=function(){var e=this.range.clone(),t=new o(e,this.placeholder);return this.subFolds.forEach(function(e){t.subFolds.push(e.clone())}),t.collapseChildren=this.collapseChildren,t},this.addSubFold=function(e){if(this.range.isEqual(e))return;if(!this.range.containsRange(e))throw new Error(\"A fold can't intersect already existing fold\"+e.range+this.range);a(e,this.start);var t=e.start.row,n=e.start.column;for(var r=0,i=-1;r<this.subFolds.length;r++){i=this.subFolds[r].range.compare(t,n);if(i!=1)break}var s=this.subFolds[r];if(i==0)return s.addSubFold(e);var t=e.range.end.row,n=e.range.end.column;for(var o=r,i=-1;o<this.subFolds.length;o++){i=this.subFolds[o].range.compare(t,n);if(i!=1)break}var u=this.subFolds[o];if(i==0)throw new Error(\"A fold can't intersect already existing fold\"+e.range+this.range);var f=this.subFolds.splice(r,o-r,e);return e.setFoldLine(this.foldLine),e},this.restoreRange=function(e){return l(e,this.start)}}.call(o.prototype)}),ace.define(\"ace/edit_session/folding\",[\"require\",\"exports\",\"module\",\"ace/range\",\"ace/edit_session/fold_line\",\"ace/edit_session/fold\",\"ace/token_iterator\"],function(e,t,n){\"use strict\";function u(){this.getFoldAt=function(e,t,n){var r=this.getFoldLine(e);if(!r)return null;var i=r.folds;for(var s=0;s<i.length;s++){var o=i[s];if(o.range.contains(e,t)){if(n==1&&o.range.isEnd(e,t))continue;if(n==-1&&o.range.isStart(e,t))continue;return o}}},this.getFoldsInRange=function(e){var t=e.start,n=e.end,r=this.$foldData,i=[];t.column+=1,n.column-=1;for(var s=0;s<r.length;s++){var o=r[s].range.compareRange(e);if(o==2)continue;if(o==-2)break;var u=r[s].folds;for(var a=0;a<u.length;a++){var f=u[a];o=f.range.compareRange(e);if(o==-2)break;if(o==2)continue;if(o==42)break;i.push(f)}}return t.column-=1,n.column+=1,i},this.getFoldsInRangeList=function(e){if(Array.isArray(e)){var t=[];e.forEach(function(e){t=t.concat(this.getFoldsInRange(e))},this)}else var t=this.getFoldsInRange(e);return t},this.getAllFolds=function(){var e=[],t=this.$foldData;for(var n=0;n<t.length;n++)for(var r=0;r<t[n].folds.length;r++)e.push(t[n].folds[r]);return e},this.getFoldStringAt=function(e,t,n,r){r=r||this.getFoldLine(e);if(!r)return null;var i={end:{column:0}},s,o;for(var u=0;u<r.folds.length;u++){o=r.folds[u];var a=o.range.compareEnd(e,t);if(a==-1){s=this.getLine(o.start.row).substring(i.end.column,o.start.column);break}if(a===0)return null;i=o}return s||(s=this.getLine(o.start.row).substring(i.end.column)),n==-1?s.substring(0,t-i.end.column):n==1?s.substring(t-i.end.column):s},this.getFoldLine=function(e,t){var n=this.$foldData,r=0;t&&(r=n.indexOf(t)),r==-1&&(r=0);for(r;r<n.length;r++){var i=n[r];if(i.start.row<=e&&i.end.row>=e)return i;if(i.end.row>e)return null}return null},this.getNextFoldLine=function(e,t){var n=this.$foldData,r=0;t&&(r=n.indexOf(t)),r==-1&&(r=0);for(r;r<n.length;r++){var i=n[r];if(i.end.row>=e)return i}return null},this.getFoldedRowCount=function(e,t){var n=this.$foldData,r=t-e+1;for(var i=0;i<n.length;i++){var s=n[i],o=s.end.row,u=s.start.row;if(o>=t){u<t&&(u>=e?r-=t-u:r=0);break}o>=e&&(u>=e?r-=o-u:r-=o-e+1)}return r},this.$addFoldLine=function(e){return this.$foldData.push(e),this.$foldData.sort(function(e,t){return e.start.row-t.start.row}),e},this.addFold=function(e,t){var n=this.$foldData,r=!1,o;e instanceof s?o=e:(o=new s(t,e),o.collapseChildren=t.collapseChildren),this.$clipRangeToDocument(o.range);var u=o.start.row,a=o.start.column,f=o.end.row,l=o.end.column;if(u<f||u==f&&a<=l-2){var c=this.getFoldAt(u,a,1),h=this.getFoldAt(f,l,-1);if(c&&h==c)return c.addSubFold(o);c&&!c.range.isStart(u,a)&&this.removeFold(c),h&&!h.range.isEnd(f,l)&&this.removeFold(h);var p=this.getFoldsInRange(o.range);p.length>0&&(this.removeFolds(p),p.forEach(function(e){o.addSubFold(e)}));for(var d=0;d<n.length;d++){var v=n[d];if(f==v.start.row){v.addFold(o),r=!0;break}if(u==v.end.row){v.addFold(o),r=!0;if(!o.sameRow){var m=n[d+1];if(m&&m.start.row==f){v.merge(m);break}}break}if(f<=v.start.row)break}return r||(v=this.$addFoldLine(new i(this.$foldData,o))),this.$useWrapMode?this.$updateWrapData(v.start.row,v.start.row):this.$updateRowLengthCache(v.start.row,v.start.row),this.$modified=!0,this._signal(\"changeFold\",{data:o,action:\"add\"}),o}throw new Error(\"The range has to be at least 2 characters width\")},this.addFolds=function(e){e.forEach(function(e){this.addFold(e)},this)},this.removeFold=function(e){var t=e.foldLine,n=t.start.row,r=t.end.row,i=this.$foldData,s=t.folds;if(s.length==1)i.splice(i.indexOf(t),1);else if(t.range.isEnd(e.end.row,e.end.column))s.pop(),t.end.row=s[s.length-1].end.row,t.end.column=s[s.length-1].end.column;else if(t.range.isStart(e.start.row,e.start.column))s.shift(),t.start.row=s[0].start.row,t.start.column=s[0].start.column;else if(e.sameRow)s.splice(s.indexOf(e),1);else{var o=t.split(e.start.row,e.start.column);s=o.folds,s.shift(),o.start.row=s[0].start.row,o.start.column=s[0].start.column}this.$updating||(this.$useWrapMode?this.$updateWrapData(n,r):this.$updateRowLengthCache(n,r)),this.$modified=!0,this._signal(\"changeFold\",{data:e,action:\"remove\"})},this.removeFolds=function(e){var t=[];for(var n=0;n<e.length;n++)t.push(e[n]);t.forEach(function(e){this.removeFold(e)},this),this.$modified=!0},this.expandFold=function(e){this.removeFold(e),e.subFolds.forEach(function(t){e.restoreRange(t),this.addFold(t)},this),e.collapseChildren>0&&this.foldAll(e.start.row+1,e.end.row,e.collapseChildren-1),e.subFolds=[]},this.expandFolds=function(e){e.forEach(function(e){this.expandFold(e)},this)},this.unfold=function(e,t){var n,i;e==null?(n=new r(0,0,this.getLength(),0),t=!0):typeof e==\"number\"?n=new r(e,0,e,this.getLine(e).length):\"row\"in e?n=r.fromPoints(e,e):n=e,i=this.getFoldsInRangeList(n);if(t)this.removeFolds(i);else{var s=i;while(s.length)this.expandFolds(s),s=this.getFoldsInRangeList(n)}if(i.length)return i},this.isRowFolded=function(e,t){return!!this.getFoldLine(e,t)},this.getRowFoldEnd=function(e,t){var n=this.getFoldLine(e,t);return n?n.end.row:e},this.getRowFoldStart=function(e,t){var n=this.getFoldLine(e,t);return n?n.start.row:e},this.getFoldDisplayLine=function(e,t,n,r,i){r==null&&(r=e.start.row),i==null&&(i=0),t==null&&(t=e.end.row),n==null&&(n=this.getLine(t).length);var s=this.doc,o=\"\";return e.walk(function(e,t,n,u){if(t<r)return;if(t==r){if(n<i)return;u=Math.max(i,u)}e!=null?o+=e:o+=s.getLine(t).substring(u,n)},t,n),o},this.getDisplayLine=function(e,t,n,r){var i=this.getFoldLine(e);if(!i){var s;return s=this.doc.getLine(e),s.substring(r||0,t||s.length)}return this.getFoldDisplayLine(i,e,t,n,r)},this.$cloneFoldData=function(){var e=[];return e=this.$foldData.map(function(t){var n=t.folds.map(function(e){return e.clone()});return new i(e,n)}),e},this.toggleFold=function(e){var t=this.selection,n=t.getRange(),r,i;if(n.isEmpty()){var s=n.start;r=this.getFoldAt(s.row,s.column);if(r){this.expandFold(r);return}(i=this.findMatchingBracket(s))?n.comparePoint(i)==1?n.end=i:(n.start=i,n.start.column++,n.end.column--):(i=this.findMatchingBracket({row:s.row,column:s.column+1}))?(n.comparePoint(i)==1?n.end=i:n.start=i,n.start.column++):n=this.getCommentFoldRange(s.row,s.column)||n}else{var o=this.getFoldsInRange(n);if(e&&o.length){this.expandFolds(o);return}o.length==1&&(r=o[0])}r||(r=this.getFoldAt(n.start.row,n.start.column));if(r&&r.range.toString()==n.toString()){this.expandFold(r);return}var u=\"...\";if(!n.isMultiLine()){u=this.getTextRange(n);if(u.length<4)return;u=u.trim().substring(0,2)+\"..\"}this.addFold(u,n)},this.getCommentFoldRange=function(e,t,n){var i=new o(this,e,t),s=i.getCurrentToken(),u=s.type;if(s&&/^comment|string/.test(u)){u=u.match(/comment|string/)[0],u==\"comment\"&&(u+=\"|doc-start\");var a=new RegExp(u),f=new r;if(n!=1){do s=i.stepBackward();while(s&&a.test(s.type));i.stepForward()}f.start.row=i.getCurrentTokenRow(),f.start.column=i.getCurrentTokenColumn()+2,i=new o(this,e,t);if(n!=-1){var l=-1;do{s=i.stepForward();if(l==-1){var c=this.getState(i.$row);a.test(c)||(l=i.$row)}else if(i.$row>l)break}while(s&&a.test(s.type));s=i.stepBackward()}else s=i.getCurrentToken();return f.end.row=i.getCurrentTokenRow(),f.end.column=i.getCurrentTokenColumn()+s.value.length-2,f}},this.foldAll=function(e,t,n){n==undefined&&(n=1e5);var r=this.foldWidgets;if(!r)return;t=t||this.getLength(),e=e||0;for(var i=e;i<t;i++){r[i]==null&&(r[i]=this.getFoldWidget(i));if(r[i]!=\"start\")continue;var s=this.getFoldWidgetRange(i);if(s&&s.isMultiLine()&&s.end.row<=t&&s.start.row>=e){i=s.end.row;try{var o=this.addFold(\"...\",s);o&&(o.collapseChildren=n)}catch(u){}}}},this.$foldStyles={manual:1,markbegin:1,markbeginend:1},this.$foldStyle=\"markbegin\",this.setFoldStyle=function(e){if(!this.$foldStyles[e])throw new Error(\"invalid fold style: \"+e+\"[\"+Object.keys(this.$foldStyles).join(\", \")+\"]\");if(this.$foldStyle==e)return;this.$foldStyle=e,e==\"manual\"&&this.unfold();var t=this.$foldMode;this.$setFolding(null),this.$setFolding(t)},this.$setFolding=function(e){if(this.$foldMode==e)return;this.$foldMode=e,this.off(\"change\",this.$updateFoldWidgets),this.off(\"tokenizerUpdate\",this.$tokenizerUpdateFoldWidgets),this._signal(\"changeAnnotation\");if(!e||this.$foldStyle==\"manual\"){this.foldWidgets=null;return}this.foldWidgets=[],this.getFoldWidget=e.getFoldWidget.bind(e,this,this.$foldStyle),this.getFoldWidgetRange=e.getFoldWidgetRange.bind(e,this,this.$foldStyle),this.$updateFoldWidgets=this.updateFoldWidgets.bind(this),this.$tokenizerUpdateFoldWidgets=this.tokenizerUpdateFoldWidgets.bind(this),this.on(\"change\",this.$updateFoldWidgets),this.on(\"tokenizerUpdate\",this.$tokenizerUpdateFoldWidgets)},this.getParentFoldRangeData=function(e,t){var n=this.foldWidgets;if(!n||t&&n[e])return{};var r=e-1,i;while(r>=0){var s=n[r];s==null&&(s=n[r]=this.getFoldWidget(r));if(s==\"start\"){var o=this.getFoldWidgetRange(r);i||(i=o);if(o&&o.end.row>=e)break}r--}return{range:r!==-1&&o,firstRange:i}},this.onFoldWidgetClick=function(e,t){t=t.domEvent;var n={children:t.shiftKey,all:t.ctrlKey||t.metaKey,siblings:t.altKey},r=this.$toggleFoldWidget(e,n);if(!r){var i=t.target||t.srcElement;i&&/ace_fold-widget/.test(i.className)&&(i.className+=\" ace_invalid\")}},this.$toggleFoldWidget=function(e,t){if(!this.getFoldWidget)return;var n=this.getFoldWidget(e),r=this.getLine(e),i=n===\"end\"?-1:1,s=this.getFoldAt(e,i===-1?0:r.length,i);if(s)return t.children||t.all?this.removeFold(s):this.expandFold(s),s;var o=this.getFoldWidgetRange(e,!0);if(o&&!o.isMultiLine()){s=this.getFoldAt(o.start.row,o.start.column,1);if(s&&o.isEqual(s.range))return this.removeFold(s),s}if(t.siblings){var u=this.getParentFoldRangeData(e);if(u.range)var a=u.range.start.row+1,f=u.range.end.row;this.foldAll(a,f,t.all?1e4:0)}else t.children?(f=o?o.end.row:this.getLength(),this.foldAll(e+1,f,t.all?1e4:0)):o&&(t.all&&(o.collapseChildren=1e4),this.addFold(\"...\",o));return o},this.toggleFoldWidget=function(e){var t=this.selection.getCursor().row;t=this.getRowFoldStart(t);var n=this.$toggleFoldWidget(t,{});if(n)return;var r=this.getParentFoldRangeData(t,!0);n=r.range||r.firstRange;if(n){t=n.start.row;var i=this.getFoldAt(t,this.getLine(t).length,1);i?this.removeFold(i):this.addFold(\"...\",n)}},this.updateFoldWidgets=function(e){var t=e.start.row,n=e.end.row-t;if(n===0)this.foldWidgets[t]=null;else if(e.action==\"remove\")this.foldWidgets.splice(t,n+1,null);else{var r=Array(n+1);r.unshift(t,1),this.foldWidgets.splice.apply(this.foldWidgets,r)}},this.tokenizerUpdateFoldWidgets=function(e){var t=e.data;t.first!=t.last&&this.foldWidgets.length>t.first&&this.foldWidgets.splice(t.first,this.foldWidgets.length)}}var r=e(\"../range\").Range,i=e(\"./fold_line\").FoldLine,s=e(\"./fold\").Fold,o=e(\"../token_iterator\").TokenIterator;t.Folding=u}),ace.define(\"ace/edit_session/bracket_match\",[\"require\",\"exports\",\"module\",\"ace/token_iterator\",\"ace/range\"],function(e,t,n){\"use strict\";function s(){this.findMatchingBracket=function(e,t){if(e.column==0)return null;var n=t||this.getLine(e.row).charAt(e.column-1);if(n==\"\")return null;var r=n.match(/([\\(\\[\\{])|([\\)\\]\\}])/);return r?r[1]?this.$findClosingBracket(r[1],e):this.$findOpeningBracket(r[2],e):null},this.getBracketRange=function(e){var t=this.getLine(e.row),n=!0,r,s=t.charAt(e.column-1),o=s&&s.match(/([\\(\\[\\{])|([\\)\\]\\}])/);o||(s=t.charAt(e.column),e={row:e.row,column:e.column+1},o=s&&s.match(/([\\(\\[\\{])|([\\)\\]\\}])/),n=!1);if(!o)return null;if(o[1]){var u=this.$findClosingBracket(o[1],e);if(!u)return null;r=i.fromPoints(e,u),n||(r.end.column++,r.start.column--),r.cursor=r.end}else{var u=this.$findOpeningBracket(o[2],e);if(!u)return null;r=i.fromPoints(u,e),n||(r.start.column++,r.end.column--),r.cursor=r.start}return r},this.$brackets={\")\":\"(\",\"(\":\")\",\"]\":\"[\",\"[\":\"]\",\"{\":\"}\",\"}\":\"{\",\"<\":\">\",\">\":\"<\"},this.$findOpeningBracket=function(e,t,n){var i=this.$brackets[e],s=1,o=new r(this,t.row,t.column),u=o.getCurrentToken();u||(u=o.stepForward());if(!u)return;n||(n=new RegExp(\"(\\\\.?\"+u.type.replace(\".\",\"\\\\.\").replace(\"rparen\",\".paren\").replace(/\\b(?:end)\\b/,\"(?:start|begin|end)\")+\")+\"));var a=t.column-o.getCurrentTokenColumn()-2,f=u.value;for(;;){while(a>=0){var l=f.charAt(a);if(l==i){s-=1;if(s==0)return{row:o.getCurrentTokenRow(),column:a+o.getCurrentTokenColumn()}}else l==e&&(s+=1);a-=1}do u=o.stepBackward();while(u&&!n.test(u.type));if(u==null)break;f=u.value,a=f.length-1}return null},this.$findClosingBracket=function(e,t,n){var i=this.$brackets[e],s=1,o=new r(this,t.row,t.column),u=o.getCurrentToken();u||(u=o.stepForward());if(!u)return;n||(n=new RegExp(\"(\\\\.?\"+u.type.replace(\".\",\"\\\\.\").replace(\"lparen\",\".paren\").replace(/\\b(?:start|begin)\\b/,\"(?:start|begin|end)\")+\")+\"));var a=t.column-o.getCurrentTokenColumn();for(;;){var f=u.value,l=f.length;while(a<l){var c=f.charAt(a);if(c==i){s-=1;if(s==0)return{row:o.getCurrentTokenRow(),column:a+o.getCurrentTokenColumn()}}else c==e&&(s+=1);a+=1}do u=o.stepForward();while(u&&!n.test(u.type));if(u==null)break;a=0}return null}}var r=e(\"../token_iterator\").TokenIterator,i=e(\"../range\").Range;t.BracketMatch=s}),ace.define(\"ace/edit_session\",[\"require\",\"exports\",\"module\",\"ace/lib/oop\",\"ace/lib/lang\",\"ace/bidihandler\",\"ace/config\",\"ace/lib/event_emitter\",\"ace/selection\",\"ace/mode/text\",\"ace/range\",\"ace/document\",\"ace/background_tokenizer\",\"ace/search_highlight\",\"ace/edit_session/folding\",\"ace/edit_session/bracket_match\"],function(e,t,n){\"use strict\";var r=e(\"./lib/oop\"),i=e(\"./lib/lang\"),s=e(\"./bidihandler\").BidiHandler,o=e(\"./config\"),u=e(\"./lib/event_emitter\").EventEmitter,a=e(\"./selection\").Selection,f=e(\"./mode/text\").Mode,l=e(\"./range\").Range,c=e(\"./document\").Document,h=e(\"./background_tokenizer\").BackgroundTokenizer,p=e(\"./search_highlight\").SearchHighlight,d=function(e,t){this.$breakpoints=[],this.$decorations=[],this.$frontMarkers={},this.$backMarkers={},this.$markerId=1,this.$undoSelect=!0,this.$foldData=[],this.id=\"session\"+ ++d.$uid,this.$foldData.toString=function(){return this.join(\"\\n\")},this.on(\"changeFold\",this.onChangeFold.bind(this)),this.$onChange=this.onChange.bind(this);if(typeof e!=\"object\"||!e.getLine)e=new c(e);this.setDocument(e),this.selection=new a(this),this.$bidiHandler=new s(this),o.resetOptions(this),this.setMode(t),o._signal(\"session\",this)};d.$uid=0,function(){function m(e){return e<4352?!1:e>=4352&&e<=4447||e>=4515&&e<=4519||e>=4602&&e<=4607||e>=9001&&e<=9002||e>=11904&&e<=11929||e>=11931&&e<=12019||e>=12032&&e<=12245||e>=12272&&e<=12283||e>=12288&&e<=12350||e>=12353&&e<=12438||e>=12441&&e<=12543||e>=12549&&e<=12589||e>=12593&&e<=12686||e>=12688&&e<=12730||e>=12736&&e<=12771||e>=12784&&e<=12830||e>=12832&&e<=12871||e>=12880&&e<=13054||e>=13056&&e<=19903||e>=19968&&e<=42124||e>=42128&&e<=42182||e>=43360&&e<=43388||e>=44032&&e<=55203||e>=55216&&e<=55238||e>=55243&&e<=55291||e>=63744&&e<=64255||e>=65040&&e<=65049||e>=65072&&e<=65106||e>=65108&&e<=65126||e>=65128&&e<=65131||e>=65281&&e<=65376||e>=65504&&e<=65510}r.implement(this,u),this.setDocument=function(e){this.doc&&this.doc.removeListener(\"change\",this.$onChange),this.doc=e,e.on(\"change\",this.$onChange),this.bgTokenizer&&this.bgTokenizer.setDocument(this.getDocument()),this.resetCaches()},this.getDocument=function(){return this.doc},this.$resetRowCache=function(e){if(!e){this.$docRowCache=[],this.$screenRowCache=[];return}var t=this.$docRowCache.length,n=this.$getRowCacheIndex(this.$docRowCache,e)+1;t>n&&(this.$docRowCache.splice(n,t),this.$screenRowCache.splice(n,t))},this.$getRowCacheIndex=function(e,t){var n=0,r=e.length-1;while(n<=r){var i=n+r>>1,s=e[i];if(t>s)n=i+1;else{if(!(t<s))return i;r=i-1}}return n-1},this.resetCaches=function(){this.$modified=!0,this.$wrapData=[],this.$rowLengthCache=[],this.$resetRowCache(0),this.bgTokenizer&&this.bgTokenizer.start(0)},this.onChangeFold=function(e){var t=e.data;this.$resetRowCache(t.start.row)},this.onChange=function(e){this.$modified=!0,this.$bidiHandler.onChange(e),this.$resetRowCache(e.start.row);var t=this.$updateInternalDataOnChange(e);!this.$fromUndo&&this.$undoManager&&(t&&t.length&&(this.$undoManager.add({action:\"removeFolds\",folds:t},this.mergeUndoDeltas),this.mergeUndoDeltas=!0),this.$undoManager.add(e,this.mergeUndoDeltas),this.mergeUndoDeltas=!0,this.$informUndoManager.schedule()),this.bgTokenizer&&this.bgTokenizer.$updateOnChange(e),this._signal(\"change\",e)},this.setValue=function(e){this.doc.setValue(e),this.selection.moveTo(0,0),this.$resetRowCache(0),this.setUndoManager(this.$undoManager),this.getUndoManager().reset()},this.getValue=this.toString=function(){return this.doc.getValue()},this.getSelection=function(){return this.selection},this.getState=function(e){return this.bgTokenizer.getState(e)},this.getTokens=function(e){return this.bgTokenizer.getTokens(e)},this.getTokenAt=function(e,t){var n=this.bgTokenizer.getTokens(e),r,i=0;if(t==null){var s=n.length-1;i=this.getLine(e).length}else for(var s=0;s<n.length;s++){i+=n[s].value.length;if(i>=t)break}return r=n[s],r?(r.index=s,r.start=i-r.value.length,r):null},this.setUndoManager=function(e){this.$undoManager=e,this.$informUndoManager&&this.$informUndoManager.cancel();if(e){var t=this;e.addSession(this),this.$syncInformUndoManager=function(){t.$informUndoManager.cancel(),t.mergeUndoDeltas=!1},this.$informUndoManager=i.delayedCall(this.$syncInformUndoManager)}else this.$syncInformUndoManager=function(){}},this.markUndoGroup=function(){this.$syncInformUndoManager&&this.$syncInformUndoManager()},this.$defaultUndoManager={undo:function(){},redo:function(){},reset:function(){},add:function(){},addSelection:function(){},startNewGroup:function(){},addSession:function(){}},this.getUndoManager=function(){return this.$undoManager||this.$defaultUndoManager},this.getTabString=function(){return this.getUseSoftTabs()?i.stringRepeat(\" \",this.getTabSize()):\"\t\"},this.setUseSoftTabs=function(e){this.setOption(\"useSoftTabs\",e)},this.getUseSoftTabs=function(){return this.$useSoftTabs&&!this.$mode.$indentWithTabs},this.setTabSize=function(e){this.setOption(\"tabSize\",e)},this.getTabSize=function(){return this.$tabSize},this.isTabStop=function(e){return this.$useSoftTabs&&e.column%this.$tabSize===0},this.setNavigateWithinSoftTabs=function(e){this.setOption(\"navigateWithinSoftTabs\",e)},this.getNavigateWithinSoftTabs=function(){return this.$navigateWithinSoftTabs},this.$overwrite=!1,this.setOverwrite=function(e){this.setOption(\"overwrite\",e)},this.getOverwrite=function(){return this.$overwrite},this.toggleOverwrite=function(){this.setOverwrite(!this.$overwrite)},this.addGutterDecoration=function(e,t){this.$decorations[e]||(this.$decorations[e]=\"\"),this.$decorations[e]+=\" \"+t,this._signal(\"changeBreakpoint\",{})},this.removeGutterDecoration=function(e,t){this.$decorations[e]=(this.$decorations[e]||\"\").replace(\" \"+t,\"\"),this._signal(\"changeBreakpoint\",{})},this.getBreakpoints=function(){return this.$breakpoints},this.setBreakpoints=function(e){this.$breakpoints=[];for(var t=0;t<e.length;t++)this.$breakpoints[e[t]]=\"ace_breakpoint\";this._signal(\"changeBreakpoint\",{})},this.clearBreakpoints=function(){this.$breakpoints=[],this._signal(\"changeBreakpoint\",{})},this.setBreakpoint=function(e,t){t===undefined&&(t=\"ace_breakpoint\"),t?this.$breakpoints[e]=t:delete this.$breakpoints[e],this._signal(\"changeBreakpoint\",{})},this.clearBreakpoint=function(e){delete this.$breakpoints[e],this._signal(\"changeBreakpoint\",{})},this.addMarker=function(e,t,n,r){var i=this.$markerId++,s={range:e,type:n||\"line\",renderer:typeof n==\"function\"?n:null,clazz:t,inFront:!!r,id:i};return r?(this.$frontMarkers[i]=s,this._signal(\"changeFrontMarker\")):(this.$backMarkers[i]=s,this._signal(\"changeBackMarker\")),i},this.addDynamicMarker=function(e,t){if(!e.update)return;var n=this.$markerId++;return e.id=n,e.inFront=!!t,t?(this.$frontMarkers[n]=e,this._signal(\"changeFrontMarker\")):(this.$backMarkers[n]=e,this._signal(\"changeBackMarker\")),e},this.removeMarker=function(e){var t=this.$frontMarkers[e]||this.$backMarkers[e];if(!t)return;var n=t.inFront?this.$frontMarkers:this.$backMarkers;delete n[e],this._signal(t.inFront?\"changeFrontMarker\":\"changeBackMarker\")},this.getMarkers=function(e){return e?this.$frontMarkers:this.$backMarkers},this.highlight=function(e){if(!this.$searchHighlight){var t=new p(null,\"ace_selected-word\",\"text\");this.$searchHighlight=this.addDynamicMarker(t)}this.$searchHighlight.setRegexp(e)},this.highlightLines=function(e,t,n,r){typeof t!=\"number\"&&(n=t,t=e),n||(n=\"ace_step\");var i=new l(e,0,t,Infinity);return i.id=this.addMarker(i,n,\"fullLine\",r),i},this.setAnnotations=function(e){this.$annotations=e,this._signal(\"changeAnnotation\",{})},this.getAnnotations=function(){return this.$annotations||[]},this.clearAnnotations=function(){this.setAnnotations([])},this.$detectNewLine=function(e){var t=e.match(/^.*?(\\r?\\n)/m);t?this.$autoNewLine=t[1]:this.$autoNewLine=\"\\n\"},this.getWordRange=function(e,t){var n=this.getLine(e),r=!1;t>0&&(r=!!n.charAt(t-1).match(this.tokenRe)),r||(r=!!n.charAt(t).match(this.tokenRe));if(r)var i=this.tokenRe;else if(/^\\s+$/.test(n.slice(t-1,t+1)))var i=/\\s/;else var i=this.nonTokenRe;var s=t;if(s>0){do s--;while(s>=0&&n.charAt(s).match(i));s++}var o=t;while(o<n.length&&n.charAt(o).match(i))o++;return new l(e,s,e,o)},this.getAWordRange=function(e,t){var n=this.getWordRange(e,t),r=this.getLine(n.end.row);while(r.charAt(n.end.column).match(/[ \\t]/))n.end.column+=1;return n},this.setNewLineMode=function(e){this.doc.setNewLineMode(e)},this.getNewLineMode=function(){return this.doc.getNewLineMode()},this.setUseWorker=function(e){this.setOption(\"useWorker\",e)},this.getUseWorker=function(){return this.$useWorker},this.onReloadTokenizer=function(e){var t=e.data;this.bgTokenizer.start(t.first),this._signal(\"tokenizerUpdate\",e)},this.$modes=o.$modes,this.$mode=null,this.$modeId=null,this.setMode=function(e,t){if(e&&typeof e==\"object\"){if(e.getTokenizer)return this.$onChangeMode(e);var n=e,r=n.path}else r=e||\"ace/mode/text\";this.$modes[\"ace/mode/text\"]||(this.$modes[\"ace/mode/text\"]=new f);if(this.$modes[r]&&!n){this.$onChangeMode(this.$modes[r]),t&&t();return}this.$modeId=r,o.loadModule([\"mode\",r],function(e){if(this.$modeId!==r)return t&&t();this.$modes[r]&&!n?this.$onChangeMode(this.$modes[r]):e&&e.Mode&&(e=new e.Mode(n),n||(this.$modes[r]=e,e.$id=r),this.$onChangeMode(e)),t&&t()}.bind(this)),this.$mode||this.$onChangeMode(this.$modes[\"ace/mode/text\"],!0)},this.$onChangeMode=function(e,t){t||(this.$modeId=e.$id);if(this.$mode===e)return;this.$mode=e,this.$stopWorker(),this.$useWorker&&this.$startWorker();var n=e.getTokenizer();if(n.addEventListener!==undefined){var r=this.onReloadTokenizer.bind(this);n.addEventListener(\"update\",r)}if(!this.bgTokenizer){this.bgTokenizer=new h(n);var i=this;this.bgTokenizer.addEventListener(\"update\",function(e){i._signal(\"tokenizerUpdate\",e)})}else this.bgTokenizer.setTokenizer(n);this.bgTokenizer.setDocument(this.getDocument()),this.tokenRe=e.tokenRe,this.nonTokenRe=e.nonTokenRe,t||(e.attachToSession&&e.attachToSession(this),this.$options.wrapMethod.set.call(this,this.$wrapMethod),this.$setFolding(e.foldingRules),this.bgTokenizer.start(0),this._emit(\"changeMode\"))},this.$stopWorker=function(){this.$worker&&(this.$worker.terminate(),this.$worker=null)},this.$startWorker=function(){try{this.$worker=this.$mode.createWorker(this)}catch(e){o.warn(\"Could not load worker\",e),this.$worker=null}},this.getMode=function(){return this.$mode},this.$scrollTop=0,this.setScrollTop=function(e){if(this.$scrollTop===e||isNaN(e))return;this.$scrollTop=e,this._signal(\"changeScrollTop\",e)},this.getScrollTop=function(){return this.$scrollTop},this.$scrollLeft=0,this.setScrollLeft=function(e){if(this.$scrollLeft===e||isNaN(e))return;this.$scrollLeft=e,this._signal(\"changeScrollLeft\",e)},this.getScrollLeft=function(){return this.$scrollLeft},this.getScreenWidth=function(){return this.$computeWidth(),this.lineWidgets?Math.max(this.getLineWidgetMaxWidth(),this.screenWidth):this.screenWidth},this.getLineWidgetMaxWidth=function(){if(this.lineWidgetsWidth!=null)return this.lineWidgetsWidth;var e=0;return this.lineWidgets.forEach(function(t){t&&t.screenWidth>e&&(e=t.screenWidth)}),this.lineWidgetWidth=e},this.$computeWidth=function(e){if(this.$modified||e){this.$modified=!1;if(this.$useWrapMode)return this.screenWidth=this.$wrapLimit;var t=this.doc.getAllLines(),n=this.$rowLengthCache,r=0,i=0,s=this.$foldData[i],o=s?s.start.row:Infinity,u=t.length;for(var a=0;a<u;a++){if(a>o){a=s.end.row+1;if(a>=u)break;s=this.$foldData[i++],o=s?s.start.row:Infinity}n[a]==null&&(n[a]=this.$getStringScreenWidth(t[a])[0]),n[a]>r&&(r=n[a])}this.screenWidth=r}},this.getLine=function(e){return this.doc.getLine(e)},this.getLines=function(e,t){return this.doc.getLines(e,t)},this.getLength=function(){return this.doc.getLength()},this.getTextRange=function(e){return this.doc.getTextRange(e||this.selection.getRange())},this.insert=function(e,t){return this.doc.insert(e,t)},this.remove=function(e){return this.doc.remove(e)},this.removeFullLines=function(e,t){return this.doc.removeFullLines(e,t)},this.undoChanges=function(e,t){if(!e.length)return;this.$fromUndo=!0;for(var n=e.length-1;n!=-1;n--){var r=e[n];r.action==\"insert\"||r.action==\"remove\"?this.doc.revertDelta(r):r.folds&&this.addFolds(r.folds)}!t&&this.$undoSelect&&(e.selectionBefore?this.selection.fromJSON(e.selectionBefore):this.selection.setRange(this.$getUndoSelection(e,!0))),this.$fromUndo=!1},this.redoChanges=function(e,t){if(!e.length)return;this.$fromUndo=!0;for(var n=0;n<e.length;n++){var r=e[n];(r.action==\"insert\"||r.action==\"remove\")&&this.doc.applyDelta(r)}!t&&this.$undoSelect&&(e.selectionAfter?this.selection.fromJSON(e.selectionAfter):this.selection.setRange(this.$getUndoSelection(e,!1))),this.$fromUndo=!1},this.setUndoSelect=function(e){this.$undoSelect=e},this.$getUndoSelection=function(e,t){function n(e){return t?e.action!==\"insert\":e.action===\"insert\"}var r,i,s;for(var o=0;o<e.length;o++){var u=e[o];if(!u.start)continue;if(!r){n(u)?(r=l.fromPoints(u.start,u.end),s=!0):(r=l.fromPoints(u.start,u.start),s=!1);continue}n(u)?(i=u.start,r.compare(i.row,i.column)==-1&&r.setStart(i),i=u.end,r.compare(i.row,i.column)==1&&r.setEnd(i),s=!0):(i=u.start,r.compare(i.row,i.column)==-1&&(r=l.fromPoints(u.start,u.start)),s=!1)}return r},this.replace=function(e,t){return this.doc.replace(e,t)},this.moveText=function(e,t,n){var r=this.getTextRange(e),i=this.getFoldsInRange(e),s=l.fromPoints(t,t);if(!n){this.remove(e);var o=e.start.row-e.end.row,u=o?-e.end.column:e.start.column-e.end.column;u&&(s.start.row==e.end.row&&s.start.column>e.end.column&&(s.start.column+=u),s.end.row==e.end.row&&s.end.column>e.end.column&&(s.end.column+=u)),o&&s.start.row>=e.end.row&&(s.start.row+=o,s.end.row+=o)}s.end=this.insert(s.start,r);if(i.length){var a=e.start,f=s.start,o=f.row-a.row,u=f.column-a.column;this.addFolds(i.map(function(e){return e=e.clone(),e.start.row==a.row&&(e.start.column+=u),e.end.row==a.row&&(e.end.column+=u),e.start.row+=o,e.end.row+=o,e}))}return s},this.indentRows=function(e,t,n){n=n.replace(/\\t/g,this.getTabString());for(var r=e;r<=t;r++)this.doc.insertInLine({row:r,column:0},n)},this.outdentRows=function(e){var t=e.collapseRows(),n=new l(0,0,0,0),r=this.getTabSize();for(var i=t.start.row;i<=t.end.row;++i){var s=this.getLine(i);n.start.row=i,n.end.row=i;for(var o=0;o<r;++o)if(s.charAt(o)!=\" \")break;o<r&&s.charAt(o)==\"\t\"?(n.start.column=o,n.end.column=o+1):(n.start.column=0,n.end.column=o),this.remove(n)}},this.$moveLines=function(e,t,n){e=this.getRowFoldStart(e),t=this.getRowFoldEnd(t);if(n<0){var r=this.getRowFoldStart(e+n);if(r<0)return 0;var i=r-e}else if(n>0){var r=this.getRowFoldEnd(t+n);if(r>this.doc.getLength()-1)return 0;var i=r-t}else{e=this.$clipRowToDocument(e),t=this.$clipRowToDocument(t);var i=t-e+1}var s=new l(e,0,t,Number.MAX_VALUE),o=this.getFoldsInRange(s).map(function(e){return e=e.clone(),e.start.row+=i,e.end.row+=i,e}),u=n==0?this.doc.getLines(e,t):this.doc.removeFullLines(e,t);return this.doc.insertFullLines(e+i,u),o.length&&this.addFolds(o),i},this.moveLinesUp=function(e,t){return this.$moveLines(e,t,-1)},this.moveLinesDown=function(e,t){return this.$moveLines(e,t,1)},this.duplicateLines=function(e,t){return this.$moveLines(e,t,0)},this.$clipRowToDocument=function(e){return Math.max(0,Math.min(e,this.doc.getLength()-1))},this.$clipColumnToRow=function(e,t){return t<0?0:Math.min(this.doc.getLine(e).length,t)},this.$clipPositionToDocument=function(e,t){t=Math.max(0,t);if(e<0)e=0,t=0;else{var n=this.doc.getLength();e>=n?(e=n-1,t=this.doc.getLine(n-1).length):t=Math.min(this.doc.getLine(e).length,t)}return{row:e,column:t}},this.$clipRangeToDocument=function(e){e.start.row<0?(e.start.row=0,e.start.column=0):e.start.column=this.$clipColumnToRow(e.start.row,e.start.column);var t=this.doc.getLength()-1;return e.end.row>t?(e.end.row=t,e.end.column=this.doc.getLine(t).length):e.end.column=this.$clipColumnToRow(e.end.row,e.end.column),e},this.$wrapLimit=80,this.$useWrapMode=!1,this.$wrapLimitRange={min:null,max:null},this.setUseWrapMode=function(e){if(e!=this.$useWrapMode){this.$useWrapMode=e,this.$modified=!0,this.$resetRowCache(0);if(e){var t=this.getLength();this.$wrapData=Array(t),this.$updateWrapData(0,t-1)}this._signal(\"changeWrapMode\")}},this.getUseWrapMode=function(){return this.$useWrapMode},this.setWrapLimitRange=function(e,t){if(this.$wrapLimitRange.min!==e||this.$wrapLimitRange.max!==t)this.$wrapLimitRange={min:e,max:t},this.$modified=!0,this.$bidiHandler.markAsDirty(),this.$useWrapMode&&this._signal(\"changeWrapMode\")},this.adjustWrapLimit=function(e,t){var n=this.$wrapLimitRange;n.max<0&&(n={min:t,max:t});var r=this.$constrainWrapLimit(e,n.min,n.max);return r!=this.$wrapLimit&&r>1?(this.$wrapLimit=r,this.$modified=!0,this.$useWrapMode&&(this.$updateWrapData(0,this.getLength()-1),this.$resetRowCache(0),this._signal(\"changeWrapLimit\")),!0):!1},this.$constrainWrapLimit=function(e,t,n){return t&&(e=Math.max(t,e)),n&&(e=Math.min(n,e)),e},this.getWrapLimit=function(){return this.$wrapLimit},this.setWrapLimit=function(e){this.setWrapLimitRange(e,e)},this.getWrapLimitRange=function(){return{min:this.$wrapLimitRange.min,max:this.$wrapLimitRange.max}},this.$updateInternalDataOnChange=function(e){var t=this.$useWrapMode,n=e.action,r=e.start,i=e.end,s=r.row,o=i.row,u=o-s,a=null;this.$updating=!0;if(u!=0)if(n===\"remove\"){this[t?\"$wrapData\":\"$rowLengthCache\"].splice(s,u);var f=this.$foldData;a=this.getFoldsInRange(e),this.removeFolds(a);var l=this.getFoldLine(i.row),c=0;if(l){l.addRemoveChars(i.row,i.column,r.column-i.column),l.shiftRow(-u);var h=this.getFoldLine(s);h&&h!==l&&(h.merge(l),l=h),c=f.indexOf(l)+1}for(c;c<f.length;c++){var l=f[c];l.start.row>=i.row&&l.shiftRow(-u)}o=s}else{var p=Array(u);p.unshift(s,0);var d=t?this.$wrapData:this.$rowLengthCache;d.splice.apply(d,p);var f=this.$foldData,l=this.getFoldLine(s),c=0;if(l){var v=l.range.compareInside(r.row,r.column);v==0?(l=l.split(r.row,r.column),l&&(l.shiftRow(u),l.addRemoveChars(o,0,i.column-r.column))):v==-1&&(l.addRemoveChars(s,0,i.column-r.column),l.shiftRow(u)),c=f.indexOf(l)+1}for(c;c<f.length;c++){var l=f[c];l.start.row>=s&&l.shiftRow(u)}}else{u=Math.abs(e.start.column-e.end.column),n===\"remove\"&&(a=this.getFoldsInRange(e),this.removeFolds(a),u=-u);var l=this.getFoldLine(s);l&&l.addRemoveChars(s,r.column,u)}return t&&this.$wrapData.length!=this.doc.getLength()&&console.error(\"doc.getLength() and $wrapData.length have to be the same!\"),this.$updating=!1,t?this.$updateWrapData(s,o):this.$updateRowLengthCache(s,o),a},this.$updateRowLengthCache=function(e,t,n){this.$rowLengthCache[e]=null,this.$rowLengthCache[t]=null},this.$updateWrapData=function(e,t){var r=this.doc.getAllLines(),i=this.getTabSize(),o=this.$wrapData,u=this.$wrapLimit,a,f,l=e;t=Math.min(t,r.length-1);while(l<=t)f=this.getFoldLine(l,f),f?(a=[],f.walk(function(e,t,i,o){var u;if(e!=null){u=this.$getDisplayTokens(e,a.length),u[0]=n;for(var f=1;f<u.length;f++)u[f]=s}else u=this.$getDisplayTokens(r[t].substring(o,i),a.length);a=a.concat(u)}.bind(this),f.end.row,r[f.end.row].length+1),o[f.start.row]=this.$computeWrapSplits(a,u,i),l=f.end.row+1):(a=this.$getDisplayTokens(r[l]),o[l]=this.$computeWrapSplits(a,u,i),l++)};var e=1,t=2,n=3,s=4,a=9,c=10,d=11,v=12;this.$computeWrapSplits=function(e,r,i){function g(){var t=0;if(m===0)return t;if(p)for(var n=0;n<e.length;n++){var r=e[n];if(r==c)t+=1;else{if(r!=d){if(r==v)continue;break}t+=i}}return h&&p!==!1&&(t+=i),Math.min(t,m)}function y(t){var n=t-f;for(var r=f;r<t;r++){var i=e[r];if(i===12||i===2)n-=1}o.length||(b=g(),o.indent=b),l+=n,o.push(l),f=t}if(e.length==0)return[];var o=[],u=e.length,f=0,l=0,h=this.$wrapAsCode,p=this.$indentedSoftWrap,m=r<=Math.max(2*i,8)||p===!1?0:Math.floor(r/2),b=0;while(u-f>r-b){var w=f+r-b;if(e[w-1]>=c&&e[w]>=c){y(w);continue}if(e[w]==n||e[w]==s){for(w;w!=f-1;w--)if(e[w]==n)break;if(w>f){y(w);continue}w=f+r;for(w;w<e.length;w++)if(e[w]!=s)break;if(w==e.length)break;y(w);continue}var E=Math.max(w-(r-(r>>2)),f-1);while(w>E&&e[w]<n)w--;if(h){while(w>E&&e[w]<n)w--;while(w>E&&e[w]==a)w--}else while(w>E&&e[w]<c)w--;if(w>E){y(++w);continue}w=f+r,e[w]==t&&w--,y(w-b)}return o},this.$getDisplayTokens=function(n,r){var i=[],s;r=r||0;for(var o=0;o<n.length;o++){var u=n.charCodeAt(o);if(u==9){s=this.getScreenTabSize(i.length+r),i.push(d);for(var f=1;f<s;f++)i.push(v)}else u==32?i.push(c):u>39&&u<48||u>57&&u<64?i.push(a):u>=4352&&m(u)?i.push(e,t):i.push(e)}return i},this.$getStringScreenWidth=function(e,t,n){if(t==0)return[0,0];t==null&&(t=Infinity),n=n||0;var r,i;for(i=0;i<e.length;i++){r=e.charCodeAt(i),r==9?n+=this.getScreenTabSize(n):r>=4352&&m(r)?n+=2:n+=1;if(n>t)break}return[n,i]},this.lineWidgets=null,this.getRowLength=function(e){if(this.lineWidgets)var t=this.lineWidgets[e]&&this.lineWidgets[e].rowCount||0;else t=0;return!this.$useWrapMode||!this.$wrapData[e]?1+t:this.$wrapData[e].length+1+t},this.getRowLineCount=function(e){return!this.$useWrapMode||!this.$wrapData[e]?1:this.$wrapData[e].length+1},this.getRowWrapIndent=function(e){if(this.$useWrapMode){var t=this.screenToDocumentPosition(e,Number.MAX_VALUE),n=this.$wrapData[t.row];return n.length&&n[0]<t.column?n.indent:0}return 0},this.getScreenLastRowColumn=function(e){var t=this.screenToDocumentPosition(e,Number.MAX_VALUE);return this.documentToScreenColumn(t.row,t.column)},this.getDocumentLastRowColumn=function(e,t){var n=this.documentToScreenRow(e,t);return this.getScreenLastRowColumn(n)},this.getDocumentLastRowColumnPosition=function(e,t){var n=this.documentToScreenRow(e,t);return this.screenToDocumentPosition(n,Number.MAX_VALUE/10)},this.getRowSplitData=function(e){return this.$useWrapMode?this.$wrapData[e]:undefined},this.getScreenTabSize=function(e){return this.$tabSize-e%this.$tabSize},this.screenToDocumentRow=function(e,t){return this.screenToDocumentPosition(e,t).row},this.screenToDocumentColumn=function(e,t){return this.screenToDocumentPosition(e,t).column},this.screenToDocumentPosition=function(e,t,n){if(e<0)return{row:0,column:0};var r,i=0,s=0,o,u=0,a=0,f=this.$screenRowCache,l=this.$getRowCacheIndex(f,e),c=f.length;if(c&&l>=0)var u=f[l],i=this.$docRowCache[l],h=e>f[c-1];else var h=!c;var p=this.getLength()-1,d=this.getNextFoldLine(i),v=d?d.start.row:Infinity;while(u<=e){a=this.getRowLength(i);if(u+a>e||i>=p)break;u+=a,i++,i>v&&(i=d.end.row+1,d=this.getNextFoldLine(i,d),v=d?d.start.row:Infinity),h&&(this.$docRowCache.push(i),this.$screenRowCache.push(u))}if(d&&d.start.row<=i)r=this.getFoldDisplayLine(d),i=d.start.row;else{if(u+a<=e||i>p)return{row:p,column:this.getLine(p).length};r=this.getLine(i),d=null}var m=0,g=Math.floor(e-u);if(this.$useWrapMode){var y=this.$wrapData[i];y&&(o=y[g],g>0&&y.length&&(m=y.indent,s=y[g-1]||y[y.length-1],r=r.substring(s)))}return n!==undefined&&this.$bidiHandler.isBidiRow(u+g,i,g)&&(t=this.$bidiHandler.offsetToCol(n)),s+=this.$getStringScreenWidth(r,t-m)[1],this.$useWrapMode&&s>=o&&(s=o-1),d?d.idxToPosition(s):{row:i,column:s}},this.documentToScreenPosition=function(e,t){if(typeof t==\"undefined\")var n=this.$clipPositionToDocument(e.row,e.column);else n=this.$clipPositionToDocument(e,t);e=n.row,t=n.column;var r=0,i=null,s=null;s=this.getFoldAt(e,t,1),s&&(e=s.start.row,t=s.start.column);var o,u=0,a=this.$docRowCache,f=this.$getRowCacheIndex(a,e),l=a.length;if(l&&f>=0)var u=a[f],r=this.$screenRowCache[f],c=e>a[l-1];else var c=!l;var h=this.getNextFoldLine(u),p=h?h.start.row:Infinity;while(u<e){if(u>=p){o=h.end.row+1;if(o>e)break;h=this.getNextFoldLine(o,h),p=h?h.start.row:Infinity}else o=u+1;r+=this.getRowLength(u),u=o,c&&(this.$docRowCache.push(u),this.$screenRowCache.push(r))}var d=\"\";h&&u>=p?(d=this.getFoldDisplayLine(h,e,t),i=h.start.row):(d=this.getLine(e).substring(0,t),i=e);var v=0;if(this.$useWrapMode){var m=this.$wrapData[i];if(m){var g=0;while(d.length>=m[g])r++,g++;d=d.substring(m[g-1]||0,d.length),v=g>0?m.indent:0}}return{row:r,column:v+this.$getStringScreenWidth(d)[0]}},this.documentToScreenColumn=function(e,t){return this.documentToScreenPosition(e,t).column},this.documentToScreenRow=function(e,t){return this.documentToScreenPosition(e,t).row},this.getScreenLength=function(){var e=0,t=null;if(!this.$useWrapMode){e=this.getLength();var n=this.$foldData;for(var r=0;r<n.length;r++)t=n[r],e-=t.end.row-t.start.row}else{var i=this.$wrapData.length,s=0,r=0,t=this.$foldData[r++],o=t?t.start.row:Infinity;while(s<i){var u=this.$wrapData[s];e+=u?u.length+1:1,s++,s>o&&(s=t.end.row+1,t=this.$foldData[r++],o=t?t.start.row:Infinity)}}return this.lineWidgets&&(e+=this.$getWidgetScreenLength()),e},this.$setFontMetrics=function(e){if(!this.$enableVarChar)return;this.$getStringScreenWidth=function(t,n,r){if(n===0)return[0,0];n||(n=Infinity),r=r||0;var i,s;for(s=0;s<t.length;s++){i=t.charAt(s),i===\"\t\"?r+=this.getScreenTabSize(r):r+=e.getCharacterWidth(i);if(r>n)break}return[r,s]}},this.destroy=function(){this.bgTokenizer&&(this.bgTokenizer.setDocument(null),this.bgTokenizer=null),this.$stopWorker()},this.isFullWidth=m}.call(d.prototype),e(\"./edit_session/folding\").Folding.call(d.prototype),e(\"./edit_session/bracket_match\").BracketMatch.call(d.prototype),o.defineOptions(d.prototype,\"session\",{wrap:{set:function(e){!e||e==\"off\"?e=!1:e==\"free\"?e=!0:e==\"printMargin\"?e=-1:typeof e==\"string\"&&(e=parseInt(e,10)||!1);if(this.$wrap==e)return;this.$wrap=e;if(!e)this.setUseWrapMode(!1);else{var t=typeof e==\"number\"?e:null;this.setWrapLimitRange(t,t),this.setUseWrapMode(!0)}},get:function(){return this.getUseWrapMode()?this.$wrap==-1?\"printMargin\":this.getWrapLimitRange().min?this.$wrap:\"free\":\"off\"},handlesSet:!0},wrapMethod:{set:function(e){e=e==\"auto\"?this.$mode.type!=\"text\":e!=\"text\",e!=this.$wrapAsCode&&(this.$wrapAsCode=e,this.$useWrapMode&&(this.$useWrapMode=!1,this.setUseWrapMode(!0)))},initialValue:\"auto\"},indentedSoftWrap:{set:function(){this.$useWrapMode&&(this.$useWrapMode=!1,this.setUseWrapMode(!0))},initialValue:!0},firstLineNumber:{set:function(){this._signal(\"changeBreakpoint\")},initialValue:1},useWorker:{set:function(e){this.$useWorker=e,this.$stopWorker(),e&&this.$startWorker()},initialValue:!0},useSoftTabs:{initialValue:!0},tabSize:{set:function(e){e=parseInt(e);if(isNaN(e)||this.$tabSize===e)return;this.$modified=!0,this.$rowLengthCache=[],this.$tabSize=e,this._signal(\"changeTabSize\")},initialValue:4,handlesSet:!0},navigateWithinSoftTabs:{initialValue:!1},foldStyle:{set:function(e){this.setFoldStyle(e)},handlesSet:!0},overwrite:{set:function(e){this._signal(\"changeOverwrite\")},initialValue:!1},newLineMode:{set:function(e){this.doc.setNewLineMode(e)},get:function(){return this.doc.getNewLineMode()},handlesSet:!0},mode:{set:function(e){this.setMode(e)},get:function(){return this.$modeId},handlesSet:!0}}),t.EditSession=d}),ace.define(\"ace/search\",[\"require\",\"exports\",\"module\",\"ace/lib/lang\",\"ace/lib/oop\",\"ace/range\"],function(e,t,n){\"use strict\";function u(e,t){function n(e){return/\\w/.test(e)||t.regExp?\"\\\\b\":\"\"}return n(e[0])+e+n(e[e.length-1])}var r=e(\"./lib/lang\"),i=e(\"./lib/oop\"),s=e(\"./range\").Range,o=function(){this.$options={}};(function(){this.set=function(e){return i.mixin(this.$options,e),this},this.getOptions=function(){return r.copyObject(this.$options)},this.setOptions=function(e){this.$options=e},this.find=function(e){var t=this.$options,n=this.$matchIterator(e,t);if(!n)return!1;var r=null;return n.forEach(function(e,n,i,o){return r=new s(e,n,i,o),n==o&&t.start&&t.start.start&&t.skipCurrent!=0&&r.isEqual(t.start)?(r=null,!1):!0}),r},this.findAll=function(e){var t=this.$options;if(!t.needle)return[];this.$assembleRegExp(t);var n=t.range,i=n?e.getLines(n.start.row,n.end.row):e.doc.getAllLines(),o=[],u=t.re;if(t.$isMultiLine){var a=u.length,f=i.length-a,l;e:for(var c=u.offset||0;c<=f;c++){for(var h=0;h<a;h++)if(i[c+h].search(u[h])==-1)continue e;var p=i[c],d=i[c+a-1],v=p.length-p.match(u[0])[0].length,m=d.match(u[a-1])[0].length;if(l&&l.end.row===c&&l.end.column>v)continue;o.push(l=new s(c,v,c+a-1,m)),a>2&&(c=c+a-2)}}else for(var g=0;g<i.length;g++){var y=r.getMatchOffsets(i[g],u);for(var h=0;h<y.length;h++){var b=y[h];o.push(new s(g,b.offset,g,b.offset+b.length))}}if(n){var w=n.start.column,E=n.start.column,g=0,h=o.length-1;while(g<h&&o[g].start.column<w&&o[g].start.row==n.start.row)g++;while(g<h&&o[h].end.column>E&&o[h].end.row==n.end.row)h--;o=o.slice(g,h+1);for(g=0,h=o.length;g<h;g++)o[g].start.row+=n.start.row,o[g].end.row+=n.start.row}return o},this.replace=function(e,t){var n=this.$options,r=this.$assembleRegExp(n);if(n.$isMultiLine)return t;if(!r)return;var i=r.exec(e);if(!i||i[0].length!=e.length)return null;t=e.replace(r,t);if(n.preserveCase){t=t.split(\"\");for(var s=Math.min(e.length,e.length);s--;){var o=e[s];o&&o.toLowerCase()!=o?t[s]=t[s].toUpperCase():t[s]=t[s].toLowerCase()}t=t.join(\"\")}return t},this.$assembleRegExp=function(e,t){if(e.needle instanceof RegExp)return e.re=e.needle;var n=e.needle;if(!e.needle)return e.re=!1;e.regExp||(n=r.escapeRegExp(n)),e.wholeWord&&(n=u(n,e));var i=e.caseSensitive?\"gm\":\"gmi\";e.$isMultiLine=!t&&/[\\n\\r]/.test(n);if(e.$isMultiLine)return e.re=this.$assembleMultilineRegExp(n,i);try{var s=new RegExp(n,i)}catch(o){s=!1}return e.re=s},this.$assembleMultilineRegExp=function(e,t){var n=e.replace(/\\r\\n|\\r|\\n/g,\"$\\n^\").split(\"\\n\"),r=[];for(var i=0;i<n.length;i++)try{r.push(new RegExp(n[i],t))}catch(s){return!1}return r},this.$matchIterator=function(e,t){var n=this.$assembleRegExp(t);if(!n)return!1;var r=t.backwards==1,i=t.skipCurrent!=0,s=t.range,o=t.start;o||(o=s?s[r?\"end\":\"start\"]:e.selection.getRange()),o.start&&(o=o[i!=r?\"end\":\"start\"]);var u=s?s.start.row:0,a=s?s.end.row:e.getLength()-1;if(r)var f=function(e){var n=o.row;if(c(n,o.column,e))return;for(n--;n>=u;n--)if(c(n,Number.MAX_VALUE,e))return;if(t.wrap==0)return;for(n=a,u=o.row;n>=u;n--)if(c(n,Number.MAX_VALUE,e))return};else var f=function(e){var n=o.row;if(c(n,o.column,e))return;for(n+=1;n<=a;n++)if(c(n,0,e))return;if(t.wrap==0)return;for(n=u,a=o.row;n<=a;n++)if(c(n,0,e))return};if(t.$isMultiLine)var l=n.length,c=function(t,i,s){var o=r?t-l+1:t;if(o<0)return;var u=e.getLine(o),a=u.search(n[0]);if(!r&&a<i||a===-1)return;for(var f=1;f<l;f++){u=e.getLine(o+f);if(u.search(n[f])==-1)return}var c=u.match(n[l-1])[0].length;if(r&&c>i)return;if(s(o,a,o+l-1,c))return!0};else if(r)var c=function(t,r,i){var s=e.getLine(t),o=[],u,a=0;n.lastIndex=0;while(u=n.exec(s)){var f=u[0].length;a=u.index;if(!f){if(a>=s.length)break;n.lastIndex=a+=1}if(u.index+f>r)break;o.push(u.index,f)}for(var l=o.length-1;l>=0;l-=2){var c=o[l-1],f=o[l];if(i(t,c,t,c+f))return!0}};else var c=function(t,r,i){var s=e.getLine(t),o,u;n.lastIndex=r;while(u=n.exec(s)){var a=u[0].length;o=u.index;if(i(t,o,t,o+a))return!0;if(!a){n.lastIndex=o+=1;if(o>=s.length)return!1}}};return{forEach:f}}}).call(o.prototype),t.Search=o}),ace.define(\"ace/keyboard/hash_handler\",[\"require\",\"exports\",\"module\",\"ace/lib/keys\",\"ace/lib/useragent\"],function(e,t,n){\"use strict\";function o(e,t){this.platform=t||(i.isMac?\"mac\":\"win\"),this.commands={},this.commandKeyBinding={},this.addCommands(e),this.$singleCommand=!0}function u(e,t){o.call(this,e,t),this.$singleCommand=!1}var r=e(\"../lib/keys\"),i=e(\"../lib/useragent\"),s=r.KEY_MODS;u.prototype=o.prototype,function(){function e(e){return typeof e==\"object\"&&e.bindKey&&e.bindKey.position||(e.isDefault?-100:0)}this.addCommand=function(e){this.commands[e.name]&&this.removeCommand(e),this.commands[e.name]=e,e.bindKey&&this._buildKeyHash(e)},this.removeCommand=function(e,t){var n=e&&(typeof e==\"string\"?e:e.name);e=this.commands[n],t||delete this.commands[n];var r=this.commandKeyBinding;for(var i in r){var s=r[i];if(s==e)delete r[i];else if(Array.isArray(s)){var o=s.indexOf(e);o!=-1&&(s.splice(o,1),s.length==1&&(r[i]=s[0]))}}},this.bindKey=function(e,t,n){typeof e==\"object\"&&e&&(n==undefined&&(n=e.position),e=e[this.platform]);if(!e)return;if(typeof t==\"function\")return this.addCommand({exec:t,bindKey:e,name:t.name||e});e.split(\"|\").forEach(function(e){var r=\"\";if(e.indexOf(\" \")!=-1){var i=e.split(/\\s+/);e=i.pop(),i.forEach(function(e){var t=this.parseKeys(e),n=s[t.hashId]+t.key;r+=(r?\" \":\"\")+n,this._addCommandToBinding(r,\"chainKeys\")},this),r+=\" \"}var o=this.parseKeys(e),u=s[o.hashId]+o.key;this._addCommandToBinding(r+u,t,n)},this)},this._addCommandToBinding=function(t,n,r){var i=this.commandKeyBinding,s;if(!n)delete i[t];else if(!i[t]||this.$singleCommand)i[t]=n;else{Array.isArray(i[t])?(s=i[t].indexOf(n))!=-1&&i[t].splice(s,1):i[t]=[i[t]],typeof r!=\"number\"&&(r=e(n));var o=i[t];for(s=0;s<o.length;s++){var u=o[s],a=e(u);if(a>r)break}o.splice(s,0,n)}},this.addCommands=function(e){e&&Object.keys(e).forEach(function(t){var n=e[t];if(!n)return;if(typeof n==\"string\")return this.bindKey(n,t);typeof n==\"function\"&&(n={exec:n});if(typeof n!=\"object\")return;n.name||(n.name=t),this.addCommand(n)},this)},this.removeCommands=function(e){Object.keys(e).forEach(function(t){this.removeCommand(e[t])},this)},this.bindKeys=function(e){Object.keys(e).forEach(function(t){this.bindKey(t,e[t])},this)},this._buildKeyHash=function(e){this.bindKey(e.bindKey,e)},this.parseKeys=function(e){var t=e.toLowerCase().split(/[\\-\\+]([\\-\\+])?/).filter(function(e){return e}),n=t.pop(),i=r[n];if(r.FUNCTION_KEYS[i])n=r.FUNCTION_KEYS[i].toLowerCase();else{if(!t.length)return{key:n,hashId:-1};if(t.length==1&&t[0]==\"shift\")return{key:n.toUpperCase(),hashId:-1}}var s=0;for(var o=t.length;o--;){var u=r.KEY_MODS[t[o]];if(u==null)return typeof console!=\"undefined\"&&console.error(\"invalid modifier \"+t[o]+\" in \"+e),!1;s|=u}return{key:n,hashId:s}},this.findKeyCommand=function(t,n){var r=s[t]+n;return this.commandKeyBinding[r]},this.handleKeyboard=function(e,t,n,r){if(r<0)return;var i=s[t]+n,o=this.commandKeyBinding[i];e.$keyChain&&(e.$keyChain+=\" \"+i,o=this.commandKeyBinding[e.$keyChain]||o);if(o)if(o==\"chainKeys\"||o[o.length-1]==\"chainKeys\")return e.$keyChain=e.$keyChain||i,{command:\"null\"};if(e.$keyChain)if(!!t&&t!=4||n.length!=1){if(t==-1||r>0)e.$keyChain=\"\"}else e.$keyChain=e.$keyChain.slice(0,-i.length-1);return{command:o}},this.getStatusText=function(e,t){return t.$keyChain||\"\"}}.call(o.prototype),t.HashHandler=o,t.MultiHashHandler=u}),ace.define(\"ace/commands/command_manager\",[\"require\",\"exports\",\"module\",\"ace/lib/oop\",\"ace/keyboard/hash_handler\",\"ace/lib/event_emitter\"],function(e,t,n){\"use strict\";var r=e(\"../lib/oop\"),i=e(\"../keyboard/hash_handler\").MultiHashHandler,s=e(\"../lib/event_emitter\").EventEmitter,o=function(e,t){i.call(this,t,e),this.byName=this.commands,this.setDefaultHandler(\"exec\",function(e){return e.command.exec(e.editor,e.args||{})})};r.inherits(o,i),function(){r.implement(this,s),this.exec=function(e,t,n){if(Array.isArray(e)){for(var r=e.length;r--;)if(this.exec(e[r],t,n))return!0;return!1}typeof e==\"string\"&&(e=this.commands[e]);if(!e)return!1;if(t&&t.$readOnly&&!e.readOnly)return!1;if(this.$checkCommandState!=0&&e.isAvailable&&!e.isAvailable(t))return!1;var i={editor:t,command:e,args:n};return i.returnValue=this._emit(\"exec\",i),this._signal(\"afterExec\",i),i.returnValue===!1?!1:!0},this.toggleRecording=function(e){if(this.$inReplay)return;return e&&e._emit(\"changeStatus\"),this.recording?(this.macro.pop(),this.removeEventListener(\"exec\",this.$addCommandToMacro),this.macro.length||(this.macro=this.oldMacro),this.recording=!1):(this.$addCommandToMacro||(this.$addCommandToMacro=function(e){this.macro.push([e.command,e.args])}.bind(this)),this.oldMacro=this.macro,this.macro=[],this.on(\"exec\",this.$addCommandToMacro),this.recording=!0)},this.replay=function(e){if(this.$inReplay||!this.macro)return;if(this.recording)return this.toggleRecording(e);try{this.$inReplay=!0,this.macro.forEach(function(t){typeof t==\"string\"?this.exec(t,e):this.exec(t[0],e,t[1])},this)}finally{this.$inReplay=!1}},this.trimMacro=function(e){return e.map(function(e){return typeof e[0]!=\"string\"&&(e[0]=e[0].name),e[1]||(e=e[0]),e})}}.call(o.prototype),t.CommandManager=o}),ace.define(\"ace/commands/default_commands\",[\"require\",\"exports\",\"module\",\"ace/lib/lang\",\"ace/config\",\"ace/range\"],function(e,t,n){\"use strict\";function o(e,t){return{win:e,mac:t}}var r=e(\"../lib/lang\"),i=e(\"../config\"),s=e(\"../range\").Range;t.commands=[{name:\"showSettingsMenu\",bindKey:o(\"Ctrl-,\",\"Command-,\"),exec:function(e){i.loadModule(\"ace/ext/settings_menu\",function(t){t.init(e),e.showSettingsMenu()})},readOnly:!0},{name:\"goToNextError\",bindKey:o(\"Alt-E\",\"F4\"),exec:function(e){i.loadModule(\"./ext/error_marker\",function(t){t.showErrorMarker(e,1)})},scrollIntoView:\"animate\",readOnly:!0},{name:\"goToPreviousError\",bindKey:o(\"Alt-Shift-E\",\"Shift-F4\"),exec:function(e){i.loadModule(\"./ext/error_marker\",function(t){t.showErrorMarker(e,-1)})},scrollIntoView:\"animate\",readOnly:!0},{name:\"selectall\",description:\"Select all\",bindKey:o(\"Ctrl-A\",\"Command-A\"),exec:function(e){e.selectAll()},readOnly:!0},{name:\"centerselection\",description:\"Center selection\",bindKey:o(null,\"Ctrl-L\"),exec:function(e){e.centerSelection()},readOnly:!0},{name:\"gotoline\",description:\"Go to line...\",bindKey:o(\"Ctrl-L\",\"Command-L\"),exec:function(e,t){typeof t==\"number\"&&!isNaN(t)&&e.gotoLine(t),e.prompt({$type:\"gotoLine\"})},readOnly:!0},{name:\"fold\",bindKey:o(\"Alt-L|Ctrl-F1\",\"Command-Alt-L|Command-F1\"),exec:function(e){e.session.toggleFold(!1)},multiSelectAction:\"forEach\",scrollIntoView:\"center\",readOnly:!0},{name:\"unfold\",bindKey:o(\"Alt-Shift-L|Ctrl-Shift-F1\",\"Command-Alt-Shift-L|Command-Shift-F1\"),exec:function(e){e.session.toggleFold(!0)},multiSelectAction:\"forEach\",scrollIntoView:\"center\",readOnly:!0},{name:\"toggleFoldWidget\",bindKey:o(\"F2\",\"F2\"),exec:function(e){e.session.toggleFoldWidget()},multiSelectAction:\"forEach\",scrollIntoView:\"center\",readOnly:!0},{name:\"toggleParentFoldWidget\",bindKey:o(\"Alt-F2\",\"Alt-F2\"),exec:function(e){e.session.toggleFoldWidget(!0)},multiSelectAction:\"forEach\",scrollIntoView:\"center\",readOnly:!0},{name:\"foldall\",description:\"Fold all\",bindKey:o(null,\"Ctrl-Command-Option-0\"),exec:function(e){e.session.foldAll()},scrollIntoView:\"center\",readOnly:!0},{name:\"foldOther\",description:\"Fold other\",bindKey:o(\"Alt-0\",\"Command-Option-0\"),exec:function(e){e.session.foldAll(),e.session.unfold(e.selection.getAllRanges())},scrollIntoView:\"center\",readOnly:!0},{name:\"unfoldall\",description:\"Unfold all\",bindKey:o(\"Alt-Shift-0\",\"Command-Option-Shift-0\"),exec:function(e){e.session.unfold()},scrollIntoView:\"center\",readOnly:!0},{name:\"findnext\",description:\"Find next\",bindKey:o(\"Ctrl-K\",\"Command-G\"),exec:function(e){e.findNext()},multiSelectAction:\"forEach\",scrollIntoView:\"center\",readOnly:!0},{name:\"findprevious\",description:\"Find previous\",bindKey:o(\"Ctrl-Shift-K\",\"Command-Shift-G\"),exec:function(e){e.findPrevious()},multiSelectAction:\"forEach\",scrollIntoView:\"center\",readOnly:!0},{name:\"selectOrFindNext\",description:\"Select or find next\",bindKey:o(\"Alt-K\",\"Ctrl-G\"),exec:function(e){e.selection.isEmpty()?e.selection.selectWord():e.findNext()},readOnly:!0},{name:\"selectOrFindPrevious\",description:\"Select or find previous\",bindKey:o(\"Alt-Shift-K\",\"Ctrl-Shift-G\"),exec:function(e){e.selection.isEmpty()?e.selection.selectWord():e.findPrevious()},readOnly:!0},{name:\"find\",description:\"Find\",bindKey:o(\"Ctrl-F\",\"Command-F\"),exec:function(e){i.loadModule(\"ace/ext/searchbox\",function(t){t.Search(e)})},readOnly:!0},{name:\"overwrite\",description:\"Overwrite\",bindKey:\"Insert\",exec:function(e){e.toggleOverwrite()},readOnly:!0},{name:\"selecttostart\",description:\"Select to start\",bindKey:o(\"Ctrl-Shift-Home\",\"Command-Shift-Home|Command-Shift-Up\"),exec:function(e){e.getSelection().selectFileStart()},multiSelectAction:\"forEach\",readOnly:!0,scrollIntoView:\"animate\",aceCommandGroup:\"fileJump\"},{name:\"gotostart\",description:\"Go to start\",bindKey:o(\"Ctrl-Home\",\"Command-Home|Command-Up\"),exec:function(e){e.navigateFileStart()},multiSelectAction:\"forEach\",readOnly:!0,scrollIntoView:\"animate\",aceCommandGroup:\"fileJump\"},{name:\"selectup\",description:\"Select up\",bindKey:o(\"Shift-Up\",\"Shift-Up|Ctrl-Shift-P\"),exec:function(e){e.getSelection().selectUp()},multiSelectAction:\"forEach\",scrollIntoView:\"cursor\",readOnly:!0},{name:\"golineup\",description:\"Go line up\",bindKey:o(\"Up\",\"Up|Ctrl-P\"),exec:function(e,t){e.navigateUp(t.times)},multiSelectAction:\"forEach\",scrollIntoView:\"cursor\",readOnly:!0},{name:\"selecttoend\",description:\"Select to end\",bindKey:o(\"Ctrl-Shift-End\",\"Command-Shift-End|Command-Shift-Down\"),exec:function(e){e.getSelection().selectFileEnd()},multiSelectAction:\"forEach\",readOnly:!0,scrollIntoView:\"animate\",aceCommandGroup:\"fileJump\"},{name:\"gotoend\",description:\"Go to end\",bindKey:o(\"Ctrl-End\",\"Command-End|Command-Down\"),exec:function(e){e.navigateFileEnd()},multiSelectAction:\"forEach\",readOnly:!0,scrollIntoView:\"animate\",aceCommandGroup:\"fileJump\"},{name:\"selectdown\",description:\"Select down\",bindKey:o(\"Shift-Down\",\"Shift-Down|Ctrl-Shift-N\"),exec:function(e){e.getSelection().selectDown()},multiSelectAction:\"forEach\",scrollIntoView:\"cursor\",readOnly:!0},{name:\"golinedown\",description:\"Go line down\",bindKey:o(\"Down\",\"Down|Ctrl-N\"),exec:function(e,t){e.navigateDown(t.times)},multiSelectAction:\"forEach\",scrollIntoView:\"cursor\",readOnly:!0},{name:\"selectwordleft\",description:\"Select word left\",bindKey:o(\"Ctrl-Shift-Left\",\"Option-Shift-Left\"),exec:function(e){e.getSelection().selectWordLeft()},multiSelectAction:\"forEach\",scrollIntoView:\"cursor\",readOnly:!0},{name:\"gotowordleft\",description:\"Go to word left\",bindKey:o(\"Ctrl-Left\",\"Option-Left\"),exec:function(e){e.navigateWordLeft()},multiSelectAction:\"forEach\",scrollIntoView:\"cursor\",readOnly:!0},{name:\"selecttolinestart\",description:\"Select to line start\",bindKey:o(\"Alt-Shift-Left\",\"Command-Shift-Left|Ctrl-Shift-A\"),exec:function(e){e.getSelection().selectLineStart()},multiSelectAction:\"forEach\",scrollIntoView:\"cursor\",readOnly:!0},{name:\"gotolinestart\",description:\"Go to line start\",bindKey:o(\"Alt-Left|Home\",\"Command-Left|Home|Ctrl-A\"),exec:function(e){e.navigateLineStart()},multiSelectAction:\"forEach\",scrollIntoView:\"cursor\",readOnly:!0},{name:\"selectleft\",description:\"Select left\",bindKey:o(\"Shift-Left\",\"Shift-Left|Ctrl-Shift-B\"),exec:function(e){e.getSelection().selectLeft()},multiSelectAction:\"forEach\",scrollIntoView:\"cursor\",readOnly:!0},{name:\"gotoleft\",description:\"Go to left\",bindKey:o(\"Left\",\"Left|Ctrl-B\"),exec:function(e,t){e.navigateLeft(t.times)},multiSelectAction:\"forEach\",scrollIntoView:\"cursor\",readOnly:!0},{name:\"selectwordright\",description:\"Select word right\",bindKey:o(\"Ctrl-Shift-Right\",\"Option-Shift-Right\"),exec:function(e){e.getSelection().selectWordRight()},multiSelectAction:\"forEach\",scrollIntoView:\"cursor\",readOnly:!0},{name:\"gotowordright\",description:\"Go to word right\",bindKey:o(\"Ctrl-Right\",\"Option-Right\"),exec:function(e){e.navigateWordRight()},multiSelectAction:\"forEach\",scrollIntoView:\"cursor\",readOnly:!0},{name:\"selecttolineend\",description:\"Select to line end\",bindKey:o(\"Alt-Shift-Right\",\"Command-Shift-Right|Shift-End|Ctrl-Shift-E\"),exec:function(e){e.getSelection().selectLineEnd()},multiSelectAction:\"forEach\",scrollIntoView:\"cursor\",readOnly:!0},{name:\"gotolineend\",description:\"Go to line end\",bindKey:o(\"Alt-Right|End\",\"Command-Right|End|Ctrl-E\"),exec:function(e){e.navigateLineEnd()},multiSelectAction:\"forEach\",scrollIntoView:\"cursor\",readOnly:!0},{name:\"selectright\",description:\"Select right\",bindKey:o(\"Shift-Right\",\"Shift-Right\"),exec:function(e){e.getSelection().selectRight()},multiSelectAction:\"forEach\",scrollIntoView:\"cursor\",readOnly:!0},{name:\"gotoright\",description:\"Go to right\",bindKey:o(\"Right\",\"Right|Ctrl-F\"),exec:function(e,t){e.navigateRight(t.times)},multiSelectAction:\"forEach\",scrollIntoView:\"cursor\",readOnly:!0},{name:\"selectpagedown\",description:\"Select page down\",bindKey:\"Shift-PageDown\",exec:function(e){e.selectPageDown()},readOnly:!0},{name:\"pagedown\",description:\"Page down\",bindKey:o(null,\"Option-PageDown\"),exec:function(e){e.scrollPageDown()},readOnly:!0},{name:\"gotopagedown\",description:\"Go to page down\",bindKey:o(\"PageDown\",\"PageDown|Ctrl-V\"),exec:function(e){e.gotoPageDown()},readOnly:!0},{name:\"selectpageup\",description:\"Select page up\",bindKey:\"Shift-PageUp\",exec:function(e){e.selectPageUp()},readOnly:!0},{name:\"pageup\",description:\"Page up\",bindKey:o(null,\"Option-PageUp\"),exec:function(e){e.scrollPageUp()},readOnly:!0},{name:\"gotopageup\",description:\"Go to page up\",bindKey:\"PageUp\",exec:function(e){e.gotoPageUp()},readOnly:!0},{name:\"scrollup\",description:\"Scroll up\",bindKey:o(\"Ctrl-Up\",null),exec:function(e){e.renderer.scrollBy(0,-2*e.renderer.layerConfig.lineHeight)},readOnly:!0},{name:\"scrolldown\",description:\"Scroll down\",bindKey:o(\"Ctrl-Down\",null),exec:function(e){e.renderer.scrollBy(0,2*e.renderer.layerConfig.lineHeight)},readOnly:!0},{name:\"selectlinestart\",description:\"Select line start\",bindKey:\"Shift-Home\",exec:function(e){e.getSelection().selectLineStart()},multiSelectAction:\"forEach\",scrollIntoView:\"cursor\",readOnly:!0},{name:\"selectlineend\",description:\"Select line end\",bindKey:\"Shift-End\",exec:function(e){e.getSelection().selectLineEnd()},multiSelectAction:\"forEach\",scrollIntoView:\"cursor\",readOnly:!0},{name:\"togglerecording\",description:\"Toggle recording\",bindKey:o(\"Ctrl-Alt-E\",\"Command-Option-E\"),exec:function(e){e.commands.toggleRecording(e)},readOnly:!0},{name:\"replaymacro\",description:\"Replay macro\",bindKey:o(\"Ctrl-Shift-E\",\"Command-Shift-E\"),exec:function(e){e.commands.replay(e)},readOnly:!0},{name:\"jumptomatching\",description:\"Jump to matching\",bindKey:o(\"Ctrl-P\",\"Ctrl-P\"),exec:function(e){e.jumpToMatching()},multiSelectAction:\"forEach\",scrollIntoView:\"animate\",readOnly:!0},{name:\"selecttomatching\",description:\"Select to matching\",bindKey:o(\"Ctrl-Shift-P\",\"Ctrl-Shift-P\"),exec:function(e){e.jumpToMatching(!0)},multiSelectAction:\"forEach\",scrollIntoView:\"animate\",readOnly:!0},{name:\"expandToMatching\",description:\"Expand to matching\",bindKey:o(\"Ctrl-Shift-M\",\"Ctrl-Shift-M\"),exec:function(e){e.jumpToMatching(!0,!0)},multiSelectAction:\"forEach\",scrollIntoView:\"animate\",readOnly:!0},{name:\"passKeysToBrowser\",description:\"Pass keys to browser\",bindKey:o(null,null),exec:function(){},passEvent:!0,readOnly:!0},{name:\"copy\",description:\"Copy\",exec:function(e){},readOnly:!0},{name:\"cut\",description:\"Cut\",exec:function(e){var t=e.$copyWithEmptySelection&&e.selection.isEmpty(),n=t?e.selection.getLineRange():e.selection.getRange();e._emit(\"cut\",n),n.isEmpty()||e.session.remove(n),e.clearSelection()},scrollIntoView:\"cursor\",multiSelectAction:\"forEach\"},{name:\"paste\",description:\"Paste\",exec:function(e,t){e.$handlePaste(t)},scrollIntoView:\"cursor\"},{name:\"removeline\",description:\"Remove line\",bindKey:o(\"Ctrl-D\",\"Command-D\"),exec:function(e){e.removeLines()},scrollIntoView:\"cursor\",multiSelectAction:\"forEachLine\"},{name:\"duplicateSelection\",description:\"Duplicate selection\",bindKey:o(\"Ctrl-Shift-D\",\"Command-Shift-D\"),exec:function(e){e.duplicateSelection()},scrollIntoView:\"cursor\",multiSelectAction:\"forEach\"},{name:\"sortlines\",description:\"Sort lines\",bindKey:o(\"Ctrl-Alt-S\",\"Command-Alt-S\"),exec:function(e){e.sortLines()},scrollIntoView:\"selection\",multiSelectAction:\"forEachLine\"},{name:\"togglecomment\",description:\"Toggle comment\",bindKey:o(\"Ctrl-/\",\"Command-/\"),exec:function(e){e.toggleCommentLines()},multiSelectAction:\"forEachLine\",scrollIntoView:\"selectionPart\"},{name:\"toggleBlockComment\",description:\"Toggle block comment\",bindKey:o(\"Ctrl-Shift-/\",\"Command-Shift-/\"),exec:function(e){e.toggleBlockComment()},multiSelectAction:\"forEach\",scrollIntoView:\"selectionPart\"},{name:\"modifyNumberUp\",description:\"Modify number up\",bindKey:o(\"Ctrl-Shift-Up\",\"Alt-Shift-Up\"),exec:function(e){e.modifyNumber(1)},scrollIntoView:\"cursor\",multiSelectAction:\"forEach\"},{name:\"modifyNumberDown\",description:\"Modify number down\",bindKey:o(\"Ctrl-Shift-Down\",\"Alt-Shift-Down\"),exec:function(e){e.modifyNumber(-1)},scrollIntoView:\"cursor\",multiSelectAction:\"forEach\"},{name:\"replace\",description:\"Replace\",bindKey:o(\"Ctrl-H\",\"Command-Option-F\"),exec:function(e){i.loadModule(\"ace/ext/searchbox\",function(t){t.Search(e,!0)})}},{name:\"undo\",description:\"Undo\",bindKey:o(\"Ctrl-Z\",\"Command-Z\"),exec:function(e){e.undo()}},{name:\"redo\",description:\"Redo\",bindKey:o(\"Ctrl-Shift-Z|Ctrl-Y\",\"Command-Shift-Z|Command-Y\"),exec:function(e){e.redo()}},{name:\"copylinesup\",description:\"Copy lines up\",bindKey:o(\"Alt-Shift-Up\",\"Command-Option-Up\"),exec:function(e){e.copyLinesUp()},scrollIntoView:\"cursor\"},{name:\"movelinesup\",description:\"Move lines up\",bindKey:o(\"Alt-Up\",\"Option-Up\"),exec:function(e){e.moveLinesUp()},scrollIntoView:\"cursor\"},{name:\"copylinesdown\",description:\"Copy lines down\",bindKey:o(\"Alt-Shift-Down\",\"Command-Option-Down\"),exec:function(e){e.copyLinesDown()},scrollIntoView:\"cursor\"},{name:\"movelinesdown\",description:\"Move lines down\",bindKey:o(\"Alt-Down\",\"Option-Down\"),exec:function(e){e.moveLinesDown()},scrollIntoView:\"cursor\"},{name:\"del\",description:\"Delete\",bindKey:o(\"Delete\",\"Delete|Ctrl-D|Shift-Delete\"),exec:function(e){e.remove(\"right\")},multiSelectAction:\"forEach\",scrollIntoView:\"cursor\"},{name:\"backspace\",description:\"Backspace\",bindKey:o(\"Shift-Backspace|Backspace\",\"Ctrl-Backspace|Shift-Backspace|Backspace|Ctrl-H\"),exec:function(e){e.remove(\"left\")},multiSelectAction:\"forEach\",scrollIntoView:\"cursor\"},{name:\"cut_or_delete\",description:\"Cut or delete\",bindKey:o(\"Shift-Delete\",null),exec:function(e){if(!e.selection.isEmpty())return!1;e.remove(\"left\")},multiSelectAction:\"forEach\",scrollIntoView:\"cursor\"},{name:\"removetolinestart\",description:\"Remove to line start\",bindKey:o(\"Alt-Backspace\",\"Command-Backspace\"),exec:function(e){e.removeToLineStart()},multiSelectAction:\"forEach\",scrollIntoView:\"cursor\"},{name:\"removetolineend\",description:\"Remove to line end\",bindKey:o(\"Alt-Delete\",\"Ctrl-K|Command-Delete\"),exec:function(e){e.removeToLineEnd()},multiSelectAction:\"forEach\",scrollIntoView:\"cursor\"},{name:\"removetolinestarthard\",description:\"Remove to line start hard\",bindKey:o(\"Ctrl-Shift-Backspace\",null),exec:function(e){var t=e.selection.getRange();t.start.column=0,e.session.remove(t)},multiSelectAction:\"forEach\",scrollIntoView:\"cursor\"},{name:\"removetolineendhard\",description:\"Remove to line end hard\",bindKey:o(\"Ctrl-Shift-Delete\",null),exec:function(e){var t=e.selection.getRange();t.end.column=Number.MAX_VALUE,e.session.remove(t)},multiSelectAction:\"forEach\",scrollIntoView:\"cursor\"},{name:\"removewordleft\",description:\"Remove word left\",bindKey:o(\"Ctrl-Backspace\",\"Alt-Backspace|Ctrl-Alt-Backspace\"),exec:function(e){e.removeWordLeft()},multiSelectAction:\"forEach\",scrollIntoView:\"cursor\"},{name:\"removewordright\",description:\"Remove word right\",bindKey:o(\"Ctrl-Delete\",\"Alt-Delete\"),exec:function(e){e.removeWordRight()},multiSelectAction:\"forEach\",scrollIntoView:\"cursor\"},{name:\"outdent\",description:\"Outdent\",bindKey:o(\"Shift-Tab\",\"Shift-Tab\"),exec:function(e){e.blockOutdent()},multiSelectAction:\"forEach\",scrollIntoView:\"selectionPart\"},{name:\"indent\",description:\"Indent\",bindKey:o(\"Tab\",\"Tab\"),exec:function(e){e.indent()},multiSelectAction:\"forEach\",scrollIntoView:\"selectionPart\"},{name:\"blockoutdent\",description:\"Block outdent\",bindKey:o(\"Ctrl-[\",\"Ctrl-[\"),exec:function(e){e.blockOutdent()},multiSelectAction:\"forEachLine\",scrollIntoView:\"selectionPart\"},{name:\"blockindent\",description:\"Block indent\",bindKey:o(\"Ctrl-]\",\"Ctrl-]\"),exec:function(e){e.blockIndent()},multiSelectAction:\"forEachLine\",scrollIntoView:\"selectionPart\"},{name:\"insertstring\",description:\"Insert string\",exec:function(e,t){e.insert(t)},multiSelectAction:\"forEach\",scrollIntoView:\"cursor\"},{name:\"inserttext\",description:\"Insert text\",exec:function(e,t){e.insert(r.stringRepeat(t.text||\"\",t.times||1))},multiSelectAction:\"forEach\",scrollIntoView:\"cursor\"},{name:\"splitline\",description:\"Split line\",bindKey:o(null,\"Ctrl-O\"),exec:function(e){e.splitLine()},multiSelectAction:\"forEach\",scrollIntoView:\"cursor\"},{name:\"transposeletters\",description:\"Transpose letters\",bindKey:o(\"Alt-Shift-X\",\"Ctrl-T\"),exec:function(e){e.transposeLetters()},multiSelectAction:function(e){e.transposeSelections(1)},scrollIntoView:\"cursor\"},{name:\"touppercase\",description:\"To uppercase\",bindKey:o(\"Ctrl-U\",\"Ctrl-U\"),exec:function(e){e.toUpperCase()},multiSelectAction:\"forEach\",scrollIntoView:\"cursor\"},{name:\"tolowercase\",description:\"To lowercase\",bindKey:o(\"Ctrl-Shift-U\",\"Ctrl-Shift-U\"),exec:function(e){e.toLowerCase()},multiSelectAction:\"forEach\",scrollIntoView:\"cursor\"},{name:\"expandtoline\",description:\"Expand to line\",bindKey:o(\"Ctrl-Shift-L\",\"Command-Shift-L\"),exec:function(e){var t=e.selection.getRange();t.start.column=t.end.column=0,t.end.row++,e.selection.setRange(t,!1)},multiSelectAction:\"forEach\",scrollIntoView:\"cursor\",readOnly:!0},{name:\"joinlines\",description:\"Join lines\",bindKey:o(null,null),exec:function(e){var t=e.selection.isBackwards(),n=t?e.selection.getSelectionLead():e.selection.getSelectionAnchor(),i=t?e.selection.getSelectionAnchor():e.selection.getSelectionLead(),o=e.session.doc.getLine(n.row).length,u=e.session.doc.getTextRange(e.selection.getRange()),a=u.replace(/\\n\\s*/,\" \").length,f=e.session.doc.getLine(n.row);for(var l=n.row+1;l<=i.row+1;l++){var c=r.stringTrimLeft(r.stringTrimRight(e.session.doc.getLine(l)));c.length!==0&&(c=\" \"+c),f+=c}i.row+1<e.session.doc.getLength()-1&&(f+=e.session.doc.getNewLineCharacter()),e.clearSelection(),e.session.doc.replace(new s(n.row,0,i.row+2,0),f),a>0?(e.selection.moveCursorTo(n.row,n.column),e.selection.selectTo(n.row,n.column+a)):(o=e.session.doc.getLine(n.row).length>o?o+1:o,e.selection.moveCursorTo(n.row,o))},multiSelectAction:\"forEach\",readOnly:!0},{name:\"invertSelection\",description:\"Invert selection\",bindKey:o(null,null),exec:function(e){var t=e.session.doc.getLength()-1,n=e.session.doc.getLine(t).length,r=e.selection.rangeList.ranges,i=[];r.length<1&&(r=[e.selection.getRange()]);for(var o=0;o<r.length;o++)o==r.length-1&&(r[o].end.row!==t||r[o].end.column!==n)&&i.push(new s(r[o].end.row,r[o].end.column,t,n)),o===0?(r[o].start.row!==0||r[o].start.column!==0)&&i.push(new s(0,0,r[o].start.row,r[o].start.column)):i.push(new s(r[o-1].end.row,r[o-1].end.column,r[o].start.row,r[o].start.column));e.exitMultiSelectMode(),e.clearSelection();for(var o=0;o<i.length;o++)e.selection.addRange(i[o],!1)},readOnly:!0,scrollIntoView:\"none\"},{name:\"openCommandPallete\",description:\"Open command pallete\",bindKey:o(\"F1\",\"F1\"),exec:function(e){e.prompt({$type:\"commands\"})},readOnly:!0},{name:\"modeSelect\",description:\"Change language mode...\",bindKey:o(null,null),exec:function(e){e.prompt({$type:\"modes\"})},readOnly:!0}]}),ace.define(\"ace/clipboard\",[\"require\",\"exports\",\"module\"],function(e,t,n){\"use strict\";n.exports={lineMode:!1}}),ace.define(\"ace/editor\",[\"require\",\"exports\",\"module\",\"ace/lib/fixoldbrowsers\",\"ace/lib/oop\",\"ace/lib/dom\",\"ace/lib/lang\",\"ace/lib/useragent\",\"ace/keyboard/textinput\",\"ace/mouse/mouse_handler\",\"ace/mouse/fold_handler\",\"ace/keyboard/keybinding\",\"ace/edit_session\",\"ace/search\",\"ace/range\",\"ace/lib/event_emitter\",\"ace/commands/command_manager\",\"ace/commands/default_commands\",\"ace/config\",\"ace/token_iterator\",\"ace/clipboard\"],function(e,t,n){\"use strict\";e(\"./lib/fixoldbrowsers\");var r=e(\"./lib/oop\"),i=e(\"./lib/dom\"),s=e(\"./lib/lang\"),o=e(\"./lib/useragent\"),u=e(\"./keyboard/textinput\").TextInput,a=e(\"./mouse/mouse_handler\").MouseHandler,f=e(\"./mouse/fold_handler\").FoldHandler,l=e(\"./keyboard/keybinding\").KeyBinding,c=e(\"./edit_session\").EditSession,h=e(\"./search\").Search,p=e(\"./range\").Range,d=e(\"./lib/event_emitter\").EventEmitter,v=e(\"./commands/command_manager\").CommandManager,m=e(\"./commands/default_commands\").commands,g=e(\"./config\"),y=e(\"./token_iterator\").TokenIterator,b=e(\"./clipboard\"),w=function(e,t,n){var r=e.getContainerElement();this.container=r,this.renderer=e,this.id=\"editor\"+ ++w.$uid,this.commands=new v(o.isMac?\"mac\":\"win\",m),typeof document==\"object\"&&(this.textInput=new u(e.getTextAreaContainer(),this),this.renderer.textarea=this.textInput.getElement(),this.$mouseHandler=new a(this),new f(this)),this.keyBinding=new l(this),this.$search=(new h).set({wrap:!0}),this.$historyTracker=this.$historyTracker.bind(this),this.commands.on(\"exec\",this.$historyTracker),this.$initOperationListeners(),this._$emitInputEvent=s.delayedCall(function(){this._signal(\"input\",{}),this.session&&this.session.bgTokenizer&&this.session.bgTokenizer.scheduleStart()}.bind(this)),this.on(\"change\",function(e,t){t._$emitInputEvent.schedule(31)}),this.setSession(t||n&&n.session||new c(\"\")),g.resetOptions(this),n&&this.setOptions(n),g._signal(\"editor\",this)};w.$uid=0,function(){r.implement(this,d),this.$initOperationListeners=function(){this.commands.on(\"exec\",this.startOperation.bind(this),!0),this.commands.on(\"afterExec\",this.endOperation.bind(this),!0),this.$opResetTimer=s.delayedCall(this.endOperation.bind(this,!0)),this.on(\"change\",function(){this.curOp||(this.startOperation(),this.curOp.selectionBefore=this.$lastSel),this.curOp.docChanged=!0}.bind(this),!0),this.on(\"changeSelection\",function(){this.curOp||(this.startOperation(),this.curOp.selectionBefore=this.$lastSel),this.curOp.selectionChanged=!0}.bind(this),!0)},this.curOp=null,this.prevOp={},this.startOperation=function(e){if(this.curOp){if(!e||this.curOp.command)return;this.prevOp=this.curOp}e||(this.previousCommand=null,e={}),this.$opResetTimer.schedule(),this.curOp=this.session.curOp={command:e.command||{},args:e.args,scrollTop:this.renderer.scrollTop},this.curOp.selectionBefore=this.selection.toJSON()},this.endOperation=function(e){if(this.curOp){if(e&&e.returnValue===!1)return this.curOp=null;if(e==1&&this.curOp.command&&this.curOp.command.name==\"mouse\")return;this._signal(\"beforeEndOperation\");if(!this.curOp)return;var t=this.curOp.command,n=t&&t.scrollIntoView;if(n){switch(n){case\"center-animate\":n=\"animate\";case\"center\":this.renderer.scrollCursorIntoView(null,.5);break;case\"animate\":case\"cursor\":this.renderer.scrollCursorIntoView();break;case\"selectionPart\":var r=this.selection.getRange(),i=this.renderer.layerConfig;(r.start.row>=i.lastRow||r.end.row<=i.firstRow)&&this.renderer.scrollSelectionIntoView(this.selection.anchor,this.selection.lead);break;default:}n==\"animate\"&&this.renderer.animateScrolling(this.curOp.scrollTop)}var s=this.selection.toJSON();this.curOp.selectionAfter=s,this.$lastSel=this.selection.toJSON(),this.session.getUndoManager().addSelection(s),this.prevOp=this.curOp,this.curOp=null}},this.$mergeableCommands=[\"backspace\",\"del\",\"insertstring\"],this.$historyTracker=function(e){if(!this.$mergeUndoDeltas)return;var t=this.prevOp,n=this.$mergeableCommands,r=t.command&&e.command.name==t.command.name;if(e.command.name==\"insertstring\"){var i=e.args;this.mergeNextCommand===undefined&&(this.mergeNextCommand=!0),r=r&&this.mergeNextCommand&&(!/\\s/.test(i)||/\\s/.test(t.args)),this.mergeNextCommand=!0}else r=r&&n.indexOf(e.command.name)!==-1;this.$mergeUndoDeltas!=\"always\"&&Date.now()-this.sequenceStartTime>2e3&&(r=!1),r?this.session.mergeUndoDeltas=!0:n.indexOf(e.command.name)!==-1&&(this.sequenceStartTime=Date.now())},this.setKeyboardHandler=function(e,t){if(e&&typeof e==\"string\"&&e!=\"ace\"){this.$keybindingId=e;var n=this;g.loadModule([\"keybinding\",e],function(r){n.$keybindingId==e&&n.keyBinding.setKeyboardHandler(r&&r.handler),t&&t()})}else this.$keybindingId=null,this.keyBinding.setKeyboardHandler(e),t&&t()},this.getKeyboardHandler=function(){return this.keyBinding.getKeyboardHandler()},this.setSession=function(e){if(this.session==e)return;this.curOp&&this.endOperation(),this.curOp={};var t=this.session;if(t){this.session.off(\"change\",this.$onDocumentChange),this.session.off(\"changeMode\",this.$onChangeMode),this.session.off(\"tokenizerUpdate\",this.$onTokenizerUpdate),this.session.off(\"changeTabSize\",this.$onChangeTabSize),this.session.off(\"changeWrapLimit\",this.$onChangeWrapLimit),this.session.off(\"changeWrapMode\",this.$onChangeWrapMode),this.session.off(\"changeFold\",this.$onChangeFold),this.session.off(\"changeFrontMarker\",this.$onChangeFrontMarker),this.session.off(\"changeBackMarker\",this.$onChangeBackMarker),this.session.off(\"changeBreakpoint\",this.$onChangeBreakpoint),this.session.off(\"changeAnnotation\",this.$onChangeAnnotation),this.session.off(\"changeOverwrite\",this.$onCursorChange),this.session.off(\"changeScrollTop\",this.$onScrollTopChange),this.session.off(\"changeScrollLeft\",this.$onScrollLeftChange);var n=this.session.getSelection();n.off(\"changeCursor\",this.$onCursorChange),n.off(\"changeSelection\",this.$onSelectionChange)}this.session=e,e?(this.$onDocumentChange=this.onDocumentChange.bind(this),e.on(\"change\",this.$onDocumentChange),this.renderer.setSession(e),this.$onChangeMode=this.onChangeMode.bind(this),e.on(\"changeMode\",this.$onChangeMode),this.$onTokenizerUpdate=this.onTokenizerUpdate.bind(this),e.on(\"tokenizerUpdate\",this.$onTokenizerUpdate),this.$onChangeTabSize=this.renderer.onChangeTabSize.bind(this.renderer),e.on(\"changeTabSize\",this.$onChangeTabSize),this.$onChangeWrapLimit=this.onChangeWrapLimit.bind(this),e.on(\"changeWrapLimit\",this.$onChangeWrapLimit),this.$onChangeWrapMode=this.onChangeWrapMode.bind(this),e.on(\"changeWrapMode\",this.$onChangeWrapMode),this.$onChangeFold=this.onChangeFold.bind(this),e.on(\"changeFold\",this.$onChangeFold),this.$onChangeFrontMarker=this.onChangeFrontMarker.bind(this),this.session.on(\"changeFrontMarker\",this.$onChangeFrontMarker),this.$onChangeBackMarker=this.onChangeBackMarker.bind(this),this.session.on(\"changeBackMarker\",this.$onChangeBackMarker),this.$onChangeBreakpoint=this.onChangeBreakpoint.bind(this),this.session.on(\"changeBreakpoint\",this.$onChangeBreakpoint),this.$onChangeAnnotation=this.onChangeAnnotation.bind(this),this.session.on(\"changeAnnotation\",this.$onChangeAnnotation),this.$onCursorChange=this.onCursorChange.bind(this),this.session.on(\"changeOverwrite\",this.$onCursorChange),this.$onScrollTopChange=this.onScrollTopChange.bind(this),this.session.on(\"changeScrollTop\",this.$onScrollTopChange),this.$onScrollLeftChange=this.onScrollLeftChange.bind(this),this.session.on(\"changeScrollLeft\",this.$onScrollLeftChange),this.selection=e.getSelection(),this.selection.on(\"changeCursor\",this.$onCursorChange),this.$onSelectionChange=this.onSelectionChange.bind(this),this.selection.on(\"changeSelection\",this.$onSelectionChange),this.onChangeMode(),this.onCursorChange(),this.onScrollTopChange(),this.onScrollLeftChange(),this.onSelectionChange(),this.onChangeFrontMarker(),this.onChangeBackMarker(),this.onChangeBreakpoint(),this.onChangeAnnotation(),this.session.getUseWrapMode()&&this.renderer.adjustWrapLimit(),this.renderer.updateFull()):(this.selection=null,this.renderer.setSession(e)),this._signal(\"changeSession\",{session:e,oldSession:t}),this.curOp=null,t&&t._signal(\"changeEditor\",{oldEditor:this}),e&&e._signal(\"changeEditor\",{editor:this}),e&&e.bgTokenizer&&e.bgTokenizer.scheduleStart()},this.getSession=function(){return this.session},this.setValue=function(e,t){return this.session.doc.setValue(e),t?t==1?this.navigateFileEnd():t==-1&&this.navigateFileStart():this.selectAll(),e},this.getValue=function(){return this.session.getValue()},this.getSelection=function(){return this.selection},this.resize=function(e){this.renderer.onResize(e)},this.setTheme=function(e,t){this.renderer.setTheme(e,t)},this.getTheme=function(){return this.renderer.getTheme()},this.setStyle=function(e){this.renderer.setStyle(e)},this.unsetStyle=function(e){this.renderer.unsetStyle(e)},this.getFontSize=function(){return this.getOption(\"fontSize\")||i.computedStyle(this.container).fontSize},this.setFontSize=function(e){this.setOption(\"fontSize\",e)},this.$highlightBrackets=function(){this.session.$bracketHighlight&&(this.session.removeMarker(this.session.$bracketHighlight),this.session.$bracketHighlight=null);if(this.$highlightPending)return;var e=this;this.$highlightPending=!0,setTimeout(function(){e.$highlightPending=!1;var t=e.session;if(!t||!t.bgTokenizer)return;var n=t.findMatchingBracket(e.getCursorPosition());if(n)var r=new p(n.row,n.column,n.row,n.column+1);else if(t.$mode.getMatching)var r=t.$mode.getMatching(e.session);r&&(t.$bracketHighlight=t.addMarker(r,\"ace_bracket\",\"text\"))},50)},this.$highlightTags=function(){if(this.$highlightTagPending)return;var e=this;this.$highlightTagPending=!0,setTimeout(function(){e.$highlightTagPending=!1;var t=e.session;if(!t||!t.bgTokenizer)return;var n=e.getCursorPosition(),r=new y(e.session,n.row,n.column),i=r.getCurrentToken();if(!i||!/\\b(?:tag-open|tag-name)/.test(i.type)){t.removeMarker(t.$tagHighlight),t.$tagHighlight=null;return}if(i.type.indexOf(\"tag-open\")!=-1){i=r.stepForward();if(!i)return}var s=i.value,o=0,u=r.stepBackward();if(u.value==\"<\"){do u=i,i=r.stepForward(),i&&i.value===s&&i.type.indexOf(\"tag-name\")!==-1&&(u.value===\"<\"?o++:u.value===\"</\"&&o--);while(i&&o>=0)}else{do i=u,u=r.stepBackward(),i&&i.value===s&&i.type.indexOf(\"tag-name\")!==-1&&(u.value===\"<\"?o++:u.value===\"</\"&&o--);while(u&&o<=0);r.stepForward()}if(!i){t.removeMarker(t.$tagHighlight),t.$tagHighlight=null;return}var a=r.getCurrentTokenRow(),f=r.getCurrentTokenColumn(),l=new p(a,f,a,f+i.value.length),c=t.$backMarkers[t.$tagHighlight];t.$tagHighlight&&c!=undefined&&l.compareRange(c.range)!==0&&(t.removeMarker(t.$tagHighlight),t.$tagHighlight=null),t.$tagHighlight||(t.$tagHighlight=t.addMarker(l,\"ace_bracket\",\"text\"))},50)},this.focus=function(){var e=this;setTimeout(function(){e.isFocused()||e.textInput.focus()}),this.textInput.focus()},this.isFocused=function(){return this.textInput.isFocused()},this.blur=function(){this.textInput.blur()},this.onFocus=function(e){if(this.$isFocused)return;this.$isFocused=!0,this.renderer.showCursor(),this.renderer.visualizeFocus(),this._emit(\"focus\",e)},this.onBlur=function(e){if(!this.$isFocused)return;this.$isFocused=!1,this.renderer.hideCursor(),this.renderer.visualizeBlur(),this._emit(\"blur\",e)},this.$cursorChange=function(){this.renderer.updateCursor()},this.onDocumentChange=function(e){var t=this.session.$useWrapMode,n=e.start.row==e.end.row?e.end.row:Infinity;this.renderer.updateLines(e.start.row,n,t),this._signal(\"change\",e),this.$cursorChange(),this.$updateHighlightActiveLine()},this.onTokenizerUpdate=function(e){var t=e.data;this.renderer.updateLines(t.first,t.last)},this.onScrollTopChange=function(){this.renderer.scrollToY(this.session.getScrollTop())},this.onScrollLeftChange=function(){this.renderer.scrollToX(this.session.getScrollLeft())},this.onCursorChange=function(){this.$cursorChange(),this.$highlightBrackets(),this.$highlightTags(),this.$updateHighlightActiveLine(),this._signal(\"changeSelection\")},this.$updateHighlightActiveLine=function(){var e=this.getSession(),t;if(this.$highlightActiveLine){if(this.$selectionStyle!=\"line\"||!this.selection.isMultiLine())t=this.getCursorPosition();this.renderer.theme&&this.renderer.theme.$selectionColorConflict&&!this.selection.isEmpty()&&(t=!1),this.renderer.$maxLines&&this.session.getLength()===1&&!(this.renderer.$minLines>1)&&(t=!1)}if(e.$highlightLineMarker&&!t)e.removeMarker(e.$highlightLineMarker.id),e.$highlightLineMarker=null;else if(!e.$highlightLineMarker&&t){var n=new p(t.row,t.column,t.row,Infinity);n.id=e.addMarker(n,\"ace_active-line\",\"screenLine\"),e.$highlightLineMarker=n}else t&&(e.$highlightLineMarker.start.row=t.row,e.$highlightLineMarker.end.row=t.row,e.$highlightLineMarker.start.column=t.column,e._signal(\"changeBackMarker\"))},this.onSelectionChange=function(e){var t=this.session;t.$selectionMarker&&t.removeMarker(t.$selectionMarker),t.$selectionMarker=null;if(!this.selection.isEmpty()){var n=this.selection.getRange(),r=this.getSelectionStyle();t.$selectionMarker=t.addMarker(n,\"ace_selection\",r)}else this.$updateHighlightActiveLine();var i=this.$highlightSelectedWord&&this.$getSelectionHighLightRegexp();this.session.highlight(i),this._signal(\"changeSelection\")},this.$getSelectionHighLightRegexp=function(){var e=this.session,t=this.getSelectionRange();if(t.isEmpty()||t.isMultiLine())return;var n=t.start.column,r=t.end.column,i=e.getLine(t.start.row),s=i.substring(n,r);if(s.length>5e3||!/[\\w\\d]/.test(s))return;var o=this.$search.$assembleRegExp({wholeWord:!0,caseSensitive:!0,needle:s}),u=i.substring(n-1,r+1);if(!o.test(u))return;return o},this.onChangeFrontMarker=function(){this.renderer.updateFrontMarkers()},this.onChangeBackMarker=function(){this.renderer.updateBackMarkers()},this.onChangeBreakpoint=function(){this.renderer.updateBreakpoints()},this.onChangeAnnotation=function(){this.renderer.setAnnotations(this.session.getAnnotations())},this.onChangeMode=function(e){this.renderer.updateText(),this._emit(\"changeMode\",e)},this.onChangeWrapLimit=function(){this.renderer.updateFull()},this.onChangeWrapMode=function(){this.renderer.onResize(!0)},this.onChangeFold=function(){this.$updateHighlightActiveLine(),this.renderer.updateFull()},this.getSelectedText=function(){return this.session.getTextRange(this.getSelectionRange())},this.getCopyText=function(){var e=this.getSelectedText(),t=this.session.doc.getNewLineCharacter(),n=!1;if(!e&&this.$copyWithEmptySelection){n=!0;var r=this.selection.getAllRanges();for(var i=0;i<r.length;i++){var s=r[i];if(i&&r[i-1].start.row==s.start.row)continue;e+=this.session.getLine(s.start.row)+t}}var o={text:e};return this._signal(\"copy\",o),b.lineMode=n?o.text:\"\",o.text},this.onCopy=function(){this.commands.exec(\"copy\",this)},this.onCut=function(){this.commands.exec(\"cut\",this)},this.onPaste=function(e,t){var n={text:e,event:t};this.commands.exec(\"paste\",this,n)},this.$handlePaste=function(e){typeof e==\"string\"&&(e={text:e}),this._signal(\"paste\",e);var t=e.text,n=t==b.lineMode,r=this.session;if(!this.inMultiSelectMode||this.inVirtualSelectionMode)n?r.insert({row:this.selection.lead.row,column:0},t):this.insert(t);else if(n)this.selection.rangeList.ranges.forEach(function(e){r.insert({row:e.start.row,column:0},t)});else{var i=t.split(/\\r\\n|\\r|\\n/),s=this.selection.rangeList.ranges;if(i.length>s.length||i.length<2||!i[1])return this.commands.exec(\"insertstring\",this,t);for(var o=s.length;o--;){var u=s[o];u.isEmpty()||r.remove(u),r.insert(u.start,i[o])}}},this.execCommand=function(e,t){return this.commands.exec(e,this,t)},this.insert=function(e,t){var n=this.session,r=n.getMode(),i=this.getCursorPosition();if(this.getBehavioursEnabled()&&!t){var s=r.transformAction(n.getState(i.row),\"insertion\",this,n,e);s&&(e!==s.text&&(this.inVirtualSelectionMode||(this.session.mergeUndoDeltas=!1,this.mergeNextCommand=!1)),e=s.text)}e==\"\t\"&&(e=this.session.getTabString());if(!this.selection.isEmpty()){var o=this.getSelectionRange();i=this.session.remove(o),this.clearSelection()}else if(this.session.getOverwrite()&&e.indexOf(\"\\n\")==-1){var o=new p.fromPoints(i,i);o.end.column+=e.length,this.session.remove(o)}if(e==\"\\n\"||e==\"\\r\\n\"){var u=n.getLine(i.row);if(i.column>u.search(/\\S|$/)){var a=u.substr(i.column).search(/\\S|$/);n.doc.removeInLine(i.row,i.column,i.column+a)}}this.clearSelection();var f=i.column,l=n.getState(i.row),u=n.getLine(i.row),c=r.checkOutdent(l,u,e),h=n.insert(i,e);s&&s.selection&&(s.selection.length==2?this.selection.setSelectionRange(new p(i.row,f+s.selection[0],i.row,f+s.selection[1])):this.selection.setSelectionRange(new p(i.row+s.selection[0],s.selection[1],i.row+s.selection[2],s.selection[3])));if(n.getDocument().isNewLine(e)){var d=r.getNextLineIndent(l,u.slice(0,i.column),n.getTabString());n.insert({row:i.row+1,column:0},d)}c&&r.autoOutdent(l,n,i.row)},this.onTextInput=function(e,t){if(!t)return this.keyBinding.onTextInput(e);this.startOperation({command:{name:\"insertstring\"}});var n=this.applyComposition.bind(this,e,t);this.selection.rangeCount?this.forEachSelection(n):n(),this.endOperation()},this.applyComposition=function(e,t){if(t.extendLeft||t.extendRight){var n=this.selection.getRange();n.start.column-=t.extendLeft,n.end.column+=t.extendRight,this.selection.setRange(n),!e&&!n.isEmpty()&&this.remove()}(e||!this.selection.isEmpty())&&this.insert(e,!0);if(t.restoreStart||t.restoreEnd){var n=this.selection.getRange();n.start.column-=t.restoreStart,n.end.column-=t.restoreEnd,this.selection.setRange(n)}},this.onCommandKey=function(e,t,n){this.keyBinding.onCommandKey(e,t,n)},this.setOverwrite=function(e){this.session.setOverwrite(e)},this.getOverwrite=function(){return this.session.getOverwrite()},this.toggleOverwrite=function(){this.session.toggleOverwrite()},this.setScrollSpeed=function(e){this.setOption(\"scrollSpeed\",e)},this.getScrollSpeed=function(){return this.getOption(\"scrollSpeed\")},this.setDragDelay=function(e){this.setOption(\"dragDelay\",e)},this.getDragDelay=function(){return this.getOption(\"dragDelay\")},this.setSelectionStyle=function(e){this.setOption(\"selectionStyle\",e)},this.getSelectionStyle=function(){return this.getOption(\"selectionStyle\")},this.setHighlightActiveLine=function(e){this.setOption(\"highlightActiveLine\",e)},this.getHighlightActiveLine=function(){return this.getOption(\"highlightActiveLine\")},this.setHighlightGutterLine=function(e){this.setOption(\"highlightGutterLine\",e)},this.getHighlightGutterLine=function(){return this.getOption(\"highlightGutterLine\")},this.setHighlightSelectedWord=function(e){this.setOption(\"highlightSelectedWord\",e)},this.getHighlightSelectedWord=function(){return this.$highlightSelectedWord},this.setAnimatedScroll=function(e){this.renderer.setAnimatedScroll(e)},this.getAnimatedScroll=function(){return this.renderer.getAnimatedScroll()},this.setShowInvisibles=function(e){this.renderer.setShowInvisibles(e)},this.getShowInvisibles=function(){return this.renderer.getShowInvisibles()},this.setDisplayIndentGuides=function(e){this.renderer.setDisplayIndentGuides(e)},this.getDisplayIndentGuides=function(){return this.renderer.getDisplayIndentGuides()},this.setShowPrintMargin=function(e){this.renderer.setShowPrintMargin(e)},this.getShowPrintMargin=function(){return this.renderer.getShowPrintMargin()},this.setPrintMarginColumn=function(e){this.renderer.setPrintMarginColumn(e)},this.getPrintMarginColumn=function(){return this.renderer.getPrintMarginColumn()},this.setReadOnly=function(e){this.setOption(\"readOnly\",e)},this.getReadOnly=function(){return this.getOption(\"readOnly\")},this.setBehavioursEnabled=function(e){this.setOption(\"behavioursEnabled\",e)},this.getBehavioursEnabled=function(){return this.getOption(\"behavioursEnabled\")},this.setWrapBehavioursEnabled=function(e){this.setOption(\"wrapBehavioursEnabled\",e)},this.getWrapBehavioursEnabled=function(){return this.getOption(\"wrapBehavioursEnabled\")},this.setShowFoldWidgets=function(e){this.setOption(\"showFoldWidgets\",e)},this.getShowFoldWidgets=function(){return this.getOption(\"showFoldWidgets\")},this.setFadeFoldWidgets=function(e){this.setOption(\"fadeFoldWidgets\",e)},this.getFadeFoldWidgets=function(){return this.getOption(\"fadeFoldWidgets\")},this.remove=function(e){this.selection.isEmpty()&&(e==\"left\"?this.selection.selectLeft():this.selection.selectRight());var t=this.getSelectionRange();if(this.getBehavioursEnabled()){var n=this.session,r=n.getState(t.start.row),i=n.getMode().transformAction(r,\"deletion\",this,n,t);if(t.end.column===0){var s=n.getTextRange(t);if(s[s.length-1]==\"\\n\"){var o=n.getLine(t.end.row);/^\\s+$/.test(o)&&(t.end.column=o.length)}}i&&(t=i)}this.session.remove(t),this.clearSelection()},this.removeWordRight=function(){this.selection.isEmpty()&&this.selection.selectWordRight(),this.session.remove(this.getSelectionRange()),this.clearSelection()},this.removeWordLeft=function(){this.selection.isEmpty()&&this.selection.selectWordLeft(),this.session.remove(this.getSelectionRange()),this.clearSelection()},this.removeToLineStart=function(){this.selection.isEmpty()&&this.selection.selectLineStart(),this.selection.isEmpty()&&this.selection.selectLeft(),this.session.remove(this.getSelectionRange()),this.clearSelection()},this.removeToLineEnd=function(){this.selection.isEmpty()&&this.selection.selectLineEnd();var e=this.getSelectionRange();e.start.column==e.end.column&&e.start.row==e.end.row&&(e.end.column=0,e.end.row++),this.session.remove(e),this.clearSelection()},this.splitLine=function(){this.selection.isEmpty()||(this.session.remove(this.getSelectionRange()),this.clearSelection());var e=this.getCursorPosition();this.insert(\"\\n\"),this.moveCursorToPosition(e)},this.transposeLetters=function(){if(!this.selection.isEmpty())return;var e=this.getCursorPosition(),t=e.column;if(t===0)return;var n=this.session.getLine(e.row),r,i;t<n.length?(r=n.charAt(t)+n.charAt(t-1),i=new p(e.row,t-1,e.row,t+1)):(r=n.charAt(t-1)+n.charAt(t-2),i=new p(e.row,t-2,e.row,t)),this.session.replace(i,r),this.session.selection.moveToPosition(i.end)},this.toLowerCase=function(){var e=this.getSelectionRange();this.selection.isEmpty()&&this.selection.selectWord();var t=this.getSelectionRange(),n=this.session.getTextRange(t);this.session.replace(t,n.toLowerCase()),this.selection.setSelectionRange(e)},this.toUpperCase=function(){var e=this.getSelectionRange();this.selection.isEmpty()&&this.selection.selectWord();var t=this.getSelectionRange(),n=this.session.getTextRange(t);this.session.replace(t,n.toUpperCase()),this.selection.setSelectionRange(e)},this.indent=function(){var e=this.session,t=this.getSelectionRange();if(t.start.row<t.end.row){var n=this.$getSelectedRows();e.indentRows(n.first,n.last,\"\t\");return}if(t.start.column<t.end.column){var r=e.getTextRange(t);if(!/^\\s+$/.test(r)){var n=this.$getSelectedRows();e.indentRows(n.first,n.last,\"\t\");return}}var i=e.getLine(t.start.row),o=t.start,u=e.getTabSize(),a=e.documentToScreenColumn(o.row,o.column);if(this.session.getUseSoftTabs())var f=u-a%u,l=s.stringRepeat(\" \",f);else{var f=a%u;while(i[t.start.column-1]==\" \"&&f)t.start.column--,f--;this.selection.setSelectionRange(t),l=\"\t\"}return this.insert(l)},this.blockIndent=function(){var e=this.$getSelectedRows();this.session.indentRows(e.first,e.last,\"\t\")},this.blockOutdent=function(){var e=this.session.getSelection();this.session.outdentRows(e.getRange())},this.sortLines=function(){var e=this.$getSelectedRows(),t=this.session,n=[];for(var r=e.first;r<=e.last;r++)n.push(t.getLine(r));n.sort(function(e,t){return e.toLowerCase()<t.toLowerCase()?-1:e.toLowerCase()>t.toLowerCase()?1:0});var i=new p(0,0,0,0);for(var r=e.first;r<=e.last;r++){var s=t.getLine(r);i.start.row=r,i.end.row=r,i.end.column=s.length,t.replace(i,n[r-e.first])}},this.toggleCommentLines=function(){var e=this.session.getState(this.getCursorPosition().row),t=this.$getSelectedRows();this.session.getMode().toggleCommentLines(e,this.session,t.first,t.last)},this.toggleBlockComment=function(){var e=this.getCursorPosition(),t=this.session.getState(e.row),n=this.getSelectionRange();this.session.getMode().toggleBlockComment(t,this.session,n,e)},this.getNumberAt=function(e,t){var n=/[\\-]?[0-9]+(?:\\.[0-9]+)?/g;n.lastIndex=0;var r=this.session.getLine(e);while(n.lastIndex<t){var i=n.exec(r);if(i.index<=t&&i.index+i[0].length>=t){var s={value:i[0],start:i.index,end:i.index+i[0].length};return s}}return null},this.modifyNumber=function(e){var t=this.selection.getCursor().row,n=this.selection.getCursor().column,r=new p(t,n-1,t,n),i=this.session.getTextRange(r);if(!isNaN(parseFloat(i))&&isFinite(i)){var s=this.getNumberAt(t,n);if(s){var o=s.value.indexOf(\".\")>=0?s.start+s.value.indexOf(\".\")+1:s.end,u=s.start+s.value.length-o,a=parseFloat(s.value);a*=Math.pow(10,u),o!==s.end&&n<o?e*=Math.pow(10,s.end-n-1):e*=Math.pow(10,s.end-n),a+=e,a/=Math.pow(10,u);var f=a.toFixed(u),l=new p(t,s.start,t,s.end);this.session.replace(l,f),this.moveCursorTo(t,Math.max(s.start+1,n+f.length-s.value.length))}}else this.toggleWord()},this.$toggleWordPairs=[[\"first\",\"last\"],[\"true\",\"false\"],[\"yes\",\"no\"],[\"width\",\"height\"],[\"top\",\"bottom\"],[\"right\",\"left\"],[\"on\",\"off\"],[\"x\",\"y\"],[\"get\",\"set\"],[\"max\",\"min\"],[\"horizontal\",\"vertical\"],[\"show\",\"hide\"],[\"add\",\"remove\"],[\"up\",\"down\"],[\"before\",\"after\"],[\"even\",\"odd\"],[\"inside\",\"outside\"],[\"next\",\"previous\"],[\"increase\",\"decrease\"],[\"attach\",\"detach\"],[\"&&\",\"||\"],[\"==\",\"!=\"]],this.toggleWord=function(){var e=this.selection.getCursor().row,t=this.selection.getCursor().column;this.selection.selectWord();var n=this.getSelectedText(),r=this.selection.getWordRange().start.column,i=n.replace(/([a-z]+|[A-Z]+)(?=[A-Z_]|$)/g,\"$1 \").split(/\\s/),o=t-r-1;o<0&&(o=0);var u=0,a=0,f=this;n.match(/[A-Za-z0-9_]+/)&&i.forEach(function(t,i){a=u+t.length,o>=u&&o<=a&&(n=t,f.selection.clearSelection(),f.moveCursorTo(e,u+r),f.selection.selectTo(e,a+r)),u=a});var l=this.$toggleWordPairs,c;for(var h=0;h<l.length;h++){var p=l[h];for(var d=0;d<=1;d++){var v=+!d,m=n.match(new RegExp(\"^\\\\s?_?(\"+s.escapeRegExp(p[d])+\")\\\\s?$\",\"i\"));if(m){var g=n.match(new RegExp(\"([_]|^|\\\\s)(\"+s.escapeRegExp(m[1])+\")($|\\\\s)\",\"g\"));g&&(c=n.replace(new RegExp(s.escapeRegExp(p[d]),\"i\"),function(e){var t=p[v];return e.toUpperCase()==e?t=t.toUpperCase():e.charAt(0).toUpperCase()==e.charAt(0)&&(t=t.substr(0,0)+p[v].charAt(0).toUpperCase()+t.substr(1)),t}),this.insert(c),c=\"\")}}}},this.removeLines=function(){var e=this.$getSelectedRows();this.session.removeFullLines(e.first,e.last),this.clearSelection()},this.duplicateSelection=function(){var e=this.selection,t=this.session,n=e.getRange(),r=e.isBackwards();if(n.isEmpty()){var i=n.start.row;t.duplicateLines(i,i)}else{var s=r?n.start:n.end,o=t.insert(s,t.getTextRange(n),!1);n.start=s,n.end=o,e.setSelectionRange(n,r)}},this.moveLinesDown=function(){this.$moveLines(1,!1)},this.moveLinesUp=function(){this.$moveLines(-1,!1)},this.moveText=function(e,t,n){return this.session.moveText(e,t,n)},this.copyLinesUp=function(){this.$moveLines(-1,!0)},this.copyLinesDown=function(){this.$moveLines(1,!0)},this.$moveLines=function(e,t){var n,r,i=this.selection;if(!i.inMultiSelectMode||this.inVirtualSelectionMode){var s=i.toOrientedRange();n=this.$getSelectedRows(s),r=this.session.$moveLines(n.first,n.last,t?0:e),t&&e==-1&&(r=0),s.moveBy(r,0),i.fromOrientedRange(s)}else{var o=i.rangeList.ranges;i.rangeList.detach(this.session),this.inVirtualSelectionMode=!0;var u=0,a=0,f=o.length;for(var l=0;l<f;l++){var c=l;o[l].moveBy(u,0),n=this.$getSelectedRows(o[l]);var h=n.first,p=n.last;while(++l<f){a&&o[l].moveBy(a,0);var d=this.$getSelectedRows(o[l]);if(t&&d.first!=p)break;if(!t&&d.first>p+1)break;p=d.last}l--,u=this.session.$moveLines(h,p,t?0:e),t&&e==-1&&(c=l+1);while(c<=l)o[c].moveBy(u,0),c++;t||(u=0),a+=u}i.fromOrientedRange(i.ranges[0]),i.rangeList.attach(this.session),this.inVirtualSelectionMode=!1}},this.$getSelectedRows=function(e){return e=(e||this.getSelectionRange()).collapseRows(),{first:this.session.getRowFoldStart(e.start.row),last:this.session.getRowFoldEnd(e.end.row)}},this.onCompositionStart=function(e){this.renderer.showComposition(e)},this.onCompositionUpdate=function(e){this.renderer.setCompositionText(e)},this.onCompositionEnd=function(){this.renderer.hideComposition()},this.getFirstVisibleRow=function(){return this.renderer.getFirstVisibleRow()},this.getLastVisibleRow=function(){return this.renderer.getLastVisibleRow()},this.isRowVisible=function(e){return e>=this.getFirstVisibleRow()&&e<=this.getLastVisibleRow()},this.isRowFullyVisible=function(e){return e>=this.renderer.getFirstFullyVisibleRow()&&e<=this.renderer.getLastFullyVisibleRow()},this.$getVisibleRowCount=function(){return this.renderer.getScrollBottomRow()-this.renderer.getScrollTopRow()+1},this.$moveByPage=function(e,t){var n=this.renderer,r=this.renderer.layerConfig,i=e*Math.floor(r.height/r.lineHeight);t===!0?this.selection.$moveSelection(function(){this.moveCursorBy(i,0)}):t===!1&&(this.selection.moveCursorBy(i,0),this.selection.clearSelection());var s=n.scrollTop;n.scrollBy(0,i*r.lineHeight),t!=null&&n.scrollCursorIntoView(null,.5),n.animateScrolling(s)},this.selectPageDown=function(){this.$moveByPage(1,!0)},this.selectPageUp=function(){this.$moveByPage(-1,!0)},this.gotoPageDown=function(){this.$moveByPage(1,!1)},this.gotoPageUp=function(){this.$moveByPage(-1,!1)},this.scrollPageDown=function(){this.$moveByPage(1)},this.scrollPageUp=function(){this.$moveByPage(-1)},this.scrollToRow=function(e){this.renderer.scrollToRow(e)},this.scrollToLine=function(e,t,n,r){this.renderer.scrollToLine(e,t,n,r)},this.centerSelection=function(){var e=this.getSelectionRange(),t={row:Math.floor(e.start.row+(e.end.row-e.start.row)/2),column:Math.floor(e.start.column+(e.end.column-e.start.column)/2)};this.renderer.alignCursor(t,.5)},this.getCursorPosition=function(){return this.selection.getCursor()},this.getCursorPositionScreen=function(){return this.session.documentToScreenPosition(this.getCursorPosition())},this.getSelectionRange=function(){return this.selection.getRange()},this.selectAll=function(){this.selection.selectAll()},this.clearSelection=function(){this.selection.clearSelection()},this.moveCursorTo=function(e,t){this.selection.moveCursorTo(e,t)},this.moveCursorToPosition=function(e){this.selection.moveCursorToPosition(e)},this.jumpToMatching=function(e,t){var n=this.getCursorPosition(),r=new y(this.session,n.row,n.column),i=r.getCurrentToken(),s=i||r.stepForward();if(!s)return;var o,u=!1,a={},f=n.column-s.start,l,c={\")\":\"(\",\"(\":\"(\",\"]\":\"[\",\"[\":\"[\",\"{\":\"{\",\"}\":\"{\"};do{if(s.value.match(/[{}()\\[\\]]/g))for(;f<s.value.length&&!u;f++){if(!c[s.value[f]])continue;l=c[s.value[f]]+\".\"+s.type.replace(\"rparen\",\"lparen\"),isNaN(a[l])&&(a[l]=0);switch(s.value[f]){case\"(\":case\"[\":case\"{\":a[l]++;break;case\")\":case\"]\":case\"}\":a[l]--,a[l]===-1&&(o=\"bracket\",u=!0)}}else s.type.indexOf(\"tag-name\")!==-1&&(isNaN(a[s.value])&&(a[s.value]=0),i.value===\"<\"?a[s.value]++:i.value===\"</\"&&a[s.value]--,a[s.value]===-1&&(o=\"tag\",u=!0));u||(i=s,s=r.stepForward(),f=0)}while(s&&!u);if(!o)return;var h,d;if(o===\"bracket\"){h=this.session.getBracketRange(n);if(!h){h=new p(r.getCurrentTokenRow(),r.getCurrentTokenColumn()+f-1,r.getCurrentTokenRow(),r.getCurrentTokenColumn()+f-1),d=h.start;if(t||d.row===n.row&&Math.abs(d.column-n.column)<2)h=this.session.getBracketRange(d)}}else if(o===\"tag\"){if(!s||s.type.indexOf(\"tag-name\")===-1)return;var v=s.value;h=new p(r.getCurrentTokenRow(),r.getCurrentTokenColumn()-2,r.getCurrentTokenRow(),r.getCurrentTokenColumn()-2);if(h.compare(n.row,n.column)===0){u=!1;do s=i,i=r.stepBackward(),i&&(i.type.indexOf(\"tag-close\")!==-1&&h.setEnd(r.getCurrentTokenRow(),r.getCurrentTokenColumn()+1),s.value===v&&s.type.indexOf(\"tag-name\")!==-1&&(i.value===\"<\"?a[v]++:i.value===\"</\"&&a[v]--,a[v]===0&&(u=!0)));while(i&&!u)}s&&s.type.indexOf(\"tag-name\")&&(d=h.start,d.row==n.row&&Math.abs(d.column-n.column)<2&&(d=h.end))}d=h&&h.cursor||d,d&&(e?h&&t?this.selection.setRange(h):h&&h.isEqual(this.getSelectionRange())?this.clearSelection():this.selection.selectTo(d.row,d.column):this.selection.moveTo(d.row,d.column))},this.gotoLine=function(e,t,n){this.selection.clearSelection(),this.session.unfold({row:e-1,column:t||0}),this.exitMultiSelectMode&&this.exitMultiSelectMode(),this.moveCursorTo(e-1,t||0),this.isRowFullyVisible(e-1)||this.scrollToLine(e-1,!0,n)},this.navigateTo=function(e,t){this.selection.moveTo(e,t)},this.navigateUp=function(e){if(this.selection.isMultiLine()&&!this.selection.isBackwards()){var t=this.selection.anchor.getPosition();return this.moveCursorToPosition(t)}this.selection.clearSelection(),this.selection.moveCursorBy(-e||-1,0)},this.navigateDown=function(e){if(this.selection.isMultiLine()&&this.selection.isBackwards()){var t=this.selection.anchor.getPosition();return this.moveCursorToPosition(t)}this.selection.clearSelection(),this.selection.moveCursorBy(e||1,0)},this.navigateLeft=function(e){if(!this.selection.isEmpty()){var t=this.getSelectionRange().start;this.moveCursorToPosition(t)}else{e=e||1;while(e--)this.selection.moveCursorLeft()}this.clearSelection()},this.navigateRight=function(e){if(!this.selection.isEmpty()){var t=this.getSelectionRange().end;this.moveCursorToPosition(t)}else{e=e||1;while(e--)this.selection.moveCursorRight()}this.clearSelection()},this.navigateLineStart=function(){this.selection.moveCursorLineStart(),this.clearSelection()},this.navigateLineEnd=function(){this.selection.moveCursorLineEnd(),this.clearSelection()},this.navigateFileEnd=function(){this.selection.moveCursorFileEnd(),this.clearSelection()},this.navigateFileStart=function(){this.selection.moveCursorFileStart(),this.clearSelection()},this.navigateWordRight=function(){this.selection.moveCursorWordRight(),this.clearSelection()},this.navigateWordLeft=function(){this.selection.moveCursorWordLeft(),this.clearSelection()},this.replace=function(e,t){t&&this.$search.set(t);var n=this.$search.find(this.session),r=0;return n?(this.$tryReplace(n,e)&&(r=1),this.selection.setSelectionRange(n),this.renderer.scrollSelectionIntoView(n.start,n.end),r):r},this.replaceAll=function(e,t){t&&this.$search.set(t);var n=this.$search.findAll(this.session),r=0;if(!n.length)return r;var i=this.getSelectionRange();this.selection.moveTo(0,0);for(var s=n.length-1;s>=0;--s)this.$tryReplace(n[s],e)&&r++;return this.selection.setSelectionRange(i),r},this.$tryReplace=function(e,t){var n=this.session.getTextRange(e);return t=this.$search.replace(n,t),t!==null?(e.end=this.session.replace(e,t),e):null},this.getLastSearchOptions=function(){return this.$search.getOptions()},this.find=function(e,t,n){t||(t={}),typeof e==\"string\"||e instanceof RegExp?t.needle=e:typeof e==\"object\"&&r.mixin(t,e);var i=this.selection.getRange();t.needle==null&&(e=this.session.getTextRange(i)||this.$search.$options.needle,e||(i=this.session.getWordRange(i.start.row,i.start.column),e=this.session.getTextRange(i)),this.$search.set({needle:e})),this.$search.set(t),t.start||this.$search.set({start:i});var s=this.$search.find(this.session);if(t.preventScroll)return s;if(s)return this.revealRange(s,n),s;t.backwards?i.start=i.end:i.end=i.start,this.selection.setRange(i)},this.findNext=function(e,t){this.find({skipCurrent:!0,backwards:!1},e,t)},this.findPrevious=function(e,t){this.find(e,{skipCurrent:!0,backwards:!0},t)},this.revealRange=function(e,t){this.session.unfold(e),this.selection.setSelectionRange(e);var n=this.renderer.scrollTop;this.renderer.scrollSelectionIntoView(e.start,e.end,.5),t!==!1&&this.renderer.animateScrolling(n)},this.undo=function(){this.session.getUndoManager().undo(this.session),this.renderer.scrollCursorIntoView(null,.5)},this.redo=function(){this.session.getUndoManager().redo(this.session),this.renderer.scrollCursorIntoView(null,.5)},this.destroy=function(){this.renderer.destroy(),this._signal(\"destroy\",this),this.session&&this.session.destroy()},this.setAutoScrollEditorIntoView=function(e){if(!e)return;var t,n=this,r=!1;this.$scrollAnchor||(this.$scrollAnchor=document.createElement(\"div\"));var i=this.$scrollAnchor;i.style.cssText=\"position:absolute\",this.container.insertBefore(i,this.container.firstChild);var s=this.on(\"changeSelection\",function(){r=!0}),o=this.renderer.on(\"beforeRender\",function(){r&&(t=n.renderer.container.getBoundingClientRect())}),u=this.renderer.on(\"afterRender\",function(){if(r&&t&&(n.isFocused()||n.searchBox&&n.searchBox.isFocused())){var e=n.renderer,s=e.$cursorLayer.$pixelPos,o=e.layerConfig,u=s.top-o.offset;s.top>=0&&u+t.top<0?r=!0:s.top<o.height&&s.top+t.top+o.lineHeight>window.innerHeight?r=!1:r=null,r!=null&&(i.style.top=u+\"px\",i.style.left=s.left+\"px\",i.style.height=o.lineHeight+\"px\",i.scrollIntoView(r)),r=t=null}});this.setAutoScrollEditorIntoView=function(e){if(e)return;delete this.setAutoScrollEditorIntoView,this.off(\"changeSelection\",s),this.renderer.off(\"afterRender\",u),this.renderer.off(\"beforeRender\",o)}},this.$resetCursorStyle=function(){var e=this.$cursorStyle||\"ace\",t=this.renderer.$cursorLayer;if(!t)return;t.setSmoothBlinking(/smooth/.test(e)),t.isBlinking=!this.$readOnly&&e!=\"wide\",i.setCssClass(t.element,\"ace_slim-cursors\",/slim/.test(e))},this.prompt=function(e,t,n){var r=this;g.loadModule(\"./ext/prompt\",function(i){i.prompt(r,e,t,n)})}}.call(w.prototype),g.defineOptions(w.prototype,\"editor\",{selectionStyle:{set:function(e){this.onSelectionChange(),this._signal(\"changeSelectionStyle\",{data:e})},initialValue:\"line\"},highlightActiveLine:{set:function(){this.$updateHighlightActiveLine()},initialValue:!0},highlightSelectedWord:{set:function(e){this.$onSelectionChange()},initialValue:!0},readOnly:{set:function(e){this.textInput.setReadOnly(e),this.$resetCursorStyle()},initialValue:!1},copyWithEmptySelection:{set:function(e){this.textInput.setCopyWithEmptySelection(e)},initialValue:!1},cursorStyle:{set:function(e){this.$resetCursorStyle()},values:[\"ace\",\"slim\",\"smooth\",\"wide\"],initialValue:\"ace\"},mergeUndoDeltas:{values:[!1,!0,\"always\"],initialValue:!0},behavioursEnabled:{initialValue:!0},wrapBehavioursEnabled:{initialValue:!0},autoScrollEditorIntoView:{set:function(e){this.setAutoScrollEditorIntoView(e)}},keyboardHandler:{set:function(e){this.setKeyboardHandler(e)},get:function(){return this.$keybindingId},handlesSet:!0},value:{set:function(e){this.session.setValue(e)},get:function(){return this.getValue()},handlesSet:!0,hidden:!0},session:{set:function(e){this.setSession(e)},get:function(){return this.session},handlesSet:!0,hidden:!0},showLineNumbers:{set:function(e){this.renderer.$gutterLayer.setShowLineNumbers(e),this.renderer.$loop.schedule(this.renderer.CHANGE_GUTTER),e&&this.$relativeLineNumbers?E.attach(this):E.detach(this)},initialValue:!0},relativeLineNumbers:{set:function(e){this.$showLineNumbers&&e?E.attach(this):E.detach(this)}},hScrollBarAlwaysVisible:\"renderer\",vScrollBarAlwaysVisible:\"renderer\",highlightGutterLine:\"renderer\",animatedScroll:\"renderer\",showInvisibles:\"renderer\",showPrintMargin:\"renderer\",printMarginColumn:\"renderer\",printMargin:\"renderer\",fadeFoldWidgets:\"renderer\",showFoldWidgets:\"renderer\",displayIndentGuides:\"renderer\",showGutter:\"renderer\",fontSize:\"renderer\",fontFamily:\"renderer\",maxLines:\"renderer\",minLines:\"renderer\",scrollPastEnd:\"renderer\",fixedWidthGutter:\"renderer\",theme:\"renderer\",hasCssTransforms:\"renderer\",maxPixelHeight:\"renderer\",useTextareaForIME:\"renderer\",scrollSpeed:\"$mouseHandler\",dragDelay:\"$mouseHandler\",dragEnabled:\"$mouseHandler\",focusTimeout:\"$mouseHandler\",tooltipFollowsMouse:\"$mouseHandler\",firstLineNumber:\"session\",overwrite:\"session\",newLineMode:\"session\",useWorker:\"session\",useSoftTabs:\"session\",navigateWithinSoftTabs:\"session\",tabSize:\"session\",wrap:\"session\",indentedSoftWrap:\"session\",foldStyle:\"session\",mode:\"session\"});var E={getText:function(e,t){return(Math.abs(e.selection.lead.row-t)||t+1+(t<9?\"\\u00b7\":\"\"))+\"\"},getWidth:function(e,t,n){return Math.max(t.toString().length,(n.lastRow+1).toString().length,2)*n.characterWidth},update:function(e,t){t.renderer.$loop.schedule(t.renderer.CHANGE_GUTTER)},attach:function(e){e.renderer.$gutterLayer.$renderer=this,e.on(\"changeSelection\",this.update),this.update(null,e)},detach:function(e){e.renderer.$gutterLayer.$renderer==this&&(e.renderer.$gutterLayer.$renderer=null),e.off(\"changeSelection\",this.update),this.update(null,e)}};t.Editor=w}),ace.define(\"ace/undomanager\",[\"require\",\"exports\",\"module\",\"ace/range\"],function(e,t,n){\"use strict\";function i(e,t){for(var n=t;n--;){var r=e[n];if(r&&!r[0].ignore){while(n<t-1){var i=d(e[n],e[n+1]);e[n]=i[0],e[n+1]=i[1],n++}return!0}}}function a(e){var t=e.action==\"insert\",n=e.start,r=e.end,i=(r.row-n.row)*(t?1:-1),s=(r.column-n.column)*(t?1:-1);t&&(r=n);for(var o in this.marks){var a=this.marks[o],f=u(a,n);if(f<0)continue;if(f===0&&t){if(a.bias!=1){a.bias==-1;continue}f=1}var l=t?f:u(a,r);if(l>0){a.row+=i,a.column+=a.row==r.row?s:0;continue}!t&&l<=0&&(a.row=n.row,a.column=n.column,l===0&&(a.bias=1))}}function f(e){return{row:e.row,column:e.column}}function l(e){return{start:f(e.start),end:f(e.end),action:e.action,lines:e.lines.slice()}}function c(e){e=e||this;if(Array.isArray(e))return e.map(c).join(\"\\n\");var t=\"\";e.action?(t=e.action==\"insert\"?\"+\":\"-\",t+=\"[\"+e.lines+\"]\"):e.value&&(Array.isArray(e.value)?t=e.value.map(h).join(\"\\n\"):t=h(e.value)),e.start&&(t+=h(e));if(e.id||e.rev)t+=\"\t(\"+(e.id||e.rev)+\")\";return t}function h(e){return e.start.row+\":\"+e.start.column+\"=>\"+e.end.row+\":\"+e.end.column}function p(e,t){var n=e.action==\"insert\",r=t.action==\"insert\";if(n&&r)if(o(t.start,e.end)>=0)m(t,e,-1);else{if(!(o(t.start,e.start)<=0))return null;m(e,t,1)}else if(n&&!r)if(o(t.start,e.end)>=0)m(t,e,-1);else{if(!(o(t.end,e.start)<=0))return null;m(e,t,-1)}else if(!n&&r)if(o(t.start,e.start)>=0)m(t,e,1);else{if(!(o(t.start,e.start)<=0))return null;m(e,t,1)}else if(!n&&!r)if(o(t.start,e.start)>=0)m(t,e,1);else{if(!(o(t.end,e.start)<=0))return null;m(e,t,-1)}return[t,e]}function d(e,t){for(var n=e.length;n--;)for(var r=0;r<t.length;r++)if(!p(e[n],t[r])){while(n<e.length){while(r--)p(t[r],e[n]);r=t.length,n++}return[e,t]}return e.selectionBefore=t.selectionBefore=e.selectionAfter=t.selectionAfter=null,[t,e]}function v(e,t){var n=e.action==\"insert\",r=t.action==\"insert\";if(n&&r)o(e.start,t.start)<0?m(t,e,1):m(e,t,1);else if(n&&!r)o(e.start,t.end)>=0?m(e,t,-1):o(e.start,t.start)<=0?m(t,e,1):(m(e,s.fromPoints(t.start,e.start),-1),m(t,e,1));else if(!n&&r)o(t.start,e.end)>=0?m(t,e,-1):o(t.start,e.start)<=0?m(e,t,1):(m(t,s.fromPoints(e.start,t.start),-1),m(e,t,1));else if(!n&&!r)if(o(t.start,e.end)>=0)m(t,e,-1);else{if(!(o(t.end,e.start)<=0)){var i,u;return o(e.start,t.start)<0&&(i=e,e=y(e,t.start)),o(e.end,t.end)>0&&(u=y(e,t.end)),g(t.end,e.start,e.end,-1),u&&!i&&(e.lines=u.lines,e.start=u.start,e.end=u.end,u=e),[t,i,u].filter(Boolean)}m(e,t,-1)}return[t,e]}function m(e,t,n){g(e.start,t.start,t.end,n),g(e.end,t.start,t.end,n)}function g(e,t,n,r){e.row==(r==1?t:n).row&&(e.column+=r*(n.column-t.column)),e.row+=r*(n.row-t.row)}function y(e,t){var n=e.lines,r=e.end;e.end=f(t);var i=e.end.row-e.start.row,s=n.splice(i,n.length),o=i?t.column:t.column-e.start.column;n.push(s[0].substring(0,o)),s[0]=s[0].substr(o);var u={start:f(t),end:r,lines:s,action:e.action};return u}function b(e,t){t=l(t);for(var n=e.length;n--;){var r=e[n];for(var i=0;i<r.length;i++){var s=r[i],o=v(s,t);t=o[0],o.length!=2&&(o[2]?(r.splice(i+1,1,o[1],o[2]),i++):o[1]||(r.splice(i,1),i--))}r.length||e.splice(n,1)}return e}function w(e,t){for(var n=0;n<t.length;n++){var r=t[n];for(var i=0;i<r.length;i++)b(e,r[i])}}var r=function(){this.$maxRev=0,this.$fromUndo=!1,this.reset()};(function(){this.addSession=function(e){this.$session=e},this.add=function(e,t,n){if(this.$fromUndo)return;if(e==this.$lastDelta)return;if(t===!1||!this.lastDeltas)this.lastDeltas=[],this.$undoStack.push(this.lastDeltas),e.id=this.$rev=++this.$maxRev;if(e.action==\"remove\"||e.action==\"insert\")this.$lastDelta=e;this.lastDeltas.push(e)},this.addSelection=function(e,t){this.selections.push({value:e,rev:t||this.$rev})},this.startNewGroup=function(){return this.lastDeltas=null,this.$rev},this.markIgnored=function(e,t){t==null&&(t=this.$rev+1);var n=this.$undoStack;for(var r=n.length;r--;){var i=n[r][0];if(i.id<=e)break;i.id<t&&(i.ignore=!0)}this.lastDeltas=null},this.getSelection=function(e,t){var n=this.selections;for(var r=n.length;r--;){var i=n[r];if(i.rev<e)return t&&(i=n[r+1]),i}},this.getRevision=function(){return this.$rev},this.getDeltas=function(e,t){t==null&&(t=this.$rev+1);var n=this.$undoStack,r=null,i=0;for(var s=n.length;s--;){var o=n[s][0];o.id<t&&!r&&(r=s+1);if(o.id<=e){i=s+1;break}}return n.slice(i,r)},this.getChangedRanges=function(e,t){t==null&&(t=this.$rev+1)},this.getChangedLines=function(e,t){t==null&&(t=this.$rev+1)},this.undo=function(e,t){this.lastDeltas=null;var n=this.$undoStack;if(!i(n,n.length))return;e||(e=this.$session),this.$redoStackBaseRev!==this.$rev&&this.$redoStack.length&&(this.$redoStack=[]),this.$fromUndo=!0;var r=n.pop(),s=null;return r&&r.length&&(s=e.undoChanges(r,t),this.$redoStack.push(r),this.$syncRev()),this.$fromUndo=!1,s},this.redo=function(e,t){this.lastDeltas=null,e||(e=this.$session),this.$fromUndo=!0;if(this.$redoStackBaseRev!=this.$rev){var n=this.getDeltas(this.$redoStackBaseRev,this.$rev+1);w(this.$redoStack,n),this.$redoStackBaseRev=this.$rev,this.$redoStack.forEach(function(e){e[0].id=++this.$maxRev},this)}var r=this.$redoStack.pop(),i=null;return r&&(i=e.redoChanges(r,t),this.$undoStack.push(r),this.$syncRev()),this.$fromUndo=!1,i},this.$syncRev=function(){var e=this.$undoStack,t=e[e.length-1],n=t&&t[0].id||0;this.$redoStackBaseRev=n,this.$rev=n},this.reset=function(){this.lastDeltas=null,this.$lastDelta=null,this.$undoStack=[],this.$redoStack=[],this.$rev=0,this.mark=0,this.$redoStackBaseRev=this.$rev,this.selections=[]},this.canUndo=function(){return this.$undoStack.length>0},this.canRedo=function(){return this.$redoStack.length>0},this.bookmark=function(e){e==undefined&&(e=this.$rev),this.mark=e},this.isAtBookmark=function(){return this.$rev===this.mark},this.toJSON=function(){},this.fromJSON=function(){},this.hasUndo=this.canUndo,this.hasRedo=this.canRedo,this.isClean=this.isAtBookmark,this.markClean=this.bookmark,this.$prettyPrint=function(e){return e?c(e):c(this.$undoStack)+\"\\n---\\n\"+c(this.$redoStack)}}).call(r.prototype);var s=e(\"./range\").Range,o=s.comparePoints,u=s.comparePoints;t.UndoManager=r}),ace.define(\"ace/layer/lines\",[\"require\",\"exports\",\"module\",\"ace/lib/dom\"],function(e,t,n){\"use strict\";var r=e(\"../lib/dom\"),i=function(e,t){this.element=e,this.canvasHeight=t||5e5,this.element.style.height=this.canvasHeight*2+\"px\",this.cells=[],this.cellCache=[],this.$offsetCoefficient=0};(function(){this.moveContainer=function(e){r.translate(this.element,0,-(e.firstRowScreen*e.lineHeight%this.canvasHeight)-e.offset*this.$offsetCoefficient)},this.pageChanged=function(e,t){return Math.floor(e.firstRowScreen*e.lineHeight/this.canvasHeight)!==Math.floor(t.firstRowScreen*t.lineHeight/this.canvasHeight)},this.computeLineTop=function(e,t,n){var r=t.firstRowScreen*t.lineHeight,i=Math.floor(r/this.canvasHeight),s=n.documentToScreenRow(e,0)*t.lineHeight;return s-i*this.canvasHeight},this.computeLineHeight=function(e,t,n){return t.lineHeight*n.getRowLength(e)},this.getLength=function(){return this.cells.length},this.get=function(e){return this.cells[e]},this.shift=function(){this.$cacheCell(this.cells.shift())},this.pop=function(){this.$cacheCell(this.cells.pop())},this.push=function(e){if(Array.isArray(e)){this.cells.push.apply(this.cells,e);var t=r.createFragment(this.element);for(var n=0;n<e.length;n++)t.appendChild(e[n].element);this.element.appendChild(t)}else this.cells.push(e),this.element.appendChild(e.element)},this.unshift=function(e){if(Array.isArray(e)){this.cells.unshift.apply(this.cells,e);var t=r.createFragment(this.element);for(var n=0;n<e.length;n++)t.appendChild(e[n].element);this.element.firstChild?this.element.insertBefore(t,this.element.firstChild):this.element.appendChild(t)}else this.cells.unshift(e),this.element.insertAdjacentElement(\"afterbegin\",e.element)},this.last=function(){return this.cells.length?this.cells[this.cells.length-1]:null},this.$cacheCell=function(e){if(!e)return;e.element.remove(),this.cellCache.push(e)},this.createCell=function(e,t,n,i){var s=this.cellCache.pop();if(!s){var o=r.createElement(\"div\");i&&i(o),this.element.appendChild(o),s={element:o,text:\"\",row:e}}return s.row=e,s}}).call(i.prototype),t.Lines=i}),ace.define(\"ace/layer/gutter\",[\"require\",\"exports\",\"module\",\"ace/lib/dom\",\"ace/lib/oop\",\"ace/lib/lang\",\"ace/lib/event_emitter\",\"ace/layer/lines\"],function(e,t,n){\"use strict\";function f(e){var t=document.createTextNode(\"\");e.appendChild(t);var n=r.createElement(\"span\");return e.appendChild(n),e}var r=e(\"../lib/dom\"),i=e(\"../lib/oop\"),s=e(\"../lib/lang\"),o=e(\"../lib/event_emitter\").EventEmitter,u=e(\"./lines\").Lines,a=function(e){this.element=r.createElement(\"div\"),this.element.className=\"ace_layer ace_gutter-layer\",e.appendChild(this.element),this.setShowFoldWidgets(this.$showFoldWidgets),this.gutterWidth=0,this.$annotations=[],this.$updateAnnotations=this.$updateAnnotations.bind(this),this.$lines=new u(this.element),this.$lines.$offsetCoefficient=1};(function(){i.implement(this,o),this.setSession=function(e){this.session&&this.session.removeEventListener(\"change\",this.$updateAnnotations),this.session=e,e&&e.on(\"change\",this.$updateAnnotations)},this.addGutterDecoration=function(e,t){window.console&&console.warn&&console.warn(\"deprecated use session.addGutterDecoration\"),this.session.addGutterDecoration(e,t)},this.removeGutterDecoration=function(e,t){window.console&&console.warn&&console.warn(\"deprecated use session.removeGutterDecoration\"),this.session.removeGutterDecoration(e,t)},this.setAnnotations=function(e){this.$annotations=[];for(var t=0;t<e.length;t++){var n=e[t],r=n.row,i=this.$annotations[r];i||(i=this.$annotations[r]={text:[]});var o=n.text;o=o?s.escapeHTML(o):n.html||\"\",i.text.indexOf(o)===-1&&i.text.push(o);var u=n.type;u==\"error\"?i.className=\" ace_error\":u==\"warning\"&&i.className!=\" ace_error\"?i.className=\" ace_warning\":u==\"info\"&&!i.className&&(i.className=\" ace_info\")}},this.$updateAnnotations=function(e){if(!this.$annotations.length)return;var t=e.start.row,n=e.end.row-t;if(n!==0)if(e.action==\"remove\")this.$annotations.splice(t,n+1,null);else{var r=new Array(n+1);r.unshift(t,1),this.$annotations.splice.apply(this.$annotations,r)}},this.update=function(e){this.config=e;var t=this.session,n=e.firstRow,r=Math.min(e.lastRow+e.gutterOffset,t.getLength()-1);this.oldLastRow=r,this.config=e,this.$lines.moveContainer(e),this.$updateCursorRow();var i=t.getNextFoldLine(n),s=i?i.start.row:Infinity,o=null,u=-1,a=n;for(;;){a>s&&(a=i.end.row+1,i=t.getNextFoldLine(a,i),s=i?i.start.row:Infinity);if(a>r){while(this.$lines.getLength()>u+1)this.$lines.pop();break}o=this.$lines.get(++u),o?o.row=a:(o=this.$lines.createCell(a,e,this.session,f),this.$lines.push(o)),this.$renderCell(o,e,i,a),a++}this._signal(\"afterRender\"),this.$updateGutterWidth(e)},this.$updateGutterWidth=function(e){var t=this.session,n=t.gutterRenderer||this.$renderer,r=t.$firstLineNumber,i=this.$lines.last()?this.$lines.last().text:\"\";if(this.$fixedWidth||t.$useWrapMode)i=t.getLength()+r-1;var s=n?n.getWidth(t,i,e):i.toString().length*e.characterWidth,o=this.$padding||this.$computePadding();s+=o.left+o.right,s!==this.gutterWidth&&!isNaN(s)&&(this.gutterWidth=s,this.element.parentNode.style.width=this.element.style.width=Math.ceil(this.gutterWidth)+\"px\",this._signal(\"changeGutterWidth\",s))},this.$updateCursorRow=function(){if(!this.$highlightGutterLine)return;var e=this.session.selection.getCursor();if(this.$cursorRow===e.row)return;this.$cursorRow=e.row},this.updateLineHighlight=function(){if(!this.$highlightGutterLine)return;var e=this.session.selection.cursor.row;this.$cursorRow=e;if(this.$cursorCell&&this.$cursorCell.row==e)return;this.$cursorCell&&(this.$cursorCell.element.className=this.$cursorCell.element.className.replace(\"ace_gutter-active-line \",\"\"));var t=this.$lines.cells;this.$cursorCell=null;for(var n=0;n<t.length;n++){var r=t[n];if(r.row>=this.$cursorRow){if(r.row>this.$cursorRow){var i=this.session.getFoldLine(this.$cursorRow);if(!(n>0&&i&&i.start.row==t[n-1].row))break;r=t[n-1]}r.element.className=\"ace_gutter-active-line \"+r.element.className,this.$cursorCell=r;break}}},this.scrollLines=function(e){var t=this.config;this.config=e,this.$updateCursorRow();if(this.$lines.pageChanged(t,e))return this.update(e);this.$lines.moveContainer(e);var n=Math.min(e.lastRow+e.gutterOffset,this.session.getLength()-1),r=this.oldLastRow;this.oldLastRow=n;if(!t||r<e.firstRow)return this.update(e);if(n<t.firstRow)return this.update(e);if(t.firstRow<e.firstRow)for(var i=this.session.getFoldedRowCount(t.firstRow,e.firstRow-1);i>0;i--)this.$lines.shift();if(r>n)for(var i=this.session.getFoldedRowCount(n+1,r);i>0;i--)this.$lines.pop();e.firstRow<t.firstRow&&this.$lines.unshift(this.$renderLines(e,e.firstRow,t.firstRow-1)),n>r&&this.$lines.push(this.$renderLines(e,r+1,n)),this.updateLineHighlight(),this._signal(\"afterRender\"),this.$updateGutterWidth(e)},this.$renderLines=function(e,t,n){var r=[],i=t,s=this.session.getNextFoldLine(i),o=s?s.start.row:Infinity;for(;;){i>o&&(i=s.end.row+1,s=this.session.getNextFoldLine(i,s),o=s?s.start.row:Infinity);if(i>n)break;var u=this.$lines.createCell(i,e,this.session,f);this.$renderCell(u,e,s,i),r.push(u),i++}return r},this.$renderCell=function(e,t,n,i){var s=e.element,o=this.session,u=s.childNodes[0],a=s.childNodes[1],f=o.$firstLineNumber,l=o.$breakpoints,c=o.$decorations,h=o.gutterRenderer||this.$renderer,p=this.$showFoldWidgets&&o.foldWidgets,d=n?n.start.row:Number.MAX_VALUE,v=\"ace_gutter-cell \";this.$highlightGutterLine&&(i==this.$cursorRow||n&&i<this.$cursorRow&&i>=d&&this.$cursorRow<=n.end.row)&&(v+=\"ace_gutter-active-line \",this.$cursorCell!=e&&(this.$cursorCell&&(this.$cursorCell.element.className=this.$cursorCell.element.className.replace(\"ace_gutter-active-line \",\"\")),this.$cursorCell=e)),l[i]&&(v+=l[i]),c[i]&&(v+=c[i]),this.$annotations[i]&&(v+=this.$annotations[i].className),s.className!=v&&(s.className=v);if(p){var m=p[i];m==null&&(m=p[i]=o.getFoldWidget(i))}if(m){var v=\"ace_fold-widget ace_\"+m;m==\"start\"&&i==d&&i<n.end.row?v+=\" ace_closed\":v+=\" ace_open\",a.className!=v&&(a.className=v);var g=t.lineHeight+\"px\";r.setStyle(a.style,\"height\",g),r.setStyle(a.style,\"display\",\"inline-block\")}else a&&r.setStyle(a.style,\"display\",\"none\");var y=(h?h.getText(o,i):i+f).toString();return y!==u.data&&(u.data=y),r.setStyle(e.element.style,\"height\",this.$lines.computeLineHeight(i,t,o)+\"px\"),r.setStyle(e.element.style,\"top\",this.$lines.computeLineTop(i,t,o)+\"px\"),e.text=y,e},this.$fixedWidth=!1,this.$highlightGutterLine=!0,this.$renderer=\"\",this.setHighlightGutterLine=function(e){this.$highlightGutterLine=e},this.$showLineNumbers=!0,this.$renderer=\"\",this.setShowLineNumbers=function(e){this.$renderer=!e&&{getWidth:function(){return 0},getText:function(){return\"\"}}},this.getShowLineNumbers=function(){return this.$showLineNumbers},this.$showFoldWidgets=!0,this.setShowFoldWidgets=function(e){e?r.addCssClass(this.element,\"ace_folding-enabled\"):r.removeCssClass(this.element,\"ace_folding-enabled\"),this.$showFoldWidgets=e,this.$padding=null},this.getShowFoldWidgets=function(){return this.$showFoldWidgets},this.$computePadding=function(){if(!this.element.firstChild)return{left:0,right:0};var e=r.computedStyle(this.element.firstChild);return this.$padding={},this.$padding.left=(parseInt(e.borderLeftWidth)||0)+(parseInt(e.paddingLeft)||0)+1,this.$padding.right=(parseInt(e.borderRightWidth)||0)+(parseInt(e.paddingRight)||0),this.$padding},this.getRegion=function(e){var t=this.$padding||this.$computePadding(),n=this.element.getBoundingClientRect();if(e.x<t.left+n.left)return\"markers\";if(this.$showFoldWidgets&&e.x>n.right-t.right)return\"foldWidgets\"}}).call(a.prototype),t.Gutter=a}),ace.define(\"ace/layer/marker\",[\"require\",\"exports\",\"module\",\"ace/range\",\"ace/lib/dom\"],function(e,t,n){\"use strict\";var r=e(\"../range\").Range,i=e(\"../lib/dom\"),s=function(e){this.element=i.createElement(\"div\"),this.element.className=\"ace_layer ace_marker-layer\",e.appendChild(this.element)};(function(){function e(e,t,n,r){return(e?1:0)|(t?2:0)|(n?4:0)|(r?8:0)}this.$padding=0,this.setPadding=function(e){this.$padding=e},this.setSession=function(e){this.session=e},this.setMarkers=function(e){this.markers=e},this.elt=function(e,t){var n=this.i!=-1&&this.element.childNodes[this.i];n?this.i++:(n=document.createElement(\"div\"),this.element.appendChild(n),this.i=-1),n.style.cssText=t,n.className=e},this.update=function(e){if(!e)return;this.config=e,this.i=0;var t;for(var n in this.markers){var r=this.markers[n];if(!r.range){r.update(t,this,this.session,e);continue}var i=r.range.clipRows(e.firstRow,e.lastRow);if(i.isEmpty())continue;i=i.toScreenRange(this.session);if(r.renderer){var s=this.$getTop(i.start.row,e),o=this.$padding+i.start.column*e.characterWidth;r.renderer(t,i,o,s,e)}else r.type==\"fullLine\"?this.drawFullLineMarker(t,i,r.clazz,e):r.type==\"screenLine\"?this.drawScreenLineMarker(t,i,r.clazz,e):i.isMultiLine()?r.type==\"text\"?this.drawTextMarker(t,i,r.clazz,e):this.drawMultiLineMarker(t,i,r.clazz,e):this.drawSingleLineMarker(t,i,r.clazz+\" ace_start\"+\" ace_br15\",e)}if(this.i!=-1)while(this.i<this.element.childElementCount)this.element.removeChild(this.element.lastChild)},this.$getTop=function(e,t){return(e-t.firstRowScreen)*t.lineHeight},this.drawTextMarker=function(t,n,i,s,o){var u=this.session,a=n.start.row,f=n.end.row,l=a,c=0,h=0,p=u.getScreenLastRowColumn(l),d=new r(l,n.start.column,l,h);for(;l<=f;l++)d.start.row=d.end.row=l,d.start.column=l==a?n.start.column:u.getRowWrapIndent(l),d.end.column=p,c=h,h=p,p=l+1<f?u.getScreenLastRowColumn(l+1):l==f?0:n.end.column,this.drawSingleLineMarker(t,d,i+(l==a?\" ace_start\":\"\")+\" ace_br\"+e(l==a||l==a+1&&n.start.column,c<h,h>p,l==f),s,l==f?0:1,o)},this.drawMultiLineMarker=function(e,t,n,r,i){var s=this.$padding,o=r.lineHeight,u=this.$getTop(t.start.row,r),a=s+t.start.column*r.characterWidth;i=i||\"\";if(this.session.$bidiHandler.isBidiRow(t.start.row)){var f=t.clone();f.end.row=f.start.row,f.end.column=this.session.getLine(f.start.row).length,this.drawBidiSingleLineMarker(e,f,n+\" ace_br1 ace_start\",r,null,i)}else this.elt(n+\" ace_br1 ace_start\",\"height:\"+o+\"px;\"+\"right:0;\"+\"top:\"+u+\"px;left:\"+a+\"px;\"+(i||\"\"));if(this.session.$bidiHandler.isBidiRow(t.end.row)){var f=t.clone();f.start.row=f.end.row,f.start.column=0,this.drawBidiSingleLineMarker(e,f,n+\" ace_br12\",r,null,i)}else{u=this.$getTop(t.end.row,r);var l=t.end.column*r.characterWidth;this.elt(n+\" ace_br12\",\"height:\"+o+\"px;\"+\"width:\"+l+\"px;\"+\"top:\"+u+\"px;\"+\"left:\"+s+\"px;\"+(i||\"\"))}o=(t.end.row-t.start.row-1)*r.lineHeight;if(o<=0)return;u=this.$getTop(t.start.row+1,r);var c=(t.start.column?1:0)|(t.end.column?0:8);this.elt(n+(c?\" ace_br\"+c:\"\"),\"height:\"+o+\"px;\"+\"right:0;\"+\"top:\"+u+\"px;\"+\"left:\"+s+\"px;\"+(i||\"\"))},this.drawSingleLineMarker=function(e,t,n,r,i,s){if(this.session.$bidiHandler.isBidiRow(t.start.row))return this.drawBidiSingleLineMarker(e,t,n,r,i,s);var o=r.lineHeight,u=(t.end.column+(i||0)-t.start.column)*r.characterWidth,a=this.$getTop(t.start.row,r),f=this.$padding+t.start.column*r.characterWidth;this.elt(n,\"height:\"+o+\"px;\"+\"width:\"+u+\"px;\"+\"top:\"+a+\"px;\"+\"left:\"+f+\"px;\"+(s||\"\"))},this.drawBidiSingleLineMarker=function(e,t,n,r,i,s){var o=r.lineHeight,u=this.$getTop(t.start.row,r),a=this.$padding,f=this.session.$bidiHandler.getSelections(t.start.column,t.end.column);f.forEach(function(e){this.elt(n,\"height:\"+o+\"px;\"+\"width:\"+e.width+(i||0)+\"px;\"+\"top:\"+u+\"px;\"+\"left:\"+(a+e.left)+\"px;\"+(s||\"\"))},this)},this.drawFullLineMarker=function(e,t,n,r,i){var s=this.$getTop(t.start.row,r),o=r.lineHeight;t.start.row!=t.end.row&&(o+=this.$getTop(t.end.row,r)-s),this.elt(n,\"height:\"+o+\"px;\"+\"top:\"+s+\"px;\"+\"left:0;right:0;\"+(i||\"\"))},this.drawScreenLineMarker=function(e,t,n,r,i){var s=this.$getTop(t.start.row,r),o=r.lineHeight;this.elt(n,\"height:\"+o+\"px;\"+\"top:\"+s+\"px;\"+\"left:0;right:0;\"+(i||\"\"))}}).call(s.prototype),t.Marker=s}),ace.define(\"ace/layer/text\",[\"require\",\"exports\",\"module\",\"ace/lib/oop\",\"ace/lib/dom\",\"ace/lib/lang\",\"ace/layer/lines\",\"ace/lib/event_emitter\"],function(e,t,n){\"use strict\";var r=e(\"../lib/oop\"),i=e(\"../lib/dom\"),s=e(\"../lib/lang\"),o=e(\"./lines\").Lines,u=e(\"../lib/event_emitter\").EventEmitter,a=function(e){this.dom=i,this.element=this.dom.createElement(\"div\"),this.element.className=\"ace_layer ace_text-layer\",e.appendChild(this.element),this.$updateEolChar=this.$updateEolChar.bind(this),this.$lines=new o(this.element)};(function(){r.implement(this,u),this.EOF_CHAR=\"\\u00b6\",this.EOL_CHAR_LF=\"\\u00ac\",this.EOL_CHAR_CRLF=\"\\u00a4\",this.EOL_CHAR=this.EOL_CHAR_LF,this.TAB_CHAR=\"\\u2014\",this.SPACE_CHAR=\"\\u00b7\",this.$padding=0,this.MAX_LINE_LENGTH=1e4,this.$updateEolChar=function(){var e=this.session.doc,t=e.getNewLineCharacter()==\"\\n\"&&e.getNewLineMode()!=\"windows\",n=t?this.EOL_CHAR_LF:this.EOL_CHAR_CRLF;if(this.EOL_CHAR!=n)return this.EOL_CHAR=n,!0},this.setPadding=function(e){this.$padding=e,this.element.style.margin=\"0 \"+e+\"px\"},this.getLineHeight=function(){return this.$fontMetrics.$characterSize.height||0},this.getCharacterWidth=function(){return this.$fontMetrics.$characterSize.width||0},this.$setFontMetrics=function(e){this.$fontMetrics=e,this.$fontMetrics.on(\"changeCharacterSize\",function(e){this._signal(\"changeCharacterSize\",e)}.bind(this)),this.$pollSizeChanges()},this.checkForSizeChanges=function(){this.$fontMetrics.checkForSizeChanges()},this.$pollSizeChanges=function(){return this.$pollSizeChangesTimer=this.$fontMetrics.$pollSizeChanges()},this.setSession=function(e){this.session=e,e&&this.$computeTabString()},this.showInvisibles=!1,this.setShowInvisibles=function(e){return this.showInvisibles==e?!1:(this.showInvisibles=e,this.$computeTabString(),!0)},this.displayIndentGuides=!0,this.setDisplayIndentGuides=function(e){return this.displayIndentGuides==e?!1:(this.displayIndentGuides=e,this.$computeTabString(),!0)},this.$tabStrings=[],this.onChangeTabSize=this.$computeTabString=function(){var e=this.session.getTabSize();this.tabSize=e;var t=this.$tabStrings=[0];for(var n=1;n<e+1;n++)if(this.showInvisibles){var r=this.dom.createElement(\"span\");r.className=\"ace_invisible ace_invisible_tab\",r.textContent=s.stringRepeat(this.TAB_CHAR,n),t.push(r)}else t.push(this.dom.createTextNode(s.stringRepeat(\" \",n),this.element));if(this.displayIndentGuides){this.$indentGuideRe=/\\s\\S| \\t|\\t |\\s$/;var i=\"ace_indent-guide\",o=\"\",u=\"\";if(this.showInvisibles){i+=\" ace_invisible\",o=\" ace_invisible_space\",u=\" ace_invisible_tab\";var a=s.stringRepeat(this.SPACE_CHAR,this.tabSize),f=s.stringRepeat(this.TAB_CHAR,this.tabSize)}else var a=s.stringRepeat(\" \",this.tabSize),f=a;var r=this.dom.createElement(\"span\");r.className=i+o,r.textContent=a,this.$tabStrings[\" \"]=r;var r=this.dom.createElement(\"span\");r.className=i+u,r.textContent=f,this.$tabStrings[\"\t\"]=r}},this.updateLines=function(e,t,n){if(this.config.lastRow!=e.lastRow||this.config.firstRow!=e.firstRow)return this.update(e);this.config=e;var r=Math.max(t,e.firstRow),i=Math.min(n,e.lastRow),s=this.element.childNodes,o=0;for(var u=e.firstRow;u<r;u++){var a=this.session.getFoldLine(u);if(a){if(a.containsRow(r)){r=a.start.row;break}u=a.end.row}o++}var f=!1,u=r,a=this.session.getNextFoldLine(u),l=a?a.start.row:Infinity;for(;;){u>l&&(u=a.end.row+1,a=this.session.getNextFoldLine(u,a),l=a?a.start.row:Infinity);if(u>i)break;var c=s[o++];if(c){this.dom.removeChildren(c),this.$renderLine(c,u,u==l?a:!1);var h=e.lineHeight*this.session.getRowLength(u)+\"px\";c.style.height!=h&&(f=!0,c.style.height=h)}u++}if(f)while(o<this.$lines.cells.length){var p=this.$lines.cells[o++];p.element.style.top=this.$lines.computeLineTop(p.row,e,this.session)+\"px\"}},this.scrollLines=function(e){var t=this.config;this.config=e;if(this.$lines.pageChanged(t,e))return this.update(e);this.$lines.moveContainer(e);var n=e.lastRow,r=t?t.lastRow:-1;if(!t||r<e.firstRow)return this.update(e);if(n<t.firstRow)return this.update(e);if(!t||t.lastRow<e.firstRow)return this.update(e);if(e.lastRow<t.firstRow)return this.update(e);if(t.firstRow<e.firstRow)for(var i=this.session.getFoldedRowCount(t.firstRow,e.firstRow-1);i>0;i--)this.$lines.shift();if(t.lastRow>e.lastRow)for(var i=this.session.getFoldedRowCount(e.lastRow+1,t.lastRow);i>0;i--)this.$lines.pop();e.firstRow<t.firstRow&&this.$lines.unshift(this.$renderLinesFragment(e,e.firstRow,t.firstRow-1)),e.lastRow>t.lastRow&&this.$lines.push(this.$renderLinesFragment(e,t.lastRow+1,e.lastRow))},this.$renderLinesFragment=function(e,t,n){var r=[],s=t,o=this.session.getNextFoldLine(s),u=o?o.start.row:Infinity;for(;;){s>u&&(s=o.end.row+1,o=this.session.getNextFoldLine(s,o),u=o?o.start.row:Infinity);if(s>n)break;var a=this.$lines.createCell(s,e,this.session),f=a.element;this.dom.removeChildren(f),i.setStyle(f.style,\"height\",this.$lines.computeLineHeight(s,e,this.session)+\"px\"),i.setStyle(f.style,\"top\",this.$lines.computeLineTop(s,e,this.session)+\"px\"),this.$renderLine(f,s,s==u?o:!1),this.$useLineGroups()?f.className=\"ace_line_group\":f.className=\"ace_line\",r.push(a),s++}return r},this.update=function(e){this.$lines.moveContainer(e),this.config=e;var t=e.firstRow,n=e.lastRow,r=this.$lines;while(r.getLength())r.pop();r.push(this.$renderLinesFragment(e,t,n))},this.$textToken={text:!0,rparen:!0,lparen:!0},this.$renderToken=function(e,t,n,r){var i=this,o=/(\\t)|( +)|([\\x00-\\x1f\\x80-\\xa0\\xad\\u1680\\u180E\\u2000-\\u200f\\u2028\\u2029\\u202F\\u205F\\uFEFF\\uFFF9-\\uFFFC]+)|(\\u3000)|([\\u1100-\\u115F\\u11A3-\\u11A7\\u11FA-\\u11FF\\u2329-\\u232A\\u2E80-\\u2E99\\u2E9B-\\u2EF3\\u2F00-\\u2FD5\\u2FF0-\\u2FFB\\u3001-\\u303E\\u3041-\\u3096\\u3099-\\u30FF\\u3105-\\u312D\\u3131-\\u318E\\u3190-\\u31BA\\u31C0-\\u31E3\\u31F0-\\u321E\\u3220-\\u3247\\u3250-\\u32FE\\u3300-\\u4DBF\\u4E00-\\uA48C\\uA490-\\uA4C6\\uA960-\\uA97C\\uAC00-\\uD7A3\\uD7B0-\\uD7C6\\uD7CB-\\uD7FB\\uF900-\\uFAFF\\uFE10-\\uFE19\\uFE30-\\uFE52\\uFE54-\\uFE66\\uFE68-\\uFE6B\\uFF01-\\uFF60\\uFFE0-\\uFFE6]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])/g,u=this.dom.createFragment(this.element),a,f=0;while(a=o.exec(r)){var l=a[1],c=a[2],h=a[3],p=a[4],d=a[5];if(!i.showInvisibles&&c)continue;var v=f!=a.index?r.slice(f,a.index):\"\";f=a.index+a[0].length,v&&u.appendChild(this.dom.createTextNode(v,this.element));if(l){var m=i.session.getScreenTabSize(t+a.index);u.appendChild(i.$tabStrings[m].cloneNode(!0)),t+=m-1}else if(c)if(i.showInvisibles){var g=this.dom.createElement(\"span\");g.className=\"ace_invisible ace_invisible_space\",g.textContent=s.stringRepeat(i.SPACE_CHAR,c.length),u.appendChild(g)}else u.appendChild(this.com.createTextNode(c,this.element));else if(h){var g=this.dom.createElement(\"span\");g.className=\"ace_invisible ace_invisible_space ace_invalid\",g.textContent=s.stringRepeat(i.SPACE_CHAR,h.length),u.appendChild(g)}else if(p){var y=i.showInvisibles?i.SPACE_CHAR:\"\";t+=1;var g=this.dom.createElement(\"span\");g.style.width=i.config.characterWidth*2+\"px\",g.className=i.showInvisibles?\"ace_cjk ace_invisible ace_invisible_space\":\"ace_cjk\",g.textContent=i.showInvisibles?i.SPACE_CHAR:\"\",u.appendChild(g)}else if(d){t+=1;var g=this.dom.createElement(\"span\");g.style.width=i.config.characterWidth*2+\"px\",g.className=\"ace_cjk\",g.textContent=d,u.appendChild(g)}}u.appendChild(this.dom.createTextNode(f?r.slice(f):r,this.element));if(!this.$textToken[n.type]){var b=\"ace_\"+n.type.replace(/\\./g,\" ace_\"),g=this.dom.createElement(\"span\");n.type==\"fold\"&&(g.style.width=n.value.length*this.config.characterWidth+\"px\"),g.className=b,g.appendChild(u),e.appendChild(g)}else e.appendChild(u);return t+r.length},this.renderIndentGuide=function(e,t,n){var r=t.search(this.$indentGuideRe);if(r<=0||r>=n)return t;if(t[0]==\" \"){r-=r%this.tabSize;var i=r/this.tabSize;for(var s=0;s<i;s++)e.appendChild(this.$tabStrings[\" \"].cloneNode(!0));return t.substr(r)}if(t[0]==\"\t\"){for(var s=0;s<r;s++)e.appendChild(this.$tabStrings[\"\t\"].cloneNode(!0));return t.substr(r)}return t},this.$createLineElement=function(e){var t=this.dom.createElement(\"div\");return t.className=\"ace_line\",t.style.height=this.config.lineHeight+\"px\",t},this.$renderWrappedLine=function(e,t,n){var r=0,i=0,o=n[0],u=0,a=this.$createLineElement();e.appendChild(a);for(var f=0;f<t.length;f++){var l=t[f],c=l.value;if(f==0&&this.displayIndentGuides){r=c.length,c=this.renderIndentGuide(a,c,o);if(!c)continue;r-=c.length}if(r+c.length<o)u=this.$renderToken(a,u,l,c),r+=c.length;else{while(r+c.length>=o)u=this.$renderToken(a,u,l,c.substring(0,o-r)),c=c.substring(o-r),r=o,a=this.$createLineElement(),e.appendChild(a),a.appendChild(this.dom.createTextNode(s.stringRepeat(\"\\u00a0\",n.indent),this.element)),i++,u=0,o=n[i]||Number.MAX_VALUE;c.length!=0&&(r+=c.length,u=this.$renderToken(a,u,l,c))}}},this.$renderSimpleLine=function(e,t){var n=0,r=t[0],i=r.value;this.displayIndentGuides&&(i=this.renderIndentGuide(e,i)),i&&(n=this.$renderToken(e,n,r,i));for(var s=1;s<t.length;s++){r=t[s],i=r.value;if(n+i.length>this.MAX_LINE_LENGTH)return this.$renderOverflowMessage(e,n,r,i);n=this.$renderToken(e,n,r,i)}},this.$renderOverflowMessage=function(e,t,n,r){this.$renderToken(e,t,n,r.slice(0,this.MAX_LINE_LENGTH-t));var i=this.dom.createElement(\"span\");i.className=\"ace_inline_button ace_keyword ace_toggle_wrap\",i.style.position=\"absolute\",i.style.right=\"0\",i.textContent=\"<click to see more...>\",e.appendChild(i)},this.$renderLine=function(e,t,n){!n&&n!=0&&(n=this.session.getFoldLine(t));if(n)var r=this.$getFoldLineTokens(t,n);else var r=this.session.getTokens(t);var i=e;if(r.length){var s=this.session.getRowSplitData(t);if(s&&s.length){this.$renderWrappedLine(e,r,s);var i=e.lastChild}else{var i=e;this.$useLineGroups()&&(i=this.$createLineElement(),e.appendChild(i)),this.$renderSimpleLine(i,r)}}else this.$useLineGroups()&&(i=this.$createLineElement(),e.appendChild(i));if(this.showInvisibles&&i){n&&(t=n.end.row);var o=this.dom.createElement(\"span\");o.className=\"ace_invisible ace_invisible_eol\",o.textContent=t==this.session.getLength()-1?this.EOF_CHAR:this.EOL_CHAR,i.appendChild(o)}},this.$getFoldLineTokens=function(e,t){function i(e,t,n){var i=0,s=0;while(s+e[i].value.length<t){s+=e[i].value.length,i++;if(i==e.length)return}if(s!=t){var o=e[i].value.substring(t-s);o.length>n-t&&(o=o.substring(0,n-t)),r.push({type:e[i].type,value:o}),s=t+o.length,i+=1}while(s<n&&i<e.length){var o=e[i].value;o.length+s>n?r.push({type:e[i].type,value:o.substring(0,n-s)}):r.push(e[i]),s+=o.length,i+=1}}var n=this.session,r=[],s=n.getTokens(e);return t.walk(function(e,t,o,u,a){e!=null?r.push({type:\"fold\",value:e}):(a&&(s=n.getTokens(t)),s.length&&i(s,u,o))},t.end.row,this.session.getLine(t.end.row).length),r},this.$useLineGroups=function(){return this.session.getUseWrapMode()},this.destroy=function(){}}).call(a.prototype),t.Text=a}),ace.define(\"ace/layer/cursor\",[\"require\",\"exports\",\"module\",\"ace/lib/dom\"],function(e,t,n){\"use strict\";var r=e(\"../lib/dom\"),i=function(e){this.element=r.createElement(\"div\"),this.element.className=\"ace_layer ace_cursor-layer\",e.appendChild(this.element),this.isVisible=!1,this.isBlinking=!0,this.blinkInterval=1e3,this.smoothBlinking=!1,this.cursors=[],this.cursor=this.addCursor(),r.addCssClass(this.element,\"ace_hidden-cursors\"),this.$updateCursors=this.$updateOpacity.bind(this)};(function(){this.$updateOpacity=function(e){var t=this.cursors;for(var n=t.length;n--;)r.setStyle(t[n].style,\"opacity\",e?\"\":\"0\")},this.$startCssAnimation=function(){var e=this.cursors;for(var t=e.length;t--;)e[t].style.animationDuration=this.blinkInterval+\"ms\";setTimeout(function(){r.addCssClass(this.element,\"ace_animate-blinking\")}.bind(this))},this.$stopCssAnimation=function(){r.removeCssClass(this.element,\"ace_animate-blinking\")},this.$padding=0,this.setPadding=function(e){this.$padding=e},this.setSession=function(e){this.session=e},this.setBlinking=function(e){e!=this.isBlinking&&(this.isBlinking=e,this.restartTimer())},this.setBlinkInterval=function(e){e!=this.blinkInterval&&(this.blinkInterval=e,this.restartTimer())},this.setSmoothBlinking=function(e){e!=this.smoothBlinking&&(this.smoothBlinking=e,r.setCssClass(this.element,\"ace_smooth-blinking\",e),this.$updateCursors(!0),this.restartTimer())},this.addCursor=function(){var e=r.createElement(\"div\");return e.className=\"ace_cursor\",this.element.appendChild(e),this.cursors.push(e),e},this.removeCursor=function(){if(this.cursors.length>1){var e=this.cursors.pop();return e.parentNode.removeChild(e),e}},this.hideCursor=function(){this.isVisible=!1,r.addCssClass(this.element,\"ace_hidden-cursors\"),this.restartTimer()},this.showCursor=function(){this.isVisible=!0,r.removeCssClass(this.element,\"ace_hidden-cursors\"),this.restartTimer()},this.restartTimer=function(){var e=this.$updateCursors;clearInterval(this.intervalId),clearTimeout(this.timeoutId),this.$stopCssAnimation(),this.smoothBlinking&&r.removeCssClass(this.element,\"ace_smooth-blinking\"),e(!0);if(!this.isBlinking||!this.blinkInterval||!this.isVisible){this.$stopCssAnimation();return}this.smoothBlinking&&setTimeout(function(){r.addCssClass(this.element,\"ace_smooth-blinking\")}.bind(this));if(r.HAS_CSS_ANIMATION)this.$startCssAnimation();else{var t=function(){this.timeoutId=setTimeout(function(){e(!1)},.6*this.blinkInterval)}.bind(this);this.intervalId=setInterval(function(){e(!0),t()},this.blinkInterval),t()}},this.getPixelPosition=function(e,t){if(!this.config||!this.session)return{left:0,top:0};e||(e=this.session.selection.getCursor());var n=this.session.documentToScreenPosition(e),r=this.$padding+(this.session.$bidiHandler.isBidiRow(n.row,e.row)?this.session.$bidiHandler.getPosLeft(n.column):n.column*this.config.characterWidth),i=(n.row-(t?this.config.firstRowScreen:0))*this.config.lineHeight;return{left:r,top:i}},this.isCursorInView=function(e,t){return e.top>=0&&e.top<t.maxHeight},this.update=function(e){this.config=e;var t=this.session.$selectionMarkers,n=0,i=0;if(t===undefined||t.length===0)t=[{cursor:null}];for(var n=0,s=t.length;n<s;n++){var o=this.getPixelPosition(t[n].cursor,!0);if((o.top>e.height+e.offset||o.top<0)&&n>1)continue;var u=this.cursors[i++]||this.addCursor(),a=u.style;this.drawCursor?this.drawCursor(u,o,e,t[n],this.session):this.isCursorInView(o,e)?(r.setStyle(a,\"display\",\"block\"),r.translate(u,o.left,o.top),r.setStyle(a,\"width\",Math.round(e.characterWidth)+\"px\"),r.setStyle(a,\"height\",e.lineHeight+\"px\")):r.setStyle(a,\"display\",\"none\")}while(this.cursors.length>i)this.removeCursor();var f=this.session.getOverwrite();this.$setOverwrite(f),this.$pixelPos=o,this.restartTimer()},this.drawCursor=null,this.$setOverwrite=function(e){e!=this.overwrite&&(this.overwrite=e,e?r.addCssClass(this.element,\"ace_overwrite-cursors\"):r.removeCssClass(this.element,\"ace_overwrite-cursors\"))},this.destroy=function(){clearInterval(this.intervalId),clearTimeout(this.timeoutId)}}).call(i.prototype),t.Cursor=i}),ace.define(\"ace/scrollbar\",[\"require\",\"exports\",\"module\",\"ace/lib/oop\",\"ace/lib/dom\",\"ace/lib/event\",\"ace/lib/event_emitter\"],function(e,t,n){\"use strict\";var r=e(\"./lib/oop\"),i=e(\"./lib/dom\"),s=e(\"./lib/event\"),o=e(\"./lib/event_emitter\").EventEmitter,u=32768,a=function(e){this.element=i.createElement(\"div\"),this.element.className=\"ace_scrollbar ace_scrollbar\"+this.classSuffix,this.inner=i.createElement(\"div\"),this.inner.className=\"ace_scrollbar-inner\",this.element.appendChild(this.inner),e.appendChild(this.element),this.setVisible(!1),this.skipEvent=!1,s.addListener(this.element,\"scroll\",this.onScroll.bind(this)),s.addListener(this.element,\"mousedown\",s.preventDefault)};(function(){r.implement(this,o),this.setVisible=function(e){this.element.style.display=e?\"\":\"none\",this.isVisible=e,this.coeff=1}}).call(a.prototype);var f=function(e,t){a.call(this,e),this.scrollTop=0,this.scrollHeight=0,t.$scrollbarWidth=this.width=i.scrollbarWidth(e.ownerDocument),this.inner.style.width=this.element.style.width=(this.width||15)+5+\"px\",this.$minWidth=0};r.inherits(f,a),function(){this.classSuffix=\"-v\",this.onScroll=function(){if(!this.skipEvent){this.scrollTop=this.element.scrollTop;if(this.coeff!=1){var e=this.element.clientHeight/this.scrollHeight;this.scrollTop=this.scrollTop*(1-e)/(this.coeff-e)}this._emit(\"scroll\",{data:this.scrollTop})}this.skipEvent=!1},this.getWidth=function(){return Math.max(this.isVisible?this.width:0,this.$minWidth||0)},this.setHeight=function(e){this.element.style.height=e+\"px\"},this.setInnerHeight=this.setScrollHeight=function(e){this.scrollHeight=e,e>u?(this.coeff=u/e,e=u):this.coeff!=1&&(this.coeff=1),this.inner.style.height=e+\"px\"},this.setScrollTop=function(e){this.scrollTop!=e&&(this.skipEvent=!0,this.scrollTop=e,this.element.scrollTop=e*this.coeff)}}.call(f.prototype);var l=function(e,t){a.call(this,e),this.scrollLeft=0,this.height=t.$scrollbarWidth,this.inner.style.height=this.element.style.height=(this.height||15)+5+\"px\"};r.inherits(l,a),function(){this.classSuffix=\"-h\",this.onScroll=function(){this.skipEvent||(this.scrollLeft=this.element.scrollLeft,this._emit(\"scroll\",{data:this.scrollLeft})),this.skipEvent=!1},this.getHeight=function(){return this.isVisible?this.height:0},this.setWidth=function(e){this.element.style.width=e+\"px\"},this.setInnerWidth=function(e){this.inner.style.width=e+\"px\"},this.setScrollWidth=function(e){this.inner.style.width=e+\"px\"},this.setScrollLeft=function(e){this.scrollLeft!=e&&(this.skipEvent=!0,this.scrollLeft=this.element.scrollLeft=e)}}.call(l.prototype),t.ScrollBar=f,t.ScrollBarV=f,t.ScrollBarH=l,t.VScrollBar=f,t.HScrollBar=l}),ace.define(\"ace/renderloop\",[\"require\",\"exports\",\"module\",\"ace/lib/event\"],function(e,t,n){\"use strict\";var r=e(\"./lib/event\"),i=function(e,t){this.onRender=e,this.pending=!1,this.changes=0,this.$recursionLimit=2,this.window=t||window;var n=this;this._flush=function(e){n.pending=!1;var t=n.changes;t&&(r.blockIdle(100),n.changes=0,n.onRender(t));if(n.changes){if(n.$recursionLimit--<0)return;n.schedule()}else n.$recursionLimit=2}};(function(){this.schedule=function(e){this.changes=this.changes|e,this.changes&&!this.pending&&(r.nextFrame(this._flush),this.pending=!0)},this.clear=function(e){var t=this.changes;return this.changes=0,t}}).call(i.prototype),t.RenderLoop=i}),ace.define(\"ace/layer/font_metrics\",[\"require\",\"exports\",\"module\",\"ace/lib/oop\",\"ace/lib/dom\",\"ace/lib/lang\",\"ace/lib/event\",\"ace/lib/useragent\",\"ace/lib/event_emitter\"],function(e,t,n){var r=e(\"../lib/oop\"),i=e(\"../lib/dom\"),s=e(\"../lib/lang\"),o=e(\"../lib/event\"),u=e(\"../lib/useragent\"),a=e(\"../lib/event_emitter\").EventEmitter,f=256,l=typeof ResizeObserver==\"function\",c=200,h=t.FontMetrics=function(e){this.el=i.createElement(\"div\"),this.$setMeasureNodeStyles(this.el.style,!0),this.$main=i.createElement(\"div\"),this.$setMeasureNodeStyles(this.$main.style),this.$measureNode=i.createElement(\"div\"),this.$setMeasureNodeStyles(this.$measureNode.style),this.el.appendChild(this.$main),this.el.appendChild(this.$measureNode),e.appendChild(this.el),this.$measureNode.innerHTML=s.stringRepeat(\"X\",f),this.$characterSize={width:0,height:0},l?this.$addObserver():this.checkForSizeChanges()};(function(){r.implement(this,a),this.$characterSize={width:0,height:0},this.$setMeasureNodeStyles=function(e,t){e.width=e.height=\"auto\",e.left=e.top=\"0px\",e.visibility=\"hidden\",e.position=\"absolute\",e.whiteSpace=\"pre\",u.isIE<8?e[\"font-family\"]=\"inherit\":e.font=\"inherit\",e.overflow=t?\"hidden\":\"visible\"},this.checkForSizeChanges=function(e){e===undefined&&(e=this.$measureSizes());if(e&&(this.$characterSize.width!==e.width||this.$characterSize.height!==e.height)){this.$measureNode.style.fontWeight=\"bold\";var t=this.$measureSizes();this.$measureNode.style.fontWeight=\"\",this.$characterSize=e,this.charSizes=Object.create(null),this.allowBoldFonts=t&&t.width===e.width&&t.height===e.height,this._emit(\"changeCharacterSize\",{data:e})}},this.$addObserver=function(){var e=this;this.$observer=new window.ResizeObserver(function(t){var n=t[0].contentRect;e.checkForSizeChanges({height:n.height,width:n.width/f})}),this.$observer.observe(this.$measureNode)},this.$pollSizeChanges=function(){if(this.$pollSizeChangesTimer||this.$observer)return this.$pollSizeChangesTimer;var e=this;return this.$pollSizeChangesTimer=o.onIdle(function t(){e.checkForSizeChanges(),o.onIdle(t,500)},500)},this.setPolling=function(e){e?this.$pollSizeChanges():this.$pollSizeChangesTimer&&(clearInterval(this.$pollSizeChangesTimer),this.$pollSizeChangesTimer=0)},this.$measureSizes=function(e){var t={height:(e||this.$measureNode).clientHeight,width:(e||this.$measureNode).clientWidth/f};return t.width===0||t.height===0?null:t},this.$measureCharWidth=function(e){this.$main.innerHTML=s.stringRepeat(e,f);var t=this.$main.getBoundingClientRect();return t.width/f},this.getCharacterWidth=function(e){var t=this.charSizes[e];return t===undefined&&(t=this.charSizes[e]=this.$measureCharWidth(e)/this.$characterSize.width),t},this.destroy=function(){clearInterval(this.$pollSizeChangesTimer),this.$observer&&this.$observer.disconnect(),this.el&&this.el.parentNode&&this.el.parentNode.removeChild(this.el)},this.$getZoom=function e(t){return t?(window.getComputedStyle(t).zoom||1)*e(t.parentElement):1},this.$initTransformMeasureNodes=function(){var e=function(e,t){return[\"div\",{style:\"position: absolute;top:\"+e+\"px;left:\"+t+\"px;\"}]};this.els=i.buildDom([e(0,0),e(c,0),e(0,c),e(c,c)],this.el)},this.transformCoordinates=function(e,t){function r(e,t,n){var r=e[1]*t[0]-e[0]*t[1];return[(-t[1]*n[0]+t[0]*n[1])/r,(+e[1]*n[0]-e[0]*n[1])/r]}function i(e,t){return[e[0]-t[0],e[1]-t[1]]}function s(e,t){return[e[0]+t[0],e[1]+t[1]]}function o(e,t){return[e*t[0],e*t[1]]}function u(e){var t=e.getBoundingClientRect();return[t.left,t.top]}if(e){var n=this.$getZoom(this.el);e=o(1/n,e)}this.els||this.$initTransformMeasureNodes();var a=u(this.els[0]),f=u(this.els[1]),l=u(this.els[2]),h=u(this.els[3]),p=r(i(h,f),i(h,l),i(s(f,l),s(h,a))),d=o(1+p[0],i(f,a)),v=o(1+p[1],i(l,a));if(t){var m=t,g=p[0]*m[0]/c+p[1]*m[1]/c+1,y=s(o(m[0],d),o(m[1],v));return s(o(1/g/c,y),a)}var b=i(e,a),w=r(i(d,o(p[0],b)),i(v,o(p[1],b)),b);return o(c,w)}}).call(h.prototype)}),ace.define(\"ace/virtual_renderer\",[\"require\",\"exports\",\"module\",\"ace/lib/oop\",\"ace/lib/dom\",\"ace/config\",\"ace/layer/gutter\",\"ace/layer/marker\",\"ace/layer/text\",\"ace/layer/cursor\",\"ace/scrollbar\",\"ace/scrollbar\",\"ace/renderloop\",\"ace/layer/font_metrics\",\"ace/lib/event_emitter\",\"ace/lib/useragent\"],function(e,t,n){\"use strict\";var r=e(\"./lib/oop\"),i=e(\"./lib/dom\"),s=e(\"./config\"),o=e(\"./layer/gutter\").Gutter,u=e(\"./layer/marker\").Marker,a=e(\"./layer/text\").Text,f=e(\"./layer/cursor\").Cursor,l=e(\"./scrollbar\").HScrollBar,c=e(\"./scrollbar\").VScrollBar,h=e(\"./renderloop\").RenderLoop,p=e(\"./layer/font_metrics\").FontMetrics,d=e(\"./lib/event_emitter\").EventEmitter,v='.ace_br1 {border-top-left-radius    : 3px;}.ace_br2 {border-top-right-radius   : 3px;}.ace_br3 {border-top-left-radius    : 3px; border-top-right-radius:    3px;}.ace_br4 {border-bottom-right-radius: 3px;}.ace_br5 {border-top-left-radius    : 3px; border-bottom-right-radius: 3px;}.ace_br6 {border-top-right-radius   : 3px; border-bottom-right-radius: 3px;}.ace_br7 {border-top-left-radius    : 3px; border-top-right-radius:    3px; border-bottom-right-radius: 3px;}.ace_br8 {border-bottom-left-radius : 3px;}.ace_br9 {border-top-left-radius    : 3px; border-bottom-left-radius:  3px;}.ace_br10{border-top-right-radius   : 3px; border-bottom-left-radius:  3px;}.ace_br11{border-top-left-radius    : 3px; border-top-right-radius:    3px; border-bottom-left-radius:  3px;}.ace_br12{border-bottom-right-radius: 3px; border-bottom-left-radius:  3px;}.ace_br13{border-top-left-radius    : 3px; border-bottom-right-radius: 3px; border-bottom-left-radius:  3px;}.ace_br14{border-top-right-radius   : 3px; border-bottom-right-radius: 3px; border-bottom-left-radius:  3px;}.ace_br15{border-top-left-radius    : 3px; border-top-right-radius:    3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px;}.ace_editor {position: relative;overflow: hidden;font: 12px/normal \\'Monaco\\', \\'Menlo\\', \\'Ubuntu Mono\\', \\'Consolas\\', \\'source-code-pro\\', monospace;direction: ltr;text-align: left;-webkit-tap-highlight-color: rgba(0, 0, 0, 0);}.ace_scroller {position: absolute;overflow: hidden;top: 0;bottom: 0;background-color: inherit;-ms-user-select: none;-moz-user-select: none;-webkit-user-select: none;user-select: none;cursor: text;}.ace_content {position: absolute;box-sizing: border-box;min-width: 100%;contain: style size layout;}.ace_dragging .ace_scroller:before{position: absolute;top: 0;left: 0;right: 0;bottom: 0;content: \\'\\';background: rgba(250, 250, 250, 0.01);z-index: 1000;}.ace_dragging.ace_dark .ace_scroller:before{background: rgba(0, 0, 0, 0.01);}.ace_selecting, .ace_selecting * {cursor: text !important;}.ace_gutter {position: absolute;overflow : hidden;width: auto;top: 0;bottom: 0;left: 0;cursor: default;z-index: 4;-ms-user-select: none;-moz-user-select: none;-webkit-user-select: none;user-select: none;contain: style size layout;}.ace_gutter-active-line {position: absolute;left: 0;right: 0;}.ace_scroller.ace_scroll-left {box-shadow: 17px 0 16px -16px rgba(0, 0, 0, 0.4) inset;}.ace_gutter-cell {position: absolute;top: 0;left: 0;right: 0;padding-left: 19px;padding-right: 6px;background-repeat: no-repeat;}.ace_gutter-cell.ace_error {background-image: url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAABOFBMVEX/////////QRswFAb/Ui4wFAYwFAYwFAaWGAfDRymzOSH/PxswFAb/SiUwFAYwFAbUPRvjQiDllog5HhHdRybsTi3/Tyv9Tir+Syj/UC3////XurebMBIwFAb/RSHbPx/gUzfdwL3kzMivKBAwFAbbvbnhPx66NhowFAYwFAaZJg8wFAaxKBDZurf/RB6mMxb/SCMwFAYwFAbxQB3+RB4wFAb/Qhy4Oh+4QifbNRcwFAYwFAYwFAb/QRzdNhgwFAYwFAbav7v/Uy7oaE68MBK5LxLewr/r2NXewLswFAaxJw4wFAbkPRy2PyYwFAaxKhLm1tMwFAazPiQwFAaUGAb/QBrfOx3bvrv/VC/maE4wFAbRPBq6MRO8Qynew8Dp2tjfwb0wFAbx6eju5+by6uns4uH9/f36+vr/GkHjAAAAYnRSTlMAGt+64rnWu/bo8eAA4InH3+DwoN7j4eLi4xP99Nfg4+b+/u9B/eDs1MD1mO7+4PHg2MXa347g7vDizMLN4eG+Pv7i5evs/v79yu7S3/DV7/498Yv24eH+4ufQ3Ozu/v7+y13sRqwAAADLSURBVHjaZc/XDsFgGIBhtDrshlitmk2IrbHFqL2pvXf/+78DPokj7+Fz9qpU/9UXJIlhmPaTaQ6QPaz0mm+5gwkgovcV6GZzd5JtCQwgsxoHOvJO15kleRLAnMgHFIESUEPmawB9ngmelTtipwwfASilxOLyiV5UVUyVAfbG0cCPHig+GBkzAENHS0AstVF6bacZIOzgLmxsHbt2OecNgJC83JERmePUYq8ARGkJx6XtFsdddBQgZE2nPR6CICZhawjA4Fb/chv+399kfR+MMMDGOQAAAABJRU5ErkJggg==\");background-repeat: no-repeat;background-position: 2px center;}.ace_gutter-cell.ace_warning {background-image: url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAmVBMVEX///8AAAD///8AAAAAAABPSzb/5sAAAAB/blH/73z/ulkAAAAAAAD85pkAAAAAAAACAgP/vGz/rkDerGbGrV7/pkQICAf////e0IsAAAD/oED/qTvhrnUAAAD/yHD/njcAAADuv2r/nz//oTj/p064oGf/zHAAAAA9Nir/tFIAAAD/tlTiuWf/tkIAAACynXEAAAAAAAAtIRW7zBpBAAAAM3RSTlMAABR1m7RXO8Ln31Z36zT+neXe5OzooRDfn+TZ4p3h2hTf4t3k3ucyrN1K5+Xaks52Sfs9CXgrAAAAjklEQVR42o3PbQ+CIBQFYEwboPhSYgoYunIqqLn6/z8uYdH8Vmdnu9vz4WwXgN/xTPRD2+sgOcZjsge/whXZgUaYYvT8QnuJaUrjrHUQreGczuEafQCO/SJTufTbroWsPgsllVhq3wJEk2jUSzX3CUEDJC84707djRc5MTAQxoLgupWRwW6UB5fS++NV8AbOZgnsC7BpEAAAAABJRU5ErkJggg==\");background-position: 2px center;}.ace_gutter-cell.ace_info {background-image: url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAAAAAA6mKC9AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAAJ0Uk5TAAB2k804AAAAPklEQVQY02NgIB68QuO3tiLznjAwpKTgNyDbMegwisCHZUETUZV0ZqOquBpXj2rtnpSJT1AEnnRmL2OgGgAAIKkRQap2htgAAAAASUVORK5CYII=\");background-position: 2px center;}.ace_dark .ace_gutter-cell.ace_info {background-image: url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQBAMAAADt3eJSAAAAJFBMVEUAAAChoaGAgIAqKiq+vr6tra1ZWVmUlJSbm5s8PDxubm56enrdgzg3AAAAAXRSTlMAQObYZgAAAClJREFUeNpjYMAPdsMYHegyJZFQBlsUlMFVCWUYKkAZMxZAGdxlDMQBAG+TBP4B6RyJAAAAAElFTkSuQmCC\");}.ace_scrollbar {contain: strict;position: absolute;right: 0;bottom: 0;z-index: 6;}.ace_scrollbar-inner {position: absolute;cursor: text;left: 0;top: 0;}.ace_scrollbar-v{overflow-x: hidden;overflow-y: scroll;top: 0;}.ace_scrollbar-h {overflow-x: scroll;overflow-y: hidden;left: 0;}.ace_print-margin {position: absolute;height: 100%;}.ace_text-input {position: absolute;z-index: 0;width: 0.5em;height: 1em;opacity: 0;background: transparent;-moz-appearance: none;appearance: none;border: none;resize: none;outline: none;overflow: hidden;font: inherit;padding: 0 1px;margin: 0 -1px;contain: strict;-ms-user-select: text;-moz-user-select: text;-webkit-user-select: text;user-select: text;white-space: pre!important;}.ace_text-input.ace_composition {background: transparent;color: inherit;z-index: 1000;opacity: 1;}.ace_composition_placeholder { color: transparent }.ace_composition_marker { border-bottom: 1px solid;position: absolute;border-radius: 0;margin-top: 1px;}[ace_nocontext=true] {transform: none!important;filter: none!important;perspective: none!important;clip-path: none!important;mask : none!important;contain: none!important;perspective: none!important;mix-blend-mode: initial!important;z-index: auto;}.ace_layer {z-index: 1;position: absolute;overflow: hidden;word-wrap: normal;white-space: pre;height: 100%;width: 100%;box-sizing: border-box;pointer-events: none;}.ace_gutter-layer {position: relative;width: auto;text-align: right;pointer-events: auto;height: 1000000px;contain: style size layout;}.ace_text-layer {font: inherit !important;position: absolute;height: 1000000px;width: 1000000px;contain: style size layout;}.ace_text-layer > .ace_line, .ace_text-layer > .ace_line_group {contain: style size layout;position: absolute;top: 0;left: 0;right: 0;}.ace_hidpi .ace_text-layer,.ace_hidpi .ace_gutter-layer,.ace_hidpi .ace_content,.ace_hidpi .ace_gutter {contain: strict;will-change: transform;}.ace_hidpi .ace_text-layer > .ace_line, .ace_hidpi .ace_text-layer > .ace_line_group {contain: strict;}.ace_cjk {display: inline-block;text-align: center;}.ace_cursor-layer {z-index: 4;}.ace_cursor {z-index: 4;position: absolute;box-sizing: border-box;border-left: 2px solid;transform: translatez(0);}.ace_multiselect .ace_cursor {border-left-width: 1px;}.ace_slim-cursors .ace_cursor {border-left-width: 1px;}.ace_overwrite-cursors .ace_cursor {border-left-width: 0;border-bottom: 1px solid;}.ace_hidden-cursors .ace_cursor {opacity: 0.2;}.ace_smooth-blinking .ace_cursor {transition: opacity 0.18s;}.ace_animate-blinking .ace_cursor {animation-duration: 1000ms;animation-timing-function: step-end;animation-name: blink-ace-animate;animation-iteration-count: infinite;}.ace_animate-blinking.ace_smooth-blinking .ace_cursor {animation-duration: 1000ms;animation-timing-function: ease-in-out;animation-name: blink-ace-animate-smooth;}@keyframes blink-ace-animate {from, to { opacity: 1; }60% { opacity: 0; }}@keyframes blink-ace-animate-smooth {from, to { opacity: 1; }45% { opacity: 1; }60% { opacity: 0; }85% { opacity: 0; }}.ace_marker-layer .ace_step, .ace_marker-layer .ace_stack {position: absolute;z-index: 3;}.ace_marker-layer .ace_selection {position: absolute;z-index: 5;}.ace_marker-layer .ace_bracket {position: absolute;z-index: 6;}.ace_marker-layer .ace_active-line {position: absolute;z-index: 2;}.ace_marker-layer .ace_selected-word {position: absolute;z-index: 4;box-sizing: border-box;}.ace_line .ace_fold {box-sizing: border-box;display: inline-block;height: 11px;margin-top: -2px;vertical-align: middle;background-image:url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABEAAAAJCAYAAADU6McMAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAJpJREFUeNpi/P//PwOlgAXGYGRklAVSokD8GmjwY1wasKljQpYACtpCFeADcHVQfQyMQAwzwAZI3wJKvCLkfKBaMSClBlR7BOQikCFGQEErIH0VqkabiGCAqwUadAzZJRxQr/0gwiXIal8zQQPnNVTgJ1TdawL0T5gBIP1MUJNhBv2HKoQHHjqNrA4WO4zY0glyNKLT2KIfIMAAQsdgGiXvgnYAAAAASUVORK5CYII=\"),url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAA3CAYAAADNNiA5AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAACJJREFUeNpi+P//fxgTAwPDBxDxD078RSX+YeEyDFMCIMAAI3INmXiwf2YAAAAASUVORK5CYII=\");background-repeat: no-repeat, repeat-x;background-position: center center, top left;color: transparent;border: 1px solid black;border-radius: 2px;cursor: pointer;pointer-events: auto;}.ace_dark .ace_fold {}.ace_fold:hover{background-image:url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABEAAAAJCAYAAADU6McMAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAJpJREFUeNpi/P//PwOlgAXGYGRklAVSokD8GmjwY1wasKljQpYACtpCFeADcHVQfQyMQAwzwAZI3wJKvCLkfKBaMSClBlR7BOQikCFGQEErIH0VqkabiGCAqwUadAzZJRxQr/0gwiXIal8zQQPnNVTgJ1TdawL0T5gBIP1MUJNhBv2HKoQHHjqNrA4WO4zY0glyNKLT2KIfIMAAQsdgGiXvgnYAAAAASUVORK5CYII=\"),url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAA3CAYAAADNNiA5AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAACBJREFUeNpi+P//fz4TAwPDZxDxD5X4i5fLMEwJgAADAEPVDbjNw87ZAAAAAElFTkSuQmCC\");}.ace_tooltip {background-color: #FFF;background-image: linear-gradient(to bottom, transparent, rgba(0, 0, 0, 0.1));border: 1px solid gray;border-radius: 1px;box-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);color: black;max-width: 100%;padding: 3px 4px;position: fixed;z-index: 999999;box-sizing: border-box;cursor: default;white-space: pre;word-wrap: break-word;line-height: normal;font-style: normal;font-weight: normal;letter-spacing: normal;pointer-events: none;}.ace_folding-enabled > .ace_gutter-cell {padding-right: 13px;}.ace_fold-widget {box-sizing: border-box;margin: 0 -12px 0 1px;display: none;width: 11px;vertical-align: top;background-image: url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAANElEQVR42mWKsQ0AMAzC8ixLlrzQjzmBiEjp0A6WwBCSPgKAXoLkqSot7nN3yMwR7pZ32NzpKkVoDBUxKAAAAABJRU5ErkJggg==\");background-repeat: no-repeat;background-position: center;border-radius: 3px;border: 1px solid transparent;cursor: pointer;}.ace_folding-enabled .ace_fold-widget {display: inline-block;   }.ace_fold-widget.ace_end {background-image: url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAANElEQVR42m3HwQkAMAhD0YzsRchFKI7sAikeWkrxwScEB0nh5e7KTPWimZki4tYfVbX+MNl4pyZXejUO1QAAAABJRU5ErkJggg==\");}.ace_fold-widget.ace_closed {background-image: url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAGCAYAAAAG5SQMAAAAOUlEQVR42jXKwQkAMAgDwKwqKD4EwQ26sSOkVWjgIIHAzPiCgaqiqnJHZnKICBERHN194O5b9vbLuAVRL+l0YWnZAAAAAElFTkSuQmCCXA==\");}.ace_fold-widget:hover {border: 1px solid rgba(0, 0, 0, 0.3);background-color: rgba(255, 255, 255, 0.2);box-shadow: 0 1px 1px rgba(255, 255, 255, 0.7);}.ace_fold-widget:active {border: 1px solid rgba(0, 0, 0, 0.4);background-color: rgba(0, 0, 0, 0.05);box-shadow: 0 1px 1px rgba(255, 255, 255, 0.8);}.ace_dark .ace_fold-widget {background-image: url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHklEQVQIW2P4//8/AzoGEQ7oGCaLLAhWiSwB146BAQCSTPYocqT0AAAAAElFTkSuQmCC\");}.ace_dark .ace_fold-widget.ace_end {background-image: url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAH0lEQVQIW2P4//8/AxQ7wNjIAjDMgC4AxjCVKBirIAAF0kz2rlhxpAAAAABJRU5ErkJggg==\");}.ace_dark .ace_fold-widget.ace_closed {background-image: url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAFCAYAAACAcVaiAAAAHElEQVQIW2P4//+/AxAzgDADlOOAznHAKgPWAwARji8UIDTfQQAAAABJRU5ErkJggg==\");}.ace_dark .ace_fold-widget:hover {box-shadow: 0 1px 1px rgba(255, 255, 255, 0.2);background-color: rgba(255, 255, 255, 0.1);}.ace_dark .ace_fold-widget:active {box-shadow: 0 1px 1px rgba(255, 255, 255, 0.2);}.ace_inline_button {border: 1px solid lightgray;display: inline-block;margin: -1px 8px;padding: 0 5px;pointer-events: auto;cursor: pointer;}.ace_inline_button:hover {border-color: gray;background: rgba(200,200,200,0.2);display: inline-block;pointer-events: auto;}.ace_fold-widget.ace_invalid {background-color: #FFB4B4;border-color: #DE5555;}.ace_fade-fold-widgets .ace_fold-widget {transition: opacity 0.4s ease 0.05s;opacity: 0;}.ace_fade-fold-widgets:hover .ace_fold-widget {transition: opacity 0.05s ease 0.05s;opacity:1;}.ace_underline {text-decoration: underline;}.ace_bold {font-weight: bold;}.ace_nobold .ace_bold {font-weight: normal;}.ace_italic {font-style: italic;}.ace_error-marker {background-color: rgba(255, 0, 0,0.2);position: absolute;z-index: 9;}.ace_highlight-marker {background-color: rgba(255, 255, 0,0.2);position: absolute;z-index: 8;}',m=e(\"./lib/useragent\"),g=m.isIE;i.importCssString(v,\"ace_editor.css\");var y=function(e,t){var n=this;this.container=e||i.createElement(\"div\"),i.addCssClass(this.container,\"ace_editor\"),i.HI_DPI&&i.addCssClass(this.container,\"ace_hidpi\"),this.setTheme(t),this.$gutter=i.createElement(\"div\"),this.$gutter.className=\"ace_gutter\",this.container.appendChild(this.$gutter),this.$gutter.setAttribute(\"aria-hidden\",!0),this.scroller=i.createElement(\"div\"),this.scroller.className=\"ace_scroller\",this.container.appendChild(this.scroller),this.content=i.createElement(\"div\"),this.content.className=\"ace_content\",this.scroller.appendChild(this.content),this.$gutterLayer=new o(this.$gutter),this.$gutterLayer.on(\"changeGutterWidth\",this.onGutterResize.bind(this)),this.$markerBack=new u(this.content);var r=this.$textLayer=new a(this.content);this.canvas=r.element,this.$markerFront=new u(this.content),this.$cursorLayer=new f(this.content),this.$horizScroll=!1,this.$vScroll=!1,this.scrollBar=this.scrollBarV=new c(this.container,this),this.scrollBarH=new l(this.container,this),this.scrollBarV.addEventListener(\"scroll\",function(e){n.$scrollAnimation||n.session.setScrollTop(e.data-n.scrollMargin.top)}),this.scrollBarH.addEventListener(\"scroll\",function(e){n.$scrollAnimation||n.session.setScrollLeft(e.data-n.scrollMargin.left)}),this.scrollTop=0,this.scrollLeft=0,this.cursorPos={row:0,column:0},this.$fontMetrics=new p(this.container),this.$textLayer.$setFontMetrics(this.$fontMetrics),this.$textLayer.addEventListener(\"changeCharacterSize\",function(e){n.updateCharacterSize(),n.onResize(!0,n.gutterWidth,n.$size.width,n.$size.height),n._signal(\"changeCharacterSize\",e)}),this.$size={width:0,height:0,scrollerHeight:0,scrollerWidth:0,$dirty:!0},this.layerConfig={width:1,padding:0,firstRow:0,firstRowScreen:0,lastRow:0,lineHeight:0,characterWidth:0,minHeight:1,maxHeight:1,offset:0,height:1,gutterOffset:1},this.scrollMargin={left:0,right:0,top:0,bottom:0,v:0,h:0},this.margin={left:0,right:0,top:0,bottom:0,v:0,h:0},this.$keepTextAreaAtCursor=!m.isIOS,this.$loop=new h(this.$renderChanges.bind(this),this.container.ownerDocument.defaultView),this.$loop.schedule(this.CHANGE_FULL),this.updateCharacterSize(),this.setPadding(4),s.resetOptions(this),s._emit(\"renderer\",this)};(function(){this.CHANGE_CURSOR=1,this.CHANGE_MARKER=2,this.CHANGE_GUTTER=4,this.CHANGE_SCROLL=8,this.CHANGE_LINES=16,this.CHANGE_TEXT=32,this.CHANGE_SIZE=64,this.CHANGE_MARKER_BACK=128,this.CHANGE_MARKER_FRONT=256,this.CHANGE_FULL=512,this.CHANGE_H_SCROLL=1024,r.implement(this,d),this.updateCharacterSize=function(){this.$textLayer.allowBoldFonts!=this.$allowBoldFonts&&(this.$allowBoldFonts=this.$textLayer.allowBoldFonts,this.setStyle(\"ace_nobold\",!this.$allowBoldFonts)),this.layerConfig.characterWidth=this.characterWidth=this.$textLayer.getCharacterWidth(),this.layerConfig.lineHeight=this.lineHeight=this.$textLayer.getLineHeight(),this.$updatePrintMargin()},this.setSession=function(e){this.session&&this.session.doc.off(\"changeNewLineMode\",this.onChangeNewLineMode),this.session=e,e&&this.scrollMargin.top&&e.getScrollTop()<=0&&e.setScrollTop(-this.scrollMargin.top),this.$cursorLayer.setSession(e),this.$markerBack.setSession(e),this.$markerFront.setSession(e),this.$gutterLayer.setSession(e),this.$textLayer.setSession(e);if(!e)return;this.$loop.schedule(this.CHANGE_FULL),this.session.$setFontMetrics(this.$fontMetrics),this.scrollBarH.scrollLeft=this.scrollBarV.scrollTop=null,this.onChangeNewLineMode=this.onChangeNewLineMode.bind(this),this.onChangeNewLineMode(),this.session.doc.on(\"changeNewLineMode\",this.onChangeNewLineMode)},this.updateLines=function(e,t,n){t===undefined&&(t=Infinity),this.$changedLines?(this.$changedLines.firstRow>e&&(this.$changedLines.firstRow=e),this.$changedLines.lastRow<t&&(this.$changedLines.lastRow=t)):this.$changedLines={firstRow:e,lastRow:t};if(this.$changedLines.lastRow<this.layerConfig.firstRow){if(!n)return;this.$changedLines.lastRow=this.layerConfig.lastRow}if(this.$changedLines.firstRow>this.layerConfig.lastRow)return;this.$loop.schedule(this.CHANGE_LINES)},this.onChangeNewLineMode=function(){this.$loop.schedule(this.CHANGE_TEXT),this.$textLayer.$updateEolChar(),this.session.$bidiHandler.setEolChar(this.$textLayer.EOL_CHAR)},this.onChangeTabSize=function(){this.$loop.schedule(this.CHANGE_TEXT|this.CHANGE_MARKER),this.$textLayer.onChangeTabSize()},this.updateText=function(){this.$loop.schedule(this.CHANGE_TEXT)},this.updateFull=function(e){e?this.$renderChanges(this.CHANGE_FULL,!0):this.$loop.schedule(this.CHANGE_FULL)},this.updateFontSize=function(){this.$textLayer.checkForSizeChanges()},this.$changes=0,this.$updateSizeAsync=function(){this.$loop.pending?this.$size.$dirty=!0:this.onResize()},this.onResize=function(e,t,n,r){if(this.resizing>2)return;this.resizing>0?this.resizing++:this.resizing=e?1:0;var i=this.container;r||(r=i.clientHeight||i.scrollHeight),n||(n=i.clientWidth||i.scrollWidth);var s=this.$updateCachedSize(e,t,n,r);if(!this.$size.scrollerHeight||!n&&!r)return this.resizing=0;e&&(this.$gutterLayer.$padding=null),e?this.$renderChanges(s|this.$changes,!0):this.$loop.schedule(s|this.$changes),this.resizing&&(this.resizing=0),this.scrollBarV.scrollLeft=this.scrollBarV.scrollTop=null},this.$updateCachedSize=function(e,t,n,r){r-=this.$extraHeight||0;var s=0,o=this.$size,u={width:o.width,height:o.height,scrollerHeight:o.scrollerHeight,scrollerWidth:o.scrollerWidth};r&&(e||o.height!=r)&&(o.height=r,s|=this.CHANGE_SIZE,o.scrollerHeight=o.height,this.$horizScroll&&(o.scrollerHeight-=this.scrollBarH.getHeight()),this.scrollBarV.element.style.bottom=this.scrollBarH.getHeight()+\"px\",s|=this.CHANGE_SCROLL);if(n&&(e||o.width!=n)){s|=this.CHANGE_SIZE,o.width=n,t==null&&(t=this.$showGutter?this.$gutter.offsetWidth:0),this.gutterWidth=t,i.setStyle(this.scrollBarH.element.style,\"left\",t+\"px\"),i.setStyle(this.scroller.style,\"left\",t+this.margin.left+\"px\"),o.scrollerWidth=Math.max(0,n-t-this.scrollBarV.getWidth()-this.margin.h),i.setStyle(this.$gutter.style,\"left\",this.margin.left+\"px\");var a=this.scrollBarV.getWidth()+\"px\";i.setStyle(this.scrollBarH.element.style,\"right\",a),i.setStyle(this.scroller.style,\"right\",a),i.setStyle(this.scroller.style,\"bottom\",this.scrollBarH.getHeight());if(this.session&&this.session.getUseWrapMode()&&this.adjustWrapLimit()||e)s|=this.CHANGE_FULL}return o.$dirty=!n||!r,s&&this._signal(\"resize\",u),s},this.onGutterResize=function(e){var t=this.$showGutter?e:0;t!=this.gutterWidth&&(this.$changes|=this.$updateCachedSize(!0,t,this.$size.width,this.$size.height)),this.session.getUseWrapMode()&&this.adjustWrapLimit()?this.$loop.schedule(this.CHANGE_FULL):this.$size.$dirty?this.$loop.schedule(this.CHANGE_FULL):this.$computeLayerConfig()},this.adjustWrapLimit=function(){var e=this.$size.scrollerWidth-this.$padding*2,t=Math.floor(e/this.characterWidth);return this.session.adjustWrapLimit(t,this.$showPrintMargin&&this.$printMarginColumn)},this.setAnimatedScroll=function(e){this.setOption(\"animatedScroll\",e)},this.getAnimatedScroll=function(){return this.$animatedScroll},this.setShowInvisibles=function(e){this.setOption(\"showInvisibles\",e),this.session.$bidiHandler.setShowInvisibles(e)},this.getShowInvisibles=function(){return this.getOption(\"showInvisibles\")},this.getDisplayIndentGuides=function(){return this.getOption(\"displayIndentGuides\")},this.setDisplayIndentGuides=function(e){this.setOption(\"displayIndentGuides\",e)},this.setShowPrintMargin=function(e){this.setOption(\"showPrintMargin\",e)},this.getShowPrintMargin=function(){return this.getOption(\"showPrintMargin\")},this.setPrintMarginColumn=function(e){this.setOption(\"printMarginColumn\",e)},this.getPrintMarginColumn=function(){return this.getOption(\"printMarginColumn\")},this.getShowGutter=function(){return this.getOption(\"showGutter\")},this.setShowGutter=function(e){return this.setOption(\"showGutter\",e)},this.getFadeFoldWidgets=function(){return this.getOption(\"fadeFoldWidgets\")},this.setFadeFoldWidgets=function(e){this.setOption(\"fadeFoldWidgets\",e)},this.setHighlightGutterLine=function(e){this.setOption(\"highlightGutterLine\",e)},this.getHighlightGutterLine=function(){return this.getOption(\"highlightGutterLine\")},this.$updatePrintMargin=function(){if(!this.$showPrintMargin&&!this.$printMarginEl)return;if(!this.$printMarginEl){var e=i.createElement(\"div\");e.className=\"ace_layer ace_print-margin-layer\",this.$printMarginEl=i.createElement(\"div\"),this.$printMarginEl.className=\"ace_print-margin\",e.appendChild(this.$printMarginEl),this.content.insertBefore(e,this.content.firstChild)}var t=this.$printMarginEl.style;t.left=Math.round(this.characterWidth*this.$printMarginColumn+this.$padding)+\"px\",t.visibility=this.$showPrintMargin?\"visible\":\"hidden\",this.session&&this.session.$wrap==-1&&this.adjustWrapLimit()},this.getContainerElement=function(){return this.container},this.getMouseEventTarget=function(){return this.scroller},this.getTextAreaContainer=function(){return this.container},this.$moveTextAreaToCursor=function(){var e=this.textarea.style;if(!this.$keepTextAreaAtCursor){i.translate(this.textarea,-100,0);return}var t=this.$cursorLayer.$pixelPos;if(!t)return;var n=this.$composition;n&&n.markerRange&&(t=this.$cursorLayer.getPixelPosition(n.markerRange.start,!0));var r=this.layerConfig,s=t.top,o=t.left;s-=r.offset;var u=n&&n.useTextareaForIME?this.lineHeight:g?0:1;if(s<0||s>r.height-u){i.translate(this.textarea,0,0);return}var a=1;if(!n)s+=this.lineHeight;else if(n.useTextareaForIME){var f=this.textarea.value;a=this.characterWidth*this.session.$getStringScreenWidth(f)[0],u+=2}else s+=this.lineHeight+2;o-=this.scrollLeft,o>this.$size.scrollerWidth-a&&(o=this.$size.scrollerWidth-a),o+=this.gutterWidth+this.margin.left,i.setStyle(e,\"height\",u+\"px\"),i.setStyle(e,\"width\",a+\"px\"),i.translate(this.textarea,Math.min(o,this.$size.scrollerWidth-a),Math.min(s,this.$size.height-u))},this.getFirstVisibleRow=function(){return this.layerConfig.firstRow},this.getFirstFullyVisibleRow=function(){return this.layerConfig.firstRow+(this.layerConfig.offset===0?0:1)},this.getLastFullyVisibleRow=function(){var e=this.layerConfig,t=e.lastRow,n=this.session.documentToScreenRow(t,0)*e.lineHeight;return n-this.session.getScrollTop()>e.height-e.lineHeight?t-1:t},this.getLastVisibleRow=function(){return this.layerConfig.lastRow},this.$padding=null,this.setPadding=function(e){this.$padding=e,this.$textLayer.setPadding(e),this.$cursorLayer.setPadding(e),this.$markerFront.setPadding(e),this.$markerBack.setPadding(e),this.$loop.schedule(this.CHANGE_FULL),this.$updatePrintMargin()},this.setScrollMargin=function(e,t,n,r){var i=this.scrollMargin;i.top=e|0,i.bottom=t|0,i.right=r|0,i.left=n|0,i.v=i.top+i.bottom,i.h=i.left+i.right,i.top&&this.scrollTop<=0&&this.session&&this.session.setScrollTop(-i.top),this.updateFull()},this.setMargin=function(e,t,n,r){var i=this.margin;i.top=e|0,i.bottom=t|0,i.right=r|0,i.left=n|0,i.v=i.top+i.bottom,i.h=i.left+i.right,this.$updateCachedSize(!0,this.gutterWidth,this.$size.width,this.$size.height),this.updateFull()},this.getHScrollBarAlwaysVisible=function(){return this.$hScrollBarAlwaysVisible},this.setHScrollBarAlwaysVisible=function(e){this.setOption(\"hScrollBarAlwaysVisible\",e)},this.getVScrollBarAlwaysVisible=function(){return this.$vScrollBarAlwaysVisible},this.setVScrollBarAlwaysVisible=function(e){this.setOption(\"vScrollBarAlwaysVisible\",e)},this.$updateScrollBarV=function(){var e=this.layerConfig.maxHeight,t=this.$size.scrollerHeight;!this.$maxLines&&this.$scrollPastEnd&&(e-=(t-this.lineHeight)*this.$scrollPastEnd,this.scrollTop>e-t&&(e=this.scrollTop+t,this.scrollBarV.scrollTop=null)),this.scrollBarV.setScrollHeight(e+this.scrollMargin.v),this.scrollBarV.setScrollTop(this.scrollTop+this.scrollMargin.top)},this.$updateScrollBarH=function(){this.scrollBarH.setScrollWidth(this.layerConfig.width+2*this.$padding+this.scrollMargin.h),this.scrollBarH.setScrollLeft(this.scrollLeft+this.scrollMargin.left)},this.$frozen=!1,this.freeze=function(){this.$frozen=!0},this.unfreeze=function(){this.$frozen=!1},this.$renderChanges=function(e,t){this.$changes&&(e|=this.$changes,this.$changes=0);if(!this.session||!this.container.offsetWidth||this.$frozen||!e&&!t){this.$changes|=e;return}if(this.$size.$dirty)return this.$changes|=e,this.onResize(!0);this.lineHeight||this.$textLayer.checkForSizeChanges(),this._signal(\"beforeRender\"),this.session&&this.session.$bidiHandler&&this.session.$bidiHandler.updateCharacterWidths(this.$fontMetrics);var n=this.layerConfig;if(e&this.CHANGE_FULL||e&this.CHANGE_SIZE||e&this.CHANGE_TEXT||e&this.CHANGE_LINES||e&this.CHANGE_SCROLL||e&this.CHANGE_H_SCROLL){e|=this.$computeLayerConfig()|this.$loop.clear();if(n.firstRow!=this.layerConfig.firstRow&&n.firstRowScreen==this.layerConfig.firstRowScreen){var r=this.scrollTop+(n.firstRow-this.layerConfig.firstRow)*this.lineHeight;r>0&&(this.scrollTop=r,e|=this.CHANGE_SCROLL,e|=this.$computeLayerConfig()|this.$loop.clear())}n=this.layerConfig,this.$updateScrollBarV(),e&this.CHANGE_H_SCROLL&&this.$updateScrollBarH(),i.translate(this.content,-this.scrollLeft,-n.offset);var s=n.width+2*this.$padding+\"px\",o=n.minHeight+\"px\";i.setStyle(this.content.style,\"width\",s),i.setStyle(this.content.style,\"height\",o)}e&this.CHANGE_H_SCROLL&&(i.translate(this.content,-this.scrollLeft,-n.offset),this.scroller.className=this.scrollLeft<=0?\"ace_scroller\":\"ace_scroller ace_scroll-left\");if(e&this.CHANGE_FULL){this.$textLayer.update(n),this.$showGutter&&this.$gutterLayer.update(n),this.$markerBack.update(n),this.$markerFront.update(n),this.$cursorLayer.update(n),this.$moveTextAreaToCursor(),this._signal(\"afterRender\");return}if(e&this.CHANGE_SCROLL){e&this.CHANGE_TEXT||e&this.CHANGE_LINES?this.$textLayer.update(n):this.$textLayer.scrollLines(n),this.$showGutter&&(e&this.CHANGE_GUTTER||e&this.CHANGE_LINES?this.$gutterLayer.update(n):this.$gutterLayer.scrollLines(n)),this.$markerBack.update(n),this.$markerFront.update(n),this.$cursorLayer.update(n),this.$moveTextAreaToCursor(),this._signal(\"afterRender\");return}e&this.CHANGE_TEXT?(this.$textLayer.update(n),this.$showGutter&&this.$gutterLayer.update(n)):e&this.CHANGE_LINES?(this.$updateLines()||e&this.CHANGE_GUTTER&&this.$showGutter)&&this.$gutterLayer.update(n):e&this.CHANGE_TEXT||e&this.CHANGE_GUTTER?this.$showGutter&&this.$gutterLayer.update(n):e&this.CHANGE_CURSOR&&this.$highlightGutterLine&&this.$gutterLayer.updateLineHighlight(n),e&this.CHANGE_CURSOR&&(this.$cursorLayer.update(n),this.$moveTextAreaToCursor()),e&(this.CHANGE_MARKER|this.CHANGE_MARKER_FRONT)&&this.$markerFront.update(n),e&(this.CHANGE_MARKER|this.CHANGE_MARKER_BACK)&&this.$markerBack.update(n),this._signal(\"afterRender\")},this.$autosize=function(){var e=this.session.getScreenLength()*this.lineHeight,t=this.$maxLines*this.lineHeight,n=Math.min(t,Math.max((this.$minLines||1)*this.lineHeight,e))+this.scrollMargin.v+(this.$extraHeight||0);this.$horizScroll&&(n+=this.scrollBarH.getHeight()),this.$maxPixelHeight&&n>this.$maxPixelHeight&&(n=this.$maxPixelHeight);var r=n<=2*this.lineHeight,i=!r&&e>t;if(n!=this.desiredHeight||this.$size.height!=this.desiredHeight||i!=this.$vScroll){i!=this.$vScroll&&(this.$vScroll=i,this.scrollBarV.setVisible(i));var s=this.container.clientWidth;this.container.style.height=n+\"px\",this.$updateCachedSize(!0,this.$gutterWidth,s,n),this.desiredHeight=n,this._signal(\"autosize\")}},this.$computeLayerConfig=function(){var e=this.session,t=this.$size,n=t.height<=2*this.lineHeight,r=this.session.getScreenLength(),i=r*this.lineHeight,s=this.$getLongestLine(),o=!n&&(this.$hScrollBarAlwaysVisible||t.scrollerWidth-s-2*this.$padding<0),u=this.$horizScroll!==o;u&&(this.$horizScroll=o,this.scrollBarH.setVisible(o));var a=this.$vScroll;this.$maxLines&&this.lineHeight>1&&this.$autosize();var f=t.scrollerHeight+this.lineHeight,l=!this.$maxLines&&this.$scrollPastEnd?(t.scrollerHeight-this.lineHeight)*this.$scrollPastEnd:0;i+=l;var c=this.scrollMargin;this.session.setScrollTop(Math.max(-c.top,Math.min(this.scrollTop,i-t.scrollerHeight+c.bottom))),this.session.setScrollLeft(Math.max(-c.left,Math.min(this.scrollLeft,s+2*this.$padding-t.scrollerWidth+c.right)));var h=!n&&(this.$vScrollBarAlwaysVisible||t.scrollerHeight-i+l<0||this.scrollTop>c.top),p=a!==h;p&&(this.$vScroll=h,this.scrollBarV.setVisible(h));var d=this.scrollTop%this.lineHeight,v=Math.ceil(f/this.lineHeight)-1,m=Math.max(0,Math.round((this.scrollTop-d)/this.lineHeight)),g=m+v,y,b,w=this.lineHeight;m=e.screenToDocumentRow(m,0);var E=e.getFoldLine(m);E&&(m=E.start.row),y=e.documentToScreenRow(m,0),b=e.getRowLength(m)*w,g=Math.min(e.screenToDocumentRow(g,0),e.getLength()-1),f=t.scrollerHeight+e.getRowLength(g)*w+b,d=this.scrollTop-y*w;var S=0;if(this.layerConfig.width!=s||u)S=this.CHANGE_H_SCROLL;if(u||p)S|=this.$updateCachedSize(!0,this.gutterWidth,t.width,t.height),this._signal(\"scrollbarVisibilityChanged\"),p&&(s=this.$getLongestLine());return this.layerConfig={width:s,padding:this.$padding,firstRow:m,firstRowScreen:y,lastRow:g,lineHeight:w,characterWidth:this.characterWidth,minHeight:f,maxHeight:i,offset:d,gutterOffset:w?Math.max(0,Math.ceil((d+t.height-t.scrollerHeight)/w)):0,height:this.$size.scrollerHeight},this.session.$bidiHandler&&this.session.$bidiHandler.setContentWidth(s-this.$padding),S},this.$updateLines=function(){if(!this.$changedLines)return;var e=this.$changedLines.firstRow,t=this.$changedLines.lastRow;this.$changedLines=null;var n=this.layerConfig;if(e>n.lastRow+1)return;if(t<n.firstRow)return;if(t===Infinity){this.$showGutter&&this.$gutterLayer.update(n),this.$textLayer.update(n);return}return this.$textLayer.updateLines(n,e,t),!0},this.$getLongestLine=function(){var e=this.session.getScreenWidth();return this.showInvisibles&&!this.session.$useWrapMode&&(e+=1),this.$textLayer&&e>this.$textLayer.MAX_LINE_LENGTH&&(e=this.$textLayer.MAX_LINE_LENGTH+30),Math.max(this.$size.scrollerWidth-2*this.$padding,Math.round(e*this.characterWidth))},this.updateFrontMarkers=function(){this.$markerFront.setMarkers(this.session.getMarkers(!0)),this.$loop.schedule(this.CHANGE_MARKER_FRONT)},this.updateBackMarkers=function(){this.$markerBack.setMarkers(this.session.getMarkers()),this.$loop.schedule(this.CHANGE_MARKER_BACK)},this.addGutterDecoration=function(e,t){this.$gutterLayer.addGutterDecoration(e,t)},this.removeGutterDecoration=function(e,t){this.$gutterLayer.removeGutterDecoration(e,t)},this.updateBreakpoints=function(e){this.$loop.schedule(this.CHANGE_GUTTER)},this.setAnnotations=function(e){this.$gutterLayer.setAnnotations(e),this.$loop.schedule(this.CHANGE_GUTTER)},this.updateCursor=function(){this.$loop.schedule(this.CHANGE_CURSOR)},this.hideCursor=function(){this.$cursorLayer.hideCursor()},this.showCursor=function(){this.$cursorLayer.showCursor()},this.scrollSelectionIntoView=function(e,t,n){this.scrollCursorIntoView(e,n),this.scrollCursorIntoView(t,n)},this.scrollCursorIntoView=function(e,t,n){if(this.$size.scrollerHeight===0)return;var r=this.$cursorLayer.getPixelPosition(e),i=r.left,s=r.top,o=n&&n.top||0,u=n&&n.bottom||0,a=this.$scrollAnimation?this.session.getScrollTop():this.scrollTop;a+o>s?(t&&a+o>s+this.lineHeight&&(s-=t*this.$size.scrollerHeight),s===0&&(s=-this.scrollMargin.top),this.session.setScrollTop(s)):a+this.$size.scrollerHeight-u<s+this.lineHeight&&(t&&a+this.$size.scrollerHeight-u<s-this.lineHeight&&(s+=t*this.$size.scrollerHeight),this.session.setScrollTop(s+this.lineHeight+u-this.$size.scrollerHeight));var f=this.scrollLeft;f>i?(i<this.$padding+2*this.layerConfig.characterWidth&&(i=-this.scrollMargin.left),this.session.setScrollLeft(i)):f+this.$size.scrollerWidth<i+this.characterWidth?this.session.setScrollLeft(Math.round(i+this.characterWidth-this.$size.scrollerWidth)):f<=this.$padding&&i-f<this.characterWidth&&this.session.setScrollLeft(0)},this.getScrollTop=function(){return this.session.getScrollTop()},this.getScrollLeft=function(){return this.session.getScrollLeft()},this.getScrollTopRow=function(){return this.scrollTop/this.lineHeight},this.getScrollBottomRow=function(){return Math.max(0,Math.floor((this.scrollTop+this.$size.scrollerHeight)/this.lineHeight)-1)},this.scrollToRow=function(e){this.session.setScrollTop(e*this.lineHeight)},this.alignCursor=function(e,t){typeof e==\"number\"&&(e={row:e,column:0});var n=this.$cursorLayer.getPixelPosition(e),r=this.$size.scrollerHeight-this.lineHeight,i=n.top-r*(t||0);return this.session.setScrollTop(i),i},this.STEPS=8,this.$calcSteps=function(e,t){var n=0,r=this.STEPS,i=[],s=function(e,t,n){return n*(Math.pow(e-1,3)+1)+t};for(n=0;n<r;++n)i.push(s(n/this.STEPS,e,t-e));return i},this.scrollToLine=function(e,t,n,r){var i=this.$cursorLayer.getPixelPosition({row:e,column:0}),s=i.top;t&&(s-=this.$size.scrollerHeight/2);var o=this.scrollTop;this.session.setScrollTop(s),n!==!1&&this.animateScrolling(o,r)},this.animateScrolling=function(e,t){var n=this.scrollTop;if(!this.$animatedScroll)return;var r=this;if(e==n)return;if(this.$scrollAnimation){var i=this.$scrollAnimation.steps;if(i.length){e=i[0];if(e==n)return}}var s=r.$calcSteps(e,n);this.$scrollAnimation={from:e,to:n,steps:s},clearInterval(this.$timer),r.session.setScrollTop(s.shift()),r.session.$scrollTop=n,this.$timer=setInterval(function(){s.length?(r.session.setScrollTop(s.shift()),r.session.$scrollTop=n):n!=null?(r.session.$scrollTop=-1,r.session.setScrollTop(n),n=null):(r.$timer=clearInterval(r.$timer),r.$scrollAnimation=null,t&&t())},10)},this.scrollToY=function(e){this.scrollTop!==e&&(this.$loop.schedule(this.CHANGE_SCROLL),this.scrollTop=e)},this.scrollToX=function(e){this.scrollLeft!==e&&(this.scrollLeft=e),this.$loop.schedule(this.CHANGE_H_SCROLL)},this.scrollTo=function(e,t){this.session.setScrollTop(t),this.session.setScrollLeft(t)},this.scrollBy=function(e,t){t&&this.session.setScrollTop(this.session.getScrollTop()+t),e&&this.session.setScrollLeft(this.session.getScrollLeft()+e)},this.isScrollableBy=function(e,t){if(t<0&&this.session.getScrollTop()>=1-this.scrollMargin.top)return!0;if(t>0&&this.session.getScrollTop()+this.$size.scrollerHeight-this.layerConfig.maxHeight<-1+this.scrollMargin.bottom)return!0;if(e<0&&this.session.getScrollLeft()>=1-this.scrollMargin.left)return!0;if(e>0&&this.session.getScrollLeft()+this.$size.scrollerWidth-this.layerConfig.width<-1+this.scrollMargin.right)return!0},this.pixelToScreenCoordinates=function(e,t){var n;if(this.$hasCssTransforms){n={top:0,left:0};var r=this.$fontMetrics.transformCoordinates([e,t]);e=r[1]-this.gutterWidth-this.margin.left,t=r[0]}else n=this.scroller.getBoundingClientRect();var i=e+this.scrollLeft-n.left-this.$padding,s=i/this.characterWidth,o=Math.floor((t+this.scrollTop-n.top)/this.lineHeight),u=this.$blockCursor?Math.floor(s):Math.round(s);return{row:o,column:u,side:s-u>0?1:-1,offsetX:i}},this.screenToTextCoordinates=function(e,t){var n;if(this.$hasCssTransforms){n={top:0,left:0};var r=this.$fontMetrics.transformCoordinates([e,t]);e=r[1]-this.gutterWidth-this.margin.left,t=r[0]}else n=this.scroller.getBoundingClientRect();var i=e+this.scrollLeft-n.left-this.$padding,s=i/this.characterWidth,o=this.$blockCursor?Math.floor(s):Math.round(s),u=Math.floor((t+this.scrollTop-n.top)/this.lineHeight);return this.session.screenToDocumentPosition(u,Math.max(o,0),i)},this.textToScreenCoordinates=function(e,t){var n=this.scroller.getBoundingClientRect(),r=this.session.documentToScreenPosition(e,t),i=this.$padding+(this.session.$bidiHandler.isBidiRow(r.row,e)?this.session.$bidiHandler.getPosLeft(r.column):Math.round(r.column*this.characterWidth)),s=r.row*this.lineHeight;return{pageX:n.left+i-this.scrollLeft,pageY:n.top+s-this.scrollTop}},this.visualizeFocus=function(){i.addCssClass(this.container,\"ace_focus\")},this.visualizeBlur=function(){i.removeCssClass(this.container,\"ace_focus\")},this.showComposition=function(e){this.$composition=e,e.cssText||(e.cssText=this.textarea.style.cssText,e.keepTextAreaAtCursor=this.$keepTextAreaAtCursor),e.useTextareaForIME=this.$useTextareaForIME,this.$useTextareaForIME?(this.$keepTextAreaAtCursor=!0,i.addCssClass(this.textarea,\"ace_composition\"),this.textarea.style.cssText=\"\",this.$moveTextAreaToCursor(),this.$cursorLayer.element.style.display=\"none\"):e.markerId=this.session.addMarker(e.markerRange,\"ace_composition_marker\",\"text\")},this.setCompositionText=function(e){var t=this.session.selection.cursor;this.addToken(e,\"composition_placeholder\",t.row,t.column),this.$moveTextAreaToCursor()},this.hideComposition=function(){if(!this.$composition)return;this.$composition.markerId&&this.session.removeMarker(this.$composition.markerId),i.removeCssClass(this.textarea,\"ace_composition\"),this.$keepTextAreaAtCursor=this.$composition.keepTextAreaAtCursor,this.textarea.style.cssText=this.$composition.cssText,this.$composition=null,this.$cursorLayer.element.style.display=\"\"},this.addToken=function(e,t,n,r){var i=this.session;i.bgTokenizer.lines[n]=null;var s={type:t,value:e},o=i.getTokens(n);if(r==null)o.push(s);else{var u=0;for(var a=0;a<o.length;a++){var f=o[a];u+=f.value.length;if(r<=u){var l=f.value.length-(u-r),c=f.value.slice(0,l),h=f.value.slice(l);o.splice(a,1,{type:f.type,value:c},s,{type:f.type,value:h});break}}}this.updateLines(n,n)},this.setTheme=function(e,t){function o(r){if(n.$themeId!=e)return t&&t();if(!r||!r.cssClass)throw new Error(\"couldn't load module \"+e+\" or it didn't call define\");r.$id&&(n.$themeId=r.$id),i.importCssString(r.cssText,r.cssClass,n.container),n.theme&&i.removeCssClass(n.container,n.theme.cssClass);var s=\"padding\"in r?r.padding:\"padding\"in(n.theme||{})?4:n.$padding;n.$padding&&s!=n.$padding&&n.setPadding(s),n.$theme=r.cssClass,n.theme=r,i.addCssClass(n.container,r.cssClass),i.setCssClass(n.container,\"ace_dark\",r.isDark),n.$size&&(n.$size.width=0,n.$updateSizeAsync()),n._dispatchEvent(\"themeLoaded\",{theme:r}),t&&t()}var n=this;this.$themeId=e,n._dispatchEvent(\"themeChange\",{theme:e});if(!e||typeof e==\"string\"){var r=e||this.$options.theme.initialValue;s.loadModule([\"theme\",r],o)}else o(e)},this.getTheme=function(){return this.$themeId},this.setStyle=function(e,t){i.setCssClass(this.container,e,t!==!1)},this.unsetStyle=function(e){i.removeCssClass(this.container,e)},this.setCursorStyle=function(e){i.setStyle(this.scroller.style,\"cursor\",e)},this.setMouseCursor=function(e){i.setStyle(this.scroller.style,\"cursor\",e)},this.attachToShadowRoot=function(){i.importCssString(v,\"ace_editor.css\",this.container)},this.destroy=function(){this.$fontMetrics.destroy(),this.$cursorLayer.destroy()}}).call(y.prototype),s.defineOptions(y.prototype,\"renderer\",{animatedScroll:{initialValue:!1},showInvisibles:{set:function(e){this.$textLayer.setShowInvisibles(e)&&this.$loop.schedule(this.CHANGE_TEXT)},initialValue:!1},showPrintMargin:{set:function(){this.$updatePrintMargin()},initialValue:!0},printMarginColumn:{set:function(){this.$updatePrintMargin()},initialValue:80},printMargin:{set:function(e){typeof e==\"number\"&&(this.$printMarginColumn=e),this.$showPrintMargin=!!e,this.$updatePrintMargin()},get:function(){return this.$showPrintMargin&&this.$printMarginColumn}},showGutter:{set:function(e){this.$gutter.style.display=e?\"block\":\"none\",this.$loop.schedule(this.CHANGE_FULL),this.onGutterResize()},initialValue:!0},fadeFoldWidgets:{set:function(e){i.setCssClass(this.$gutter,\"ace_fade-fold-widgets\",e)},initialValue:!1},showFoldWidgets:{set:function(e){this.$gutterLayer.setShowFoldWidgets(e),this.$loop.schedule(this.CHANGE_GUTTER)},initialValue:!0},displayIndentGuides:{set:function(e){this.$textLayer.setDisplayIndentGuides(e)&&this.$loop.schedule(this.CHANGE_TEXT)},initialValue:!0},highlightGutterLine:{set:function(e){this.$gutterLayer.setHighlightGutterLine(e),this.$loop.schedule(this.CHANGE_GUTTER)},initialValue:!0},hScrollBarAlwaysVisible:{set:function(e){(!this.$hScrollBarAlwaysVisible||!this.$horizScroll)&&this.$loop.schedule(this.CHANGE_SCROLL)},initialValue:!1},vScrollBarAlwaysVisible:{set:function(e){(!this.$vScrollBarAlwaysVisible||!this.$vScroll)&&this.$loop.schedule(this.CHANGE_SCROLL)},initialValue:!1},fontSize:{set:function(e){typeof e==\"number\"&&(e+=\"px\"),this.container.style.fontSize=e,this.updateFontSize()},initialValue:12},fontFamily:{set:function(e){this.container.style.fontFamily=e,this.updateFontSize()}},maxLines:{set:function(e){this.updateFull()}},minLines:{set:function(e){this.$minLines<562949953421311||(this.$minLines=0),this.updateFull()}},maxPixelHeight:{set:function(e){this.updateFull()},initialValue:0},scrollPastEnd:{set:function(e){e=+e||0;if(this.$scrollPastEnd==e)return;this.$scrollPastEnd=e,this.$loop.schedule(this.CHANGE_SCROLL)},initialValue:0,handlesSet:!0},fixedWidthGutter:{set:function(e){this.$gutterLayer.$fixedWidth=!!e,this.$loop.schedule(this.CHANGE_GUTTER)}},theme:{set:function(e){this.setTheme(e)},get:function(){return this.$themeId||this.theme},initialValue:\"./theme/textmate\",handlesSet:!0},hasCssTransforms:{},useTextareaForIME:{initialValue:!m.isMobile&&!m.isIE}}),t.VirtualRenderer=y}),ace.define(\"ace/worker/worker_client\",[\"require\",\"exports\",\"module\",\"ace/lib/oop\",\"ace/lib/net\",\"ace/lib/event_emitter\",\"ace/config\"],function(e,t,n){\"use strict\";function u(e){var t=\"importScripts('\"+i.qualifyURL(e)+\"');\";try{return new Blob([t],{type:\"application/javascript\"})}catch(n){var r=window.BlobBuilder||window.WebKitBlobBuilder||window.MozBlobBuilder,s=new r;return s.append(t),s.getBlob(\"application/javascript\")}}function a(e){if(typeof Worker==\"undefined\")return{postMessage:function(){},terminate:function(){}};if(o.get(\"loadWorkerFromBlob\")){var t=u(e),n=window.URL||window.webkitURL,r=n.createObjectURL(t);return new Worker(r)}return new Worker(e)}var r=e(\"../lib/oop\"),i=e(\"../lib/net\"),s=e(\"../lib/event_emitter\").EventEmitter,o=e(\"../config\"),f=function(e){e.postMessage||(e=this.$createWorkerFromOldConfig.apply(this,arguments)),this.$worker=e,this.$sendDeltaQueue=this.$sendDeltaQueue.bind(this),this.changeListener=this.changeListener.bind(this),this.onMessage=this.onMessage.bind(this),this.callbackId=1,this.callbacks={},this.$worker.onmessage=this.onMessage};(function(){r.implement(this,s),this.$createWorkerFromOldConfig=function(t,n,r,i,s){e.nameToUrl&&!e.toUrl&&(e.toUrl=e.nameToUrl);if(o.get(\"packaged\")||!e.toUrl)i=i||o.moduleUrl(n,\"worker\");else{var u=this.$normalizePath;i=i||u(e.toUrl(\"ace/worker/worker.js\",null,\"_\"));var f={};t.forEach(function(t){f[t]=u(e.toUrl(t,null,\"_\").replace(/(\\.js)?(\\?.*)?$/,\"\"))})}return this.$worker=a(i),s&&this.send(\"importScripts\",s),this.$worker.postMessage({init:!0,tlns:f,module:n,classname:r}),this.$worker},this.onMessage=function(e){var t=e.data;switch(t.type){case\"event\":this._signal(t.name,{data:t.data});break;case\"call\":var n=this.callbacks[t.id];n&&(n(t.data),delete this.callbacks[t.id]);break;case\"error\":this.reportError(t.data);break;case\"log\":window.console&&console.log&&console.log.apply(console,t.data)}},this.reportError=function(e){window.console&&console.error&&console.error(e)},this.$normalizePath=function(e){return i.qualifyURL(e)},this.terminate=function(){this._signal(\"terminate\",{}),this.deltaQueue=null,this.$worker.terminate(),this.$worker=null,this.$doc&&this.$doc.off(\"change\",this.changeListener),this.$doc=null},this.send=function(e,t){this.$worker.postMessage({command:e,args:t})},this.call=function(e,t,n){if(n){var r=this.callbackId++;this.callbacks[r]=n,t.push(r)}this.send(e,t)},this.emit=function(e,t){try{t.data&&t.data.err&&(t.data.err={message:t.data.err.message,stack:t.data.err.stack,code:t.data.err.code}),this.$worker.postMessage({event:e,data:{data:t.data}})}catch(n){console.error(n.stack)}},this.attachToDocument=function(e){this.$doc&&this.terminate(),this.$doc=e,this.call(\"setValue\",[e.getValue()]),e.on(\"change\",this.changeListener)},this.changeListener=function(e){this.deltaQueue||(this.deltaQueue=[],setTimeout(this.$sendDeltaQueue,0)),e.action==\"insert\"?this.deltaQueue.push(e.start,e.lines):this.deltaQueue.push(e.start,e.end)},this.$sendDeltaQueue=function(){var e=this.deltaQueue;if(!e)return;this.deltaQueue=null,e.length>50&&e.length>this.$doc.getLength()>>1?this.call(\"setValue\",[this.$doc.getValue()]):this.emit(\"change\",{data:e})}}).call(f.prototype);var l=function(e,t,n){var r=null,i=!1,u=Object.create(s),a=[],l=new f({messageBuffer:a,terminate:function(){},postMessage:function(e){a.push(e);if(!r)return;i?setTimeout(c):c()}});l.setEmitSync=function(e){i=e};var c=function(){var e=a.shift();e.command?r[e.command].apply(r,e.args):e.event&&u._signal(e.event,e.data)};return u.postMessage=function(e){l.onMessage({data:e})},u.callback=function(e,t){this.postMessage({type:\"call\",id:t,data:e})},u.emit=function(e,t){this.postMessage({type:\"event\",name:e,data:t})},o.loadModule([\"worker\",t],function(e){r=new e[n](u);while(a.length)c()}),l};t.UIWorkerClient=l,t.WorkerClient=f,t.createWorker=a}),ace.define(\"ace/placeholder\",[\"require\",\"exports\",\"module\",\"ace/range\",\"ace/lib/event_emitter\",\"ace/lib/oop\"],function(e,t,n){\"use strict\";var r=e(\"./range\").Range,i=e(\"./lib/event_emitter\").EventEmitter,s=e(\"./lib/oop\"),o=function(e,t,n,r,i,s){var o=this;this.length=t,this.session=e,this.doc=e.getDocument(),this.mainClass=i,this.othersClass=s,this.$onUpdate=this.onUpdate.bind(this),this.doc.on(\"change\",this.$onUpdate),this.$others=r,this.$onCursorChange=function(){setTimeout(function(){o.onCursorChange()})},this.$pos=n;var u=e.getUndoManager().$undoStack||e.getUndoManager().$undostack||{length:-1};this.$undoStackDepth=u.length,this.setup(),e.selection.on(\"changeCursor\",this.$onCursorChange)};(function(){s.implement(this,i),this.setup=function(){var e=this,t=this.doc,n=this.session;this.selectionBefore=n.selection.toJSON(),n.selection.inMultiSelectMode&&n.selection.toSingleRange(),this.pos=t.createAnchor(this.$pos.row,this.$pos.column);var i=this.pos;i.$insertRight=!0,i.detach(),i.markerId=n.addMarker(new r(i.row,i.column,i.row,i.column+this.length),this.mainClass,null,!1),this.others=[],this.$others.forEach(function(n){var r=t.createAnchor(n.row,n.column);r.$insertRight=!0,r.detach(),e.others.push(r)}),n.setUndoSelect(!1)},this.showOtherMarkers=function(){if(this.othersActive)return;var e=this.session,t=this;this.othersActive=!0,this.others.forEach(function(n){n.markerId=e.addMarker(new r(n.row,n.column,n.row,n.column+t.length),t.othersClass,null,!1)})},this.hideOtherMarkers=function(){if(!this.othersActive)return;this.othersActive=!1;for(var e=0;e<this.others.length;e++)this.session.removeMarker(this.others[e].markerId)},this.onUpdate=function(e){if(this.$updating)return this.updateAnchors(e);var t=e;if(t.start.row!==t.end.row)return;if(t.start.row!==this.pos.row)return;this.$updating=!0;var n=e.action===\"insert\"?t.end.column-t.start.column:t.start.column-t.end.column,i=t.start.column>=this.pos.column&&t.start.column<=this.pos.column+this.length+1,s=t.start.column-this.pos.column;this.updateAnchors(e),i&&(this.length+=n);if(i&&!this.session.$fromUndo)if(e.action===\"insert\")for(var o=this.others.length-1;o>=0;o--){var u=this.others[o],a={row:u.row,column:u.column+s};this.doc.insertMergedLines(a,e.lines)}else if(e.action===\"remove\")for(var o=this.others.length-1;o>=0;o--){var u=this.others[o],a={row:u.row,column:u.column+s};this.doc.remove(new r(a.row,a.column,a.row,a.column-n))}this.$updating=!1,this.updateMarkers()},this.updateAnchors=function(e){this.pos.onChange(e);for(var t=this.others.length;t--;)this.others[t].onChange(e);this.updateMarkers()},this.updateMarkers=function(){if(this.$updating)return;var e=this,t=this.session,n=function(n,i){t.removeMarker(n.markerId),n.markerId=t.addMarker(new r(n.row,n.column,n.row,n.column+e.length),i,null,!1)};n(this.pos,this.mainClass);for(var i=this.others.length;i--;)n(this.others[i],this.othersClass)},this.onCursorChange=function(e){if(this.$updating||!this.session)return;var t=this.session.selection.getCursor();t.row===this.pos.row&&t.column>=this.pos.column&&t.column<=this.pos.column+this.length?(this.showOtherMarkers(),this._emit(\"cursorEnter\",e)):(this.hideOtherMarkers(),this._emit(\"cursorLeave\",e))},this.detach=function(){this.session.removeMarker(this.pos&&this.pos.markerId),this.hideOtherMarkers(),this.doc.removeEventListener(\"change\",this.$onUpdate),this.session.selection.removeEventListener(\"changeCursor\",this.$onCursorChange),this.session.setUndoSelect(!0),this.session=null},this.cancel=function(){if(this.$undoStackDepth===-1)return;var e=this.session.getUndoManager(),t=(e.$undoStack||e.$undostack).length-this.$undoStackDepth;for(var n=0;n<t;n++)e.undo(this.session,!0);this.selectionBefore&&this.session.selection.fromJSON(this.selectionBefore)}}).call(o.prototype),t.PlaceHolder=o}),ace.define(\"ace/mouse/multi_select_handler\",[\"require\",\"exports\",\"module\",\"ace/lib/event\",\"ace/lib/useragent\"],function(e,t,n){function s(e,t){return e.row==t.row&&e.column==t.column}function o(e){var t=e.domEvent,n=t.altKey,o=t.shiftKey,u=t.ctrlKey,a=e.getAccelKey(),f=e.getButton();u&&i.isMac&&(f=t.button);if(e.editor.inMultiSelectMode&&f==2){e.editor.textInput.onContextMenu(e.domEvent);return}if(!u&&!n&&!a){f===0&&e.editor.inMultiSelectMode&&e.editor.exitMultiSelectMode();return}if(f!==0)return;var l=e.editor,c=l.selection,h=l.inMultiSelectMode,p=e.getDocumentPosition(),d=c.getCursor(),v=e.inSelection()||c.isEmpty()&&s(p,d),m=e.x,g=e.y,y=function(e){m=e.clientX,g=e.clientY},b=l.session,w=l.renderer.pixelToScreenCoordinates(m,g),E=w,S;if(l.$mouseHandler.$enableJumpToDef)u&&n||a&&n?S=o?\"block\":\"add\":n&&l.$blockSelectEnabled&&(S=\"block\");else if(a&&!n){S=\"add\";if(!h&&o)return}else n&&l.$blockSelectEnabled&&(S=\"block\");S&&i.isMac&&t.ctrlKey&&l.$mouseHandler.cancelContextMenu();if(S==\"add\"){if(!h&&v)return;if(!h){var x=c.toOrientedRange();l.addSelectionMarker(x)}var T=c.rangeList.rangeAtPoint(p);l.inVirtualSelectionMode=!0,o&&(T=null,x=c.ranges[0]||x,l.removeSelectionMarker(x)),l.once(\"mouseup\",function(){var e=c.toOrientedRange();T&&e.isEmpty()&&s(T.cursor,e.cursor)?c.substractPoint(e.cursor):(o?c.substractPoint(x.cursor):x&&(l.removeSelectionMarker(x),c.addRange(x)),c.addRange(e)),l.inVirtualSelectionMode=!1})}else if(S==\"block\"){e.stop(),l.inVirtualSelectionMode=!0;var N,C=[],k=function(){var e=l.renderer.pixelToScreenCoordinates(m,g),t=b.screenToDocumentPosition(e.row,e.column,e.offsetX);if(s(E,e)&&s(t,c.lead))return;E=e,l.selection.moveToPosition(t),l.renderer.scrollCursorIntoView(),l.removeSelectionMarkers(C),C=c.rectangularRangeBlock(E,w),l.$mouseHandler.$clickSelection&&C.length==1&&C[0].isEmpty()&&(C[0]=l.$mouseHandler.$clickSelection.clone()),C.forEach(l.addSelectionMarker,l),l.updateSelectionMarkers()};h&&!a?c.toSingleRange():!h&&a&&(N=c.toOrientedRange(),l.addSelectionMarker(N)),o?w=b.documentToScreenPosition(c.lead):c.moveToPosition(p),E={row:-1,column:-1};var L=function(e){k(),clearInterval(O),l.removeSelectionMarkers(C),C.length||(C=[c.toOrientedRange()]),N&&(l.removeSelectionMarker(N),c.toSingleRange(N));for(var t=0;t<C.length;t++)c.addRange(C[t]);l.inVirtualSelectionMode=!1,l.$mouseHandler.$clickSelection=null},A=k;r.capture(l.container,y,L);var O=setInterval(function(){A()},20);return e.preventDefault()}}var r=e(\"../lib/event\"),i=e(\"../lib/useragent\");t.onMouseDown=o}),ace.define(\"ace/commands/multi_select_commands\",[\"require\",\"exports\",\"module\",\"ace/keyboard/hash_handler\"],function(e,t,n){t.defaultCommands=[{name:\"addCursorAbove\",description:\"Add cursor above\",exec:function(e){e.selectMoreLines(-1)},bindKey:{win:\"Ctrl-Alt-Up\",mac:\"Ctrl-Alt-Up\"},scrollIntoView:\"cursor\",readOnly:!0},{name:\"addCursorBelow\",description:\"Add cursor below\",exec:function(e){e.selectMoreLines(1)},bindKey:{win:\"Ctrl-Alt-Down\",mac:\"Ctrl-Alt-Down\"},scrollIntoView:\"cursor\",readOnly:!0},{name:\"addCursorAboveSkipCurrent\",description:\"Add cursor above (skip current)\",exec:function(e){e.selectMoreLines(-1,!0)},bindKey:{win:\"Ctrl-Alt-Shift-Up\",mac:\"Ctrl-Alt-Shift-Up\"},scrollIntoView:\"cursor\",readOnly:!0},{name:\"addCursorBelowSkipCurrent\",description:\"Add cursor below (skip current)\",exec:function(e){e.selectMoreLines(1,!0)},bindKey:{win:\"Ctrl-Alt-Shift-Down\",mac:\"Ctrl-Alt-Shift-Down\"},scrollIntoView:\"cursor\",readOnly:!0},{name:\"selectMoreBefore\",description:\"Select more before\",exec:function(e){e.selectMore(-1)},bindKey:{win:\"Ctrl-Alt-Left\",mac:\"Ctrl-Alt-Left\"},scrollIntoView:\"cursor\",readOnly:!0},{name:\"selectMoreAfter\",description:\"Select more after\",exec:function(e){e.selectMore(1)},bindKey:{win:\"Ctrl-Alt-Right\",mac:\"Ctrl-Alt-Right\"},scrollIntoView:\"cursor\",readOnly:!0},{name:\"selectNextBefore\",description:\"Select next before\",exec:function(e){e.selectMore(-1,!0)},bindKey:{win:\"Ctrl-Alt-Shift-Left\",mac:\"Ctrl-Alt-Shift-Left\"},scrollIntoView:\"cursor\",readOnly:!0},{name:\"selectNextAfter\",description:\"Select next after\",exec:function(e){e.selectMore(1,!0)},bindKey:{win:\"Ctrl-Alt-Shift-Right\",mac:\"Ctrl-Alt-Shift-Right\"},scrollIntoView:\"cursor\",readOnly:!0},{name:\"splitIntoLines\",description:\"Split into lines\",exec:function(e){e.multiSelect.splitIntoLines()},bindKey:{win:\"Ctrl-Alt-L\",mac:\"Ctrl-Alt-L\"},readOnly:!0},{name:\"alignCursors\",description:\"Align cursors\",exec:function(e){e.alignCursors()},bindKey:{win:\"Ctrl-Alt-A\",mac:\"Ctrl-Alt-A\"},scrollIntoView:\"cursor\"},{name:\"findAll\",description:\"Find all\",exec:function(e){e.findAll()},bindKey:{win:\"Ctrl-Alt-K\",mac:\"Ctrl-Alt-G\"},scrollIntoView:\"cursor\",readOnly:!0}],t.multiSelectCommands=[{name:\"singleSelection\",description:\"Single selection\",bindKey:\"esc\",exec:function(e){e.exitMultiSelectMode()},scrollIntoView:\"cursor\",readOnly:!0,isAvailable:function(e){return e&&e.inMultiSelectMode}}];var r=e(\"../keyboard/hash_handler\").HashHandler;t.keyboardHandler=new r(t.multiSelectCommands)}),ace.define(\"ace/multi_select\",[\"require\",\"exports\",\"module\",\"ace/range_list\",\"ace/range\",\"ace/selection\",\"ace/mouse/multi_select_handler\",\"ace/lib/event\",\"ace/lib/lang\",\"ace/commands/multi_select_commands\",\"ace/search\",\"ace/edit_session\",\"ace/editor\",\"ace/config\"],function(e,t,n){function h(e,t,n){return c.$options.wrap=!0,c.$options.needle=t,c.$options.backwards=n==-1,c.find(e)}function v(e,t){return e.row==t.row&&e.column==t.column}function m(e){if(e.$multiselectOnSessionChange)return;e.$onAddRange=e.$onAddRange.bind(e),e.$onRemoveRange=e.$onRemoveRange.bind(e),e.$onMultiSelect=e.$onMultiSelect.bind(e),e.$onSingleSelect=e.$onSingleSelect.bind(e),e.$multiselectOnSessionChange=t.onSessionChange.bind(e),e.$checkMultiselectChange=e.$checkMultiselectChange.bind(e),e.$multiselectOnSessionChange(e),e.on(\"changeSession\",e.$multiselectOnSessionChange),e.on(\"mousedown\",o),e.commands.addCommands(f.defaultCommands),g(e)}function g(e){function r(t){n&&(e.renderer.setMouseCursor(\"\"),n=!1)}var t=e.textInput.getElement(),n=!1;u.addListener(t,\"keydown\",function(t){var i=t.keyCode==18&&!(t.ctrlKey||t.shiftKey||t.metaKey);e.$blockSelectEnabled&&i?n||(e.renderer.setMouseCursor(\"crosshair\"),n=!0):n&&r()}),u.addListener(t,\"keyup\",r),u.addListener(t,\"blur\",r)}var r=e(\"./range_list\").RangeList,i=e(\"./range\").Range,s=e(\"./selection\").Selection,o=e(\"./mouse/multi_select_handler\").onMouseDown,u=e(\"./lib/event\"),a=e(\"./lib/lang\"),f=e(\"./commands/multi_select_commands\");t.commands=f.defaultCommands.concat(f.multiSelectCommands);var l=e(\"./search\").Search,c=new l,p=e(\"./edit_session\").EditSession;(function(){this.getSelectionMarkers=function(){return this.$selectionMarkers}}).call(p.prototype),function(){this.ranges=null,this.rangeList=null,this.addRange=function(e,t){if(!e)return;if(!this.inMultiSelectMode&&this.rangeCount===0){var n=this.toOrientedRange();this.rangeList.add(n),this.rangeList.add(e);if(this.rangeList.ranges.length!=2)return this.rangeList.removeAll(),t||this.fromOrientedRange(e);this.rangeList.removeAll(),this.rangeList.add(n),this.$onAddRange(n)}e.cursor||(e.cursor=e.end);var r=this.rangeList.add(e);return this.$onAddRange(e),r.length&&this.$onRemoveRange(r),this.rangeCount>1&&!this.inMultiSelectMode&&(this._signal(\"multiSelect\"),this.inMultiSelectMode=!0,this.session.$undoSelect=!1,this.rangeList.attach(this.session)),t||this.fromOrientedRange(e)},this.toSingleRange=function(e){e=e||this.ranges[0];var t=this.rangeList.removeAll();t.length&&this.$onRemoveRange(t),e&&this.fromOrientedRange(e)},this.substractPoint=function(e){var t=this.rangeList.substractPoint(e);if(t)return this.$onRemoveRange(t),t[0]},this.mergeOverlappingRanges=function(){var e=this.rangeList.merge();e.length&&this.$onRemoveRange(e)},this.$onAddRange=function(e){this.rangeCount=this.rangeList.ranges.length,this.ranges.unshift(e),this._signal(\"addRange\",{range:e})},this.$onRemoveRange=function(e){this.rangeCount=this.rangeList.ranges.length;if(this.rangeCount==1&&this.inMultiSelectMode){var t=this.rangeList.ranges.pop();e.push(t),this.rangeCount=0}for(var n=e.length;n--;){var r=this.ranges.indexOf(e[n]);this.ranges.splice(r,1)}this._signal(\"removeRange\",{ranges:e}),this.rangeCount===0&&this.inMultiSelectMode&&(this.inMultiSelectMode=!1,this._signal(\"singleSelect\"),this.session.$undoSelect=!0,this.rangeList.detach(this.session)),t=t||this.ranges[0],t&&!t.isEqual(this.getRange())&&this.fromOrientedRange(t)},this.$initRangeList=function(){if(this.rangeList)return;this.rangeList=new r,this.ranges=[],this.rangeCount=0},this.getAllRanges=function(){return this.rangeCount?this.rangeList.ranges.concat():[this.getRange()]},this.splitIntoLines=function(){if(this.rangeCount>1){var e=this.rangeList.ranges,t=e[e.length-1],n=i.fromPoints(e[0].start,t.end);this.toSingleRange(),this.setSelectionRange(n,t.cursor==t.start)}else{var n=this.getRange(),r=this.isBackwards(),s=n.start.row,o=n.end.row;if(s==o){if(r)var u=n.end,a=n.start;else var u=n.start,a=n.end;this.addRange(i.fromPoints(a,a)),this.addRange(i.fromPoints(u,u));return}var f=[],l=this.getLineRange(s,!0);l.start.column=n.start.column,f.push(l);for(var c=s+1;c<o;c++)f.push(this.getLineRange(c,!0));l=this.getLineRange(o,!0),l.end.column=n.end.column,f.push(l),f.forEach(this.addRange,this)}},this.toggleBlockSelection=function(){if(this.rangeCount>1){var e=this.rangeList.ranges,t=e[e.length-1],n=i.fromPoints(e[0].start,t.end);this.toSingleRange(),this.setSelectionRange(n,t.cursor==t.start)}else{var r=this.session.documentToScreenPosition(this.cursor),s=this.session.documentToScreenPosition(this.anchor),o=this.rectangularRangeBlock(r,s);o.forEach(this.addRange,this)}},this.rectangularRangeBlock=function(e,t,n){var r=[],s=e.column<t.column;if(s)var o=e.column,u=t.column,a=e.offsetX,f=t.offsetX;else var o=t.column,u=e.column,a=t.offsetX,f=e.offsetX;var l=e.row<t.row;if(l)var c=e.row,h=t.row;else var c=t.row,h=e.row;o<0&&(o=0),c<0&&(c=0),c==h&&(n=!0);var p;for(var d=c;d<=h;d++){var m=i.fromPoints(this.session.screenToDocumentPosition(d,o,a),this.session.screenToDocumentPosition(d,u,f));if(m.isEmpty()){if(p&&v(m.end,p))break;p=m.end}m.cursor=s?m.start:m.end,r.push(m)}l&&r.reverse();if(!n){var g=r.length-1;while(r[g].isEmpty()&&g>0)g--;if(g>0){var y=0;while(r[y].isEmpty())y++}for(var b=g;b>=y;b--)r[b].isEmpty()&&r.splice(b,1)}return r}}.call(s.prototype);var d=e(\"./editor\").Editor;(function(){this.updateSelectionMarkers=function(){this.renderer.updateCursor(),this.renderer.updateBackMarkers()},this.addSelectionMarker=function(e){e.cursor||(e.cursor=e.end);var t=this.getSelectionStyle();return e.marker=this.session.addMarker(e,\"ace_selection\",t),this.session.$selectionMarkers.push(e),this.session.selectionMarkerCount=this.session.$selectionMarkers.length,e},this.removeSelectionMarker=function(e){if(!e.marker)return;this.session.removeMarker(e.marker);var t=this.session.$selectionMarkers.indexOf(e);t!=-1&&this.session.$selectionMarkers.splice(t,1),this.session.selectionMarkerCount=this.session.$selectionMarkers.length},this.removeSelectionMarkers=function(e){var t=this.session.$selectionMarkers;for(var n=e.length;n--;){var r=e[n];if(!r.marker)continue;this.session.removeMarker(r.marker);var i=t.indexOf(r);i!=-1&&t.splice(i,1)}this.session.selectionMarkerCount=t.length},this.$onAddRange=function(e){this.addSelectionMarker(e.range),this.renderer.updateCursor(),this.renderer.updateBackMarkers()},this.$onRemoveRange=function(e){this.removeSelectionMarkers(e.ranges),this.renderer.updateCursor(),this.renderer.updateBackMarkers()},this.$onMultiSelect=function(e){if(this.inMultiSelectMode)return;this.inMultiSelectMode=!0,this.setStyle(\"ace_multiselect\"),this.keyBinding.addKeyboardHandler(f.keyboardHandler),this.commands.setDefaultHandler(\"exec\",this.$onMultiSelectExec),this.renderer.updateCursor(),this.renderer.updateBackMarkers()},this.$onSingleSelect=function(e){if(this.session.multiSelect.inVirtualMode)return;this.inMultiSelectMode=!1,this.unsetStyle(\"ace_multiselect\"),this.keyBinding.removeKeyboardHandler(f.keyboardHandler),this.commands.removeDefaultHandler(\"exec\",this.$onMultiSelectExec),this.renderer.updateCursor(),this.renderer.updateBackMarkers(),this._emit(\"changeSelection\")},this.$onMultiSelectExec=function(e){var t=e.command,n=e.editor;if(!n.multiSelect)return;if(!t.multiSelectAction){var r=t.exec(n,e.args||{});n.multiSelect.addRange(n.multiSelect.toOrientedRange()),n.multiSelect.mergeOverlappingRanges()}else t.multiSelectAction==\"forEach\"?r=n.forEachSelection(t,e.args):t.multiSelectAction==\"forEachLine\"?r=n.forEachSelection(t,e.args,!0):t.multiSelectAction==\"single\"?(n.exitMultiSelectMode(),r=t.exec(n,e.args||{})):r=t.multiSelectAction(n,e.args||{});return r},this.forEachSelection=function(e,t,n){if(this.inVirtualSelectionMode)return;var r=n&&n.keepOrder,i=n==1||n&&n.$byLines,o=this.session,u=this.selection,a=u.rangeList,f=(r?u:a).ranges,l;if(!f.length)return e.exec?e.exec(this,t||{}):e(this,t||{});var c=u._eventRegistry;u._eventRegistry={};var h=new s(o);this.inVirtualSelectionMode=!0;for(var p=f.length;p--;){if(i)while(p>0&&f[p].start.row==f[p-1].end.row)p--;h.fromOrientedRange(f[p]),h.index=p,this.selection=o.selection=h;var d=e.exec?e.exec(this,t||{}):e(this,t||{});!l&&d!==undefined&&(l=d),h.toOrientedRange(f[p])}h.detach(),this.selection=o.selection=u,this.inVirtualSelectionMode=!1,u._eventRegistry=c,u.mergeOverlappingRanges(),u.ranges[0]&&u.fromOrientedRange(u.ranges[0]);var v=this.renderer.$scrollAnimation;return this.onCursorChange(),this.onSelectionChange(),v&&v.from==v.to&&this.renderer.animateScrolling(v.from),l},this.exitMultiSelectMode=function(){if(!this.inMultiSelectMode||this.inVirtualSelectionMode)return;this.multiSelect.toSingleRange()},this.getSelectedText=function(){var e=\"\";if(this.inMultiSelectMode&&!this.inVirtualSelectionMode){var t=this.multiSelect.rangeList.ranges,n=[];for(var r=0;r<t.length;r++)n.push(this.session.getTextRange(t[r]));var i=this.session.getDocument().getNewLineCharacter();e=n.join(i),e.length==(n.length-1)*i.length&&(e=\"\")}else this.selection.isEmpty()||(e=this.session.getTextRange(this.getSelectionRange()));return e},this.$checkMultiselectChange=function(e,t){if(this.inMultiSelectMode&&!this.inVirtualSelectionMode){var n=this.multiSelect.ranges[0];if(this.multiSelect.isEmpty()&&t==this.multiSelect.anchor)return;var r=t==this.multiSelect.anchor?n.cursor==n.start?n.end:n.start:n.cursor;r.row!=t.row||this.session.$clipPositionToDocument(r.row,r.column).column!=t.column?this.multiSelect.toSingleRange(this.multiSelect.toOrientedRange()):this.multiSelect.mergeOverlappingRanges()}},this.findAll=function(e,t,n){t=t||{},t.needle=e||t.needle;if(t.needle==undefined){var r=this.selection.isEmpty()?this.selection.getWordRange():this.selection.getRange();t.needle=this.session.getTextRange(r)}this.$search.set(t);var i=this.$search.findAll(this.session);if(!i.length)return 0;var s=this.multiSelect;n||s.toSingleRange(i[0]);for(var o=i.length;o--;)s.addRange(i[o],!0);return r&&s.rangeList.rangeAtPoint(r.start)&&s.addRange(r,!0),i.length},this.selectMoreLines=function(e,t){var n=this.selection.toOrientedRange(),r=n.cursor==n.end,s=this.session.documentToScreenPosition(n.cursor);this.selection.$desiredColumn&&(s.column=this.selection.$desiredColumn);var o=this.session.screenToDocumentPosition(s.row+e,s.column);if(!n.isEmpty())var u=this.session.documentToScreenPosition(r?n.end:n.start),a=this.session.screenToDocumentPosition(u.row+e,u.column);else var a=o;if(r){var f=i.fromPoints(o,a);f.cursor=f.start}else{var f=i.fromPoints(a,o);f.cursor=f.end}f.desiredColumn=s.column;if(!this.selection.inMultiSelectMode)this.selection.addRange(n);else if(t)var l=n.cursor;this.selection.addRange(f),l&&this.selection.substractPoint(l)},this.transposeSelections=function(e){var t=this.session,n=t.multiSelect,r=n.ranges;for(var i=r.length;i--;){var s=r[i];if(s.isEmpty()){var o=t.getWordRange(s.start.row,s.start.column);s.start.row=o.start.row,s.start.column=o.start.column,s.end.row=o.end.row,s.end.column=o.end.column}}n.mergeOverlappingRanges();var u=[];for(var i=r.length;i--;){var s=r[i];u.unshift(t.getTextRange(s))}e<0?u.unshift(u.pop()):u.push(u.shift());for(var i=r.length;i--;){var s=r[i],o=s.clone();t.replace(s,u[i]),s.start.row=o.start.row,s.start.column=o.start.column}n.fromOrientedRange(n.ranges[0])},this.selectMore=function(e,t,n){var r=this.session,i=r.multiSelect,s=i.toOrientedRange();if(s.isEmpty()){s=r.getWordRange(s.start.row,s.start.column),s.cursor=e==-1?s.start:s.end,this.multiSelect.addRange(s);if(n)return}var o=r.getTextRange(s),u=h(r,o,e);u&&(u.cursor=e==-1?u.start:u.end,this.session.unfold(u),this.multiSelect.addRange(u),this.renderer.scrollCursorIntoView(null,.5)),t&&this.multiSelect.substractPoint(s.cursor)},this.alignCursors=function(){var e=this.session,t=e.multiSelect,n=t.ranges,r=-1,s=n.filter(function(e){if(e.cursor.row==r)return!0;r=e.cursor.row});if(!n.length||s.length==n.length-1){var o=this.selection.getRange(),u=o.start.row,f=o.end.row,l=u==f;if(l){var c=this.session.getLength(),h;do h=this.session.getLine(f);while(/[=:]/.test(h)&&++f<c);do h=this.session.getLine(u);while(/[=:]/.test(h)&&--u>0);u<0&&(u=0),f>=c&&(f=c-1)}var p=this.session.removeFullLines(u,f);p=this.$reAlignText(p,l),this.session.insert({row:u,column:0},p.join(\"\\n\")+\"\\n\"),l||(o.start.column=0,o.end.column=p[p.length-1].length),this.selection.setRange(o)}else{s.forEach(function(e){t.substractPoint(e.cursor)});var d=0,v=Infinity,m=n.map(function(t){var n=t.cursor,r=e.getLine(n.row),i=r.substr(n.column).search(/\\S/g);return i==-1&&(i=0),n.column>d&&(d=n.column),i<v&&(v=i),i});n.forEach(function(t,n){var r=t.cursor,s=d-r.column,o=m[n]-v;s>o?e.insert(r,a.stringRepeat(\" \",s-o)):e.remove(new i(r.row,r.column,r.row,r.column-s+o)),t.start.column=t.end.column=d,t.start.row=t.end.row=r.row,t.cursor=t.end}),t.fromOrientedRange(n[0]),this.renderer.updateCursor(),this.renderer.updateBackMarkers()}},this.$reAlignText=function(e,t){function u(e){return a.stringRepeat(\" \",e)}function f(e){return e[2]?u(i)+e[2]+u(s-e[2].length+o)+e[4].replace(/^([=:])\\s+/,\"$1 \"):e[0]}function l(e){return e[2]?u(i+s-e[2].length)+e[2]+u(o)+e[4].replace(/^([=:])\\s+/,\"$1 \"):e[0]}function c(e){return e[2]?u(i)+e[2]+u(o)+e[4].replace(/^([=:])\\s+/,\"$1 \"):e[0]}var n=!0,r=!0,i,s,o;return e.map(function(e){var t=e.match(/(\\s*)(.*?)(\\s*)([=:].*)/);return t?i==null?(i=t[1].length,s=t[2].length,o=t[3].length,t):(i+s+o!=t[1].length+t[2].length+t[3].length&&(r=!1),i!=t[1].length&&(n=!1),i>t[1].length&&(i=t[1].length),s<t[2].length&&(s=t[2].length),o>t[3].length&&(o=t[3].length),t):[e]}).map(t?f:n?r?l:f:c)}}).call(d.prototype),t.onSessionChange=function(e){var t=e.session;t&&!t.multiSelect&&(t.$selectionMarkers=[],t.selection.$initRangeList(),t.multiSelect=t.selection),this.multiSelect=t&&t.multiSelect;var n=e.oldSession;n&&(n.multiSelect.off(\"addRange\",this.$onAddRange),n.multiSelect.off(\"removeRange\",this.$onRemoveRange),n.multiSelect.off(\"multiSelect\",this.$onMultiSelect),n.multiSelect.off(\"singleSelect\",this.$onSingleSelect),n.multiSelect.lead.off(\"change\",this.$checkMultiselectChange),n.multiSelect.anchor.off(\"change\",this.$checkMultiselectChange)),t&&(t.multiSelect.on(\"addRange\",this.$onAddRange),t.multiSelect.on(\"removeRange\",this.$onRemoveRange),t.multiSelect.on(\"multiSelect\",this.$onMultiSelect),t.multiSelect.on(\"singleSelect\",this.$onSingleSelect),t.multiSelect.lead.on(\"change\",this.$checkMultiselectChange),t.multiSelect.anchor.on(\"change\",this.$checkMultiselectChange)),t&&this.inMultiSelectMode!=t.selection.inMultiSelectMode&&(t.selection.inMultiSelectMode?this.$onMultiSelect():this.$onSingleSelect())},t.MultiSelect=m,e(\"./config\").defineOptions(d.prototype,\"editor\",{enableMultiselect:{set:function(e){m(this),e?(this.on(\"changeSession\",this.$multiselectOnSessionChange),this.on(\"mousedown\",o)):(this.off(\"changeSession\",this.$multiselectOnSessionChange),this.off(\"mousedown\",o))},value:!0},enableBlockSelect:{set:function(e){this.$blockSelectEnabled=e},value:!0}})}),ace.define(\"ace/mode/folding/fold_mode\",[\"require\",\"exports\",\"module\",\"ace/range\"],function(e,t,n){\"use strict\";var r=e(\"../../range\").Range,i=t.FoldMode=function(){};(function(){this.foldingStartMarker=null,this.foldingStopMarker=null,this.getFoldWidget=function(e,t,n){var r=e.getLine(n);return this.foldingStartMarker.test(r)?\"start\":t==\"markbeginend\"&&this.foldingStopMarker&&this.foldingStopMarker.test(r)?\"end\":\"\"},this.getFoldWidgetRange=function(e,t,n){return null},this.indentationBlock=function(e,t,n){var i=/\\S/,s=e.getLine(t),o=s.search(i);if(o==-1)return;var u=n||s.length,a=e.getLength(),f=t,l=t;while(++t<a){var c=e.getLine(t).search(i);if(c==-1)continue;if(c<=o)break;l=t}if(l>f){var h=e.getLine(l).length;return new r(f,u,l,h)}},this.openingBracketBlock=function(e,t,n,i,s){var o={row:n,column:i+1},u=e.$findClosingBracket(t,o,s);if(!u)return;var a=e.foldWidgets[u.row];return a==null&&(a=e.getFoldWidget(u.row)),a==\"start\"&&u.row>o.row&&(u.row--,u.column=e.getLine(u.row).length),r.fromPoints(o,u)},this.closingBracketBlock=function(e,t,n,i,s){var o={row:n,column:i},u=e.$findOpeningBracket(t,o);if(!u)return;return u.column++,o.column--,r.fromPoints(u,o)}}).call(i.prototype)}),ace.define(\"ace/theme/textmate\",[\"require\",\"exports\",\"module\",\"ace/lib/dom\"],function(e,t,n){\"use strict\";t.isDark=!1,t.cssClass=\"ace-tm\",t.cssText='.ace-tm .ace_gutter {background: #f0f0f0;color: #333;}.ace-tm .ace_print-margin {width: 1px;background: #e8e8e8;}.ace-tm .ace_fold {background-color: #6B72E6;}.ace-tm {background-color: #FFFFFF;color: black;}.ace-tm .ace_cursor {color: black;}.ace-tm .ace_invisible {color: rgb(191, 191, 191);}.ace-tm .ace_storage,.ace-tm .ace_keyword {color: blue;}.ace-tm .ace_constant {color: rgb(197, 6, 11);}.ace-tm .ace_constant.ace_buildin {color: rgb(88, 72, 246);}.ace-tm .ace_constant.ace_language {color: rgb(88, 92, 246);}.ace-tm .ace_constant.ace_library {color: rgb(6, 150, 14);}.ace-tm .ace_invalid {background-color: rgba(255, 0, 0, 0.1);color: red;}.ace-tm .ace_support.ace_function {color: rgb(60, 76, 114);}.ace-tm .ace_support.ace_constant {color: rgb(6, 150, 14);}.ace-tm .ace_support.ace_type,.ace-tm .ace_support.ace_class {color: rgb(109, 121, 222);}.ace-tm .ace_keyword.ace_operator {color: rgb(104, 118, 135);}.ace-tm .ace_string {color: rgb(3, 106, 7);}.ace-tm .ace_comment {color: rgb(76, 136, 107);}.ace-tm .ace_comment.ace_doc {color: rgb(0, 102, 255);}.ace-tm .ace_comment.ace_doc.ace_tag {color: rgb(128, 159, 191);}.ace-tm .ace_constant.ace_numeric {color: rgb(0, 0, 205);}.ace-tm .ace_variable {color: rgb(49, 132, 149);}.ace-tm .ace_xml-pe {color: rgb(104, 104, 91);}.ace-tm .ace_entity.ace_name.ace_function {color: #0000A2;}.ace-tm .ace_heading {color: rgb(12, 7, 255);}.ace-tm .ace_list {color:rgb(185, 6, 144);}.ace-tm .ace_meta.ace_tag {color:rgb(0, 22, 142);}.ace-tm .ace_string.ace_regex {color: rgb(255, 0, 0)}.ace-tm .ace_marker-layer .ace_selection {background: rgb(181, 213, 255);}.ace-tm.ace_multiselect .ace_selection.ace_start {box-shadow: 0 0 3px 0px white;}.ace-tm .ace_marker-layer .ace_step {background: rgb(252, 255, 0);}.ace-tm .ace_marker-layer .ace_stack {background: rgb(164, 229, 101);}.ace-tm .ace_marker-layer .ace_bracket {margin: -1px 0 0 -1px;border: 1px solid rgb(192, 192, 192);}.ace-tm .ace_marker-layer .ace_active-line {background: rgba(0, 0, 0, 0.07);}.ace-tm .ace_gutter-active-line {background-color : #dcdcdc;}.ace-tm .ace_marker-layer .ace_selected-word {background: rgb(250, 250, 255);border: 1px solid rgb(200, 200, 250);}.ace-tm .ace_indent-guide {background: url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAE0lEQVQImWP4////f4bLly//BwAmVgd1/w11/gAAAABJRU5ErkJggg==\") right repeat-y;}',t.$id=\"ace/theme/textmate\";var r=e(\"../lib/dom\");r.importCssString(t.cssText,t.cssClass)}),ace.define(\"ace/line_widgets\",[\"require\",\"exports\",\"module\",\"ace/lib/oop\",\"ace/lib/dom\",\"ace/range\"],function(e,t,n){\"use strict\";function o(e){this.session=e,this.session.widgetManager=this,this.session.getRowLength=this.getRowLength,this.session.$getWidgetScreenLength=this.$getWidgetScreenLength,this.updateOnChange=this.updateOnChange.bind(this),this.renderWidgets=this.renderWidgets.bind(this),this.measureWidgets=this.measureWidgets.bind(this),this.session._changedWidgets=[],this.$onChangeEditor=this.$onChangeEditor.bind(this),this.session.on(\"change\",this.updateOnChange),this.session.on(\"changeFold\",this.updateOnFold),this.session.on(\"changeEditor\",this.$onChangeEditor)}var r=e(\"./lib/oop\"),i=e(\"./lib/dom\"),s=e(\"./range\").Range;(function(){this.getRowLength=function(e){var t;return this.lineWidgets?t=this.lineWidgets[e]&&this.lineWidgets[e].rowCount||0:t=0,!this.$useWrapMode||!this.$wrapData[e]?1+t:this.$wrapData[e].length+1+t},this.$getWidgetScreenLength=function(){var e=0;return this.lineWidgets.forEach(function(t){t&&t.rowCount&&!t.hidden&&(e+=t.rowCount)}),e},this.$onChangeEditor=function(e){this.attach(e.editor)},this.attach=function(e){e&&e.widgetManager&&e.widgetManager!=this&&e.widgetManager.detach();if(this.editor==e)return;this.detach(),this.editor=e,e&&(e.widgetManager=this,e.renderer.on(\"beforeRender\",this.measureWidgets),e.renderer.on(\"afterRender\",this.renderWidgets))},this.detach=function(e){var t=this.editor;if(!t)return;this.editor=null,t.widgetManager=null,t.renderer.off(\"beforeRender\",this.measureWidgets),t.renderer.off(\"afterRender\",this.renderWidgets);var n=this.session.lineWidgets;n&&n.forEach(function(e){e&&e.el&&e.el.parentNode&&(e._inDocument=!1,e.el.parentNode.removeChild(e.el))})},this.updateOnFold=function(e,t){var n=t.lineWidgets;if(!n||!e.action)return;var r=e.data,i=r.start.row,s=r.end.row,o=e.action==\"add\";for(var u=i+1;u<s;u++)n[u]&&(n[u].hidden=o);n[s]&&(o?n[i]?n[s].hidden=o:n[i]=n[s]:(n[i]==n[s]&&(n[i]=undefined),n[s].hidden=o))},this.updateOnChange=function(e){var t=this.session.lineWidgets;if(!t)return;var n=e.start.row,r=e.end.row-n;if(r!==0)if(e.action==\"remove\"){var i=t.splice(n+1,r);i.forEach(function(e){e&&this.removeLineWidget(e)},this),this.$updateRows()}else{var s=new Array(r);s.unshift(n,0),t.splice.apply(t,s),this.$updateRows()}},this.$updateRows=function(){var e=this.session.lineWidgets;if(!e)return;var t=!0;e.forEach(function(e,n){if(e){t=!1,e.row=n;while(e.$oldWidget)e.$oldWidget.row=n,e=e.$oldWidget}}),t&&(this.session.lineWidgets=null)},this.addLineWidget=function(e){this.session.lineWidgets||(this.session.lineWidgets=new Array(this.session.getLength()));var t=this.session.lineWidgets[e.row];t&&(e.$oldWidget=t,t.el&&t.el.parentNode&&(t.el.parentNode.removeChild(t.el),t._inDocument=!1)),this.session.lineWidgets[e.row]=e,e.session=this.session;var n=this.editor.renderer;e.html&&!e.el&&(e.el=i.createElement(\"div\"),e.el.innerHTML=e.html),e.el&&(i.addCssClass(e.el,\"ace_lineWidgetContainer\"),e.el.style.position=\"absolute\",e.el.style.zIndex=5,n.container.appendChild(e.el),e._inDocument=!0),e.coverGutter||(e.el.style.zIndex=3),e.pixelHeight==null&&(e.pixelHeight=e.el.offsetHeight),e.rowCount==null&&(e.rowCount=e.pixelHeight/n.layerConfig.lineHeight);var r=this.session.getFoldAt(e.row,0);e.$fold=r;if(r){var s=this.session.lineWidgets;e.row==r.end.row&&!s[r.start.row]?s[r.start.row]=e:e.hidden=!0}return this.session._emit(\"changeFold\",{data:{start:{row:e.row}}}),this.$updateRows(),this.renderWidgets(null,n),this.onWidgetChanged(e),e},this.removeLineWidget=function(e){e._inDocument=!1,e.session=null,e.el&&e.el.parentNode&&e.el.parentNode.removeChild(e.el);if(e.editor&&e.editor.destroy)try{e.editor.destroy()}catch(t){}if(this.session.lineWidgets){var n=this.session.lineWidgets[e.row];if(n==e)this.session.lineWidgets[e.row]=e.$oldWidget,e.$oldWidget&&this.onWidgetChanged(e.$oldWidget);else while(n){if(n.$oldWidget==e){n.$oldWidget=e.$oldWidget;break}n=n.$oldWidget}}this.session._emit(\"changeFold\",{data:{start:{row:e.row}}}),this.$updateRows()},this.getWidgetsAtRow=function(e){var t=this.session.lineWidgets,n=t&&t[e],r=[];while(n)r.push(n),n=n.$oldWidget;return r},this.onWidgetChanged=function(e){this.session._changedWidgets.push(e),this.editor&&this.editor.renderer.updateFull()},this.measureWidgets=function(e,t){var n=this.session._changedWidgets,r=t.layerConfig;if(!n||!n.length)return;var i=Infinity;for(var s=0;s<n.length;s++){var o=n[s];if(!o||!o.el)continue;if(o.session!=this.session)continue;if(!o._inDocument){if(this.session.lineWidgets[o.row]!=o)continue;o._inDocument=!0,t.container.appendChild(o.el)}o.h=o.el.offsetHeight,o.fixedWidth||(o.w=o.el.offsetWidth,o.screenWidth=Math.ceil(o.w/r.characterWidth));var u=o.h/r.lineHeight;o.coverLine&&(u-=this.session.getRowLineCount(o.row),u<0&&(u=0)),o.rowCount!=u&&(o.rowCount=u,o.row<i&&(i=o.row))}i!=Infinity&&(this.session._emit(\"changeFold\",{data:{start:{row:i}}}),this.session.lineWidgetWidth=null),this.session._changedWidgets=[]},this.renderWidgets=function(e,t){var n=t.layerConfig,r=this.session.lineWidgets;if(!r)return;var i=Math.min(this.firstRow,n.firstRow),s=Math.max(this.lastRow,n.lastRow,r.length);while(i>0&&!r[i])i--;this.firstRow=n.firstRow,this.lastRow=n.lastRow,t.$cursorLayer.config=n;for(var o=i;o<=s;o++){var u=r[o];if(!u||!u.el)continue;if(u.hidden){u.el.style.top=-100-(u.pixelHeight||0)+\"px\";continue}u._inDocument||(u._inDocument=!0,t.container.appendChild(u.el));var a=t.$cursorLayer.getPixelPosition({row:o,column:0},!0).top;u.coverLine||(a+=n.lineHeight*this.session.getRowLineCount(u.row)),u.el.style.top=a-n.offset+\"px\";var f=u.coverGutter?0:t.gutterWidth;u.fixedWidth||(f-=t.scrollLeft),u.el.style.left=f+\"px\",u.fullWidth&&u.screenWidth&&(u.el.style.minWidth=n.width+2*n.padding+\"px\"),u.fixedWidth?u.el.style.right=t.scrollBar.getWidth()+\"px\":u.el.style.right=\"\"}}}).call(o.prototype),t.LineWidgets=o}),ace.define(\"ace/ext/error_marker\",[\"require\",\"exports\",\"module\",\"ace/line_widgets\",\"ace/lib/dom\",\"ace/range\"],function(e,t,n){\"use strict\";function o(e,t,n){var r=0,i=e.length-1;while(r<=i){var s=r+i>>1,o=n(t,e[s]);if(o>0)r=s+1;else{if(!(o<0))return s;i=s-1}}return-(r+1)}function u(e,t,n){var r=e.getAnnotations().sort(s.comparePoints);if(!r.length)return;var i=o(r,{row:t,column:-1},s.comparePoints);i<0&&(i=-i-1),i>=r.length?i=n>0?0:r.length-1:i===0&&n<0&&(i=r.length-1);var u=r[i];if(!u||!n)return;if(u.row===t){do u=r[i+=n];while(u&&u.row===t);if(!u)return r.slice()}var a=[];t=u.row;do a[n<0?\"unshift\":\"push\"](u),u=r[i+=n];while(u&&u.row==t);return a.length&&a}var r=e(\"../line_widgets\").LineWidgets,i=e(\"../lib/dom\"),s=e(\"../range\").Range;t.showErrorMarker=function(e,t){var n=e.session;n.widgetManager||(n.widgetManager=new r(n),n.widgetManager.attach(e));var s=e.getCursorPosition(),o=s.row,a=n.widgetManager.getWidgetsAtRow(o).filter(function(e){return e.type==\"errorMarker\"})[0];a?a.destroy():o-=t;var f=u(n,o,t),l;if(f){var c=f[0];s.column=(c.pos&&typeof c.column!=\"number\"?c.pos.sc:c.column)||0,s.row=c.row,l=e.renderer.$gutterLayer.$annotations[s.row]}else{if(a)return;l={text:[\"Looks good!\"],className:\"ace_ok\"}}e.session.unfold(s.row),e.selection.moveToPosition(s);var h={row:s.row,fixedWidth:!0,coverGutter:!0,el:i.createElement(\"div\"),type:\"errorMarker\"},p=h.el.appendChild(i.createElement(\"div\")),d=h.el.appendChild(i.createElement(\"div\"));d.className=\"error_widget_arrow \"+l.className;var v=e.renderer.$cursorLayer.getPixelPosition(s).left;d.style.left=v+e.renderer.gutterWidth-5+\"px\",h.el.className=\"error_widget_wrapper\",p.className=\"error_widget \"+l.className,p.innerHTML=l.text.join(\"<br>\"),p.appendChild(i.createElement(\"div\"));var m=function(e,t,n){if(t===0&&(n===\"esc\"||n===\"return\"))return h.destroy(),{command:\"null\"}};h.destroy=function(){if(e.$mouseHandler.isMousePressed)return;e.keyBinding.removeKeyboardHandler(m),n.widgetManager.removeLineWidget(h),e.off(\"changeSelection\",h.destroy),e.off(\"changeSession\",h.destroy),e.off(\"mouseup\",h.destroy),e.off(\"change\",h.destroy)},e.keyBinding.addKeyboardHandler(m),e.on(\"changeSelection\",h.destroy),e.on(\"changeSession\",h.destroy),e.on(\"mouseup\",h.destroy),e.on(\"change\",h.destroy),e.session.widgetManager.addLineWidget(h),h.el.onmousedown=e.focus.bind(e),e.renderer.scrollCursorIntoView(null,.5,{bottom:h.el.offsetHeight})},i.importCssString(\"    .error_widget_wrapper {        background: inherit;        color: inherit;        border:none    }    .error_widget {        border-top: solid 2px;        border-bottom: solid 2px;        margin: 5px 0;        padding: 10px 40px;        white-space: pre-wrap;    }    .error_widget.ace_error, .error_widget_arrow.ace_error{        border-color: #ff5a5a    }    .error_widget.ace_warning, .error_widget_arrow.ace_warning{        border-color: #F1D817    }    .error_widget.ace_info, .error_widget_arrow.ace_info{        border-color: #5a5a5a    }    .error_widget.ace_ok, .error_widget_arrow.ace_ok{        border-color: #5aaa5a    }    .error_widget_arrow {        position: absolute;        border: solid 5px;        border-top-color: transparent!important;        border-right-color: transparent!important;        border-left-color: transparent!important;        top: -5px;    }\",\"\")}),ace.define(\"ace/ace\",[\"require\",\"exports\",\"module\",\"ace/lib/fixoldbrowsers\",\"ace/lib/dom\",\"ace/lib/event\",\"ace/range\",\"ace/editor\",\"ace/edit_session\",\"ace/undomanager\",\"ace/virtual_renderer\",\"ace/worker/worker_client\",\"ace/keyboard/hash_handler\",\"ace/placeholder\",\"ace/multi_select\",\"ace/mode/folding/fold_mode\",\"ace/theme/textmate\",\"ace/ext/error_marker\",\"ace/config\"],function(e,t,n){\"use strict\";e(\"./lib/fixoldbrowsers\");var r=e(\"./lib/dom\"),i=e(\"./lib/event\"),s=e(\"./range\").Range,o=e(\"./editor\").Editor,u=e(\"./edit_session\").EditSession,a=e(\"./undomanager\").UndoManager,f=e(\"./virtual_renderer\").VirtualRenderer;e(\"./worker/worker_client\"),e(\"./keyboard/hash_handler\"),e(\"./placeholder\"),e(\"./multi_select\"),e(\"./mode/folding/fold_mode\"),e(\"./theme/textmate\"),e(\"./ext/error_marker\"),t.config=e(\"./config\"),t.require=e,typeof define==\"function\"&&(t.define=define),t.edit=function(e,n){if(typeof e==\"string\"){var s=e;e=document.getElementById(s);if(!e)throw new Error(\"ace.edit can't find div #\"+s)}if(e&&e.env&&e.env.editor instanceof o)return e.env.editor;var u=\"\";if(e&&/input|textarea/i.test(e.tagName)){var a=e;u=a.value,e=r.createElement(\"pre\"),a.parentNode.replaceChild(e,a)}else e&&(u=e.textContent,e.innerHTML=\"\");var l=t.createEditSession(u),c=new o(new f(e),l,n),h={document:l,editor:c,onResize:c.resize.bind(c,null)};return a&&(h.textarea=a),i.addListener(window,\"resize\",h.onResize),c.on(\"destroy\",function(){i.removeListener(window,\"resize\",h.onResize),h.editor.container.env=null}),c.container.env=c.env=h,c},t.createEditSession=function(e,t){var n=new u(e,t);return n.setUndoManager(new a),n},t.Range=s,t.Editor=o,t.EditSession=u,t.UndoManager=a,t.VirtualRenderer=f,t.version=\"1.4.4\"});            (function() {\n                ace.require([\"ace/ace\"], function(a) {\n                    if (a) {\n                        a.config.init(true);\n                        a.define = ace.define;\n                    }\n                    if (!window.ace)\n                        window.ace = a;\n                    for (var key in a) if (a.hasOwnProperty(key))\n                        window.ace[key] = a[key];\n                    window.ace[\"default\"] = window.ace;\n                    if (typeof module == \"object\" && typeof exports == \"object\" && module) {\n                        module.exports = window.ace;\n                    }\n                });\n            })();\n"
  },
  {
    "path": "crates/mdbook-html/front-end/playground_editor/editor.js",
    "content": "\"use strict\";\nwindow.editors = [];\n(function(editors) {\n    if (typeof(ace) === 'undefined' || !ace) {\n        return;\n    }\n\n    Array.from(document.querySelectorAll('.editable')).forEach(function(editable) {\n        let display_line_numbers = window.playground_line_numbers || false;\n\n        let editor = ace.edit(editable);\n            editor.setOptions({\n            highlightActiveLine: false,\n            showPrintMargin: false,\n            showLineNumbers: display_line_numbers,\n            showGutter: display_line_numbers,\n            maxLines: Infinity,\n            fontSize: \"0.875em\" // please adjust the font size of the code in general.css\n        });\n\n        editor.$blockScrolling = Infinity;\n\n        editor.getSession().setMode(\"ace/mode/rust\");\n\n        editor.originalCode = editor.getValue();\n\n        editors.push(editor);\n    });\n})(window.editors);\n"
  },
  {
    "path": "crates/mdbook-html/front-end/playground_editor/mode-rust.js",
    "content": "ace.define(\"ace/mode/rust_highlight_rules\",[\"require\",\"exports\",\"module\",\"ace/lib/oop\",\"ace/mode/text_highlight_rules\"],function(e,t,n){\"use strict\";var r=e(\"../lib/oop\"),i=e(\"./text_highlight_rules\").TextHighlightRules,s=/\\\\(?:[nrt0'\"\\\\]|x[\\da-fA-F]{2}|u\\{[\\da-fA-F]{6}\\})/.source,o=function(){this.$rules={start:[{token:\"variable.other.source.rust\",regex:\"'[a-zA-Z_][a-zA-Z0-9_]*(?![\\\\'])\"},{token:\"string.quoted.single.source.rust\",regex:\"'(?:[^'\\\\\\\\]|\"+s+\")'\"},{token:\"identifier\",regex:/r#[a-zA-Z_][a-zA-Z0-9_]*\\b/},{stateName:\"bracketedComment\",onMatch:function(e,t,n){return n.unshift(this.next,e.length-1,t),\"string.quoted.raw.source.rust\"},regex:/r#*\"/,next:[{onMatch:function(e,t,n){var r=\"string.quoted.raw.source.rust\";return e.length>=n[1]?(e.length>n[1]&&(r=\"invalid\"),n.shift(),n.shift(),this.next=n.shift()):this.next=\"\",r},regex:/\"#*/,next:\"start\"},{defaultToken:\"string.quoted.raw.source.rust\"}]},{token:\"string.quoted.double.source.rust\",regex:'\"',push:[{token:\"string.quoted.double.source.rust\",regex:'\"',next:\"pop\"},{token:\"constant.character.escape.source.rust\",regex:s},{defaultToken:\"string.quoted.double.source.rust\"}]},{token:[\"keyword.source.rust\",\"text\",\"entity.name.function.source.rust\"],regex:\"\\\\b(fn)(\\\\s+)((?:r#)?[a-zA-Z_][a-zA-Z0-9_]*)\"},{token:\"support.constant\",regex:\"\\\\b[a-zA-Z_][\\\\w\\\\d]*::\"},{token:\"keyword.source.rust\",regex:\"\\\\b(?:abstract|alignof|as|become|box|break|catch|continue|const|crate|default|do|dyn|else|enum|extern|for|final|if|impl|in|let|loop|macro|match|mod|move|mut|offsetof|override|priv|proc|pub|pure|ref|return|self|sizeof|static|struct|super|trait|type|typeof|union|unsafe|unsized|use|virtual|where|while|yield)\\\\b\"},{token:\"storage.type.source.rust\",regex:\"\\\\b(?:Self|isize|usize|char|bool|u8|u16|u32|u64|u128|f16|f32|f64|i8|i16|i32|i64|i128|str|option|either|c_float|c_double|c_void|FILE|fpos_t|DIR|dirent|c_char|c_schar|c_uchar|c_short|c_ushort|c_int|c_uint|c_long|c_ulong|size_t|ptrdiff_t|clock_t|time_t|c_longlong|c_ulonglong|intptr_t|uintptr_t|off_t|dev_t|ino_t|pid_t|mode_t|ssize_t)\\\\b\"},{token:\"variable.language.source.rust\",regex:\"\\\\bself\\\\b\"},{token:\"comment.line.doc.source.rust\",regex:\"//!.*$\"},{token:\"comment.line.double-dash.source.rust\",regex:\"//.*$\"},{token:\"comment.start.block.source.rust\",regex:\"/\\\\*\",stateName:\"comment\",push:[{token:\"comment.start.block.source.rust\",regex:\"/\\\\*\",push:\"comment\"},{token:\"comment.end.block.source.rust\",regex:\"\\\\*/\",next:\"pop\"},{defaultToken:\"comment.block.source.rust\"}]},{token:\"keyword.operator\",regex:/\\$|[-=]>|[-+%^=!&|<>]=?|[*/](?![*/])=?/},{token:\"punctuation.operator\",regex:/[?:,;.]/},{token:\"paren.lparen\",regex:/[\\[({]/},{token:\"paren.rparen\",regex:/[\\])}]/},{token:\"constant.language.source.rust\",regex:\"\\\\b(?:true|false|Some|None|Ok|Err)\\\\b\"},{token:\"support.constant.source.rust\",regex:\"\\\\b(?:EXIT_FAILURE|EXIT_SUCCESS|RAND_MAX|EOF|SEEK_SET|SEEK_CUR|SEEK_END|_IOFBF|_IONBF|_IOLBF|BUFSIZ|FOPEN_MAX|FILENAME_MAX|L_tmpnam|TMP_MAX|O_RDONLY|O_WRONLY|O_RDWR|O_APPEND|O_CREAT|O_EXCL|O_TRUNC|S_IFIFO|S_IFCHR|S_IFBLK|S_IFDIR|S_IFREG|S_IFMT|S_IEXEC|S_IWRITE|S_IREAD|S_IRWXU|S_IXUSR|S_IWUSR|S_IRUSR|F_OK|R_OK|W_OK|X_OK|STDIN_FILENO|STDOUT_FILENO|STDERR_FILENO)\\\\b\"},{token:\"meta.preprocessor.source.rust\",regex:\"\\\\b\\\\w\\\\(\\\\w\\\\)*!|#\\\\[[\\\\w=\\\\(\\\\)_]+\\\\]\\\\b\"},{token:\"constant.numeric.source.rust\",regex:/\\b(?:0x[a-fA-F0-9_]+|0o[0-7_]+|0b[01_]+|[0-9][0-9_]*(?!\\.))(?:[iu](?:size|8|16|32|64|128))?\\b/},{token:\"constant.numeric.source.rust\",regex:/\\b(?:[0-9][0-9_]*)(?:\\.[0-9][0-9_]*)?(?:[Ee][+-][0-9][0-9_]*)?(?:f32|f64)?\\b/}]},this.normalizeRules()};o.metaData={fileTypes:[\"rs\",\"rc\"],foldingStartMarker:\"^.*\\\\bfn\\\\s*(\\\\w+\\\\s*)?\\\\([^\\\\)]*\\\\)(\\\\s*\\\\{[^\\\\}]*)?\\\\s*$\",foldingStopMarker:\"^\\\\s*\\\\}\",name:\"Rust\",scopeName:\"source.rust\"},r.inherits(o,i),t.RustHighlightRules=o}),ace.define(\"ace/mode/folding/cstyle\",[\"require\",\"exports\",\"module\",\"ace/lib/oop\",\"ace/range\",\"ace/mode/folding/fold_mode\"],function(e,t,n){\"use strict\";var r=e(\"../../lib/oop\"),i=e(\"../../range\").Range,s=e(\"./fold_mode\").FoldMode,o=t.FoldMode=function(e){e&&(this.foldingStartMarker=new RegExp(this.foldingStartMarker.source.replace(/\\|[^|]*?$/,\"|\"+e.start)),this.foldingStopMarker=new RegExp(this.foldingStopMarker.source.replace(/\\|[^|]*?$/,\"|\"+e.end)))};r.inherits(o,s),function(){this.foldingStartMarker=/([\\{\\[\\(])[^\\}\\]\\)]*$|^\\s*(\\/\\*)/,this.foldingStopMarker=/^[^\\[\\{\\(]*([\\}\\]\\)])|^[\\s\\*]*(\\*\\/)/,this.singleLineBlockCommentRe=/^\\s*(\\/\\*).*\\*\\/\\s*$/,this.tripleStarBlockCommentRe=/^\\s*(\\/\\*\\*\\*).*\\*\\/\\s*$/,this.startRegionRe=/^\\s*(\\/\\*|\\/\\/)#?region\\b/,this._getFoldWidgetBase=this.getFoldWidget,this.getFoldWidget=function(e,t,n){var r=e.getLine(n);if(this.singleLineBlockCommentRe.test(r)&&!this.startRegionRe.test(r)&&!this.tripleStarBlockCommentRe.test(r))return\"\";var i=this._getFoldWidgetBase(e,t,n);return!i&&this.startRegionRe.test(r)?\"start\":i},this.getFoldWidgetRange=function(e,t,n,r){var i=e.getLine(n);if(this.startRegionRe.test(i))return this.getCommentRegionBlock(e,i,n);var s=i.match(this.foldingStartMarker);if(s){var o=s.index;if(s[1])return this.openingBracketBlock(e,s[1],n,o);var u=e.getCommentFoldRange(n,o+s[0].length,1);return u&&!u.isMultiLine()&&(r?u=this.getSectionRange(e,n):t!=\"all\"&&(u=null)),u}if(t===\"markbegin\")return;var s=i.match(this.foldingStopMarker);if(s){var o=s.index+s[0].length;return s[1]?this.closingBracketBlock(e,s[1],n,o):e.getCommentFoldRange(n,o,-1)}},this.getSectionRange=function(e,t){var n=e.getLine(t),r=n.search(/\\S/),s=t,o=n.length;t+=1;var u=t,a=e.getLength();while(++t<a){n=e.getLine(t);var f=n.search(/\\S/);if(f===-1)continue;if(r>f)break;var l=this.getFoldWidgetRange(e,\"all\",t);if(l){if(l.start.row<=s)break;if(l.isMultiLine())t=l.end.row;else if(r==f)break}u=t}return new i(s,o,u,e.getLine(u).length)},this.getCommentRegionBlock=function(e,t,n){var r=t.search(/\\s*$/),s=e.getLength(),o=n,u=/^\\s*(?:\\/\\*|\\/\\/|--)#?(end)?region\\b/,a=1;while(++n<s){t=e.getLine(n);var f=u.exec(t);if(!f)continue;f[1]?a--:a++;if(!a)break}var l=n;if(l>o)return new i(o,r,l,t.length)}}.call(o.prototype)}),ace.define(\"ace/mode/rust\",[\"require\",\"exports\",\"module\",\"ace/lib/oop\",\"ace/mode/text\",\"ace/mode/rust_highlight_rules\",\"ace/mode/folding/cstyle\"],function(e,t,n){\"use strict\";var r=e(\"../lib/oop\"),i=e(\"./text\").Mode,s=e(\"./rust_highlight_rules\").RustHighlightRules,o=e(\"./folding/cstyle\").FoldMode,u=function(){this.HighlightRules=s,this.foldingRules=new o,this.$behaviour=this.$defaultBehaviour};r.inherits(u,i),function(){this.lineCommentStart=\"//\",this.blockComment={start:\"/*\",end:\"*/\",nestable:!0},this.$quotes={'\"':'\"'},this.$id=\"ace/mode/rust\"}.call(u.prototype),t.Mode=u});                (function() {\n                    ace.require([\"ace/mode/rust\"], function(m) {\n                        if (typeof module == \"object\" && typeof exports == \"object\" && module) {\n                            module.exports = m;\n                        }\n                    });\n                })();\n"
  },
  {
    "path": "crates/mdbook-html/front-end/playground_editor/theme-dawn.js",
    "content": "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() {\n                    ace.require([\"ace/theme/dawn\"], function(m) {\n                        if (typeof module == \"object\" && typeof exports == \"object\" && module) {\n                            module.exports = m;\n                        }\n                    });\n                })();\n"
  },
  {
    "path": "crates/mdbook-html/front-end/playground_editor/theme-tomorrow_night.js",
    "content": "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() {\n                    ace.require([\"ace/theme/tomorrow_night\"], function(m) {\n                        if (typeof module == \"object\" && typeof exports == \"object\" && module) {\n                            module.exports = m;\n                        }\n                    });\n                })();\n"
  },
  {
    "path": "crates/mdbook-html/front-end/searcher/searcher.js",
    "content": "'use strict';\n\n/* global Mark, elasticlunr, path_to_root */\n\nwindow.search = window.search || {};\n(function search() {\n    // Search functionality\n    //\n    // You can use !hasFocus() to prevent keyhandling in your key\n    // event handlers while the user is typing their search.\n\n    if (!Mark || !elasticlunr) {\n        return;\n    }\n\n    // eslint-disable-next-line max-len\n    // IE 11 Compatibility from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/startsWith\n    if (!String.prototype.startsWith) {\n        String.prototype.startsWith = function(search, pos) {\n            return this.substr(!pos || pos < 0 ? 0 : +pos, search.length) === search;\n        };\n    }\n\n    const search_wrap = document.getElementById('mdbook-search-wrapper'),\n        searchbar_outer = document.getElementById('mdbook-searchbar-outer'),\n        searchbar = document.getElementById('mdbook-searchbar'),\n        searchresults = document.getElementById('mdbook-searchresults'),\n        searchresults_outer = document.getElementById('mdbook-searchresults-outer'),\n        searchresults_header = document.getElementById('mdbook-searchresults-header'),\n        searchicon = document.getElementById('mdbook-search-toggle'),\n        content = document.getElementById('mdbook-content'),\n\n        // SVG text elements don't render if inside a <mark> tag.\n        mark_exclude = ['text'],\n        marker = new Mark(content),\n        URL_SEARCH_PARAM = 'search',\n        URL_MARK_PARAM = 'highlight';\n\n    let current_searchterm = '',\n        doc_urls = [],\n        search_options = {\n            bool: 'AND',\n            expand: true,\n            fields: {\n                title: {boost: 1},\n                body: {boost: 1},\n                breadcrumbs: {boost: 0},\n            },\n        },\n        searchindex = null,\n        results_options = {\n            teaser_word_count: 30,\n            limit_results: 30,\n        },\n        teaser_count = 0;\n\n    function hasFocus() {\n        return searchbar === document.activeElement;\n    }\n\n    function removeChildren(elem) {\n        while (elem.firstChild) {\n            elem.removeChild(elem.firstChild);\n        }\n    }\n\n    // Helper to parse a url into its building blocks.\n    function parseURL(url) {\n        const a = document.createElement('a');\n        a.href = url;\n        return {\n            source: url,\n            protocol: a.protocol.replace(':', ''),\n            host: a.hostname,\n            port: a.port,\n            params: (function() {\n                const ret = {};\n                const seg = a.search.replace(/^\\?/, '').split('&');\n                for (const part of seg) {\n                    if (!part) {\n                        continue;\n                    }\n                    const s = part.split('=');\n                    ret[s[0]] = s[1];\n                }\n                return ret;\n            })(),\n            file: (a.pathname.match(/\\/([^/?#]+)$/i) || ['', ''])[1],\n            hash: a.hash.replace('#', ''),\n            path: a.pathname.replace(/^([^/])/, '/$1'),\n        };\n    }\n\n    // Helper to recreate a url string from its building blocks.\n    function renderURL(urlobject) {\n        let url = urlobject.protocol + '://' + urlobject.host;\n        if (urlobject.port !== '') {\n            url += ':' + urlobject.port;\n        }\n        url += urlobject.path;\n        let joiner = '?';\n        for (const prop in urlobject.params) {\n            if (Object.prototype.hasOwnProperty.call(urlobject.params, prop)) {\n                url += joiner + prop + '=' + urlobject.params[prop];\n                joiner = '&';\n            }\n        }\n        if (urlobject.hash !== '') {\n            url += '#' + urlobject.hash;\n        }\n        return url;\n    }\n\n    // Helper to escape html special chars for displaying the teasers\n    const escapeHTML = (function() {\n        const MAP = {\n            '&': '&amp;',\n            '<': '&lt;',\n            '>': '&gt;',\n            '\"': '&#34;',\n            '\\'': '&#39;',\n        };\n        const repl = function(c) {\n            return MAP[c];\n        };\n        return function(s) {\n            return s.replace(/[&<>'\"]/g, repl);\n        };\n    })();\n\n    function formatSearchMetric(count, searchterm) {\n        if (count === 1) {\n            return count + ' search result for \\'' + searchterm + '\\':';\n        } else if (count === 0) {\n            return 'No search results for \\'' + searchterm + '\\'.';\n        } else {\n            return count + ' search results for \\'' + searchterm + '\\':';\n        }\n    }\n\n    function formatSearchResult(result, searchterms) {\n        const teaser = makeTeaser(escapeHTML(result.doc.body), searchterms);\n        teaser_count++;\n\n        // The ?URL_MARK_PARAM= parameter belongs inbetween the page and the #heading-anchor\n        const url = doc_urls[result.ref].split('#');\n        if (url.length === 1) { // no anchor found\n            url.push('');\n        }\n\n        // encodeURIComponent escapes all chars that could allow an XSS except\n        // for '. Due to that we also manually replace ' with its url-encoded\n        // representation (%27).\n        const encoded_search = encodeURIComponent(searchterms.join(' ')).replace(/'/g, '%27');\n\n        return '<a href=\"' + path_to_root + url[0] + '?' + URL_MARK_PARAM + '=' + encoded_search\n            + '#' + url[1] + '\" aria-details=\"mdbook-teaser_' + teaser_count + '\">'\n            + result.doc.breadcrumbs + '</a>'\n            + '<span class=\"teaser\" id=\"mdbook-teaser_' + teaser_count\n            + '\" aria-label=\"Search Result Teaser\">' + teaser + '</span>';\n    }\n\n    function makeTeaser(body, searchterms) {\n        // The strategy is as follows:\n        // First, assign a value to each word in the document:\n        //  Words that correspond to search terms (stemmer aware): 40\n        //  Normal words: 2\n        //  First word in a sentence: 8\n        // Then use a sliding window with a constant number of words and count the\n        // sum of the values of the words within the window. Then use the window that got the\n        // maximum sum. If there are multiple maximas, then get the last one.\n        // Enclose the terms in <em>.\n        const stemmed_searchterms = searchterms.map(function(w) {\n            return elasticlunr.stemmer(w.toLowerCase());\n        });\n        const searchterm_weight = 40;\n        const weighted = []; // contains elements of [\"word\", weight, index_in_document]\n        // split in sentences, then words\n        const sentences = body.toLowerCase().split('. ');\n        let index = 0;\n        let value = 0;\n        let searchterm_found = false;\n        for (const sentenceindex in sentences) {\n            const words = sentences[sentenceindex].split(' ');\n            value = 8;\n            for (const wordindex in words) {\n                const word = words[wordindex];\n                if (word.length > 0) {\n                    for (const searchtermindex in stemmed_searchterms) {\n                        if (elasticlunr.stemmer(word).startsWith(\n                            stemmed_searchterms[searchtermindex])\n                        ) {\n                            value = searchterm_weight;\n                            searchterm_found = true;\n                        }\n                    }\n                    weighted.push([word, value, index]);\n                    value = 2;\n                }\n                index += word.length;\n                index += 1; // ' ' or '.' if last word in sentence\n            }\n            index += 1; // because we split at a two-char boundary '. '\n        }\n\n        if (weighted.length === 0) {\n            return body;\n        }\n\n        const window_weight = [];\n        const window_size = Math.min(weighted.length, results_options.teaser_word_count);\n\n        let cur_sum = 0;\n        for (let wordindex = 0; wordindex < window_size; wordindex++) {\n            cur_sum += weighted[wordindex][1];\n        }\n        window_weight.push(cur_sum);\n        for (let wordindex = 0; wordindex < weighted.length - window_size; wordindex++) {\n            cur_sum -= weighted[wordindex][1];\n            cur_sum += weighted[wordindex + window_size][1];\n            window_weight.push(cur_sum);\n        }\n\n        let max_sum_window_index = 0;\n        if (searchterm_found) {\n            let max_sum = 0;\n            // backwards\n            for (let i = window_weight.length - 1; i >= 0; i--) {\n                if (window_weight[i] > max_sum) {\n                    max_sum = window_weight[i];\n                    max_sum_window_index = i;\n                }\n            }\n        } else {\n            max_sum_window_index = 0;\n        }\n\n        // add <em/> around searchterms\n        const teaser_split = [];\n        index = weighted[max_sum_window_index][2];\n        for (let i = max_sum_window_index; i < max_sum_window_index + window_size; i++) {\n            const word = weighted[i];\n            if (index < word[2]) {\n                // missing text from index to start of `word`\n                teaser_split.push(body.substring(index, word[2]));\n                index = word[2];\n            }\n            if (word[1] === searchterm_weight) {\n                teaser_split.push('<em>');\n            }\n            index = word[2] + word[0].length;\n            teaser_split.push(body.substring(word[2], index));\n            if (word[1] === searchterm_weight) {\n                teaser_split.push('</em>');\n            }\n        }\n\n        return teaser_split.join('');\n    }\n\n    function init(config) {\n        results_options = config.results_options;\n        search_options = config.search_options;\n        doc_urls = config.doc_urls;\n        searchindex = elasticlunr.Index.load(config.index);\n\n        searchbar_outer.classList.remove('searching');\n\n        searchbar.focus();\n\n        const searchterm = searchbar.value.trim();\n        if (searchterm !== '') {\n            searchbar.classList.add('active');\n            doSearch(searchterm);\n        }\n    }\n\n    function initSearchInteractions(config) {\n        // Set up events\n        searchicon.addEventListener('click', () => {\n            searchIconClickHandler();\n        }, false);\n        searchbar.addEventListener('keyup', () => {\n            searchbarKeyUpHandler();\n        }, false);\n        document.addEventListener('keydown', e => {\n            globalKeyHandler(e);\n        }, false);\n        // If the user uses the browser buttons, do the same as if a reload happened\n        window.onpopstate = () => {\n            doSearchOrMarkFromUrl();\n        };\n        // Suppress \"submit\" events so the page doesn't reload when the user presses Enter\n        document.addEventListener('submit', e => {\n            e.preventDefault();\n        }, false);\n\n        // If reloaded, do the search or mark again, depending on the current url parameters\n        doSearchOrMarkFromUrl();\n\n        // Exported functions\n        config.hasFocus = hasFocus;\n    }\n\n    initSearchInteractions(window.search);\n\n    function unfocusSearchbar() {\n        // hacky, but just focusing a div only works once\n        const tmp = document.createElement('input');\n        tmp.setAttribute('style', 'position: absolute; opacity: 0;');\n        searchicon.appendChild(tmp);\n        tmp.focus();\n        tmp.remove();\n    }\n\n    // On reload or browser history backwards/forwards events, parse the url and do search or mark\n    function doSearchOrMarkFromUrl() {\n        // Check current URL for search request\n        const url = parseURL(window.location.href);\n        if (Object.prototype.hasOwnProperty.call(url.params, URL_SEARCH_PARAM)\n            && url.params[URL_SEARCH_PARAM] !== '') {\n            showSearch(true);\n            searchbar.value = decodeURIComponent(\n                (url.params[URL_SEARCH_PARAM] + '').replace(/\\+/g, '%20'));\n            searchbarKeyUpHandler(); // -> doSearch()\n        } else {\n            showSearch(false);\n        }\n\n        if (Object.prototype.hasOwnProperty.call(url.params, URL_MARK_PARAM)) {\n            const words = decodeURIComponent(url.params[URL_MARK_PARAM]).split(' ');\n            marker.mark(words, {\n                exclude: mark_exclude,\n            });\n\n            const markers = document.querySelectorAll('mark');\n            const hide = () => {\n                for (let i = 0; i < markers.length; i++) {\n                    markers[i].classList.add('fade-out');\n                    window.setTimeout(() => {\n                        marker.unmark();\n                    }, 300);\n                }\n            };\n\n            for (let i = 0; i < markers.length; i++) {\n                markers[i].addEventListener('click', hide);\n            }\n        }\n    }\n\n    // Eventhandler for keyevents on `document`\n    function globalKeyHandler(e) {\n        if (e.altKey ||\n            e.ctrlKey ||\n            e.metaKey ||\n            e.shiftKey ||\n            e.target.type === 'textarea' ||\n            e.target.type === 'text' ||\n            !hasFocus() && /^(?:input|select|textarea)$/i.test(e.target.nodeName)\n        ) {\n            return;\n        }\n\n        if (e.key === 'Escape') {\n            e.preventDefault();\n            searchbar.classList.remove('active');\n            setSearchUrlParameters('',\n                searchbar.value.trim() !== '' ? 'push' : 'replace');\n            if (hasFocus()) {\n                unfocusSearchbar();\n            }\n            showSearch(false);\n            marker.unmark();\n        } else if (!hasFocus() && (e.key === 's' || e.key === '/')) {\n            e.preventDefault();\n            showSearch(true);\n            window.scrollTo(0, 0);\n            searchbar.select();\n        } else if (hasFocus() && (e.key === 'ArrowDown'\n                               || e.key === 'Enter')) {\n            e.preventDefault();\n            const first = searchresults.firstElementChild;\n            if (first !== null) {\n                unfocusSearchbar();\n                first.classList.add('focus');\n                if (e.key === 'Enter') {\n                    window.location.assign(first.querySelector('a'));\n                }\n            }\n        } else if (!hasFocus() && (e.key === 'ArrowDown'\n                                || e.key === 'ArrowUp'\n                                || e.key === 'Enter')) {\n            // not `:focus` because browser does annoying scrolling\n            const focused = searchresults.querySelector('li.focus');\n            if (!focused) {\n                return;\n            }\n            e.preventDefault();\n            if (e.key === 'ArrowDown') {\n                const next = focused.nextElementSibling;\n                if (next) {\n                    focused.classList.remove('focus');\n                    next.classList.add('focus');\n                }\n            } else if (e.key === 'ArrowUp') {\n                focused.classList.remove('focus');\n                const prev = focused.previousElementSibling;\n                if (prev) {\n                    prev.classList.add('focus');\n                } else {\n                    searchbar.select();\n                }\n            } else { // Enter\n                window.location.assign(focused.querySelector('a'));\n            }\n        }\n    }\n\n    function loadSearchScript(url, id) {\n        if (document.getElementById(id)) {\n            return;\n        }\n        searchbar_outer.classList.add('searching');\n\n        const script = document.createElement('script');\n        script.src = url;\n        script.id = id;\n        script.onload = () => init(window.search);\n        script.onerror = error => {\n            console.error(`Failed to load \\`${url}\\`: ${error}`);\n        };\n        document.head.append(script);\n    }\n\n    function showSearch(yes) {\n        if (yes) {\n            loadSearchScript(\n                window.path_to_searchindex_js ||\n                path_to_root + '{{ resource \"searchindex.js\" }}',\n                'mdbook-search-index');\n            search_wrap.classList.remove('hidden');\n            searchicon.setAttribute('aria-expanded', 'true');\n        } else {\n            search_wrap.classList.add('hidden');\n            searchicon.setAttribute('aria-expanded', 'false');\n            const results = searchresults.children;\n            for (let i = 0; i < results.length; i++) {\n                results[i].classList.remove('focus');\n            }\n        }\n    }\n\n    function showResults(yes) {\n        if (yes) {\n            searchresults_outer.classList.remove('hidden');\n        } else {\n            searchresults_outer.classList.add('hidden');\n        }\n    }\n\n    // Eventhandler for search icon\n    function searchIconClickHandler() {\n        if (search_wrap.classList.contains('hidden')) {\n            showSearch(true);\n            window.scrollTo(0, 0);\n            searchbar.select();\n        } else {\n            showSearch(false);\n        }\n    }\n\n    // Eventhandler for keyevents while the searchbar is focused\n    function searchbarKeyUpHandler() {\n        const searchterm = searchbar.value.trim();\n        if (searchterm !== '') {\n            searchbar.classList.add('active');\n            doSearch(searchterm);\n        } else {\n            searchbar.classList.remove('active');\n            showResults(false);\n            removeChildren(searchresults);\n        }\n\n        setSearchUrlParameters(searchterm, 'push_if_new_search_else_replace');\n\n        // Remove marks\n        marker.unmark();\n    }\n\n    // Update current url with ?URL_SEARCH_PARAM= parameter, remove ?URL_MARK_PARAM and\n    // `#heading-anchor`. `action` can be one of \"push\", \"replace\",\n    // \"push_if_new_search_else_replace\" and replaces or pushes a new browser history item.\n    // \"push_if_new_search_else_replace\" pushes if there is no `?URL_SEARCH_PARAM=abc` yet.\n    function setSearchUrlParameters(searchterm, action) {\n        const url = parseURL(window.location.href);\n        const first_search = !Object.prototype.hasOwnProperty.call(url.params, URL_SEARCH_PARAM);\n\n        if (searchterm !== '' || action === 'push_if_new_search_else_replace') {\n            url.params[URL_SEARCH_PARAM] = searchterm;\n            delete url.params[URL_MARK_PARAM];\n            url.hash = '';\n        } else {\n            delete url.params[URL_MARK_PARAM];\n            delete url.params[URL_SEARCH_PARAM];\n        }\n        // A new search will also add a new history item, so the user can go back\n        // to the page prior to searching. A updated search term will only replace\n        // the url.\n        if (action === 'push' || action === 'push_if_new_search_else_replace' && first_search ) {\n            history.pushState({}, document.title, renderURL(url));\n        } else if (action === 'replace' ||\n            action === 'push_if_new_search_else_replace' &&\n            !first_search\n        ) {\n            history.replaceState({}, document.title, renderURL(url));\n        }\n    }\n\n    function doSearch(searchterm) {\n        // Don't search the same twice\n        if (current_searchterm === searchterm) {\n            return;\n        }\n        searchbar_outer.classList.add('searching');\n        if (searchindex === null) {\n            return;\n        }\n\n        current_searchterm = searchterm;\n\n        // Do the actual search\n        const results = searchindex.search(searchterm, search_options);\n        const resultcount = Math.min(results.length, results_options.limit_results);\n\n        // Display search metrics\n        searchresults_header.innerText = formatSearchMetric(resultcount, searchterm);\n\n        // Clear and insert results\n        const searchterms = searchterm.split(' ');\n        removeChildren(searchresults);\n        for (let i = 0; i < resultcount ; i++) {\n            const resultElem = document.createElement('li');\n            resultElem.innerHTML = formatSearchResult(results[i], searchterms);\n            searchresults.appendChild(resultElem);\n        }\n\n        // Display results\n        showResults(true);\n        searchbar_outer.classList.remove('searching');\n    }\n\n    // Exported functions\n    search.hasFocus = hasFocus;\n})(window.search);\n"
  },
  {
    "path": "crates/mdbook-html/front-end/templates/head.hbs",
    "content": "{{!-- Put your head HTML text here --}}\n"
  },
  {
    "path": "crates/mdbook-html/front-end/templates/header.hbs",
    "content": "{{!-- Put your header HTML text here --}}"
  },
  {
    "path": "crates/mdbook-html/front-end/templates/index.hbs",
    "content": "<!DOCTYPE HTML>\n<html lang=\"{{ language }}\" class=\"{{ default_theme }} sidebar-visible\" dir=\"{{ text_direction }}\">\n    <head>\n        <!-- Book generated using mdBook -->\n        <meta charset=\"UTF-8\">\n        <title>{{ title }}</title>\n        {{#if is_print }}\n        <meta name=\"robots\" content=\"noindex\">\n        {{/if}}\n        {{#if base_url}}\n        <base href=\"{{ base_url }}\">\n        {{/if}}\n\n\n        <!-- Custom HTML head -->\n        {{> head}}\n\n        <meta name=\"description\" content=\"{{ description }}\">\n        <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n        <meta name=\"theme-color\" content=\"#ffffff\">\n\n        {{#if favicon_svg}}\n        <link rel=\"icon\" href=\"{{ resource \"favicon.svg\" }}\">\n        {{/if}}\n        {{#if favicon_png}}\n        <link rel=\"shortcut icon\" href=\"{{ resource \"favicon.png\" }}\">\n        {{/if}}\n        <link rel=\"stylesheet\" href=\"{{ resource \"css/variables.css\" }}\">\n        <link rel=\"stylesheet\" href=\"{{ resource \"css/general.css\" }}\">\n        <link rel=\"stylesheet\" href=\"{{ resource \"css/chrome.css\" }}\">\n        {{#if print_enable}}\n        <link rel=\"stylesheet\" href=\"{{ resource \"css/print.css\" }}\" media=\"print\">\n        {{/if}}\n\n        <!-- Fonts -->\n        <link rel=\"stylesheet\" href=\"{{ resource \"fonts/fonts.css\" }}\">\n\n        <!-- Highlight.js Stylesheets -->\n        <link rel=\"stylesheet\" id=\"mdbook-highlight-css\" href=\"{{ resource \"highlight.css\" }}\">\n        <link rel=\"stylesheet\" id=\"mdbook-tomorrow-night-css\" href=\"{{ resource \"tomorrow-night.css\" }}\">\n        <link rel=\"stylesheet\" id=\"mdbook-ayu-highlight-css\" href=\"{{ resource \"ayu-highlight.css\" }}\">\n\n        <!-- Custom theme stylesheets -->\n        {{#each additional_css}}\n        <link rel=\"stylesheet\" href=\"{{ resource this }}\">\n        {{/each}}\n\n        {{#if mathjax_support}}\n        <!-- MathJax -->\n        <script async src=\"https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.1/MathJax.js?config=TeX-AMS-MML_HTMLorMML\"></script>\n        {{/if}}\n\n        <!-- Provide site root and default themes to javascript -->\n        <script>\n            const path_to_root = \"{{ path_to_root }}\";\n            const default_light_theme = \"{{ default_theme }}\";\n            const default_dark_theme = \"{{ preferred_dark_theme }}\";\n        {{#if search_js}}\n            window.path_to_searchindex_js = \"{{ resource \"searchindex.js\" }}\";\n        {{/if}}\n        </script>\n        <!-- Start loading toc.js asap -->\n        <script src=\"{{ resource \"toc.js\" }}\"></script>\n    </head>\n    <body>\n    <div id=\"mdbook-help-container\">\n        <div id=\"mdbook-help-popup\">\n            <h2 class=\"mdbook-help-title\">Keyboard shortcuts</h2>\n            <div>\n                <p>Press <kbd>←</kbd> or <kbd>→</kbd> to navigate between chapters</p>\n                {{#if search_enabled}}\n                <p>Press <kbd>S</kbd> or <kbd>/</kbd> to search in the book</p>\n                {{/if}}\n                <p>Press <kbd>?</kbd> to show this help</p>\n                <p>Press <kbd>Esc</kbd> to hide this help</p>\n            </div>\n        </div>\n    </div>\n    <div id=\"mdbook-body-container\">\n        <!-- Work around some values being stored in localStorage wrapped in quotes -->\n        <script>\n            try {\n                let theme = localStorage.getItem('mdbook-theme');\n                let sidebar = localStorage.getItem('mdbook-sidebar');\n\n                if (theme.startsWith('\"') && theme.endsWith('\"')) {\n                    localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));\n                }\n\n                if (sidebar.startsWith('\"') && sidebar.endsWith('\"')) {\n                    localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));\n                }\n            } catch (e) { }\n        </script>\n\n        <!-- Set the theme before any content is loaded, prevents flash -->\n        <script>\n            const default_theme = window.matchMedia(\"(prefers-color-scheme: dark)\").matches ? default_dark_theme : default_light_theme;\n            let theme;\n            try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }\n            if (theme === null || theme === undefined) { theme = default_theme; }\n            const html = document.documentElement;\n            html.classList.remove('{{ default_theme }}')\n            html.classList.add(theme);\n            html.classList.add(\"js\");\n        </script>\n\n        <input type=\"checkbox\" id=\"mdbook-sidebar-toggle-anchor\" class=\"hidden\">\n\n        <!-- Hide / unhide sidebar before it is displayed -->\n        <script>\n            let sidebar = null;\n            const sidebar_toggle = document.getElementById(\"mdbook-sidebar-toggle-anchor\");\n            if (document.body.clientWidth >= 1080) {\n                try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }\n                sidebar = sidebar || 'visible';\n            } else {\n                sidebar = 'hidden';\n                sidebar_toggle.checked = false;\n            }\n            if (sidebar === 'visible') {\n                sidebar_toggle.checked = true;\n            } else {\n                html.classList.remove('sidebar-visible');\n            }\n        </script>\n\n        <nav id=\"mdbook-sidebar\" class=\"sidebar\" aria-label=\"Table of contents\">\n            <!-- populated by js -->\n            <mdbook-sidebar-scrollbox class=\"sidebar-scrollbox\"></mdbook-sidebar-scrollbox>\n            <noscript>\n                <iframe class=\"sidebar-iframe-outer\" src=\"{{ path_to_root }}toc.html\"></iframe>\n            </noscript>\n            <div id=\"mdbook-sidebar-resize-handle\" class=\"sidebar-resize-handle\">\n                <div class=\"sidebar-resize-indicator\"></div>\n            </div>\n        </nav>\n\n        <div id=\"mdbook-page-wrapper\" class=\"page-wrapper\">\n\n            <div class=\"page\">\n                {{> header}}\n                <div id=\"mdbook-menu-bar-hover-placeholder\"></div>\n                <div id=\"mdbook-menu-bar\" class=\"menu-bar sticky\">\n                    <div class=\"left-buttons\">\n                        <label id=\"mdbook-sidebar-toggle\" class=\"icon-button\" for=\"mdbook-sidebar-toggle-anchor\" title=\"Toggle Table of Contents\" aria-label=\"Toggle Table of Contents\" aria-controls=\"mdbook-sidebar\">\n                            {{fa \"solid\" \"bars\"}}\n                        </label>\n                        <button id=\"mdbook-theme-toggle\" class=\"icon-button\" type=\"button\" title=\"Change theme\" aria-label=\"Change theme\" aria-haspopup=\"true\" aria-expanded=\"false\" aria-controls=\"mdbook-theme-list\">\n                            {{fa \"solid\" \"paintbrush\"}}\n                        </button>\n                        <ul id=\"mdbook-theme-list\" class=\"theme-popup\" aria-label=\"Themes\" role=\"menu\">\n                            <li role=\"none\"><button role=\"menuitem\" class=\"theme\" id=\"mdbook-theme-default_theme\">Auto</button></li>\n                            <li role=\"none\"><button role=\"menuitem\" class=\"theme\" id=\"mdbook-theme-light\">Light</button></li>\n                            <li role=\"none\"><button role=\"menuitem\" class=\"theme\" id=\"mdbook-theme-rust\">Rust</button></li>\n                            <li role=\"none\"><button role=\"menuitem\" class=\"theme\" id=\"mdbook-theme-coal\">Coal</button></li>\n                            <li role=\"none\"><button role=\"menuitem\" class=\"theme\" id=\"mdbook-theme-navy\">Navy</button></li>\n                            <li role=\"none\"><button role=\"menuitem\" class=\"theme\" id=\"mdbook-theme-ayu\">Ayu</button></li>\n                        </ul>\n                        {{#if search_enabled}}\n                        <button id=\"mdbook-search-toggle\" class=\"icon-button\" type=\"button\" title=\"Search (`/`)\" aria-label=\"Toggle Searchbar\" aria-expanded=\"false\" aria-keyshortcuts=\"/ s\" aria-controls=\"mdbook-searchbar\">\n                            {{fa \"solid\" \"magnifying-glass\"}}\n                        </button>\n                        {{/if}}\n                    </div>\n\n                    <h1 class=\"menu-title\">{{ book_title }}</h1>\n\n                    <div class=\"right-buttons\">\n                        {{#if print_enable}}\n                        <a href=\"{{ path_to_root }}print.html\" title=\"Print this book\" aria-label=\"Print this book\">\n                            {{fa \"solid\" \"print\" \"print-button\"}}\n                        </a>\n                        {{/if}}\n                        {{#if git_repository_url}}\n                        <a href=\"{{git_repository_url}}\" title=\"Git repository\" aria-label=\"Git repository\">\n                            {{fa git_repository_icon_class git_repository_icon}}\n                        </a>\n                        {{/if}}\n                        {{#if git_repository_edit_url}}\n                        <a href=\"{{git_repository_edit_url}}\" title=\"Suggest an edit\" aria-label=\"Suggest an edit\" rel=\"edit\">\n                            {{fa \"solid\" \"pencil\" \"git-edit-button\"}}\n                        </a>\n                        {{/if}}\n\n                    </div>\n                </div>\n\n                {{#if search_enabled}}\n                <div id=\"mdbook-search-wrapper\" class=\"hidden\">\n                    <form id=\"mdbook-searchbar-outer\" class=\"searchbar-outer\">\n                        <div class=\"search-wrapper\">\n                            <input type=\"search\" id=\"mdbook-searchbar\" name=\"searchbar\" placeholder=\"Search this book ...\" aria-controls=\"mdbook-searchresults-outer\" aria-describedby=\"searchresults-header\">\n                            <div class=\"spinner-wrapper\">\n                                {{fa \"solid\" \"spinner\" \"fa-spin\"}}\n                            </div>\n                        </div>\n                    </form>\n                    <div id=\"mdbook-searchresults-outer\" class=\"searchresults-outer hidden\">\n                        <div id=\"mdbook-searchresults-header\" class=\"searchresults-header\"></div>\n                        <ul id=\"mdbook-searchresults\">\n                        </ul>\n                    </div>\n                </div>\n                {{/if}}\n\n                <!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->\n                <script>\n                    document.getElementById('mdbook-sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');\n                    document.getElementById('mdbook-sidebar').setAttribute('aria-hidden', sidebar !== 'visible');\n                    Array.from(document.querySelectorAll('#mdbook-sidebar a')).forEach(function(link) {\n                        link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);\n                    });\n                </script>\n\n                <div id=\"mdbook-content\" class=\"content\">\n                    <main>\n                        {{{ content }}}\n                    </main>\n\n                    <nav class=\"nav-wrapper\" aria-label=\"Page navigation\">\n                        <!-- Mobile navigation buttons -->\n                        {{#if previous}}\n                            <a rel=\"prev\" href=\"{{ path_to_root }}{{previous.link}}\" class=\"mobile-nav-chapters previous\" title=\"Previous chapter\" aria-label=\"Previous chapter\" aria-keyshortcuts=\"Left\">\n                                {{#if (eq ../text_direction \"rtl\")}}\n                                {{fa \"solid\" \"angle-right\"}}\n                                {{else}}\n                                {{fa \"solid\" \"angle-left\"}}\n                                {{/if}}\n                            </a>\n                        {{/if}}\n\n                        {{#if next}}\n                            <a rel=\"next prefetch\" href=\"{{ path_to_root }}{{next.link}}\" class=\"mobile-nav-chapters next\" title=\"Next chapter\" aria-label=\"Next chapter\" aria-keyshortcuts=\"Right\">\n                                {{#if (eq ../text_direction \"rtl\")}}\n                                {{fa \"solid\" \"angle-left\"}}\n                                {{else}}\n                                {{fa \"solid\" \"angle-right\"}}\n                                {{/if}}\n                            </a>\n                        {{/if}}\n\n                        <div style=\"clear: both\"></div>\n                    </nav>\n                </div>\n            </div>\n\n            <nav class=\"nav-wide-wrapper\" aria-label=\"Page navigation\">\n                {{#if previous}}\n                    <a rel=\"prev\" href=\"{{ path_to_root }}{{previous.link}}\" class=\"nav-chapters previous\" title=\"Previous chapter\" aria-label=\"Previous chapter\" aria-keyshortcuts=\"Left\">\n                        {{#if (eq ../text_direction \"rtl\")}}\n                        {{fa \"solid\" \"angle-right\"}}\n                        {{else}}\n                        {{fa \"solid\" \"angle-left\"}}\n                        {{/if}}\n                    </a>\n                {{/if}}\n\n                {{#if next}}\n                    <a rel=\"next prefetch\" href=\"{{ path_to_root }}{{next.link}}\" class=\"nav-chapters next\" title=\"Next chapter\" aria-label=\"Next chapter\" aria-keyshortcuts=\"Right\">\n                        {{#if (eq text_direction \"rtl\")}}\n                        {{fa \"solid\" \"angle-left\"}}\n                        {{else}}\n                        {{fa \"solid\" \"angle-right\"}}\n                        {{/if}}\n                    </a>\n                {{/if}}\n            </nav>\n\n        </div>\n\n        <template id=fa-eye>{{fa \"solid\" \"eye\"}}</template>\n        <template id=fa-eye-slash>{{fa \"solid\" \"eye-slash\"}}</template>\n        <template id=fa-copy>{{fa \"regular\" \"copy\"}}</template>\n        <template id=fa-play>{{fa \"solid\" \"play\"}}</template>\n        <template id=fa-clock-rotate-left>{{fa \"solid\" \"clock-rotate-left\"}}</template>\n\n        {{#if live_reload_endpoint}}\n        <!-- Livereload script (if served using the cli tool) -->\n        <script>\n            const wsProtocol = location.protocol === 'https:' ? 'wss:' : 'ws:';\n            const wsAddress = wsProtocol + \"//\" + location.host + \"/\" + \"{{{live_reload_endpoint}}}\";\n            const socket = new WebSocket(wsAddress);\n            socket.onmessage = function (event) {\n                if (event.data === \"reload\") {\n                    socket.close();\n                    location.reload();\n                }\n            };\n\n            window.onbeforeunload = function() {\n                socket.close();\n            }\n        </script>\n        {{/if}}\n\n        {{#if playground_line_numbers}}\n        <script>\n            window.playground_line_numbers = true;\n        </script>\n        {{/if}}\n\n        {{#if playground_copyable}}\n        <script>\n            window.playground_copyable = true;\n        </script>\n        {{/if}}\n\n        {{#if playground_js}}\n        <script src=\"{{ resource \"ace.js\" }}\"></script>\n        <script src=\"{{ resource \"mode-rust.js\" }}\"></script>\n        <script src=\"{{ resource \"editor.js\" }}\"></script>\n        <script src=\"{{ resource \"theme-dawn.js\" }}\"></script>\n        <script src=\"{{ resource \"theme-tomorrow_night.js\" }}\"></script>\n        {{/if}}\n\n        {{#if search_js}}\n        <script src=\"{{ resource \"elasticlunr.min.js\" }}\"></script>\n        <script src=\"{{ resource \"mark.min.js\" }}\"></script>\n        <script src=\"{{ resource \"searcher.js\" }}\"></script>\n        {{/if}}\n\n        <script src=\"{{ resource \"clipboard.min.js\" }}\"></script>\n        <script src=\"{{ resource \"highlight.js\" }}\"></script>\n        <script src=\"{{ resource \"book.js\" }}\"></script>\n\n        <!-- Custom JS scripts -->\n        {{#each additional_js}}\n        <script src=\"{{ resource this}}\"></script>\n        {{/each}}\n\n        {{#if is_print}}\n        {{#if mathjax_support}}\n        <script>\n        window.addEventListener('load', function() {\n            MathJax.Hub.Register.StartupHook('End', function() {\n                window.setTimeout(window.print, 100);\n            });\n        });\n        </script>\n        {{else}}\n        <script>\n        window.addEventListener('load', function() {\n            window.setTimeout(window.print, 100);\n        });\n        </script>\n        {{/if}}\n        {{/if}}\n\n        {{#if fragment_map}}\n        <script>\n            document.addEventListener('DOMContentLoaded', function() {\n                const fragmentMap =\n                    {{{fragment_map}}}\n                ;\n                const target = fragmentMap[window.location.hash];\n                if (target) {\n                    let url = new URL(target, window.location.href);\n                    window.location.replace(url.href);\n                }\n            });\n        </script>\n        {{/if}}\n\n    </div>\n    </body>\n</html>\n"
  },
  {
    "path": "crates/mdbook-html/front-end/templates/redirect.hbs",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"utf-8\">\n    <title>Redirecting...</title>\n    <meta http-equiv=\"refresh\" content=\"0; URL={{url}}\">\n    <link rel=\"canonical\" href=\"{{url}}\">\n  </head>\n  <body>\n      <p>Redirecting to... <a href=\"{{url}}\">{{url}}</a>.</p>\n\n<script>\n    // This handles redirects that involve fragments.\n    document.addEventListener('DOMContentLoaded', function() {\n        const fragmentMap =\n            {{{fragment_map}}}\n        ;\n        const fragment = window.location.hash;\n        if (fragment) {\n            let redirectUrl = \"{{url}}\";\n            const target = fragmentMap[fragment];\n            if (target) {\n                let url = new URL(target, window.location.href);\n                redirectUrl = url.href;\n            } else {\n                let url = new URL(redirectUrl, window.location.href);\n                url.hash = window.location.hash;\n                redirectUrl = url.href;\n            }\n            window.location.replace(redirectUrl);\n        }\n        // else redirect handled by http-equiv\n    });\n</script>\n  </body>\n</html>\n"
  },
  {
    "path": "crates/mdbook-html/front-end/templates/toc.html.hbs",
    "content": "<!DOCTYPE HTML>\n<html lang=\"{{ language }}\" class=\"{{ default_theme }}\" dir=\"{{ text_direction }}\">\n    <head>\n        <!-- sidebar iframe generated using mdBook\n\n        This is a frame, and not included directly in the page, to control the total size of the\n        book. The TOC contains an entry for each page, so if each page includes a copy of the TOC,\n        the total size of the page becomes O(n**2).\n\n        The frame is only used as a fallback when JS is turned off. When it's on, the sidebar is\n        instead added to the main page by `toc.js` instead. The JavaScript mode is better\n        because, when running in a `file:///` URL, the iframed page would not be Same-Origin as\n        the rest of the page, so the sidebar and the main page theme would fall out of sync.\n        -->\n        <meta charset=\"UTF-8\">\n        <meta name=\"robots\" content=\"noindex\">\n        {{#if base_url}}\n        <base href=\"{{ base_url }}\">\n        {{/if}}\n        <!-- Custom HTML head -->\n        {{> head}}\n        <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n        <meta name=\"theme-color\" content=\"#ffffff\">\n        <link rel=\"stylesheet\" href=\"{{ resource \"css/variables.css\" }}\">\n        <link rel=\"stylesheet\" href=\"{{ resource \"css/general.css\" }}\">\n        <link rel=\"stylesheet\" href=\"{{ resource \"css/chrome.css\" }}\">\n        {{#if print_enable}}\n        <link rel=\"stylesheet\" href=\"{{ resource \"css/print.css\" }}\" media=\"print\">\n        {{/if}}\n        <!-- Fonts -->\n        <link rel=\"stylesheet\" href=\"{{ resource \"fonts/fonts.css\" }}\">\n        <!-- Custom theme stylesheets -->\n        {{#each additional_css}}\n        <link rel=\"stylesheet\" href=\"{{ resource this }}\">\n        {{/each}}\n    </head>\n    <body class=\"sidebar-iframe-inner\">\n        {{#toc}}{{/toc}}\n    </body>\n</html>\n"
  },
  {
    "path": "crates/mdbook-html/front-end/templates/toc.js.hbs",
    "content": "// Populate the sidebar\n//\n// This is a script, and not included directly in the page, to control the total size of the book.\n// The TOC contains an entry for each page, so if each page includes a copy of the TOC,\n// the total size of the page becomes O(n**2).\nclass MDBookSidebarScrollbox extends HTMLElement {\n    constructor() {\n        super();\n    }\n    connectedCallback() {\n        this.innerHTML = '{{#toc}}{{/toc}}';\n        // Set the current, active page, and reveal it if it's hidden\n        let current_page = document.location.href.toString().split('#')[0].split('?')[0];\n        if (current_page.endsWith('/')) {\n            current_page += 'index.html';\n        }\n        const links = Array.prototype.slice.call(this.querySelectorAll('a'));\n        const l = links.length;\n        for (let i = 0; i < l; ++i) {\n            const link = links[i];\n            const href = link.getAttribute('href');\n            if (href && !href.startsWith('#') && !/^(?:[a-z+]+:)?\\/\\//.test(href)) {\n                link.href = path_to_root + href;\n            }\n            // The 'index' page is supposed to alias the first chapter in the book.\n            if (link.href === current_page\n                || i === 0\n                && path_to_root === ''\n                && current_page.endsWith('/index.html')) {\n                link.classList.add('active');\n                let parent = link.parentElement;\n                while (parent) {\n                    if (parent.tagName === 'LI' && parent.classList.contains('chapter-item')) {\n                        parent.classList.add('expanded');\n                    }\n                    parent = parent.parentElement;\n                }\n            }\n        }\n        // Track and set sidebar scroll position\n        this.addEventListener('click', e => {\n            if (e.target.tagName === 'A') {\n                const clientRect = e.target.getBoundingClientRect();\n                const sidebarRect = this.getBoundingClientRect();\n                sessionStorage.setItem('sidebar-scroll-offset', clientRect.top - sidebarRect.top);\n            }\n        }, { passive: true });\n        const sidebarScrollOffset = sessionStorage.getItem('sidebar-scroll-offset');\n        sessionStorage.removeItem('sidebar-scroll-offset');\n        if (sidebarScrollOffset !== null) {\n            // preserve sidebar scroll position when navigating via links within sidebar\n            const activeSection = this.querySelector('.active');\n            if (activeSection) {\n                const clientRect = activeSection.getBoundingClientRect();\n                const sidebarRect = this.getBoundingClientRect();\n                const currentOffset = clientRect.top - sidebarRect.top;\n                this.scrollTop += currentOffset - parseFloat(sidebarScrollOffset);\n            }\n        } else {\n            // scroll sidebar to current active section when navigating via\n            // 'next/previous chapter' buttons\n            const activeSection = document.querySelector('#mdbook-sidebar .active');\n            if (activeSection) {\n                activeSection.scrollIntoView({ block: 'center' });\n            }\n        }\n        // Toggle buttons\n        const sidebarAnchorToggles = document.querySelectorAll('.chapter-fold-toggle');\n        function toggleSection(ev) {\n            ev.currentTarget.parentElement.parentElement.classList.toggle('expanded');\n        }\n        Array.from(sidebarAnchorToggles).forEach(el => {\n            el.addEventListener('click', toggleSection);\n        });\n    }\n}\nwindow.customElements.define('mdbook-sidebar-scrollbox', MDBookSidebarScrollbox);\n\n{{#if sidebar_header_nav}}\n\n// ---------------------------------------------------------------------------\n// Support for dynamically adding headers to the sidebar.\n\n(function() {\n    // This is used to detect which direction the page has scrolled since the\n    // last scroll event.\n    let lastKnownScrollPosition = 0;\n    // This is the threshold in px from the top of the screen where it will\n    // consider a header the \"current\" header when scrolling down.\n    const defaultDownThreshold = 150;\n    // Same as defaultDownThreshold, except when scrolling up.\n    const defaultUpThreshold = 300;\n    // The threshold is a virtual horizontal line on the screen where it\n    // considers the \"current\" header to be above the line. The threshold is\n    // modified dynamically to handle headers that are near the bottom of the\n    // screen, and to slightly offset the behavior when scrolling up vs down.\n    let threshold = defaultDownThreshold;\n    // This is used to disable updates while scrolling. This is needed when\n    // clicking the header in the sidebar, which triggers a scroll event. It\n    // is somewhat finicky to detect when the scroll has finished, so this\n    // uses a relatively dumb system of disabling scroll updates for a short\n    // time after the click.\n    let disableScroll = false;\n    // Array of header elements on the page.\n    let headers;\n    // Array of li elements that are initially collapsed headers in the sidebar.\n    // I'm not sure why eslint seems to have a false positive here.\n    // eslint-disable-next-line prefer-const\n    let headerToggles = [];\n    // This is a debugging tool for the threshold which you can enable in the console.\n    let thresholdDebug = false;\n\n    // Updates the threshold based on the scroll position.\n    function updateThreshold() {\n        const scrollTop = window.pageYOffset || document.documentElement.scrollTop;\n        const windowHeight = window.innerHeight;\n        const documentHeight = document.documentElement.scrollHeight;\n\n        // The number of pixels below the viewport, at most documentHeight.\n        // This is used to push the threshold down to the bottom of the page\n        // as the user scrolls towards the bottom.\n        const pixelsBelow = Math.max(0, documentHeight - (scrollTop + windowHeight));\n        // The number of pixels above the viewport, at least defaultDownThreshold.\n        // Similar to pixelsBelow, this is used to push the threshold back towards\n        // the top when reaching the top of the page.\n        const pixelsAbove = Math.max(0, defaultDownThreshold - scrollTop);\n        // How much the threshold should be offset once it gets close to the\n        // bottom of the page.\n        const bottomAdd = Math.max(0, windowHeight - pixelsBelow - defaultDownThreshold);\n        let adjustedBottomAdd = bottomAdd;\n\n        // Adjusts bottomAdd for a small document. The calculation above\n        // assumes the document is at least twice the windowheight in size. If\n        // it is less than that, then bottomAdd needs to be shrunk\n        // proportional to the difference in size.\n        if (documentHeight < windowHeight * 2) {\n            const maxPixelsBelow = documentHeight - windowHeight;\n            const t = 1 - pixelsBelow / Math.max(1, maxPixelsBelow);\n            const clamp = Math.max(0, Math.min(1, t));\n            adjustedBottomAdd *= clamp;\n        }\n\n        let scrollingDown = true;\n        if (scrollTop < lastKnownScrollPosition) {\n            scrollingDown = false;\n        }\n\n        if (scrollingDown) {\n            // When scrolling down, move the threshold up towards the default\n            // downwards threshold position. If near the bottom of the page,\n            // adjustedBottomAdd will offset the threshold towards the bottom\n            // of the page.\n            const amountScrolledDown = scrollTop - lastKnownScrollPosition;\n            const adjustedDefault = defaultDownThreshold + adjustedBottomAdd;\n            threshold = Math.max(adjustedDefault, threshold - amountScrolledDown);\n        } else {\n            // When scrolling up, move the threshold down towards the default\n            // upwards threshold position. If near the bottom of the page,\n            // quickly transition the threshold back up where it normally\n            // belongs.\n            const amountScrolledUp = lastKnownScrollPosition - scrollTop;\n            const adjustedDefault = defaultUpThreshold - pixelsAbove\n                + Math.max(0, adjustedBottomAdd - defaultDownThreshold);\n            threshold = Math.min(adjustedDefault, threshold + amountScrolledUp);\n        }\n\n        if (documentHeight <= windowHeight) {\n            threshold = 0;\n        }\n\n        if (thresholdDebug) {\n            const id = 'mdbook-threshold-debug-data';\n            let data = document.getElementById(id);\n            if (data === null) {\n                data = document.createElement('div');\n                data.id = id;\n                data.style.cssText = `\n                    position: fixed;\n                    top: 50px;\n                    right: 10px;\n                    background-color: 0xeeeeee;\n                    z-index: 9999;\n                    pointer-events: none;\n                `;\n                document.body.appendChild(data);\n            }\n            data.innerHTML = `\n                <table>\n                  <tr><td>documentHeight</td><td>${documentHeight.toFixed(1)}</td></tr>\n                  <tr><td>windowHeight</td><td>${windowHeight.toFixed(1)}</td></tr>\n                  <tr><td>scrollTop</td><td>${scrollTop.toFixed(1)}</td></tr>\n                  <tr><td>pixelsAbove</td><td>${pixelsAbove.toFixed(1)}</td></tr>\n                  <tr><td>pixelsBelow</td><td>${pixelsBelow.toFixed(1)}</td></tr>\n                  <tr><td>bottomAdd</td><td>${bottomAdd.toFixed(1)}</td></tr>\n                  <tr><td>adjustedBottomAdd</td><td>${adjustedBottomAdd.toFixed(1)}</td></tr>\n                  <tr><td>scrollingDown</td><td>${scrollingDown}</td></tr>\n                  <tr><td>threshold</td><td>${threshold.toFixed(1)}</td></tr>\n                </table>\n            `;\n            drawDebugLine();\n        }\n\n        lastKnownScrollPosition = scrollTop;\n    }\n\n    function drawDebugLine() {\n        if (!document.body) {\n            return;\n        }\n        const id = 'mdbook-threshold-debug-line';\n        const existingLine = document.getElementById(id);\n        if (existingLine) {\n            existingLine.remove();\n        }\n        const line = document.createElement('div');\n        line.id = id;\n        line.style.cssText = `\n            position: fixed;\n            top: ${threshold}px;\n            left: 0;\n            width: 100vw;\n            height: 2px;\n            background-color: red;\n            z-index: 9999;\n            pointer-events: none;\n        `;\n        document.body.appendChild(line);\n    }\n\n    function mdbookEnableThresholdDebug() {\n        thresholdDebug = true;\n        updateThreshold();\n        drawDebugLine();\n    }\n\n    window.mdbookEnableThresholdDebug = mdbookEnableThresholdDebug;\n\n    // Updates which headers in the sidebar should be expanded. If the current\n    // header is inside a collapsed group, then it, and all its parents should\n    // be expanded.\n    function updateHeaderExpanded(currentA) {\n        // Add expanded to all header-item li ancestors.\n        let current = currentA.parentElement;\n        while (current) {\n            if (current.tagName === 'LI' && current.classList.contains('header-item')) {\n                current.classList.add('expanded');\n            }\n            current = current.parentElement;\n        }\n    }\n\n    // Updates which header is marked as the \"current\" header in the sidebar.\n    // This is done with a virtual Y threshold, where headers at or below\n    // that line will be considered the current one.\n    function updateCurrentHeader() {\n        if (!headers || !headers.length) {\n            return;\n        }\n\n        // Reset the classes, which will be rebuilt below.\n        const els = document.getElementsByClassName('current-header');\n        for (const el of els) {\n            el.classList.remove('current-header');\n        }\n        for (const toggle of headerToggles) {\n            toggle.classList.remove('expanded');\n        }\n\n        // Find the last header that is above the threshold.\n        let lastHeader = null;\n        for (const header of headers) {\n            const rect = header.getBoundingClientRect();\n            if (rect.top <= threshold) {\n                lastHeader = header;\n            } else {\n                break;\n            }\n        }\n        if (lastHeader === null) {\n            lastHeader = headers[0];\n            const rect = lastHeader.getBoundingClientRect();\n            const windowHeight = window.innerHeight;\n            if (rect.top >= windowHeight) {\n                return;\n            }\n        }\n\n        // Get the anchor in the summary.\n        const href = '#' + lastHeader.id;\n        const a = [...document.querySelectorAll('.header-in-summary')]\n            .find(element => element.getAttribute('href') === href);\n        if (!a) {\n            return;\n        }\n\n        a.classList.add('current-header');\n\n        updateHeaderExpanded(a);\n    }\n\n    // Updates which header is \"current\" based on the threshold line.\n    function reloadCurrentHeader() {\n        if (disableScroll) {\n            return;\n        }\n        updateThreshold();\n        updateCurrentHeader();\n    }\n\n\n    // When clicking on a header in the sidebar, this adjusts the threshold so\n    // that it is located next to the header. This is so that header becomes\n    // \"current\".\n    function headerThresholdClick(event) {\n        // See disableScroll description why this is done.\n        disableScroll = true;\n        setTimeout(() => {\n            disableScroll = false;\n        }, 100);\n        // requestAnimationFrame is used to delay the update of the \"current\"\n        // header until after the scroll is done, and the header is in the new\n        // position.\n        requestAnimationFrame(() => {\n            requestAnimationFrame(() => {\n                // Closest is needed because if it has child elements like <code>.\n                const a = event.target.closest('a');\n                const href = a.getAttribute('href');\n                const targetId = href.substring(1);\n                const targetElement = document.getElementById(targetId);\n                if (targetElement) {\n                    threshold = targetElement.getBoundingClientRect().bottom;\n                    updateCurrentHeader();\n                }\n            });\n        });\n    }\n\n    // Takes the nodes from the given head and copies them over to the\n    // destination, along with some filtering.\n    function filterHeader(source, dest) {\n        const clone = source.cloneNode(true);\n        clone.querySelectorAll('mark').forEach(mark => {\n            mark.replaceWith(...mark.childNodes);\n        });\n        dest.append(...clone.childNodes);\n    }\n\n    // Scans page for headers and adds them to the sidebar.\n    document.addEventListener('DOMContentLoaded', function() {\n        const activeSection = document.querySelector('#mdbook-sidebar .active');\n        if (activeSection === null) {\n            return;\n        }\n\n        const main = document.getElementsByTagName('main')[0];\n        headers = Array.from(main.querySelectorAll('h2, h3, h4, h5, h6'))\n            .filter(h => h.id !== '' && h.children.length && h.children[0].tagName === 'A');\n\n        if (headers.length === 0) {\n            return;\n        }\n\n        // Build a tree of headers in the sidebar.\n\n        const stack = [];\n\n        const firstLevel = parseInt(headers[0].tagName.charAt(1));\n        for (let i = 1; i < firstLevel; i++) {\n            const ol = document.createElement('ol');\n            ol.classList.add('section');\n            if (stack.length > 0) {\n                stack[stack.length - 1].ol.appendChild(ol);\n            }\n            stack.push({level: i + 1, ol: ol});\n        }\n\n        // The level where it will start folding deeply nested headers.\n        const foldLevel = 3;\n\n        for (let i = 0; i < headers.length; i++) {\n            const header = headers[i];\n            const level = parseInt(header.tagName.charAt(1));\n\n            const currentLevel = stack[stack.length - 1].level;\n            if (level > currentLevel) {\n                // Begin nesting to this level.\n                for (let nextLevel = currentLevel + 1; nextLevel <= level; nextLevel++) {\n                    const ol = document.createElement('ol');\n                    ol.classList.add('section');\n                    const last = stack[stack.length - 1];\n                    const lastChild = last.ol.lastChild;\n                    // Handle the case where jumping more than one nesting\n                    // level, which doesn't have a list item to place this new\n                    // list inside of.\n                    if (lastChild) {\n                        lastChild.appendChild(ol);\n                    } else {\n                        last.ol.appendChild(ol);\n                    }\n                    stack.push({level: nextLevel, ol: ol});\n                }\n            } else if (level < currentLevel) {\n                while (stack.length > 1 && stack[stack.length - 1].level > level) {\n                    stack.pop();\n                }\n            }\n\n            const li = document.createElement('li');\n            li.classList.add('header-item');\n            li.classList.add('expanded');\n            if (level < foldLevel) {\n                li.classList.add('expanded');\n            }\n            const span = document.createElement('span');\n            span.classList.add('chapter-link-wrapper');\n            const a = document.createElement('a');\n            span.appendChild(a);\n            a.href = '#' + header.id;\n            a.classList.add('header-in-summary');\n            filterHeader(header.children[0], a);\n            a.addEventListener('click', headerThresholdClick);\n            const nextHeader = headers[i + 1];\n            if (nextHeader !== undefined) {\n                const nextLevel = parseInt(nextHeader.tagName.charAt(1));\n                if (nextLevel > level && level >= foldLevel) {\n                    const toggle = document.createElement('a');\n                    toggle.classList.add('chapter-fold-toggle');\n                    toggle.classList.add('header-toggle');\n                    toggle.addEventListener('click', () => {\n                        li.classList.toggle('expanded');\n                    });\n                    const toggleDiv = document.createElement('div');\n                    toggleDiv.textContent = '❱';\n                    toggle.appendChild(toggleDiv);\n                    span.appendChild(toggle);\n                    headerToggles.push(li);\n                }\n            }\n            li.appendChild(span);\n\n            const currentParent = stack[stack.length - 1];\n            currentParent.ol.appendChild(li);\n        }\n\n        const onThisPage = document.createElement('div');\n        onThisPage.classList.add('on-this-page');\n        onThisPage.append(stack[0].ol);\n        const activeItemSpan = activeSection.parentElement;\n        activeItemSpan.after(onThisPage);\n    });\n\n    document.addEventListener('DOMContentLoaded', reloadCurrentHeader);\n    document.addEventListener('scroll', reloadCurrentHeader, { passive: true });\n})();\n\n{{/if}}\n"
  },
  {
    "path": "crates/mdbook-html/src/html/admonitions.rs",
    "content": "use pulldown_cmark::BlockQuoteKind;\n\n// This icon is from GitHub, MIT License, see https://github.com/primer/octicons\nconst ICON_NOTE: &str = r#\"<path d=\"M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8Zm8-6.5a6.5 6.5 0 1 0 0 13 6.5 6.5 0 0 0 0-13ZM6.5 7.75A.75.75 0 0 1 7.25 7h1a.75.75 0 0 1 .75.75v2.75h.25a.75.75 0 0 1 0 1.5h-2a.75.75 0 0 1 0-1.5h.25v-2h-.25a.75.75 0 0 1-.75-.75ZM8 6a1 1 0 1 1 0-2 1 1 0 0 1 0 2Z\"></path>\"#;\n\n// This icon is from GitHub, MIT License, see https://github.com/primer/octicons\nconst ICON_TIP: &str = r#\"<path d=\"M8 1.5c-2.363 0-4 1.69-4 3.75 0 .984.424 1.625.984 2.304l.214.253c.223.264.47.556.673.848.284.411.537.896.621 1.49a.75.75 0 0 1-1.484.211c-.04-.282-.163-.547-.37-.847a8.456 8.456 0 0 0-.542-.68c-.084-.1-.173-.205-.268-.32C3.201 7.75 2.5 6.766 2.5 5.25 2.5 2.31 4.863 0 8 0s5.5 2.31 5.5 5.25c0 1.516-.701 2.5-1.328 3.259-.095.115-.184.22-.268.319-.207.245-.383.453-.541.681-.208.3-.33.565-.37.847a.751.751 0 0 1-1.485-.212c.084-.593.337-1.078.621-1.489.203-.292.45-.584.673-.848.075-.088.147-.173.213-.253.561-.679.985-1.32.985-2.304 0-2.06-1.637-3.75-4-3.75ZM5.75 12h4.5a.75.75 0 0 1 0 1.5h-4.5a.75.75 0 0 1 0-1.5ZM6 15.25a.75.75 0 0 1 .75-.75h2.5a.75.75 0 0 1 0 1.5h-2.5a.75.75 0 0 1-.75-.75Z\"></path>\"#;\n\n// This icon is from GitHub, MIT License, see https://github.com/primer/octicons\nconst ICON_IMPORTANT: &str = r#\"<path d=\"M0 1.75C0 .784.784 0 1.75 0h12.5C15.216 0 16 .784 16 1.75v9.5A1.75 1.75 0 0 1 14.25 13H8.06l-2.573 2.573A1.458 1.458 0 0 1 3 14.543V13H1.75A1.75 1.75 0 0 1 0 11.25Zm1.75-.25a.25.25 0 0 0-.25.25v9.5c0 .138.112.25.25.25h2a.75.75 0 0 1 .75.75v2.19l2.72-2.72a.749.749 0 0 1 .53-.22h6.5a.25.25 0 0 0 .25-.25v-9.5a.25.25 0 0 0-.25-.25Zm7 2.25v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 9a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\"></path>\"#;\n\n// This icon is from GitHub, MIT License, see https://github.com/primer/octicons\nconst ICON_WARNING: &str = r#\"<path d=\"M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\"></path>\"#;\n\n// This icon is from GitHub, MIT License, see https://github.com/primer/octicons\nconst ICON_CAUTION: &str = r#\"<path d=\"M4.47.22A.749.749 0 0 1 5 0h6c.199 0 .389.079.53.22l4.25 4.25c.141.14.22.331.22.53v6a.749.749 0 0 1-.22.53l-4.25 4.25A.749.749 0 0 1 11 16H5a.749.749 0 0 1-.53-.22L.22 11.53A.749.749 0 0 1 0 11V5c0-.199.079-.389.22-.53Zm.84 1.28L1.5 5.31v5.38l3.81 3.81h5.38l3.81-3.81V5.31L10.69 1.5ZM8 4a.75.75 0 0 1 .75.75v3.5a.75.75 0 0 1-1.5 0v-3.5A.75.75 0 0 1 8 4Zm0 8a1 1 0 1 1 0-2 1 1 0 0 1 0 2Z\"></path>\"#;\n\npub(crate) fn select_tag(kind: BlockQuoteKind) -> (&'static str, &'static str, &'static str) {\n    match kind {\n        BlockQuoteKind::Note => (\"note\", ICON_NOTE, \"Note\"),\n        BlockQuoteKind::Tip => (\"tip\", ICON_TIP, \"Tip\"),\n        BlockQuoteKind::Important => (\"important\", ICON_IMPORTANT, \"Important\"),\n        BlockQuoteKind::Warning => (\"warning\", ICON_WARNING, \"Warning\"),\n        BlockQuoteKind::Caution => (\"caution\", ICON_CAUTION, \"Caution\"),\n    }\n}\n"
  },
  {
    "path": "crates/mdbook-html/src/html/hide_lines.rs",
    "content": "//! Support for hiding code lines.\n\nuse crate::html::{Element, Node};\nuse ego_tree::{NodeId, Tree};\nuse html5ever::tendril::StrTendril;\nuse mdbook_core::static_regex;\nuse std::collections::HashMap;\n\n/// Wraps hidden lines in a `<span>` for the given code block.\npub(crate) fn hide_lines(\n    tree: &mut Tree<Node>,\n    code_id: NodeId,\n    hidelines: &HashMap<String, String>,\n) {\n    let mut node = tree.get_mut(code_id).unwrap();\n    let el = node.value().as_element().unwrap();\n\n    let classes: Vec<_> = el.attr(\"class\").unwrap_or_default().split(' ').collect();\n    let language = classes\n        .iter()\n        .filter_map(|cls| cls.strip_prefix(\"language-\"))\n        .next()\n        .unwrap_or_default()\n        .to_string();\n    let hideline_info = classes\n        .iter()\n        .filter_map(|cls| cls.strip_prefix(\"hidelines=\"))\n        .map(|prefix| prefix.to_string())\n        .next();\n\n    if let Some(mut child) = node.first_child()\n        && let Node::Text(text) = child.value()\n    {\n        if language == \"rust\" {\n            let new_nodes = hide_lines_rust(text);\n            child.detach();\n            let root = tree.extend_tree(new_nodes);\n            let root_id = root.id();\n            let mut node = tree.get_mut(code_id).unwrap();\n            node.reparent_from_id_append(root_id);\n        } else {\n            // Use the prefix from the code block, else the prefix from config.\n            let hidelines_prefix = hideline_info\n                .as_deref()\n                .or_else(|| hidelines.get(&language).map(|p| p.as_str()));\n            if let Some(prefix) = hidelines_prefix {\n                let new_nodes = hide_lines_with_prefix(text, prefix);\n                child.detach();\n                let root = tree.extend_tree(new_nodes);\n                let root_id = root.id();\n                let mut node = tree.get_mut(code_id).unwrap();\n                node.reparent_from_id_append(root_id);\n            }\n        }\n    }\n}\n\n/// Wraps hidden lines in a `<span>` specifically for Rust code blocks.\nfn hide_lines_rust(text: &StrTendril) -> Tree<Node> {\n    static_regex!(BORING_LINES_REGEX, r\"^(\\s*)#(.?)(.*)$\");\n\n    let mut tree = Tree::new(Node::Fragment);\n    let mut root = tree.root_mut();\n    let mut lines = text.lines().peekable();\n    while let Some(line) = lines.next() {\n        // Don't include newline on the last line.\n        let newline = if lines.peek().is_none() { \"\" } else { \"\\n\" };\n        if let Some(caps) = BORING_LINES_REGEX.captures(line) {\n            if &caps[2] == \"#\" {\n                root.append(Node::Text(\n                    format!(\"{}{}{}{newline}\", &caps[1], &caps[2], &caps[3]).into(),\n                ));\n                continue;\n            } else if matches!(&caps[2], \"\" | \" \") {\n                let mut span = Element::new(\"span\");\n                span.insert_attr(\"class\", \"boring\".into());\n                let mut span = root.append(Node::Element(span));\n                span.append(Node::Text(\n                    format!(\"{}{}{newline}\", &caps[1], &caps[3]).into(),\n                ));\n                continue;\n            }\n        }\n        root.append(Node::Text(format!(\"{line}{newline}\").into()));\n    }\n    tree\n}\n\n/// Wraps hidden lines in a `<span>` tag for lines starting with the given prefix.\nfn hide_lines_with_prefix(content: &str, prefix: &str) -> Tree<Node> {\n    let mut tree = Tree::new(Node::Fragment);\n    let mut root = tree.root_mut();\n    for line in content.lines() {\n        if line.trim_start().starts_with(prefix) {\n            let pos = line.find(prefix).unwrap();\n            let (ws, rest) = (&line[..pos], &line[pos + prefix.len()..]);\n            let mut span = Element::new(\"span\");\n            span.insert_attr(\"class\", \"boring\".into());\n            let mut span = root.append(Node::Element(span));\n            span.append(Node::Text(format!(\"{ws}{rest}\\n\").into()));\n        } else {\n            root.append(Node::Text(format!(\"{line}\\n\").into()));\n        }\n    }\n    tree\n}\n\n/// If this code text is missing an `fn main`, the wrap it with `fn main` in a\n/// fashion similar to rustdoc, with the wrapper hidden.\npub(crate) fn wrap_rust_main(text: &str) -> Option<String> {\n    if !text.contains(\"fn main\") && !text.contains(\"quick_main!\") {\n        let (attrs, code) = partition_rust_source(text);\n        let newline = if code.is_empty() || code.ends_with('\\n') {\n            \"\"\n        } else {\n            \"\\n\"\n        };\n        Some(format!(\n            \"# #![allow(unused)]\\n{attrs}# fn main() {{\\n{code}{newline}# }}\"\n        ))\n    } else {\n        None\n    }\n}\n\n/// Splits Rust inner attributes from the given source string.\n///\n/// Returns `(inner_attrs, rest_of_code)`.\nfn partition_rust_source(s: &str) -> (&str, &str) {\n    static_regex!(\n        HEADER_RE,\n        r\"^(?mx)\n        (\n            (?:\n                ^[ \\t]*\\#!\\[.* (?:\\r?\\n)?\n                |\n                ^\\s* (?:\\r?\\n)?\n            )*\n        )\"\n    );\n    let split_idx = match HEADER_RE.captures(s) {\n        Some(caps) => {\n            let attributes = &caps[1];\n            if attributes.trim().is_empty() {\n                // Don't include pure whitespace as an attribute. The\n                // whitespace in the regex is intended to handle multiple\n                // attributes *separated* by potential whitespace.\n                0\n            } else {\n                attributes.len()\n            }\n        }\n        None => 0,\n    };\n    s.split_at(split_idx)\n}\n\n#[test]\nfn it_partitions_rust_source() {\n    assert_eq!(partition_rust_source(\"\"), (\"\", \"\"));\n    assert_eq!(partition_rust_source(\"let x = 1;\"), (\"\", \"let x = 1;\"));\n    assert_eq!(\n        partition_rust_source(\"fn main()\\n{ let x = 1; }\\n\"),\n        (\"\", \"fn main()\\n{ let x = 1; }\\n\")\n    );\n    assert_eq!(\n        partition_rust_source(\"#![allow(foo)]\"),\n        (\"#![allow(foo)]\", \"\")\n    );\n    assert_eq!(\n        partition_rust_source(\"#![allow(foo)]\\n\"),\n        (\"#![allow(foo)]\\n\", \"\")\n    );\n    assert_eq!(\n        partition_rust_source(\"#![allow(foo)]\\nlet x = 1;\"),\n        (\"#![allow(foo)]\\n\", \"let x = 1;\")\n    );\n    assert_eq!(\n        partition_rust_source(\n            \"\\n\\\n        #![allow(foo)]\\n\\\n        \\n\\\n        #![allow(bar)]\\n\\\n        \\n\\\n        let x = 1;\"\n        ),\n        (\"\\n#![allow(foo)]\\n\\n#![allow(bar)]\\n\\n\", \"let x = 1;\")\n    );\n    assert_eq!(\n        partition_rust_source(\"    // Example\"),\n        (\"\", \"    // Example\")\n    );\n}\n"
  },
  {
    "path": "crates/mdbook-html/src/html/mod.rs",
    "content": "//! HTML rendering support.\n//!\n//! This module's primary entry point is [`render_markdown`] which will take\n//! markdown text and render it to HTML. In summary, the general procedure of\n//! that function is:\n//!\n//! 1. Use [`pulldown_cmark`] to parse the markdown and generate events.\n//! 2. [`tree`] converts those events to a tree data structure.\n//!      1. Parse HTML inside the markdown using [`tokenizer`].\n//!      2. Apply various transformations to the tree data structure, such as adding header links.\n//! 3. Serialize the tree to HTML in [`serialize()`].\n\nuse ego_tree::Tree;\nuse mdbook_core::book::{Book, Chapter};\nuse mdbook_core::config::{HtmlConfig, RustEdition};\nuse mdbook_markdown::{MarkdownOptions, new_cmark_parser};\nuse std::path::{Path, PathBuf};\n\nmod admonitions;\nmod hide_lines;\nmod print;\nmod serialize;\n#[cfg(test)]\nmod tests;\nmod tokenizer;\nmod tree;\n\npub(crate) use hide_lines::{hide_lines, wrap_rust_main};\npub(crate) use print::render_print_page;\npub(crate) use serialize::serialize;\npub(crate) use tree::{Element, Node};\n\n/// Options for converting a single chapter's markdown to HTML.\npub(crate) struct HtmlRenderOptions<'a> {\n    /// Options for parsing markdown.\n    pub markdown_options: MarkdownOptions,\n    /// The chapter's location, relative to the `SUMMARY.md` file.\n    pub path: &'a Path,\n    /// The default Rust edition, used to set the proper class on the code blocks.\n    pub edition: Option<RustEdition>,\n    /// The [`HtmlConfig`], whose options affect how the HTML is generated.\n    pub config: &'a HtmlConfig,\n}\n\nimpl<'a> HtmlRenderOptions<'a> {\n    /// Creates a new [`HtmlRenderOptions`].\n    pub(crate) fn new(\n        path: &'a Path,\n        config: &'a HtmlConfig,\n        edition: Option<RustEdition>,\n    ) -> HtmlRenderOptions<'a> {\n        let mut markdown_options = MarkdownOptions::default();\n        markdown_options.smart_punctuation = config.smart_punctuation;\n        markdown_options.definition_lists = config.definition_lists;\n        markdown_options.admonitions = config.admonitions;\n        HtmlRenderOptions {\n            markdown_options,\n            path,\n            edition,\n            config,\n        }\n    }\n}\n\n/// Renders markdown to HTML.\npub(crate) fn render_markdown(text: &str, options: &HtmlRenderOptions<'_>) -> String {\n    let tree = build_tree(text, options);\n    let mut output = String::new();\n    serialize::serialize(&tree, &mut output);\n    output\n}\n\n/// Renders markdown to a [`Tree`].\nfn build_tree(text: &str, options: &HtmlRenderOptions<'_>) -> Tree<Node> {\n    let events = new_cmark_parser(text, &options.markdown_options);\n    tree::MarkdownTreeBuilder::build(options, events)\n}\n\n/// The parsed chapter, and some information about the chapter.\npub(crate) struct ChapterTree<'book> {\n    pub(crate) chapter: &'book Chapter,\n    /// The path to the chapter relative to the root with the `.html` extension.\n    pub(crate) html_path: PathBuf,\n    /// The chapter tree.\n    pub(crate) tree: Tree<Node>,\n}\n\n/// Creates all of the [`ChapterTree`]s for the book.\npub(crate) fn build_trees<'book>(\n    book: &'book Book,\n    html_config: &HtmlConfig,\n    edition: Option<RustEdition>,\n) -> Vec<ChapterTree<'book>> {\n    book.chapters()\n        .map(|ch| {\n            let path = ch.path.as_ref().unwrap();\n            let html_path = ch.path.as_ref().unwrap().with_extension(\"html\");\n            let options = HtmlRenderOptions::new(path, html_config, edition);\n            let tree = build_tree(&ch.content, &options);\n\n            ChapterTree {\n                chapter: ch,\n                html_path,\n                tree,\n            }\n        })\n        .collect()\n}\n"
  },
  {
    "path": "crates/mdbook-html/src/html/print.rs",
    "content": "//! Support for generating the print page.\n//!\n//! The print page takes all the individual chapters (as `Tree<Node>`\n//! elements) and modifies the chapters so that they work on a consolidated\n//! print page, and then serializes it all as one HTML page.\n\nuse super::Node;\nuse crate::html::{ChapterTree, Element, serialize};\nuse crate::utils::{ToUrlPath, id_from_content, normalize_path, unique_id};\nuse mdbook_core::static_regex;\nuse std::collections::{HashMap, HashSet};\nuse std::path::PathBuf;\n\n/// Takes all the chapter trees, modifies them to be suitable to render for\n/// the print page, and returns an string of all the chapters rendered to a\n/// single HTML page.\npub(crate) fn render_print_page(mut chapter_trees: Vec<ChapterTree<'_>>) -> String {\n    let (id_remap, mut id_counter) = make_ids_unique(&mut chapter_trees);\n    let path_to_root_id = make_root_id_map(&mut chapter_trees, &mut id_counter);\n    rewrite_links(&mut chapter_trees, &id_remap, &path_to_root_id);\n\n    let mut print_content = String::new();\n    for ChapterTree { tree, .. } in chapter_trees {\n        if !print_content.is_empty() {\n            // Add page break between chapters\n            // See https://developer.mozilla.org/en-US/docs/Web/CSS/break-before and https://developer.mozilla.org/en-US/docs/Web/CSS/page-break-before\n            // Add both two CSS properties because of the compatibility issue\n            print_content\n                .push_str(r#\"<div style=\"break-before: page; page-break-before: always;\"></div>\"#);\n        }\n        serialize(&tree, &mut print_content);\n    }\n    print_content\n}\n\n/// Make all IDs unique, and create a map from old to new IDs.\n///\n/// The first map is a map of the chapter path to the IDs that were rewritten\n/// in that chapter (old ID to new ID).\n///\n/// The second map is a map of every ID seen to the number of times it has\n/// been seen. This is used to generate unique IDs.\nfn make_ids_unique(\n    chapter_trees: &mut [ChapterTree<'_>],\n) -> (HashMap<PathBuf, HashMap<String, String>>, HashSet<String>) {\n    let mut id_remap = HashMap::new();\n    let mut id_counter = HashSet::new();\n    for ChapterTree {\n        html_path, tree, ..\n    } in chapter_trees\n    {\n        for value in tree.values_mut() {\n            if let Node::Element(el) = value\n                && let Some(id) = el.attr(\"id\")\n            {\n                let new_id = unique_id(id, &mut id_counter);\n                if new_id != id {\n                    let id = id.to_string();\n                    el.insert_attr(\"id\", new_id.clone().into());\n\n                    let map: &mut HashMap<_, _> = id_remap.entry(html_path.clone()).or_default();\n                    map.insert(id, new_id);\n                }\n            }\n        }\n    }\n    (id_remap, id_counter)\n}\n\n/// Generates a map of a chapter path to the ID of the top of the chapter.\n///\n/// If a chapter is missing an `h1` tag, then one is synthesized so that the\n/// print output has something to link to.\nfn make_root_id_map(\n    chapter_trees: &mut [ChapterTree<'_>],\n    id_counter: &mut HashSet<String>,\n) -> HashMap<PathBuf, String> {\n    let mut path_to_root_id = HashMap::new();\n    for ChapterTree {\n        chapter,\n        html_path,\n        tree,\n        ..\n    } in chapter_trees\n    {\n        let mut h1_found = false;\n        for value in tree.values_mut() {\n            if let Node::Element(el) = value {\n                if el.name() == \"h1\" {\n                    if let Some(id) = el.attr(\"id\") {\n                        h1_found = true;\n                        path_to_root_id.insert(html_path.clone(), id.to_string());\n                    }\n                    break;\n                } else if matches!(el.name(), \"h2\" | \"h3\" | \"h4\" | \"h5\" | \"h6\") {\n                    // h1 not found.\n                    break;\n                }\n            }\n        }\n        if !h1_found {\n            // Synthesize a root id to be able to link to the start of the page.\n            // TODO: This might want to be a warning? Chapters generally\n            // should start with an h1.\n            let mut h1 = Element::new(\"h1\");\n            let id = id_from_content(&chapter.name);\n            let id = unique_id(&id, id_counter);\n            h1.insert_attr(\"id\", id.clone().into());\n            let mut root = tree.root_mut();\n            let mut h1 = root.prepend(Node::Element(h1));\n            let mut a = Element::new(\"a\");\n            a.insert_attr(\"href\", format!(\"#{id}\").into());\n            a.insert_attr(\"class\", \"header\".into());\n            let mut a = h1.append(Node::Element(a));\n            a.append(Node::Text(chapter.name.clone().into()));\n            path_to_root_id.insert(html_path.clone(), id);\n        }\n    }\n\n    path_to_root_id\n}\n\n/// Rewrite links so that they point to IDs on the print page.\nfn rewrite_links(\n    chapter_trees: &mut [ChapterTree<'_>],\n    id_remap: &HashMap<PathBuf, HashMap<String, String>>,\n    path_to_root_id: &HashMap<PathBuf, String>,\n) {\n    static_regex!(\n        LINK,\n        r\"(?x)\n            (?P<scheme>^[a-z][a-z0-9+.-]*:)?\n            (?P<path>[^\\#]+)?\n            (?:\\#(?P<anchor>.*))?\"\n    );\n\n    // Rewrite path links to go to the appropriate place.\n    for ChapterTree {\n        html_path, tree, ..\n    } in chapter_trees\n    {\n        let base = html_path.parent().expect(\"path can't be empty\");\n\n        for value in tree.values_mut() {\n            let Node::Element(el) = value else {\n                continue;\n            };\n            if !matches!(el.name(), \"a\" | \"img\") {\n                continue;\n            }\n            for attr in [\"href\", \"src\", \"xlink:href\"] {\n                let Some(dest) = el.attr(attr) else {\n                    continue;\n                };\n                let Some(caps) = LINK.captures(&dest) else {\n                    continue;\n                };\n                if caps.name(\"scheme\").is_some() {\n                    continue;\n                }\n                // The lookup_key is the key to look up in the remap table.\n                let mut lookup_key = html_path.clone();\n                if let Some(href_path) = caps.name(\"path\")\n                    && let href_path = href_path.as_str()\n                    && !href_path.is_empty()\n                {\n                    lookup_key.pop();\n                    lookup_key.push(href_path);\n                    lookup_key = normalize_path(&lookup_key);\n                    let is_a_chapter = path_to_root_id.contains_key(&lookup_key);\n                    if !is_a_chapter {\n                        // Make the link relative to the print page location.\n                        let mut rel_path = normalize_path(&base.join(href_path)).to_url_path();\n                        if let Some(anchor) = caps.name(\"anchor\") {\n                            rel_path.push('#');\n                            rel_path.push_str(anchor.as_str());\n                        }\n                        el.insert_attr(attr, rel_path.into());\n                        continue;\n                    }\n                }\n\n                let id = match caps.name(\"anchor\") {\n                    Some(anchor_id) => {\n                        let anchor_id = anchor_id.as_str().to_string();\n                        match id_remap.get(&lookup_key) {\n                            Some(id_map) => match id_map.get(&anchor_id) {\n                                Some(new_id) => new_id.clone(),\n                                None => anchor_id,\n                            },\n                            None => {\n                                // Assume the anchor goes to some non-remapped\n                                // ID that already exists.\n                                anchor_id\n                            }\n                        }\n                    }\n                    None => match path_to_root_id.get(&lookup_key) {\n                        Some(id) => id.to_string(),\n                        None => {\n                            // This should be guaranteed that either the\n                            // chapter itself is in the map (for anchor-only\n                            // links), or the is_a_chapter check above.\n                            panic!(\n                                \"internal error: expected `{lookup_key:?}` to be in \\\n                                 root map (chapter path is `{html_path:?}`)\"\n                            );\n                        }\n                    },\n                };\n                el.insert_attr(attr, format!(\"#{id}\").into());\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "crates/mdbook-html/src/html/serialize.rs",
    "content": "//! Serializes the [`Node`] tree to an HTML string.\n\nuse super::tree::is_void_element;\nuse super::tree::{Element, Node};\nuse ego_tree::{Tree, iter::Edge};\nuse html5ever::{local_name, ns};\nuse mdbook_core::utils::{escape_html, escape_html_attribute};\nuse std::ops::Deref;\n\n/// Serializes the given tree of [`Node`] elements to an HTML string.\npub(crate) fn serialize(tree: &Tree<Node>, output: &mut String) {\n    for edge in tree.root().traverse() {\n        match edge {\n            Edge::Open(node) => match node.value() {\n                Node::Element(el) => serialize_start(el, output),\n                Node::Text(text) => {\n                    output.push_str(&escape_html(text));\n                }\n                Node::Comment(comment) => {\n                    output.push_str(\"<!--\");\n                    output.push_str(comment);\n                    output.push_str(\"-->\");\n                }\n                Node::Fragment => {}\n                Node::RawData(html) => {\n                    output.push_str(html);\n                }\n            },\n            Edge::Close(node) => {\n                if let Node::Element(el) = node.value() {\n                    serialize_end(el, output);\n                }\n            }\n        }\n    }\n}\n\n/// Returns true if this HTML element wants a newline to keep the emitted\n/// output more readable.\nfn wants_pretty_html_newline(name: &str) -> bool {\n    matches!(name, |\"blockquote\"| \"dd\"\n        | \"div\"\n        | \"dl\"\n        | \"dt\"\n        | \"h1\"\n        | \"h2\"\n        | \"h3\"\n        | \"h4\"\n        | \"h5\"\n        | \"h6\"\n        | \"hr\"\n        | \"li\"\n        | \"ol\"\n        | \"p\"\n        | \"pre\"\n        | \"table\"\n        | \"tbody\"\n        | \"thead\"\n        | \"tr\"\n        | \"ul\")\n}\n\n/// Emit the start tag of an element.\nfn serialize_start(el: &Element, output: &mut String) {\n    let el_name = el.name();\n    if wants_pretty_html_newline(el_name) {\n        if !output.is_empty() {\n            if !output.ends_with('\\n') {\n                output.push('\\n');\n            }\n        }\n    }\n    output.push('<');\n    output.push_str(el_name);\n    for (attr_name, value) in &el.attrs {\n        output.push(' ');\n        match attr_name.ns {\n            ns!() => (),\n            ns!(xml) => output.push_str(\"xml:\"),\n            ns!(xmlns) => {\n                if el.name.local != local_name!(\"xmlns\") {\n                    output.push_str(\"xmlns:\");\n                }\n            }\n            ns!(xlink) => output.push_str(\"xlink:\"),\n            _ => (), // TODO what should it do here?\n        }\n        output.push_str(attr_name.local.deref());\n        output.push_str(\"=\\\"\");\n        output.push_str(&escape_html_attribute(&value));\n        output.push('\"');\n    }\n    if el.self_closing {\n        output.push_str(\" /\");\n    }\n    output.push('>');\n}\n\n/// Emit the end tag of an element.\nfn serialize_end(el: &Element, output: &mut String) {\n    // Void elements do not have an end tag.\n    if el.self_closing || is_void_element(el.name()) {\n        return;\n    }\n    let name = el.name();\n    output.push_str(\"</\");\n    output.push_str(name);\n    output.push('>');\n    if wants_pretty_html_newline(name) {\n        output.push('\\n');\n    }\n}\n"
  },
  {
    "path": "crates/mdbook-html/src/html/tests.rs",
    "content": "use crate::html::tokenizer::parse_html;\nuse html5ever::tokenizer::{Tag, TagKind, Token};\n\n// Basic tokenizer behavior of a script.\n#[test]\nfn parse_html_script() {\n    let script = r#\"\nif (3 < 5 > 10)\n{\n    alert(\"The sky is falling!\");\n}\n\"#;\n    let t = format!(\"<script>{script}</script>\");\n    let ts = parse_html(&t);\n    eprintln!(\"{ts:#?}\",);\n    let mut output = String::new();\n    let mut in_script = false;\n    for t in ts {\n        match t {\n            Token::ParseError(e) => panic!(\"{e:?}\"),\n            Token::CharacterTokens(s) => {\n                if in_script {\n                    output.push_str(&s)\n                }\n            }\n            Token::TagToken(Tag {\n                kind: TagKind::StartTag,\n                ..\n            }) => in_script = true,\n            Token::TagToken(Tag {\n                kind: TagKind::EndTag,\n                ..\n            }) => in_script = false,\n            _ => {}\n        }\n    }\n    assert_eq!(output, script);\n}\n\n// What happens if a script doesn't end.\n#[test]\nfn parse_html_script_unclosed() {\n    let t = r#\"<script>\n// Test\n\"#;\n    let ts = parse_html(t);\n    eprintln!(\"{ts:#?}\",);\n    for t in ts {\n        if let Token::ParseError(e) = t {\n            panic!(\"{e:?}\",);\n        }\n    }\n}\n"
  },
  {
    "path": "crates/mdbook-html/src/html/tokenizer.rs",
    "content": "//! Support for parsing HTML.\n//!\n//! The primary entry point is [`parse_html`] which uses [`html5ever`] to\n//! tokenize the input.\n\nuse html5ever::TokenizerResult;\nuse html5ever::tendril::ByteTendril;\nuse html5ever::tokenizer::states::RawKind;\nuse html5ever::tokenizer::{\n    BufferQueue, TagKind, Token, TokenSink, TokenSinkResult, Tokenizer, TokenizerOpts,\n};\nuse std::cell::RefCell;\n\n/// Collector for HTML tokens.\n#[derive(Default)]\nstruct TokenCollector {\n    /// Parsed HTML tokens.\n    tokens: RefCell<Vec<Token>>,\n}\n\nimpl TokenSink for TokenCollector {\n    type Handle = ();\n\n    fn process_token(&self, token: Token, _line_number: u64) -> TokenSinkResult<()> {\n        match &token {\n            Token::DoctypeToken(_) => {}\n            Token::TagToken(tag) => {\n                let tag_name = tag.name.as_bytes();\n                // TODO: This could probably use special support for SVG and MathML.\n                if tag_name == b\"script\" {\n                    match tag.kind {\n                        TagKind::StartTag => {\n                            self.tokens.borrow_mut().push(token);\n                            return TokenSinkResult::RawData(RawKind::ScriptData);\n                        }\n                        TagKind::EndTag => {}\n                    }\n                }\n                if tag_name == b\"style\" {\n                    match tag.kind {\n                        TagKind::StartTag => {\n                            self.tokens.borrow_mut().push(token);\n                            return TokenSinkResult::RawData(RawKind::Rawtext);\n                        }\n                        TagKind::EndTag => {}\n                    }\n                }\n                self.tokens.borrow_mut().push(token);\n            }\n            Token::CommentToken(_) => {\n                self.tokens.borrow_mut().push(token);\n            }\n            Token::CharacterTokens(_) => {\n                self.tokens.borrow_mut().push(token);\n            }\n            Token::NullCharacterToken => {}\n            Token::EOFToken => {}\n            Token::ParseError(_) => {\n                self.tokens.borrow_mut().push(token);\n            }\n        }\n        TokenSinkResult::Continue\n    }\n}\n\n/// Parse HTML into tokens.\npub(crate) fn parse_html(html: &str) -> Vec<Token> {\n    let tendril: ByteTendril = html.as_bytes().into();\n    let mut queue = BufferQueue::default();\n    queue.push_back(tendril.try_reinterpret().unwrap());\n\n    let collector = TokenCollector::default();\n    let tok = Tokenizer::new(collector, TokenizerOpts::default());\n    let result = tok.feed(&mut queue);\n    assert_eq!(result, TokenizerResult::Done);\n    assert!(\n        queue.is_empty(),\n        \"queue wasn't empty: {:?}\",\n        queue.pop_front()\n    );\n    tok.end();\n    tok.sink.tokens.take()\n}\n"
  },
  {
    "path": "crates/mdbook-html/src/html/tree.rs",
    "content": "//! Tree data structure for representing a markdown document.\n//!\n//! [`MarkdownTreeBuilder::build`] is the primary entry point of this module.\n//! It takes events from [`pulldown_cmark`], and generates a [`Tree`]\n//! structure of [`Node`] elements. It also handles all the various\n//! transformations that mdbook performs, such as creating header links.\n\nuse super::tokenizer::parse_html;\nuse super::{HtmlRenderOptions, hide_lines, wrap_rust_main};\nuse crate::utils::{id_from_content, unique_id};\nuse ego_tree::{NodeId, NodeRef, Tree};\nuse html5ever::tendril::StrTendril;\nuse html5ever::tokenizer::{TagKind, Token};\nuse html5ever::{LocalName, QualName};\nuse indexmap::IndexMap;\nuse mdbook_core::config::RustEdition;\nuse mdbook_core::static_regex;\nuse pulldown_cmark::{Alignment, CodeBlockKind, CowStr, Event, LinkType, Tag, TagEnd};\nuse std::borrow::Cow;\nuse std::collections::{HashMap, HashSet};\nuse std::ops::Deref;\nuse tracing::{trace, warn};\n\n/// Helper to create a [`QualName`].\nmacro_rules! attr_qual_name {\n    ($name:expr) => {\n        QualName::new(None, html5ever::ns!(), LocalName::from($name))\n    };\n}\n\n/// A node in the [`Tree`].\n#[derive(Debug)]\npub(crate) enum Node {\n    /// An HTML [`Element`].\n    Element(Element),\n    /// Plain text.\n    ///\n    /// This will be escaped when serialized.\n    Text(StrTendril),\n    /// An HTML comment.\n    Comment(StrTendril),\n    /// Root node of a tree fragment.\n    ///\n    /// This is a general purpose node whenever it is convenient to have a\n    /// container of other nodes.\n    Fragment,\n    /// Raw data that should be copied into the output as-is without escaping.\n    RawData(StrTendril),\n}\n\nimpl Node {\n    /// If this is an [`Element`], return it.\n    pub(crate) fn as_element(&self) -> Option<&Element> {\n        if let Node::Element(el) = self {\n            Some(el)\n        } else {\n            None\n        }\n    }\n\n    /// If this is an [`Element`], return it (mutable).\n    fn as_element_mut(&mut self) -> Option<&mut Element> {\n        if let Node::Element(el) = self {\n            Some(el)\n        } else {\n            None\n        }\n    }\n}\n\n/// An HTML element.\n#[derive(Debug)]\npub(crate) struct Element {\n    /// The tag name.\n    pub(crate) name: QualName,\n    /// Element attributes.\n    pub(crate) attrs: Attributes,\n    /// True if this tag ends with `/>`.\n    pub(crate) self_closing: bool,\n    /// True if this was raw HTML written in the markdown.\n    pub(crate) was_raw: bool,\n}\n\nimpl Element {\n    /// Creates a new HTML element.\n    pub(crate) fn new(tag_name: &str) -> Element {\n        let name = QualName::new(None, html5ever::ns!(html), LocalName::from(tag_name));\n        Element {\n            name,\n            attrs: Attributes::new(),\n            self_closing: false,\n            was_raw: false,\n        }\n    }\n\n    /// The name of this element.\n    pub(crate) fn name(&self) -> &str {\n        self.name.local.deref()\n    }\n\n    /// If this is a heading element, returns the level of the heading.\n    #[allow(dead_code, reason = \"currently only used in search\")]\n    pub(crate) fn heading_level(&self) -> Option<u8> {\n        let name = self.name();\n        if matches!(name, \"h1\" | \"h2\" | \"h3\" | \"h4\" | \"h5\" | \"h6\") {\n            Some(name.as_bytes()[1] - b'0')\n        } else {\n            None\n        }\n    }\n\n    /// Returns the value of an attribute.\n    pub(crate) fn attr(&self, name: &str) -> Option<&str> {\n        let qname = attr_qual_name!(name);\n        self.attrs.get(&qname).map(Deref::deref)\n    }\n\n    /// Inserts an attribute.\n    pub(crate) fn insert_attr(&mut self, name: &str, value: StrTendril) {\n        let name = attr_qual_name!(name);\n        self.attrs.insert(name, value);\n    }\n}\n\n/// A map of attributes on an [`Element`].\ntype Attributes = IndexMap<QualName, StrTendril>;\n\n/// Helper to convert [`CowStr`] to a [`StrTendril`].\ntrait ToTendril {\n    /// Converts self to a [`StrTendril`].\n    fn into_tendril(self) -> StrTendril;\n}\n\nimpl ToTendril for CowStr<'_> {\n    fn into_tendril(self) -> StrTendril {\n        match self {\n            CowStr::Boxed(s) => {\n                let s: String = s.into();\n                StrTendril::from(s)\n            }\n            CowStr::Borrowed(s) => StrTendril::from(s),\n            CowStr::Inlined(s) => StrTendril::from(s.as_ref()),\n        }\n    }\n}\n\n/// Tracks the current state of parsing a table.\n///\n/// This is used to determine if it should generate `<th>` or `<td>` tags.\nenum TableState {\n    /// Currently in the table head.\n    Head,\n    /// Currently in the table body.\n    Body,\n}\n\n/// A builder used to create a [`Tree`] of [`Node`] elements.\n///\n/// Parts of this are based on pulldown-cmark's serializer (like table handling).\npub(crate) struct MarkdownTreeBuilder<'opts, 'event, EventIter> {\n    /// [`pulldown_cmark`] iterator of [`pulldown_cmark::Event`] elements.\n    events: EventIter,\n    /// Options for how to generate the HTML.\n    options: &'opts HtmlRenderOptions<'opts>,\n    /// The tree that is being built.\n    tree: Tree<Node>,\n    /// The ID of the current [`Node`].\n    current_node: NodeId,\n    /// The tag stack.\n    ///\n    /// This is used to set the `current_node` as the parser enters and leaves\n    /// a tag.\n    tag_stack: Vec<NodeId>,\n    /// When parsing a table, whether or not we are currently in the head or\n    /// the body.\n    table_state: TableState,\n    /// When parsing a table, the alignments of the columns.\n    ///\n    /// The count should match the number of columns.\n    table_alignments: Vec<Alignment>,\n    /// What parsing a table, the index of the current column.\n    table_cell_index: usize,\n    /// Mapping of footnote numbers.\n    ///\n    /// This is used for generating linkbacks in the definitions.\n    ///\n    /// This is a map of `name -> (number, count)`.\n    ///\n    /// - `name` is the name of the footnote.\n    /// - `number` is the footnote number displayed in the output.\n    /// - `count` is the number of references to this footnote (used for multiple\n    ///   linkbacks, and checking for unused footnotes).\n    footnote_numbers: HashMap<CowStr<'event>, (usize, u32)>,\n    /// Footnote definitions.\n    ///\n    /// This is a map of `name -> NodeId` of each footnote definition. When\n    /// parsing footnotes, they are initially left in the position where they\n    /// were defined as an `<li>` tag. The [`NodeId`] here is the id of that\n    /// tag. After the document has been parsed, all the definitions are moved\n    /// to the end of the document.\n    footnote_defs: HashMap<CowStr<'event>, NodeId>,\n}\n\nimpl<'opts, 'event, EventIter> MarkdownTreeBuilder<'opts, 'event, EventIter>\nwhere\n    EventIter: Iterator<Item = Event<'event>>,\n{\n    /// Processes a [`pulldown_cmark`] iterator of [`pulldown_cmark::Event`]\n    /// values, and generates a tree of [`Node`] values.\n    pub(crate) fn build(options: &'opts HtmlRenderOptions<'opts>, events: EventIter) -> Tree<Node> {\n        let tree = Tree::new(Node::Fragment);\n        let root = tree.root().id();\n\n        let mut builder = Self {\n            events,\n            options,\n            tree,\n            current_node: root,\n            tag_stack: vec![root],\n            table_state: TableState::Head,\n            table_alignments: Vec::new(),\n            table_cell_index: 0,\n            footnote_numbers: HashMap::new(),\n            footnote_defs: HashMap::new(),\n        };\n        builder.process_events();\n        builder.add_header_links();\n        builder.update_code_blocks();\n        builder.convert_fontawesome();\n        builder.tree\n    }\n\n    /// Append a new child to the current node.\n    ///\n    /// Returns the [`NodeId`] of the new node.\n    fn append(&mut self, node: Node) -> NodeId {\n        self.tree\n            .get_mut(self.current_node)\n            .unwrap()\n            .append(node)\n            .id()\n    }\n\n    /// Appends text to the current node.\n    ///\n    /// If the previous sibling is a text node, then it merges with that node.\n    /// This makes some processing more convenient.\n    fn append_text(&mut self, text: StrTendril) {\n        let mut current = self.tree.get_mut(self.current_node).unwrap();\n        if let Some(mut prev) = current.last_child()\n            && let Node::Text(prev_text) = prev.value()\n        {\n            prev_text.push_slice(&text);\n        } else {\n            self.append(Node::Text(text));\n        }\n    }\n\n    /// Append a new child to the current node, and make the new node the current node.\n    ///\n    /// This should only be used if you expect `pop` to be called.\n    fn push(&mut self, node: Node) {\n        let new_node = self.append(node);\n        self.tag_stack.push(new_node);\n        self.current_node = new_node;\n    }\n\n    /// Append a new child to the current node, and make the new node the current node.\n    ///\n    /// As compared to `push`, it is *not* expected that there will be a `pop` called\n    /// for this node. The next call to `pop` will unwind the stack past this node.\n    fn push_no_stack(&mut self, node: Node) {\n        let new_node = self.append(node);\n        self.current_node = new_node;\n    }\n\n    /// Switch the current node to the current node's parent.\n    fn pop(&mut self) {\n        self.tag_stack.pop();\n        if let Some(&parent) = self.tag_stack.last() {\n            self.current_node = parent;\n        } else {\n            panic!(\"pop too far processing `{}`\", self.options.path.display());\n        }\n    }\n\n    /// Returns all of the [`NodeId`]s, filtering out just the [`Element`]\n    /// nodes where the given callback returns `true` based on the element\n    /// name.\n    fn node_ids_for_tag(&self, filter: &dyn Fn(&str) -> bool) -> Vec<NodeId> {\n        self.tree\n            .nodes()\n            .filter(|node| {\n                let Node::Element(el) = node.value() else {\n                    return false;\n                };\n                filter(el.name())\n            })\n            .map(|node| node.id())\n            .collect()\n    }\n\n    /// The main processing loop. Processes all events until the end.\n    fn process_events(&mut self) {\n        while let Some(event) = self.events.next() {\n            trace!(\"event={event:?}\");\n            match event {\n                Event::Start(tag) => self.start_tag(tag),\n                Event::End(tag) => self.end_tag(tag),\n                Event::Text(text) => {\n                    self.append_text(text.into_tendril());\n                }\n                Event::Code(code) => {\n                    self.push(Node::Element(Element::new(\"code\")));\n                    self.append(Node::Text(code.into_tendril()));\n                    self.pop();\n                }\n                Event::InlineMath(text) => {\n                    let mut span = Element::new(\"span\");\n                    span.insert_attr(\"class\", \"math math-inline\".into());\n                    self.push(Node::Element(span));\n                    self.append(Node::Text(text.into_tendril()));\n                    self.pop();\n                }\n                Event::DisplayMath(text) => {\n                    let mut span = Element::new(\"span\");\n                    span.insert_attr(\"class\", \"math math-display\".into());\n                    self.push(Node::Element(span));\n                    self.append(Node::Text(text.into_tendril()));\n                    self.pop();\n                }\n                Event::Html(html) => {\n                    // The loop in Tag::HtmlBlock should have consumed all\n                    // Html events.\n                    panic!(\n                        \"`{}` unexpected Html event: {html}\",\n                        self.options.path.display()\n                    );\n                }\n                Event::InlineHtml(html) => self.append_html(&html),\n                Event::FootnoteReference(name) => self.footnote_reference(name),\n                Event::SoftBreak => {\n                    self.append_text(\"\\n\".into());\n                }\n                Event::HardBreak => {\n                    self.append(Node::Element(Element::new(\"br\")));\n                }\n                Event::Rule => {\n                    self.append(Node::Element(Element::new(\"hr\")));\n                }\n                Event::TaskListMarker(checked) => {\n                    let mut input = Element::new(\"input\");\n                    input.insert_attr(\"disabled\", \"\".into());\n                    input.insert_attr(\"type\", \"checkbox\".into());\n                    if checked {\n                        input.insert_attr(\"checked\", \"\".into());\n                    }\n                    self.push(Node::Element(input));\n                    // Add some space before whatever follows.\n                    self.append(Node::Text(\" \".into()));\n                    self.pop();\n                }\n            }\n        }\n        self.finish_stack();\n        self.collect_footnote_defs();\n    }\n\n    fn start_tag(&mut self, tag: Tag<'event>) {\n        let element = match tag {\n            Tag::Paragraph => Element::new(\"p\"),\n            Tag::Heading {\n                level,\n                id,\n                classes,\n                attrs,\n            } => {\n                let mut el = Element::new(&level.to_string());\n                for (name, value) in attrs {\n                    let name =\n                        QualName::new(None, html5ever::ns!(), LocalName::from(Cow::from(name)));\n                    let value = value.unwrap_or_else(|| CowStr::from(\"\"));\n                    el.attrs.insert(name, value.into_tendril());\n                }\n                if let Some(id) = id {\n                    el.insert_attr(\"id\", id.into_tendril());\n                }\n                if !classes.is_empty() {\n                    let classes = classes.join(\" \");\n                    el.insert_attr(\"class\", classes.into());\n                }\n                el\n            }\n            Tag::BlockQuote(kind) => {\n                let mut b = Element::new(\"blockquote\");\n                if let Some(kind) = kind {\n                    let (class_kind, icon, text) = super::admonitions::select_tag(kind);\n                    let class = format!(\"blockquote-tag blockquote-tag-{class_kind}\");\n                    b.insert_attr(\"class\", class.into());\n                    self.push(Node::Element(b));\n\n                    let mut title = Element::new(\"p\");\n                    title.insert_attr(\"class\", \"blockquote-tag-title\".into());\n                    self.push(Node::Element(title));\n\n                    let mut svg = Element::new(\"svg\");\n                    svg.insert_attr(\"viewbox\", \"0 0 16 16\".into());\n                    svg.insert_attr(\"width\", \"18\".into());\n                    svg.insert_attr(\"height\", \"18\".into());\n                    self.push(Node::Element(svg));\n                    self.append_html(icon);\n                    self.pop();\n\n                    self.append(Node::Text(text.into()));\n\n                    self.pop();\n                    return;\n                }\n                b\n            }\n            Tag::CodeBlock(kind) => {\n                let mut code = Element::new(\"code\");\n                match kind {\n                    CodeBlockKind::Fenced(info) => {\n                        let mut infos =\n                            info.split([' ', '\\t', ',']).filter(|info| !info.is_empty());\n                        if let Some(lang) = infos.next() {\n                            let mut classes = String::with_capacity(info.len() + 10);\n                            // The first element in the infostring is treated as the language.\n                            classes.push_str(\"language-\");\n                            classes.push_str(lang);\n                            // The rest are just added as classes.\n                            while let Some(info) = infos.next() {\n                                classes.push(' ');\n                                classes.push_str(info);\n                            }\n                            code.insert_attr(\"class\", classes.into());\n                        }\n                    }\n                    CodeBlockKind::Indented => {}\n                }\n                self.push_no_stack(Node::Element(Element::new(\"pre\")));\n                code\n            }\n            Tag::HtmlBlock => {\n                // To process the HTML correctly, this needs to\n                // collect it all into a single string.\n                let mut html = String::new();\n                while let Some(event) = self.events.next() {\n                    match event {\n                        Event::Html(text) | Event::Text(text) => html.push_str(&text),\n                        Event::End(TagEnd::HtmlBlock) => break,\n                        _ => panic!(\n                            \"`{}` unexpected event in html block {event:?}\",\n                            self.options.path.display()\n                        ),\n                    }\n                }\n                self.append_html(&html);\n                // TagEnd::HtmlBlock must not pop.\n                return;\n            }\n            Tag::List(Some(start)) => {\n                let mut ol = Element::new(\"ol\");\n                if start != 1 {\n                    ol.insert_attr(\"start\", format!(\"{start}\").into());\n                }\n                ol\n            }\n            Tag::List(None) => Element::new(\"ul\"),\n            Tag::Item => Element::new(\"li\"),\n            Tag::FootnoteDefinition(name) => {\n                if self.footnote_defs.contains_key(&name) {\n                    warn!(\n                        \"footnote `{name}` in {} defined multiple times - \\\n                                     not updating to new definition\",\n                        self.options.path.display()\n                    );\n                    self.eat_till_end();\n                    return;\n                } else {\n                    let mut el = Element::new(\"li\");\n                    el.insert_attr(\"id\", format!(\"footnote-{name}\").into());\n                    self.push(Node::Element(el));\n                    self.footnote_defs.insert(name, self.current_node);\n                    return;\n                }\n            }\n            Tag::DefinitionList => Element::new(\"dl\"),\n            Tag::DefinitionListTitle => Element::new(\"dt\"),\n            Tag::DefinitionListDefinition => Element::new(\"dd\"),\n            Tag::Table(alignments) => {\n                self.table_alignments = alignments.clone();\n                // This div wrapper around the table is used to apply\n                // `overflow-x: auto` so that wide tables can be scrolled\n                // horizontally, rather than overflowing or scrolling the\n                // entire page. See\n                // https://github.com/rust-lang/mdBook/pull/1617\n                let mut div = Element::new(\"div\");\n                div.insert_attr(\"class\", \"table-wrapper\".into());\n                self.push_no_stack(Node::Element(div));\n                Element::new(\"table\")\n            }\n            Tag::TableHead => {\n                self.table_state = TableState::Head;\n                self.table_cell_index = 0;\n                let thead = Element::new(\"thead\");\n                self.push_no_stack(Node::Element(thead));\n                Element::new(\"tr\")\n            }\n            Tag::TableRow => {\n                self.table_cell_index = 0;\n                Element::new(\"tr\")\n            }\n            Tag::TableCell => {\n                let mut cell = match self.table_state {\n                    TableState::Head => Element::new(\"th\"),\n                    TableState::Body => Element::new(\"td\"),\n                };\n                let style = match self.table_alignments.get(self.table_cell_index) {\n                    Some(&Alignment::Left) => \"text-align: left\",\n                    Some(&Alignment::Center) => \"text-align: center\",\n                    Some(&Alignment::Right) => \"text-align: right\",\n                    Some(&Alignment::None) | None => \"\",\n                };\n                if !style.is_empty() {\n                    cell.insert_attr(\"style\", style.into());\n                }\n                cell\n            }\n            Tag::Emphasis => Element::new(\"em\"),\n            Tag::Strong => Element::new(\"strong\"),\n            Tag::Strikethrough => Element::new(\"del\"),\n            Tag::Superscript => Element::new(\"sup\"),\n            Tag::Subscript => Element::new(\"sub\"),\n            Tag::Link {\n                link_type,\n                dest_url,\n                title,\n                id: _,\n            } => {\n                let href: StrTendril = if matches!(link_type, LinkType::Email) {\n                    format!(\"mailto:{dest_url}\").into()\n                } else {\n                    fix_link(dest_url).into_tendril()\n                };\n                let mut a = Element::new(\"a\");\n                a.insert_attr(\"href\", href);\n                if !title.is_empty() {\n                    a.insert_attr(\"title\", title.into_tendril());\n                }\n                a\n            }\n            Tag::Image {\n                link_type: _,\n                dest_url,\n                title,\n                id: _,\n            } => {\n                let mut img = Element::new(\"img\");\n                let src = fix_link(dest_url).into_tendril();\n                img.insert_attr(\"src\", src);\n                if !title.is_empty() {\n                    img.insert_attr(\"title\", title.into_tendril());\n                }\n                // This will eat TagEnd::Image\n                let alt = self.text_for_img_alt();\n                img.insert_attr(\"alt\", alt.into());\n                self.append(Node::Element(img));\n                return;\n            }\n            Tag::MetadataBlock(_) => {\n                // Eat all events till the end of MetadataBlock.\n                while let Some(event) = self.events.next() {\n                    if matches!(event, Event::End(TagEnd::MetadataBlock(_))) {\n                        break;\n                    }\n                }\n                return;\n            }\n        };\n        self.push(Node::Element(element));\n    }\n\n    fn end_tag(&mut self, tag: TagEnd) {\n        // TODO: This should validate that the event stack is properly\n        // synchronized with the tag stack. That, would likely require keeping\n        // a parallel \"expected end tag\" with the tag stack, since mapping a\n        // pulldown-cmark event tag to an HTML tag isn't always clear.\n        //\n        // Check for unclosed HTML tags when exiting a markdown event.\n        while let Some(node_id) = self.tag_stack.last() {\n            let node = self.tree.get(*node_id).unwrap().value();\n            let Node::Element(el) = node else {\n                break;\n            };\n            if !el.was_raw {\n                break;\n            }\n            warn!(\n                \"unclosed HTML tag `<{}>` found in `{}` while exiting {tag:?}\\n\\\n                HTML tags must be closed before exiting a markdown element.\",\n                el.name.local,\n                self.options.path.display(),\n            );\n            self.pop();\n        }\n        self.pop();\n        match tag {\n            TagEnd::TableHead => {\n                self.table_state = TableState::Body;\n                self.push(Node::Element(Element::new(\"tbody\")));\n            }\n            TagEnd::TableCell => {\n                self.table_cell_index += 1;\n            }\n            TagEnd::Table => {\n                // Pop tbody or thead\n                self.pop();\n            }\n            _ => {}\n        }\n    }\n\n    /// Given some HTML, parse it into [`Node`] elements and append them to\n    /// the current node.\n    fn append_html(&mut self, html: &str) {\n        let tokens = parse_html(&html);\n        let mut is_raw = false;\n        for token in tokens {\n            trace!(\"html token={token:?}\");\n            match token {\n                Token::DoctypeToken(_) => {}\n                Token::TagToken(tag) => match tag.kind {\n                    TagKind::StartTag => self.start_html_tag(tag, &mut is_raw),\n                    TagKind::EndTag => self.end_html_tag(tag, &mut is_raw),\n                },\n                Token::CommentToken(comment) => {\n                    self.append(Node::Comment(comment));\n                }\n                Token::CharacterTokens(chars) => {\n                    if is_raw {\n                        self.append(Node::RawData(chars));\n                    } else {\n                        self.append_text(chars);\n                    }\n                }\n                Token::NullCharacterToken => {}\n                Token::EOFToken => {}\n                Token::ParseError(error) => {\n                    warn!(\n                        \"html parse error in `{}`: {error}\\n\\\n                         Html text was:\\n\\\n                         {html}\",\n                        self.options.path.display()\n                    );\n                }\n            }\n        }\n    }\n\n    /// Adds an open HTML tag.\n    fn start_html_tag(&mut self, tag: html5ever::tokenizer::Tag, is_raw: &mut bool) {\n        let is_closed = is_void_element(&tag.name) || tag.self_closing;\n        *is_raw = matches!(&*tag.name, \"script\" | \"style\");\n        let name = QualName::new(None, html5ever::ns!(html), tag.name);\n        let attrs = tag\n            .attrs\n            .into_iter()\n            .map(|attr| (attr.name, attr.value))\n            .collect();\n        let mut el = Element {\n            name,\n            attrs,\n            self_closing: tag.self_closing,\n            was_raw: true,\n        };\n        fix_html_link(&mut el);\n        self.push(Node::Element(el));\n        if is_closed {\n            // No end element.\n            self.pop();\n        }\n    }\n\n    /// Closes the given HTML tag.\n    fn end_html_tag(&mut self, tag: html5ever::tokenizer::Tag, is_raw: &mut bool) {\n        *is_raw = false;\n        if self.is_html_tag_matching(&tag.name) {\n            self.pop();\n        } else {\n            // The proper thing to do here is to recover. However, the HTML\n            // parsing algorithm for that is quite complex. See\n            // https://html.spec.whatwg.org/multipage/parsing.html#parsing-main-inbody\n            // and the adoption agency algorithm.\n            warn!(\n                \"unexpected HTML end tag `</{}>` found in `{}`\\n\\\n                 Check that the HTML tags are properly balanced.\",\n                tag.name,\n                self.options.path.display()\n            );\n        }\n    }\n\n    /// This is used to verify HTML parsing keeps the stack of tags in sync.\n    fn is_html_tag_matching(&self, name: &str) -> bool {\n        let current = self.tree.get(self.current_node).unwrap().value();\n        if let Node::Element(el) = current\n            && el.name() == name\n        {\n            true\n        } else {\n            false\n        }\n    }\n\n    /// Eats all pulldown-cmark events until the next `End` matching the\n    /// current nesting level.\n    fn eat_till_end(&mut self) {\n        let mut nest = 0;\n        while let Some(event) = self.events.next() {\n            match event {\n                Event::Start(_) => nest += 1,\n                Event::End(_) => {\n                    if nest == 0 {\n                        break;\n                    }\n                    nest -= 1;\n                }\n                _ => {}\n            }\n        }\n    }\n\n    /// Eats events generating a plain text string, stripping out any\n    /// formatting elements.\n    fn text_for_img_alt(&mut self) -> String {\n        let mut nest = 0;\n        let mut output = String::new();\n        while let Some(event) = self.events.next() {\n            match event {\n                Event::Start(_) => nest += 1,\n                Event::End(_) => {\n                    if nest == 0 {\n                        break;\n                    }\n                    nest -= 1;\n                }\n                Event::Html(_) => {}\n                Event::InlineHtml(text) | Event::Code(text) | Event::Text(text) => {\n                    output.push_str(&text);\n                }\n                Event::InlineMath(text) => {\n                    output.push('$');\n                    output.push_str(&text);\n                    output.push('$');\n                }\n                Event::DisplayMath(text) => {\n                    output.push_str(\"$$\");\n                    output.push_str(&text);\n                    output.push_str(\"$$\");\n                }\n                Event::SoftBreak | Event::HardBreak | Event::Rule => output.push(' '),\n                Event::FootnoteReference(_) => {}\n                Event::TaskListMarker(_) => {}\n            }\n        }\n        output\n    }\n\n    /// Deals with any unclosed elements on the stack.\n    fn finish_stack(&mut self) {\n        while let Some(node_id) = self.tag_stack.pop() {\n            let node = self.tree.get(node_id).unwrap().value();\n            match node {\n                Node::Fragment => {}\n                Node::Element(el) => {\n                    if el.was_raw {\n                        warn!(\n                            \"unclosed HTML tag `<{}>` found in `{}`\",\n                            el.name.local,\n                            self.options.path.display()\n                        );\n                    } else {\n                        panic!(\n                            \"internal error: expected empty tag stack.\\n\n                             path: `{}`\\n\\\n                             element={el:?}\",\n                            self.options.path.display()\n                        );\n                    }\n                }\n                node => {\n                    panic!(\n                        \"internal error: expected empty tag stack.\\n\n                         path: `{}`\\n\\\n                         node={node:?}\",\n                        self.options.path.display()\n                    );\n                }\n            }\n        }\n    }\n\n    /// Appends a new footnote reference.\n    fn footnote_reference(&mut self, name: CowStr<'event>) {\n        let len = self.footnote_numbers.len() + 1;\n        let (n, count) = self\n            .footnote_numbers\n            .entry(name.clone())\n            .or_insert((len, 0));\n        *count += 1;\n        let (n, count) = (*n, *count);\n\n        let current = self.tree.get(self.current_node).unwrap();\n        if let Some(last) = current.last_child()\n            && let Node::Element(el) = last.value()\n        {\n            if el.attr(\"class\") == Some(\"footnote-reference\") {\n                self.append(Node::Text(\" \".into()));\n            }\n        }\n        let mut sup = Element::new(\"sup\");\n        sup.insert_attr(\"class\", \"footnote-reference\".into());\n        let id = format!(\"fr-{name}-{count}\");\n        sup.insert_attr(\"id\", id.into());\n        self.push(Node::Element(sup));\n        let mut a = Element::new(\"a\");\n        a.insert_attr(\"href\", format!(\"#footnote-{name}\").into());\n        self.push(Node::Element(a));\n        self.append(Node::Text(format!(\"{n}\").into()));\n        self.pop(); // a\n        self.pop(); // sup\n    }\n\n    /// This is used after parsing is complete to move the footnote\n    /// definitions to the end of the document.\n    fn collect_footnote_defs(&mut self) {\n        if self.footnote_defs.is_empty() {\n            return;\n        }\n        let defs = std::mem::take(&mut self.footnote_defs);\n        let mut defs: Vec<_> = defs.into_iter().collect();\n        // Detach nodes and remove unused.\n        defs.retain(|(name, def_id)| {\n            let mut node = self.tree.get_mut(*def_id).unwrap();\n            node.detach();\n\n            if !self.footnote_numbers.contains_key(name) {\n                warn!(\n                    \"footnote `{name}` in `{}` is defined but not referenced\",\n                    self.options.path.display()\n                );\n                false\n            } else {\n                true\n            }\n        });\n        defs.sort_by_cached_key(|(name, _)| self.footnote_numbers[name].0);\n\n        // Move defs to the end of the chapter.\n        self.append(Node::Element(Element::new(\"hr\")));\n        let mut ol = Element::new(\"ol\");\n        ol.insert_attr(\"class\", \"footnote-definition\".into());\n        let ol_id = self.append(Node::Element(ol));\n        for (name, def_id) in defs {\n            // Generate the linkbacks.\n            let count = self.footnote_numbers[&name].1;\n            for usage in 1..=count {\n                let nth = if usage == 1 {\n                    String::new()\n                } else {\n                    usage.to_string()\n                };\n                let space = self.tree.orphan(Node::Text(\" \".into())).id();\n                let mut backlink = Element::new(\"a\");\n                backlink.insert_attr(\"href\", format!(\"#fr-{name}-{usage}\").into());\n                let mut backlink = self.tree.orphan(Node::Element(backlink));\n                backlink.append(Node::Text(format!(\"↩{nth}\").into()));\n                let backlink = backlink.id();\n                let mut def = self.tree.get_mut(def_id).unwrap();\n                if let Some(mut last_child) = def.last_child()\n                    && let value = last_child.value()\n                    && let Node::Element(last_el) = value\n                    && last_el.name() == \"p\"\n                {\n                    // Put the linkback at the end of the last paragraph instead\n                    // of on a line by itself.\n                    last_child.append_id(space);\n                    last_child.append_id(backlink);\n                } else {\n                    // Not a clear place to put it in this circumstance, so put it\n                    // at the end.\n                    def.append_id(space);\n                    def.append_id(backlink);\n                };\n            }\n            let mut ol = self.tree.get_mut(ol_id).unwrap();\n            ol.append_id(def_id);\n        }\n    }\n\n    /// This is used after parsing is complete to add a unique `id` attribute\n    /// to all header and dt elements, and to also add an `<a>` tag so that\n    /// clicking the element will set the current URL to that element's\n    /// fragment.\n    fn add_header_links(&mut self) {\n        let mut id_counter = HashSet::new();\n        let headings = self.node_ids_for_tag(&|name| {\n            matches!(name, \"h1\" | \"h2\" | \"h3\" | \"h4\" | \"h5\" | \"h6\" | \"dt\")\n        });\n        for heading in headings {\n            let node = self.tree.get(heading).unwrap();\n            let el = node.value().as_element().unwrap();\n            // Don't modify tags if they were manually written HTML. The\n            // user probably had some intent, and we don't want to mess it up.\n            if el.was_raw {\n                continue;\n            }\n            let href = if let Some(id) = el.attr(\"id\") {\n                format!(\"#{id}\")\n            } else {\n                let mut id = String::new();\n                let node_id = node.id();\n                let node_ref = self.tree.get(node_id).unwrap();\n                text_in_node(node_ref, &mut id);\n                let id = id_from_content(&id);\n                let id = unique_id(&id, &mut id_counter);\n                let mut node = self.tree.get_mut(heading).unwrap();\n                let el = node.value().as_element_mut().unwrap();\n                let href = format!(\"#{id}\");\n                el.insert_attr(\"id\", id.into());\n                href\n            };\n            // Insert an <a> element between the heading and its children.\n            let mut a = Element::new(\"a\");\n            a.insert_attr(\"class\", \"header\".into());\n            a.insert_attr(\"href\", href.into());\n            let mut a = self.tree.orphan(Node::Element(a));\n            a.reparent_from_id_append(heading);\n            let a_id = a.id();\n            let mut node = self.tree.get_mut(heading).unwrap();\n            node.append_id(a_id);\n        }\n    }\n\n    /// This is used after parsing is complete to set the appropriate classes\n    /// on a code block, to wrap hidden lines in `<span>` tags, and to add an\n    /// `fn main() {}` wrapper for Rust code blocks.\n    fn update_code_blocks(&mut self) {\n        let mut code_ids = self.node_ids_for_tag(&|name| name == \"code\");\n        // The processing below assumes the code block is in a contiguous\n        // chunk. The text nodes should have been merged during event\n        // processing. I don't know exactly what this should do if it\n        // encounters code blocks with non-text nodes.\n        code_ids.retain(|id| {\n            let code = self.tree.get(*id).unwrap();\n            code.children().count() == 1\n        });\n\n        for code_id in code_ids.iter().copied() {\n            let mut node = self.tree.get_mut(code_id).unwrap();\n            let parent_id = node.parent().unwrap().id();\n            let code_el = node.value().as_element_mut().unwrap();\n            let class = code_el.attr(\"class\").unwrap_or_default();\n            let class_set: HashSet<_> = class.split(' ').collect();\n            let is_editable = class_set.contains(\"editable\");\n            let is_playground = class_set.contains(\"language-rust\")\n                && ((!class_set.contains(\"ignore\")\n                    && !class_set.contains(\"noplayground\")\n                    && !class_set.contains(\"noplaypen\")\n                    && self.options.config.playground.runnable)\n                    || class_set.contains(\"mdbook-runnable\"));\n            if !is_playground {\n                continue;\n            }\n            let add_edition = if class_set.iter().any(|cls| cls.starts_with(\"edition\")) {\n                None\n            } else {\n                self.options.edition.map(|edition| match edition {\n                    RustEdition::E2015 => \"edition2015\",\n                    RustEdition::E2018 => \"edition2018\",\n                    RustEdition::E2021 => \"edition2021\",\n                    RustEdition::E2024 => \"edition2024\",\n                    _ => panic!(\"edition {edition:?} not covered\"),\n                })\n            };\n            if let Some(edition) = add_edition {\n                code_el.insert_attr(\"class\", format!(\"{class} {edition}\").into());\n            }\n\n            let mut node = self.tree.get_mut(code_id).unwrap();\n            if !self.options.config.playground.editable || !is_editable {\n                if let Some(mut child) = node.first_child()\n                    && let Node::Text(text) = child.value()\n                {\n                    if let Some(new_text) = wrap_rust_main(text) {\n                        *text = new_text.into();\n                    }\n                }\n            }\n\n            let mut pre = self.tree.get_mut(parent_id).unwrap();\n            let pre = pre.value().as_element_mut().unwrap();\n            assert_eq!(pre.name(), \"pre\");\n            pre.insert_attr(\"class\", \"playground\".into());\n        }\n\n        for code_id in code_ids {\n            hide_lines(&mut self.tree, code_id, &self.options.config.code.hidelines);\n        }\n    }\n\n    /// This is used after parsing is complete to replace `<i>` tags with a\n    /// `<span>` that includes the corresponding SVG code.\n    fn convert_fontawesome(&mut self) {\n        use font_awesome_as_a_crate as fa;\n\n        let is = self.node_ids_for_tag(&|name| name == \"i\");\n        for i_id in is {\n            let mut icon = String::new();\n            let mut type_ = fa::Type::Regular;\n            let mut new_classes = String::from(\"fa-svg\");\n\n            let mut node = self.tree.get_mut(i_id).unwrap();\n            if node.first_child().is_some() {\n                // Just to be safe, only translate <i></i>.\n                continue;\n            }\n            let i_el = node.value().as_element().unwrap();\n            let classes = i_el.attr(\"class\").unwrap_or_default();\n            for class in classes.split(\" \") {\n                if matches!(class, \"fa\" | \"fa-regular\") {\n                    type_ = fa::Type::Regular;\n                } else if matches!(class, \"fas\" | \"fa-solid\") {\n                    type_ = fa::Type::Solid;\n                } else if matches!(class, \"fab\" | \"fa-brands\") {\n                    type_ = fa::Type::Brands;\n                } else if let Some(class) = class.strip_prefix(\"fa-\") {\n                    icon = class.to_owned();\n                } else {\n                    new_classes += \" \";\n                    new_classes += class;\n                }\n            }\n            if icon.is_empty() {\n                continue;\n            }\n\n            match fa::svg(type_, &icon) {\n                Ok(svg) => {\n                    let mut span = Element::new(\"span\");\n                    span.insert_attr(\"class\", new_classes.into());\n                    for (name, value) in &i_el.attrs {\n                        if *name != attr_qual_name!(\"class\") {\n                            span.attrs.insert(name.clone(), value.clone());\n                        }\n                    }\n                    *node.value() = Node::Element(span);\n                    node.append(Node::RawData(svg.into()));\n                }\n                Err(e) => {\n                    warn!(\n                        \"failed to find Font Awesome icon for icon `{icon}` \\\n                         with type `{type_}` in `{}`: {e}\",\n                        self.options.path.display()\n                    );\n                }\n            }\n        }\n    }\n}\n\n/// Traverse the given node, emitting any plain text into the output.\n///\n/// This is used to generate the `id` of a header.\nfn text_in_node(node: NodeRef<'_, Node>, output: &mut String) {\n    for child in node.children() {\n        match child.value() {\n            Node::Element(_) => {}\n            Node::Text(text) => output.push_str(text),\n            Node::Comment(_) => {}\n            Node::Fragment => {}\n            Node::RawData(_) => {}\n        }\n        text_in_node(child, output);\n    }\n}\n\n/// Modifies links to work with HTML.\n///\n/// For local paths, this changes the `.md` extension to `.html`.\nfn fix_link<'a>(link: CowStr<'a>) -> CowStr<'a> {\n    static_regex!(SCHEME_LINK, r\"^[a-z][a-z0-9+.-]*:\");\n    static_regex!(MD_LINK, r\"(?P<link>.*)\\.md(?P<anchor>#.*)?\");\n\n    if link.starts_with('#') {\n        // Fragment-only link.\n        return link;\n    }\n    // Don't modify links with schemes like `https`.\n    if SCHEME_LINK.is_match(&link) {\n        return link;\n    }\n\n    // This is a relative link, adjust it as necessary.\n    if let Some(caps) = MD_LINK.captures(&link) {\n        let mut fixed_link = String::from(&caps[\"link\"]);\n        fixed_link.push_str(\".html\");\n        if let Some(anchor) = caps.name(\"anchor\") {\n            fixed_link.push_str(anchor.as_str());\n        }\n        CowStr::from(fixed_link)\n    } else {\n        link\n    }\n}\n\n/// Calls [`fix_link`] for HTML elements.\nfn fix_html_link(el: &mut Element) {\n    if el.name() != \"a\" {\n        return;\n    }\n    for attr in [\"href\", \"xlink:href\"] {\n        if let Some(value) = el.attr(attr) {\n            let fixed = fix_link(value.into());\n            el.insert_attr(attr, fixed.into_tendril());\n        }\n    }\n}\n\n/// Whether or not this element name is a [void element].\n///\n/// This is used to know whether or not to expect a `</>` end tag.\n///\n/// [void element]: https://developer.mozilla.org/en-US/docs/Glossary/Void_element\npub(crate) fn is_void_element(name: &str) -> bool {\n    matches!(\n        name,\n        \"area\"\n            | \"base\"\n            | \"br\"\n            | \"col\"\n            | \"embed\"\n            | \"hr\"\n            | \"img\"\n            | \"input\"\n            | \"link\"\n            | \"meta\"\n            | \"param\"\n            | \"source\"\n            | \"track\"\n            | \"wbr\"\n    )\n}\n"
  },
  {
    "path": "crates/mdbook-html/src/html_handlebars/hbs_renderer.rs",
    "content": "use super::helpers;\nuse super::static_files::StaticFiles;\nuse crate::html::ChapterTree;\nuse crate::html::{build_trees, render_markdown, serialize};\nuse crate::theme::Theme;\nuse crate::utils::ToUrlPath;\nuse anyhow::{Context, Result, bail};\nuse handlebars::Handlebars;\nuse mdbook_core::book::{Book, BookItem, Chapter};\nuse mdbook_core::config::{BookConfig, Config, HtmlConfig};\nuse mdbook_core::utils::fs;\nuse mdbook_renderer::{RenderContext, Renderer};\nuse serde_json::json;\nuse std::collections::{BTreeMap, HashMap};\nuse std::path::{Path, PathBuf};\nuse tracing::error;\nuse tracing::{debug, info, trace, warn};\n\n/// The HTML renderer for mdBook.\n#[derive(Default)]\n#[non_exhaustive]\npub struct HtmlHandlebars;\n\nimpl HtmlHandlebars {\n    /// Returns a new instance of [`HtmlHandlebars`].\n    pub fn new() -> Self {\n        HtmlHandlebars\n    }\n\n    fn render_chapter(\n        &self,\n        chapter_tree: &ChapterTree<'_>,\n        prev_ch: Option<&Chapter>,\n        next_ch: Option<&Chapter>,\n        mut ctx: RenderChapterContext<'_>,\n    ) -> Result<()> {\n        // FIXME: This should be made DRY-er and rely less on mutable state\n        let ch = chapter_tree.chapter;\n\n        let path = ch.path.as_ref().unwrap();\n        // \"print.html\" is used for the print page.\n        if path == Path::new(\"print.md\") {\n            bail!(\"{} is reserved for internal use\", path.display());\n        };\n\n        if let Some(ref edit_url_template) = ctx.html_config.edit_url_template {\n            let full_path = ctx.book_config.src.to_str().unwrap_or_default().to_owned()\n                + \"/\"\n                + ch.source_path\n                    .clone()\n                    .unwrap_or_default()\n                    .to_str()\n                    .unwrap_or_default();\n\n            let edit_url = edit_url_template.replace(\"{path}\", &full_path);\n            ctx.data\n                .insert(\"git_repository_edit_url\".to_owned(), json!(edit_url));\n        }\n\n        let mut content = String::new();\n        serialize(&chapter_tree.tree, &mut content);\n\n        let ctx_path = path\n            .to_str()\n            .with_context(|| \"Could not convert path to str\")?;\n        let filepath = Path::new(&ctx_path).with_extension(\"html\");\n\n        let book_title = ctx\n            .data\n            .get(\"book_title\")\n            .and_then(serde_json::Value::as_str)\n            .unwrap_or(\"\");\n\n        let title = if let Some(title) = ctx.chapter_titles.get(path) {\n            title.clone()\n        } else if book_title.is_empty() {\n            ch.name.clone()\n        } else {\n            ch.name.clone() + \" - \" + book_title\n        };\n\n        ctx.data.insert(\"path\".to_owned(), json!(path));\n        ctx.data.insert(\"content\".to_owned(), json!(content));\n        ctx.data.insert(\"chapter_title\".to_owned(), json!(ch.name));\n        ctx.data.insert(\"title\".to_owned(), json!(title));\n        ctx.data\n            .insert(\"path_to_root\".to_owned(), json!(fs::path_to_root(path)));\n        if let Some(ref section) = ch.number {\n            ctx.data\n                .insert(\"section\".to_owned(), json!(section.to_string()));\n        }\n\n        let redirects = collect_redirects_for_path(&filepath, &ctx.html_config.redirect)?;\n        if !redirects.is_empty() {\n            ctx.data.insert(\n                \"fragment_map\".to_owned(),\n                json!(serde_json::to_string(&redirects)?),\n            );\n        }\n\n        let mut nav = |name: &str, ch: Option<&Chapter>| {\n            let Some(ch) = ch else { return };\n            let path = ch\n                .path\n                .as_ref()\n                .unwrap()\n                .with_extension(\"html\")\n                .to_url_path();\n            let obj = json!( {\n                \"title\": ch.name,\n                \"link\": path,\n            });\n            ctx.data.insert(name.to_string(), obj);\n        };\n        nav(\"previous\", prev_ch);\n        nav(\"next\", next_ch);\n\n        // Render the handlebars template with the data\n        debug!(\"Render template\");\n        let rendered = ctx.handlebars.render(\"index\", &ctx.data)?;\n\n        // Write to file\n        let out_path = ctx.destination.join(filepath);\n        fs::write(&out_path, rendered)?;\n\n        if prev_ch.is_none() {\n            ctx.data.insert(\"path\".to_owned(), json!(\"index.md\"));\n            ctx.data.insert(\"path_to_root\".to_owned(), json!(\"\"));\n            ctx.data.insert(\"is_index\".to_owned(), json!(true));\n            let rendered_index = ctx.handlebars.render(\"index\", &ctx.data)?;\n            debug!(\"Creating index.html from {}\", ctx_path);\n            fs::write(ctx.destination.join(\"index.html\"), rendered_index)?;\n        }\n\n        Ok(())\n    }\n\n    fn render_404(\n        &self,\n        ctx: &RenderContext,\n        html_config: &HtmlConfig,\n        src_dir: &Path,\n        handlebars: &mut Handlebars<'_>,\n        data: &mut serde_json::Map<String, serde_json::Value>,\n    ) -> Result<()> {\n        let content_404 = if let Some(ref filename) = html_config.input_404 {\n            let path = src_dir.join(filename);\n            fs::read_to_string(&path).with_context(|| \"failed to read the 404 input file\")?\n        } else {\n            // 404 input not explicitly configured try the default file 404.md\n            let default_404_location = src_dir.join(\"404.md\");\n            if default_404_location.exists() {\n                fs::read_to_string(&default_404_location)\n                    .with_context(|| \"failed to read the 404 input file\")?\n            } else {\n                \"# Document not found (404)\\n\\nThis URL is invalid, sorry. Please use the \\\n                navigation bar or search to continue.\"\n                    .to_string()\n            }\n        };\n        let options = crate::html::HtmlRenderOptions::new(\n            Path::new(\"404.md\"),\n            html_config,\n            ctx.config.rust.edition,\n        );\n        let html_content_404 = render_markdown(&content_404, &options);\n\n        let mut data_404 = data.clone();\n        let base_url = if let Some(site_url) = &html_config.site_url {\n            site_url\n        } else {\n            debug!(\n                \"HTML 'site-url' parameter not set, defaulting to '/'. Please configure \\\n                this to ensure the 404 page work correctly, especially if your site is hosted in a \\\n                subdirectory on the HTTP server.\"\n            );\n            \"/\"\n        };\n        data_404.insert(\"base_url\".to_owned(), json!(base_url));\n        // Set a dummy path to ensure other paths (e.g. in the TOC) are generated correctly\n        data_404.insert(\"path\".to_owned(), json!(\"404.md\"));\n        data_404.insert(\"content\".to_owned(), json!(html_content_404));\n\n        let mut title = String::from(\"Page not found\");\n        if let Some(book_title) = &ctx.config.book.title {\n            title.push_str(\" - \");\n            title.push_str(book_title);\n        }\n        data_404.insert(\"title\".to_owned(), json!(title));\n        let rendered = handlebars.render(\"index\", &data_404)?;\n\n        let output_file = ctx.destination.join(html_config.get_404_output_file());\n        fs::write(output_file, rendered)?;\n        debug!(\"Creating 404.html ✓\");\n        Ok(())\n    }\n\n    fn render_print_page(\n        &self,\n        ctx: &RenderContext,\n        handlebars: &Handlebars<'_>,\n        data: &mut serde_json::Map<String, serde_json::Value>,\n        chapter_trees: Vec<ChapterTree<'_>>,\n    ) -> Result<String> {\n        let print_content = crate::html::render_print_page(chapter_trees);\n\n        if let Some(ref title) = ctx.config.book.title {\n            data.insert(\"title\".to_owned(), json!(title));\n        } else {\n            // Make sure that the Print chapter does not display the title from\n            // the last rendered chapter by removing it from its context\n            data.remove(\"title\");\n        }\n        data.insert(\"is_print\".to_owned(), json!(true));\n        data.insert(\"path\".to_owned(), json!(\"print.md\"));\n        data.insert(\"content\".to_owned(), json!(print_content));\n        data.insert(\n            \"path_to_root\".to_owned(),\n            json!(fs::path_to_root(Path::new(\"print.md\"))),\n        );\n\n        debug!(\"Render template\");\n        let rendered = handlebars.render(\"index\", &data)?;\n        Ok(rendered)\n    }\n\n    fn register_hbs_helpers(&self, handlebars: &mut Handlebars<'_>, html_config: &HtmlConfig) {\n        handlebars.register_helper(\n            \"toc\",\n            Box::new(helpers::toc::RenderToc {\n                no_section_label: html_config.no_section_label,\n            }),\n        );\n        handlebars.register_helper(\"fa\", Box::new(helpers::fontawesome::fa_helper));\n    }\n\n    fn emit_redirects(\n        &self,\n        root: &Path,\n        handlebars: &Handlebars<'_>,\n        redirects: &HashMap<String, String>,\n    ) -> Result<()> {\n        if redirects.is_empty() {\n            return Ok(());\n        }\n\n        debug!(\"Emitting redirects\");\n        let redirects = combine_fragment_redirects(redirects);\n\n        for (original, (dest, fragment_map)) in redirects {\n            // Note: all paths are relative to the build directory, so the\n            // leading slash in an absolute path means nothing (and would mess\n            // up `root.join(original)`).\n            let original = original.trim_start_matches('/');\n            let filename = root.join(original);\n            if filename.exists() {\n                // This redirect is handled by the in-page fragment mapper.\n                continue;\n            }\n            if dest.is_empty() {\n                bail!(\n                    \"redirect entry for `{original}` only has source paths with `#` fragments\\n\\\n                     There must be an entry without the `#` fragment to determine the default \\\n                     destination.\"\n                );\n            }\n            debug!(\"Redirecting \\\"{}\\\" → \\\"{}\\\"\", original, dest);\n            self.emit_redirect(handlebars, &filename, &dest, &fragment_map)?;\n        }\n\n        Ok(())\n    }\n\n    fn emit_redirect(\n        &self,\n        handlebars: &Handlebars<'_>,\n        original: &Path,\n        destination: &str,\n        fragment_map: &BTreeMap<String, String>,\n    ) -> Result<()> {\n        if let Some(parent) = original.parent() {\n            fs::create_dir_all(parent)?\n        }\n\n        let js_map = serde_json::to_string(fragment_map)?;\n\n        let ctx = json!({\n            \"fragment_map\": js_map,\n            \"url\": destination,\n        });\n        let rendered = handlebars.render(\"redirect\", &ctx).with_context(|| {\n            format!(\n                \"Unable to create a redirect file at `{}`\",\n                original.display()\n            )\n        })?;\n        fs::write(original, rendered)?;\n\n        Ok(())\n    }\n}\n\nimpl Renderer for HtmlHandlebars {\n    fn name(&self) -> &str {\n        \"html\"\n    }\n\n    fn render(&self, ctx: &RenderContext) -> Result<()> {\n        let book_config = &ctx.config.book;\n        let html_config = ctx.config.html_config().unwrap_or_default();\n        let src_dir = ctx.root.join(&ctx.config.book.src);\n        let destination = &ctx.destination;\n        let book = &ctx.book;\n        let build_dir = ctx.root.join(&ctx.config.build.build_dir);\n\n        if destination.exists() {\n            fs::remove_dir_content(destination)\n                .with_context(|| \"Unable to remove stale HTML output\")?;\n        }\n\n        trace!(\"render\");\n        let mut handlebars = Handlebars::new();\n\n        let theme_dir = match html_config.theme {\n            Some(ref theme) => {\n                let dir = ctx.root.join(theme);\n                if !dir.is_dir() {\n                    bail!(\"theme dir {} does not exist\", dir.display());\n                }\n                dir\n            }\n            None => ctx.root.join(\"theme\"),\n        };\n\n        let theme = Theme::new(theme_dir);\n\n        debug!(\"Register the index handlebars template\");\n        handlebars.register_template_string(\"index\", String::from_utf8(theme.index.clone())?)?;\n\n        debug!(\"Register the head handlebars template\");\n        handlebars.register_partial(\"head\", String::from_utf8(theme.head.clone())?)?;\n\n        debug!(\"Register the redirect handlebars template\");\n        handlebars\n            .register_template_string(\"redirect\", String::from_utf8(theme.redirect.clone())?)?;\n\n        debug!(\"Register the header handlebars template\");\n        handlebars.register_partial(\"header\", String::from_utf8(theme.header.clone())?)?;\n\n        debug!(\"Register the toc handlebars template\");\n        handlebars.register_template_string(\"toc_js\", String::from_utf8(theme.toc_js.clone())?)?;\n        handlebars\n            .register_template_string(\"toc_html\", String::from_utf8(theme.toc_html.clone())?)?;\n\n        debug!(\"Register handlebars helpers\");\n        self.register_hbs_helpers(&mut handlebars, &html_config);\n\n        let mut data = make_data(&ctx.root, book, &ctx.config, &html_config, &theme)?;\n\n        let chapter_trees = build_trees(book, &html_config, ctx.config.rust.edition);\n\n        fs::create_dir_all(destination)\n            .with_context(|| \"Unexpected error when constructing destination path\")?;\n\n        let mut static_files = StaticFiles::new(&theme, &html_config, &ctx.root)?;\n\n        // Render search index\n        #[cfg(feature = \"search\")]\n        {\n            let default = mdbook_core::config::Search::default();\n            let search = html_config.search.as_ref().unwrap_or(&default);\n            if search.enable {\n                super::search::create_files(&search, &mut static_files, &chapter_trees)?;\n            }\n        }\n\n        debug!(\"Render toc js\");\n        {\n            let rendered_toc = handlebars.render(\"toc_js\", &data)?;\n            static_files.add_builtin(\"toc.js\", rendered_toc.as_bytes());\n            debug!(\"Creating toc.js ✓\");\n        }\n\n        if html_config.hash_files {\n            static_files.hash_files()?;\n        }\n\n        debug!(\"Copy static files\");\n        let resource_helper = static_files\n            .write_files(&destination)\n            .with_context(|| \"Unable to copy across static files\")?;\n\n        handlebars.register_helper(\"resource\", Box::new(resource_helper));\n\n        debug!(\"Render toc html\");\n        {\n            data.insert(\"is_toc_html\".to_owned(), json!(true));\n            data.insert(\"path\".to_owned(), json!(\"toc.html\"));\n            let rendered_toc = handlebars.render(\"toc_html\", &data)?;\n            fs::write(destination.join(\"toc.html\"), rendered_toc)?;\n            debug!(\"Creating toc.html ✓\");\n            data.remove(\"path\");\n            data.remove(\"is_toc_html\");\n        }\n\n        fs::write(\n            destination.join(\".nojekyll\"),\n            b\"This file makes sure that Github Pages doesn't process mdBook's output.\\n\",\n        )?;\n\n        if let Some(cname) = &html_config.cname {\n            fs::write(destination.join(\"CNAME\"), format!(\"{cname}\\n\"))?;\n        }\n\n        for (i, chapter_tree) in chapter_trees.iter().enumerate() {\n            let previous = (i != 0).then(|| chapter_trees[i - 1].chapter);\n            let next = (i != chapter_trees.len() - 1).then(|| chapter_trees[i + 1].chapter);\n            let ctx = RenderChapterContext {\n                handlebars: &handlebars,\n                destination: destination.to_path_buf(),\n                data: data.clone(),\n                book_config: book_config.clone(),\n                html_config: html_config.clone(),\n                chapter_titles: &ctx.chapter_titles,\n            };\n            self.render_chapter(chapter_tree, previous, next, ctx)?;\n        }\n\n        // Render 404 page\n        if html_config.input_404 != Some(\"\".to_string()) {\n            self.render_404(ctx, &html_config, &src_dir, &mut handlebars, &mut data)?;\n        }\n\n        // Render the print version.\n        if html_config.print.enable {\n            let print_rendered =\n                self.render_print_page(ctx, &handlebars, &mut data, chapter_trees)?;\n\n            fs::write(destination.join(\"print.html\"), print_rendered)?;\n            debug!(\"Creating print.html ✓\");\n        }\n\n        self.emit_redirects(&ctx.destination, &handlebars, &html_config.redirect)\n            .context(\"Unable to emit redirects\")?;\n\n        // Copy all remaining files, avoid a recursive copy from/to the book build dir\n        fs::copy_files_except_ext(&src_dir, destination, true, Some(&build_dir), &[\"md\"])?;\n\n        info!(\"HTML book written to `{}`\", destination.display());\n\n        Ok(())\n    }\n}\n\nfn make_data(\n    root: &Path,\n    book: &Book,\n    config: &Config,\n    html_config: &HtmlConfig,\n    theme: &Theme,\n) -> Result<serde_json::Map<String, serde_json::Value>> {\n    trace!(\"make_data\");\n\n    let mut data = serde_json::Map::new();\n    data.insert(\n        \"language\".to_owned(),\n        json!(config.book.language.clone().unwrap_or_default()),\n    );\n    data.insert(\n        \"text_direction\".to_owned(),\n        json!(config.book.realized_text_direction()),\n    );\n    data.insert(\n        \"book_title\".to_owned(),\n        json!(config.book.title.clone().unwrap_or_default()),\n    );\n    data.insert(\n        \"description\".to_owned(),\n        json!(config.book.description.clone().unwrap_or_default()),\n    );\n    if theme.favicon_png.is_some() {\n        data.insert(\"favicon_png\".to_owned(), json!(\"favicon.png\"));\n    }\n    if theme.favicon_svg.is_some() {\n        data.insert(\"favicon_svg\".to_owned(), json!(\"favicon.svg\"));\n    }\n    if let Some(ref live_reload_endpoint) = html_config.live_reload_endpoint {\n        data.insert(\n            \"live_reload_endpoint\".to_owned(),\n            json!(live_reload_endpoint),\n        );\n    }\n\n    let default_theme = match html_config.default_theme {\n        Some(ref theme) => theme.to_lowercase(),\n        None => \"light\".to_string(),\n    };\n    data.insert(\"default_theme\".to_owned(), json!(default_theme));\n\n    let preferred_dark_theme = match html_config.preferred_dark_theme {\n        Some(ref theme) => theme.to_lowercase(),\n        None => \"navy\".to_string(),\n    };\n    data.insert(\n        \"preferred_dark_theme\".to_owned(),\n        json!(preferred_dark_theme),\n    );\n\n    if html_config.mathjax_support {\n        data.insert(\"mathjax_support\".to_owned(), json!(true));\n    }\n\n    // Add check to see if there is an additional style\n    if !html_config.additional_css.is_empty() {\n        let mut css = Vec::new();\n        for style in &html_config.additional_css {\n            match style.strip_prefix(root) {\n                Ok(p) => css.push(p.to_str().expect(\"Could not convert to str\")),\n                Err(_) => css.push(style.to_str().expect(\"Could not convert to str\")),\n            }\n        }\n        data.insert(\"additional_css\".to_owned(), json!(css));\n    }\n\n    // Add check to see if there is an additional script\n    if !html_config.additional_js.is_empty() {\n        let mut js = Vec::new();\n        for script in &html_config.additional_js {\n            match script.strip_prefix(root) {\n                Ok(p) => js.push(p.to_str().expect(\"Could not convert to str\")),\n                Err(_) => js.push(script.to_str().expect(\"Could not convert to str\")),\n            }\n        }\n        data.insert(\"additional_js\".to_owned(), json!(js));\n    }\n\n    if html_config.playground.editable && html_config.playground.copy_js {\n        data.insert(\"playground_js\".to_owned(), json!(true));\n        if html_config.playground.line_numbers {\n            data.insert(\"playground_line_numbers\".to_owned(), json!(true));\n        }\n    }\n    if html_config.playground.copyable {\n        data.insert(\"playground_copyable\".to_owned(), json!(true));\n    }\n\n    data.insert(\"print_enable\".to_owned(), json!(html_config.print.enable));\n    data.insert(\"fold_enable\".to_owned(), json!(html_config.fold.enable));\n    data.insert(\"fold_level\".to_owned(), json!(html_config.fold.level));\n    data.insert(\n        \"sidebar_header_nav\".to_owned(),\n        json!(html_config.sidebar_header_nav),\n    );\n\n    let search = html_config.search.clone();\n    if cfg!(feature = \"search\") {\n        let search = search.unwrap_or_default();\n        data.insert(\"search_enabled\".to_owned(), json!(search.enable));\n        data.insert(\n            \"search_js\".to_owned(),\n            json!(search.enable && search.copy_js),\n        );\n    } else if search.is_some() {\n        warn!(\"mdBook compiled without search support, ignoring `output.html.search` table\");\n        warn!(\n            \"please reinstall with `cargo install mdbook --force --features search`to use the \\\n             search feature\"\n        )\n    }\n\n    if let Some(ref git_repository_url) = html_config.git_repository_url {\n        data.insert(\"git_repository_url\".to_owned(), json!(git_repository_url));\n    }\n\n    let git_repository_icon = match html_config.git_repository_icon {\n        Some(ref git_repository_icon) => git_repository_icon,\n        None => \"fab-github\",\n    };\n    let git_repository_icon_class = match git_repository_icon.split('-').next() {\n        Some(\"fa\") => \"regular\",\n        Some(\"fas\") => \"solid\",\n        Some(\"fab\") => \"brands\",\n        _ => \"regular\",\n    };\n    data.insert(\"git_repository_icon\".to_owned(), json!(git_repository_icon));\n    data.insert(\n        \"git_repository_icon_class\".to_owned(),\n        json!(git_repository_icon_class),\n    );\n\n    let mut chapters = vec![];\n\n    for item in book.iter() {\n        // Create the data to inject in the template\n        let mut chapter = BTreeMap::new();\n\n        match *item {\n            BookItem::PartTitle(ref title) => {\n                chapter.insert(\"part\".to_owned(), json!(title));\n            }\n            BookItem::Chapter(ref ch) => {\n                if let Some(ref section) = ch.number {\n                    chapter.insert(\"section\".to_owned(), json!(section.to_string()));\n                }\n\n                chapter.insert(\n                    \"has_sub_items\".to_owned(),\n                    json!((!ch.sub_items.is_empty()).to_string()),\n                );\n\n                chapter.insert(\"name\".to_owned(), json!(ch.name));\n                if let Some(ref path) = ch.path {\n                    let p = path\n                        .to_str()\n                        .with_context(|| \"Could not convert path to str\")?;\n                    chapter.insert(\"path\".to_owned(), json!(p));\n                }\n            }\n            BookItem::Separator => {\n                chapter.insert(\"spacer\".to_owned(), json!(\"_spacer_\"));\n            }\n        }\n\n        chapters.push(chapter);\n    }\n\n    data.insert(\"chapters\".to_owned(), json!(chapters));\n\n    debug!(\"[*]: JSON constructed\");\n    Ok(data)\n}\n\nstruct RenderChapterContext<'a> {\n    handlebars: &'a Handlebars<'a>,\n    destination: PathBuf,\n    data: serde_json::Map<String, serde_json::Value>,\n    book_config: BookConfig,\n    html_config: HtmlConfig,\n    chapter_titles: &'a HashMap<PathBuf, String>,\n}\n\n/// Redirect mapping.\n///\n/// The key is the source path (like `foo/bar.html`). The value is a tuple\n/// `(destination_path, fragment_map)`. The `destination_path` is the page to\n/// redirect to. `fragment_map` is the map of fragments that override the\n/// destination. For example, a fragment `#foo` could redirect to any other\n/// page or site.\ntype CombinedRedirects = BTreeMap<String, (String, BTreeMap<String, String>)>;\nfn combine_fragment_redirects(redirects: &HashMap<String, String>) -> CombinedRedirects {\n    let mut combined: CombinedRedirects = BTreeMap::new();\n    // This needs to extract the fragments to generate the fragment map.\n    for (original, new) in redirects {\n        if let Some((source_path, source_fragment)) = original.rsplit_once('#') {\n            let e = combined.entry(source_path.to_string()).or_default();\n            if let Some(old) = e.1.insert(format!(\"#{source_fragment}\"), new.clone()) {\n                error!(\n                    \"internal error: found duplicate fragment redirect \\\n                     {old} for {source_path}#{source_fragment}\"\n                );\n            }\n        } else {\n            let e = combined.entry(original.to_string()).or_default();\n            e.0 = new.clone();\n        }\n    }\n    combined\n}\n\n/// Collects fragment redirects for an existing page.\n///\n/// The returned map has keys like `#foo` and the value is the new destination\n/// path or URL.\nfn collect_redirects_for_path(\n    path: &Path,\n    redirects: &HashMap<String, String>,\n) -> Result<BTreeMap<String, String>> {\n    let path = format!(\"/{}\", path.to_url_path());\n    if redirects.contains_key(&path) {\n        bail!(\n            \"redirect found for existing chapter at `{path}`\\n\\\n            Either delete the redirect or remove the chapter.\"\n        );\n    }\n\n    let key_prefix = format!(\"{path}#\");\n    let map = redirects\n        .iter()\n        .filter_map(|(source, dest)| {\n            source\n                .strip_prefix(&key_prefix)\n                .map(|fragment| (format!(\"#{fragment}\"), dest.to_string()))\n        })\n        .collect();\n    Ok(map)\n}\n"
  },
  {
    "path": "crates/mdbook-html/src/html_handlebars/helpers/fontawesome.rs",
    "content": "use font_awesome_as_a_crate as fa;\nuse handlebars::{\n    Context, Handlebars, Helper, Output, RenderContext, RenderError, RenderErrorReason,\n};\nuse std::str::FromStr;\nuse tracing::trace;\n\npub(crate) fn fa_helper(\n    h: &Helper<'_>,\n    _r: &Handlebars<'_>,\n    _ctx: &Context,\n    _rc: &mut RenderContext<'_, '_>,\n    out: &mut dyn Output,\n) -> Result<(), RenderError> {\n    trace!(\"fa_helper (handlebars helper)\");\n\n    let type_ = h\n        .param(0)\n        .and_then(|v| v.value().as_str())\n        .and_then(|v| fa::Type::from_str(v).ok())\n        .ok_or_else(|| {\n            RenderErrorReason::Other(\n                \"Param 0 with String type is required for fontawesome helper.\".to_owned(),\n            )\n        })?;\n\n    let name = h.param(1).and_then(|v| v.value().as_str()).ok_or_else(|| {\n        RenderErrorReason::Other(\n            \"Param 1 with String type is required for fontawesome helper.\".to_owned(),\n        )\n    })?;\n\n    trace!(\"fa_helper: {} {}\", type_, name);\n\n    let name = name\n        .strip_prefix(\"fa-\")\n        .or_else(|| name.strip_prefix(\"fab-\"))\n        .or_else(|| name.strip_prefix(\"fas-\"))\n        .unwrap_or(name);\n\n    if let Some(id) = h.param(2).and_then(|v| v.value().as_str()) {\n        out.write(&format!(\"<span class=fa-svg id=\\\"{}\\\">\", id))?;\n    } else {\n        out.write(\"<span class=fa-svg>\")?;\n    }\n    out.write(fa::svg(type_, name).map_err(|_| {\n        let valid_types = \"fas (solid), fab (brands), or far (regular)\";\n        RenderErrorReason::Other(format!(\n            \"Unknown Font Awesome icon `{name}` for type `{type_}`. \\\n             Hint: check the icon name and prefix ({valid_types}) at \\\n             https://fontawesome.com/v6/search?m=free\"\n        ))\n    })?)?;\n    out.write(\"</span>\")?;\n\n    Ok(())\n}\n"
  },
  {
    "path": "crates/mdbook-html/src/html_handlebars/helpers/mod.rs",
    "content": "pub(crate) mod fontawesome;\npub(crate) mod resources;\npub(crate) mod toc;\n"
  },
  {
    "path": "crates/mdbook-html/src/html_handlebars/helpers/resources.rs",
    "content": "use std::collections::HashMap;\n\nuse mdbook_core::utils;\n\nuse handlebars::{\n    Context, Handlebars, Helper, HelperDef, Output, RenderContext, RenderError, RenderErrorReason,\n};\n\n// Handlebars helper to find filenames with hashes in them\n#[derive(Clone)]\npub(crate) struct ResourceHelper {\n    pub hash_map: HashMap<String, String>,\n}\n\nimpl HelperDef for ResourceHelper {\n    fn call<'reg: 'rc, 'rc>(\n        &self,\n        h: &Helper<'rc>,\n        _r: &'reg Handlebars<'_>,\n        ctx: &'rc Context,\n        rc: &mut RenderContext<'reg, 'rc>,\n        out: &mut dyn Output,\n    ) -> Result<(), RenderError> {\n        let param = h.param(0).and_then(|v| v.value().as_str()).ok_or_else(|| {\n            RenderErrorReason::Other(\n                \"Param 0 with String type is required for resource helper.\".to_owned(),\n            )\n        })?;\n\n        let base_path = rc\n            .evaluate(ctx, \"@root/path\")?\n            .as_json()\n            .as_str()\n            .ok_or_else(|| {\n                RenderErrorReason::Other(\"Type error for `path`, string expected\".to_owned())\n            })?\n            .replace(\"\\\"\", \"\");\n\n        let path_to_root = utils::fs::path_to_root(&base_path);\n\n        out.write(&path_to_root)?;\n        out.write(self.hash_map.get(param).map(|p| &p[..]).unwrap_or(&param))?;\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "crates/mdbook-html/src/html_handlebars/helpers/toc.rs",
    "content": "use crate::utils::ToUrlPath;\nuse handlebars::{\n    Context, Handlebars, Helper, HelperDef, Output, RenderContext, RenderError, RenderErrorReason,\n};\nuse mdbook_core::utils::escape_html_attribute;\nuse std::path::Path;\nuse std::{cmp::Ordering, collections::BTreeMap};\n\n// Handlebars helper to construct TOC\n#[derive(Clone, Copy)]\npub(crate) struct RenderToc {\n    pub no_section_label: bool,\n}\n\nimpl HelperDef for RenderToc {\n    fn call<'reg: 'rc, 'rc>(\n        &self,\n        _h: &Helper<'rc>,\n        _r: &'reg Handlebars<'_>,\n        ctx: &'rc Context,\n        rc: &mut RenderContext<'reg, 'rc>,\n        out: &mut dyn Output,\n    ) -> Result<(), RenderError> {\n        // get value from context data\n        // rc.get_path() is current json parent path, you should always use it like this\n        // param is the key of value you want to display\n        let chapters = rc.evaluate(ctx, \"@root/chapters\").and_then(|c| {\n            serde_json::value::from_value::<Vec<BTreeMap<String, String>>>(c.as_json().clone())\n                .map_err(|_| {\n                    RenderErrorReason::Other(\"Could not decode the JSON data\".to_owned()).into()\n                })\n        })?;\n\n        let fold_enable = rc\n            .evaluate(ctx, \"@root/fold_enable\")?\n            .as_json()\n            .as_bool()\n            .ok_or_else(|| {\n                RenderErrorReason::Other(\"Type error for `fold_enable`, bool expected\".to_owned())\n            })?;\n\n        let fold_level = rc\n            .evaluate(ctx, \"@root/fold_level\")?\n            .as_json()\n            .as_u64()\n            .ok_or_else(|| {\n                RenderErrorReason::Other(\"Type error for `fold_level`, u64 expected\".to_owned())\n            })?;\n\n        // If true, then this is the iframe and we need target=\"_parent\"\n        let is_toc_html = rc\n            .evaluate(ctx, \"@root/is_toc_html\")?\n            .as_json()\n            .as_bool()\n            .unwrap_or(false);\n\n        out.write(\"<ol class=\\\"chapter\\\">\")?;\n\n        let mut current_level = 1;\n        let mut first = true;\n\n        for item in chapters {\n            let level = item\n                .get(\"section\")\n                .map(|s| s.matches('.').count())\n                .unwrap_or(1);\n\n            // Expand if folding is disabled, or if levels that are larger than this would not\n            // be folded.\n            let is_expanded = !fold_enable || level - 1 < (fold_level as usize);\n\n            match level.cmp(&current_level) {\n                Ordering::Greater => {\n                    // There is an assumption that when descending, it can\n                    // only go one level down at a time. This should be\n                    // enforced by the nature of markdown lists and the\n                    // summary parser.\n                    assert_eq!(level, current_level + 1);\n                    current_level += 1;\n                    out.write(\"<ol class=\\\"section\\\">\")?;\n                    write_li_open_tag(out, is_expanded)?;\n                }\n                Ordering::Less => {\n                    while level < current_level {\n                        out.write(\"</li>\")?;\n                        out.write(\"</ol>\")?;\n                        current_level -= 1;\n                    }\n                    write_li_open_tag(out, is_expanded)?;\n                }\n                Ordering::Equal => {\n                    if !first {\n                        out.write(\"</li>\")?;\n                    }\n                    write_li_open_tag(out, is_expanded)?;\n                }\n            }\n            first = false;\n\n            // Spacer\n            if item.contains_key(\"spacer\") {\n                out.write(\"<li class=\\\"spacer\\\"></li>\")?;\n                continue;\n            }\n\n            // Part title\n            if let Some(title) = item.get(\"part\") {\n                out.write(\"<li class=\\\"part-title\\\">\")?;\n                out.write(&escape_html_attribute(title))?;\n                out.write(\"</li>\")?;\n                continue;\n            }\n\n            out.write(\"<span class=\\\"chapter-link-wrapper\\\">\")?;\n\n            // Link\n            let path_exists = match item.get(\"path\") {\n                Some(path) if !path.is_empty() => {\n                    out.write(\"<a href=\\\"\")?;\n                    let tmp = Path::new(path).with_extension(\"html\").to_url_path();\n\n                    // Add link\n                    out.write(&tmp)?;\n                    out.write(if is_toc_html {\n                        \"\\\" target=\\\"_parent\\\">\"\n                    } else {\n                        \"\\\">\"\n                    })?;\n                    true\n                }\n                _ => {\n                    out.write(\"<span>\")?;\n                    false\n                }\n            };\n\n            if !self.no_section_label {\n                // Section does not necessarily exist\n                if let Some(section) = item.get(\"section\") {\n                    out.write(\"<strong aria-hidden=\\\"true\\\">\")?;\n                    out.write(section)?;\n                    out.write(\"</strong> \")?;\n                }\n            }\n\n            if let Some(name) = item.get(\"name\") {\n                out.write(&escape_html_attribute(name))?;\n            }\n\n            if path_exists {\n                out.write(\"</a>\")?;\n            } else {\n                out.write(\"</span>\")?;\n            }\n\n            // Render expand/collapse toggle\n            if let Some(flag) = item.get(\"has_sub_items\") {\n                let has_sub_items = flag.parse::<bool>().unwrap_or_default();\n                if fold_enable && has_sub_items {\n                    // The <div> here is to manage rotating the element when\n                    // the chapter title is long and word-wraps.\n                    out.write(\"<a class=\\\"chapter-fold-toggle\\\"><div>❱</div></a>\")?;\n                }\n            }\n            out.write(\"</span>\")?;\n        }\n        while current_level > 0 {\n            out.write(\"</li>\")?;\n            out.write(\"</ol>\")?;\n            current_level -= 1;\n        }\n\n        Ok(())\n    }\n}\n\nfn write_li_open_tag(out: &mut dyn Output, is_expanded: bool) -> Result<(), std::io::Error> {\n    let mut li = String::from(\"<li class=\\\"chapter-item \");\n    if is_expanded {\n        li.push_str(\"expanded \");\n    }\n    li.push_str(\"\\\">\");\n    out.write(&li)\n}\n"
  },
  {
    "path": "crates/mdbook-html/src/html_handlebars/mod.rs",
    "content": "mod hbs_renderer;\nmod helpers;\n#[cfg(feature = \"search\")]\nmod search;\nmod static_files;\n\npub use self::hbs_renderer::HtmlHandlebars;\n"
  },
  {
    "path": "crates/mdbook-html/src/html_handlebars/search.rs",
    "content": "use super::static_files::StaticFiles;\nuse crate::html::{ChapterTree, Node};\nuse crate::theme::searcher;\nuse crate::utils::ToUrlPath;\nuse anyhow::{Result, bail};\nuse ego_tree::iter::Edge;\nuse elasticlunr::{Index, IndexBuilder};\nuse mdbook_core::book::Chapter;\nuse mdbook_core::config::{Search, SearchChapterSettings};\nuse mdbook_core::static_regex;\nuse serde::Serialize;\nuse std::borrow::Cow;\nuse std::collections::HashMap;\nuse std::path::{Path, PathBuf};\nuse tracing::{debug, warn};\n\nconst MAX_WORD_LENGTH_TO_INDEX: usize = 80;\n\n/// Tokenizes in the same way as elasticlunr-rs (for English), but also drops long tokens.\nfn tokenize(text: &str) -> Vec<String> {\n    text.split(|c: char| c.is_whitespace() || c == '-')\n        .filter(|s| !s.is_empty())\n        .map(|s| s.trim().to_lowercase())\n        .filter(|s| s.len() <= MAX_WORD_LENGTH_TO_INDEX)\n        .collect()\n}\n\n/// Creates all files required for search.\npub(super) fn create_files(\n    search_config: &Search,\n    static_files: &mut StaticFiles,\n    chapter_trees: &[ChapterTree<'_>],\n) -> Result<()> {\n    let mut index = IndexBuilder::new()\n        .add_field_with_tokenizer(\"title\", Box::new(&tokenize))\n        .add_field_with_tokenizer(\"body\", Box::new(&tokenize))\n        .add_field_with_tokenizer(\"breadcrumbs\", Box::new(&tokenize))\n        .build();\n\n    // These are links to all of the headings in all of the chapters.\n    let mut doc_urls = Vec::new();\n\n    let chapter_configs = sort_search_config(&search_config.chapter);\n    validate_chapter_config(&chapter_configs, chapter_trees)?;\n\n    for ct in chapter_trees {\n        let path = settings_path(ct.chapter);\n        let chapter_settings = get_chapter_settings(&chapter_configs, path);\n        if !chapter_settings.enable.unwrap_or(true) {\n            continue;\n        }\n        index_chapter(&mut index, search_config, &mut doc_urls, ct)?;\n    }\n\n    let index = write_to_json(index, search_config, doc_urls)?;\n    debug!(\"Writing search index ✓\");\n    if index.len() > 10_000_000 {\n        warn!(\"search index is very large ({} bytes)\", index.len());\n    }\n\n    if search_config.copy_js {\n        static_files.add_builtin(\n            \"searchindex.js\",\n            // To reduce the size of the generated JSON by preventing all `\"` characters to be\n            // escaped, we instead surround the string with much less common `'` character.\n            format!(\n                \"window.search = Object.assign(window.search, JSON.parse('{}'));\",\n                index.replace(\"\\\\\", \"\\\\\\\\\").replace(\"'\", \"\\\\'\")\n            )\n            .as_bytes(),\n        );\n        static_files.add_builtin(\"searcher.js\", searcher::JS);\n        static_files.add_builtin(\"mark.min.js\", searcher::MARK_JS);\n        static_files.add_builtin(\"elasticlunr.min.js\", searcher::ELASTICLUNR_JS);\n        debug!(\"Copying search files ✓\");\n    }\n\n    Ok(())\n}\n\n/// Uses the given arguments to construct a search document, then inserts it to the given index.\nfn add_doc(\n    index: &mut Index,\n    doc_urls: &mut Vec<String>,\n    anchor_base: &str,\n    heading_id: &str,\n    items: &[&str],\n) {\n    let mut url = anchor_base.to_string();\n    if !heading_id.is_empty() {\n        url.push('#');\n        url.push_str(heading_id);\n    }\n\n    let doc_ref = doc_urls.len().to_string();\n    doc_urls.push(url);\n\n    let items = items.iter().map(|&x| collapse_whitespace(x.trim()));\n    index.add_doc(&doc_ref, items);\n}\n\n/// Adds the chapter to the search index.\nfn index_chapter(\n    index: &mut Index,\n    search_config: &Search,\n    doc_urls: &mut Vec<String>,\n    chapter_tree: &ChapterTree<'_>,\n) -> Result<()> {\n    let anchor_base = chapter_tree.html_path.to_url_path();\n\n    let mut in_heading = false;\n    let max_section_depth = search_config.heading_split_level;\n    let mut section_id = None;\n    let mut heading = String::new();\n    let mut body = String::new();\n    let mut breadcrumbs = chapter_tree.chapter.parent_names.clone();\n\n    breadcrumbs.push(chapter_tree.chapter.name.clone());\n\n    let mut traverse = chapter_tree.tree.root().traverse();\n\n    while let Some(edge) = traverse.next() {\n        match edge {\n            Edge::Open(node) => match node.value() {\n                Node::Element(el) => {\n                    if let Some(level) = el.heading_level()\n                        && level <= max_section_depth\n                        && let Some(heading_id) = el.attr(\"id\")\n                    {\n                        if !heading.is_empty() {\n                            // Section finished, the next heading is following now\n                            // Write the data to the index, and clear it for the next section\n                            add_doc(\n                                index,\n                                doc_urls,\n                                &anchor_base,\n                                section_id.unwrap(),\n                                &[&heading, &body, &breadcrumbs.join(\" » \")],\n                            );\n                            heading.clear();\n                            body.clear();\n                            breadcrumbs.pop();\n                        }\n                        section_id = Some(heading_id);\n                        in_heading = true;\n                    } else if matches!(el.name(), \"script\" | \"style\") {\n                        // Skip this node.\n                        while let Some(edge) = traverse.next() {\n                            if let Edge::Close(close) = edge\n                                && close == node\n                            {\n                                break;\n                            }\n                        }\n                    // Insert spaces where HTML output would usually separate text\n                    // to ensure words don't get merged together\n                    } else if in_heading {\n                        heading.push(' ');\n                    } else {\n                        body.push(' ');\n                    }\n                }\n                Node::Text(text) => {\n                    if in_heading {\n                        heading.push_str(text);\n                    } else {\n                        body.push_str(text);\n                    }\n                }\n                Node::Comment(_) => {}\n                Node::Fragment => {}\n                Node::RawData(_) => {}\n            },\n            Edge::Close(node) => match node.value() {\n                Node::Element(el) => {\n                    if let Some(level) = el.heading_level()\n                        && level <= max_section_depth\n                    {\n                        in_heading = false;\n                        breadcrumbs.push(heading.clone());\n                    }\n                }\n                _ => {}\n            },\n        }\n    }\n\n    if !body.is_empty() || !heading.is_empty() {\n        // Make sure the last section is added to the index\n        let title = if heading.is_empty() {\n            if let Some(chapter) = breadcrumbs.first() {\n                chapter\n            } else {\n                \"\"\n            }\n        } else {\n            &heading\n        };\n        add_doc(\n            index,\n            doc_urls,\n            &anchor_base,\n            section_id.unwrap_or_default(),\n            &[title, &body, &breadcrumbs.join(\" » \")],\n        );\n    }\n\n    Ok(())\n}\n\nfn write_to_json(index: Index, search_config: &Search, doc_urls: Vec<String>) -> Result<String> {\n    use elasticlunr::config::{SearchBool, SearchOptions, SearchOptionsField};\n    use std::collections::BTreeMap;\n\n    #[derive(Serialize)]\n    struct ResultsOptions {\n        limit_results: u32,\n        teaser_word_count: u32,\n    }\n\n    #[derive(Serialize)]\n    struct SearchindexJson {\n        /// The options used for displaying search results\n        results_options: ResultsOptions,\n        /// The searchoptions for elasticlunr.js\n        search_options: SearchOptions,\n        /// Used to lookup a document's URL from an integer document ref.\n        doc_urls: Vec<String>,\n        /// The index for elasticlunr.js\n        index: elasticlunr::Index,\n    }\n\n    let mut fields = BTreeMap::new();\n    let mut opt = SearchOptionsField::default();\n    let mut insert_boost = |key: &str, boost| {\n        opt.boost = Some(boost);\n        fields.insert(key.into(), opt);\n    };\n    insert_boost(\"title\", search_config.boost_title);\n    insert_boost(\"body\", search_config.boost_paragraph);\n    insert_boost(\"breadcrumbs\", search_config.boost_hierarchy);\n\n    let search_options = SearchOptions {\n        bool: if search_config.use_boolean_and {\n            SearchBool::And\n        } else {\n            SearchBool::Or\n        },\n        expand: search_config.expand,\n        fields,\n    };\n\n    let results_options = ResultsOptions {\n        limit_results: search_config.limit_results,\n        teaser_word_count: search_config.teaser_word_count,\n    };\n\n    let json_contents = SearchindexJson {\n        results_options,\n        search_options,\n        doc_urls,\n        index,\n    };\n\n    // By converting to serde_json::Value as an intermediary, we use a\n    // BTreeMap internally and can force a stable ordering of map keys.\n    let json_contents = serde_json::to_value(&json_contents)?;\n    let json_contents = serde_json::to_string(&json_contents)?;\n\n    Ok(json_contents)\n}\n\nfn settings_path(ch: &Chapter) -> &Path {\n    ch.source_path\n        .as_deref()\n        .unwrap_or_else(|| ch.path.as_deref().unwrap())\n}\n\nfn validate_chapter_config(\n    chapter_configs: &[(PathBuf, SearchChapterSettings)],\n    chapter_trees: &[ChapterTree<'_>],\n) -> Result<()> {\n    for (path, _) in chapter_configs {\n        let found = chapter_trees\n            .iter()\n            .any(|ct| settings_path(ct.chapter).starts_with(path));\n        if !found {\n            bail!(\n                \"[output.html.search.chapter] key `{}` does not match any chapter paths\",\n                path.display()\n            );\n        }\n    }\n    Ok(())\n}\n\nfn sort_search_config(\n    map: &HashMap<String, SearchChapterSettings>,\n) -> Vec<(PathBuf, SearchChapterSettings)> {\n    let mut settings: Vec<_> = map\n        .iter()\n        .map(|(key, value)| (PathBuf::from(key), value.clone()))\n        .collect();\n    // Note: This is case-sensitive, and assumes the author uses the same case\n    // as the actual filename.\n    settings.sort_by(|a, b| a.0.cmp(&b.0));\n    settings\n}\n\nfn get_chapter_settings(\n    chapter_configs: &[(PathBuf, SearchChapterSettings)],\n    source_path: &Path,\n) -> SearchChapterSettings {\n    let mut result = SearchChapterSettings::default();\n    for (path, config) in chapter_configs {\n        if source_path.starts_with(path) {\n            result.enable = config.enable.or(result.enable);\n        }\n    }\n    result\n}\n\n/// Replaces multiple consecutive whitespace characters with a single space character.\nfn collapse_whitespace(text: &str) -> Cow<'_, str> {\n    static_regex!(WS, r\"\\s\\s+\");\n    WS.replace_all(text, \" \")\n}\n\n#[test]\nfn chapter_settings_priority() {\n    let cfg = r#\"\n        [output.html.search.chapter]\n        \"cli/watch.md\" = { enable = true }\n        \"cli\" = { enable = false }\n        \"cli/inner/foo.md\" = { enable = false }\n        \"cli/inner\" = { enable = true }\n        \"foo\" = {} # Just to make sure empty table is allowed.\n    \"#;\n    let cfg: mdbook_core::config::Config = toml::from_str(cfg).unwrap();\n    let html = cfg.html_config().unwrap();\n    let chapter_configs = sort_search_config(&html.search.unwrap().chapter);\n    for (path, enable) in [\n        (\"foo.md\", None),\n        (\"cli/watch.md\", Some(true)),\n        (\"cli/index.md\", Some(false)),\n        (\"cli/inner/index.md\", Some(true)),\n        (\"cli/inner/foo.md\", Some(false)),\n    ] {\n        let mut settings = SearchChapterSettings::default();\n        settings.enable = enable;\n        assert_eq!(\n            get_chapter_settings(&chapter_configs, Path::new(path)),\n            settings\n        );\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn test_tokenize_basic() {\n        assert_eq!(tokenize(\"hello world\"), vec![\"hello\", \"world\"]);\n    }\n\n    #[test]\n    fn test_tokenize_with_hyphens() {\n        assert_eq!(\n            tokenize(\"hello-world test-case\"),\n            vec![\"hello\", \"world\", \"test\", \"case\"]\n        );\n    }\n\n    #[test]\n    fn test_tokenize_mixed_whitespace() {\n        assert_eq!(\n            tokenize(\"hello\\tworld\\ntest\\r\\ncase\"),\n            vec![\"hello\", \"world\", \"test\", \"case\"]\n        );\n    }\n\n    #[test]\n    fn test_tokenize_empty_string() {\n        assert_eq!(tokenize(\"\"), Vec::<String>::new());\n    }\n\n    #[test]\n    fn test_tokenize_only_whitespace() {\n        assert_eq!(tokenize(\"   \\t\\n  \"), Vec::<String>::new());\n    }\n\n    #[test]\n    fn test_tokenize_case_normalization() {\n        assert_eq!(tokenize(\"Hello WORLD Test\"), vec![\"hello\", \"world\", \"test\"]);\n    }\n\n    #[test]\n    fn test_tokenize_trim_whitespace() {\n        assert_eq!(tokenize(\"  hello   world  \"), vec![\"hello\", \"world\"]);\n    }\n\n    #[test]\n    fn test_tokenize_long_words_filtered() {\n        let long_word = \"a\".repeat(MAX_WORD_LENGTH_TO_INDEX + 1);\n        let short_word = \"a\".repeat(MAX_WORD_LENGTH_TO_INDEX);\n        let input = format!(\"{} hello {}\", long_word, short_word);\n        assert_eq!(tokenize(&input), vec![\"hello\", &short_word]);\n    }\n\n    #[test]\n    fn test_tokenize_max_length_word() {\n        let max_word = \"a\".repeat(MAX_WORD_LENGTH_TO_INDEX);\n        assert_eq!(tokenize(&max_word), vec![max_word]);\n    }\n\n    #[test]\n    fn test_tokenize_special_characters() {\n        assert_eq!(\n            tokenize(\"hello,world.test!case?\"),\n            vec![\"hello,world.test!case?\"]\n        );\n    }\n\n    #[test]\n    fn test_tokenize_unicode() {\n        assert_eq!(\n            tokenize(\"café naïve résumé\"),\n            vec![\"café\", \"naïve\", \"résumé\"]\n        );\n    }\n\n    #[test]\n    fn test_tokenize_unicode_rtl_hebre() {\n        assert_eq!(tokenize(\"שלום עולם\"), vec![\"שלום\", \"עולם\"]);\n    }\n\n    #[test]\n    fn test_tokenize_numbers() {\n        assert_eq!(\n            tokenize(\"test123 456-789 hello\"),\n            vec![\"test123\", \"456\", \"789\", \"hello\"]\n        );\n    }\n}\n"
  },
  {
    "path": "crates/mdbook-html/src/html_handlebars/static_files.rs",
    "content": "//! Support for writing static files.\n\nuse super::helpers::resources::ResourceHelper;\nuse crate::theme::{self, Theme, playground_editor};\nuse anyhow::{Context, Result};\nuse mdbook_core::config::HtmlConfig;\nuse mdbook_core::static_regex;\nuse mdbook_core::utils::fs;\nuse std::borrow::Cow;\nuse std::collections::HashMap;\nuse std::path::{Path, PathBuf};\nuse tracing::debug;\n\n/// Map static files to their final names and contents.\n///\n/// It performs [fingerprinting], if you call the `hash_files` method.\n/// If hash-files is turned off, then the files will not be renamed.\n/// It also writes files to their final destination, when `write_files` is called,\n/// and interprets the `{{ resource }}` directives to allow assets to name each other.\n///\n/// [fingerprinting]: https://guides.rubyonrails.org/asset_pipeline.html#fingerprinting-versioning-with-digest-based-urls\npub(super) struct StaticFiles {\n    static_files: Vec<StaticFile>,\n    hash_map: HashMap<String, String>,\n}\n\nenum StaticFile {\n    Builtin {\n        data: Vec<u8>,\n        filename: String,\n    },\n    Additional {\n        input_location: PathBuf,\n        filename: String,\n    },\n}\n\nimpl StaticFiles {\n    pub(super) fn new(theme: &Theme, html_config: &HtmlConfig, root: &Path) -> Result<StaticFiles> {\n        let static_files = Vec::new();\n        let mut this = StaticFiles {\n            hash_map: HashMap::new(),\n            static_files,\n        };\n\n        this.add_builtin(\"book.js\", &theme.js);\n        this.add_builtin(\"css/general.css\", &theme.general_css);\n        this.add_builtin(\"css/chrome.css\", &theme.chrome_css);\n        if html_config.print.enable {\n            this.add_builtin(\"css/print.css\", &theme.print_css);\n        }\n        this.add_builtin(\"css/variables.css\", &theme.variables_css);\n        if let Some(contents) = &theme.favicon_png {\n            this.add_builtin(\"favicon.png\", contents);\n        }\n        if let Some(contents) = &theme.favicon_svg {\n            this.add_builtin(\"favicon.svg\", contents);\n        }\n        this.add_builtin(\"highlight.css\", &theme.highlight_css);\n        this.add_builtin(\"tomorrow-night.css\", &theme.tomorrow_night_css);\n        this.add_builtin(\"ayu-highlight.css\", &theme.ayu_highlight_css);\n        this.add_builtin(\"highlight.js\", &theme.highlight_js);\n        this.add_builtin(\"clipboard.min.js\", &theme.clipboard_js);\n        if theme.fonts_css.is_none() {\n            this.add_builtin(\"fonts/fonts.css\", theme::fonts::CSS);\n            for (file_name, contents) in theme::fonts::LICENSES.iter() {\n                this.add_builtin(file_name, contents);\n            }\n            for (file_name, contents) in theme::fonts::OPEN_SANS.iter() {\n                this.add_builtin(file_name, contents);\n            }\n            this.add_builtin(\n                theme::fonts::SOURCE_CODE_PRO.0,\n                theme::fonts::SOURCE_CODE_PRO.1,\n            );\n        } else if let Some(fonts_css) = &theme.fonts_css {\n            if !fonts_css.is_empty() {\n                this.add_builtin(\"fonts/fonts.css\", fonts_css);\n            }\n        }\n\n        let playground_config = &html_config.playground;\n\n        // Ace is a very large dependency, so only load it when requested\n        if playground_config.editable && playground_config.copy_js {\n            // Load the editor\n            this.add_builtin(\"editor.js\", playground_editor::JS);\n            this.add_builtin(\"ace.js\", playground_editor::ACE_JS);\n            this.add_builtin(\"mode-rust.js\", playground_editor::MODE_RUST_JS);\n            this.add_builtin(\"theme-dawn.js\", playground_editor::THEME_DAWN_JS);\n            this.add_builtin(\n                \"theme-tomorrow_night.js\",\n                playground_editor::THEME_TOMORROW_NIGHT_JS,\n            );\n        }\n\n        let custom_files = html_config\n            .additional_css\n            .iter()\n            .chain(html_config.additional_js.iter());\n\n        for custom_file in custom_files {\n            let input_location = root.join(custom_file);\n\n            this.static_files.push(StaticFile::Additional {\n                input_location,\n                filename: custom_file\n                    .to_str()\n                    .with_context(|| \"resource file names must be valid utf8\")?\n                    .to_owned(),\n            });\n        }\n\n        for input_location in theme.font_files.iter().cloned() {\n            let filename = Path::new(\"fonts\")\n                .join(input_location.file_name().unwrap())\n                .to_str()\n                .with_context(|| \"resource file names must be valid utf8\")?\n                .to_owned();\n            this.static_files.push(StaticFile::Additional {\n                input_location,\n                filename,\n            });\n        }\n\n        Ok(this)\n    }\n\n    pub(super) fn add_builtin(&mut self, filename: &str, data: &[u8]) {\n        self.static_files.push(StaticFile::Builtin {\n            filename: filename.to_owned(),\n            data: data.to_owned(),\n        });\n    }\n\n    /// Updates this [`StaticFiles`] to hash the contents for determining the\n    /// filename for each resource.\n    pub(super) fn hash_files(&mut self) -> Result<()> {\n        use sha2::{Digest, Sha256};\n        use std::io::Read;\n        for static_file in &mut self.static_files {\n            match static_file {\n                &mut StaticFile::Builtin {\n                    ref mut filename,\n                    ref data,\n                } => {\n                    let mut parts = filename.splitn(2, '.');\n                    let parts = parts.next().and_then(|p| Some((p, parts.next()?)));\n                    if let Some((name, suffix)) = parts {\n                        if name != \"\" && suffix != \"\" && suffix != \"txt\" {\n                            let hex = hex::encode(&Sha256::digest(data)[..4]);\n                            let new_filename = format!(\"{}-{}.{}\", name, hex, suffix);\n                            self.hash_map.insert(filename.clone(), new_filename.clone());\n                            *filename = new_filename;\n                        }\n                    }\n                }\n                &mut StaticFile::Additional {\n                    ref mut filename,\n                    ref input_location,\n                } => {\n                    let mut parts = filename.splitn(2, '.');\n                    let parts = parts.next().and_then(|p| Some((p, parts.next()?)));\n                    if let Some((name, suffix)) = parts {\n                        if name != \"\" && suffix != \"\" {\n                            let mut digest = Sha256::new();\n                            let mut input_file =\n                                std::fs::File::open(input_location).with_context(|| {\n                                    format!(\"failed to open `{filename}` for hashing\")\n                                })?;\n                            let mut buf = vec![0; 1024];\n                            loop {\n                                let amt = input_file\n                                    .read(&mut buf)\n                                    .with_context(|| \"read static file for hashing\")?;\n                                if amt == 0 {\n                                    break;\n                                };\n                                digest.update(&buf[..amt]);\n                            }\n                            let hex = hex::encode(&digest.finalize()[..4]);\n                            let new_filename = format!(\"{}-{}.{}\", name, hex, suffix);\n                            self.hash_map.insert(filename.clone(), new_filename.clone());\n                            *filename = new_filename;\n                        }\n                    }\n                }\n            }\n        }\n        Ok(())\n    }\n\n    pub(super) fn write_files(self, destination: &Path) -> Result<ResourceHelper> {\n        use regex::bytes::Captures;\n        // The `{{ resource \"name\" }}` directive in static resources look like\n        // handlebars syntax, even if they technically aren't.\n        static_regex!(RESOURCE, bytes, r#\"\\{\\{ resource \"([^\"]+)\" \\}\\}\"#);\n        fn replace_all<'a>(\n            hash_map: &HashMap<String, String>,\n            data: &'a [u8],\n            filename: &str,\n        ) -> Cow<'a, [u8]> {\n            RESOURCE.replace_all(data, move |captures: &Captures<'_>| {\n                let name = captures\n                    .get(1)\n                    .expect(\"capture 1 in resource regex\")\n                    .as_bytes();\n                let name = std::str::from_utf8(name).expect(\"resource name with invalid utf8\");\n                let resource_filename = hash_map.get(name).map(|s| &s[..]).unwrap_or(name);\n                let path_to_root = fs::path_to_root(filename);\n                format!(\"{}{}\", path_to_root, resource_filename)\n                    .as_bytes()\n                    .to_owned()\n            })\n        }\n        for static_file in &self.static_files {\n            match static_file {\n                StaticFile::Builtin { filename, data } => {\n                    debug!(\"Writing builtin -> {}\", filename);\n                    let data = if filename.ends_with(\".css\") || filename.ends_with(\".js\") {\n                        replace_all(&self.hash_map, data, filename)\n                    } else {\n                        Cow::Borrowed(&data[..])\n                    };\n                    let path = destination.join(filename);\n                    fs::write(path, &data)?;\n                }\n                StaticFile::Additional {\n                    input_location,\n                    filename,\n                } => {\n                    let output_location = destination.join(filename);\n                    debug!(\n                        \"Copying {} -> {}\",\n                        input_location.display(),\n                        output_location.display()\n                    );\n                    if let Some(parent) = output_location.parent() {\n                        fs::create_dir_all(parent)\n                            .with_context(|| format!(\"Unable to create {}\", parent.display()))?;\n                    }\n                    if filename.ends_with(\".css\") || filename.ends_with(\".js\") {\n                        let data = fs::read_to_string(input_location)?;\n                        let data = replace_all(&self.hash_map, data.as_bytes(), filename);\n                        let path = destination.join(filename);\n                        fs::write(path, &data)?;\n                    } else {\n                        std::fs::copy(input_location, &output_location).with_context(|| {\n                            format!(\n                                \"Unable to copy {} to {}\",\n                                input_location.display(),\n                                output_location.display()\n                            )\n                        })?;\n                    }\n                }\n            }\n        }\n        let hash_map = self.hash_map;\n        Ok(ResourceHelper { hash_map })\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use crate::theme::Theme;\n    use mdbook_core::config::HtmlConfig;\n    use mdbook_core::utils::fs;\n    use tempfile::TempDir;\n\n    #[test]\n    fn test_write_directive() {\n        let theme = Theme {\n            index: Vec::new(),\n            head: Vec::new(),\n            redirect: Vec::new(),\n            header: Vec::new(),\n            chrome_css: Vec::new(),\n            general_css: Vec::new(),\n            print_css: Vec::new(),\n            variables_css: Vec::new(),\n            favicon_png: Some(Vec::new()),\n            favicon_svg: Some(Vec::new()),\n            js: Vec::new(),\n            highlight_css: Vec::new(),\n            tomorrow_night_css: Vec::new(),\n            ayu_highlight_css: Vec::new(),\n            highlight_js: Vec::new(),\n            clipboard_js: Vec::new(),\n            toc_js: Vec::new(),\n            toc_html: Vec::new(),\n            fonts_css: None,\n            font_files: Vec::new(),\n        };\n        let temp_dir = TempDir::with_prefix(\"mdbook-\").unwrap();\n        let reference_js = Path::new(\"static-files-test-case-reference.js\");\n        let mut html_config = HtmlConfig::default();\n        html_config.additional_js.push(reference_js.to_owned());\n        fs::write(\n            temp_dir.path().join(reference_js),\n            br#\"{{ resource \"book.js\" }}\"#,\n        )\n        .unwrap();\n        let mut static_files = StaticFiles::new(&theme, &html_config, temp_dir.path()).unwrap();\n        static_files.hash_files().unwrap();\n        static_files.write_files(temp_dir.path()).unwrap();\n        // custom JS winds up referencing book.js\n        let reference_js_content = fs::read_to_string(\n            temp_dir\n                .path()\n                .join(\"static-files-test-case-reference-635c9cdc.js\"),\n        )\n        .unwrap();\n        assert_eq!(\"book-e3b0c442.js\", reference_js_content);\n        // book.js winds up empty\n        let book_js_content = fs::read_to_string(temp_dir.path().join(\"book-e3b0c442.js\")).unwrap();\n        assert_eq!(\"\", book_js_content);\n    }\n}\n"
  },
  {
    "path": "crates/mdbook-html/src/lib.rs",
    "content": "//! mdBook HTML renderer.\n\nmod html;\nmod html_handlebars;\npub mod theme;\npub(crate) mod utils;\n\npub use html_handlebars::HtmlHandlebars;\n"
  },
  {
    "path": "crates/mdbook-html/src/theme/fonts.rs",
    "content": "pub(crate) static CSS: &[u8] = include_bytes!(\"../../front-end/fonts/fonts.css\");\n// An array of (file_name, file_contents) pairs\npub(crate) static LICENSES: [(&str, &[u8]); 2] = [\n    (\n        \"fonts/OPEN-SANS-LICENSE.txt\",\n        include_bytes!(\"../../front-end/fonts/OPEN-SANS-LICENSE.txt\"),\n    ),\n    (\n        \"fonts/SOURCE-CODE-PRO-LICENSE.txt\",\n        include_bytes!(\"../../front-end/fonts/SOURCE-CODE-PRO-LICENSE.txt\"),\n    ),\n];\n// An array of (file_name, file_contents) pairs\npub(crate) static OPEN_SANS: [(&str, &[u8]); 10] = [\n    (\n        \"fonts/open-sans-v17-all-charsets-300.woff2\",\n        include_bytes!(\"../../front-end/fonts/open-sans-v17-all-charsets-300.woff2\"),\n    ),\n    (\n        \"fonts/open-sans-v17-all-charsets-300italic.woff2\",\n        include_bytes!(\"../../front-end/fonts/open-sans-v17-all-charsets-300italic.woff2\"),\n    ),\n    (\n        \"fonts/open-sans-v17-all-charsets-regular.woff2\",\n        include_bytes!(\"../../front-end/fonts/open-sans-v17-all-charsets-regular.woff2\"),\n    ),\n    (\n        \"fonts/open-sans-v17-all-charsets-italic.woff2\",\n        include_bytes!(\"../../front-end/fonts/open-sans-v17-all-charsets-italic.woff2\"),\n    ),\n    (\n        \"fonts/open-sans-v17-all-charsets-600.woff2\",\n        include_bytes!(\"../../front-end/fonts/open-sans-v17-all-charsets-600.woff2\"),\n    ),\n    (\n        \"fonts/open-sans-v17-all-charsets-600italic.woff2\",\n        include_bytes!(\"../../front-end/fonts/open-sans-v17-all-charsets-600italic.woff2\"),\n    ),\n    (\n        \"fonts/open-sans-v17-all-charsets-700.woff2\",\n        include_bytes!(\"../../front-end/fonts/open-sans-v17-all-charsets-700.woff2\"),\n    ),\n    (\n        \"fonts/open-sans-v17-all-charsets-700italic.woff2\",\n        include_bytes!(\"../../front-end/fonts/open-sans-v17-all-charsets-700italic.woff2\"),\n    ),\n    (\n        \"fonts/open-sans-v17-all-charsets-800.woff2\",\n        include_bytes!(\"../../front-end/fonts/open-sans-v17-all-charsets-800.woff2\"),\n    ),\n    (\n        \"fonts/open-sans-v17-all-charsets-800italic.woff2\",\n        include_bytes!(\"../../front-end/fonts/open-sans-v17-all-charsets-800italic.woff2\"),\n    ),\n];\n\n// A (file_name, file_contents) pair\npub(crate) static SOURCE_CODE_PRO: (&str, &[u8]) = (\n    \"fonts/source-code-pro-v11-all-charsets-500.woff2\",\n    include_bytes!(\"../../front-end/fonts/source-code-pro-v11-all-charsets-500.woff2\"),\n);\n"
  },
  {
    "path": "crates/mdbook-html/src/theme/mod.rs",
    "content": "//! Support for theme files.\n\nuse anyhow::Result;\nuse mdbook_core::config::HtmlConfig;\nuse mdbook_core::utils::fs;\nuse std::path::{Path, PathBuf};\nuse tracing::{info, warn};\n\npub(crate) mod fonts;\npub(crate) mod playground_editor;\n#[cfg(feature = \"search\")]\npub(crate) mod searcher;\n\nstatic INDEX: &[u8] = include_bytes!(\"../../front-end/templates/index.hbs\");\nstatic HEAD: &[u8] = include_bytes!(\"../../front-end/templates/head.hbs\");\nstatic REDIRECT: &[u8] = include_bytes!(\"../../front-end/templates/redirect.hbs\");\nstatic HEADER: &[u8] = include_bytes!(\"../../front-end/templates/header.hbs\");\nstatic TOC_JS: &[u8] = include_bytes!(\"../../front-end/templates/toc.js.hbs\");\nstatic TOC_HTML: &[u8] = include_bytes!(\"../../front-end/templates/toc.html.hbs\");\nstatic CHROME_CSS: &[u8] = include_bytes!(\"../../front-end/css/chrome.css\");\nstatic GENERAL_CSS: &[u8] = include_bytes!(\"../../front-end/css/general.css\");\nstatic PRINT_CSS: &[u8] = include_bytes!(\"../../front-end/css/print.css\");\nstatic VARIABLES_CSS: &[u8] = include_bytes!(\"../../front-end/css/variables.css\");\nstatic FAVICON_PNG: &[u8] = include_bytes!(\"../../front-end/images/favicon.png\");\nstatic FAVICON_SVG: &[u8] = include_bytes!(\"../../front-end/images/favicon.svg\");\nstatic JS: &[u8] = include_bytes!(\"../../front-end/js/book.js\");\nstatic HIGHLIGHT_JS: &[u8] = include_bytes!(\"../../front-end/js/highlight.js\");\nstatic TOMORROW_NIGHT_CSS: &[u8] = include_bytes!(\"../../front-end/css/tomorrow-night.css\");\nstatic HIGHLIGHT_CSS: &[u8] = include_bytes!(\"../../front-end/css/highlight.css\");\nstatic AYU_HIGHLIGHT_CSS: &[u8] = include_bytes!(\"../../front-end/css/ayu-highlight.css\");\nstatic CLIPBOARD_JS: &[u8] = include_bytes!(\"../../front-end/js/clipboard.min.js\");\n\n/// The `Theme` struct should be used instead of the static variables because\n/// the `new()` method will look if the user has a theme directory in their\n/// source folder and use the users theme instead of the default.\n///\n/// You should only ever use the static variables directly if you want to\n/// override the user's theme with the defaults.\n#[derive(Debug, PartialEq)]\npub struct Theme {\n    pub(crate) index: Vec<u8>,\n    pub(crate) head: Vec<u8>,\n    pub(crate) redirect: Vec<u8>,\n    pub(crate) header: Vec<u8>,\n    pub(crate) toc_js: Vec<u8>,\n    pub(crate) toc_html: Vec<u8>,\n    pub(crate) chrome_css: Vec<u8>,\n    pub(crate) general_css: Vec<u8>,\n    pub(crate) print_css: Vec<u8>,\n    pub(crate) variables_css: Vec<u8>,\n    pub(crate) fonts_css: Option<Vec<u8>>,\n    pub(crate) font_files: Vec<PathBuf>,\n    pub(crate) favicon_png: Option<Vec<u8>>,\n    pub(crate) favicon_svg: Option<Vec<u8>>,\n    pub(crate) js: Vec<u8>,\n    pub(crate) highlight_css: Vec<u8>,\n    pub(crate) tomorrow_night_css: Vec<u8>,\n    pub(crate) ayu_highlight_css: Vec<u8>,\n    pub(crate) highlight_js: Vec<u8>,\n    pub(crate) clipboard_js: Vec<u8>,\n}\n\nimpl Theme {\n    /// Creates a `Theme` from the given `theme_dir`.\n    /// If a file is found in the theme dir, it will override the default version.\n    pub fn new<P: AsRef<Path>>(theme_dir: P) -> Self {\n        let theme_dir = theme_dir.as_ref();\n        let mut theme = Theme::default();\n\n        // If the theme directory doesn't exist there's no point continuing...\n        if !theme_dir.exists() || !theme_dir.is_dir() {\n            return theme;\n        }\n\n        // Check for individual files, if they exist copy them across\n        {\n            let files = vec![\n                (theme_dir.join(\"index.hbs\"), &mut theme.index),\n                (theme_dir.join(\"head.hbs\"), &mut theme.head),\n                (theme_dir.join(\"redirect.hbs\"), &mut theme.redirect),\n                (theme_dir.join(\"header.hbs\"), &mut theme.header),\n                (theme_dir.join(\"toc.js.hbs\"), &mut theme.toc_js),\n                (theme_dir.join(\"toc.html.hbs\"), &mut theme.toc_html),\n                (theme_dir.join(\"book.js\"), &mut theme.js),\n                (theme_dir.join(\"css/chrome.css\"), &mut theme.chrome_css),\n                (theme_dir.join(\"css/general.css\"), &mut theme.general_css),\n                (theme_dir.join(\"css/print.css\"), &mut theme.print_css),\n                (\n                    theme_dir.join(\"css/variables.css\"),\n                    &mut theme.variables_css,\n                ),\n                (theme_dir.join(\"highlight.js\"), &mut theme.highlight_js),\n                (theme_dir.join(\"clipboard.min.js\"), &mut theme.clipboard_js),\n                (theme_dir.join(\"highlight.css\"), &mut theme.highlight_css),\n                (\n                    theme_dir.join(\"tomorrow-night.css\"),\n                    &mut theme.tomorrow_night_css,\n                ),\n                (\n                    theme_dir.join(\"ayu-highlight.css\"),\n                    &mut theme.ayu_highlight_css,\n                ),\n            ];\n\n            let load_with_warn = |filename: &Path, dest: &mut Vec<u8>| {\n                if !filename.exists() {\n                    // Don't warn if the file doesn't exist.\n                    return false;\n                }\n                if let Err(e) = load_file_contents(filename, dest) {\n                    warn!(\"Couldn't load custom file, {}: {}\", filename.display(), e);\n                    false\n                } else {\n                    true\n                }\n            };\n\n            for (filename, dest) in files {\n                load_with_warn(&filename, dest);\n            }\n\n            let fonts_dir = theme_dir.join(\"fonts\");\n            if fonts_dir.exists() {\n                let mut fonts_css = Vec::new();\n                if load_with_warn(&fonts_dir.join(\"fonts.css\"), &mut fonts_css) {\n                    theme.fonts_css.replace(fonts_css);\n                }\n                if let Ok(entries) = fonts_dir.read_dir() {\n                    theme.font_files = entries\n                        .filter_map(|entry| {\n                            let entry = entry.ok()?;\n                            if entry.file_name() == \"fonts.css\" {\n                                None\n                            } else if entry.file_type().ok()?.is_dir() {\n                                info!(\"skipping font directory {:?}\", entry.path());\n                                None\n                            } else {\n                                Some(entry.path())\n                            }\n                        })\n                        .collect();\n                }\n            }\n\n            // If the user overrides one favicon, but not the other, do not\n            // copy the default for the other.\n            let favicon_png = &mut theme.favicon_png.as_mut().unwrap();\n            let png = load_with_warn(&theme_dir.join(\"favicon.png\"), favicon_png);\n            let favicon_svg = &mut theme.favicon_svg.as_mut().unwrap();\n            let svg = load_with_warn(&theme_dir.join(\"favicon.svg\"), favicon_svg);\n            match (png, svg) {\n                (true, true) | (false, false) => {}\n                (true, false) => {\n                    theme.favicon_svg = None;\n                }\n                (false, true) => {\n                    theme.favicon_png = None;\n                }\n            }\n        }\n\n        theme\n    }\n\n    /// Copies the default theme files to the theme directory.\n    pub fn copy_theme(html_config: &HtmlConfig, root: &Path) -> Result<()> {\n        let themedir = html_config.theme_dir(root);\n\n        fs::write(themedir.join(\"book.js\"), JS)?;\n        fs::write(themedir.join(\"favicon.png\"), FAVICON_PNG)?;\n        fs::write(themedir.join(\"favicon.svg\"), FAVICON_SVG)?;\n        fs::write(themedir.join(\"highlight.css\"), HIGHLIGHT_CSS)?;\n        fs::write(themedir.join(\"highlight.js\"), HIGHLIGHT_JS)?;\n        fs::write(themedir.join(\"index.hbs\"), INDEX)?;\n\n        let cssdir = themedir.join(\"css\");\n\n        fs::write(cssdir.join(\"general.css\"), GENERAL_CSS)?;\n        fs::write(cssdir.join(\"chrome.css\"), CHROME_CSS)?;\n        fs::write(cssdir.join(\"variables.css\"), VARIABLES_CSS)?;\n        if html_config.print.enable {\n            fs::write(cssdir.join(\"print.css\"), PRINT_CSS)?;\n        }\n\n        fs::write(themedir.join(\"fonts\").join(\"fonts.css\"), fonts::CSS)?;\n        for (file_name, contents) in fonts::LICENSES {\n            fs::write(themedir.join(file_name), contents)?;\n        }\n        for (file_name, contents) in fonts::OPEN_SANS.iter() {\n            fs::write(themedir.join(file_name), contents)?;\n        }\n        fs::write(\n            themedir.join(fonts::SOURCE_CODE_PRO.0),\n            fonts::SOURCE_CODE_PRO.1,\n        )?;\n        Ok(())\n    }\n}\n\nimpl Default for Theme {\n    fn default() -> Theme {\n        Theme {\n            index: INDEX.to_owned(),\n            head: HEAD.to_owned(),\n            redirect: REDIRECT.to_owned(),\n            header: HEADER.to_owned(),\n            toc_js: TOC_JS.to_owned(),\n            toc_html: TOC_HTML.to_owned(),\n            chrome_css: CHROME_CSS.to_owned(),\n            general_css: GENERAL_CSS.to_owned(),\n            print_css: PRINT_CSS.to_owned(),\n            variables_css: VARIABLES_CSS.to_owned(),\n            fonts_css: None,\n            font_files: Vec::new(),\n            favicon_png: Some(FAVICON_PNG.to_owned()),\n            favicon_svg: Some(FAVICON_SVG.to_owned()),\n            js: JS.to_owned(),\n            highlight_css: HIGHLIGHT_CSS.to_owned(),\n            tomorrow_night_css: TOMORROW_NIGHT_CSS.to_owned(),\n            ayu_highlight_css: AYU_HIGHLIGHT_CSS.to_owned(),\n            highlight_js: HIGHLIGHT_JS.to_owned(),\n            clipboard_js: CLIPBOARD_JS.to_owned(),\n        }\n    }\n}\n\n/// Checks if a file exists, if so, the destination buffer will be filled with\n/// its contents.\nfn load_file_contents<P: AsRef<Path>>(filename: P, dest: &mut Vec<u8>) -> Result<()> {\n    let filename = filename.as_ref();\n    let mut buffer = std::fs::read(filename)?;\n\n    // We needed the buffer so we'd only overwrite the existing content if we\n    // could successfully load the file into memory.\n    dest.clear();\n    dest.append(&mut buffer);\n\n    Ok(())\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use std::fs;\n    use tempfile::Builder as TempFileBuilder;\n\n    #[test]\n    fn theme_uses_defaults_with_nonexistent_src_dir() {\n        let non_existent = PathBuf::from(\"/non/existent/directory/\");\n        assert!(!non_existent.exists());\n\n        let should_be = Theme::default();\n        let got = Theme::new(&non_existent);\n\n        assert_eq!(got, should_be);\n    }\n\n    #[test]\n    fn theme_dir_overrides_defaults() {\n        let files = [\n            \"index.hbs\",\n            \"head.hbs\",\n            \"redirect.hbs\",\n            \"header.hbs\",\n            \"toc.js.hbs\",\n            \"toc.html.hbs\",\n            \"favicon.png\",\n            \"favicon.svg\",\n            \"css/chrome.css\",\n            \"css/general.css\",\n            \"css/print.css\",\n            \"css/variables.css\",\n            \"fonts/fonts.css\",\n            \"book.js\",\n            \"highlight.js\",\n            \"tomorrow-night.css\",\n            \"highlight.css\",\n            \"ayu-highlight.css\",\n            \"clipboard.min.js\",\n        ];\n\n        let temp = TempFileBuilder::new().prefix(\"mdbook-\").tempdir().unwrap();\n        fs::create_dir(temp.path().join(\"css\")).unwrap();\n        fs::create_dir(temp.path().join(\"fonts\")).unwrap();\n\n        // \"touch\" all of the special files so we have empty copies\n        for file in &files {\n            fs::File::create(&temp.path().join(file)).unwrap();\n        }\n\n        let got = Theme::new(temp.path());\n\n        let empty = Theme {\n            index: Vec::new(),\n            head: Vec::new(),\n            redirect: Vec::new(),\n            header: Vec::new(),\n            toc_js: Vec::new(),\n            toc_html: Vec::new(),\n            chrome_css: Vec::new(),\n            general_css: Vec::new(),\n            print_css: Vec::new(),\n            variables_css: Vec::new(),\n            fonts_css: Some(Vec::new()),\n            font_files: Vec::new(),\n            favicon_png: Some(Vec::new()),\n            favicon_svg: Some(Vec::new()),\n            js: Vec::new(),\n            highlight_css: Vec::new(),\n            tomorrow_night_css: Vec::new(),\n            ayu_highlight_css: Vec::new(),\n            highlight_js: Vec::new(),\n            clipboard_js: Vec::new(),\n        };\n\n        assert_eq!(got, empty);\n    }\n\n    #[test]\n    fn favicon_override() {\n        let temp = TempFileBuilder::new().prefix(\"mdbook-\").tempdir().unwrap();\n        fs::write(temp.path().join(\"favicon.png\"), \"1234\").unwrap();\n        let got = Theme::new(temp.path());\n        assert_eq!(got.favicon_png.as_ref().unwrap(), b\"1234\");\n        assert_eq!(got.favicon_svg, None);\n\n        let temp = TempFileBuilder::new().prefix(\"mdbook-\").tempdir().unwrap();\n        fs::write(temp.path().join(\"favicon.svg\"), \"4567\").unwrap();\n        let got = Theme::new(temp.path());\n        assert_eq!(got.favicon_png, None);\n        assert_eq!(got.favicon_svg.as_ref().unwrap(), b\"4567\");\n    }\n}\n"
  },
  {
    "path": "crates/mdbook-html/src/theme/playground_editor.rs",
    "content": "//! Theme dependencies for the playground editor.\n\npub(crate) static JS: &[u8] = include_bytes!(\"../../front-end/playground_editor/editor.js\");\npub(crate) static ACE_JS: &[u8] = include_bytes!(\"../../front-end/playground_editor/ace.js\");\npub(crate) static MODE_RUST_JS: &[u8] =\n    include_bytes!(\"../../front-end/playground_editor/mode-rust.js\");\npub(crate) static THEME_DAWN_JS: &[u8] =\n    include_bytes!(\"../../front-end/playground_editor/theme-dawn.js\");\npub(crate) static THEME_TOMORROW_NIGHT_JS: &[u8] =\n    include_bytes!(\"../../front-end/playground_editor/theme-tomorrow_night.js\");\n"
  },
  {
    "path": "crates/mdbook-html/src/theme/searcher.rs",
    "content": "//! Theme dependencies for in-browser search. Not included in mdbook when\n//! the \"search\" cargo feature is disabled.\n\npub(crate) static JS: &[u8] = include_bytes!(\"../../front-end/searcher/searcher.js\");\npub(crate) static MARK_JS: &[u8] = include_bytes!(\"../../front-end/searcher/mark.min.js\");\npub(crate) static ELASTICLUNR_JS: &[u8] =\n    include_bytes!(\"../../front-end/searcher/elasticlunr.min.js\");\n"
  },
  {
    "path": "crates/mdbook-html/src/utils.rs",
    "content": "//! Utilities for processing HTML.\n\nuse std::collections::HashSet;\nuse std::path::{Component, Path, PathBuf};\n\n/// Utility function to normalize path elements like `..`.\npub(crate) fn normalize_path(path: &Path) -> PathBuf {\n    let mut components = path.components().peekable();\n    let mut ret = if let Some(c @ Component::Prefix(..)) = components.peek().cloned() {\n        components.next();\n        PathBuf::from(c.as_os_str())\n    } else {\n        PathBuf::new()\n    };\n\n    for component in components {\n        match component {\n            Component::Prefix(..) => unreachable!(),\n            Component::RootDir => {\n                ret.push(Component::RootDir);\n            }\n            Component::CurDir => {}\n            Component::ParentDir => {\n                if ret.ends_with(Component::ParentDir) {\n                    ret.push(Component::ParentDir);\n                } else {\n                    let popped = ret.pop();\n                    if !popped && !ret.has_root() {\n                        ret.push(Component::ParentDir);\n                    }\n                }\n            }\n            Component::Normal(c) => {\n                ret.push(c);\n            }\n        }\n    }\n    ret\n}\n\n/// Helper trait for converting a [`Path`] to a string suitable for an HTML path.\npub(crate) trait ToUrlPath {\n    fn to_url_path(&self) -> String;\n}\n\nimpl ToUrlPath for Path {\n    fn to_url_path(&self) -> String {\n        // We're generally assuming that all paths we deal with are utf-8.\n        // The replace here is to handle Windows paths.\n        self.to_str().unwrap().replace('\\\\', \"/\")\n    }\n}\n\n/// Make sure an HTML id is unique.\n///\n/// Keeps a set of all previously returned IDs; if the requested id is already\n/// used, numeric suffixes (-1, -2, ...) are tried until an unused one is found.\npub(crate) fn unique_id(id: &str, used: &mut HashSet<String>) -> String {\n    if used.insert(id.to_string()) {\n        return id.to_string();\n    }\n\n    // This ID is already in use. Generate one that is not by appending a\n    // numeric suffix.\n    let mut counter: u32 = 1;\n    loop {\n        let candidate = format!(\"{id}-{counter}\");\n        if used.insert(candidate.clone()) {\n            return candidate;\n        }\n        counter += 1;\n    }\n}\n\n/// Generates an HTML id from the given text.\npub(crate) fn id_from_content(content: &str) -> String {\n    // This is intended to be close to how header ID generation is done in\n    // other sites and tools, but is not 100% the same. Not all sites and\n    // tools use the same algorithm. See these for more information:\n    //\n    // - https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax#section-links\n    // - https://docs.gitlab.com/user/markdown/#heading-ids-and-links\n    // - https://pandoc.org/MANUAL.html#extension-auto_identifiers\n    // - https://kramdown.gettalong.org/converter/html#auto-ids\n    // - https://docs.rs/comrak/latest/comrak/options/struct.Extension.html#structfield.header_ids\n    content\n        .trim()\n        .to_lowercase()\n        .chars()\n        .filter_map(|ch| {\n            if ch.is_alphanumeric() || ch == '_' || ch == '-' {\n                Some(ch)\n            } else if ch.is_whitespace() {\n                Some('-')\n            } else {\n                None\n            }\n        })\n        .collect()\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn it_generates_unique_ids() {\n        let mut id_counter = Default::default();\n\n        assert_eq!(unique_id(\"\", &mut id_counter), \"\");\n        assert_eq!(unique_id(\"Über\", &mut id_counter), \"Über\");\n        assert_eq!(unique_id(\"Über\", &mut id_counter), \"Über-1\");\n        assert_eq!(unique_id(\"Über\", &mut id_counter), \"Über-2\");\n    }\n\n    #[test]\n    fn it_normalizes_ids() {\n        assert_eq!(\n            id_from_content(\"`--passes`: add more rustdoc passes\"),\n            \"--passes-add-more-rustdoc-passes\"\n        );\n        assert_eq!(\n            id_from_content(\"Method-call 🐙 expressions \\u{1f47c}\"),\n            \"method-call--expressions-\"\n        );\n        assert_eq!(id_from_content(\"_-_12345\"), \"_-_12345\");\n        assert_eq!(id_from_content(\"12345\"), \"12345\");\n        assert_eq!(id_from_content(\"中文\"), \"中文\");\n        assert_eq!(id_from_content(\"にほんご\"), \"にほんご\");\n        assert_eq!(id_from_content(\"한국어\"), \"한국어\");\n        assert_eq!(id_from_content(\"\"), \"\");\n        assert_eq!(id_from_content(\"中文標題 CJK title\"), \"中文標題-cjk-title\");\n        assert_eq!(id_from_content(\"Über\"), \"über\");\n    }\n}\n"
  },
  {
    "path": "crates/mdbook-markdown/Cargo.toml",
    "content": "[package]\nname = \"mdbook-markdown\"\nversion = \"0.5.2\"\ndescription = \"Markdown processing used in mdBook\"\nedition.workspace = true\nlicense.workspace = true\nrepository.workspace = true\nrust-version.workspace = true\n\n[dependencies]\npulldown-cmark.workspace = true\nregex.workspace = true\ntracing.workspace = true\n\n[lints]\nworkspace = true\n"
  },
  {
    "path": "crates/mdbook-markdown/README.md",
    "content": "# mdbook-markdown\n\n[![Documentation](https://img.shields.io/docsrs/mdbook-markdown)](https://docs.rs/mdbook-markdown)\n[![crates.io](https://img.shields.io/crates/v/mdbook-markdown.svg)](https://crates.io/crates/mdbook-markdown)\n[![Changelog](https://img.shields.io/badge/CHANGELOG-Latest-green)](https://github.com/rust-lang/mdBook/blob/master/CHANGELOG.md)\n\nThis is the Markdown support library for [mdBook](https://rust-lang.github.io/mdBook/). Rust crates (such as preprocessors) can use this library to process Markdown in the same way as mdBook.\n\n> This crate is maintained by the mdBook team for use by the wider ecosystem. This crate follows [semver compatibility](https://doc.rust-lang.org/cargo/reference/semver.html) for its APIs.\n\n## License\n\n[Mozilla Public License, version 2.0](https://github.com/rust-lang/mdBook/blob/master/LICENSE)\n"
  },
  {
    "path": "crates/mdbook-markdown/src/lib.rs",
    "content": "//! Markdown processing used in mdBook.\n//!\n//! This crate provides functions for processing Markdown in the same way as\n//! [mdBook](https://rust-lang.github.io/mdBook/). The [`pulldown_cmark`]\n//! crate is used as the underlying parser. This crate re-exports\n//! [`pulldown_cmark`] so that you can access its types.\n\nuse pulldown_cmark::{Options, Parser};\n\n#[doc(inline)]\npub use pulldown_cmark;\n\n/// Options for parsing markdown.\n#[non_exhaustive]\npub struct MarkdownOptions {\n    /// Enables smart punctuation.\n    ///\n    /// Converts quotes to curly quotes, `...` to `…`, `--` to en-dash, and\n    /// `---` to em-dash.\n    ///\n    /// This is `true` by default.\n    pub smart_punctuation: bool,\n    /// Enables definition lists.\n    ///\n    /// This is `true` by default.\n    pub definition_lists: bool,\n    /// Enables admonitions.\n    ///\n    /// This is `true` by default.\n    pub admonitions: bool,\n}\n\nimpl Default for MarkdownOptions {\n    fn default() -> MarkdownOptions {\n        MarkdownOptions {\n            smart_punctuation: true,\n            definition_lists: true,\n            admonitions: true,\n        }\n    }\n}\n\n/// Creates a new pulldown-cmark parser of the given text.\npub fn new_cmark_parser<'text>(text: &'text str, options: &MarkdownOptions) -> Parser<'text> {\n    let mut opts = Options::empty();\n    opts.insert(Options::ENABLE_TABLES);\n    opts.insert(Options::ENABLE_FOOTNOTES);\n    opts.insert(Options::ENABLE_STRIKETHROUGH);\n    opts.insert(Options::ENABLE_TASKLISTS);\n    opts.insert(Options::ENABLE_HEADING_ATTRIBUTES);\n    if options.smart_punctuation {\n        opts.insert(Options::ENABLE_SMART_PUNCTUATION);\n    }\n    if options.definition_lists {\n        opts.insert(Options::ENABLE_DEFINITION_LIST);\n    }\n    if options.admonitions {\n        opts.insert(Options::ENABLE_GFM);\n    }\n    Parser::new_ext(text, opts)\n}\n"
  },
  {
    "path": "crates/mdbook-preprocessor/Cargo.toml",
    "content": "[package]\nname = \"mdbook-preprocessor\"\nversion = \"0.5.2\"\ndescription = \"Library to assist implementing an mdBook preprocessor\"\nedition.workspace = true\nlicense.workspace = true\nrepository.workspace = true\nrust-version.workspace = true\n\n[dependencies]\nanyhow.workspace = true\nmdbook-core.workspace = true\nserde.workspace = true\nserde_json.workspace = true\n\n[lints]\nworkspace = true\n"
  },
  {
    "path": "crates/mdbook-preprocessor/README.md",
    "content": "# mdbook-preprocessor\n\n[![Documentation](https://img.shields.io/docsrs/mdbook-preprocessor)](https://docs.rs/mdbook-preprocessor)\n[![crates.io](https://img.shields.io/crates/v/mdbook-preprocessor.svg)](https://crates.io/crates/mdbook-preprocessor)\n[![Changelog](https://img.shields.io/badge/CHANGELOG-Latest-green)](https://github.com/rust-lang/mdBook/blob/master/CHANGELOG.md)\n\nThis is the Rust library to implement a [preprocessor](https://rust-lang.github.io/mdBook/for_developers/preprocessors.html) for [mdBook](https://rust-lang.github.io/mdBook/).\n\n> This crate is maintained by the mdBook team for use by the wider ecosystem. This crate follows [semver compatibility](https://doc.rust-lang.org/cargo/reference/semver.html) for its APIs.\n\n## License\n\n[Mozilla Public License, version 2.0](https://github.com/rust-lang/mdBook/blob/master/LICENSE)\n"
  },
  {
    "path": "crates/mdbook-preprocessor/src/lib.rs",
    "content": "//! Library to assist implementing an mdbook preprocessor.\n//!\n//! This library is used to implement a\n//! [preprocessor](https://rust-lang.github.io/mdBook/for_developers/preprocessors.html)\n//! for [mdBook](https://rust-lang.github.io/mdBook/). See the linked chapter\n//! for more information on how to implement a preprocessor.\n\nuse anyhow::Context;\nuse mdbook_core::book::Book;\nuse mdbook_core::config::Config;\nuse mdbook_core::errors::Result;\nuse serde::{Deserialize, Serialize};\nuse std::cell::RefCell;\nuse std::collections::HashMap;\nuse std::io::Read;\nuse std::path::PathBuf;\n\npub use mdbook_core::MDBOOK_VERSION;\npub use mdbook_core::book;\npub use mdbook_core::config;\npub use mdbook_core::errors;\n\n/// An operation which is run immediately after loading a book into memory and\n/// before it gets rendered.\n///\n/// Types that implement the `Preprocessor` trait can be used with\n/// [`MDBook::with_preprocessor`] to programmatically add preprocessors.\n///\n/// [`MDBook::with_preprocessor`]: https://docs.rs/mdbook-driver/latest/mdbook_driver/struct.MDBook.html#method.with_preprocessor\npub trait Preprocessor {\n    /// Get the `Preprocessor`'s name.\n    fn name(&self) -> &str;\n\n    /// Run this `Preprocessor`, allowing it to update the book before it is\n    /// given to a renderer.\n    fn run(&self, ctx: &PreprocessorContext, book: Book) -> Result<Book>;\n\n    /// A hint to `MDBook` whether this preprocessor is compatible with a\n    /// particular renderer.\n    ///\n    /// By default, always returns `true`.\n    fn supports_renderer(&self, _renderer: &str) -> Result<bool> {\n        Ok(true)\n    }\n}\n\n/// Extra information for a `Preprocessor` to give them more context when\n/// processing a book.\n#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]\n#[non_exhaustive]\npub struct PreprocessorContext {\n    /// The location of the book directory on disk.\n    pub root: PathBuf,\n    /// The book configuration (`book.toml`).\n    pub config: Config,\n    /// The `Renderer` this preprocessor is being used with.\n    pub renderer: String,\n    /// The calling `mdbook` version.\n    pub mdbook_version: String,\n    /// Internal mapping of chapter titles.\n    ///\n    /// This is used internally by mdbook to compute custom chapter titles.\n    /// This should not be used outside of mdbook's internals.\n    #[serde(skip)]\n    pub chapter_titles: RefCell<HashMap<PathBuf, String>>,\n}\n\nimpl PreprocessorContext {\n    /// Create a new `PreprocessorContext`.\n    pub fn new(root: PathBuf, config: Config, renderer: String) -> Self {\n        PreprocessorContext {\n            root,\n            config,\n            renderer,\n            mdbook_version: crate::MDBOOK_VERSION.to_string(),\n            chapter_titles: RefCell::new(HashMap::new()),\n        }\n    }\n}\n\n/// Parses the input given to a preprocessor.\npub fn parse_input<R: Read>(reader: R) -> Result<(PreprocessorContext, Book)> {\n    serde_json::from_reader(reader).with_context(|| \"Unable to parse the input\")\n}\n"
  },
  {
    "path": "crates/mdbook-renderer/Cargo.toml",
    "content": "[package]\nname = \"mdbook-renderer\"\nversion = \"0.5.2\"\ndescription = \"Library to assist implementing an mdBook renderer\"\nedition.workspace = true\nlicense.workspace = true\nrepository.workspace = true\nrust-version.workspace = true\n\n[dependencies]\nanyhow.workspace = true\nmdbook-core.workspace = true\nserde.workspace = true\nserde_json.workspace = true\n\n[lints]\nworkspace = true\n"
  },
  {
    "path": "crates/mdbook-renderer/README.md",
    "content": "# mdbook-renderer\n\n[![Documentation](https://img.shields.io/docsrs/mdbook-renderer)](https://docs.rs/mdbook-renderer)\n[![crates.io](https://img.shields.io/crates/v/mdbook-renderer.svg)](https://crates.io/crates/mdbook-renderer)\n[![Changelog](https://img.shields.io/badge/CHANGELOG-Latest-green)](https://github.com/rust-lang/mdBook/blob/master/CHANGELOG.md)\n\nThis is the Rust library to implement a [renderer](https://rust-lang.github.io/mdBook/for_developers/backends.html) for [mdBook](https://rust-lang.github.io/mdBook/).\n\n> This crate is maintained by the mdBook team for use by the wider ecosystem. This crate follows [semver compatibility](https://doc.rust-lang.org/cargo/reference/semver.html) for its APIs.\n\n## License\n\n[Mozilla Public License, version 2.0](https://github.com/rust-lang/mdBook/blob/master/LICENSE)\n"
  },
  {
    "path": "crates/mdbook-renderer/src/lib.rs",
    "content": "//! Library to assist implementing an mdbook renderer.\n//!\n//! This library is used to implement a\n//! [renderer](https://rust-lang.github.io/mdBook/for_developers/backends.html)\n//! for [mdBook](https://rust-lang.github.io/mdBook/). See the linked chapter\n//! for more information on how to implement a renderer.\n\nuse anyhow::Context;\nuse mdbook_core::book::Book;\nuse mdbook_core::config::Config;\nuse mdbook_core::errors::Result;\nuse serde::{Deserialize, Serialize};\nuse std::collections::HashMap;\nuse std::io::Read;\nuse std::path::PathBuf;\n\npub use mdbook_core::MDBOOK_VERSION;\npub use mdbook_core::book;\npub use mdbook_core::config;\npub use mdbook_core::errors;\n\n/// An mdbook backend.\n///\n/// Types that implement the `Renderer` trait can be used with\n/// [`MDBook::with_renderer`] to programmatically add renderers.\n///\n/// [`MDBook::with_renderer`]: https://docs.rs/mdbook-driver/latest/mdbook_driver/struct.MDBook.html#method.with_renderer\npub trait Renderer {\n    /// The `Renderer`'s name.\n    fn name(&self) -> &str;\n\n    /// Invoke the `Renderer`, passing in all the necessary information for\n    /// describing a book.\n    fn render(&self, ctx: &RenderContext) -> Result<()>;\n}\n\n/// The context provided to all renderers.\n#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]\n#[non_exhaustive]\npub struct RenderContext {\n    /// Which version of `mdbook` did this come from (as written in `mdbook`'s\n    /// `Cargo.toml`). Useful if you know the renderer is only compatible with\n    /// certain versions of `mdbook`.\n    pub version: String,\n    /// The book's root directory.\n    pub root: PathBuf,\n    /// A loaded representation of the book itself.\n    pub book: Book,\n    /// The loaded configuration file.\n    pub config: Config,\n    /// Where the renderer *must* put any build artefacts generated. To allow\n    /// renderers to cache intermediate results, this directory is not\n    /// guaranteed to be empty or even exist.\n    pub destination: PathBuf,\n    /// Internal mapping of chapter titles.\n    ///\n    /// This is used internally by mdbook to compute custom chapter titles.\n    /// This should not be used outside of mdbook's internals.\n    #[serde(skip)]\n    pub chapter_titles: HashMap<PathBuf, String>,\n}\n\nimpl RenderContext {\n    /// Create a new `RenderContext`.\n    pub fn new<P, Q>(root: P, book: Book, config: Config, destination: Q) -> RenderContext\n    where\n        P: Into<PathBuf>,\n        Q: Into<PathBuf>,\n    {\n        RenderContext {\n            book,\n            config,\n            version: crate::MDBOOK_VERSION.to_string(),\n            root: root.into(),\n            destination: destination.into(),\n            chapter_titles: HashMap::new(),\n        }\n    }\n\n    /// Get the source directory's (absolute) path on disk.\n    pub fn source_dir(&self) -> PathBuf {\n        self.root.join(&self.config.book.src)\n    }\n\n    /// Load a `RenderContext` from its JSON representation.\n    pub fn from_json<R: Read>(reader: R) -> Result<RenderContext> {\n        serde_json::from_reader(reader).with_context(|| \"Unable to deserialize the `RenderContext`\")\n    }\n}\n"
  },
  {
    "path": "crates/mdbook-summary/Cargo.toml",
    "content": "[package]\nname = \"mdbook-summary\"\nversion = \"0.5.2\"\ndescription = \"Summary parser for mdBook\"\nedition.workspace = true\nlicense.workspace = true\nrepository.workspace = true\nrust-version.workspace = true\n\n[dependencies]\nanyhow.workspace = true\nmdbook-core.workspace = true\nmemchr.workspace = true\npulldown-cmark.workspace = true\nserde.workspace = true\ntracing.workspace = true\n\n[lints]\nworkspace = true\n"
  },
  {
    "path": "crates/mdbook-summary/README.md",
    "content": "# mdbook-summary\n\n[![Documentation](https://img.shields.io/docsrs/mdbook-summary)](https://docs.rs/mdbook-summary)\n[![crates.io](https://img.shields.io/crates/v/mdbook-summary.svg)](https://crates.io/crates/mdbook-summary)\n[![Changelog](https://img.shields.io/badge/CHANGELOG-Latest-green)](https://github.com/rust-lang/mdBook/blob/master/CHANGELOG.md)\n\nThis is the Rust library used to parse the [`SUMMARY.md`](https://rust-lang.github.io/mdBook/format/summary.html) file structure for [mdBook](https://rust-lang.github.io/mdBook/).\n\n> This crate is maintained by the mdBook team for use by the wider ecosystem. This crate follows [semver compatibility](https://doc.rust-lang.org/cargo/reference/semver.html) for its APIs.\n\n## License\n\n[Mozilla Public License, version 2.0](https://github.com/rust-lang/mdBook/blob/master/LICENSE)\n"
  },
  {
    "path": "crates/mdbook-summary/src/lib.rs",
    "content": "//! Summary parser for mdBook.\n//!\n//! This is used to parse the\n//! [`SUMMARY.md`](https://rust-lang.github.io/mdBook/format/summary.html)\n//! file structure for [mdBook](https://rust-lang.github.io/mdBook/).\n\nuse anyhow::{Context, Error, Result, bail};\npub use mdbook_core::book::SectionNumber;\nuse memchr::Memchr;\nuse pulldown_cmark::{DefaultBrokenLinkCallback, Event, HeadingLevel, Tag, TagEnd};\nuse serde::{Deserialize, Serialize};\nuse std::collections::HashSet;\nuse std::fmt::Display;\nuse std::path::{Path, PathBuf};\nuse tracing::{debug, trace, warn};\n\n/// Parse the text from a `SUMMARY.md` file into a sort of \"recipe\" to be\n/// used when loading a book from disk.\n///\n/// # Summary Format\n///\n/// **Title:** It's common practice to begin with a title, generally\n/// \"# Summary\". It's not mandatory and the parser (currently) ignores it, so\n/// you can too if you feel like it.\n///\n/// **Prefix Chapter:** Before the main numbered chapters you can add a couple\n/// of elements that will not be numbered. This is useful for forewords,\n/// introductions, etc. There are however some constraints. You can not nest\n/// prefix chapters, they should all be on the root level. And you can not add\n/// prefix chapters once you have added numbered chapters.\n///\n/// ```markdown\n/// [Title of prefix element](relative/path/to/markdown.md)\n/// ```\n///\n/// **Part Title:** An optional title for the next collect of numbered chapters. The numbered\n/// chapters can be broken into as many parts as desired.\n///\n/// **Numbered Chapter:** Numbered chapters are the main content of the book,\n/// they\n/// will be numbered and can be nested, resulting in a nice hierarchy (chapters,\n/// sub-chapters, etc.)\n///\n/// ```markdown\n/// # Title of Part\n///\n/// - [Title of the Chapter](relative/path/to/markdown.md)\n/// ```\n///\n/// You can either use - or * to indicate a numbered chapter, the parser doesn't\n/// care but you'll probably want to stay consistent.\n///\n/// **Suffix Chapter:** After the numbered chapters you can add a couple of\n/// non-numbered chapters. They are the same as prefix chapters but come after\n/// the numbered chapters instead of before.\n///\n/// All other elements are unsupported and will be ignored at best or result in\n/// an error.\npub fn parse_summary(summary: &str) -> Result<Summary> {\n    let parser = SummaryParser::new(summary);\n    parser.parse()\n}\n\n/// The parsed `SUMMARY.md`, specifying how the book should be laid out.\n#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]\n#[non_exhaustive]\npub struct Summary {\n    /// An optional title for the `SUMMARY.md`, currently just ignored.\n    pub title: Option<String>,\n    /// Chapters before the main text (e.g. an introduction).\n    pub prefix_chapters: Vec<SummaryItem>,\n    /// The main numbered chapters of the book, broken into one or more possibly named parts.\n    pub numbered_chapters: Vec<SummaryItem>,\n    /// Items which come after the main document (e.g. a conclusion).\n    pub suffix_chapters: Vec<SummaryItem>,\n}\n\n/// A struct representing an entry in the `SUMMARY.md`, possibly with nested\n/// entries.\n///\n/// This is roughly the equivalent of `[Some section](./path/to/file.md)`.\n#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]\n#[non_exhaustive]\npub struct Link {\n    /// The name of the chapter.\n    pub name: String,\n    /// The location of the chapter's source file, taking the book's `src`\n    /// directory as the root.\n    pub location: Option<PathBuf>,\n    /// The section number, if this chapter is in the numbered section.\n    pub number: Option<SectionNumber>,\n    /// Any nested items this chapter may contain.\n    pub nested_items: Vec<SummaryItem>,\n}\n\nimpl Link {\n    /// Create a new link with no nested items.\n    pub fn new<S: Into<String>, P: AsRef<Path>>(name: S, location: P) -> Link {\n        Link {\n            name: name.into(),\n            location: Some(location.as_ref().to_path_buf()),\n            number: None,\n            nested_items: Vec::new(),\n        }\n    }\n}\n\nimpl Default for Link {\n    fn default() -> Self {\n        Link {\n            name: String::new(),\n            location: Some(PathBuf::new()),\n            number: None,\n            nested_items: Vec::new(),\n        }\n    }\n}\n\n/// An item in `SUMMARY.md`.\n#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]\n#[non_exhaustive]\npub enum SummaryItem {\n    /// A link to a chapter.\n    Link(Link),\n    /// A separator (`---`).\n    Separator,\n    /// A part title.\n    PartTitle(String),\n}\n\nimpl SummaryItem {\n    fn maybe_link_mut(&mut self) -> Option<&mut Link> {\n        match *self {\n            SummaryItem::Link(ref mut l) => Some(l),\n            _ => None,\n        }\n    }\n}\n\nimpl From<Link> for SummaryItem {\n    fn from(other: Link) -> SummaryItem {\n        SummaryItem::Link(other)\n    }\n}\n\n/// A recursive descent (-ish) parser for a `SUMMARY.md`.\n///\n///\n/// # Grammar\n///\n/// The `SUMMARY.md` file has a grammar which looks something like this:\n///\n/// ```text\n/// summary           ::= title prefix_chapters numbered_chapters\n///                         suffix_chapters\n/// title             ::= \"# \" TEXT\n///                     | EPSILON\n/// prefix_chapters   ::= item*\n/// suffix_chapters   ::= item*\n/// numbered_chapters ::= part+\n/// part              ::= title dotted_item+\n/// dotted_item       ::= INDENT* DOT_POINT item\n/// item              ::= link\n///                     | separator\n/// separator         ::= \"---\"\n/// link              ::= \"[\" TEXT \"]\" \"(\" TEXT \")\"\n/// DOT_POINT         ::= \"-\"\n///                     | \"*\"\n/// ```\n///\n/// > **Note:** the `TEXT` terminal is \"normal\" text, and should (roughly)\n/// > match the following regex: \"[^<>\\n[]]+\".\nstruct SummaryParser<'a> {\n    src: &'a str,\n    stream: pulldown_cmark::OffsetIter<'a, DefaultBrokenLinkCallback>,\n    offset: usize,\n\n    /// We can't actually put an event back into the `OffsetIter` stream, so instead we store it\n    /// here until somebody calls `next_event` again.\n    back: Option<Event<'a>>,\n}\n\n/// Reads `Events` from the provided stream until the corresponding\n/// `Event::End` is encountered which matches the `$delimiter` pattern.\n///\n/// This is the equivalent of doing\n/// `$stream.take_while(|e| e != $delimiter).collect()` but it allows you to\n/// use pattern matching and you won't get errors because `take_while()`\n/// moves `$stream` out of self.\nmacro_rules! collect_events {\n    ($stream:expr,start $delimiter:pat) => {\n        collect_events!($stream, Event::Start($delimiter))\n    };\n    ($stream:expr,end $delimiter:pat) => {\n        collect_events!($stream, Event::End($delimiter))\n    };\n    ($stream:expr, $delimiter:pat) => {{\n        let mut events = Vec::new();\n\n        loop {\n            let event = $stream.next().map(|(ev, _range)| ev);\n            trace!(\"Next event: {:?}\", event);\n\n            match event {\n                Some($delimiter) => break,\n                Some(other) => events.push(other),\n                None => {\n                    debug!(\n                        \"Reached end of stream without finding the closing pattern, {}\",\n                        stringify!($delimiter)\n                    );\n                    break;\n                }\n            }\n        }\n\n        events\n    }};\n}\n\nimpl<'a> SummaryParser<'a> {\n    fn new(text: &'a str) -> SummaryParser<'a> {\n        let pulldown_parser = pulldown_cmark::Parser::new(text).into_offset_iter();\n\n        SummaryParser {\n            src: text,\n            stream: pulldown_parser,\n            offset: 0,\n            back: None,\n        }\n    }\n\n    /// Get the current line and column to give the user more useful error\n    /// messages.\n    fn current_location(&self) -> (usize, usize) {\n        let previous_text = self.src[..self.offset].as_bytes();\n        let line = Memchr::new(b'\\n', previous_text).count() + 1;\n        let start_of_line = memchr::memrchr(b'\\n', previous_text).unwrap_or(0);\n        let col = self.src[start_of_line..self.offset].chars().count();\n\n        (line, col)\n    }\n\n    /// Parse the text the `SummaryParser` was created with.\n    fn parse(mut self) -> Result<Summary> {\n        let title = self.parse_title();\n\n        let prefix_chapters = self\n            .parse_affix(true)\n            .with_context(|| \"There was an error parsing the prefix chapters\")?;\n        let numbered_chapters = self\n            .parse_parts()\n            .with_context(|| \"There was an error parsing the numbered chapters\")?;\n        let suffix_chapters = self\n            .parse_affix(false)\n            .with_context(|| \"There was an error parsing the suffix chapters\")?;\n\n        let mut files = HashSet::new();\n        for part in [&prefix_chapters, &numbered_chapters, &suffix_chapters] {\n            Self::check_for_duplicates(&part, &mut files)?;\n        }\n\n        Ok(Summary {\n            title,\n            prefix_chapters,\n            numbered_chapters,\n            suffix_chapters,\n        })\n    }\n\n    /// Recursively check for duplicate files in the summary items.\n    fn check_for_duplicates<'b>(\n        items: &'b [SummaryItem],\n        files: &mut HashSet<&'b PathBuf>,\n    ) -> Result<()> {\n        for item in items {\n            if let SummaryItem::Link(link) = item {\n                if let Some(location) = &link.location {\n                    if !files.insert(location) {\n                        bail!(anyhow::anyhow!(\n                            \"Duplicate file in SUMMARY.md: {:?}\",\n                            location\n                        ));\n                    }\n                }\n                // Recursively check nested items\n                Self::check_for_duplicates(&link.nested_items, files)?;\n            }\n        }\n        Ok(())\n    }\n\n    /// Parse the affix chapters.\n    fn parse_affix(&mut self, is_prefix: bool) -> Result<Vec<SummaryItem>> {\n        let mut items = Vec::new();\n        debug!(\n            \"Parsing {} items\",\n            if is_prefix { \"prefix\" } else { \"suffix\" }\n        );\n\n        loop {\n            match self.next_event() {\n                Some(ev @ Event::Start(Tag::List(..)))\n                | Some(\n                    ev @ Event::Start(Tag::Heading {\n                        level: HeadingLevel::H1,\n                        ..\n                    }),\n                ) => {\n                    if is_prefix {\n                        // we've finished prefix chapters and are at the start\n                        // of the numbered section.\n                        self.back(ev);\n                        break;\n                    } else {\n                        bail!(self.parse_error(\"Suffix chapters cannot be followed by a list\"));\n                    }\n                }\n                Some(Event::Start(Tag::Link { dest_url, .. })) => {\n                    let link = self.parse_link(dest_url.to_string());\n                    items.push(SummaryItem::Link(link));\n                }\n                Some(Event::Rule) => items.push(SummaryItem::Separator),\n                Some(_) => {}\n                None => break,\n            }\n        }\n\n        Ok(items)\n    }\n\n    fn parse_parts(&mut self) -> Result<Vec<SummaryItem>> {\n        let mut parts = vec![];\n\n        // We want the section numbers to be continues through all parts.\n        let mut root_number = SectionNumber::default();\n        let mut root_items = 0;\n\n        loop {\n            // Possibly match a title or the end of the \"numbered chapters part\".\n            let title = match self.next_event() {\n                Some(ev @ Event::Start(Tag::Paragraph)) => {\n                    // we're starting the suffix chapters\n                    self.back(ev);\n                    break;\n                }\n\n                Some(Event::Start(Tag::Heading {\n                    level: HeadingLevel::H1,\n                    ..\n                })) => {\n                    debug!(\"Found a h1 in the SUMMARY\");\n\n                    let tags = collect_events!(self.stream, end TagEnd::Heading(HeadingLevel::H1));\n                    Some(stringify_events(tags))\n                }\n\n                Some(ev) => {\n                    self.back(ev);\n                    None\n                }\n\n                None => break, // EOF, bail...\n            };\n\n            // Parse the rest of the part.\n            let numbered_chapters = self\n                .parse_numbered(&mut root_items, &mut root_number)\n                .with_context(|| \"There was an error parsing the numbered chapters\")?;\n\n            if let Some(title) = title {\n                parts.push(SummaryItem::PartTitle(title));\n            }\n            parts.extend(numbered_chapters);\n        }\n\n        Ok(parts)\n    }\n\n    /// Finishes parsing a link once the `Event::Start(Tag::Link(..))` has been opened.\n    fn parse_link(&mut self, href: String) -> Link {\n        let href = href.replace(\"%20\", \" \");\n        let link_content = collect_events!(self.stream, end TagEnd::Link);\n        let name = stringify_events(link_content);\n\n        let path = if href.is_empty() {\n            None\n        } else {\n            Some(PathBuf::from(href))\n        };\n\n        Link {\n            name,\n            location: path,\n            number: None,\n            nested_items: Vec::new(),\n        }\n    }\n\n    /// Parse the numbered chapters.\n    fn parse_numbered(\n        &mut self,\n        root_items: &mut u32,\n        root_number: &mut SectionNumber,\n    ) -> Result<Vec<SummaryItem>> {\n        let mut items = Vec::new();\n\n        // For the first iteration, we want to just skip any opening paragraph tags, as that just\n        // marks the start of the list. But after that, another opening paragraph indicates that we\n        // have started a new part or the suffix chapters.\n        let mut first = true;\n\n        loop {\n            match self.next_event() {\n                Some(ev @ Event::Start(Tag::Paragraph)) => {\n                    if !first {\n                        // we're starting the suffix chapters\n                        self.back(ev);\n                        break;\n                    }\n                }\n                // The expectation is that pulldown cmark will terminate a paragraph before a new\n                // heading, so we can always count on this to return without skipping headings.\n                Some(\n                    ev @ Event::Start(Tag::Heading {\n                        level: HeadingLevel::H1,\n                        ..\n                    }),\n                ) => {\n                    // we're starting a new part\n                    self.back(ev);\n                    break;\n                }\n                Some(ev @ Event::Start(Tag::List(..))) => {\n                    self.back(ev);\n                    let mut bunch_of_items = self.parse_nested_numbered(root_number)?;\n\n                    // if we've resumed after something like a rule the root sections\n                    // will be numbered from 1. We need to manually go back and update\n                    // them\n                    update_section_numbers(&mut bunch_of_items, 0, *root_items);\n                    *root_items += bunch_of_items.len() as u32;\n                    items.extend(bunch_of_items);\n                }\n                Some(Event::Start(other_tag)) => {\n                    trace!(\"Skipping contents of {:?}\", other_tag);\n\n                    // Skip over the contents of this tag\n                    while let Some(event) = self.next_event() {\n                        if event == Event::End(other_tag.clone().into()) {\n                            break;\n                        }\n                    }\n                }\n                Some(Event::Rule) => {\n                    items.push(SummaryItem::Separator);\n                }\n\n                // something else... ignore\n                Some(_) => {}\n\n                // EOF, bail...\n                None => {\n                    break;\n                }\n            }\n\n            // From now on, we cannot accept any new paragraph opening tags.\n            first = false;\n        }\n\n        Ok(items)\n    }\n\n    /// Push an event back to the tail of the stream.\n    fn back(&mut self, ev: Event<'a>) {\n        assert!(self.back.is_none());\n        trace!(\"Back: {:?}\", ev);\n        self.back = Some(ev);\n    }\n\n    fn next_event(&mut self) -> Option<Event<'a>> {\n        let next = self.back.take().or_else(|| {\n            self.stream.next().map(|(ev, range)| {\n                self.offset = range.start;\n                ev\n            })\n        });\n\n        trace!(\"Next event: {:?}\", next);\n\n        next\n    }\n\n    fn parse_nested_numbered(&mut self, parent: &SectionNumber) -> Result<Vec<SummaryItem>> {\n        debug!(\"Parsing numbered chapters at level {}\", parent);\n        let mut items = Vec::new();\n\n        loop {\n            match self.next_event() {\n                Some(Event::Start(Tag::Item)) => {\n                    let item = self.parse_nested_item(parent, items.len())?;\n                    items.push(item);\n                }\n                Some(Event::Start(Tag::List(..))) => {\n                    // Skip this tag after comment because it is not nested.\n                    if items.is_empty() {\n                        continue;\n                    }\n                    // recurse to parse the nested list\n                    let (_, last_item) = get_last_link(&mut items)?;\n                    let last_item_number = last_item\n                        .number\n                        .as_ref()\n                        .expect(\"All numbered chapters have numbers\");\n\n                    let sub_items = self.parse_nested_numbered(last_item_number)?;\n\n                    last_item.nested_items = sub_items;\n                }\n                Some(Event::End(TagEnd::List(..))) => break,\n                Some(_) => {}\n                None => break,\n            }\n        }\n\n        Ok(items)\n    }\n\n    fn parse_nested_item(\n        &mut self,\n        parent: &SectionNumber,\n        num_existing_items: usize,\n    ) -> Result<SummaryItem> {\n        loop {\n            match self.next_event() {\n                Some(Event::Start(Tag::Paragraph)) => continue,\n                Some(Event::Start(Tag::Link { dest_url, .. })) => {\n                    let mut link = self.parse_link(dest_url.to_string());\n\n                    let mut number = parent.clone();\n                    number.push(num_existing_items as u32 + 1);\n                    trace!(\n                        \"Found chapter: {} {} ({})\",\n                        number,\n                        link.name,\n                        link.location\n                            .as_ref()\n                            .map(|p| p.to_str().unwrap_or(\"\"))\n                            .unwrap_or(\"[draft]\")\n                    );\n\n                    link.number = Some(number);\n\n                    return Ok(SummaryItem::Link(link));\n                }\n                other => {\n                    warn!(\"Expected a start of a link, actually got {:?}\", other);\n                    bail!(self.parse_error(\n                        \"The link items for nested chapters must only contain a hyperlink\"\n                    ));\n                }\n            }\n        }\n    }\n\n    fn parse_error<D: Display>(&self, msg: D) -> Error {\n        let (line, col) = self.current_location();\n        anyhow::anyhow!(\n            \"failed to parse SUMMARY.md line {}, column {}: {}\",\n            line,\n            col,\n            msg\n        )\n    }\n\n    /// Try to parse the title line.\n    fn parse_title(&mut self) -> Option<String> {\n        loop {\n            match self.next_event() {\n                Some(Event::Start(Tag::Heading {\n                    level: HeadingLevel::H1,\n                    ..\n                })) => {\n                    debug!(\"Found a h1 in the SUMMARY\");\n\n                    let tags = collect_events!(self.stream, end TagEnd::Heading(HeadingLevel::H1));\n                    return Some(stringify_events(tags));\n                }\n                // Skip a HTML element such as a comment line.\n                Some(Event::Html(_) | Event::InlineHtml(_))\n                | Some(Event::Start(Tag::HtmlBlock) | Event::End(TagEnd::HtmlBlock)) => {}\n                // Otherwise, no title.\n                Some(ev) => {\n                    self.back(ev);\n                    return None;\n                }\n                _ => return None,\n            }\n        }\n    }\n}\n\nfn update_section_numbers(items: &mut [SummaryItem], level: usize, by: u32) {\n    for item in items {\n        if let SummaryItem::Link(ref mut link) = *item {\n            if let Some(ref mut number) = link.number {\n                number[level] += by;\n            }\n\n            update_section_numbers(&mut link.nested_items, level, by);\n        }\n    }\n}\n\n/// Gets a pointer to the last `Link` in a list of `SummaryItem`s, and its\n/// index.\nfn get_last_link(links: &mut [SummaryItem]) -> Result<(usize, &mut Link)> {\n    links\n        .iter_mut()\n        .enumerate()\n        .filter_map(|(i, item)| item.maybe_link_mut().map(|l| (i, l)))\n        .next_back()\n        .ok_or_else(|| {\n            anyhow::anyhow!(\n                \"Unable to get last link because the list of SummaryItems \\\n                 doesn't contain any Links\"\n            )\n        })\n}\n\n/// Removes the styling from a list of Markdown events and returns just the\n/// plain text.\nfn stringify_events(events: Vec<Event<'_>>) -> String {\n    events\n        .into_iter()\n        .filter_map(|t| match t {\n            Event::Text(text) | Event::Code(text) => Some(text.into_string()),\n            Event::SoftBreak => Some(String::from(\" \")),\n            _ => None,\n        })\n        .collect()\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn parse_initial_title() {\n        let src = \"# Summary\";\n        let should_be = String::from(\"Summary\");\n\n        let mut parser = SummaryParser::new(src);\n        let got = parser.parse_title().unwrap();\n\n        assert_eq!(got, should_be);\n    }\n\n    #[test]\n    fn no_initial_title() {\n        let src = \"[Link]()\";\n        let mut parser = SummaryParser::new(src);\n\n        assert!(parser.parse_title().is_none());\n        assert!(matches!(\n            parser.next_event(),\n            Some(Event::Start(Tag::Paragraph))\n        ));\n    }\n\n    #[test]\n    fn parse_title_with_styling() {\n        let src = \"# My **Awesome** Summary\";\n        let should_be = String::from(\"My Awesome Summary\");\n\n        let mut parser = SummaryParser::new(src);\n        let got = parser.parse_title().unwrap();\n\n        assert_eq!(got, should_be);\n    }\n\n    #[test]\n    fn convert_markdown_events_to_a_string() {\n        let src = \"Hello *World*, `this` is some text [and a link](./path/to/link)\";\n        let should_be = \"Hello World, this is some text and a link\";\n\n        let events = pulldown_cmark::Parser::new(src).collect();\n        let got = stringify_events(events);\n\n        assert_eq!(got, should_be);\n    }\n\n    #[test]\n    fn parse_some_prefix_items() {\n        let src = \"[First](./first.md)\\n[Second](./second.md)\\n\";\n        let mut parser = SummaryParser::new(src);\n\n        let should_be = vec![\n            SummaryItem::Link(Link {\n                name: String::from(\"First\"),\n                location: Some(PathBuf::from(\"./first.md\")),\n                ..Default::default()\n            }),\n            SummaryItem::Link(Link {\n                name: String::from(\"Second\"),\n                location: Some(PathBuf::from(\"./second.md\")),\n                ..Default::default()\n            }),\n        ];\n\n        let got = parser.parse_affix(true).unwrap();\n\n        assert_eq!(got, should_be);\n    }\n\n    #[test]\n    fn parse_prefix_items_with_a_separator() {\n        let src = \"[First](./first.md)\\n\\n---\\n\\n[Second](./second.md)\\n\";\n        let mut parser = SummaryParser::new(src);\n\n        let got = parser.parse_affix(true).unwrap();\n\n        assert_eq!(got.len(), 3);\n        assert_eq!(got[1], SummaryItem::Separator);\n    }\n\n    #[test]\n    fn suffix_items_cannot_be_followed_by_a_list() {\n        let src = \"[First](./first.md)\\n- [Second](./second.md)\\n\";\n        let mut parser = SummaryParser::new(src);\n\n        let got = parser.parse_affix(false);\n\n        assert!(got.is_err());\n        let error_message = got.err().unwrap().to_string();\n        assert_eq!(\n            error_message,\n            \"failed to parse SUMMARY.md line 2, column 1: Suffix chapters cannot be followed by a list\"\n        );\n    }\n\n    #[test]\n    fn expected_a_start_of_a_link() {\n        let src = \"- Title\\n\";\n        let mut parser = SummaryParser::new(src);\n\n        let got = parser.parse_affix(false);\n\n        assert!(got.is_err());\n        let error_message = got.err().unwrap().to_string();\n        assert_eq!(\n            error_message,\n            \"failed to parse SUMMARY.md line 1, column 0: Suffix chapters cannot be followed by a list\"\n        );\n    }\n\n    #[test]\n    fn parse_a_link() {\n        let src = \"[First](./first.md)\";\n        let should_be = Link {\n            name: String::from(\"First\"),\n            location: Some(PathBuf::from(\"./first.md\")),\n            ..Default::default()\n        };\n\n        let mut parser = SummaryParser::new(src);\n        let _ = parser.stream.next(); // Discard opening paragraph\n\n        let href = match parser.stream.next() {\n            Some((Event::Start(Tag::Link { dest_url, .. }), _range)) => dest_url.to_string(),\n            other => panic!(\"Unreachable, {other:?}\"),\n        };\n\n        let got = parser.parse_link(href);\n        assert_eq!(got, should_be);\n    }\n\n    #[test]\n    fn parse_a_numbered_chapter() {\n        let src = \"- [First](./first.md)\\n\";\n        let link = Link {\n            name: String::from(\"First\"),\n            location: Some(PathBuf::from(\"./first.md\")),\n            number: Some(SectionNumber::new([1])),\n            ..Default::default()\n        };\n        let should_be = vec![SummaryItem::Link(link)];\n\n        let mut parser = SummaryParser::new(src);\n        let got = parser\n            .parse_numbered(&mut 0, &mut SectionNumber::default())\n            .unwrap();\n\n        assert_eq!(got, should_be);\n    }\n\n    #[test]\n    fn parse_nested_numbered_chapters() {\n        let src = \"- [First](./first.md)\\n  - [Nested](./nested.md)\\n- [Second](./second.md)\";\n\n        let should_be = vec![\n            SummaryItem::Link(Link {\n                name: String::from(\"First\"),\n                location: Some(PathBuf::from(\"./first.md\")),\n                number: Some(SectionNumber::new([1])),\n                nested_items: vec![SummaryItem::Link(Link {\n                    name: String::from(\"Nested\"),\n                    location: Some(PathBuf::from(\"./nested.md\")),\n                    number: Some(SectionNumber::new([1, 1])),\n                    nested_items: Vec::new(),\n                })],\n            }),\n            SummaryItem::Link(Link {\n                name: String::from(\"Second\"),\n                location: Some(PathBuf::from(\"./second.md\")),\n                number: Some(SectionNumber::new([2])),\n                nested_items: Vec::new(),\n            }),\n        ];\n\n        let mut parser = SummaryParser::new(src);\n        let got = parser\n            .parse_numbered(&mut 0, &mut SectionNumber::default())\n            .unwrap();\n\n        assert_eq!(got, should_be);\n    }\n\n    #[test]\n    fn parse_numbered_chapters_separated_by_comment() {\n        let src = \"- [First](./first.md)\\n<!-- this is a comment -->\\n- [Second](./second.md)\";\n\n        let should_be = vec![\n            SummaryItem::Link(Link {\n                name: String::from(\"First\"),\n                location: Some(PathBuf::from(\"./first.md\")),\n                number: Some(SectionNumber::new([1])),\n                nested_items: Vec::new(),\n            }),\n            SummaryItem::Link(Link {\n                name: String::from(\"Second\"),\n                location: Some(PathBuf::from(\"./second.md\")),\n                number: Some(SectionNumber::new([2])),\n                nested_items: Vec::new(),\n            }),\n        ];\n\n        let mut parser = SummaryParser::new(src);\n        let got = parser\n            .parse_numbered(&mut 0, &mut SectionNumber::default())\n            .unwrap();\n\n        assert_eq!(got, should_be);\n    }\n\n    #[test]\n    fn parse_titled_parts() {\n        let src = \"- [First](./first.md)\\n- [Second](./second.md)\\n\\\n                   # Title 2\\n- [Third](./third.md)\\n\\t- [Fourth](./fourth.md)\";\n\n        let should_be = vec![\n            SummaryItem::Link(Link {\n                name: String::from(\"First\"),\n                location: Some(PathBuf::from(\"./first.md\")),\n                number: Some(SectionNumber::new([1])),\n                nested_items: Vec::new(),\n            }),\n            SummaryItem::Link(Link {\n                name: String::from(\"Second\"),\n                location: Some(PathBuf::from(\"./second.md\")),\n                number: Some(SectionNumber::new([2])),\n                nested_items: Vec::new(),\n            }),\n            SummaryItem::PartTitle(String::from(\"Title 2\")),\n            SummaryItem::Link(Link {\n                name: String::from(\"Third\"),\n                location: Some(PathBuf::from(\"./third.md\")),\n                number: Some(SectionNumber::new([3])),\n                nested_items: vec![SummaryItem::Link(Link {\n                    name: String::from(\"Fourth\"),\n                    location: Some(PathBuf::from(\"./fourth.md\")),\n                    number: Some(SectionNumber::new([3, 1])),\n                    nested_items: Vec::new(),\n                })],\n            }),\n        ];\n\n        let mut parser = SummaryParser::new(src);\n        let got = parser.parse_parts().unwrap();\n\n        assert_eq!(got, should_be);\n    }\n\n    /// This test ensures the book will continue to pass because it breaks the\n    /// `SUMMARY.md` up using level 2 headers ([example]).\n    ///\n    /// [example]: https://github.com/rust-lang/book/blob/2c942dc094f4ddcdc7aba7564f80782801197c99/second-edition/src/SUMMARY.md#basic-rust-literacy\n    #[test]\n    fn can_have_a_subheader_between_nested_items() {\n        let src = \"- [First](./first.md)\\n\\n## Subheading\\n\\n- [Second](./second.md)\\n\";\n        let should_be = vec![\n            SummaryItem::Link(Link {\n                name: String::from(\"First\"),\n                location: Some(PathBuf::from(\"./first.md\")),\n                number: Some(SectionNumber::new([1])),\n                nested_items: Vec::new(),\n            }),\n            SummaryItem::Link(Link {\n                name: String::from(\"Second\"),\n                location: Some(PathBuf::from(\"./second.md\")),\n                number: Some(SectionNumber::new([2])),\n                nested_items: Vec::new(),\n            }),\n        ];\n\n        let mut parser = SummaryParser::new(src);\n        let got = parser\n            .parse_numbered(&mut 0, &mut SectionNumber::default())\n            .unwrap();\n\n        assert_eq!(got, should_be);\n    }\n\n    #[test]\n    fn an_empty_link_location_is_a_draft_chapter() {\n        let src = \"- [Empty]()\\n\";\n        let mut parser = SummaryParser::new(src);\n\n        let got = parser.parse_numbered(&mut 0, &mut SectionNumber::default());\n        let should_be = vec![SummaryItem::Link(Link {\n            name: String::from(\"Empty\"),\n            location: None,\n            number: Some(SectionNumber::new([1])),\n            nested_items: Vec::new(),\n        })];\n\n        assert!(got.is_ok());\n        assert_eq!(got.unwrap(), should_be);\n    }\n\n    /// Regression test for https://github.com/rust-lang/mdBook/issues/779\n    /// Ensure section numbers are correctly incremented after a horizontal separator.\n    #[test]\n    fn keep_numbering_after_separator() {\n        let src =\n            \"- [First](./first.md)\\n---\\n- [Second](./second.md)\\n---\\n- [Third](./third.md)\\n\";\n        let should_be = vec![\n            SummaryItem::Link(Link {\n                name: String::from(\"First\"),\n                location: Some(PathBuf::from(\"./first.md\")),\n                number: Some(SectionNumber::new([1])),\n                nested_items: Vec::new(),\n            }),\n            SummaryItem::Separator,\n            SummaryItem::Link(Link {\n                name: String::from(\"Second\"),\n                location: Some(PathBuf::from(\"./second.md\")),\n                number: Some(SectionNumber::new([2])),\n                nested_items: Vec::new(),\n            }),\n            SummaryItem::Separator,\n            SummaryItem::Link(Link {\n                name: String::from(\"Third\"),\n                location: Some(PathBuf::from(\"./third.md\")),\n                number: Some(SectionNumber::new([3])),\n                nested_items: Vec::new(),\n            }),\n        ];\n\n        let mut parser = SummaryParser::new(src);\n        let got = parser\n            .parse_numbered(&mut 0, &mut SectionNumber::default())\n            .unwrap();\n\n        assert_eq!(got, should_be);\n    }\n\n    /// Regression test for https://github.com/rust-lang/mdBook/issues/1218\n    /// Ensure chapter names spread across multiple lines have spaces between all the words.\n    #[test]\n    fn add_space_for_multi_line_chapter_names() {\n        let src = \"- [Chapter\\ntitle](./chapter.md)\";\n        let should_be = vec![SummaryItem::Link(Link {\n            name: String::from(\"Chapter title\"),\n            location: Some(PathBuf::from(\"./chapter.md\")),\n            number: Some(SectionNumber::new([1])),\n            nested_items: Vec::new(),\n        })];\n\n        let mut parser = SummaryParser::new(src);\n        let got = parser\n            .parse_numbered(&mut 0, &mut SectionNumber::default())\n            .unwrap();\n\n        assert_eq!(got, should_be);\n    }\n\n    #[test]\n    fn allow_space_in_link_destination() {\n        let src = \"- [test1](./test%20link1.md)\\n- [test2](<./test link2.md>)\";\n        let should_be = vec![\n            SummaryItem::Link(Link {\n                name: String::from(\"test1\"),\n                location: Some(PathBuf::from(\"./test link1.md\")),\n                number: Some(SectionNumber::new([1])),\n                nested_items: Vec::new(),\n            }),\n            SummaryItem::Link(Link {\n                name: String::from(\"test2\"),\n                location: Some(PathBuf::from(\"./test link2.md\")),\n                number: Some(SectionNumber::new([2])),\n                nested_items: Vec::new(),\n            }),\n        ];\n        let mut parser = SummaryParser::new(src);\n        let got = parser\n            .parse_numbered(&mut 0, &mut SectionNumber::default())\n            .unwrap();\n\n        assert_eq!(got, should_be);\n    }\n\n    #[test]\n    fn skip_html_comments() {\n        let src = r#\"<!--\n# Title - En\n-->\n# Title - Local\n\n<!--\n[Prefix 00-01 - En](ch00-01.md)\n[Prefix 00-02 - En](ch00-02.md)\n-->\n[Prefix 00-01 - Local](ch00-01.md)\n[Prefix 00-02 - Local](ch00-02.md)\n\n<!--\n## Section Title - En\n-->\n## Section Title - Localized\n\n<!--\n- [Ch 01-00 - En](ch01-00.md)\n    - [Ch 01-01 - En](ch01-01.md)\n    - [Ch 01-02 - En](ch01-02.md)\n-->\n- [Ch 01-00 - Local](ch01-00.md)\n    - [Ch 01-01 - Local](ch01-01.md)\n    - [Ch 01-02 - Local](ch01-02.md)\n\n<!--\n- [Ch 02-00 - En](ch02-00.md)\n-->\n- [Ch 02-00 - Local](ch02-00.md)\n\n<!--\n[Appendix A - En](appendix-01.md)\n[Appendix B - En](appendix-02.md)\n-->`\n[Appendix A - Local](appendix-01.md)\n[Appendix B - Local](appendix-02.md)\n\"#;\n\n        let mut parser = SummaryParser::new(src);\n\n        // ---- Title ----\n        let title = parser.parse_title();\n        assert_eq!(title, Some(String::from(\"Title - Local\")));\n\n        // ---- Prefix Chapters ----\n\n        let new_affix_item = |name, location| {\n            SummaryItem::Link(Link {\n                name: String::from(name),\n                location: Some(PathBuf::from(location)),\n                ..Default::default()\n            })\n        };\n\n        let should_be = vec![\n            new_affix_item(\"Prefix 00-01 - Local\", \"ch00-01.md\"),\n            new_affix_item(\"Prefix 00-02 - Local\", \"ch00-02.md\"),\n        ];\n\n        let got = parser.parse_affix(true).unwrap();\n        assert_eq!(got, should_be);\n\n        // ---- Numbered Chapters ----\n\n        let new_numbered_item = |name, location, numbers: &[u32], nested_items| {\n            SummaryItem::Link(Link {\n                name: String::from(name),\n                location: Some(PathBuf::from(location)),\n                number: Some(SectionNumber::new(numbers)),\n                nested_items,\n            })\n        };\n\n        let ch01_nested = vec![\n            new_numbered_item(\"Ch 01-01 - Local\", \"ch01-01.md\", &[1, 1], vec![]),\n            new_numbered_item(\"Ch 01-02 - Local\", \"ch01-02.md\", &[1, 2], vec![]),\n        ];\n\n        let should_be = vec![\n            new_numbered_item(\"Ch 01-00 - Local\", \"ch01-00.md\", &[1], ch01_nested),\n            new_numbered_item(\"Ch 02-00 - Local\", \"ch02-00.md\", &[2], vec![]),\n        ];\n        let got = parser.parse_parts().unwrap();\n        assert_eq!(got, should_be);\n\n        // ---- Suffix Chapters ----\n\n        let should_be = vec![\n            new_affix_item(\"Appendix A - Local\", \"appendix-01.md\"),\n            new_affix_item(\"Appendix B - Local\", \"appendix-02.md\"),\n        ];\n\n        let got = parser.parse_affix(false).unwrap();\n        assert_eq!(got, should_be);\n    }\n\n    #[test]\n    fn duplicate_entries_1() {\n        let src = r#\"\n# Summary\n- [A](./a.md)\n- [A](./a.md)\n\"#;\n\n        let res = parse_summary(src);\n        assert!(res.is_err());\n        let error_message = res.err().unwrap().to_string();\n        assert_eq!(error_message, r#\"Duplicate file in SUMMARY.md: \"./a.md\"\"#);\n    }\n\n    #[test]\n    fn duplicate_entries_2() {\n        let src = r#\"\n# Summary\n- [A](./a.md)\n  - [A](./a.md)\n\"#;\n\n        let res = parse_summary(src);\n        assert!(res.is_err());\n        let error_message = res.err().unwrap().to_string();\n        assert_eq!(error_message, r#\"Duplicate file in SUMMARY.md: \"./a.md\"\"#);\n    }\n    #[test]\n    fn duplicate_entries_3() {\n        let src = r#\"\n# Summary\n- [A](./a.md)\n- [B](./b.md)\n  - [A](./a.md)\n\"#;\n\n        let res = parse_summary(src);\n        assert!(res.is_err());\n        let error_message = res.err().unwrap().to_string();\n        assert_eq!(error_message, r#\"Duplicate file in SUMMARY.md: \"./a.md\"\"#);\n    }\n\n    #[test]\n    fn duplicate_entries_4() {\n        let src = r#\"\n# Summary\n[A](./a.md)\n- [B](./b.md)\n- [A](./a.md)\n\"#;\n\n        let res = parse_summary(src);\n        assert!(res.is_err());\n        let error_message = res.err().unwrap().to_string();\n        assert_eq!(error_message, r#\"Duplicate file in SUMMARY.md: \"./a.md\"\"#);\n    }\n\n    #[test]\n    fn duplicate_entries_5() {\n        let src = r#\"\n# Summary\n[A](./a.md)\n\n# hi\n- [B](./b.md)\n\n# bye\n\n---\n\n[A](./a.md)\n\"#;\n\n        let res = parse_summary(src);\n        assert!(res.is_err());\n        let error_message = res.err().unwrap().to_string();\n        assert_eq!(error_message, r#\"Duplicate file in SUMMARY.md: \"./a.md\"\"#);\n    }\n}\n"
  },
  {
    "path": "crates/xtask/Cargo.toml",
    "content": "[package]\nname = \"xtask\"\npublish = false\nedition.workspace = true\nlicense.workspace = true\nrepository.workspace = true\nrust-version.workspace = true\n\n[dependencies]\n\n[lints]\nworkspace = true\n"
  },
  {
    "path": "crates/xtask/README.md",
    "content": "# xtask\n\nThis is a CLI utility for running development commands for mdbook.\nSee [CONTRIBUTING.md](../../CONTRIBUTING.md) for how to use this.\n"
  },
  {
    "path": "crates/xtask/src/changelog.rs",
    "content": "//! Helper to generate a changelog for a new release.\n\nuse super::Result;\nuse std::fs;\nuse std::process::Command;\nuse std::process::exit;\n\nconst CHANGELOG_PATH: &str = \"CHANGELOG.md\";\n\npub(crate) fn changelog() -> Result<()> {\n    let previous = get_previous()?;\n    let current = get_current()?;\n    if current == previous {\n        eprintln!(\n            \"error: Current version is `{current}` which is the same as the \\\n             previous version in the changelog. Run `cargo set-version --bump <BUMP> first.\"\n        );\n        exit(1);\n    }\n    let prs = get_prs(&previous)?;\n    update_changelog(&previous, &current, &prs)?;\n    Ok(())\n}\n\nfn get_previous() -> Result<String> {\n    let contents = fs::read_to_string(CHANGELOG_PATH)?;\n    let version = contents\n        .lines()\n        .filter_map(|line| line.strip_prefix(\"## mdBook \"))\n        .next()\n        .expect(\"at least one entry\")\n        .to_owned();\n    Ok(version)\n}\n\nfn get_current() -> Result<String> {\n    let contents = fs::read_to_string(\"Cargo.toml\")?;\n    let mut lines = contents\n        .lines()\n        .filter_map(|line| line.strip_prefix(\"version = \"))\n        .map(|version| &version[1..version.len() - 1]);\n    let version = lines.next().expect(\"version should exist\").to_owned();\n    assert_eq!(lines.next(), None);\n    Ok(version)\n}\n\nfn get_prs(previous: &str) -> Result<Vec<(String, String)>> {\n    println!(\"running `git fetch upstream`\");\n    let status = Command::new(\"git\").args([\"fetch\", \"upstream\"]).status()?;\n    if !status.success() {\n        eprintln!(\"error: git fetch failed\");\n        exit(1);\n    }\n    println!(\"running `git log`\");\n    const SEPARATOR: &str = \"---COMMIT_SEPARATOR---\";\n    let output = Command::new(\"git\")\n        .args([\n            \"log\",\n            \"--first-parent\",\n            &format!(\"--pretty=format:%B%n{SEPARATOR}\"),\n            \"upstream/master\",\n            &format!(\"v{previous}...upstream/HEAD\"),\n        ])\n        .output()?;\n    if !output.status.success() {\n        eprintln!(\"error: git log failed\");\n        exit(1);\n    }\n    let stdout = std::str::from_utf8(&output.stdout).unwrap();\n    let prs = stdout\n        .split(&format!(\"{SEPARATOR}\\n\"))\n        .filter_map(|entry| {\n            let mut lines = entry.lines();\n            let first = match lines.next().unwrap().strip_prefix(\"Merge pull request #\") {\n                Some(f) => f,\n                None => {\n                    println!(\"warning: merge line not found in {entry}\");\n                    return None;\n                }\n            };\n            let number = first.split_whitespace().next().unwrap();\n            assert_eq!(lines.next(), Some(\"\"));\n            let title = lines.next().expect(\"title is set\");\n            assert_eq!(lines.next(), Some(\"\"));\n            Some((number.to_string(), title.to_string()))\n        })\n        .collect();\n    Ok(prs)\n}\n\nfn update_changelog(previous: &str, current: &str, prs: &[(String, String)]) -> Result<()> {\n    let prs: String = prs\n        .iter()\n        .map(|(number, title)| {\n            format!(\n                \"- {title}\\n  \\\n                [#{number}](https://github.com/rust-lang/mdBook/pull/{number})\\n\"\n            )\n        })\n        .collect();\n    let new = format!(\n        \"## mdBook {current}\\n\\\n        [v{previous}...v{current}](https://github.com/rust-lang/mdBook/compare/v{previous}...v{current})\\n\\\n        \\n\\\n        {prs}\\\n        \\n\\\n        ### Added\\n\\\n        \\n\\\n        ### Changed\\n\\\n        \\n\\\n        ### Fixed\\n\\\n        \\n\"\n    );\n\n    let mut contents = fs::read_to_string(CHANGELOG_PATH)?;\n    let insertion_point = contents.find(\"## \").unwrap();\n    contents.insert_str(insertion_point, &new);\n    fs::write(CHANGELOG_PATH, contents)?;\n    Ok(())\n}\n"
  },
  {
    "path": "crates/xtask/src/main.rs",
    "content": "//! Helper for local development.\n\nuse std::collections::BTreeMap;\nuse std::error::Error;\nuse std::io::Write;\nuse std::process::exit;\nuse std::process::{Command, Stdio};\n\nmod changelog;\n\ntype Result<T> = std::result::Result<T, Box<dyn Error>>;\n\nfn main() -> Result<()> {\n    macro_rules! commands {\n        ($($name:literal => $func:expr),* $(,)?) => {\n            [$(($name, $func as fn() -> Result<()>)),*]\n        };\n    }\n\n    let cmds: BTreeMap<&'static str, fn() -> Result<()>> = commands! {\n        \"test-all\" => test_all,\n        \"test-workspace\" => test_workspace,\n        \"clippy\" => clippy,\n        \"doc\" => doc,\n        \"fmt\" => fmt,\n        \"semver-checks\" => semver_checks,\n        \"eslint\" => eslint,\n        \"gui\" => gui,\n        \"changelog\" => changelog::changelog,\n    }\n    .into_iter()\n    .collect();\n    let keys = cmds.keys().copied().collect::<Vec<_>>().join(\", \");\n    let mut args = std::env::args().skip(1).peekable();\n    if args.peek().is_none() {\n        eprintln!(\"error: specify a command (valid options: {keys})\");\n        exit(1);\n    }\n    while let Some(arg) = args.next() {\n        if let Some(cmd_fn) = cmds.get(arg.as_str()) {\n            cmd_fn()?;\n        } else if arg == \"bump\" {\n            let bump_arg = args\n                .next()\n                .expect(\"the next argument should be one of major, minor, patch, rc, beta, alpha\");\n            bump(&bump_arg)?;\n        } else if matches!(arg.as_str(), \"-h\" | \"--help\") {\n            println!(\"valid options: {keys}\");\n            exit(0)\n        } else {\n            eprintln!(\"error: unknown command `{arg}` (valid options: {keys}\");\n            exit(1);\n        }\n    }\n    println!(\"success!\");\n    Ok(())\n}\n\nfn test_all() -> Result<()> {\n    test_workspace()?;\n    clippy()?;\n    doc()?;\n    fmt()?;\n    semver_checks()?;\n    eslint()?;\n    gui()?;\n    Ok(())\n}\n\nfn cargo(args: &str, cb: &dyn Fn(&mut Command)) -> Result<()> {\n    println!(\"Running `cargo {args}`\");\n    let mut cmd = Command::new(\"cargo\");\n    cmd.args(args.split_whitespace());\n    cb(&mut cmd);\n    let status = cmd.status().expect(\"cargo should be installed\");\n    if !status.success() {\n        return Err(format!(\"command `cargo {args}` failed\").into());\n    }\n    Ok(())\n}\n\nfn test_workspace() -> Result<()> {\n    cargo(\"test --workspace\", &|_| {})?;\n    cargo(\"test --workspace --no-default-features\", &|_| {})?;\n    Ok(())\n}\n\nfn clippy() -> Result<()> {\n    cargo(\n        \"clippy --workspace --all-targets --no-deps -- -D warnings\",\n        &|_| {},\n    )?;\n    Ok(())\n}\n\nfn doc() -> Result<()> {\n    cargo(\n        \"doc --workspace --document-private-items --no-deps\",\n        &|cmd| {\n            cmd.env(\"RUSTDOCFLAGS\", \"-D warnings\");\n        },\n    )?;\n    Ok(())\n}\n\nfn fmt() -> Result<()> {\n    cargo(\"fmt --check\", &|_| {})?;\n    Ok(())\n}\n\nfn semver_checks() -> Result<()> {\n    cargo(\"+stable semver-checks --workspace\", &|_| {})?;\n    Ok(())\n}\n\nfn gui() -> Result<()> {\n    cargo(\"test --test gui\", &|_| {})?;\n    Ok(())\n}\n\nfn eslint() -> Result<()> {\n    println!(\"Running `npm run lint`\");\n    let status = Command::new(\"npm\")\n        .args([\"run\", \"lint\"])\n        .status()\n        .expect(\"npm should be installed\");\n    if !status.success() {\n        return Err(\"eslint failed\".into());\n    }\n    Ok(())\n}\n\nfn bump(bump: &str) -> Result<()> {\n    // Grab all the publishable crate names.\n    let metadata = Command::new(\"cargo\")\n        .args([\"metadata\", \"--format-version=1\", \"--no-deps\"])\n        .output()?;\n    let mut jq = Command::new(\"jq\")\n        .args([\"-r\", \".packages[] | select(.publish == null) | .name\"])\n        .stdin(Stdio::piped())\n        .stdout(Stdio::piped())\n        .spawn()?;\n    jq.stdin.as_mut().unwrap().write_all(&metadata.stdout)?;\n    let jq_out = jq.wait_with_output()?;\n    if !jq_out.status.success() {\n        eprintln!(\"jq failed\");\n        exit(1);\n    }\n    let names = std::str::from_utf8(&jq_out.stdout).unwrap();\n    let mut names: Vec<_> = names.split_whitespace().collect();\n    for i in (0..names.len()).rev() {\n        names.insert(i, \"-p\");\n    }\n\n    let status = Command::new(\"cargo\")\n        .args([\"set-version\", \"--bump\"])\n        .arg(bump)\n        .args(names)\n        .status()?;\n    if !status.success() {\n        eprintln!(\"cargo set-version failed\");\n        exit(1);\n    }\n    Ok(())\n}\n"
  },
  {
    "path": "eslint.config.mjs",
    "content": "import { defineConfig, globalIgnores } from \"eslint/config\";\n\n// Custom preprocessor to strip Handlebars templates.\nconst handlebarsPreprocessor = {\n    processors: {\n        \"handlebars-js\": {\n            preprocess(text, filename) {\n                if (filename.endsWith('.hbs')) {\n                    // This is a really dumb strip, which will likely not work\n                    // for more complex expressions, but for our use is good\n                    // enough for now.\n                    return [text.replace(/\\{\\{.*?\\}\\}/g, '')];\n                }\n                return [text];\n            },\n            postprocess(messages, filename) {\n                // Ideally this would update the locations so that they would\n                // compensate for the removed ranges.\n                return [].concat(...messages);\n            },\n        },\n    },\n};\n\nexport default defineConfig([\n    globalIgnores([\"**/**min.js\", \"**/highlight.js\", \"**/playground_editor/*\"]),\n    {\n        rules: {\n            indent: [\"error\", 4],\n            \"linebreak-style\": [\"error\", \"unix\"],\n            quotes: [\"error\", \"single\"],\n            semi: [\"error\", \"always\"],\n\n            \"brace-style\": [\"error\", \"1tbs\", {\n                allowSingleLine: false,\n            }],\n\n            curly: \"error\",\n            \"no-trailing-spaces\": \"error\",\n            \"no-multi-spaces\": \"error\",\n\n            \"keyword-spacing\": [\"error\", {\n                before: true,\n                after: true,\n            }],\n\n            \"comma-spacing\": [\"error\", {\n                before: false,\n                after: true,\n            }],\n\n            \"arrow-spacing\": [\"error\", {\n                before: true,\n                after: true,\n            }],\n\n            \"key-spacing\": [\"error\", {\n                beforeColon: false,\n                afterColon: true,\n                mode: \"strict\",\n            }],\n\n            \"func-call-spacing\": [\"error\", \"never\"],\n            \"space-infix-ops\": \"error\",\n            \"space-before-function-paren\": [\"error\", \"never\"],\n            \"space-before-blocks\": \"error\",\n\n            \"no-console\": [\"error\", {\n                allow: [\"warn\", \"error\"],\n            }],\n\n            \"comma-dangle\": [\"error\", \"always-multiline\"],\n            \"comma-style\": [\"error\", \"last\"],\n\n            \"max-len\": [\"error\", {\n                code: 100,\n                tabWidth: 2,\n            }],\n\n            \"eol-last\": [\"error\", \"always\"],\n            \"no-extra-parens\": \"error\",\n            \"arrow-parens\": [\"error\", \"as-needed\"],\n\n            \"no-unused-vars\": [\"error\", {\n                argsIgnorePattern: \"^_\",\n                varsIgnorePattern: \"^_\",\n            }],\n\n            \"prefer-const\": [\"error\"],\n            \"no-var\": \"error\",\n            eqeqeq: \"error\",\n        },\n    },\n    {\n        files: [\"**/*.js.hbs\"],\n        processor: handlebarsPreprocessor.processors[\"handlebars-js\"],\n    },\n]);\n"
  },
  {
    "path": "examples/nop-preprocessor.rs",
    "content": "//! A basic example of a preprocessor that does nothing.\n\nuse crate::nop_lib::Nop;\nuse clap::{Arg, ArgMatches, Command};\nuse mdbook_preprocessor::book::Book;\nuse mdbook_preprocessor::errors::Result;\nuse mdbook_preprocessor::{Preprocessor, PreprocessorContext};\nuse semver::{Version, VersionReq};\nuse std::io;\nuse std::process;\n\nfn make_app() -> Command {\n    Command::new(\"nop-preprocessor\")\n        .about(\"A mdbook preprocessor which does precisely nothing\")\n        .subcommand(\n            Command::new(\"supports\")\n                .arg(Arg::new(\"renderer\").required(true))\n                .about(\"Check whether a renderer is supported by this preprocessor\"),\n        )\n}\n\nfn main() {\n    let matches = make_app().get_matches();\n\n    // Users will want to construct their own preprocessor here\n    let preprocessor = Nop::new();\n\n    if let Some(sub_args) = matches.subcommand_matches(\"supports\") {\n        handle_supports(&preprocessor, sub_args);\n    } else if let Err(e) = handle_preprocessing(&preprocessor) {\n        eprintln!(\"{e:?}\");\n        process::exit(1);\n    }\n}\n\nfn handle_preprocessing(pre: &dyn Preprocessor) -> Result<()> {\n    let (ctx, book) = mdbook_preprocessor::parse_input(io::stdin())?;\n\n    let book_version = Version::parse(&ctx.mdbook_version)?;\n    let version_req = VersionReq::parse(mdbook_preprocessor::MDBOOK_VERSION)?;\n\n    if !version_req.matches(&book_version) {\n        eprintln!(\n            \"Warning: The {} plugin was built against version {} of mdbook, \\\n             but we're being called from version {}\",\n            pre.name(),\n            mdbook_preprocessor::MDBOOK_VERSION,\n            ctx.mdbook_version\n        );\n    }\n\n    let processed_book = pre.run(&ctx, book)?;\n    serde_json::to_writer(io::stdout(), &processed_book)?;\n\n    Ok(())\n}\n\nfn handle_supports(pre: &dyn Preprocessor, sub_args: &ArgMatches) -> ! {\n    let renderer = sub_args\n        .get_one::<String>(\"renderer\")\n        .expect(\"Required argument\");\n    let supported = pre.supports_renderer(renderer).unwrap();\n\n    // Signal whether the renderer is supported by exiting with 1 or 0.\n    if supported {\n        process::exit(0);\n    } else {\n        process::exit(1);\n    }\n}\n\n/// The actual implementation of the `Nop` preprocessor. This would usually go\n/// in your main `lib.rs` file.\n#[allow(unreachable_pub, reason = \"wouldn't be a problem in a proper lib.rs\")]\nmod nop_lib {\n    use super::*;\n\n    /// A no-op preprocessor.\n    pub struct Nop;\n\n    impl Nop {\n        pub fn new() -> Nop {\n            Nop\n        }\n    }\n\n    impl Preprocessor for Nop {\n        fn name(&self) -> &str {\n            \"nop-preprocessor\"\n        }\n\n        fn run(&self, ctx: &PreprocessorContext, book: Book) -> Result<Book> {\n            // In testing we want to tell the preprocessor to blow up by setting a\n            // particular config value\n            match ctx\n                .config\n                .get::<bool>(\"preprocessor.nop-preprocessor.blow-up\")\n            {\n                Ok(Some(true)) => anyhow::bail!(\"Boom!!1!\"),\n                Ok(_) => {}\n                Err(e) => anyhow::bail!(\"expect bool for blow-up: {e}\"),\n            }\n\n            // we *are* a no-op preprocessor after all\n            Ok(book)\n        }\n\n        fn supports_renderer(&self, renderer: &str) -> Result<bool> {\n            Ok(renderer != \"not-supported\")\n        }\n    }\n\n    #[cfg(test)]\n    mod test {\n        use super::*;\n\n        #[test]\n        fn nop_preprocessor_run() {\n            let input_json = r##\"[\n                {\n                    \"root\": \"/path/to/book\",\n                    \"config\": {\n                        \"book\": {\n                            \"authors\": [\"AUTHOR\"],\n                            \"language\": \"en\",\n                            \"src\": \"src\",\n                            \"title\": \"TITLE\"\n                        },\n                        \"preprocessor\": {\n                            \"nop\": {}\n                        }\n                    },\n                    \"renderer\": \"html\",\n                    \"mdbook_version\": \"0.4.21\"\n                },\n                {\n                    \"items\": [\n                        {\n                            \"Chapter\": {\n                                \"name\": \"Chapter 1\",\n                                \"content\": \"# Chapter 1\\n\",\n                                \"number\": [1],\n                                \"sub_items\": [],\n                                \"path\": \"chapter_1.md\",\n                                \"source_path\": \"chapter_1.md\",\n                                \"parent_names\": []\n                            }\n                        }\n                    ]\n                }\n            ]\"##;\n            let input_json = input_json.as_bytes();\n\n            let (ctx, book) = mdbook_preprocessor::parse_input(input_json).unwrap();\n            let expected_book = book.clone();\n            let result = Nop::new().run(&ctx, book);\n            assert!(result.is_ok());\n\n            // The nop-preprocessor should not have made any changes to the book content.\n            let actual_book = result.unwrap();\n            assert_eq!(actual_book, expected_book);\n        }\n    }\n}\n"
  },
  {
    "path": "examples/remove-emphasis/.gitignore",
    "content": "book\n"
  },
  {
    "path": "examples/remove-emphasis/book.toml",
    "content": "[book]\ntitle = \"remove-emphasis\"\n\n[preprocessor.remove-emphasis]\ncommand = \"cargo run --manifest-path=mdbook-remove-emphasis/Cargo.toml --locked\"\n"
  },
  {
    "path": "examples/remove-emphasis/mdbook-remove-emphasis/Cargo.toml",
    "content": "[package]\nname = \"mdbook-remove-emphasis\"\nversion = \"0.1.0\"\nedition.workspace = true\npublish = false\n\n[dependencies]\nmdbook-preprocessor.workspace = true\npulldown-cmark = { workspace = true, default-features = false }\npulldown-cmark-to-cmark = \"22.0.0\"\nserde_json.workspace = true\n\n[[bin]]\nname = \"mdbook-remove-emphasis\"\n# This is tested through a separate test from the main package.\ntest = false\n"
  },
  {
    "path": "examples/remove-emphasis/mdbook-remove-emphasis/src/main.rs",
    "content": "//! This is a demonstration of an mdBook preprocessor which parses markdown\n//! and removes any instances of emphasis.\n\nuse mdbook_preprocessor::book::{Book, Chapter};\nuse mdbook_preprocessor::errors::Result;\nuse mdbook_preprocessor::{Preprocessor, PreprocessorContext};\nuse pulldown_cmark::{Event, Parser, Tag, TagEnd};\nuse std::io;\n\nfn main() {\n    let mut args = std::env::args().skip(1);\n    match args.next().as_deref() {\n        Some(\"supports\") => {\n            // Supports all renderers.\n            return;\n        }\n        Some(arg) => {\n            eprintln!(\"unknown argument: {arg}\");\n            std::process::exit(1);\n        }\n        None => {}\n    }\n\n    if let Err(e) = handle_preprocessing() {\n        eprintln!(\"{e}\");\n        std::process::exit(1);\n    }\n}\n\nstruct RemoveEmphasis;\n\nimpl Preprocessor for RemoveEmphasis {\n    fn name(&self) -> &str {\n        \"remove-emphasis\"\n    }\n\n    fn run(&self, _ctx: &PreprocessorContext, mut book: Book) -> Result<Book> {\n        let mut total = 0;\n        book.for_each_chapter_mut(|ch| match remove_emphasis(&mut total, ch) {\n            Ok(s) => ch.content = s,\n            Err(e) => eprintln!(\"failed to process chapter: {e:?}\"),\n        });\n        eprintln!(\"removed {total} emphasis\");\n        Ok(book)\n    }\n}\n\n// ANCHOR: remove_emphasis\nfn remove_emphasis(num_removed_items: &mut usize, chapter: &mut Chapter) -> Result<String> {\n    let mut buf = String::with_capacity(chapter.content.len());\n\n    let events = Parser::new(&chapter.content).filter(|e| match e {\n        Event::Start(Tag::Emphasis) | Event::Start(Tag::Strong) => {\n            *num_removed_items += 1;\n            false\n        }\n        Event::End(TagEnd::Emphasis) | Event::End(TagEnd::Strong) => false,\n        _ => true,\n    });\n\n    Ok(pulldown_cmark_to_cmark::cmark(events, &mut buf).map(|_| buf)?)\n}\n// ANCHOR_END: remove_emphasis\n\npub fn handle_preprocessing() -> Result<()> {\n    let pre = RemoveEmphasis;\n    let (ctx, book) = mdbook_preprocessor::parse_input(io::stdin())?;\n\n    let processed_book = pre.run(&ctx, book)?;\n    serde_json::to_writer(io::stdout(), &processed_book)?;\n\n    Ok(())\n}\n"
  },
  {
    "path": "examples/remove-emphasis/src/SUMMARY.md",
    "content": "# Summary\n\n- [Chapter 1](./chapter_1.md)\n"
  },
  {
    "path": "examples/remove-emphasis/src/chapter_1.md",
    "content": "# Chapter 1\n\nThis has *light emphasis* and **bold emphasis**.\n"
  },
  {
    "path": "examples/remove-emphasis/test.rs",
    "content": "//! A test to ensure that the remove-emphasis example works.\n\n#[test]\nfn remove_emphasis_works() {\n    // Tests that the remove-emphasis example works as expected.\n\n    // Workaround for https://github.com/rust-lang/mdBook/issues/1424\n    std::env::set_current_dir(\"examples/remove-emphasis\").unwrap();\n    let book = mdbook_driver::MDBook::load(\".\").unwrap();\n    book.build().unwrap();\n    let ch1 = std::fs::read_to_string(\"book/chapter_1.html\").unwrap();\n    assert!(ch1.contains(\"This has light emphasis and bold emphasis.\"));\n}\n"
  },
  {
    "path": "guide/book.toml",
    "content": "[book]\ntitle = \"mdBook Documentation\"\ndescription = \"Create book from markdown files. Like Gitbook but implemented in Rust\"\nauthors = [\"Mathieu David\", \"Michael-F-Bryan\"]\nlanguage = \"en\"\n\n[rust]\nedition = \"2018\"\n\n[output.html]\nsmart-punctuation = true\nmathjax-support = true\nsite-url = \"/mdBook/\"\ngit-repository-url = \"https://github.com/rust-lang/mdBook/tree/master/guide\"\nedit-url-template = \"https://github.com/rust-lang/mdBook/edit/master/guide/{path}\"\nhash-files = true\n\n[output.html.playground]\neditable = true\nline-numbers = true\n\n[output.html.code.hidelines]\npython = \"~\"\n\n[output.html.search]\nlimit-results = 20\nuse-boolean-and = true\nboost-title = 2\nboost-hierarchy = 2\nboost-paragraph = 1\nexpand = true\nheading-split-level = 2\n\n[output.html.redirect]\n\"/format/config.html\" = \"configuration/index.html\"\n\n[preprocessor.guide-helper]\ncommand = \"cargo run --quiet --manifest-path guide-helper/Cargo.toml\"\n\n[build]\nextra-watch-dirs = [\"guide-helper/src\"]\n"
  },
  {
    "path": "guide/guide-helper/Cargo.toml",
    "content": "[package]\nname = \"guide-helper\"\npublish = false\nedition.workspace = true\nlicense.workspace = true\nrepository.workspace = true\nrust-version.workspace = true\n\n[dependencies]\nmdbook-preprocessor.workspace = true\nsemver.workspace = true\nserde_json.workspace = true\ntoml.workspace = true\n\n[lints]\nworkspace = true\n"
  },
  {
    "path": "guide/guide-helper/src/lib.rs",
    "content": "//! Preprocessor for the mdBook guide.\n\nuse mdbook_preprocessor::book::Book;\nuse mdbook_preprocessor::errors::Result;\nuse mdbook_preprocessor::{Preprocessor, PreprocessorContext};\nuse semver::{Version, VersionReq};\nuse std::io;\n\n/// Preprocessing entry point.\npub fn handle_preprocessing() -> Result<()> {\n    let pre = GuideHelper;\n    let (ctx, book) = mdbook_preprocessor::parse_input(io::stdin())?;\n\n    let book_version = Version::parse(&ctx.mdbook_version)?;\n    let version_req = VersionReq::parse(mdbook_preprocessor::MDBOOK_VERSION)?;\n\n    if !version_req.matches(&book_version) {\n        eprintln!(\n            \"warning: The {} plugin was built against version {} of mdbook, \\\n             but we're being called from version {}\",\n            pre.name(),\n            mdbook_preprocessor::MDBOOK_VERSION,\n            ctx.mdbook_version\n        );\n    }\n\n    let processed_book = pre.run(&ctx, book)?;\n    serde_json::to_writer(io::stdout(), &processed_book)?;\n\n    Ok(())\n}\n\nstruct GuideHelper;\n\nimpl Preprocessor for GuideHelper {\n    fn name(&self) -> &str {\n        \"guide-helper\"\n    }\n\n    fn run(&self, _ctx: &PreprocessorContext, mut book: Book) -> Result<Book> {\n        insert_version(&mut book);\n        Ok(book)\n    }\n}\n\nfn insert_version(book: &mut Book) {\n    let path = std::env::current_dir()\n        .unwrap()\n        .parent()\n        .unwrap()\n        .join(\"Cargo.toml\");\n    let manifest_contents = std::fs::read_to_string(&path).unwrap();\n    let manifest: toml::Value = toml::from_str(&manifest_contents).unwrap();\n    let version = manifest[\"package\"][\"version\"].as_str().unwrap();\n    const MARKER: &str = \"{{ mdbook-version }}\";\n    book.for_each_chapter_mut(|ch| {\n        if ch.content.contains(MARKER) {\n            ch.content = ch.content.replace(MARKER, version);\n        }\n    });\n}\n"
  },
  {
    "path": "guide/guide-helper/src/main.rs",
    "content": "//! Preprocessor for the mdBook guide.\n\nfn main() {\n    let mut args = std::env::args().skip(1);\n    match args.next().as_deref() {\n        Some(\"supports\") => {\n            // Supports all renderers.\n            return;\n        }\n        Some(arg) => {\n            eprintln!(\"unknown argument: {arg}\");\n            std::process::exit(1);\n        }\n        None => {}\n    }\n\n    if let Err(e) = guide_helper::handle_preprocessing() {\n        eprintln!(\"{e:?}\");\n        std::process::exit(1);\n    }\n}\n"
  },
  {
    "path": "guide/src/404.md",
    "content": "# Document not found (404)\n\nThis URL is invalid, sorry. Try the search instead!"
  },
  {
    "path": "guide/src/README.md",
    "content": "# Introduction\n\n<style>\n    .mdbook-version {\n        position: absolute;\n        right: 20px;\n        top: 60px;\n        background-color: var(--theme-popup-bg);\n        border-radius: 8px;\n        padding: 2px 5px 2px 5px;\n        border: 1px solid var(--theme-popup-border);\n        font-size: 0.9em;\n    }\n</style>\n\n<div class=\"mdbook-version\">\nVersion: {{ mdbook-version }}\n</div>\n\n**mdBook** is a command line tool to create books with Markdown.\nIt is ideal for creating product or API documentation, tutorials, course materials or anything that requires a clean,\neasily navigable and customizable presentation.\n\n* Lightweight [Markdown] syntax helps you focus more on your content\n* Integrated [search] support\n* Color [syntax highlighting] for code blocks for many different languages\n* [Theme] files allow customizing the formatting of the output\n* [Preprocessors] can provide extensions for custom syntax and modifying content\n* [Backends] can render the output to multiple formats\n* Written in [Rust] for speed, safety, and simplicity\n* Automated testing of [Rust code samples]\n\nThis guide is an example of what mdBook produces.\nmdBook is used by the Rust programming language project, and [The Rust Programming Language][trpl] book is another fine example of mdBook in action.\n\n[Markdown]: format/markdown.md\n[search]: guide/reading.md#search\n[syntax highlighting]: format/theme/syntax-highlighting.md\n[theme]: format/theme/index.html\n[preprocessors]: format/configuration/preprocessors.md\n[backends]: format/configuration/renderers.md\n[Rust]: https://www.rust-lang.org/\n[trpl]: https://doc.rust-lang.org/book/\n[Rust code samples]: cli/test.md\n\n## Contributing\n\nmdBook is free and open source. You can find the source code on\n[GitHub](https://github.com/rust-lang/mdBook) and issues and feature requests can be posted on\nthe [GitHub issue tracker](https://github.com/rust-lang/mdBook/issues). mdBook relies on the community to fix bugs and\nadd features: if you'd like to contribute, please read\nthe [CONTRIBUTING](https://github.com/rust-lang/mdBook/blob/master/CONTRIBUTING.md) guide and consider opening\na [pull request](https://github.com/rust-lang/mdBook/pulls).\n\n## License\n\nThe mdBook source and documentation are released under\nthe [Mozilla Public License v2.0](https://www.mozilla.org/MPL/2.0/).\n"
  },
  {
    "path": "guide/src/SUMMARY.md",
    "content": "# Summary\n\n[Introduction](README.md)\n\n# User guide\n\n- [Installation](guide/installation.md)\n- [Reading books](guide/reading.md)\n- [Creating a book](guide/creating.md)\n\n# Reference guide\n\n- [Command-line tool](cli/README.md)\n    - [init](cli/init.md)\n    - [build](cli/build.md)\n    - [watch](cli/watch.md)\n    - [serve](cli/serve.md)\n    - [test](cli/test.md)\n    - [clean](cli/clean.md)\n    - [completions](cli/completions.md)\n- [Format](format/README.md)\n    - [SUMMARY.md](format/summary.md)\n        - [Draft chapter]()\n    - [Configuration](format/configuration/README.md)\n        - [General](format/configuration/general.md)\n        - [Preprocessors](format/configuration/preprocessors.md)\n        - [Renderers](format/configuration/renderers.md)\n        - [Environment variables](format/configuration/environment-variables.md)\n    - [Theme](format/theme/README.md)\n        - [index.hbs](format/theme/index-hbs.md)\n        - [Syntax highlighting](format/theme/syntax-highlighting.md)\n        - [Editor](format/theme/editor.md)\n    - [MathJax support](format/mathjax.md)\n    - [mdBook-specific features](format/mdbook.md)\n    - [Markdown](format/markdown.md)\n- [Continuous integration](continuous-integration.md)\n- [For developers](for_developers/README.md)\n    - [Preprocessors](for_developers/preprocessors.md)\n    - [Alternative backends](for_developers/backends.md)\n\n-----------\n\n[Contributors](misc/contributors.md)\n"
  },
  {
    "path": "guide/src/cli/README.md",
    "content": "# Command-line tool\n\nThe `mdbook` command-line tool is used to create and build books.\nAfter you have [installed](../guide/installation.md) `mdbook`, you can run the `mdbook help` command in your terminal to view the available commands.\n\nThis following sections provide in-depth information on the different commands available.\n\n* [`mdbook init <directory>`](init.md) --- Creates a new book with minimal boilerplate to start with.\n* [`mdbook build`](build.md) --- Renders the book.\n* [`mdbook watch`](watch.md) --- Rebuilds the book any time a source file changes.\n* [`mdbook serve`](serve.md) --- Runs a web server to view the book, and rebuilds on changes.\n* [`mdbook test`](test.md) --- Tests Rust code samples.\n* [`mdbook clean`](clean.md) --- Deletes the rendered output.\n* [`mdbook completions`](completions.md) --- Support for shell auto-completion.\n"
  },
  {
    "path": "guide/src/cli/arg-watcher.md",
    "content": "#### `--watcher`\n\nThere are different backends used to determine when a file has changed.\n\n* `poll` (default) --- Checks for file modifications by scanning the filesystem every second.\n* `native` --- Uses the native operating system facilities to receive notifications when files change.\n  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)\n"
  },
  {
    "path": "guide/src/cli/build.md",
    "content": "# The build command\n\nThe build command is used to render your book:\n\n```bash\nmdbook build\n```\n\nIt will try to parse your `SUMMARY.md` file to understand the structure of your\nbook and fetch the corresponding files. Note that this will also create files\nmentioned in `SUMMARY.md` which are not yet present.\n\nThe rendered output will maintain the same directory structure as the source for\nconvenience. Large books will therefore remain structured when rendered.\n\n#### Specify a directory\n\nThe `build` command can take a directory as an argument to use as the book's\nroot instead of the current working directory.\n\n```bash\nmdbook build path/to/book\n```\n\n#### `--open`\n\nWhen you use the `--open` (`-o`) flag, mdbook will open the rendered book in\nyour default web browser after building it.\n\n#### `--dest-dir`\n\nThe `--dest-dir` (`-d`) option allows you to change the output directory for the\nbook. Relative paths are interpreted relative to the current directory. If\nnot specified it will default to the value of the `build.build-dir` key in\n`book.toml`, or to `./book`.\n\n-------------------\n\n***Note:*** *The build command copies all files (excluding files with `.md` extension) from the source directory\ninto the build directory.*\n"
  },
  {
    "path": "guide/src/cli/clean.md",
    "content": "# The clean command\n\nThe clean command is used to delete the generated book and any other build\nartifacts.\n\n```bash\nmdbook clean\n```\n\n#### Specify a directory\n\nThe `clean` command can take a directory as an argument to use as the book's\nroot instead of the current working directory.\n\n```bash\nmdbook clean path/to/book\n```\n\n#### `--dest-dir`\n\nThe `--dest-dir` (`-d`) option allows you to override the book's output\ndirectory, which will be deleted by this command. Relative paths are interpreted\nrelative to the current directory. If not specified it will default to the\nvalue of the `build.build-dir` key in `book.toml`, or to `./book`.\n\n```bash\nmdbook clean --dest-dir=path/to/book\n```\n\n`path/to/book` could be absolute or relative.\n"
  },
  {
    "path": "guide/src/cli/completions.md",
    "content": "# The completions command\n\nThe completions command is used to generate auto-completions for some common shells.\nThis 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.\n\nThe completions first need to be installed for your shell:\n\n```bash\n# bash\nmdbook completions bash > ~/.local/share/bash-completion/completions/mdbook\n# oh-my-zsh\nmdbook completions zsh > ~/.oh-my-zsh/completions/_mdbook\nautoload -U compinit && compinit\n```\n\nThe command prints a completion script for the given shell.\nRun `mdbook completions --help` for a list of supported shells.\n\nWhere to place the completions depend on which shell you are using and your operating system.\nConsult your shell's documentation for more information one where to place the script.\n"
  },
  {
    "path": "guide/src/cli/init.md",
    "content": "# The init command\n\nThere is some minimal boilerplate that is the same for every new book. It's for\nthis purpose that mdBook includes an `init` command.\n\nThe `init` command is used like this:\n\n```bash\nmdbook init\n```\n\nWhen using the `init` command for the first time, a couple of files will be set\nup for you:\n```bash\nbook-test/\n├── book\n└── src\n    ├── chapter_1.md\n    └── SUMMARY.md\n```\n\n- The `src` directory is where you write your book in markdown. It contains all\n  the source files, configuration files, etc.\n\n- The `book` directory is where your book is rendered. All the output is ready\n  to be uploaded to a server to be seen by your audience.\n\n- The `SUMMARY.md` is the skeleton of your\n  book, and is discussed in more detail [in another\n  chapter](../format/summary.md).\n\n#### Tip: Generate chapters from SUMMARY.md\n\nWhen a `SUMMARY.md` file already exists, the `init` command will first parse it\nand generate the missing files according to the paths used in the `SUMMARY.md`.\nThis allows you to think and create the whole structure of your book and then\nlet mdBook generate it for you.\n\n#### Specify a directory\n\nThe `init` command can take a directory as an argument to use as the book's root\ninstead of the current working directory.\n\n```bash\nmdbook init path/to/book\n```\n\n#### `--theme`\n\nWhen you use the `--theme` flag, the default theme will be copied into a\ndirectory called `theme` in your source directory so that you can modify it.\n\nThe theme is selectively overwritten, this means that if you don't want to\noverwrite a specific file, just delete it and the default file will be used.\n\n#### `--title`\n\nSpecify a title for the book. If not supplied, an interactive prompt will ask for \na title. \n\n```bash\nmdbook init --title=\"my amazing book\"\n```\n\n#### `--ignore`\n\nCreate a `.gitignore` file configured to ignore the `book` directory created when [building] a book. \nIf not supplied, an interactive prompt will ask whether it should be created.\n\n```bash\nmdbook init --ignore=none\n```\n\n```bash\nmdbook init --ignore=git\n```\n\n[building]: build.md\n\n#### `--force`\n\nSkip the prompts to create a `.gitignore` and for the title for the book.\n"
  },
  {
    "path": "guide/src/cli/serve.md",
    "content": "# The serve command\n\nThe serve command is used to preview a book by serving it via HTTP at\n`localhost:3000` by default: \n\n```bash\nmdbook serve\n```\n\nThe `serve` command  watches the book's `src` directory for\nchanges, rebuilding the book and refreshing clients for each change; this includes\nre-creating deleted files still mentioned in `SUMMARY.md`! A websocket\nconnection is used to trigger the client-side refresh.\n\n***Note:*** *The `serve` command is for testing a book's HTML output, and is not\nintended to be a complete HTTP server for a website.*\n\n#### Specify a directory\n\nThe `serve` command can take a directory as an argument to use as the book's\nroot instead of the current working directory.\n\n```bash\nmdbook serve path/to/book\n```\n\n### Server options\n\nThe `serve` hostname defaults to `localhost`, and the port defaults to `3000`. Either option can be specified on the command line:\n\n```bash\nmdbook serve path/to/book -p 8000 -n 127.0.0.1 \n```\n\n#### `--open`\n\nWhen you use the `--open` (`-o`) flag, mdbook will open the book in your\ndefault web browser after starting the server.\n\n#### `--dest-dir`\n\nThe `--dest-dir` (`-d`) option allows you to change the output directory for the\nbook. Relative paths are interpreted relative to the current directory. If\nnot specified it will default to the value of the `build.build-dir` key in\n`book.toml`, or to `./book`.\n\n{{#include arg-watcher.md}}\n\n#### Specify exclude patterns\n\nThe `serve` command will not automatically trigger a build for files listed in\nthe `.gitignore` file in the book root directory. The `.gitignore` file may\ncontain file patterns described in the [gitignore\ndocumentation](https://git-scm.com/docs/gitignore). This can be useful for\nignoring temporary files created by some editors.\n\n***Note:*** *Only the `.gitignore` from the book root directory is used. Global\n`$HOME/.gitignore` or `.gitignore` files in parent directories are not used.*\n"
  },
  {
    "path": "guide/src/cli/test.md",
    "content": "# The test command\n\nWhen writing a book, you sometimes need to automate some tests. For example,\n[The Rust Programming Book](https://doc.rust-lang.org/stable/book/) uses a lot\nof code examples that could get outdated. Therefore it is very important for\nthem to be able to automatically test these code examples.\n\nmdBook supports a `test` command that will run all available tests in a book. At\nthe moment, only Rust tests are supported.\n\n#### Disable tests on a code block\n\nrustdoc doesn't test code blocks which contain the `ignore` attribute:\n\n    ```rust,ignore\n    fn main() {}\n    ```\n\nrustdoc also doesn't test code blocks which specify a language other than Rust:\n\n    ```markdown\n    **Foo**: _bar_\n    ```\n\nrustdoc *does* test code blocks which have no language specified:\n\n    ```\n    This is going to cause an error!\n    ```\n\n#### Specify a directory\n\nThe `test` command can take a directory as an argument to use as the book's root\ninstead of the current working directory.\n\n```bash\nmdbook test path/to/book\n```\n\n#### `--library-path`\n\nThe `--library-path` (`-L`) option allows you to add directories to the library\nsearch path used by `rustdoc` when it builds and tests the examples. Multiple\ndirectories can be specified with multiple options (`-L foo -L bar`) or with a\ncomma-delimited list (`-L foo,bar`). The path should point to the Cargo\n[build cache](https://doc.rust-lang.org/cargo/guide/build-cache.html) `deps` directory that\ncontains the build output of your project. For example, if your Rust project's book is in a directory\nnamed `my-book`, the following command would include the crate's dependencies when running `test`:\n\n```shell\nmdbook test my-book -L target/debug/deps/\n```\n\nSee the `rustdoc` command-line [documentation](https://doc.rust-lang.org/rustdoc/command-line-arguments.html#-l--library-path-where-to-look-for-dependencies)\nfor more information.\n\n#### `--chapter`\n\nThe `--chapter` (`-c`) option allows you to test a specific chapter of the\nbook using the chapter name or the relative path to the chapter.\n"
  },
  {
    "path": "guide/src/cli/watch.md",
    "content": "# The watch command\n\nThe `watch` command is useful when you want your book to be rendered on every\nfile change. You could repeatedly issue `mdbook build` every time a file is\nchanged. But using `mdbook watch` once will watch your files and will trigger a\nbuild automatically whenever you modify a file; this includes re-creating\ndeleted files still mentioned in `SUMMARY.md`!\n\n#### Specify a directory\n\nThe `watch` command can take a directory as an argument to use as the book's\nroot instead of the current working directory.\n\n```bash\nmdbook watch path/to/book\n```\n\n#### `--open`\n\nWhen you use the `--open` (`-o`) option, mdbook will open the rendered book in\nyour default web browser.\n\n#### `--dest-dir`\n\nThe `--dest-dir` (`-d`) option allows you to change the output directory for the\nbook. Relative paths are interpreted relative to the current directory. If\nnot specified it will default to the value of the `build.build-dir` key in\n`book.toml`, or to `./book`.\n\n{{#include arg-watcher.md}}\n\n#### Specify exclude patterns\n\nThe `watch` command will not automatically trigger a build for files listed in\nthe `.gitignore` file in the book root directory. The `.gitignore` file may\ncontain file patterns described in the [gitignore\ndocumentation](https://git-scm.com/docs/gitignore). This can be useful for\nignoring temporary files created by some editors.\n\n_Note: Only `.gitignore` from book root directory is used. Global\n`$HOME/.gitignore` or `.gitignore` files in parent directories are not used._\n"
  },
  {
    "path": "guide/src/continuous-integration.md",
    "content": "# Running `mdbook` in continuous integration\n\nThere are a variety of services such as [GitHub Actions] or [GitLab CI/CD] which can be used to test and deploy your book automatically.\n\nThe following provides some general guidelines on how to configure your service to run mdBook.\nSpecific recipes can be found at the [Automated Deployment] wiki page.\n\n[GitHub Actions]: https://docs.github.com/en/actions\n[GitLab CI/CD]: https://docs.gitlab.com/ee/ci/\n[Automated Deployment]: https://github.com/rust-lang/mdBook/wiki/Automated-Deployment\n\n## Installing mdBook\n\nThere are several different strategies for installing mdBook.\nThe particular method depends on your needs and preferences.\n\n### Pre-compiled binaries\n\nPerhaps the easiest method is to use the pre-compiled binaries found on the [GitHub Releases page][releases].\nA simple approach would be to use the popular `curl` CLI tool to download the executable:\n\n```sh\nmkdir bin\ncurl -sSL https://github.com/rust-lang/mdBook/releases/download/{{ mdbook-version }}/mdbook-{{ mdbook-version }}-x86_64-unknown-linux-gnu.tar.gz | tar -xz --directory=bin\nbin/mdbook build\n```\n\nSome considerations for this approach:\n\n* This is relatively fast, and does not necessarily require dealing with caching.\n* This does not require installing Rust.\n* Specifying a specific URL means you have to manually update your script to get a new version.\n  This may be a benefit if you want to lock to a specific version.\n  However, some users prefer to automatically get a newer version when they are published.\n* You are reliant on the GitHub CDN being available.\n\n[releases]: https://github.com/rust-lang/mdBook/releases\n\n### Building from source\n\nBuilding from source will require having Rust installed.\nSome services have Rust pre-installed, but if your service does not, you will need to add a step to install it.\n\nAfter Rust is installed, `cargo install` can be used to build and install mdBook.\nWe recommend using a SemVer version specifier so that you get the latest **non-breaking** version of mdBook.\nFor example:\n\n```sh\ncargo install mdbook --no-default-features --features search --vers \"^0.4\" --locked\n```\n\nThis includes several recommended options:\n\n* `--no-default-features` --- Disables features like the HTTP server used by `mdbook serve` that is likely not needed on CI.\n  This will speed up the build time significantly.\n* `--features search` --- Disabling default features means you should then manually enable features that you want, such as the built-in [search] capability.\n* `--vers \"^0.4\"` --- This will install the most recent version of the `0.4` series.\n  However, versions after like `0.5.0` won't be installed, as they may break your build.\n  Cargo will automatically upgrade mdBook if you have an older version already installed.\n* `--locked` --- This will use the dependencies that were used when mdBook was released.\n  Without `--locked`, it will use the latest version of all dependencies, which may include some fixes since the last release, but may also (rarely) cause build problems.\n\nYou will likely want to investigate caching options, as building mdBook can be somewhat slow.\n\n[search]: guide/reading.md#search\n\n## Running tests\n\nYou may want to run tests using [`mdbook test`] every time you push a change or create a pull request.\nThis can be used to validate Rust code examples in the book.\n\nThis will require having Rust installed.\nSome services have Rust pre-installed, but if your service does not, you will need to add a step to install it.\n\nOther than making sure the appropriate version of Rust is installed, there's not much more than just running `mdbook test` from the book directory.\n\nYou may also want to consider running other kinds of tests, like [mdbook-linkcheck] which will check for broken links.\nOr if you have your own style checks, spell checker, or any other tests it might be good to run them in CI.\n\n[`mdbook test`]: cli/test.md\n[mdbook-linkcheck]: https://github.com/Michael-F-Bryan/mdbook-linkcheck#continuous-integration\n\n## Deploying\n\nYou may want to automatically deploy your book.\nSome may want to do this every time a change is pushed, and others may want to only deploy when a specific release is tagged.\n\nYou'll also need to understand the specifics on how to push a change to your web service.\nFor example, [GitHub Pages] just requires committing the output onto a specific git branch.\nOther services may require using something like SSH to connect to a remote server.\n\nThe basic outline is that you need to run `mdbook build` to generate the output, and then transfer the files (which are in the `book` directory) to the correct location.\n\nYou may then want to consider if you need to invalidate any caches on your web service.\n\nSee the [Automated Deployment] wiki page for examples of various different services.\n\n[GitHub Pages]: https://docs.github.com/en/pages\n\n### 404 handling\n\nmdBook automatically generates a 404 page to be used for broken links.\nThe default output is a file named `404.html` at the root of the book.\nSome services like [GitHub Pages] will automatically use this page for broken links.\nFor other services, you may want to consider configuring the web server to use this page as it will provide the reader navigation to get back to the book.\n\nIf your book is not deployed at the root of the domain, then you should set the [`output.html.site-url`] setting so that the 404 page works correctly.\nIt needs to know where the book is deployed in order to load the static files (like CSS) correctly.\nFor example, this guide is deployed at <https://rust-lang.github.io/mdBook/>, and the `site-url` setting is configured like this:\n\n```toml\n# book.toml\n[output.html]\nsite-url = \"/mdBook/\"\n```\n\nYou can customize the look of the 404 page by creating a file named `src/404.md` in your book.\nIf you want to use a different filename, you can set [`output.html.input-404`] to a different filename.\n\n[`output.html.site-url`]: format/configuration/renderers.md#html-renderer-options\n[`output.html.input-404`]: format/configuration/renderers.md#html-renderer-options\n"
  },
  {
    "path": "guide/src/for_developers/README.md",
    "content": "# For developers\n\nWhile `mdbook` is mainly used as a command line tool, you can also import the\nunderlying libraries directly and use those to manage a book. It also has a fairly\nflexible plugin mechanism, allowing you to create your own custom tooling and\nconsumers (often referred to as *backends*) if you need to do some analysis of\nthe book or render it in a different format.\n\nThe *For Developers* chapters are here to show you the more advanced usage of\n`mdbook`.\n\nThe two main ways a developer can hook into the book's build process is via,\n\n- [Preprocessors](preprocessors.md)\n- [Alternative Backends](backends.md)\n\n## The build process\n\nThe process of rendering a book project goes through several steps.\n\n1. Load the book\n    - Parse the `book.toml`, falling back to the default `Config` if it doesn't\n       exist\n    - Load the book chapters into memory\n    - Discover which preprocessors/backends should be used\n2. For each backend:\n   1. Run all the preprocessors.\n   2. Call the backend to render the processed result.\n\n## Using `mdbook` as a library\n\nThe `mdbook` binary is just a wrapper around the underlying mdBook crates,\nexposing their functionality as a command-line program. If you want to\nprogrammatically drive mdBook, you can use the [`mdbook-driver`] crate.\nThis can be used to add your own functionality or tweak the build process.\n\nThe easiest way to find out how to use the `mdbook-driver` crate is by looking at the\n[API Docs]. The top level documentation explains how one would use the\n[`MDBook`] type to load and build a book, while the [config] module gives a good\nexplanation on the configuration system.\n\n[`MDBook`]: https://docs.rs/mdbook-driver/latest/mdbook_driver/struct.MDBook.html\n[API Docs]: https://docs.rs/mdbook-driver/latest/mdbook_driver/\n[config]: https://docs.rs/mdbook-driver/latest/mdbook_driver/config/index.html\n"
  },
  {
    "path": "guide/src/for_developers/backends.md",
    "content": "# Alternative backends\n\nA \"backend\" is simply a program which `mdbook` will invoke during the book\nrendering process. This program is passed a JSON representation of the book and\nconfiguration information via `stdin`. Once the backend receives this\ninformation it is free to do whatever it wants.\n\nSee [Configuring Renderers](../format/configuration/renderers.md) for more information about using backends.\n\nThe community has developed several backends.\nSee the [Third Party Plugins] wiki page for a list of available backends.\n\n## Setting up\n\nThis page will step you through creating your own alternative backend in the form\nof a simple word counting program. Although it will be written in Rust, there's\nno reason why it couldn't be accomplished using something like Python or Ruby.\n\nFirst you'll want to create a new binary program and add `mdbook-renderer` as a\ndependency.\n\n```shell\n$ cargo new --bin mdbook-wordcount\n$ cd mdbook-wordcount\n$ cargo add mdbook-renderer\n```\n\nWhen our `mdbook-wordcount` plugin is invoked, `mdbook` will send it a JSON\nversion of [`RenderContext`] via our plugin's `stdin`. For convenience, there's\na [`RenderContext::from_json()`] constructor which will load a `RenderContext`.\n\nThis is all the boilerplate necessary for our backend to load the book.\n\n```rust\n// src/main.rs\nuse std::io;\nuse mdbook_renderer::RenderContext;\n\nfn main() {\n    let mut stdin = io::stdin();\n    let ctx = RenderContext::from_json(&mut stdin).unwrap();\n}\n```\n\n> **Note:** The `RenderContext` contains a `version` field. This lets backends\n> figure out whether they are compatible with the version of `mdbook` it's being\n> called by. This `version` comes directly from the corresponding field in\n> `mdbook`'s `Cargo.toml`.\n>\n> It is recommended that backends use the [`semver`] crate to inspect this field\n> and emit a warning if there may be a compatibility issue.\n\n## Inspecting the book\n\nNow our backend has a copy of the book, lets count how many words are in each\nchapter!\n\nBecause the `RenderContext` contains a [`Book`] field (`book`), and a `Book` has\nthe [`Book::iter()`] method for iterating over all items in a `Book`, this step\nturns out to be just as easy as the first.\n\n```rust\n\nfn main() {\n    let mut stdin = io::stdin();\n    let ctx = RenderContext::from_json(&mut stdin).unwrap();\n\n    for item in ctx.book.iter() {\n        if let BookItem::Chapter(ref ch) = *item {\n            let num_words = count_words(ch);\n            println!(\"{}: {}\", ch.name, num_words);\n        }\n    }\n}\n\nfn count_words(ch: &Chapter) -> usize {\n    ch.content.split_whitespace().count()\n}\n```\n\n\n## Enabling the backend\n\nNow we've got the basics running, we want to actually use it. First, install the\nprogram.\n\n```shell\n$ cargo install --path .\n```\n\nThen `cd` to the particular book you'd like to count the words of and update its\n`book.toml` file.\n\n```diff\n  [book]\n  title = \"mdBook Documentation\"\n  description = \"Create book from markdown files. Like Gitbook but implemented in Rust\"\n  authors = [\"Mathieu David\", \"Michael-F-Bryan\"]\n\n+ [output.html]\n\n+ [output.wordcount]\n```\n\nWhen it loads a book into memory, `mdbook` will inspect your `book.toml` file to\ntry and figure out which backends to use by looking for all `output.*` tables.\nIf none are provided it'll fall back to using the default HTML renderer.\n\nNotably, this means if you want to add your own custom backend you'll also need\nto make sure to add the HTML backend, even if its table just stays empty.\n\nNow you just need to build your book like normal, and everything should *Just\nWork*.\n\n```shell\n$ mdbook build\n...\n2018-01-16 07:31:15 [INFO] (mdbook::renderer): Invoking the \"mdbook-wordcount\" renderer\nmdBook: 126\nCommand Line Tool: 224\ninit: 283\nbuild: 145\nwatch: 146\nserve: 292\ntest: 139\nFormat: 30\nSUMMARY.md: 259\nConfiguration: 784\nTheme: 304\nindex.hbs: 447\nSyntax highlighting: 314\nMathJax Support: 153\nRust code specific features: 148\nFor Developers: 788\nAlternative Backends: 710\nContributors: 85\n```\n\nThe reason we didn't need to specify the full name/path of our `wordcount`\nbackend is because `mdbook` will try to *infer* the program's name via\nconvention. The executable for the `foo` backend is typically called\n`mdbook-foo`, with an associated `[output.foo]` entry in the `book.toml`. To\nexplicitly tell `mdbook` what command to invoke (it may require command-line\narguments or be an interpreted script), you can use the `command` field.\n\n```diff\n  [book]\n  title = \"mdBook Documentation\"\n  description = \"Create book from markdown files. Like Gitbook but implemented in Rust\"\n  authors = [\"Mathieu David\", \"Michael-F-Bryan\"]\n\n  [output.html]\n\n  [output.wordcount]\n+ command = \"python /path/to/wordcount.py\"\n```\n\n\n## Configuration\n\nNow imagine you don't want to count the number of words on a particular chapter\n(it might be generated text/code, etc). The canonical way to do this is via the\nusual `book.toml` configuration file by adding items to your `[output.foo]`\ntable.\n\nThe `Config` can be treated roughly as a nested hashmap which lets you call\nmethods like `get()` to access the config's contents, with a\n`get_deserialized()` convenience method for retrieving a value and automatically\ndeserializing to some arbitrary type `T`.\n\nTo implement this, we'll create our own serializable `WordcountConfig` struct\nwhich will encapsulate all configuration for this backend.\n\nFirst add `serde` and `serde_derive` to your `Cargo.toml`,\n\n```\n$ cargo add serde serde_derive\n```\n\nAnd then you can create the config struct,\n\n```rust\nuse serde_derive::{Serialize, Deserialize};\n\n...\n\n#[derive(Debug, Default, Serialize, Deserialize)]\n#[serde(default, rename_all = \"kebab-case\")]\npub struct WordcountConfig {\n  pub ignores: Vec<String>,\n}\n```\n\nNow we just need to deserialize the `WordcountConfig` from our `RenderContext`\nand then add a check to make sure we skip ignored chapters.\n\n```diff\n  fn main() {\n      let mut stdin = io::stdin();\n      let ctx = RenderContext::from_json(&mut stdin).unwrap();\n+     let cfg: WordcountConfig = ctx.config\n+         .get_deserialized(\"output.wordcount\")\n+         .unwrap_or_default();\n\n      for item in ctx.book.iter() {\n          if let BookItem::Chapter(ref ch) = *item {\n+             if cfg.ignores.contains(&ch.name) {\n+                 continue;\n+             }\n+\n              let num_words = count_words(ch);\n              println!(\"{}: {}\", ch.name, num_words);\n          }\n      }\n  }\n```\n\n\n## Output and signalling failure\n\nWhile it's nice to print word counts to the terminal when a book is built, it\nmight also be a good idea to output them to a file somewhere. `mdbook` tells a\nbackend where it should place any generated output via the `destination` field\nin [`RenderContext`].\n\n```diff\n+ use std::fs::{self, File};\n+ use std::io::{self, Write};\n- use std::io;\n  use mdbook::renderer::RenderContext;\n  use mdbook::book::{BookItem, Chapter};\n\n  fn main() {\n    ...\n\n+     let _ = fs::create_dir_all(&ctx.destination);\n+     let mut f = File::create(ctx.destination.join(\"wordcounts.txt\")).unwrap();\n+\n      for item in ctx.book.iter() {\n          if let BookItem::Chapter(ref ch) = *item {\n              ...\n\n              let num_words = count_words(ch);\n              println!(\"{}: {}\", ch.name, num_words);\n+             writeln!(f, \"{}: {}\", ch.name, num_words).unwrap();\n          }\n      }\n  }\n```\n\n> **Note:** There is no guarantee that the destination directory exists or is\n> empty (`mdbook` may leave the previous contents to let backends do caching),\n> so it's always a good idea to create it with `fs::create_dir_all()`.\n>\n> If the destination directory already exists, don't assume it will be empty.\n> To allow backends to cache the results from previous runs, `mdbook` may leave\n> old content in the directory.\n\nThere's always the possibility that an error will occur while processing a book\n(just look at all the `unwrap()`'s we've written already), so `mdbook` will\ninterpret a non-zero exit code as a rendering failure.\n\nFor example, if we wanted to make sure all chapters have an *even* number of\nwords, erroring out if an odd number is encountered, then you may do something\nlike this:\n\n```diff\n+ use std::process;\n  ...\n\n  fn main() {\n      ...\n\n      for item in ctx.book.iter() {\n          if let BookItem::Chapter(ref ch) = *item {\n              ...\n\n              let num_words = count_words(ch);\n              println!(\"{}: {}\", ch.name, num_words);\n              writeln!(f, \"{}: {}\", ch.name, num_words).unwrap();\n\n+             if cfg.deny_odds && num_words % 2 == 1 {\n+               eprintln!(\"{} has an odd number of words!\", ch.name);\n+               process::exit(1);\n+             }\n          }\n      }\n  }\n\n  #[derive(Debug, Default, Serialize, Deserialize)]\n  #[serde(default, rename_all = \"kebab-case\")]\n  pub struct WordcountConfig {\n      pub ignores: Vec<String>,\n+     pub deny_odds: bool,\n  }\n```\n\nNow, if we reinstall the backend and build a book,\n\n```shell\n$ cargo install --path . --force\n$ mdbook build /path/to/book\n...\n2018-01-16 21:21:39 [INFO] (mdbook::renderer): Invoking the \"wordcount\" renderer\nmdBook: 126\nCommand Line Tool: 224\ninit: 283\ninit has an odd number of words!\n2018-01-16 21:21:39 [ERROR] (mdbook::renderer): Renderer exited with non-zero return code.\n2018-01-16 21:21:39 [ERROR] (mdbook::utils): Error: Rendering failed\n2018-01-16 21:21:39 [ERROR] (mdbook::utils):    Caused By: The \"mdbook-wordcount\" renderer failed\n```\n\nAs you've probably already noticed, output from the plugin's subprocess is\nimmediately passed through to the user. It is encouraged for plugins to follow\nthe \"rule of silence\" and only generate output when necessary (e.g. an error in\ngeneration or a warning).\n\nAll environment variables are passed through to the backend, allowing you to use\nthe usual `MDBOOK_LOG` to control logging verbosity.\n\n## Wrapping up\n\nAlthough contrived, hopefully this example was enough to show how you'd create\nan alternative backend for `mdbook`. If you feel it's missing something, don't\nhesitate to create an issue in the [issue tracker] so we can improve the user\nguide.\n\nThe existing backends mentioned towards the start of this chapter should serve\nas a good example of how it's done in real life, so feel free to skim through\nthe source code or ask questions.\n\n\n[Third Party Plugins]: https://github.com/rust-lang/mdBook/wiki/Third-party-plugins\n[`RenderContext`]: https://docs.rs/mdbook-renderer/latest/mdbook_renderer/struct.RenderContext.html\n[`RenderContext::from_json()`]: https://docs.rs/mdbook-renderer/latest/mdbook_renderer/struct.RenderContext.html#method.from_json\n[`semver`]: https://crates.io/crates/semver\n[`Book`]: https://docs.rs/mdbook-renderer/latest/mdbook_renderer/book/struct.Book.html\n[`Book::iter()`]: https://docs.rs/mdbook-renderer/latest/mdbook_renderer/book/struct.Book.html#method.iter\n[`Config`]: https://docs.rs/mdbook-renderer/latest/mdbook_renderer/config/struct.Config.html\n[issue tracker]: https://github.com/rust-lang/mdBook/issues\n"
  },
  {
    "path": "guide/src/for_developers/mdbook-wordcount/Cargo.toml",
    "content": "[package]\nname = \"mdbook-wordcount\"\nversion = \"0.1.0\"\nauthors = [\"Michael Bryan <michaelfbryan@gmail.com>\"]\n\n[dependencies]\nmdbook = { path = \"../../../..\", version = \"*\" }\nserde = \"1.0\"\nserde_derive = \"1.0\"\n"
  },
  {
    "path": "guide/src/for_developers/mdbook-wordcount/src/main.rs",
    "content": "extern crate mdbook;\nextern crate serde;\n#[macro_use]\nextern crate serde_derive;\n\nuse std::process;\nuse std::fs::{self, File};\nuse std::io::{self, Write};\nuse mdbook::renderer::RenderContext;\nuse mdbook::book::{BookItem, Chapter};\n\nfn main() {\n    let mut stdin = io::stdin();\n    let ctx = RenderContext::from_json(&mut stdin).unwrap();\n    let cfg: WordcountConfig = ctx.config\n        .get_deserialized(\"output.wordcount\")\n        .unwrap_or_default();\n\n    let _ = fs::create_dir_all(&ctx.destination);\n    let mut f = File::create(ctx.destination.join(\"wordcounts.txt\")).unwrap();\n\n    for item in ctx.book.iter() {\n        if let BookItem::Chapter(ref ch) = *item {\n            if cfg.ignores.contains(&ch.name) {\n                continue;\n            }\n\n            let num_words = count_words(ch);\n            println!(\"{}: {}\", ch.name, num_words);\n            writeln!(f, \"{}: {}\", ch.name, num_words).unwrap();\n\n            if cfg.deny_odds && num_words % 2 == 1 {\n                eprintln!(\"{} has an odd number of words!\", ch.name);\n                process::exit(1);\n            }\n        }\n    }\n}\n\nfn count_words(ch: &Chapter) -> usize {\n    ch.content.split_whitespace().count()\n}\n\n#[derive(Debug, Default, Serialize, Deserialize)]\n#[serde(default, rename_all = \"kebab-case\")]\npub struct WordcountConfig {\n    pub ignores: Vec<String>,\n    pub deny_odds: bool,\n}\n"
  },
  {
    "path": "guide/src/for_developers/preprocessors.md",
    "content": "# Preprocessors\n\nA *preprocessor* is simply a bit of code which gets run immediately after the\nbook is loaded and before it gets rendered, allowing you to update and mutate\nthe book. Possible use cases are:\n\n- Creating custom helpers like `\\{{#include /path/to/file.md}}`\n- Substituting in latex-style expressions (`$$ \\frac{1}{3} $$`) with their\n  mathjax equivalents\n\nSee [Configuring Preprocessors](../format/configuration/preprocessors.md) for more information about using preprocessors.\n\n## Hooking into MDBook\n\nMDBook uses a fairly simple mechanism for discovering third party plugins.\nA new table is added to `book.toml` (e.g. `[preprocessor.foo]` for the `foo`\npreprocessor) and then `mdbook` will try to invoke the `mdbook-foo` program as\npart of the build process.\n\nOnce the preprocessor has been defined and the build process starts, mdBook executes the command defined in the `preprocessor.foo.command` key twice.\nThe first time it runs the preprocessor to determine if it supports the given renderer.\nmdBook passes two arguments to the process: the first argument is the string `supports` and the second argument is the renderer name.\nThe preprocessor should exit with a status code 0 if it supports the given renderer, or return a non-zero exit code if it does not.\n\nIf the preprocessor supports the renderer, then mdbook runs it a second time, passing JSON data into stdin.\nThe JSON consists of an array of `[context, book]` where `context` is the serialized object [`PreprocessorContext`] and `book` is a [`Book`] object containing the content of the book.\n\nThe preprocessor should return the JSON format of the [`Book`] object to stdout, with any modifications it wishes to perform.\n\nThe easiest way to get started is by creating your own implementation of the\n`Preprocessor` trait (e.g. in `lib.rs`) and then creating a shell binary which\ntranslates inputs to the correct `Preprocessor` method. For convenience, there\nis [an example no-op preprocessor] in the `examples/` directory which can easily\nbe adapted for other preprocessors.\n\n<details>\n<summary>Example no-op preprocessor</summary>\n\n```rust\n// nop-preprocessors.rs\n\n{{#include ../../../examples/nop-preprocessor.rs}}\n```\n</details>\n\n## Hints for implementing a preprocessor\n\nBy pulling in `mdbook-preprocessor` as a library, preprocessors can have access to the\nexisting infrastructure for dealing with books.\n\nFor example, a custom preprocessor could use the\n[`parse_input()`] function to deserialize the JSON written to\n`stdin`. Then each chapter of the `Book` can be mutated in-place via\n[`Book::for_each_mut()`], and then written to `stdout` with the `serde_json`\ncrate.\n\nChapters can be accessed either directly (by recursively iterating over\nchapters) or via the `Book::for_each_mut()` convenience method.\n\nThe `chapter.content` is just a string which happens to be markdown. While it's\nentirely possible to use regular expressions or do a manual find & replace,\nyou'll probably want to process the input into something more computer-friendly.\nThe [`mdbook-markdown`] crate exposes the [`pulldown-cmark`][pc] crate used by mdBook to parse Markdown. The [`pulldown-cmark-to-cmark`][pctc] crate can be used to translate events back into markdown text.\n\nThe following code block shows how to remove all emphasis from markdown,\nwithout accidentally breaking the document.\n\n```rust\n{{#rustdoc_include ../../../examples/remove-emphasis/mdbook-remove-emphasis/src/main.rs:remove_emphasis}}\n```\n\nTake a look at the [full example source][emphasis-example] for more details.\n\n## Implementing a preprocessor with a different language\n\nThe fact that mdBook utilizes stdin and stdout to communicate with the preprocessors makes it easy to implement them in a language other than Rust.\nThe following code shows how to implement a simple preprocessor in Python, which will modify the content of the first chapter.\nThe example below follows the configuration shown above with `preprocessor.foo.command` actually pointing to a Python script.\n\n```python\nimport json\nimport sys\n\n\nif __name__ == '__main__':\n    if len(sys.argv) > 1: # we check if we received any argument\n        if sys.argv[1] == \"supports\": \n            # then we are good to return an exit status code of 0, since the other argument will just be the renderer's name\n            sys.exit(0)\n\n    # load both the context and the book representations from stdin\n    context, book = json.load(sys.stdin)\n    # and now, we can just modify the content of the first chapter\n    book['items'][0]['Chapter']['content'] = '# Hello'\n    # we are done with the book's modification, we can just print it to stdout, \n    print(json.dumps(book))\n```\n\n\n[emphasis-example]: https://github.com/rust-lang/mdBook/tree/master/examples/remove-emphasis/\n[pc]: https://crates.io/crates/pulldown-cmark\n[pctc]: https://crates.io/crates/pulldown-cmark-to-cmark\n[an example no-op preprocessor]: https://github.com/rust-lang/mdBook/blob/master/examples/nop-preprocessor.rs\n[`parse_input()`]: https://docs.rs/mdbook-preprocessor/latest/mdbook_preprocessor/fn.parse_input.html\n[`Book::for_each_mut()`]: https://docs.rs/mdbook-preprocessor/latest/mdbook_preprocessor/book/struct.Book.html#method.for_each_mut\n[`PreprocessorContext`]: https://docs.rs/mdbook-preprocessor/latest/mdbook_preprocessor/struct.PreprocessorContext.html\n[`Book`]: https://docs.rs/mdbook-preprocessor/latest/mdbook_preprocessor/book/struct.Book.html\n[`mdbook-markdown`]: https://docs.rs/mdbook-markdown/latest/mdbook_markdown/\n"
  },
  {
    "path": "guide/src/format/README.md",
    "content": "# Format\n\nIn this section you will learn how to:\n\n- Structure your book correctly\n- Format your `SUMMARY.md` file\n- Configure your book using `book.toml`\n- Customize your theme\n"
  },
  {
    "path": "guide/src/format/configuration/README.md",
    "content": "# Configuration\n\nThis section details the configuration options available in the ***book.toml***:\n- **[General]** configuration including the `book`, `rust`, `build` sections\n- **[Preprocessor]** configuration for default and custom book preprocessors\n- **[Renderer]** configuration for the HTML, Markdown and custom renderers\n- **[Environment Variable]** configuration for overriding configuration options in your environment\n\n[General]: general.md\n[Preprocessor]: preprocessors.md\n[Renderer]: renderers.md\n[Environment Variable]: environment-variables.md"
  },
  {
    "path": "guide/src/format/configuration/environment-variables.md",
    "content": "# Environment variables\n\nAll configuration values can be overridden from the command line by setting the\ncorresponding environment variable. Because many operating systems restrict\nenvironment variables to be alphanumeric characters or `_`, the configuration\nkey needs to be formatted slightly differently to the normal `foo.bar.baz` form.\n\nVariables starting with `MDBOOK_` are used for configuration. The key is created\nby removing the `MDBOOK_` prefix and turning the resulting string into\n`kebab-case`. Double underscores (`__`) separate nested keys, while a single\nunderscore (`_`) is replaced with a dash (`-`).\n\nFor example:\n\n- `MDBOOK_book` -> `book`\n- `MDBOOK_BOOK` -> `book`\n- `MDBOOK_BOOK__TITLE` -> `book.title`\n- `MDBOOK_BOOK__TEXT_DIRECTION` -> `book.text-direction`\n\nSo by setting the `MDBOOK_BOOK__TITLE` environment variable you can override the\nbook's title without needing to touch your `book.toml`.\n\n> **Note:** To facilitate setting more complex config items, the value of an\n> environment variable is first parsed as JSON, falling back to a string if the\n> parse fails.\n>\n> This means, if you so desired, you could override all book metadata when\n> building the book with something like\n>\n> ```shell\n> $ export MDBOOK_BOOK='{\"title\": \"My Awesome Book\", \"authors\": [\"Michael-F-Bryan\"]}'\n> $ mdbook build\n> ```\n\nThe latter case may be useful in situations where `mdbook` is invoked from a\nscript or CI, where it sometimes isn't possible to update the `book.toml` before\nbuilding.\n"
  },
  {
    "path": "guide/src/format/configuration/general.md",
    "content": "# General configuration\n\nYou can configure the parameters for your book in the ***book.toml*** file.\n\nHere is an example of what a ***book.toml*** file might look like:\n\n```toml\n[book]\ntitle = \"Example book\"\nauthors = [\"John Doe\"]\ndescription = \"The example book covers examples.\"\n\n[rust]\nedition = \"2018\"\n\n[build]\nbuild-dir = \"my-example-book\"\ncreate-missing = false\n\n[preprocessor.index]\n\n[preprocessor.links]\n\n[output.html]\nadditional-css = [\"custom.css\"]\n\n[output.html.search]\nlimit-results = 15\n```\n\n## Supported configuration options\n\nIt is important to note that **any** relative path specified in the\nconfiguration will always be taken relative from the root of the book where the\nconfiguration file is located.\n\n### General metadata\n\nThis is general information about your book.\n\n- **title:** The title of the book\n- **authors:** The author(s) of the book\n- **description:** A description for the book, which is added as meta\n  information in the html `<head>` of each page\n- **src:** By default, the source directory is found in the directory named\n  `src` directly under the root folder. But this is configurable with the `src`\n  key in the configuration file.\n- **language:** The main language of the book, which is used as a language attribute `<html lang=\"en\">` for example.\n  This is also used to derive the direction of text (RTL, LTR) within the book.\n- **text-direction**: The direction of text in the book: Left-to-right (LTR) or Right-to-left (RTL). Possible values: `ltr`, `rtl`.\n  When not specified, the text direction is derived from the book's `language` attribute.\n\n**book.toml**\n```toml\n[book]\ntitle = \"Example book\"\nauthors = [\"John Doe\", \"Jane Doe\"]\ndescription = \"The example book covers examples.\"\nsrc = \"my-src\"  # the source files will be found in `root/my-src` instead of `root/src`\nlanguage = \"en\"\ntext-direction = \"ltr\"\n```\n\n### Rust options\n\nOptions for the Rust language, relevant to running tests and playground\nintegration.\n\n```toml\n[rust]\nedition = \"2015\"   # the default edition for code blocks\n```\n\n- **edition**: Rust edition to use by default for the code snippets. Default\n  is `\"2015\"`. Individual code blocks can be controlled with the `edition2015`,\n  `edition2018`, `edition2021` or `edition2024` annotations, such as:\n\n  ~~~text\n  ```rust,edition2015\n  // This only works in 2015.\n  let try = true;\n  ```\n  ~~~\n\n### Build options\n\nThis controls the build process of your book.\n\n```toml\n[build]\nbuild-dir = \"book\"                # the directory where the output is placed\ncreate-missing = true             # whether or not to create missing pages\nuse-default-preprocessors = true  # use the default preprocessors\nextra-watch-dirs = []             # directories to watch for triggering builds\n```\n\n- **build-dir:** The directory to put the rendered book in. By default this is\n  `book/` in the book's root directory.\n  This can overridden with the `--dest-dir` CLI option.\n- **create-missing:** By default, any missing files specified in `SUMMARY.md`\n  will be created when the book is built (i.e. `create-missing = true`). If this\n  is `false` then the build process will instead exit with an error if any files\n  do not exist.\n- **use-default-preprocessors:** Disable the default preprocessors (of `links` &\n  `index`) by setting this option to `false`.\n\n  If you have the same, and/or other preprocessors declared via their table\n  of configuration, they will run instead.\n\n  - For clarity, with no preprocessor configuration, the default `links` and\n    `index` will run.\n  - Setting `use-default-preprocessors = false` will disable these\n    default preprocessors from running.\n  - Adding `[preprocessor.links]`, for example, will ensure, regardless of\n    `use-default-preprocessors` that `links` it will run.\n- **extra-watch-dirs**: A list of paths to directories that will be watched in\n  the `watch` and `serve` commands. Changes to files under these directories will\n  trigger rebuilds. Useful if your book depends on files outside its `src` directory.\n"
  },
  {
    "path": "guide/src/format/configuration/preprocessors.md",
    "content": "# Configuring Preprocessors\n\nPreprocessors are extensions that can modify the raw Markdown source before it gets sent to the renderer.\n\nThe following preprocessors are built-in and included by default:\n\n- `links`: Expands the `{{ #playground }}`, `{{ #include }}`, and `{{ #rustdoc_include }}` handlebars\n  helpers in a chapter to include the contents of a file.\n  See [Including files] for more.\n- `index`: Convert all chapter files named `README.md` into `index.md`. That is\n  to say, all `README.md` would be rendered to an index file `index.html` in the\n  rendered book.\n\nThe built-in preprocessors can be disabled with the [`build.use-default-preprocessors`] config option.\n\nThe community has developed several preprocessors.\nSee the [Third Party Plugins] wiki page for a list of available preprocessors.\n\nFor information on how to create a new preprocessor, see the [Preprocessors for Developers] chapter.\n\n[Including files]: ../mdbook.md#including-files\n[`build.use-default-preprocessors`]: general.md#build-options\n[Third Party Plugins]: https://github.com/rust-lang/mdBook/wiki/Third-party-plugins\n[Preprocessors for Developers]: ../../for_developers/preprocessors.md\n\n## Custom preprocessor configuration\n\nPreprocessors can be added by including a `preprocessor` table in `book.toml` with the name of the preprocessor.\nFor example, if you have a preprocessor called `mdbook-example`, then you can include it with:\n\n```toml\n[preprocessor.example]\n```\n\nWith this table, mdBook will execute the `mdbook-example` preprocessor.\n\nThis table can include additional key-value pairs that are specific to the preprocessor.\nFor example, if our example preprocessor needed some extra configuration options:\n\n```toml\n[preprocessor.example]\nsome-extra-feature = true\n```\n\n## Locking a preprocessor dependency to a renderer\n\nYou can explicitly specify that a preprocessor should run for a renderer by\nbinding the two together.\n\n```toml\n[preprocessor.example]\nrenderers = [\"html\"]  # example preprocessor only runs with the HTML renderer\n```\n\n## Provide your own command\n\nBy default when you add a `[preprocessor.foo]` table to your `book.toml` file,\n`mdbook` will try to invoke the `mdbook-foo` executable. If you want to use a\ndifferent program name or pass in command-line arguments, this behaviour can\nbe overridden by adding a `command` field.\n\n```toml\n[preprocessor.random]\ncommand = \"python random.py\"\n```\n\n### Optional preprocessors\n\nIf you enable a preprocessor that isn't installed, the default behavior is to throw an error.\nThis behavior can be changed by marking the preprocessor as optional:\n\n```toml\n[preprocessor.example]\noptional = true\n```\n\nThis demotes the error to a warning.\n\n## Require a certain order\n\nThe order in which preprocessors are run can be controlled with the `before` and `after` fields.\nFor 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:\n\n```toml\n[preprocessor.linenos]\nafter = [ \"links\" ]\n```\n\nor\n\n```toml\n[preprocessor.links]\nbefore = [ \"linenos\" ]\n```\n\nIt would also be possible, though redundant, to specify both of the above in the same config file.\n\nPreprocessors having the same priority specified through `before` and `after` are sorted by name.\nAny infinite loops will be detected and produce an error.\n"
  },
  {
    "path": "guide/src/format/configuration/renderers.md",
    "content": "# Configuring Renderers\n\nRenderers (also called \"backends\") are responsible for creating the output of the book.\n\nThe following backends are built-in:\n\n* [`html`](#html-renderer-options) --- This renders the book to HTML.\n  This is enabled by default if no other `[output]` tables are defined in `book.toml`.\n* [`markdown`](#markdown-renderer) --- This outputs the book as markdown after running the preprocessors.\n  This is useful for debugging preprocessors.\n\nThe community has developed several backends.\nSee the [Third Party Plugins] wiki page for a list of available backends.\n\nFor information on how to create a new backend, see the [Backends for Developers] chapter.\n\n[Third Party Plugins]: https://github.com/rust-lang/mdBook/wiki/Third-party-plugins\n[Backends for Developers]: ../../for_developers/backends.md\n\n## Output tables\n\nBackends can be added by including a `output` table in `book.toml` with the name of the backend.\nFor example, if you have a backend called `mdbook-wordcount`, then you can include it with:\n\n```toml\n[output.wordcount]\n```\n\nWith this table, mdBook will execute the `mdbook-wordcount` backend.\n\nThis table can include additional key-value pairs that are specific to the backend.\nFor example, if our example backend needed some extra configuration options:\n\n```toml\n[output.wordcount]\nignores = [\"Example Chapter\"]\n```\n\nIf you define any `[output]` tables, then the `html` backend is not enabled by default.\nIf you want to keep the `html` backend running, then just include it in the `book.toml` file.\nFor example:\n\n```toml\n[book]\ntitle = \"My Awesome Book\"\n\n[output.wordcount]\n\n[output.html]\n```\n\nIf more than one `output` table is included, this changes the behavior for the layout of the output directory.\nIf there is only one backend, then it places its output directly in the `book` directory (see [`build.build-dir`] to override this location).\nIf there is more than one backend, then each backend is placed in a separate directory underneath `book`.\nFor example, the above would have directories `book/html` and `book/wordcount`.\n\n[`build.build-dir`]: general.md#build-options\n\n### Custom backend commands\n\nBy default when you add an `[output.foo]` table to your `book.toml` file,\n`mdbook` will try to invoke the `mdbook-foo` executable.\nIf you want to use a different program name or pass in command-line arguments,\nthis behaviour can be overridden by adding a `command` field.\n\n```toml\n[output.random]\ncommand = \"python random.py\"\n```\n\n### Optional backends\n\nIf you enable a backend that isn't installed, the default behavior is to throw an error.\nThis behavior can be changed by marking the backend as optional:\n\n```toml\n[output.wordcount]\noptional = true\n```\n\nThis demotes the error to a warning.\n\n\n## HTML renderer options\n\nThe HTML renderer has a variety of options detailed below.\nThey should be specified in the `[output.html]` table of the `book.toml` file.\n\n```toml\n# Example book.toml file with all output options.\n[book]\ntitle = \"Example book\"\nauthors = [\"John Doe\", \"Jane Doe\"]\ndescription = \"The example book covers examples.\"\n\n[output.html]\ntheme = \"my-theme\"\ndefault-theme = \"light\"\npreferred-dark-theme = \"navy\"\nsmart-punctuation = true\ndefinition-lists = true\nadmonitions = true\nmathjax-support = false\nadditional-css = [\"custom.css\", \"custom2.css\"]\nadditional-js = [\"custom.js\"]\nno-section-label = false\ngit-repository-url = \"https://github.com/rust-lang/mdBook\"\ngit-repository-icon = \"fab-github\"\nedit-url-template = \"https://github.com/rust-lang/mdBook/edit/master/guide/{path}\"\nsite-url = \"/example-book/\"\ncname = \"myproject.rs\"\ninput-404 = \"not-found.md\"\nsidebar-header-nav = true\n```\n\nThe following configuration options are available:\n\n- **theme:** mdBook comes with a default theme and all the resource files needed\n  for it. But if this option is set, mdBook will selectively overwrite the theme\n  files with the ones found in the specified folder.\n- **default-theme:** The theme color scheme to select by default in the\n  'Change Theme' dropdown. Defaults to `light`.\n- **preferred-dark-theme:** The default dark theme. This theme will be used if\n  the browser requests the dark version of the site via the\n  [`prefers-color-scheme`](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme)\n  CSS media query. Defaults to `navy`.\n- **smart-punctuation:** Converts quotes to curly quotes, `...` to `…`, `--` to en-dash, and `---` to em-dash.\n  See [Smart Punctuation](../markdown.md#smart-punctuation).\n  Defaults to `true`.\n- **definition-lists:** Enables [definition lists](../markdown.md#definition-lists). Defaults to `true`.\n- **admonitions:** Enables [admonitions](../markdown.md#admonitions). Defaults to `true`.\n- **mathjax-support:** Adds support for [MathJax](../mathjax.md). Defaults to\n  `false`.\n- **additional-css:** If you need to slightly change the appearance of your book\n  without overwriting the whole style, you can specify a set of stylesheets that\n  will be loaded after the default ones where you can surgically change the\n  style.\n- **additional-js:** If you need to add some behaviour to your book without\n  removing the current behaviour, you can specify a set of JavaScript files that\n  will be loaded alongside the default one.\n- **no-section-label:** mdBook by defaults adds numeric section labels in the table of\n  contents column. For example, \"1.\", \"2.1\". Set this option to true to disable\n  those labels. Defaults to `false`.\n- **git-repository-url:**  A url to the git repository for the book. If provided\n  an icon link will be output in the menu bar of the book.\n- **git-repository-icon:** The Font Awesome icon class to use for the git repository link. Defaults to `fab-github` which looks like <i class=\"fab fa-github\"></i>. If you are not using GitHub, another option to consider is `fas-code-fork` which looks like <i class=\"fas fa-code-fork\"></i>. The start of the string should be `fa-` for regular icons, `fas-` for solid icons, or `fab-` for brand icons. See the [free icon set](https://fontawesome.com/v6/search) for the available icons.\n- **edit-url-template:** Edit url template, when provided shows a\n  \"Suggest an edit\" button (which looks like <i class=\"fas fa-pencil\"></i>) for directly jumping to editing the currently\n  viewed page. For e.g. GitHub projects set this to\n  `https://github.com/<owner>/<repo>/edit/<branch>/{path}` or for\n  Bitbucket projects set it to\n  `https://bitbucket.org/<owner>/<repo>/src/<branch>/{path}?mode=edit`\n  where {path} will be replaced with the full path of the file in the\n  repository.\n- **input-404:** The name of the markdown file used for missing files.\n  The corresponding output file will be the same, with the extension replaced with `html`.\n  Defaults to `404.md`.\n- **site-url:** The url where the book will be hosted. This is required to ensure\n  navigation links and script/css imports in the 404 file work correctly, even when accessing\n  urls in subdirectories. Defaults to `/`. If `site-url` is set,\n  make sure to use document relative links for your assets, meaning they should not start with `/`.\n- **cname:** The DNS subdomain or apex domain at which your book will be hosted.\n  This string will be written to a file named CNAME in the root of your site, as\n  required by GitHub Pages (see [*Managing a custom domain for your GitHub Pages\n  site*][custom domain]).\n- **hash-files:** Include a cryptographic \"fingerprint\" of the files' contents in static asset filenames,\n  so that if the contents of the file are changed, the name of the file will also change.\n  For example, `css/chrome.css` may become `css/chrome-9b8f428e.css`.\n  Chapter HTML files are not renamed.\n  Static CSS and JS files can reference each other using `{{ resource \"filename\" }}` directives.\n  Defaults to `true`.\n- **sidebar-header-nav:** If `true`, the sidebar will contain navigation for headers on the current page. Default is `true`.\n\n[custom domain]: https://docs.github.com/en/github/working-with-github-pages/managing-a-custom-domain-for-your-github-pages-site\n\n### `[output.html.print]`\n\nThe `[output.html.print]` table provides options for controlling the printable output.\nBy default, mdBook will include an icon on the top right of the book (which looks like <i class=\"fas fa-print\"></i>) that will print the book as a single page.\n\n```toml\n[output.html.print]\nenable = true    # include support for printable output\npage-break = true # insert page-break after each chapter\n```\n\n- **enable:** Enable print support. When `false`, all print support will not be\n  rendered. Defaults to `true`.\n- **page-break:** Insert page breaks between chapters. Defaults to `true`.\n\n### `[output.html.fold]`\n\nThe `[output.html.fold]` table provides options for controlling folding of the chapter listing in the navigation sidebar.\n\n```toml\n[output.html.fold]\nenable = false    # whether or not to enable section folding\nlevel = 0         # the depth to start folding\n```\n\n- **enable:** Enable section-folding. When off, all folds are open.\n  Defaults to `false`.\n- **level:** The higher the more folded regions are open. When level is 0, all\n  folds are closed. Defaults to `0`.\n\n### `[output.html.playground]`\n\nThe `[output.html.playground]` table provides options for controlling Rust sample code blocks, and their integration with the [Rust Playground].\n\n[Rust Playground]: https://play.rust-lang.org/\n\n```toml\n[output.html.playground]\neditable = false         # allows editing the source code\ncopyable = true          # include the copy button for copying code snippets\ncopy-js = true           # includes the JavaScript for the code editor\nline-numbers = false     # displays line numbers for editable code\nrunnable = true          # displays a run button for rust code\n```\n\n- **editable:** Allow editing the source code. Defaults to `false`.\n- **copyable:** Display the copy button on code snippets. Defaults to `true`.\n- **copy-js:** Copy JavaScript files for the editor to the output directory.\n  Defaults to `true`.\n- **line-numbers:** Display line numbers on editable sections of code. Requires both `editable` and `copy-js` to be `true`. Defaults to `false`.\n- **runnable:** Displays a run button for rust code snippets. Changing this to `false` will disable the run in playground feature globally. Defaults to `true`.\n\n[Ace]: https://ace.c9.io/\n\n### `[output.html.code]`\n\nThe `[output.html.code]` table provides options for controlling code blocks.\n\n```toml\n[output.html.code]\n# A prefix string per language (one or more chars).\n# Any line starting with whitespace+prefix is hidden.\nhidelines = { python = \"~\" }\n```\n\n- **hidelines:** A table that defines how [hidden code lines](../mdbook.md#hiding-code-lines) work for each language.\n  The key is the language and the value is a string that will cause code lines starting with that prefix to be hidden.\n\n### `[output.html.search]`\n\nThe `[output.html.search]` table provides options for controlling the built-in text [search].\nmdBook must be compiled with the `search` feature enabled (on by default).\n\n[search]: ../../guide/reading.md#search\n\n```toml\n[output.html.search]\nenable = true            # enables the search feature\nlimit-results = 30       # maximum number of search results\nteaser-word-count = 30   # number of words used for a search result teaser\nuse-boolean-and = true   # multiple search terms must all match\nboost-title = 2          # ranking boost factor for matches in headers\nboost-hierarchy = 1      # ranking boost factor for matches in page names\nboost-paragraph = 1      # ranking boost factor for matches in text\nexpand = true            # partial words will match longer terms\nheading-split-level = 3  # link results to heading levels\ncopy-js = true           # include Javascript code for search\n```\n\n- **enable:** Enables the search feature. Defaults to `true`.\n- **limit-results:** The maximum number of search results. Defaults to `30`.\n- **teaser-word-count:** The number of words used for a search result teaser.\n  Defaults to `30`.\n- **use-boolean-and:** Define the logical link between multiple search words. If\n  true, all search words must appear in each result. Defaults to `false`.\n- **boost-title:** Boost factor for the search result score if a search word\n  appears in the header. Defaults to `2`.\n- **boost-hierarchy:** Boost factor for the search result score if a search word\n  appears in the hierarchy. The hierarchy contains all titles of the parent\n  documents and all parent headings. Defaults to `1`.\n- **boost-paragraph:** Boost factor for the search result score if a search word\n  appears in the text. Defaults to `1`.\n- **expand:** True if search should match longer results e.g. search `micro`\n  should match `microwave`. Defaults to `true`.\n- **heading-split-level:** Search results will link to a section of the document\n  which contains the result. Documents are split into sections by headings this\n  level or less. Defaults to `3`. (`### This is a level 3 heading`)\n- **copy-js:** Copy JavaScript files for the search implementation to the output\n  directory. Defaults to `true`.\n\n#### `[output.html.search.chapter]`\n\nThe [`output.html.search.chapter`] table provides the ability to modify search settings per chapter or directory. Each key is the path to the chapter source file or directory, and the value is a table of settings to apply to that path. This will merge recursively, with more specific paths taking precedence.\n\n```toml\n[output.html.search.chapter]\n# Disables search indexing for all chapters in the `appendix` directory.\n\"appendix\" = { enable = false }\n# Enables search indexing for just this one appendix chapter.\n\"appendix/glossary.md\" = { enable = true }\n```\n\n- **enable:** Enables or disables search indexing for the given chapters. Defaults to `true`. This does not override the overall `output.html.search.enable` setting; that must be `true` for any search functionality to be enabled. Be cautious when disabling indexing for chapters because that can potentially lead to user confusion when they search for terms and expect them to be found. This should only be used in exceptional circumstances where keeping the chapter in the index will cause issues with the quality of the search results.\n\n### `[output.html.redirect]`\n\nThe `[output.html.redirect]` table provides a way to add redirects.\nThis is useful when you move, rename, or remove a page to ensure that links to the old URL will go to the new location.\n\n```toml\n[output.html.redirect]\n\"/appendices/bibliography.html\" = \"https://rustc-dev-guide.rust-lang.org/appendix/bibliography.html\"\n\"/other-installation-methods.html\" = \"../infra/other-installation-methods.html\"\n\n# Fragment redirects also work.\n\"/some-existing-page.html#old-fragment\" = \"some-existing-page.html#new-fragment\"\n\n# Fragment redirects also work for deleted pages.\n\"/old-page.html\" = \"new-page.html\"\n\"/old-page.html#old-fragment\" = \"new-page.html#new-fragment\"\n```\n\nThe table contains key-value pairs where the key is where the redirect file needs to be created, as an absolute path from the build directory, (e.g. `/appendices/bibliography.html`).\nThe value can be any valid URI the browser should navigate to (e.g. `https://rust-lang.org/`, `/overview.html`, or `../bibliography.html`).\n\nThis will generate an HTML page which will automatically redirect to the given location.\n\nWhen fragment redirects are specified, the page must use JavaScript to redirect to the correct location. This is useful if you rename or move a section header. Fragment redirects work with existing pages and deleted pages.\n\n## Markdown renderer\n\nThe Markdown renderer will run preprocessors and then output the resulting\nMarkdown. This is mostly useful for debugging preprocessors, especially in\nconjunction with `mdbook test` to see the Markdown that `mdbook` is passing\nto `rustdoc`.\n\nThe Markdown renderer is included with `mdbook` but disabled by default.\nEnable it by adding an empty table to your `book.toml` as follows:\n\n```toml\n[output.markdown]\n```\n\nThere are no configuration options for the Markdown renderer at this time;\nonly whether it is enabled or disabled.\n\nSee [the preprocessors documentation](preprocessors.md) for how to\nspecify which preprocessors should run before the Markdown renderer.\n"
  },
  {
    "path": "guide/src/format/example.rs",
    "content": "fn main() {\n    println!(\"Hello World!\");\n#\n#     // You can even hide lines! :D\n#     println!(\"I am hidden! Expand the code snippet to see me\");\n}\n"
  },
  {
    "path": "guide/src/format/markdown.md",
    "content": "# Markdown\n\nmdBook's [parser](https://github.com/raphlinus/pulldown-cmark) adheres to the [CommonMark](https://commonmark.org/) specification with some extensions described below.\nYou can take a quick [tutorial](https://commonmark.org/help/tutorial/),\nor [try out](https://spec.commonmark.org/dingus/) CommonMark in real time. A complete Markdown overview is out of scope for \nthis documentation, but below is a high level overview of some of the basics. For a more in-depth experience, check out the\n[Markdown Guide](https://www.markdownguide.org).\n\n## Text and paragraphs\n\nText is rendered relatively predictably: \n\n```markdown\nHere is a line of text.\n\nThis is a new line.\n```\n\nWill look like you might expect:\n\nHere is a line of text.\n\nThis is a new line.\n\n## Headings\n\nHeadings use the `#` marker and should be on a line by themselves. More `#` mean smaller headings:\n\n```markdown\n### A heading \n\nSome text.\n\n#### A smaller heading \n\nMore text.\n```\n\n### A heading \n\nSome text.\n\n#### A smaller heading \n\nMore text.\n\n## Lists\n\nLists can be unordered or ordered. Ordered lists will order automatically:\n\n```markdown\n* milk\n* eggs\n* butter\n\n1. carrots\n1. celery\n1. radishes\n```\n\n* milk\n* eggs\n* butter\n\n1. carrots\n1. celery\n1. radishes\n\n## Links\n\nLinking to a URL or local file is easy:\n\n```markdown\nUse [mdBook](https://github.com/rust-lang/mdBook). \n\nRead about [mdBook](mdbook.md).\n\nAnd now [an mdBook link] that is not inline, unlike the above.\n\nA bare url: <https://www.rust-lang.org>.\n\n[an mdBook link]: https://github.com/rust-lang/mdBook\n```\n\nUse [mdBook](https://github.com/rust-lang/mdBook). \n\nRead about [mdBook](mdbook.md).\n\nAnd now [an mdBook link] that is not inline, unlike the above.\n\nA bare url: <https://www.rust-lang.org>.\n\n[an mdBook link]: https://github.com/rust-lang/mdBook\n----\n\nRelative links that end with `.md` will be converted to the `.html` extension.\nIt is recommended to use `.md` links when possible.\nThis is useful when viewing the Markdown file outside of mdBook, for example on GitHub or GitLab which render Markdown automatically.\n\nLinks to `README.md` will be converted to `index.html`.\nThis is done since some services like GitHub render README files automatically, but web servers typically expect the root file to be called `index.html`.\n\nYou can link to individual headings with `#` fragments.\nFor example, `mdbook.md#text-and-paragraphs` would link to the [Text and Paragraphs](#text-and-paragraphs) section above.\nThe ID is created by transforming the heading such as converting to lowercase and replacing spaces with dashes.\nYou can click on any heading and look at the URL in your browser to see what the fragment looks like.\n\n## Images\n\nIncluding images is simply a matter of including a link to them, much like in the _Links_ section above. The following markdown\nincludes the Rust logo SVG image found in the `images` directory at the same level as this file:\n\n```markdown\n![The Rust Logo](images/rust-logo-blk.svg)\n```\n\nProduces the following HTML when built with mdBook:\n\n```html\n<p><img src=\"images/rust-logo-blk.svg\" alt=\"The Rust Logo\" /></p>\n```\n\nWhich, of course displays the image like so:\n\n![The Rust Logo](images/rust-logo-blk.svg)\n\n## Extensions\n\nmdBook has several extensions beyond the standard CommonMark specification.\n\n### Strikethrough\n\nText may be rendered with a horizontal line through the center by wrapping the\ntext with one or two tilde characters on each side:\n\n```text\nAn example of ~~strikethrough text~~.\n```\n\nThis example will render as:\n\n> An example of ~~strikethrough text~~.\n\nThis follows the [GitHub Strikethrough extension][strikethrough].\n\n### Footnotes\n\nA footnote generates a small numbered link in the text which when clicked\ntakes the reader to the footnote text at the bottom of the item. The footnote\nlabel is written similarly to a link reference with a caret at the front. The\nfootnote text is written like a link reference definition, with the text\nfollowing the label. Example:\n\n```text\nThis is an example of a footnote[^note].\n\n[^note]: This text is the contents of the footnote, which will be rendered\n    towards the bottom.\n```\n\nThis example will render as:\n\n> This is an example of a footnote[^note].\n>\n> [^note]: This text is the contents of the footnote, which will be rendered\n>     towards the bottom.\n\nThe footnotes are automatically numbered based on the order the footnotes are\nwritten.\n\n### Tables\n\nTables can be written using pipes and dashes to draw the rows and columns of\nthe table. These will be translated to HTML table matching the shape. Example:\n\n```text\n| Header1 | Header2 |\n|---------|---------|\n| abc     | def     |\n```\n\nThis example will render similarly to this:\n\n| Header1 | Header2 |\n|---------|---------|\n| abc     | def     |\n\nSee the specification for the [GitHub Tables extension][tables] for more\ndetails on the exact syntax supported.\n\n### Task lists\n\nTask lists can be used as a checklist of items that have been completed.\nExample:\n\n```md\n- [x] Complete task\n- [ ] Incomplete task\n```\n\nThis will render as:\n\n> - [x] Complete task\n> - [ ] Incomplete task\n\nSee the specification for the [task list extension] for more details.\n\n### Smart punctuation\n\nSome ASCII punctuation sequences will be automatically turned into fancy Unicode\ncharacters:\n\n| ASCII sequence | Unicode |\n|----------------|---------|\n| `--`           | –       |\n| `---`          | —       |\n| `...`          | …       |\n| `\"`            | “ or ”, depending on context |\n| `'`            | ‘ or ’, depending on context |\n\nSo, no need to manually enter those Unicode characters!\n\nThis feature is enabled by default.\nTo disable it, see the [`output.html.smart-punctuation`] config option.\n\n[strikethrough]: https://github.github.com/gfm/#strikethrough-extension-\n[tables]: https://github.github.com/gfm/#tables-extension-\n[task list extension]: https://github.github.com/gfm/#task-list-items-extension-\n[`output.html.smart-punctuation`]: configuration/renderers.md#html-renderer-options\n\n### Heading attributes\n\nHeadings can have a custom HTML ID and classes. This lets you maintain the same ID even if you change the heading's text, it also lets you add multiple classes in the heading.\n\nExample:\n```md\n# Example heading { #first .class1 .class2 }\n```\n\nThis makes the level 1 heading with the content `Example heading`, ID `first`, and classes `class1` and `class2`. Note that the attributes should be space-separated.\n\nMore information can be found in the [heading attrs spec page](https://github.com/raphlinus/pulldown-cmark/blob/master/pulldown-cmark/specs/heading_attrs.txt).\n\n### Definition lists\n\nDefinition lists can be used for things like glossary entries. The term is listed on a line by itself, followed by one or more definitions. Each definition must begin with a `:` (after 0-2 spaces).\n\nExample:\n\n```md\nterm A\n  : This is a definition of term A. Text\n    can span multiple lines.\n\nterm B\n  : This is a definition of term B.\n  : This has more than one definition.\n```\n\nThis will render as:\n\nterm A\n  : This is a definition of term A. Text\n    can span multiple lines.\n\nterm B\n  : This is a definition of term B.\n  : This has more than one definition.\n\nTerms are clickable just like headers, which will set the browser's URL to point directly to that term.\n\nSee the [definition lists spec](https://github.com/pulldown-cmark/pulldown-cmark/blob/HEAD/pulldown-cmark/specs/definition_lists.txt) for more information on the specifics of the syntax. See the [Wikipedia guidelines for glossaries](https://en.wikipedia.org/wiki/Wikipedia:Manual_of_Style/Glossaries#General_guidelines_for_writing_glossaries) for some guidelines on how to write a glossary.\n\nThis feature is enabled by default.\nTo disable it, see the [`output.html.definition-lists`] config option.\n\n[`output.html.definition-lists`]: configuration/renderers.md#html-renderer-options\n\n### Admonitions\n\nAn admonition is a special type of callout or notice block used to highlight important information. It is written as a blockquote with a special tag on the first line.\n\n```md\n> [!NOTE]\n> General information or additional context.\n\n> [!TIP]\n> A helpful suggestion or best practice.\n\n> [!IMPORTANT]\n> Key information that shouldn't be missed.\n\n> [!WARNING]\n> Critical information that highlights a potential risk.\n\n> [!CAUTION]\n> Information about potential issues that require caution.\n```\n\nThese will render as:\n\n> [!NOTE]\n> General information or additional context.\n\n> [!TIP]\n> A helpful suggestion or best practice.\n\n> [!IMPORTANT]\n> Key information that shouldn't be missed.\n\n> [!WARNING]\n> Critical information that highlights a potential risk.\n\n> [!CAUTION]\n> Information about potential issues that require caution.\n\nThis feature is enabled by default.\nTo disable it, see the [`output.html.admonitions`] config option.\n\n[`output.html.admonitions`]: configuration/renderers.md#html-renderer-options\n"
  },
  {
    "path": "guide/src/format/mathjax.md",
    "content": "# MathJax support\n\nmdBook has optional support for math equations through\n[MathJax](https://www.mathjax.org/).\n\nTo enable MathJax, you need to add the `mathjax-support` key to your `book.toml`\nunder the `output.html` section.\n\n```toml\n[output.html]\nmathjax-support = true\n```\n\n>**Note:** The usual delimiters MathJax uses are not yet supported. You can't\ncurrently use `$$ ... $$` as delimiters and the `\\[ ... \\]` delimiters need an\nextra backslash to work. Hopefully this limitation will be lifted soon.\n\n>**Note:** When you use double backslashes in MathJax blocks (for example in\n> commands such as `\\begin{cases} \\frac 1 2 \\\\ \\frac 3 4 \\end{cases}`) you need\n> to add _two extra_ backslashes (e.g., `\\begin{cases} \\frac 1 2 \\\\\\\\ \\frac 3 4\n> \\end{cases}`).\n\n\n### Inline equations\nInline equations are delimited by `\\\\(` and `\\\\)`. So for example, to render the\nfollowing inline equation \\\\( \\int x dx = \\frac{x^2}{2} + C \\\\) you would write\nthe following:\n```\n\\\\( \\int x dx = \\frac{x^2}{2} + C \\\\)\n```\n\n### Block equations\nBlock equations are delimited by `\\\\[` and `\\\\]`. To render the following\nequation\n\n\\\\[ \\mu = \\frac{1}{N} \\sum_{i=0} x_i \\\\]\n\n\nyou would write:\n\n```bash\n\\\\[ \\mu = \\frac{1}{N} \\sum_{i=0} x_i \\\\]\n```\n"
  },
  {
    "path": "guide/src/format/mdbook.md",
    "content": "# mdBook-specific features\n\n## Hiding code lines\n\nThere is a feature in mdBook that lets you hide code lines by prepending them with a specific prefix.\n\nFor the Rust language, you can prefix lines with `# ` (`#` followed by a space) to hide them [like you would with Rustdoc][rustdoc-hide].\nThis prefix can be escaped with `##` to prevent the hiding of a line that should begin with the literal string `# ` (see [Rustdoc's docs][rustdoc-hide] for more details)\n\n[rustdoc-hide]: https://doc.rust-lang.org/stable/rustdoc/write-documentation/documentation-tests.html#hiding-portions-of-the-example\n\n```bash\n# fn main() {\n    let x = 5;\n    let y = 6;\n\n    println!(\"{}\", x + y);\n# }\n```\n\nWill render as\n\n```rust\n# fn main() {\n    let x = 5;\n    let y = 6;\n\n    println!(\"{}\", x + y);\n# }\n```\n\nWhen you tap or hover the mouse over the code block, there will be an eyeball icon (<i class=\"fa fa-eye\"></i>) which will toggle the visibility of the hidden lines.\n\nBy default, this only works for code examples that are annotated with `rust`.\nHowever, you can define custom prefixes for other languages by adding a new line-hiding prefix in your `book.toml` with the language name and prefix character(s):\n\n```toml\n[output.html.code.hidelines]\npython = \"~\"\n```\n\nThe prefix will hide any lines that begin with the given prefix. With the python prefix shown above, this:\n\n```bash\n~hidden()\nnothidden():\n~    hidden()\n    ~hidden()\n    nothidden()\n```\n\nwill render as\n\n```python\n~hidden()\nnothidden():\n~    hidden()\n    ~hidden()\n    nothidden()\n```\n\nThis behavior can be overridden locally with a different prefix. This has the same effect as above:\n\n~~~markdown\n```python,hidelines=!!!\n!!!hidden()\nnothidden():\n!!!    hidden()\n    !!!hidden()\n    nothidden()\n```\n~~~\n\n## Rust playground\n\nRust language code blocks will automatically get a play button (<i class=\"fas fa-play\"></i>) which will execute the code and display the output just below the code block.\nThis works by sending the code to the [Rust Playground].\n\n```rust\nprintln!(\"Hello, World!\");\n```\n\nIf there is no `main` function, then the code is automatically wrapped inside one.\n\nIf you wish to disable the play button for a code block, you can include the `noplayground` option on the code block like this:\n\n~~~markdown\n```rust,noplayground\nlet mut name = String::new();\nstd::io::stdin().read_line(&mut name).expect(\"failed to read line\");\nprintln!(\"Hello {}!\", name);\n```\n~~~\n\nOr, if you wish to disable the play button for all code blocks in your book, you can write the config to the `book.toml` like this.\n\n```toml\n[output.html.playground]\nrunnable = false\n```\n\n## Rust code block attributes\n\nAdditional attributes can be included in Rust code blocks with comma, space, or tab-separated terms just after the language term. For example:\n\n~~~markdown\n```rust,ignore\n# This example won't be tested.\npanic!(\"oops!\");\n```\n~~~\n\nThese are particularly important when using [`mdbook test`] to test Rust examples.\nThese use the same attributes as [rustdoc attributes], with a few additions:\n\n* `editable` --- Enables the [editor].\n* `noplayground` --- Removes the play button, but will still be tested.\n* `mdbook-runnable` --- Forces the play button to be displayed.\n  This is intended to be combined with the `ignore` attribute for examples that should not be tested, but you want to allow the reader to run.\n* `ignore` --- Will not be tested and no play button is shown, but it is still highlighted as Rust syntax.\n* `should_panic` --- When executed, it should produce a panic.\n* `no_run` --- The code is compiled when tested, but it is not run.\n  The play button is also not shown.\n* `compile_fail` --- The code should fail to compile.\n* `edition2015`, `edition2018`, `edition2021`, `edition2024` --- Forces the use of a specific Rust edition.\n  See [`rust.edition`] to set this globally.\n\n[`mdbook test`]: ../cli/test.md\n[rustdoc attributes]: https://doc.rust-lang.org/rustdoc/documentation-tests.html#attributes\n[editor]: theme/editor.md\n[`rust.edition`]: configuration/general.md#rust-options\n\n## Including files\n\nWith the following syntax, you can include files into your book:\n\n```hbs\n\\{{#include file.rs}}\n```\n\nThe path to the file has to be relative from the current source file.\n\nmdBook will interpret included files as Markdown. Since the include command\nis usually used for inserting code snippets and examples, you will often\nwrap the command with ```` ``` ```` to display the file contents without\ninterpreting them.\n\n````hbs\n```\n\\{{#include file.rs}}\n```\n````\n\n## Including portions of a file\nOften you only need a specific part of the file, e.g. relevant lines for an\nexample. We support four different modes of partial includes:\n\n```hbs\n\\{{#include file.rs:2}}\n\\{{#include file.rs::10}}\n\\{{#include file.rs:2:}}\n\\{{#include file.rs:2:10}}\n```\n\nThe first command only includes the second line from file `file.rs`. The second\ncommand includes all lines up to line 10, i.e. the lines from 11 till the end of\nthe file are omitted. The third command includes all lines from line 2, i.e. the\nfirst line is omitted. The last command includes the excerpt of `file.rs`\nconsisting of lines 2 to 10.\n\nTo avoid breaking your book when modifying included files, you can also\ninclude a specific section using anchors instead of line numbers.\nAn anchor is a pair of matching lines. The line beginning an anchor must\nmatch the regex `ANCHOR:\\s*[\\w_-]+` and similarly the ending line must match\nthe regex `ANCHOR_END:\\s*[\\w_-]+`. This allows you to put anchors in\nany kind of commented line.\n\nConsider the following file to include:\n```rs\n/* ANCHOR: all */\n\n// ANCHOR: component\nstruct Paddle {\n    hello: f32,\n}\n// ANCHOR_END: component\n\n////////// ANCHOR: system\nimpl System for MySystem { ... }\n////////// ANCHOR_END: system\n\n/* ANCHOR_END: all */\n```\n\nThen in the book, all you have to do is:\n````hbs\nHere is a component:\n```rust,no_run,noplayground\n\\{{#include file.rs:component}}\n```\n\nHere is a system:\n```rust,no_run,noplayground\n\\{{#include file.rs:system}}\n```\n\nThis is the full file.\n```rust,no_run,noplayground\n\\{{#include file.rs:all}}\n```\n````\n\nLines containing anchor patterns inside the included anchor are ignored.\n\n## Including a file but initially hiding all except specified lines\n\nThe `rustdoc_include` helper is for including code from external Rust files that contain complete\nexamples, but only initially showing particular lines specified with line numbers or anchors in the\nsame way as with `include`.\n\nThe lines not in the line number range or between the anchors will still be included, but they will\nbe prefaced with `#`. This way, a reader can expand the snippet to see the complete example, and\nRustdoc will use the complete example when you run `mdbook test`.\n\nFor example, consider a file named `file.rs` that contains this Rust program:\n\n```rust\nfn main() {\n    let x = add_one(2);\n    assert_eq!(x, 3);\n}\n\nfn add_one(num: i32) -> i32 {\n    num + 1\n}\n```\n\nWe can include a snippet that initially shows only line 2 by using this syntax:\n\n````hbs\nTo call the `add_one` function, we pass it an `i32` and bind the returned value to `x`:\n\n```rust\n\\{{#rustdoc_include file.rs:2}}\n```\n````\n\nThis would have the same effect as if we had manually inserted the code and hidden all but line 2\nusing `#`:\n\n````hbs\nTo call the `add_one` function, we pass it an `i32` and bind the returned value to `x`:\n\n```rust\n# fn main() {\n    let x = add_one(2);\n#     assert_eq!(x, 3);\n# }\n#\n# fn add_one(num: i32) -> i32 {\n#     num + 1\n# }\n```\n````\n\nThat is, it looks like this (click the \"expand\" icon to see the rest of the file):\n\n```rust\n# fn main() {\n    let x = add_one(2);\n#     assert_eq!(x, 3);\n# }\n#\n# fn add_one(num: i32) -> i32 {\n#     num + 1\n# }\n```\n\n## Inserting runnable Rust files\n\nWith the following syntax, you can insert runnable Rust files into your book:\n\n```hbs\n\\{{#playground file.rs}}\n```\n\nThe path to the Rust file has to be relative from the current source file.\n\nWhen play is clicked, the code snippet will be sent to the [Rust Playground] to be\ncompiled and run. The result is sent back and displayed directly underneath the\ncode.\n\nHere is what a rendered code snippet looks like:\n\n{{#playground example.rs}}\n\nAny additional values passed after the filename will be included as attributes of the code block.\nFor example `\\{{#playground example.rs editable}}` will create the code block like the following:\n\n~~~markdown\n```rust,editable\n# Contents of example.rs here.\n```\n~~~\n\nAnd the `editable` attribute will enable the [editor] as described at [Rust code block attributes](#rust-code-block-attributes).\n\n[Rust Playground]: https://play.rust-lang.org/\n\n## Controlling page \\<title\\>\n\nA chapter can set a \\<title\\> that is different from its entry in the table of\ncontents (sidebar) by including a `\\{{#title ...}}` near the top of the page.\n\n```hbs\n\\{{#title My Title}}\n```\n\n## HTML classes provided by mdBook\n\n<img class=\"right\" src=\"images/rust-logo-blk.svg\" alt=\"The Rust logo\">\n\n### `class=\"left\"` and `\"right\"`\n\nThese classes are provided by default, for inline HTML to float images.\n\n```html\n<img class=\"right\" src=\"images/rust-logo-blk.svg\" alt=\"The Rust logo\">\n```\n\n### `class=\"hidden\"`\n\nHTML tags with class `hidden` will not be shown.\n\n```html\n<div class=\"hidden\">This will not be seen.</div>\n```\n\n<div class=\"hidden\">This will not be seen.</div>\n\n## Font-Awesome icons\n\nmdBook includes a copy of version 6 of [Font Awesome Free's](https://fontawesome.com)\nMIT-licensed SVG files. It emulates the `<i>` syntax, but converts the results\nto inline SVG. Only the regular, solid, and brands icons are included; paid\nfeatures like the light icons are not.\n\nFor example, given this HTML syntax:\n\n```hbs\nThe result looks like this: <i class=\"fa-solid fa-print\"></i>\n```\n\nThe result looks like this: <i class=\"fa-solid fa-print\"></i>\n\nSee the [free icon set](https://fontawesome.com/v6/search) for the available icons.\n"
  },
  {
    "path": "guide/src/format/summary.md",
    "content": "# SUMMARY.md\n\nThe summary file is used by mdBook to know what chapters to include, in what\norder they should appear, what their hierarchy is and where the source files\nare. Without this file, there is no book.\n\nThis markdown file must be named `SUMMARY.md`. Its formatting\nis very strict and must follow the structure outlined below to allow for easy\nparsing. Any element not specified below, be it formatting or textual, is likely\nto be ignored at best, or may cause an error when attempting to build the book.\n\n### Structure\n\n1. ***Title*** - While optional, it's common practice to begin with a title, generally <code\n   class=\"language-markdown\"># Summary</code>. This is ignored by the parser however, and\n   can be omitted.\n   ```markdown\n   # Summary\n   ```\n\n1. ***Prefix Chapter*** - Before the main numbered chapters, prefix chapters can be added\n   that will not be numbered. This is useful for forewords,\n   introductions, etc. There are, however, some constraints. Prefix chapters cannot be\n   nested; they should all be on the root level. And you cannot add\n   prefix chapters once you have added numbered chapters.\n   ```markdown\n   [A Prefix Chapter](relative/path/to/markdown.md)\n\n   - [First Chapter](relative/path/to/markdown2.md)\n   ```\n\n1. ***Part Title*** -\n   Level 1 headers can be used as a title for the following numbered chapters.\n   This can be used to logically separate different sections of the book.\n   The title is rendered as unclickable text.\n   Titles are optional, and the numbered chapters can be broken into as many parts as desired.\n   Part titles must be h1 headers (one `#`), other heading levels are ignored.\n   ```markdown\n   # My Part Title\n\n   - [First Chapter](relative/path/to/markdown.md)\n   ```\n\n1. ***Numbered Chapter*** - Numbered chapters outline the main content of the book\n   and can be nested, resulting in a nice hierarchy\n   (chapters, sub-chapters, etc.).\n   ```markdown\n   # Title of Part\n\n   - [First Chapter](relative/path/to/markdown.md)\n   - [Second Chapter](relative/path/to/markdown2.md)\n      - [Sub Chapter](relative/path/to/markdown3.md)\n\n   # Title of Another Part\n\n   - [Another Chapter](relative/path/to/markdown4.md)\n   ```\n   Numbered chapters can be denoted with either `-` or `*` (do not mix delimiters). \n   \n1. ***Suffix Chapter*** - Like prefix chapters, suffix chapters are unnumbered, but they come after \n   numbered chapters.\n   ```markdown\n   - [Last Chapter](relative/path/to/markdown.md)\n\n   [Title of Suffix Chapter](relative/path/to/markdown2.md)\n   ```\n\n1. ***Draft chapters*** - Draft chapters are chapters without a file and thus content.\n   The purpose of a draft chapter is to signal future chapters still to be written.\n   Or when still laying out the structure of the book to avoid creating the files\n   while you are still changing the structure of the book a lot.\n   Draft chapters will be rendered in the HTML renderer as disabled links in the table\n   of contents, as you can see for the next chapter in the table of contents on the left.\n   Draft chapters are written like normal chapters but without writing the path to the file.\n   ```markdown\n   - [Draft Chapter]()\n   ```\n\n1. ***Separators*** - Separators can be added before, in between, and after any other element. They result\n   in an HTML rendered line in the built table of contents.  A separator is\n   a line containing exclusively dashes and at least three of them: `---`.\n   ```markdown\n   # My Part Title\n   \n   [A Prefix Chapter](relative/path/to/markdown.md)\n\n   ---\n\n   - [First Chapter](relative/path/to/markdown2.md)\n   ```\n  \n\n### Example\n\nBelow is the markdown source for the `SUMMARY.md` for this guide, with the resulting table\nof contents as rendered to the left.\n\n```markdown\n{{#include ../SUMMARY.md}}\n```\n"
  },
  {
    "path": "guide/src/format/theme/README.md",
    "content": "# Theme\n\nThe default renderer uses a [handlebars](https://handlebarsjs.com) template to\nrender your markdown files and comes with a default theme included in the mdBook\nbinary.\n\nThe theme is totally customizable, you can selectively replace every file from\nthe theme by your own by adding a `theme` directory next to `src` folder in your\nproject root. Create a new file with the name of the file you want to override\nand now that file will be used instead of the default file.\n\nHere are the files you can override:\n\n- **_index.hbs_** is the handlebars template.\n- **_head.hbs_** is appended to the HTML `<head>` section.\n- **_header.hbs_** content is appended on top of every book page.\n- **_css/_** contains the CSS files for styling the book.\n    - **_css/chrome.css_** is for UI elements.\n    - **_css/general.css_** is the base styles.\n    - **_css/print.css_** is the style for printer output.\n    - **_css/variables.css_** contains variables used in other CSS files.\n- **_book.js_** is mostly used to add client side functionality, like hiding /\n  un-hiding the sidebar, changing the theme, ...\n- **_highlight.js_** is the JavaScript that is used to highlight code snippets,\n  you should not need to modify this.\n- **_highlight.css_** is the theme used for the code highlighting.\n- **_favicon.svg_** and **_favicon.png_** the favicon that will be used. The SVG\n  version is used by [newer browsers].\n- **fonts/fonts.css** contains the definition of which fonts to load.\n  Custom fonts can be included in the `fonts` directory.\n\nGenerally, when you want to tweak the theme, you don't need to override all the\nfiles. If you only need changes in the stylesheet, there is no point in\noverriding all the other files. Because custom files take precedence over\nbuilt-in ones, they will not get updated with new fixes / features.\n\n**Note:** When you override a file, it is possible that you break some\nfunctionality. Therefore I recommend to use the file from the default theme as\ntemplate and only add / modify what you need. You can copy the default theme\ninto your source directory automatically by using `mdbook init --theme` and just\nremove the files you don't want to override.\n\n`mdbook init --theme` will not create every file listed above.\nSome files, such as `head.hbs`, do not have built-in equivalents.\nJust create the file if you need it.\n\nIf you completely replace all built-in themes, be sure to also set\n[`output.html.preferred-dark-theme`] in the config, which defaults to the\nbuilt-in `navy` theme.\n\n[`output.html.preferred-dark-theme`]: ../configuration/renderers.md#html-renderer-options\n[newer browsers]: https://caniuse.com/#feat=link-icon-svg\n"
  },
  {
    "path": "guide/src/format/theme/editor.md",
    "content": "# Editor\n\nIn addition to providing runnable code playgrounds, mdBook optionally allows them\nto be editable. In order to enable editable code blocks, the following needs to\nbe added to the ***book.toml***:\n\n```toml\n[output.html.playground]\neditable = true\n```\n\nAfter enabling editable code blocks, the `editable` attribute must be added to a\ncode block to make it editable:\n\n~~~markdown\n```rust,editable\nfn main() {\n    let number = 5;\n    print!(\"{}\", number);\n}\n```\n~~~\n\nThe above will result in this editable playground:\n\n```rust,editable\nfn main() {\n    let number = 5;\n    print!(\"{}\", number);\n}\n```\n\nNote the new `Undo Changes` button in the editable playgrounds.\n\n## Customizing the editor\n\nBy default, the editor is the [Ace](https://ace.c9.io/) editor, but, if desired,\nthe functionality may be overridden by providing a different folder:\n\n```toml\n[output.html.playground]\neditable = true\neditor = \"/path/to/editor\"\n```\n\nNote that for the editor changes to function correctly, the `book.js` inside of\nthe `theme` folder will need to be overridden as it has some couplings with the\ndefault Ace editor.\n"
  },
  {
    "path": "guide/src/format/theme/index-hbs.md",
    "content": "# index.hbs\n\n`index.hbs` is the handlebars template that is used to render the book. The\nmarkdown files are processed to html and then injected in that template.\n\nIf you want to change the layout or style of your book, chances are that you\nwill have to modify this template a little bit. Here is what you need to know.\n\n## Data\n\nA lot of data is exposed to the handlebars template with the \"context\". In the\nhandlebars template you can access this information by using\n\n```handlebars\n{{name_of_property}}\n```\n\nHere is a list of the properties that are exposed:\n\n- ***language*** Language of the book in the form `en`, as specified in `book.toml` (if not specified, defaults to `en`). To use in <code\n  class=\"language-html\">\\<html lang=\\\"{{ language }}\\\"></code> for example.\n- ***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`.\n- ***book_title*** Title of the book, as specified in `book.toml`\n- ***chapter_title*** Title of the current chapter, as listed in `SUMMARY.md`\n\n- ***path*** Relative path to the original markdown file from the source\n  directory\n- ***content*** This is the rendered markdown.\n- ***path_to_root*** This is a path containing exclusively `../`'s that points\n  to the root of the book from the current file. Since the original directory\n  structure is maintained, it is useful to prepend relative links with this\n  `path_to_root`.\n- ***previous*** and ***next*** These are objects used for linking to the previous and next chapter. They contain the properties `title` and `link` of the corresponding chapter.\n- ***chapters*** Is an array of dictionaries of the form\n  ```json\n  {\"section\": \"1.2.1\", \"name\": \"name of this chapter\", \"path\": \"dir/markdown.md\"}\n  ```\n  containing all the chapters of the book. It is used for example to construct\n  the table of contents (sidebar).\n\n## Handlebars helpers\n\nIn addition to the properties you can access, there are some handlebars helpers\nat your disposal.\n\n### toc\n\nThe toc helper is used like this\n\n```handlebars\n{{#toc}}{{/toc}}\n```\n\nand outputs something that looks like this, depending on the structure of your\nbook\n\n```html\n<ul class=\"chapter\">\n    <li><a href=\"link/to/file.html\">Some chapter</a></li>\n    <li>\n        <ul class=\"section\">\n            <li><a href=\"link/to/other_file.html\">Some other Chapter</a></li>\n        </ul>\n    </li>\n</ul>\n```\n\nIf you would like to make a toc with another structure, you have access to the\nchapters property containing all the data. The only limitation at the moment\nis that you would have to do it with JavaScript instead of with a handlebars\nhelper.\n\n```html\n<script>\nvar chapters = {{chapters}};\n// Processing here\n</script>\n```\n\n### resource\n\nThe path to a static file.\nIt implicitly includes `path_to_root`,\nand accounts for files that are renamed with a hash in their filename.\n\n```handlebars\n<link rel=\"stylesheet\" href=\"{{ resource \"css/chrome.css\" }}\">\n```\n\n### fa\n\nmdBook includes a copy of [Font Awesome Free's](https://fontawesome.com)\nMIT-licensed SVG files. It accepts three positional arguments:\n\n1. Type: one of \"solid\", \"regular\", and \"brands\" (light and duotone are not\n   currently supported)\n2. Icon: anything chosen from the\n   [free icon set](https://fontawesome.com/v6/search)\n3. ID (optional): if included, an HTML ID attribute will be added to the\n   icon's wrapping `<span>` tag\n\nFor example, this handlebars syntax will become this HTML:\n\n```handlebars\n{{fa \"solid\" \"print\" \"print-button\"}}\n```\n\n```html\n<span class=fa-svg id=\"print-button\"><svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"><path d=\"M448 192V77.25c0-8.49-3.37-16.62-9.37-22.63L393.37 9.37c-6-6-14.14-9.37-22.63-9.37H96C78.33 0 64 14.33 64 32v160c-35.35 0-64 28.65-64 64v112c0 8.84 7.16 16 16 16h48v96c0 17.67 14.33 32 32 32h320c17.67 0 32-14.33 32-32v-96h48c8.84 0 16-7.16 16-16V256c0-35.35-28.65-64-64-64zm-64 256H128v-96h256v96zm0-224H128V64h192v48c0 8.84 7.16 16 16 16h48v96zm48 72c-13.25 0-24-10.75-24-24 0-13.26 10.75-24 24-24s24 10.74 24 24c0 13.25-10.75 24-24 24z\"/></svg></span>\n```\n"
  },
  {
    "path": "guide/src/format/theme/syntax-highlighting.md",
    "content": "# Syntax highlighting\n\nmdBook uses [Highlight.js](https://highlightjs.org) with a custom theme\nfor syntax highlighting.\n\nAutomatic language detection has been turned off, so you will probably want to\nspecify the programming language you use like this:\n\n~~~markdown\n```rust\nfn main() {\n    // Some code\n}\n```\n~~~\n\n## Supported languages\n\nThese languages are supported by default, but you can add more by supplying\nyour own `highlight.js` file:\n\n- apache\n- armasm\n- bash\n- c\n- coffeescript\n- cpp\n- csharp\n- css\n- d\n- diff\n- go\n- handlebars\n- haskell\n- http\n- ini\n- java\n- javascript\n- json\n- julia\n- kotlin\n- less\n- lua\n- makefile\n- markdown\n- nginx\n- nim\n- nix\n- objectivec\n- perl\n- php\n- plaintext\n- properties\n- python\n- r\n- ruby\n- rust\n- scala\n- scss\n- shell\n- sql\n- swift\n- typescript\n- x86asm\n- xml\n- yaml\n\n## Custom theme\nLike the rest of the theme, the files used for syntax highlighting can be\noverridden with your own.\n\n- ***highlight.js*** normally you shouldn't have to overwrite this file, unless\n  you want to use a more recent version.\n- ***highlight.css*** theme used by highlight.js for syntax highlighting.\n\nIf you want to use another theme for `highlight.js` download it from their\nwebsite, or make it yourself, rename it to `highlight.css` and put it in\nthe `theme` folder of your book.\n\nNow your theme will be used instead of the default theme.\n\n## Improve default theme\n\nIf you think the default theme doesn't look quite right for a specific language,\nor could be improved, feel free to [submit a new\nissue](https://github.com/rust-lang/mdBook/issues) explaining what you\nhave in mind and I will take a look at it.\n\nYou could also create a pull-request with the proposed improvements.\n\nOverall the theme should be light and sober, without too many flashy colors.\n"
  },
  {
    "path": "guide/src/guide/README.md",
    "content": "# User guide\n\nThis user guide provides an introduction to basic concepts of using mdBook.\n\n- [Installation](installation.md)\n- [Reading Books](reading.md)\n- [Creating a Book](creating.md)\n"
  },
  {
    "path": "guide/src/guide/creating.md",
    "content": "# Creating a book\n\nOnce you have the `mdbook` CLI tool installed, you can use it to create and render a book.\n\n## Initializing a book\n\nThe `mdbook init` command will create a new directory containing an empty book for you to get started.\nGive it the name of the directory that you want to create:\n\n```sh\nmdbook init my-first-book\n```\n\nIt will ask a few questions before generating the book.\nAfter answering the questions, you can change the current directory into the new book:\n\n```sh\ncd my-first-book\n```\n\nThere are several ways to render a book, but one of the easiest methods is to use the `serve` command, which will build your book and start a local webserver:\n\n```sh\nmdbook serve --open\n```\n\nThe `--open` option will open your default web browser to view your new book.\nYou can leave the server running even while you edit the content of the book, and `mdbook` will automatically rebuild the output *and* automatically refresh your web browser.\n\nCheck out the [CLI Guide](../cli/index.html) for more information about other `mdbook` commands and CLI options.\n\n## Anatomy of a book\n\nA book is built from several files which define the settings and layout of the book.\n\n### `book.toml`\n\nIn the root of your book, there is a `book.toml` file which contains settings for describing how to build your book.\nThis is written in the [TOML markup language](https://toml.io/).\nThe default settings are usually good enough to get you started.\nWhen you are interested in exploring more features and options that mdBook provides, check out the [Configuration chapter](../format/configuration/index.html) for more details.\n\nA very basic `book.toml` can be as simple as this:\n\n```toml\n[book]\ntitle = \"My First Book\"\n```\n\n### `SUMMARY.md`\n\nThe next major part of a book is the summary file located at `src/SUMMARY.md`.\nThis file contains a list of all the chapters in the book.\nBefore a chapter can be viewed, it must be added to this list.\n\nHere's a basic summary file with a few chapters:\n\n```md\n# Summary\n\n[Introduction](README.md)\n\n- [My First Chapter](my-first-chapter.md)\n- [Nested example](nested/README.md)\n    - [Sub-chapter](nested/sub-chapter.md)\n```\n\nTry opening up `src/SUMMARY.md` in your editor and adding a few chapters.\nIf any of the chapter files do not exist, `mdbook` will automatically create them for you.\n\nFor more details on other formatting options for the summary file, check out the [Summary chapter](../format/summary.md).\n\n### Source files\n\nThe content of your book is all contained in the `src` directory.\nEach chapter is a separate Markdown file.\nTypically, each chapter starts with a level 1 heading with the title of the chapter.\n\n```md\n# My First Chapter\n\nFill out your content here.\n```\n\nThe precise layout of the files is up to you.\nThe organization of the files will correspond to the HTML files generated, so keep in mind that the file layout is part of the URL of each chapter.\n\nWhile the `mdbook serve` command is running, you can open any of the chapter files and start editing them.\nEach time you save the file, `mdbook` will rebuild the book and refresh your web browser.\n\nCheck out the [Markdown chapter](../format/markdown.md) for more information on formatting the content of your chapters.\n\nAll other files in the `src` directory will be included in the output.\nSo if you have images or other static files, just include them somewhere in the `src` directory.\n\n## Publishing a book\n\nOnce you've written your book, you may want to host it somewhere for others to view.\nThe first step is to build the output of the book.\nThis can be done with the `mdbook build` command in the same directory where the `book.toml` file is located:\n\n```sh\nmdbook build\n```\n\nThis will generate a directory named `book` which contains the HTML content of your book.\nYou can then place this directory on any web server to host it.\n\nFor more information about publishing and deploying, check out the [Continuous Integration chapter](../continuous-integration.md) for more.\n"
  },
  {
    "path": "guide/src/guide/installation.md",
    "content": "# Installation\n\nThere are multiple ways to install the mdBook CLI tool.\nChoose any one of the methods below that best suit your needs.\nIf you are installing mdBook for automatic deployment, check out the [continuous integration] chapter for more examples on how to install.\n\n[continuous integration]: ../continuous-integration.md\n\n## Pre-compiled binaries\n\nExecutable binaries are available for download on the [GitHub Releases page][releases].\nDownload the binary for your platform (Windows, macOS, or Linux) and extract the archive.\nThe archive contains an `mdbook` executable which you can run to build your books.\n\nTo make it easier to run, put the path to the binary into your `PATH`.\n\n[releases]: https://github.com/rust-lang/mdBook/releases\n\n## Build from source using Rust\n\nTo build the `mdbook` executable from source, you will first need to install Rust and Cargo.\nFollow the instructions on the [Rust installation page].\nmdBook currently requires at least Rust version 1.88.\n\nOnce you have installed Rust, the following command can be used to build and install mdBook:\n\n```sh\ncargo install mdbook\n```\n\nThis will automatically download mdBook from [crates.io], build it, and install it in Cargo's global binary directory (`~/.cargo/bin/` by default).\n\nYou can run `cargo install mdbook` again whenever you want to update to a new version.\nThat command will check if there is a newer version, and re-install mdBook if a newer version is found.\n\nTo uninstall, run the command `cargo uninstall mdbook`.\n\n[Rust installation page]: https://www.rust-lang.org/tools/install\n[crates.io]: https://crates.io/\n\n### Installing the latest master version\n\nThe version published to crates.io will ever so slightly be behind the version hosted on GitHub.\nIf you need the latest version you can build the git version of mdBook yourself.\nCargo makes this ***super easy***!\n\n```sh\ncargo install --git https://github.com/rust-lang/mdBook.git mdbook\n```\n\nAgain, make sure to add the Cargo bin directory to your `PATH`.\n\n## Modifying and contributing\n\nIf you are interested in making modifications to mdBook itself, check out the [Contributing Guide] for more information.\n\n[Contributing Guide]: https://github.com/rust-lang/mdBook/blob/master/CONTRIBUTING.md\n"
  },
  {
    "path": "guide/src/guide/reading.md",
    "content": "# Reading books\n\nThis chapter gives an introduction on how to interact with a book produced by mdBook.\nThis assumes you are reading an HTML book.\nThe options and formatting will be different for other output formats such as PDF.\n\nA book is organized into *chapters*.\nEach chapter is a separate page.\nChapters can be nested into a hierarchy of sub-chapters.\nTypically, each chapter will be organized into a series of *headings* to subdivide a chapter.\n\n## Navigation\n\nThere are several methods for navigating through the chapters of a book.\n\nThe **sidebar** on the left provides a list of all chapters.\nClicking on any of the chapter titles will load that page.\n\nThe sidebar may not automatically appear if the window is too narrow, particularly on mobile displays.\nIn that situation, the menu icon (three horizontal bars) at the top-left of the page can be pressed to open and close the sidebar.\n\nThe **arrow buttons** at the bottom of the page can be used to navigate to the previous or the next chapter.\n\nThe **left and right arrow keys** on the keyboard can be used to navigate to the previous or the next chapter.\n\n## Top menu bar\n\nThe menu bar at the top of the page provides some icons for interacting with the book.\nThe icons displayed will depend on the settings of how the book was generated.\n\n| Icon | Description |\n|------|-------------|\n| <i class=\"fas fa-bars\"></i> | Opens and closes the chapter listing sidebar. |\n| <i class=\"fas fa-paintbrush\"></i> | Opens a picker to choose a different color theme. |\n| <i class=\"fas fa-magnifying-glass\"></i> | Opens a search bar for searching within the book. |\n| <i class=\"fas fa-print\"></i> | Instructs the web browser to print the entire book. |\n| <i class=\"fab fa-github\"></i> | Opens a link to the website that hosts the source code of the book. |\n| <i class=\"fas fa-pencil\"></i> | Opens a page to directly edit the source of the page you are currently reading. |\n\nTapping the menu bar will scroll the page to the top.\n\n## Search\n\nEach book has a built-in search system.\nPressing the search icon <i class=\"fas fa-magnifying-glass\"></i> in the menu bar, or pressing the <kbd>/</kbd> or <kbd>S</kbd> key on the keyboard will open an input box for entering search terms.\nTyping some terms will show matching chapters and sections in real time.\n\nClicking any of the results will jump to that section.\nThe up and down arrow keys can be used to navigate the results, and enter will open the highlighted section.\n\nAfter loading a search result, the matching search terms will be highlighted in the text.\nClicking a highlighted word or pressing the <kbd>Escape</kbd> key will remove the highlighting.\n\n## Code blocks\n\nmdBook books are often used for programming projects, and thus support highlighting code blocks and samples.\nCode blocks may contain several different icons for interacting with them:\n\n| Icon | Description |\n|------|-------------|\n| <i class=\"fa fa-copy\"></i> | Copies the code block into your local clipboard, to allow pasting into another application. |\n| <i class=\"fas fa-play\"></i> | For Rust code examples, this will execute the sample code and display the compiler output just below the example (see [playground]). |\n| <i class=\"fa fa-eye\"></i> | 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]). |\n| <i class=\"fas fa-clock-rotate-left\"></i> | For [editable code examples][editor], this will undo any changes you have made. |\n\nHere's an example:\n\n```rust\nprintln!(\"Hello, World!\");\n```\n\n[editor]: ../format/theme/editor.md\n[playground]: ../format/mdbook.md#rust-playground\n[hiding code lines]: ../format/mdbook.md#hiding-code-lines\n"
  },
  {
    "path": "guide/src/misc/contributors.md",
    "content": "# Contributors\n\nHere is a list of the contributors who have helped improving mdBook. Big\nshout-out to them!\n\n- [mdinger](https://github.com/mdinger)\n- Kevin ([kbknapp](https://github.com/kbknapp))\n- Steve Klabnik ([steveklabnik](https://github.com/steveklabnik))\n- Adam Solove ([asolove](https://github.com/asolove))\n- Wayne Nilsen ([waynenilsen](https://github.com/waynenilsen))\n- [funnkill](https://github.com/funkill)\n- Fu Gangqiang ([FuGangqiang](https://github.com/FuGangqiang))\n- [Michael-F-Bryan](https://github.com/Michael-F-Bryan)\n- Chris Spiegel ([cspiegel](https://github.com/cspiegel))\n- [projektir](https://github.com/projektir)\n- [Phaiax](https://github.com/Phaiax)\n- Matt Ickstadt ([mattico](https://github.com/mattico))\n- Weihang Lo ([weihanglo](https://github.com/weihanglo))\n- Avision Ho ([avisionh](https://github.com/avisionh))\n- Vivek Akupatni ([apatniv](https://github.com/apatniv))\n- Eric Huss ([ehuss](https://github.com/ehuss))\n- Josh Rotenberg ([joshrotenberg](https://github.com/joshrotenberg))\n\nIf you feel you're missing from this list, feel free to add yourself in a PR.\n"
  },
  {
    "path": "rustfmt.toml",
    "content": "style_edition = \"2024\"\n"
  },
  {
    "path": "src/cmd/build.rs",
    "content": "use super::command_prelude::*;\nuse crate::{get_book_dir, open};\nuse anyhow::Result;\nuse mdbook_driver::MDBook;\nuse tracing::error;\n\n// Create clap subcommand arguments\npub fn make_subcommand() -> Command {\n    Command::new(\"build\")\n        .about(\"Builds a book from its markdown files\")\n        .arg_dest_dir()\n        .arg_root_dir()\n        .arg_open()\n}\n\n// Build command implementation\npub fn execute(args: &ArgMatches) -> Result<()> {\n    let book_dir = get_book_dir(args);\n    let mut book = MDBook::load(book_dir)?;\n\n    set_dest_dir(args, &mut book);\n\n    book.build()?;\n\n    if args.get_flag(\"open\") {\n        // FIXME: What's the right behaviour if we don't use the HTML renderer?\n        let path = book.build_dir_for(\"html\").join(\"index.html\");\n        if !path.exists() {\n            error!(\"No chapter available to open\");\n            std::process::exit(1)\n        }\n        open(path);\n    }\n\n    Ok(())\n}\n"
  },
  {
    "path": "src/cmd/clean.rs",
    "content": "use super::command_prelude::*;\nuse crate::get_book_dir;\nuse anyhow::Context;\nuse anyhow::Result;\nuse mdbook_driver::MDBook;\nuse std::mem::take;\nuse std::path::PathBuf;\nuse std::{fmt, fs};\n\n// Create clap subcommand arguments\npub fn make_subcommand() -> Command {\n    Command::new(\"clean\")\n        .about(\"Deletes a built book\")\n        .arg_dest_dir()\n        .arg_root_dir()\n}\n\n// Clean command implementation\npub fn execute(args: &ArgMatches) -> Result<()> {\n    let book_dir = get_book_dir(args);\n    let book = MDBook::load(book_dir)?;\n\n    let dir_to_remove = match args.get_one::<PathBuf>(\"dest-dir\") {\n        Some(dest_dir) => std::env::current_dir()\n            .expect(\"current dir should be valid\")\n            .join(dest_dir),\n        None => book.root.join(&book.config.build.build_dir),\n    };\n\n    let removed = Clean::new(&dir_to_remove)?;\n    println!(\"{removed}\");\n\n    Ok(())\n}\n\n/// Formats a number of bytes into a human readable SI-prefixed size.\n/// Returns a tuple of `(quantity, units)`.\npub fn human_readable_bytes(bytes: u64) -> (f32, &'static str) {\n    static UNITS: [&str; 7] = [\"B\", \"KiB\", \"MiB\", \"GiB\", \"TiB\", \"PiB\", \"EiB\"];\n    let bytes = bytes as f32;\n    let i = ((bytes.log2() / 10.0) as usize).min(UNITS.len() - 1);\n    (bytes / 1024_f32.powi(i as i32), UNITS[i])\n}\n\n#[derive(Debug)]\npub struct Clean {\n    num_files_removed: u64,\n    num_dirs_removed: u64,\n    total_bytes_removed: u64,\n}\n\nimpl Clean {\n    fn new(dir: &PathBuf) -> Result<Clean> {\n        let mut files = vec![dir.clone()];\n        let mut children = Vec::new();\n        let mut num_files_removed = 0;\n        let mut num_dirs_removed = 0;\n        let mut total_bytes_removed = 0;\n\n        if dir.exists() {\n            while !files.is_empty() {\n                for file in files {\n                    if let Ok(meta) = file.metadata() {\n                        // Note: This can over-count bytes removed for hard-linked\n                        // files. It also under-counts since it only counts the exact\n                        // byte sizes and not the block sizes.\n                        total_bytes_removed += meta.len();\n                    }\n                    if file.is_file() {\n                        num_files_removed += 1;\n                    } else if file.is_dir() {\n                        num_dirs_removed += 1;\n                        for entry in fs::read_dir(file)? {\n                            children.push(entry?.path());\n                        }\n                    }\n                }\n                files = take(&mut children);\n            }\n            fs::remove_dir_all(&dir).with_context(|| \"Unable to remove the build directory\")?;\n        }\n\n        Ok(Clean {\n            num_files_removed,\n            num_dirs_removed,\n            total_bytes_removed,\n        })\n    }\n}\n\nimpl fmt::Display for Clean {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        write!(f, \"Removed \")?;\n        match (self.num_files_removed, self.num_dirs_removed) {\n            (0, 0) => write!(f, \"0 files\")?,\n            (0, 1) => write!(f, \"1 directory\")?,\n            (0, 2..) => write!(f, \"{} directories\", self.num_dirs_removed)?,\n            (1, _) => write!(f, \"1 file\")?,\n            (2.., _) => write!(f, \"{} files\", self.num_files_removed)?,\n        }\n\n        if self.total_bytes_removed == 0 {\n            Ok(())\n        } else {\n            // Don't show a fractional number of bytes.\n            if self.total_bytes_removed < 1024 {\n                write!(f, \", {}B total\", self.total_bytes_removed)\n            } else {\n                let (bytes, unit) = human_readable_bytes(self.total_bytes_removed);\n                write!(f, \", {bytes:.2}{unit} total\")\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/cmd/command_prelude.rs",
    "content": "//! Helpers for building the command-line arguments for commands.\n\npub use clap::{Arg, ArgMatches, Command, arg};\nuse mdbook_driver::MDBook;\nuse std::path::PathBuf;\n\npub trait CommandExt: Sized {\n    fn _arg(self, arg: Arg) -> Self;\n\n    fn arg_dest_dir(self) -> Self {\n        self._arg(\n            Arg::new(\"dest-dir\")\n                .short('d')\n                .long(\"dest-dir\")\n                .value_name(\"dest-dir\")\n                .value_parser(clap::value_parser!(PathBuf))\n                .help(\n                    \"Output directory for the book\\n\\\n                    Relative paths are interpreted relative to the current directory.\\n\\\n                    If omitted, mdBook uses build.build-dir from book.toml \\\n                    or defaults to `./book`.\",\n                ),\n        )\n    }\n\n    fn arg_root_dir(self) -> Self {\n        self._arg(\n            Arg::new(\"dir\")\n                .help(\n                    \"Root directory for the book\\n\\\n                    (Defaults to the current directory when omitted)\",\n                )\n                .value_parser(clap::value_parser!(PathBuf)),\n        )\n    }\n\n    fn arg_open(self) -> Self {\n        self._arg(arg!(-o --open \"Opens the compiled book in a web browser\"))\n    }\n\n    #[cfg(any(feature = \"watch\", feature = \"serve\"))]\n    fn arg_watcher(self) -> Self {\n        #[cfg(feature = \"watch\")]\n        return self._arg(\n            Arg::new(\"watcher\")\n                .long(\"watcher\")\n                .value_parser([\"poll\", \"native\"])\n                .default_value(\"poll\")\n                .help(\"The filesystem watching technique\"),\n        );\n        #[cfg(not(feature = \"watch\"))]\n        return self;\n    }\n}\n\nimpl CommandExt for Command {\n    fn _arg(self, arg: Arg) -> Self {\n        self.arg(arg)\n    }\n}\n\npub fn set_dest_dir(args: &ArgMatches, book: &mut MDBook) {\n    if let Some(dest_dir) = args.get_one::<PathBuf>(\"dest-dir\") {\n        let build_dir = std::env::current_dir()\n            .expect(\"current dir should be valid\")\n            .join(dest_dir);\n        book.config.build.build_dir = build_dir;\n    }\n}\n"
  },
  {
    "path": "src/cmd/init.rs",
    "content": "use crate::get_book_dir;\nuse anyhow::Result;\nuse clap::{ArgMatches, Command as ClapCommand, arg};\nuse mdbook_core::config;\nuse mdbook_driver::MDBook;\nuse std::io;\nuse std::io::Write;\nuse std::process::Command;\nuse tracing::debug;\n\n// Create clap subcommand arguments\npub fn make_subcommand() -> ClapCommand {\n    ClapCommand::new(\"init\")\n        .about(\"Creates the boilerplate structure and files for a new book\")\n        .arg(\n            arg!([dir]\n                \"Directory to create the book in\\n\\\n                (Defaults to the current directory when omitted)\"\n            )\n            .value_parser(clap::value_parser!(std::path::PathBuf)),\n        )\n        .arg(arg!(--theme \"Copies the default theme into your source folder\"))\n        .arg(arg!(--force \"Skips confirmation prompts\"))\n        .arg(arg!(--title <title> \"Sets the book title\"))\n        .arg(\n            arg!(--ignore <ignore> \"Creates a VCS ignore file (i.e. .gitignore)\")\n                .value_parser([\"none\", \"git\"]),\n        )\n}\n\n// Init command implementation\npub fn execute(args: &ArgMatches) -> Result<()> {\n    let book_dir = get_book_dir(args);\n    let mut builder = MDBook::init(&book_dir);\n    let mut config = config::Config::default();\n    // If flag `--theme` is present, copy theme to src\n    if args.get_flag(\"theme\") {\n        let theme_dir = book_dir.join(\"theme\");\n        println!();\n        println!(\"Copying the default theme to {}\", theme_dir.display());\n        // Skip this if `--force` is present\n        if !args.get_flag(\"force\") && theme_dir.exists() {\n            println!(\"This could potentially overwrite files already present in that directory.\");\n            print!(\"\\nAre you sure you want to continue? (y/n) \");\n\n            // Read answer from user and exit if it's not 'yes'\n            if confirm() {\n                builder.copy_theme(true);\n            }\n        } else {\n            builder.copy_theme(true);\n        }\n    }\n\n    if let Some(ignore) = args.get_one::<String>(\"ignore\").map(|s| s.as_str()) {\n        match ignore {\n            \"git\" => builder.create_gitignore(true),\n            _ => builder.create_gitignore(false),\n        };\n    } else if !args.get_flag(\"force\") {\n        println!(\"\\nDo you want a .gitignore to be created? (y/n)\");\n        if confirm() {\n            builder.create_gitignore(true);\n        }\n    }\n\n    config.book.title = if args.contains_id(\"title\") {\n        args.get_one::<String>(\"title\").map(String::from)\n    } else if args.get_flag(\"force\") {\n        None\n    } else {\n        request_book_title()\n    };\n\n    if let Some(author) = get_author_name() {\n        debug!(\"Obtained user name from gitconfig: {:?}\", author);\n        config.book.authors.push(author);\n    }\n\n    builder.with_config(config);\n    builder.build()?;\n    println!(\"\\nAll done, no errors...\");\n\n    Ok(())\n}\n\n/// Obtains author name from git config file by running the `git config` command.\nfn get_author_name() -> Option<String> {\n    let output = Command::new(\"git\")\n        .args([\"config\", \"--get\", \"user.name\"])\n        .output()\n        .ok()?;\n\n    if output.status.success() {\n        Some(String::from_utf8_lossy(&output.stdout).trim().to_owned())\n    } else {\n        None\n    }\n}\n\n/// Request book title from user and return if provided.\nfn request_book_title() -> Option<String> {\n    println!(\"What title would you like to give the book? \");\n    io::stdout().flush().unwrap();\n    let mut resp = String::new();\n    io::stdin().read_line(&mut resp).unwrap();\n    let resp = resp.trim();\n    if resp.is_empty() {\n        None\n    } else {\n        Some(resp.into())\n    }\n}\n\n// Simple function for user confirmation\nfn confirm() -> bool {\n    io::stdout().flush().unwrap();\n    let mut s = String::new();\n    io::stdin().read_line(&mut s).ok();\n    matches!(s.trim(), \"Y\" | \"y\" | \"yes\" | \"Yes\")\n}\n"
  },
  {
    "path": "src/cmd/mod.rs",
    "content": "//! Subcommand modules for the `mdbook` binary.\n\npub mod build;\npub mod clean;\npub mod command_prelude;\npub mod init;\n#[cfg(feature = \"serve\")]\npub mod serve;\npub mod test;\n#[cfg(feature = \"watch\")]\npub mod watch;\n"
  },
  {
    "path": "src/cmd/serve.rs",
    "content": "use super::command_prelude::*;\n#[cfg(feature = \"watch\")]\nuse super::watch;\nuse crate::{get_book_dir, open};\nuse anyhow::Result;\nuse axum::Router;\nuse axum::extract::ws::{Message, WebSocket, WebSocketUpgrade};\nuse axum::routing::get;\nuse clap::builder::NonEmptyStringValueParser;\nuse futures_util::StreamExt;\nuse futures_util::sink::SinkExt;\nuse mdbook_driver::MDBook;\nuse std::net::{SocketAddr, ToSocketAddrs};\nuse std::path::PathBuf;\nuse tokio::sync::broadcast;\nuse tower_http::services::{ServeDir, ServeFile};\nuse tracing::{error, info, trace};\n\n/// The HTTP endpoint for the websocket used to trigger reloads when a file changes.\nconst LIVE_RELOAD_ENDPOINT: &str = \"__livereload\";\n\n// Create clap subcommand arguments\npub fn make_subcommand() -> Command {\n    Command::new(\"serve\")\n        .about(\"Serves a book at http://localhost:3000, and rebuilds it on changes\")\n        .arg_dest_dir()\n        .arg_root_dir()\n        .arg(\n            Arg::new(\"hostname\")\n                .short('n')\n                .long(\"hostname\")\n                .num_args(1)\n                .default_value(\"localhost\")\n                .value_parser(NonEmptyStringValueParser::new())\n                .help(\"Hostname to listen on for HTTP connections\"),\n        )\n        .arg(\n            Arg::new(\"port\")\n                .short('p')\n                .long(\"port\")\n                .num_args(1)\n                .default_value(\"3000\")\n                .value_parser(NonEmptyStringValueParser::new())\n                .help(\"Port to use for HTTP connections\"),\n        )\n        .arg_open()\n        .arg_watcher()\n}\n\n// Serve command implementation\npub fn execute(args: &ArgMatches) -> Result<()> {\n    let book_dir = get_book_dir(args);\n    let mut book = MDBook::load(&book_dir)?;\n\n    let port = args.get_one::<String>(\"port\").unwrap();\n    let hostname = args.get_one::<String>(\"hostname\").unwrap();\n    let open_browser = args.get_flag(\"open\");\n\n    let address = format!(\"{hostname}:{port}\");\n\n    let update_config = |book: &mut MDBook| {\n        book.config\n            .set(\"output.html.live-reload-endpoint\", LIVE_RELOAD_ENDPOINT)\n            .expect(\"live-reload-endpoint update failed\");\n        set_dest_dir(args, book);\n        // Override site-url for local serving of the 404 file\n        book.config.set(\"output.html.site-url\", \"/\").unwrap();\n    };\n    update_config(&mut book);\n    book.build()?;\n\n    let sockaddr: SocketAddr = address\n        .to_socket_addrs()?\n        .next()\n        .ok_or_else(|| anyhow::anyhow!(\"no address found for {}\", address))?;\n    let build_dir = book.build_dir_for(\"html\");\n    let html_config = book.config.html_config().unwrap_or_default();\n    let file_404 = html_config.get_404_output_file();\n\n    // A channel used to broadcast to any websockets to reload when a file changes.\n    let (tx, _rx) = tokio::sync::broadcast::channel::<Message>(100);\n\n    let reload_tx = tx.clone();\n    let thread_handle = std::thread::spawn(move || {\n        serve(build_dir, sockaddr, reload_tx, &file_404);\n    });\n\n    let serving_url = format!(\"http://{address}\");\n    info!(\"Serving on: {}\", serving_url);\n\n    if open_browser {\n        open(serving_url);\n    }\n\n    #[cfg(feature = \"watch\")]\n    {\n        let watcher = watch::WatcherKind::from_str(args.get_one::<String>(\"watcher\").unwrap());\n        watch::rebuild_on_change(watcher, &book_dir, &update_config, &move || {\n            let _ = tx.send(Message::text(\"reload\"));\n        });\n    }\n\n    let _ = thread_handle.join();\n\n    Ok(())\n}\n\n#[tokio::main]\nasync fn serve(\n    build_dir: PathBuf,\n    address: SocketAddr,\n    reload_tx: broadcast::Sender<Message>,\n    file_404: &str,\n) {\n    let reload_tx_clone = reload_tx.clone();\n\n    // WebSocket handler for live reload\n    let websocket_handler = move |ws: WebSocketUpgrade| async move {\n        let reload_tx = reload_tx_clone.clone();\n        ws.on_upgrade(move |socket| websocket_connection(socket, reload_tx))\n    };\n\n    let app = Router::new()\n        .route(&format!(\"/{LIVE_RELOAD_ENDPOINT}\"), get(websocket_handler))\n        .fallback_service(\n            ServeDir::new(&build_dir).not_found_service(ServeFile::new(build_dir.join(file_404))),\n        );\n\n    std::panic::set_hook(Box::new(move |panic_info| {\n        // exit if serve panics\n        error!(\"Unable to serve: {}\", panic_info);\n        std::process::exit(1);\n    }));\n\n    let listener = tokio::net::TcpListener::bind(&address)\n        .await\n        .unwrap_or_else(|e| panic!(\"Unable to bind to {address}: {e}\"));\n\n    axum::serve(listener, app).await.unwrap();\n}\n\nasync fn websocket_connection(ws: WebSocket, reload_tx: broadcast::Sender<Message>) {\n    let (mut user_ws_tx, _user_ws_rx) = ws.split();\n    let mut rx = reload_tx.subscribe();\n\n    trace!(\"websocket got connection\");\n    if let Ok(m) = rx.recv().await {\n        trace!(\"notify of reload\");\n        let _ = user_ws_tx.send(m).await;\n    }\n}\n"
  },
  {
    "path": "src/cmd/test.rs",
    "content": "use super::command_prelude::*;\nuse crate::get_book_dir;\nuse anyhow::Result;\nuse clap::ArgAction;\nuse clap::builder::NonEmptyStringValueParser;\nuse mdbook_driver::MDBook;\n\n// Create clap subcommand arguments\npub fn make_subcommand() -> Command {\n    Command::new(\"test\")\n        .about(\"Tests that a book's Rust code samples compile\")\n        .arg_root_dir()\n        .arg(\n            Arg::new(\"chapter\")\n                .short('c')\n                .long(\"chapter\")\n                .value_name(\"chapter\"),\n        )\n        .arg(\n            Arg::new(\"library-path\")\n                .short('L')\n                .long(\"library-path\")\n                .value_name(\"dir\")\n                .value_delimiter(',')\n                .value_parser(NonEmptyStringValueParser::new())\n                .action(ArgAction::Append)\n                .help(\n                    \"A comma-separated list of directories to add to the crate \\\n                    search path when building tests\",\n                ),\n        )\n}\n\n// test command implementation\npub fn execute(args: &ArgMatches) -> Result<()> {\n    let library_paths: Vec<&str> = args\n        .get_many(\"library-path\")\n        .map(|it| it.map(String::as_str).collect())\n        .unwrap_or_default();\n\n    let chapter: Option<&str> = args.get_one::<String>(\"chapter\").map(|s| s.as_str());\n\n    let book_dir = get_book_dir(args);\n    let mut book = MDBook::load(book_dir)?;\n\n    match chapter {\n        Some(_) => book.test_chapter(library_paths, chapter),\n        None => book.test(library_paths),\n    }?;\n\n    Ok(())\n}\n"
  },
  {
    "path": "src/cmd/watch/native.rs",
    "content": "//! A filesystem watcher using native operating system facilities.\n\nuse ignore::gitignore::Gitignore;\nuse mdbook_driver::MDBook;\nuse std::path::{Path, PathBuf};\nuse std::sync::mpsc::channel;\nuse std::thread::sleep;\nuse std::time::Duration;\nuse tracing::{error, info, warn};\n\npub fn rebuild_on_change(\n    book_dir: &Path,\n    update_config: &dyn Fn(&mut MDBook),\n    post_build: &dyn Fn(),\n) {\n    use notify::RecursiveMode::*;\n\n    let mut book = MDBook::load(book_dir).unwrap_or_else(|e| {\n        error!(\"failed to load book: {e}\");\n        std::process::exit(1);\n    });\n\n    // Create a channel to receive the events.\n    let (tx, rx) = channel();\n\n    let mut debouncer = match notify_debouncer_mini::new_debouncer(Duration::from_secs(1), tx) {\n        Ok(d) => d,\n        Err(e) => {\n            error!(\"Error while trying to watch the files:\\n\\n\\t{:?}\", e);\n            std::process::exit(1)\n        }\n    };\n    let watcher = debouncer.watcher();\n\n    // Add the source directory to the watcher\n    if let Err(e) = watcher.watch(&book.source_dir(), Recursive) {\n        error!(\"Error while watching {:?}:\\n    {:?}\", book.source_dir(), e);\n        std::process::exit(1);\n    };\n\n    let _ = watcher.watch(&book.theme_dir(), Recursive);\n\n    // Add the book.toml file to the watcher if it exists\n    let _ = watcher.watch(&book.root.join(\"book.toml\"), NonRecursive);\n\n    for dir in &book.config.build.extra_watch_dirs {\n        let path = book.root.join(dir);\n        let canonical_path = path.canonicalize().unwrap_or_else(|e| {\n            error!(\"Error while watching extra directory {path:?}:\\n    {e}\");\n            std::process::exit(1);\n        });\n\n        if let Err(e) = watcher.watch(&canonical_path, Recursive) {\n            error!(\n                \"Error while watching extra directory {:?}:\\n    {:?}\",\n                canonical_path, e\n            );\n            std::process::exit(1);\n        }\n    }\n\n    info!(\"Listening for changes...\");\n\n    loop {\n        let first_event = rx.recv().unwrap();\n        sleep(Duration::from_millis(50));\n        let other_events = rx.try_iter();\n\n        let all_events = std::iter::once(first_event).chain(other_events);\n\n        let paths: Vec<_> = all_events\n            .filter_map(|event| match event {\n                Ok(events) => Some(events),\n                Err(error) => {\n                    warn!(\"error while watching for changes: {error}\");\n                    None\n                }\n            })\n            .flatten()\n            .map(|event| event.path)\n            .collect();\n\n        // If we are watching files outside the current repository (via extra-watch-dirs), then they are definitionally\n        // ignored by gitignore. So we handle this case by including such files into the watched paths list.\n        let any_external_paths = paths.iter().filter(|p| !p.starts_with(&book.root)).cloned();\n        let mut paths = remove_ignored_files(&book.root, &paths[..]);\n        paths.extend(any_external_paths);\n\n        if !paths.is_empty() {\n            info!(\"Files changed: {paths:?}\");\n            match MDBook::load(book_dir) {\n                Ok(mut b) => {\n                    update_config(&mut b);\n                    if let Err(e) = b.build() {\n                        error!(\"failed to build the book: {e:?}\");\n                    } else {\n                        post_build();\n                    }\n                    book = b;\n                }\n                Err(e) => error!(\"failed to load book config: {e:?}\"),\n            }\n        }\n    }\n}\n\nfn remove_ignored_files(book_root: &Path, paths: &[PathBuf]) -> Vec<PathBuf> {\n    if paths.is_empty() {\n        return vec![];\n    }\n\n    match super::find_gitignore(book_root) {\n        Some(gitignore_path) => {\n            let (ignore, err) = Gitignore::new(&gitignore_path);\n            if let Some(err) = err {\n                warn!(\n                    \"error reading gitignore `{}`: {err}\",\n                    gitignore_path.display()\n                );\n            }\n            filter_ignored_files(ignore, paths)\n        }\n        None => {\n            // There is no .gitignore file.\n            paths.iter().map(|path| path.to_path_buf()).collect()\n        }\n    }\n}\n\n// Note: The usage of `canonicalize` may encounter occasional failures on the Windows platform, presenting a potential risk.\n// For more details, refer to [Pull Request #2229](https://github.com/rust-lang/mdBook/pull/2229#discussion_r1408665981).\nfn filter_ignored_files(ignore: Gitignore, paths: &[PathBuf]) -> Vec<PathBuf> {\n    let ignore_root = ignore\n        .path()\n        .canonicalize()\n        .expect(\"ignore root canonicalize error\");\n\n    paths\n        .iter()\n        .filter(|path| {\n            let relative_path = pathdiff::diff_paths(&path, &ignore_root)\n                .expect(\"One of the paths should be an absolute\");\n            !ignore\n                .matched_path_or_any_parents(&relative_path, relative_path.is_dir())\n                .is_ignore()\n        })\n        .map(|path| path.to_path_buf())\n        .collect()\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use ignore::gitignore::GitignoreBuilder;\n    use std::env;\n\n    #[test]\n    fn test_filter_ignored_files() {\n        let current_dir = env::current_dir().unwrap();\n\n        let ignore = GitignoreBuilder::new(&current_dir)\n            .add_line(None, \"*.html\")\n            .unwrap()\n            .build()\n            .unwrap();\n        let should_remain = current_dir.join(\"record.text\");\n        let should_filter = current_dir.join(\"index.html\");\n\n        let remain = filter_ignored_files(ignore, &[should_remain.clone(), should_filter]);\n        assert_eq!(remain, vec![should_remain])\n    }\n\n    #[test]\n    fn filter_ignored_files_should_handle_parent_dir() {\n        let current_dir = env::current_dir().unwrap();\n\n        let ignore = GitignoreBuilder::new(&current_dir)\n            .add_line(None, \"*.html\")\n            .unwrap()\n            .build()\n            .unwrap();\n\n        let parent_dir = current_dir.join(\"..\");\n        let should_remain = parent_dir.join(\"record.text\");\n        let should_filter = parent_dir.join(\"index.html\");\n\n        let remain = filter_ignored_files(ignore, &[should_remain.clone(), should_filter]);\n        assert_eq!(remain, vec![should_remain])\n    }\n}\n"
  },
  {
    "path": "src/cmd/watch/poller.rs",
    "content": "//! A simple poll-based filesystem watcher.\n//!\n//! This exists because the native change notifications have historically had\n//! lots of problems. Various operating systems and different filesystems have\n//! had problems correctly reporting changes.\n\nuse ignore::gitignore::Gitignore;\nuse mdbook_driver::MDBook;\nuse pathdiff::diff_paths;\nuse std::collections::HashMap;\nuse std::fs::FileType;\nuse std::path::{Path, PathBuf};\nuse std::time::{Duration, Instant, SystemTime};\nuse tracing::{debug, error, info, trace, warn};\nuse walkdir::WalkDir;\n\n/// Calls the closure when a book source file is changed, blocking indefinitely.\npub fn rebuild_on_change(\n    book_dir: &Path,\n    update_config: &dyn Fn(&mut MDBook),\n    post_build: &dyn Fn(),\n) {\n    let mut book = MDBook::load(book_dir).unwrap_or_else(|e| {\n        error!(\"failed to load book: {e}\");\n        std::process::exit(1);\n    });\n\n    let mut watcher = Watcher::new(book_dir);\n\n    info!(\"Watching for changes...\");\n    // Scan once to initialize the starting point.\n    watcher.set_roots(&book);\n    watcher.scan();\n\n    // Track average scan time, to help investigate if the poller is taking\n    // undesirably long. This is not a rigorous benchmark, just a rough\n    // estimate.\n    const AVG_SIZE: usize = 60;\n    let mut avgs = vec![0.0; AVG_SIZE];\n    let mut avg_i = 0;\n\n    loop {\n        std::thread::sleep(Duration::new(1, 0));\n        let start = Instant::now();\n        let paths = watcher.scan();\n        let elapsed = start.elapsed().as_secs_f64();\n        avgs[avg_i] = elapsed;\n        avg_i += 1;\n        if avg_i >= AVG_SIZE {\n            avg_i = 0;\n            let avg = avgs.iter().sum::<f64>() / (avgs.len() as f64);\n            trace!(\n                \"scan average time: {avg:.2}s, scan size is {}\",\n                watcher.path_data.len()\n            );\n        }\n\n        if !paths.is_empty() {\n            info!(\"Files changed: {paths:?}\");\n            match MDBook::load(book_dir) {\n                Ok(mut b) => {\n                    update_config(&mut b);\n                    if let Err(e) = b.build() {\n                        error!(\"failed to build the book: {e:?}\");\n                    } else {\n                        post_build();\n                    }\n                    book = b;\n                    watcher.set_roots(&book);\n                }\n                Err(e) => error!(\"failed to load book config: {e:?}\"),\n            }\n        }\n    }\n}\n\n#[derive(PartialEq)]\nstruct PathData {\n    file_type: FileType,\n    mtime: SystemTime,\n    size: u64,\n}\n\n/// A very simple poll-watcher that scans for modified files.\n#[derive(Default)]\nstruct Watcher {\n    /// The root paths where it will recursively scan for changes.\n    root_paths: Vec<PathBuf>,\n    /// Data about files on disk.\n    path_data: HashMap<PathBuf, PathData>,\n    /// Filters paths that will be watched.\n    ignore: Option<(PathBuf, Gitignore)>,\n}\n\nimpl Watcher {\n    fn new(book_root: &Path) -> Watcher {\n        // FIXME: ignore should be reloaded when it changes.\n        let ignore = super::find_gitignore(book_root).map(|gitignore_path| {\n            let (ignore, err) = Gitignore::new(&gitignore_path);\n            if let Some(err) = err {\n                warn!(\n                    \"error reading gitignore `{}`: {err}\",\n                    gitignore_path.display()\n                );\n            }\n            // Note: The usage of `canonicalize` may encounter occasional\n            // failures on the Windows platform, presenting a potential risk.\n            // For more details, refer to [Pull Request\n            // #2229](https://github.com/rust-lang/mdBook/pull/2229#discussion_r1408665981).\n            let ignore_path = ignore\n                .path()\n                .canonicalize()\n                .expect(\"ignore root canonicalize error\");\n            (ignore_path, ignore)\n        });\n\n        Watcher {\n            ignore,\n            ..Default::default()\n        }\n    }\n\n    /// Sets the root directories where scanning will start.\n    fn set_roots(&mut self, book: &MDBook) {\n        let mut root_paths = vec![\n            book.source_dir(),\n            book.theme_dir(),\n            book.root.join(\"book.toml\"),\n        ];\n        root_paths.extend(\n            book.config\n                .build\n                .extra_watch_dirs\n                .iter()\n                .map(|path| book.root.join(path)),\n        );\n        if let Some(html_config) = book.config.html_config() {\n            root_paths.extend(\n                html_config\n                    .additional_css\n                    .iter()\n                    .chain(html_config.additional_js.iter())\n                    .map(|path| book.root.join(path)),\n            );\n        }\n\n        self.root_paths = root_paths;\n    }\n\n    /// Scans for changes.\n    ///\n    /// Returns the paths that have changed.\n    fn scan(&mut self) -> Vec<PathBuf> {\n        let ignore = &self.ignore;\n        let new_path_data: HashMap<_, _> = self\n            .root_paths\n            .iter()\n            .filter(|root| root.exists())\n            .flat_map(|root| {\n                WalkDir::new(root)\n                    .follow_links(true)\n                    .into_iter()\n                    .filter_entry(|entry| {\n                        if let Some((ignore_path, ignore)) = ignore {\n                            let path = entry.path();\n                            // Canonicalization helps with removing `..` and\n                            // `.` entries, which can cause issues with\n                            // diff_paths.\n                            let path = path.canonicalize().unwrap_or_else(|_| path.to_path_buf());\n                            let relative_path = diff_paths(&path, &ignore_path)\n                                .expect(\"One of the paths should be an absolute\");\n                            if ignore\n                                .matched_path_or_any_parents(&relative_path, relative_path.is_dir())\n                                .is_ignore()\n                            {\n                                trace!(\"ignoring {path:?}\");\n                                return false;\n                            }\n                        }\n                        true\n                    })\n                    .filter_map(move |entry| {\n                        let entry = match entry {\n                            Ok(e) => e,\n                            Err(e) => {\n                                debug!(\"failed to scan {root:?}: {e}\");\n                                return None;\n                            }\n                        };\n                        if entry.file_type().is_dir() {\n                            // Changes to directories themselves aren't\n                            // particularly interesting.\n                            return None;\n                        }\n                        let path = entry.path().to_path_buf();\n\n                        let meta = match entry.metadata() {\n                            Ok(meta) => meta,\n                            Err(e) => {\n                                debug!(\"failed to scan {path:?}: {e}\");\n                                return None;\n                            }\n                        };\n                        let mtime = meta.modified().unwrap_or(SystemTime::UNIX_EPOCH);\n                        let pd = PathData {\n                            file_type: meta.file_type(),\n                            mtime,\n                            size: meta.len(),\n                        };\n                        Some((path, pd))\n                    })\n            })\n            .collect();\n        let mut paths = Vec::new();\n        for (new_path, new_data) in &new_path_data {\n            match self.path_data.get(new_path) {\n                Some(old_data) => {\n                    if new_data != old_data {\n                        paths.push(new_path.to_path_buf());\n                    }\n                }\n                None => {\n                    paths.push(new_path.clone());\n                }\n            }\n        }\n        for old_path in self.path_data.keys() {\n            if !new_path_data.contains_key(old_path) {\n                paths.push(old_path.to_path_buf());\n            }\n        }\n        self.path_data = new_path_data;\n        paths\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    /// Helper for testing the watcher.\n    fn check_watch_behavior(\n        gitignore_path: &str,\n        gitignore: &str,\n        book_root_path: &str,\n        ignored: &[&str],\n        not_ignored: &[&str],\n        extra_setup: &dyn Fn(&Path),\n    ) {\n        // Create the book and initialize things.\n        let temp = tempfile::Builder::new()\n            .prefix(\"mdbook-\")\n            .tempdir()\n            .unwrap();\n        let root = temp.path();\n        let book_root = root.join(book_root_path);\n        // eprintln!(\"book_root={book_root:?}\",);\n        MDBook::init(&book_root).build().unwrap();\n        std::fs::write(root.join(gitignore_path), gitignore).unwrap();\n        let create = |paths: &[&str]| {\n            let mut paths = paths\n                .iter()\n                .map(|path| root.join(path))\n                .inspect(|path| {\n                    std::fs::create_dir_all(path.parent().unwrap()).unwrap();\n                    std::fs::write(path, \"initial content\").unwrap();\n                })\n                .map(|path| path.canonicalize().unwrap())\n                .collect::<Vec<_>>();\n            paths.sort();\n            paths\n        };\n        let ignored = create(ignored);\n        let not_ignored = create(not_ignored);\n        extra_setup(&book_root);\n        // Create a watcher and check its behavior.\n        let book = MDBook::load(&book_root).unwrap();\n        let mut watcher = Watcher::new(&book_root);\n        watcher.set_roots(&book);\n        // Do an initial scan to initialize its state.\n        watcher.scan();\n        // Verify the steady state is empty.\n        let changed = watcher.scan();\n        assert_eq!(changed, Vec::<PathBuf>::new());\n        // Modify all files, and verify that only not_ignored are detected.\n        for path in ignored.iter().chain(not_ignored.iter()) {\n            std::fs::write(path, \"modified\").unwrap();\n        }\n        let changed = watcher.scan();\n        let mut changed = changed\n            .into_iter()\n            .map(|p| p.canonicalize().unwrap())\n            .collect::<Vec<_>>();\n        changed.sort();\n        assert_eq!(changed, not_ignored);\n        // Verify again that steady state is empty.\n        let changed = watcher.scan();\n        assert_eq!(changed, Vec::<PathBuf>::new());\n    }\n\n    #[test]\n    fn test_ignore() {\n        // Basic gitignore test.\n        check_watch_behavior(\n            \"foo/.gitignore\",\n            \"*.tmp\",\n            \"foo\",\n            &[\"foo/src/somefile.tmp\"],\n            &[\"foo/src/chapter.md\"],\n            &|_book_root| {},\n        );\n    }\n\n    #[test]\n    fn test_ignore_in_parent() {\n        // gitignore is in the parent of the book\n        check_watch_behavior(\n            \".gitignore\",\n            \"*.tmp\\nsomedir/\\n/inroot\\n/foo/src/inbook\\n\",\n            \"foo\",\n            &[\n                \"foo/src/somefile.tmp\",\n                \"foo/src/somedir/somefile\",\n                \"inroot/somefile\",\n                \"foo/src/inbook/somefile\",\n            ],\n            &[\"foo/src/inroot/somefile\"],\n            &|_book_root| {},\n        );\n    }\n\n    #[test]\n    fn test_ignore_canonical() {\n        // test with path with ..\n        check_watch_behavior(\n            \".gitignore\",\n            \"*.tmp\\nsomedir/\\n/foo/src/inbook\\n\",\n            \"bar/../foo\",\n            &[\n                \"foo/src/somefile.tmp\",\n                \"foo/src/somedir/somefile\",\n                \"foo/src/inbook/somefile\",\n            ],\n            &[\"foo/src/chapter.md\"],\n            &|_book_root| {},\n        );\n    }\n\n    #[test]\n    fn test_scan_extra_watch() {\n        // Check behavior with extra-watch-dirs\n        check_watch_behavior(\n            \".gitignore\",\n            \"*.tmp\\n/outside-root/ignoreme\\n/foo/examples/ignoreme\\n\",\n            \"foo\",\n            &[\n                \"foo/src/somefile.tmp\",\n                \"foo/examples/example.tmp\",\n                \"outside-root/somefile.tmp\",\n                \"outside-root/ignoreme\",\n                \"foo/examples/ignoreme\",\n            ],\n            &[\n                \"foo/src/chapter.md\",\n                \"foo/examples/example.rs\",\n                \"foo/examples/example2.rs\",\n                \"outside-root/image.png\",\n            ],\n            &|book_root| {\n                std::fs::write(\n                    book_root.join(\"book.toml\"),\n                    r#\"\n                        [book]\n                        title = \"foo\"\n\n                        [build]\n                        extra-watch-dirs = [\n                            \"examples\",\n                            \"../outside-root\",\n                        ]\n                    \"#,\n                )\n                .unwrap();\n            },\n        );\n    }\n}\n"
  },
  {
    "path": "src/cmd/watch.rs",
    "content": "use super::command_prelude::*;\nuse crate::{get_book_dir, open};\nuse anyhow::Result;\nuse mdbook_driver::MDBook;\nuse std::path::{Path, PathBuf};\nuse tracing::error;\n\nmod native;\nmod poller;\n\n// Create clap subcommand arguments\npub fn make_subcommand() -> Command {\n    Command::new(\"watch\")\n        .about(\"Watches a book's files and rebuilds it on changes\")\n        .arg_dest_dir()\n        .arg_root_dir()\n        .arg_open()\n        .arg_watcher()\n}\n\npub enum WatcherKind {\n    Poll,\n    Native,\n}\n\nimpl WatcherKind {\n    pub fn from_str(s: &str) -> WatcherKind {\n        match s {\n            \"poll\" => WatcherKind::Poll,\n            \"native\" => WatcherKind::Native,\n            _ => panic!(\"unsupported watcher {s}\"),\n        }\n    }\n}\n\n// Watch command implementation\npub fn execute(args: &ArgMatches) -> Result<()> {\n    let book_dir = get_book_dir(args);\n    let mut book = MDBook::load(&book_dir)?;\n\n    let update_config = |book: &mut MDBook| {\n        set_dest_dir(args, book);\n    };\n    update_config(&mut book);\n\n    if args.get_flag(\"open\") {\n        book.build()?;\n        let path = book.build_dir_for(\"html\").join(\"index.html\");\n        if !path.exists() {\n            error!(\"No chapter available to open\");\n            std::process::exit(1)\n        }\n        open(path);\n    }\n\n    let watcher = WatcherKind::from_str(args.get_one::<String>(\"watcher\").unwrap());\n    rebuild_on_change(watcher, &book_dir, &update_config, &|| {});\n\n    Ok(())\n}\n\npub fn rebuild_on_change(\n    kind: WatcherKind,\n    book_dir: &Path,\n    update_config: &dyn Fn(&mut MDBook),\n    post_build: &dyn Fn(),\n) {\n    match kind {\n        WatcherKind::Poll => self::poller::rebuild_on_change(book_dir, update_config, post_build),\n        WatcherKind::Native => self::native::rebuild_on_change(book_dir, update_config, post_build),\n    }\n}\n\nfn find_gitignore(book_root: &Path) -> Option<PathBuf> {\n    book_root\n        .ancestors()\n        .map(|p| p.join(\".gitignore\"))\n        .find(|p| p.exists())\n}\n"
  },
  {
    "path": "src/main.rs",
    "content": "//! The mdbook CLI.\n\n#![allow(unreachable_pub, reason = \"not needed in a bin crate\")]\n\nuse anyhow::anyhow;\nuse clap::{Arg, ArgMatches, Command};\nuse clap_complete::Shell;\nuse mdbook_core::utils;\nuse std::env;\nuse std::ffi::OsStr;\nuse std::path::PathBuf;\nuse tracing::{error, info};\n\nmod cmd;\n\nconst VERSION: &str = concat!(\"v\", clap::crate_version!());\n\nfn main() {\n    init_logger();\n\n    let command = create_clap_command();\n\n    // Check which subcommand the user ran...\n    let res = match command.get_matches().subcommand() {\n        Some((\"init\", sub_matches)) => cmd::init::execute(sub_matches),\n        Some((\"build\", sub_matches)) => cmd::build::execute(sub_matches),\n        Some((\"clean\", sub_matches)) => cmd::clean::execute(sub_matches),\n        #[cfg(feature = \"watch\")]\n        Some((\"watch\", sub_matches)) => cmd::watch::execute(sub_matches),\n        #[cfg(feature = \"serve\")]\n        Some((\"serve\", sub_matches)) => cmd::serve::execute(sub_matches),\n        Some((\"test\", sub_matches)) => cmd::test::execute(sub_matches),\n        Some((\"completions\", sub_matches)) => (|| {\n            let shell = sub_matches\n                .get_one::<Shell>(\"shell\")\n                .ok_or_else(|| anyhow!(\"Shell name missing.\"))?;\n\n            let mut complete_app = create_clap_command();\n            clap_complete::generate(\n                *shell,\n                &mut complete_app,\n                \"mdbook\",\n                &mut std::io::stdout().lock(),\n            );\n            Ok(())\n        })(),\n        _ => unreachable!(),\n    };\n\n    if let Err(e) = res {\n        utils::log_backtrace(&e);\n\n        std::process::exit(101);\n    }\n}\n\n/// Create a list of valid arguments and sub-commands\nfn create_clap_command() -> Command {\n    let app = Command::new(clap::crate_name!())\n        .about(clap::crate_description!())\n        .author(\"Mathieu David <mathieudavid@mathieudavid.org>\")\n        .version(VERSION)\n        .propagate_version(true)\n        .arg_required_else_help(true)\n        .after_help(\n            \"For more information about a specific command, try `mdbook <command> --help`\\n\\\n             The source code for mdBook is available at: https://github.com/rust-lang/mdBook\",\n        )\n        .subcommand(cmd::init::make_subcommand())\n        .subcommand(cmd::build::make_subcommand())\n        .subcommand(cmd::test::make_subcommand())\n        .subcommand(cmd::clean::make_subcommand())\n        .subcommand(\n            Command::new(\"completions\")\n                .about(\"Generate shell completions for your shell to stdout\")\n                .arg(\n                    Arg::new(\"shell\")\n                        .value_parser(clap::value_parser!(Shell))\n                        .help(\"the shell to generate completions for\")\n                        .value_name(\"SHELL\")\n                        .required(true),\n                ),\n        );\n\n    #[cfg(feature = \"watch\")]\n    let app = app.subcommand(cmd::watch::make_subcommand());\n    #[cfg(feature = \"serve\")]\n    let app = app.subcommand(cmd::serve::make_subcommand());\n\n    app\n}\n\nfn init_logger() {\n    let filter = tracing_subscriber::EnvFilter::builder()\n        .with_env_var(\"MDBOOK_LOG\")\n        .with_default_directive(tracing_subscriber::filter::LevelFilter::INFO.into())\n        .from_env_lossy();\n    let log_env = std::env::var(\"MDBOOK_LOG\");\n    // Silence some particularly noisy dependencies unless the user\n    // specifically asks for them.\n    let silence_unless_specified = |filter: tracing_subscriber::EnvFilter, target| {\n        if !log_env.as_ref().map_or(false, |s| {\n            s.split(',').any(|directive| directive.starts_with(target))\n        }) {\n            filter.add_directive(format!(\"{target}=warn\").parse().unwrap())\n        } else {\n            filter\n        }\n    };\n    let filter = silence_unless_specified(filter, \"handlebars\");\n    let filter = silence_unless_specified(filter, \"html5ever\");\n\n    // Don't show the target by default, since it generally isn't useful\n    // unless you are overriding the level.\n    let with_target = log_env.is_ok();\n\n    tracing_subscriber::fmt()\n        .without_time()\n        .with_ansi(std::io::IsTerminal::is_terminal(&std::io::stderr()))\n        .with_writer(std::io::stderr)\n        .with_env_filter(filter)\n        .with_target(with_target)\n        .init();\n}\n\nfn get_book_dir(args: &ArgMatches) -> PathBuf {\n    if let Some(p) = args.get_one::<PathBuf>(\"dir\") {\n        // Check if path is relative from current dir, or absolute...\n        if p.is_relative() {\n            env::current_dir().unwrap().join(p)\n        } else {\n            p.to_path_buf()\n        }\n    } else {\n        env::current_dir().expect(\"Unable to determine the current directory\")\n    }\n}\n\nfn open<P: AsRef<OsStr>>(path: P) {\n    info!(\"Opening web browser\");\n    if let Err(e) = opener::open(path) {\n        error!(\"Error opening web browser: {}\", e);\n    }\n}\n\n#[test]\nfn verify_app() {\n    create_clap_command().debug_assert();\n}\n"
  },
  {
    "path": "tests/gui/books/all-summary/README.md",
    "content": "# All summary\n\nThis GUI test book tests all the different kinds of book items in the summary.\n"
  },
  {
    "path": "tests/gui/books/all-summary/book.toml",
    "content": "[book]\ntitle = \"all-summary\"\n"
  },
  {
    "path": "tests/gui/books/all-summary/src/SUMMARY.md",
    "content": "# Summary\n\n[Prefix 1](prefix-1.md)\n[Prefix 2](prefix-2.md)\n\n- [Introduction](intro.md)\n- [Draft]()\n\n# Part 1\n\n- [P1 C1](part-1/chapter-1.md)\n\n---\n\n# Part 2\n\n- [P2 C1](part-2/chapter-1.md)\n\n[Suffix 1](suffix-1.md)\n[Suffix 2](suffix-2.md)\n"
  },
  {
    "path": "tests/gui/books/all-summary/src/intro.md",
    "content": "# Introduction\n"
  },
  {
    "path": "tests/gui/books/all-summary/src/part-1/chapter-1.md",
    "content": "# P1 C1\n"
  },
  {
    "path": "tests/gui/books/all-summary/src/part-2/chapter-1.md",
    "content": "# P2 C1\n"
  },
  {
    "path": "tests/gui/books/all-summary/src/prefix-1.md",
    "content": "# Prefix 1\n"
  },
  {
    "path": "tests/gui/books/all-summary/src/prefix-2.md",
    "content": "# Prefix 2\n"
  },
  {
    "path": "tests/gui/books/all-summary/src/suffix-1.md",
    "content": "# Suffix 1\n"
  },
  {
    "path": "tests/gui/books/all-summary/src/suffix-2.md",
    "content": "# Suffix 2\n"
  },
  {
    "path": "tests/gui/books/basic/book.toml",
    "content": "[book]\ntitle = \"basic\"\n"
  },
  {
    "path": "tests/gui/books/basic/src/SUMMARY.md",
    "content": "# Summary\n\n- [Chapter 1](./chapter_1.md)\n"
  },
  {
    "path": "tests/gui/books/basic/src/chapter_1.md",
    "content": "# Chapter 1\n"
  },
  {
    "path": "tests/gui/books/heading-nav/README.md",
    "content": "# Heading nav\n\nThis GUI test book is used for testing sidebar heading navigation.\n"
  },
  {
    "path": "tests/gui/books/heading-nav/book.toml",
    "content": "[book]\ntitle = \"heading-nav\"\n"
  },
  {
    "path": "tests/gui/books/heading-nav/src/SUMMARY.md",
    "content": "# Summary\n\n- [Empty page](empty.md)\n- [Large text before first heading](large-intro.md)\n- [Normal text before first heading](normal-intro.md)\n- [Collapsed headings](collapsed.md)\n- [Headings with markup](markup.md)\n- [Current scrolls to bottom](current-to-bottom.md)\n- [Unusual heading levels](unusual-heading-levels.md)\n- [Filtered headings](filtered-headings.md)\n"
  },
  {
    "path": "tests/gui/books/heading-nav/src/collapsed.md",
    "content": "# Collapsed headings\n\nTests collapsed headings.\n\n## Heading 1\n\n1\\\n2\\\n3\\\n4\\\n5\n\n### Heading 1.1\n\n1\\\n2\\\n3\\\n4\\\n5\n\n### Heading 1.2\n\n1\\\n2\\\n3\\\n4\\\n5\n\n#### Heading 1.2.1\n\n1\\\n2\\\n3\\\n4\\\n5\n\n#### Heading 1.2.2\n\n1\\\n2\\\n3\\\n4\\\n5\n\n### Heading 1.3\n\n1\\\n2\\\n3\\\n4\\\n5\n\n## Heading 2\n\n1\\\n2\\\n3\\\n4\\\n5\n\n### Heading 2.1\n\n1\\\n2\\\n3\\\n4\\\n5\n\n#### Heading 2.1.1\n\n1\\\n2\\\n3\\\n4\\\n5\n\n##### Heading 2.1.1.1\n\n1\\\n2\\\n3\\\n4\\\n5\n\n###### Heading 2.1.1.1.1\n\n1\\\n2\\\n3\\\n4\\\n5\n"
  },
  {
    "path": "tests/gui/books/heading-nav/src/current-to-bottom.md",
    "content": "# Current scrolls to bottom\n\nChecks that the \"current\" header works even when there are headers near the bottom.\n\n## First header\n\n<span id=\"scroll-to-1\">1</span>\\\n<span id=\"scroll-to-2\">2</span>\\\n<span id=\"scroll-to-3\">3</span>\\\n<span id=\"scroll-to-4\">4</span>\\\n<span id=\"scroll-to-5\">5</span>\\\n<span id=\"scroll-to-6\">6</span>\\\n<span id=\"scroll-to-7\">7</span>\\\n<span id=\"scroll-to-8\">8</span>\\\n<span id=\"scroll-to-9\">9</span>\\\n<span id=\"scroll-to-10\">10</span>\\\n<span id=\"scroll-to-11\">11</span>\\\n<span id=\"scroll-to-12\">12</span>\\\n<span id=\"scroll-to-13\">13</span>\\\n<span id=\"scroll-to-14\">14</span>\\\n<span id=\"scroll-to-15\">15</span>\\\n<span id=\"scroll-to-16\">16</span>\\\n<span id=\"scroll-to-17\">17</span>\\\n<span id=\"scroll-to-18\">18</span>\\\n<span id=\"scroll-to-19\">19</span>\\\n<span id=\"scroll-to-20\">20</span>\n\n## Second header\n\n<span id=\"scroll-to-21\">21</span>\n\n### Second sub-header\n\n<span id=\"scroll-to-22\">22</span>\n\n## Third header\n\n<span id=\"scroll-to-23\">23</span>\n\n## Fourth header\n\n<span id=\"scroll-to-24\">24</span>\n\n## Fifth header\n\n<span id=\"scroll-to-25\">25</span>\n"
  },
  {
    "path": "tests/gui/books/heading-nav/src/empty.md",
    "content": "# Empty page\n"
  },
  {
    "path": "tests/gui/books/heading-nav/src/filtered-headings.md",
    "content": "# Filtered headings\n\n## Skateboard\n\nChecking for search marking.\n"
  },
  {
    "path": "tests/gui/books/heading-nav/src/large-intro.md",
    "content": "# Large text before first heading\n\nThis tests what happens if there is a lot of text before the first header, which is off the bottom of the screen.\n\n1\n\n2\n\n3\n\n4\n\n5\n\n6\n\n7\n\n8\n\n9\n\n10\n\n11\n\n12\n\n13\n\n14\n\n15\n\n16\n\n17\n\n18\n\n19\n\n20\n\n## First header\n\nText for first header.\n"
  },
  {
    "path": "tests/gui/books/heading-nav/src/markup.md",
    "content": "# Headings with markup\n\nTests that heading markup gets copied to the sidebar.\n\n## Heading with `code` or *italic* or **bold** or ~~strike~~\n\nBasic markup should be copied.\n\n## Heading with a [link](../index.html)\n\nProbably not super-wise to have headings with links, but at least they shouldn't explode.\n\n## Heading with a custom id { #custom-id .custom-class }\n\nMake sure navigation works on a custom id.\n\n## Heading with <span>html</span>\n\nWhat happens if there is inline HTML?\n"
  },
  {
    "path": "tests/gui/books/heading-nav/src/normal-intro.md",
    "content": "# Normal text before first heading\n\nThis test is to ensure the first heading shows up as \"current\" on page load.\n\n## The first heading\n\n1\n\n2\n\n3\n\n## The second heading\n\n### And a sub heading\n"
  },
  {
    "path": "tests/gui/books/heading-nav/src/unusual-heading-levels.md",
    "content": "# Unusual heading levels\n\n### Heading 3\n\n## Heading 2\n\n#### Heading 5\n\n#### Heading 5.1\n"
  },
  {
    "path": "tests/gui/books/heading-nav-folded/book.toml",
    "content": "[book]\ntitle = \"heading-nav-folded\"\n\n[output.html.fold]\nenable = true\nlevel = 0\n"
  },
  {
    "path": "tests/gui/books/heading-nav-folded/src/SUMMARY.md",
    "content": "# Summary\n\n- [Introduction](./intro.md)\n    - [Sub chapter](./sub/index.md)\n        - [Sub inner](./sub/inner/index.md)\n    - [Sub second chapter](./sub/second.md)\n- [Next main chapter](./next-main.md)\n"
  },
  {
    "path": "tests/gui/books/heading-nav-folded/src/intro.md",
    "content": "# Introduction\n\n## Heading A\n\n### Heading A2\n\n### Heading A3\n\n## Heading B\n"
  },
  {
    "path": "tests/gui/books/heading-nav-folded/src/next-main.md",
    "content": "# Next main chapter\n"
  },
  {
    "path": "tests/gui/books/heading-nav-folded/src/sub/index.md",
    "content": "# Sub chapter\n\n## Sub-chapter heading\n"
  },
  {
    "path": "tests/gui/books/heading-nav-folded/src/sub/inner/index.md",
    "content": "# Sub inner\n\n## Inner chapter heading\n"
  },
  {
    "path": "tests/gui/books/heading-nav-folded/src/sub/second.md",
    "content": "# Sub second chapter\n\n## Second chapter heading\n"
  },
  {
    "path": "tests/gui/books/highlighting/README.md",
    "content": "# Syntax Highlighting\n\nThis GUI test book is used for testing syntax highlighting.\n"
  },
  {
    "path": "tests/gui/books/highlighting/book.toml",
    "content": "[book]\ntitle = \"Syntax Highlighting\"\n"
  },
  {
    "path": "tests/gui/books/highlighting/src/SUMMARY.md",
    "content": "# Summary\n\n- [Languages](./languages.md)\n"
  },
  {
    "path": "tests/gui/books/highlighting/src/languages.md",
    "content": "# Syntax Highlights\n\n## apache\n\n```apache\n# rewrite`s rules for wordpress pretty url\nLoadModule rewrite_module  modules/mod_rewrite.so\nRewriteCond %{REQUEST_FILENAME} !-f\nRewriteCond %{REQUEST_FILENAME} !-d\nRewriteRule . index.php [NC,L]\n\nExpiresActive On\nExpiresByType application/x-javascript  \"access plus 1 days\"\n\nOrder Deny,Allow\nAllow from All\n\n<Location /maps/>\n  RewriteMap map txt:map.txt\n  RewriteMap lower int:tolower\n  RewriteCond %{REQUEST_URI} ^/([^/.]+)\\.html$ [NC]\n  RewriteCond ${map:${lower:%1}|NOT_FOUND} !NOT_FOUND\n  RewriteRule .? /index.php?q=${map:${lower:%1}} [NC,L]\n</Location>\n\n20.164.151.111 - - [20/Aug/2015:22:20:18 -0400] \"GET /mywebpage/index.php HTTP/1.1\" 403 772 \"-\" \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/13.0.782.220 Safari/535.1\"\n```\n\n## armasm\n\n```armasm\n.data\n\n/* Data segment: define our message string and calculate its length. */\nmsg:\n    .ascii      \"Hello, ARM!\\n\"\nlen = . - msg\n\n.text\n\n/* Our application's entry point. */\n.globl _start\n_start:\n    /* syscall write(int fd, const void *buf, size_t count) */\n    mov     %r0, $1     /* fd := STDOUT_FILENO */\n    ldr     %r1, =msg   /* buf := msg */\n    ldr     %r2, =len   /* count := len */\n    mov     %r7, $4     /* write is syscall #4 */\n    swi     $0          /* invoke syscall */\n\n    /* syscall exit(int status) */\n    mov     %r0, $0     /* status := 0 */\n    mov     %r7, $1     /* exit is syscall #1 */\n    swi     $0          /* invoke syscall */\n\n```\n\n## bash\n\n```bash\n#!/bin/bash\n\n###### CONFIG\nACCEPTED_HOSTS=\"/root/.hag_accepted.conf\"\nBE_VERBOSE=false\n\nif [ \"$UID\" -ne 0 ]\nthen\n echo \"Superuser rights required\"\n exit 2\nfi\n\ngenApacheConf(){\n echo -e \"# Host ${HOME_DIR}$1/$2 :\"\n}\n\necho '\"quoted\"' | tr -d \\\" > text.txt\n\n```\n\n## c\n\n```c\n#include <stdio.h>\nvoid main(int argc,char ** argv){\n    printf(\"Hello World!\");\n}\n\n```\n\n## coffeescript\n\n```coffeescript\ngrade = (student, period=(if b? then 7 else 6)) ->\n  if student.excellentWork\n    \"A+\"\n  else if student.okayStuff\n    if student.triedHard then \"B\" else \"B-\"\n  else\n    \"C\"\n\nclass Animal extends Being\n  constructor: (@name) ->\n\n  move: (meters) ->\n    alert @name + \" moved #{meters}m.\"\n```\n\n## cpp\n\n```cpp\n#include <iostream>\nusing namespace std;\nint main() {\n   cout << \"Hello, World!\" << endl; // This prints Hello, World!\n   return 0;\n}\n```\n\n## csharp\n\n```csharp\nusing System;\nclass App\n{\n  static void Main()\n  {\n    Console.WriteLine(\"Hello World!\");\n  }\n}\n```\n\n## css\n\n```css\n@font-face {\n  font-family: Chunkfive;\n  src: url('Chunkfive.otf');\n}\n\nbody,\n.usertext {\n  color: #f0f0f0;\n  background: #600;\n  font-family: Chunkfive, sans;\n  --heading-1: 30px/32px Helvetica, sans-serif;\n}\n\n@import url(print.css);\n@media print {\n  a[href^='http']::after {\n    content: attr(href);\n  }\n}\n```\n\n## d\n\n```d\n/* This program prints a\n   hello world message\n   to the console.  */\n\nimport std.stdio;\n\nvoid main()\n{\n    writeln(\"Hello, World!\");\n}\n```\n\n## diff\n\n```diff\nIndex: languages/ini.js\n===================================================================\n--- languages/ini.js    (revision 199)\n+++ languages/ini.js    (revision 200)\n@@ -1,8 +1,7 @@\n hljs.LANGUAGES.ini =\n {\n   case_insensitive: true,\n-  defaultMode:\n-  {\n+  defaultMode: {\n     contains: ['comment', 'title', 'setting'],\n     illegal: '[^\\\\s]'\n   },\n\n*** /path/to/original timestamp\n--- /path/to/new      timestamp\n***************\n*** 1,3 ****\n--- 1,9 ----\n+ This is an important\n+ notice! It should\n+ therefore be located at\n+ the beginning of this\n+ document!\n\n! compress the size of the\n! changes.\n\n  It is important to spell\n```\n\n## go\n\n```go\npackage main\nimport \"fmt\"\n\nfunc main() {\n    fmt.Println(\"Hello World!\")\n}\n```\n\n## handlebars\n\n```handlebars\n<div class='entry'>\n  {{! only show if author exists }}\n  {{#if author}}\n    <h1>{{firstName}} {{lastName}}</h1>\n  {{/if}}\n</div>\n```\n\n## haskell\n\n```haskell\nmain :: IO ()\nmain = putStrLn \"Hello World!\"\n\n```\n\n## http\n\n```http\nPOST /task?id=1 HTTP/1.1\nHost: example.org\nContent-Type: application/json; charset=utf-8\nContent-Length: 137\n\n{\n  \"status\": \"ok\",\n  \"extended\": true,\n  \"results\": [\n    {\"value\": 0, \"type\": \"int64\"},\n    {\"value\": 1.0e+3, \"type\": \"decimal\"}\n  ]\n}\n\n```\n\n## ini\n\n```ini\n; boilerplate\n[package]\nname = \"some_name\"\nauthors = [\"Author\"]\ndescription = \"This is \\\na description\"\n\n[[lib]]\nname = ${NAME}\ndefault = True\nauto = no\ncounter = 1_000\n```\n\n## java\n\n```java\nclass Main {\n public static void main(String[] args) {\n    System.out.println(\"Hello World!\");\n  }\n}\n```\n\n## javascript\n\n```javascript\nfunction $initHighlight(block, cls) {\n  try {\n    if (cls.search(/\\bno\\-highlight\\b/) != -1)\n      return process(block, true, 0x0F) +\n             ` class=\"${cls}\"`;\n  } catch (e) {\n    /* handle exception */\n  }\n  for (var i = 0 / 2; i < classes.length; i++) {\n    if (checkCondition(classes[i]) === undefined)\n      console.log('undefined');\n  }\n\n  return (\n    <div>\n      <web-component>{block}</web-component>\n    </div>\n  )\n}\n\nexport  $initHighlight;\n```\n\n## json\n\n```json\n[\n  {\n    \"title\": \"apples\",\n    \"count\": [12000, 20000],\n    \"description\": { \"text\": \"...\", \"sensitive\": false }\n  },\n  {\n    \"title\": \"oranges\",\n    \"count\": [17500, null],\n    \"description\": { \"text\": \"...\", \"sensitive\": false }\n  }\n]\n```\n\n## julia\n\n```julia\n# function to calculate the volume of a sphere\nfunction sphere_vol(r)\n    # julia allows Unicode names (in UTF-8 encoding)\n    # so either \"pi\" or the symbol π can be used\n    return 4/3*pi*r^3\nend\n\n# functions can also be defined more succinctly\nquadratic(a, sqr_term, b) = (-b + sqr_term) / 2a\n\n# calculates x for 0 = a*x^2+b*x+c, arguments types can be defined in function definitions\nfunction quadratic2(a::Float64, b::Float64, c::Float64)\n    # unlike other languages 2a is equivalent to 2*a\n    # a^2 is used instead of a**2 or pow(a,2)\n    sqr_term = sqrt(b^2-4a*c)\n    r1 = quadratic(a, sqr_term, b)\n    r2 = quadratic(a, -sqr_term, b)\n    # multiple values can be returned from a function using tuples\n    # if the return keyword is omitted, the last term is returned\n    r1, r2\nend\n\nvol = sphere_vol(3)\n```\n\n## kotlin\n\n```kotlin\npackage org.kotlinlang.play\n\nfun main() {\n    println(\"Hello, World!\")\n}\n```\n\n## less\n\n```less\n@import 'fruits';\n\n@rhythm: 1.5em;\n\n@media screen and (min-resolution: 2dppx) {\n  body {\n    font-size: 125%;\n  }\n}\n\nsection > .foo + #bar:hover [href*='less'] {\n  margin: @rhythm 0 0 @rhythm;\n  padding: calc(5% + 20px);\n  background: #f00ba7 url(http://placehold.alpha-centauri/42.png) no-repeat;\n  background-image: linear-gradient(-135deg, wheat, fuchsia) !important ;\n  background-blend-mode: multiply;\n}\n\n@font-face {\n  font-family: /* ? */ 'Omega';\n  src: url('../fonts/omega-webfont.woff?v=2.0.2');\n}\n\n.icon-baz::before {\n  display: inline-block;\n  font-family: 'Omega', Alpha, sans-serif;\n  content: '\\f085';\n  color: rgba(98, 76 /* or 54 */, 231, 0.75);\n}\n```\n\n## lua\n\n```lua\n--[[\nSimple signal/slot implementation\n]]\nlocal signal_mt = {\n    __index = {\n        register = table.insert\n    }\n}\nfunction signal_mt.__index:emit(... --[[ Comment in params ]])\n    for _, slot in ipairs(self) do\n        slot(self, ...)\n    end\nend\nlocal function create_signal()\n    return setmetatable({}, signal_mt)\nend\n\n-- Signal test\nlocal signal = create_signal()\nsignal:register(function(signal, ...)\n    print(...)\nend)\nsignal:emit('Answer to Life, the Universe, and Everything:', 42)\n\n--[==[ [=[ [[\nNested ]]\nmulti-line ]=]\ncomment ]==]\n[==[ Nested\n[=[ multi-line\n[[ string\n]] ]=] ]==]\n```\n\n## makefile\n\n```makefile\n# Makefile\n\nBUILDDIR      = _build\nEXTRAS       ?= $(BUILDDIR)/extras\n\n.PHONY: main clean\n\nmain:\n\t@echo \"Building main facility...\"\n\tbuild_main $(BUILDDIR)\n\nclean:\n\trm -rf $(BUILDDIR)/*\n\n```\n\n## markdown\n\n```markdown\n# hello world\n\nyou can write text [with links](http://example.com) inline or [link references][1].\n\n- one _thing_ has *em*phasis\n- two **things** are **bold**\n\n[1]: http://example.com\n\n---\n\n# hello world\n\n<this_is inline=\"xml\"></this_is>\n\n> markdown is so cool\n\n    so are code segments\n\n1. one thing (yeah!)\n2. two thing `i can write code`, and `more` wipee!\n```\n\n## nginx\n\n```nginx\nuser  www www;\nworker_processes  2;\npid /var/run/nginx.pid;\nerror_log  /var/log/nginx.error_log  debug | info | notice | warn | error | crit;\n\nevents {\n    connections   2000;\n    use kqueue | rtsig | epoll | /dev/poll | select | poll;\n}\n\nhttp {\n    log_format main      '$remote_addr - $remote_user [$time_local] '\n                         '\"$request\" $status $bytes_sent '\n                         '\"$http_referer\" \"$http_user_agent\" '\n                         '\"$gzip_ratio\"';\n\n    send_timeout 3m;\n    client_header_buffer_size 1k;\n\n    gzip on;\n    gzip_min_length 1100;\n\n    #lingering_time 30;\n\n    server {\n        server_name   one.example.com  www.one.example.com;\n        access_log   /var/log/nginx.access_log  main;\n\n        rewrite (.*) /index.php?page=$1 break;\n\n        location / {\n            proxy_pass         http://127.0.0.1/;\n            proxy_redirect     off;\n            proxy_set_header   Host             $host;\n            proxy_set_header   X-Real-IP        $remote_addr;\n            charset            koi8-r;\n        }\n\n        location /api/ {\n            fastcgi_pass 127.0.0.1:9000;\n        }\n\n        location ~* \\.(jpg|jpeg|gif)$ {\n            root         /spool/www;\n        }\n    }\n}\n```\n\n## nim\n\n```nim\nfrom strutils import `%`\n\nconst numDoors = 100\nvar doors {.compileTime.}: array[1..numDoors, bool]\n\nproc calcDoors(): string =\n  for pass in 1..numDoors:\n    for door in countup(pass, numDoors, pass):\n      doors[door] = not doors[door]\n  for door in 1..numDoors:\n    result.add(\"Door $1 is $2.\\n\" % [$door, if doors[door]: \"open\" else: \"closed\"])\n\nconst outputString: string = calcDoors()\n\necho outputString\n```\n\n## objectivec\n\n```objectivec\n#import <Foundation/Foundation.h>\n\nint main(int argc, const char * argv[]) {\n    @mylak {\n        NSLog(@\"Hello World!\");\n    }\n    return 0;\n}\n\n```\n\n## nix\n\n```nix\nlet\n  world = \"World!\";\nin\n\"Hello \" + world\n```\n\n## perl\n\n```perl\nprint \"Hello World!\\n\";\n```\n\n## php\n\n```php\n<?php\necho \"Hello World!\";\n?>\n```\n\n## plaintext\n\n```plaintext\nI think this is simply plain text?\nHello World!\n```\n\n## properties\n\n```properties\n# .properties\n! Exclamation mark = comments, too\n\nkey1 = value1\nkey2 : value2\nkey3   value3\nkey\\ spaces multiline\\\n            value4\nempty_key\n! Key can contain escaped chars\n\\:\\= = value5\n```\n\n## python\n\n```python\n@requires_authorization(roles=[\"ADMIN\"])\ndef somefunc(param1='', param2=0):\n    r'''A docstring'''\n    if param1 > param2: # interesting\n        print 'Gre\\'ater'\n    return (param2 - param1 + 1 + 0b10l) or None\n\nclass SomeClass:\n    pass\n\n>>> message = '''interpreter\n... prompt'''\n```\n\n## r\n\n```r\nrequire(stats)\n\n#' Compute different averages\n#'\n#' @param x \\code{numeric} vector of sample data\n#' @param type \\code{character} vector of length 1 specifying the average type\n#' @return \\code{centre} returns the sample average according to the chosen method.\n#' @examples\n#' centre(rcauchy(10), \"mean\")\n#' @export\ncentre <- function(x, type) {\n  switch(type,\n         mean = mean(x),\n         median = median(x),\n         trimmed = mean(x, trim = .1))\n}\nx <- rcauchy(10)\ncentre(x, \"mean\")\n\nlibrary(ggplot2)\n\nmodels <- tibble::tribble(\n  ~model_name,    ~ formula,\n  \"length-width\", Sepal.Length ~ Petal.Width + Petal.Length,\n  \"interaction\",  Sepal.Length ~ Petal.Width * Petal.Length\n)\n\niris %>%\n  nest_by(Species) %>%\n  left_join(models, by = character()) %>%\n  rowwise(Species, model_name) %>%\n  mutate(model = list(lm(formula, data = data))) %>%\n  summarise(broom::glance(model))\n```\n\n## ruby\n\n```ruby\n# The Greeter class\nclass Greeter\n  def initialize(name)\n    @name = name.capitalize\n  end\n\n  def salute\n    puts \"Hello #{@name}!\"\n  end\nend\n\ng = Greeter.new(\"world\")\ng.salute\n```\n\n## rust\n\n```rust\nfn main()->(){\n    println!(\"Hello World!\");\n}\n```\n\n## scala\n\n```scala\n/**\n * A person has a name and an age.\n */\ncase class Person(name: String, age: Int)\n\nabstract class Vertical extends CaseJeu\ncase class Haut(a: Int) extends Vertical\ncase class Bas(name: String, b: Double) extends Vertical\n\nsealed trait Ior[+A, +B]\ncase class Left[A](a: A) extends Ior[A, Nothing]\ncase class Right[B](b: B) extends Ior[Nothing, B]\ncase class Both[A, B](a: A, b: B) extends Ior[A, B]\n\ntrait Functor[F[_]] {\n  def map[A, B](fa: F[A], f: A => B): F[B]\n}\n\n// beware Int.MinValue\ndef absoluteValue(n: Int): Int =\n  if (n < 0) -n else n\n\ndef interp(n: Int): String =\n  s\"there are $n ${color} balloons.\\n\"\n\ntype ξ[A] = (A, A)\n\ntrait Hist { lhs =>\n  def ⊕(rhs: Hist): Hist\n}\n\ndef gsum[A: Ring](as: Seq[A]): A =\n  as.foldLeft(Ring[A].zero)(_ + _)\n\nval actions: List[Symbol] =\n  'init :: 'read :: 'write :: 'close :: Nil\n```\n\n## scss\n\n```scss\nimport \"compass/reset\";\n\n// variables\n$colorGreen: #008000;\n$colorGreenDark: darken($colorGreen, 10);\n\n@mixin container {\n    max-width: 980px;\n}\n\n// mixins with parameters\n@mixin button($color:green) {\n    @if ($color == green) {\n        background-color: #008000;\n    }\n    @else if ($color == red) {\n        background-color: #B22222;\n    }\n}\n\nbutton {\n    @include button(red);\n}\n\ndiv,\n.navbar,\n#header,\ninput[type=\"input\"] {\n    font-family: \"Helvetica Neue\", Arial, sans-serif;\n    width: auto;\n    margin: 0 auto;\n    display: block;\n}\n\n.row-12 > [class*=\"spans\"] {\n    border-left: 1px solid #B5C583;\n}\n\n```\n\n## shell\n\n```shell\n$ echo $EDITOR\nvim\n$ git checkout main\nSwitched to branch 'main'\nYour branch is up-to-date with 'origin/main'.\n$ git push\nEverything up-to-date\n$ echo 'All\n> done!'\nAll\ndone!\n\n```\n\n## sql\n\n```sql\nCREATE TABLE \"topic\" (\n    \"id\" integer NOT NULL PRIMARY KEY,\n    \"forum_id\" integer NOT NULL,\n    \"subject\" varchar(255) NOT NULL\n);\nALTER TABLE \"topic\"\nADD CONSTRAINT forum_id FOREIGN KEY (\"forum_id\")\nREFERENCES \"forum\" (\"id\");\n\n-- Initials\ninsert into \"topic\" (\"forum_id\", \"subject\")\nvalues (2, 'D''artagnian');\n```\n\n## swift\n\n```swift\nimport Foundation\n\n@objc class Person: Entity {\n  var name: String!\n  var age:  Int!\n\n  init(name: String, age: Int) {\n    /* /* ... */ */\n  }\n\n  // Return a descriptive string for this person\n  func description(offset: Int = 0) -> String {\n    return \"\\(name) is \\(age + offset) years old\"\n  }\n}\n```\n\n## typescript\n\n```typescript\nclass MyClass {\n  public static myValue: string;\n  constructor(init: string) {\n    this.myValue = init;\n  }\n}\nimport fs = require(\"fs\");\nmodule MyModule {\n  export interface MyInterface extends Other {\n    myProperty: any;\n  }\n}\ndeclare magicNumber number;\nmyArray.forEach(() => { }); // fat arrow syntax\n\n```\n\n## x86asm\n\n```x86asm\nsection .text\nextern  _MessageBoxA@16\n%if     __NASM_VERSION_ID__ >= 0x02030000\nsafeseh handler         ; register handler as \"safe handler\"\n%endif\n\nhandler:\n        push    dword 1 ; MB_OKCANCEL\n        push    dword caption\n        push    dword text\n        push    dword 0\n        call    _MessageBoxA@16\n        sub     eax,1   ; incidentally suits as return value\n                        ; for exception handler\n        ret\n\nglobal  _main\n_main:  push    dword handler\n        push    dword [fs:0]\n        mov     dword [fs:0], esp\n        xor     eax,eax\n        mov     eax, dword[eax]   ; cause exception\n        pop     dword [fs:0]      ; disengage exception handler\n        add     esp, 4\n        ret\n\navx2:   vzeroupper\n        push      rbx\n        mov       rbx,   rsp\n        sub       rsp,   0h20\n        vmovdqa   ymm0,  [rcx]\n        vpaddb    ymm0,  [rdx]\n        leave\n        ret\n\ntext:   db      'OK to rethrow, CANCEL to generate core dump',0\ncaption:db      'SEGV',0\n\nsection .drectve info\n        db      '/defaultlib:user32.lib /defaultlib:msvcrt.lib '\n```\n\n## xml\n\n```xml\n<!DOCTYPE html>\n<title>Title</title>\n\n<style>body {width: 500px;}</style>\n\n<script type=\"application/javascript\">\n  function $init() {return true;}\n</script>\n\n<body>\n  <p checked class=\"title\" id='title'>Title</p>\n  <!-- here goes the rest of the page -->\n</body>\n```\n\n## yaml\n\n```yaml\n---\n# comment\nstring_1: \"Bar\"\nstring_2: 'bar'\nstring_3: bar\ninline_keys_ignored: sompath/name/file.jpg\nkeywords_in_yaml:\n  - true\n  - false\n  - TRUE\n  - FALSE\n  - 21\n  - 21.0\n  - !!str 123\n\"quoted_key\": &foobar\n  bar: foo\n  foo:\n  \"foo\": bar\n\nreference: *foobar\n\nmultiline_1: |\n  Multiline\n  String\nmultiline_2: >\n  Multiline\n  String\nmultiline_3: \"\n  Multiline string\n  \"\n\nansible_variables: \"foo {{variable}}\"\n\narray_nested:\n- a\n- b: 1\n  c: 2\n- b\n- comment\n```\n"
  },
  {
    "path": "tests/gui/books/redirect/README.md",
    "content": "# Redirect\n\nThis GUI test book tests the redirect configuration.\n"
  },
  {
    "path": "tests/gui/books/redirect/book.toml",
    "content": "[book]\ntitle = \"redirect\"\n\n[output.html.redirect]\n\"/inner/old.html\" = \"../new-chapter.html\"\n\n# This is a source without a fragment, and one with a fragment that goes to\n# the same place. The redirect with the fragment is not necessary, since that\n# is the default behavior.\n\"/pointless-fragment.html\" = \"new-chapter.html\"\n\"/pointless-fragment.html#foo\" = \"new-chapter.html#foo\"\n\n\"/rename-page-and-fragment.html\" = \"new-chapter.html\"\n\"/rename-page-and-fragment.html#orig\" = \"new-chapter.html#new\"\n\n\"/rename-page-fragment-elsewhere.html\" = \"new-chapter.html\"\n\"/rename-page-fragment-elsewhere.html#orig\" = \"other-chapter.html#new\"\n\n# Rename fragment on an existing page.\n\"/new-chapter.html#orig\" = \"new-chapter.html#new\"\n# Rename fragment on an existing page to another page.\n\"/new-chapter.html#orig-new-chapter\" = \"other-chapter.html#new\"\n\n\"/full-url-with-fragment.html\" = \"https://www.rust-lang.org/#fragment\"\n\n\"/full-url-with-fragment-map.html\" = \"https://www.rust-lang.org/\"\n\"/full-url-with-fragment-map.html#a\" = \"https://www.rust-lang.org/#new1\"\n\"/full-url-with-fragment-map.html#b\" = \"https://www.rust-lang.org/#new2\"\n"
  },
  {
    "path": "tests/gui/books/redirect/src/SUMMARY.md",
    "content": "# Summary\n\n- [New chapter](new-chapter.md)\n- [Other chapter](other-chapter.md)\n"
  },
  {
    "path": "tests/gui/books/redirect/src/chapter_1.md",
    "content": "# Chapter 1\n"
  },
  {
    "path": "tests/gui/books/redirect/src/new-chapter.md",
    "content": "# New chapter\n"
  },
  {
    "path": "tests/gui/books/redirect/src/other-chapter.md",
    "content": "# Other chapter\n"
  },
  {
    "path": "tests/gui/books/search/README.md",
    "content": "# Search\n\nThis GUI test book is used for testing basic search interaction.\n"
  },
  {
    "path": "tests/gui/books/search/book.toml",
    "content": "[book]\ntitle = \"search\"\n"
  },
  {
    "path": "tests/gui/books/search/src/SUMMARY.md",
    "content": "# Summary\n\n- [Chapter 1](./chapter_1.md)\n- [Chapter 2](./inner/chapter_2.md)\n"
  },
  {
    "path": "tests/gui/books/search/src/chapter_1.md",
    "content": "# Chapter 1\n\nextraordinary refrigerator philosophical thunderstorm kaleidoscope\n\n## Repeat on same page\n\nkaleidoscope\n"
  },
  {
    "path": "tests/gui/books/search/src/inner/chapter_2.md",
    "content": "# Chapter 2\n\nchampionship mediterranean sophisticated tuberculosis photographer\n\n## Repeat from other chapter\n\nextraordinary\n"
  },
  {
    "path": "tests/gui/books/sidebar-scroll/book.toml",
    "content": "[book]\ntitle = \"sidebar-scroll\"\nlanguage = \"en\"\n"
  },
  {
    "path": "tests/gui/books/sidebar-scroll/src/SUMMARY.md",
    "content": "# Summary\n\n- [Chapter 1](./chapter_1.md)\n- [Chapter 2](./chapter_2.md)\n- [Chapter 3](./chapter_3.md)\n- [Chapter 4](./chapter_4.md)\n- [Chapter 5](./chapter_5.md)\n- [Chapter 6](./chapter_6.md)\n- [Chapter 7](./chapter_7.md)\n- [Chapter 8](./chapter_8.md)\n- [Chapter 9](./chapter_9.md)\n- [Chapter 10](./chapter_10.md)\n- [Chapter 11](./chapter_11.md)\n- [Chapter 12](./chapter_12.md)\n- [Chapter 13](./chapter_13.md)\n- [Chapter 14](./chapter_14.md)\n- [Chapter 15](./chapter_15.md)\n- [Chapter 16](./chapter_16.md)\n- [Chapter 17](./chapter_17.md)\n- [Chapter 18](./chapter_18.md)\n- [Chapter 19](./chapter_19.md)\n- [Chapter 20](./chapter_20.md)\n- [Chapter 21](./chapter_21.md)\n- [Chapter 22](./chapter_22.md)\n- [Chapter 23](./chapter_23.md)\n- [Chapter 24](./chapter_24.md)\n- [Chapter 25](./chapter_25.md)\n- [Chapter 26](./chapter_26.md)\n- [Chapter 27](./chapter_27.md)\n- [Chapter 28](./chapter_28.md)\n- [Chapter 29](./chapter_29.md)\n- [Chapter 30](./chapter_30.md)\n- [Chapter 31](./chapter_31.md)\n- [Chapter 32](./chapter_32.md)\n- [Chapter 33](./chapter_33.md)\n- [Chapter 34](./chapter_34.md)\n- [Chapter 35](./chapter_35.md)\n- [Chapter 36](./chapter_36.md)\n- [Chapter 37](./chapter_37.md)\n- [Chapter 38](./chapter_38.md)\n- [Chapter 39](./chapter_39.md)\n- [Chapter 40](./chapter_40.md)\n- [Chapter 41](./chapter_41.md)\n- [Chapter 42](./chapter_42.md)\n- [Chapter 43](./chapter_43.md)\n- [Chapter 44](./chapter_44.md)\n- [Chapter 45](./chapter_45.md)\n- [Chapter 46](./chapter_46.md)\n- [Chapter 47](./chapter_47.md)\n- [Chapter 48](./chapter_48.md)\n- [Chapter 49](./chapter_49.md)\n- [Chapter 50](./chapter_50.md)\n- [Chapter 51](./chapter_51.md)\n- [Chapter 52](./chapter_52.md)\n- [Chapter 53](./chapter_53.md)\n- [Chapter 54](./chapter_54.md)\n- [Chapter 55](./chapter_55.md)\n- [Chapter 56](./chapter_56.md)\n- [Chapter 57](./chapter_57.md)\n- [Chapter 58](./chapter_58.md)\n- [Chapter 59](./chapter_59.md)\n- [Chapter 60](./chapter_60.md)\n- [Chapter 61](./chapter_61.md)\n- [Chapter 62](./chapter_62.md)\n- [Chapter 63](./chapter_63.md)\n- [Chapter 64](./chapter_64.md)\n- [Chapter 65](./chapter_65.md)\n- [Chapter 66](./chapter_66.md)\n- [Chapter 67](./chapter_67.md)\n- [Chapter 68](./chapter_68.md)\n- [Chapter 69](./chapter_69.md)\n- [Chapter 70](./chapter_70.md)\n- [Chapter 71](./chapter_71.md)\n- [Chapter 72](./chapter_72.md)\n- [Chapter 73](./chapter_73.md)\n- [Chapter 74](./chapter_74.md)\n- [Chapter 75](./chapter_75.md)\n- [Chapter 76](./chapter_76.md)\n- [Chapter 77](./chapter_77.md)\n- [Chapter 78](./chapter_78.md)\n- [Chapter 79](./chapter_79.md)\n- [Chapter 80](./chapter_80.md)\n- [Chapter 81](./chapter_81.md)\n- [Chapter 82](./chapter_82.md)\n- [Chapter 83](./chapter_83.md)\n- [Chapter 84](./chapter_84.md)\n- [Chapter 85](./chapter_85.md)\n- [Chapter 86](./chapter_86.md)\n- [Chapter 87](./chapter_87.md)\n- [Chapter 88](./chapter_88.md)\n- [Chapter 89](./chapter_89.md)\n- [Chapter 90](./chapter_90.md)\n- [Chapter 91](./chapter_91.md)\n- [Chapter 92](./chapter_92.md)\n- [Chapter 93](./chapter_93.md)\n- [Chapter 94](./chapter_94.md)\n- [Chapter 95](./chapter_95.md)\n- [Chapter 96](./chapter_96.md)\n- [Chapter 97](./chapter_97.md)\n- [Chapter 98](./chapter_98.md)\n- [Chapter 99](./chapter_99.md)\n- [Chapter 100](./chapter_100.md)\n"
  },
  {
    "path": "tests/gui/books/sidebar-scroll/src/chapter_1.md",
    "content": "# Chapter 1\n\n## This has a single heading\n"
  },
  {
    "path": "tests/gui/books/sidebar-scroll/src/chapter_10.md",
    "content": "# Chapter 10\n\n## Heading A\n## Heading B\n## Heading C\n## Heading D\n## Heading E\n## Heading F\n## Heading G\n## Heading H\n## Heading I\n## Heading J\n## Heading K\n## Heading L\n## Heading M\n## Heading N\n## Heading O\n## Heading P\n## Heading Q\n## Heading R\n## Heading S\n## Heading T\n## Heading U\n## Heading V\n## Heading W\n## Heading X\n## Heading Y\n## Heading Z\n"
  },
  {
    "path": "tests/gui/books/sidebar-scroll/src/chapter_100.md",
    "content": "# Chapter 100\n"
  },
  {
    "path": "tests/gui/books/sidebar-scroll/src/chapter_11.md",
    "content": "# Chapter 11\n"
  },
  {
    "path": "tests/gui/books/sidebar-scroll/src/chapter_12.md",
    "content": "# Chapter 12\n"
  },
  {
    "path": "tests/gui/books/sidebar-scroll/src/chapter_13.md",
    "content": "# Chapter 13\n"
  },
  {
    "path": "tests/gui/books/sidebar-scroll/src/chapter_14.md",
    "content": "# Chapter 14\n"
  },
  {
    "path": "tests/gui/books/sidebar-scroll/src/chapter_15.md",
    "content": "# Chapter 15\n"
  },
  {
    "path": "tests/gui/books/sidebar-scroll/src/chapter_16.md",
    "content": "# Chapter 16\n"
  },
  {
    "path": "tests/gui/books/sidebar-scroll/src/chapter_17.md",
    "content": "# Chapter 17\n"
  },
  {
    "path": "tests/gui/books/sidebar-scroll/src/chapter_18.md",
    "content": "# Chapter 18\n"
  },
  {
    "path": "tests/gui/books/sidebar-scroll/src/chapter_19.md",
    "content": "# Chapter 19\n"
  },
  {
    "path": "tests/gui/books/sidebar-scroll/src/chapter_2.md",
    "content": "# Chapter 2\n"
  },
  {
    "path": "tests/gui/books/sidebar-scroll/src/chapter_20.md",
    "content": "# Chapter 20\n"
  },
  {
    "path": "tests/gui/books/sidebar-scroll/src/chapter_21.md",
    "content": "# Chapter 21\n"
  },
  {
    "path": "tests/gui/books/sidebar-scroll/src/chapter_22.md",
    "content": "# Chapter 22\n"
  },
  {
    "path": "tests/gui/books/sidebar-scroll/src/chapter_23.md",
    "content": "# Chapter 23\n"
  },
  {
    "path": "tests/gui/books/sidebar-scroll/src/chapter_24.md",
    "content": "# Chapter 24\n"
  },
  {
    "path": "tests/gui/books/sidebar-scroll/src/chapter_25.md",
    "content": "# Chapter 25\n"
  },
  {
    "path": "tests/gui/books/sidebar-scroll/src/chapter_26.md",
    "content": "# Chapter 26\n"
  },
  {
    "path": "tests/gui/books/sidebar-scroll/src/chapter_27.md",
    "content": "# Chapter 27\n"
  },
  {
    "path": "tests/gui/books/sidebar-scroll/src/chapter_28.md",
    "content": "# Chapter 28\n"
  },
  {
    "path": "tests/gui/books/sidebar-scroll/src/chapter_29.md",
    "content": "# Chapter 29\n"
  },
  {
    "path": "tests/gui/books/sidebar-scroll/src/chapter_3.md",
    "content": "# Chapter 3\n"
  },
  {
    "path": "tests/gui/books/sidebar-scroll/src/chapter_30.md",
    "content": "# Chapter 30\n"
  },
  {
    "path": "tests/gui/books/sidebar-scroll/src/chapter_31.md",
    "content": "# Chapter 31\n"
  },
  {
    "path": "tests/gui/books/sidebar-scroll/src/chapter_32.md",
    "content": "# Chapter 32\n"
  },
  {
    "path": "tests/gui/books/sidebar-scroll/src/chapter_33.md",
    "content": "# Chapter 33\n"
  },
  {
    "path": "tests/gui/books/sidebar-scroll/src/chapter_34.md",
    "content": "# Chapter 34\n"
  },
  {
    "path": "tests/gui/books/sidebar-scroll/src/chapter_35.md",
    "content": "# Chapter 35\n\n## Heading A\n## Heading B\n## Heading C\n## Heading D\n## Heading E\n## Heading F\n## Heading G\n## Heading H\n## Heading I\n## Heading J\n## Heading K\n## Heading L\n## Heading M\n## Heading N\n## Heading O\n## Heading P\n## Heading Q\n## Heading R\n## Heading S\n## Heading T\n## Heading U\n## Heading V\n## Heading W\n## Heading X\n## Heading Y\n## Heading Z\n"
  },
  {
    "path": "tests/gui/books/sidebar-scroll/src/chapter_36.md",
    "content": "# Chapter 36\n"
  },
  {
    "path": "tests/gui/books/sidebar-scroll/src/chapter_37.md",
    "content": "# Chapter 37\n"
  },
  {
    "path": "tests/gui/books/sidebar-scroll/src/chapter_38.md",
    "content": "# Chapter 38\n"
  },
  {
    "path": "tests/gui/books/sidebar-scroll/src/chapter_39.md",
    "content": "# Chapter 39\n"
  },
  {
    "path": "tests/gui/books/sidebar-scroll/src/chapter_4.md",
    "content": "# Chapter 4\n"
  },
  {
    "path": "tests/gui/books/sidebar-scroll/src/chapter_40.md",
    "content": "# Chapter 40\n"
  },
  {
    "path": "tests/gui/books/sidebar-scroll/src/chapter_41.md",
    "content": "# Chapter 41\n"
  },
  {
    "path": "tests/gui/books/sidebar-scroll/src/chapter_42.md",
    "content": "# Chapter 42\n"
  },
  {
    "path": "tests/gui/books/sidebar-scroll/src/chapter_43.md",
    "content": "# Chapter 43\n"
  },
  {
    "path": "tests/gui/books/sidebar-scroll/src/chapter_44.md",
    "content": "# Chapter 44\n"
  },
  {
    "path": "tests/gui/books/sidebar-scroll/src/chapter_45.md",
    "content": "# Chapter 45\n"
  },
  {
    "path": "tests/gui/books/sidebar-scroll/src/chapter_46.md",
    "content": "# Chapter 46\n"
  },
  {
    "path": "tests/gui/books/sidebar-scroll/src/chapter_47.md",
    "content": "# Chapter 47\n"
  },
  {
    "path": "tests/gui/books/sidebar-scroll/src/chapter_48.md",
    "content": "# Chapter 48\n"
  },
  {
    "path": "tests/gui/books/sidebar-scroll/src/chapter_49.md",
    "content": "# Chapter 49\n"
  },
  {
    "path": "tests/gui/books/sidebar-scroll/src/chapter_5.md",
    "content": "# Chapter 5\n"
  },
  {
    "path": "tests/gui/books/sidebar-scroll/src/chapter_50.md",
    "content": "# Chapter 50\n"
  },
  {
    "path": "tests/gui/books/sidebar-scroll/src/chapter_51.md",
    "content": "# Chapter 51\n"
  },
  {
    "path": "tests/gui/books/sidebar-scroll/src/chapter_52.md",
    "content": "# Chapter 52\n"
  },
  {
    "path": "tests/gui/books/sidebar-scroll/src/chapter_53.md",
    "content": "# Chapter 53\n"
  },
  {
    "path": "tests/gui/books/sidebar-scroll/src/chapter_54.md",
    "content": "# Chapter 54\n"
  },
  {
    "path": "tests/gui/books/sidebar-scroll/src/chapter_55.md",
    "content": "# Chapter 55\n"
  },
  {
    "path": "tests/gui/books/sidebar-scroll/src/chapter_56.md",
    "content": "# Chapter 56\n"
  },
  {
    "path": "tests/gui/books/sidebar-scroll/src/chapter_57.md",
    "content": "# Chapter 57\n"
  },
  {
    "path": "tests/gui/books/sidebar-scroll/src/chapter_58.md",
    "content": "# Chapter 58\n"
  },
  {
    "path": "tests/gui/books/sidebar-scroll/src/chapter_59.md",
    "content": "# Chapter 59\n"
  },
  {
    "path": "tests/gui/books/sidebar-scroll/src/chapter_6.md",
    "content": "# Chapter 6\n"
  },
  {
    "path": "tests/gui/books/sidebar-scroll/src/chapter_60.md",
    "content": "# Chapter 60\n"
  },
  {
    "path": "tests/gui/books/sidebar-scroll/src/chapter_61.md",
    "content": "# Chapter 61\n"
  },
  {
    "path": "tests/gui/books/sidebar-scroll/src/chapter_62.md",
    "content": "# Chapter 62\n"
  },
  {
    "path": "tests/gui/books/sidebar-scroll/src/chapter_63.md",
    "content": "# Chapter 63\n"
  },
  {
    "path": "tests/gui/books/sidebar-scroll/src/chapter_64.md",
    "content": "# Chapter 64\n"
  },
  {
    "path": "tests/gui/books/sidebar-scroll/src/chapter_65.md",
    "content": "# Chapter 65\n"
  },
  {
    "path": "tests/gui/books/sidebar-scroll/src/chapter_66.md",
    "content": "# Chapter 66\n"
  },
  {
    "path": "tests/gui/books/sidebar-scroll/src/chapter_67.md",
    "content": "# Chapter 67\n"
  },
  {
    "path": "tests/gui/books/sidebar-scroll/src/chapter_68.md",
    "content": "# Chapter 68\n"
  },
  {
    "path": "tests/gui/books/sidebar-scroll/src/chapter_69.md",
    "content": "# Chapter 69\n"
  },
  {
    "path": "tests/gui/books/sidebar-scroll/src/chapter_7.md",
    "content": "# Chapter 7\n"
  },
  {
    "path": "tests/gui/books/sidebar-scroll/src/chapter_70.md",
    "content": "# Chapter 70\n"
  },
  {
    "path": "tests/gui/books/sidebar-scroll/src/chapter_71.md",
    "content": "# Chapter 71\n"
  },
  {
    "path": "tests/gui/books/sidebar-scroll/src/chapter_72.md",
    "content": "# Chapter 72\n"
  },
  {
    "path": "tests/gui/books/sidebar-scroll/src/chapter_73.md",
    "content": "# Chapter 73\n"
  },
  {
    "path": "tests/gui/books/sidebar-scroll/src/chapter_74.md",
    "content": "# Chapter 74\n"
  },
  {
    "path": "tests/gui/books/sidebar-scroll/src/chapter_75.md",
    "content": "# Chapter 75\n"
  },
  {
    "path": "tests/gui/books/sidebar-scroll/src/chapter_76.md",
    "content": "# Chapter 76\n"
  },
  {
    "path": "tests/gui/books/sidebar-scroll/src/chapter_77.md",
    "content": "# Chapter 77\n"
  },
  {
    "path": "tests/gui/books/sidebar-scroll/src/chapter_78.md",
    "content": "# Chapter 78\n"
  },
  {
    "path": "tests/gui/books/sidebar-scroll/src/chapter_79.md",
    "content": "# Chapter 79\n"
  },
  {
    "path": "tests/gui/books/sidebar-scroll/src/chapter_8.md",
    "content": "# Chapter 8\n"
  },
  {
    "path": "tests/gui/books/sidebar-scroll/src/chapter_80.md",
    "content": "# Chapter 80\n"
  },
  {
    "path": "tests/gui/books/sidebar-scroll/src/chapter_81.md",
    "content": "# Chapter 81\n"
  },
  {
    "path": "tests/gui/books/sidebar-scroll/src/chapter_82.md",
    "content": "# Chapter 82\n"
  },
  {
    "path": "tests/gui/books/sidebar-scroll/src/chapter_83.md",
    "content": "# Chapter 83\n"
  },
  {
    "path": "tests/gui/books/sidebar-scroll/src/chapter_84.md",
    "content": "# Chapter 84\n"
  },
  {
    "path": "tests/gui/books/sidebar-scroll/src/chapter_85.md",
    "content": "# Chapter 85\n"
  },
  {
    "path": "tests/gui/books/sidebar-scroll/src/chapter_86.md",
    "content": "# Chapter 86\n"
  },
  {
    "path": "tests/gui/books/sidebar-scroll/src/chapter_87.md",
    "content": "# Chapter 87\n"
  },
  {
    "path": "tests/gui/books/sidebar-scroll/src/chapter_88.md",
    "content": "# Chapter 88\n"
  },
  {
    "path": "tests/gui/books/sidebar-scroll/src/chapter_89.md",
    "content": "# Chapter 89\n"
  },
  {
    "path": "tests/gui/books/sidebar-scroll/src/chapter_9.md",
    "content": "# Chapter 9\n"
  },
  {
    "path": "tests/gui/books/sidebar-scroll/src/chapter_90.md",
    "content": "# Chapter 90\n"
  },
  {
    "path": "tests/gui/books/sidebar-scroll/src/chapter_91.md",
    "content": "# Chapter 91\n"
  },
  {
    "path": "tests/gui/books/sidebar-scroll/src/chapter_92.md",
    "content": "# Chapter 92\n"
  },
  {
    "path": "tests/gui/books/sidebar-scroll/src/chapter_93.md",
    "content": "# Chapter 93\n"
  },
  {
    "path": "tests/gui/books/sidebar-scroll/src/chapter_94.md",
    "content": "# Chapter 94\n"
  },
  {
    "path": "tests/gui/books/sidebar-scroll/src/chapter_95.md",
    "content": "# Chapter 95\n"
  },
  {
    "path": "tests/gui/books/sidebar-scroll/src/chapter_96.md",
    "content": "# Chapter 96\n"
  },
  {
    "path": "tests/gui/books/sidebar-scroll/src/chapter_97.md",
    "content": "# Chapter 97\n"
  },
  {
    "path": "tests/gui/books/sidebar-scroll/src/chapter_98.md",
    "content": "# Chapter 98\n"
  },
  {
    "path": "tests/gui/books/sidebar-scroll/src/chapter_99.md",
    "content": "# Chapter 99\n"
  },
  {
    "path": "tests/gui/heading-nav-collapsed.goml",
    "content": "// Tests for collapsed heading sidebar navigation.\n\nset-window-size: (1400, 800)\ngo-to: |DOC_PATH| + \"heading-nav/collapsed.html\"\n\nassert-count: (\".header-item\", 11)\nassert-count: (\".current-header\", 1)\nassert-text: (\".current-header\", \"Heading 1\")\n// Collapsed elements do not have \"expanded\" class.\nassert-attribute: (\"li:has(> span > a[href='#heading-12'])\", {\"class\": \"header-item\"})\nassert-attribute: (\"li:has(> span > a[href='#heading-21'])\", {\"class\": \"header-item\"})\n\nassert-property: (\"div.on-this-page\", {\"innerHTML\": '<ol class=\"section\"><li class=\"header-item expanded\"><span class=\"chapter-link-wrapper\"><a href=\"#heading-1\" class=\"header-in-summary current-header\">Heading 1</a></span><ol class=\"section\"><li class=\"header-item expanded\"><span class=\"chapter-link-wrapper\"><a href=\"#heading-11\" class=\"header-in-summary\">Heading 1.1</a></span></li><li class=\"header-item\"><span class=\"chapter-link-wrapper\"><a href=\"#heading-12\" class=\"header-in-summary\">Heading 1.2</a><a class=\"chapter-fold-toggle header-toggle\"><div>❱</div></a></span><ol class=\"section\"><li class=\"header-item expanded\"><span class=\"chapter-link-wrapper\"><a href=\"#heading-121\" class=\"header-in-summary\">Heading 1.2.1</a></span></li><li class=\"header-item expanded\"><span class=\"chapter-link-wrapper\"><a href=\"#heading-122\" class=\"header-in-summary\">Heading 1.2.2</a></span></li></ol></li><li class=\"header-item expanded\"><span class=\"chapter-link-wrapper\"><a href=\"#heading-13\" class=\"header-in-summary\">Heading 1.3</a></span></li></ol></li><li class=\"header-item expanded\"><span class=\"chapter-link-wrapper\"><a href=\"#heading-2\" class=\"header-in-summary\">Heading 2</a></span><ol class=\"section\"><li class=\"header-item\"><span class=\"chapter-link-wrapper\"><a href=\"#heading-21\" class=\"header-in-summary\">Heading 2.1</a><a class=\"chapter-fold-toggle header-toggle\"><div>❱</div></a></span><ol class=\"section\"><li class=\"header-item\"><span class=\"chapter-link-wrapper\"><a href=\"#heading-211\" class=\"header-in-summary\">Heading 2.1.1</a><a class=\"chapter-fold-toggle header-toggle\"><div>❱</div></a></span><ol class=\"section\"><li class=\"header-item\"><span class=\"chapter-link-wrapper\"><a href=\"#heading-2111\" class=\"header-in-summary\">Heading 2.1.1.1</a><a class=\"chapter-fold-toggle header-toggle\"><div>❱</div></a></span><ol class=\"section\"><li class=\"header-item expanded\"><span class=\"chapter-link-wrapper\"><a href=\"#heading-21111\" class=\"header-in-summary\">Heading 2.1.1.1.1</a></span></li></ol></li></ol></li></ol></li></ol></li></ol>'})\n\n// Click 1.2, expands it.\nclick: \"a.header-in-summary[href='#heading-12']\"\nassert-attribute: (\"li:has(> span > a[href='#heading-12'])\", {\"class\": \"header-item expanded\"})\nassert-attribute: (\"li:has(> span > a[href='#heading-21'])\", {\"class\": \"header-item\"})\nassert-css: (\"//a[@href='#heading-12']/../following-sibling::ol\", {\"display\": \"block\"})\n\n// Click 1.1, should collapse it.\nclick: \"a.header-in-summary[href='#heading-11']\"\nassert-attribute: (\"li:has(> span > a[href='#heading-12'])\", {\"class\": \"header-item\"})\nassert-attribute: (\"li:has(> span > a[href='#heading-21'])\", {\"class\": \"header-item\"})\nassert-css: (\"//a[@href='#heading-12']/../following-sibling::ol\", {\"display\": \"none\"})\n\n// Click the chevron, should expand it.\nclick: \"a.header-in-summary[href='#heading-12'] ~ a.header-toggle\"\nassert-attribute: (\"li:has(> span > a[href='#heading-12'])\", {\"class\": \"header-item expanded\"})\nassert-attribute: (\"li:has(> span > a[href='#heading-21'])\", {\"class\": \"header-item\"})\nassert-css: (\"//a[@href='#heading-12']/../following-sibling::ol\", {\"display\": \"block\"})\n\n// Click 1.3\nclick: \"a.header-in-summary[href='#heading-13']\"\n// Everything should be collapsed\nassert-attribute: (\"li:has(> span > a[href='#heading-12'])\", {\"class\": \"header-item\"})\nassert-attribute: (\"li:has(> span > a[href='#heading-21'])\", {\"class\": \"header-item\"})\nassert-css: (\"//a[@href='#heading-12']/../following-sibling::ol\", {\"display\": \"none\"})\nassert-css: (\"//a[@href='#heading-21']/../following-sibling::ol\", {\"display\": \"none\"})\nassert-attribute: (\"li:has(> span > a[href='#heading-12'])\", {\"class\": \"header-item\"})\nassert-attribute: (\"li:has(> span > a[href='#heading-21'])\", {\"class\": \"header-item\"})\nassert-attribute: (\"li:has(> span > a[href='#heading-211'])\", {\"class\": \"header-item\"})\nassert-attribute: (\"li:has(> span > a[href='#heading-2111'])\", {\"class\": \"header-item\"})\n\n// Scroll to bottom of page\npress-key: 'PageDown'\npress-key: 'PageDown'\npress-key: 'PageDown'\npress-key: 'PageDown'\n// 2.1.1.1.1 should be visible, and all the chevrons should be open, and expanded should be on each one\nassert-attribute: (\"li:has(> span > a[href='#heading-12'])\", {\"class\": \"header-item\"})\nassert-attribute: (\"li:has(> span > a[href='#heading-21'])\", {\"class\": \"header-item expanded\"})\nassert-attribute: (\"li:has(> span > a[href='#heading-211'])\", {\"class\": \"header-item expanded\"})\nassert-attribute: (\"li:has(> span > a[href='#heading-2111'])\", {\"class\": \"header-item expanded\"})\nassert-css: (\"//a[@href='#heading-12']/../following-sibling::ol\", {\"display\": \"none\"})\nassert-css: (\"//a[@href='#heading-21']/../following-sibling::ol\", {\"display\": \"block\"})\nassert-css: (\"//a[@href='#heading-211']/../following-sibling::ol\", {\"display\": \"block\"})\nassert-css: (\"//a[@href='#heading-2111']/../following-sibling::ol\", {\"display\": \"block\"})\n"
  },
  {
    "path": "tests/gui/heading-nav-current-to-bottom.goml",
    "content": "// Checks that the \"current\" header works even when there are headers near the\n// bottom.\n\nset-window-size: (1400, 800)\ngo-to: |DOC_PATH| + \"heading-nav/current-to-bottom.html\"\nassert-count: (\".current-header\", 1)\nassert-text: (\".current-header\", \"First header\")\n\nscroll-to: \"#scroll-to-1\"\nassert-text: (\".current-header\", \"First header\")\nscroll-to: \"#scroll-to-2\"\nassert-text: (\".current-header\", \"First header\")\nscroll-to: \"#scroll-to-3\"\nassert-text: (\".current-header\", \"First header\")\nscroll-to: \"#scroll-to-4\"\nassert-text: (\".current-header\", \"First header\")\nscroll-to: \"#scroll-to-5\"\nassert-text: (\".current-header\", \"First header\")\nscroll-to: \"#scroll-to-6\"\nassert-text: (\".current-header\", \"First header\")\nscroll-to: \"#scroll-to-7\"\nassert-text: (\".current-header\", \"First header\")\nscroll-to: \"#scroll-to-8\"\nassert-text: (\".current-header\", \"First header\")\nscroll-to: \"#scroll-to-9\"\nassert-text: (\".current-header\", \"First header\")\nscroll-to: \"#scroll-to-10\"\nassert-text: (\".current-header\", \"First header\")\nscroll-to: \"#scroll-to-11\"\nassert-text: (\".current-header\", \"First header\")\nscroll-to: \"#scroll-to-12\"\nassert-text: (\".current-header\", \"First header\")\nscroll-to: \"#scroll-to-13\"\nassert-text: (\".current-header\", \"First header\")\nscroll-to: \"#scroll-to-14\"\nassert-text: (\".current-header\", \"First header\")\nscroll-to: \"#scroll-to-15\"\nassert-text: (\".current-header\", \"First header\")\nscroll-to: \"#scroll-to-16\"\nassert-text: (\".current-header\", \"First header\")\nscroll-to: \"#scroll-to-17\"\nassert-text: (\".current-header\", \"First header\")\nscroll-to: \"#scroll-to-18\"\nassert-text: (\".current-header\", \"First header\")\nscroll-to: \"#scroll-to-19\"\nassert-text: (\".current-header\", \"First header\")\nscroll-to: \"#scroll-to-20\"\nassert-text: (\".current-header\", \"First header\")\nscroll-to: \"#scroll-to-21\"\nwait-for-text: (\".current-header\", \"Second sub-header\")\nscroll-to: \"#scroll-to-22\"\nassert-text: (\".current-header\", \"Second sub-header\")\nscroll-to: \"#scroll-to-23\"\nassert-text: (\".current-header\", \"Second sub-header\")\nscroll-to: \"#scroll-to-24\"\nassert-text: (\".current-header\", \"Second sub-header\")\nscroll-to: \"#scroll-to-25\"\nwait-for-text: (\".current-header\", \"Fifth header\")\n"
  },
  {
    "path": "tests/gui/heading-nav-empty.goml",
    "content": "// When there aren't any headings, there shouldn't be any header items in the sidebar.\n\nset-window-size: (1400, 800)\ngo-to: |DOC_PATH| + \"heading-nav/empty.html\"\nassert-count: (\".header-item\", 0)\nassert-count: (\".current-header\", 0)\n"
  },
  {
    "path": "tests/gui/heading-nav-filter.goml",
    "content": "// Tests for collapsed heading sidebar navigation.\n\nset-window-size: (1400, 800)\ngo-to: |DOC_PATH| + \"heading-nav/filtered-headings.html?highlight=skateboard#skateboard\"\n\nassert-property: (\"//h2[@id='skateboard']\", {\"innerHTML\": '<a class=\"header\" href=\"#skateboard\"><mark data-markjs=\"true\">Skateboard</mark></a>'})\n\nassert-property: (\"//a[contains(@class, 'header-in-summary') and @href='#skateboard']\", {\"innerHTML\": 'Skateboard'})\n"
  },
  {
    "path": "tests/gui/heading-nav-folded.goml",
    "content": "// Tests when chapter folding is enabled.\n\ngo-to: |DOC_PATH| + \"heading-nav-folded/index.html\"\n"
  },
  {
    "path": "tests/gui/heading-nav-large-intro.goml",
    "content": "// When there is a large intro, there shouldn't be any \"current\" headers until\n// you scroll down and make it visible on screen.\n\nset-window-size: (1400, 800)\ngo-to: |DOC_PATH| + \"heading-nav/large-intro.html\"\nassert-count: (\".header-item\", 1)\nassert-count: (\".current-header\", 0)\n\nscroll-to: \"#first-header\"\nwait-for-count: (\".current-header\", 1)\nassert-text: (\".current-header\", \"First header\")\n\n// Scrolling back to the top should set it to 0.\nscroll-to: (0, 0)\nwait-for-count: (\".current-header\", 0)\n"
  },
  {
    "path": "tests/gui/heading-nav-markup.goml",
    "content": "// When a header has various markup, the sidebar should replicate it.\n\nset-window-size: (1400, 800)\ngo-to: |DOC_PATH| + \"heading-nav/markup.html\"\n\nassert-count: (\".header-item\", 4)\nassert-count: (\".current-header\", 1)\nassert-text: (\".current-header\", \"Heading with code or italic or bold or strike\")\nassert-property: (\".current-header\", {\"innerHTML\": \"Heading with <code>code</code> or <em>italic</em> or <strong>bold</strong> or <del>strike</del>\"})\n\n// Clicking the custom one should work and should make it current.\nclick: \"a.header-in-summary[href='#custom-id']\"\nassert-count: (\".current-header\", 1)\nassert-text: (\".current-header\", \"Heading with a custom id\")\n\n// Click the one with HTML, and check it.\nclick: \"a.header-in-summary[href='#heading-with-html']\"\nassert-count: (\".current-header\", 1)\nassert-text: (\".current-header\", \"Heading with html\")\n"
  },
  {
    "path": "tests/gui/heading-nav-normal-intro.goml",
    "content": "// When there is a normal-sized intro, when the page loads the first heading\n// should be \"current\".\n\nset-window-size: (1400, 800)\ngo-to: |DOC_PATH| + \"heading-nav/normal-intro.html\"\nassert-count: (\".header-item\", 3)\nassert-count: (\".current-header\", 1)\nassert-text: (\".current-header\", \"The first heading\")\n\nclick: \"a[href='#and-a-sub-heading']\"\nwait-for-text: (\".current-header\", \"And a sub heading\")\n"
  },
  {
    "path": "tests/gui/heading-nav-unusual-levels.goml",
    "content": "// Tests for unusual heading levels\n\nset-window-size: (1400, 800)\ngo-to: |DOC_PATH| + \"heading-nav/unusual-heading-levels.html\"\n\nassert-property: (\"//a[@href='unusual-heading-levels.html']/../following-sibling::div\", {\"innerHTML\": '<ol class=\"section\"><ol class=\"section\"><li class=\"header-item expanded\"><span class=\"chapter-link-wrapper\"><a href=\"#heading-3\" class=\"header-in-summary current-header\">Heading 3</a></span></li></ol><li class=\"header-item expanded\"><span class=\"chapter-link-wrapper\"><a href=\"#heading-2\" class=\"header-in-summary\">Heading 2</a></span><ol class=\"section\"><ol class=\"section\"><li class=\"header-item expanded\"><span class=\"chapter-link-wrapper\"><a href=\"#heading-5\" class=\"header-in-summary\">Heading 5</a></span></li><li class=\"header-item expanded\"><span class=\"chapter-link-wrapper\"><a href=\"#heading-51\" class=\"header-in-summary\">Heading 5.1</a></span></li></ol></ol></li></ol>'})\n"
  },
  {
    "path": "tests/gui/help.goml",
    "content": "// This GUI test checks help popup.\n\ngo-to: |DOC_PATH| + \"basic/index.html\"\nassert-css: (\"#mdbook-help-container\", {\"display\": \"none\"})\npress-key: '?'\nwait-for-css: (\"#mdbook-help-container\", {\"display\": \"flex\"})\npress-key: 'Escape'\nwait-for-css: (\"#mdbook-help-container\", {\"display\": \"none\"})\npress-key: '?'\nwait-for-css: (\"#mdbook-help-container\", {\"display\": \"flex\"})\n// Click inside does nothing.\nclick: \"#mdbook-help-popup\"\nwait-for-css: (\"#mdbook-help-container\", {\"display\": \"flex\"})\n// Click outside dismisses.\nclick: \"*\"\nwait-for-css: (\"#mdbook-help-container\", {\"display\": \"none\"})\n"
  },
  {
    "path": "tests/gui/highlighting.goml",
    "content": "// This tests syntax highlighting.\n\ngo-to: |DOC_PATH| + \"highlighting/index.html\"\n\nassert: \"#apache ~ pre > code > span.hljs-comment\"\nassert: \"#armasm ~ pre > code > span.hljs-symbol\"\nassert: \"#bash ~ pre > code > span.hljs-meta\"\nassert: \"#c ~ pre > code > span.hljs-meta\"\nassert: \"#coffeescript ~ pre > code > span.hljs-title\"\nassert: \"#cpp ~ pre > code > span.hljs-meta\"\nassert: \"#csharp ~ pre > code > span.hljs-keyword\"\nassert: \"#css ~ pre > code > span.hljs-keyword\"\nassert: \"#d ~ pre > code > span.hljs-comment\"\nassert: \"#diff ~ pre > code > span.hljs-comment\"\nassert: \"#go ~ pre > code > span.hljs-keyword\"\n// Not clear why this doesn't have the hljs- prefix.\nassert: \"#handlebars ~ pre > code > span.xml\"\nassert: \"#haskell ~ pre > code > span.hljs-title\"\nassert: \"#http ~ pre > code > span.hljs-keyword\"\nassert: \"#ini ~ pre > code > span.hljs-comment\"\nassert: \"#java ~ pre > code > span.hljs-class\"\nassert: \"#javascript ~ pre > code > span.hljs-function\"\nassert: \"#json ~ pre > code > span.hljs-attr\"\nassert: \"#julia ~ pre > code > span.hljs-comment\"\nassert: \"#kotlin ~ pre > code > span.hljs-keyword\"\nassert: \"#less ~ pre > code > span.hljs-keyword\"\nassert: \"#lua ~ pre > code > span.hljs-comment\"\nassert: \"#makefile ~ pre > code > span.hljs-comment\"\nassert: \"#markdown ~ pre > code > span.hljs-section\"\nassert: \"#nginx ~ pre > code > span.hljs-attribute\"\nassert: \"#nim ~ pre > code > span.hljs-keyword\"\nassert: \"#objectivec ~ pre > code > span.hljs-meta\"\nassert: \"#nix ~ pre > code > span.hljs-keyword\"\nassert: \"#perl ~ pre > code > span.hljs-keyword\"\nassert: \"#php ~ pre > code > span.hljs-meta\"\nassert: \"#properties ~ pre > code > span.hljs-comment\"\nassert: \"#python ~ pre > code > span.hljs-meta\"\nassert: \"#r ~ pre > code > span.hljs-keyword\"\nassert: \"#ruby ~ pre > code > span.hljs-comment\"\nassert: \"#rust ~ pre > code > span.hljs-function\"\nassert: \"#scala ~ pre > code > span.hljs-comment\"\nassert: \"#scss ~ pre > code > span.hljs-comment\"\nassert: \"#shell ~ pre > code > span.hljs-meta\"\nassert: \"#sql ~ pre > code > span.hljs-keyword\"\nassert: \"#swift ~ pre > code > span.hljs-keyword\"\nassert: \"#typescript ~ pre > code > span.hljs-keyword\"\nassert: \"#x86asm ~ pre > code > span.hljs-meta\"\nassert: \"#xml ~ pre > code > span.hljs-meta\"\nassert: \"#yaml ~ pre > code > span.hljs-meta\"\n"
  },
  {
    "path": "tests/gui/move-between-pages.goml",
    "content": "// This tests pressing the left and right arrows moving to previous and next page.\n\ngo-to: |DOC_PATH| + \"all-summary/index.html\"\n\n// default page is the first chapter\nassert-text: (\"title\", \"Prefix 1 - all-summary\")\n\n// Trying to move to the left beyond the prefix pages - nothing changes\npress-key: 'ArrowLeft'\nassert-text: (\"title\", \"Prefix 1 - all-summary\")\n\n// Move left\ngo-to: |DOC_PATH| + \"all-summary/intro.html\"\nassert-text: (\"title\", \"Introduction - all-summary\")\n\npress-key: 'ArrowLeft'\nassert-text: (\"title\", \"Prefix 2 - all-summary\")\n\npress-key: 'ArrowLeft'\nassert-text: (\"title\", \"Prefix 1 - all-summary\")\n\n// Move right\npress-key: 'ArrowRight'\nassert-text: (\"title\", \"Prefix 2 - all-summary\")\n\npress-key: 'ArrowRight'\nassert-text: (\"title\", \"Introduction - all-summary\")\n\npress-key: 'ArrowRight'\nassert-text: (\"title\", \"P1 C1 - all-summary\")\n\npress-key: 'ArrowRight'\nassert-text: (\"title\", \"P2 C1 - all-summary\")\n\npress-key: 'ArrowRight'\nassert-text: (\"title\", \"Suffix 1 - all-summary\")\n\npress-key: 'ArrowRight'\nassert-text: (\"title\", \"Suffix 2 - all-summary\")\n\n// Try to go beyond the last page\npress-key: 'ArrowRight'\nassert-text: (\"title\", \"Suffix 2 - all-summary\")\n"
  },
  {
    "path": "tests/gui/redirect.goml",
    "content": "go-to: |DOC_PATH| + \"redirect/inner/old.html\"\nassert-window-property: ({\"location\": |DOC_PATH| + \"redirect/new-chapter.html\"})\n\n// Check that it preserves fragments when redirecting.\ngo-to: |DOC_PATH| + \"redirect/inner/old.html#fragment\"\nassert-window-property: ({\"location\": |DOC_PATH| + \"redirect/new-chapter.html#fragment\"})\n\n// The fragment one here isn't necessary, but should still work.\ngo-to: |DOC_PATH| + \"redirect/pointless-fragment.html\"\nassert-window-property: ({\"location\": |DOC_PATH| + \"redirect/new-chapter.html\"})\ngo-to: |DOC_PATH| + \"redirect/pointless-fragment.html#foo\"\nassert-window-property: ({\"location\": |DOC_PATH| + \"redirect/new-chapter.html#foo\"})\n\n// Page rename, and a fragment rename.\ngo-to: |DOC_PATH| + \"redirect/rename-page-and-fragment.html\"\nassert-window-property: ({\"location\": |DOC_PATH| + \"redirect/new-chapter.html\"})\ngo-to: |DOC_PATH| + \"redirect/rename-page-and-fragment.html#orig\"\nassert-window-property: ({\"location\": |DOC_PATH| + \"redirect/new-chapter.html#new\"})\n\n// Page rename, and the fragment goes to a *different* page from the default.\ngo-to: |DOC_PATH| + \"redirect/rename-page-fragment-elsewhere.html\"\nassert-window-property: ({\"location\": |DOC_PATH| + \"redirect/new-chapter.html\"})\ngo-to: |DOC_PATH| + \"redirect/rename-page-fragment-elsewhere.html#orig\"\nassert-window-property: ({\"location\": |DOC_PATH| + \"redirect/other-chapter.html#new\"})\n\n// Rename fragment on an existing page.\ngo-to: |DOC_PATH| + \"redirect/new-chapter.html#orig\"\nassert-window-property: ({\"location\": |DOC_PATH| + \"redirect/new-chapter.html#new\"})\n\n// Other fragments aren't affected.\ngo-to: |DOC_PATH| + \"redirect/index.html\" // Reset page since redirects are processed on load.\ngo-to: |DOC_PATH| + \"redirect/new-chapter.html\"\nassert-window-property: ({\"location\": |DOC_PATH| + \"redirect/new-chapter.html\"})\ngo-to: |DOC_PATH| + \"redirect/index.html\" // Reset page since redirects are processed on load.\ngo-to: |DOC_PATH| + \"redirect/new-chapter.html#dont-change\"\nassert-window-property: ({\"location\": |DOC_PATH| + \"redirect/new-chapter.html#dont-change\"})\n\n// Rename fragment on an existing page to another page.\ngo-to: |DOC_PATH| + \"redirect/index.html\" // Reset page since redirects are processed on load.\ngo-to: |DOC_PATH| + \"redirect/new-chapter.html#orig-new-chapter\"\nassert-window-property: ({\"location\": |DOC_PATH| + \"redirect/other-chapter.html#new\"})\n"
  },
  {
    "path": "tests/gui/runner.rs",
    "content": "//! The GUI test runner.\n//!\n//! This uses the browser-ui-test npm package to use a headless Chrome to\n//! exercise the behavior of rendered books. See `CONTRIBUTING.md` for more\n//! information.\n\nuse serde_json::Value;\nuse std::fs::read_to_string;\nuse std::path::Path;\nuse std::process::{Command, Output};\n\nfn get_available_browser_ui_test_version_inner(global: bool) -> Option<String> {\n    let mut command = Command::new(\"npm\");\n    command\n        .arg(\"list\")\n        .arg(\"--parseable\")\n        .arg(\"--long\")\n        .arg(\"--depth=0\");\n    if global {\n        command.arg(\"--global\");\n    }\n    let stdout = command.output().expect(\"`npm` command not found\").stdout;\n    let lines = String::from_utf8_lossy(&stdout);\n    lines\n        .lines()\n        .find_map(|l| l.split(':').nth(1)?.strip_prefix(\"browser-ui-test@\"))\n        .map(std::borrow::ToOwned::to_owned)\n}\n\nfn get_available_browser_ui_test_version() -> Option<String> {\n    get_available_browser_ui_test_version_inner(false)\n        .or_else(|| get_available_browser_ui_test_version_inner(true))\n}\n\nfn expected_browser_ui_test_version() -> String {\n    let content = read_to_string(\"package.json\").expect(\"failed to read `package.json`\");\n    let v: Value = serde_json::from_str(&content).expect(\"failed to parse `package.json`\");\n    let Some(dependencies) = v.get(\"dependencies\") else {\n        panic!(\"Missing `dependencies` key in `package.json`\");\n    };\n    let Some(browser_ui_test) = dependencies.get(\"browser-ui-test\") else {\n        panic!(\"Missing `browser-ui-test` key in \\\"dependencies\\\" object in `package.json`\");\n    };\n    let Value::String(version) = browser_ui_test else {\n        panic!(\"`browser-ui-test` version is not a string\");\n    };\n    version.trim().to_string()\n}\n\nfn main() {\n    let browser_ui_test_version = expected_browser_ui_test_version();\n    match get_available_browser_ui_test_version() {\n        Some(version) => {\n            if version != browser_ui_test_version {\n                eprintln!(\n                    \"⚠️ Installed version of browser-ui-test (`{version}`) is different than the \\\n                     one used in the CI (`{browser_ui_test_version}`) You can install this version \\\n                     using `npm update browser-ui-test` or by using `npm install browser-ui-test\\\n                     @{browser_ui_test_version}`\",\n                );\n            }\n        }\n        None => {\n            panic!(\n                \"`browser-ui-test` is not installed. You can install this package using `npm \\\n                 update browser-ui-test` or by using `npm install browser-ui-test\\\n                 @{browser_ui_test_version}`\",\n            );\n        }\n    }\n\n    let out_dir = Path::new(env!(\"CARGO_TARGET_TMPDIR\")).join(\"gui\");\n    build_books(&out_dir);\n    run_browser_ui_test(&out_dir);\n}\n\nfn build_books(out_dir: &Path) {\n    let root = Path::new(env!(\"CARGO_MANIFEST_DIR\"));\n    let books_dir = root.join(\"tests/gui/books\");\n    for entry in books_dir.read_dir().unwrap() {\n        let entry = entry.unwrap();\n        let path = entry.path();\n        if !path.is_dir() {\n            continue;\n        }\n        println!(\"Building `{}`\", path.display());\n        let mut cmd = Command::new(env!(\"CARGO_BIN_EXE_mdbook\"));\n        let output = cmd\n            .arg(\"build\")\n            .arg(\"--dest-dir\")\n            .arg(out_dir.join(path.file_name().unwrap()))\n            .arg(&path)\n            .output()\n            .expect(\"mdbook should be built\");\n        check_status(&cmd, &output);\n    }\n}\n\nfn check_status(cmd: &Command, output: &Output) {\n    if !output.status.success() {\n        eprintln!(\"error: `{cmd:?}` failed\");\n        let stdout = std::str::from_utf8(&output.stdout).expect(\"stdout is not utf8\");\n        let stderr = std::str::from_utf8(&output.stderr).expect(\"stderr is not utf8\");\n        eprintln!(\"\\n--- stdout\\n{stdout}\\n--- stderr\\n{stderr}\");\n        std::process::exit(1);\n    }\n}\n\nfn run_browser_ui_test(out_dir: &Path) {\n    let mut command = Command::new(\"npx\");\n    let mut doc_path = format!(\"file://{}\", out_dir.display());\n    if !doc_path.ends_with('/') {\n        doc_path.push('/');\n    }\n    command\n        .arg(\"browser-ui-test\")\n        .args([\"--variable\", \"DOC_PATH\", doc_path.as_str()])\n        .args([\"--display-format\", \"compact\"]);\n\n    for arg in std::env::args().skip(1) {\n        if arg == \"--disable-headless-test\" {\n            command.arg(\"--no-headless\");\n        } else if arg.starts_with(\"--\") {\n            command.arg(arg);\n        } else {\n            command.args([\"--filter\", arg.as_str()]);\n        }\n    }\n\n    let test_dir = \"tests/gui\";\n    command.args([\"--test-folder\", test_dir]);\n\n    // Then we run the GUI tests on it.\n    let status = command.status().expect(\"failed to get command output\");\n    assert!(status.success(), \"{status:?}\");\n}\n"
  },
  {
    "path": "tests/gui/search.goml",
    "content": "// This tests basic search behavior.\n\nfail-on-js-error: true\ngo-to: |DOC_PATH| + \"search/index.html\"\n\ndefine-function: (\n    \"open-search\",\n    [],\n    block {\n        assert-css: (\"#mdbook-search-wrapper\", {\"display\": \"none\"})\n        press-key: 's'\n        wait-for-css-false: (\"#mdbook-search-wrapper\", {\"display\": \"none\"})\n    }\n)\n\ncall-function: (\"open-search\", {})\nassert-text: (\"#mdbook-searchresults-header\", \"\")\nwrite: \"extraordinary\"\nwait-for-text: (\"#mdbook-searchresults-header\", \"2 search results for 'extraordinary':\")\n// Close the search display\npress-key: 'Escape'\nwait-for-css: (\"#mdbook-search-wrapper\", {\"display\": \"none\"})\n// Reopening the search should show the last value\ncall-function: (\"open-search\", {})\nassert-text: (\"#mdbook-searchresults-header\", \"2 search results for 'extraordinary':\")\n// Navigate to a sub-chapter\ngo-to: \"./inner/chapter_2.html\"\nassert-text: (\"#mdbook-searchresults-header\", \"\")\ncall-function: (\"open-search\", {})\nwrite: \"kaleidoscope\"\nwait-for-text: (\"#mdbook-searchresults-header\", \"2 search results for 'kaleidoscope':\")\n\n// Now we test search shortcuts and more page changes.\ngo-to: |DOC_PATH| + \"search/index.html\"\n\n// This check is to ensure that the search bar is inside the search wrapper.\nassert: \"#mdbook-search-wrapper #mdbook-searchbar\"\nassert-css: (\"#mdbook-search-wrapper\", {\"display\": \"none\"})\n\n// Now we make sure the search input appear with the `S` shortcut.\npress-key: 's'\nwait-for-css-false: (\"#mdbook-search-wrapper\", {\"display\": \"none\"})\n// We ensure the search bar has the focus.\nwait-for: \"#mdbook-searchbar:focus\"\n// Pressing a key will therefore update the search input.\npress-key: 't'\nassert-text: (\"#mdbook-searchbar\", \"t\")\n\n// Now we press `Escape` to ensure that the search input disappears again.\npress-key: 'Escape'\nwait-for-css: (\"#mdbook-search-wrapper\", {\"display\": \"none\"})\n\n// Making it appear by clicking on the search button.\nclick: \"#mdbook-search-toggle\"\nwait-for-css: (\"#mdbook-search-wrapper\", {\"display\": \"block\"})\n// We ensure the search bar has the focus.\nassert: \"#mdbook-searchbar:focus\"\n\n// We input \"thunder\".\nwrite: \"thunder\"\n// The results should now appear.\nwait-for-text: (\"#mdbook-searchresults-header\", \"1 search result for 'thunder':\")\nassert: \"#mdbook-searchresults\"\n// Ensure that the URL was updated as well.\nassert-document-property: ({\"URL\": \"?search=thunder\"}, ENDS_WITH)\n\n// Now we ensure that when we land on the page with a \"search in progress\", the search results are\n// loaded and that the search input has focus.\ngo-to: |DOC_PATH| + \"search/index.html?search=thunder\"\nwait-for-text: (\"#mdbook-searchresults-header\", \"1 search result for 'thunder':\")\nassert: \"#mdbook-searchbar:focus\"\nassert: \"#mdbook-searchresults\"\n\n// And now we press `Escape` to close everything.\npress-key: 'Escape'\nwait-for-css: (\"#mdbook-search-wrapper\", {\"display\": \"none\"})\n"
  },
  {
    "path": "tests/gui/sidebar-active.goml",
    "content": "// This GUI test checks the active page sidebar highlight.\n\ngo-to: |DOC_PATH| + \"all-summary/index.html\"\n\nassert-text: (\"mdbook-sidebar-scrollbox a.active\", \"Prefix 1\")\n\ngo-to: |DOC_PATH| + \"all-summary/part-1/chapter-1.html\"\n\nassert-text: (\"mdbook-sidebar-scrollbox a.active\", \"3. P1 C1\")\n\ngo-to: |DOC_PATH| + \"all-summary/index.html?highlight=test\"\n\nassert-text: (\"mdbook-sidebar-scrollbox a.active\", \"Prefix 1\")\n\ngo-to: |DOC_PATH| + \"all-summary/part-1/chapter-1.html?highlight=test\"\n\nassert-text: (\"mdbook-sidebar-scrollbox a.active\", \"3. P1 C1\")\n"
  },
  {
    "path": "tests/gui/sidebar-nojs.goml",
    "content": "// This GUI test checks that the sidebar takes the whole height when it's inside\n// an iframe (because of JS disabled).\n// Regression test for <https://github.com/rust-lang/mdBook/issues/2528>.\n\n// We disable javascript\njavascript: false\ngo-to: |DOC_PATH| + \"basic/index.html\"\nstore-value: (height, 1028)\nset-window-size: (1028, |height|)\n\nwithin-iframe: (\".sidebar-iframe-outer\", block {\n    assert-size: (\" body\", {\"height\": |height|})\n})\n"
  },
  {
    "path": "tests/gui/sidebar-scroll.goml",
    "content": "// This GUI test checks the sidebar scroll position when navigating.\n\nstore-value: (windowHeight, 900)\nset-window-size: (1200, |windowHeight|)\ngo-to: |DOC_PATH| + \"sidebar-scroll/index.html\"\nassert-property: (\"mdbook-sidebar-scrollbox\", {\"scrollTop\": 0})\n\nclick: \".chapter a[href='chapter_2.html']\"\nassert-text: (\"title\", \"Chapter 2 - sidebar-scroll\")\nassert-property: (\"mdbook-sidebar-scrollbox\", {\"scrollTop\": 0})\n\nclick: \".chapter a[href='chapter_10.html']\"\nassert-text: (\"title\", \"Chapter 10 - sidebar-scroll\")\nassert-property: (\"mdbook-sidebar-scrollbox\", {\"scrollTop\": 0})\n\n// Check that heading nav of chapter 10 pushes 11 off the bottom.\nstore-position: (\".chapter a[href='chapter_11.html']\", {\"y\": chapter_y})\nassert: |chapter_y| > |windowHeight|\n\n// Clicking 11 should scroll back up because it is not possible for it to stay\n// in position since there are only a few lines above.\nclick: \".chapter a[href='chapter_11.html']\"\nassert-text: (\"title\", \"Chapter 11 - sidebar-scroll\")\nassert-property: (\"mdbook-sidebar-scrollbox\", {\"scrollTop\": 0})\n\n// Scroll down a little, and click on a chapter that can stay in place when\n// clicked.\nscroll-element-to: (\"mdbook-sidebar-scrollbox\", (0, 230))\nstore-property: (\"mdbook-sidebar-scrollbox\", {\"scrollTop\": sidebarScrollTop})\nassert: |sidebarScrollTop| > 0\nclick: \".chapter a[href='chapter_35.html']\"\nassert-text: (\"title\", \"Chapter 35 - sidebar-scroll\")\nassert-property: (\"mdbook-sidebar-scrollbox\", {\"scrollTop\": |sidebarScrollTop|})\n\n// Go to the next chapter, and verify that it scrolls to the middle.\npress-key: \"ArrowRight\"\nassert-text: (\"title\", \"Chapter 36 - sidebar-scroll\")\n// The active link should be roughly in the middle of the screen.\nstore-position: (\".chapter a.active\", {\"y\": active_y})\n// Approximate check just in case the browser has slight rendering differences.\nassert: |active_y| > 400\nassert: |active_y| < 450\n\n// Go to something near the top, it shouldn't scroll.\nstore-property: (\"mdbook-sidebar-scrollbox\", {\"scrollTop\": sidebarScrollTop})\nclick: \".chapter a[href='chapter_24.html']\"\nassert-text: (\"title\", \"Chapter 24 - sidebar-scroll\")\nassert-property: (\"mdbook-sidebar-scrollbox\", {\"scrollTop\": |sidebarScrollTop|})\n\n// Go to the last chapter, and verify it is scrolled to the bottom.\ngo-to: |DOC_PATH| + \"sidebar-scroll/chapter_100.html\"\nstore-property: (\"mdbook-sidebar-scrollbox\", {\"scrollTop\": scrollTop, \"scrollHeight\": scrollHeight, \"clientHeight\": clientHeight})\n// This needs to be approximate since there is a slight difference.\nassert: |scrollTop| >= |scrollHeight| - |clientHeight| - 1\n\n// Clicking upwards shouldn't scroll.\nstore-property: (\"mdbook-sidebar-scrollbox\", {\"scrollTop\": sidebarScrollTop})\nclick: \".chapter a[href='chapter_97.html']\"\nassert-text: (\"title\", \"Chapter 97 - sidebar-scroll\")\nassert-property: (\"mdbook-sidebar-scrollbox\", {\"scrollTop\": |sidebarScrollTop|})\n\nstore-property: (\"mdbook-sidebar-scrollbox\", {\"scrollTop\": sidebarScrollTop})\nclick: \".chapter a[href='chapter_76.html']\"\nassert-text: (\"title\", \"Chapter 76 - sidebar-scroll\")\nassert-property: (\"mdbook-sidebar-scrollbox\", {\"scrollTop\": |sidebarScrollTop|})\n"
  },
  {
    "path": "tests/gui/sidebar.goml",
    "content": "// This GUI test checks sidebar hide/show and also its behaviour on smaller\n// width.\n\ngo-to: |DOC_PATH| + \"all-summary/index.html\"\nset-window-size: (1100, 600)\n// Need to reload for the new size to be taken account by the JS.\nreload:\n\nstore-value: (content_indent, 308)\nstore-value: (sidebar_storage_value, \"mdbook-sidebar\")\nstore-value: (sidebar_storage_hidden_value, \"hidden\")\nstore-value: (sidebar_storage_displayed_value, \"visible\")\n\ndefine-function: (\n    \"hide-sidebar\",\n    [],\n    block {\n        assert-position: (\"#mdbook-page-wrapper\", {\"x\": |content_indent|})\n\n        // We now hide the sidebar.\n        click: \"#mdbook-sidebar-toggle\"\n        wait-for-css: (\"#mdbook-sidebar\", {\"display\": \"none\"})\n        assert-local-storage: {|sidebar_storage_value|: |sidebar_storage_hidden_value|}\n    },\n)\n\ndefine-function: (\n    \"show-sidebar\",\n    [],\n    block {\n        assert-css: (\"#mdbook-sidebar\", {\"display\": \"none\"})\n        assert-position: (\"#mdbook-page-wrapper\", {\"x\": 0})\n\n        // We expand the sidebar.\n        click: \"#mdbook-sidebar-toggle\"\n        wait-for-css-false: (\"#mdbook-sidebar\", {\"display\": \"none\"})\n        // `transform` is 0.3s so we need to wait a bit (0.5s) to ensure the animation is done.\n        wait-for: 5000\n        assert-css-false: (\"#mdbook-sidebar\", {\"transform\": \"matrix(1, 0, 0, 1, -308, 0)\"})\n        // The page content should be moved to the right.\n        assert-position: (\"#mdbook-page-wrapper\", {\"x\": |content_indent|})\n        assert-local-storage: {|sidebar_storage_value|: |sidebar_storage_displayed_value|}\n    },\n)\n\n// Since the sidebar is visible, we should be able to find this text.\nassert-find-text: (\"3. P1 C1\", {\"case-sensitive\": true})\ncall-function: (\"hide-sidebar\", {})\n// Text should not be findeable anymore since the sidebar is collapsed.\nassert-find-text-false: (\"3. P1 C1\", {\"case-sensitive\": true})\ncall-function: (\"show-sidebar\", {})\n// We should be able to find this text again.\nassert-find-text: (\"3. P1 C1\", {\"case-sensitive\": true})\n\n// We now test on smaller width to ensure that the sidebar is collapsed by default.\nset-window-size: (900, 600)\nreload:\ncall-function: (\"show-sidebar\", {})\ncall-function: (\"hide-sidebar\", {})\n\n// We now test that if the sidebar is considered open and we reload the page, since\n// the width is small, it will still be collapsed.\nset-local-storage: {|sidebar_storage_value|: |sidebar_storage_displayed_value|}\nreload:\n// The stored value shouldn't have changed.\nassert-local-storage: {|sidebar_storage_value|: |sidebar_storage_displayed_value|}\n// But the sidebar should be hidden anyway.\nassert-css: (\"#mdbook-sidebar\", {\"display\": \"none\"})\nassert-position: (\"#mdbook-page-wrapper\", {\"x\": 0})\n"
  },
  {
    "path": "tests/gui/theme.goml",
    "content": "// Basic theme switcher test.\n\ndebug: true\n\ngo-to: |DOC_PATH| + \"all-summary/index.html\"\n\n// TODO: Dark mode is automatic, how to check that here?\nassert-css: (\"#mdbook-theme-list\", {\"display\": \"none\"})\nclick: \"#mdbook-theme-toggle\"\nassert-css: (\"#mdbook-theme-list\", {\"display\": \"block\"})\nclick: \"#mdbook-theme-rust\"\nassert-attribute: (\"html\", {\"class\": \"js rust\"})\n// Clicking a theme doesn't dismiss the popup.\nassert-css: (\"#mdbook-theme-list\", {\"display\": \"block\"})\nassert-local-storage: {\"mdbook-theme\": \"rust\"}\n\n// Dismiss via toggle.\nclick: \"#mdbook-theme-toggle\"\nassert-css: (\"#mdbook-theme-list\", {\"display\": \"none\"})\n\n// Check for dismissal for click outside.\nclick: \"#mdbook-theme-toggle\"\nassert-css: (\"#mdbook-theme-list\", {\"display\": \"block\"})\nclick: \"main\"\nassert-css: (\"#mdbook-theme-list\", {\"display\": \"none\"})\n\n// Check for escape.\nclick: \"#mdbook-theme-toggle\"\nassert-css: (\"#mdbook-theme-list\", {\"display\": \"block\"})\npress-key: 'Escape'\nassert-css: (\"#mdbook-theme-list\", {\"display\": \"none\"})\n\n// Check for navigation retains theme.\ngo-to: \"./part-1/chapter-1.html\"\nassert-attribute: (\"html\", {\"class\": \"rust js\"})\n"
  },
  {
    "path": "tests/testsuite/README.md",
    "content": "# Testsuite\n\n## Introduction\n\nThis is the main testsuite for exercising all functionality of mdBook.\n\nTests 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.\n\n## Basic structure of a test\n\nUsing `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.\n\nSee `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.\n\nFor 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.\n\n`BookTest` is designed to be able to chain a series of actions. For example, you can do something like:\n\n```rust\nBookTest::from_dir(\"theme/mytest\")\n    .build()\n    .check_main_file(\"book/index.html\", str![[\"file contents\"]])\n    .change_file(\"src/index.md\", \"new contents\")\n    .build()\n    .check_main_file(\"book/index.html\", str![[\"new contents\"]]);\n```\n\n## Snapbox\n\nThe 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.\n\nThe 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.\n\nExpected contents can have wildcards like `...` (matches any lines) or `[..]` (matches any characters on a line). See [snapbox filters] for more info and other filters.\n\nTypically 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.\n\nNote that there is some normalization applied to the strings. See `book_test::assert` for how some of these normalizations happen.\n\n[`snapbox`]: https://docs.rs/snapbox/latest/snapbox/\n[`str!`]: https://docs.rs/snapbox/latest/snapbox/macro.str.html\n[`file!`]: https://docs.rs/snapbox/latest/snapbox/macro.file.html\n[snapbox filters]: https://docs.rs/snapbox/latest/snapbox/assert/struct.Assert.html#method.eq\n"
  },
  {
    "path": "tests/testsuite/book_test.rs",
    "content": "//! Utility for building and running tests against mdbook.\n\nuse mdbook_core::utils::fs;\nuse mdbook_driver::MDBook;\nuse mdbook_driver::init::BookBuilder;\nuse snapbox::IntoData;\nuse std::collections::BTreeMap;\nuse std::path::{Path, PathBuf};\nuse std::process::Command;\nuse std::sync::atomic::{AtomicU32, Ordering};\n\n/// Test number used for generating unique temp directory names.\nstatic NEXT_TEST_ID: AtomicU32 = AtomicU32::new(0);\n\n#[derive(Clone, Copy, Eq, PartialEq)]\nenum StatusCode {\n    Success,\n    Failure,\n    Code(i32),\n}\n\n/// Main helper for driving mdbook tests.\npub struct BookTest {\n    /// The temp directory where the test should perform its work.\n    pub dir: PathBuf,\n    /// The original source directory if created from [`BookTest::from_dir`].\n    original_source: Option<PathBuf>,\n    /// Snapshot assertion support.\n    pub assert: snapbox::Assert,\n    /// This indicates whether or not the book has been built.\n    built: bool,\n}\n\nimpl BookTest {\n    /// Creates a new test, copying the contents from the given directory into\n    /// a temp directory.\n    pub fn from_dir(dir: &str) -> BookTest {\n        // Copy this test book to a temp directory.\n        let dir = Path::new(\"tests/testsuite\").join(dir);\n        assert!(dir.exists(), \"{dir:?} should exist\");\n        let tmp = Self::new_tmp();\n        mdbook_core::utils::fs::copy_files_except_ext(\n            &dir,\n            &tmp,\n            true,\n            Some(&PathBuf::from(\"book\")),\n            &[],\n        )\n        .unwrap_or_else(|e| panic!(\"failed to copy test book {dir:?} to {tmp:?}: {e:?}\"));\n        Self::new(tmp, Some(dir))\n    }\n\n    /// Creates a new test with an empty temp directory.\n    pub fn empty() -> BookTest {\n        Self::new(Self::new_tmp(), None)\n    }\n\n    /// Creates a new test with the given function to initialize a new book.\n    ///\n    /// The book itself is not built.\n    pub fn init(f: impl Fn(&mut BookBuilder)) -> BookTest {\n        let tmp = Self::new_tmp();\n        let mut bb = MDBook::init(&tmp);\n        f(&mut bb);\n        bb.build()\n            .unwrap_or_else(|e| panic!(\"failed to initialize book at {tmp:?}: {e:?}\"));\n        Self::new(tmp, None)\n    }\n\n    fn new_tmp() -> PathBuf {\n        let id = NEXT_TEST_ID.fetch_add(1, Ordering::SeqCst);\n        let tmp = Path::new(env!(\"CARGO_TARGET_TMPDIR\"))\n            .join(\"ts\")\n            .join(format!(\"t{id}\"));\n        if tmp.exists() {\n            std::fs::remove_dir_all(&tmp)\n                .unwrap_or_else(|e| panic!(\"failed to remove {tmp:?}: {e:?}\"));\n        }\n        fs::create_dir_all(&tmp).unwrap();\n        tmp\n    }\n\n    fn new(dir: PathBuf, original_source: Option<PathBuf>) -> BookTest {\n        let assert = assert(&dir);\n        BookTest {\n            dir,\n            original_source,\n            assert,\n            built: false,\n        }\n    }\n\n    /// Checks the contents of an HTML file that it has the given contents\n    /// between the `<main>` tag.\n    ///\n    /// Normally the contents outside of the `<main>` tag aren't interesting,\n    /// and they add a significant amount of noise.\n    #[track_caller]\n    pub fn check_main_file(&mut self, path: &str, expected: impl IntoData) -> &mut Self {\n        if !self.built {\n            self.build();\n        }\n        let full_path = self.dir.join(path);\n        let actual = read_to_string(&full_path);\n        let start = actual\n            .find(\"<main>\")\n            .unwrap_or_else(|| panic!(\"didn't find <main> for `{full_path:?}` in:\\n{actual}\"));\n        let end = actual.find(\"</main>\").unwrap();\n        let contents = actual[start + 6..end - 7].trim();\n        self.assert.eq(contents, expected);\n        self\n    }\n\n    /// Verifies the HTML output of all chapters in a book.\n    ///\n    /// This calls [`BookTest::check_main_file`] for every `.html` file\n    /// generated by building the book. All of expected files are stored in a\n    /// director called \"expected\" in the original book source directory.\n    ///\n    /// This only works when created with [`BookTest::from_dir`].\n    ///\n    /// `404.html`, `print.html`, and `toc.html` are not validated. The root\n    /// `index.html` is also not validated (since it is often duplicated with\n    /// the first chapter). If you need to validate it, call\n    /// [`BookTest::check_main_file`] directly.\n    #[track_caller]\n    pub fn check_all_main_files(&mut self) -> &mut Self {\n        if !self.built {\n            self.build();\n        }\n        let book_root = self.dir.join(\"book\");\n        let mut files = list_all_files(&book_root);\n        files.retain(|file| {\n            file.extension().is_some_and(|ext| ext == \"html\")\n                && !matches!(\n                    file.to_str().unwrap(),\n                    \"index.html\" | \"404.html\" | \"print.html\" | \"toc.html\"\n                )\n        });\n        let expected_path = self\n            .original_source\n            .as_ref()\n            .expect(\"created with BookTest::from_dir\")\n            .join(\"expected\");\n        let mut expected_list = list_all_files(&expected_path);\n        for file in &files {\n            let expected = expected_path.join(file);\n            let data = snapbox::Data::read_from(&expected, None);\n            self.check_main_file(book_root.join(file).to_str().unwrap(), data);\n            if let Some(i) = expected_list.iter().position(|p| p == file) {\n                expected_list.remove(i);\n            }\n        }\n        // Verify there aren't any unused expected files.\n        if !expected_list.is_empty() {\n            panic!(\n                \"extra expected files found in `{expected_path:?}:\\n\\\n                {expected_list:#?}\\n\\\n                Verify that these files are no longer needed and delete them.\"\n            );\n        }\n        self\n    }\n\n    /// Checks the summary contents of `toc.js` against the expected value.\n    #[track_caller]\n    pub fn check_toc_js(&mut self, expected: impl IntoData) -> &mut Self {\n        if !self.built {\n            self.build();\n        }\n        let inner = self.toc_js_html();\n        // Would be nice if this were prettified, but a primitive wrapping will do for now.\n        let inner = inner.replace(\"><\", \">\\n<\");\n        self.assert.eq(inner, expected);\n        self\n    }\n\n    /// Returns the summary contents from `toc.js`.\n    #[track_caller]\n    pub fn toc_js_html(&self) -> String {\n        let toc_path = glob_one(&self.dir, \"book/toc*.js\");\n        let actual = read_to_string(&toc_path);\n        let inner = actual\n            .lines()\n            .filter_map(|line| {\n                let line = line.trim().strip_prefix(\"this.innerHTML = '\")?;\n                let line = line.strip_suffix(\"';\")?;\n                Some(line)\n            })\n            .next()\n            .expect(\"should have innerHTML\");\n        inner.to_string()\n    }\n\n    /// Checks that the contents of the given file matches the expected value.\n    ///\n    /// The path can use glob-style wildcards, but it must match only a single file.\n    #[track_caller]\n    pub fn check_file(&mut self, path_pattern: &str, expected: impl IntoData) -> &mut Self {\n        if !self.built {\n            self.build();\n        }\n        let path = glob_one(&self.dir, path_pattern);\n        let actual = read_to_string(&path);\n        self.assert.eq(actual, expected);\n        self\n    }\n\n    /// Checks that the given file contains the given [`snapbox::Assert`] pattern somewhere.\n    ///\n    /// The path can use glob-style wildcards, but it must match only a single file.\n    #[track_caller]\n    pub fn check_file_contains(&mut self, path_pattern: &str, expected: &str) -> &mut Self {\n        if !self.built {\n            self.build();\n        }\n        let path = glob_one(&self.dir, path_pattern);\n        let actual = read_to_string(&path);\n        let expected = format!(\"...\\n[..]{expected}[..]\\n...\\n\");\n        self.assert.eq(actual, expected);\n        self\n    }\n\n    /// Checks that the given file does not contain the given string anywhere.\n    ///\n    /// Beware that using this is fragile, as it may be unable to catch\n    /// regressions (it can't tell the difference between success, or the\n    /// string being looked for changed).\n    ///\n    /// The path can use glob-style wildcards, but it must match only a single file.\n    #[track_caller]\n    pub fn check_file_doesnt_contain(&mut self, path_pattern: &str, string: &str) -> &mut Self {\n        if !self.built {\n            self.build();\n        }\n        let path = glob_one(&self.dir, path_pattern);\n        let actual = read_to_string(&path);\n        assert!(\n            !actual.contains(string),\n            \"Unexpectedly found {string:?} in {path:?}\\n\\n{actual}\",\n        );\n        self\n    }\n\n    /// Checks that the list of files at the given path matches the given value.\n    #[track_caller]\n    pub fn check_file_list(&mut self, path: &str, expected: impl IntoData) -> &mut Self {\n        let mut all_paths: Vec<_> = walkdir::WalkDir::new(&self.dir.join(path))\n            .into_iter()\n            // Skip the outer directory.\n            .skip(1)\n            .map(|e| {\n                e.unwrap()\n                    .into_path()\n                    .strip_prefix(&self.dir)\n                    .unwrap()\n                    .to_str()\n                    .unwrap()\n                    .replace('\\\\', \"/\")\n            })\n            .collect();\n        all_paths.sort();\n        let actual = all_paths.join(\"\\n\");\n        self.assert.eq(actual, expected);\n        self\n    }\n\n    /// Loads an [`MDBook`] from the temp directory.\n    pub fn load_book(&self) -> MDBook {\n        MDBook::load(&self.dir).unwrap_or_else(|e| panic!(\"book failed to load: {e:?}\"))\n    }\n\n    /// Builds the book in the temp directory.\n    pub fn build(&mut self) -> &mut Self {\n        let book = self.load_book();\n        book.build()\n            .unwrap_or_else(|e| panic!(\"book failed to build: {e:?}\"));\n        self.built = true;\n        self\n    }\n\n    /// Runs the `mdbook` binary in the temp directory.\n    ///\n    /// This runs `mdbook` with the given args. The args are split on spaces\n    /// (if you need args with spaces, use the `args` method). The given\n    /// callback receives a [`BookCommand`] for you to customize how the\n    /// executable is run.\n    pub fn run(&mut self, args: &str, f: impl Fn(&mut BookCommand)) -> &mut Self {\n        let mut cmd = BookCommand {\n            assert: self.assert.clone(),\n            dir: self.dir.clone(),\n            args: split_args(args),\n            env: BTreeMap::new(),\n            expect_status: StatusCode::Success,\n            expect_stderr_data: None,\n            expect_stdout_data: None,\n            debug: None,\n        };\n        f(&mut cmd);\n        cmd.run();\n        // Ensure that `built` gets set if a build command is used so that all\n        // the `check` methods do not overwrite the contents of what was just\n        // built.\n        if cmd.args.first().map(String::as_str) == Some(\"build\") {\n            self.built = true\n        }\n        self\n    }\n\n    /// Change a file's contents in the given path.\n    pub fn change_file(&mut self, path: impl AsRef<Path>, body: &str) -> &mut Self {\n        let path = self.dir.join(path);\n        fs::write(&path, body).unwrap();\n        self\n    }\n\n    /// Removes a file or directory relative to the test root.\n    pub fn rm_r(&mut self, path: impl AsRef<Path>) -> &mut Self {\n        let path = self.dir.join(path.as_ref());\n        let meta = match path.symlink_metadata() {\n            Ok(meta) => meta,\n            Err(e) => panic!(\"failed to remove {path:?}, could not read: {e:?}\"),\n        };\n        // There is a race condition between fetching the metadata and\n        // actually performing the removal, but we don't care all that much\n        // for our tests.\n        if meta.is_dir() {\n            if let Err(e) = std::fs::remove_dir_all(&path) {\n                panic!(\"failed to remove {path:?}: {e:?}\");\n            }\n        } else if let Err(e) = std::fs::remove_file(&path) {\n            panic!(\"failed to remove {path:?}: {e:?}\")\n        }\n        self\n    }\n\n    /// Builds a Rust program with the given src.\n    ///\n    /// The given path should be the path where to output the executable in\n    /// the temp directory.\n    pub fn rust_program(&mut self, path: &str, src: &str) -> &mut Self {\n        let rs = self.dir.join(path).with_extension(\"rs\");\n        let parent = rs.parent().unwrap();\n        if !parent.exists() {\n            fs::create_dir_all(&parent).unwrap();\n        }\n        fs::write(&rs, src).unwrap();\n        let status = std::process::Command::new(\"rustc\")\n            .arg(&rs)\n            .current_dir(&parent)\n            .status()\n            .expect(\"rustc should run\");\n        assert!(status.success());\n        self\n    }\n}\n\n/// A builder for preparing to run the `mdbook` executable.\n///\n/// By default, it expects the process to succeed.\npub struct BookCommand {\n    pub dir: PathBuf,\n    assert: snapbox::Assert,\n    args: Vec<String>,\n    env: BTreeMap<String, Option<String>>,\n    expect_status: StatusCode,\n    expect_stderr_data: Option<snapbox::Data>,\n    expect_stdout_data: Option<snapbox::Data>,\n    debug: Option<String>,\n}\n\nimpl BookCommand {\n    /// Indicates that the process should fail.\n    pub fn expect_failure(&mut self) -> &mut Self {\n        self.expect_status = StatusCode::Failure;\n        self\n    }\n\n    /// Indicates the process should fail with the given exit code.\n    pub fn expect_code(&mut self, code: i32) -> &mut Self {\n        self.expect_status = StatusCode::Code(code);\n        self\n    }\n\n    /// Verifies that stderr matches the given value.\n    pub fn expect_stderr(&mut self, expected: impl snapbox::IntoData) -> &mut Self {\n        self.expect_stderr_data = Some(expected.into_data());\n        self\n    }\n\n    /// Verifies that stdout matches the given value.\n    pub fn expect_stdout(&mut self, expected: impl snapbox::IntoData) -> &mut Self {\n        self.expect_stdout_data = Some(expected.into_data());\n        self\n    }\n\n    /// Adds arguments to the command to run.\n    pub fn args(&mut self, args: &[&str]) -> &mut Self {\n        self.args.extend(args.into_iter().map(|t| t.to_string()));\n        self\n    }\n\n    /// Specifies an environment variable to set on the executable.\n    pub fn env<T: Into<String>>(&mut self, key: &str, value: T) -> &mut Self {\n        self.env.insert(key.to_string(), Some(value.into()));\n        self\n    }\n\n    /// Sets the directory used for running the command.\n    pub fn current_dir<S: AsRef<std::path::Path>>(&mut self, path: S) -> &mut Self {\n        self.dir = self.dir.join(path.as_ref());\n        self\n    }\n\n    /// Use this to debug a command.\n    ///\n    /// Pass the value that you would normally pass to `MDBOOK_LOG`, and this\n    /// will enable logging, print the command that runs and its output.\n    ///\n    /// This will fail if you use it in CI.\n    #[allow(unused)]\n    pub fn debug(&mut self, value: &str) -> &mut Self {\n        if std::env::var_os(\"CI\").is_some() {\n            panic!(\"debug is not allowed on CI\");\n        }\n        self.debug = Some(value.into());\n        self\n    }\n\n    /// Runs the command, and verifies the output.\n    fn run(&mut self) {\n        let mut cmd = Command::new(env!(\"CARGO_BIN_EXE_mdbook\"));\n        cmd.current_dir(&self.dir)\n            .args(&self.args)\n            .env_remove(\"MDBOOK_LOG\")\n            // Don't read the system git config which is out of our control.\n            .env(\"GIT_CONFIG_NOSYSTEM\", \"1\")\n            .env(\"GIT_CONFIG_GLOBAL\", &self.dir)\n            .env(\"GIT_CONFIG_SYSTEM\", &self.dir)\n            .env_remove(\"GIT_AUTHOR_EMAIL\")\n            .env_remove(\"GIT_AUTHOR_NAME\")\n            .env_remove(\"GIT_COMMITTER_EMAIL\")\n            .env_remove(\"GIT_COMMITTER_NAME\");\n\n        if let Some(debug) = &self.debug {\n            cmd.env(\"MDBOOK_LOG\", debug);\n        }\n\n        for (k, v) in &self.env {\n            match v {\n                Some(v) => cmd.env(k, v),\n                None => cmd.env_remove(k),\n            };\n        }\n\n        if self.debug.is_some() {\n            eprintln!(\"running {cmd:#?}\");\n        }\n        let output = cmd.output().expect(\"mdbook should be runnable\");\n        let stdout = std::str::from_utf8(&output.stdout).expect(\"stdout is not utf8\");\n        let stderr = std::str::from_utf8(&output.stderr).expect(\"stderr is not utf8\");\n        let render_output = || format!(\"\\n--- stdout\\n{stdout}\\n--- stderr\\n{stderr}\");\n        match (self.expect_status, output.status.success()) {\n            (StatusCode::Success, false) => {\n                panic!(\"mdbook failed, but expected success{}\", render_output())\n            }\n            (StatusCode::Failure, true) => {\n                panic!(\"mdbook succeeded, but expected failure{}\", render_output())\n            }\n            (StatusCode::Code(expected), _) => match output.status.code() {\n                Some(actual) => assert_eq!(\n                    actual, expected,\n                    \"process exit code did not match as expected\"\n                ),\n                None => panic!(\"process exited via signal {:?}\", output.status),\n            },\n            _ => {}\n        }\n        if self.debug.is_some() {\n            eprintln!(\"{}\", render_output());\n        }\n        self.expect_status = StatusCode::Success; // Reset to default.\n        if let Some(expect_stderr_data) = &self.expect_stderr_data {\n            if let Err(e) = self.assert.try_eq(\n                Some(&\"stderr\"),\n                stderr.into_data(),\n                expect_stderr_data.clone(),\n            ) {\n                panic!(\"{e}\");\n            }\n        }\n        if let Some(expect_stdout_data) = &self.expect_stdout_data {\n            if let Err(e) = self.assert.try_eq(\n                Some(&\"stdout\"),\n                stdout.into_data(),\n                expect_stdout_data.clone(),\n            ) {\n                panic!(\"{e}\");\n            }\n        }\n    }\n}\n\nfn split_args(s: &str) -> Vec<String> {\n    s.split_whitespace()\n        .map(|arg| {\n            if arg.contains(&['\"', '\\''][..]) {\n                panic!(\"shell-style argument parsing is not supported\");\n            }\n            String::from(arg)\n        })\n        .collect()\n}\n\nstatic LITERAL_REDACTIONS: &[(&str, &str)] = &[\n    // Unix message for an entity was not found\n    (\"[NOT_FOUND]\", \"No such file or directory (os error 2)\"),\n    // Windows message for an entity was not found\n    (\n        \"[NOT_FOUND]\",\n        \"The system cannot find the file specified. (os error 2)\",\n    ),\n    (\n        \"[NOT_FOUND]\",\n        \"The system cannot find the path specified. (os error 3)\",\n    ),\n    (\"[NOT_FOUND]\", \"program not found\"),\n    // Unix message for exit status\n    (\"[EXIT_STATUS]\", \"exit status\"),\n    // Windows message for exit status\n    (\"[EXIT_STATUS]\", \"exit code\"),\n    (\"[TAB]\", \"\\t\"),\n    (\"[EXE]\", std::env::consts::EXE_SUFFIX),\n];\n\nfn assert(root: &Path) -> snapbox::Assert {\n    let mut subs = snapbox::Redactions::new();\n    subs.insert(\"[ROOT]\", root.to_path_buf()).unwrap();\n    subs.insert(\"[VERSION]\", mdbook_core::MDBOOK_VERSION)\n        .unwrap();\n\n    subs.extend(LITERAL_REDACTIONS.into_iter().cloned())\n        .unwrap();\n\n    snapbox::Assert::new()\n        .action_env(snapbox::assert::DEFAULT_ACTION_ENV)\n        .redact_with(subs)\n}\n\n/// Helper to read a string from the filesystem.\n#[track_caller]\npub fn read_to_string<P: AsRef<Path>>(path: P) -> String {\n    let path = path.as_ref();\n    fs::read_to_string(path).unwrap()\n}\n\n/// Returns the first path from the given glob pattern.\npub fn glob_one<P: AsRef<Path>>(path: P, pattern: &str) -> PathBuf {\n    let path = path.as_ref();\n    let mut matches = glob::glob(path.join(pattern).to_str().unwrap()).unwrap();\n    let Some(first) = matches.next() else {\n        panic!(\"expected at least one file at `{path:?}` with pattern `{pattern}`, found none\");\n    };\n    let first = first.unwrap();\n    if let Some(next) = matches.next() {\n        panic!(\n            \"expected only one file for pattern `{pattern}` in `{path:?}`, \\\n             found `{first:?}` and `{:?}`\",\n            next.unwrap()\n        );\n    }\n    first\n}\n\n/// Lists all files at the given directory.\n///\n/// Recursively walks the tree. Paths are relative to the directory.\npub fn list_all_files(dir: &Path) -> Vec<PathBuf> {\n    walkdir::WalkDir::new(dir)\n        .sort_by_file_name()\n        .into_iter()\n        // Skip the outer directory.\n        .skip(1)\n        .map(|entry| {\n            let entry = entry.unwrap();\n            let path = entry.path();\n            path.strip_prefix(dir).unwrap().to_path_buf()\n        })\n        .collect()\n}\n"
  },
  {
    "path": "tests/testsuite/build/basic_build/README.md",
    "content": "# Basic book\n\nThis GUI test book is the default book with a single chapter.\n"
  },
  {
    "path": "tests/testsuite/build/basic_build/book.toml",
    "content": "[book]\ntitle = \"basic_build\"\n"
  },
  {
    "path": "tests/testsuite/build/basic_build/src/SUMMARY.md",
    "content": "# Summary\n\n- [Chapter 1](./chapter_1.md)\n"
  },
  {
    "path": "tests/testsuite/build/basic_build/src/chapter_1.md",
    "content": "# Chapter 1\n"
  },
  {
    "path": "tests/testsuite/build/create_missing/book.toml",
    "content": "[book]\ntitle = \"create_missing\"\n"
  },
  {
    "path": "tests/testsuite/build/create_missing/src/SUMMARY.md",
    "content": "# Summary\n\n- [Chapter 1](./chapter_1.md)\n"
  },
  {
    "path": "tests/testsuite/build/missing_file/book.toml",
    "content": "[book]\ntitle = \"missing_file\"\n\n[build]\ncreate-missing = false\n"
  },
  {
    "path": "tests/testsuite/build/missing_file/src/SUMMARY.md",
    "content": "# Summary\n\n- [Chapter 1](./chapter_1.md)\n"
  },
  {
    "path": "tests/testsuite/build/no_reserved_filename/book.toml",
    "content": "[book]\ntitle = \"no_reserved_filename\"\n"
  },
  {
    "path": "tests/testsuite/build/no_reserved_filename/src/SUMMARY.md",
    "content": "# Summary\n\n- [Print](print.md)\n"
  },
  {
    "path": "tests/testsuite/build/no_reserved_filename/src/print.md",
    "content": "# Print\n"
  },
  {
    "path": "tests/testsuite/build.rs",
    "content": "//! General build tests.\n//!\n//! More specific tests should usually go into a module based on the feature.\n//! This module should just have general build tests, or misc small things.\n\nuse crate::prelude::*;\n\n// Simple smoke test that building works.\n#[test]\nfn basic_build() {\n    BookTest::from_dir(\"build/basic_build\").run(\"build\", |cmd| {\n        cmd.expect_stderr(str![[r#\"\n INFO Book building has started\n INFO Running the html backend\n INFO HTML book written to `[ROOT]/book`\n\n\"#]]);\n    });\n}\n\n// Ensure building fails if `create-missing` is false and one of the files does\n// not exist.\n#[test]\nfn failure_on_missing_file() {\n    BookTest::from_dir(\"build/missing_file\").run(\"build\", |cmd| {\n        cmd.expect_failure().expect_stderr(str![[r#\"\nERROR failed to read chapter `./chapter_1.md`\n[TAB]Caused by: [NOT_FOUND]\n\n\"#]]);\n    });\n}\n\n// Ensure a missing file is created if `create-missing` is true.\n#[test]\nfn create_missing() {\n    let test = BookTest::from_dir(\"build/create_missing\");\n    assert!(test.dir.join(\"src/SUMMARY.md\").exists());\n    assert!(!test.dir.join(\"src/chapter_1.md\").exists());\n    test.load_book();\n    assert!(test.dir.join(\"src/chapter_1.md\").exists());\n}\n\n// Checks that it fails if the summary has a reserved filename.\n#[test]\nfn no_reserved_filename() {\n    BookTest::from_dir(\"build/no_reserved_filename\").run(\"build\", |cmd| {\n        cmd.expect_failure().expect_stderr(str![[r#\"\n INFO Book building has started\n INFO Running the html backend\nERROR Rendering failed\n[TAB]Caused by: print.md is reserved for internal use\n\n\"#]]);\n    });\n}\n\n// Build without book.toml should be OK.\n#[test]\nfn book_toml_isnt_required() {\n    let mut test = BookTest::init(|_| {});\n    std::fs::remove_file(test.dir.join(\"book.toml\")).unwrap();\n    test.build();\n    test.check_main_file(\n        \"book/chapter_1.html\",\n        str![[r##\"<h1 id=\"chapter-1\"><a class=\"header\" href=\"#chapter-1\">Chapter 1</a></h1>\"##]],\n    );\n}\n\n// Dest dir relative path behavior.\n#[test]\nfn dest_dir_relative_path() {\n    let mut test = BookTest::from_dir(\"build/basic_build\");\n    let current_dir = test.dir.join(\"work\");\n    std::fs::create_dir_all(&current_dir).unwrap();\n    test.run(\"build\", |cmd| {\n        cmd.args(&[\"--dest-dir\", \"foo\", \"..\"])\n            .current_dir(&current_dir)\n            .expect_stderr(str![[r#\"\n INFO Book building has started\n INFO Running the html backend\n INFO HTML book written to `[ROOT]/work/foo`\n\n\"#]]);\n    });\n    assert!(current_dir.join(\"foo/index.html\").exists());\n}\n"
  },
  {
    "path": "tests/testsuite/cli.rs",
    "content": "//! Basic tests for mdbook's CLI.\n\nuse crate::prelude::*;\nuse snapbox::file;\n\n// Test with no args.\n#[test]\n#[cfg_attr(\n    not(all(feature = \"watch\", feature = \"serve\")),\n    ignore = \"needs all features\"\n)]\nfn no_args() {\n    BookTest::empty().run(\"\", |cmd| {\n        cmd.expect_code(2)\n            .expect_stdout(str![[\"\"]])\n            .expect_stderr(file![\"cli/no_args.term.svg\"]);\n    });\n}\n\n// Help command.\n#[test]\n#[cfg_attr(\n    not(all(feature = \"watch\", feature = \"serve\")),\n    ignore = \"needs all features\"\n)]\nfn help() {\n    BookTest::empty()\n        .run(\"help\", |cmd| {\n            cmd.expect_stdout(file![\"cli/help.term.svg\"])\n                .expect_stderr(str![[\"\"]]);\n        })\n        .run(\"--help\", |cmd| {\n            cmd.expect_stdout(file![\"cli/help.term.svg\"])\n                .expect_stderr(str![[\"\"]]);\n        });\n}\n"
  },
  {
    "path": "tests/testsuite/config/empty/book.toml",
    "content": ""
  },
  {
    "path": "tests/testsuite/config/empty/src/SUMMARY.md",
    "content": "# Summary\n\n- [Chapter 1](./chapter_1.md)\n"
  },
  {
    "path": "tests/testsuite/config/empty/src/chapter_1.md",
    "content": "# Chapter 1\n"
  },
  {
    "path": "tests/testsuite/config.rs",
    "content": "//! Tests for book configuration loading.\n\nuse crate::prelude::*;\n\n// Test that config can load from environment variable.\n#[test]\nfn config_from_env() {\n    BookTest::from_dir(\"config/empty\")\n        .run(\"build\", |cmd| {\n            cmd.env(\"MDBOOK_BOOK__TITLE\", \"Custom env title\");\n        })\n        .check_file_contains(\n            \"book/index.html\",\n            \"<title>Chapter 1 - Custom env title</title>\",\n        );\n\n    // json for some subtable\n    //\n}\n\n// Test environment config with JSON.\n#[test]\nfn config_json_from_env() {\n    // build table\n    BookTest::from_dir(\"config/empty\")\n        .run(\"build\", |cmd| {\n            cmd.env(\n                \"MDBOOK_BOOK\",\n                r#\"{\"title\": \"My Awesome Book\", \"authors\": [\"Michael-F-Bryan\"]}\"#,\n            );\n        })\n        .check_file_contains(\n            \"book/index.html\",\n            \"<title>Chapter 1 - My Awesome Book</title>\",\n        );\n\n    // book table\n    BookTest::from_dir(\"config/empty\")\n        .run(\"build\", |cmd| {\n            cmd.env(\"MDBOOK_BUILD\", r#\"{\"build-dir\": \"alt\"}\"#);\n        })\n        .check_file_contains(\"alt/index.html\", \"<title>Chapter 1</title>\");\n}\n\n// Test that a preprocessor receives config set in the environment.\n#[test]\nfn preprocessor_cfg_from_env() {\n    let mut test = BookTest::from_dir(\"config/empty\");\n    test.rust_program(\n        \"cat-to-file\",\n        r#\"\n        fn main() {\n            use std::io::Read;\n            let mut s = String::new();\n            std::io::stdin().read_to_string(&mut s).unwrap();\n            std::fs::write(\"out.txt\", s).unwrap();\n            println!(\"{{\\\"items\\\": []}}\");\n        }\n        \"#,\n    )\n    .run(\"build\", |cmd| {\n        cmd.env(\n            \"MDBOOK_PREPROCESSOR__CAT_TO_FILE\",\n            r#\"{\"command\":\"./cat-to-file\", \"array\": [1,2,3], \"number\": 123}\"#,\n        );\n    });\n    let out = read_to_string(test.dir.join(\"out.txt\"));\n    let (ctx, _book) = mdbook_preprocessor::parse_input(out.as_bytes()).unwrap();\n    let cfg: serde_json::Value = ctx.config.get(\"preprocessor.cat-to-file\").unwrap().unwrap();\n    assert_eq!(\n        cfg,\n        serde_json::json!({\n            \"command\": \"./cat-to-file\",\n            \"array\": [1,2,3],\n            \"number\": 123,\n        })\n    );\n}\n\n// Test that a renderer receives config set in the environment.\n#[test]\nfn output_cfg_from_env() {\n    let mut test = BookTest::from_dir(\"config/empty\");\n    test.rust_program(\n        \"cat-to-file\",\n        r#\"\n        fn main() {\n            use std::io::Read;\n            let mut s = String::new();\n            std::io::stdin().read_to_string(&mut s).unwrap();\n            std::fs::write(\"out.txt\", s).unwrap();\n        }\n        \"#,\n    )\n    .run(\"build\", |cmd| {\n        cmd.env(\n            \"MDBOOK_OUTPUT__CAT_TO_FILE\",\n            r#\"{\"command\":\"./cat-to-file\", \"array\": [1,2,3], \"number\": 123}\"#,\n        );\n    });\n    let out = read_to_string(test.dir.join(\"book/out.txt\"));\n    let ctx = mdbook_renderer::RenderContext::from_json(out.as_bytes()).unwrap();\n    let cfg: serde_json::Value = ctx.config.get(\"output.cat-to-file\").unwrap().unwrap();\n    assert_eq!(\n        cfg,\n        serde_json::json!({\n            \"command\": \"./cat-to-file\",\n            \"array\": [1,2,3],\n            \"number\": 123,\n        })\n    );\n}\n\n// An invalid key at the top level.\n#[test]\nfn bad_config_top_level() {\n    BookTest::init(|_| {})\n        .change_file(\"book.toml\", \"foo = 123\")\n        .run(\"build\", |cmd| {\n            cmd.expect_failure()\n                .expect_stdout(str![[\"\"]])\n                .expect_stderr(str![[r#\"\nERROR Invalid configuration file\n[TAB]Caused by: TOML parse error at line 1, column 1\n  |\n1 | foo = 123\n  | ^^^\nunknown field `foo`, expected one of `book`, `build`, `rust`, `output`, `preprocessor`\n\n\n\"#]]);\n        });\n}\n\n// An invalid table at the top level.\n#[test]\nfn bad_config_top_level_table() {\n    BookTest::init(|_| {})\n        .change_file(\n            \"book.toml\",\n            \"[other]\\n\\\n            foo = 123\",\n        )\n        .run(\"build\", |cmd| {\n            cmd.expect_failure()\n                .expect_stdout(str![[\"\"]])\n                .expect_stderr(str![[r#\"\nERROR Invalid configuration file\n[TAB]Caused by: TOML parse error at line 1, column 2\n  |\n1 | [other]\n  |  ^^^^^\nunknown field `other`, expected one of `book`, `build`, `rust`, `output`, `preprocessor`\n\n\n\"#]]);\n        });\n}\n\n// An invalid key in the main book table.\n#[test]\nfn bad_config_in_book_table() {\n    BookTest::init(|_| {})\n        .change_file(\n            \"book.toml\",\n            \"[book]\\n\\\n             title = \\\"bad-config\\\"\\n\\\n             foo = 123\"\n        )\n        .run(\"build\", |cmd| {\n            cmd.expect_failure()\n                .expect_stdout(str![[\"\"]])\n                .expect_stderr(str![[r#\"\nERROR Invalid configuration file\n[TAB]Caused by: TOML parse error at line 3, column 1\n  |\n3 | foo = 123\n  | ^^^\nunknown field `foo`, expected one of `title`, `authors`, `description`, `src`, `language`, `text-direction`\n\n\n\"#]]);\n        });\n}\n\n// An invalid key in the main rust table.\n#[test]\nfn bad_config_in_rust_table() {\n    BookTest::init(|_| {})\n        .change_file(\n            \"book.toml\",\n            \"[rust]\\n\\\n             title = \\\"bad-config\\\"\\n\",\n        )\n        .run(\"build\", |cmd| {\n            cmd.expect_failure()\n                .expect_stdout(str![[\"\"]])\n                .expect_stderr(str![[r#\"\nERROR Invalid configuration file\n[TAB]Caused by: TOML parse error at line 2, column 1\n  |\n2 | title = \"bad-config\"\n  | ^^^^^\nunknown field `title`, expected `edition`\n\n\n\"#]]);\n        });\n}\n\n// An invalid top-level key in the environment.\n#[test]\nfn env_invalid_config_key() {\n    BookTest::from_dir(\"config/empty\").run(\"build\", |cmd| {\n        cmd.env(\"MDBOOK_FOO\", \"testing\")\n            .expect_stdout(str![[\"\"]])\n            .expect_stderr(str![[r#\"\n INFO Book building has started\n INFO Running the html backend\n INFO HTML book written to `[ROOT]/book`\n\n\"#]]);\n    });\n}\n\n// An invalid value in the environment.\n#[test]\nfn env_invalid_value() {\n    BookTest::from_dir(\"config/empty\")\n        .run(\"build\", |cmd| {\n            cmd.env(\"MDBOOK_BOOK\", r#\"{\"titlez\": \"typo\"}\"#)\n                .expect_failure()\n                .expect_stdout(str![[\"\"]])\n                .expect_stderr(str![[r#\"\nERROR unknown field `titlez`, expected one of `title`, `authors`, `description`, `src`, `language`, `text-direction`\n\n\n\"#]]);\n        })\n        .run(\"build\", |cmd| {\n            cmd.env(\"MDBOOK_BOOK__TITLE\", r#\"{\"looks like obj\": \"abc\"}\"#)\n                .expect_failure()\n                .expect_stdout(str![[\"\"]])\n                .expect_stderr(str![[r#\"\nERROR invalid type: map, expected a string\nin `title`\n\n\n\"#]]);\n        })\n        // This is not valid JSON, so falls back to be interpreted as a string.\n        .run(\"build\", |cmd| {\n            cmd.env(\"MDBOOK_BOOK__TITLE\", r#\"{braces}\"#)\n                .expect_stdout(str![[\"\"]])\n                .expect_stderr(str![[r#\"\n INFO Book building has started\n INFO Running the html backend\n INFO HTML book written to `[ROOT]/book`\n\n\"#]]);\n        })\n        .check_file_contains(\"book/index.html\", \"<title>Chapter 1 - {braces}</title>\");\n}\n\n// Replacing the entire book table from the environment.\n#[test]\nfn env_entire_book_table() {\n    BookTest::init(|_| {})\n        .change_file(\n            \"book.toml\",\n            \"[book]\\n\\\n             title = \\\"config title\\\"\\n\\\n            \",\n        )\n        .run(\"build\", |cmd| {\n            cmd.env(\"MDBOOK_BOOK\", r#\"{\"description\": \"custom description\"}\"#);\n        })\n        // The book.toml title is removed.\n        .check_file_contains(\"book/index.html\", \"<title>Chapter 1</title>\")\n        .check_file_contains(\n            \"book/index.html\",\n            r#\"<meta name=\"description\" content=\"custom description\">\"#,\n        );\n}\n\n// Replacing the entire output or preprocessor table from the environment.\n#[test]\nfn env_entire_output_preprocessor_table() {\n    BookTest::from_dir(\"config/empty\")\n        .rust_program(\n            \"mdbook-my-preprocessor\",\n            r#\"\n            fn main() {\n                let mut args = std::env::args().skip(1);\n                if args.next().as_deref() == Some(\"supports\") {\n                    return;\n                }\n                use std::io::Read;\n                let mut s = String::new();\n                std::io::stdin().read_to_string(&mut s).unwrap();\n                assert!(s.contains(\"custom preprocessor config\"));\n                println!(\"{{\\\"items\\\": []}}\");\n            }\n            \"#,\n        )\n        .rust_program(\n            \"mdbook-my-output\",\n            r#\"\n            fn main() {\n                use std::io::Read;\n                let mut s = String::new();\n                std::io::stdin().read_to_string(&mut s).unwrap();\n                assert!(s.contains(\"custom output config\"));\n                eprintln!(\"preprocessor saw custom config\");\n            }\n            \"#,\n        )\n        .run(\"build\", |cmd| {\n            let mut paths: Vec<_> =\n                std::env::split_paths(&std::env::var_os(\"PATH\").unwrap_or_default()).collect();\n            paths.push(cmd.dir.clone());\n            let path = std::env::join_paths(paths).unwrap().into_string().unwrap();\n\n            cmd.env(\n                \"MDBOOK_OUTPUT\",\n                r#\"{\"my-output\": {\"foo\": \"custom output config\"}}\"#,\n            )\n            .env(\n                \"MDBOOK_PREPROCESSOR\",\n                r#\"{\"my-preprocessor\": {\"foo\": \"custom preprocessor config\"}}\"#,\n            )\n            .env(\"PATH\", path)\n            .expect_stdout(str![[\"\"]])\n            .expect_stderr(str![[r#\"\n INFO Book building has started\n INFO Running the my-output backend\n INFO Invoking the \"my-output\" renderer\npreprocessor saw custom config\n\n\"#]]);\n        })\n        // No HTML output\n        .check_file_list(\"book\", str![[\"\"]]);\n}\n"
  },
  {
    "path": "tests/testsuite/includes/all_includes/book.toml",
    "content": "[book]\ntitle = \"all_includes\"\n"
  },
  {
    "path": "tests/testsuite/includes/all_includes/src/SUMMARY.md",
    "content": "# Summary\n\n- [Basic Includes](./includes.md)\n    - [Relative Includes](./relative/includes.md)\n- [Recursive Includes](./recursive.md)\n- [Include Anchors](./anchors.md)\n- [Rustdoc Includes](./rustdoc.md)\n- [Playground Includes](./playground.md)\n"
  },
  {
    "path": "tests/testsuite/includes/all_includes/src/anchors.md",
    "content": "# Include Anchors\n\n```rust\n{{#include nested-test-with-anchors.rs:myanchor}}\n```\n"
  },
  {
    "path": "tests/testsuite/includes/all_includes/src/example.rs",
    "content": "fn main() {\n    println!(\"Hello World!\");\n#\n#    // You can even hide lines! :D\n#   println!(\"I am hidden! Expand the code snippet to see me\");\n}\n"
  },
  {
    "path": "tests/testsuite/includes/all_includes/src/includes.md",
    "content": "# Basic Includes\n\n{{#include sample.md}}\n\n"
  },
  {
    "path": "tests/testsuite/includes/all_includes/src/nested-test-with-anchors.rs",
    "content": "// This is a test of includes with anchors.\n\n// ANCHOR: myanchor\n// ANCHOR: unendinganchor\nlet x = 1;\n// ANCHOR_END: myanchor\n"
  },
  {
    "path": "tests/testsuite/includes/all_includes/src/partially-included-test-with-anchors.rs",
    "content": "fn some_other_function() {\n    // ANCHOR: unused-anchor-that-should-be-stripped\n    println!(\"unused anchor\");\n    // ANCHOR_END: unused-anchor-that-should-be-stripped\n}\n\n// ANCHOR: rustdoc-include-anchor\nfn main() {\n    some_other_function();\n}\n// ANCHOR_END: rustdoc-include-anchor\n"
  },
  {
    "path": "tests/testsuite/includes/all_includes/src/partially-included-test.rs",
    "content": "fn some_function() {\n    println!(\"some function\");\n}\n\nfn main() {\n    some_function();\n}\n"
  },
  {
    "path": "tests/testsuite/includes/all_includes/src/playground.md",
    "content": "# Playground Includes\n\n{{#playground example.rs}}\n"
  },
  {
    "path": "tests/testsuite/includes/all_includes/src/recursive.md",
    "content": "Around the world, around the world\n{{#include recursive.md}}\n"
  },
  {
    "path": "tests/testsuite/includes/all_includes/src/relative/includes.md",
    "content": "# Relative Includes\n\n{{#include ../sample.md}}\n"
  },
  {
    "path": "tests/testsuite/includes/all_includes/src/rustdoc.md",
    "content": "# Rustdoc Includes\n\n## Rustdoc include adds the rest of the file as hidden\n\n```rust\n{{#rustdoc_include partially-included-test.rs:5:7}}\n```\n\n## Rustdoc include works with anchors too\n\n```rust\n{{#rustdoc_include partially-included-test-with-anchors.rs:rustdoc-include-anchor}}\n```\n"
  },
  {
    "path": "tests/testsuite/includes/all_includes/src/sample.md",
    "content": "## Sample\n\nThis is a sample include.\n"
  },
  {
    "path": "tests/testsuite/includes.rs",
    "content": "//! Tests for include preprocessor.\n\nuse crate::prelude::*;\n\n// Basic test for #include.\n#[test]\nfn include() {\n    BookTest::from_dir(\"includes/all_includes\")\n        .check_main_file(\n            \"book/includes.html\",\n            str![[r##\"\n<h1 id=\"basic-includes\"><a class=\"header\" href=\"#basic-includes\">Basic Includes</a></h1>\n<h2 id=\"sample\"><a class=\"header\" href=\"#sample\">Sample</a></h2>\n<p>This is a sample include.</p>\n\"##]],\n        )\n        .check_main_file(\n            \"book/relative/includes.html\",\n            str![[r##\"\n<h1 id=\"relative-includes\"><a class=\"header\" href=\"#relative-includes\">Relative Includes</a></h1>\n<h2 id=\"sample\"><a class=\"header\" href=\"#sample\">Sample</a></h2>\n<p>This is a sample include.</p>\n\"##]],\n        );\n}\n\n// Checks for anchored includes.\n#[test]\nfn anchored_include() {\n    BookTest::from_dir(\"includes/all_includes\").check_main_file(\n        \"book/anchors.html\",\n        str![[r##\"\n<h1 id=\"include-anchors\"><a class=\"header\" href=\"#include-anchors\">Include Anchors</a></h1>\n<pre class=\"playground\"><code class=\"language-rust\"><span class=\"boring\">#![allow(unused)]\n</span><span class=\"boring\">fn main() {\n</span>let x = 1;\n<span class=\"boring\">}</span></code></pre>\n\"##]],\n    );\n}\n\n// Checks behavior of recursive include.\n#[test]\nfn recursive_include() {\n    BookTest::from_dir(\"includes/all_includes\")\n        .run(\"build\", |cmd| {\n            cmd.expect_stderr(str![[r#\"\n INFO Book building has started\nERROR Stack depth exceeded in recursive.md. Check for cyclic includes\n INFO Running the html backend\n INFO HTML book written to `[ROOT]/book`\n\n\"#]]);\n        })\n        .check_main_file(\n            \"book/recursive.html\",\n            str![[r#\"\n<p>Around the world, around the world\nAround the world, around the world\nAround the world, around the world\nAround the world, around the world\nAround the world, around the world\nAround the world, around the world\nAround the world, around the world\nAround the world, around the world\nAround the world, around the world\nAround the world, around the world\nAround the world, around the world</p>\n\"#]],\n        );\n}\n\n// Checks the behavior of `{{#playground}}` include.\n#[test]\nfn playground_include() {\n    BookTest::from_dir(\"includes/all_includes\")\n        .check_main_file(\"book/playground.html\",\n            str![[r##\"\n<h1 id=\"playground-includes\"><a class=\"header\" href=\"#playground-includes\">Playground Includes</a></h1>\n<pre class=\"playground\"><code class=\"language-rust\">fn main() {\n    println!(\"Hello World!\");\n<span class=\"boring\">\n</span><span class=\"boring\">   // You can even hide lines! :D\n</span><span class=\"boring\">  println!(\"I am hidden! Expand the code snippet to see me\");\n</span>}</code></pre>\n\"##]]);\n}\n\n// Checks the behavior of `{{#rustdoc_include}}`.\n#[test]\nfn rustdoc_include() {\n    BookTest::from_dir(\"includes/all_includes\")\n        .check_main_file(\"book/rustdoc.html\",\n            str![[r##\"\n<h1 id=\"rustdoc-includes\"><a class=\"header\" href=\"#rustdoc-includes\">Rustdoc Includes</a></h1>\n<h2 id=\"rustdoc-include-adds-the-rest-of-the-file-as-hidden\"><a class=\"header\" href=\"#rustdoc-include-adds-the-rest-of-the-file-as-hidden\">Rustdoc include adds the rest of the file as hidden</a></h2>\n<pre class=\"playground\"><code class=\"language-rust\"><span class=\"boring\">fn some_function() {\n</span><span class=\"boring\">    println!(\"some function\");\n</span><span class=\"boring\">}\n</span><span class=\"boring\">\n</span>fn main() {\n    some_function();\n}</code></pre>\n<h2 id=\"rustdoc-include-works-with-anchors-too\"><a class=\"header\" href=\"#rustdoc-include-works-with-anchors-too\">Rustdoc include works with anchors too</a></h2>\n<pre class=\"playground\"><code class=\"language-rust\"><span class=\"boring\">fn some_other_function() {\n</span><span class=\"boring\">    println!(\"unused anchor\");\n</span><span class=\"boring\">}\n</span><span class=\"boring\">\n</span>fn main() {\n    some_other_function();\n}</code></pre>\n\"##]]);\n}\n"
  },
  {
    "path": "tests/testsuite/index/basic_readme/book.toml",
    "content": "[book]\ntitle = \"basic_readme\"\n"
  },
  {
    "path": "tests/testsuite/index/basic_readme/src/README.md",
    "content": "# Intro\n"
  },
  {
    "path": "tests/testsuite/index/basic_readme/src/SUMMARY.md",
    "content": "# Summary\n\n[Intro](./README.md)\n\n- [First](first/README)\n- [Second](second/Readme.md)\n"
  },
  {
    "path": "tests/testsuite/index/basic_readme/src/first/README",
    "content": "# First\n"
  },
  {
    "path": "tests/testsuite/index/basic_readme/src/second/Readme.md",
    "content": "# Second\n"
  },
  {
    "path": "tests/testsuite/index.rs",
    "content": "//! Tests for the index preprocessor.\n\nuse crate::prelude::*;\n\n// Checks basic README to index.html conversion.\n#[test]\nfn readme_to_index() {\n    let mut test = BookTest::from_dir(\"index/basic_readme\");\n    test.check_main_file(\n        \"book/index.html\",\n        str![[r##\"<h1 id=\"intro\"><a class=\"header\" href=\"#intro\">Intro</a></h1>\"##]],\n    )\n    .check_main_file(\n        \"book/first/index.html\",\n        str![[r##\"<h1 id=\"first\"><a class=\"header\" href=\"#first\">First</a></h1>\"##]],\n    )\n    .check_main_file(\n        \"book/second/index.html\",\n        str![[r##\"<h1 id=\"second\"><a class=\"header\" href=\"#second\">Second</a></h1>\"##]],\n    )\n    .check_toc_js(str![[r#\"\n<ol class=\"chapter\">\n<li class=\"chapter-item expanded \">\n<span class=\"chapter-link-wrapper\">\n<a href=\"index.html\">Intro</a>\n</span>\n</li>\n<li class=\"chapter-item expanded \">\n<span class=\"chapter-link-wrapper\">\n<a href=\"first/index.html\">\n<strong aria-hidden=\"true\">1.</strong> First</a>\n</span>\n</li>\n<li class=\"chapter-item expanded \">\n<span class=\"chapter-link-wrapper\">\n<a href=\"second/index.html\">\n<strong aria-hidden=\"true\">2.</strong> Second</a>\n</span>\n</li>\n</ol>\n\"#]]);\n    assert!(test.dir.join(\"book/index.html\").exists());\n    assert!(!test.dir.join(\"book/README.html\").exists());\n}\n"
  },
  {
    "path": "tests/testsuite/init/init_from_summary/src/SUMMARY.md",
    "content": "# Summary\n\n[intro](intro.md)\n\n- [First chapter](first.md)\n\n[outro](outro.md)\n\n"
  },
  {
    "path": "tests/testsuite/init.rs",
    "content": "//! Tests for `mdbook init`.\n\nuse crate::prelude::*;\nuse mdbook_core::config::Config;\nuse mdbook_driver::MDBook;\nuse std::path::PathBuf;\n\n// Tests \"init\" with no args.\n#[test]\nfn basic_init() {\n    let mut test = BookTest::empty();\n    test.run(\"init\", |cmd| {\n        cmd.expect_stdout(str![[r#\"\n\nDo you want a .gitignore to be created? (y/n)\nWhat title would you like to give the book? \n\nAll done, no errors...\n\n\"#]])\n            .expect_stderr(str![[r#\"\n INFO Creating a new book with stub content\n\n\"#]]);\n    })\n    .check_file(\n        \"book.toml\",\n        str![[r#\"\n[book]\nauthors = []\nlanguage = \"en\"\n\n\"#]],\n    )\n    .check_file(\n        \"src/SUMMARY.md\",\n        str![[r#\"\n# Summary\n\n- [Chapter 1](./chapter_1.md)\n\n\"#]],\n    )\n    .check_file(\n        \"src/chapter_1.md\",\n        str![[r#\"\n# Chapter 1\n\n\"#]],\n    )\n    .check_main_file(\n        \"book/chapter_1.html\",\n        str![[r##\"<h1 id=\"chapter-1\"><a class=\"header\" href=\"#chapter-1\">Chapter 1</a></h1>\"##]],\n    );\n    assert!(!test.dir.join(\".gitignore\").exists());\n    assert!(test.dir.join(\"book\").exists());\n}\n\n// Test init via API. This does a little less than the CLI does.\n#[test]\nfn init_api() {\n    let mut test = BookTest::empty();\n    MDBook::init(&test.dir).build().unwrap();\n    test.check_file_list(\n        \".\",\n        str![[r#\"\nbook\nbook.toml\nsrc\nsrc/SUMMARY.md\nsrc/chapter_1.md\n\"#]],\n    );\n}\n\n// Run `mdbook init` with `--force` to skip the confirmation prompts\n#[test]\nfn init_force() {\n    let mut test = BookTest::empty();\n    test.run(\"init --force\", |cmd| {\n        cmd.expect_stdout(str![[r#\"\n\nAll done, no errors...\n\n\"#]])\n            .expect_stderr(str![[r#\"\n INFO Creating a new book with stub content\n\n\"#]]);\n    })\n    .check_file(\n        \"book.toml\",\n        str![[r#\"\n[book]\nauthors = []\nlanguage = \"en\"\n\n\"#]],\n    );\n    assert!(!test.dir.join(\".gitignore\").exists());\n}\n\n// Run `mdbook init` with `--title` without git config.\n//\n// Regression test for https://github.com/rust-lang/mdBook/issues/2485\n#[test]\nfn no_git_config_with_title() {\n    let mut test = BookTest::empty();\n    test.run(\"init\", |cmd| {\n        cmd.expect_stdout(str![[r#\"\n\nDo you want a .gitignore to be created? (y/n)\n\nAll done, no errors...\n\n\"#]])\n            .expect_stderr(str![[r#\"\n INFO Creating a new book with stub content\n\n\"#]])\n            .args(&[\"--title\", \"Example title\"]);\n    })\n    .check_file(\n        \"book.toml\",\n        str![[r#\"\n[book]\ntitle = \"Example title\"\nauthors = []\nlanguage = \"en\"\n\n\"#]],\n    );\n    assert!(!test.dir.join(\".gitignore\").exists());\n}\n\n// Run `mdbook init` in a directory containing a SUMMARY.md should create the\n// files listed in the summary.\n#[test]\nfn init_from_summary() {\n    BookTest::from_dir(\"init/init_from_summary\")\n        .run(\"init\", |_| {})\n        .check_file(\n            \"src/intro.md\",\n            str![[r#\"\n# intro\n\n\"#]],\n        )\n        .check_file(\n            \"src/first.md\",\n            str![[r#\"\n# First chapter\n\n\"#]],\n        )\n        .check_file(\n            \"src/outro.md\",\n            str![[r#\"\n# outro\n\n\"#]],\n        );\n}\n\n// Set some custom arguments for where to place the source and destination\n// files, then call `mdbook init`.\n#[test]\nfn init_with_custom_book_and_src_locations() {\n    let mut test = BookTest::empty();\n    let mut cfg = Config::default();\n    cfg.book.src = PathBuf::from(\"in\");\n    cfg.build.build_dir = PathBuf::from(\"out\");\n    MDBook::init(&test.dir).with_config(cfg).build().unwrap();\n    test.check_file(\n        \"book.toml\",\n        str![[r#\"\n[book]\nauthors = []\nsrc = \"in\"\nlanguage = \"en\"\n\n[build]\nbuild-dir = \"out\"\ncreate-missing = true\nuse-default-preprocessors = true\nextra-watch-dirs = []\n\n\"#]],\n    )\n    .check_file(\n        \"in/SUMMARY.md\",\n        str![[r#\"\n# Summary\n\n- [Chapter 1](./chapter_1.md)\n\n\"#]],\n    )\n    .check_file(\n        \"in/chapter_1.md\",\n        str![[r#\"\n# Chapter 1\n\n\"#]],\n    );\n    assert!(test.dir.join(\"out\").exists());\n}\n\n// Copies the theme into the initialized directory.\n#[test]\nfn copy_theme() {\n    BookTest::empty()\n        .run(\"init --theme\", |_| {})\n        .check_file_list(\n            \".\",\n            str![[r#\"\nbook\nbook.toml\nsrc\nsrc/SUMMARY.md\nsrc/chapter_1.md\ntheme\ntheme/book.js\ntheme/css\ntheme/css/chrome.css\ntheme/css/general.css\ntheme/css/print.css\ntheme/css/variables.css\ntheme/favicon.png\ntheme/favicon.svg\ntheme/fonts\ntheme/fonts/OPEN-SANS-LICENSE.txt\ntheme/fonts/SOURCE-CODE-PRO-LICENSE.txt\ntheme/fonts/fonts.css\ntheme/fonts/open-sans-v17-all-charsets-300.woff2\ntheme/fonts/open-sans-v17-all-charsets-300italic.woff2\ntheme/fonts/open-sans-v17-all-charsets-600.woff2\ntheme/fonts/open-sans-v17-all-charsets-600italic.woff2\ntheme/fonts/open-sans-v17-all-charsets-700.woff2\ntheme/fonts/open-sans-v17-all-charsets-700italic.woff2\ntheme/fonts/open-sans-v17-all-charsets-800.woff2\ntheme/fonts/open-sans-v17-all-charsets-800italic.woff2\ntheme/fonts/open-sans-v17-all-charsets-italic.woff2\ntheme/fonts/open-sans-v17-all-charsets-regular.woff2\ntheme/fonts/source-code-pro-v11-all-charsets-500.woff2\ntheme/highlight.css\ntheme/highlight.js\ntheme/index.hbs\n\"#]],\n        );\n}\n"
  },
  {
    "path": "tests/testsuite/main.rs",
    "content": "//! Main testsuite for exercising all functionality of mdBook.\n//!\n//! See README.md for documentation.\n\n#![allow(unreachable_pub, reason = \"not needed in an integration test crate\")]\n\nmod book_test;\nmod build;\nmod cli;\nmod config;\nmod includes;\nmod index;\nmod init;\nmod markdown;\nmod playground;\nmod preprocessor;\nmod print;\nmod redirects;\nmod renderer;\nmod rendering;\n#[cfg(feature = \"search\")]\nmod search;\nmod test;\nmod theme;\nmod toc;\n\nmod prelude {\n    pub use crate::book_test::{BookTest, glob_one, read_to_string};\n    pub use snapbox::str;\n}\n"
  },
  {
    "path": "tests/testsuite/markdown/admonitions/book.toml",
    "content": "[book]\ntitle = \"admonitions\"\n"
  },
  {
    "path": "tests/testsuite/markdown/admonitions/expected/admonitions.html",
    "content": "<h1 id=\"admonitions\"><a class=\"header\" href=\"#admonitions\">Admonitions</a></h1>\n<blockquote class=\"blockquote-tag blockquote-tag-note\">\n<p class=\"blockquote-tag-title\"><svg viewbox=\"0 0 16 16\" width=\"18\" height=\"18\"><path d=\"M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8Zm8-6.5a6.5 6.5 0 1 0 0 13 6.5 6.5 0 0 0 0-13ZM6.5 7.75A.75.75 0 0 1 7.25 7h1a.75.75 0 0 1 .75.75v2.75h.25a.75.75 0 0 1 0 1.5h-2a.75.75 0 0 1 0-1.5h.25v-2h-.25a.75.75 0 0 1-.75-.75ZM8 6a1 1 0 1 1 0-2 1 1 0 0 1 0 2Z\"></path></svg>Note</p>\n<p>This is a note.</p>\n<p>There are multiple paragraphs.</p>\n</blockquote>\n<blockquote class=\"blockquote-tag blockquote-tag-tip\">\n<p class=\"blockquote-tag-title\"><svg viewbox=\"0 0 16 16\" width=\"18\" height=\"18\"><path d=\"M8 1.5c-2.363 0-4 1.69-4 3.75 0 .984.424 1.625.984 2.304l.214.253c.223.264.47.556.673.848.284.411.537.896.621 1.49a.75.75 0 0 1-1.484.211c-.04-.282-.163-.547-.37-.847a8.456 8.456 0 0 0-.542-.68c-.084-.1-.173-.205-.268-.32C3.201 7.75 2.5 6.766 2.5 5.25 2.5 2.31 4.863 0 8 0s5.5 2.31 5.5 5.25c0 1.516-.701 2.5-1.328 3.259-.095.115-.184.22-.268.319-.207.245-.383.453-.541.681-.208.3-.33.565-.37.847a.751.751 0 0 1-1.485-.212c.084-.593.337-1.078.621-1.489.203-.292.45-.584.673-.848.075-.088.147-.173.213-.253.561-.679.985-1.32.985-2.304 0-2.06-1.637-3.75-4-3.75ZM5.75 12h4.5a.75.75 0 0 1 0 1.5h-4.5a.75.75 0 0 1 0-1.5ZM6 15.25a.75.75 0 0 1 .75-.75h2.5a.75.75 0 0 1 0 1.5h-2.5a.75.75 0 0 1-.75-.75Z\"></path></svg>Tip</p>\n<p>This is a tip.</p>\n</blockquote>\n<blockquote class=\"blockquote-tag blockquote-tag-important\">\n<p class=\"blockquote-tag-title\"><svg viewbox=\"0 0 16 16\" width=\"18\" height=\"18\"><path d=\"M0 1.75C0 .784.784 0 1.75 0h12.5C15.216 0 16 .784 16 1.75v9.5A1.75 1.75 0 0 1 14.25 13H8.06l-2.573 2.573A1.458 1.458 0 0 1 3 14.543V13H1.75A1.75 1.75 0 0 1 0 11.25Zm1.75-.25a.25.25 0 0 0-.25.25v9.5c0 .138.112.25.25.25h2a.75.75 0 0 1 .75.75v2.19l2.72-2.72a.749.749 0 0 1 .53-.22h6.5a.25.25 0 0 0 .25-.25v-9.5a.25.25 0 0 0-.25-.25Zm7 2.25v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 9a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\"></path></svg>Important</p>\n<p>This is important.</p>\n</blockquote>\n<blockquote class=\"blockquote-tag blockquote-tag-warning\">\n<p class=\"blockquote-tag-title\"><svg viewbox=\"0 0 16 16\" width=\"18\" height=\"18\"><path d=\"M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\"></path></svg>Warning</p>\n<p>This is a warning.</p>\n</blockquote>\n<blockquote class=\"blockquote-tag blockquote-tag-caution\">\n<p class=\"blockquote-tag-title\"><svg viewbox=\"0 0 16 16\" width=\"18\" height=\"18\"><path d=\"M4.47.22A.749.749 0 0 1 5 0h6c.199 0 .389.079.53.22l4.25 4.25c.141.14.22.331.22.53v6a.749.749 0 0 1-.22.53l-4.25 4.25A.749.749 0 0 1 11 16H5a.749.749 0 0 1-.53-.22L.22 11.53A.749.749 0 0 1 0 11V5c0-.199.079-.389.22-.53Zm.84 1.28L1.5 5.31v5.38l3.81 3.81h5.38l3.81-3.81V5.31L10.69 1.5ZM8 4a.75.75 0 0 1 .75.75v3.5a.75.75 0 0 1-1.5 0v-3.5A.75.75 0 0 1 8 4Zm0 8a1 1 0 1 1 0-2 1 1 0 0 1 0 2Z\"></path></svg>Caution</p>\n<p>This is a caution.</p>\n</blockquote>\n<blockquote>\n<p>[!UNKNOWN]\nThis is an unknown tag.</p>\n</blockquote>\n<blockquote class=\"blockquote-tag blockquote-tag-important\">\n<p class=\"blockquote-tag-title\"><svg viewbox=\"0 0 16 16\" width=\"18\" height=\"18\"><path d=\"M0 1.75C0 .784.784 0 1.75 0h12.5C15.216 0 16 .784 16 1.75v9.5A1.75 1.75 0 0 1 14.25 13H8.06l-2.573 2.573A1.458 1.458 0 0 1 3 14.543V13H1.75A1.75 1.75 0 0 1 0 11.25Zm1.75-.25a.25.25 0 0 0-.25.25v9.5c0 .138.112.25.25.25h2a.75.75 0 0 1 .75.75v2.19l2.72-2.72a.749.749 0 0 1 .53-.22h6.5a.25.25 0 0 0 .25-.25v-9.5a.25.25 0 0 0-.25-.25Zm7 2.25v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 9a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\"></path></svg>Important</p>\n<p>This is an important admonition.</p>\n<blockquote class=\"blockquote-tag blockquote-tag-note\">\n<p class=\"blockquote-tag-title\"><svg viewbox=\"0 0 16 16\" width=\"18\" height=\"18\"><path d=\"M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8Zm8-6.5a6.5 6.5 0 1 0 0 13 6.5 6.5 0 0 0 0-13ZM6.5 7.75A.75.75 0 0 1 7.25 7h1a.75.75 0 0 1 .75.75v2.75h.25a.75.75 0 0 1 0 1.5h-2a.75.75 0 0 1 0-1.5h.25v-2h-.25a.75.75 0 0 1-.75-.75ZM8 6a1 1 0 1 1 0-2 1 1 0 0 1 0 2Z\"></path></svg>Note</p>\n<p>This nested note should have its own color.</p>\n</blockquote>\n</blockquote>"
  },
  {
    "path": "tests/testsuite/markdown/admonitions/expected_disabled/admonitions.html",
    "content": "<h1 id=\"admonitions\"><a class=\"header\" href=\"#admonitions\">Admonitions</a></h1>\n<blockquote>\n<p>[!NOTE]\nThis is a note.</p>\n<p>There are multiple paragraphs.</p>\n</blockquote>\n<blockquote>\n<p>[!TIP]\nThis is a tip.</p>\n</blockquote>\n<blockquote>\n<p>[!IMPORTANT]\nThis is important.</p>\n</blockquote>\n<blockquote>\n<p>[!WARNING]\nThis is a warning.</p>\n</blockquote>\n<blockquote>\n<p>[!CAUTION]\nThis is a caution.</p>\n</blockquote>\n<blockquote>\n<p>[!UNKNOWN]\nThis is an unknown tag.</p>\n</blockquote>\n<blockquote>\n<p>[!IMPORTANT]\nThis is an important admonition.</p>\n<blockquote>\n<p>[!NOTE]\nThis nested note should have its own color.</p>\n</blockquote>\n</blockquote>"
  },
  {
    "path": "tests/testsuite/markdown/admonitions/src/SUMMARY.md",
    "content": "# Summary\n\n- [Admonitions](./admonitions.md)\n"
  },
  {
    "path": "tests/testsuite/markdown/admonitions/src/admonitions.md",
    "content": "# Admonitions\n\n> [!NOTE]\n> This is a note.\n>\n> There are multiple paragraphs.\n\n> [!TIP]\n> This is a tip.\n\n> [!IMPORTANT]\n> This is important.\n\n> [!WARNING]\n> This is a warning.\n\n> [!CAUTION]\n> This is a caution.\n\n> [!UNKNOWN]\n> This is an unknown tag.\n\n> [!IMPORTANT]\n> This is an important admonition.\n>\n> > [!NOTE]\n> > This nested note should have its own color.\n"
  },
  {
    "path": "tests/testsuite/markdown/basic_markdown/book.toml",
    "content": "[book]\ntitle = \"basic_markdown\"\n"
  },
  {
    "path": "tests/testsuite/markdown/basic_markdown/expected/blank.html",
    "content": ""
  },
  {
    "path": "tests/testsuite/markdown/basic_markdown/expected/blockquotes.html",
    "content": "<h1 id=\"blockquotes\"><a class=\"header\" href=\"#blockquotes\">Blockquotes</a></h1>\n<p>Empty:</p>\n<blockquote></blockquote>\n<p>Normal:</p>\n<blockquote>\n<p>foo\nbar</p>\n</blockquote>\n<p>Contains code block:</p>\n<blockquote>\n<pre class=\"playground\"><code class=\"language-rust\"><span class=\"boring\">#![allow(unused)]\n</span><span class=\"boring\">fn main() {\n</span>let x = 1;\n<span class=\"boring\">}</span></code></pre>\n</blockquote>\n<p>Random stuff:</p>\n<blockquote>\n<h3 id=\"and-now\"><a class=\"header\" href=\"#and-now\">And now,</a></h3>\n<p><strong>Let us <em>introduce</em></strong>\nAll kinds of</p>\n<ul>\n<li>tags</li>\n<li>etc</li>\n<li>stuff</li>\n</ul>\n<ol>\n<li>\n<p>In</p>\n</li>\n<li>\n<p>The</p>\n</li>\n<li>\n<p>blockquote</p>\n<blockquote>\n<p>cause we can</p>\n<blockquote>\n<p>Cause we can</p>\n</blockquote>\n</blockquote>\n</li>\n</ol>\n</blockquote>"
  },
  {
    "path": "tests/testsuite/markdown/basic_markdown/expected/code-blocks.html",
    "content": "<h1 id=\"code-blocks\"><a class=\"header\" href=\"#code-blocks\">Code blocks</a></h1>\n<pre><code>This is a codeblock\n</code></pre>\n<pre class=\"playground\"><code class=\"language-rust\"><span class=\"boring\">#![allow(unused)]\n</span><span class=\"boring\">fn main() {\n</span>// This links to a playpen\n<span class=\"boring\">}</span></code></pre>\n<pre><code class=\"language-bash editable\"># This is an editable codeblock\n</code></pre>\n<pre><code class=\"language-text cls1 cls2 cls3\">Text with different classes.\n</code></pre>\n<pre><code>Indented\ncode\nblock.\n</code></pre>\n<pre class=\"playground\"><code class=\"language-rust edition2021\"><span class=\"boring\">#![allow(unused)]\n</span><span class=\"boring\">fn main() {\n</span>let x = 1;\n<span class=\"boring\">}</span></code></pre>\n<pre class=\"playground\"><code class=\"language-rust\">fn main() {\n    println!(\"hello\");\n}</code></pre>"
  },
  {
    "path": "tests/testsuite/markdown/basic_markdown/expected/html.html",
    "content": "<h1 id=\"inline-html\"><a class=\"header\" href=\"#inline-html\">Inline HTML</a></h1>\n<h2 id=\"comments\"><a class=\"header\" href=\"#comments\">Comments</a></h2>\n<!--comment-->\n<!--\n\n**bigger**\n\ncomment\n\n-->\n<h2 id=\"void-elements\"><a class=\"header\" href=\"#void-elements\">Void elements</a></h2>\n<map name=\"my-map\">\n  <area shape=\"rect\" coords=\"0,0,10,20\" href=\"https://example.com/\" alt=\"alt text\">\n</map>\n<p>Line<br>break</p>\n<p>Word<wbr>break</p>\n<table>\n  <colgroup>\n    <col>\n    <col span=\"2\" class=\"a\">\n  </colgroup>\n</table>\n\n<p><embed type=\"image/jpeg\" src=\"/image.jpg\" width=\"100\" height=\"200\"></p>\n<p>Rule:</p>\n<hr>\n<img src=\"example.jpg\">\n<input type=\"text\">\n<link href=\"example.css\" rel=\"stylesheet\">\n<p><meta name=\"example\" content=\"Example content\"></p>\n<video>\n  <source src=\"video.webm\" type=\"video/webm\">\n  <track kind=\"captions\" src=\"captions.vtt\" srclang=\"en\">\n</video>\n<h2 id=\"blocks\"><a class=\"header\" href=\"#blocks\">Blocks</a></h2>\n<div>\nA block HTML element trying to do *markup*.\n</div>\n\n<div>\n<p>A block HTML with spaces that <strong>cause</strong> it to be interleaved with markdown.</p>\n</div>\n\n<h2 id=\"scripts\"><a class=\"header\" href=\"#scripts\">Scripts</a></h2>\n<script></script>\n<script async=\"\" src=\"foo.js\"></script>\n<script>\nconst x = 'some *text* inside';\n\n*don't* < try to \"parse me\n</script>\n<h2 id=\"style\"><a class=\"header\" href=\"#style\">Style</a></h2>\n<style>\n\n.foo {\n    background-color: red;\n}\n\n/* **don't** < try to \"parse me\n*/\n\n</style>\n<style media=\"(width &lt; 500px)\">\n.bar { background-color: green }\n</style>\n<h2 id=\"manual-headers\"><a class=\"header\" href=\"#manual-headers\">Manual headers</a></h2>\n<h2 id=\"my header\"><a href=\"#foo\">My Header</a></h2>\n\n<h3>Another header</h3>"
  },
  {
    "path": "tests/testsuite/markdown/basic_markdown/expected/images.html",
    "content": "<h1 id=\"images\"><a class=\"header\" href=\"#images\">Images</a></h1>\n<p><img src=\"https://rust-lang.org/logos/rust-logo-256x256.png\" alt=\"Image “alt” &amp; &quot; &quot;text&quot; &amp; &lt;stuff&gt; url &lt;em&gt;html&lt;/em&gt; — hard break \"></p>\n<p><img src=\"https://rust-lang.org/logos/rust-logo-256x256.png\" title=\"Some title\" alt=\"Image with title\"></p>"
  },
  {
    "path": "tests/testsuite/markdown/basic_markdown/expected/inlines.html",
    "content": "<h1 id=\"inlines\"><a class=\"header\" href=\"#inlines\">Inlines</a></h1>\n<p><em>emphasis</em> <strong>bold</strong> <strong><em>bold emphasis</em></strong></p>\n<p>Some <code>inline code</code>.</p>\n<p>Hard<br>break</p>\n<p>Invisible hard<br>break</p>\n<p>[escaped] &lt;html&gt; *here*</p>"
  },
  {
    "path": "tests/testsuite/markdown/basic_markdown/expected/links.html",
    "content": "<h1 id=\"links\"><a class=\"header\" href=\"#links\">Links</a></h1>\n<ul>\n<li><a href=\"https://example.com/inline\">Inline</a></li>\n<li><a href=\"https://example.com/collapsed\">Collapsed</a></li>\n<li>[Emtpy reference][]</li>\n<li><a href=\"https://example.com/specific\">Specific reference</a></li>\n<li><a href=\"https://example.com/\">https://example.com/</a></li>\n<li><a href=\"mailto:john@example.com\">john@example.com</a></li>\n<li><a href=\"https://example.com/title\" title=\"with title\">Titled</a></li>\n<li>[Broken collapsed]</li>\n<li>[Broken reference][missing]</li>\n<li><a href=\"path/foo.html\">Markdown link</a></li>\n<li><a href=\"path/foo.html#anchor\">Markdown link anchor</a></li>\n<li><a href=\"#anchor\">Link anchor</a></li>\n<li><a href=\"path.html#phantomdata\">With md in anchor</a></li>\n</ul>"
  },
  {
    "path": "tests/testsuite/markdown/basic_markdown/expected/lists.html",
    "content": "<h1 id=\"lists\"><a class=\"header\" href=\"#lists\">Lists</a></h1>\n<ol>\n<li>A</li>\n<li>Normal</li>\n<li>Ordered</li>\n<li>List</li>\n</ol>\n<hr>\n<ol>\n<li>A\n<ol>\n<li>Nested</li>\n<li>List</li>\n</ol>\n</li>\n<li>But</li>\n<li>Still</li>\n<li>Normal</li>\n</ol>\n<hr>\n<ol start=\"7\">\n<li>Start list</li>\n<li>with a different number.</li>\n</ol>\n<hr>\n<ul>\n<li>An</li>\n<li>Unordered</li>\n<li>Normal</li>\n<li>List</li>\n</ul>\n<hr>\n<ul>\n<li>Nested\n<ul>\n<li>Unordered</li>\n</ul>\n</li>\n<li>List</li>\n</ul>\n<hr>\n<ul>\n<li>This\n<ol>\n<li>Is</li>\n<li>Normal</li>\n</ol>\n</li>\n<li>?!</li>\n</ul>"
  },
  {
    "path": "tests/testsuite/markdown/basic_markdown/expected/svg.html",
    "content": "<h1 id=\"svg\"><a class=\"header\" href=\"#svg\">SVG</a></h1>\n<svg version=\"1.1\" width=\"300\" height=\"200\" xmlns=\"http://www.w3.org/2000/svg\">\n<style>\n    rect {\n        stroke: green;\n        stroke-width: 10px;\n    }\n</style>\n<rect width=\"100%\" height=\"100%\" fill=\"red\" />\n<circle cx=\"150\" cy=\"100\" r=\"80\" fill=\"green\" />\n<text x=\"150\" y=\"125\" font-size=\"60\" text-anchor=\"middle\" fill=\"white\">SVG</text>\n</svg>"
  },
  {
    "path": "tests/testsuite/markdown/basic_markdown/src/SUMMARY.md",
    "content": "# Summary\n\n- [Blank](./blank.md)\n- [Blockquotes](./blockquotes.md)\n- [Code blocks](./code-blocks.md)\n- [Lists](./lists.md)\n- [Inlines](./inlines.md)\n- [Links](./links.md)\n- [Images](./images.md)\n- [HTML](./html.md)\n- [SVG](./svg.md)\n"
  },
  {
    "path": "tests/testsuite/markdown/basic_markdown/src/blank.md",
    "content": ""
  },
  {
    "path": "tests/testsuite/markdown/basic_markdown/src/blockquotes.md",
    "content": "# Blockquotes\n\nEmpty:\n>\n\nNormal:\n> foo\n> bar\n\nContains code block:\n> ```rust\n> let x = 1;\n> ```\n\nRandom stuff:\n> ### And now,\n>\n> **Let us _introduce_**\n> All kinds of\n>\n> - tags\n> - etc\n> - stuff\n>\n> 1. In\n> 2. The\n> 3. blockquote\n>\n>    > cause we can\n>    >\n>    > > Cause we can\n"
  },
  {
    "path": "tests/testsuite/markdown/basic_markdown/src/code-blocks.md",
    "content": "# Code blocks\n\n```\nThis is a codeblock\n```\n\n```rust\n// This links to a playpen\n```\n\n```bash,editable\n# This is an editable codeblock\n```\n\n```text cls1,,cls2\tcls3\nText with different classes.\n```\n\n    Indented\n    code\n    block.\n\n```rust,edition2021\nlet x = 1;\n```\n\n```rust\nfn main() {\n    println!(\"hello\");\n}\n```\n"
  },
  {
    "path": "tests/testsuite/markdown/basic_markdown/src/html.md",
    "content": "# Inline HTML\n\n## Comments\n\n<!--comment-->\n\n<!--\n\n**bigger**\n\ncomment\n\n-->\n\n## Void elements\n\n<map name=\"my-map\">\n  <area shape=\"rect\" coords=\"0,0,10,20\" href=\"https://example.com/\" alt=\"alt text\">\n</map>\n\nLine<br>break\n\nWord<wbr>break\n\n<table>\n  <colgroup>\n    <col>\n    <col span=\"2\" class=\"a\">\n  </colgroup>\n</table>\n\n<embed\n  type=\"image/jpeg\"\n  src=\"/image.jpg\"\n  width=\"100\"\n  height=\"200\">\n\nRule:\n<hr>\n\n<img src=\"example.jpg\">\n\n<input type=\"text\">\n\n<link href=\"example.css\" rel=\"stylesheet\">\n\n<meta name=\"example\"\n  content=\"Example content\">\n\n<video>\n  <source src=\"video.webm\" type=\"video/webm\">\n  <track kind=\"captions\" src=\"captions.vtt\" srclang=\"en\">\n</video>\n\n## Blocks\n\n<div>\nA block HTML element trying to do *markup*.\n</div>\n\n<div>\n\nA block HTML with spaces that **cause** it to be interleaved with markdown.\n\n</div>\n\n## Scripts\n\n<script></script>\n\n<script async src=\"foo.js\"></script>\n\n<script>\nconst x = 'some *text* inside';\n\n*don't* < try to \"parse me\n</script>\n\n## Style\n\n<style>\n\n.foo {\n    background-color: red;\n}\n\n/* **don't** < try to \"parse me\n*/\n\n</style>\n\n<style media=\"(width < 500px)\">\n.bar { background-color: green }\n</style>\n\n## Manual headers\n\n<h2 id=\"my header\"><a href=\"#foo\">My Header</a></h2>\n\n<h3>Another header</h3>\n"
  },
  {
    "path": "tests/testsuite/markdown/basic_markdown/src/images.md",
    "content": "# Images\n\n![Image *\"alt\"* & \\\"\n    `\"text\" & <stuff>`\n    [url](/foo)\n    <em>html</em>\n    ---\n    hard\\\n    break\n](https://rust-lang.org/logos/rust-logo-256x256.png)\n\n![Image with title](https://rust-lang.org/logos/rust-logo-256x256.png \"Some title\")\n"
  },
  {
    "path": "tests/testsuite/markdown/basic_markdown/src/inlines.md",
    "content": "# Inlines\n\n*emphasis* **bold** **_bold emphasis_**\n\nSome `inline code`.\n\nHard\\\nbreak\n\nInvisible hard  \nbreak\n\n\\[escaped] \\<html> \\*here*\n"
  },
  {
    "path": "tests/testsuite/markdown/basic_markdown/src/links.md",
    "content": "# Links\n\n- [Inline](https://example.com/inline)\n- [Collapsed]\n- [Emtpy reference][]\n- [Specific reference][specific]\n- <https://example.com/>\n- <john@example.com>\n- [Titled](https://example.com/title \"with title\")\n- [Broken collapsed]\n- [Broken reference][missing]\n- [Markdown link](path/foo.md)\n- [Markdown link anchor](path/foo.md#anchor)\n- [Link anchor](#anchor)\n- [With md in anchor](path.html#phantomdata)\n\n[collapsed]: https://example.com/collapsed\n[specific]: https://example.com/specific\n"
  },
  {
    "path": "tests/testsuite/markdown/basic_markdown/src/lists.md",
    "content": "# Lists\n\n1. A\n2. Normal\n3. Ordered\n4. List\n\n---\n\n1. A\n   1. Nested\n   2. List\n2. But\n3. Still\n4. Normal\n\n---\n\n7. Start list\n7. with a different number.\n\n---\n\n- An\n- Unordered\n- Normal\n- List\n\n---\n\n- Nested\n  - Unordered\n- List\n\n---\n\n- This\n  1. Is\n  2. Normal\n- ?!\n"
  },
  {
    "path": "tests/testsuite/markdown/basic_markdown/src/svg.md",
    "content": "# SVG\n\n<svg version=\"1.1\" width=\"300\" height=\"200\" xmlns=\"http://www.w3.org/2000/svg\">\n<style>\n    rect {\n        stroke: green;\n        stroke-width: 10px;\n    }\n</style>\n<rect width=\"100%\" height=\"100%\" fill=\"red\" />\n<circle cx=\"150\" cy=\"100\" r=\"80\" fill=\"green\" />\n<text x=\"150\" y=\"125\" font-size=\"60\" text-anchor=\"middle\" fill=\"white\">SVG</text>\n</svg>\n\n"
  },
  {
    "path": "tests/testsuite/markdown/custom_header_attributes/book.toml",
    "content": "[book]\ntitle = \"custom_header_attributes\"\n"
  },
  {
    "path": "tests/testsuite/markdown/custom_header_attributes/src/SUMMARY.md",
    "content": "# Summary\n\n- [Heading Attributes](./custom_header_attributes.md)\n"
  },
  {
    "path": "tests/testsuite/markdown/custom_header_attributes/src/custom_header_attributes.md",
    "content": "# Heading Attributes {#attrs}\n\n## Heading with classes {.class1 .class2}\n\n## Heading with id and classes {#both .class1 .class2}\n\n## Heading with attribute {.myclass1 myattr #myh3 otherattr=value .myclass2}\n"
  },
  {
    "path": "tests/testsuite/markdown/definition_lists/book.toml",
    "content": "[book]\ntitle = \"definition_lists\"\n"
  },
  {
    "path": "tests/testsuite/markdown/definition_lists/expected/definition_lists.html",
    "content": "<h1 id=\"definition-lists\"><a class=\"header\" href=\"#definition-lists\">Definition Lists</a></h1>\n<dl>\n<dt id=\"apple\"><a class=\"header\" href=\"#apple\">apple</a></dt>\n<dd>red fruit</dd>\n<dt id=\"orange\"><a class=\"header\" href=\"#orange\">orange</a></dt>\n<dd>orange fruit</dd>\n<dt id=\"apple-1\"><a class=\"header\" href=\"#apple-1\">apple</a></dt>\n<dd>red fruit</dd>\n<dd>computer company</dd>\n<dt id=\"orange-1\"><a class=\"header\" href=\"#orange-1\">orange</a></dt>\n<dd>orange fruit</dd>\n<dd>telecom company</dd>\n<dt id=\"term\"><a class=\"header\" href=\"#term\">term</a></dt>\n<dd>\n<ol>\n<li>\n<p>Para one</p>\n<p>Para two</p>\n</li>\n</ol>\n</dd>\n</dl>\n<h2 id=\"term-with-link\"><a class=\"header\" href=\"#term-with-link\">Term with link</a></h2>\n<dl>\n<dt id=\"apple-2\"><a class=\"header\" href=\"#apple-2\"><a href=\"some-page.html#apple\">apple</a></a></dt>\n<dd>red fruit</dd>\n</dl>\n<h2 id=\"multi-line-term\"><a class=\"header\" href=\"#multi-line-term\">Multi-line term</a></h2>\n<dl>\n<dt id=\"a-bc\"><a class=\"header\" href=\"#a-bc\">a\nb<br>c</a></dt>\n<dd>\n<p>foo</p>\n</dd>\n</dl>\n<h2 id=\"nested\"><a class=\"header\" href=\"#nested\">Nested</a></h2>\n<dl>\n<dt id=\"level-one\"><a class=\"header\" href=\"#level-one\">level one</a></dt>\n<dd>\n<dl>\n<dt id=\"l1-level-two\"><a class=\"header\" href=\"#l1-level-two\">l1\nlevel two</a></dt>\n<dd>\n<dl>\n<dt id=\"l2-level-three\"><a class=\"header\" href=\"#l2-level-three\">l2\nlevel three</a></dt>\n<dd>l3</dd>\n</dl>\n</dd>\n</dl>\n</dd>\n<dt id=\"level-one-1\"><a class=\"header\" href=\"#level-one-1\">level one</a></dt>\n<dd>l1</dd>\n</dl>\n<h2 id=\"loose\"><a class=\"header\" href=\"#loose\">Loose</a></h2>\n<dl>\n<dt id=\"apple-3\"><a class=\"header\" href=\"#apple-3\">apple</a></dt>\n<dd>\n<p>red fruit</p>\n</dd>\n<dd>\n<p>computer company</p>\n</dd>\n<dt id=\"orange-2\"><a class=\"header\" href=\"#orange-2\">orange</a></dt>\n<dd>\n<p>orange fruit</p>\n</dd>\n</dl>"
  },
  {
    "path": "tests/testsuite/markdown/definition_lists/expected/html_definition_lists.html",
    "content": "<h1 id=\"html-definition-lists\"><a class=\"header\" href=\"#html-definition-lists\">HTML definition lists</a></h1>\n<p>Test for definition lists manually written in HTML.</p>\n<dl>\n    \n<dt>Some tag</dt>\n\n    \n<dd>Some defintion</dd>\n\n    \n<dt class=\"myclass\" id=\"myid\"><a class=\"option-anchor\" href=\"#foo\">Another definition</a></dt>\n\n    \n<dd class=\"def-class\">A definition.</dd>\n\n</dl>"
  },
  {
    "path": "tests/testsuite/markdown/definition_lists/expected_disabled/definition_lists.html",
    "content": "<h1 id=\"definition-lists\"><a class=\"header\" href=\"#definition-lists\">Definition Lists</a></h1>\n<p>apple\n:   red fruit</p>\n<p>orange\n:   orange fruit</p>\n<p>apple\n:   red fruit\n:   computer company</p>\n<p>orange\n:   orange fruit\n:   telecom company</p>\n<p>term\n:   1. Para one</p>\n<pre><code>   Para two\n</code></pre>\n<h2 id=\"term-with-link\"><a class=\"header\" href=\"#term-with-link\">Term with link</a></h2>\n<p><a href=\"some-page.html#apple\">apple</a>\n: red fruit</p>\n<h2 id=\"multi-line-term\"><a class=\"header\" href=\"#multi-line-term\">Multi-line term</a></h2>\n<p>a\nb<br>c</p>\n<p>:   foo</p>\n<h2 id=\"nested\"><a class=\"header\" href=\"#nested\">Nested</a></h2>\n<p>level one\n: l1\nlevel two\n: l2\nlevel three\n: l3</p>\n<p>level one\n: l1</p>\n<h2 id=\"loose\"><a class=\"header\" href=\"#loose\">Loose</a></h2>\n<p>apple</p>\n<p>:   red fruit\n:   computer company</p>\n<p>orange</p>\n<p>:   orange fruit</p>"
  },
  {
    "path": "tests/testsuite/markdown/definition_lists/expected_disabled/html_definition_lists.html",
    "content": "<h1 id=\"html-definition-lists\"><a class=\"header\" href=\"#html-definition-lists\">HTML definition lists</a></h1>\n<p>Test for definition lists manually written in HTML.</p>\n<dl>\n    \n<dt>Some tag</dt>\n\n    \n<dd>Some defintion</dd>\n\n    \n<dt class=\"myclass\" id=\"myid\"><a class=\"option-anchor\" href=\"#foo\">Another definition</a></dt>\n\n    \n<dd class=\"def-class\">A definition.</dd>\n\n</dl>"
  },
  {
    "path": "tests/testsuite/markdown/definition_lists/src/SUMMARY.md",
    "content": "# Summary\n\n- [Definition lists](./definition_lists.md)\n- [HTML definition lists](./html_definition_lists.md)\n"
  },
  {
    "path": "tests/testsuite/markdown/definition_lists/src/definition_lists.md",
    "content": "# Definition Lists\n\napple\n:   red fruit\n\norange\n:   orange fruit\n\napple\n:   red fruit\n:   computer company\n\norange\n:   orange fruit\n:   telecom company\n\nterm\n:   1. Para one\n\n       Para two\n\n## Term with link\n\n[apple](some-page.md#apple)\n  : red fruit\n\n## Multi-line term\n\na\nb\\\nc\n\n:   foo\n\n## Nested\n\nlevel one\n: l1\n    level two\n    : l2\n        level three\n        : l3\n\nlevel one\n: l1\n\n## Loose\n\napple\n\n:   red fruit\n:   computer company\n\norange\n\n:   orange fruit\n"
  },
  {
    "path": "tests/testsuite/markdown/definition_lists/src/html_definition_lists.md",
    "content": "# HTML definition lists\n\nTest for definition lists manually written in HTML.\n\n<dl>\n    <dt>Some tag</dt>\n    <dd>Some defintion</dd>\n    <dt class=\"myclass\" id=\"myid\"><a class=\"option-anchor\" href=\"#foo\">Another definition</a></dt>\n    <dd class=\"def-class\">A definition.</dd>\n</dl>\n"
  },
  {
    "path": "tests/testsuite/markdown/footnotes/expected/footnotes.html",
    "content": "<h1 id=\"footnote-tests\"><a class=\"header\" href=\"#footnote-tests\">Footnote tests</a></h1>\n<p>Footnote example<sup class=\"footnote-reference\" id=\"fr-1-1\"><a href=\"#footnote-1\">1</a></sup>, or with a word<sup class=\"footnote-reference\" id=\"fr-word-1\"><a href=\"#footnote-word\">2</a></sup>.</p>\n<p>There are multiple references to word<sup class=\"footnote-reference\" id=\"fr-word-2\"><a href=\"#footnote-word\">2</a></sup>.</p>\n<p>Footnote without a paragraph<sup class=\"footnote-reference\" id=\"fr-para-1\"><a href=\"#footnote-para\">3</a></sup></p>\n<p>Footnote with multiple paragraphs<sup class=\"footnote-reference\" id=\"fr-multiple-1\"><a href=\"#footnote-multiple\">4</a></sup></p>\n<p>Footnote name with wacky characters<sup class=\"footnote-reference\" id=\"fr-&quot;wacky&quot;-1\"><a href=\"#footnote-&quot;wacky&quot;\">5</a></sup></p>\n<p>Testing when referring to something earlier.<sup class=\"footnote-reference\" id=\"fr-define-before-use-1\"><a href=\"#footnote-define-before-use\">6</a></sup></p>\n<p>Footnote that is defined multiple times.<sup class=\"footnote-reference\" id=\"fr-multiple-definitions-1\"><a href=\"#footnote-multiple-definitions\">7</a></sup></p>\n<p>And another<sup class=\"footnote-reference\" id=\"fr-in-between-1\"><a href=\"#footnote-in-between\">8</a></sup> that references the duplicate again.<sup class=\"footnote-reference\" id=\"fr-multiple-definitions-2\"><a href=\"#footnote-multiple-definitions\">7</a></sup></p>\n<p>Multiple footnotes in a row.<sup class=\"footnote-reference\" id=\"fr-a-1\"><a href=\"#footnote-a\">9</a></sup> <sup class=\"footnote-reference\" id=\"fr-b-1\"><a href=\"#footnote-b\">10</a></sup> <sup class=\"footnote-reference\" id=\"fr-c-1\"><a href=\"#footnote-c\">11</a></sup></p>\n<hr>\n<ol class=\"footnote-definition\">\n<li id=\"footnote-1\">\n<p>This is a footnote. <a href=\"#fr-1-1\">↩</a> <a href=\"#fr-1-2\">↩2</a></p>\n</li>\n<li id=\"footnote-word\">\n<p>A longer footnote.\nWith multiple lines. <a href=\"other.html\">Link to other</a>.\nWith a reference inside.<sup class=\"footnote-reference\" id=\"fr-1-2\"><a href=\"#footnote-1\">1</a></sup> <a href=\"#fr-word-1\">↩</a> <a href=\"#fr-word-2\">↩2</a></p>\n</li>\n<li id=\"footnote-para\">\n<ol>\n<li>Item one\n<ol>\n<li>Sub-item</li>\n</ol>\n</li>\n<li>Item two</li>\n</ol>\n <a href=\"#fr-para-1\">↩</a></li>\n<li id=\"footnote-multiple\">\n<p>One</p>\n<p>Two</p>\n<p>Three <a href=\"#fr-multiple-1\">↩</a></p>\n</li>\n<li id=\"footnote-&quot;wacky&quot;\">\n<p>Testing footnote id with special characters. <a href=\"#fr-&quot;wacky&quot;-1\">↩</a></p>\n</li>\n<li id=\"footnote-define-before-use\">\n<p>This is defined before it is referred to. <a href=\"#fr-define-before-use-1\">↩</a></p>\n</li>\n<li id=\"footnote-multiple-definitions\">\n<p>This is the first definition of the footnote with tag multiple-definitions <a href=\"#fr-multiple-definitions-1\">↩</a> <a href=\"#fr-multiple-definitions-2\">↩2</a></p>\n</li>\n<li id=\"footnote-in-between\">\n<p>Footnote between duplicates. <a href=\"#fr-in-between-1\">↩</a></p>\n</li>\n<li id=\"footnote-a\">\n<p>Footnote 1 <a href=\"#fr-a-1\">↩</a></p>\n</li>\n<li id=\"footnote-b\">\n<p>Footnote 2 <a href=\"#fr-b-1\">↩</a></p>\n</li>\n<li id=\"footnote-c\">\n<p>Footnote 3 <a href=\"#fr-c-1\">↩</a></p>\n</li>\n</ol>"
  },
  {
    "path": "tests/testsuite/markdown/footnotes/src/SUMMARY.md",
    "content": "- [Footnotes](footnotes.md)\n"
  },
  {
    "path": "tests/testsuite/markdown/footnotes/src/footnotes.md",
    "content": "# Footnote tests\n\nFootnote example[^1], or with a word[^word].\n\n[^1]: This is a footnote.\n\n[^word]: A longer footnote.\n    With multiple lines. [Link to other](other.md).\n    With a reference inside.[^1]\n\nThere are multiple references to word[^word].\n\nFootnote without a paragraph[^para]\n\n[^para]:\n    1. Item one\n       1. Sub-item\n    2. Item two\n\nFootnote with multiple paragraphs[^multiple]\n\n[^define-before-use]: This is defined before it is referred to.\n\n[^multiple]:\n    One\n\n    Two\n\n    Three\n\n[^unused]: This footnote is defined by not used.\n\nFootnote name with wacky characters[^\"wacky\"]\n\n[^\"wacky\"]: Testing footnote id with special characters.\n\nTesting when referring to something earlier.[^define-before-use]\n\nFootnote that is defined multiple times.[^multiple-definitions]\n\n[^multiple-definitions]: This is the first definition of the footnote with tag multiple-definitions\n\nAnd another[^in-between] that references the duplicate again.[^multiple-definitions]\n\n[^in-between]: Footnote between duplicates.\n\n[^multiple-definitions]: This is the second definition of the footnote with tag multiple-definitions\n\nMultiple footnotes in a row.[^a][^b][^c]\n\n[^a]: Footnote 1\n[^b]: Footnote 2\n[^c]: Footnote 3\n"
  },
  {
    "path": "tests/testsuite/markdown/smart_punctuation/src/SUMMARY.md",
    "content": "- [Smart Punctuation](smart_punctuation.md)\n"
  },
  {
    "path": "tests/testsuite/markdown/smart_punctuation/src/smart_punctuation.md",
    "content": "# Smart Punctuation\n\n- En dash: --\n- Em dash: ---\n- Ellipsis: ...\n- Double quote: \"quote\"\n- Single quote: 'quote'\n- Quote in `\"code\"`\n\n```\n\"quoted\"\n```\n"
  },
  {
    "path": "tests/testsuite/markdown/strikethrough/src/SUMMARY.md",
    "content": "- [Strikethrough](strikethrough.md)\n"
  },
  {
    "path": "tests/testsuite/markdown/strikethrough/src/strikethrough.md",
    "content": "# Strikethrough\n\n~~strikethrough example~~\n\n"
  },
  {
    "path": "tests/testsuite/markdown/tables/src/SUMMARY.md",
    "content": "- [Tables](tables.md)\n"
  },
  {
    "path": "tests/testsuite/markdown/tables/src/tables.md",
    "content": "# Tables\n\n| foo | bar |\n| --- | --- |\n| baz | bim |\n| Backslash in code | `\\` |\n| Double back in code | `\\\\` |\n| Pipe in code | `\\|` |\n| Pipe in code2 | `test \\| inside` |\n\n| Neither | Left | Center | Right |\n|---------|:-----|:------:|------:|\n| one     | two  | three  | four  |\n"
  },
  {
    "path": "tests/testsuite/markdown/tasklists/src/SUMMARY.md",
    "content": "- [Tasklists](tasklists.md)\n"
  },
  {
    "path": "tests/testsuite/markdown/tasklists/src/tasklists.md",
    "content": "## Tasklisks\n\n- [X] Apples\n- [X] Broccoli\n- [ ] Carrots\n"
  },
  {
    "path": "tests/testsuite/markdown.rs",
    "content": "//! Tests for special markdown rendering.\n\nuse crate::prelude::*;\nuse snapbox::file;\n\n// Checks custom header id and classes.\n#[test]\nfn custom_header_attributes() {\n    BookTest::from_dir(\"markdown/custom_header_attributes\")\n        .check_main_file(\"book/custom_header_attributes.html\", str![[r##\"\n<h1 id=\"attrs\"><a class=\"header\" href=\"#attrs\">Heading Attributes</a></h1>\n<h2 class=\"class1 class2\" id=\"heading-with-classes\"><a class=\"header\" href=\"#heading-with-classes\">Heading with classes</a></h2>\n<h2 id=\"both\" class=\"class1 class2\"><a class=\"header\" href=\"#both\">Heading with id and classes</a></h2>\n<h2 myattr=\"\" otherattr=\"value\" id=\"myh3\" class=\"myclass1 myclass2\"><a class=\"header\" href=\"#myh3\">Heading with attribute</a></h2>\n\"##]]);\n}\n\n// Test for a variety of footnote renderings.\n#[test]\nfn footnotes() {\n    BookTest::from_dir(\"markdown/footnotes\")\n        .run(\"build\", |cmd| {\n            cmd.expect_stderr(str![[r#\"\n INFO Book building has started\n INFO Running the html backend\n WARN footnote `multiple-definitions` in footnotes.md defined multiple times - not updating to new definition\n WARN footnote `unused` in `footnotes.md` is defined but not referenced\n INFO HTML book written to `[ROOT]/book`\n\n\"#]]);\n        })\n        .check_main_file(\n            \"book/footnotes.html\",\n            file![\"markdown/footnotes/expected/footnotes.html\"],\n        );\n}\n\n// Basic table test.\n#[test]\nfn tables() {\n    BookTest::from_dir(\"markdown/tables\").check_main_file(\n        \"book/tables.html\",\n        str![[r##\"\n<h1 id=\"tables\"><a class=\"header\" href=\"#tables\">Tables</a></h1>\n<div class=\"table-wrapper\">\n<table>\n<thead>\n<tr><th>foo</th><th>bar</th></tr>\n</thead>\n<tbody>\n<tr><td>baz</td><td>bim</td></tr>\n<tr><td>Backslash in code</td><td><code>/</code></td></tr>\n<tr><td>Double back in code</td><td><code>//</code></td></tr>\n<tr><td>Pipe in code</td><td><code>|</code></td></tr>\n<tr><td>Pipe in code2</td><td><code>test | inside</code></td></tr>\n</tbody>\n</table>\n</div>\n<div class=\"table-wrapper\">\n<table>\n<thead>\n<tr><th>Neither</th><th style=\"text-align: left\">Left</th><th style=\"text-align: center\">Center</th><th style=\"text-align: right\">Right</th></tr>\n</thead>\n<tbody>\n<tr><td>one</td><td style=\"text-align: left\">two</td><td style=\"text-align: center\">three</td><td style=\"text-align: right\">four</td></tr>\n</tbody>\n</table>\n</div>\n\"##]],\n    );\n}\n\n// Strikethrough test.\n#[test]\nfn strikethrough() {\n    BookTest::from_dir(\"markdown/strikethrough\").check_main_file(\n        \"book/strikethrough.html\",\n        str![[r##\"\n<h1 id=\"strikethrough\"><a class=\"header\" href=\"#strikethrough\">Strikethrough</a></h1>\n<p><del>strikethrough example</del></p>\n\"##]],\n    );\n}\n\n// Tasklist test.\n#[test]\nfn tasklists() {\n    BookTest::from_dir(\"markdown/tasklists\").check_main_file(\n        \"book/tasklists.html\",\n        str![[r##\"\n<h2 id=\"tasklisks\"><a class=\"header\" href=\"#tasklisks\">Tasklisks</a></h2>\n<ul>\n<li><input disabled=\"\" type=\"checkbox\" checked=\"\"> Apples</li>\n<li><input disabled=\"\" type=\"checkbox\" checked=\"\"> Broccoli</li>\n<li><input disabled=\"\" type=\"checkbox\"> Carrots</li>\n</ul>\n\"##]],\n    );\n}\n\n// Smart punctuation test.\n#[test]\nfn smart_punctuation() {\n    BookTest::from_dir(\"markdown/smart_punctuation\")\n        // Default is on.\n        .check_main_file(\n            \"book/smart_punctuation.html\",\n            str![[r##\"\n<h1 id=\"smart-punctuation\"><a class=\"header\" href=\"#smart-punctuation\">Smart Punctuation</a></h1>\n<ul>\n<li>En dash: –</li>\n<li>Em dash: —</li>\n<li>Ellipsis: …</li>\n<li>Double quote: “quote”</li>\n<li>Single quote: ‘quote’</li>\n<li>Quote in <code>\"code\"</code></li>\n</ul>\n<pre><code>\"quoted\"\n</code></pre>\n\"##]],\n        )\n        .run(\"build\", |cmd| {\n            cmd.env(\"MDBOOK_OUTPUT__HTML__SMART_PUNCTUATION\", \"false\");\n        })\n        .check_main_file(\n            \"book/smart_punctuation.html\",\n            str![[r##\"\n<h1 id=\"smart-punctuation\"><a class=\"header\" href=\"#smart-punctuation\">Smart Punctuation</a></h1>\n<ul>\n<li>En dash: --</li>\n<li>Em dash: ---</li>\n<li>Ellipsis: ...</li>\n<li>Double quote: \"quote\"</li>\n<li>Single quote: 'quote'</li>\n<li>Quote in <code>\"code\"</code></li>\n</ul>\n<pre><code>\"quoted\"\n</code></pre>\n\"##]],\n        );\n}\n\n// Basic markdown syntax.\n// This doesn't try to cover the commonmark test suite, but maybe it could some day?\n#[test]\nfn basic_markdown() {\n    BookTest::from_dir(\"markdown/basic_markdown\").check_all_main_files();\n}\n\n#[test]\nfn definition_lists() {\n    BookTest::from_dir(\"markdown/definition_lists\")\n        .check_all_main_files()\n        .run(\"build\", |cmd| {\n            cmd.env(\"MDBOOK_OUTPUT__HTML__DEFINITION_LISTS\", \"false\");\n        })\n        .check_main_file(\n            \"book/definition_lists.html\",\n            file![\"markdown/definition_lists/expected_disabled/definition_lists.html\"],\n        )\n        .check_main_file(\n            \"book/html_definition_lists.html\",\n            file![\"markdown/definition_lists/expected_disabled/html_definition_lists.html\"],\n        );\n}\n\n#[test]\nfn admonitions() {\n    BookTest::from_dir(\"markdown/admonitions\")\n        .check_all_main_files()\n        .run(\"build\", |cmd| {\n            cmd.env(\"MDBOOK_OUTPUT__HTML__ADMONITIONS\", \"false\");\n        })\n        .check_main_file(\n            \"book/admonitions.html\",\n            file![\"markdown/admonitions/expected_disabled/admonitions.html\"],\n        );\n}\n"
  },
  {
    "path": "tests/testsuite/playground/disabled_playground/book.toml",
    "content": "[book]\ntitle = \"playground_on_rust_code\"\n\n[output.html.playground]\nrunnable = false\n"
  },
  {
    "path": "tests/testsuite/playground/disabled_playground/src/SUMMARY.md",
    "content": "# Summary\n\n- [Rust Playground](./disabled-rust-playground.md)\n"
  },
  {
    "path": "tests/testsuite/playground/disabled_playground/src/disabled-rust-playground.md",
    "content": "# Rust Sample\n\n```rust\nlet x = 1;\n```\n"
  },
  {
    "path": "tests/testsuite/playground/playground_on_rust_code/book.toml",
    "content": "[book]\ntitle = \"playground_on_rust_code\"\n"
  },
  {
    "path": "tests/testsuite/playground/playground_on_rust_code/src/SUMMARY.md",
    "content": "# Summary\n\n- [Rust Playground](./rust-playground.md)\n"
  },
  {
    "path": "tests/testsuite/playground/playground_on_rust_code/src/rust-playground.md",
    "content": "# Rust Sample\n\n```rust\nlet x = 1;\n```\n"
  },
  {
    "path": "tests/testsuite/playground.rs",
    "content": "//! Tests for Rust playground support.\n\nuse crate::prelude::*;\n\n// Verifies that a rust codeblock gets the playground class.\n#[test]\nfn playground_on_rust_code() {\n    BookTest::from_dir(\"playground/playground_on_rust_code\").check_main_file(\n        \"book/index.html\",\n        str![[r##\"\n<h1 id=\"rust-sample\"><a class=\"header\" href=\"#rust-sample\">Rust Sample</a></h1>\n<pre class=\"playground\"><code class=\"language-rust\"><span class=\"boring\">#![allow(unused)]\n</span><span class=\"boring\">fn main() {\n</span>let x = 1;\n<span class=\"boring\">}</span></code></pre>\n\"##]],\n    );\n}\n\n// When the playground is disabled, there should be no playground class.\n#[test]\nfn disabled_playground() {\n    BookTest::from_dir(\"playground/disabled_playground\").check_main_file(\n        \"book/index.html\",\n        str![[r##\"\n<h1 id=\"rust-sample\"><a class=\"header\" href=\"#rust-sample\">Rust Sample</a></h1>\n<pre><code class=\"language-rust\">let x = 1;</code></pre>\n\"##]],\n    );\n}\n"
  },
  {
    "path": "tests/testsuite/preprocessor/extension_compatibility/book.toml",
    "content": "[book]\ntitle = \"extension_compatibility\"\n\n[preprocessor.my-preprocessor]\ncommand = \"./my-preprocessor\"\ncustom-config = true\noptional = true\n[preprocessor.my-preprocessor.custom-table]\nextra = \"abc\"\n\n[output.html]\n\n[output.my-renderer]\ncommand = \"./my-renderer\"\ncustom-config = \"renderer settings\"\noptional = true\n[output.my-renderer.custom-table]\nextra = \"xyz\"\n"
  },
  {
    "path": "tests/testsuite/preprocessor/extension_compatibility/src/SUMMARY.md",
    "content": "# Summary\n\n[Prefix chapter](./prefix.md)\n\n- [Chapter 1](./chapter_1.md)\n- [Draft chapter]()\n\n# Part title\n\n- [Part chapter](./part/chapter.md)\n    - [Part sub chapter](./part/sub-chapter.md)\n\n---\n\n[Suffix chapter](./suffix.md)\n"
  },
  {
    "path": "tests/testsuite/preprocessor/extension_compatibility/src/chapter_1.md",
    "content": "# Chapter 1\n"
  },
  {
    "path": "tests/testsuite/preprocessor/extension_compatibility/src/part/chapter.md",
    "content": "# Part chapter\n"
  },
  {
    "path": "tests/testsuite/preprocessor/extension_compatibility/src/part/sub-chapter.md",
    "content": "# Part sub chapter\n"
  },
  {
    "path": "tests/testsuite/preprocessor/extension_compatibility/src/prefix.md",
    "content": "# Prefix chapter\n"
  },
  {
    "path": "tests/testsuite/preprocessor/extension_compatibility/src/suffix.md",
    "content": "# Suffix chapter\n"
  },
  {
    "path": "tests/testsuite/preprocessor/failing_preprocessor/book.toml",
    "content": "[preprocessor.nop-preprocessor]\ncommand = \"cargo run --quiet --example nop-preprocessor --\"\nblow-up = true\n\n"
  },
  {
    "path": "tests/testsuite/preprocessor/failing_preprocessor/src/SUMMARY.md",
    "content": ""
  },
  {
    "path": "tests/testsuite/preprocessor/missing_optional_not_fatal/book.toml",
    "content": "[preprocessor.missing]\ncommand = \"trduyvbhijnorgevfuhn\"\noptional = true\n"
  },
  {
    "path": "tests/testsuite/preprocessor/missing_optional_not_fatal/src/SUMMARY.md",
    "content": ""
  },
  {
    "path": "tests/testsuite/preprocessor/missing_preprocessor/book.toml",
    "content": "[preprocessor.missing]\ncommand = \"trduyvbhijnorgevfuhn\"\n\n"
  },
  {
    "path": "tests/testsuite/preprocessor/missing_preprocessor/src/SUMMARY.md",
    "content": ""
  },
  {
    "path": "tests/testsuite/preprocessor/nop_preprocessor/book.toml",
    "content": "[preprocessor.nop-preprocessor]\ncommand = \"cargo run --quiet --example nop-preprocessor --\"\n"
  },
  {
    "path": "tests/testsuite/preprocessor/nop_preprocessor/src/SUMMARY.md",
    "content": ""
  },
  {
    "path": "tests/testsuite/preprocessor.rs",
    "content": "//! Tests for custom preprocessors.\n\nuse crate::book_test::list_all_files;\nuse crate::prelude::*;\nuse anyhow::Result;\nuse mdbook_core::book::{Book, BookItem, Chapter};\nuse mdbook_driver::builtin_preprocessors::CmdPreprocessor;\nuse mdbook_preprocessor::{Preprocessor, PreprocessorContext};\nuse snapbox::IntoData;\nuse std::sync::{Arc, Mutex};\n\nstruct Spy(Arc<Mutex<Inner>>);\n\n#[derive(Debug, Default)]\nstruct Inner {\n    run_count: usize,\n    rendered_with: Vec<String>,\n}\n\nimpl Preprocessor for Spy {\n    fn name(&self) -> &str {\n        \"dummy\"\n    }\n\n    fn run(&self, ctx: &PreprocessorContext, book: Book) -> Result<Book> {\n        let mut inner = self.0.lock().unwrap();\n        inner.run_count += 1;\n        inner.rendered_with.push(ctx.renderer.clone());\n        Ok(book)\n    }\n}\n\n// Test that preprocessor gets run.\n#[test]\nfn runs_preprocessors() {\n    let test = BookTest::init(|_| {});\n    let spy: Arc<Mutex<Inner>> = Default::default();\n    let mut book = test.load_book();\n    book.with_preprocessor(Spy(Arc::clone(&spy)));\n    book.build().unwrap();\n\n    let inner = spy.lock().unwrap();\n    assert_eq!(inner.run_count, 1);\n    assert_eq!(inner.rendered_with, [\"html\"]);\n}\n\n// Run tests with a custom preprocessor.\n#[test]\nfn test_with_custom_preprocessor() {\n    let test = BookTest::init(|_| {});\n    let spy: Arc<Mutex<Inner>> = Default::default();\n    let mut book = test.load_book();\n    book.with_preprocessor(Spy(Arc::clone(&spy)));\n    book.test(vec![]).unwrap();\n\n    let inner = spy.lock().unwrap();\n    assert_eq!(inner.run_count, 1);\n    assert_eq!(inner.rendered_with, [\"test\"]);\n}\n\n// No-op preprocessor works.\n#[test]\nfn nop_preprocessor() {\n    BookTest::from_dir(\"preprocessor/nop_preprocessor\").run(\"build\", |cmd| {\n        cmd.expect_stdout(str![[\"\"]]).expect_stderr(str![[r#\"\n INFO Book building has started\n INFO Running the html backend\n INFO HTML book written to `[ROOT]/book`\n\n\"#]]);\n    });\n}\n\n// Failing preprocessor generates an error.\n#[test]\nfn failing_preprocessor() {\n    BookTest::from_dir(\"preprocessor/failing_preprocessor\").run(\"build\", |cmd| {\n        cmd.expect_failure()\n            .expect_stdout(str![[\"\"]])\n            .expect_stderr(str![[r#\"\n INFO Book building has started\nBoom!!1!\nERROR The \"nop-preprocessor\" preprocessor exited unsuccessfully with [EXIT_STATUS]: 1 status\n\n\"#]]);\n    });\n}\n\nfn example() -> CmdPreprocessor {\n    CmdPreprocessor::new(\n        \"nop-preprocessor\".to_string(),\n        \"cargo run --quiet --example nop-preprocessor --\".to_string(),\n        std::env::current_dir().unwrap(),\n        false,\n    )\n}\n\n#[test]\nfn example_supports_whatever() {\n    let cmd = example();\n\n    let got = cmd.supports_renderer(\"whatever\").unwrap();\n\n    assert_eq!(got, true);\n}\n\n#[test]\nfn example_doesnt_support_not_supported() {\n    let cmd = example();\n\n    let got = cmd.supports_renderer(\"not-supported\").unwrap();\n\n    assert_eq!(got, false);\n}\n\n// Checks the behavior of a relative path to a preprocessor.\n#[test]\nfn relative_command_path() {\n    let mut test = BookTest::init(|_| {});\n    test.rust_program(\n        \"preprocessors/my-preprocessor\",\n        r#\"\n        fn main() {\n            let mut args = std::env::args().skip(1);\n            if args.next().as_deref() == Some(\"supports\") {\n                std::fs::write(\"support-check\", args.next().unwrap()).unwrap();\n                return;\n            }\n            use std::io::Read;\n            let mut s = String::new();\n            std::io::stdin().read_to_string(&mut s).unwrap();\n            std::fs::write(\"preprocessor-ran\", \"test\").unwrap();\n            println!(\"{{\\\"items\\\": []}}\");\n        }\n        \"#,\n    )\n    .change_file(\n        \"book.toml\",\n        \"[preprocessor.my-preprocessor]\\n\\\n         command = 'preprocessors/my-preprocessor'\\n\",\n    )\n    .run(\"build\", |cmd| {\n        cmd.expect_stdout(str![\"\"]).expect_stderr(str![[r#\"\n INFO Book building has started\n INFO Running the html backend\n INFO HTML book written to `[ROOT]/book`\n\n\"#]]);\n    })\n    .check_file(\"support-check\", \"html\")\n    .check_file(\"preprocessor-ran\", \"test\")\n    // Try again, but outside of the book root to check relative path behavior.\n    .rm_r(\"support-check\")\n    .rm_r(\"preprocessor-ran\")\n    .run(\"build ..\", |cmd| {\n        cmd.current_dir(cmd.dir.join(\"src\"))\n            .expect_stdout(str![\"\"])\n            .expect_stderr(str![[r#\"\n INFO Book building has started\n INFO Running the html backend\n INFO HTML book written to `[ROOT]/src/../book`\n\n\"#]]);\n    })\n    .check_file(\"support-check\", \"html\")\n    .check_file(\"preprocessor-ran\", \"test\");\n}\n\n// Preprocessor command is missing.\n#[test]\nfn missing_preprocessor() {\n    BookTest::from_dir(\"preprocessor/missing_preprocessor\").run(\"build\", |cmd| {\n        cmd.expect_failure()\n            .expect_stdout(str![[\"\"]])\n            .expect_stderr(str![[r#\"\n INFO Book building has started\nERROR The command `trduyvbhijnorgevfuhn` wasn't found, is the `missing` preprocessor installed? If you want to ignore this error when the `missing` preprocessor is not installed, set `optional = true` in the `[preprocessor.missing]` section of the book.toml configuration file.\nERROR Unable to run the preprocessor `missing`\n[TAB]Caused by: [NOT_FOUND]\n\n\"#]]);\n    });\n}\n\n// Optional missing is not an error.\n#[test]\nfn missing_optional_not_fatal() {\n    BookTest::from_dir(\"preprocessor/missing_optional_not_fatal\").run(\"build\", |cmd| {\n        cmd.expect_stdout(str![[\"\"]]).expect_stderr(str![[r#\"\n INFO Book building has started\n WARN The command `trduyvbhijnorgevfuhn` for preprocessor `missing` was not found, but is marked as optional.\n INFO Running the html backend\n INFO HTML book written to `[ROOT]/book`\n\n\"#]]);\n    });\n}\n\n// with_preprocessor of an existing name.\n#[test]\nfn with_preprocessor_same_name() {\n    let mut test = BookTest::init(|_| {});\n    test.change_file(\n        \"book.toml\",\n        \"[preprocessor.dummy]\\n\\\n         command = 'mdbook-preprocessor-does-not-exist'\\n\",\n    );\n    let spy: Arc<Mutex<Inner>> = Default::default();\n    let mut book = test.load_book();\n    book.with_preprocessor(Spy(Arc::clone(&spy)));\n    // Unfortunately this is unable to capture the output when using the API.\n    book.build().unwrap();\n    let inner = spy.lock().unwrap();\n    assert_eq!(inner.run_count, 1);\n    assert_eq!(inner.rendered_with, [\"html\"]);\n}\n\n// Checks that the interface stays backwards compatible. The interface here\n// should not be changed to fix a compatibility issue unless there is a\n// major-semver version update to mdbook.\n//\n// Note that this tests both preprocessors and renderers. It's in this module\n// for lack of a better location.\n#[test]\nfn extension_compatibility() {\n    // This is here to force you to look at this test if you alter any of\n    // these types such as adding new fields/variants. This test should be\n    // updated accordingly. For example, new `BookItem` variants should be\n    // added to the extension_compatibility book, or new fields should be\n    // added to the expected input/output. This is also a check that these\n    // should only be changed in a semver-breaking release\n    let chapter = Chapter {\n        name: \"example\".to_string(),\n        content: \"content\".to_string(),\n        number: None,\n        sub_items: Vec::new(),\n        path: None,\n        source_path: None,\n        parent_names: Vec::new(),\n    };\n    let item = BookItem::Chapter(chapter);\n    match &item {\n        BookItem::Chapter(_) => {}\n        BookItem::Separator => {}\n        BookItem::PartTitle(_) => {}\n    }\n    let items = vec![item];\n    let _book = Book { items };\n\n    let mut test = BookTest::from_dir(\"preprocessor/extension_compatibility\");\n    // Run it once with the preprocessor disabled so that we can verify\n    // that the built book is identical with the preprocessor enabled.\n    test.run(\"build\", |cmd| {\n        cmd.expect_stdout(str![[\"\"]]).expect_stderr(str![[r#\"\n INFO Book building has started\n WARN The command `./my-preprocessor` for preprocessor `my-preprocessor` was not found, but is marked as optional.\n INFO Running the html backend\n INFO HTML book written to `[ROOT]/book/html`\n WARN The command `./my-preprocessor` for preprocessor `my-preprocessor` was not found, but is marked as optional.\n INFO Running the my-renderer backend\n INFO Invoking the \"my-renderer\" renderer\n WARN The command `./my-renderer` for backend `my-renderer` was not found, but is marked as optional.\n\n\"#]]);\n    });\n    let orig_dir = test.dir.join(\"book.orig\");\n    let pre_dir = test.dir.join(\"book\");\n    std::fs::rename(&pre_dir, &orig_dir).unwrap();\n\n    // **CAUTION** DO NOT modify this value unless this is a major-semver change.\n    let book_output = serde_json::json!({\n        \"items\": [\n          {\n            \"Chapter\": {\n              \"content\": \"# Prefix chapter\\n\",\n              \"name\": \"Prefix chapter\",\n              \"number\": null,\n              \"parent_names\": [],\n              \"path\": \"prefix.md\",\n              \"source_path\": \"prefix.md\",\n              \"sub_items\": []\n            }\n          },\n          {\n            \"Chapter\": {\n              \"content\": \"# Chapter 1\\n\",\n              \"name\": \"Chapter 1\",\n              \"number\": [\n                1\n              ],\n              \"parent_names\": [],\n              \"path\": \"chapter_1.md\",\n              \"source_path\": \"chapter_1.md\",\n              \"sub_items\": []\n            }\n          },\n          {\n            \"Chapter\": {\n              \"content\": \"\",\n              \"name\": \"Draft chapter\",\n              \"number\": [\n                2\n              ],\n              \"parent_names\": [],\n              \"path\": null,\n              \"source_path\": null,\n              \"sub_items\": []\n            }\n          },\n          {\n            \"PartTitle\": \"Part title\"\n          },\n          {\n            \"Chapter\": {\n              \"content\": \"# Part chapter\\n\",\n              \"name\": \"Part chapter\",\n              \"number\": [\n                3\n              ],\n              \"parent_names\": [],\n              \"path\": \"part/chapter.md\",\n              \"source_path\": \"part/chapter.md\",\n              \"sub_items\": [\n                {\n                  \"Chapter\": {\n                    \"content\": \"# Part sub chapter\\n\",\n                    \"name\": \"Part sub chapter\",\n                    \"number\": [\n                      3,\n                      1\n                    ],\n                    \"parent_names\": [\n                      \"Part chapter\"\n                    ],\n                    \"path\": \"part/sub-chapter.md\",\n                    \"source_path\": \"part/sub-chapter.md\",\n                    \"sub_items\": []\n                  }\n                }\n              ]\n            }\n          },\n          \"Separator\",\n          {\n            \"Chapter\": {\n              \"content\": \"# Suffix chapter\\n\",\n              \"name\": \"Suffix chapter\",\n              \"number\": null,\n              \"parent_names\": [],\n              \"path\": \"suffix.md\",\n              \"source_path\": \"suffix.md\",\n              \"sub_items\": []\n            }\n          }\n        ]\n    });\n    let output_str = serde_json::to_string(&book_output).unwrap();\n    // **CAUTION** The only updates allowed here in a semver-compatible\n    // release is to add new fields.\n    let expected_config = serde_json::json!({\n        \"book\": {\n          \"authors\": [],\n          \"description\": null,\n          \"language\": \"en\",\n          \"text-direction\": null,\n          \"title\": \"extension_compatibility\"\n        },\n        \"output\": {\n          \"html\": {},\n          \"my-renderer\": {\n            \"command\": \"./my-renderer\",\n            \"custom-config\": \"renderer settings\",\n            \"custom-table\": {\n              \"extra\": \"xyz\"\n            },\n            \"optional\": true\n          }\n        },\n        \"preprocessor\": {\n          \"my-preprocessor\": {\n            \"command\": \"./my-preprocessor\",\n            \"custom-config\": true,\n            \"custom-table\": {\n              \"extra\": \"abc\"\n            },\n            \"optional\": true\n          }\n        }\n    });\n\n    // **CAUTION** The only updates allowed here in a semver-compatible\n    // release is to add new fields. The output should not change.\n    let expected_preprocessor_input = serde_json::json!([\n        {\n            \"config\": expected_config,\n            \"mdbook_version\": \"[VERSION]\",\n            \"renderer\": \"html\",\n            \"root\": \"[ROOT]\"\n        },\n        book_output\n    ]);\n    let expected_renderer_input = serde_json::json!(\n        {\n            \"version\": \"[VERSION]\",\n            \"root\": \"[ROOT]\",\n            \"book\": book_output,\n            \"config\": expected_config,\n            \"destination\": \"[ROOT]/book/my-renderer\",\n        }\n    );\n\n    // This preprocessor writes its input to some files, and writes the\n    // hard-coded output specified above.\n    test.rust_program(\n        \"my-preprocessor\",\n        &r###\"\n            use std::fs::OpenOptions;\n            use std::io::{Read, Write};\n            fn main() {\n                let mut args = std::env::args().skip(1);\n                if args.next().as_deref() == Some(\"supports\") {\n                    let mut file = OpenOptions::new()\n                        .create(true)\n                        .append(true)\n                        .open(\"support-check\")\n                        .unwrap();\n                    let renderer = args.next().unwrap();\n                    writeln!(file, \"{renderer}\").unwrap();\n                    if renderer != \"html\" {\n                        std::process::exit(1);\n                    }\n                    return;\n                }\n                let mut s = String::new();\n                std::io::stdin().read_to_string(&mut s).unwrap();\n                std::fs::write(\"preprocessor-input\", &s).unwrap();\n                let output = r##\"OUTPUT_REPLACE\"##;\n                println!(\"{output}\");\n            }\n            \"###\n        .replace(\"OUTPUT_REPLACE\", &output_str),\n    )\n    // This renderer writes its input to a file.\n    .rust_program(\n        \"my-renderer\",\n        &r#\"\n            fn main() {\n                use std::io::Read;\n                let mut s = String::new();\n                std::io::stdin().read_to_string(&mut s).unwrap();\n                std::fs::write(\"renderer-input\", &s).unwrap();\n            }\n        \"#,\n    )\n    .run(\"build\", |cmd| {\n        cmd.expect_stdout(str![[\"\"]]).expect_stderr(str![[r#\"\n INFO Book building has started\n INFO Running the html backend\n INFO HTML book written to `[ROOT]/book/html`\n INFO Running the my-renderer backend\n INFO Invoking the \"my-renderer\" renderer\n\n\"#]]);\n    })\n    .check_file(\"support-check\", \"html\\nmy-renderer\\n\")\n    .check_file(\n        \"preprocessor-input\",\n        serde_json::to_string(&expected_preprocessor_input)\n            .unwrap()\n            .is_json(),\n    )\n    .check_file(\n        \"book/my-renderer/renderer-input\",\n        serde_json::to_string(&expected_renderer_input)\n            .unwrap()\n            .is_json(),\n    );\n    // Verify both directories have the exact same output.\n    test.rm_r(\"book/my-renderer/renderer-input\");\n    let orig_files = list_all_files(&orig_dir);\n    let pre_files = list_all_files(&pre_dir);\n    assert_eq!(orig_files, pre_files);\n    for file in &orig_files {\n        let orig_path = orig_dir.join(file);\n        if orig_path.is_file() {\n            let orig = std::fs::read(&orig_path).unwrap();\n            let pre = std::fs::read(&pre_dir.join(file)).unwrap();\n            test.assert.eq(pre, orig);\n        }\n    }\n}\n"
  },
  {
    "path": "tests/testsuite/print/chapter_no_h1/book.toml",
    "content": "[book]\ntitle = \"chapter_no_h1\"\n"
  },
  {
    "path": "tests/testsuite/print/chapter_no_h1/expected/print.html",
    "content": "<h1 id=\"chapter-1\"><a class=\"header\" href=\"#chapter-1\">Chapter 1</a></h1>\n<p>See <a href=\"#chapter-2\">chapter 2</a>.</p>\n<div style=\"break-before: page; page-break-before: always;\"></div>\n<h1 id=\"chapter-2\"><a href=\"#chapter-2\" class=\"header\">Chapter 2</a></h1>\n<p>See <a href=\"#chapter-2\">this</a>.</p>\n<div style=\"break-before: page; page-break-before: always;\"></div>\n<h1 id=\"h2-instead-1\"><a href=\"#h2-instead-1\" class=\"header\">H2 instead</a></h1>\n<h2 id=\"h2-instead\"><a class=\"header\" href=\"#h2-instead\">H2 instead</a></h2>\n<p>This has H2 instead of H1.</p>"
  },
  {
    "path": "tests/testsuite/print/chapter_no_h1/src/SUMMARY.md",
    "content": "# Summary\n\n- [Chapter 1](./chapter_1.md)\n- [Chapter 2](./chapter_2.md)\n- [H2 instead](./h2-instead.md)\n"
  },
  {
    "path": "tests/testsuite/print/chapter_no_h1/src/chapter_1.md",
    "content": "# Chapter 1\n\nSee [chapter 2](chapter_2.md).\n"
  },
  {
    "path": "tests/testsuite/print/chapter_no_h1/src/chapter_2.md",
    "content": "See [this](./chapter_2.md).\n"
  },
  {
    "path": "tests/testsuite/print/chapter_no_h1/src/h2-instead.md",
    "content": "## H2 instead\n\nThis has H2 instead of H1.\n\n"
  },
  {
    "path": "tests/testsuite/print/duplicate_ids/book.toml",
    "content": "[book]\ntitle = \"duplicate_ids\"\n"
  },
  {
    "path": "tests/testsuite/print/duplicate_ids/expected/print.html",
    "content": "<h1 id=\"chapter-1\"><a class=\"header\" href=\"#chapter-1\">Chapter 1</a></h1>\n<h2 id=\"some-title\"><a class=\"header\" href=\"#some-title\">Some title</a></h2>\n<p>See <a href=\"#some-title-1\">other</a></p>\n<p>See <a href=\"#some-title\">this</a></p>\n<p>See <a href=\"#some-title\">this anchor only</a></p>\n<div style=\"break-before: page; page-break-before: always;\"></div>\n<h1 id=\"chapter-2\"><a class=\"header\" href=\"#chapter-2\">Chapter 2</a></h1>\n<h2 id=\"some-title-1\"><a class=\"header\" href=\"#some-title-1\">Some title</a></h2>\n<p>See <a href=\"#some-title\">other</a></p>\n<p>See <a href=\"#some-title-1\">this</a></p>\n<p>See <a href=\"#some-title-1\">this anchor only</a></p>\n<p><a href=\"#some-title\">Works with HTML extension too</a></p>"
  },
  {
    "path": "tests/testsuite/print/duplicate_ids/src/SUMMARY.md",
    "content": "# Summary\n\n- [Chapter 1](./chapter_1.md)\n- [Chapter 2](./chapter_2.md)\n"
  },
  {
    "path": "tests/testsuite/print/duplicate_ids/src/chapter_1.md",
    "content": "# Chapter 1\n\n## Some title\n\nSee [other](chapter_2.md#some-title)\n\nSee [this](chapter_1.md#some-title)\n\nSee [this anchor only](#some-title)\n"
  },
  {
    "path": "tests/testsuite/print/duplicate_ids/src/chapter_2.md",
    "content": "# Chapter 2\n\n## Some title\n\nSee [other](chapter_1.md#some-title)\n\nSee [this](chapter_2.md#some-title)\n\nSee [this anchor only](#some-title)\n\n[Works with HTML extension too](chapter_1.html#some-title)\n"
  },
  {
    "path": "tests/testsuite/print/noindex/src/SUMMARY.md",
    "content": "- [Intro](index.md)\n"
  },
  {
    "path": "tests/testsuite/print/noindex/src/index.md",
    "content": "# Intro\n"
  },
  {
    "path": "tests/testsuite/print/relative_links/book.toml",
    "content": "[book]\ntitle = \"relative_links\"\n"
  },
  {
    "path": "tests/testsuite/print/relative_links/expected/print.html",
    "content": "<h1 id=\"first-chapter\"><a class=\"header\" href=\"#first-chapter\">First Chapter</a></h1>\n<div style=\"break-before: page; page-break-before: always;\"></div>\n<h1 id=\"first-nested\"><a class=\"header\" href=\"#first-nested\">First Nested</a></h1>\n<div style=\"break-before: page; page-break-before: always;\"></div>\n<h1 id=\"testing-relative-links-for-the-print-page\"><a class=\"header\" href=\"#testing-relative-links-for-the-print-page\">Testing relative links for the print page</a></h1>\n<p>When we link to <a href=\"#first-nested\">the first section</a>, it should work on\nboth the print page and the non-print page.</p>\n<p>The same link should work <a href=\"#first-nested\">with an html extension</a>.</p>\n<p>A <a href=\"#some-section\">fragment link</a> should work.</p>\n<p>Link <a href=\"../std/foo/bar.html\">outside</a>.</p>\n<p>Link <a href=\"../std/foo/bar.html#panic\">outside with anchor</a>.</p>\n<p>Link <a href=\"first/alpha/beta.html\">inside but doesn’t exist</a>.\nLink <a href=\"first/alpha/beta.html#anchor\">inside but doesn’t exist with anchor</a>.\nLink <a href=\"first/alpha/gamma.html\">inside to html</a>.\nLink <a href=\"first/alpha/gamma.html#anchor\">inside to html with anchor</a>.</p>\n<p><img src=\"images/picture.png\" alt=\"Some image\"></p>\n<p><a href=\"#first-nested\">HTML Link</a></p>\n<img src=\"images/picture.png\" alt=\"raw html\">\n<h2 id=\"some-section\"><a class=\"header\" href=\"#some-section\">Some section</a></h2>\n<p><a href=\"https://example.com/foo.html#bar\">Links with scheme shouldn’t be touched.</a></p>\n<p><a href=\"images/not-html?arg1&amp;arg2#with-anchor\">Non-html link</a></p>"
  },
  {
    "path": "tests/testsuite/print/relative_links/src/SUMMARY.md",
    "content": "# Summary\n\n- [First Chapter](first/index.md)\n    - [First Nested](first/nested.md)\n- [Second Chapter](second/nested.md)\n"
  },
  {
    "path": "tests/testsuite/print/relative_links/src/first/index.md",
    "content": "# First Chapter\n"
  },
  {
    "path": "tests/testsuite/print/relative_links/src/first/nested.md",
    "content": "# First Nested\n"
  },
  {
    "path": "tests/testsuite/print/relative_links/src/second/nested.md",
    "content": "# Testing relative links for the print page\n\nWhen we link to [the first section](../first/nested.md), it should work on\nboth the print page and the non-print page.\n\nThe same link should work [with an html extension](../first/nested.html).\n\nA [fragment link](#some-section) should work.\n\nLink [outside](../../std/foo/bar.html).\n\nLink [outside with anchor](../../std/foo/bar.html#panic).\n\nLink [inside but doesn't exist](../first/alpha/beta.md).\nLink [inside but doesn't exist with anchor](../first/alpha/beta.md#anchor).\nLink [inside to html](../first/alpha/gamma.html).\nLink [inside to html with anchor](../first/alpha/gamma.html#anchor).\n\n![Some image](../images/picture.png)\n\n<a href=\"../first/nested.md\">HTML Link</a>\n\n<img src=\"../images/picture.png\" alt=\"raw html\">\n\n## Some section\n\n[Links with scheme shouldn't be touched.](https://example.com/foo.html#bar)\n\n<a href=\"../images/not-html?arg1&arg2#with-anchor\">Non-html link</a>\n"
  },
  {
    "path": "tests/testsuite/print.rs",
    "content": "//! Tests for print page.\n\nuse crate::prelude::*;\nuse snapbox::file;\n\n// Tests relative links from the print page.\n#[test]\nfn relative_links() {\n    BookTest::from_dir(\"print/relative_links\").check_main_file(\n        \"book/print.html\",\n        file!(\"print/relative_links/expected/print.html\"),\n    );\n}\n\n// Test for duplicate IDs, and links to those duplicates.\n#[test]\nfn duplicate_ids() {\n    BookTest::from_dir(\"print/duplicate_ids\").check_main_file(\n        \"book/print.html\",\n        file!(\"print/duplicate_ids/expected/print.html\"),\n    );\n}\n\n// Test for synthesized link to a chapter that does not have an h1.\n#[test]\nfn chapter_no_h1() {\n    BookTest::from_dir(\"print/chapter_no_h1\").check_main_file(\n        \"book/print.html\",\n        file!(\"print/chapter_no_h1/expected/print.html\"),\n    );\n}\n\n// Checks that print.html is noindex.\n#[test]\nfn noindex() {\n    let robots = r#\"<meta name=\"robots\" content=\"noindex\">\"#;\n    BookTest::from_dir(\"print/noindex\")\n        .check_file_contains(\"book/print.html\", robots)\n        .check_file_doesnt_contain(\"book/index.html\", robots);\n}\n"
  },
  {
    "path": "tests/testsuite/redirects/redirect_existing_page/book.toml",
    "content": "[book]\ntitle = \"redirect_existing_page\"\n\n[output.html.redirect]\n\"/chapter_1.html\" = \"other-page.html\"\n"
  },
  {
    "path": "tests/testsuite/redirects/redirect_existing_page/src/SUMMARY.md",
    "content": "# Summary\n\n- [Chapter 1](./chapter_1.md)\n"
  },
  {
    "path": "tests/testsuite/redirects/redirect_existing_page/src/chapter_1.md",
    "content": "# Chapter 1\n"
  },
  {
    "path": "tests/testsuite/redirects/redirect_removed_with_fragments_only/book.toml",
    "content": "[book]\ntitle = \"redirect_removed_with_fragments_only\"\n\n[output.html.redirect]\n\"/old-file.html#foo\" = \"chapter_1.html\"\n"
  },
  {
    "path": "tests/testsuite/redirects/redirect_removed_with_fragments_only/src/SUMMARY.md",
    "content": "# Summary\n\n- [Chapter 1](./chapter_1.md)\n"
  },
  {
    "path": "tests/testsuite/redirects/redirect_removed_with_fragments_only/src/chapter_1.md",
    "content": "# Chapter 1\n"
  },
  {
    "path": "tests/testsuite/redirects/redirects_are_emitted_correctly/book.toml",
    "content": "[book]\ntitle = \"redirects_are_emitted_correctly\"\n\n[output.html.redirect]\n\"/overview.html\" = \"index.html\"\n\"/overview.html#old\" = \"index.html#new\"\n\"/nested/page.html\" = \"https://rust-lang.org/\"\n"
  },
  {
    "path": "tests/testsuite/redirects/redirects_are_emitted_correctly/expected/nested/page.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"utf-8\">\n    <title>Redirecting...</title>\n    <meta http-equiv=\"refresh\" content=\"0; URL=https://rust-lang.org/\">\n    <link rel=\"canonical\" href=\"https://rust-lang.org/\">\n  </head>\n  <body>\n      <p>Redirecting to... <a href=\"https://rust-lang.org/\">https://rust-lang.org/</a>.</p>\n\n<script>\n    // This handles redirects that involve fragments.\n    document.addEventListener('DOMContentLoaded', function() {\n        const fragmentMap =\n            {}\n        ;\n        const fragment = window.location.hash;\n        if (fragment) {\n            let redirectUrl = \"https://rust-lang.org/\";\n            const target = fragmentMap[fragment];\n            if (target) {\n                let url = new URL(target, window.location.href);\n                redirectUrl = url.href;\n            } else {\n                let url = new URL(redirectUrl, window.location.href);\n                url.hash = window.location.hash;\n                redirectUrl = url.href;\n            }\n            window.location.replace(redirectUrl);\n        }\n        // else redirect handled by http-equiv\n    });\n</script>\n  </body>\n</html>\n"
  },
  {
    "path": "tests/testsuite/redirects/redirects_are_emitted_correctly/expected/overview.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"utf-8\">\n    <title>Redirecting...</title>\n    <meta http-equiv=\"refresh\" content=\"0; URL=index.html\">\n    <link rel=\"canonical\" href=\"index.html\">\n  </head>\n  <body>\n      <p>Redirecting to... <a href=\"index.html\">index.html</a>.</p>\n\n<script>\n    // This handles redirects that involve fragments.\n    document.addEventListener('DOMContentLoaded', function() {\n        const fragmentMap =\n            {\"#old\":\"index.html#new\"}\n        ;\n        const fragment = window.location.hash;\n        if (fragment) {\n            let redirectUrl = \"index.html\";\n            const target = fragmentMap[fragment];\n            if (target) {\n                let url = new URL(target, window.location.href);\n                redirectUrl = url.href;\n            } else {\n                let url = new URL(redirectUrl, window.location.href);\n                url.hash = window.location.hash;\n                redirectUrl = url.href;\n            }\n            window.location.replace(redirectUrl);\n        }\n        // else redirect handled by http-equiv\n    });\n</script>\n  </body>\n</html>\n"
  },
  {
    "path": "tests/testsuite/redirects/redirects_are_emitted_correctly/src/SUMMARY.md",
    "content": "# Redirects\n\n- [Chapter 1](chapter_1.md)\n- [Chapter 2](chapter_2.md)\n"
  },
  {
    "path": "tests/testsuite/redirects/redirects_are_emitted_correctly/src/chapter_1.md",
    "content": "# Chapter 1\n"
  },
  {
    "path": "tests/testsuite/redirects/redirects_are_emitted_correctly/src/chapter_2.md",
    "content": "# Chapter 2\n"
  },
  {
    "path": "tests/testsuite/redirects.rs",
    "content": "//! Tests for the HTML redirect feature.\n\nuse crate::prelude::*;\nuse snapbox::file;\n\n// Basic check of redirects.\n#[test]\nfn redirects_are_emitted_correctly() {\n    BookTest::from_dir(\"redirects/redirects_are_emitted_correctly\")\n        .check_file(\n            \"book/overview.html\",\n            file![\"redirects/redirects_are_emitted_correctly/expected/overview.html\"],\n        )\n        .check_file(\n            \"book/nested/page.html\",\n            file![\"redirects/redirects_are_emitted_correctly/expected/nested/page.html\"],\n        );\n}\n\n// Invalid redirect with only fragments.\n#[test]\nfn redirect_removed_with_fragments_only() {\n    BookTest::from_dir(\"redirects/redirect_removed_with_fragments_only\").run(\"build\", |cmd| {\n        cmd.expect_failure().expect_stderr(str![[r#\"\n INFO Book building has started\n INFO Running the html backend\nERROR Rendering failed\n[TAB]Caused by: Unable to emit redirects\n[TAB]Caused by: redirect entry for `old-file.html` only has source paths with `#` fragments\nThere must be an entry without the `#` fragment to determine the default destination.\n\n\"#]]);\n    });\n}\n\n// Invalid redirect for an existing page.\n#[test]\nfn redirect_existing_page() {\n    BookTest::from_dir(\"redirects/redirect_existing_page\").run(\"build\", |cmd| {\n        cmd.expect_failure().expect_stderr(str![[r#\"\n INFO Book building has started\n INFO Running the html backend\nERROR Rendering failed\n[TAB]Caused by: redirect found for existing chapter at `/chapter_1.html`\nEither delete the redirect or remove the chapter.\n\n\"#]]);\n    });\n}\n"
  },
  {
    "path": "tests/testsuite/renderer/backends_receive_render_context_via_stdin/book.toml",
    "content": "[output.cat-to-file]\ncommand = \"./cat-to-file\"\n\n"
  },
  {
    "path": "tests/testsuite/renderer/backends_receive_render_context_via_stdin/src/SUMMARY.md",
    "content": "- [Chapter 1](./chapter_1.md)\n"
  },
  {
    "path": "tests/testsuite/renderer/backends_receive_render_context_via_stdin/src/chapter_1.md",
    "content": "# Chapter 1\n"
  },
  {
    "path": "tests/testsuite/renderer/missing_optional_not_fatal/book.toml",
    "content": "[output.missing]\ncommand = \"trduyvbhijnorgevfuhn\"\noptional = true\n\n"
  },
  {
    "path": "tests/testsuite/renderer/missing_optional_not_fatal/src/SUMMARY.md",
    "content": ""
  },
  {
    "path": "tests/testsuite/renderer/missing_renderer/book.toml",
    "content": "[output.missing]\ncommand = \"trduyvbhijnorgevfuhn\"\n\n"
  },
  {
    "path": "tests/testsuite/renderer/missing_renderer/src/SUMMARY.md",
    "content": ""
  },
  {
    "path": "tests/testsuite/renderer/renderer_with_arguments/book.toml",
    "content": "[output.arguments]\ncommand = \"./arguments arg1 arg2\"\n\n"
  },
  {
    "path": "tests/testsuite/renderer/renderer_with_arguments/src/SUMMARY.md",
    "content": ""
  },
  {
    "path": "tests/testsuite/renderer.rs",
    "content": "//! Tests for custom renderers.\n\nuse crate::prelude::*;\nuse anyhow::Result;\nuse mdbook_renderer::{RenderContext, Renderer};\nuse snapbox::IntoData;\nuse std::fs::File;\nuse std::sync::{Arc, Mutex};\n\nstruct Spy(Arc<Mutex<Inner>>);\n\n#[derive(Debug, Default)]\nstruct Inner {\n    run_count: usize,\n}\n\nimpl Renderer for Spy {\n    fn name(&self) -> &str {\n        \"dummy\"\n    }\n\n    fn render(&self, _ctx: &RenderContext) -> Result<()> {\n        let mut inner = self.0.lock().unwrap();\n        inner.run_count += 1;\n        Ok(())\n    }\n}\n\n// Test that renderer gets run.\n#[test]\nfn runs_renderers() {\n    let test = BookTest::init(|_| {});\n    let spy: Arc<Mutex<Inner>> = Default::default();\n    let mut book = test.load_book();\n    book.with_renderer(Spy(Arc::clone(&spy)));\n    book.build().unwrap();\n\n    let inner = spy.lock().unwrap();\n    assert_eq!(inner.run_count, 1);\n}\n\n// Test renderer with a failing command fails.\n#[test]\nfn failing_command() {\n    BookTest::init(|_| {})\n        .rust_program(\n            \"failing\",\n            r#\"\n            fn main() {\n                // Read from stdin to avoid random pipe failures on Linux.\n                use std::io::Read;\n                let mut s = String::new();\n                std::io::stdin().read_to_string(&mut s).unwrap();\n                std::process::exit(1);\n            }\n            \"#,\n        )\n        .change_file(\n            \"book.toml\",\n            \"[output.failing]\\n\\\n             command = './failing'\\n\",\n        )\n        .run(\"build\", |cmd| {\n            cmd.expect_failure()\n                .expect_stdout(str![[\"\"]])\n                .expect_stderr(str![[r#\"\n INFO Book building has started\n INFO Running the failing backend\n INFO Invoking the \"failing\" renderer\nERROR Renderer exited with non-zero return code.\nERROR Rendering failed\n[TAB]Caused by: The \"failing\" renderer failed\n\n\"#]]);\n        });\n}\n\n// Renderer command is missing.\n#[test]\nfn missing_renderer() {\n    BookTest::from_dir(\"renderer/missing_renderer\").run(\"build\", |cmd| {\n        cmd.expect_failure()\n            .expect_stdout(str![[\"\"]])\n            .expect_stderr(str![[r#\"\n INFO Book building has started\n INFO Running the missing backend\n INFO Invoking the \"missing\" renderer\nERROR The command `trduyvbhijnorgevfuhn` wasn't found, is the `missing` backend installed? If you want to ignore this error when the `missing` backend is not installed, set `optional = true` in the `[output.missing]` section of the book.toml configuration file.\nERROR Rendering failed\n[TAB]Caused by: Unable to run the backend `missing`\n[TAB]Caused by: [NOT_FOUND]\n\n\"#]]);\n    });\n}\n\n// Optional missing is not an error.\n#[test]\nfn missing_optional_not_fatal() {\n    BookTest::from_dir(\"renderer/missing_optional_not_fatal\").run(\"build\", |cmd| {\n        cmd.expect_stdout(str![[\"\"]]).expect_stderr(str![[r#\"\n INFO Book building has started\n INFO Running the missing backend\n INFO Invoking the \"missing\" renderer\n WARN The command `trduyvbhijnorgevfuhn` for backend `missing` was not found, but is marked as optional.\n\n\"#]]);\n    });\n}\n\n// Command can include arguments.\n#[test]\nfn renderer_with_arguments() {\n    BookTest::from_dir(\"renderer/renderer_with_arguments\")\n        .rust_program(\n            \"arguments\",\n            r#\"\n            fn main() {\n                let args: Vec<_> = std::env::args().skip(1).collect();\n                assert_eq!(args, &[\"arg1\", \"arg2\"]);\n                println!(\"Hello World!\");\n                use std::io::Read;\n                let mut s = String::new();\n                std::io::stdin().read_to_string(&mut s).unwrap();\n            }\n            \"#,\n        )\n        .run(\"build\", |cmd| {\n            cmd.expect_stdout(str![[r#\"\nHello World!\n\n\"#]])\n                .expect_stderr(str![[r#\"\n INFO Book building has started\n INFO Running the arguments backend\n INFO Invoking the \"arguments\" renderer\n\n\"#]]);\n        });\n}\n\n// Checks the render context received by the renderer.\n#[test]\nfn backends_receive_render_context_via_stdin() {\n    let mut test = BookTest::from_dir(\"renderer/backends_receive_render_context_via_stdin\");\n    test.rust_program(\n        \"cat-to-file\",\n        r#\"\n        fn main() {\n            use std::io::Read;\n            let mut s = String::new();\n            std::io::stdin().read_to_string(&mut s).unwrap();\n            std::fs::write(\"out.txt\", s).unwrap();\n        }\n        \"#,\n    )\n    .run(\"build\", |cmd| {\n        cmd.expect_stdout(str![[\"\"]]).expect_stderr(str![[r#\"\n INFO Book building has started\n INFO Running the cat-to-file backend\n INFO Invoking the \"cat-to-file\" renderer\n\n\"#]]);\n    })\n    .check_file(\n        \"book/out.txt\",\n        str![[r##\"\n{\n  \"book\": {\n    \"items\": [\n      {\n        \"Chapter\": {\n          \"content\": \"# Chapter 1\\n\",\n          \"name\": \"Chapter 1\",\n          \"number\": [\n            1\n          ],\n          \"parent_names\": [],\n          \"path\": \"chapter_1.md\",\n          \"source_path\": \"chapter_1.md\",\n          \"sub_items\": []\n        }\n      }\n    ]\n  },\n  \"config\": {\n    \"book\": {\n      \"authors\": [],\n      \"description\": null,\n      \"language\": \"en\",\n      \"text-direction\": null,\n      \"title\": null\n    },\n    \"output\": {\n      \"cat-to-file\": {\n        \"command\": \"./cat-to-file\"\n      }\n    }\n  },\n  \"destination\": \"[ROOT]/book\",\n  \"root\": \"[ROOT]\",\n  \"version\": \"[VERSION]\"\n}\n\"##]]\n        .is_json(),\n    );\n\n    // Can round-trip.\n    let f = File::open(test.dir.join(\"book/out.txt\")).unwrap();\n    RenderContext::from_json(f).unwrap();\n}\n\n// Verifies that a relative path for the renderer command is relative to the\n// book root.\n#[test]\nfn relative_command_path() {\n    let mut test = BookTest::init(|_| {});\n    test.rust_program(\n        \"renderers/myrenderer\",\n        r#\"\n        fn main() {\n            use std::io::Read;\n            let mut s = String::new();\n            std::io::stdin().read_to_string(&mut s).unwrap();\n            std::fs::write(\"output\", \"test\").unwrap();\n        }\n        \"#,\n    )\n    .change_file(\n        \"book.toml\",\n        \"[output.myrenderer]\\n\\\n         command = 'renderers/myrenderer'\\n\",\n    )\n    .run(\"build\", |cmd| {\n        cmd.expect_stdout(str![[\"\"]]).expect_stderr(str![[r#\"\n INFO Book building has started\n INFO Running the myrenderer backend\n INFO Invoking the \"myrenderer\" renderer\n\n\"#]]);\n    })\n    .check_file(\"book/output\", \"test\");\n}\n\n// with_renderer of an existing name.\n#[test]\nfn with_renderer_same_name() {\n    let mut test = BookTest::init(|_| {});\n    test.change_file(\n        \"book.toml\",\n        \"[output.dummy]\\n\\\n         command = 'mdbook-renderer-does-not-exist'\\n\",\n    );\n    let spy: Arc<Mutex<Inner>> = Default::default();\n    let mut book = test.load_book();\n    book.with_renderer(Spy(Arc::clone(&spy)));\n    // Unfortunately this is unable to capture the output when using the API.\n    book.build().unwrap();\n    let inner = spy.lock().unwrap();\n    assert_eq!(inner.run_count, 1);\n}\n"
  },
  {
    "path": "tests/testsuite/rendering/code_blocks_fenced_with_indent/book.toml",
    "content": "[book]\ntitle = \"code_blocks_fenced_with_indent\"\n"
  },
  {
    "path": "tests/testsuite/rendering/code_blocks_fenced_with_indent/expected/code-blocks-fenced-with-indent.html",
    "content": "<h1 id=\"code-blocks-fenced-with-indent\"><a class=\"header\" href=\"#code-blocks-fenced-with-indent\">Code blocks fenced with indent</a></h1>\n<pre class=\"playground\"><code class=\"language-rust\"><span class=\"boring\">#![allow(unused)]\n</span><span class=\"boring\">fn main() {\n</span>    // This has a first line that is indented.\n    println!(\"hello\");\n<span class=\"boring\">}</span></code></pre>"
  },
  {
    "path": "tests/testsuite/rendering/code_blocks_fenced_with_indent/src/SUMMARY.md",
    "content": "# Summary\n\n- [Code blocks fenced with indent](./code-blocks-fenced-with-indent.md)\n"
  },
  {
    "path": "tests/testsuite/rendering/code_blocks_fenced_with_indent/src/code-blocks-fenced-with-indent.md",
    "content": "# Code blocks fenced with indent\n\n```rust\n    // This has a first line that is indented.\n    println!(\"hello\");\n```\n"
  },
  {
    "path": "tests/testsuite/rendering/default_rust_edition/book.toml",
    "content": "[book]\ntitle = \"default_rust_edition\"\n\n[rust]\nedition = \"2021\"\n"
  },
  {
    "path": "tests/testsuite/rendering/default_rust_edition/expected/default-rust-edition.html",
    "content": "<h1 id=\"chapter-1\"><a class=\"header\" href=\"#chapter-1\">Chapter 1</a></h1>\n<pre class=\"playground\"><code class=\"language-rust edition2021\"><span class=\"boring\">#![allow(unused)]\n</span><span class=\"boring\">fn main() {\n</span>let x = 2021;\n<span class=\"boring\">}</span></code></pre>\n<pre class=\"playground\"><code class=\"language-rust edition2021\"><span class=\"boring\">#![allow(unused)]\n</span><span class=\"boring\">fn main() {\n</span>let x = 2021;\n<span class=\"boring\">}</span></code></pre>\n<pre class=\"playground\"><code class=\"language-rust edition2024\"><span class=\"boring\">#![allow(unused)]\n</span><span class=\"boring\">fn main() {\n</span>let x = 2024;\n<span class=\"boring\">}</span></code></pre>"
  },
  {
    "path": "tests/testsuite/rendering/default_rust_edition/src/SUMMARY.md",
    "content": "# Summary\n\n- [Default rust edition](./default-rust-edition.md)\n"
  },
  {
    "path": "tests/testsuite/rendering/default_rust_edition/src/default-rust-edition.md",
    "content": "# Chapter 1\n\n```rust\nlet x = 2021;\n```\n\n```rust,edition2021\nlet x = 2021;\n```\n\n```rust,edition2024\nlet x = 2024;\n```\n"
  },
  {
    "path": "tests/testsuite/rendering/edit_url_template/book.toml",
    "content": "[book]\ntitle = \"edit_url_template\"\n\n[output.html]\nedit-url-template = \"https://github.com/rust-lang/mdBook/edit/master/guide/{path}\"\n"
  },
  {
    "path": "tests/testsuite/rendering/edit_url_template/src/SUMMARY.md",
    "content": "- [Intro](README.md)\n"
  },
  {
    "path": "tests/testsuite/rendering/edit_url_template_explicit_src/book.toml",
    "content": "[book]\ntitle = \"edit_url_template\"\nsrc = \"src2\"\n\n[output.html]\nedit-url-template = \"https://github.com/rust-lang/mdBook/edit/master/guide/{path}\"\n"
  },
  {
    "path": "tests/testsuite/rendering/edit_url_template_explicit_src/src2/SUMMARY.md",
    "content": "- [Intro](README.md)\n"
  },
  {
    "path": "tests/testsuite/rendering/editable_rust_block/book.toml",
    "content": "[book]\ntitle = \"editable_rust_block\"\n\n[output.html.playground]\neditable = true\n"
  },
  {
    "path": "tests/testsuite/rendering/editable_rust_block/expected/editable-rust.html",
    "content": "<h1 id=\"chapter-1\"><a class=\"header\" href=\"#chapter-1\">Chapter 1</a></h1>\n<pre class=\"playground\"><code class=\"language-rust editable\">fn f() {\n    println!(\"hello\");\n}</code></pre>\n<pre class=\"playground\"><code class=\"language-rust\"><span class=\"boring\">#![allow(unused)]\n</span><span class=\"boring\">fn main() {\n</span>// Not editable.\n<span class=\"boring\">}</span></code></pre>"
  },
  {
    "path": "tests/testsuite/rendering/editable_rust_block/src/SUMMARY.md",
    "content": "# Summary\n\n- [Editable rust blocks](./editable-rust.md)\n"
  },
  {
    "path": "tests/testsuite/rendering/editable_rust_block/src/editable-rust.md",
    "content": "# Chapter 1\n\n```rust,editable\nfn f() {\n    println!(\"hello\");\n}\n```\n\n```rust\n// Not editable.\n```\n"
  },
  {
    "path": "tests/testsuite/rendering/first_chapter_is_copied_as_index_even_if_not_first_elem/src/SUMMARY.md",
    "content": "# Summary\n\n---\n\n- [None of these should be treated as the \"index chapter\"]()\n\n# Part 1\n\n- [Not this either]()\n- [Chapter 1](./chapter_1.md)\n- [And not this]()\n"
  },
  {
    "path": "tests/testsuite/rendering/fontawesome/book.toml",
    "content": "[book]\ntitle = \"fontawesome\"\n"
  },
  {
    "path": "tests/testsuite/rendering/fontawesome/expected/fa.html",
    "content": "<h1 id=\"chapter-1\"><a class=\"header\" href=\"#chapter-1\">Chapter 1</a></h1>\n<p><span class=\"fa-svg extra-class\" id=\"example1\"><svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d=\"M47.6 300.4L228.3 469.1c7.5 7 17.4 10.9 27.7 10.9s20.2-3.9 27.7-10.9L464.4 300.4c30.4-28.3 47.6-68 47.6-109.5v-5.8c0-69.9-50.5-129.5-119.4-141C347 36.5 300.6 51.4 268 84L256 96 244 84c-32.6-32.6-79-47.5-124.6-39.9C50.5 55.6 0 115.2 0 185.1v5.8c0 41.5 17.2 81.2 47.6 109.5z\"/></svg></span></p>\n<p><span class=\"fa-svg\"><svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d=\"M272 304h-96C78.8 304 0 382.8 0 480c0 17.67 14.33 32 32 32h384c17.67 0 32-14.33 32-32C448 382.8 369.2 304 272 304zM48.99 464C56.89 400.9 110.8 352 176 352h96c65.16 0 119.1 48.95 127 112H48.99zM224 256c70.69 0 128-57.31 128-128c0-70.69-57.31-128-128-128S96 57.31 96 128C96 198.7 153.3 256 224 256zM224 48c44.11 0 80 35.89 80 80c0 44.11-35.89 80-80 80S144 172.1 144 128C144 83.89 179.9 48 224 48z\"/></svg></span></p>\n<p><span class=\"fa-svg\"><svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 448 512\"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d=\"M448 48V384C385 407 366 416 329 416C266 416 242 384 179 384C159 384 143 388 128 392V328C143 324 159 320 179 320C242 320 266 352 329 352C349 352 364 349 384 343V135C364 141 349 144 329 144C266 144 242 112 179 112C128 112 104 133 64 141V448C64 466 50 480 32 480S0 466 0 448V64C0 46 14 32 32 32S64 46 64 64V77C104 69 128 48 179 48C242 48 266 80 329 80C366 80 385 71 448 48Z\"/></svg></span></p>\n<p><i class=\"fas fa-heart\">Text prevents translation.</i></p>\n<p><i class=\"fa fa-does-not-exist\"></i></p>\n<p><span class=\"fa-svg\"><svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d=\"M288 192h17.1c22.1 38.3 63.5 64 110.9 64c11 0 21.8-1.4 32-4v4 32V480c0 17.7-14.3 32-32 32s-32-14.3-32-32V339.2L248 448h56c17.7 0 32 14.3 32 32s-14.3 32-32 32H160c-53 0-96-43-96-96V192.5c0-16.1-12-29.8-28-31.8l-7.9-1C10.5 157.6-1.9 141.6 .2 124s18.2-30 35.7-27.8l7.9 1c48 6 84.1 46.8 84.1 95.3v85.3c34.4-51.7 93.2-85.8 160-85.8zm160 26.5v0c-10 3.5-20.8 5.5-32 5.5c-28.4 0-54-12.4-71.6-32h0c-3.7-4.1-7-8.5-9.9-13.2C325.3 164 320 146.6 320 128v0V32 12 10.7C320 4.8 324.7 .1 330.6 0h.2c3.3 0 6.4 1.6 8.4 4.2l0 .1L352 21.3l27.2 36.3L384 64h64l4.8-6.4L480 21.3 492.8 4.3l0-.1c2-2.6 5.1-4.2 8.4-4.2h.2C507.3 .1 512 4.8 512 10.7V12 32v96c0 17.3-4.6 33.6-12.6 47.6c-11.3 19.8-29.6 35.2-51.4 42.9zM400 128c0-8.8-7.2-16-16-16s-16 7.2-16 16s7.2 16 16 16s16-7.2 16-16zm48 16c8.8 0 16-7.2 16-16s-7.2-16-16-16s-16 7.2-16 16s7.2 16 16 16z\"/></svg></span></p>"
  },
  {
    "path": "tests/testsuite/rendering/fontawesome/src/SUMMARY.md",
    "content": "# Summary\n\n- [Font Awesome](./fa.md)\n"
  },
  {
    "path": "tests/testsuite/rendering/fontawesome/src/fa.md",
    "content": "# Chapter 1\n\n<i id=\"example1\" class=\"fas fa-heart extra-class\"></i>\n\n<i class=\"fa fa-user\"></i>\n\n<i class=\"fab fa-font-awesome\"></i>\n\n<i class=\"fas fa-heart\">Text prevents translation.</i>\n\n<i class=\"fa fa-does-not-exist\"></i>\n\n<i class=\"fa-solid fa-cat\"></i>\n"
  },
  {
    "path": "tests/testsuite/rendering/fontawesome_error/book.toml",
    "content": "[book]\ntitle = \"fontawesome_error\"\n\n[output.html]\ngit-repository-url = \"https://github.com/example/test\"\ngit-repository-icon = \"fa-github\"\n"
  },
  {
    "path": "tests/testsuite/rendering/fontawesome_error/src/SUMMARY.md",
    "content": "# Summary\n\n- [Chapter 1](./chapter_1.md)\n"
  },
  {
    "path": "tests/testsuite/rendering/fontawesome_error/src/chapter_1.md",
    "content": "# Chapter 1\n\nHello, world!\n"
  },
  {
    "path": "tests/testsuite/rendering/header_links/book.toml",
    "content": "[book]\ntitle = \"header_links\"\n"
  },
  {
    "path": "tests/testsuite/rendering/header_links/expected/header_links.html",
    "content": "<h1 id=\"header-links\"><a class=\"header\" href=\"#header-links\">Header Links</a></h1>\n<h2 id=\"foobar\"><a class=\"header\" href=\"#foobar\">Foo^bar</a></h2>\n<h3 id=\"\"><a class=\"header\" href=\"#\"></a></h3>\n<h4 id=\"-1\"><a class=\"header\" href=\"#-1\"></a></h4>\n<h2 id=\"hï\"><a class=\"header\" href=\"#hï\">Hï</a></h2>\n<h2 id=\"repeat\"><a class=\"header\" href=\"#repeat\">Repeat</a></h2>\n<h2 id=\"repeat-1\"><a class=\"header\" href=\"#repeat-1\">Repeat</a></h2>\n<h2 id=\"repeat-2\"><a class=\"header\" href=\"#repeat-2\">Repeat</a></h2>\n<h2 id=\"repeat-1-1\"><a class=\"header\" href=\"#repeat-1-1\">Repeat 1</a></h2>\n<h2 id=\"with-emphasis-bold-bold_emphasis-code-escaped-html-link-httpsexamplecom\"><a class=\"header\" href=\"#with-emphasis-bold-bold_emphasis-code-escaped-html-link-httpsexamplecom\"><!--comment--> With <em>emphasis</em> <strong>bold</strong> <strong><em>bold_emphasis</em></strong> <code>code</code> &lt;escaped&gt; <span>html</span> <a href=\"https://example.com/link\">link</a> <a href=\"https://example.com/\">https://example.com/</a></a></h2>"
  },
  {
    "path": "tests/testsuite/rendering/header_links/src/SUMMARY.md",
    "content": "# Summary\n\n- [Header Links](./header_links.md)\n"
  },
  {
    "path": "tests/testsuite/rendering/header_links/src/header_links.md",
    "content": "# Header Links\n\n## Foo^bar\n\n###\n\n####\n\n## Hï\n\n## Repeat\n## Repeat\n## Repeat\n\n## Repeat 1\n\n## <!--comment--> With *emphasis* **bold** **_bold_emphasis_** `code` \\<escaped> <span>html</span> [link] <https://example.com/>\n\n[link]: https://example.com/link\n"
  },
  {
    "path": "tests/testsuite/rendering/hidelines/book.toml",
    "content": "[book]\ntitle = \"hidelines\"\n\n[output.html.code.hidelines]\npython = \"~\"\n"
  },
  {
    "path": "tests/testsuite/rendering/hidelines/expected/hide-lines.html",
    "content": "<h1 id=\"hide-lines\"><a class=\"header\" href=\"#hide-lines\">Hide Lines</a></h1>\n<pre><code class=\"language-python\"><span class=\"boring\">hidden()\n</span>nothidden():\n<span class=\"boring\">    hidden()\n</span><span class=\"boring\">    hidden()\n</span>    nothidden()\n</code></pre>\n<pre><code class=\"language-python hidelines=!!!\"><span class=\"boring\">hidden()\n</span>nothidden():\n<span class=\"boring\">    hidden()\n</span><span class=\"boring\">    hidden()\n</span>    nothidden()\n</code></pre>\n<pre class=\"playground\"><code class=\"language-rust\"><span class=\"boring\">#![allow(unused)]\n</span>#![allow(something)]\n<span class=\"boring\">fn main() {\n</span><span class=\"boring\">\n</span>#hidden();\n<span class=\"boring\">hidden();\n</span># not_hidden();\n#[not_hidden]\nnot_hidden();\n<span class=\"boring\">}</span></code></pre>"
  },
  {
    "path": "tests/testsuite/rendering/hidelines/src/SUMMARY.md",
    "content": "# Summary\n\n- [Hide Lines](./hide-lines.md)\n"
  },
  {
    "path": "tests/testsuite/rendering/hidelines/src/hide-lines.md",
    "content": "# Hide Lines\n\n```python\n~hidden()\nnothidden():\n~    hidden()\n    ~hidden()\n    nothidden()\n```\n\n```python,hidelines=!!!\n!!!hidden()\nnothidden():\n!!!    hidden()\n    !!!hidden()\n    nothidden()\n```\n\n```rust\n#![allow(something)]\n#\n#hidden();\n# hidden();\n## not_hidden();\n#[not_hidden]\nnot_hidden();\n```\n"
  },
  {
    "path": "tests/testsuite/rendering/html_blocks/book.toml",
    "content": "[book]\ntitle = \"html_blocks\"\n"
  },
  {
    "path": "tests/testsuite/rendering/html_blocks/expected/comment-in-list.html",
    "content": "<ul>\n<li>\n<p>List</p>\n  <!-- Comment in list -->\n</li>\n</ul>"
  },
  {
    "path": "tests/testsuite/rendering/html_blocks/expected/script-in-block.html",
    "content": "<div>\n    HTML block start\n<script>\n    // script stuff <here>\n</script>\n    &lt; still in block\n</div>"
  },
  {
    "path": "tests/testsuite/rendering/html_blocks/src/SUMMARY.md",
    "content": "# Summary\n\n- [Comment in list](./comment-in-list.md)\n- [Script in block](./script-in-block.md)\n"
  },
  {
    "path": "tests/testsuite/rendering/html_blocks/src/comment-in-list.md",
    "content": "* List\n\n    <!-- Comment in list -->\n"
  },
  {
    "path": "tests/testsuite/rendering/html_blocks/src/script-in-block.md",
    "content": "<div>\n    HTML block start\n<script>\n    // script stuff <here>\n</script>\n    &lt; still in block\n</div>\n"
  },
  {
    "path": "tests/testsuite/rendering.rs",
    "content": "//! Tests for HTML rendering.\n//!\n//! Note that markdown-specific rendering tests are in the `markdown` module.\n\nuse crate::prelude::*;\n\n// Checks that edit-url-template works.\n#[test]\nfn edit_url_template() {\n    BookTest::from_dir(\"rendering/edit_url_template\").check_file_contains(\n        \"book/index.html\",\n        \"<a href=\\\"https://github.com/rust-lang/mdBook/edit/master/guide/src/README.md\\\" \\\n         title=\\\"Suggest an edit\\\" aria-label=\\\"Suggest an edit\\\" rel=\\\"edit\\\">\",\n    );\n}\n\n// Checks that an alternate `src` setting works with the edit url template.\n#[test]\nfn edit_url_template_explicit_src() {\n    BookTest::from_dir(\"rendering/edit_url_template_explicit_src\").check_file_contains(\n        \"book/index.html\",\n        \"<a href=\\\"https://github.com/rust-lang/mdBook/edit/master/guide/src2/README.md\\\" \\\n         title=\\\"Suggest an edit\\\" aria-label=\\\"Suggest an edit\\\" rel=\\\"edit\\\">\",\n    );\n}\n\n// Checks that index.html is generated correctly, even when the first few\n// chapters are drafts.\n#[test]\nfn first_chapter_is_copied_as_index_even_if_not_first_elem() {\n    BookTest::from_dir(\"rendering/first_chapter_is_copied_as_index_even_if_not_first_elem\")\n        // These two files should be equal.\n        .check_main_file(\n            \"book/chapter_1.html\",\n            str![[\n                r##\"<h1 id=\"chapter-1\"><a class=\"header\" href=\"#chapter-1\">Chapter 1</a></h1>\"##\n            ]],\n        )\n        .check_main_file(\n            \"book/index.html\",\n            str![[\n                r##\"<h1 id=\"chapter-1\"><a class=\"header\" href=\"#chapter-1\">Chapter 1</a></h1>\"##\n            ]],\n        );\n}\n\n// Fontawesome `<i>` tag support.\n#[test]\nfn fontawesome() {\n    BookTest::from_dir(\"rendering/fontawesome\")\n        .run(\"build\", |cmd| {\n            cmd.expect_stderr(str![[r#\"\n INFO Book building has started\n INFO Running the html backend\n WARN failed to find Font Awesome icon for icon `does-not-exist` with type `regular` in `fa.md`: Invalid Font Awesome icon name: visit https://fontawesome.com/icons?d=gallery&m=free to see valid names\n INFO HTML book written to `[ROOT]/book`\n\n\"#]]);\n        })\n        .check_all_main_files();\n}\n\n// Verifies that an invalid `git-repository-icon` in book.toml produces a\n// helpful error message with the icon name, type, and a link to FontAwesome.\n#[test]\nfn fontawesome_error_message() {\n    BookTest::from_dir(\"rendering/fontawesome_error\")\n        .run(\"build\", |cmd| {\n            cmd.expect_failure();\n            cmd.expect_stderr(str![[r#\"\n INFO Book building has started\n INFO Running the html backend\nERROR Rendering failed\n[TAB]Caused by: Error rendering \"index\" line [..], col [..]: Unknown Font Awesome icon `github` for type `regular`. Hint: check the icon name and prefix (fas (solid), fab (brands), or far (regular)) at https://fontawesome.com/v6/search?m=free\n[TAB]Caused by: Unknown Font Awesome icon `github` for type `regular`. Hint: check the icon name and prefix (fas (solid), fab (brands), or far (regular)) at https://fontawesome.com/v6/search?m=free\n\n\"#]]);\n        });\n}\n\n// Tests the rendering when setting the default rust edition.\n#[test]\nfn default_rust_edition() {\n    BookTest::from_dir(\"rendering/default_rust_edition\").check_all_main_files();\n}\n\n// Tests the rendering for editable code blocks.\n#[test]\nfn editable_rust_block() {\n    BookTest::from_dir(\"rendering/editable_rust_block\").check_all_main_files();\n}\n\n// Tests for custom hide lines.\n#[test]\nfn hidelines() {\n    BookTest::from_dir(\"rendering/hidelines\").check_all_main_files();\n}\n\n// Tests for code blocks of basic rust code.\n#[test]\nfn language_rust_playground() {\n    fn expect(input: &str, info: &str, expected: impl snapbox::IntoData) {\n        BookTest::init(|_| {})\n            .change_file(\"book.toml\", \"output.html.playground.editable = true\")\n            .change_file(\"src/chapter_1.md\", &format!(\"```rust {info}\\n{input}\\n```\"))\n            .check_main_file(\"book/chapter_1.html\", expected);\n    }\n    // No-main should be wrapped in `fn main` boring lines.\n    expect(\n        \"x()\",\n        \"\",\n        str![[r#\"\n<pre class=\"playground\"><code class=\"language-rust\"><span class=\"boring\">#![allow(unused)]\n</span><span class=\"boring\">fn main() {\n</span>x()\n<span class=\"boring\">}</span></code></pre>\n\"#]],\n    );\n    // `fn main` should not be wrapped, not boring.\n    expect(\n        \"fn main() {}\",\n        \"\",\n        str![[r#\"<pre class=\"playground\"><code class=\"language-rust\">fn main() {}</code></pre>\"#]],\n    );\n    // Lines starting with `#` are boring.\n    expect(\n        \"let s = \\\"foo\\n # bar\\n\\\";\",\n        \"editable\",\n        str![[r#\"\n<pre class=\"playground\"><code class=\"language-rust editable\">let s = \"foo\n<span class=\"boring\"> bar\n</span>\";</code></pre>\n\"#]],\n    );\n    // `##` is not boring and is used as an escape.\n    expect(\n        \"let s = \\\"foo\\n ## bar\\n\\\";\",\n        \"editable\",\n        str![[r#\"\n<pre class=\"playground\"><code class=\"language-rust editable\">let s = \"foo\n # bar\n\";</code></pre>\n\"#]],\n    );\n    // `#` on a line by itself is boring.\n    expect(\n        \"let s = \\\"foo\\n # bar\\n#\\n\\\";\",\n        \"editable\",\n        str![[r#\"\n<pre class=\"playground\"><code class=\"language-rust editable\">let s = \"foo\n<span class=\"boring\"> bar\n</span><span class=\"boring\">\n</span>\";</code></pre>\n\"#]],\n    );\n    // `#` must be followed by a space to be boring.\n    expect(\n        \"#x;\",\n        \"\",\n        str![[r#\"\n<pre class=\"playground\"><code class=\"language-rust\"><span class=\"boring\">#![allow(unused)]\n</span><span class=\"boring\">fn main() {\n</span>#x;\n<span class=\"boring\">}</span></code></pre>\n\"#]],\n    );\n\n    // Other classes like \"ignore\" should not change things, and the class is\n    // included in the code tag.\n    expect(\n        \"let s = \\\"foo\\n # bar\\n\\\";\",\n        \"ignore\",\n        str![[r#\"\n<pre><code class=\"language-rust ignore\">let s = \"foo\n<span class=\"boring\"> bar\n</span>\";</code></pre>\n\"#]],\n    );\n    // Inner attributes and normal attributes are not boring.\n    expect(\n        \"#![no_std]\\nlet s = \\\"foo\\\";\\n #[some_attr]\",\n        \"editable\",\n        str![[r#\"\n<pre class=\"playground\"><code class=\"language-rust editable\">#![no_std]\nlet s = \"foo\";\n #[some_attr]</code></pre>\n\"#]],\n    );\n}\n\n// Rust code block in a list.\n#[test]\nfn code_block_in_list() {\n    BookTest::init(|_| {})\n        .change_file(\n            \"src/chapter_1.md\",\n            r#\"- inside list\n\n  ```rust\n  fn foo() {\n    let x = 1;\n  }\n  ```\n\"#,\n        )\n        .check_main_file(\n            \"book/chapter_1.html\",\n            str![[r#\"\n<ul>\n<li>\n<p>inside list</p>\n<pre class=\"playground\"><code class=\"language-rust\"><span class=\"boring\">#![allow(unused)]\n</span><span class=\"boring\">fn main() {\n</span>fn foo() {\n  let x = 1;\n}\n<span class=\"boring\">}</span></code></pre>\n</li>\n</ul>\n\"#]],\n        );\n}\n\n// Checks the rendering of links added to headers.\n#[test]\nfn header_links() {\n    BookTest::from_dir(\"rendering/header_links\").check_all_main_files();\n}\n\n// A corrupted HTML end tag.\n#[test]\nfn busted_end_tag() {\n    BookTest::init(|_| {})\n        .change_file(\"src/chapter_1.md\", \"<div>x<span>foo</span/>y</div>\")\n        .run(\"build\", |cmd| {\n            cmd.expect_stderr(str![[r#\"\n INFO Book building has started\n INFO Running the html backend\n WARN html parse error in `chapter_1.md`: Self-closing end tag\nHtml text was:\n<div>x<span>foo</span/>y</div>\n INFO HTML book written to `[ROOT]/book`\n\n\"#]]);\n        })\n        .check_main_file(\"book/chapter_1.html\", str![\"<div>x<span>foo</span>y</div>\"]);\n}\n\n// Various html blocks.\n#[test]\nfn html_blocks() {\n    BookTest::from_dir(\"rendering/html_blocks\").check_all_main_files();\n}\n\n// Test for a fenced code block that is also indented.\n#[test]\nfn code_block_fenced_with_indent() {\n    BookTest::from_dir(\"rendering/code_blocks_fenced_with_indent\").check_all_main_files();\n}\n\n// Unclosed HTML tags.\n//\n// Note that the HTML parsing algorithm is much more complicated than what\n// this is checking.\n#[test]\nfn unclosed_html_tags() {\n    BookTest::init(|_| {})\n        .change_file(\"src/chapter_1.md\", \"<div>x<span>foo<i>xyz\")\n        .run(\"build\", |cmd| {\n            cmd.expect_stderr(str![[r#\"\n INFO Book building has started\n INFO Running the html backend\n WARN unclosed HTML tag `<i>` found in `chapter_1.md`\n WARN unclosed HTML tag `<span>` found in `chapter_1.md`\n WARN unclosed HTML tag `<div>` found in `chapter_1.md`\n INFO HTML book written to `[ROOT]/book`\n\n\"#]]);\n        })\n        .check_main_file(\n            \"book/chapter_1.html\",\n            str![\"<div>x<span>foo<i>xyz</i></span></div>\"],\n        );\n}\n\n// Test for HTML tags out of sync.\n#[test]\nfn unbalanced_html_tags() {\n    BookTest::init(|_| {})\n        .change_file(\"src/chapter_1.md\", \"<div>x<span>foo</div></span>\")\n        .run(\"build\", |cmd| {\n            cmd.expect_stderr(str![[r#\"\n INFO Book building has started\n INFO Running the html backend\n WARN unexpected HTML end tag `</div>` found in `chapter_1.md`\nCheck that the HTML tags are properly balanced.\n WARN unclosed HTML tag `<div>` found in `chapter_1.md`\n INFO HTML book written to `[ROOT]/book`\n\n\"#]]);\n        })\n        .check_main_file(\"book/chapter_1.html\", str![\"<div>x<span>foo</span></div>\"]);\n}\n\n// Test for bug with unbalanced HTML handling in the heading.\n#[test]\nfn heading_with_unbalanced_html() {\n    BookTest::init(|_| {})\n        .change_file(\"src/chapter_1.md\", \"### Option<T>\")\n        .run(\"build\", |cmd| {\n            cmd.expect_stderr(str![[r#\"\n INFO Book building has started\n INFO Running the html backend\n WARN unclosed HTML tag `<t>` found in `chapter_1.md` while exiting Heading(H3)\nHTML tags must be closed before exiting a markdown element.\n INFO HTML book written to `[ROOT]/book`\n\n\"#]]);\n        })\n        .check_main_file(\n            \"book/chapter_1.html\",\n            str![[r##\"<h3 id=\"option\"><a class=\"header\" href=\"#option\">Option<t></t></a></h3>\"##]],\n        );\n}\n"
  },
  {
    "path": "tests/testsuite/search/chapter_settings_validation_error/book.toml",
    "content": "[book]\ntitle = \"Search Test\"\n\n[output.html.search.chapter]\n\"does-not-exist\" = { enable = false }\n"
  },
  {
    "path": "tests/testsuite/search/chapter_settings_validation_error/src/SUMMARY.md",
    "content": ""
  },
  {
    "path": "tests/testsuite/search/disable_search_chapter/book.toml",
    "content": "[book]\ntitle = \"disable_search_chapter\"\n\n[output.html.search.chapter]\n\"second\" = { enable = false }\n\"first/disable_me.md\" = { enable = false }\n"
  },
  {
    "path": "tests/testsuite/search/disable_search_chapter/src/SUMMARY.md",
    "content": "# Summary\n\n- [Keep Me](first/keep_me.md)\n- [Disable Me](first/disable_me.md)\n- [Second](second.md)\n    - [Second Nested](second/nested.md)\n"
  },
  {
    "path": "tests/testsuite/search/disable_search_chapter/src/first/disable_me.md",
    "content": "# Disable Me\n"
  },
  {
    "path": "tests/testsuite/search/disable_search_chapter/src/first/keep_me.md",
    "content": "# Keep Me\n"
  },
  {
    "path": "tests/testsuite/search/disable_search_chapter/src/second/nested.md",
    "content": "# Second Nested\n"
  },
  {
    "path": "tests/testsuite/search/disable_search_chapter/src/second.md",
    "content": "# Second\n"
  },
  {
    "path": "tests/testsuite/search/reasonable_search_index/expected_index.js",
    "content": "window.search = Object.assign(window.search, JSON.parse('{\"doc_urls\":[\"intro.html#introduction\",\"intro.html#sneaky\",\"first/index.html#first-chapter\",\"first/index.html#some-section\",\"first/includes.html#includes\",\"first/includes.html#summary\",\"first/unicode.html#unicode-stress-tests\",\"first/no-headers.html\",\"first/duplicate-headers.html#duplicate-headers\",\"first/duplicate-headers.html#header-text\",\"first/duplicate-headers.html#header-text-1\",\"first/duplicate-headers.html#header-text-2\",\"first/heading-attributes.html#attrs\",\"first/heading-attributes.html#heading-with-classes\",\"first/heading-attributes.html#both\"],\"index\":{\"documentStore\":{\"docInfo\":{\"0\":{\"body\":3,\"breadcrumbs\":2,\"title\":1},\"1\":{\"body\":9,\"breadcrumbs\":2,\"title\":1},\"10\":{\"body\":0,\"breadcrumbs\":6,\"title\":2},\"11\":{\"body\":0,\"breadcrumbs\":6,\"title\":2},\"12\":{\"body\":0,\"breadcrumbs\":6,\"title\":2},\"13\":{\"body\":0,\"breadcrumbs\":6,\"title\":2},\"14\":{\"body\":0,\"breadcrumbs\":7,\"title\":3},\"2\":{\"body\":2,\"breadcrumbs\":4,\"title\":2},\"3\":{\"body\":0,\"breadcrumbs\":3,\"title\":1},\"4\":{\"body\":0,\"breadcrumbs\":4,\"title\":1},\"5\":{\"body\":10,\"breadcrumbs\":4,\"title\":1},\"6\":{\"body\":29,\"breadcrumbs\":6,\"title\":3},\"7\":{\"body\":6,\"breadcrumbs\":3,\"title\":2},\"8\":{\"body\":5,\"breadcrumbs\":6,\"title\":2},\"9\":{\"body\":0,\"breadcrumbs\":6,\"title\":2}},\"docs\":{\"0\":{\"body\":\"Here’s some interesting text…\",\"breadcrumbs\":\"Introduction » Introduction\",\"id\":\"0\",\"title\":\"Introduction\"},\"1\":{\"body\":\"I put <HTML> in here! Sneaky inline event . But regular inline is indexed.\",\"breadcrumbs\":\"Introduction » Sneaky\",\"id\":\"1\",\"title\":\"Sneaky\"},\"10\":{\"body\":\"\",\"breadcrumbs\":\"First Chapter » Duplicate Headers » Header Text\",\"id\":\"10\",\"title\":\"Header Text\"},\"11\":{\"body\":\"\",\"breadcrumbs\":\"First Chapter » Duplicate Headers » header-text\",\"id\":\"11\",\"title\":\"header-text\"},\"12\":{\"body\":\"\",\"breadcrumbs\":\"First Chapter » Heading Attributes » Heading Attributes\",\"id\":\"12\",\"title\":\"Heading Attributes\"},\"13\":{\"body\":\"\",\"breadcrumbs\":\"First Chapter » Heading Attributes » Heading with classes\",\"id\":\"13\",\"title\":\"Heading with classes\"},\"14\":{\"body\":\"\",\"breadcrumbs\":\"First Chapter » Heading Attributes » Heading with id and classes\",\"id\":\"14\",\"title\":\"Heading with id and classes\"},\"2\":{\"body\":\"more text.\",\"breadcrumbs\":\"First Chapter » First Chapter\",\"id\":\"2\",\"title\":\"First Chapter\"},\"3\":{\"body\":\"\",\"breadcrumbs\":\"First Chapter » Some Section\",\"id\":\"3\",\"title\":\"Some Section\"},\"4\":{\"body\":\"\",\"breadcrumbs\":\"First Chapter » Includes » Includes\",\"id\":\"4\",\"title\":\"Includes\"},\"5\":{\"body\":\"Introduction First Chapter Includes Unicode No Headers Duplicate Headers Heading Attributes\",\"breadcrumbs\":\"First Chapter » Includes » Summary\",\"id\":\"5\",\"title\":\"Summary\"},\"6\":{\"body\":\"Please be careful editing, this contains carefully crafted characters. Two byte character: spatiëring Combining character: spatiëring Three byte character: 书こんにちは Four byte character: 𐌀‮𐌁‮𐌂‮𐌃‮𐌄‮𐌅‮𐌆‮𐌇‮𐌈‬ Right-to-left: مرحبا Emoticons: 🔊 😍 💜 1️⃣ right-to-left mark: hello באמת!‏ Zalgo: ǫ̛̖̱̗̝͈̋͒͋̏ͥͫ̒̆ͩ̏͌̾͊͐ͪ̾̚\",\"breadcrumbs\":\"First Chapter » Unicode » Unicode stress tests\",\"id\":\"6\",\"title\":\"Unicode stress tests\"},\"7\":{\"body\":\"Capybara capybara capybara. Capybara capybara capybara. ThisLongWordIsIncludedSoWeCanCheckThatSufficientlyLongWordsAreOmittedFromTheSearchIndex.\",\"breadcrumbs\":\"First Chapter » No Headers\",\"id\":\"7\",\"title\":\"First Chapter\"},\"8\":{\"body\":\"This page validates behaviour of duplicate headers.\",\"breadcrumbs\":\"First Chapter » Duplicate Headers » Duplicate headers\",\"id\":\"8\",\"title\":\"Duplicate headers\"},\"9\":{\"body\":\"\",\"breadcrumbs\":\"First Chapter » Duplicate Headers » Header Text\",\"id\":\"9\",\"title\":\"Header Text\"}},\"length\":15,\"save\":true},\"fields\":[\"title\",\"body\",\"breadcrumbs\"],\"index\":{\"body\":{\"root\":{\"1\":{\"df\":1,\"docs\":{\"6\":{\"tf\":1.0}}},\"a\":{\"df\":0,\"docs\":{},\"t\":{\"df\":0,\"docs\":{},\"t\":{\"df\":0,\"docs\":{},\"r\":{\"df\":0,\"docs\":{},\"i\":{\"b\":{\"df\":0,\"docs\":{},\"u\":{\"df\":0,\"docs\":{},\"t\":{\"df\":2,\"docs\":{\"12\":{\"tf\":1.0},\"5\":{\"tf\":1.0}}}}},\"df\":0,\"docs\":{}}}}}},\"b\":{\"df\":0,\"docs\":{},\"e\":{\"df\":0,\"docs\":{},\"h\":{\"a\":{\"df\":0,\"docs\":{},\"v\":{\"df\":0,\"docs\":{},\"i\":{\"df\":0,\"docs\":{},\"o\":{\"df\":0,\"docs\":{},\"u\":{\"df\":0,\"docs\":{},\"r\":{\"df\":1,\"docs\":{\"8\":{\"tf\":1.0}}}}}}}},\"df\":0,\"docs\":{}}},\"y\":{\"df\":0,\"docs\":{},\"t\":{\"df\":0,\"docs\":{},\"e\":{\"df\":1,\"docs\":{\"6\":{\"tf\":1.7320508075688772}}}}}},\"c\":{\"a\":{\"df\":0,\"docs\":{},\"p\":{\"df\":0,\"docs\":{},\"y\":{\"b\":{\"a\":{\"df\":0,\"docs\":{},\"r\":{\"a\":{\"df\":1,\"docs\":{\"7\":{\"tf\":2.449489742783178}}},\"df\":0,\"docs\":{}}},\"df\":0,\"docs\":{}},\"df\":0,\"docs\":{}}},\"r\":{\"df\":0,\"docs\":{},\"e\":{\"df\":1,\"docs\":{\"6\":{\"tf\":1.0}},\"f\":{\"df\":0,\"docs\":{},\"u\":{\"df\":0,\"docs\":{},\"l\":{\"df\":0,\"docs\":{},\"l\":{\"df\":0,\"docs\":{},\"i\":{\"df\":1,\"docs\":{\"6\":{\"tf\":1.0}}}}}}}}}},\"df\":0,\"docs\":{},\"h\":{\"a\":{\"df\":0,\"docs\":{},\"p\":{\"df\":0,\"docs\":{},\"t\":{\"df\":0,\"docs\":{},\"e\":{\"df\":0,\"docs\":{},\"r\":{\"df\":3,\"docs\":{\"2\":{\"tf\":1.0},\"5\":{\"tf\":1.0},\"7\":{\"tf\":1.0}}}}}},\"r\":{\"a\":{\"c\":{\"df\":0,\"docs\":{},\"t\":{\"df\":1,\"docs\":{\"6\":{\"tf\":2.23606797749979}}}},\"df\":0,\"docs\":{}},\"df\":0,\"docs\":{}}},\"df\":0,\"docs\":{}},\"l\":{\"a\":{\"df\":0,\"docs\":{},\"s\":{\"df\":0,\"docs\":{},\"s\":{\"df\":2,\"docs\":{\"13\":{\"tf\":1.0},\"14\":{\"tf\":1.0}}}}},\"df\":0,\"docs\":{}},\"o\":{\"df\":0,\"docs\":{},\"m\":{\"b\":{\"df\":0,\"docs\":{},\"i\":{\"df\":0,\"docs\":{},\"n\":{\"df\":1,\"docs\":{\"6\":{\"tf\":1.0}}}}},\"df\":0,\"docs\":{}},\"n\":{\"df\":0,\"docs\":{},\"t\":{\"a\":{\"df\":0,\"docs\":{},\"i\":{\"df\":0,\"docs\":{},\"n\":{\"df\":1,\"docs\":{\"6\":{\"tf\":1.0}}}}},\"df\":0,\"docs\":{}}}},\"r\":{\"a\":{\"df\":0,\"docs\":{},\"f\":{\"df\":0,\"docs\":{},\"t\":{\"df\":1,\"docs\":{\"6\":{\"tf\":1.0}}}}},\"df\":0,\"docs\":{}}},\"d\":{\"df\":0,\"docs\":{},\"u\":{\"df\":0,\"docs\":{},\"p\":{\"df\":0,\"docs\":{},\"l\":{\"df\":0,\"docs\":{},\"i\":{\"c\":{\"df\":2,\"docs\":{\"5\":{\"tf\":1.0},\"8\":{\"tf\":1.4142135623730951}}},\"df\":0,\"docs\":{}}}}}},\"df\":0,\"docs\":{},\"e\":{\"d\":{\"df\":0,\"docs\":{},\"i\":{\"df\":0,\"docs\":{},\"t\":{\"df\":1,\"docs\":{\"6\":{\"tf\":1.0}}}}},\"df\":0,\"docs\":{},\"m\":{\"df\":0,\"docs\":{},\"o\":{\"df\":0,\"docs\":{},\"t\":{\"df\":0,\"docs\":{},\"i\":{\"c\":{\"df\":0,\"docs\":{},\"o\":{\"df\":0,\"docs\":{},\"n\":{\"df\":1,\"docs\":{\"6\":{\"tf\":1.0}}}}},\"df\":0,\"docs\":{}}}}},\"v\":{\"df\":0,\"docs\":{},\"e\":{\"df\":0,\"docs\":{},\"n\":{\"df\":0,\"docs\":{},\"t\":{\"df\":1,\"docs\":{\"1\":{\"tf\":1.0}}}}}}},\"f\":{\"df\":0,\"docs\":{},\"i\":{\"df\":0,\"docs\":{},\"r\":{\"df\":0,\"docs\":{},\"s\":{\"df\":0,\"docs\":{},\"t\":{\"df\":3,\"docs\":{\"2\":{\"tf\":1.0},\"5\":{\"tf\":1.0},\"7\":{\"tf\":1.0}}}}}},\"o\":{\"df\":0,\"docs\":{},\"u\":{\"df\":0,\"docs\":{},\"r\":{\"df\":1,\"docs\":{\"6\":{\"tf\":1.0}}}}}},\"h\":{\"df\":0,\"docs\":{},\"e\":{\"a\":{\"d\":{\"df\":4,\"docs\":{\"12\":{\"tf\":1.0},\"13\":{\"tf\":1.0},\"14\":{\"tf\":1.0},\"5\":{\"tf\":1.0}},\"e\":{\"df\":0,\"docs\":{},\"r\":{\"df\":5,\"docs\":{\"10\":{\"tf\":1.0},\"11\":{\"tf\":1.0},\"5\":{\"tf\":1.4142135623730951},\"8\":{\"tf\":1.4142135623730951},\"9\":{\"tf\":1.0}}}}},\"df\":0,\"docs\":{}},\"df\":0,\"docs\":{},\"l\":{\"df\":0,\"docs\":{},\"l\":{\"df\":0,\"docs\":{},\"o\":{\"df\":1,\"docs\":{\"6\":{\"tf\":1.0}}}}},\"r\":{\"df\":0,\"docs\":{},\"e\":{\"df\":1,\"docs\":{\"1\":{\"tf\":1.0}},\"’\":{\"df\":1,\"docs\":{\"0\":{\"tf\":1.0}}}}}},\"t\":{\"df\":0,\"docs\":{},\"m\":{\"df\":0,\"docs\":{},\"l\":{\"df\":1,\"docs\":{\"1\":{\"tf\":1.0}}}}}},\"i\":{\"d\":{\"df\":1,\"docs\":{\"14\":{\"tf\":1.0}}},\"df\":0,\"docs\":{},\"n\":{\"c\":{\"df\":0,\"docs\":{},\"l\":{\"df\":0,\"docs\":{},\"u\":{\"d\":{\"df\":2,\"docs\":{\"4\":{\"tf\":1.0},\"5\":{\"tf\":1.0}}},\"df\":0,\"docs\":{}}}},\"d\":{\"df\":0,\"docs\":{},\"e\":{\"df\":0,\"docs\":{},\"x\":{\"df\":1,\"docs\":{\"1\":{\"tf\":1.0}}}}},\"df\":0,\"docs\":{},\"l\":{\"df\":0,\"docs\":{},\"i\":{\"df\":0,\"docs\":{},\"n\":{\"df\":1,\"docs\":{\"1\":{\"tf\":1.4142135623730951}}}}},\"t\":{\"df\":0,\"docs\":{},\"e\":{\"df\":0,\"docs\":{},\"r\":{\"df\":0,\"docs\":{},\"e\":{\"df\":0,\"docs\":{},\"s\":{\"df\":0,\"docs\":{},\"t\":{\"df\":1,\"docs\":{\"0\":{\"tf\":1.0}}}}}}},\"r\":{\"df\":0,\"docs\":{},\"o\":{\"d\":{\"df\":0,\"docs\":{},\"u\":{\"c\":{\"df\":0,\"docs\":{},\"t\":{\"df\":2,\"docs\":{\"0\":{\"tf\":1.0},\"5\":{\"tf\":1.0}}}},\"df\":0,\"docs\":{}}},\"df\":0,\"docs\":{}}}}}},\"l\":{\"df\":0,\"docs\":{},\"e\":{\"df\":0,\"docs\":{},\"f\":{\"df\":0,\"docs\":{},\"t\":{\"df\":1,\"docs\":{\"6\":{\"tf\":1.4142135623730951}}}}}},\"m\":{\"a\":{\"df\":0,\"docs\":{},\"r\":{\"df\":0,\"docs\":{},\"k\":{\"df\":1,\"docs\":{\"6\":{\"tf\":1.0}}}}},\"df\":0,\"docs\":{},\"o\":{\"df\":0,\"docs\":{},\"r\":{\"df\":0,\"docs\":{},\"e\":{\"df\":1,\"docs\":{\"2\":{\"tf\":1.0}}}}}},\"p\":{\"a\":{\"df\":0,\"docs\":{},\"g\":{\"df\":0,\"docs\":{},\"e\":{\"df\":1,\"docs\":{\"8\":{\"tf\":1.0}}}}},\"df\":0,\"docs\":{},\"l\":{\"df\":0,\"docs\":{},\"e\":{\"a\":{\"df\":0,\"docs\":{},\"s\":{\"df\":1,\"docs\":{\"6\":{\"tf\":1.0}}}},\"df\":0,\"docs\":{}}},\"u\":{\"df\":0,\"docs\":{},\"t\":{\"df\":1,\"docs\":{\"1\":{\"tf\":1.0}}}}},\"r\":{\"df\":0,\"docs\":{},\"e\":{\"df\":0,\"docs\":{},\"g\":{\"df\":0,\"docs\":{},\"u\":{\"df\":0,\"docs\":{},\"l\":{\"a\":{\"df\":0,\"docs\":{},\"r\":{\"df\":1,\"docs\":{\"1\":{\"tf\":1.0}}}},\"df\":0,\"docs\":{}}}}},\"i\":{\"df\":0,\"docs\":{},\"g\":{\"df\":0,\"docs\":{},\"h\":{\"df\":0,\"docs\":{},\"t\":{\"df\":1,\"docs\":{\"6\":{\"tf\":1.4142135623730951}}}}}}},\"s\":{\"df\":0,\"docs\":{},\"e\":{\"c\":{\"df\":0,\"docs\":{},\"t\":{\"df\":0,\"docs\":{},\"i\":{\"df\":0,\"docs\":{},\"o\":{\"df\":0,\"docs\":{},\"n\":{\"df\":1,\"docs\":{\"3\":{\"tf\":1.0}}}}}}},\"df\":0,\"docs\":{}},\"n\":{\"df\":0,\"docs\":{},\"e\":{\"a\":{\"df\":0,\"docs\":{},\"k\":{\"df\":0,\"docs\":{},\"i\":{\"df\":1,\"docs\":{\"1\":{\"tf\":1.4142135623730951}}}}},\"df\":0,\"docs\":{}}},\"p\":{\"a\":{\"df\":0,\"docs\":{},\"t\":{\"df\":0,\"docs\":{},\"i\":{\"df\":0,\"docs\":{},\"e\":{\"df\":0,\"docs\":{},\"̈\":{\"df\":0,\"docs\":{},\"r\":{\"df\":1,\"docs\":{\"6\":{\"tf\":1.0}}}}},\"ë\":{\"df\":0,\"docs\":{},\"r\":{\"df\":1,\"docs\":{\"6\":{\"tf\":1.0}}}}}}},\"df\":0,\"docs\":{}},\"t\":{\"df\":0,\"docs\":{},\"r\":{\"df\":0,\"docs\":{},\"e\":{\"df\":0,\"docs\":{},\"s\":{\"df\":0,\"docs\":{},\"s\":{\"df\":1,\"docs\":{\"6\":{\"tf\":1.0}}}}}}},\"u\":{\"df\":0,\"docs\":{},\"m\":{\"df\":0,\"docs\":{},\"m\":{\"a\":{\"df\":0,\"docs\":{},\"r\":{\"df\":0,\"docs\":{},\"i\":{\"df\":1,\"docs\":{\"5\":{\"tf\":1.0}}}}},\"df\":0,\"docs\":{}}}}},\"t\":{\"df\":0,\"docs\":{},\"e\":{\"df\":0,\"docs\":{},\"s\":{\"df\":0,\"docs\":{},\"t\":{\"df\":1,\"docs\":{\"6\":{\"tf\":1.0}}}},\"x\":{\"df\":0,\"docs\":{},\"t\":{\"df\":5,\"docs\":{\"0\":{\"tf\":1.0},\"10\":{\"tf\":1.0},\"11\":{\"tf\":1.0},\"2\":{\"tf\":1.0},\"9\":{\"tf\":1.0}}}}},\"h\":{\"df\":0,\"docs\":{},\"r\":{\"df\":0,\"docs\":{},\"e\":{\"df\":0,\"docs\":{},\"e\":{\"df\":1,\"docs\":{\"6\":{\"tf\":1.0}}}}}},\"w\":{\"df\":0,\"docs\":{},\"o\":{\"df\":1,\"docs\":{\"6\":{\"tf\":1.0}}}}},\"u\":{\"df\":0,\"docs\":{},\"n\":{\"df\":0,\"docs\":{},\"i\":{\"c\":{\"df\":0,\"docs\":{},\"o\":{\"d\":{\"df\":2,\"docs\":{\"5\":{\"tf\":1.0},\"6\":{\"tf\":1.0}}},\"df\":0,\"docs\":{}}},\"df\":0,\"docs\":{}}}},\"v\":{\"a\":{\"df\":0,\"docs\":{},\"l\":{\"df\":0,\"docs\":{},\"i\":{\"d\":{\"df\":1,\"docs\":{\"8\":{\"tf\":1.0}}},\"df\":0,\"docs\":{}}}},\"df\":0,\"docs\":{}},\"z\":{\"a\":{\"df\":0,\"docs\":{},\"l\":{\"df\":0,\"docs\":{},\"g\":{\"df\":0,\"docs\":{},\"o\":{\"df\":1,\"docs\":{\"6\":{\"tf\":1.0}}}}}},\"df\":0,\"docs\":{}}}},\"breadcrumbs\":{\"root\":{\"1\":{\"df\":1,\"docs\":{\"6\":{\"tf\":1.0}}},\"a\":{\"df\":0,\"docs\":{},\"t\":{\"df\":0,\"docs\":{},\"t\":{\"df\":0,\"docs\":{},\"r\":{\"df\":0,\"docs\":{},\"i\":{\"b\":{\"df\":0,\"docs\":{},\"u\":{\"df\":0,\"docs\":{},\"t\":{\"df\":4,\"docs\":{\"12\":{\"tf\":1.7320508075688772},\"13\":{\"tf\":1.0},\"14\":{\"tf\":1.0},\"5\":{\"tf\":1.0}}}}},\"df\":0,\"docs\":{}}}}}},\"b\":{\"df\":0,\"docs\":{},\"e\":{\"df\":0,\"docs\":{},\"h\":{\"a\":{\"df\":0,\"docs\":{},\"v\":{\"df\":0,\"docs\":{},\"i\":{\"df\":0,\"docs\":{},\"o\":{\"df\":0,\"docs\":{},\"u\":{\"df\":0,\"docs\":{},\"r\":{\"df\":1,\"docs\":{\"8\":{\"tf\":1.0}}}}}}}},\"df\":0,\"docs\":{}}},\"y\":{\"df\":0,\"docs\":{},\"t\":{\"df\":0,\"docs\":{},\"e\":{\"df\":1,\"docs\":{\"6\":{\"tf\":1.7320508075688772}}}}}},\"c\":{\"a\":{\"df\":0,\"docs\":{},\"p\":{\"df\":0,\"docs\":{},\"y\":{\"b\":{\"a\":{\"df\":0,\"docs\":{},\"r\":{\"a\":{\"df\":1,\"docs\":{\"7\":{\"tf\":2.449489742783178}}},\"df\":0,\"docs\":{}}},\"df\":0,\"docs\":{}},\"df\":0,\"docs\":{}}},\"r\":{\"df\":0,\"docs\":{},\"e\":{\"df\":1,\"docs\":{\"6\":{\"tf\":1.0}},\"f\":{\"df\":0,\"docs\":{},\"u\":{\"df\":0,\"docs\":{},\"l\":{\"df\":0,\"docs\":{},\"l\":{\"df\":0,\"docs\":{},\"i\":{\"df\":1,\"docs\":{\"6\":{\"tf\":1.0}}}}}}}}}},\"df\":0,\"docs\":{},\"h\":{\"a\":{\"df\":0,\"docs\":{},\"p\":{\"df\":0,\"docs\":{},\"t\":{\"df\":0,\"docs\":{},\"e\":{\"df\":0,\"docs\":{},\"r\":{\"df\":13,\"docs\":{\"10\":{\"tf\":1.0},\"11\":{\"tf\":1.0},\"12\":{\"tf\":1.0},\"13\":{\"tf\":1.0},\"14\":{\"tf\":1.0},\"2\":{\"tf\":1.7320508075688772},\"3\":{\"tf\":1.0},\"4\":{\"tf\":1.0},\"5\":{\"tf\":1.4142135623730951},\"6\":{\"tf\":1.0},\"7\":{\"tf\":1.4142135623730951},\"8\":{\"tf\":1.0},\"9\":{\"tf\":1.0}}}}}},\"r\":{\"a\":{\"c\":{\"df\":0,\"docs\":{},\"t\":{\"df\":1,\"docs\":{\"6\":{\"tf\":2.23606797749979}}}},\"df\":0,\"docs\":{}},\"df\":0,\"docs\":{}}},\"df\":0,\"docs\":{}},\"l\":{\"a\":{\"df\":0,\"docs\":{},\"s\":{\"df\":0,\"docs\":{},\"s\":{\"df\":2,\"docs\":{\"13\":{\"tf\":1.4142135623730951},\"14\":{\"tf\":1.4142135623730951}}}}},\"df\":0,\"docs\":{}},\"o\":{\"df\":0,\"docs\":{},\"m\":{\"b\":{\"df\":0,\"docs\":{},\"i\":{\"df\":0,\"docs\":{},\"n\":{\"df\":1,\"docs\":{\"6\":{\"tf\":1.0}}}}},\"df\":0,\"docs\":{}},\"n\":{\"df\":0,\"docs\":{},\"t\":{\"a\":{\"df\":0,\"docs\":{},\"i\":{\"df\":0,\"docs\":{},\"n\":{\"df\":1,\"docs\":{\"6\":{\"tf\":1.0}}}}},\"df\":0,\"docs\":{}}}},\"r\":{\"a\":{\"df\":0,\"docs\":{},\"f\":{\"df\":0,\"docs\":{},\"t\":{\"df\":1,\"docs\":{\"6\":{\"tf\":1.0}}}}},\"df\":0,\"docs\":{}}},\"d\":{\"df\":0,\"docs\":{},\"u\":{\"df\":0,\"docs\":{},\"p\":{\"df\":0,\"docs\":{},\"l\":{\"df\":0,\"docs\":{},\"i\":{\"c\":{\"df\":5,\"docs\":{\"10\":{\"tf\":1.0},\"11\":{\"tf\":1.0},\"5\":{\"tf\":1.0},\"8\":{\"tf\":2.0},\"9\":{\"tf\":1.0}}},\"df\":0,\"docs\":{}}}}}},\"df\":0,\"docs\":{},\"e\":{\"d\":{\"df\":0,\"docs\":{},\"i\":{\"df\":0,\"docs\":{},\"t\":{\"df\":1,\"docs\":{\"6\":{\"tf\":1.0}}}}},\"df\":0,\"docs\":{},\"m\":{\"df\":0,\"docs\":{},\"o\":{\"df\":0,\"docs\":{},\"t\":{\"df\":0,\"docs\":{},\"i\":{\"c\":{\"df\":0,\"docs\":{},\"o\":{\"df\":0,\"docs\":{},\"n\":{\"df\":1,\"docs\":{\"6\":{\"tf\":1.0}}}}},\"df\":0,\"docs\":{}}}}},\"v\":{\"df\":0,\"docs\":{},\"e\":{\"df\":0,\"docs\":{},\"n\":{\"df\":0,\"docs\":{},\"t\":{\"df\":1,\"docs\":{\"1\":{\"tf\":1.0}}}}}}},\"f\":{\"df\":0,\"docs\":{},\"i\":{\"df\":0,\"docs\":{},\"r\":{\"df\":0,\"docs\":{},\"s\":{\"df\":0,\"docs\":{},\"t\":{\"df\":13,\"docs\":{\"10\":{\"tf\":1.0},\"11\":{\"tf\":1.0},\"12\":{\"tf\":1.0},\"13\":{\"tf\":1.0},\"14\":{\"tf\":1.0},\"2\":{\"tf\":1.7320508075688772},\"3\":{\"tf\":1.0},\"4\":{\"tf\":1.0},\"5\":{\"tf\":1.4142135623730951},\"6\":{\"tf\":1.0},\"7\":{\"tf\":1.4142135623730951},\"8\":{\"tf\":1.0},\"9\":{\"tf\":1.0}}}}}},\"o\":{\"df\":0,\"docs\":{},\"u\":{\"df\":0,\"docs\":{},\"r\":{\"df\":1,\"docs\":{\"6\":{\"tf\":1.0}}}}}},\"h\":{\"df\":0,\"docs\":{},\"e\":{\"a\":{\"d\":{\"df\":4,\"docs\":{\"12\":{\"tf\":1.7320508075688772},\"13\":{\"tf\":1.7320508075688772},\"14\":{\"tf\":1.7320508075688772},\"5\":{\"tf\":1.0}},\"e\":{\"df\":0,\"docs\":{},\"r\":{\"df\":6,\"docs\":{\"10\":{\"tf\":1.7320508075688772},\"11\":{\"tf\":1.7320508075688772},\"5\":{\"tf\":1.4142135623730951},\"7\":{\"tf\":1.0},\"8\":{\"tf\":2.0},\"9\":{\"tf\":1.7320508075688772}}}}},\"df\":0,\"docs\":{}},\"df\":0,\"docs\":{},\"l\":{\"df\":0,\"docs\":{},\"l\":{\"df\":0,\"docs\":{},\"o\":{\"df\":1,\"docs\":{\"6\":{\"tf\":1.0}}}}},\"r\":{\"df\":0,\"docs\":{},\"e\":{\"df\":1,\"docs\":{\"1\":{\"tf\":1.0}},\"’\":{\"df\":1,\"docs\":{\"0\":{\"tf\":1.0}}}}}},\"t\":{\"df\":0,\"docs\":{},\"m\":{\"df\":0,\"docs\":{},\"l\":{\"df\":1,\"docs\":{\"1\":{\"tf\":1.0}}}}}},\"i\":{\"d\":{\"df\":1,\"docs\":{\"14\":{\"tf\":1.4142135623730951}}},\"df\":0,\"docs\":{},\"n\":{\"c\":{\"df\":0,\"docs\":{},\"l\":{\"df\":0,\"docs\":{},\"u\":{\"d\":{\"df\":2,\"docs\":{\"4\":{\"tf\":1.7320508075688772},\"5\":{\"tf\":1.4142135623730951}}},\"df\":0,\"docs\":{}}}},\"d\":{\"df\":0,\"docs\":{},\"e\":{\"df\":0,\"docs\":{},\"x\":{\"df\":1,\"docs\":{\"1\":{\"tf\":1.0}}}}},\"df\":0,\"docs\":{},\"l\":{\"df\":0,\"docs\":{},\"i\":{\"df\":0,\"docs\":{},\"n\":{\"df\":1,\"docs\":{\"1\":{\"tf\":1.4142135623730951}}}}},\"t\":{\"df\":0,\"docs\":{},\"e\":{\"df\":0,\"docs\":{},\"r\":{\"df\":0,\"docs\":{},\"e\":{\"df\":0,\"docs\":{},\"s\":{\"df\":0,\"docs\":{},\"t\":{\"df\":1,\"docs\":{\"0\":{\"tf\":1.0}}}}}}},\"r\":{\"df\":0,\"docs\":{},\"o\":{\"d\":{\"df\":0,\"docs\":{},\"u\":{\"c\":{\"df\":0,\"docs\":{},\"t\":{\"df\":3,\"docs\":{\"0\":{\"tf\":1.7320508075688772},\"1\":{\"tf\":1.0},\"5\":{\"tf\":1.0}}}},\"df\":0,\"docs\":{}}},\"df\":0,\"docs\":{}}}}}},\"l\":{\"df\":0,\"docs\":{},\"e\":{\"df\":0,\"docs\":{},\"f\":{\"df\":0,\"docs\":{},\"t\":{\"df\":1,\"docs\":{\"6\":{\"tf\":1.4142135623730951}}}}}},\"m\":{\"a\":{\"df\":0,\"docs\":{},\"r\":{\"df\":0,\"docs\":{},\"k\":{\"df\":1,\"docs\":{\"6\":{\"tf\":1.0}}}}},\"df\":0,\"docs\":{},\"o\":{\"df\":0,\"docs\":{},\"r\":{\"df\":0,\"docs\":{},\"e\":{\"df\":1,\"docs\":{\"2\":{\"tf\":1.0}}}}}},\"p\":{\"a\":{\"df\":0,\"docs\":{},\"g\":{\"df\":0,\"docs\":{},\"e\":{\"df\":1,\"docs\":{\"8\":{\"tf\":1.0}}}}},\"df\":0,\"docs\":{},\"l\":{\"df\":0,\"docs\":{},\"e\":{\"a\":{\"df\":0,\"docs\":{},\"s\":{\"df\":1,\"docs\":{\"6\":{\"tf\":1.0}}}},\"df\":0,\"docs\":{}}},\"u\":{\"df\":0,\"docs\":{},\"t\":{\"df\":1,\"docs\":{\"1\":{\"tf\":1.0}}}}},\"r\":{\"df\":0,\"docs\":{},\"e\":{\"df\":0,\"docs\":{},\"g\":{\"df\":0,\"docs\":{},\"u\":{\"df\":0,\"docs\":{},\"l\":{\"a\":{\"df\":0,\"docs\":{},\"r\":{\"df\":1,\"docs\":{\"1\":{\"tf\":1.0}}}},\"df\":0,\"docs\":{}}}}},\"i\":{\"df\":0,\"docs\":{},\"g\":{\"df\":0,\"docs\":{},\"h\":{\"df\":0,\"docs\":{},\"t\":{\"df\":1,\"docs\":{\"6\":{\"tf\":1.4142135623730951}}}}}}},\"s\":{\"df\":0,\"docs\":{},\"e\":{\"c\":{\"df\":0,\"docs\":{},\"t\":{\"df\":0,\"docs\":{},\"i\":{\"df\":0,\"docs\":{},\"o\":{\"df\":0,\"docs\":{},\"n\":{\"df\":1,\"docs\":{\"3\":{\"tf\":1.4142135623730951}}}}}}},\"df\":0,\"docs\":{}},\"n\":{\"df\":0,\"docs\":{},\"e\":{\"a\":{\"df\":0,\"docs\":{},\"k\":{\"df\":0,\"docs\":{},\"i\":{\"df\":1,\"docs\":{\"1\":{\"tf\":1.7320508075688772}}}}},\"df\":0,\"docs\":{}}},\"p\":{\"a\":{\"df\":0,\"docs\":{},\"t\":{\"df\":0,\"docs\":{},\"i\":{\"df\":0,\"docs\":{},\"e\":{\"df\":0,\"docs\":{},\"̈\":{\"df\":0,\"docs\":{},\"r\":{\"df\":1,\"docs\":{\"6\":{\"tf\":1.0}}}}},\"ë\":{\"df\":0,\"docs\":{},\"r\":{\"df\":1,\"docs\":{\"6\":{\"tf\":1.0}}}}}}},\"df\":0,\"docs\":{}},\"t\":{\"df\":0,\"docs\":{},\"r\":{\"df\":0,\"docs\":{},\"e\":{\"df\":0,\"docs\":{},\"s\":{\"df\":0,\"docs\":{},\"s\":{\"df\":1,\"docs\":{\"6\":{\"tf\":1.4142135623730951}}}}}}},\"u\":{\"df\":0,\"docs\":{},\"m\":{\"df\":0,\"docs\":{},\"m\":{\"a\":{\"df\":0,\"docs\":{},\"r\":{\"df\":0,\"docs\":{},\"i\":{\"df\":1,\"docs\":{\"5\":{\"tf\":1.4142135623730951}}}}},\"df\":0,\"docs\":{}}}}},\"t\":{\"df\":0,\"docs\":{},\"e\":{\"df\":0,\"docs\":{},\"s\":{\"df\":0,\"docs\":{},\"t\":{\"df\":1,\"docs\":{\"6\":{\"tf\":1.4142135623730951}}}},\"x\":{\"df\":0,\"docs\":{},\"t\":{\"df\":5,\"docs\":{\"0\":{\"tf\":1.0},\"10\":{\"tf\":1.4142135623730951},\"11\":{\"tf\":1.4142135623730951},\"2\":{\"tf\":1.0},\"9\":{\"tf\":1.4142135623730951}}}}},\"h\":{\"df\":0,\"docs\":{},\"r\":{\"df\":0,\"docs\":{},\"e\":{\"df\":0,\"docs\":{},\"e\":{\"df\":1,\"docs\":{\"6\":{\"tf\":1.0}}}}}},\"w\":{\"df\":0,\"docs\":{},\"o\":{\"df\":1,\"docs\":{\"6\":{\"tf\":1.0}}}}},\"u\":{\"df\":0,\"docs\":{},\"n\":{\"df\":0,\"docs\":{},\"i\":{\"c\":{\"df\":0,\"docs\":{},\"o\":{\"d\":{\"df\":2,\"docs\":{\"5\":{\"tf\":1.0},\"6\":{\"tf\":1.7320508075688772}}},\"df\":0,\"docs\":{}}},\"df\":0,\"docs\":{}}}},\"v\":{\"a\":{\"df\":0,\"docs\":{},\"l\":{\"df\":0,\"docs\":{},\"i\":{\"d\":{\"df\":1,\"docs\":{\"8\":{\"tf\":1.0}}},\"df\":0,\"docs\":{}}}},\"df\":0,\"docs\":{}},\"z\":{\"a\":{\"df\":0,\"docs\":{},\"l\":{\"df\":0,\"docs\":{},\"g\":{\"df\":0,\"docs\":{},\"o\":{\"df\":1,\"docs\":{\"6\":{\"tf\":1.0}}}}}},\"df\":0,\"docs\":{}}}},\"title\":{\"root\":{\"a\":{\"df\":0,\"docs\":{},\"t\":{\"df\":0,\"docs\":{},\"t\":{\"df\":0,\"docs\":{},\"r\":{\"df\":0,\"docs\":{},\"i\":{\"b\":{\"df\":0,\"docs\":{},\"u\":{\"df\":0,\"docs\":{},\"t\":{\"df\":1,\"docs\":{\"12\":{\"tf\":1.0}}}}},\"df\":0,\"docs\":{}}}}}},\"c\":{\"df\":0,\"docs\":{},\"h\":{\"a\":{\"df\":0,\"docs\":{},\"p\":{\"df\":0,\"docs\":{},\"t\":{\"df\":0,\"docs\":{},\"e\":{\"df\":0,\"docs\":{},\"r\":{\"df\":2,\"docs\":{\"2\":{\"tf\":1.0},\"7\":{\"tf\":1.0}}}}}}},\"df\":0,\"docs\":{}},\"l\":{\"a\":{\"df\":0,\"docs\":{},\"s\":{\"df\":0,\"docs\":{},\"s\":{\"df\":2,\"docs\":{\"13\":{\"tf\":1.0},\"14\":{\"tf\":1.0}}}}},\"df\":0,\"docs\":{}}},\"d\":{\"df\":0,\"docs\":{},\"u\":{\"df\":0,\"docs\":{},\"p\":{\"df\":0,\"docs\":{},\"l\":{\"df\":0,\"docs\":{},\"i\":{\"c\":{\"df\":1,\"docs\":{\"8\":{\"tf\":1.0}}},\"df\":0,\"docs\":{}}}}}},\"df\":0,\"docs\":{},\"f\":{\"df\":0,\"docs\":{},\"i\":{\"df\":0,\"docs\":{},\"r\":{\"df\":0,\"docs\":{},\"s\":{\"df\":0,\"docs\":{},\"t\":{\"df\":2,\"docs\":{\"2\":{\"tf\":1.0},\"7\":{\"tf\":1.0}}}}}}},\"h\":{\"df\":0,\"docs\":{},\"e\":{\"a\":{\"d\":{\"df\":3,\"docs\":{\"12\":{\"tf\":1.0},\"13\":{\"tf\":1.0},\"14\":{\"tf\":1.0}},\"e\":{\"df\":0,\"docs\":{},\"r\":{\"df\":4,\"docs\":{\"10\":{\"tf\":1.0},\"11\":{\"tf\":1.0},\"8\":{\"tf\":1.0},\"9\":{\"tf\":1.0}}}}},\"df\":0,\"docs\":{}},\"df\":0,\"docs\":{}}},\"i\":{\"d\":{\"df\":1,\"docs\":{\"14\":{\"tf\":1.0}}},\"df\":0,\"docs\":{},\"n\":{\"c\":{\"df\":0,\"docs\":{},\"l\":{\"df\":0,\"docs\":{},\"u\":{\"d\":{\"df\":1,\"docs\":{\"4\":{\"tf\":1.0}}},\"df\":0,\"docs\":{}}}},\"df\":0,\"docs\":{},\"t\":{\"df\":0,\"docs\":{},\"r\":{\"df\":0,\"docs\":{},\"o\":{\"d\":{\"df\":0,\"docs\":{},\"u\":{\"c\":{\"df\":0,\"docs\":{},\"t\":{\"df\":1,\"docs\":{\"0\":{\"tf\":1.0}}}},\"df\":0,\"docs\":{}}},\"df\":0,\"docs\":{}}}}}},\"s\":{\"df\":0,\"docs\":{},\"e\":{\"c\":{\"df\":0,\"docs\":{},\"t\":{\"df\":0,\"docs\":{},\"i\":{\"df\":0,\"docs\":{},\"o\":{\"df\":0,\"docs\":{},\"n\":{\"df\":1,\"docs\":{\"3\":{\"tf\":1.0}}}}}}},\"df\":0,\"docs\":{}},\"n\":{\"df\":0,\"docs\":{},\"e\":{\"a\":{\"df\":0,\"docs\":{},\"k\":{\"df\":0,\"docs\":{},\"i\":{\"df\":1,\"docs\":{\"1\":{\"tf\":1.0}}}}},\"df\":0,\"docs\":{}}},\"t\":{\"df\":0,\"docs\":{},\"r\":{\"df\":0,\"docs\":{},\"e\":{\"df\":0,\"docs\":{},\"s\":{\"df\":0,\"docs\":{},\"s\":{\"df\":1,\"docs\":{\"6\":{\"tf\":1.0}}}}}}},\"u\":{\"df\":0,\"docs\":{},\"m\":{\"df\":0,\"docs\":{},\"m\":{\"a\":{\"df\":0,\"docs\":{},\"r\":{\"df\":0,\"docs\":{},\"i\":{\"df\":1,\"docs\":{\"5\":{\"tf\":1.0}}}}},\"df\":0,\"docs\":{}}}}},\"t\":{\"df\":0,\"docs\":{},\"e\":{\"df\":0,\"docs\":{},\"s\":{\"df\":0,\"docs\":{},\"t\":{\"df\":1,\"docs\":{\"6\":{\"tf\":1.0}}}},\"x\":{\"df\":0,\"docs\":{},\"t\":{\"df\":3,\"docs\":{\"10\":{\"tf\":1.0},\"11\":{\"tf\":1.0},\"9\":{\"tf\":1.0}}}}}},\"u\":{\"df\":0,\"docs\":{},\"n\":{\"df\":0,\"docs\":{},\"i\":{\"c\":{\"df\":0,\"docs\":{},\"o\":{\"d\":{\"df\":1,\"docs\":{\"6\":{\"tf\":1.0}}},\"df\":0,\"docs\":{}}},\"df\":0,\"docs\":{}}}}}}},\"lang\":\"English\",\"pipeline\":[\"trimmer\",\"stopWordFilter\",\"stemmer\"],\"ref\":\"id\",\"version\":\"0.9.5\"},\"results_options\":{\"limit_results\":30,\"teaser_word_count\":30},\"search_options\":{\"bool\":\"OR\",\"expand\":true,\"fields\":{\"body\":{\"boost\":1},\"breadcrumbs\":{\"boost\":1},\"title\":{\"boost\":2}}}}'));"
  },
  {
    "path": "tests/testsuite/search/reasonable_search_index/src/SUMMARY.md",
    "content": "# Summary\n\n[Introduction](intro.md)\n\n- [First Chapter](first/index.md)\n    - [Includes](first/includes.md)\n    - [Unicode](first/unicode.md)\n    - [No Headers](first/no-headers.md)\n    - [Duplicate Headers](first/duplicate-headers.md)\n    - [Heading Attributes](first/heading-attributes.md)\n"
  },
  {
    "path": "tests/testsuite/search/reasonable_search_index/src/first/duplicate-headers.md",
    "content": "# Duplicate headers\n\nThis page validates behaviour of duplicate headers.\n\n# Header Text\n\n# Header Text\n\n# header-text\n"
  },
  {
    "path": "tests/testsuite/search/reasonable_search_index/src/first/heading-attributes.md",
    "content": "# Heading Attributes {#attrs}\n\n## Heading with classes {.class1 .class2}\n\n## Heading with id and classes {#both .class1 .class2}\n"
  },
  {
    "path": "tests/testsuite/search/reasonable_search_index/src/first/includes.md",
    "content": "# Includes\n\n{{#include ../SUMMARY.md::}}\n"
  },
  {
    "path": "tests/testsuite/search/reasonable_search_index/src/first/index.md",
    "content": "# First Chapter\n\nmore text.\n\n## Some Section\n"
  },
  {
    "path": "tests/testsuite/search/reasonable_search_index/src/first/no-headers.md",
    "content": "Capybara capybara capybara.\n\nCapybara capybara capybara.\n\nThisLongWordIsIncludedSoWeCanCheckThatSufficientlyLongWordsAreOmittedFromTheSearchIndex.\n"
  },
  {
    "path": "tests/testsuite/search/reasonable_search_index/src/first/unicode.md",
    "content": "# Unicode stress tests\n\nPlease be careful editing, this contains carefully crafted characters.\n\nTwo byte character: spatiëring\n\nCombining character: spatiëring\n\nThree byte character: 书こんにちは\n\nFour byte character: 𐌀‮𐌁‮𐌂‮𐌃‮𐌄‮𐌅‮𐌆‮𐌇‮𐌈‬\n\nRight-to-left: مرحبا\n\nEmoticons: 🔊 😍 💜 1️⃣\n\nright-to-left mark: hello באמת!‏\n\n\nZalgo: ǫ̛̖̱̗̝͈̋͒͋̏ͥͫ̒̆ͩ̏͌̾͊͐ͪ̾̚\n\n"
  },
  {
    "path": "tests/testsuite/search/reasonable_search_index/src/intro.md",
    "content": "# Introduction\n\nHere's some interesting text...\n\n## Sneaky\n\n<p>\n<!--secret secret-->\nI put &lt;HTML&gt; in here!<br/>\n</p>\n<script type=\"text/javascript\" >\n// I probably shouldn't do this\nif (3 < 5 > 10)\n{\n    alert(\"The sky is falling!\");\n}\n</script >\n<style >\n/*\ncss looks, like this {\n    foo: < 3 <bar >\n}\n*/\n</style>\n\nSneaky inline event <script>alert(\"inline\");</script>.\n\nBut regular <b>inline</b> is indexed.\n"
  },
  {
    "path": "tests/testsuite/search.rs",
    "content": "//! Tests for search support.\n\nuse crate::prelude::*;\nuse mdbook_core::book::{BookItem, Chapter};\nuse snapbox::file;\nuse std::path::Path;\n\nfn read_book_index(root: &Path) -> serde_json::Value {\n    let index_path = glob_one(root, \"book/searchindex*.js\");\n    let index = read_to_string(&index_path);\n    let index =\n        index.trim_start_matches(\"window.search = Object.assign(window.search, JSON.parse('\");\n    let index = index.trim_end_matches(\"'));\");\n    // We need unescape the string as it's supposed to be an escaped JS string.\n    serde_json::from_str(&index.replace(\"\\\\'\", \"'\").replace(\"\\\\\\\\\", \"\\\\\")).unwrap()\n}\n\n// Some spot checks for the generation of the search index.\n#[test]\nfn reasonable_search_index() {\n    let mut test = BookTest::from_dir(\"search/reasonable_search_index\");\n    test.build();\n    let index = read_book_index(&test.dir);\n\n    let doc_urls = index[\"doc_urls\"].as_array().unwrap();\n    eprintln!(\"doc_urls={doc_urls:#?}\",);\n    let get_doc_ref = |url: &str| -> String {\n        doc_urls\n            .iter()\n            .position(|s| s == url)\n            .unwrap_or_else(|| panic!(\"failed to find {url}\"))\n            .to_string()\n    };\n\n    let first_chapter = get_doc_ref(\"first/index.html#first-chapter\");\n    let introduction = get_doc_ref(\"intro.html#introduction\");\n    let some_section = get_doc_ref(\"first/index.html#some-section\");\n    let summary = get_doc_ref(\"first/includes.html#summary\");\n    let no_headers = get_doc_ref(\"first/no-headers.html\");\n    let duplicate_headers_1 = get_doc_ref(\"first/duplicate-headers.html#header-text-1\");\n    let heading_attrs = get_doc_ref(\"first/heading-attributes.html#both\");\n    let sneaky = get_doc_ref(\"intro.html#sneaky\");\n\n    let bodyidx = &index[\"index\"][\"index\"][\"body\"][\"root\"];\n    let textidx = &bodyidx[\"t\"][\"e\"][\"x\"][\"t\"];\n    assert_eq!(textidx[\"df\"], 5);\n    assert_eq!(textidx[\"docs\"][&first_chapter][\"tf\"], 1.0);\n    assert_eq!(textidx[\"docs\"][&introduction][\"tf\"], 1.0);\n\n    let docs = &index[\"index\"][\"documentStore\"][\"docs\"];\n    assert_eq!(docs[&first_chapter][\"body\"], \"more text.\");\n    assert_eq!(docs[&some_section][\"body\"], \"\");\n    assert_eq!(\n        docs[&summary][\"body\"],\n        \"Introduction First Chapter Includes Unicode No Headers Duplicate Headers Heading Attributes\"\n    );\n    assert_eq!(\n        docs[&summary][\"breadcrumbs\"],\n        \"First Chapter » Includes » Summary\"\n    );\n    // See note about InlineHtml in search.rs. Ideally the `alert()` part\n    // should not be in the index, but we don't have a way to scrub inline\n    // html.\n    assert_eq!(\n        docs[&sneaky][\"body\"],\n        \"I put <HTML> in here! Sneaky inline event . But regular inline is indexed.\"\n    );\n    assert_eq!(\n        docs[&no_headers][\"breadcrumbs\"],\n        \"First Chapter » No Headers\"\n    );\n    assert_eq!(\n        docs[&duplicate_headers_1][\"breadcrumbs\"],\n        \"First Chapter » Duplicate Headers » Header Text\"\n    );\n    assert_eq!(\n        docs[&no_headers][\"body\"],\n        \"Capybara capybara capybara. Capybara capybara capybara. ThisLongWordIsIncludedSoWeCanCheckThatSufficientlyLongWordsAreOmittedFromTheSearchIndex.\"\n    );\n    assert_eq!(\n        docs[&heading_attrs][\"breadcrumbs\"],\n        \"First Chapter » Heading Attributes » Heading with id and classes\"\n    );\n}\n\n// This test is here to catch any unexpected changes to the search index.\n#[test]\nfn search_index_hasnt_changed_accidentally() {\n    BookTest::from_dir(\"search/reasonable_search_index\").check_file(\n        \"book/searchindex*.js\",\n        file![\"search/reasonable_search_index/expected_index.js\"],\n    );\n}\n\n// Ability to disable search chapters.\n#[test]\nfn can_disable_individual_chapters() {\n    let mut test = BookTest::from_dir(\"search/disable_search_chapter\");\n    test.build();\n    let index = read_book_index(&test.dir);\n    let doc_urls = index[\"doc_urls\"].as_array().unwrap();\n    let contains = |path| {\n        doc_urls\n            .iter()\n            .any(|p| p.as_str().unwrap().starts_with(path))\n    };\n    assert!(contains(\"second.html\"));\n    assert!(!contains(\"second/\"));\n    assert!(!contains(\"first/disable_me.html\"));\n    assert!(contains(\"first/keep_me.html\"));\n}\n\n// Test for a regression where search would fail if source_path is None.\n// https://github.com/rust-lang/mdBook/pull/2550\n#[test]\nfn with_no_source_path() {\n    let test = BookTest::from_dir(\"search/reasonable_search_index\");\n    let mut book = test.load_book();\n    let chapter = Chapter::new(\"Sample chapter\", String::new(), \"sample.html\", vec![]);\n    book.book.items.push(BookItem::Chapter(chapter));\n    book.build().unwrap();\n}\n\n// Checks that invalid settings in search chapter is rejected.\n#[test]\nfn chapter_settings_validation_error() {\n    BookTest::from_dir(\"search/chapter_settings_validation_error\").run(\"build\", |cmd| {\n        cmd.expect_failure().expect_stderr(str![[r#\"\n INFO Book building has started\n INFO Running the html backend\nERROR Rendering failed\n[TAB]Caused by: [output.html.search.chapter] key `does-not-exist` does not match any chapter paths\n\n\"#]]);\n    });\n}\n"
  },
  {
    "path": "tests/testsuite/test/failing_tests/book.toml",
    "content": "[book]\ntitle = \"failing_tests\"\n"
  },
  {
    "path": "tests/testsuite/test/failing_tests/src/SUMMARY.md",
    "content": "# Summary\n\n- [Failing Tests](./failing.md)\n- [Failing Include](./failing_include.md)\n"
  },
  {
    "path": "tests/testsuite/test/failing_tests/src/failing.md",
    "content": "# Failing Tests\n\n```rust\npanic!(\"fail\");\n```\n"
  },
  {
    "path": "tests/testsuite/test/failing_tests/src/failing_include.md",
    "content": "# Failing Include\n\n```rust\n{{#include test1.rs:FAILING}}\n```\n"
  },
  {
    "path": "tests/testsuite/test/failing_tests/src/test1.rs",
    "content": "fn test2() {\n    println!(\"test2\");\n}\n\n// ANCHOR: PASSING\nprintln!(\"passing!\");\n// ANCHOR_END: PASSING\n\n// ANCHOR: FAILING\npanic!(\"failing!\");\n// ANCHOR_END: FAILING\n"
  },
  {
    "path": "tests/testsuite/test/passing_tests/book.toml",
    "content": "[book]\ntitle = \"passing_tests\"\n"
  },
  {
    "path": "tests/testsuite/test/passing_tests/src/SUMMARY.md",
    "content": "# Summary\n\n[Intro](./intro.md)\n\n- [Passing 1](./passing1.md)\n- [Passing 2](./passing2.md)\n"
  },
  {
    "path": "tests/testsuite/test/passing_tests/src/passing1.md",
    "content": "# Passing Tests 1\n\n```rust\nassert!(true);\n```\n\n```rust\nprintln!(\"hello!\");\n```\n\n## Also check includes\n\n```rust\n{{#include test1.rs}}\n```\n\n```rust\n{{#include test2.rs:2}}\n```\n\n```rust\n{{#include test2.rs:PASSING}}\n```\n\n```rust\n{{#rustdoc_include test3.rs:2}}\n```\n\n{{#playground test1.rs}}\n"
  },
  {
    "path": "tests/testsuite/test/passing_tests/src/passing2.md",
    "content": "# Passing Tests 2\n\n```rust\nprintln!(\"also passing\");\n```\n"
  },
  {
    "path": "tests/testsuite/test/passing_tests/src/test1.rs",
    "content": "println!(\"test1\");\n"
  },
  {
    "path": "tests/testsuite/test/passing_tests/src/test2.rs",
    "content": "fn test2() {\n    println!(\"test2\");\n}\n\n// ANCHOR: PASSING\nprintln!(\"passing!\");\n// ANCHOR_END: PASSING\n\n// ANCHOR: FAILING\npanic!(\"failing!\");\n// ANCHOR_END: FAILING\n"
  },
  {
    "path": "tests/testsuite/test/passing_tests/src/test3.rs",
    "content": "println!(\"test3\");\n"
  },
  {
    "path": "tests/testsuite/test.rs",
    "content": "//! Tests for the `mdbook test` command.\n\nuse crate::prelude::*;\n\n// Simple test for passing tests.\n#[test]\nfn passing_tests() {\n    BookTest::from_dir(\"test/passing_tests\").run(\"test\", |cmd| {\n        cmd.expect_stdout(str![[\"\"]]).expect_stderr(str![[r#\"\n INFO Testing chapter 'Intro': \"intro.md\"\n INFO Testing chapter 'Passing 1': \"passing1.md\"\n INFO Testing chapter 'Passing 2': \"passing2.md\"\n\n\"#]]);\n    });\n}\n\n// Test for a test failure\n#[test]\nfn failing_tests() {\n    BookTest::from_dir(\"test/failing_tests\").run(\"test\", |cmd| {\n        cmd.expect_code(101)\n            .expect_stdout(str![[\"\"]])\n            // This redacts a large number of lines that come from rustdoc and\n            // libtest. If the output from those ever changes, then it would not\n            // make it possible to test against different versions of Rust. This\n            // still includes a little bit of output, so if that is a problem,\n            // add more redactions.\n            .expect_stderr(str![[r#\"\n INFO Testing chapter 'Failing Tests': \"failing.md\"\nERROR rustdoc returned an error:\n\n--- stdout\n\n...\ntest failing.md - Failing_Tests (line 3) ... FAILED\n...\nthread [..] panicked at failing.md:3:1:\nfail\n...\n INFO Testing chapter 'Failing Include': \"failing_include.md\"\nERROR rustdoc returned an error:\n\n--- stdout\n...\ntest failing_include.md - Failing_Include (line 3) ... FAILED\n...\nthread [..] panicked at failing_include.md:3:1:\nfailing!\n...\nERROR One or more tests failed\n\n\"#]]);\n    });\n}\n\n// Test with a specific chapter.\n#[test]\nfn test_individual_chapter() {\n    let mut test = BookTest::from_dir(\"test/passing_tests\");\n    test.run(\"test -c\", |cmd| {\n        cmd.args(&[\"Passing 1\"])\n            .expect_stdout(str![[\"\"]])\n            .expect_stderr(str![[r#\"\n INFO Testing chapter 'Passing 1': \"passing1.md\"\n\n\"#]]);\n    })\n    // Can also be a source path.\n    .run(\"test -c passing2.md\", |cmd| {\n        cmd.expect_stdout(str![[\"\"]]).expect_stderr(str![[r#\"\n INFO Testing chapter 'Passing 2': \"passing2.md\"\n\n\"#]]);\n    });\n}\n\n// Unknown chapter name.\n#[test]\nfn chapter_not_found() {\n    BookTest::from_dir(\"test/passing_tests\").run(\"test -c bogus\", |cmd| {\n        cmd.expect_failure()\n            .expect_stdout(str![[\"\"]])\n            .expect_stderr(str![[r#\"\nERROR Chapter not found: bogus\n\n\"#]]);\n    });\n}\n"
  },
  {
    "path": "tests/testsuite/theme/custom_fonts_css/book.toml",
    "content": "[book]\ntitle = \"custom_fonts_css\"\n"
  },
  {
    "path": "tests/testsuite/theme/custom_fonts_css/src/SUMMARY.md",
    "content": "- [Intro](index.md)\n"
  },
  {
    "path": "tests/testsuite/theme/custom_fonts_css/theme/fonts/fonts.css",
    "content": "/*custom*/\n"
  },
  {
    "path": "tests/testsuite/theme/empty_fonts_css/book.toml",
    "content": "[book]\ntitle = \"empty_fonts_css\"\n"
  },
  {
    "path": "tests/testsuite/theme/empty_fonts_css/src/SUMMARY.md",
    "content": "- [Intro](index.md)\n"
  },
  {
    "path": "tests/testsuite/theme/empty_fonts_css/theme/fonts/fonts.css",
    "content": ""
  },
  {
    "path": "tests/testsuite/theme/empty_theme/book.toml",
    "content": "[book]\ntitle = \"empty_theme\"\n\n[output.html]\ntheme = \"./theme\"\n"
  },
  {
    "path": "tests/testsuite/theme/empty_theme/src/SUMMARY.md",
    "content": "# Summary\n\n- [Chapter 1](./chapter_1.md)\n"
  },
  {
    "path": "tests/testsuite/theme/empty_theme/src/chapter_1.md",
    "content": "# Chapter 1\n"
  },
  {
    "path": "tests/testsuite/theme/fonts_css/src/SUMMARY.md",
    "content": "- [With Fonts](index.md)\n"
  },
  {
    "path": "tests/testsuite/theme/fonts_css/theme/fonts/fonts.css",
    "content": "/*custom*/\n"
  },
  {
    "path": "tests/testsuite/theme/missing_theme/book.toml",
    "content": "[book]\ntitle = \"missing_theme\"\n\n[output.html]\ntheme = \"./non-existent-directory\"\n"
  },
  {
    "path": "tests/testsuite/theme/missing_theme/src/SUMMARY.md",
    "content": "# Summary\n\n- [Chapter 1](./chapter_1.md)\n"
  },
  {
    "path": "tests/testsuite/theme/missing_theme/src/chapter_1.md",
    "content": "# Chapter 1\n"
  },
  {
    "path": "tests/testsuite/theme/override_index/src/SUMMARY.md",
    "content": "- [Intro](index.md)\n"
  },
  {
    "path": "tests/testsuite/theme/override_index/theme/index.hbs",
    "content": "This is a modified index.hbs!\n"
  },
  {
    "path": "tests/testsuite/theme.rs",
    "content": "//! Tests for theme handling.\n\nuse crate::prelude::*;\n\n// Checks what happens if the theme directory is missing.\n#[test]\nfn missing_theme() {\n    BookTest::from_dir(\"theme/missing_theme\").run(\"build\", |cmd| {\n        cmd.expect_failure().expect_stderr(str![[r#\"\n INFO Book building has started\n INFO Running the html backend\nERROR Rendering failed\n[TAB]Caused by: theme dir [ROOT]/./non-existent-directory does not exist\n\n\"#]]);\n    });\n}\n\n// Checks what happens if the theme directory is empty.\n#[test]\nfn empty_theme() {\n    BookTest::from_dir(\"theme/empty_theme\").run(\"build\", |cmd| {\n        std::fs::create_dir(cmd.dir.join(\"theme\")).unwrap();\n        cmd.expect_stderr(str![[r#\"\n INFO Book building has started\n INFO Running the html backend\n INFO HTML book written to `[ROOT]/book`\n\n\"#]]);\n    });\n}\n\n// Checks overriding index.hbs.\n#[test]\nfn override_index() {\n    BookTest::from_dir(\"theme/override_index\").check_file(\n        \"book/index.html\",\n        str![[r#\"\nThis is a modified index.hbs!\n\n\"#]],\n    );\n}\n\n// After building, what are the default set of fonts?\n#[test]\nfn default_fonts() {\n    BookTest::init(|_| {})\n        .check_file_contains(\"book/index.html\", \"fonts/fonts-[..].css\")\n        .check_file_list(\n            \"book/fonts\",\n            str![[r#\"\nbook/fonts/OPEN-SANS-LICENSE.txt\nbook/fonts/SOURCE-CODE-PRO-LICENSE.txt\nbook/fonts/fonts-[..].css\nbook/fonts/open-sans-v17-all-charsets-300-[..].woff2\nbook/fonts/open-sans-v17-all-charsets-300italic-[..].woff2\nbook/fonts/open-sans-v17-all-charsets-600-[..].woff2\nbook/fonts/open-sans-v17-all-charsets-600italic-[..].woff2\nbook/fonts/open-sans-v17-all-charsets-700-[..].woff2\nbook/fonts/open-sans-v17-all-charsets-700italic-[..].woff2\nbook/fonts/open-sans-v17-all-charsets-800-[..].woff2\nbook/fonts/open-sans-v17-all-charsets-800italic-[..].woff2\nbook/fonts/open-sans-v17-all-charsets-italic-[..].woff2\nbook/fonts/open-sans-v17-all-charsets-regular-[..].woff2\nbook/fonts/source-code-pro-v11-all-charsets-500-[..].woff2\n\"#]],\n        );\n}\n\n// When the theme is initialized, what does the fonts list look like?\n#[test]\nfn theme_fonts_copied() {\n    BookTest::init(|bb| {\n        bb.copy_theme(true);\n    })\n    .check_file_contains(\"book/index.html\", \"fonts/fonts-[..].css\")\n    .check_file_list(\n        \"theme/fonts\",\n        str![[r#\"\ntheme/fonts/OPEN-SANS-LICENSE.txt\ntheme/fonts/SOURCE-CODE-PRO-LICENSE.txt\ntheme/fonts/fonts.css\ntheme/fonts/open-sans-v17-all-charsets-300.woff2\ntheme/fonts/open-sans-v17-all-charsets-300italic.woff2\ntheme/fonts/open-sans-v17-all-charsets-600.woff2\ntheme/fonts/open-sans-v17-all-charsets-600italic.woff2\ntheme/fonts/open-sans-v17-all-charsets-700.woff2\ntheme/fonts/open-sans-v17-all-charsets-700italic.woff2\ntheme/fonts/open-sans-v17-all-charsets-800.woff2\ntheme/fonts/open-sans-v17-all-charsets-800italic.woff2\ntheme/fonts/open-sans-v17-all-charsets-italic.woff2\ntheme/fonts/open-sans-v17-all-charsets-regular.woff2\ntheme/fonts/source-code-pro-v11-all-charsets-500.woff2\n\"#]],\n    )\n    // Note that license files get hashed, which is not like the behavior when\n    // the theme directory is empty. It kinda makes sense, but is weird.\n    .check_file_list(\n        \"book/fonts\",\n        str![[r#\"\nbook/fonts/OPEN-SANS-LICENSE-[..].txt\nbook/fonts/SOURCE-CODE-PRO-LICENSE-[..].txt\nbook/fonts/fonts-[..].css\nbook/fonts/open-sans-v17-all-charsets-300-[..].woff2\nbook/fonts/open-sans-v17-all-charsets-300italic-[..].woff2\nbook/fonts/open-sans-v17-all-charsets-600-[..].woff2\nbook/fonts/open-sans-v17-all-charsets-600italic-[..].woff2\nbook/fonts/open-sans-v17-all-charsets-700-[..].woff2\nbook/fonts/open-sans-v17-all-charsets-700italic-[..].woff2\nbook/fonts/open-sans-v17-all-charsets-800-[..].woff2\nbook/fonts/open-sans-v17-all-charsets-800italic-[..].woff2\nbook/fonts/open-sans-v17-all-charsets-italic-[..].woff2\nbook/fonts/open-sans-v17-all-charsets-regular-[..].woff2\nbook/fonts/source-code-pro-v11-all-charsets-500-[..].woff2\n\"#]],\n    );\n}\n\n// Custom fonts.css.\n#[test]\nfn fonts_css() {\n    BookTest::from_dir(\"theme/fonts_css\")\n        .check_file_contains(\"book/index.html\", \"fonts/fonts-[..].css\")\n        .check_file(\n            \"book/fonts/fonts-*.css\",\n            str![[r#\"\n/*custom*/\n\n\"#]],\n        )\n        .check_file(\"book/fonts/myfont-*.woff\", str![[\"\"]])\n        .check_file_list(\n            \"book/fonts\",\n            str![[r#\"\nbook/fonts/fonts-[..].css\nbook/fonts/myfont-[..].woff\n\"#]],\n        );\n}\n\n// Empty fonts.css should not copy the default fonts.\n#[test]\nfn empty_fonts_css() {\n    BookTest::from_dir(\"theme/empty_fonts_css\")\n        .run(\"build\", |cmd| {\n            cmd.expect_stderr(str![[r#\"\n INFO Book building has started\n INFO Running the html backend\n INFO HTML book written to `[ROOT]/book`\n\n\"#]]);\n        })\n        .check_file_contains(\"book/index.html\", \"fonts.css\")\n        .check_file_list(\"book/fonts\", str![[\"\"]]);\n}\n\n// Custom fonts.css file shouldn't copy default fonts.\n#[test]\nfn custom_fonts_css() {\n    BookTest::from_dir(\"theme/custom_fonts_css\")\n        .run(\"build\", |cmd| {\n            cmd.expect_stderr(str![[r#\"\n INFO Book building has started\n INFO Running the html backend\n INFO HTML book written to `[ROOT]/book`\n\n\"#]]);\n        })\n        .check_file_contains(\"book/index.html\", \"fonts-[..].css\")\n        .check_file_list(\n            \"book/fonts\",\n            str![[r#\"\nbook/fonts/fonts-[..].css\nbook/fonts/myfont-[..].woff\n\"#]],\n        );\n}\n"
  },
  {
    "path": "tests/testsuite/toc/basic_toc/book.toml",
    "content": "[book]\ntitle = \"basic_toc\"\n"
  },
  {
    "path": "tests/testsuite/toc/basic_toc/src/README.md",
    "content": "# With Readme\n"
  },
  {
    "path": "tests/testsuite/toc/basic_toc/src/SUMMARY.md",
    "content": "# Summary\n\n[Prefix 1](prefix1.md)\n[Prefix 2](prefix2.md)\n\n- [With Readme](README.md)\n    - [Nested Index](nested/index.md)\n    - [Nested two](nested/two.md)\n- [Draft]()\n\n---\n\n# Deep Nest\n\n- [Deep Nest 1](deep/index.md)\n    - [Deep Nest 2](deep/a/index.md)\n        - [Deep Nest 3](deep/a/b/index.md)\n            [Deep Nest 4](deep/a/b/c/index.md)\n\n---\n\n[Suffix 1](suffix1.md)\n[Suffix 2](suffix2.md)\n"
  },
  {
    "path": "tests/testsuite/toc/basic_toc/src/deep/a/b/index.md",
    "content": "# Deep Nest 3\n"
  },
  {
    "path": "tests/testsuite/toc/basic_toc/src/deep/a/index.md",
    "content": "# Deep Nest 2\n"
  },
  {
    "path": "tests/testsuite/toc/basic_toc/src/deep/index.md",
    "content": "# Deep Nest 1\n"
  },
  {
    "path": "tests/testsuite/toc/basic_toc/src/nested/index.md",
    "content": "# Nested Index\n"
  },
  {
    "path": "tests/testsuite/toc/basic_toc/src/nested/two.md",
    "content": "# Nested two\n"
  },
  {
    "path": "tests/testsuite/toc/basic_toc/src/prefix1.md",
    "content": "# Prefix 1\n"
  },
  {
    "path": "tests/testsuite/toc/basic_toc/src/prefix2.md",
    "content": "# Prefix 2\n"
  },
  {
    "path": "tests/testsuite/toc/basic_toc/src/suffix1.md",
    "content": "# Suffix 1\n"
  },
  {
    "path": "tests/testsuite/toc/basic_toc/src/suffix2.md",
    "content": "# Suffix 2\n"
  },
  {
    "path": "tests/testsuite/toc/summary_with_markdown_formatting/src/SUMMARY.md",
    "content": "# Summary formatting tests\n\n- [*Italic* `code` \\*escape\\* \\`escape2\\`](formatted-summary.md)\n- [Soft\nline break](soft.md)\n- [\\<escaped tag\\>](escaped-tag.md)\n"
  },
  {
    "path": "tests/testsuite/toc.rs",
    "content": "//! Tests for table of contents (sidebar).\n\nuse crate::prelude::*;\nuse anyhow::Result;\nuse select::document::Document;\nuse select::predicate::{Attr, Class, Name, Predicate};\n\nconst TOC_TOP_LEVEL: &[&str] = &[\n    \"1. With Readme\",\n    \"3. Deep Nest 1\",\n    \"Prefix 1\",\n    \"Prefix 2\",\n    \"Suffix 1\",\n    \"Suffix 2\",\n];\nconst TOC_SECOND_LEVEL: &[&str] = &[\n    \"1.1. Nested Index\",\n    \"1.2. Nested two\",\n    \"3.1. Deep Nest 2\",\n    \"3.1.1. Deep Nest 3\",\n];\n\n/// Apply a series of predicates to some root predicate, where each\n/// successive predicate is the descendant of the last one. Similar to how you\n/// might do `ul.foo li a` in CSS to access all anchor tags in the `foo` list.\nmacro_rules! descendants {\n    ($root:expr, $($child:expr),*) => {\n        $root\n        $(\n            .descendant($child)\n        )*\n    };\n}\n\n/// Read the TOC (`book/toc.js`) nested HTML and expose it as a DOM which we\n/// can search with the `select` crate\nfn toc_js_html() -> Document {\n    let mut test = BookTest::from_dir(\"toc/basic_toc\");\n    test.build();\n    let html = test.toc_js_html();\n    Document::from(html.as_str())\n}\n\n/// Read the TOC fallback (`book/toc.html`) HTML and expose it as a DOM which we\n/// can search with the `select` crate\nfn toc_fallback_html() -> Result<Document> {\n    let mut test = BookTest::from_dir(\"toc/basic_toc\");\n    test.build();\n\n    let toc_path = test.dir.join(\"book\").join(\"toc.html\");\n    let html = read_to_string(toc_path);\n    Ok(Document::from(html.as_str()))\n}\n\n#[test]\nfn check_second_toc_level() {\n    let doc = toc_js_html();\n    let mut should_be = Vec::from(TOC_SECOND_LEVEL);\n    should_be.sort_unstable();\n\n    let pred = descendants!(\n        Class(\"chapter\"),\n        Name(\"li\"),\n        Name(\"li\"),\n        Name(\"a\").and(Class(\"toggle\").not())\n    );\n\n    let mut children_of_children: Vec<_> = doc\n        .find(pred)\n        .map(|elem| elem.text().trim().to_string())\n        .collect();\n    children_of_children.sort();\n\n    assert_eq!(children_of_children, should_be);\n}\n\n#[test]\nfn check_first_toc_level() {\n    let doc = toc_js_html();\n    let mut should_be = Vec::from(TOC_TOP_LEVEL);\n\n    should_be.extend(TOC_SECOND_LEVEL);\n    should_be.sort_unstable();\n\n    let pred = descendants!(\n        Class(\"chapter\"),\n        Name(\"li\"),\n        Name(\"a\").and(Class(\"toggle\").not())\n    );\n\n    let mut children: Vec<_> = doc\n        .find(pred)\n        .map(|elem| elem.text().trim().to_string())\n        .collect();\n    children.sort();\n\n    assert_eq!(children, should_be);\n}\n\n#[test]\nfn check_spacers() {\n    let doc = toc_js_html();\n    let should_be = 2;\n\n    let num_spacers = doc\n        .find(Class(\"chapter\").descendant(Name(\"li\").and(Class(\"spacer\"))))\n        .count();\n    assert_eq!(num_spacers, should_be);\n}\n\n// don't use target=\"_parent\" in JS\n#[test]\nfn check_link_target_js() {\n    let doc = toc_js_html();\n\n    let num_parent_links = doc\n        .find(\n            Class(\"chapter\")\n                .descendant(Name(\"li\"))\n                .descendant(Name(\"a\").and(Attr(\"target\", \"_parent\"))),\n        )\n        .count();\n    assert_eq!(num_parent_links, 0);\n}\n\n// don't use target=\"_parent\" in IFRAME\n#[test]\nfn check_link_target_fallback() {\n    let doc = toc_fallback_html().unwrap();\n\n    let num_parent_links = doc\n        .find(\n            Class(\"chapter\")\n                .descendant(Name(\"li\"))\n                .descendant(Name(\"a\").and(Attr(\"target\", \"_parent\"))),\n        )\n        .count();\n    assert_eq!(\n        num_parent_links,\n        TOC_TOP_LEVEL.len() + TOC_SECOND_LEVEL.len()\n    );\n}\n\n// Checks formatting of summary names with inline elements.\n#[test]\nfn summary_with_markdown_formatting() {\n    BookTest::from_dir(\"toc/summary_with_markdown_formatting\")\n        .check_toc_js(str![[r#\"\n<ol class=\"chapter\">\n<li class=\"chapter-item expanded \">\n<span class=\"chapter-link-wrapper\">\n<a href=\"formatted-summary.html\">\n<strong aria-hidden=\"true\">1.</strong> Italic code *escape* `escape2`</a>\n</span>\n</li>\n<li class=\"chapter-item expanded \">\n<span class=\"chapter-link-wrapper\">\n<a href=\"soft.html\">\n<strong aria-hidden=\"true\">2.</strong> Soft line break</a>\n</span>\n</li>\n<li class=\"chapter-item expanded \">\n<span class=\"chapter-link-wrapper\">\n<a href=\"escaped-tag.html\">\n<strong aria-hidden=\"true\">3.</strong> &lt;escaped tag&gt;</a>\n</span>\n</li>\n</ol>\n\"#]])\n        .check_file(\n            \"src/formatted-summary.md\",\n            str![[r#\"\n# Italic code *escape* `escape2`\n\n\"#]],\n        )\n        .check_file(\n            \"src/soft.md\",\n            str![[r#\"\n# Soft line break\n\n\"#]],\n        )\n        .check_file(\n            \"src/escaped-tag.md\",\n            str![[r#\"\n# &lt;escaped tag&gt;\n\n\"#]],\n        );\n}\n"
  },
  {
    "path": "triagebot.toml",
    "content": "# This will allow users to self assign, and/or drop assignment\n[assign]\n\n# Allows @rustbot ready, review, author, or blocked\n[shortcut]\n\n# Closes/reopens PRs created by the GitHub Actions bot so that checks will run.\n[bot-pull-requests]\n\n# When rebasing, this will add a diff link in a comment.\n[range-diff]\n\n[relabel]\nallow-unauthenticated = [\n    # For Issue areas\n    \"A-*\",\n    # Categories\n    \"C-*\",\n    # Commands\n    \"Command-*\",\n    # Status\n    \"S-*\",\n    \"regression\",\n    \"Breaking Change\",\n    \"msrv-bump\",\n]\n\n[autolabel.\"S-waiting-on-review\"]\nnew_pr = true\n\n[merge-conflicts]\nremove = []\nadd = [\"S-waiting-on-author\"]\nunless = [\"S-blocked\", \"S-waiting-on-review\"]\n\n[review-submitted]\nreviewed_label = \"S-waiting-on-author\"\nreview_labels = [\"S-waiting-on-review\"]\n\n[review-requested]\nremove_labels = [\"S-waiting-on-author\"]\nadd_labels = [\"S-waiting-on-review\"]\n"
  }
]