[
  {
    "path": ".git-blame-ignore-revs",
    "content": "# Format JSON\nd7c548537cd5828b2d58e09f3207ddacc517b227\nf356d27ab21e0f93839da90393c0edf9225740c2\n"
  },
  {
    "path": ".gitattributes",
    "content": "tests/data/* linguist-documentation\n"
  },
  {
    "path": ".github/FUNDING.yml",
    "content": "# These are supported funding model platforms\n\ngithub: XAMPPRocky\n"
  },
  {
    "path": ".github/workflows/mean_bean_ci.yml",
    "content": "name: Mean Bean CI\n\non:\n  push:\n    branches:\n      - master\n  pull_request:\n\njobs:\n  # This job downloads and stores `cross` as an artifact, so that it can be\n  # redownloaded across all of the jobs. Currently this copied pasted between\n  # `ci.yml` and `deploy.yml`. Make sure to update both places when making\n  # changes.\n  install-cross:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: XAMPPRocky/get-github-release@v1\n        id: cross\n        with:\n          owner: rust-embedded\n          repo: cross\n          matches: ${{ matrix.platform }}\n          token: ${{ secrets.GITHUB_TOKEN }}\n      - uses: actions/upload-artifact@v4\n        with:\n          name: cross-${{ matrix.platform }}\n          path: ${{ steps.cross.outputs.install_path }}\n    strategy:\n      matrix:\n        platform: [linux-musl, apple-darwin]\n\n  windows:\n    runs-on: windows-latest\n    # Windows technically doesn't need this, but if we don't block windows on it\n    # some of the windows jobs could fill up the concurrent job queue before\n    # one of the install-cross jobs has started, so this makes sure all\n    # artifacts are downloaded first.\n    needs: install-cross\n    steps:\n      - uses: actions/checkout@v3\n      - run: ci/set_rust_version.bash ${{ matrix.channel }} ${{ matrix.target }}\n        shell: bash\n      - run: ci/build.bash cargo ${{ matrix.target }}\n        shell: bash\n      - run: ci/test.bash cargo ${{ matrix.target }}\n        shell: bash\n\n    strategy:\n      fail-fast: true\n      matrix:\n        channel: [stable, beta, nightly]\n        target:\n          # MSVC\n          - i686-pc-windows-msvc\n          - x86_64-pc-windows-msvc\n          # GNU: You typically only need to test Windows GNU if you're\n          # specifically targeting it, and it can cause issues with some\n          # dependencies if you're not so it's disabled by self.\n          # - i686-pc-windows-gnu\n          # - x86_64-pc-windows-gnu\n\n  macos:\n    runs-on: macos-latest\n    needs: install-cross\n    steps:\n      - uses: actions/checkout@v3\n      - uses: actions/download-artifact@v4\n        with:\n          name: cross-apple-darwin\n          path: /usr/local/bin/\n\n      - run: chmod +x /usr/local/bin/cross\n\n      - run: ci/set_rust_version.bash ${{ matrix.channel }} ${{ matrix.target }}\n      - run: ci/build.bash cross ${{ matrix.target }}\n        # Only test on macOS platforms since we can't simulate iOS.\n      - run: ci/test.bash cross ${{ matrix.target }}\n        if: matrix.target == 'x86_64-apple-darwin'\n\n    strategy:\n      fail-fast: true\n      matrix:\n        channel: [stable, beta, nightly]\n        target:\n          # macOS\n          - x86_64-apple-darwin\n          # iOS\n          - aarch64-apple-ios\n          - x86_64-apple-ios\n\n  linux:\n    runs-on: ubuntu-latest\n    needs: install-cross\n    steps:\n      - uses: actions/checkout@v3\n      - name: Download Cross\n        uses: actions/download-artifact@v4\n        with:\n          name: cross-linux-musl\n          path: /tmp/\n      - run: chmod +x /tmp/cross\n      - run: ci/set_rust_version.bash ${{ matrix.channel }} ${{ matrix.target }}\n      - run: ci/build.bash /tmp/cross ${{ matrix.target }}\n        # These targets have issues with being tested so they are disabled\n        # by default. You can try disabling to see if they work for\n        # your project.\n      - run: ci/test.bash /tmp/cross ${{ matrix.target }}\n        if: |\n          !contains(matrix.target, 'android') &&\n          !contains(matrix.target, 'bsd') &&\n          !contains(matrix.target, 'solaris') &&\n          matrix.target != 'armv5te-unknown-linux-musleabi' &&\n          matrix.target != 'sparc64-unknown-linux-gnu'\n\n    strategy:\n      fail-fast: true\n      matrix:\n        channel: [stable, beta, nightly]\n        target:\n          # WASM, off by default as most rust projects aren't compatible yet.\n          # - wasm32-unknown-emscripten\n          # Linux\n          - aarch64-unknown-linux-gnu\n          - aarch64-unknown-linux-musl\n          - arm-unknown-linux-gnueabi\n          - arm-unknown-linux-gnueabihf\n          - arm-unknown-linux-musleabi\n          - arm-unknown-linux-musleabihf\n          - armv5te-unknown-linux-musleabi\n          - armv7-unknown-linux-gnueabihf\n          - armv7-unknown-linux-musleabihf\n          - i586-unknown-linux-gnu\n          - i586-unknown-linux-musl\n          - i686-unknown-linux-gnu\n          - i686-unknown-linux-musl\n          # - mips-unknown-linux-gnu\n          # - mips-unknown-linux-musl\n          # - mips64-unknown-linux-gnuabi64\n          # - mips64el-unknown-linux-gnuabi64\n          # - mipsel-unknown-linux-gnu\n          # - mipsel-unknown-linux-musl\n          - powerpc-unknown-linux-gnu\n          - powerpc64le-unknown-linux-gnu\n          - s390x-unknown-linux-gnu\n          - x86_64-unknown-linux-gnu\n          - x86_64-unknown-linux-musl\n          # Android\n          # - aarch64-linux-android\n          # - arm-linux-androideabi\n          # - armv7-linux-androideabi\n          # - i686-linux-android\n          # - x86_64-linux-android\n          # *BSD\n          # The FreeBSD targets can have issues linking so they are disabled\n          # by default.\n          # - i686-unknown-freebsd\n          # - x86_64-unknown-freebsd\n          # - x86_64-unknown-netbsd\n          # Solaris\n          # - sparcv9-sun-solaris\n          # - x86_64-sun-solaris\n          # Bare Metal\n          # These are no-std embedded targets, so they will only build if your\n          # crate is `no_std` compatible.\n          # - thumbv6m-none-eabi\n          # - thumbv7em-none-eabi\n          # - thumbv7em-none-eabihf\n          # - thumbv7m-none-eabi\n"
  },
  {
    "path": ".github/workflows/mean_bean_deploy.yml",
    "content": "on:\n  workflow_run:\n    workflows: [\"Release-plz\"]\n    branches: [master]\n    types: \n      - completed\n\nname: Mean Bean Deploy\nenv:\n  BIN: tokei\n\njobs:\n  # This job downloads and stores `cross` as an artifact, so that it can be\n  # redownloaded across all of the jobs. Currently this copied pasted between\n  # `ci.yml` and `deploy.yml`. Make sure to update both places when making\n  # changes.\n  install-cross:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: XAMPPRocky/get-github-release@v1\n        id: cross\n        with:\n          owner: rust-embedded\n          repo: cross\n          matches: ${{ matrix.platform }}\n          token: ${{ secrets.GITHUB_TOKEN }}\n      - uses: actions/upload-artifact@v4\n        with:\n          name: cross-${{ matrix.platform }}\n          path: ${{ steps.cross.outputs.install_path }}\n    strategy:\n      matrix:\n        platform: [linux-musl, apple-darwin]\n\n  windows:\n    runs-on: windows-latest\n    needs: install-cross\n    strategy:\n      matrix:\n        target:\n          # MSVC\n          - i686-pc-windows-msvc\n          - x86_64-pc-windows-msvc\n          # GNU\n          # - i686-pc-windows-gnu\n          # - x86_64-pc-windows-gnu\n    steps:\n      - uses: actions/checkout@v3\n      # FIXME: Hack around thinLTO being broken.\n      - run: echo \"RUSTFLAGS=-Clto=fat\" >> $GITHUB_ENV\n      - run: bash ci/set_rust_version.bash stable ${{ matrix.target }}\n      - run: bash ci/build.bash cargo ${{ matrix.target }} RELEASE\n        # We're using using a fork of `actions/create-release` that detects\n        # whether a release is already available or not first.\n      - uses: XAMPPRocky/create-release@v1.0.2\n        id: create_release\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n        with:\n          tag_name: ${{ github.ref }}\n          release_name: ${{ github.ref }}\n          # Draft should **always** be false. GitHub doesn't provide a way to\n          # get draft releases from its API, so there's no point using it.\n          draft: false\n          prerelease: true\n      - uses: actions/upload-release-asset@v1\n        id: upload-release-asset\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n        with:\n          upload_url: ${{ steps.create_release.outputs.upload_url }}\n          asset_path: target/${{ matrix.target }}/release/${{ env.BIN }}.exe\n          asset_name: ${{ env.BIN }}-${{ matrix.target }}.exe\n          asset_content_type: application/zip\n\n  macos:\n    runs-on: macos-latest\n    needs: install-cross\n    strategy:\n      matrix:\n        target:\n          # macOS\n          - x86_64-apple-darwin\n          # iOS\n          # - aarch64-apple-ios\n          # - armv7-apple-ios\n          # - armv7s-apple-ios\n          # - i386-apple-ios\n          # - x86_64-apple-ios\n    steps:\n      - uses: actions/checkout@v3\n      - uses: actions/download-artifact@v4\n        with:\n          name: cross-apple-darwin\n          path: /usr/local/bin/\n      - run: chmod +x /usr/local/bin/cross\n\n      - run: ci/set_rust_version.bash stable ${{ matrix.target }}\n      - run: ci/build.bash cross ${{ matrix.target }} RELEASE\n      - run: tar -czvf ${{ env.BIN }}.tar.gz --directory=target/${{ matrix.target }}/release ${{ env.BIN }}\n      - uses: XAMPPRocky/create-release@v1.0.2\n        id: create_release\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n        with:\n          tag_name: ${{ github.ref }}\n          release_name: ${{ github.ref }}\n          draft: false\n          prerelease: true\n      - uses: actions/upload-release-asset@v1\n        id: upload-release-asset\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n        with:\n          upload_url: ${{ steps.create_release.outputs.upload_url }}\n          asset_path: ${{ env.BIN }}.tar.gz\n          asset_name: ${{ env.BIN }}-${{ matrix.target }}.tar.gz\n          asset_content_type: application/gzip\n\n  linux:\n    runs-on: ubuntu-latest\n    needs: install-cross\n    strategy:\n      fail-fast: false\n      matrix:\n        target:\n          # WASM, off by default as most rust projects aren't compatible yet.\n          # - wasm32-unknown-emscripten\n          # Linux\n          - aarch64-unknown-linux-gnu\n          - arm-unknown-linux-gnueabi\n          - armv7-unknown-linux-gnueabihf\n          - i686-unknown-linux-gnu\n          - i686-unknown-linux-musl\n          - mips-unknown-linux-gnu\n          - mips64-unknown-linux-gnuabi64\n          - mips64el-unknown-linux-gnuabi64\n          - mipsel-unknown-linux-gnu\n          - powerpc64-unknown-linux-gnu\n          - powerpc64le-unknown-linux-gnu\n          - s390x-unknown-linux-gnu\n          - x86_64-unknown-linux-gnu\n          - x86_64-unknown-linux-musl\n          # Android\n          - aarch64-linux-android\n          - arm-linux-androideabi\n          - armv7-linux-androideabi\n          - i686-linux-android\n          - x86_64-linux-android\n          # *BSD\n          # The FreeBSD targets can have issues linking so they are disabled\n          # by default.\n          # - i686-unknown-freebsd\n          # - x86_64-unknown-freebsd\n          - x86_64-unknown-netbsd\n          # Solaris\n          - sparcv9-sun-solaris\n          # Bare Metal\n          # These are no-std embedded targets, so they will only build if your\n          # crate is `no_std` compatible.\n          # - thumbv6m-none-eabi\n          # - thumbv7em-none-eabi\n          # - thumbv7em-none-eabihf\n          # - thumbv7m-none-eabi\n    steps:\n      - uses: actions/checkout@v3\n      - uses: actions/download-artifact@v4\n        with:\n          name: cross-linux-musl\n          path: /tmp/\n      - run: chmod +x /tmp/cross\n\n      - run: ci/set_rust_version.bash stable ${{ matrix.target }}\n      - run: ci/build.bash /tmp/cross ${{ matrix.target }} RELEASE\n      - run: tar -czvf ${{ env.BIN }}.tar.gz --directory=target/${{ matrix.target }}/release ${{ env.BIN }}\n      - uses: XAMPPRocky/create-release@v1.0.2\n        id: create_release\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n        with:\n          tag_name: ${{ github.ref }}\n          release_name: ${{ github.ref }}\n          draft: false\n          prerelease: false\n      - name: Upload Release Asset\n        id: upload-release-asset\n        uses: actions/upload-release-asset@v1\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n        with:\n          upload_url: ${{ steps.create_release.outputs.upload_url }}\n          asset_path: ${{ env.BIN }}.tar.gz\n          asset_name: ${{ env.BIN }}-${{ matrix.target }}.tar.gz\n          asset_content_type: application/gzip\n"
  },
  {
    "path": ".github/workflows/publish_image.yaml",
    "content": "name: Publish Docker Images\n\non:\n  push:\n    branches:\n      - master\n    tags:\n      - v*\n\njobs:\n  image:\n    concurrency:\n      group: ${{ github.workflow }}-${{ github.ref }}\n      cancel-in-progress: true\n    runs-on: ubuntu-latest\n    permissions:\n      packages: write\n      contents: read\n      attestations: write\n    steps:\n      - uses: earthly/actions-setup@v1\n        with:\n          github-token: ${{ secrets.GITHUB_TOKEN }}\n      - name: Check out the repo\n        uses: actions/checkout@v4\n      - name: Extract metadata (tags, labels) for Docker\n        id: meta\n        uses: docker/metadata-action@v5\n        with:\n          images: ghcr.io/${{ github.repository }}\n          tags: |\n            type=semver,pattern={{raw}}\n            type=raw,value=latest,enable={{is_default_branch}}\n      - name: Log in to the Container registry\n        uses: docker/login-action@v3\n        with:\n          registry: ghcr.io\n          username: ${{ github.actor }}\n          password: ${{ secrets.GITHUB_TOKEN }}\n      - name: Build and Push Docker Image\n        run: echo \"${{ steps.meta.outputs.tags }}\" | xargs -I {} earthly --ci --push +docker --image_name=\"{}\"\n"
  },
  {
    "path": ".github/workflows/release-plz.yaml",
    "content": "name: Release-plz\n\npermissions:\n  pull-requests: write\n  contents: write\n\non:\n  push:\n    branches:\n      - master\n\njobs:\n\n  release-plz:\n    name: Release-plz\n    runs-on: ubuntu-22.04\n    steps:\n      - name: Checkout repository\n        uses: actions/checkout@v3\n        with:\n          fetch-depth: 0\n      - name: Install Rust toolchain\n        uses: dtolnay/rust-toolchain@stable\n      - name: Run release-plz\n        uses: MarcoIeni/release-plz-action@v0.5\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n          CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}\n"
  },
  {
    "path": ".gitignore",
    "content": "# Created by https://www.toptal.com/developers/gitignore/api/rust\n# Edit at https://www.toptal.com/developers/gitignore?templates=rust\n\n### Rust ###\n# Generated by Cargo\n# will have compiled files and executables\ndebug/\ntarget/\n\n# These are backup files generated by rustfmt\n**/*.rs.bk\n\n# MSVC Windows builds of rustc generate these, which store debugging information\n*.pdb\n\n# End of https://www.toptal.com/developers/gitignore/api/rust\n\n\n### IDE ###\n.vscode\n.idea/\n*.iml\n\n### Other ###\n\n# macOS\n.DS_Store\n\n# settings\n.settings\n.tokeirc\n\n# benchmark\nresults.csv\n\nnode_modules\n*.code-workspace\n"
  },
  {
    "path": ".mailmap",
    "content": "Erin Power <xampprocky@gmail.com> <theaaronepower@gmail.com>\nErin Power <xampprocky@gmail.com> <Aaronepower@users.noreply.github.com>\nErin Power <xampprocky@gmail.com> <4464295+XAMPPRocky@users.noreply.github.com>\nErin Power <xampprocky@gmail.com> <aaron.power@softwaredesign.ie>\n"
  },
  {
    "path": ".tokeignore",
    "content": "tests/data\nresources\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "# Changelog\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),\nand this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).\n\n## [Unreleased]\n\n## [14.0.0](https://github.com/XAMPPRocky/tokei/compare/v13.0.0...v14.0.0) - 2025-12-26\n\n### Added\n\n- add support for C++20 modules ([#1278](https://github.com/XAMPPRocky/tokei/pull/1278))\n- Add language support for Ark TypeScript ([#1300](https://github.com/XAMPPRocky/tokei/pull/1300))\n\n### Other\n\n- Fix downcast type mismatches in clap_builder ([#1310](https://github.com/XAMPPRocky/tokei/pull/1310))\n- remove tokei.rs references\n- Add support for Koka ([#1306](https://github.com/XAMPPRocky/tokei/pull/1306))\n- Stop recommending comma-separated CLI args ([#1305](https://github.com/XAMPPRocky/tokei/pull/1305))\n- Update clap-cargo from 0.13 to 0.18 ([#1298](https://github.com/XAMPPRocky/tokei/pull/1298))\n\n## [13.0.0-alpha.9](https://github.com/XAMPPRocky/tokei/compare/v13.0.0-alpha.8...v13.0.0-alpha.9) - 2025-07-21\n\n### Other\n\n- Update README.md\n- Fix CRLF or mixed CRLF/LF line terminations in Markdown files ([#1219](https://github.com/XAMPPRocky/tokei/pull/1219))\n- Fix a minor typo in CLI help text ([#1217](https://github.com/XAMPPRocky/tokei/pull/1217))\n- Fix a missing space in CLI help text ([#1218](https://github.com/XAMPPRocky/tokei/pull/1218))\n- Relax lifetime constraints in language::embedding ([#1225](https://github.com/XAMPPRocky/tokei/pull/1225))\n\n## [13.0.0-alpha.8](https://github.com/XAMPPRocky/tokei/compare/v13.0.0-alpha.7...v13.0.0-alpha.8) - 2025-01-14\n\n### Other\n\n- add Mojo support ([#1107](https://github.com/XAMPPRocky/tokei/pull/1107)) ([#1185](https://github.com/XAMPPRocky/tokei/pull/1185))\n- Add support for 8th language ([#1192](https://github.com/XAMPPRocky/tokei/pull/1192))\n- Add support for `Roc` language ([#1197](https://github.com/XAMPPRocky/tokei/pull/1197))\n- Add support for Ballerina language ([#1196](https://github.com/XAMPPRocky/tokei/pull/1196))\n- Remove 'conf' extension from the Bitbake config. ([#1001](https://github.com/XAMPPRocky/tokei/pull/1001))\n- Add support for Cairo language ([#1193](https://github.com/XAMPPRocky/tokei/pull/1193))\n- Add support for Uiua language ([#1191](https://github.com/XAMPPRocky/tokei/pull/1191))\n\n## [13.0.0-alpha.7](https://github.com/XAMPPRocky/tokei/compare/v13.0.0-alpha.6...v13.0.0-alpha.7) - 2024-11-10\n\n### Other\n\n- Fix alternative output formats ([#1188](https://github.com/XAMPPRocky/tokei/pull/1188))\n- Add missing extension `fsti` for F* ([#1184](https://github.com/XAMPPRocky/tokei/pull/1184))\n\n## [13.0.0-alpha.6](https://github.com/XAMPPRocky/tokei/compare/v13.0.0-alpha.5...v13.0.0-alpha.6) - 2024-10-11\n\n### Added\n\n- add `mbti` extension for MoonBit ([#1168](https://github.com/XAMPPRocky/tokei/pull/1168))\n\n### Other\n\n- Add language definition for Lauterbach PRACTICE Script ([#1162](https://github.com/XAMPPRocky/tokei/pull/1162))\n- Add support for justfiles ([#1175](https://github.com/XAMPPRocky/tokei/pull/1175))\n- Add Virgil ([#1178](https://github.com/XAMPPRocky/tokei/pull/1178))\n- Add templ support ([#1122](https://github.com/XAMPPRocky/tokei/pull/1122))\n- Update README.md with HiCAD from d4a1814 ([#1143](https://github.com/XAMPPRocky/tokei/pull/1143))\n- Add BQN support ([#1151](https://github.com/XAMPPRocky/tokei/pull/1151))\n- add more extensions for `Hlsl` ([#1164](https://github.com/XAMPPRocky/tokei/pull/1164))\n- Add Phix ([#1167](https://github.com/XAMPPRocky/tokei/pull/1167))\n- Add APL support ([#1152](https://github.com/XAMPPRocky/tokei/pull/1152))\n- Add support for SIL ([#1153](https://github.com/XAMPPRocky/tokei/pull/1153))\n- Use `OR` operator in Cargo.toml `license` field ([#1165](https://github.com/XAMPPRocky/tokei/pull/1165))\n- Disable legacy Cargo features ([#1158](https://github.com/XAMPPRocky/tokei/pull/1158))\n- add slint language support ([#1054](https://github.com/XAMPPRocky/tokei/pull/1054))\n- Add Pyret support ([#1032](https://github.com/XAMPPRocky/tokei/pull/1032))\n- Recognize GNUmakefile ([#1021](https://github.com/XAMPPRocky/tokei/pull/1021))\n\n## [13.0.0-alpha.5](https://github.com/XAMPPRocky/tokei/compare/v13.0.0-alpha.4...v13.0.0-alpha.5) - 2024-08-23\n\n### Fixed\n- fix issue https://github.com/XAMPPRocky/tokei/issues/1147 ([#1149](https://github.com/XAMPPRocky/tokei/pull/1149))\n\n### Other\n- Fix issue [#1145](https://github.com/XAMPPRocky/tokei/pull/1145) (part 2) ([#1148](https://github.com/XAMPPRocky/tokei/pull/1148))\n\n## [13.0.0-alpha.4](https://github.com/XAMPPRocky/tokei/compare/v13.0.0-alpha.3...v13.0.0-alpha.4) - 2024-08-22\n\n### Fixed\n- fix issue https://github.com/XAMPPRocky/tokei/issues/1145 ([#1146](https://github.com/XAMPPRocky/tokei/pull/1146))\n\n### Other\n- Add support for Glimmer JS/TS ([#1052](https://github.com/XAMPPRocky/tokei/pull/1052))\n- Fix issue [#1141](https://github.com/XAMPPRocky/tokei/pull/1141) ([#1142](https://github.com/XAMPPRocky/tokei/pull/1142))\n\n## [13.0.0-alpha.3](https://github.com/XAMPPRocky/tokei/compare/v13.0.0-alpha.2...v13.0.0-alpha.3) - 2024-08-20\n\n### Fixed\n- fix issue https://github.com/XAMPPRocky/tokei/issues/1138 ([#1139](https://github.com/XAMPPRocky/tokei/pull/1139))\n\n## [13.0.0-alpha.2](https://github.com/XAMPPRocky/tokei/compare/v13.0.0-alpha.1...v13.0.0-alpha.2) - 2024-08-19\n\n### Added\n- Add support for Monkey C ([#1081](https://github.com/XAMPPRocky/tokei/pull/1081))\n- added plantuml support ([#1125](https://github.com/XAMPPRocky/tokei/pull/1125))\n- add language Tact ([#1103](https://github.com/XAMPPRocky/tokei/pull/1103))\n- add support for bicep ([#1100](https://github.com/XAMPPRocky/tokei/pull/1100))\n- add hledger ([#1121](https://github.com/XAMPPRocky/tokei/pull/1121))\n- add SELinux CIL policy source files ([#1124](https://github.com/XAMPPRocky/tokei/pull/1124))\n- --files argument now sorts alphabetically ([#1059](https://github.com/XAMPPRocky/tokei/pull/1059))\n- add support for LALRPOP ([#1077](https://github.com/XAMPPRocky/tokei/pull/1077))\n\n### Fixed\n- read hidden from config file ([#1093](https://github.com/XAMPPRocky/tokei/pull/1093))\n\n### Other\n- Fix cargo audit issues ([#1137](https://github.com/XAMPPRocky/tokei/pull/1137))\n- Add support for MDX ([#1046](https://github.com/XAMPPRocky/tokei/pull/1046))\n- Add PRQL to README.md ([#1088](https://github.com/XAMPPRocky/tokei/pull/1088))\n- add fypp extension `.fpp` to `languages.json` for Modern Fortran ([#1060](https://github.com/XAMPPRocky/tokei/pull/1060))\n- Add support for Lex ([#1087](https://github.com/XAMPPRocky/tokei/pull/1087))\n- Add d2 ([#1091](https://github.com/XAMPPRocky/tokei/pull/1091))\n- Add support for Stata ([#1112](https://github.com/XAMPPRocky/tokei/pull/1112))\n- Add support for CUE ([#1049](https://github.com/XAMPPRocky/tokei/pull/1049))\n- bump libc from 0.2.147 to 0.2.155 ([#1104](https://github.com/XAMPPRocky/tokei/pull/1104))\n- add cangjie language support ([#1127](https://github.com/XAMPPRocky/tokei/pull/1127)) ([#1128](https://github.com/XAMPPRocky/tokei/pull/1128))\n- Add support for JSLT ([#1129](https://github.com/XAMPPRocky/tokei/pull/1129))\n- Add Arturo support ([#1108](https://github.com/XAMPPRocky/tokei/pull/1108))\n- Support Bazel's MODULE files and *.bzlmod files ([#1130](https://github.com/XAMPPRocky/tokei/pull/1130))\n- read only first 128B from the file when searching for shebang ([#1040](https://github.com/XAMPPRocky/tokei/pull/1040))\n- add OpenCL as a languages.json entry ([#980](https://github.com/XAMPPRocky/tokei/pull/980))\n- Add GetText Portable Object (PO) files ([#814](https://github.com/XAMPPRocky/tokei/pull/814))\n- Support godot shader ([#1118](https://github.com/XAMPPRocky/tokei/pull/1118))\n- Add Modelica language ([#1061](https://github.com/XAMPPRocky/tokei/pull/1061))\n- Add menhir support ([#781](https://github.com/XAMPPRocky/tokei/pull/781))\n- Update README.md\n- [issue_1114] remove Cargo.lock from .gitignore ([#1115](https://github.com/XAMPPRocky/tokei/pull/1115))\n- [issue_891] give more space for Files column ([#933](https://github.com/XAMPPRocky/tokei/pull/933))\n- GitHub Action to publish docker images ([#1096](https://github.com/XAMPPRocky/tokei/pull/1096))\n- Support MoonBit language. ([#1095](https://github.com/XAMPPRocky/tokei/pull/1095))\n- Add OpenSCAD ([#1097](https://github.com/XAMPPRocky/tokei/pull/1097))\n- add jinja extension for Jinja2 ([#1083](https://github.com/XAMPPRocky/tokei/pull/1083))\n- Fix slang ([#1089](https://github.com/XAMPPRocky/tokei/pull/1089))\n- Temporarily remove Hare\n- Support .pyi python file ([#1075](https://github.com/XAMPPRocky/tokei/pull/1075))\n- add luau extension to lua ([#1066](https://github.com/XAMPPRocky/tokei/pull/1066))\n- Adding support for Snakemake ([#1045](https://github.com/XAMPPRocky/tokei/pull/1045))\n- Add Janet to languages.json ([#1042](https://github.com/XAMPPRocky/tokei/pull/1042))\n- Add OpenQASM support ([#1041](https://github.com/XAMPPRocky/tokei/pull/1041))\n- typst ([#1037](https://github.com/XAMPPRocky/tokei/pull/1037))\n- Add the ZoKrates language ([#1035](https://github.com/XAMPPRocky/tokei/pull/1035))\n- Add PRQL ([#1030](https://github.com/XAMPPRocky/tokei/pull/1030))\n- remove refs ([#1006](https://github.com/XAMPPRocky/tokei/pull/1006))\n- Add lingua franca language ([#993](https://github.com/XAMPPRocky/tokei/pull/993))\n- Add support for Razor Components ([#992](https://github.com/XAMPPRocky/tokei/pull/992))\n- Add arch's PKGBUILD files ([#972](https://github.com/XAMPPRocky/tokei/pull/972))\n- Add Hare support ([#971](https://github.com/XAMPPRocky/tokei/pull/971))\n- Add Max support ([#963](https://github.com/XAMPPRocky/tokei/pull/963))\n- Add support for Chapel ([#960](https://github.com/XAMPPRocky/tokei/pull/960))\n- Add language support for Slang ([#956](https://github.com/XAMPPRocky/tokei/pull/956))\n- Update TypeScript language ([#953](https://github.com/XAMPPRocky/tokei/pull/953))\n- Added support for Circom ([#949](https://github.com/XAMPPRocky/tokei/pull/949))\n- link to earthly project ([#1078](https://github.com/XAMPPRocky/tokei/pull/1078))\n\n## [13.0.0-alpha.1](https://github.com/XAMPPRocky/tokei/compare/v13.0.0-alpha.0...v13.0.0-alpha.1) - 2024-03-04\n\n### Fixed\n- fixed language names not showing when in Light mode (light background) ([#1048](https://github.com/XAMPPRocky/tokei/pull/1048))\n\n### Other\n- Create release-plz.yaml\n- Update mean_bean_ci.yml\n- Fix LD Script language data ([#1028](https://github.com/XAMPPRocky/tokei/pull/1028))\n- Fix language data example in CONTRIBUTING.md ([#1029](https://github.com/XAMPPRocky/tokei/pull/1029))\n- Update dependencies\n- Add widget install instructions\n- Update mean_bean_ci.yml\n- Dockerize tokei ([#930](https://github.com/XAMPPRocky/tokei/pull/930))\n- Ignore format commits for `languages.json` ([#1013](https://github.com/XAMPPRocky/tokei/pull/1013))\n- Upgrade GitHub Actions ([#955](https://github.com/XAMPPRocky/tokei/pull/955))\n- add --languages ouput formatter ([#1007](https://github.com/XAMPPRocky/tokei/pull/1007))\n- Add Nuget Config, Bazel and EdgeQL Support, Fix Output Formatter ([#999](https://github.com/XAMPPRocky/tokei/pull/999))\n- show nushell in the readme ([#991](https://github.com/XAMPPRocky/tokei/pull/991))\n- Add support for Redscript ([#994](https://github.com/XAMPPRocky/tokei/pull/994))\n- Add support for jq ([#965](https://github.com/XAMPPRocky/tokei/pull/965))\n- Add support for Astro ([#966](https://github.com/XAMPPRocky/tokei/pull/966))\n- Use XDG conventions on macOS too ([#989](https://github.com/XAMPPRocky/tokei/pull/989))\n- Add JSON5 support for languages.json ([#986](https://github.com/XAMPPRocky/tokei/pull/986))\n- Delete Smalltalk.cs.st ([#990](https://github.com/XAMPPRocky/tokei/pull/990))\n- Add support for smalltalk ([#839](https://github.com/XAMPPRocky/tokei/pull/839))\n- Disable *-android\n- Add HiCAD to languages.json ([#985](https://github.com/XAMPPRocky/tokei/pull/985))\n- Add Nushell to languages.json ([#982](https://github.com/XAMPPRocky/tokei/pull/982))\n# 12.1.0\n\n## Introduction\nTokei is a fast and accurate code analysis CLI tool and library, allowing you to\neasily and quickly see how many blank lines, comments, and lines of code are in\nyour codebase. All releases and work on Tokei and tokei.rs ([the free companion\nbadge service][rs-info]) are [funded by the community through\nGitHub Sponsors][sponsor].\n\nYou can always download the latest version of tokei through GitHub Releases or\nCargo. Tokei is also available through other [package managers][pkg], though\nthey may not always contain the latest release.\n\n```\ncargo install tokei\n```\n\n[pkg]: https://github.com/XAMPPRocky/tokei#package-managers\n[rs-info]: https://github.com/XAMPPRocky/tokei/blob/master/README.md#Badges\n[sponsor]: https://github.com/sponsors/XAMPPRocky\n\n## What's New?\n\n- [Added `-n/--num-format=[commas, dots, plain, underscores]` for adding\n  separator formatting for numbers.](https://github.com/XAMPPRocky/tokei/pull/591)\n- [The total is now included in output formats such as JSON.](https://github.com/XAMPPRocky/tokei/pull/580)\n- [`--no-ignore` now implies other ignore flags.](https://github.com/XAMPPRocky/tokei/pull/588)\n- [Added `--no-ignore-dot` flag to ignore files such as `.ignore`.](https://github.com/XAMPPRocky/tokei/pull/588)\n- [Added single line comments to F\\*](https://github.com/XAMPPRocky/tokei/pull/670)\n- Updated various dependencies.\n\n### Added Languages\n\n- [ABNF](https://github.com/XAMPPRocky/tokei/pull/577)\n- [CodeQL](https://github.com/XAMPPRocky/tokei/pull/604)\n- [LiveScript](https://github.com/XAMPPRocky/tokei/pull/607)\n- [Stylus](https://github.com/XAMPPRocky/tokei/pull/619)\n- [DAML](https://github.com/XAMPPRocky/tokei/pull/620)\n- [Tera](https://github.com/XAMPPRocky/tokei/pull/627)\n- [TTCN-3](https://github.com/XAMPPRocky/tokei/pull/621)\n- [Beancount](https://github.com/XAMPPRocky/tokei/pull/630)\n- [Gleam](https://github.com/XAMPPRocky/tokei/pull/646)\n- [JSONNet](https://github.com/XAMPPRocky/tokei/pull/634)\n- [Stan](https://github.com/XAMPPRocky/tokei/pull/633)\n- [Gwion](https://github.com/XAMPPRocky/tokei/pull/659)\n\n# 12.0.0\n\n## What's New? \nTokei 12 comes with some of the biggest user facing changes since 1.0, now in\nthe latest version tokei will now **analyse and count multiple languages\nembedded in your source code** as well as adding support for\n**Jupyter Notebooks**. Now for the first time is able to handle and display\ndifferent languages contained in a single source file. This is currently available\nfor a limited set of languages, with plans to add more support for more in the\nfuture. The currently supported languages are;\n\n### HTML + Siblings (Vue, Svelte, Etc...)\nTokei will now analyse and report the source code contained in `<script>`,\n`<style>`, and `<template>` tags in HTML and other similar languages. Tokei will\nread the value of the`type` attribute from the `<script>` tag and detects the\nappropriate language based on its mime type or JavaScript if not present. Tokei\nwill do the same for `<style>` and `<template>` except reading the `lang`\nattribute instead of `type` and defaulting to CSS and HTML each respectively.\n\n### Jupyter Notebooks\nTokei will now read Jupyter Notebook files (`.ipynb`) and will read the source\ncode and markdown from Jupyter's JSON and output the analysed result.\n\n### Markdown\nTokei will now detect any code blocks marked with specified source language and\ncount each as their respective languages or as Markdown if not present or not\nfound. Now you can easily see how many code examples are included in\nyour documentation.\n\n### Rust\nTokei will now detect blocks of rustdoc documentation  (e.g. `///`/`//!`) and\nparse them as markdown.\n\n### Verbatim Strings\nTokei is now also capable of handling \"verbatim\" strings, which are strings that\ndo not accept escape sequences like `\\`. Thanks to @NickHackman for providing\nthe implementation! This is initially supported for C++, C#, F#, and Rust.\n\n## New Look\nTo be able to show these new features, tokei's output has been changed to look\nlike below. For brevity the CLI only displays one level deep in each language,\nhowever the library's parser is fully recursive and you can get access to the\ncomplete report using the library or by outputting the JSON format.\n\n```\n===============================================================================\n Language            Files        Lines         Code     Comments       Blanks\n===============================================================================\n BASH                    4           49           30           10            9\n JSON                    1         1332         1332            0            0\n Shell                   1           49           38            1           10\n TOML                    2           77           64            4            9\n-------------------------------------------------------------------------------\n Markdown                5         1230            0          965          265\n |- JSON                 1           41           41            0            0\n |- Rust                 2           53           42            6            5\n |- Shell                1           22           18            0            4\n (Total)                           1346          101          971          274\n-------------------------------------------------------------------------------\n Rust                   19         3349         2782          116          451\n |- Markdown            12          351            5          295           51\n (Total)                           3700         2787          411          502\n===============================================================================\n Total                  32         6553         4352         1397          804\n===============================================================================\n```\n\nThis feature is not just limited to the default output of tokei. You can see it\nbroken down by each file with the `--files` option.\n\n```\n===============================================================================\n Language            Files        Lines         Code     Comments       Blanks\n===============================================================================\n Markdown                5         1230            0          965          265\n |- JSON                 1           41           41            0            0\n |- Rust                 2           53           42            6            5\n |- Shell                1           22           18            0            4\n (Total)                           1346          101          971          274\n-------------------------------------------------------------------------------\n ./CODE_OF_CONDUCT.md                46            0           28           18\n ./CHANGELOG.md                     570            0          434          136\n-- ./markdown.md --------------------------------------------------------------\n |- Markdown                          4            0            3            1\n |- Rust                              6            4            1            1\n |- (Total)                          10            4            4            2\n-- ./README.md ----------------------------------------------------------------\n |- Markdown                        498            0          421           77\n |- Shell                            22           18            0            4\n |- (Total)                         520           18          421           81\n-- ./CONTRIBUTING.md ----------------------------------------------------------\n |- Markdown                        112            0           79           33\n |- JSON                             41           41            0            0\n |- Rust                             46           38            4            4\n |- (Total)                         200           79           84           37\n===============================================================================\n Total                   5         1346          101          971          274\n===============================================================================\n```\n\n## Breaking Changes\n- The JSON Output and format of `Languages` has changed.\n- The JSON feature has been removed and is now included by default.\n- `Stats` has been split into `Report` and `CodeStats` to better represent the\n  separation between analysing a file versus a blob of code.\n\n# 11.2.0\n\n- @alexmaco Added shebang and env detection for Crystal.\n- @NickHackman Updated both Vue and HTML to count CSS & JS comments as comments.\n- @XAMPPRocky renamed Perl6's display name to Rakudo.\n- @dbackeus Added `erb` extension for Ruby HTML.\n- @kobataiwan Tokei will now check for a configuration file in your home\n  directory as well as your current and configuration directory.\n- @dependabot Updated dependencies\n\n**Added Languages**\n- @alexmaco Dhall\n- @NickHackman Svelte\n- @athas Futhark\n- @morphy2k Gohtml\n- @LucasMW Headache\n- @rosasynstylae Tsx\n- @XAMPPRocky OpenType Feature Files\n\n# 11.1.0\n\n**Added Languages**\n\n- @rubdos Arduino\n- @LuqueDaniel Pan\n- @itkovian Ren'Py\n\n- Added `LanguageType::shebangs`, `LanguageType::from_file_extension`, and\n  `LanguageType::from_shebang`. (@solanav)\n\n\n# 11.0.0\n\n**Added languages**\n\n- @bwidawsk GNU Assembly, GDB Script\n- @isker Dust, Apache Velocity\n- @andreblanke FreeMarker\n\n\nThanks to some major internal refactoring, Tokei has received significant\nperformance improvements, and is now one of the fastest code counters across any\nsize of codebase. With Tokei 11 showing up to 40–60% faster results than tokei's\nprevious version. To showcase the improvements I've highlighted benchmarks\nof counting five differently sized codebases. Redis (~220k lines), Rust (~16M\nlines), and the Unreal Engine (~37.5M lines). In every one of these benchmarks\nTokei 11 performed the best by a noticeable margin.\n\n*All benchmarks were done on a 15-inch MacBook Pro, with a 2.7GHz Intel Core i7\nprocessor and 16GB 2133 MHz LPDDR3 RAM running macOS Catalina 10.15.3. Your\nmileage may vary, All benchmarks were done using [hyperfine], using default\nsettings for all programs.*\n\n[hyperfine]: https://github.com/sharkdp/hyperfine\n\n### Tokei\n**Note** This benchmark is not accurate due to `tokei` and `loc` both taking\nless than 5ms to complete, there is a high degree of error between the times and\nshould mostly be considered equivalent. However it is included because it is\nnotable that `scc` takes nearly 3x as long to complete on smaller codebases\n(~5k lines).\n![Graph comparing programs running on the tokei source code](https://docs.google.com/spreadsheets/d/e/2PACX-1vRN2Um3G9Mn4Bg6UVWwgntsMy4faZMIP3EDjAfY5Y6Tav7T5z1TxVKmPu7wUNIpUSsSJDfCNH0SAKBB/pubchart?oid=1242634543&format=image)\n\n### Redis\n![Graph comparing programs running on the redis source code](https://docs.google.com/spreadsheets/d/e/2PACX-1vRN2Um3G9Mn4Bg6UVWwgntsMy4faZMIP3EDjAfY5Y6Tav7T5z1TxVKmPu7wUNIpUSsSJDfCNH0SAKBB/pubchart?oid=2009389097&format=image)\n\n### Rust\n![Graph comparing programs running on the rust source code](https://docs.google.com/spreadsheets/d/e/2PACX-1vRN2Um3G9Mn4Bg6UVWwgntsMy4faZMIP3EDjAfY5Y6Tav7T5z1TxVKmPu7wUNIpUSsSJDfCNH0SAKBB/pubchart?oid=424069399&format=image)\n\n### Unreal\n![Graph comparing programs running on the unreal source code](https://docs.google.com/spreadsheets/d/e/2PACX-1vRN2Um3G9Mn4Bg6UVWwgntsMy4faZMIP3EDjAfY5Y6Tav7T5z1TxVKmPu7wUNIpUSsSJDfCNH0SAKBB/pubchart?oid=439405321&format=image)\n\n# 10.1.2\n\n- Added `pyw` extension to Python.\n- Updated dependencies\n\n# 10.1.1\n\n- Fixed `.tokeignore` always working even when `--no-ignore` is present.\n- Updated dependencies\n\n**Added languages**\n\n- @erikaxel Gherkin (Cucumber)\n\n# 10.1.0\n\n- Added `cjsx` extension to CoffeeScript.\n- Tokei will now recognise files with `#!/usr/bin/env ruby` as Ruby.\n- Updated dependencies.\n- Tokei now uses `crossbeam` channels over `std::mpsc`, which should have a\n  noticeable performance improvement on large repos.\n- Improved documentation for `libtokei`.\n\n**Added languages**\n\n- @lzybkr PowerShell\n- @turbo MoonScript\n- @dtolnay Thrift\n- @Tranzystorek FlatBuffers\n- @NieDzejkob Emojicode\n- @DanteFalzone0 HolyC\n- @sci4me Odin\n- @fkarg Rusty Object Notation (RON)\n\n# 10.0.0\n\n- Fixed minor parsing bugs.\n- Width is now limited to 80 unless you use the `--files` flag.\n- Added the `mjs` extension to JavaScript.\n- Added the `tpp` extension to C++.\n- You can now disable Tokei's git ignore detection, similar to ripgrep. See\n  `--help` for options.\n- You can now add a `.tokeignore` file to your project to specify file paths\n  for tokei to always ignore. This file uses the same syntax as `.gitignore`.\n- Improved Pascal representation\n\n**Added languages**\n\n- @hobofan solidity\n- @stefanmaric GraphQL\n- @jhpratt PostCSS\n- @evitalis RPM\n- @alexmaco Pony\n- @yjhmelody WASM, LLVM, Pest\n- @XAMPPRocky ASN.1\n\n# 9.0.0\n\n- Tokei now has config files. You can now specify some commonly used arguments\n  in a `.tokeirc`/`tokei.toml`. Namely `columns` to set the default column\n  output, `types` to filter your count to just a single set of languages, and\n  `treat_doc_strings_as_comments` which is a new option that allows you to\n  specify whether to treat doc strings such as `\"\"\"` in Python as comments\n  or code.\n  The config files can be specified in two places, the current directory tokei\n  is running in and your [system configuration\n  directory](//docs.rs/tokei/struct.Config.html#method.from_config_files). The\n  priority of options is as follows\n  `CLI > <current_directory> > <configuration_directory>`.\n- Tokei is now available on [Conda](https://anaconda.org/conda-forge/tokei).\n- [Tokei's README has been translated\n  to chinese.](https://github.com/chinanf-boy/tokei-zh#tokei-)\n- `LanguageType` now implements `Hash`.\n- Tokei now batches its console output, this should result in a small\n  performance boost.\n- There is now a `--columns` argument for manually setting tokei's output width.\n- The `--sort` argument is now case-insensitive.\n- Tokei will now mark languages who's files failed to parse correctly as\n  potentially inaccurate.\n- Due to a bug in trust-ci `x86_64-unknown-netbsd` versions will not be\n  available in GitHub releases. (You will still be able to install from source.)\n- Due to toml-rs's lacking enum support the TOML output option has\n  been disabled.\n\n**Added languages**\n\n- @t-richards Liquid\n- @diaphore Added the `.glsl` extension to GLSL.\n- @ahmedelgabri Twig\n- @pmoura Logtalk\n- @alekratz Perl, Not Quite Perl\n- @XAMPPRocky Automake, .NET Resource, HLSL, INI, Unreal Plugin,\n  Unreal Project, Unreal Shader, Unreal Shader Header, Unreal Markdown,\n  Visual Basic, Visual Studio Solution, Visual Studio Project, Xcode Config,\n- @TheMrNomis SWIG\n- @xnorme Added the `.vhdl` extension to VHDL\n\n# 8.0.0\n\n- A language's comments, and quotes are now available through the `LanguageType`\n  enum.\n- You can filter by language using the `-t/--type` option. e.g. `tokei -t \"Rust,C\"`\n  will print only Rust and C files.\n- Tokei now understands terminal width and will expand to fit it. (Thanks\n  to @Veykril)\n- Added [comparison](./COMPARISON.md) document to compare Tokei to other\n  code counters.\n- Updated dependencies\n\n**Added languages**\n\n- @BrandonBoone VB6, VBScript, XSLT\n- @ialpert BrightScript\n- @PJB3005 Dream Maker\n- @schmee edn\n\n# 7.0.3\n\nMade various optimisations, up to 65% faster in some cases.\n\n**Added languages**\n\n- @DenialAdams Added Forsyth-Edwards-Notation (FEN)\n- @DjebbZ Added ClojureC\n- @grimm26 Added HCL/Terraform\n\n# 7.0.2\n\n- Updated dependencies.\n- Changed how compilied serialization formats are handled.\n- Fixed minor parser inaccuracies.\n- Tokei should now recognise more python files from their shebang.\n\n**Added languages**\n\n- @ignatenko Added Meson\n- @sprang Added Scheme\n- @fengcms Added Vue\n- @mark.knol Added Haxe\n- @rleungx Added ABAP, COBOL, and Groovy\n- @tiehuis Added Zig\n- @murielsilveira Added Mint\n- @notramo Added Elvish Shell and Kakoune\n- @aatxe Added Racket\n- @kamilchm Added ReasonML\n- @cyplp Added XSL\n\n# 7.0.1\n\n- Updated dependencies\n\n# 7.0.0\n\n- Fixed parsing corner cases\n- Changed storage of comments and quotes from `Vec` to static slices.\n- Added tracing for debugging single files. Not recommended for use on\n  multiple file\n- Updated `log`\n\n# 6.1.0\n\n- Fixed inaccuracies relating to the end comment being smaller than start\n  comment.\n\n**Added languages**\n\n- @mattico Added Xaml\n- @weakish Added Ceylon\n- @theduke Added tsx extension to typescript\n- @vmchale Added Hamlet, Cassius, Lucius, Cabal, Nix, Happy, Alex, and Madlang\n- @notramo Added Crystal\n\n# 6.0.2\n\n- Now can recognise file languages based on their filename.\n\n**Added Languages:**\n\n- @kazimuth CMake, Dockerfile, Rakefile, Scons\n\n# 6.0.1\n\n- Multiple exclude flags now allowed.\n\n**Added Languages:**\n\n- @seiks Added Fish Shell\n- @XAMPPRocky Added Module-Definition\n- @tbu- Added Vala\n\n# 6.0.0\n\n- Reworked internals\n- Now uses serde*derive(\\_and thusly requires rust v1.15*)\n- Now has better file based testing\n\n**Added languages:**\n\n- @tuncer Added Ur/Web\n- @svisser Added PureScript\n- @tjodden Add some common extensions for HTML, C++ and Makefile\n- @xd009642 Added VHDL\n\n# 5.0.0\n\n- Optimised internals\n\n**Added languages:**\n\n- @GungnirInd Added GDScript\n- @tuncer Differentiate between sh and Bash, Added Cogent, F\\*, F#\n- @pthariensflame Added Agda\n\n# 4.5.0\n\n- Added Regex based hueristics so more expensive multi line handling isn't used\n  if there are no multi line comments in the file.\n- Now uses the `ignore` crate for getting files. Which now also makes\n  determining language from path/file parallelised\n- File counting used to only be parallelised per language, now it is also\n  parallelised per file per language.\n- Updated homepage, and documentation links\n- @rmbreak Tokei will now not add directories with `foo.bar` like syntax\n  to a language.\n- @Michael-F-Bryan tokei will now exit gracefully when a feature is missing\n  instead of panicking\n\n**Added languages:**\n\n- @hauleth Added Elixir support\n\n# 4.4.0\n\n- Simplified language definitions, now consolidated into a single JSON file.\n- Fixed regression where lines and files weren't sorted.\n- @llogiq : made clippy fixes\n- @lligo : Added long verbose name\n\n**Added languages:**\n\n- @little-dude : Tcl(_tcl_)\n- @svenstaro : GLSL(_vert, tesc, tese, geom, frag, comp_)\n- @not-fl3 : Elm(_elm_)\n\n**Changes to existing languages:**\n\n- @xpayn : Added `pm` extension to Perl.\n\n# 4.3.0\n\n- @lligo : Tokei no longer panics on non-character-boundary when printing file names.\n- Fixed regression where no comment style files(_json, markdown_) weren't counted.\n- Tokei can now handle files in different encodings.(_using the [encoding](https://crates.io/crates/encoding) library_)\n- Tokei now prints errors instead of silently skipping them.\n- Tokei can now print unused extensions using `-v` option.\n\n**Added languages:**\n\n- Asp(_asa, asp_)\n- Asp.NET(_asax, ascx, asmx, aspx, master, sitemap, webinfo_)\n- Hex(_hex_)\n- Intel Hex(_ihex_)\n- ReStructuredText(_rst_)\n- Razor(_cshtml_)\n\n**Changes to existing languages Thanks to @mwilli20 :**\n\n- Another Ada extension(_pad_)\n- Assembly - Uses `' '` or `\" \"` and added another extension(_asm_)\n- Bash - Uses `' '` or `\" \"`\n- Batch - They don't use quotes for strings, added `::`\n- Cold Fusion - Uses `' '` or `\" \"`\n- D - Uses `\" \"` or\n- Dart - Uses `\" \"` or `' '` or `\"\"\" \"\"\"` or `''' '''`\n- Forth - Uses `\" \"` but new, doesn't have a preset\n- Fortrans - Use `\" \"` or `' '`\n- Idris - Uses `\" \"` or `\"\"\" \"\"\"`\n- Julia - Uses `\" \"` or `\"\"\" \"\"\"`\n- Kotlin - Uses `\" \"` or `\"\"\" \"\"\"`\n- Lisp - Comments can be nested\n- Moustache - Uses `\" \"` or `' '`\n- Nim - Uses `\" \"` or `\"\"\" \"\"\"`\n- Pascal - Uses `' '`\n- Perl - Uses `\" \"` or `' '`\n- Php - Uses `\" \"` or `' '`\n- Python - Uses `\" \"` or `' '` or `\"\"\" \"\"\"` or `''' '''`\n- Ruby - Uses `\" \"` or `' '`\n- Sass - Uses `\" \"` or `' '`\n- Sql - Uses `' '`\n- Toml - Uses `\" \"` or `' '` or `\"\"\" \"\"\"` or `''' '''`\n- Typescript - Uses `\" \"` or `' '` or\n- Vimscript - Uses `\" \"` or `' '`\n- Yaml - Uses `\" \"` or `' '`\n- Zsh - Uses `\" \"` or `' '`\n- Clojure - Removed `#`\n- Forth - `( Comment)` style comments need a space after the opening paren\n- Haskell - Has nested comments\n- Idris - Has nested comments\n- Jai - Has nested block comments\n- Julia - Has nested block comments\n- Kotlin - Has nested block comments\n- Pascal - Pascal should be multiline from `{` or `(*` to `}` or `*)`\n- Perl - Perl5 and earlier for multiline comments need `=pod` to `=cut`.\n- Swift - Has nested block comments\n\n### Tokei's code count\n\n```\n-------------------------------------------------------------------------------\n Language            Files        Lines         Code     Comments       Blanks\n-------------------------------------------------------------------------------\n Rust                   13         2413         1596          601          216\n-------------------------------------------------------------------------------\n |ib\\language\\languages.rs          693          420          197           76\n |anguage\\language_type.rs          500          386          102           12\n .\\src\\main.rs                      314          256           17           41\n |lib\\language\\language.rs          356          166          166           24\n .\\src\\lib\\utils\\fs.rs              129          107            9           13\n |\\lib\\utils\\multi_line.rs          149           89           39           21\n .\\src\\lib\\utils\\macros.rs           59           50            3            6\n .\\src\\lib\\stats.rs                  63           45           12            6\n .\\src\\lib\\lib.rs                    76           25           47            4\n .\\src\\lib\\build.rs                  31           23            0            8\n .\\src\\lib\\sort.rs                   28           19            6            3\n .\\src\\lib\\language\\mod.rs           11            6            3            2\n .\\src\\lib\\utils\\mod.rs               4            4            0            0\n-------------------------------------------------------------------------------\n Markdown                4          492          492            0            0\n-------------------------------------------------------------------------------\n .\\README.md                        252          252            0            0\n .\\CHANGELOG.md                     202          202            0            0\n .\\CONTRIBUTING.md                   25           25            0            0\n .\\CONTRIBUTORS.md                   13           13            0            0\n-------------------------------------------------------------------------------\n YAML                    2           70           67            3            0\n-------------------------------------------------------------------------------\n .\\cli.yml                           53           50            3            0\n .\\.travis.yml                       17           17            0            0\n-------------------------------------------------------------------------------\n TOML                    1           80           65            0           15\n-------------------------------------------------------------------------------\n .\\Cargo.toml                        80           65            0           15\n-------------------------------------------------------------------------------\n Autoconf                1            9            7            1            1\n-------------------------------------------------------------------------------\n .\\src\\lib\\lib.rs.in                  9            7            1            1\n-------------------------------------------------------------------------------\n Total                  21         3064         2227          605          232\n-------------------------------------------------------------------------------\n```\n\n# 4.2.0\n\nTokei is now more precise, and shouldn't ever panic.\nTokei now handles comments in quotes and more precise nested comments properly.\nFixes #53\n\n### Tokei's code count.\n\n```\n-------------------------------------------------------------------------------\n Language            Files        Lines         Code     Comments       Blanks\n-------------------------------------------------------------------------------\n Rust                   13         2303         1487          594          222\n-------------------------------------------------------------------------------\n |ib\\language\\languages.rs          682          401          198           83\n |anguage\\language_type.rs          467          359           96           12\n .\\src\\main.rs                      302          243           17           42\n |lib\\language\\language.rs          356          166          166           24\n .\\src\\lib\\utils\\fs.rs              116           95            9           12\n |\\lib\\utils\\multi_line.rs          156           93           41           22\n .\\src\\lib\\stats.rs                  54           36           12            6\n .\\src\\lib\\build.rs                  31           23            0            8\n .\\src\\lib\\lib.rs                    69           22           43            4\n .\\src\\lib\\utils\\macros.rs           27           20            3            4\n .\\src\\lib\\sort.rs                   28           19            6            3\n .\\src\\lib\\language\\mod.rs           11            6            3            2\n .\\src\\lib\\utils\\mod.rs               4            4            0            0\n-------------------------------------------------------------------------------\n YAML                    2           68           65            3            0\n-------------------------------------------------------------------------------\n .\\cli.yml                           49           46            3            0\n .\\.travis.yml                       19           19            0            0\n-------------------------------------------------------------------------------\n TOML                    1           71           58            0           13\n-------------------------------------------------------------------------------\n .\\Cargo.toml                        71           58            0           13\n-------------------------------------------------------------------------------\n Autoconf                1            9            7            1            1\n-------------------------------------------------------------------------------\n .\\src\\lib\\lib.rs.in                  9            7            1            1\n-------------------------------------------------------------------------------\n Total                  17         2451         1617          598          236\n-------------------------------------------------------------------------------\n```\n\n# 4.1.0\n\nTokei is now **~40%** faster.\n\n**Added languages**\n\n- Ada\n- Forth\n\n# 4.0.0\n\nTokei now has a minimal version without `serde` for faster compilation.\n\nUpdated various dependencies.\n\nInternal dependencies removed.\n\n## Regressions\n\n- CBOR is not supported till it supports `serde 0.8`\n\n**Added languages**\n\n- Handlebars\n\n# 3.0.0\n\nTokei is now available as a library.\n\nTokei now has a lot more tests.\n\nTokei now supports TOML\n\nFixed #41\n\nFixed #44\n\nFixed #45\n\n# 2.1.0\n\nTokei, can now output results in various formats(_cbor, json, yaml_)\n\nConversely tokei can now take in results in those formats, and add them to the current run.\n\nPremilarily support for nested comments(_currently only supported for rust_)\n\nChange in the output format [PR #35](https://github.com/XAMPPRocky/tokei/pull/35)\n\nMoved `.sc` from Lisp to Scala.\n\nInternals changed to allow for multiple multi line comment formats.\n\n**Added languages:**\n\n- Isabelle\n\n# 2.0.0\n\nMajor rewrite, now parallelized.\nCan now support sorting files.\nAdded a progress message for when it is counting files.\nFixed #29\n\n**Added languages:**\n\n- Coq\n- Erlang\n- Kotlin\n- Idris\n- Nim\n- Oz\n- Prolog\n- Qcl\n- Scala\n- Unreal Script\n- Wolfram\n\n# 1.6.0\n\nAdded file counting.\n\n# 1.5.0\n\nAdded Shebang support.\n\n**Added languages:**\n\n- Assembly\n- LD Scripts\n- Device Trees\n- Makefiles\n- Plain Text\n- C Shell\n\n# 1.4.1\n\nChanged the formatting so tokei looks nice for consoles of 80 column width.\n\n# 1.4.0\n\nChanged from handmade recursive file opening to [walkdir](https://github.com/BurntSushi/walkdir)\n"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "content": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nIn the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.\n\n## Our Standards\n\nExamples of behavior that contributes to creating a positive environment include:\n\n* Using welcoming and inclusive language\n* Being respectful of differing viewpoints and experiences\n* Gracefully accepting constructive criticism\n* Focusing on what is best for the community\n* Showing empathy towards other community members\n\nExamples of unacceptable behavior by participants include:\n\n* The use of sexualized language or imagery and unwelcome sexual attention or advances\n* Trolling, insulting/derogatory comments, and personal or political attacks\n* Public or private harassment\n* Publishing others' private information, such as a physical or electronic address, without explicit permission\n* Other conduct which could reasonably be considered inappropriate in a professional setting\n\n## Our Responsibilities\n\nProject maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.\n\nProject maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.\n\n## Scope\n\nThis Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.\n\n## Enforcement\n\nInstances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at xampprocky+coc@gmail.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.\n\nProject maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.\n\n## Attribution\n\nThis Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]\n\n[homepage]: http://contributor-covenant.org\n[version]: http://contributor-covenant.org/version/1/4/\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# Contributing to Tokei\n\n- [Language Addition](#language-addition)\n- [Bug Reports](#bug-reports)\n\n# Language Addition\n\nCurrently, Tokei generates languages from the [`languages.json`](languages.json)\nfile. JSON was chosen to make it easy to add new languages and change code\nstructure without changing large data structures. Here, we will go over the\nproperties of a language in `languages.json` through examples.\n\n```json\n\"JavaScript\": {\n      \"line_comment\": [\"//\"],\n      \"multi_line_comments\": [[\"/*\", \"*/\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"], [\"'\", \"'\"], [\"`\", \"`\"]],\n      \"extensions\": [\"js\", \"mjs\"]\n},\n```\n\nAbove is the JavaScript's definition. The first thing that needs to be defined\nis the key. The key's format should be same as [Rust's enum style]. As this key\nwill be used in an enum for identifying the language. For a lot of languages,\nthis also works for showing the language when we print to the screen.\n\nHowever, there are some languages whose names don't work with the enum style.\nFor example, `JSON` is usually shown in all caps, but that doesn't fit in Rust's\nenum style. So we have an additional optional field called `name` which defines\nhow the language should look when displayed to the user.\n\n```json\n\"Json\": {\n    \"name\": \"JSON\",\n    //...\n},\n```\n\nFor defining comments, there are a few properties. The most commonly used\nproperty is `line_comment` which defines single line comments. These are comments\nwhich don't continue onto the next line. Here is an example in Rust:\n\n```rust\nlet x = 5; // default x position\nlet y = 0; // default y position\n```\n\nThe `line_comment` property expects an array of strings, as some languages have\nmultiple syntaxes for defining a single line comment. For example, `PHP` allows\nboth `#` and `//` for single line comments.\n\n```json\n\"Php\": {\n    \"line_comment\": [\n        \"#\",\n        \"//\"\n    ],\n    //...\n},\n```\n\nFor defining comments that also have an ending syntax, there is the `multi_line`\nproperty. An example for such comments in Rust:\n\n```rust\nlet x = /* There is a reason\n    for this comment, I swear! */\n    10;\n```\n\nThe `verbatim_quotes` property expects an array of strings, as some languages\nhave multiple syntaxes for defining verbatim strings. A verbatim string\nin the context of Tokei is a string literal that can have unescaped `\"`s. For example [`CSharp`](https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/strings/#regular-and-verbatim-string-literals)\n\n```json\n\"CSharp\": {\n  \"verbatim_quotes\": [\n    [\n      \"@\\\\\\\"\",\n      \"\\\\\\\"\"\n    ]\n  ],\n  //...\n},\n```\n\n```csharp\nconst string BasePath = @\"C:\\\";\n```\n\nSome languages have a single, standard filename with no extension\nlike `Makefile` or `Dockerfile`. These can be defined with the\n`filenames` property:\n\n```json\n\"Makefile\": {\n    \"filenames\": [\n        \"makefile\"\n    ],\n    \"extensions\": [\n        \"makefile\",\n        \"mak\",\n        \"mk\"\n    ]\n},\n```\n\nFilenames should be all-lowercase, whether or not the filename\ntypically has capital letters included.\n\nNote that filenames will **override** extensions. With the\nfollowing definition, a file named `CMakeLists.txt` will be\ndetected as a `CMake` file, not a `Text` file.\n\n```json\n\"Text\": {\n    \"extensions\": [\n        \"txt\"\n    ]\n},\n\"CMake\": {\n    \"filenames\": [\n        \"cmakelists.txt\"\n    ]\n},\n```\n\n# Tests\n\nA test file is required for language additions. The file should\ncontain every variant comments and quotes, as well as a comment\nat the top of the file containing the manually verified lines,\ncode, comments, blanks in the following format:\n\n```\nNUM lines NUM code NUM comments NUM blanks\n```\n\n### Example\n\nIn Rust for example, the first line should look like the following:\n\n```rust\n//! 39 lines 32 code 2 comments 5 blanks\n```\n\nThe comment should use the syntax of the language you're testing.\nA good example of a test file is [`tests/data/rust.rs`](tests/data/rust.rs).\n\n```rust\n//! 48 lines 36 code 6 comments 6 blanks\n//! ```rust\n//! fn main () {\n//!     // Comment\n//!\n//!     println!(\"Hello World!\");\n//! }\n//! ```\n\n/* /**/ */\nfn main() {\n    let start = r##\"/*##\\\"\n\\\"##;\n    // comment\n    loop {\n        if x.len() >= 2 && x[0] == '*' && x[1] == '/' { // found the */\n            break;\n        }\n    }\n}\n\nfn foo<'a, 'b>(name: &'b str) {\n    let this_ends = \"a \\\"test/*.\";\n    call1();\n    call2();\n    let this_does_not = /* a /* nested */ comment \" */\n        \"*/another /*test\n            call3();\n            */\";\n}\n\nfn foobar() {\n    let does_not_start = // \"\n        \"until here,\n        test/*\n        test\"; // a quote: \"\n    let also_doesnt_start = /* \" */\n        \"until here,\n        test,*/\n        test\"; // another quote: \"\n}\n\nfn foo() {\n    let a = 4; // /*\n    let b = 5;\n    let c = 6; // */\n}\n\n\n```\n\n# Bug Reports\n\nPlease include the error message and a minimum working example\nincluding the file or file structure.\n\n````\nThis file crashes the program:\n\n<filename>\n```\n<file/file structure>\n```\n````\n\n[Rust's enum style]: https://github.com/rust-lang/rfcs/blob/master/text/0430-finalizing-naming-conventions.md#general-naming-conventions\n"
  },
  {
    "path": "Cargo.toml",
    "content": "[package]\nauthors = [\"Erin Power <xampprocky@gmail.com>\"]\nbuild = \"build.rs\"\ncategories = [\"command-line-utilities\", \"development-tools\", \"visualization\"]\ndescription = \"Count your code, quickly.\"\nhomepage = \"https://tokei.rs\"\ninclude = [\n  \"Cargo.lock\",\n  \"Cargo.toml\",\n  \"LICENCE-APACHE\",\n  \"LICENCE-MIT\",\n  \"build.rs\",\n  \"languages.json\",\n  \"src/**/*\",\n]\nkeywords = [\"utility\", \"cli\", \"cloc\", \"lines\", \"statistics\"]\nlicense = \"MIT OR Apache-2.0\"\nname = \"tokei\"\nreadme = \"README.md\"\nrepository = \"https://github.com/XAMPPRocky/tokei.git\"\nversion = \"14.0.0\"\nrust-version = \"1.71\"\nedition = \"2021\"\n\n[features]\nall = [\"cbor\", \"yaml\"]\ncbor = [\"dep:hex\", \"dep:serde_cbor\"]\ncli = [\"dep:clap\", \"dep:colored\", \"dep:env_logger\", \"dep:num-format\"]\ndefault = [\"cli\"]\nyaml = [\"dep:serde_yaml\"]\n\n[profile.release]\nlto = \"thin\"\npanic = \"abort\"\n\n[[bin]]\nname = \"tokei\"\nrequired-features = [\"cli\"]\n\n[build-dependencies]\ntera = \"1.20.0\"\nignore = \"0.4.22\"\nserde_json = \"1.0.125\"\njson5 = \"0.4.1\"\n\n[dependencies]\naho-corasick = \"1.1.3\"\narbitrary = { version = \"1.3.2\", features = [\"derive\"] }\nclap = { version = \"4\", optional = true, features = [\"cargo\", \"string\", \"wrap_help\"] }\ncolored = { version = \"2.1.0\", optional = true }\ncrossbeam-channel = \"0.5.13\"\nencoding_rs_io = \"0.1.7\"\ngrep-searcher = \"0.1.13\"\nignore = \"0.4.22\"\nlog = \"0.4.22\"\nrayon = \"1.10.0\"\nserde = { version = \"1.0.208\", features = [\"derive\", \"rc\"] }\nterm_size = \"0.3.2\"\ntoml = \"0.8.19\"\nparking_lot = \"0.12.3\"\ndashmap = { version = \"6.0.1\", features = [\"serde\"] }\nnum-format = { version = \"0.4.4\", optional = true }\nonce_cell = \"1.19.0\"\nregex = \"1.10.6\"\nserde_json = \"1.0.125\"\netcetera = \"0.8.0\"\ntable_formatter = \"0.6.1\"\nclap-cargo = \"0.18.1\"\n\n[dependencies.env_logger]\noptional = true\nfeatures = []\nversion = \"0.11.5\"\n\n[dependencies.hex]\noptional = true\nversion = \"0.4.3\"\n\n[dependencies.serde_cbor]\noptional = true\nversion = \"0.11.2\"\n\n[dependencies.serde_yaml]\noptional = true\nversion = \"0.9.34\"\n\n[dev-dependencies]\nproptest = \"1.5.0\"\nstrum = \"0.27.2\"\nstrum_macros = \"0.27.2\"\ntempfile = \"3.12.0\"\ngit2 = { version = \"0.19.0\", default-features = false, features = [] }\n"
  },
  {
    "path": "Earthfile",
    "content": "VERSION 0.6\nFROM alpine:3.19\nWORKDIR /src\n\nbuild:\n    FROM rust:alpine3.19\n    RUN apk update \\\n        && apk add \\\n            git \\\n            gcc \\\n            g++ \\\n            pkgconfig\n\n    COPY . /src\n    WORKDIR /src\n    RUN cargo build --release\n    SAVE ARTIFACT /src/target/release/tokei AS LOCAL ./tokei\n\ndocker:\n    COPY +build/tokei /usr/local/bin/\n    WORKDIR /src\n    ENTRYPOINT [ \"tokei\" ]\n    CMD [ \"--help\" ]\n    ARG image_name=tokei:latest\n    SAVE IMAGE --push $image_name\n"
  },
  {
    "path": "LICENCE-APACHE",
    "content": "Copyright 2016 Erin Power\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n"
  },
  {
    "path": "LICENCE-MIT",
    "content": "MIT License (MIT)\n\nCopyright (c) 2016 Erin Power\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# Tokei ([時計](https://en.wiktionary.org/wiki/%E6%99%82%E8%A8%88))\n[![Mean Bean CI](https://github.com/XAMPPRocky/tokei/workflows/Mean%20Bean%20CI/badge.svg)](https://github.com/XAMPPRocky/tokei/actions?query=workflow%3A%22Mean+Bean+CI%22)\n[![Help Wanted](https://img.shields.io/github/issues/XAMPPRocky/tokei/help%20wanted?color=green)](https://github.com/XAMPPRocky/tokei/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22)\n[![Documentation](https://docs.rs/tokei/badge.svg)](https://docs.rs/tokei/)\n![](https://img.shields.io/crates/d/tokei?label=downloads%20%28crates.io%29)\n![](https://img.shields.io/github/downloads/xampprocky/tokei/total?label=downloads%20%28GH%29)\n![](https://img.shields.io/homebrew/installs/dy/tokei?color=brightgreen&label=downloads%20%28brew%29)\n![Chocolatey Downloads](https://img.shields.io/chocolatey/dt/tokei?label=Downloads%20(Chocolately))\n[![dependency status](https://deps.rs/repo/github/XAMPPRocky/tokei/status.svg)](https://deps.rs/repo/github/XAMPPRocky/tokei)\n[![Packaging status](https://repology.org/badge/tiny-repos/tokei.svg)](https://repology.org/project/tokei/versions)\n\n\nTokei is a program that displays statistics about your code. Tokei will show the number of files, total lines within those files and code, comments, and blanks grouped by language.\n\n## Example\n```console\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n Language            Files        Lines         Code     Comments       Blanks\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n BASH                    4           49           30           10            9\n JSON                    1         1332         1332            0            0\n Shell                   1           49           38            1           10\n TOML                    2           77           64            4            9\n───────────────────────────────────────────────────────────────────────────────\n Markdown                5         1355            0         1074          281\n |- JSON                 1           41           41            0            0\n |- Rust                 2           53           42            6            5\n |- Shell                1           22           18            0            4\n (Total)                           1471          101         1080          290\n───────────────────────────────────────────────────────────────────────────────\n Rust                   19         3416         2840          116          460\n |- Markdown            12          351            5          295           51\n (Total)                           3767         2845          411          511\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n Total                  32         6745         4410         1506          829\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n```\n\n## [API Documentation](https://docs.rs/tokei)\n\n## Table of Contents\n\n- [Features](#features)\n- [Installation](#installation)\n    - [Package Managers](#package-managers)\n    - [Manual](#manual)\n- [Configuration](#configuration)\n- [How to use Tokei](#how-to-use-tokei)\n- [Options](#options)\n- [Supported Languages](#supported-languages)\n- [Changelog](CHANGELOG.md)\n- [Common Issues](#common-issues)\n- [Canonical Source](#canonical-source)\n- [Copyright and License](#copyright-and-license)\n\n## Features\n\n- Tokei is **very fast**, and is able to count millions of lines of code in seconds.\n  Check out the [11.0.0 release](https://github.com/XAMPPRocky/tokei/releases/v11.0.0)\n  to see how Tokei's speed compares to others.\n\n- Tokei is **accurate**, Tokei correctly handles multi line comments,\n  nested comments, and not counting comments that are in strings. Providing an\n  accurate code statistics.\n\n- Tokei has huge range of languages, supporting over **150** languages, and\n  their various extensions.\n\n- Tokei can output in multiple formats (**CBOR**, **JSON**, **YAML**)\n  allowing Tokei's output to be easily stored, and reused. These can also be\n  reused in tokei combining a previous run's statistics with another set.\n\n- Tokei is available on **Mac**, **Linux**, and **Windows**. See [installation\n  instructions](#installation) for how to get Tokei on your platform.\n\n- Tokei is also a **library** allowing you to easily integrate it with other\n  projects.\n\n- Tokei comes with and without color. Set the env variable NO_COLOR to 1, and\n  it'll be black and white.\n\n## Installation\n\n### Package Managers\n\n#### Unix\n```console\n# Alpine Linux (since 3.13)\napk add tokei\n# Arch Linux\npacman -S tokei\n# Cargo\ncargo install tokei\n# Conda\nconda install -c conda-forge tokei\n# Fedora\nsudo dnf install tokei\n# FreeBSD\npkg install tokei\n# NetBSD\npkgin install tokei\n# Nix/NixOS\nnix-env -i tokei\n# OpenSUSE\nsudo zypper install tokei\n# Void Linux\nsudo xbps-install tokei\n```\n\n#### macOS\n```console\n# Homebrew\nbrew install tokei\n# MacPorts\nsudo port selfupdate\nsudo port install tokei\n```\n\n#### Windows\n```console\n# Winget\nwinget install XAMPPRocky.tokei\n# Scoop\nscoop install tokei\n```\n\n### Manual\n\n#### Downloading\nYou can download prebuilt binaries in the\n[releases section](https://github.com/XAMPPRocky/tokei/releases).\n\n#### Building\nYou can also build and install from source (requires the latest stable [Rust] compiler.)\n```console\ncargo install --git https://github.com/XAMPPRocky/tokei.git tokei\n```\n\n[rust]: https://www.rust-lang.org\n\n\n## Configuration\n\nTokei has a [configuration] file that allows you to change default behaviour.\nThe file can be named `tokei.toml` or `.tokeirc`. Currently tokei looks for\nthis file in three different places. The current directory, your home directory,\nand your configuration directory.\n\n## How to use Tokei\n\n#### Basic usage\n\nThis is the basic way to use tokei. Which will report on the code in `./foo`\nand all subfolders.\n\n```shell\n$ tokei ./foo\n```\n\n[configuration]: ./tokei.example.toml\n\n#### Multiple folders\nTo have tokei report on multiple folders in the same call, simply add all the\nfolders you'd like tokei to examine.\n\n```shell\n$ tokei ./foo ./bar ./baz\n```\n\n#### Excluding folders\nTokei will respect all `.gitignore` and `.ignore` files, and you can use\nthe `--exclude` option to exclude any additional files. The `--exclude` flag has\nthe same semantics as `.gitignore`.\n\n```shell\n$ tokei ./foo --exclude *.rs\n```\n\nPaths to exclude can also be listed in a `.tokeignore` file, using the same\n[syntax](https://git-scm.com/docs/gitignore) as .gitignore files.\n\n#### Sorting output\nBy default tokei sorts alphabetically by language name, however using `--sort`\ntokei can also sort by any of the columns.\n\n`blanks, code, comments, lines`\n\n```shell\n$ tokei ./foo --sort code\n```\n\n#### Outputting file statistics\nBy default tokei only outputs the total of the languages, and using `--files`\nflag tokei can also output individual file statistics.\n\n```shell\n$ tokei ./foo --files\n```\n\n#### Outputting into different formats\nTokei normally outputs into a nice human readable format designed for terminals.\nThere is also using the `--output` option various other formats that are more\nuseful for bringing the data into another program.\n\n**Note:** This version of tokei was compiled without any serialization formats, to enable serialization, reinstall\ntokei with the features flag.\n\n```shell\n  ALL:\n  cargo install tokei --features all\n\n  CBOR:\n  cargo install tokei --features cbor\n\n  YAML:\n  cargo install tokei --features yaml\n```\n\n**Currently supported formats**\n- JSON `--output json`\n- YAML `--output yaml`\n- CBOR `--output cbor`\n\n```shell\n$ tokei ./foo --output json\n```\n\n#### Reading in stored formats\nTokei can also take in the outputted formats added in the previous results to its\ncurrent run. Tokei can take either a path to a file, the format passed in as a\nvalue to the option, or from stdin.\n\n```shell\n$ tokei ./foo --input ./stats.json\n```\n\n## Options\n\n```\nUSAGE:\n    tokei [FLAGS] [OPTIONS] [--] [input]...\n\nFLAGS:\n    -f, --files               Will print out statistics on individual files.\n    -h, --help                Prints help information\n        --hidden              Count hidden files.\n    -l, --languages           Prints out supported languages and their extensions.\n        --no-ignore           Don't respect ignore files (.gitignore, .ignore, etc.). This implies --no-ignore-parent,\n                              --no-ignore-dot, and --no-ignore-vcs.\n        --no-ignore-dot       Don't respect .ignore and .tokeignore files, including those in parent directories.\n        --no-ignore-parent    Don't respect ignore files (.gitignore, .ignore, etc.) in parent directories.\n        --no-ignore-vcs       Don't respect VCS ignore files (.gitignore, .hgignore, etc.), including those in parent\n                              directories.\n    -V, --version             Prints version information\n    -v, --verbose             Set log output level:\n                                          1: to show unknown file extensions,\n                                          2: reserved for future debugging,\n                                          3: enable file level trace. Not recommended on multiple files\n\nOPTIONS:\n    -c, --columns <columns>       Sets a strict column width of the output, only available for terminal output.\n    -e, --exclude <exclude>...    Ignore all files & directories matching the pattern.\n    -i, --input <file_input>      Gives statistics from a previous tokei run. Can be given a file path, or \"stdin\" to\n                                  read from stdin.\n    -o, --output <output>         Outputs Tokei in a specific format. Compile with additional features for more format\n                                  support. [possible values: cbor, json, yaml]\n    -s, --sort <sort>             Sort languages based on column [possible values: files, lines, blanks, code, comments]\n    -t, --type <types>            Filters output by language type, separated by a comma. i.e. -t=Rust,Markdown\n\nARGS:\n    <input>...    The path(s) to the file or directory to be counted.\n```\n\n## Dockerized version\nTokei is available in a small `alpine`-based docker image, buildable through [earthly](https://github.com/earthly/earthly):\n```bash\nearthly +docker\n```\n\nOnce built, one can run the image with:\n```bash\ndocker run --rm -v /path/to/analyze:/src tokei .\n```\n\nOr, to simply analyze the current folder (linux):\n```bash\ndocker run --rm -v $(pwd):/src tokei .\n```\n\n## Supported Languages\n\nIf there is a language that you would to add to tokei feel free to make a pull\nrequest. Languages are defined in [`languages.json`](./languages.json), and you can\nread how to add and test your language in our [CONTRIBUTING.md](./CONTRIBUTING.md).\n\n```\nAbap\nActionScript\nAda\nAgda\nAlex\nAlloy\nAPL\nAsn1\nAsp\nAspNet\nAssembly\nAssemblyGAS\nATS\nAutoconf\nAutoHotKey\nAutomake\nAWK\nBash\nBatch\nBazel\nBean\nBicep\nBitbake\nBQN\nBrightScript\nC\nC3\nCabal\nCassius\nCeylon\nCHeader\nCil\nClojure\nClojureC\nClojureScript\nCMake\nCobol\nCoffeeScript\nCogent\nColdFusion\nColdFusionScript\nCoq\nCpp\nCppHeader\nCrystal\nCSharp\nCShell\nCss\nCuda\nCUE\nCython\nD\nD2\nDAML\nDart\nDeviceTree\nDhall\nDockerfile\nDotNetResource\nDreamMaker\nDust\nEbuild\nEdgeDB\nEdn\nElisp\nElixir\nElm\nElvish\nEmacsDevEnv\nEmojicode\nErlang\nFactor\nFEN\nFish\nFlatBuffers\nForgeConfig\nForth\nFortranLegacy\nFortranModern\nFreeMarker\nFSharp\nFstar\nGDB\nGdScript\nGdShader\nGherkin\nGleam\nGlsl\nGo\nGraphql\nGroovy\nGwion\nHamlet\nHandlebars\nHappy\nHare\nHaskell\nHaxe\nHcl\nHex\nHex0\nHex1\nHex2\nHiCAD\nhledger\nHlsl\nHolyC\nHtml\nHy\nIdris\nIni\nIntelHex\nIsabelle\nJai\nJanet\nJava\nJavaScript\nJq\nJson\nJsx\nJulia\nJulius\nJust\nKakouneScript\nKaemFile\nKoka\nKotlin\nLean\nLess\nLingua Franca\nLinkerScript\nLiquid\nLisp\nLLVM\nLogtalk\nLua\nLucius\nM1Assembly\nMadlang\nMax\nMakefile\nMarkdown\nMdx\nMeson\nMint\nMlatu\nModuleDef\nMonkeyC\nMoonScript\nMsBuild\nMustache\nNim\nNix\nNotQuitePerl\nNuGetConfig\nNushell\nObjectiveC\nObjectiveCpp\nOCaml\nOdin\nOpenSCAD\nOpenQASM\nOrg\nOz\nPascal\nPerl\nPerl6\nPest\nPhix\nPhp\nPo\nPoke\nPolly\nPony\nPostCss\nPowerShell\nProcessing\nProlog\nProtobuf\nPRQL\nPSL\nPureScript\nPyret\nPython\nQcl\nQml\nR\nRacket\nRakefile\nRazor\nRenpy\nReStructuredText\nRON\nRPMSpecfile\nRuby\nRubyHtml\nRust\nSass\nScala\nScheme\nScons\nSh\nShaderLab\nSlang\nSml\nSolidity\nSpecmanE\nSpice\nSql\nSRecode\nStata\nStratego\nSvelte\nSvg\nSwift\nSwig\nSystemVerilog\nSlint\nTact\nTcl\nTempl\nTex\nText\nThrift\nToml\nTsx\nTwig\nTypeScript\nUMPL\nUnrealDeveloperMarkdown\nUnrealPlugin\nUnrealProject\nUnrealScript\nUnrealShader\nUnrealShaderHeader\nUrWeb\nUrWebProject\nVala\nVB6\nVBScript\nVelocity\nVerilog\nVerilogArgsFile\nVhdl\nVimScript\nVisualBasic\nVisualStudioProject\nVisualStudioSolution\nVue\nWebAssembly\nWolfram\nXaml\nXcodeConfig\nXml\nXSL\nXtend\nYaml\nZenCode\nZig\nZoKrates\nZsh\n```\n\n## Common issues\n\n### Tokei says I have a lot of D code, but I know there is no D code!\nThis is likely due to `gcc` generating `.d` files. Until the D people decide on\na different file extension, you can always exclude `.d` files using the\n`-e --exclude` flag like so\n\n```\n$ tokei . -e *.d\n```\n\n## Canonical Source\nThe canonical source of this repo is hosted on\n[GitHub](https://github.com/XAMPPRocky/tokei). If you have a GitHub account,\nplease make your issues, and pull requests there.\n\n## Related Tools\n\n- [tokei-pie](https://github.com/laixintao/tokei-pie): Render tokei's output to\n  interactive sunburst chart.\n\n## Copyright and License\n(C) Copyright 2015 by XAMPPRocky and contributors\n\nSee [the graph](https://github.com/XAMPPRocky/tokei/graphs/contributors) for a full list of contributors.\n\nTokei is distributed under the terms of both the MIT license and the Apache License (Version 2.0).\n\nSee [LICENCE-APACHE](./LICENCE-APACHE), [LICENCE-MIT](./LICENCE-MIT) for more information.\n"
  },
  {
    "path": "benchmark.sh",
    "content": "#!/usr/bin/env bash\nset -e\n\nif [ \"$1\" = \"--full\" ]; then\n    FILE=$2\n    FULL=true\n    else\n    FILE=$1\n    FULL=false\nfi\n\necho 'Tokei Benchmarking Tool'\n\nif [ $FULL = true ]; then\n    REQUIRED='cloc, tokei, loc, hyperfine, and scc'\nelse\n    REQUIRED='tokei, and hyperfine'\nfi\n\necho \"The use of this tool requires $REQUIRED to be installed and available in your PATH variable.\"\n\necho 'Please enter the path you would like to benchmark:'\n\nif [ -z ${FILE+x} ]; then\n    read -r input\nelse\n    input=$FILE\nfi\n\nhyperfine --version\necho \"old tokei: $(tokei --version)\"\n\nif [ $FULL = true ]; then\n    scc --version\n    loc --version\n    echo \"cloc: $(cloc --version)\"\nfi\n\ncargo build --release\n\nif [ $FULL = true ]; then\n    hyperfine -w 10 --export-csv './results.csv' \"target/release/tokei $input\" \\\n                \"tokei $input\" \\\n                \"scc $input\" \\\n                \"loc $input\" # \\ \"cloc $input\"\nelse\n    hyperfine -w 5 \"target/release/tokei $input\" \\\n                \"tokei $input\"\nfi\n"
  },
  {
    "path": "build.rs",
    "content": "extern crate ignore;\nextern crate json5;\nextern crate serde_json;\n\nuse std::ffi::OsStr;\nuse std::fs;\nuse std::path::Path;\nuse std::{cmp, env, error};\n\nuse ignore::Walk;\nuse serde_json::Value;\n\nfn main() -> Result<(), Box<dyn error::Error>> {\n    let out_dir = env::var_os(\"OUT_DIR\").expect(\"No OUT_DIR variable.\");\n    generate_languages(&out_dir)?;\n    generate_tests(&out_dir)?;\n\n    Ok(())\n}\n\nfn generate_languages(out_dir: &OsStr) -> Result<(), Box<dyn error::Error>> {\n    let mut tera = tera::Tera::default();\n\n    let json_string: String = fs::read_to_string(\"languages.json\")?.parse()?;\n    let mut json: Value = json5::from_str(&json_string)?;\n\n    for (_key, ref mut item) in json\n        .get_mut(\"languages\")\n        .unwrap()\n        .as_object_mut()\n        .unwrap()\n        .iter_mut()\n    {\n        macro_rules! sort_prop {\n            ($prop:expr) => {{\n                if let Some(ref mut prop) = item.get_mut($prop) {\n                    prop.as_array_mut()\n                        .unwrap()\n                        .sort_unstable_by(compare_json_str_len)\n                }\n            }};\n        }\n\n        sort_prop!(\"quotes\");\n        sort_prop!(\"verbatim_quotes\");\n        sort_prop!(\"multi_line\");\n    }\n\n    let output_path = Path::new(&out_dir).join(\"language_type.rs\");\n    let rust_code = tera.render_str(\n        &std::fs::read_to_string(\"src/language/language_type.tera.rs\")?,\n        &tera::Context::from_value(json)?,\n    )?;\n    std::fs::write(output_path, rust_code)?;\n\n    Ok(())\n}\n\nfn compare_json_str_len(a: &Value, b: &Value) -> cmp::Ordering {\n    let a = a.as_array().expect(\"a as array\");\n    let b = b.as_array().expect(\"b as array\");\n\n    let max_a_size = a.iter().map(|e| e.as_str().unwrap().len()).max().unwrap();\n    let max_b_size = b.iter().map(|e| e.as_str().unwrap().len()).max().unwrap();\n\n    max_b_size.cmp(&max_a_size)\n}\n\nfn generate_tests(out_dir: &OsStr) -> Result<(), Box<dyn error::Error>> {\n    // Length of string literal below by number of languages\n    const INITIAL_BUFFER_SIZE: usize = 989 * 130;\n    let mut string = String::with_capacity(INITIAL_BUFFER_SIZE);\n\n    generate_tests_batch(\"./tests/data\", None, &mut string)?;\n    generate_tests_batch(\"./tests/embedding\", Some(\"embedding\"), &mut string)?;\n\n    Ok(fs::write(Path::new(&out_dir).join(\"tests.rs\"), string)?)\n}\n\nfn generate_tests_batch(\n    src_dir: &str,\n    test_module: Option<&str>,\n    string: &mut String,\n) -> Result<(), Box<dyn error::Error>> {\n    let walker = Walk::new(src_dir).filter(|p| match p {\n        Ok(ref p) => {\n            if let Ok(ref p) = p.metadata() {\n                p.is_file()\n            } else {\n                false\n            }\n        }\n        _ => false,\n    });\n\n    if let Some(test_module) = test_module {\n        string.push_str(&format!(\n            r####\"\n#[cfg(test)]\nmod {0} {{\nuse super::*;\n        \"####,\n            test_module\n        ));\n    }\n\n    for path in walker {\n        let path = path?;\n        let path = path.path();\n        let root = std::path::PathBuf::from(std::env::var(\"CARGO_MANIFEST_DIR\").unwrap());\n\n        let name = path.file_stem().unwrap().to_str().unwrap().to_lowercase();\n\n        if name == \"jupyter\" {\n            continue;\n        }\n\n        string.push_str(&format!(\n            r####\"\n        #[test]\n        fn {0}() {{\n            const _: &str = include_str!(r###\"{2}\"###);\n            let mut languages = Languages::new();\n            languages.get_statistics(&[\"{1}\"], &[], &Config::default());\n\n            if languages.len() != 1 {{\n                panic!(\"wrong languages detected: expected just {0}, found {{:?}}\",\n                       languages.into_iter().collect::<Vec<_>>());\n            }}\n\n            let (name, language) = languages.into_iter().next().unwrap();\n            let mut language = language.summarise();\n\n            let contents = fs::read_to_string(\"{1}\").unwrap();\n\n            println!(\"{{}} {1}\", name);\n            assert_eq!(get_digit!(LINES, contents), language.lines());\n            println!(\"{{}} LINES MATCH\", name);\n            assert_eq!(get_digit!(CODE, contents), language.code);\n            println!(\"{{}} CODE MATCH\", name);\n            assert_eq!(get_digit!(COMMENTS, contents), language.comments);\n            println!(\"{{}} COMMENTS MATCH\", name);\n            assert_eq!(get_digit!(BLANKS, contents), language.blanks);\n            println!(\"{{}} BLANKS MATCH\", name);\n\n            let report = language.reports.pop().unwrap();\n            let stats = report.stats.summarise();\n\n            assert_eq!(language.lines(), stats.lines());\n            assert_eq!(language.code, stats.code);\n            assert_eq!(language.comments, stats.comments);\n            assert_eq!(language.blanks, stats.blanks);\n        }}\n        \"####,\n            name,\n            path.to_string_lossy().replace('\\\\', \"/\"),\n            std::fs::canonicalize(root.join(path)).unwrap().display(),\n        ));\n    }\n\n    if test_module.is_some() {\n        string.push_str(\"\\n}\");\n    }\n\n    Ok(())\n}\n"
  },
  {
    "path": "ci/build.bash",
    "content": "#!/usr/bin/env bash\n# Script for building your rust projects.\nset -e\n\nsource ci/common.bash\n\n# $1 {path} = Path to cross/cargo executable\nCROSS=$1\n# $1 {string} = <Target Triple>\nTARGET_TRIPLE=$2\n# $3 {boolean} = Whether or not building for release or not.\nRELEASE_BUILD=$3\n\nrequired_arg \"$CROSS\" 'CROSS'\nrequired_arg \"$TARGET_TRIPLE\" '<Target Triple>'\n\nif [ -z \"$RELEASE_BUILD\" ]; then\n    $CROSS build --target \"$TARGET_TRIPLE\"\n    $CROSS build --target \"$TARGET_TRIPLE\" --all-features\nelse\n    $CROSS build --target \"$TARGET_TRIPLE\" --all-features --release\nfi\n"
  },
  {
    "path": "ci/common.bash",
    "content": "required_arg() {\n    if [ -z \"$1\" ]; then\n        echo \"Required argument $2 missing\"\n        exit 1\n    fi\n}\n"
  },
  {
    "path": "ci/set_rust_version.bash",
    "content": "#!/usr/bin/env bash\nset -e\nrustup default \"$1\"\nrustup target add \"$2\"\n"
  },
  {
    "path": "ci/test.bash",
    "content": "#!/usr/bin/env bash\n# Script for building your rust projects.\nset -e\n\nsource ci/common.bash\n\n# $1 {path} = Path to cross/cargo executable\nCROSS=$1\n# $1 {string} = <Target Triple>\nTARGET_TRIPLE=$2\n\nrequired_arg \"$CROSS\" 'CROSS'\nrequired_arg \"$TARGET_TRIPLE\" '<Target Triple>'\n\n$CROSS test --target \"$TARGET_TRIPLE\"\n$CROSS build --target \"$TARGET_TRIPLE\" --all-features\n"
  },
  {
    "path": "fuzz/.gitignore",
    "content": "\ntarget\ncorpus\nartifacts\n"
  },
  {
    "path": "fuzz/Cargo.toml",
    "content": "\n[package]\nname = \"tokei-fuzz\"\nversion = \"0.0.1\"\nauthors = [\"Michael Macnair\"]\npublish = false\nedition = \"2018\"\n\n[package.metadata]\ncargo-fuzz = true\n\n[dependencies]\nlibfuzzer-sys = \"0.4\"\narbitrary = { version = \"1.0.0\", features = [\"derive\"] }\n\n[dependencies.tokei]\npath = \"..\"\n\n# Prevent this from interfering with workspaces\n[workspace]\nmembers = [\".\"]\n\n[[bin]]\nname = \"parse_from_slice_total\"\npath = \"fuzz_targets/parse_from_slice_total.rs\"\ntest = false\ndoc = false\n\n[[bin]]\nname = \"parse_from_slice_panic\"\npath = \"fuzz_targets/parse_from_slice_panic.rs\"\ntest = false\ndoc = false\n"
  },
  {
    "path": "fuzz/README.md",
    "content": "## Fuzzing Tokei\n\nTokei can be fuzzed using libFuzzer, via [cargo-fuzz](https://github.com/rust-fuzz/cargo-fuzz/).\n\nFirst install cargo-fuzz: `cargo install cargo-fuzz`.\n\nTo launch a fuzzing job: `cargo +nightly fuzz run <target>` - it will run until you kill it with ctrl-c.\n\nTo use multiple cores: `cargo +nightly fuzz run <target> --jobs=6`\n\nTo speed things up (at the expense of missing bugs that only manifest in larger files):\n`cargo +nightly fuzz run <target> -- -max_len=200`\n\nAvailable fuzz targets:\n\n- `parse_from_slice_panic` - checks that all of the LanguageType instances' `parse_from_slice` function doesn't panic.\n- `parse_from_slice_total` - checks that the language stats pass a basic test of reporting no more total lines than\n  there are new lines in the file. At the time of writing there are low-hanging bugs here.\n\nWith the two `parse_from_slice` fuzz targets, it makes sense to share a common corpus directory as they have identical\ninput formats, e.g.: `cargo +nightly fuzz run parse_from_slice_{panic,total} fuzz/corpus/common`\n\nPotential improvements:\n\n- Build the fuzz harnesses in CI, so they don't rot.\n- Do some coverage analysis to check if we're missing any code we would benefit from fuzzing (once it's\n  [integrated into cargo-fuzz](https://github.com/rust-fuzz/cargo-fuzz/pull/248))\n- Tighten the `parse_from_slice_total` fuzz target to check the total lines exactly matches the number of lines in the\n  file. Only once any bugs found with the current fuzzer are fixed.\n- Check in a minimized corpus, and run regression over it in CI.\n"
  },
  {
    "path": "fuzz/fuzz_targets/parse_from_slice.rs",
    "content": "use arbitrary::Arbitrary;\nuse std::str;\n\nuse tokei::{Config, LanguageType};\n\n#[derive(Arbitrary, Debug)]\npub struct FuzzInput<'a> {\n    lang: LanguageType,\n    treat_doc_strings_as_comments: bool,\n    data: &'a [u8],\n}\n\n// The first byte of data is used to select a language; remaining input is parsed\n// If check_total is true, asserts that the parsed stats pass a basic sanity test\npub fn parse_from_slice(input: FuzzInput, check_total: bool) {\n    let config = &Config {\n        treat_doc_strings_as_comments: Some(input.treat_doc_strings_as_comments),\n\n        // these options don't impact the behaviour of parse_from_slice:\n        columns: None,\n        hidden: None,\n        no_ignore: None,\n        no_ignore_parent: None,\n        no_ignore_dot: None,\n        no_ignore_vcs: None,\n        sort: None,\n        types: None,\n        for_each_fn: None,\n    };\n\n    // check that parsing doesn't panic\n    let stats = input.lang.parse_from_slice(input.data, config);\n\n    if check_total {\n        // verify that the parsed total lines is not more than the total occurrences of \\n and \\r\\n.\n        // if/when all of the current discrepancies are fixed, we could make this stronger by checking it is equal.\n        if let Ok(s) = str::from_utf8(input.data) {\n            assert!(\n            stats.lines() <= s.lines().count(),\n            \"{} got more total lines ({}) than str::lines ({}). Code: {}, Comments: {}, Blanks: {}. treat_doc_strings_as_comments: {}. File contents (as UTF-8):\\n{}\",\n            input.lang.name(),\n            stats.lines(),\n            s.lines().count(),\n            stats.code,\n            stats.comments,\n            input.treat_doc_strings_as_comments,\n            stats.blanks,\n            s\n        )\n        };\n    }\n}\n"
  },
  {
    "path": "fuzz/fuzz_targets/parse_from_slice_panic.rs",
    "content": "#![no_main]\nuse libfuzzer_sys::fuzz_target;\n\nmod parse_from_slice;\nuse parse_from_slice::{parse_from_slice, FuzzInput};\n\nfuzz_target!(|data: FuzzInput| {\n    parse_from_slice(data, false);\n});\n"
  },
  {
    "path": "fuzz/fuzz_targets/parse_from_slice_total.rs",
    "content": "#![no_main]\nuse libfuzzer_sys::fuzz_target;\n\nmod parse_from_slice;\nuse parse_from_slice::{parse_from_slice, FuzzInput};\n\nfuzz_target!(|data: FuzzInput| {\n    parse_from_slice(data, true);\n});\n"
  },
  {
    "path": "languages.json",
    "content": "{\n  \"languages\": {\n    \"Abap\": {\n      \"name\": \"ABAP\",\n      \"line_comment\": [\"*\", \"\\\\\\\"\"],\n      \"extensions\": [\"abap\"]\n    },\n    \"ABNF\": {\n      \"line_comment\": [\";\"],\n      \"extensions\": [\"abnf\"]\n    },\n    \"ActionScript\": {\n      \"line_comment\": [\"//\"],\n      \"multi_line_comments\": [[\"/*\", \"*/\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"]],\n      \"extensions\": [\"as\"]\n    },\n    \"Ada\": {\n      \"line_comment\": [\"--\"],\n      \"extensions\": [\"ada\", \"adb\", \"ads\", \"pad\"]\n    },\n    \"Agda\": {\n      \"nested\": true,\n      \"line_comment\": [\"--\"],\n      \"multi_line_comments\": [[\"{-\", \"-}\"]],\n      \"extensions\": [\"agda\"]\n    },\n    \"Alex\": {\n      \"extensions\": [\"x\"]\n    },\n    \"Alloy\": {\n      \"line_comment\": [\"--\", \"//\"],\n      \"multi_line_comments\": [[\"/*\", \"*/\"]],\n      \"extensions\": [\"als\"]\n    },\n    \"Apl\": {\n      \"name\": \"APL\",\n      \"line_comment\": [\"⍝\"],\n      \"extensions\": [\"apl\", \"aplf\", \"apls\"],\n      \"quotes\": [[\"'\", \"'\"]]\n    },\n    \"Arduino\": {\n      \"name\": \"Arduino C++\",\n      \"line_comment\": [\"//\"],\n      \"multi_line_comments\": [[\"/*\", \"*/\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"]],\n      \"extensions\": [\"ino\"]\n    },\n    \"ArkTS\": {\n      \"name\": \"Ark TypeScript\",\n      \"line_comment\": [\"//\"],\n      \"multi_line_comments\": [[\"/*\", \"*/\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"], [\"'\", \"'\"], [\"`\", \"`\"]],\n      \"extensions\": [\"ets\"]\n    },\n    \"Arturo\": {\n      \"line_comment\": [\";\"],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"]],\n      \"extensions\": [\"art\"]\n    },\n    \"AsciiDoc\": {\n      \"line_comment\": [\"//\"],\n      \"multi_line_comments\": [[\"////\", \"////\"]],\n      \"extensions\": [\"adoc\", \"asciidoc\"]\n    },\n    \"Asn1\": {\n      \"name\": \"ASN.1\",\n      \"line_comment\": [\"--\"],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"], [\"'\", \"'\"]],\n      \"multi_line_comments\": [[\"/*\", \"*/\"]],\n      \"extensions\": [\"asn1\"]\n    },\n    \"Asp\": {\n      \"name\": \"ASP\",\n      \"line_comment\": [\"'\", \"REM\"],\n      \"extensions\": [\"asa\", \"asp\"]\n    },\n    \"AspNet\": {\n      \"name\": \"ASP.NET\",\n      \"multi_line_comments\": [[\"<!--\", \"-->\"], [\"<%--\", \"-->\"]],\n      \"extensions\": [\n        \"asax\",\n        \"ascx\",\n        \"asmx\",\n        \"aspx\",\n        \"master\",\n        \"sitemap\",\n        \"webinfo\"\n      ]\n    },\n    \"Assembly\": {\n      \"line_comment\": [\";\"],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"], [\"'\", \"'\"]],\n      \"extensions\": [\"asm\"]\n    },\n    \"AssemblyGAS\": {\n      \"name\": \"GNU Style Assembly\",\n      \"line_comment\": [\"//\"],\n      \"multi_line_comments\": [[\"/*\", \"*/\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"]],\n      \"extensions\": [\"s\"]\n    },\n    \"Astro\": {\n      \"line_comment\": [\"//\"],\n      \"multi_line_comments\": [[\"/*\", \"*/\"], [\"<!--\", \"-->\"]],\n      \"extensions\": [\"astro\"]\n    },\n    \"Ats\": {\n      \"name\": \"ATS\",\n      \"line_comment\": [\"//\"],\n      \"multi_line_comments\": [[\"(*\", \"*)\"], [\"/*\", \"*/\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"]],\n      \"extensions\": [\n        \"dats\",\n        \"hats\",\n        \"sats\",\n        \"atxt\"\n      ]\n    },\n    \"Autoconf\": {\n      \"line_comment\": [\"#\", \"dnl\"],\n      \"extensions\": [\"in\"]\n    },\n    \"Autoit\": {\n      \"line_comment\": [\";\"],\n      \"multi_line_comments\": [[\"#comments-start\", \"#comments-end\"], [\"#cs\", \"#ce\"]],\n      \"extensions\": [\"au3\"]\n    },\n    \"AutoHotKey\": {\n      \"line_comment\": [\";\"],\n      \"multi_line_comments\": [[\"/*\", \"*/\"]],\n      \"extensions\": [\"ahk\"]\n    },\n    \"Automake\": {\n      \"line_comment\": [\"#\"],\n      \"extensions\": [\"am\"]\n    },\n    \"AvaloniaXaml\": {\n      \"name\": \"AXAML\",\n      \"multi_line_comments\": [[\"<!--\", \"-->\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"], [\"'\", \"'\"]],\n      \"extensions\": [\"axaml\"]\n    },\n    \"AWK\": {\n      \"line_comment\": [\"#\"],\n      \"shebangs\": [\"#!/bin/awk -f\"],\n      \"extensions\": [\"awk\"]\n    },\n    \"Ballerina\": {\n      \"line_comment\": [\"//\", \"#\"],\n      \"quotes\": [\n        [\"\\\\\\\"\", \"\\\\\\\"\"],\n        [\"`\", \"`\"]\n      ],\n      \"extensions\": [\"bal\"]\n    },\n    \"Bash\": {\n      \"name\": \"BASH\",\n      \"shebangs\": [\"#!/bin/bash\"],\n      \"line_comment\": [\"#\"],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"], [\"'\", \"'\"]],\n      \"env\": [\"bash\"],\n      \"extensions\": [\"bash\"]\n    },\n    \"Batch\": {\n      \"line_comment\": [\"REM\", \"::\"],\n      \"extensions\": [\"bat\", \"btm\", \"cmd\"]\n    },\n    \"Bazel\": {\n      \"line_comment\": [\"#\"],\n      \"doc_quotes\": [[\"\\\\\\\"\\\\\\\"\\\\\\\"\", \"\\\\\\\"\\\\\\\"\\\\\\\"\"], [\"'''\", \"'''\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"], [\"'\", \"'\"]],\n      \"extensions\": [\"bzl\", \"bazel\", \"bzlmod\"],\n      \"filenames\": [\"build\", \"workspace\", \"module\"]\n    },\n    \"Bean\": {\n      \"line_comment\": [\";\"],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"]],\n      \"extensions\": [\"bean\", \"beancount\"]\n    },\n    \"Bicep\" : {\n      \"line_comment\": [\"//\"],\n      \"multi_line_comments\": [[\"/*\", \"*/\"]],\n      \"quotes\": [[\"'\", \"'\"], [\"'''\", \"'''\"]],\n      \"extensions\": [\"bicep\", \"bicepparam\"]\n    },\n    \"Bitbake\": {\n      \"name\": \"Bitbake\",\n      \"line_comment\": [\"#\"],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"], [\"'\", \"'\"]],\n      \"extensions\": [\"bb\", \"bbclass\", \"bbappend\", \"inc\"]\n    },\n    \"Bqn\": {\n      \"name\": \"BQN\",\n      \"line_comment\": [\"#\"],\n      \"extensions\": [\"bqn\"],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"], [\"'\", \"'\"]]\n    },\n    \"BrightScript\": {\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"]],\n      \"line_comment\": [\"'\", \"REM\"],\n      \"extensions\": [\"brs\"]\n    },\n    \"C\": {\n      \"line_comment\": [\"//\"],\n      \"multi_line_comments\": [[\"/*\", \"*/\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"]],\n      \"extensions\": [\"c\", \"ec\", \"pgc\"]\n    },\n    \"C3\": {\n      \"line_comment\": [\"//\"],\n      \"multi_line_comments\": [[\"/*\", \"*/\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"]],\n      \"extensions\": [\"c3\"]\n    },\n    \"Cabal\": {\n      \"nested\": true,\n      \"line_comment\": [\"--\"],\n      \"multi_line_comments\": [[\"{-\", \"-}\"]],\n      \"extensions\": [\"cabal\"]\n    },\n    \"Cairo\": {\n      \"line_comment\": [\"//\"],\n      \"extensions\": [\"cairo\"],\n      \"quotes\": [\n        [\"\\\\\\\"\", \"\\\\\\\"\"],\n        [\"'\", \"'\"]\n      ]\n    },\n    \"Cangjie\": {\n      \"line_comment\": [\"//\"],\n      \"multi_line_comments\": [[\"/*\", \"*/\"]],\n      \"nested\": true,\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"],[\"\\\\\\\"\\\\\\\"\\\\\\\"\", \"\\\\\\\"\\\\\\\"\\\\\\\"\"]],\n      \"verbatim_quotes\": [[\"#\\\\\\\"\", \"\\\\\\\"#\"],[\"##\\\\\\\"\", \"\\\\\\\"##\"],[\"###\\\\\\\"\", \"\\\\\\\"###\"],\n        [\"#'\", \"'#\"],[\"##'\", \"'##\"],[\"###'\", \"'###\"]],\n      \"extensions\": [\"cj\"]\n    },\n    \"Cassius\": {\n      \"line_comment\": [\"//\"],\n      \"multi_line_comments\": [[\"/*\", \"*/\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"], [\"'\", \"'\"]],\n      \"extensions\": [\"cassius\"]\n    },\n    \"Ceylon\": {\n      \"line_comment\": [\"//\"],\n      \"multi_line_comments\": [[\"/*\", \"*/\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"], [\"\\\\\\\"\\\\\\\"\\\\\\\"\", \"\\\\\\\"\\\\\\\"\\\\\\\"\"]],\n      \"extensions\": [\"ceylon\"]\n    },\n    \"Chapel\": {\n      \"line_comment\": [\"//\"],\n      \"multi_line_comments\": [[\"/*\", \"*/\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"], [\"'\", \"'\"]],\n      \"extensions\": [\"chpl\"]\n    },\n    \"CHeader\": {\n      \"name\": \"C Header\",\n      \"line_comment\": [\"//\"],\n      \"multi_line_comments\": [[\"/*\", \"*/\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"]],\n      \"extensions\": [\"h\"]\n    },\n    \"Cil\": {\n      \"name\": \"CIL (SELinux)\",\n      \"line_comment\": [\";\"],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"]],\n      \"extensions\": [\"cil\"]\n    },\n    \"Circom\": {\n      \"line_comment\": [\"//\"],\n      \"multi_line_comments\": [[\"/*\", \"*/\"]],\n      \"extensions\": [\"circom\"]\n    },\n    \"Clojure\": {\n      \"line_comment\": [\";\"],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"]],\n      \"extensions\": [\"clj\"]\n    },\n    \"ClojureC\": {\n      \"line_comment\": [\";\"],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"]],\n      \"extensions\": [\"cljc\"]\n    },\n    \"ClojureScript\": {\n      \"line_comment\": [\";\"],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"]],\n      \"extensions\": [\"cljs\"]\n    },\n    \"CMake\": {\n      \"line_comment\": [\"#\"],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"]],\n      \"extensions\": [\"cmake\"],\n      \"filenames\": [\"cmakelists.txt\"]\n    },\n    \"Cobol\": {\n      \"name\": \"COBOL\",\n      \"line_comment\": [\"*\"],\n      \"extensions\": [\"cob\", \"cbl\", \"ccp\", \"cobol\", \"cpy\"]\n    },\n    \"CodeQL\": {\n      \"line_comment\": [\"//\"],\n      \"multi_line_comments\": [[\"/*\", \"*/\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"]],\n      \"extensions\": [\"ql\", \"qll\"]\n    },\n    \"CoffeeScript\": {\n      \"line_comment\": [\"#\"],\n      \"multi_line_comments\": [[\"###\", \"###\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"], [\"'\", \"'\"]],\n      \"extensions\": [\"coffee\", \"cjsx\"]\n    },\n    \"Cogent\": {\n      \"line_comment\": [\"--\"],\n      \"extensions\": [\"cogent\"]\n    },\n    \"ColdFusion\": {\n      \"multi_line_comments\": [[\"<!---\", \"--->\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"], [\"'\", \"'\"]],\n      \"extensions\": [\"cfm\"]\n    },\n    \"ColdFusionScript\": {\n      \"name\": \"ColdFusion CFScript\",\n      \"line_comment\": [\"//\"],\n      \"multi_line_comments\": [[\"/*\", \"*/\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"]],\n      \"extensions\": [\"cfc\"]\n    },\n    \"Coq\": {\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"]],\n      \"multi_line_comments\": [[\"(*\", \"*)\"]],\n      \"extensions\": [\"v\"]\n    },\n    \"Cpp\": {\n      \"name\": \"C++\",\n      \"line_comment\": [\"//\"],\n      \"multi_line_comments\": [[\"/*\", \"*/\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"]],\n      \"verbatim_quotes\": [[\"R\\\\\\\"(\", \")\\\\\\\"\"]],\n      \"extensions\": [\"cc\", \"cpp\", \"cxx\", \"c++\", \"pcc\", \"tpp\"]\n    },\n    \"CppHeader\": {\n      \"name\": \"C++ Header\",\n      \"line_comment\": [\"//\"],\n      \"multi_line_comments\": [[\"/*\", \"*/\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"]],\n      \"extensions\": [\"hh\", \"hpp\", \"hxx\", \"inl\", \"ipp\"]\n    },\n    \"CppModule\": {\n      \"name\": \"C++ Module\",\n      \"line_comment\": [\"//\"],\n      \"multi_line_comments\": [[\"/*\", \"*/\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"]],\n      \"verbatim_quotes\": [[\"R\\\\\\\"(\", \")\\\\\\\"\"]],\n      \"extensions\": [\"cppm\", \"ixx\", \"ccm\", \"mpp\", \"mxx\", \"cxxm\", \"hppm\", \"hxxm\"]\n    },\n    \"Crystal\": {\n      \"line_comment\": [\"#\"],\n      \"shebangs\": [\"#!/usr/bin/crystal\"],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"], [\"'\", \"'\"]],\n      \"env\": [\"crystal\"],\n      \"extensions\": [\"cr\"]\n    },\n    \"CSharp\": {\n      \"name\": \"C#\",\n      \"line_comment\": [\"//\"],\n      \"multi_line_comments\": [[\"/*\", \"*/\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"]],\n      \"verbatim_quotes\": [[\"@\\\\\\\"\", \"\\\\\\\"\"]],\n      \"extensions\": [\"cs\", \"csx\"]\n    },\n    \"CShell\": {\n      \"name\": \"C Shell\",\n      \"shebangs\": [\"#!/bin/csh\"],\n      \"line_comment\": [\"#\"],\n      \"env\": [\"csh\"],\n      \"extensions\": [\"csh\"]\n    },\n    \"Css\": {\n      \"name\": \"CSS\",\n      \"line_comment\": [\"//\"],\n      \"multi_line_comments\": [[\"/*\", \"*/\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"], [\"'\", \"'\"]],\n      \"mime\": [\"text/css\"],\n      \"extensions\": [\"css\"]\n    },\n    \"Cuda\": {\n      \"name\": \"CUDA\",\n      \"line_comment\": [\"//\"],\n      \"multi_line_comments\": [[\"/*\", \"*/\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"]],\n      \"extensions\": [\"cu\"]\n    },\n    \"Cue\": {\n      \"name\": \"CUE\",\n      \"line_comment\": [\"//\"],\n      \"quotes\": [\n        [\"\\\\\\\"\", \"\\\\\\\"\"],\n        [\"'\", \"'\"],\n        [\"\\\\\\\"\\\\\\\"\\\\\\\"\", \"\\\\\\\"\\\\\\\"\\\\\\\"\"]\n      ],\n      \"verbatim_quotes\": [[\"#\\\\\\\"\", \"\\\\\\\"#\"]],\n      \"extensions\": [\"cue\"]\n    },\n    \"Cython\": {\n      \"line_comment\": [\"#\"],\n      \"doc_quotes\": [[\"\\\\\\\"\\\\\\\"\\\\\\\"\", \"\\\\\\\"\\\\\\\"\\\\\\\"\"], [\"'''\", \"'''\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"], [\"'\", \"'\"]],\n      \"env\": [\"cython\"],\n      \"extensions\": [\"pyx\", \"pxd\", \"pxi\"]\n    },\n    \"D\": {\n      \"line_comment\": [\"//\"],\n      \"multi_line_comments\": [[\"/*\", \"*/\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"], [\"'\", \"'\"]],\n      \"nested_comments\": [[\"/+\", \"+/\"]],\n      \"extensions\": [\"d\"]\n    },\n    \"D2\": {\n      \"line_comment\": [\"#\"],\n      \"multi_line_comments\": [[\"\\\\\\\"\\\\\\\"\\\\\\\"\", \"\\\\\\\"\\\\\\\"\\\\\\\"\"]],\n      \"extensions\": [\"d2\"]\n    },\n    \"Daml\": {\n      \"name\": \"DAML\",\n      \"nested\": true,\n      \"line_comment\": [\"-- \"],\n      \"multi_line_comments\": [[\"{-\", \"-}\"]],\n      \"extensions\": [\"daml\"]\n    },\n    \"Dart\": {\n      \"line_comment\": [\"//\"],\n      \"multi_line_comments\": [[\"/*\", \"*/\"]],\n      \"quotes\": [\n        [\"\\\\\\\"\", \"\\\\\\\"\"],\n        [\"'\", \"'\"],\n        [\"\\\\\\\"\\\\\\\"\\\\\\\"\", \"\\\\\\\"\\\\\\\"\\\\\\\"\"],\n        [\"'''\", \"'''\"]\n      ],\n      \"extensions\": [\"dart\"]\n    },\n    \"DeviceTree\": {\n      \"name\": \"Device Tree\",\n      \"line_comment\": [\"//\"],\n      \"multi_line_comments\": [[\"/*\", \"*/\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"]],\n      \"extensions\": [\"dts\", \"dtsi\"]\n    },\n    \"Dhall\":{\n      \"nested\": true,\n      \"line_comment\": [\"--\"],\n      \"multi_line_comments\": [[\"{-\", \"-}\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"], [\"''\", \"''\"]],\n      \"extensions\": [\"dhall\"]\n    },\n    \"Djot\": {\n      \"literate\": true,\n      \"important_syntax\": [\"```\"],\n      \"extensions\": [\"dj\", \"djot\"]\n    },\n    \"Dockerfile\": {\n      \"line_comment\": [\"#\"],\n      \"extensions\": [\"dockerfile\", \"dockerignore\"],\n      \"filenames\": [\"dockerfile\"],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"], [\"'\", \"'\"]]\n    },\n    \"DotNetResource\": {\n      \"name\": \".NET Resource\",\n      \"multi_line_comments\": [[\"<!--\", \"-->\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"]],\n      \"extensions\": [\"resx\"]\n    },\n    \"DreamMaker\": {\n      \"name\": \"Dream Maker\",\n      \"line_comment\": [\"//\"],\n      \"multi_line_comments\": [[\"/*\", \"*/\"]],\n      \"nested\": true,\n      \"extensions\": [\"dm\", \"dme\"],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"], [\"{\\\\\\\"\", \"\\\\\\\"}\"], [\"'\", \"'\"]]\n    },\n    \"Dust\": {\n      \"name\": \"Dust.js\",\n      \"multi_line_comments\": [[\"{!\", \"!}\"]],\n      \"extensions\": [\"dust\"]\n    },\n    \"Ebuild\": {\n      \"line_comment\": [\"#\"],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"], [\"'\", \"'\"]],\n      \"extensions\": [\"ebuild\", \"eclass\"]\n    },\n    \"EdgeQL\": {\n      \"name\": \"EdgeQL\",\n      \"line_comment\": [\"#\"],\n      \"quotes\": [[\"'\", \"'\"], [\"\\\\\\\"\", \"\\\\\\\"\"], [\"$\", \"$\"]],\n      \"extensions\": [\"edgeql\"]\n    },\n    \"ESDL\": {\n      \"name\": \"EdgeDB Schema Definition\",\n      \"line_comment\": [\"#\"],\n      \"quotes\": [[\"'\", \"'\"], [\"\\\\\\\"\", \"\\\\\\\"\"]],\n      \"extensions\": [\"esdl\"]\n    },\n    \"Edn\": {\n      \"line_comment\": [\";\"],\n      \"extensions\": [\"edn\"]\n    },\n    \"Eighth\": {\n      \"name\": \"8th\",\n      \"line_comment\": [\"\\\\\\\\ \", \"-- \"],\n      \"multi_line_comments\": [[\"(*\", \"*)\"]],\n      \"nested\": true,\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"]],\n      \"extensions\": [\"8th\"]\n    },\n    \"Elisp\": {\n      \"name\": \"Emacs Lisp\",\n      \"line_comment\": [\";\"],\n      \"extensions\": [\"el\"]\n    },\n    \"Elixir\": {\n      \"line_comment\": [\"#\"],\n      \"quotes\": [\n        [\"\\\\\\\"\\\\\\\"\\\\\\\"\", \"\\\\\\\"\\\\\\\"\\\\\\\"\"],\n        [\"\\\\\\\"\", \"\\\\\\\"\"],\n        [\"'''\", \"'''\"],\n        [\"'\", \"'\"]\n      ],\n      \"extensions\": [\"ex\", \"exs\"]\n    },\n    \"Elm\": {\n      \"nested\": true,\n      \"line_comment\": [\"--\"],\n      \"multi_line_comments\": [[\"{-\", \"-}\"]],\n      \"extensions\": [\"elm\"]\n    },\n    \"Elvish\": {\n      \"line_comment\": [\"#\"],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"], [\"'\", \"'\"]],\n      \"env\": [\"elvish\"],\n      \"extensions\": [\"elv\"]\n    },\n    \"EmacsDevEnv\": {\n      \"name\": \"Emacs Dev Env\",\n      \"line_comment\": [\";\"],\n      \"extensions\": [\"ede\"]\n    },\n    \"Emojicode\": {\n      \"line_comment\": [\"💭\"],\n      \"multi_line_comments\": [[\"💭🔜\", \"🔚💭\"], [\"📗\", \"📗\"], [\"📘\", \"📘\"]],\n      \"quotes\": [[\"❌🔤\", \"❌🔤\"]],\n      \"extensions\": [\"emojic\", \"🍇\"]\n    },\n    \"Erlang\": {\n      \"line_comment\": [\"%\"],\n      \"extensions\": [\"erl\", \"hrl\"]\n    },\n    \"Factor\": {\n        \"line_comment\": [\"!\", \"#!\"],\n        \"multi_line_comments\": [[\"/*\", \"*/\"]],\n        \"extensions\": [\"factor\"]\n    },\n    \"FEN\": {\n      \"name\": \"FEN\",\n      \"blank\": true,\n      \"extensions\": [\"fen\"]\n    },\n    \"Fennel\" : {\n      \"line_comment\": [\";\", \";;\"],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"]],\n      \"extensions\": [\"fnl\", \"fnlm\"]\n    },\n    \"Fish\": {\n      \"shebangs\": [\"#!/bin/fish\"],\n      \"line_comment\": [\"#\"],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"], [\"'\", \"'\"]],\n      \"env\": [\"fish\"],\n      \"extensions\": [\"fish\"]\n    },\n    \"FlatBuffers\": {\n      \"name\": \"FlatBuffers Schema\",\n      \"line_comment\": [\"//\"],\n      \"multi_line_comments\": [[\"/*\", \"*/\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"]],\n      \"extensions\": [\"fbs\"]\n    },\n    \"ForgeConfig\": {\n      \"name\": \"Forge Config\",\n      \"line_comment\": [\"#\", \"~\"],\n      \"extensions\": [\"cfg\"]\n    },\n    \"Forth\": {\n      \"line_comment\": [\"\\\\\\\\\"],\n      \"multi_line_comments\": [[\"( \", \")\"]],\n      \"extensions\": [\n        \"4th\",\n        \"forth\",\n        \"fr\",\n        \"frt\",\n        \"fth\",\n        \"f83\",\n        \"fb\",\n        \"fpm\",\n        \"e4\",\n        \"rx\",\n        \"ft\"\n      ]\n    },\n    \"FortranLegacy\": {\n      \"name\": \"FORTRAN Legacy\",\n      \"line_comment\": [\"c\", \"C\", \"!\", \"*\"],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"], [\"'\", \"'\"]],\n      \"extensions\": [\"f\", \"for\", \"ftn\", \"f77\", \"pfo\"]\n    },\n    \"FortranModern\": {\n      \"name\": \"FORTRAN Modern\",\n      \"line_comment\": [\"!\"],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"]],\n      \"extensions\": [\"f03\", \"f08\", \"f90\", \"f95\", \"fpp\"]\n    },\n    \"FreeMarker\": {\n      \"multi_line_comments\": [[\"<#--\", \"-->\"]],\n      \"extensions\": [\"ftl\", \"ftlh\", \"ftlx\"]\n    },\n    \"FSharp\": {\n      \"name\": \"F#\",\n      \"line_comment\": [\"//\"],\n      \"multi_line_comments\": [[\"(*\", \"*)\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"]],\n      \"verbatim_quotes\": [[\"@\\\\\\\"\", \"\\\\\\\"\"]],\n      \"extensions\": [\"fs\", \"fsi\", \"fsx\", \"fsscript\"]\n    },\n    \"Fstar\": {\n      \"name\": \"F*\",\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"]],\n      \"line_comment\": [\"//\"],\n      \"multi_line_comments\": [[\"(*\", \"*)\"]],\n      \"extensions\": [\"fst\", \"fsti\"]\n    },\n    \"Futhark\": {\n      \"line_comment\": [\"--\"],\n      \"extensions\": [\"fut\"]\n    },\n    \"GDB\": {\n      \"name\": \"GDB Script\",\n      \"line_comment\": [\"#\"],\n      \"extensions\": [\"gdb\"]\n    },\n    \"GdScript\": {\n      \"name\": \"GDScript\",\n      \"line_comment\": [\"#\"],\n      \"quotes\": [\n        [\"\\\\\\\"\", \"\\\\\\\"\"],\n        [\"'\", \"'\"],\n        [\"\\\\\\\"\\\\\\\"\\\\\\\"\", \"\\\\\\\"\\\\\\\"\\\\\\\"\"]\n      ],\n      \"extensions\": [\"gd\"]\n    },\n    \"Gherkin\": {\n      \"name\": \"Gherkin (Cucumber)\",\n      \"line_comment\": [\"#\"],\n      \"extensions\": [\"feature\"]\n    },\n    \"Gleam\": {\n      \"name\": \"Gleam\",\n      \"line_comment\": [\"//\", \"///\", \"////\"],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"]],\n      \"extensions\": [\"gleam\"]\n    },\n    \"GlimmerJs\": {\n      \"name\": \"Glimmer JS\",\n      \"line_comment\": [\"//\"],\n      \"multi_line_comments\": [[\"/*\", \"*/\"], [\"<!--\", \"-->\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"], [\"'\", \"'\"], [\"`\", \"`\"]],\n      \"important_syntax\": [\"<template\", \"<style\"],\n      \"extensions\": [\"gjs\"]\n    },\n    \"GlimmerTs\": {\n      \"name\": \"Glimmer TS\",\n      \"line_comment\": [\"//\"],\n      \"multi_line_comments\": [[\"/*\", \"*/\"], [\"<!--\", \"-->\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"], [\"'\", \"'\"], [\"`\", \"`\"]],\n      \"important_syntax\": [\"<template\", \"<style\"],\n      \"extensions\": [\"gts\"]\n    },\n    \"Glsl\": {\n      \"name\": \"GLSL\",\n      \"line_comment\": [\"//\"],\n      \"multi_line_comments\": [[\"/*\", \"*/\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"]],\n      \"extensions\": [\"vert\", \"tesc\", \"tese\", \"geom\", \"frag\", \"comp\", \"mesh\", \"task\", \"rgen\", \"rint\", \"rahit\", \"rchit\", \"rmiss\", \"rcall\", \"glsl\"]\n    },\n    \"Gml\": {\n      \"name\": \"Gml\",\n      \"line_comment\": [\"//\"],\n      \"multi_line_comments\": [[\"/*\", \"*/\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"]],\n      \"extensions\": [\"gml\"]\n    },\n    \"Go\": {\n      \"line_comment\": [\"//\"],\n      \"multi_line_comments\": [[\"/*\", \"*/\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"]],\n      \"extensions\": [\"go\"]\n    },\n    \"Gohtml\": {\n      \"name\": \"Go HTML\",\n      \"multi_line_comments\": [[\"<!--\", \"-->\"], [\"{{/*\", \"*/}}\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"], [\"'\", \"'\"]],\n      \"extensions\": [\"gohtml\"]\n    },\n    \"Graphql\": {\n      \"name\": \"GraphQL\",\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"], [\"\\\\\\\"\\\\\\\"\\\\\\\"\", \"\\\\\\\"\\\\\\\"\\\\\\\"\"]],\n      \"line_comment\": [\"#\"],\n      \"extensions\": [\"gql\", \"graphql\"]\n    },\n    \"Groovy\": {\n      \"line_comment\": [\"//\"],\n      \"multi_line_comments\": [[\"/*\", \"*/\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"]],\n      \"env\": [\"groovy\"],\n      \"extensions\": [\"groovy\", \"grt\", \"gtpl\", \"gvy\"]\n    },\n    \"Gwion\": {\n      \"line_comment\": [\"#!\"],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"]],\n      \"extensions\": [\"gw\"]\n    },\n    \"Haml\": {\n      \"line_comment\": [\"-#\"],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"], [\"'\", \"'\"]],\n      \"extensions\": [\"haml\"]\n    },\n    \"Hamlet\": {\n      \"multi_line_comments\": [[\"<!--\", \"-->\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"], [\"'\", \"'\"]],\n      \"extensions\": [\"hamlet\"]\n    },\n    \"Happy\": {\n      \"extensions\": [\"y\", \"ly\"]\n    },\n    \"Handlebars\": {\n      \"multi_line_comments\": [[\"<!--\", \"-->\"], [\"{{!\", \"}}\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"], [\"'\", \"'\"]],\n      \"extensions\": [\"hbs\", \"handlebars\"]\n    },\n    \"Haskell\": {\n      \"nested\": true,\n      \"line_comment\": [\"--\"],\n      \"multi_line_comments\": [[\"{-\", \"-}\"]],\n      \"extensions\": [\"hs\"]\n    },\n    \"Haxe\": {\n      \"line_comment\": [\"//\"],\n      \"multi_line_comments\": [[\"/*\", \"*/\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"], [\"'\", \"'\"]],\n      \"extensions\": [\"hx\"]\n    },\n    \"Hcl\": {\n      \"name\": \"HCL\",\n      \"line_comment\": [\"#\", \"//\"],\n      \"multi_line_comments\": [[\"/*\", \"*/\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"]],\n      \"extensions\": [\"hcl\", \"tf\", \"tfvars\"]\n    },\n    \"Headache\": {\n      \"line_comment\": [\"//\"],\n      \"multi_line_comments\": [[\"/*\", \"*/\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"]],\n      \"extensions\": [\"ha\"]\n    },\n    \"Hex\": {\n      \"name\": \"HEX\",\n      \"blank\": true,\n      \"extensions\": [\"hex\"]\n    },\n    \"Hex0\": {\n      \"extensions\": [\"hex0\"],\n      \"line_comment\": [\"#\", \";\"]\n    },\n    \"Hex1\": {\n      \"extensions\": [\"hex1\"],\n      \"line_comment\": [\"#\", \";\"]\n    },\n    \"Hex2\": {\n      \"extensions\": [\"hex2\"],\n      \"line_comment\": [\"#\", \";\"]\n    },\n    \"HiCad\": {\n      \"name\": \"HICAD\",\n      \"line_comment\": [\"REM\", \"rem\"],\n      \"extensions\": [\"MAC\", \"mac\"]\n    },\n    \"Hlsl\": {\n      \"name\": \"HLSL\",\n      \"line_comment\": [\"//\"],\n      \"multi_line_comments\": [[\"/*\", \"*/\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"]],\n      \"extensions\": [\"hlsl\", \"fx\", \"fxsub\"]\n    },\n    \"HolyC\": {\n      \"line_comment\": [\"//\"],\n      \"multi_line_comments\": [[\"/*\", \"*/\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"]],\n      \"extensions\": [\"HC\", \"hc\",\"ZC\",\"zc\"]\n    },\n    \"Html\": {\n      \"name\": \"HTML\",\n      \"multi_line_comments\": [[\"<!--\", \"-->\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"], [\"'\", \"'\"]],\n      \"kind\": \"html\",\n      \"important_syntax\": [\"<script\", \"<style\"],\n      \"mime\": [\"text/html\"],\n      \"extensions\": [\"html\", \"htm\"]\n    },\n    \"Hy\": {\n      \"line_comment\": [\";\"],\n      \"extensions\": [\"hy\"]\n    },\n    \"Idris\": {\n      \"line_comment\": [\"--\"],\n      \"multi_line_comments\": [[\"{-\", \"-}\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"], [\"\\\\\\\"\\\\\\\"\\\\\\\"\", \"\\\\\\\"\\\\\\\"\\\\\\\"\"]],\n      \"extensions\": [\"idr\", \"lidr\"],\n      \"nested\": true\n    },\n    \"Ini\": {\n      \"name\": \"INI\",\n      \"line_comment\": [\";\", \"#\"],\n      \"extensions\": [\"ini\"]\n    },\n    \"IntelHex\": {\n      \"name\": \"Intel HEX\",\n      \"blank\": true,\n      \"extensions\": [\"ihex\"]\n    },\n    \"Isabelle\": {\n      \"line_comment\": [\"--\"],\n      \"multi_line_comments\": [\n        [\"{*\", \"*}\"],\n        [\"(*\", \"*)\"],\n        [\"‹\", \"›\"],\n        [\"\\\\\\\\<open>\", \"\\\\\\\\<close>\"]\n      ],\n      \"quotes\": [[\"''\", \"''\"]],\n      \"extensions\": [\"thy\"]\n    },\n    \"Jai\": {\n      \"name\": \"JAI\",\n      \"line_comment\": [\"//\"],\n      \"multi_line_comments\": [[\"/*\", \"*/\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"]],\n      \"extensions\": [\"jai\"],\n      \"nested\": true\n    },\n    \"Janet\": {\n      \"line_comment\": [\"#\"],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"], [\"'\", \"'\"], [\"`\", \"`\"]],\n      \"extensions\": [\"janet\"]\n    },\n    \"Java\": {\n      \"line_comment\": [\"//\"],\n      \"multi_line_comments\": [[\"/*\", \"*/\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"]],\n      \"extensions\": [\"java\"]\n    },\n    \"JavaScript\": {\n      \"line_comment\": [\"//\"],\n      \"multi_line_comments\": [[\"/*\", \"*/\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"], [\"'\", \"'\"], [\"`\", \"`\"]],\n      \"mime\": [\n          \"application/javascript\",\n          \"application/ecmascript\",\n          \"application/x-ecmascript\",\n          \"application/x-javascript\",\n          \"text/javascript\",\n          \"text/ecmascript\",\n          \"text/javascript1.0\",\n          \"text/javascript1.1\",\n          \"text/javascript1.2\",\n          \"text/javascript1.3\",\n          \"text/javascript1.4\",\n          \"text/javascript1.5\",\n          \"text/jscript\",\n          \"text/livescript\",\n          \"text/x-ecmascript\",\n          \"text/x-javascript\"\n      ],\n      \"extensions\": [\"cjs\", \"js\", \"mjs\"]\n    },\n    \"Jinja2\": {\n      \"name\": \"Jinja2\",\n      \"blank\": true,\n      \"extensions\": [\"j2\", \"jinja\"],\n      \"multi_line_comments\": [[\"{#\", \"#}\"]]\n    },\n    \"Jq\": {\n      \"name\": \"jq\",\n      \"line_comment\": [\"#\"],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"]],\n      \"extensions\": [\"jq\"]\n    },\n    \"JSLT\": {\n      \"name\": \"JSLT\",\n      \"line_comment\": [\"//\"],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"]],\n      \"extensions\": [\"jslt\"]\n    },\n    \"Json\": {\n      \"name\": \"JSON\",\n      \"blank\": true,\n      \"mime\": [\"application/json\", \"application/manifest+json\"],\n      \"extensions\": [\"json\"]\n    },\n    \"Jsonnet\": {\n      \"line_comment\": [\"//\", \"#\"],\n      \"multi_line_comments\": [[\"/*\", \"*/\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"], [\"'\", \"'\"]],\n      \"extensions\": [\"jsonnet\", \"libsonnet\"]\n    },\n    \"Jsx\": {\n      \"name\": \"JSX\",\n      \"line_comment\": [\"//\"],\n      \"multi_line_comments\": [[\"/*\", \"*/\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"], [\"'\", \"'\"], [\"`\", \"`\"]],\n      \"extensions\": [\"jsx\"]\n    },\n    \"Julia\": {\n      \"line_comment\": [\"#\"],\n      \"multi_line_comments\": [[\"#=\", \"=#\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"], [\"\\\\\\\"\\\\\\\"\\\\\\\"\", \"\\\\\\\"\\\\\\\"\\\\\\\"\"]],\n      \"nested\": true,\n      \"extensions\": [\"jl\"]\n    },\n    \"Julius\": {\n      \"line_comment\": [\"//\"],\n      \"multi_line_comments\": [[\"/*\", \"*/\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"], [\"'\", \"'\"], [\"`\", \"`\"]],\n      \"extensions\": [\"julius\"]\n    },\n    \"Jupyter\": {\n      \"name\": \"Jupyter Notebooks\",\n      \"extensions\": [\"ipynb\"]\n    },\n    \"Just\": {\n      \"shebangs\": [\"#!/usr/bin/env just --justfile\"],\n      \"env\": [\"just\"],\n      \"line_comment\": [\"#\"],\n      \"extensions\": [\"just\"],\n      \"filenames\": [\"justfile\"]\n    },\n    \"K\": {\n      \"name\": \"K\",\n      \"nested\": true,\n      \"line_comment\": [\"/\"],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"]],\n      \"extensions\": [\"k\"]\n    },\n    \"KakouneScript\": {\n      \"name\": \"Kakoune script\",\n      \"line_comment\": [\"#\"],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"], [\"'\", \"'\"]],\n      \"extensions\": [\"kak\"]\n    },\n    \"Kaem\": {\n      \"name\": \"Kaem\",\n      \"line_comment\": [\"#\"],\n      \"extensions\": [\"kaem\"]\n    },\n    \"Koka\": {\n      \"line_comment\": [\"//\"],\n      \"multi_line_comments\": [[\"/*\", \"*/\"]],\n      \"nested\": true,\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"]],\n      \"extensions\": [\"kk\"]\n    },\n    \"Kotlin\": {\n      \"line_comment\": [\"//\"],\n      \"multi_line_comments\": [[\"/*\", \"*/\"]],\n      \"nested\": true,\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"], [\"\\\\\\\"\\\\\\\"\\\\\\\"\", \"\\\\\\\"\\\\\\\"\\\\\\\"\"]],\n      \"extensions\": [\"kt\", \"kts\"]\n    },\n    \"Ksh\": {\n      \"name\": \"Korn shell\",\n      \"shebangs\": [\"#!/bin/ksh\"],\n      \"line_comment\": [\"#\"],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"], [\"'\", \"'\"]],\n      \"env\": [\"ksh\"],\n      \"extensions\": [\"ksh\"]\n    },\n    \"Lalrpop\": {\n      \"name\": \"LALRPOP\",\n      \"line_comment\": [\"//\"],\n      \"extensions\": [\"lalrpop\"],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"], [\"#\\\\\\\"\", \"\\\\\\\"#\"]],\n      \"verbatim_quotes\": [[\"r##\\\\\\\"\", \"\\\\\\\"##\"], [\"r#\\\\\\\"\", \"\\\\\\\"#\"]]\n    },\n    \"KvLanguage\": {\n      \"name\":\"KV Language\",\n      \"line_comment\": [\"# \"],\n      \"doc_quotes\": [[\"\\\\\\\"\\\\\\\"\\\\\\\"\", \"\\\\\\\"\\\\\\\"\\\\\\\"\"], [\"'''\", \"'''\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"], [\"'\", \"'\"]],\n      \"extensions\": [\"kv\"]\n    },\n    \"Lean\": {\n      \"line_comment\": [\"--\"],\n      \"multi_line_comments\": [[\"/-\", \"-/\"]],\n      \"nested\": true,\n      \"extensions\": [\"lean\", \"hlean\"]\n    },\n    \"Hledger\": {\n      \"name\": \"hledger\",\n      \"line_comment\": [\";\", \"#\"],\n      \"multi_line_comments\": [[\"comment\", \"end comment\"]],\n      \"nested\": false,\n      \"extensions\": [\"hledger\"]\n    },\n    \"Less\": {\n      \"name\": \"LESS\",\n      \"line_comment\": [\"//\"],\n      \"multi_line_comments\": [[\"/*\", \"*/\"]],\n      \"extensions\": [\"less\"],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"], [\"'\", \"'\"]]\n    },\n    \"Lex\": {\n      \"line_comment\": [\"//\"],\n      \"multi_line_comments\": [[\"/*\", \"*/\"]],\n      \"extensions\": [\"l\", \"lex\"]\n    },\n    \"Liquid\": {\n      \"name\": \"Liquid\",\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"], [\"'\", \"'\"]],\n      \"extensions\": [\"liquid\"],\n      \"multi_line_comments\": [[\"<!--\", \"-->\"], [\"{% comment %}\", \"{% endcomment %}\"]]\n    },\n    \"LinguaFranca\": {\n      \"name\": \"Lingua Franca\",\n      \"line_comment\": [\"//\", \"#\"],\n      \"important_syntax\": [\"{=\"],\n      \"multi_line_comments\": [[\"/*\", \"*/\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"]],\n      \"nested\": true,\n      \"extensions\": [\"lf\"]\n    },\n    \"LinkerScript\": {\n      \"name\": \"LD Script\",\n      \"multi_line_comments\": [[\"/*\", \"*/\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"]],\n      \"extensions\": [\"ld\", \"lds\"]\n    },\n    \"Lisp\": {\n      \"name\": \"Common Lisp\",\n      \"line_comment\": [\";\"],\n      \"multi_line_comments\": [[\"#|\", \"|#\"]],\n      \"nested\": true,\n      \"extensions\": [\"lisp\", \"lsp\", \"asd\"]\n    },\n    \"LiveScript\": {\n      \"line_comment\": [\"#\"],\n      \"multi_line_comments\": [[\"/*\", \"*/\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"], [\"'\", \"'\"]],\n      \"extensions\": [\"ls\"]\n    },\n    \"LLVM\": {\n      \"line_comment\": [\";\"],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"], [\"'\", \"'\"]],\n      \"extensions\": [\"ll\"]\n    },\n    \"Logtalk\": {\n      \"line_comment\": [\"%\"],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"]],\n      \"multi_line_comments\": [[\"/*\", \"*/\"]],\n      \"extensions\": [\"lgt\", \"logtalk\"]\n    },\n    \"LolCode\": {\n      \"name\": \"LOLCODE\",\n      \"line_comment\": [\"BTW\"],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"]],\n      \"multi_line_comments\": [[\"OBTW\", \"TLDR\"]],\n      \"extensions\": [\"lol\"]\n    },\n    \"Lua\": {\n      \"line_comment\": [\"--\"],\n      \"multi_line_comments\": [[\"--[[\", \"]]\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"], [\"'\", \"'\"]],\n      \"extensions\": [\"lua\", \"luau\"]\n    },\n    \"Lucius\": {\n      \"line_comment\": [\"//\"],\n      \"multi_line_comments\": [[\"/*\", \"*/\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"], [\"'\", \"'\"]],\n      \"extensions\": [\"lucius\"]\n    },\n    \"M1Assembly\": {\n      \"name\": \"M1 Assembly\",\n      \"extensions\": [\"m1\"],\n      \"line_comment\": [\"#\", \";\"],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"]]\n    },\n    \"M4\": {\n      \"extensions\": [\"m4\"],\n      \"line_comment\": [\"#\", \"dnl\"],\n      \"quotes\": [[\"`\", \"'\"]]\n    },\n    \"Madlang\": {\n      \"extensions\": [\"mad\"],\n      \"line_comment\": [\"#\"],\n      \"multi_line_comments\": [[\"{#\", \"#}\"]]\n    },\n    \"Makefile\": {\n      \"line_comment\": [\"#\"],\n      \"extensions\": [\"makefile\", \"mak\", \"mk\"],\n      \"filenames\": [\"gnumakefile\", \"makefile\"]\n    },\n    \"Markdown\": {\n      \"literate\": true,\n      \"important_syntax\": [\"```\"],\n      \"extensions\": [\"md\", \"markdown\"]\n    },\n    \"Max\": {\n      \"extensions\": [\"maxpat\"]\n    },\n    \"Mdx\": {\n      \"name\": \"MDX\",\n      \"literate\": true,\n      \"important_syntax\": [\"```\"],\n      \"extensions\": [\"mdx\"]\n    },\n    \"Menhir\": {\n      \"nested\": true,\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"]],\n      \"line_comment\": [\"//\"],\n      \"multi_line_comments\": [\n        [\"(*\", \"*)\"],\n        [\"/*\", \"*/\"]\n      ],\n      \"extensions\": [\"mll\", \"mly\", \"vy\"]\n    },\n    \"Meson\": {\n      \"line_comment\": [\"#\"],\n      \"quotes\": [[\"'\", \"'\"], [\"'''\", \"'''\"]],\n      \"filenames\": [\"meson.build\", \"meson_options.txt\"]\n    },\n    \"Metal\": {\n      \"name\": \"Metal Shading Language\",\n      \"line_comment\": [\"//\"],\n      \"multi_line_comments\": [[\"/*\", \"*/\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"]],\n      \"extensions\": [\"metal\"]\n    },\n    \"Mint\": {\n      \"blank\": true,\n      \"extensions\": [\"mint\"]\n    },\n    \"Mlatu\": {\n      \"line_comment\": [\"//\"],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"]],\n      \"extensions\": [\"mlt\"]\n    },\n    \"Modelica\": {\n      \"line_comment\": [\"//\"],\n      \"multi_line_comments\": [[\"/*\", \"*/\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"]],\n      \"extensions\": [\"mo\", \"mos\"]\n    },\n    \"ModuleDef\": {\n      \"name\": \"Module-Definition\",\n      \"extensions\": [\"def\"],\n      \"line_comment\": [\";\"]\n    },\n    \"Mojo\": {\n      \"line_comment\": [\"#\"],\n      \"doc_quotes\": [[\"\\\\\\\"\\\\\\\"\\\\\\\"\", \"\\\\\\\"\\\\\\\"\\\\\\\"\"], [\"'''\", \"'''\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"], [\"'\", \"'\"]],\n      \"extensions\": [\"mojo\", \"🔥\"]\n    },\n    \"MonkeyC\": {\n      \"name\": \"Monkey C\",\n      \"extensions\": [\"mc\"],\n      \"line_comment\": [\"//\"],\n      \"multi_line_comments\": [[\"/*\", \"*/\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"]]\n    },\n    \"MoonBit\": {\n      \"line_comment\": [\"//\"],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"]],\n      \"extensions\": [\"mbt\", \"mbti\"]\n    },\n    \"MoonScript\": {\n      \"line_comment\": [\"--\"],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"], [\"'\", \"'\"]],\n      \"extensions\": [\"moon\"]\n    },\n    \"MsBuild\": {\n      \"name\": \"MSBuild\",\n      \"multi_line_comments\": [[\"<!--\", \"-->\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"], [\"'\", \"'\"]],\n      \"extensions\": [\"csproj\", \"vbproj\", \"fsproj\", \"props\", \"targets\"]\n    },\n    \"Mustache\": {\n      \"multi_line_comments\": [[\"{{!\", \"}}\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"], [\"'\", \"'\"]],\n      \"extensions\": [\"mustache\"]\n    },\n    \"Nextflow\": {\n      \"line_comment\": [\"//\"],\n      \"multi_line_comments\": [[\"/*\", \"*/\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"]],\n      \"extensions\": [\"nextflow\", \"nf\"]\n    },\n    \"Nim\": {\n      \"line_comment\": [\"#\"],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"], [\"\\\\\\\"\\\\\\\"\\\\\\\"\", \"\\\\\\\"\\\\\\\"\\\\\\\"\"]],\n      \"extensions\": [\"nim\"]\n    },\n    \"Nix\": {\n      \"multi_line_comments\": [[\"/*\", \"*/\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"]],\n      \"line_comment\": [\"#\"],\n      \"extensions\": [\"nix\"]\n    },\n    \"NotQuitePerl\": {\n      \"name\": \"Not Quite Perl\",\n      \"line_comment\": [\"#\"],\n      \"multi_line_comments\": [[\"=begin\", \"=end\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"], [\"'\", \"'\"]],\n      \"extensions\": [\"nqp\"]\n    },\n    \"NuGetConfig\": {\n      \"name\": \"NuGet Config\",\n      \"multi_line_comments\": [[\"<!--\", \"-->\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"], [\"'\", \"'\"]],\n      \"filenames\": [\"nuget.config\", \"packages.config\", \"nugetdefaults.config\"]\n    },\n    \"Nushell\": {\n      \"line_comment\": [\"#\"],\n      \"quotes\": [\n        [\"\\\\\\\"\", \"\\\\\\\"\"],\n        [\"'\", \"'\"]\n      ],\n      \"extensions\": [\"nu\"]\n    },\n    \"ObjectiveC\": {\n      \"name\": \"Objective-C\",\n      \"line_comment\": [\"//\"],\n      \"multi_line_comments\": [[\"/*\", \"*/\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"]],\n      \"extensions\": [\"m\"]\n    },\n    \"ObjectiveCpp\": {\n      \"name\": \"Objective-C++\",\n      \"line_comment\": [\"//\"],\n      \"multi_line_comments\": [[\"/*\", \"*/\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"]],\n      \"extensions\": [\"mm\"]\n    },\n    \"OCaml\": {\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"]],\n      \"multi_line_comments\": [[\"(*\", \"*)\"]],\n      \"extensions\": [\"ml\", \"mli\", \"re\", \"rei\"]\n    },\n    \"Odin\": {\n      \"extensions\": [\"odin\"],\n      \"line_comment\": [\"//\"],\n      \"multi_line_comments\": [[\"/*\", \"*/\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"], [\"'\", \"'\"]]\n    },\n    \"OpenScad\": {\n      \"name\": \"OpenSCAD\",\n      \"extensions\": [\"scad\"],\n      \"line_comment\": [\"//\"],\n      \"multi_line_comments\": [[\"/*\", \"*/\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"], [\"'\", \"'\"]]\n    },\n    \"OpenPolicyAgent\": {\n      \"name\": \"Open Policy Agent\",\n      \"line_comment\": [\"#\"],\n      \"quotes\": [[\"\\\\\\\"\",\"\\\\\\\"\"], [\"`\", \"`\"]],\n      \"extensions\": [\"rego\"]\n    },\n    \"OpenCL\": {\n      \"name\": \"OpenCL\",\n      \"multi_line_comments\": [[\"/*\", \"*/\"]],\n      \"extensions\": [\"cl\", \"ocl\"]\n    },\n    \"OpenQasm\": {\n      \"name\": \"OpenQASM\",\n      \"line_comment\": [\"//\"],\n      \"multi_line_comments\": [[\"/*\", \"*/\"]],\n      \"extensions\": [\"qasm\"]\n    },\n    \"OpenType\": {\n      \"name\": \"OpenType Feature File\",\n      \"line_comment\": [\"#\"],\n      \"extensions\": [\"fea\"]\n    },\n    \"Org\": {\n      \"line_comment\": [\"# \"],\n      \"extensions\": [\"org\"]\n    },\n    \"Oz\": {\n      \"line_comment\": [\"%\"],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"]],\n      \"multi_line_comments\": [[\"/*\", \"*/\"]],\n      \"extensions\": [\"oz\"]\n    },\n    \"PacmanMakepkg\": {\n      \"name\": \"Pacman's makepkg\",\n      \"line_comment\": [\"#\"],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"], [\"'\", \"'\"]],\n      \"filenames\": [\"pkgbuild\"]\n    },\n    \"Pan\": {\n      \"line_comment\": [\"#\"],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"], [\"'\", \"'\"]],\n      \"extensions\": [\"pan\", \"tpl\"]\n    },\n    \"Pascal\": {\n      \"nested\": true,\n      \"line_comment\": [\"//\"],\n      \"multi_line_comments\": [[\"{\", \"}\"], [\"(*\", \"*)\"]],\n      \"quotes\": [[\"'\", \"'\"]],\n      \"extensions\": [\"pas\"]\n    },\n    \"Perl\": {\n      \"shebangs\": [\"#!/usr/bin/perl\"],\n      \"line_comment\": [\"#\"],\n      \"multi_line_comments\": [[\"=pod\", \"=cut\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"], [\"'\", \"'\"]],\n      \"extensions\": [\"pl\", \"pm\"]\n    },\n    \"Pest\": {\n      \"line_comment\": [\"//\"],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"], [\"'\", \"'\"]],\n      \"extensions\": [\"pest\"]\n    },\n    \"Phix\": {\n      \"line_comment\": [\"--\", \"//\", \"#!\"],\n      \"multi_line_comments\": [[\"/*\", \"*/\"], [\"--/*\", \"--*/\"]],\n      \"nested\": true,\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"], [\"'\", \"'\"]],\n      \"verbatim_quotes\": [[\"\\\\\\\"\\\\\\\"\\\\\\\"\", \"\\\\\\\"\\\\\\\"\\\\\\\"\"], [\"`\", \"`\"]],\n      \"extensions\": [\"e\",\"exw\"]\n    },\n    \"Php\": {\n      \"name\": \"PHP\",\n      \"line_comment\": [\"#\", \"//\"],\n      \"multi_line_comments\": [[\"/*\", \"*/\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"], [\"'\", \"'\"]],\n      \"extensions\": [\"php\"]\n    },\n    \"PlantUml\": {\n      \"name\": \"PlantUML\",\n      \"line_comment\": [\"'\"],\n      \"multi_line_comments\": [[\"/'\", \"'/\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"]],\n      \"extensions\": [\"puml\"]\n    },\n    \"Po\": {\n        \"name\": \"PO File\",\n        \"line_comment\": [\"#\"],\n        \"extensions\": [\"po\", \"pot\"]\n    },\n    \"Poke\": {\n      \"multi_line_comments\": [[\"/*\", \"*/\"]],\n      \"extensions\": [\"pk\"]\n    },\n    \"Polly\": {\n      \"multi_line_comments\": [[\"<!--\", \"-->\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"], [\"'\", \"'\"]],\n      \"extensions\": [\"polly\"]\n    },\n    \"Pony\": {\n      \"line_comment\": [\"//\"],\n      \"multi_line_comments\": [[\"/*\", \"*/\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"]],\n      \"doc_quotes\": [[\"\\\\\\\"\\\\\\\"\\\\\\\"\", \"\\\\\\\"\\\\\\\"\\\\\\\"\"]],\n      \"extensions\": [\"pony\"]\n    },\n    \"PostCss\": {\n      \"name\": \"PostCSS\",\n      \"line_comment\": [\"//\"],\n      \"multi_line_comments\": [[\"/*\", \"*/\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"], [\"'\", \"'\"]],\n      \"extensions\": [\"pcss\", \"sss\"]\n    },\n    \"PowerShell\": {\n      \"line_comment\": [\"#\"],\n      \"multi_line_comments\": [[\"<#\", \"#>\"]],\n      \"quotes\": [\n        [\"\\\\\\\"\", \"\\\\\\\"\"],\n        [\"'\", \"'\"],\n        [\"\\\\\\\"@\", \"@\\\\\\\"\"],\n        [\"@'\", \"'@\"]\n      ],\n      \"extensions\": [\"ps1\", \"psm1\", \"psd1\", \"ps1xml\", \"cdxml\", \"pssc\", \"psc1\"]\n    },\n    \"PRACTICE\": {\n      \"name\": \"Lauterbach PRACTICE Script\",\n      \"line_comment\": [\";\", \"//\"],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"]],\n      \"extensions\": [\"cmm\"]\n    },\n    \"Processing\": {\n      \"line_comment\": [\"//\"],\n      \"multi_line_comments\": [[\"/*\", \"*/\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"]],\n      \"extensions\": [\"pde\"]\n    },\n    \"Prolog\": {\n      \"line_comment\": [\"%\"],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"]],\n      \"multi_line_comments\": [[\"/*\", \"*/\"]],\n      \"extensions\": [\"p\", \"pro\"]\n    },\n    \"PSL\": {\n      \"name\": \"PSL Assertion\",\n      \"line_comment\": [\"//\"],\n      \"multi_line_comments\": [[\"/*\", \"*/\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"]],\n      \"extensions\": [\"psl\"]\n    },\n    \"Protobuf\": {\n      \"name\": \"Protocol Buffers\",\n      \"line_comment\": [\"//\"],\n      \"extensions\": [\"proto\"]\n    },\n    \"Pug\" : {\n      \"line_comment\": [\"//\", \"//-\"],\n      \"quotes\": [\n        [\"#{\\\\\\\"\", \"\\\\\\\"}\"],\n        [\"#{'\", \"'}\"],\n        [\"#{`\", \"`}\"]\n      ],\n      \"extensions\": [\"pug\"]\n    },\n    \"Puppet\": {\n      \"line_comment\": [\"#\"],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"], [\"'\", \"'\"]],\n      \"extensions\": [\"pp\"]\n    },\n    \"PureScript\": {\n      \"nested\": true,\n      \"line_comment\": [\"--\"],\n      \"multi_line_comments\": [[\"{-\", \"-}\"]],\n      \"extensions\": [\"purs\"]\n    },\n    \"Pyret\": {\n      \"line_comment\": [\"#\"],\n      \"multi_line_comments\": [[\"#|\", \"|#\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"], [\"'\", \"'\"]],\n      \"extensions\": [\"arr\"],\n      \"nested\": true\n    },\n    \"Python\": {\n      \"line_comment\": [\"#\"],\n      \"doc_quotes\": [[\"\\\\\\\"\\\\\\\"\\\\\\\"\", \"\\\\\\\"\\\\\\\"\\\\\\\"\"], [\"'''\", \"'''\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"], [\"'\", \"'\"]],\n      \"env\": [\"python\", \"python2\", \"python3\"],\n      \"mime\": [\"text/x-python\"],\n      \"extensions\": [\"py\", \"pyw\", \"pyi\"]\n    },\n    \"PRQL\": {\n      \"line_comment\": [\"#\"],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"], [\"'\", \"'\"]],\n      \"mime\": [\"application/prql\"],\n      \"extensions\": [\"prql\"]\n    },\n    \"Q\": {\n      \"name\": \"Q\",\n      \"nested\": true,\n      \"line_comment\": [\"/\"],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"]],\n      \"extensions\": [\"q\"]\n    },\n    \"Qcl\": {\n      \"name\": \"QCL\",\n      \"line_comment\": [\"//\"],\n      \"multi_line_comments\": [[\"/*\", \"*/\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"]],\n      \"extensions\": [\"qcl\"]\n    },\n    \"Qml\": {\n      \"name\": \"QML\",\n      \"line_comment\": [\"//\"],\n      \"multi_line_comments\": [[\"/*\", \"*/\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"], [\"'\", \"'\"]],\n      \"extensions\": [\"qml\"]\n    },\n    \"R\": {\n      \"line_comment\": [\"#\"],\n      \"extensions\": [\"r\"]\n    },\n    \"Racket\": {\n      \"line_comment\": [\";\"],\n      \"multi_line_comments\": [[\"#|\", \"|#\"]],\n      \"nested\": true,\n      \"env\": [\"racket\"],\n      \"extensions\": [\"rkt\", \"scrbl\"]\n    },\n    \"Rakefile\": {\n      \"line_comment\": [\"#\"],\n      \"multi_line_comments\": [[\"=begin\", \"=end\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"], [\"'\", \"'\"]],\n      \"filenames\": [\"rakefile\"],\n      \"extensions\": [\"rake\"]\n    },\n    \"Raku\": {\n      \"shebangs\": [\"#!/usr/bin/raku\", \"#!/usr/bin/perl6\"],\n      \"line_comment\": [\"#\"],\n      \"multi_line_comments\": [\n        [\"#`(\", \")\"],\n        [\"#`[\", \"]\"],\n        [\"#`{\", \"}\"],\n        [\"#`｢\", \"｣\"]\n      ],\n      \"nested\": true,\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"] , [\"'\", \"'\"]],\n      \"verbatim_quotes\": [[\"｢\", \"｣\"]],\n      \"doc_quotes\": [\n        [\"#|{\", \"}\"],\n        [\"#={\", \"}\"],\n        [\"#|(\", \")\"],\n        [\"#=(\", \")\"],\n        [\"#|[\", \"]\"],\n        [\"#=[\", \"]\"],\n        [\"#|｢\", \"｣\"],\n        [\"#=｢\", \"｣\"],\n        [\"=begin pod\", \"=end pod\"],\n        [\"=begin code\", \"=end code\"],\n        [\"=begin head\", \"=end head\"],\n        [\"=begin item\", \"=end item\"],\n        [\"=begin table\", \"=end table\"],\n        [\"=begin defn\", \"=end defn\"],\n        [\"=begin para\", \"=end para\"],\n        [\"=begin comment\", \"=end comment\"],\n        [\"=begin data\", \"=end data\"],\n        [\"=begin DESCRIPTION\", \"=end DESCRIPTION\"],\n        [\"=begin SYNOPSIS\", \"=end SYNOPSIS\"],\n        [\"=begin \", \"=end \"]\n      ],\n      \"env\": [\"raku\", \"perl6\"],\n      \"extensions\": [\"raku\", \"rakumod\", \"rakutest\", \"pm6\", \"pl6\", \"p6\"]\n    },\n    \"Razor\": {\n      \"line_comment\": [\"//\"],\n      \"multi_line_comments\": [[\"<!--\", \"-->\"], [\"@*\", \"*@\"], [\"/*\", \"*/\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"]],\n      \"verbatim_quotes\": [[\"@\\\\\\\"\", \"\\\\\\\"\"]],\n      \"extensions\": [\"cshtml\", \"razor\"]\n    },\n    \"Redscript\": {\n      \"name\": \"Redscript\",\n      \"line_comment\": [\"//\", \"///\"],\n      \"multi_line_comments\": [[\"/*\", \"*/\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"]],\n      \"nested\": true,\n      \"extensions\": [\"reds\"]\n    },\n    \"Renpy\": {\n      \"name\": \"Ren'Py\",\n      \"line_comment\": [\"#\"],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"], [\"'\", \"'\"], [\"`\", \"`\"]],\n      \"extensions\": [\"rpy\"]\n    },\n    \"ReScript\": {\n      \"line_comment\": [\"//\"],\n      \"multi_line_comments\": [[\"/*\", \"*/\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"]],\n      \"extensions\": [\"res\", \"resi\"]\n    },\n    \"ReStructuredText\": {\n      \"blank\": true,\n      \"extensions\": [\"rst\"]\n    },\n    \"Roc\": {\n      \"line_comment\": [\"#\"],\n      \"quotes\": [\n        [\"\\\\\\\"\", \"\\\\\\\"\"],\n        [\"'\", \"'\"]\n      ],\n      \"doc_quotes\": [[\"\\\\\\\"\\\\\\\"\\\\\\\"\", \"\\\\\\\"\\\\\\\"\\\\\\\"\"]],\n      \"extensions\": [\"roc\"]\n    },\n    \"RON\": {\n      \"name\": \"Rusty Object Notation\",\n      \"line_comment\": [\"//\"],\n      \"multi_line_comments\": [[\"/*\", \"*/\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"]],\n      \"nested\": true,\n      \"extensions\": [\"ron\"]\n    },\n    \"RPMSpecfile\": {\n      \"name\": \"RPM Specfile\",\n      \"line_comment\": [\"#\"],\n      \"extensions\": [\"spec\"]\n    },\n    \"Ruby\": {\n      \"line_comment\": [\"#\"],\n      \"multi_line_comments\": [[\"=begin\", \"=end\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"], [\"'\", \"'\"]],\n      \"env\": [\"ruby\"],\n      \"extensions\": [\"rb\"]\n    },\n    \"RubyHtml\": {\n      \"name\": \"Ruby HTML\",\n      \"multi_line_comments\": [[\"<!--\", \"-->\"]],\n      \"important_syntax\": [\"<script\", \"<style\"],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"], [\"'\", \"'\"]],\n      \"extensions\": [\"rhtml\", \"erb\"]\n    },\n    \"Rust\": {\n      \"line_comment\": [\"//\"],\n      \"multi_line_comments\": [[\"/*\", \"*/\"]],\n      \"nested\": true,\n      \"important_syntax\": [\"///\", \"//!\"],\n      \"extensions\": [\"rs\"],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"], [\"#\\\\\\\"\", \"\\\\\\\"#\"]],\n      \"verbatim_quotes\": [[\"r##\\\\\\\"\", \"\\\\\\\"##\"], [\"r#\\\\\\\"\", \"\\\\\\\"#\"]]\n    },\n    \"Sass\": {\n      \"line_comment\": [\"//\"],\n      \"multi_line_comments\": [[\"/*\", \"*/\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"], [\"'\", \"'\"]],\n      \"extensions\": [\"sass\", \"scss\"]\n    },\n    \"Scala\": {\n      \"line_comment\": [\"//\"],\n      \"multi_line_comments\": [[\"/*\", \"*/\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"]],\n      \"extensions\": [\"sc\", \"scala\"]\n    },\n    \"Scheme\": {\n      \"line_comment\": [\";\"],\n      \"multi_line_comments\": [[\"#|\", \"|#\"]],\n      \"nested\": true,\n      \"extensions\": [\"scm\", \"ss\"]\n    },\n    \"Scons\": {\n      \"line_comment\": [\"#\"],\n      \"quotes\": [\n        [\"\\\\\\\"\", \"\\\\\\\"\"],\n        [\"'\", \"'\"],\n        [\"\\\\\\\"\\\\\\\"\\\\\\\"\", \"\\\\\\\"\\\\\\\"\\\\\\\"\"],\n        [\"'''\", \"'''\"]\n      ],\n      \"filenames\": [\"sconstruct\", \"sconscript\"]\n    },\n    \"Sh\": {\n      \"name\": \"Shell\",\n      \"shebangs\": [\"#!/bin/sh\"],\n      \"line_comment\": [\"#\"],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"], [\"'\", \"'\"]],\n      \"env\": [\"sh\"],\n      \"extensions\": [\"sh\"]\n    },\n    \"ShaderLab\": {\n      \"name\": \"ShaderLab\",\n      \"line_comment\": [\"//\"],\n      \"multi_line_comments\": [[\"/*\", \"*/\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"]],\n      \"extensions\": [\"shader\", \"cginc\"]\n    },\n    \"SIL\": {\n      \"name\": \"SIL\",\n      \"line_comment\": [\"//\"],\n      \"multi_line_comments\": [[\"/*\", \"*/\"], [\"/+\", \"+/\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"], [\"'\", \"'\"], [\"`\", \"`\"]],\n      \"extensions\": [\"sil\"]\n    },\n    \"Slang\": {\n      \"name\": \"Slang\",\n      \"line_comment\": [\"//\"],\n      \"multi_line_comments\": [[\"/*\", \"*/\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"]],\n      \"extensions\": [\"slang\"]\n    },\n    \"Sml\": {\n      \"name\": \"Standard ML (SML)\",\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"]],\n      \"multi_line_comments\": [[\"(*\", \"*)\"]],\n      \"extensions\": [\"sml\"]\n    },\n    \"Smalltalk\": {\n      \"name\": \"Smalltalk\",\n      \"quotes\": [[\"'\", \"'\"]],\n      \"multi_line_comments\": [[\"\\\\\\\"\", \"\\\\\\\"\"]],\n      \"extensions\": [\"cs.st\", \"pck.st\"]\n    },\n    \"Snakemake\": {\n      \"line_comment\": [\"#\"],\n      \"doc_quotes\": [[\"\\\\\\\"\\\\\\\"\\\\\\\"\", \"\\\\\\\"\\\\\\\"\\\\\\\"\"], [\"'''\", \"'''\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"], [\"'\", \"'\"]],\n      \"extensions\": [\"smk\", \"rules\"],\n      \"filenames\": [\"snakefile\"]\n    },\n    \"Solidity\": {\n      \"name\": \"Solidity\",\n      \"line_comment\": [\"//\"],\n      \"multi_line_comments\": [[\"/*\", \"*/\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"]],\n      \"extensions\": [\"sol\"]\n    },\n    \"SpecmanE\": {\n      \"name\": \"Specman e\",\n      \"line_comment\": [\"--\", \"//\"],\n      \"multi_line_comments\": [[\"'>\", \"<'\"]],\n      \"extensions\": [\"e\"]\n    },\n    \"Spice\": {\n      \"name\": \"Spice Netlist\",\n      \"line_comment\": [\"*\"],\n      \"extensions\": [\"ckt\"]\n    },\n    \"Sql\": {\n      \"name\": \"SQL\",\n      \"line_comment\": [\"--\"],\n      \"multi_line_comments\": [[\"/*\", \"*/\"]],\n      \"quotes\": [[\"'\", \"'\"]],\n      \"extensions\": [\"sql\"]\n    },\n    \"Sqf\": {\n      \"name\": \"SQF\",\n      \"line_comment\": [\"//\"],\n      \"multi_line_comments\": [[\"/*\", \"*/\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"], [\"'\", \"'\"]],\n      \"extensions\": [\"sqf\"]\n    },\n    \"SRecode\": {\n      \"name\": \"SRecode Template\",\n      \"line_comment\": [\";;\"],\n      \"extensions\": [\"srt\"]\n    },\n    \"Stan\": {\n      \"line_comment\": [\"//\", \"#\"],\n      \"multi_line_comments\": [[\"/*\", \"*/\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"]],\n      \"extensions\": [\"stan\"]\n    },\n    \"Stata\": {\n      \"line_comment\": [\"//\", \"*\"],\n      \"multi_line_comments\": [[\"/*\", \"*/\"]],\n      \"extensions\": [\"do\"]\n    },\n    \"Stratego\": {\n      \"name\": \"Stratego/XT\",\n      \"line_comment\": [\"//\"],\n      \"multi_line_comments\": [[\"/*\", \"*/\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"], [\"$[\", \"]\"], [\"$<\", \">\"], [\"${\", \"}\"]],\n      \"extensions\": [\"str\"]\n    },\n    \"Stylus\": {\n      \"line_comment\": [\"//\"],\n      \"multi_line_comments\": [[\"/*\", \"*/\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"], [\"'\", \"'\"]],\n      \"extensions\": [\"styl\"]\n    },\n    \"Svelte\": {\n      \"multi_line_comments\": [[\"<!--\", \"-->\"]],\n      \"important_syntax\": [\"<script\", \"<style\"],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"], [\"'\", \"'\"]],\n      \"extensions\": [\"svelte\"]\n    },\n    \"Svg\": {\n      \"name\": \"SVG\",\n      \"multi_line_comments\": [[\"<!--\", \"-->\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"], [\"'\", \"'\"]],\n      \"mime\": [\"image/svg+xml\"],\n      \"extensions\": [\"svg\"]\n    },\n    \"Swift\": {\n      \"line_comment\": [\"//\"],\n      \"multi_line_comments\": [[\"/*\", \"*/\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"]],\n      \"nested\": true,\n      \"extensions\": [\"swift\"]\n    },\n    \"Swig\": {\n      \"name\": \"SWIG\",\n      \"line_comment\": [\"//\"],\n      \"multi_line_comments\": [[\"/*\", \"*/\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"]],\n      \"nested\": true,\n      \"extensions\": [\"swg\", \"i\"]\n    },\n    \"SystemVerilog\": {\n      \"line_comment\": [\"//\"],\n      \"multi_line_comments\": [[\"/*\", \"*/\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"]],\n      \"extensions\": [\"sv\", \"svh\"]\n    },\n    \"Slint\": {\n      \"line_comment\": [\"//\"],\n      \"multi_line_comments\": [[\"/*\", \"*/\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"]],\n      \"extensions\": [\"slint\"]\n    },\n    \"Tact\": {\n      \"line_comment\": [\"//\"],\n      \"multi_line_comments\": [[\"/*\", \"*/\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"]],\n      \"extensions\": [\"tact\"]\n    },\n    \"Tcl\": {\n      \"name\": \"TCL\",\n      \"line_comment\": [\"#\"],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"], [\"'\", \"'\"]],\n      \"extensions\": [\"tcl\"]\n    },\n    \"Tera\": {\n      \"multi_line_comments\": [[\"<!--\", \"-->\"], [\"{#\", \"#}\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"], [\"'\", \"'\"]],\n      \"extensions\": [\"tera\"]\n    },\n    \"Templ\": {\n      \"name\": \"Templ\",\n      \"line_comment\": [\"//\"],\n      \"multi_line_comments\": [[\"<!--\", \"-->\"], [\"/*\", \"*/\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"], [\"'\", \"'\"], [\"`\", \"`\"]],\n      \"important_syntax\": [\"templ\", \"script\", \"css\"],\n      \"extensions\": [\"templ\", \"tmpl\"]\n    },\n    \"Tex\": {\n      \"name\": \"TeX\",\n      \"line_comment\": [\"%\"],\n      \"extensions\": [\"tex\", \"sty\"]\n    },\n    \"Text\": {\n      \"name\": \"Plain Text\",\n      \"literate\": true,\n      \"mime\": [\"text/plain\"],\n      \"extensions\": [\"text\", \"txt\"]\n    },\n    \"Thrift\": {\n      \"line_comment\": [\"#\", \"//\"],\n      \"multi_line_comments\": [[\"/*\", \"*/\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"], [\"'\", \"'\"]],\n      \"extensions\": [\"thrift\"]\n    },\n    \"Toml\": {\n      \"name\": \"TOML\",\n      \"line_comment\": [\"#\"],\n      \"quotes\": [\n        [\"\\\\\\\"\", \"\\\\\\\"\"],\n        [\"'\", \"'\"],\n        [\"\\\\\\\"\\\\\\\"\\\\\\\"\", \"\\\\\\\"\\\\\\\"\\\\\\\"\"],\n        [\"'''\", \"'''\"]\n      ],\n      \"extensions\": [\"toml\"]\n    },\n    \"Tsx\": {\n      \"name\": \"TSX\",\n      \"line_comment\": [\"//\"],\n      \"multi_line_comments\": [[\"/*\", \"*/\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"], [\"'\", \"'\"], [\"`\", \"`\"]],\n      \"extensions\": [\"tsx\"]\n    },\n    \"Ttcn\": {\n      \"name\": \"TTCN-3\",\n      \"line_comment\": [\"//\"],\n      \"multi_line_comments\": [[\"/*\", \"*/\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"]],\n      \"extensions\": [\"ttcn\", \"ttcn3\", \"ttcnpp\"]\n    },\n    \"Twig\": {\n      \"name\": \"Twig\",\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"], [\"'\", \"'\"]],\n      \"extensions\": [\"twig\"],\n      \"multi_line_comments\": [[\"<!--\", \"-->\"], [\"{#\", \"#}\"]]\n    },\n    \"TypeScript\": {\n      \"line_comment\": [\"//\"],\n      \"multi_line_comments\": [[\"/*\", \"*/\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"], [\"'\", \"'\"], [\"`\", \"`\"]],\n      \"extensions\": [\"ts\", \"mts\", \"cts\"]\n    },\n    \"Typst\": {\n      \"nested\": true,\n      \"line_comment\": [\"//\"],\n      \"multi_line_comments\": [[\"/*\", \"*/\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"]],\n      \"extensions\": [\"typ\"]\n    },\n    \"Uiua\": {\n      \"line_comment\": [\"#\"],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"]],\n      \"extensions\": [\"ua\"]\n    },\n    \"UMPL\": {\n      \"line_comment\": [\"!\"],\n      \"quotes\": [[\"`\", \"`\"]],\n      \"extensions\": [\"umpl\"]\n    },\n    \"Unison\": {\n      \"nested\": true,\n      \"line_comment\": [\"--\"],\n      \"multi_line_comments\": [[\"{-\", \"-}\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"]],\n      \"extensions\": [\"u\"]\n    },\n    \"UnrealDeveloperMarkdown\": {\n      \"name\": \"Unreal Markdown\",\n      \"important_syntax\": [\"```\"],\n      \"extensions\": [\"udn\"]\n    },\n    \"UnrealPlugin\": {\n      \"name\": \"Unreal Plugin\",\n      \"blank\": true,\n      \"extensions\": [\"uplugin\"]\n    },\n    \"UnrealProject\": {\n      \"name\": \"Unreal Project\",\n      \"blank\": true,\n      \"extensions\": [\"uproject\"]\n    },\n    \"UnrealScript\": {\n      \"name\": \"Unreal Script\",\n      \"line_comment\": [\"//\"],\n      \"multi_line_comments\": [[\"/*\", \"*/\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"]],\n      \"extensions\": [\"uc\", \"uci\", \"upkg\"]\n    },\n    \"UnrealShader\": {\n      \"name\": \"Unreal Shader\",\n      \"line_comment\": [\"//\"],\n      \"multi_line_comments\": [[\"/*\", \"*/\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"]],\n      \"extensions\": [\"usf\"]\n    },\n    \"UnrealShaderHeader\": {\n      \"name\": \"Unreal Shader Header\",\n      \"line_comment\": [\"//\"],\n      \"multi_line_comments\": [[\"/*\", \"*/\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"]],\n      \"extensions\": [\"ush\"]\n    },\n    \"UrWeb\": {\n      \"name\": \"Ur/Web\",\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"]],\n      \"multi_line_comments\": [[\"(*\", \"*)\"]],\n      \"extensions\": [\"ur\", \"urs\"]\n    },\n    \"UrWebProject\": {\n      \"name\": \"Ur/Web Project\",\n      \"line_comment\": [\"#\"],\n      \"extensions\": [\"urp\"]\n    },\n    \"Vala\": {\n      \"line_comment\": [\"//\"],\n      \"multi_line_comments\": [[\"/*\", \"*/\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"]],\n      \"extensions\": [\"vala\"]\n    },\n    \"VB6\": {\n      \"name\": \"VB6/VBA\",\n      \"line_comment\": [\"'\"],\n      \"extensions\": [\"frm\", \"bas\", \"cls\", \"ctl\", \"dsr\"]\n    },\n    \"VBScript\": {\n      \"name\": \"VBScript\",\n      \"line_comment\": [\"'\", \"REM\"],\n      \"extensions\": [\"vbs\"]\n    },\n    \"Velocity\": {\n      \"name\": \"Apache Velocity\",\n      \"line_comment\": [\"##\"],\n      \"multi_line_comments\": [[\"#*\", \"*#\"]],\n      \"extensions\": [\"vm\"],\n      \"quotes\": [[\"'\", \"'\"], [\"\\\\\\\"\", \"\\\\\\\"\"]]\n    },\n    \"Verilog\": {\n      \"line_comment\": [\"//\"],\n      \"multi_line_comments\": [[\"/*\", \"*/\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"]],\n      \"extensions\": [\"vg\", \"vh\"]\n    },\n    \"VerilogArgsFile\": {\n      \"name\": \"Verilog Args File\",\n      \"extensions\": [\"irunargs\", \"xrunargs\"]\n    },\n    \"Vhdl\": {\n      \"name\": \"VHDL\",\n      \"line_comment\": [\"--\"],\n      \"multi_line_comments\": [[\"/*\", \"*/\"]],\n      \"extensions\": [\"vhd\", \"vhdl\"]\n    },\n    \"Virgil\": {\n      \"line_comment\": [\"//\"],\n      \"multi_line_comments\": [[\"/*\", \"*/\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"]],\n      \"extensions\": [\"v3\"]\n    },\n    \"VisualBasic\": {\n      \"name\": \"Visual Basic\",\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"]],\n      \"line_comment\": [\"'\"],\n      \"extensions\": [\"vb\"]\n    },\n    \"VisualStudioProject\": {\n      \"name\": \"Visual Studio Project\",\n      \"multi_line_comments\": [[\"<!--\", \"-->\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"], [\"'\", \"'\"]],\n      \"extensions\": [\"vcproj\", \"vcxproj\"]\n    },\n    \"VisualStudioSolution\": {\n      \"name\": \"Visual Studio Solution\",\n      \"blank\": true,\n      \"extensions\": [\"sln\"]\n    },\n    \"VimScript\": {\n      \"name\": \"Vim Script\",\n      \"line_comment\": [\"\\\\\\\"\"],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"], [\"'\", \"'\"]],\n      \"extensions\": [\"vim\"]\n    },\n    \"Vue\": {\n      \"name\": \"Vue\",\n      \"line_comment\": [\"//\"],\n      \"multi_line_comments\": [[\"<!--\", \"-->\"], [\"/*\", \"*/\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"], [\"'\", \"'\"], [\"`\", \"`\"]],\n      \"important_syntax\": [\"<script\", \"<style\", \"<template\"],\n      \"extensions\": [\"vue\"]\n    },\n    \"WebAssembly\": {\n      \"line_comment\": [\";;\"],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"], [\"'\", \"'\"]],\n      \"extensions\": [\"wat\", \"wast\"]\n    },\n    \"WenYan\":{\n      \"name\":\"The WenYan Programming Language\",\n      \"multi_line_comments\":[[\"批曰。\",\"。\"],[\"疏曰。\",\"。\"]],\n      \"extensions\":[\"wy\"]\n    },\n    \"WGSL\": {\n      \"name\": \"WebGPU Shader Language\",\n      \"line_comment\": [\"//\"],\n      \"extensions\": [\"wgsl\"]\n    },\n    \"Wolfram\": {\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"]],\n      \"multi_line_comments\": [[\"(*\", \"*)\"]],\n      \"extensions\": [\"nb\", \"wl\"]\n    },\n    \"Xaml\": {\n      \"name\": \"XAML\",\n      \"multi_line_comments\": [[\"<!--\", \"-->\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"], [\"'\", \"'\"]],\n      \"extensions\": [\"xaml\"]\n    },\n    \"XcodeConfig\": {\n      \"name\": \"Xcode Config\",\n      \"line_comment\": [\"//\"],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"], [\"'\", \"'\"]],\n      \"extensions\": [\"xcconfig\"]\n    },\n    \"Xml\": {\n      \"name\": \"XML\",\n      \"multi_line_comments\": [[\"<!--\", \"-->\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"], [\"'\", \"'\"]],\n      \"extensions\": [\"xml\"]\n    },\n    \"XSL\": {\n      \"name\": \"XSL\",\n      \"multi_line_comments\": [[\"<!--\", \"-->\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"], [\"'\", \"'\"]],\n      \"extensions\": [\"xsl\", \"xslt\"]\n    },\n    \"Xtend\": {\n      \"line_comment\": [\"//\"],\n      \"multi_line_comments\": [[\"/*\", \"*/\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"], [\"'\", \"'\"], [\"'''\", \"'''\"]],\n      \"extensions\": [\"xtend\"]\n    },\n    \"Yaml\": {\n      \"name\": \"YAML\",\n      \"line_comment\": [\"#\"],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"], [\"'\", \"'\"]],\n      \"extensions\": [\"yaml\", \"yml\"]\n    },\n    \"ZenCode\": {\n      \"line_comment\": [\"//\", \"#\"],\n      \"multi_line_comments\": [[\"/*\", \"*/\"]],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"], [\"'\", \"'\"]],\n      \"verbatim_quotes\": [[\"@\\\\\\\"\", \"\\\\\\\"\"], [\"@'\", \"'\"]],\n      \"extensions\": [\"zs\"]\n    },\n    \"Zig\": {\n      \"line_comment\": [\"//\"],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"]],\n      \"extensions\": [\"zig\"]\n    },\n    \"Zokrates\": {\n      \"name\": \"ZoKrates\",\n      \"line_comment\": [\"//\"],\n      \"multi_line_comments\": [[\"/*\", \"*/\"]],\n      \"extensions\": [\"zok\"]\n    },\n    \"Zsh\": {\n      \"shebangs\": [\"#!/bin/zsh\"],\n      \"line_comment\": [\"#\"],\n      \"quotes\": [[\"\\\\\\\"\", \"\\\\\\\"\"], [\"'\", \"'\"]],\n      \"extensions\": [\"zsh\"]\n    },\n    \"GdShader\": {\n      \"name\": \"GDShader\",\n      \"line_comment\": [\"//\"],\n      \"multi_line_comments\": [[\"/*\", \"*/\"]],\n      \"extensions\": [\"gdshader\"]\n    }\n  }\n}\n"
  },
  {
    "path": "src/cli.rs",
    "content": "use std::{process, str::FromStr};\n\nuse clap::{crate_description, value_parser, Arg, ArgAction, ArgMatches};\nuse colored::Colorize;\nuse tokei::{Config, LanguageType, Sort};\n\nuse crate::{\n    cli_utils::{crate_version, parse_or_exit, NumberFormatStyle},\n    consts::{\n        BLANKS_COLUMN_WIDTH, CODE_COLUMN_WIDTH, COMMENTS_COLUMN_WIDTH, LANGUAGE_COLUMN_WIDTH,\n        LINES_COLUMN_WIDTH, PATH_COLUMN_WIDTH,\n    },\n    input::Format,\n};\n\n/// Used for sorting languages.\n#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]\npub enum Streaming {\n    /// simple lines.\n    Simple,\n    /// Json outputs.\n    Json,\n}\n\nimpl std::str::FromStr for Streaming {\n    type Err = String;\n\n    fn from_str(s: &str) -> Result<Self, Self::Err> {\n        Ok(match s.to_lowercase().as_ref() {\n            \"simple\" => Streaming::Simple,\n            \"json\" => Streaming::Json,\n            s => return Err(format!(\"Unsupported streaming option: {}\", s)),\n        })\n    }\n}\n\n#[derive(Debug)]\npub struct Cli {\n    matches: ArgMatches,\n    pub columns: Option<usize>,\n    pub files: bool,\n    pub hidden: bool,\n    pub no_ignore: bool,\n    pub no_ignore_parent: bool,\n    pub no_ignore_dot: bool,\n    pub no_ignore_vcs: bool,\n    pub output: Option<Format>,\n    pub streaming: Option<Streaming>,\n    pub print_languages: bool,\n    pub sort: Option<Sort>,\n    pub sort_reverse: bool,\n    pub types: Option<Vec<LanguageType>>,\n    pub compact: bool,\n    pub number_format: num_format::CustomFormat,\n}\n\nimpl Cli {\n    pub fn from_args() -> Self {\n        let matches = clap::Command::new(\"tokei\")\n            .version(crate_version())\n            .author(\"Erin P. <xampprocky@gmail.com> + Contributors\")\n            .styles(clap_cargo::style::CLAP_STYLING)\n            .about(concat!(\n                crate_description!(),\n                \"\\n\",\n                \"Support this project on GitHub Sponsors: https://github.com/sponsors/XAMPPRocky\"\n            ))\n            .arg(\n                Arg::new(\"columns\")\n                    .long(\"columns\")\n                    .short('c')\n                    .value_parser(value_parser!(usize))\n                    .conflicts_with(\"output\")\n                    .help(\n                        \"Sets a strict column width of the output, only available for \\\n                        terminal output.\",\n                    ),\n            )\n            .arg(\n                Arg::new(\"exclude\")\n                    .long(\"exclude\")\n                    .short('e')\n                    .action(ArgAction::Append)\n                    .help(\"Ignore all files & directories matching the pattern.\"),\n            )\n            .arg(\n                Arg::new(\"files\")\n                    .long(\"files\")\n                    .short('f')\n                    .action(ArgAction::SetTrue)\n                    .help(\"Will print out statistics on individual files.\"),\n            )\n            .arg(\n                Arg::new(\"file_input\")\n                    .long(\"input\")\n                    .short('i')\n                    .help(\n                        \"Gives statistics from a previous tokei run. Can be given a file path, \\\n                        or \\\"stdin\\\" to read from stdin.\",\n                    ),\n            )\n            .arg(\n                Arg::new(\"hidden\")\n                    .long(\"hidden\")\n                    .action(ArgAction::SetTrue)\n                    .help(\"Count hidden files.\"),\n            )\n            .arg(\n                Arg::new(\"input\")\n                    .num_args(1..)\n                    .conflicts_with(\"languages\")\n                    .help(\"The path(s) to the file or directory to be counted. (default current directory)\"),\n            )\n            .arg(\n                Arg::new(\"languages\")\n                    .long(\"languages\")\n                    .short('l')\n                    .action(ArgAction::SetTrue)\n                    .conflicts_with(\"input\")\n                    .help(\"Prints out supported languages and their extensions.\"),\n            )\n            .arg(Arg::new(\"no_ignore\")\n                .long(\"no-ignore\")\n                .action(ArgAction::SetTrue)\n                .help(\n                    \"\\\n                        Don't respect ignore files (.gitignore, .ignore, etc.). This implies \\\n                        --no-ignore-parent, --no-ignore-dot, and --no-ignore-vcs.\\\n                    \",\n                ))\n            .arg(Arg::new(\"no_ignore_parent\")\n                .long(\"no-ignore-parent\")\n                .action(ArgAction::SetTrue)\n                .help(\n                    \"\\\n                        Don't respect ignore files (.gitignore, .ignore, etc.) in parent \\\n                        directories.\\\n                    \",\n                ))\n            .arg(Arg::new(\"no_ignore_dot\")\n                .long(\"no-ignore-dot\")\n                .action(ArgAction::SetTrue)\n                .help(\n                    \"\\\n                        Don't respect .ignore and .tokeignore files, including those in \\\n                        parent directories.\\\n                    \",\n                ))\n            .arg(Arg::new(\"no_ignore_vcs\")\n                .long(\"no-ignore-vcs\")\n                .action(ArgAction::SetTrue)\n                .help(\n                    \"\\\n                        Don't respect VCS ignore files (.gitignore, .hgignore, etc.) including \\\n                        those in parent directories.\\\n                    \",\n                ))\n            .arg(\n                Arg::new(\"output\")\n                    .long(\"output\")\n                    .short('o')\n                    .value_parser(Format::from_str)\n                    .help(\n                        \"Outputs Tokei in a specific format. Compile with additional features for \\\n                        more format support.\",\n                    ),\n            )\n            .arg(\n                Arg::new(\"streaming\")\n                    .long(\"streaming\")\n                    .value_parser([\"simple\", \"json\"])\n                    .ignore_case(true)\n                    .help(\n                        \"prints the (language, path, lines, blanks, code, comments) records as \\\n                        simple lines or as Json for batch processing\",\n                    ),\n            )\n            .arg(\n                Arg::new(\"sort\")\n                    .long(\"sort\")\n                    .short('s')\n                    .value_parser([\"files\", \"lines\", \"blanks\", \"code\", \"comments\"])\n                    .ignore_case(true)\n                    .conflicts_with(\"rsort\")\n                    .help(\"Sort languages based on column\"),\n            )\n            .arg(\n                Arg::new(\"rsort\")\n                    .long(\"rsort\")\n                    .short('r')\n                    .value_parser([\"files\", \"lines\", \"blanks\", \"code\", \"comments\"])\n                    .ignore_case(true)\n                    .conflicts_with(\"sort\")\n                    .help(\"Reverse sort languages based on column\"),\n            )\n            .arg(\n                Arg::new(\"types\")\n                    .long(\"types\")\n                    .short('t')\n                    .action(ArgAction::Append)\n                    .help(\n                        \"Filters output by language type, separated by a comma. i.e. \\\n                        -t=Rust,Markdown\",\n                    ),\n            )\n            .arg(\n                Arg::new(\"compact\")\n                    .long(\"compact\")\n                    .short('C')\n                    .action(ArgAction::SetTrue)\n                    .help(\"Do not print statistics about embedded languages.\"),\n            )\n            .arg(\n                Arg::new(\"num_format_style\")\n                    .long(\"num-format\")\n                    .short('n')\n                    .value_parser([\"commas\", \"dots\", \"plain\", \"underscores\"])\n                    .conflicts_with(\"output\")\n                    .help(\n                        \"Format of printed numbers, i.e., plain (1234, default), \\\n                        commas (1,234), dots (1.234), or underscores (1_234). Cannot be \\\n                        used with --output.\",\n                    ),\n            )\n            .arg(\n                Arg::new(\"verbose\")\n                    .long(\"verbose\")\n                    .short('v')\n                    .action(ArgAction::Count)\n                    .help(\n                        \"Set log output level:\n                        1: to show unknown file extensions,\n                        2: reserved for future debugging,\n                        3: enable file level trace. Not recommended on multiple files\",\n                    ),\n            )\n            .get_matches();\n\n        let columns = matches.get_one::<usize>(\"columns\").cloned();\n        let files = matches.get_flag(\"files\");\n        let hidden = matches.get_flag(\"hidden\");\n        let no_ignore = matches.get_flag(\"no_ignore\");\n        let no_ignore_parent = matches.get_flag(\"no_ignore_parent\");\n        let no_ignore_dot = matches.get_flag(\"no_ignore_dot\");\n        let no_ignore_vcs = matches.get_flag(\"no_ignore_vcs\");\n        let print_languages = matches.get_flag(\"languages\");\n        let verbose = matches.get_count(\"verbose\") as u64;\n        let compact = matches.get_flag(\"compact\");\n        let types = matches.get_many(\"types\").map(|e| {\n            e.flat_map(|x: &String| {\n                x.split(',')\n                    .map(str::parse::<LanguageType>)\n                    .filter_map(Result::ok)\n                    .collect::<Vec<_>>()\n            })\n            .collect()\n        });\n\n        let num_format_style = matches\n            .get_one::<String>(\"num_format_style\")\n            .map(parse_or_exit::<NumberFormatStyle>)\n            .unwrap_or_default();\n\n        let number_format = match num_format_style.get_format() {\n            Ok(format) => format,\n            Err(e) => {\n                eprintln!(\"Error:\\n{}\", e);\n                process::exit(1);\n            }\n        };\n\n        let sort = matches.get_one::<String>(\"sort\");\n        let rsort = matches.get_one::<String>(\"rsort\");\n        let sort = sort.or(rsort).map(parse_or_exit);\n        let sort_reverse = rsort.is_some();\n\n        // Format category is overly accepting by clap (so the user knows what\n        // is supported) but this will fail if support is not compiled in and\n        // give a useful error to the user.\n        let output = matches.get_one(\"output\").cloned();\n        let streaming = matches.get_one::<String>(\"streaming\").map(parse_or_exit);\n\n        crate::cli_utils::setup_logger(verbose);\n\n        let cli = Cli {\n            matches,\n            columns,\n            files,\n            hidden,\n            no_ignore,\n            no_ignore_parent,\n            no_ignore_dot,\n            no_ignore_vcs,\n            output,\n            streaming,\n            print_languages,\n            sort,\n            sort_reverse,\n            types,\n            compact,\n            number_format,\n        };\n\n        debug!(\"CLI Config: {:#?}\", cli);\n\n        cli\n    }\n\n    pub fn file_input(&self) -> Option<&String> {\n        self.matches.get_one(\"file_input\")\n    }\n\n    pub fn ignored_directories(&self) -> Vec<&str> {\n        let mut ignored_directories: Vec<&str> = Vec::new();\n        if let Some(user_ignored) = self.matches.get_many::<String>(\"exclude\") {\n            ignored_directories.extend(user_ignored.map(|x| x.as_str()));\n        }\n        ignored_directories\n    }\n\n    pub fn input(&self) -> Vec<&str> {\n        match self.matches.get_many::<String>(\"input\") {\n            Some(vs) => vs.map(|x| x.as_str()).collect(),\n            None => vec![\".\"],\n        }\n    }\n\n    pub fn print_supported_languages() -> Result<(), Box<dyn std::error::Error>> {\n        use table_formatter::table::*;\n        use table_formatter::{cell, table};\n        let term_width = term_size::dimensions().map(|(w, _)| w).unwrap_or(75) - 8;\n        let (lang_w, suffix_w) = if term_width <= 80 {\n            (term_width / 2, term_width / 2)\n        } else {\n            (40, term_width - 40)\n        };\n\n        let header = vec![\n            cell!(\n                \"Language\",\n                align = Align::Left,\n                padding = Padding::NONE,\n                width = Some(lang_w)\n            )\n            .with_formatter(vec![table_formatter::table::FormatterFunc::Normal(\n                Colorize::bold,\n            )]),\n            cell!(\n                \"Extensions\",\n                align = Align::Left,\n                padding = Padding::new(3, 0),\n                width = Some(suffix_w)\n            )\n            .with_formatter(vec![table_formatter::table::FormatterFunc::Normal(\n                Colorize::bold,\n            )]),\n        ];\n        let content = LanguageType::list()\n            .iter()\n            .map(|(key, ext)| {\n                vec![\n                    // table::TableCell::new(table::Cell::TextCell(key.name().to_string()))\n                    //     .with_width(lang_w),\n                    cell!(key.name()).with_width(Some(lang_w)),\n                    cell!(\n                        if matches!(key, LanguageType::Emojicode) {\n                            ext.join(\", \") + \"\\u{200b}\"\n                        } else if ext.is_empty() {\n                            \"<None>\".to_string()\n                        } else {\n                            ext.join(\", \")\n                        },\n                        align = Align::Left,\n                        padding = Padding::new(3, 0),\n                        width = Some(suffix_w)\n                    ),\n                ]\n            })\n            .collect();\n        let t = table!(header - content with Border::ALL);\n\n        let mut render_result = Vec::new();\n        t.render(&mut render_result)?;\n        println!(\"{}\", String::from_utf8(render_result)?);\n        Ok(())\n    }\n\n    /// Overrides the shared options (See `tokei::Config` for option\n    /// descriptions) between the CLI and the config files. CLI flags have\n    /// higher precedence than options present in config files.\n    ///\n    /// #### Shared options\n    /// * `hidden`\n    /// * `no_ignore`\n    /// * `no_ignore_parent`\n    /// * `no_ignore_dot`\n    /// * `no_ignore_vcs`\n    /// * `types`\n    pub fn override_config(&mut self, mut config: Config) -> Config {\n        config.hidden = if self.hidden {\n            Some(true)\n        } else {\n            config.hidden\n        };\n\n        config.no_ignore = if self.no_ignore {\n            Some(true)\n        } else {\n            config.no_ignore\n        };\n\n        config.no_ignore_parent = if self.no_ignore_parent {\n            Some(true)\n        } else {\n            config.no_ignore_parent\n        };\n\n        config.no_ignore_dot = if self.no_ignore_dot {\n            Some(true)\n        } else {\n            config.no_ignore_dot\n        };\n\n        config.no_ignore_vcs = if self.no_ignore_vcs {\n            Some(true)\n        } else {\n            config.no_ignore_vcs\n        };\n\n        config.for_each_fn = match self.streaming {\n            Some(Streaming::Json) => Some(|l: LanguageType, e| {\n                println!(\"{}\", serde_json::json!({\"language\": l.name(), \"stats\": e}));\n            }),\n            Some(Streaming::Simple) => Some(|l: LanguageType, e| {\n                println!(\n                    \"{:>LANGUAGE_COLUMN_WIDTH$} {:<PATH_COLUMN_WIDTH$} {:>LINES_COLUMN_WIDTH$} {:>CODE_COLUMN_WIDTH$} {:>COMMENTS_COLUMN_WIDTH$} {:>BLANKS_COLUMN_WIDTH$}\",\n                    l.name(),\n                    e.name.to_string_lossy().to_string(),\n                    e.stats.lines(),\n                    e.stats.code,\n                    e.stats.comments,\n                    e.stats.blanks\n                );\n            }),\n            _ => None,\n        };\n\n        config.types = self.types.take().or(config.types);\n\n        config\n    }\n\n    pub fn print_input_parse_failure(input_filename: &str) {\n        eprintln!(\"Error:\\n Failed to parse input file: {}\", input_filename);\n\n        let not_supported = Format::not_supported();\n        if !not_supported.is_empty() {\n            eprintln!(\n                \"\nThis version of tokei was compiled without serialization support for the following formats:\n\n    {not_supported}\n\nYou may want to install any comma separated combination of {all:?}:\n\n    cargo install tokei --features {all:?}\n\nOr use the 'all' feature:\n\n    cargo install tokei --features all\n    \\n\",\n                not_supported = not_supported.join(\", \"),\n                // no space after comma to ease copypaste\n                all = self::Format::all_feature_names().join(\",\")\n            );\n        }\n    }\n}\n"
  },
  {
    "path": "src/cli_utils.rs",
    "content": "use std::{\n    borrow::Cow,\n    fmt,\n    io::{self, Write},\n    process,\n    str::FromStr,\n};\n\nuse clap::crate_version;\nuse colored::Colorize;\nuse num_format::ToFormattedString;\n\nuse crate::input::Format;\nuse tokei::{find_char_boundary, CodeStats, Language, LanguageType, Report};\n\nuse crate::consts::{\n    BLANKS_COLUMN_WIDTH, CODE_COLUMN_WIDTH, COMMENTS_COLUMN_WIDTH, FILES_COLUMN_WIDTH,\n    LINES_COLUMN_WIDTH,\n};\n\nconst NO_LANG_HEADER_ROW_LEN: usize = 69;\nconst NO_LANG_ROW_LEN: usize = 63;\nconst NO_LANG_ROW_LEN_NO_SPACES: usize = 56;\nconst IDENT_INACCURATE: &str = \"(!)\";\n\npub fn crate_version() -> String {\n    if Format::supported().is_empty() {\n        format!(\n            \"{} compiled without serialization formats.\",\n            crate_version!()\n        )\n    } else {\n        format!(\n            \"{} compiled with serialization support: {}\",\n            crate_version!(),\n            Format::supported().join(\", \")\n        )\n    }\n}\n\npub fn setup_logger(verbose_option: u64) {\n    use log::LevelFilter;\n\n    let mut builder = env_logger::Builder::new();\n\n    let filter_level = match verbose_option {\n        1 => LevelFilter::Warn,\n        2 => LevelFilter::Debug,\n        3 => LevelFilter::Trace,\n        _ => LevelFilter::Error,\n    };\n\n    builder.filter(None, filter_level);\n    builder.init();\n}\n\npub fn parse_or_exit<T>(s: impl AsRef<str>) -> T\nwhere\n    T: FromStr,\n    T::Err: fmt::Display,\n{\n    T::from_str(s.as_ref()).unwrap_or_else(|e| {\n        eprintln!(\"Error:\\n{}\", e);\n        process::exit(1);\n    })\n}\n\n#[non_exhaustive]\n#[derive(Debug, Copy, Clone)]\npub enum NumberFormatStyle {\n    // 1234 (Default)\n    Plain,\n    // 1,234\n    Commas,\n    // 1.234\n    Dots,\n    // 1_234\n    Underscores,\n}\n\nimpl Default for NumberFormatStyle {\n    fn default() -> Self {\n        Self::Plain\n    }\n}\n\nimpl FromStr for NumberFormatStyle {\n    type Err = String;\n\n    fn from_str(s: &str) -> Result<Self, Self::Err> {\n        match s {\n            \"plain\" => Ok(Self::Plain),\n            \"commas\" => Ok(Self::Commas),\n            \"dots\" => Ok(Self::Dots),\n            \"underscores\" => Ok(Self::Underscores),\n            _ => Err(format!(\n                \"Expected 'plain', 'commas', 'underscores', or 'dots' for num-format, but got '{}'\",\n                s,\n            )),\n        }\n    }\n}\n\nimpl NumberFormatStyle {\n    fn separator(self) -> &'static str {\n        match self {\n            Self::Plain => \"\",\n            Self::Commas => \",\",\n            Self::Dots => \".\",\n            Self::Underscores => \"_\",\n        }\n    }\n\n    pub fn get_format(self) -> Result<num_format::CustomFormat, num_format::Error> {\n        num_format::CustomFormat::builder()\n            .grouping(num_format::Grouping::Standard)\n            .separator(self.separator())\n            .build()\n    }\n}\n\npub struct Printer<W> {\n    writer: W,\n    columns: usize,\n    path_length: usize,\n    row: String,\n    subrow: String,\n    list_files: bool,\n    number_format: num_format::CustomFormat,\n}\n\nimpl<W> Printer<W> {\n    pub fn new(\n        columns: usize,\n        list_files: bool,\n        writer: W,\n        number_format: num_format::CustomFormat,\n    ) -> Self {\n        Self {\n            columns,\n            list_files,\n            path_length: columns - NO_LANG_ROW_LEN_NO_SPACES,\n            writer,\n            row: \"━\".repeat(columns),\n            subrow: \"─\".repeat(columns),\n            number_format,\n        }\n    }\n}\n\nimpl<W: Write> Printer<W> {\n    pub fn print_header(&mut self) -> io::Result<()> {\n        self.print_row()?;\n\n        let files_column_width: usize = FILES_COLUMN_WIDTH + 6;\n        writeln!(\n            self.writer,\n            \" {:<6$} {:>files_column_width$} {:>LINES_COLUMN_WIDTH$} {:>CODE_COLUMN_WIDTH$} {:>COMMENTS_COLUMN_WIDTH$} {:>BLANKS_COLUMN_WIDTH$}\",\n            \"Language\".bold().blue(),\n            \"Files\".bold().blue(),\n            \"Lines\".bold().blue(),\n            \"Code\".bold().blue(),\n            \"Comments\".bold().blue(),\n            \"Blanks\".bold().blue(),\n            self.columns - NO_LANG_HEADER_ROW_LEN\n        )?;\n        self.print_row()\n    }\n\n    pub fn print_inaccuracy_warning(&mut self) -> io::Result<()> {\n        writeln!(\n            self.writer,\n            \"Note: results can be inaccurate for languages marked with '{}'\",\n            IDENT_INACCURATE\n        )\n    }\n\n    pub fn print_language(&mut self, language: &Language, name: &str) -> io::Result<()>\n    where\n        W: Write,\n    {\n        self.print_language_name(language.inaccurate, name, None)?;\n        write!(self.writer, \" \")?;\n        writeln!(\n            self.writer,\n            \"{:>FILES_COLUMN_WIDTH$} {:>LINES_COLUMN_WIDTH$} {:>CODE_COLUMN_WIDTH$} {:>COMMENTS_COLUMN_WIDTH$} {:>BLANKS_COLUMN_WIDTH$}\",\n            language\n                .reports\n                .len()\n                .to_formatted_string(&self.number_format),\n            language.lines().to_formatted_string(&self.number_format),\n            language.code.to_formatted_string(&self.number_format),\n            language.comments.to_formatted_string(&self.number_format),\n            language.blanks.to_formatted_string(&self.number_format),\n        )\n    }\n\n    fn print_language_in_print_total(&mut self, language: &Language) -> io::Result<()>\n    where\n        W: Write,\n    {\n        self.print_language_name(language.inaccurate, \"Total\", None)?;\n        write!(self.writer, \" \")?;\n        writeln!(\n            self.writer,\n            \"{:>FILES_COLUMN_WIDTH$} {:>LINES_COLUMN_WIDTH$} {:>CODE_COLUMN_WIDTH$} {:>COMMENTS_COLUMN_WIDTH$} {:>BLANKS_COLUMN_WIDTH$}\",\n            language\n                .children\n                .values()\n                .map(Vec::len)\n                .sum::<usize>()\n                .to_formatted_string(&self.number_format)\n                .blue(),\n            language\n                .lines()\n                .to_formatted_string(&self.number_format)\n                .blue(),\n            language\n                .code\n                .to_formatted_string(&self.number_format)\n                .blue(),\n            language\n                .comments\n                .to_formatted_string(&self.number_format)\n                .blue(),\n            language\n                .blanks\n                .to_formatted_string(&self.number_format)\n                .blue(),\n        )\n    }\n\n    pub fn print_language_name(\n        &mut self,\n        inaccurate: bool,\n        name: &str,\n        prefix: Option<&str>,\n    ) -> io::Result<()> {\n        let mut lang_section_len = self.columns - NO_LANG_ROW_LEN - prefix.map_or(0, str::len);\n        if inaccurate {\n            lang_section_len -= IDENT_INACCURATE.len();\n        }\n\n        if let Some(prefix) = prefix {\n            write!(self.writer, \"{}\", prefix)?;\n        }\n        // truncate and replace the last char with a `|` if the name is too long\n        if lang_section_len < name.len() {\n            write!(self.writer, \" {:.len$}\", name, len = lang_section_len - 1)?;\n            write!(self.writer, \"|\")?;\n        } else {\n            write!(\n                self.writer,\n                \" {:<len$}\",\n                name.bold().magenta(),\n                len = lang_section_len\n            )?;\n        }\n        if inaccurate {\n            write!(self.writer, \"{}\", IDENT_INACCURATE)?;\n        };\n\n        Ok(())\n    }\n\n    fn print_code_stats(\n        &mut self,\n        language_type: LanguageType,\n        stats: &[CodeStats],\n    ) -> io::Result<()> {\n        self.print_language_name(false, &language_type.to_string(), Some(\" |-\"))?;\n        let mut code = 0;\n        let mut comments = 0;\n        let mut blanks = 0;\n\n        for stats in stats.iter().map(tokei::CodeStats::summarise) {\n            code += stats.code;\n            comments += stats.comments;\n            blanks += stats.blanks;\n        }\n\n        if stats.is_empty() {\n            Ok(())\n        } else {\n            writeln!(\n                self.writer,\n                \" {:>FILES_COLUMN_WIDTH$} {:>LINES_COLUMN_WIDTH$} {:>CODE_COLUMN_WIDTH$} {:>COMMENTS_COLUMN_WIDTH$} {:>BLANKS_COLUMN_WIDTH$}\",\n                stats.len().to_formatted_string(&self.number_format),\n                (code + comments + blanks).to_formatted_string(&self.number_format),\n                code.to_formatted_string(&self.number_format),\n                comments.to_formatted_string(&self.number_format),\n                blanks.to_formatted_string(&self.number_format),\n            )\n        }\n    }\n\n    fn print_language_total(&mut self, parent: &Language) -> io::Result<()> {\n        for (language, reports) in &parent.children {\n            self.print_code_stats(\n                *language,\n                &reports\n                    .iter()\n                    .map(|r| r.stats.summarise())\n                    .collect::<Vec<_>>(),\n            )?;\n        }\n        let mut subtotal = tokei::Report::new(\"(Total)\".into());\n        let summary = parent.summarise();\n        subtotal.stats.code += summary.code;\n        subtotal.stats.comments += summary.comments;\n        subtotal.stats.blanks += summary.blanks;\n        self.print_report_with_name(&subtotal)?;\n\n        Ok(())\n    }\n\n    pub fn print_results<'a, I>(\n        &mut self,\n        languages: I,\n        compact: bool,\n        is_sorted: bool,\n    ) -> io::Result<()>\n    where\n        I: Iterator<Item = (&'a LanguageType, &'a Language)>,\n    {\n        let (a, b): (Vec<_>, Vec<_>) = languages\n            .filter(|(_, v)| !v.is_empty())\n            .partition(|(_, l)| compact || l.children.is_empty());\n        let mut first = true;\n\n        for languages in &[&a, &b] {\n            for &(name, language) in *languages {\n                let has_children = !(compact || language.children.is_empty());\n                if first {\n                    first = false;\n                } else if has_children || self.list_files {\n                    self.print_subrow()?;\n                }\n\n                self.print_language(language, name.name())?;\n                if has_children {\n                    self.print_language_total(language)?;\n                }\n\n                if self.list_files {\n                    self.print_subrow()?;\n                    let mut reports: Vec<&Report> = language.reports.iter().collect();\n                    if !is_sorted {\n                        reports.sort_by(|&a, &b| a.name.cmp(&b.name));\n                    }\n                    if compact {\n                        for &report in &reports {\n                            writeln!(self.writer, \"{:1$}\", report, self.path_length)?;\n                        }\n                    } else {\n                        let (a, b): (Vec<&Report>, Vec<&Report>) =\n                            reports.iter().partition(|&r| r.stats.blobs.is_empty());\n                        for reports in &[&a, &b] {\n                            let mut first = true;\n                            for report in reports.iter() {\n                                if report.stats.blobs.is_empty() {\n                                    writeln!(self.writer, \"{:1$}\", report, self.path_length)?;\n                                } else {\n                                    if first && a.is_empty() {\n                                        writeln!(self.writer, \" {}\", report.name.display())?;\n                                        first = false;\n                                    } else {\n                                        writeln!(\n                                            self.writer,\n                                            \"-- {} {}\",\n                                            report.name.display(),\n                                            \"-\".repeat(\n                                                self.columns\n                                                    - 4\n                                                    - report.name.display().to_string().len()\n                                            )\n                                        )?;\n                                    }\n                                    let mut new_report = (*report).clone();\n                                    new_report.name = name.to_string().into();\n                                    writeln!(\n                                        self.writer,\n                                        \" |-{:1$}\",\n                                        new_report,\n                                        self.path_length - 3\n                                    )?;\n                                    self.print_report_total(report, language.inaccurate)?;\n                                }\n                            }\n                        }\n                    }\n                }\n            }\n        }\n\n        Ok(())\n    }\n\n    fn print_row(&mut self) -> io::Result<()> {\n        writeln!(self.writer, \"{}\", self.row)\n    }\n\n    fn print_subrow(&mut self) -> io::Result<()> {\n        writeln!(self.writer, \"{}\", self.subrow.dimmed())\n    }\n\n    fn print_report(\n        &mut self,\n        language_type: LanguageType,\n        stats: &CodeStats,\n        inaccurate: bool,\n    ) -> io::Result<()> {\n        self.print_language_name(inaccurate, &language_type.to_string(), Some(\" |-\"))?;\n\n        writeln!(\n            self.writer,\n            \" {:>FILES_COLUMN_WIDTH$} {:>LINES_COLUMN_WIDTH$} {:>CODE_COLUMN_WIDTH$} {:>COMMENTS_COLUMN_WIDTH$} {:>BLANKS_COLUMN_WIDTH$}\",\n            \" \",\n            stats.lines().to_formatted_string(&self.number_format),\n            stats.code.to_formatted_string(&self.number_format),\n            stats.comments.to_formatted_string(&self.number_format),\n            stats.blanks.to_formatted_string(&self.number_format),\n        )\n    }\n\n    fn print_report_total(&mut self, report: &Report, inaccurate: bool) -> io::Result<()> {\n        if report.stats.blobs.is_empty() {\n            return Ok(());\n        }\n\n        let mut subtotal = tokei::Report::new(\"|- (Total)\".into());\n        subtotal.stats.code += report.stats.code;\n        subtotal.stats.comments += report.stats.comments;\n        subtotal.stats.blanks += report.stats.blanks;\n\n        for (language_type, stats) in &report.stats.blobs {\n            self.print_report(*language_type, stats, inaccurate)?;\n            subtotal.stats += stats.summarise();\n        }\n\n        self.print_report_with_name(report)?;\n\n        Ok(())\n    }\n\n    fn print_report_with_name(&mut self, report: &Report) -> io::Result<()> {\n        let name = report.name.to_string_lossy();\n        let name_length = name.len();\n\n        if name_length > self.path_length {\n            let mut formatted = String::from(\"|\");\n            // Add 1 to the index to account for the '|' we add to the output string\n            let from = find_char_boundary(&name, name_length + 1 - self.path_length);\n            formatted.push_str(&name[from..]);\n        }\n        self.print_report_total_formatted(name, self.path_length, report)?;\n\n        Ok(())\n    }\n\n    fn print_report_total_formatted(\n        &mut self,\n        name: Cow<'_, str>,\n        max_len: usize,\n        report: &Report,\n    ) -> io::Result<()> {\n        let lines_column_width: usize = FILES_COLUMN_WIDTH + 6;\n        writeln!(\n            self.writer,\n            \" {: <max$} {:>lines_column_width$} {:>CODE_COLUMN_WIDTH$} {:>COMMENTS_COLUMN_WIDTH$} {:>BLANKS_COLUMN_WIDTH$}\",\n            name,\n            report\n                .stats\n                .lines()\n                .to_formatted_string(&self.number_format),\n            report.stats.code.to_formatted_string(&self.number_format),\n            report\n                .stats\n                .comments\n                .to_formatted_string(&self.number_format),\n            report.stats.blanks.to_formatted_string(&self.number_format),\n            max = max_len\n        )\n    }\n\n    pub fn print_total(&mut self, languages: &tokei::Languages) -> io::Result<()> {\n        let total = languages.total();\n        self.print_row()?;\n        self.print_language_in_print_total(&total)?;\n        self.print_row()\n    }\n}\n"
  },
  {
    "path": "src/config.rs",
    "content": "use std::{env, fs, path::PathBuf};\n\nuse etcetera::BaseStrategy;\n\nuse crate::language::LanguageType;\nuse crate::sort::Sort;\nuse crate::stats::Report;\n\n/// A configuration struct for how [`Languages::get_statistics`] searches and\n/// counts languages.\n///\n/// ```\n/// use tokei::Config;\n///\n/// let config = Config {\n///     treat_doc_strings_as_comments: Some(true),\n///     ..Config::default()\n/// };\n/// ```\n///\n/// [`Languages::get_statistics`]: struct.Languages.html#method.get_statistics\n#[derive(Debug, Default, Deserialize)]\npub struct Config {\n    /// Width of columns to be printed to the terminal. _This option is ignored\n    /// in the library._ *Default:* Auto detected width of the terminal.\n    pub columns: Option<usize>,\n    /// Count hidden files and directories. *Default:* `false`.\n    pub hidden: Option<bool>,\n    /// Don't respect ignore files (.gitignore, .ignore, etc.). This implies --no-ignore-parent,\n    /// --no-ignore-dot, and --no-ignore-vcs. *Default:* `false`.\n    pub no_ignore: Option<bool>,\n    /// Don't respect ignore files (.gitignore, .ignore, etc.) in parent directories.\n    /// *Default:* `false`.\n    pub no_ignore_parent: Option<bool>,\n    /// Don't respect .ignore and .tokeignore files, including those in parent directories.\n    /// *Default:* `false`.\n    pub no_ignore_dot: Option<bool>,\n    /// Don't respect VCS ignore files (.gitignore, .hgignore, etc.), including those in\n    /// parent directories. *Default:* `false`.\n    pub no_ignore_vcs: Option<bool>,\n    /// Whether to treat doc strings in languages as comments.  *Default:*\n    /// `false`.\n    pub treat_doc_strings_as_comments: Option<bool>,\n    /// Sort languages. *Default:* `None`.\n    pub sort: Option<Sort>,\n    /// Filters languages searched to just those provided. E.g. A directory\n    /// containing `C`, `Cpp`, and `Rust` with a `Config.types` of `[Cpp, Rust]`\n    /// will count only `Cpp` and `Rust`. *Default:* `None`.\n    pub types: Option<Vec<LanguageType>>,\n    // /// A map of individual language configuration.\n    // pub languages: Option<HashMap<LanguageType, LanguageConfig>>,\n    /// Whether to output only the paths for downstream batch processing\n    /// *Default:* false\n    #[serde(skip)]\n    /// Adds a closure for each function, e.g., print the result\n    pub for_each_fn: Option<fn(LanguageType, Report)>,\n}\n\nimpl Config {\n    /// Constructs a new `Config` from either `$base/tokei.toml` or\n    /// `$base/.tokeirc`. `tokei.toml` takes precedence over `.tokeirc`\n    /// as the latter is a hidden file on Unix and not an idiomatic\n    /// filename on Windows.\n    fn get_config(base: PathBuf) -> Option<Self> {\n        fs::read_to_string(base.join(\"tokei.toml\"))\n            .ok()\n            .or_else(|| fs::read_to_string(base.join(\".tokeirc\")).ok())\n            .and_then(|s| toml::from_str(&s).ok())\n    }\n\n    /// Creates a `Config` from three configuration files if they are available.\n    /// Files can have two different names `tokei.toml` and `.tokeirc`.\n    /// Firstly it will attempt to find a config in the configuration directory\n    /// (see below), secondly from the home directory, `$HOME/`,\n    /// and thirdly from the current directory, `./`.\n    /// The current directory's configuration will take priority over the configuration\n    /// directory.\n    ///\n    /// |Platform | Value                                 | Example                        |\n    /// | ------- | ------------------------------------- | ------------------------------ |\n    /// | Linux   | `$XDG_CONFIG_HOME` or `$HOME`/.config | /home/alice/.config            |\n    /// | macOS   | `$XDG_CONFIG_HOME` or `$HOME`/.config | /Users/alice/.config           |\n    /// | Windows | `{FOLDERID_RoamingAppData}`           | C:\\Users\\Alice\\AppData\\Roaming |\n    ///\n    /// # Example\n    /// ```toml\n    /// columns = 80\n    /// types = [\"Python\"]\n    /// treat_doc_strings_as_comments = true\n    // ///\n    // /// [[languages.Python]]\n    // /// extensions = [\"py3\"]\n    /// ```\n    pub fn from_config_files() -> Self {\n        let conf_dir = etcetera::choose_base_strategy()\n            .ok()\n            .map(|basedirs| basedirs.config_dir())\n            .and_then(Self::get_config)\n            .unwrap_or_default();\n\n        let home_dir = etcetera::home_dir()\n            .ok()\n            .and_then(Self::get_config)\n            .unwrap_or_default();\n\n        let current_dir = env::current_dir()\n            .ok()\n            .and_then(Self::get_config)\n            .unwrap_or_default();\n\n        #[allow(clippy::or_fun_call)]\n        Config {\n            columns: current_dir\n                .columns\n                .or(home_dir.columns.or(conf_dir.columns)),\n            hidden: current_dir.hidden.or(home_dir.hidden.or(conf_dir.hidden)),\n            //languages: current_dir.languages.or(conf_dir.languages),\n            treat_doc_strings_as_comments: current_dir.treat_doc_strings_as_comments.or(home_dir\n                .treat_doc_strings_as_comments\n                .or(conf_dir.treat_doc_strings_as_comments)),\n            sort: current_dir.sort.or(home_dir.sort.or(conf_dir.sort)),\n            types: current_dir.types.or(home_dir.types.or(conf_dir.types)),\n            for_each_fn: current_dir\n                .for_each_fn\n                .or(home_dir.for_each_fn.or(conf_dir.for_each_fn)),\n            no_ignore: current_dir\n                .no_ignore\n                .or(home_dir.no_ignore.or(conf_dir.no_ignore)),\n            no_ignore_parent: current_dir\n                .no_ignore_parent\n                .or(home_dir.no_ignore_parent.or(conf_dir.no_ignore_parent)),\n            no_ignore_dot: current_dir\n                .no_ignore_dot\n                .or(home_dir.no_ignore_dot.or(conf_dir.no_ignore_dot)),\n            no_ignore_vcs: current_dir\n                .no_ignore_vcs\n                .or(home_dir.no_ignore_vcs.or(conf_dir.no_ignore_vcs)),\n        }\n    }\n}\n\n/*\n/// Configuration for an individual [`LanguageType`].\n///\n/// ```\n/// use std::collections::HashMap;\n/// use tokei::{Config, LanguageConfig, LanguageType};\n///\n/// let config = Config {\n///     languages: {\n///         let cpp_conf = LanguageConfig {\n///             extensions: vec![String::from(\"c\")],\n///         };\n///\n///         let mut languages_config = HashMap::new();\n///         languages_config.insert(LanguageType::Cpp, cpp_conf);\n///\n///         Some(languages_config)\n///     },\n///\n///     ..Config::default()\n/// };\n///\n/// ```\n///\n/// [`LanguageType`]: enum.LanguageType.html\n#[derive(Debug, Default, Deserialize)]\npub struct LanguageConfig {\n    /// Additional extensions for a language. Any extensions that overlap with\n    /// already defined extensions from `tokei` will be ignored.\n    pub extensions: Vec<String>,\n}\n\nimpl LanguageConfig {\n    /// Creates a new empty configuration. By default this will not change\n    /// anything from the default.\n    pub fn new() -> Self {\n        Self::default()\n    }\n\n    /// Accepts a `Vec<String>` representing additional extensions for a\n    /// language. Any extensions that overlap with already defined extensions\n    /// from `tokei` will be ignored.\n    pub fn extensions(&mut self, extensions: Vec<String>) {\n        self.extensions = extensions;\n    }\n}\n*/\n"
  },
  {
    "path": "src/consts.rs",
    "content": "// Set of common pub consts.\n\n/// Fallback row length\npub const FALLBACK_ROW_LEN: usize = 81;\n\n// Column widths used for console printing.\n\n/// Language column width\npub const LANGUAGE_COLUMN_WIDTH: usize = 10;\n\n/// Path column width\npub const PATH_COLUMN_WIDTH: usize = 80;\n\n/// Files column width\npub const FILES_COLUMN_WIDTH: usize = 8;\n\n/// Lines column width\npub const LINES_COLUMN_WIDTH: usize = 12;\n\n/// Code column width\npub const CODE_COLUMN_WIDTH: usize = 12;\n\n/// Comments column width\npub const COMMENTS_COLUMN_WIDTH: usize = 12;\n\n/// Blanks column width\npub const BLANKS_COLUMN_WIDTH: usize = 12;\n"
  },
  {
    "path": "src/input.rs",
    "content": "use serde::{Deserialize, Serialize};\nuse std::{collections::BTreeMap, error::Error, str::FromStr};\n\nuse tokei::{Language, LanguageType, Languages};\n\ntype LanguageMap = BTreeMap<LanguageType, Language>;\n\n#[derive(Deserialize, Serialize, Debug)]\nstruct Output {\n    #[serde(flatten)]\n    languages: LanguageMap,\n    #[serde(rename = \"Total\")]\n    totals: Language,\n}\n\nmacro_rules! supported_formats {\n    ($(\n        ($name:ident, $feature:expr, $variant:ident [$($krate:ident),+]) =>\n            $parse_kode:expr,\n            $print_kode:expr,\n    )+) => (\n        $( // for each format\n            $( // for each required krate\n                #[cfg(feature = $feature)] extern crate $krate;\n            )+\n        )+\n\n        /// Supported serialization formats.\n        ///\n        /// To enable all formats compile with the `all` feature.\n        #[cfg_attr(test, derive(strum_macros::EnumIter))]\n        #[derive(Debug, Clone)]\n        pub enum Format {\n            Json,\n            $(\n                #[cfg(feature = $feature)] $variant\n            ),+\n            // TODO: Allow adding format at runtime when used as a lib?\n        }\n\n        impl Format {\n            pub fn supported() -> &'static [&'static str] {\n                &[\n                    \"json\",\n                    $(\n                        #[cfg(feature = $feature)] stringify!($name)\n                    ),+\n                ]\n            }\n\n            pub fn all() -> &'static [&'static str] {\n                &[\n                    $( stringify!($name) ),+\n                ]\n            }\n\n            pub fn all_feature_names() -> &'static [&'static str] {\n                &[\n                    $( $feature ),+\n                ]\n            }\n\n            pub fn not_supported() -> &'static [&'static str] {\n                &[\n                    $(\n                        #[cfg(not(feature = $feature))] stringify!($name)\n                    ),+\n                ]\n            }\n\n            pub fn parse(input: &str) -> Option<LanguageMap> {\n                if input.is_empty() {\n                    return None\n                }\n\n                if let Ok(Output { languages, .. }) = serde_json::from_str::<Output>(input) {\n                    return Some(languages);\n                }\n\n                $(\n                    // attributes are not yet allowed on `if` expressions\n                    #[cfg(feature = $feature)]\n                    {\n                        let parse = &{ $parse_kode };\n\n                        if let Ok(Output { languages, .. }) = parse(input) {\n                            return Some(languages)\n                        }\n                    }\n                )+\n\n                // Didn't match any of the compiled serialization formats\n                None\n            }\n\n            pub fn print(&self, languages: &Languages) -> Result<String, Box<dyn Error>> {\n                let output = Output {\n                    languages: (*languages).to_owned(),\n                    totals: languages.total()\n                };\n\n                match *self {\n                    Format::Json => Ok(serde_json::to_string(&output)?),\n                    $(\n                        #[cfg(feature = $feature)] Format::$variant => {\n                            let print= &{ $print_kode };\n                            Ok(print(&output)?)\n                        }\n                    ),+\n                }\n            }\n        }\n\n        impl FromStr for Format {\n            type Err = String;\n\n            fn from_str(format: &str) -> Result<Self, Self::Err> {\n                match format {\n                    \"json\" => Ok(Format::Json),\n                    $(\n                        stringify!($name) => {\n                            #[cfg(feature = $feature)]\n                            return Ok(Format::$variant);\n\n                            #[cfg(not(feature = $feature))]\n                            return Err(format!(\n\"This version of tokei was compiled without \\\nany '{format}' serialization support, to enable serialization, \\\nreinstall tokei with the features flag.\n\n    cargo install tokei --features {feature}\n\nIf you want to enable all supported serialization formats, you can use the 'all' feature.\n\n    cargo install tokei --features all\\n\",\n                                format = stringify!($name),\n                                feature = $feature)\n                            );\n                        }\n                    ),+\n                    format => Err(format!(\"{:?} is not a supported serialization format\", format)),\n                }\n            }\n        }\n    )\n}\n\n// The ordering of these determines the attempted order when parsing.\nsupported_formats!(\n    (cbor, \"cbor\", Cbor [serde_cbor, hex]) =>\n        |input| {\n            hex::FromHex::from_hex(input)\n                .map_err(|e: hex::FromHexError| <Box<dyn Error>>::from(e))\n                .and_then(|hex: Vec<_>| Ok(serde_cbor::from_slice(&hex)?))\n        },\n        |languages| serde_cbor::to_vec(&languages).map(hex::encode),\n\n    (json, \"json\", Json [serde_json]) =>\n        serde_json::from_str,\n        serde_json::to_string,\n\n    (yaml, \"yaml\", Yaml [serde_yaml]) =>\n        serde_yaml::from_str,\n        serde_yaml::to_string,\n);\n\npub fn add_input(input: &str, languages: &mut Languages) -> bool {\n    use std::fs::File;\n    use std::io::Read;\n\n    let map = match File::open(input) {\n        Ok(mut file) => {\n            let contents = {\n                let mut contents = String::new();\n                file.read_to_string(&mut contents)\n                    .expect(\"Couldn't read file\");\n                contents\n            };\n\n            convert_input(&contents)\n        }\n        Err(_) => {\n            if input == \"stdin\" {\n                let mut stdin = ::std::io::stdin();\n                let mut buffer = String::new();\n\n                let _ = stdin.read_to_string(&mut buffer);\n                convert_input(&buffer)\n            } else {\n                convert_input(input)\n            }\n        }\n    };\n\n    if let Some(map) = map {\n        *languages += map;\n        true\n    } else {\n        false\n    }\n}\n\nfn convert_input(contents: &str) -> Option<LanguageMap> {\n    self::Format::parse(contents)\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    use strum::IntoEnumIterator;\n    use tokei::Config;\n\n    use std::path::Path;\n\n    #[test]\n    fn formatting_print_matches_parse() {\n        // Get language results from sample dir\n        let data_dir = Path::new(\"tests\").join(\"data\");\n        let mut langs = Languages::new();\n        langs.get_statistics(&[data_dir], &[], &Config::default());\n\n        // Check that the value matches after serializing and deserializing\n        for variant in Format::iter() {\n            let serialized = variant\n                .print(&langs)\n                .unwrap_or_else(|_| panic!(\"Failed serializing variant: {:?}\", variant));\n            let deserialized = Format::parse(&serialized)\n                .unwrap_or_else(|| panic!(\"Failed deserializing variant: {:?}\", variant));\n            assert_eq!(*langs, deserialized);\n        }\n    }\n}\n"
  },
  {
    "path": "src/language/embedding.rs",
    "content": "#![allow(clippy::trivial_regex)]\n\nuse crate::LanguageType;\nuse once_cell::sync::Lazy;\nuse regex::bytes::Regex;\n\npub static START_SCRIPT: Lazy<Regex> =\n    Lazy::new(|| Regex::new(r#\"<script(?:.*type=\"(.*)\")?.*?>\"#).unwrap());\npub static END_SCRIPT: Lazy<Regex> = Lazy::new(|| Regex::new(r#\"</script>\"#).unwrap());\n\npub static START_STYLE: Lazy<Regex> =\n    Lazy::new(|| Regex::new(r#\"<style(?:.*lang=\"(.*)\")?.*?>\"#).unwrap());\npub static END_STYLE: Lazy<Regex> = Lazy::new(|| Regex::new(r#\"</style>\"#).unwrap());\n\npub static START_TEMPLATE: Lazy<Regex> =\n    Lazy::new(|| Regex::new(r#\"<template(?:.*lang=\"(.*)\")?.*?>\"#).unwrap());\npub static END_TEMPLATE: Lazy<Regex> = Lazy::new(|| Regex::new(r#\"</template>\"#).unwrap());\n\npub static STARTING_MARKDOWN_REGEX: Lazy<Regex> = Lazy::new(|| Regex::new(r#\"```\\S+\\s\"#).unwrap());\npub static ENDING_MARKDOWN_REGEX: Lazy<Regex> = Lazy::new(|| Regex::new(r#\"```\\s?\"#).unwrap());\n\npub static STARTING_LF_BLOCK_REGEX: Lazy<Regex> = Lazy::new(|| Regex::new(r#\"\\{=\"#).unwrap());\npub static ENDING_LF_BLOCK_REGEX: Lazy<Regex> = Lazy::new(|| Regex::new(r#\"=}\"#).unwrap());\n\n/// A memory of a regex matched.\n/// The values provided by `Self::start` and `Self::end` are in the same space as the\n/// start value supplied to `RegexCache::build`\npub struct Capture<'a> {\n    start: usize,\n    text: &'a [u8],\n}\n\nimpl Capture<'_> {\n    #[inline(always)]\n    fn start(&self) -> usize {\n        self.start\n    }\n    #[inline(always)]\n    pub fn end(&self) -> usize {\n        self.start + self.text.len()\n    }\n    #[inline(always)]\n    pub fn as_bytes(&self) -> &[u8] {\n        self.text\n    }\n}\n\nimpl<'a> std::fmt::Debug for Capture<'a> {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        f.debug_struct(\"Capture\")\n            .field(\"start\", &self.start)\n            .field(\"end\", &self.end())\n            .field(\"text\", &String::from_utf8_lossy(self.text))\n            .finish()\n    }\n}\n\npub(crate) struct RegexCache<'a> {\n    inner: Option<RegexFamily<'a>>,\n}\n\n/// Embedding regexes are similar between different sets of languages.\n/// `RegexFamily` records both which family the language belongs to,\n/// as well as the actual matches\npub(crate) enum RegexFamily<'a> {\n    HtmlLike(HtmlLike<'a>),\n    LinguaFranca(SimpleCapture<'a>),\n    Markdown(SimpleCapture<'a>),\n    Rust,\n}\n\npub(crate) struct HtmlLike<'a> {\n    start_script: Option<Box<[Capture<'a>]>>,\n    start_style: Option<Box<[Capture<'a>]>>,\n    start_template: Option<Box<[Capture<'a>]>>,\n}\n\npub(crate) struct SimpleCapture<'a> {\n    starts: Option<Box<[Capture<'a>]>>,\n}\n\nimpl<'a> HtmlLike<'a> {\n    pub fn start_script_in_range<'this>(\n        &'this self,\n        start: usize,\n        end: usize,\n    ) -> Option<impl Iterator<Item = &'this Capture<'a>>> {\n        filter_range(self.start_script.as_ref()?, start, end)\n    }\n\n    pub fn start_style_in_range<'this>(\n        &'this self,\n        start: usize,\n        end: usize,\n    ) -> Option<impl Iterator<Item = &'this Capture<'a>>> {\n        filter_range(self.start_style.as_ref()?, start, end)\n    }\n\n    pub fn start_template_in_range<'this>(\n        &'this self,\n        start: usize,\n        end: usize,\n    ) -> Option<impl Iterator<Item = &'this Capture<'a>>> {\n        filter_range(self.start_template.as_ref()?, start, end)\n    }\n}\n\nimpl<'a> SimpleCapture<'a> {\n    pub fn starts_in_range<'this>(\n        &'this self,\n        start: usize,\n        end: usize,\n    ) -> Option<&'this Capture<'a>> {\n        filter_range(self.starts.as_ref()?, start, end).and_then(|mut it| it.next())\n    }\n\n    fn make_capture(\n        regex: &Regex,\n        lines: &'a [u8],\n        start: usize,\n        end: usize,\n    ) -> Option<SimpleCapture<'a>> {\n        let capture = SimpleCapture {\n            starts: save_captures(regex, lines, start, end),\n        };\n\n        if capture.starts.is_some() {\n            Some(capture)\n        } else {\n            None\n        }\n    }\n}\n\nfn filter_range<'dataset, 'cap>(\n    dataset: &'dataset [Capture<'cap>],\n    start: usize,\n    end: usize,\n) -> Option<impl Iterator<Item = &'dataset Capture<'cap>>> {\n    let pos = dataset\n        .binary_search_by_key(&start, |cap| cap.start())\n        .ok()?;\n\n    if pos >= dataset.len() || dataset[pos].end() > end {\n        None\n    } else {\n        Some(\n            dataset[pos..]\n                .iter()\n                .take_while(move |cap| cap.end() <= end),\n        )\n    }\n}\n\nimpl<'a> RegexCache<'a> {\n    /// Returns the language family for which regexes were matched, if any\n    pub(crate) fn family(&self) -> Option<&RegexFamily> {\n        self.inner.as_ref()\n    }\n\n    /// Tries to memoize any matches of embedding regexes that occur within lines[start..end]\n    /// for the given language. Any `Capture` values eventually recovered will use the same\n    /// zero for their start as the given `start` argument.\n    pub(crate) fn build(lang: LanguageType, lines: &'a [u8], start: usize, end: usize) -> Self {\n        let inner = match lang {\n            LanguageType::Markdown | LanguageType::UnrealDeveloperMarkdown => {\n                SimpleCapture::make_capture(&STARTING_MARKDOWN_REGEX, lines, start, end)\n                    .map(RegexFamily::Markdown)\n            }\n            LanguageType::Rust => Some(RegexFamily::Rust),\n            LanguageType::LinguaFranca => {\n                SimpleCapture::make_capture(&STARTING_LF_BLOCK_REGEX, lines, start, end)\n                    .map(RegexFamily::LinguaFranca)\n            }\n            LanguageType::Html\n            | LanguageType::RubyHtml\n            | LanguageType::Svelte\n            | LanguageType::Vue\n            | LanguageType::GlimmerJs\n            | LanguageType::GlimmerTs => {\n                let html = HtmlLike {\n                    start_script: save_captures(&START_SCRIPT, lines, start, end),\n                    start_style: save_captures(&START_STYLE, lines, start, end),\n                    start_template: save_captures(&START_TEMPLATE, lines, start, end),\n                };\n\n                if html.start_script.is_some()\n                    || html.start_style.is_some()\n                    || html.start_template.is_some()\n                {\n                    Some(RegexFamily::HtmlLike(html))\n                } else {\n                    None\n                }\n            }\n            _ => None,\n        };\n        Self { inner }\n    }\n}\n\nfn save_captures<'a>(\n    regex: &Regex,\n    lines: &'a [u8],\n    start: usize,\n    end: usize,\n) -> Option<Box<[Capture<'a>]>> {\n    let v: Vec<_> = regex\n        .captures(&lines[start..end])?\n        .iter()\n        .flatten()\n        .map(|cap| Capture {\n            start: start + cap.start(),\n            text: cap.as_bytes(),\n        })\n        .collect();\n\n    if v.is_empty() {\n        None\n    } else {\n        Some(v.into())\n    }\n}\n"
  },
  {
    "path": "src/language/language_type.rs",
    "content": "use std::{\n    borrow::Cow,\n    fmt,\n    fs::File,\n    io::{self, Read},\n    path::{Path, PathBuf},\n    str::FromStr,\n};\n\nuse crate::{\n    config::Config,\n    language::syntax::{FileContext, LanguageContext, SyntaxCounter},\n    stats::{CodeStats, Report},\n    utils::{ext::SliceExt, fs as fsutils},\n};\n\nuse encoding_rs_io::DecodeReaderBytesBuilder;\nuse grep_searcher::{LineIter, LineStep};\nuse once_cell::sync::Lazy;\nuse rayon::prelude::*;\nuse serde::Serialize;\n\nuse self::LanguageType::*;\n\ninclude!(concat!(env!(\"OUT_DIR\"), \"/language_type.rs\"));\n\nimpl Serialize for LanguageType {\n    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>\n    where\n        S: serde::Serializer,\n    {\n        serializer.serialize_str(self.name())\n    }\n}\n\nimpl LanguageType {\n    /// Parses a given [`Path`] using the [`LanguageType`]. Returning [`Report`]\n    /// on success and giving back ownership of [`PathBuf`] on error.\n    pub fn parse(self, path: PathBuf, config: &Config) -> Result<Report, (io::Error, PathBuf)> {\n        let text = {\n            let f = match File::open(&path) {\n                Ok(f) => f,\n                Err(e) => return Err((e, path)),\n            };\n            let mut s = Vec::new();\n            let mut reader = DecodeReaderBytesBuilder::new().build(f);\n\n            if let Err(e) = reader.read_to_end(&mut s) {\n                return Err((e, path));\n            }\n            s\n        };\n\n        let mut stats = Report::new(path);\n\n        stats += self.parse_from_slice(text, config);\n\n        Ok(stats)\n    }\n\n    /// Parses the text provided as the given [`LanguageType`].\n    pub fn parse_from_str<A: AsRef<str>>(self, text: A, config: &Config) -> CodeStats {\n        self.parse_from_slice(text.as_ref().as_bytes(), config)\n    }\n\n    /// Parses the bytes provided as the given [`LanguageType`].\n    pub fn parse_from_slice<A: AsRef<[u8]>>(self, text: A, config: &Config) -> CodeStats {\n        let text = text.as_ref();\n\n        if self == Jupyter {\n            return self\n                .parse_jupyter(text.as_ref(), config)\n                .unwrap_or_default();\n        }\n\n        let syntax = {\n            let mut syntax_mut = SyntaxCounter::new(self);\n            if self == LinguaFranca {\n                syntax_mut.lf_embedded_language = self.find_lf_target_language(text);\n            }\n            syntax_mut\n        };\n\n        if let Some(end) = syntax.shared.important_syntax.find(text).and_then(|m| {\n            // Get the position of the last line before the important\n            // syntax.\n            text[..=m.start()]\n                .iter()\n                .rev()\n                .position(|&c| c == b'\\n')\n                .filter(|&p| p != 0)\n                .map(|p| m.start() - p)\n        }) {\n            let (skippable_text, rest) = text.split_at(end + 1);\n            let is_fortran = syntax.shared.is_fortran;\n            let is_literate = syntax.shared.is_literate;\n            let comments = syntax.shared.line_comments;\n            trace!(\n                \"Using Simple Parse on {:?}\",\n                String::from_utf8_lossy(skippable_text)\n            );\n            let parse_lines = move || self.parse_lines(config, rest, CodeStats::new(), syntax);\n            let simple_parse = move || {\n                LineIter::new(b'\\n', skippable_text)\n                    .par_bridge()\n                    .map(|line| {\n                        // FORTRAN has a rule where it only counts as a comment if it's the\n                        // first character in the column, so removing starting whitespace\n                        // could cause a miscount.\n                        let line = if is_fortran { line } else { line.trim() };\n                        if line.trim().is_empty() {\n                            (1, 0, 0)\n                        } else if is_literate\n                            || comments.iter().any(|c| line.starts_with(c.as_bytes()))\n                        {\n                            (0, 0, 1)\n                        } else {\n                            (0, 1, 0)\n                        }\n                    })\n                    .reduce(|| (0, 0, 0), |a, b| (a.0 + b.0, a.1 + b.1, a.2 + b.2))\n            };\n\n            let (mut stats, (blanks, code, comments)) = rayon::join(parse_lines, simple_parse);\n\n            stats.blanks += blanks;\n            stats.code += code;\n            stats.comments += comments;\n            stats\n        } else {\n            self.parse_lines(config, text, CodeStats::new(), syntax)\n        }\n    }\n\n    #[inline]\n    fn parse_lines(\n        self,\n        config: &Config,\n        lines: &[u8],\n        mut stats: CodeStats,\n        mut syntax: SyntaxCounter,\n    ) -> CodeStats {\n        let mut stepper = LineStep::new(b'\\n', 0, lines.len());\n\n        while let Some((start, end)) = stepper.next(lines) {\n            let line = &lines[start..end];\n            // FORTRAN has a rule where it only counts as a comment if it's the\n            // first character in the column, so removing starting whitespace\n            // could cause a miscount.\n            let line = if syntax.shared.is_fortran {\n                line\n            } else {\n                line.trim()\n            };\n            trace!(\"{}\", String::from_utf8_lossy(line));\n\n            if syntax.try_perform_single_line_analysis(line, &mut stats) {\n                continue;\n            }\n\n            let started_in_comments = !syntax.stack.is_empty()\n                || (config.treat_doc_strings_as_comments == Some(true)\n                    && syntax.quote.is_some()\n                    && syntax.quote_is_doc_quote);\n            let ended_with_comments =\n                match syntax.perform_multi_line_analysis(lines, start, end, config) {\n                    crate::language::syntax::AnalysisReport::Normal(end) => end,\n                    crate::language::syntax::AnalysisReport::ChildLanguage(FileContext {\n                        language,\n                        end,\n                        stats: blob,\n                    }) => {\n                        match language {\n                            LanguageContext::Markdown { balanced, language } => {\n                                // Add the lines for the code fences.\n                                stats.comments += if balanced { 2 } else { 1 };\n                                // Add the code inside the fence to the stats.\n                                *stats.blobs.entry(language).or_default() += blob;\n                            }\n                            LanguageContext::Rust => {\n                                // Add all the markdown blobs.\n                                *stats.blobs.entry(LanguageType::Markdown).or_default() += blob;\n                            }\n                            LanguageContext::LinguaFranca => {\n                                let child_lang = syntax.get_lf_target_language();\n                                *stats.blobs.entry(child_lang).or_default() += blob;\n                            }\n                            LanguageContext::Html { language } => {\n                                stats.code += 1;\n                                // Add all the markdown blobs.\n                                *stats.blobs.entry(language).or_default() += blob;\n                            }\n                        }\n\n                        // Advance to after the language code and the delimiter..\n                        stepper = LineStep::new(b'\\n', end, lines.len());\n                        continue;\n                    }\n                };\n            trace!(\"{}\", String::from_utf8_lossy(line));\n\n            if syntax.shared.is_literate\n                || syntax.line_is_comment(line, config, ended_with_comments, started_in_comments)\n            {\n                stats.comments += 1;\n                trace!(\"Comment No.{}\", stats.comments);\n                trace!(\"Was the Comment stack empty?: {}\", !started_in_comments);\n            } else {\n                stats.code += 1;\n                trace!(\"Code No.{}\", stats.code);\n            }\n        }\n\n        stats\n    }\n\n    fn parse_jupyter(&self, json: &[u8], config: &Config) -> Option<CodeStats> {\n        #[derive(Deserialize)]\n        struct Jupyter {\n            cells: Vec<JupyterCell>,\n            metadata: JupyterMetadata,\n        }\n\n        #[derive(Clone, Copy, Deserialize, PartialEq, Eq)]\n        #[serde(rename_all = \"lowercase\")]\n        enum CellType {\n            Markdown,\n            Code,\n        }\n\n        #[derive(Deserialize)]\n        struct JupyterCell {\n            cell_type: CellType,\n            source: Vec<String>,\n        }\n\n        #[derive(Deserialize)]\n        struct JupyterMetadata {\n            kernelspec: serde_json::Value,\n            language_info: serde_json::Value,\n        }\n\n        let jupyter: Jupyter = serde_json::from_slice(json).ok()?;\n\n        let mut jupyter_stats = CodeStats::new();\n\n        let language = jupyter\n            .metadata\n            .kernelspec\n            .get(\"language\")\n            .and_then(serde_json::Value::as_str)\n            .and_then(|v| LanguageType::from_str(v).ok())\n            .or_else(|| {\n                jupyter\n                    .metadata\n                    .language_info\n                    .get(\"file_extension\")\n                    .and_then(serde_json::Value::as_str)\n                    .and_then(LanguageType::from_file_extension)\n            })\n            .unwrap_or(LanguageType::Python);\n\n        let iter = jupyter\n            .cells\n            .par_iter()\n            .map(|cell| match cell.cell_type {\n                CellType::Markdown => (\n                    LanguageType::Markdown,\n                    LanguageType::Markdown.parse_from_str(cell.source.join(\"\"), config),\n                ),\n                CellType::Code => (\n                    language,\n                    language.parse_from_str(cell.source.join(\"\"), config),\n                ),\n            })\n            .collect::<Vec<_>>();\n\n        for (language, stats) in iter {\n            *jupyter_stats.blobs.entry(language).or_default() += &stats;\n            jupyter_stats += &stats;\n        }\n\n        Some(jupyter_stats)\n    }\n\n    /// The embedded language in LF is declared in a construct that looks like this: `target C;`, `target Python`.\n    /// This is the first thing in the file (although there may be comments before).\n    fn find_lf_target_language(&self, bytes: &[u8]) -> Option<LanguageType> {\n        use regex::bytes::Regex;\n        static LF_TARGET_REGEX: Lazy<Regex> =\n            Lazy::new(|| Regex::new(r#\"(?m)\\btarget\\s+(\\w+)\\s*($|;|\\{)\"#).unwrap());\n        LF_TARGET_REGEX.captures(bytes).and_then(|captures| {\n            let name = captures.get(1).unwrap().as_bytes();\n            if name == b\"CCpp\" {\n                // this is a special alias for the C target in LF\n                Some(C)\n            } else {\n                let name_str = &String::from_utf8_lossy(name);\n                let by_name = LanguageType::from_name(name_str);\n                if by_name.is_none() {\n                    trace!(\"LF target not recognized: {}\", name_str);\n                }\n                by_name\n            }\n        })\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    use std::{fs, path::Path};\n\n    #[test]\n    fn rust_allows_nested() {\n        assert!(LanguageType::Rust.allows_nested());\n    }\n\n    fn assert_stats(stats: &CodeStats, blanks: usize, code: usize, comments: usize) {\n        assert_eq!(stats.blanks, blanks, \"expected {} blank lines\", blanks);\n        assert_eq!(stats.code, code, \"expected {} code lines\", code);\n        assert_eq!(\n            stats.comments, comments,\n            \"expected {} comment lines\",\n            comments\n        );\n    }\n\n    #[test]\n    fn jupyter_notebook_has_correct_totals() {\n        let sample_notebook =\n            fs::read_to_string(Path::new(\"tests\").join(\"data\").join(\"jupyter.ipynb\")).unwrap();\n\n        let stats = LanguageType::Jupyter\n            .parse_jupyter(sample_notebook.as_bytes(), &Config::default())\n            .unwrap();\n\n        assert_stats(&stats, 115, 528, 333);\n    }\n\n    #[test]\n    fn lf_embedded_language_is_counted() {\n        let file_text =\n            fs::read_to_string(Path::new(\"tests\").join(\"data\").join(\"linguafranca.lf\")).unwrap();\n\n        let stats = LinguaFranca.parse_from_str(file_text, &Config::default());\n\n        assert_stats(&stats, 9, 11, 8);\n\n        assert_eq!(stats.blobs.len(), 1, \"num embedded languages\");\n        let rust_stats = stats.blobs.get(&Rust).expect(\"should have a Rust entry\");\n        assert_stats(rust_stats, 2, 5, 1);\n    }\n}\n"
  },
  {
    "path": "src/language/language_type.tera.rs",
    "content": "use arbitrary::Arbitrary;\n\n/// Represents a individual programming language. Can be used to provide\n/// information about the language, such as multi line comments, single line\n/// comments, string literal syntax, whether a given language allows nesting\n/// comments.\n#[derive(Deserialize)]\n#[derive(Arbitrary, Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]\n#[non_exhaustive]\n#[allow(clippy::upper_case_acronyms)]\npub enum LanguageType {\n    {% for key, value in languages -%}\n        #[allow(missing_docs)] {% if value.name is defined %} #[serde(alias = \"{{value.name}}\")] {% else %} #[serde(alias = \"{{key}}\")] {% endif %} {{key}},\n    {% endfor %}\n}\n\nimpl LanguageType {\n\n    /// Returns the display name of a language.\n    ///\n    /// ```\n    /// # use tokei::*;\n    /// let bash = LanguageType::Bash;\n    ///\n    /// assert_eq!(bash.name(), \"BASH\");\n    /// ```\n    pub fn name(self) -> &'static str {\n        match self {\n            {% for key, value in languages -%}\n                {{key}} => {% if value.name %}\"{{value.name}}\"{% else %}\"{{key}}\"{% endif %},\n            {% endfor %}\n        }\n    }\n\n    pub(crate) fn _is_blank(self) -> bool {\n        match self {\n            {% for key, v in languages -%}\n                {{key}} => {{ v.blank | default(value=false) }},\n            {% endfor %}\n        }\n    }\n\n    pub(crate) fn is_fortran(self) -> bool {\n        self == LanguageType::FortranModern ||\n        self == LanguageType::FortranLegacy\n    }\n\n    /// Returns whether the language is \"literate\", meaning that it considered\n    /// to primarily be documentation and is counted primarily as comments\n    /// rather than procedural code.\n    pub fn is_literate(self) -> bool {\n        match self {\n            {% for key, v in languages -%}\n                {{key}} => {{ v.literate | default(value=false) }},\n            {% endfor %}\n        }\n    }\n\n    /// Provides every variant in a Vec\n    pub fn list() -> &'static [(Self, &'static [&'static str])] {\n        &[{% for key, val in languages -%}\n            ({{key}},\n            {% if val.extensions %} &[{% for extension in val.extensions %}\"{{extension}}\", {% endfor %}],\n            {% else %} &[],\n            {% endif %}),\n        {% endfor %}]\n    }\n\n    /// Returns the single line comments of a language.\n    /// ```\n    /// use tokei::LanguageType;\n    /// let lang = LanguageType::Rust;\n    /// assert_eq!(lang.line_comments(), &[\"//\"]);\n    /// ```\n    pub fn line_comments(self) -> &'static [&'static str] {\n        match self {\n            {% for key, value in languages -%}\n                {{key}} => &[{% for item in value.line_comment | default(value=[]) %}\"{{item}}\",{% endfor %}],\n            {% endfor %}\n        }\n    }\n\n    /// Returns the single line comments of a language.\n    /// ```\n    /// use tokei::LanguageType;\n    /// let lang = LanguageType::Rust;\n    /// assert_eq!(lang.multi_line_comments(), &[(\"/*\", \"*/\")]);\n    /// ```\n    pub fn multi_line_comments(self) -> &'static [(&'static str, &'static str)]\n    {\n        match self {\n            {% for key, value in languages -%}\n                {{key}} => &[\n                    {%- for items in value.multi_line_comments | default(value=[]) -%}\n                        ({% for item in items %}\"{{item}}\",{% endfor %}),\n                    {%- endfor -%}\n                ],\n            {% endfor %}\n        }\n    }\n\n\n    /// Returns whether the language allows nested multi line comments.\n    /// ```\n    /// use tokei::LanguageType;\n    /// let lang = LanguageType::Rust;\n    /// assert!(lang.allows_nested());\n    /// ```\n    pub fn allows_nested(self) -> bool {\n        match self {\n            {% for key, v in languages -%}\n                {{key}} => {{ v.nested | default(value=false) }},\n            {% endfor %}\n        }\n    }\n\n    /// Returns what nested comments the language has. (Currently only D has\n    /// any of this type.)\n    /// ```\n    /// use tokei::LanguageType;\n    /// let lang = LanguageType::D;\n    /// assert_eq!(lang.nested_comments(), &[(\"/+\", \"+/\")]);\n    /// ```\n    pub fn nested_comments(self) -> &'static [(&'static str, &'static str)]\n    {\n        match self {\n            {% for key, value in languages -%}\n                {{key}} => &[\n                    {%- for items in value.nested_comments | default(value=[]) -%}\n                        ({% for item in items %}\"{{item}}\",{% endfor %}),\n                    {%- endfor -%}\n                ],\n            {% endfor %}\n        }\n    }\n\n    /// Returns the quotes of a language.\n    /// ```\n    /// use tokei::LanguageType;\n    /// let lang = LanguageType::C;\n    /// assert_eq!(lang.quotes(), &[(\"\\\"\", \"\\\"\")]);\n    /// ```\n    pub fn quotes(self) -> &'static [(&'static str, &'static str)] {\n        match self {\n            {% for key, value in languages -%}\n                {{key}} => &[\n                    {%- for items in value.quotes | default(value=[]) -%}\n                        ({% for item in items %}\"{{item}}\",{% endfor %}),\n                    {%- endfor -%}\n                ],\n            {% endfor %}\n        }\n    }\n\n    /// Returns the verbatim quotes of a language.\n    /// ```\n    /// use tokei::LanguageType;\n    /// let lang = LanguageType::CSharp;\n    /// assert_eq!(lang.verbatim_quotes(), &[(\"@\\\"\", \"\\\"\")]);\n    /// ```\n    pub fn verbatim_quotes(self) -> &'static [(&'static str, &'static str)] {\n        match self {\n            {% for key, value in languages -%}\n                {{key}} => &[\n                    {%- for items in value.verbatim_quotes | default(value=[]) -%}\n                        ({% for item in items %}\"{{item}}\",{% endfor %}),\n                    {%- endfor -%}\n                ],\n            {% endfor %}\n        }\n    }\n\n    /// Returns the doc quotes of a language.\n    /// ```\n    /// use tokei::LanguageType;\n    /// let lang = LanguageType::Python;\n    /// assert_eq!(lang.doc_quotes(), &[(\"\\\"\\\"\\\"\", \"\\\"\\\"\\\"\"), (\"'''\", \"'''\")]);\n    /// ```\n    pub fn doc_quotes(self) -> &'static [(&'static str, &'static str)] {\n        match self {\n            {% for key, value in languages -%}\n                {{key}} => &[\n                    {% for items in value.doc_quotes | default(value=[])-%}\n                        ({% for item in items %}\"{{item}}\",{% endfor %}),\n                    {%- endfor %}\n                ],\n            {%- endfor %}\n        }\n    }\n\n    /// Returns the shebang of a language.\n    /// ```\n    /// use tokei::LanguageType;\n    /// let lang = LanguageType::Bash;\n    /// assert_eq!(lang.shebangs(), &[\"#!/bin/bash\"]);\n    /// ```\n    pub fn shebangs(self) -> &'static [&'static str] {\n        match self {\n            {% for key, lang in languages -%}\n                {{key}} => &[{% for item in lang.shebangs | default(value=[]) %}\"{{item}}\",{% endfor %}],\n            {% endfor %}\n        }\n    }\n\n    pub(crate) fn any_multi_line_comments(self) -> &'static [(&'static str, &'static str)] {\n        match self {\n            {% for key, value in languages -%}\n                {{key}} => &[\n                {%- set starting_multi_line_comments = value.multi_line_comments | default(value=[]) -%}\n                {%- set starting_nested_comments = value.nested_comments | default(value=[]) -%}\n                    {%- for item in starting_multi_line_comments | concat(with=starting_nested_comments) -%}\n                        (\"{{item.0}}\", \"{{item.1}}\"),\n                    {%- endfor -%}\n                ],\n            {% endfor %}\n        }\n    }\n\n    pub(crate) fn any_comments(self) -> &'static [&'static str] {\n        match self {\n            {% for key, value in languages -%}\n                {{key}} => &[\n                {%- set starting_multi_line_comments = value.multi_line_comments | default(value=[]) -%}\n                {%- set starting_nested_comments = value.nested_comments | default(value=[]) -%}\n\n                    {%- for item in starting_multi_line_comments | concat(with=starting_nested_comments) -%}\n                        \"{{item.0}}\",\n                        \"{{item.1}}\",\n                    {%- endfor -%}\n                    {%- for item in value.line_comment | default(value=[]) -%}\n                        \"{{item}}\",\n                    {%- endfor -%}\n                ],\n            {% endfor %}\n        }\n    }\n\n    /// Returns the parts of syntax that determines whether tokei can skip large\n    /// parts of analysis.\n    pub fn important_syntax(self) -> &'static [&'static str] {\n        match self {\n            {% for key, value in languages -%}\n                {%- set starting_quotes = value.quotes | default(value=[]) | map(attribute=\"0\") -%}\n                {%- set starting_doc_quotes = value.doc_quotes | default(value=[]) | map(attribute=\"0\") -%}\n                {%- set starting_multi_line_comments = value.multi_line_comments | default(value=[]) | map(attribute=\"0\") -%}\n                {%- set starting_nested_comments = value.nested_comments | default(value=[]) | map(attribute=\"0\") -%}\n                {%- set important_syntax = value.important_syntax | default(value=[]) -%}\n\n                {{key}} => &[\n                    {%- for item in starting_quotes |\n                                   concat(with=starting_doc_quotes) |\n                                   concat(with=starting_multi_line_comments) |\n                                   concat(with=starting_nested_comments) |\n                                   concat(with=important_syntax) -%}\n                        \"{{item}}\",\n                    {%- endfor -%}\n                    {%- for context in value.contexts | default(value=[]) -%}\n                        {% if value.kind == \"html\" %}\n                            \"<{{context.tag}}\",\n                        {% endif %}\n                    {%- endfor -%}\n                ],\n            {% endfor %}\n        }\n    }\n\n    /// Get language from a file path. May open and read the file.\n    ///\n    /// ```no_run\n    /// use tokei::{Config, LanguageType};\n    ///\n    /// let rust = LanguageType::from_path(\"./main.rs\", &Config::default());\n    ///\n    /// assert_eq!(rust, Some(LanguageType::Rust));\n    /// ```\n    pub fn from_path<P: AsRef<Path>>(entry: P, _config: &Config)\n        -> Option<Self>\n    {\n        let entry = entry.as_ref();\n\n        if let Some(filename) = fsutils::get_filename(entry) {\n            match &*filename {\n                {% for key, value in languages -%}\n                    {%- if value.filenames -%}\n                        {%- for item in value.filenames -%}\n                            | \"{{item}}\"\n                        {%- endfor -%}\n                            => return Some({{key}}),\n                    {% endif -%}\n                {%- endfor %}\n                _ => ()\n            }\n        }\n\n        match fsutils::get_extension(entry) {\n            Some(extension) => LanguageType::from_file_extension(extension.as_str()),\n            None => LanguageType::from_shebang(entry),\n        }\n    }\n\n    /// Get language from a file extension.\n    ///\n    /// ```no_run\n    /// use tokei::LanguageType;\n    ///\n    /// let rust = LanguageType::from_file_extension(\"rs\");\n    ///\n    /// assert_eq!(rust, Some(LanguageType::Rust));\n    /// ```\n    #[must_use]\n    pub fn from_file_extension(extension: &str) -> Option<Self> {\n        match extension {\n            {% for key, value in languages -%}\n                {%- if value.extensions -%}\n                    {%- for item in value.extensions  %}| \"{{item}}\" {% endfor %}=> Some({{key}}),\n                {% endif -%}\n            {%- endfor %}\n            extension => {\n                warn!(\"Unknown extension: {}\", extension);\n                None\n            },\n        }\n    }\n\n    /// Get language from its name.\n    ///\n    /// ```no_run\n    /// use tokei::LanguageType;\n    ///\n    /// let rust = LanguageType::from_name(\"Rust\");\n    ///\n    /// assert_eq!(rust, Some(LanguageType::Rust));\n    /// ```\n    #[must_use]\n    pub fn from_name(name: &str) -> Option<Self> {\n        match name {\n            {% for key, value in languages -%}\n                {% if value.name and value.name != key -%}\n                    | \"{{value.name}}\"\n                {% endif -%}\n                    | \"{{key}}\" => Some({{key}}),\n            {% endfor %}\n            unknown => {\n                warn!(\"Unknown language name: {}\", unknown);\n                None\n            },\n        }\n    }\n\n    /// Get language from its MIME type if available.\n    ///\n    /// ```no_run\n    /// use tokei::LanguageType;\n    ///\n    /// let lang = LanguageType::from_mime(\"application/javascript\");\n    ///\n    /// assert_eq!(lang, Some(LanguageType::JavaScript));\n    /// ```\n    #[must_use]\n    pub fn from_mime(mime: &str) -> Option<Self> {\n        match mime {\n            {% for key, value in languages -%}\n                {%- if value.mime -%}\n                    {%- for item in value.mime  %}| \"{{item}}\" {% endfor %}=> Some({{key}}),\n                {% endif -%}\n            {%- endfor %}\n            _ => {\n                warn!(\"Unknown MIME: {}\", mime);\n                None\n            },\n        }\n    }\n\n    /// Get language from a shebang. May open and read the file.\n    ///\n    /// ```no_run\n    /// use tokei::LanguageType;\n    ///\n    /// let rust = LanguageType::from_shebang(\"./main.rs\");\n    ///\n    /// assert_eq!(rust, Some(LanguageType::Rust));\n    /// ```\n    pub fn from_shebang<P: AsRef<Path>>(entry: P) -> Option<Self> {\n        // Read at max `READ_LIMIT` bytes from the given file.\n        // A typical shebang line has a length less than 32 characters;\n        // e.g. '#!/bin/bash' - 11B / `#!/usr/bin/env python3` - 22B\n        // It is *very* unlikely the file contains a valid shebang syntax\n        // if we don't find a newline character after searching the first 128B.\n        const READ_LIMIT: usize = 128;\n\n        let mut file = File::open(entry).ok()?;\n        let mut buf = [0; READ_LIMIT];\n\n        let len = file.read(&mut buf).ok()?;\n        let buf = &buf[..len];\n\n        let first_line = buf.split(|b| *b == b'\\n').next()?;\n        let first_line = std::str::from_utf8(first_line).ok()?;\n\n        let mut words = first_line.split_whitespace();\n        match words.next() {\n            {# First match against any shebang paths, and then check if the\n               language matches any found in the environment shebang path. #}\n            {% for key, value in languages -%}\n                {%- if value.shebangs %}\n                    {%- for item in value.shebangs  %}| Some(\"{{item}}\") {% endfor %}=> Some({{key}}),\n                {% endif -%}\n            {%- endfor %}\n\n            Some(\"#!/usr/bin/env\") => {\n                if let Some(word) = words.next() {\n                    match word {\n                        {% for key, value in languages -%}\n                            {%- if value.env -%}\n                                {%- for item in value.env  %}\n                                    {% if loop.index == 1 %}\n                                        _ if word.starts_with(\"{{item}}\")\n                                    {% else %}\n                                        || word.starts_with(\"{{item}}\")\n                                    {% endif %}\n                                {% endfor %}=> Some({{key}}),\n                            {% endif -%}\n                        {%- endfor %}\n                        env => {\n                            warn!(\"Unknown environment: {:?}\", env);\n                            None\n                        }\n                    }\n                } else {\n                    None\n                }\n            }\n            _ => None,\n        }\n    }\n}\n\nimpl FromStr for LanguageType {\n    type Err = &'static str;\n\n    fn from_str(from: &str) -> Result<Self, Self::Err> {\n        match &*from.to_lowercase() {\n            {% for key, value in languages %}\n                {% if value.name %}\"{{value.name | lower}}\"{% else %}\"{{key | lower}}\"{% endif %}\n                => Ok({{key}}),\n            {% endfor %}\n            _ => Err(\"Language not found, please use `-l` to see all available\\\n                     languages.\"),\n        }\n    }\n}\n\nimpl fmt::Display for LanguageType {\n    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n        write!(f, \"{}\", self.name())\n    }\n}\n\n\nimpl<'a> From<LanguageType> for Cow<'a, LanguageType> {\n    fn from(from: LanguageType) -> Self {\n        Cow::Owned(from)\n    }\n}\n\nimpl<'a> From<&'a LanguageType> for Cow<'a, LanguageType> {\n    fn from(from: &'a LanguageType) -> Self {\n        Cow::Borrowed(from)\n    }\n}\n"
  },
  {
    "path": "src/language/languages.rs",
    "content": "use std::{\n    collections::{btree_map, BTreeMap},\n    iter::IntoIterator,\n    ops::{AddAssign, Deref, DerefMut},\n    path::Path,\n};\n\nuse rayon::prelude::*;\n\nuse crate::{\n    config::Config,\n    language::{Language, LanguageType},\n    utils,\n};\n\n/// A newtype representing a list of languages counted in the provided\n/// directory.\n/// ([_List of\n/// Languages_](https://github.com/XAMPPRocky/tokei#supported-languages))\n#[derive(Debug, Default, PartialEq)]\npub struct Languages {\n    inner: BTreeMap<LanguageType, Language>,\n}\n\nimpl serde::Serialize for Languages {\n    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>\n    where\n        S: serde::Serializer,\n    {\n        self.inner.serialize(serializer)\n    }\n}\n\nimpl<'de> serde::Deserialize<'de> for Languages {\n    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>\n    where\n        D: serde::Deserializer<'de>,\n    {\n        let map = <_>::deserialize(deserializer)?;\n\n        Ok(Self::from_previous(map))\n    }\n}\n\nimpl Languages {\n    fn from_previous(map: BTreeMap<LanguageType, Language>) -> Self {\n        use std::collections::btree_map::Entry;\n        let mut me = Self::new();\n\n        for (name, input_language) in map {\n            match me.entry(name) {\n                Entry::Occupied(mut entry) => {\n                    *entry.get_mut() += input_language;\n                }\n                Entry::Vacant(entry) => {\n                    entry.insert(input_language);\n                }\n            }\n        }\n        me\n    }\n\n    /// Populates the `Languages` struct with statistics about languages\n    /// provided by [`Language`].\n    ///\n    /// Takes a `&[&str]` of paths to recursively traverse, paths can be\n    /// relative, absolute or glob paths. A second `&[&str]` of paths to ignore,\n    /// these strings use the `.gitignore` syntax, such as `target`\n    /// or `**/*.bk`.\n    ///\n    /// ```no_run\n    /// use tokei::{Config, Languages};\n    ///\n    /// let mut languages = Languages::new();\n    /// languages.get_statistics(&[\".\"], &[\".git\", \"target\"], &Config::default());\n    /// ```\n    ///\n    /// [`Language`]: struct.Language.html\n    pub fn get_statistics<A: AsRef<Path>>(\n        &mut self,\n        paths: &[A],\n        ignored: &[&str],\n        config: &Config,\n    ) {\n        utils::fs::get_all_files(paths, ignored, &mut self.inner, config);\n        self.inner.par_iter_mut().for_each(|(_, l)| l.total());\n    }\n\n    /// Constructs a new, Languages struct. Languages is always empty and does\n    /// not allocate.\n    ///\n    /// ```rust\n    /// # use tokei::*;\n    /// let languages = Languages::new();\n    /// ```\n    #[must_use]\n    pub fn new() -> Self {\n        Languages::default()\n    }\n\n    /// Summary of the Languages struct.\n    #[must_use]\n    pub fn total(self: &Languages) -> Language {\n        let mut total = Language::new();\n        for (ty, l) in self {\n            let language = l.summarise();\n            total.comments += language.comments;\n            total.blanks += language.blanks;\n            total.code += language.code;\n            total.inaccurate |= language.inaccurate;\n            total.children.insert(*ty, language.reports.clone());\n        }\n        total\n    }\n}\n\nimpl IntoIterator for Languages {\n    type Item = <BTreeMap<LanguageType, Language> as IntoIterator>::Item;\n    type IntoIter = <BTreeMap<LanguageType, Language> as IntoIterator>::IntoIter;\n\n    fn into_iter(self) -> Self::IntoIter {\n        self.inner.into_iter()\n    }\n}\n\nimpl<'a> IntoIterator for &'a Languages {\n    type Item = (&'a LanguageType, &'a Language);\n    type IntoIter = btree_map::Iter<'a, LanguageType, Language>;\n\n    fn into_iter(self) -> Self::IntoIter {\n        self.inner.iter()\n    }\n}\n\nimpl<'a> IntoIterator for &'a mut Languages {\n    type Item = (&'a LanguageType, &'a mut Language);\n    type IntoIter = btree_map::IterMut<'a, LanguageType, Language>;\n\n    fn into_iter(self) -> Self::IntoIter {\n        self.inner.iter_mut()\n    }\n}\n\nimpl AddAssign<BTreeMap<LanguageType, Language>> for Languages {\n    fn add_assign(&mut self, rhs: BTreeMap<LanguageType, Language>) {\n        for (name, language) in rhs {\n            if let Some(result) = self.inner.get_mut(&name) {\n                *result += language;\n            }\n        }\n    }\n}\n\nimpl Deref for Languages {\n    type Target = BTreeMap<LanguageType, Language>;\n\n    fn deref(&self) -> &Self::Target {\n        &self.inner\n    }\n}\n\nimpl DerefMut for Languages {\n    fn deref_mut(&mut self) -> &mut BTreeMap<LanguageType, Language> {\n        &mut self.inner\n    }\n}\n"
  },
  {
    "path": "src/language/mod.rs",
    "content": "mod embedding;\npub mod language_type;\npub mod languages;\nmod syntax;\n\nuse std::{collections::BTreeMap, mem, ops::AddAssign};\n\npub use self::{language_type::*, languages::Languages};\n\nuse crate::{sort::Sort, stats::Report};\n\n/// A struct representing statistics about a single Language.\n#[derive(Clone, Debug, Deserialize, Default, PartialEq, Serialize)]\npub struct Language {\n    /// The total number of blank lines.\n    pub blanks: usize,\n    /// The total number of lines of code.\n    pub code: usize,\n    /// The total number of comments(both single, and multi-line)\n    pub comments: usize,\n    /// A collection of statistics of individual files.\n    pub reports: Vec<Report>,\n    /// A map of any languages found in the reports.\n    pub children: BTreeMap<LanguageType, Vec<Report>>,\n    /// Whether this language had problems with file parsing\n    pub inaccurate: bool,\n}\n\nimpl Language {\n    /// Constructs a new empty Language with the comments provided.\n    ///\n    /// ```\n    /// # use tokei::*;\n    /// let mut rust = Language::new();\n    /// ```\n    #[must_use]\n    pub fn new() -> Self {\n        Self::default()\n    }\n\n    /// Returns the total number of lines.\n    #[inline]\n    #[must_use]\n    pub fn lines(&self) -> usize {\n        self.blanks + self.code + self.comments\n    }\n\n    /// Add a `Report` to the Language. This will not update the totals in the\n    /// Language struct.\n    pub fn add_report(&mut self, report: Report) {\n        for (lang, stats) in &report.stats.blobs {\n            let mut new_report = Report::new(report.name.clone());\n            new_report.stats = stats.clone();\n\n            self.children.entry(*lang).or_default().push(new_report);\n        }\n\n        self.reports.push(report);\n    }\n\n    /// Marks this language as possibly not reflecting correct stats.\n    #[inline]\n    pub fn mark_inaccurate(&mut self) {\n        self.inaccurate = true;\n    }\n\n    /// Creates a new `Language` from `self`, which is a summarised version\n    /// of the language that doesn't contain any children. It will count\n    /// non-blank lines in child languages as code unless the child language is\n    /// considered \"literate\" then it will be counted as comments.\n    #[must_use]\n    pub fn summarise(&self) -> Language {\n        let mut summary = self.clone();\n\n        for reports in self.children.values() {\n            for stats in reports.iter().map(|r| r.stats.summarise()) {\n                summary.comments += stats.comments;\n                summary.code += stats.code;\n                summary.blanks += stats.blanks;\n            }\n        }\n\n        summary\n    }\n\n    /// Totals up the statistics of the `Stat` structs currently contained in\n    /// the language.\n    ///\n    /// ```no_run\n    /// use std::{collections::BTreeMap, path::PathBuf};\n    /// use tokei::Language;\n    ///\n    /// let mut language = Language::new();\n    ///\n    /// // Add stats...\n    ///\n    /// assert_eq!(0, language.lines());\n    ///\n    /// language.total();\n    ///\n    /// assert_eq!(10, language.lines());\n    /// ```\n    pub fn total(&mut self) {\n        let mut blanks = 0;\n        let mut code = 0;\n        let mut comments = 0;\n\n        for report in &self.reports {\n            blanks += report.stats.blanks;\n            code += report.stats.code;\n            comments += report.stats.comments;\n        }\n\n        self.blanks = blanks;\n        self.code = code;\n        self.comments = comments;\n    }\n\n    /// Checks if the language is empty. Empty meaning it doesn't have any\n    /// statistics.\n    ///\n    /// ```\n    /// # use tokei::*;\n    /// let rust = Language::new();\n    ///\n    /// assert!(rust.is_empty());\n    /// ```\n    #[must_use]\n    pub fn is_empty(&self) -> bool {\n        self.code == 0 && self.comments == 0 && self.blanks == 0 && self.children.is_empty()\n    }\n\n    /// Sorts each of the `Report`s contained in the language based\n    /// on what category is provided.\n    ///\n    /// ```no_run\n    /// use std::{collections::BTreeMap, path::PathBuf};\n    /// use tokei::{Language, Sort};\n    ///\n    /// let mut language = Language::new();\n    ///\n    /// // Add stats...\n    ///\n    /// language.sort_by(Sort::Lines);\n    /// assert_eq!(20, language.reports[0].stats.lines());\n    ///\n    /// language.sort_by(Sort::Code);\n    /// assert_eq!(8, language.reports[0].stats.code);\n    /// ```\n    pub fn sort_by(&mut self, category: Sort) {\n        match category {\n            Sort::Blanks => self\n                .reports\n                .sort_by(|a, b| b.stats.blanks.cmp(&a.stats.blanks)),\n            Sort::Comments => self\n                .reports\n                .sort_by(|a, b| b.stats.comments.cmp(&a.stats.comments)),\n            Sort::Code => self.reports.sort_by(|a, b| b.stats.code.cmp(&a.stats.code)),\n            Sort::Files => self.reports.sort_by(|a, b| a.name.cmp(&b.name)),\n            Sort::Lines => self\n                .reports\n                .sort_by(|a, b| b.stats.lines().cmp(&a.stats.lines())),\n        }\n    }\n}\n\nimpl AddAssign for Language {\n    fn add_assign(&mut self, mut rhs: Self) {\n        self.comments += rhs.comments;\n        self.blanks += rhs.blanks;\n        self.code += rhs.code;\n        self.reports.extend(mem::take(&mut rhs.reports));\n        self.children.extend(mem::take(&mut rhs.children));\n        self.inaccurate |= rhs.inaccurate;\n    }\n}\n"
  },
  {
    "path": "src/language/syntax.rs",
    "content": "use std::sync::Arc;\n\nuse aho_corasick::AhoCorasick;\nuse dashmap::DashMap;\nuse grep_searcher::LineStep;\nuse log::Level::Trace;\nuse once_cell::sync::Lazy;\n\nuse super::embedding::{\n    RegexCache, RegexFamily, ENDING_LF_BLOCK_REGEX, ENDING_MARKDOWN_REGEX, END_SCRIPT, END_STYLE,\n    END_TEMPLATE,\n};\nuse crate::LanguageType::LinguaFranca;\nuse crate::{stats::CodeStats, utils::ext::SliceExt, Config, LanguageType};\n\n/// Tracks the syntax of the language as well as the current state in the file.\n/// Current has what could be consider three types of mode.\n/// - `plain` mode: This is the normal state, blanks are counted as blanks,\n///   string literals can trigger `string` mode, and comments can trigger\n///   `comment` mode.\n/// - `string` mode: This when the state machine is current inside a string\n///   literal for a given language, comments cannot trigger `comment` mode while\n///   in `string` mode.\n/// - `comment` mode: This when the state machine is current inside a comment\n///   for a given language, strings cannot trigger `string` mode while in\n///   `comment` mode.\n#[derive(Clone, Debug)]\npub(crate) struct SyntaxCounter {\n    pub(crate) shared: Arc<SharedMatchers>,\n    pub(crate) quote: Option<&'static str>,\n    pub(crate) quote_is_doc_quote: bool,\n    pub(crate) stack: Vec<&'static str>,\n    pub(crate) quote_is_verbatim: bool,\n    pub(crate) lf_embedded_language: Option<LanguageType>,\n}\n\n#[derive(Clone, Debug)]\npub(crate) struct FileContext {\n    pub(crate) language: LanguageContext,\n    pub(crate) stats: CodeStats,\n    pub(crate) end: usize,\n}\n\nimpl FileContext {\n    pub fn new(language: LanguageContext, end: usize, stats: CodeStats) -> Self {\n        Self {\n            language,\n            stats,\n            end,\n        }\n    }\n}\n\n#[derive(Clone, Debug)]\npub(crate) enum LanguageContext {\n    Html {\n        language: LanguageType,\n    },\n    LinguaFranca,\n    Markdown {\n        balanced: bool,\n        language: LanguageType,\n    },\n    Rust,\n}\n\n#[derive(Clone, Debug)]\npub(crate) struct SharedMatchers {\n    pub language: LanguageType,\n    pub allows_nested: bool,\n    pub doc_quotes: &'static [(&'static str, &'static str)],\n    pub important_syntax: AhoCorasick,\n    #[allow(dead_code)]\n    pub any_comments: &'static [&'static str],\n    pub is_fortran: bool,\n    pub is_literate: bool,\n    pub line_comments: &'static [&'static str],\n    pub any_multi_line_comments: &'static [(&'static str, &'static str)],\n    pub multi_line_comments: &'static [(&'static str, &'static str)],\n    pub nested_comments: &'static [(&'static str, &'static str)],\n    pub string_literals: &'static [(&'static str, &'static str)],\n    pub verbatim_string_literals: &'static [(&'static str, &'static str)],\n}\n\nimpl SharedMatchers {\n    pub fn new(language: LanguageType) -> Arc<Self> {\n        static MATCHERS: Lazy<DashMap<LanguageType, Arc<SharedMatchers>>> = Lazy::new(DashMap::new);\n\n        MATCHERS\n            .entry(language)\n            .or_insert_with(|| Arc::new(Self::init(language)))\n            .value()\n            .clone()\n    }\n\n    pub fn init(language: LanguageType) -> Self {\n        fn init_corasick(pattern: &[&'static str]) -> AhoCorasick {\n            AhoCorasick::builder()\n                .match_kind(aho_corasick::MatchKind::LeftmostLongest)\n                .start_kind(aho_corasick::StartKind::Unanchored)\n                .prefilter(true)\n                .kind(Some(aho_corasick::AhoCorasickKind::DFA))\n                .build(pattern)\n                .unwrap()\n        }\n\n        Self {\n            language,\n            allows_nested: language.allows_nested(),\n            doc_quotes: language.doc_quotes(),\n            is_fortran: language.is_fortran(),\n            is_literate: language.is_literate(),\n            important_syntax: init_corasick(language.important_syntax()),\n            any_comments: language.any_comments(),\n            line_comments: language.line_comments(),\n            multi_line_comments: language.multi_line_comments(),\n            any_multi_line_comments: language.any_multi_line_comments(),\n            nested_comments: language.nested_comments(),\n            string_literals: language.quotes(),\n            verbatim_string_literals: language.verbatim_quotes(),\n        }\n    }\n}\n\n#[derive(Debug)]\npub(crate) enum AnalysisReport {\n    /// No child languages were found, contains a boolean representing whether\n    /// the line ended with comments or not.\n    Normal(bool),\n    ChildLanguage(FileContext),\n}\n\nimpl SyntaxCounter {\n    pub(crate) fn new(language: LanguageType) -> Self {\n        Self {\n            shared: SharedMatchers::new(language),\n            quote_is_doc_quote: false,\n            quote_is_verbatim: false,\n            stack: Vec::with_capacity(1),\n            lf_embedded_language: None,\n            quote: None,\n        }\n    }\n\n    /// Returns whether the syntax is currently in plain mode.\n    pub(crate) fn is_plain_mode(&self) -> bool {\n        self.quote.is_none() && self.stack.is_empty()\n    }\n\n    /// Returns whether the syntax is currently in string mode.\n    pub(crate) fn _is_string_mode(&self) -> bool {\n        self.quote.is_some()\n    }\n\n    /// Returns whether the syntax is currently in comment mode.\n    pub(crate) fn _is_comment_mode(&self) -> bool {\n        !self.stack.is_empty()\n    }\n\n    pub(crate) fn get_lf_target_language(&self) -> LanguageType {\n        // in case the target declaration was not found, default it to that language\n        const DEFAULT_LANG: LanguageType = LinguaFranca;\n        self.lf_embedded_language.unwrap_or(DEFAULT_LANG)\n    }\n\n    #[inline]\n    pub(crate) fn parse_line_comment(&self, window: &[u8]) -> bool {\n        if self.quote.is_some() || !self.stack.is_empty() {\n            false\n        } else if let Some(comment) = self\n            .shared\n            .line_comments\n            .iter()\n            .find(|c| window.starts_with(c.as_bytes()))\n        {\n            trace!(\"Start {:?}\", comment);\n            true\n        } else {\n            false\n        }\n    }\n\n    /// Try to see if we can determine what a line is from examining the whole\n    /// line at once. Returns `true` if successful.\n    pub(crate) fn try_perform_single_line_analysis(\n        &self,\n        line: &[u8],\n        stats: &mut crate::stats::CodeStats,\n    ) -> bool {\n        if !self.is_plain_mode() {\n            false\n        } else if line.trim().is_empty() {\n            stats.blanks += 1;\n            trace!(\"Blank No.{}\", stats.blanks);\n            true\n        } else if self.shared.important_syntax.is_match(line) {\n            false\n        } else {\n            trace!(\"^ Skippable\");\n\n            if self.shared.is_literate\n                || self\n                    .shared\n                    .line_comments\n                    .iter()\n                    .any(|c| line.starts_with(c.as_bytes()))\n            {\n                stats.comments += 1;\n                trace!(\"Comment No.{}\", stats.comments);\n            } else {\n                stats.code += 1;\n                trace!(\"Code No.{}\", stats.code);\n            }\n\n            true\n        }\n    }\n\n    pub(crate) fn perform_multi_line_analysis(\n        &mut self,\n        lines: &[u8],\n        start: usize,\n        end: usize,\n        config: &Config,\n    ) -> AnalysisReport {\n        let mut ended_with_comments = false;\n        let mut skip = 0;\n        macro_rules! skip {\n            ($skip:expr) => {{\n                skip = $skip - 1;\n            }};\n        }\n\n        let regex_cache = RegexCache::build(self.shared.language, lines, start, end);\n\n        for i in start..end {\n            if skip != 0 {\n                skip -= 1;\n                continue;\n            }\n\n            let window = &lines[i..];\n\n            if window.trim().is_empty() {\n                break;\n            }\n\n            ended_with_comments = false;\n            let is_end_of_quote_or_multi_line = self\n                .parse_end_of_quote(window)\n                .or_else(|| self.parse_end_of_multi_line(window));\n\n            if let Some(skip_amount) = is_end_of_quote_or_multi_line {\n                ended_with_comments = true;\n                skip!(skip_amount);\n                continue;\n            } else if self.quote.is_some() {\n                continue;\n            }\n\n            if let Some(child) = self.parse_context(lines, i, end, config, &regex_cache) {\n                return AnalysisReport::ChildLanguage(child);\n            }\n\n            let is_quote_or_multi_line = self\n                .parse_quote(window)\n                .or_else(|| self.parse_multi_line_comment(window));\n\n            if let Some(skip_amount) = is_quote_or_multi_line {\n                skip!(skip_amount);\n                continue;\n            }\n\n            if self.parse_line_comment(window) {\n                ended_with_comments = true;\n                break;\n            }\n        }\n\n        AnalysisReport::Normal(ended_with_comments)\n    }\n\n    /// Performs a set of heuristics to determine whether a line is a comment or\n    /// not. The procedure is as follows.\n    ///\n    /// - Yes/No: Counted as Comment\n    ///\n    /// 1. Check if we're in string mode\n    ///  1. Check if string literal is a doc string and whether tokei has\n    ///     been configured to treat them as comments.\n    ///     - Yes: When the line starts with the doc string or when we are\n    ///            continuing from a previous line.\n    ///  - No: The string is a normal string literal or tokei isn't\n    ///        configured to count them as comments.\n    /// 2. If we're not in string mode, check if we left it this on this line.\n    ///    - Yes: When we found a doc quote and we started in comments.\n    /// 3. Yes: When the whole line is a comment e.g. `/* hello */`\n    /// 4. Yes: When the previous line started a multi-line comment.\n    /// 5. Yes: When the line starts with a comment.\n    /// 6. No: Any other input.\n    pub(crate) fn line_is_comment(\n        &self,\n        line: &[u8],\n        config: &crate::Config,\n        _ended_with_comments: bool,\n        started_in_comments: bool,\n    ) -> bool {\n        let trimmed = line.trim();\n        let whole_line_is_comment = || {\n            self.shared\n                .line_comments\n                .iter()\n                .any(|c| trimmed.starts_with(c.as_bytes()))\n                || self\n                    .shared\n                    .any_multi_line_comments\n                    .iter()\n                    .any(|(start, end)| {\n                        trimmed.starts_with(start.as_bytes()) && trimmed.ends_with(end.as_bytes())\n                    })\n        };\n        let starts_with_comment = || {\n            let quote = match self.stack.last() {\n                Some(q) => q,\n                _ => return false,\n            };\n\n            self.shared\n                .any_multi_line_comments\n                .iter()\n                .any(|(start, end)| end == quote && trimmed.starts_with(start.as_bytes()))\n        };\n\n        // `Some(true)` in order to respect the current configuration.\n        #[allow(clippy::if_same_then_else)]\n        if self.quote.is_some() {\n            if self.quote_is_doc_quote && config.treat_doc_strings_as_comments == Some(true) {\n                self.quote.map_or(false, |q| line.starts_with(q.as_bytes()))\n                    || (self.quote.is_some())\n            } else {\n                false\n            }\n        } else if self\n            .shared\n            .doc_quotes\n            .iter()\n            .any(|(_, e)| line.contains_slice(e.as_bytes()))\n            && started_in_comments\n        {\n            true\n        } else if (whole_line_is_comment)() {\n            true\n        } else if started_in_comments {\n            true\n        } else {\n            (starts_with_comment)()\n        }\n    }\n\n    #[inline]\n    pub(crate) fn parse_context(\n        &mut self,\n        lines: &[u8],\n        start: usize,\n        end: usize,\n        config: &Config,\n        regex_cache: &RegexCache,\n    ) -> Option<FileContext> {\n        use std::str::FromStr;\n\n        // static TYPE_REGEX: Lazy<Regex> = Lazy::new(|| Regex::new(r#\"type=\"(.*)\".*>\"#).unwrap());\n        if self.quote.is_some() || !self.stack.is_empty() {\n            return None;\n        }\n\n        match regex_cache.family()? {\n            RegexFamily::Markdown(md) => {\n                if !lines[start..end].contains_slice(b\"```\") {\n                    return None;\n                }\n\n                let opening_fence = md.starts_in_range(start, end)?;\n                let start_of_code = opening_fence.end();\n                let closing_fence = ENDING_MARKDOWN_REGEX.find(&lines[start_of_code..]);\n                if let Some(m) = &closing_fence {\n                    trace!(\"{:?}\", String::from_utf8_lossy(m.as_bytes()));\n                }\n                let end_of_code = closing_fence\n                    .map_or_else(|| lines.len(), |fence| start_of_code + fence.start());\n                let end_of_code_block =\n                    closing_fence.map_or_else(|| lines.len(), |fence| start_of_code + fence.end());\n                let balanced = closing_fence.is_some();\n                let identifier = &opening_fence.as_bytes().trim()[3..];\n\n                let language = identifier\n                    .split(|&b| b == b',')\n                    .find_map(|s| LanguageType::from_str(&String::from_utf8_lossy(s)).ok())?;\n                trace!(\n                    \"{} BLOCK: {:?}\",\n                    language,\n                    String::from_utf8_lossy(&lines[start_of_code..end_of_code])\n                );\n                let stats =\n                    language.parse_from_slice(lines[start_of_code..end_of_code].trim(), config);\n\n                Some(FileContext::new(\n                    LanguageContext::Markdown { balanced, language },\n                    end_of_code_block,\n                    stats,\n                ))\n            }\n            RegexFamily::Rust => {\n                let rest = &lines[start..];\n                let comment_syntax = if rest.trim_start().starts_with(b\"///\") {\n                    b\"///\"\n                } else if rest.trim_start().starts_with(b\"//!\") {\n                    b\"//!\"\n                } else {\n                    return None;\n                };\n\n                let mut stepper = LineStep::new(b'\\n', start, lines.len());\n                let mut markdown = Vec::new();\n                let mut end_of_block = lines.len();\n\n                while let Some((start, end)) = stepper.next(lines) {\n                    if lines[start..].trim().starts_with(comment_syntax) {\n                        trace!(\"{}\", String::from_utf8_lossy(&lines[start..end]));\n                        let line = lines[start..end].trim_start();\n                        let stripped_line = &line[3.min(line.len())..];\n                        markdown.extend_from_slice(stripped_line);\n                        end_of_block = end;\n                    } else {\n                        end_of_block = start;\n                        break;\n                    }\n                }\n\n                trace!(\"Markdown found: {:?}\", String::from_utf8_lossy(&markdown));\n                let doc_block = LanguageType::Markdown.parse_from_slice(markdown.trim(), config);\n\n                Some(FileContext::new(\n                    LanguageContext::Rust,\n                    end_of_block,\n                    doc_block,\n                ))\n            }\n            RegexFamily::LinguaFranca(lf) => {\n                let opening_fence = lf.starts_in_range(start, end)?;\n                let start_of_code = opening_fence.end();\n                let closing_fence = ENDING_LF_BLOCK_REGEX.find(&lines[start_of_code..]);\n                let end_of_code = closing_fence\n                    .map_or_else(|| lines.len(), |fence| start_of_code + fence.start());\n\n                let block_contents = &lines[start_of_code..end_of_code];\n                trace!(\"LF block: {:?}\", String::from_utf8_lossy(block_contents));\n                let stats = self.get_lf_target_language().parse_from_slice(\n                    block_contents.trim_first_and_last_line_of_whitespace(),\n                    config,\n                );\n                trace!(\"-> stats: {:?}\", stats);\n\n                Some(FileContext::new(\n                    LanguageContext::LinguaFranca,\n                    end_of_code,\n                    stats,\n                ))\n            }\n            RegexFamily::HtmlLike(html) => {\n                if let Some(mut captures) = html.start_script_in_range(start, end) {\n                    let start_of_code = captures.next().unwrap().end();\n                    let closing_tag = END_SCRIPT.find(&lines[start_of_code..])?;\n                    let end_of_code = start_of_code + closing_tag.start();\n                    let language = captures\n                        .next()\n                        .and_then(|m| {\n                            LanguageType::from_mime(&String::from_utf8_lossy(m.as_bytes().trim()))\n                        })\n                        .unwrap_or(LanguageType::JavaScript);\n                    let script_contents = &lines[start_of_code..end_of_code];\n                    if script_contents.trim().is_empty() {\n                        return None;\n                    }\n\n                    let stats = language.parse_from_slice(\n                        script_contents.trim_first_and_last_line_of_whitespace(),\n                        config,\n                    );\n                    Some(FileContext::new(\n                        LanguageContext::Html { language },\n                        end_of_code,\n                        stats,\n                    ))\n                } else if let Some(mut captures) = html.start_style_in_range(start, end) {\n                    let start_of_code = captures.next().unwrap().end();\n                    let closing_tag = END_STYLE.find(&lines[start_of_code..])?;\n                    let end_of_code = start_of_code + closing_tag.start();\n                    let language = captures\n                        .next()\n                        .and_then(|m| {\n                            LanguageType::from_str(\n                                &String::from_utf8_lossy(m.as_bytes().trim()).to_lowercase(),\n                            )\n                            .ok()\n                        })\n                        .unwrap_or(LanguageType::Css);\n                    let style_contents = &lines[start_of_code..end_of_code];\n                    if style_contents.trim().is_empty() {\n                        return None;\n                    }\n\n                    let stats = language.parse_from_slice(\n                        style_contents.trim_first_and_last_line_of_whitespace(),\n                        config,\n                    );\n                    Some(FileContext::new(\n                        LanguageContext::Html { language },\n                        end_of_code,\n                        stats,\n                    ))\n                } else if let Some(mut captures) = html.start_template_in_range(start, end) {\n                    let start_of_code = captures.next().unwrap().end();\n                    let closing_tag = END_TEMPLATE.find(&lines[start_of_code..])?;\n                    let end_of_code = start_of_code + closing_tag.start();\n                    let language = captures\n                        .next()\n                        .and_then(|m| {\n                            LanguageType::from_str(\n                                &String::from_utf8_lossy(m.as_bytes().trim()).to_lowercase(),\n                            )\n                            .ok()\n                        })\n                        .unwrap_or(LanguageType::Html);\n\n                    let template_contents = &lines[start_of_code..end_of_code];\n                    if template_contents.trim().is_empty() {\n                        return None;\n                    }\n                    let stats = language.parse_from_slice(\n                        template_contents.trim_first_and_last_line_of_whitespace(),\n                        config,\n                    );\n                    Some(FileContext::new(\n                        LanguageContext::Html { language },\n                        end_of_code,\n                        stats,\n                    ))\n                } else {\n                    None\n                }\n            }\n        }\n    }\n\n    #[inline]\n    pub(crate) fn parse_quote(&mut self, window: &[u8]) -> Option<usize> {\n        if !self.stack.is_empty() {\n            return None;\n        }\n\n        if let Some((start, end)) = self\n            .shared\n            .doc_quotes\n            .iter()\n            .find(|(s, _)| window.starts_with(s.as_bytes()))\n        {\n            trace!(\"Start Doc {:?}\", start);\n            self.quote = Some(end);\n            self.quote_is_verbatim = false;\n            self.quote_is_doc_quote = true;\n            return Some(start.len());\n        }\n\n        if let Some((start, end)) = self\n            .shared\n            .verbatim_string_literals\n            .iter()\n            .find(|(s, _)| window.starts_with(s.as_bytes()))\n        {\n            trace!(\"Start verbatim {:?}\", start);\n            self.quote = Some(end);\n            self.quote_is_verbatim = true;\n            self.quote_is_doc_quote = false;\n            return Some(start.len());\n        }\n\n        if let Some((start, end)) = self\n            .shared\n            .string_literals\n            .iter()\n            .find(|(s, _)| window.starts_with(s.as_bytes()))\n        {\n            trace!(\"Start {:?}\", start);\n            self.quote = Some(end);\n            self.quote_is_verbatim = false;\n            self.quote_is_doc_quote = false;\n            return Some(start.len());\n        }\n\n        None\n    }\n\n    #[inline]\n    pub(crate) fn parse_end_of_quote(&mut self, window: &[u8]) -> Option<usize> {\n        #[allow(clippy::if_same_then_else)]\n        if self._is_string_mode() && window.starts_with(self.quote?.as_bytes()) {\n            let quote = self.quote.take().unwrap();\n            trace!(\"End {:?}\", quote);\n            Some(quote.len())\n        } else if !self.quote_is_verbatim && window.starts_with(br\"\\\\\") {\n            Some(2)\n        } else if !self.quote_is_verbatim\n            && window.starts_with(br\"\\\")\n            && self\n                .shared\n                .string_literals\n                .iter()\n                .any(|(start, _)| window[1..].starts_with(start.as_bytes()))\n        {\n            // Tell the state machine to skip the next character because it\n            // has been escaped if the string isn't a verbatim string.\n            Some(2)\n        } else {\n            None\n        }\n    }\n\n    #[inline]\n    pub(crate) fn parse_multi_line_comment(&mut self, window: &[u8]) -> Option<usize> {\n        if self.quote.is_some() {\n            return None;\n        }\n\n        let iter = self\n            .shared\n            .multi_line_comments\n            .iter()\n            .chain(self.shared.nested_comments);\n        for &(start, end) in iter {\n            if window.starts_with(start.as_bytes()) {\n                if self.stack.is_empty()\n                    || self.shared.allows_nested\n                    || self.shared.nested_comments.contains(&(start, end))\n                {\n                    self.stack.push(end);\n\n                    if log_enabled!(Trace) && self.shared.allows_nested {\n                        trace!(\"Start nested {:?}\", start);\n                    } else {\n                        trace!(\"Start {:?}\", start);\n                    }\n                }\n\n                return Some(start.len());\n            }\n        }\n\n        None\n    }\n\n    #[inline]\n    pub(crate) fn parse_end_of_multi_line(&mut self, window: &[u8]) -> Option<usize> {\n        if self\n            .stack\n            .last()\n            .map_or(false, |l| window.starts_with(l.as_bytes()))\n        {\n            let last = self.stack.pop().unwrap();\n\n            if log_enabled!(Trace) {\n                if self.stack.is_empty() {\n                    trace!(\"End {:?}\", last);\n                } else {\n                    trace!(\"End {:?}. Still in comments.\", last);\n                }\n            }\n\n            Some(last.len())\n        } else {\n            None\n        }\n    }\n}\n"
  },
  {
    "path": "src/lib.rs",
    "content": "//! # Tokei: Count your code quickly.\n//!\n//! A simple, efficient library for counting code in directories. This\n//! functionality is also provided as a\n//! [CLI utility](//github.com/XAMPPRocky/tokei). Tokei uses a small state\n//! machine rather than regular expressions found in other code counters. Tokei\n//! can accurately count a lot more edge cases such as nested comments, or\n//! comment syntax inside string literals.\n//!\n//! # Examples\n//!\n//! Gets the total lines of code from all rust files in current directory,\n//! and all subdirectories.\n//!\n//! ```no_run\n//! use std::collections::BTreeMap;\n//! use std::fs::File;\n//! use std::io::Read;\n//!\n//! use tokei::{Config, Languages, LanguageType};\n//!\n//! // The paths to search. Accepts absolute, relative, and glob paths.\n//! let paths = &[\"src\", \"tests\"];\n//! // Exclude any path that contains any of these strings.\n//! let excluded = &[\"target\"];\n//! // `Config` allows you to configure what is searched and counted.\n//! let config = Config::default();\n//!\n//! let mut languages = Languages::new();\n//! languages.get_statistics(paths, excluded, &config);\n//! let rust = &languages[&LanguageType::Rust];\n//!\n//! println!(\"Lines of code: {}\", rust.code);\n//! ```\n\n#![deny(\n    trivial_casts,\n    trivial_numeric_casts,\n    unused_variables,\n    unstable_features,\n    unused_import_braces,\n    missing_docs\n)]\n\n#[macro_use]\nextern crate log;\n#[macro_use]\nextern crate serde;\n\n#[macro_use]\nmod utils;\nmod config;\nmod consts;\nmod language;\nmod sort;\nmod stats;\n\npub use self::{\n    config::Config,\n    consts::*,\n    language::{Language, LanguageType, Languages},\n    sort::Sort,\n    stats::{find_char_boundary, CodeStats, Report},\n};\n"
  },
  {
    "path": "src/main.rs",
    "content": "#[macro_use]\nextern crate log;\n\nmod cli;\nmod cli_utils;\nmod consts;\nmod input;\n\nuse std::{error::Error, io, process};\n\nuse tokei::{Config, Languages, Sort};\n\nuse crate::{\n    cli::Cli,\n    cli_utils::Printer,\n    consts::{\n        BLANKS_COLUMN_WIDTH, CODE_COLUMN_WIDTH, COMMENTS_COLUMN_WIDTH, FALLBACK_ROW_LEN,\n        LANGUAGE_COLUMN_WIDTH, LINES_COLUMN_WIDTH, PATH_COLUMN_WIDTH,\n    },\n    input::add_input,\n};\n\nfn main() -> Result<(), Box<dyn Error>> {\n    let mut cli = Cli::from_args();\n\n    if cli.print_languages {\n        Cli::print_supported_languages()?;\n        process::exit(0);\n    }\n    let config = cli.override_config(Config::from_config_files());\n    let mut languages = Languages::new();\n\n    if let Some(input) = cli.file_input() {\n        if !add_input(input, &mut languages) {\n            Cli::print_input_parse_failure(input);\n            process::exit(1);\n        }\n    }\n\n    let input = cli.input();\n\n    for path in &input {\n        if ::std::fs::metadata(path).is_err() {\n            eprintln!(\"Error: '{}' not found.\", path);\n            process::exit(1);\n        }\n    }\n\n    let columns = cli\n        .columns\n        .or(config.columns)\n        .or_else(|| {\n            if cli.files {\n                term_size::dimensions().map(|(w, _)| w)\n            } else {\n                None\n            }\n        })\n        .unwrap_or(FALLBACK_ROW_LEN)\n        .max(FALLBACK_ROW_LEN);\n\n    if cli.streaming == Some(crate::cli::Streaming::Simple) {\n        println!(\n            \"#{:^LANGUAGE_COLUMN_WIDTH$} {:^PATH_COLUMN_WIDTH$} {:^LINES_COLUMN_WIDTH$} {:^CODE_COLUMN_WIDTH$} {:^COMMENTS_COLUMN_WIDTH$} {:^BLANKS_COLUMN_WIDTH$}\",\n            \"language\", \"path\", \"lines\", \"code\", \"comments\", \"blanks\"\n        );\n        println!(\n            \"{:>LANGUAGE_COLUMN_WIDTH$} {:<PATH_COLUMN_WIDTH$} {:>LINES_COLUMN_WIDTH$} {:>CODE_COLUMN_WIDTH$} {:>COMMENTS_COLUMN_WIDTH$} {:>BLANKS_COLUMN_WIDTH$}\",\n            (0..10).map(|_| \"#\").collect::<String>(),\n            (0..80).map(|_| \"#\").collect::<String>(),\n            (0..12).map(|_| \"#\").collect::<String>(),\n            (0..12).map(|_| \"#\").collect::<String>(),\n            (0..12).map(|_| \"#\").collect::<String>(),\n            (0..12).map(|_| \"#\").collect::<String>()\n        );\n    }\n\n    languages.get_statistics(&input, &cli.ignored_directories(), &config);\n    if config.for_each_fn.is_some() {\n        process::exit(0);\n    }\n\n    if let Some(format) = cli.output {\n        print!(\"{}\", format.print(&languages).unwrap());\n        process::exit(0);\n    }\n\n    let mut printer = Printer::new(\n        columns,\n        cli.files,\n        io::BufWriter::new(io::stdout()),\n        cli.number_format,\n    );\n\n    if languages.iter().any(|(_, lang)| lang.inaccurate) {\n        printer.print_inaccuracy_warning()?;\n    }\n\n    printer.print_header()?;\n\n    let mut is_sorted = false;\n    if let Some(sort_category) = cli.sort.or(config.sort) {\n        for (_, ref mut language) in &mut languages {\n            language.sort_by(sort_category);\n        }\n\n        let mut languages: Vec<_> = languages.iter().collect();\n        match sort_category {\n            Sort::Blanks => languages.sort_by(|a, b| b.1.blanks.cmp(&a.1.blanks)),\n            Sort::Comments => languages.sort_by(|a, b| b.1.comments.cmp(&a.1.comments)),\n            Sort::Code => languages.sort_by(|a, b| b.1.code.cmp(&a.1.code)),\n            Sort::Files => languages.sort_by(|a, b| b.1.reports.len().cmp(&a.1.reports.len())),\n            Sort::Lines => languages.sort_by(|a, b| b.1.lines().cmp(&a.1.lines())),\n        }\n        is_sorted = true;\n        if cli.sort_reverse {\n            printer.print_results(languages.into_iter().rev(), cli.compact, is_sorted)?;\n        } else {\n            printer.print_results(languages.into_iter(), cli.compact, is_sorted)?;\n        }\n    } else {\n        printer.print_results(languages.iter(), cli.compact, is_sorted)?;\n    }\n\n    printer.print_total(&languages)?;\n\n    Ok(())\n}\n"
  },
  {
    "path": "src/sort.rs",
    "content": "use std::{borrow::Cow, str::FromStr};\n\nuse serde::de::{self, Deserialize, Deserializer};\n\n/// Used for sorting languages.\n#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]\npub enum Sort {\n    /// Sort by number blank lines.\n    Blanks,\n    /// Sort by number comments lines.\n    Comments,\n    /// Sort by number code lines.\n    Code,\n    /// Sort by number files lines.\n    Files,\n    /// Sort by number of lines.\n    Lines,\n}\n\nimpl FromStr for Sort {\n    type Err = String;\n\n    fn from_str(s: &str) -> Result<Self, Self::Err> {\n        Ok(if s.eq_ignore_ascii_case(\"blanks\") {\n            Sort::Blanks\n        } else if s.eq_ignore_ascii_case(\"comments\") {\n            Sort::Comments\n        } else if s.eq_ignore_ascii_case(\"code\") {\n            Sort::Code\n        } else if s.eq_ignore_ascii_case(\"files\") {\n            Sort::Files\n        } else if s.eq_ignore_ascii_case(\"lines\") {\n            Sort::Lines\n        } else {\n            return Err(format!(\"Unsupported sorting option: {}\", s));\n        })\n    }\n}\n\nimpl<'de> Deserialize<'de> for Sort {\n    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>\n    where\n        D: Deserializer<'de>,\n    {\n        String::deserialize(deserializer)?\n            .parse()\n            .map_err(de::Error::custom)\n    }\n}\n\nimpl<'a> From<Sort> for Cow<'a, Sort> {\n    fn from(from: Sort) -> Self {\n        Cow::Owned(from)\n    }\n}\n\nimpl<'a> From<&'a Sort> for Cow<'a, Sort> {\n    fn from(from: &'a Sort) -> Self {\n        Cow::Borrowed(from)\n    }\n}\n"
  },
  {
    "path": "src/stats.rs",
    "content": "use crate::consts::{\n    BLANKS_COLUMN_WIDTH, CODE_COLUMN_WIDTH, COMMENTS_COLUMN_WIDTH, LINES_COLUMN_WIDTH,\n};\nuse crate::LanguageType;\nuse std::{collections::BTreeMap, fmt, ops, path::PathBuf};\n\n/// A struct representing stats about a single blob of code.\n#[derive(Clone, Debug, Default, PartialEq, serde::Deserialize, serde::Serialize)]\n#[non_exhaustive]\npub struct CodeStats {\n    /// The blank lines in the blob.\n    pub blanks: usize,\n    /// The lines of code in the blob.\n    pub code: usize,\n    /// The lines of comments in the blob.\n    pub comments: usize,\n    /// Language blobs that were contained inside this blob.\n    pub blobs: BTreeMap<LanguageType, CodeStats>,\n}\n\nimpl CodeStats {\n    /// Creates a new blank `CodeStats`.\n    #[must_use]\n    pub fn new() -> Self {\n        Self::default()\n    }\n\n    /// Get the total lines in a blob of code.\n    #[must_use]\n    pub fn lines(&self) -> usize {\n        self.blanks + self.code + self.comments\n    }\n\n    /// Creates a new `CodeStats` from an existing one with all of the child\n    /// blobs merged.\n    #[must_use]\n    pub fn summarise(&self) -> Self {\n        let mut summary = self.clone();\n\n        for (_, stats) in std::mem::take(&mut summary.blobs) {\n            let child_summary = stats.summarise();\n\n            summary.blanks += child_summary.blanks;\n            summary.comments += child_summary.comments;\n            summary.code += child_summary.code;\n        }\n\n        summary\n    }\n}\n\nimpl ops::AddAssign for CodeStats {\n    fn add_assign(&mut self, rhs: Self) {\n        self.add_assign(&rhs);\n    }\n}\n\nimpl ops::AddAssign<&'_ CodeStats> for CodeStats {\n    fn add_assign(&mut self, rhs: &'_ CodeStats) {\n        self.blanks += rhs.blanks;\n        self.code += rhs.code;\n        self.comments += rhs.comments;\n\n        for (language, stats) in &rhs.blobs {\n            *self.blobs.entry(*language).or_default() += stats;\n        }\n    }\n}\n\n/// A struct representing the statistics of a file.\n#[derive(Deserialize, Serialize, Clone, Debug, Default, PartialEq)]\n#[non_exhaustive]\npub struct Report {\n    /// The code statistics found in the file.\n    pub stats: CodeStats,\n    /// File name.\n    pub name: PathBuf,\n}\n\nimpl Report {\n    /// Create a new `Report` from a [`PathBuf`].\n    ///\n    /// [`PathBuf`]: //doc.rust-lang.org/std/path/struct.PathBuf.html\n    #[must_use]\n    pub fn new(name: PathBuf) -> Self {\n        Self {\n            name,\n            ..Self::default()\n        }\n    }\n}\n\nimpl ops::AddAssign<CodeStats> for Report {\n    fn add_assign(&mut self, rhs: CodeStats) {\n        self.stats += rhs;\n    }\n}\n\n#[doc(hidden)]\n#[must_use]\npub fn find_char_boundary(s: &str, index: usize) -> usize {\n    for i in 0..4 {\n        if s.is_char_boundary(index + i) {\n            return index + i;\n        }\n    }\n    unreachable!();\n}\n\nmacro_rules! display_stats {\n    ($f:expr, $this:expr, $name:expr, $max:expr) => {\n        write!(\n            $f,\n            \" {: <max$} {:>LINES_COLUMN_WIDTH$} {:>CODE_COLUMN_WIDTH$} {:>COMMENTS_COLUMN_WIDTH$} {:>BLANKS_COLUMN_WIDTH$}\",\n            $name,\n            $this.stats.lines(),\n            $this.stats.code,\n            $this.stats.comments,\n            $this.stats.blanks,\n            max = $max\n        )\n    };\n}\n\nimpl fmt::Display for Report {\n    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n        let name = self.name.to_string_lossy();\n        let name_length = name.len();\n\n        // Added 2 to max length to cover wider Files column (see https://github.com/XAMPPRocky/tokei/issues/891).\n        let max_len = f.width().unwrap_or(27) + 2;\n\n        if name_length <= max_len {\n            display_stats!(f, self, name, max_len)\n        } else {\n            let mut formatted = String::from(\"|\");\n            // Add 1 to the index to account for the '|' we add to the output string\n            let from = find_char_boundary(&name, name_length + 1 - max_len);\n            formatted.push_str(&name[from..]);\n            display_stats!(f, self, formatted, max_len)\n        }\n    }\n}\n"
  },
  {
    "path": "src/utils/ext.rs",
    "content": "//! Various extensions to Rust std types.\n\npub(crate) trait AsciiExt {\n    fn is_whitespace(&self) -> bool;\n    fn is_line_ending_whitespace(&self) -> bool;\n}\n\nimpl AsciiExt for u8 {\n    fn is_whitespace(&self) -> bool {\n        *self == b' ' || (b'\\x09'..=b'\\x0d').contains(self)\n    }\n\n    fn is_line_ending_whitespace(&self) -> bool {\n        *self == b'\\n'\n    }\n}\n\npub(crate) trait SliceExt {\n    fn trim_first_and_last_line_of_whitespace(&self) -> &Self;\n    fn trim_start(&self) -> &Self;\n    fn trim(&self) -> &Self;\n    fn contains_slice(&self, needle: &Self) -> bool;\n}\n\nimpl SliceExt for [u8] {\n    fn trim_first_and_last_line_of_whitespace(&self) -> &Self {\n        let start = self\n            .iter()\n            .position(|c| c.is_line_ending_whitespace() || !c.is_whitespace())\n            .map_or(0, |i| (i + 1).min(self.len().saturating_sub(1)));\n\n        let end = self\n            .iter()\n            .rposition(|c| c.is_line_ending_whitespace() || !c.is_whitespace())\n            .map_or_else(\n                || self.len().saturating_sub(1),\n                |i| {\n                    // Remove the entire `\\r\\n` in the case that it was the line ending whitespace\n                    if self[i.saturating_sub(1)] == b'\\r' && self[i] == b'\\n' {\n                        i - 1\n                    } else {\n                        i\n                    }\n                },\n            );\n\n        if self[start..].is_empty() {\n            return &[];\n        }\n\n        &self[start..=end]\n    }\n\n    fn trim_start(&self) -> &Self {\n        let length = self.len();\n\n        if length == 0 {\n            return self;\n        }\n\n        let start = match self.iter().position(|c| !c.is_whitespace()) {\n            Some(start) => start,\n            None => return &[],\n        };\n\n        &self[start..]\n    }\n\n    fn trim(&self) -> &Self {\n        let length = self.len();\n\n        if length == 0 {\n            return self;\n        }\n\n        let start = match self.iter().position(|c| !c.is_whitespace()) {\n            Some(start) => start,\n            None => return &[],\n        };\n\n        let end = match self.iter().rposition(|c| !c.is_whitespace()) {\n            Some(end) => end.max(start),\n            _ => length,\n        };\n\n        &self[start..=end]\n    }\n\n    fn contains_slice(&self, needle: &Self) -> bool {\n        let self_length = self.len();\n        let needle_length = needle.len();\n\n        if needle_length == 0 || needle_length > self_length {\n            return false;\n        } else if needle_length == self_length {\n            return self == needle;\n        }\n\n        for window in self.windows(needle_length) {\n            if needle == window {\n                return true;\n            }\n        }\n\n        false\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    use proptest::prelude::*;\n\n    #[test]\n    fn is_whitespace() {\n        assert!(b' '.is_whitespace());\n        assert!(b'\\r'.is_whitespace());\n        assert!(b'\\n'.is_whitespace());\n    }\n\n    #[test]\n    fn trim() {\n        assert!([b' ', b' ', b' '].trim().is_empty());\n        assert!([b' ', b'\\r', b'\\n'].trim().is_empty());\n        assert!([b'\\n'].trim().is_empty());\n        assert!([].trim().is_empty());\n\n        assert_eq!([b'a', b'b'], [b'a', b'b'].trim());\n        assert_eq!([b'h', b'i'], [b' ', b'h', b'i'].trim());\n        assert_eq!([b'h', b'i'], [b'h', b'i', b' '].trim());\n        assert_eq!([b'h', b'i'], [b' ', b'h', b'i', b' '].trim());\n    }\n\n    #[test]\n    fn contains() {\n        assert!([1, 2, 3, 4, 5].contains_slice(&[1, 2, 3, 4, 5]));\n        assert!([1, 2, 3, 4, 5].contains_slice(&[1, 2, 3]));\n        assert!([1, 2, 3, 4, 5].contains_slice(&[3, 4, 5]));\n        assert!([1, 2, 3, 4, 5].contains_slice(&[2, 3, 4]));\n        assert!(![1, 2, 3, 4, 5].contains_slice(&[]));\n    }\n\n    #[test]\n    fn trim_first_and_last_line_of_whitespace_edge_cases() {\n        assert_eq!(b\"\", b\"\\ra \".trim_first_and_last_line_of_whitespace());\n        assert_eq!(b\"a\", b\"\\r\\na \".trim_first_and_last_line_of_whitespace());\n\n        assert_eq!(b\" \", b\" \".trim_first_and_last_line_of_whitespace());\n    }\n\n    proptest! {\n        #[test]\n        fn trim_first_and_last_line_of_whitespace_doesnt_panic(input: Vec<u8>) {\n            let _ = &input.trim_first_and_last_line_of_whitespace();\n        }\n    }\n}\n"
  },
  {
    "path": "src/utils/fs.rs",
    "content": "use std::{collections::BTreeMap, path::Path};\n\nuse ignore::{overrides::OverrideBuilder, DirEntry, WalkBuilder, WalkState::Continue};\n\nuse rayon::prelude::*;\n\nuse crate::{\n    config::Config,\n    language::{Language, LanguageType},\n};\n\nconst IGNORE_FILE: &str = \".tokeignore\";\n\npub fn get_all_files<A: AsRef<Path>>(\n    paths: &[A],\n    ignored_directories: &[&str],\n    languages: &mut BTreeMap<LanguageType, Language>,\n    config: &Config,\n) {\n    let languages = parking_lot::Mutex::new(languages);\n    let (tx, rx) = crossbeam_channel::unbounded();\n\n    let mut paths = paths.iter();\n    let mut walker = WalkBuilder::new(paths.next().unwrap());\n\n    for path in paths {\n        walker.add(path);\n    }\n\n    if !ignored_directories.is_empty() {\n        let mut overrides = OverrideBuilder::new(\".\");\n\n        for ignored in ignored_directories {\n            rs_error!(overrides.add(&format!(\"!{}\", ignored)));\n        }\n\n        walker.overrides(overrides.build().expect(\"Excludes provided were invalid\"));\n    }\n\n    let ignore = config.no_ignore.map(|b| !b).unwrap_or(true);\n    let ignore_dot = ignore && config.no_ignore_dot.map(|b| !b).unwrap_or(true);\n    let ignore_vcs = ignore && config.no_ignore_vcs.map(|b| !b).unwrap_or(true);\n\n    // Custom ignore files always work even if the `ignore` option is false,\n    // so we only add if that option is not present.\n    if ignore_dot {\n        walker.add_custom_ignore_filename(IGNORE_FILE);\n    }\n\n    walker\n        .git_exclude(ignore_vcs)\n        .git_global(ignore_vcs)\n        .git_ignore(ignore_vcs)\n        .hidden(config.hidden.map(|b| !b).unwrap_or(true))\n        .ignore(ignore_dot)\n        .parents(ignore && config.no_ignore_parent.map(|b| !b).unwrap_or(true));\n\n    walker.build_parallel().run(move || {\n        let tx = tx.clone();\n        Box::new(move |entry| {\n            let entry = match entry {\n                Ok(entry) => entry,\n                Err(error) => {\n                    use ignore::Error;\n                    if let Error::WithDepth { err: ref error, .. } = error {\n                        if let Error::WithPath {\n                            ref path,\n                            err: ref error,\n                        } = **error\n                        {\n                            error!(\"{} reading {}\", error, path.display());\n                            return Continue;\n                        }\n                    }\n                    error!(\"{}\", error);\n                    return Continue;\n                }\n            };\n\n            if entry.file_type().map_or(false, |ft| ft.is_file()) {\n                tx.send(entry).unwrap();\n            }\n\n            Continue\n        })\n    });\n\n    let rx_iter = rx\n        .into_iter()\n        .par_bridge()\n        .filter_map(|e| LanguageType::from_path(e.path(), config).map(|l| (e, l)));\n\n    let process = |(entry, language): (DirEntry, LanguageType)| {\n        let result = language.parse(entry.into_path(), config);\n        let mut lock = languages.lock();\n        let entry = lock.entry(language).or_insert_with(Language::new);\n        match result {\n            Ok(stats) => {\n                let func = config.for_each_fn;\n                if let Some(f) = func {\n                    f(language, stats.clone())\n                };\n                entry.add_report(stats)\n            }\n            Err((error, path)) => {\n                entry.mark_inaccurate();\n                error!(\"Error reading {}:\\n{}\", path.display(), error);\n            }\n        }\n    };\n\n    if let Some(types) = config.types.as_deref() {\n        rx_iter.filter(|(_, l)| types.contains(l)).for_each(process)\n    } else {\n        rx_iter.for_each(process)\n    }\n}\n\npub(crate) fn get_extension(path: &Path) -> Option<String> {\n    path.extension().map(|e| e.to_string_lossy().to_lowercase())\n}\n\npub(crate) fn get_filename(path: &Path) -> Option<String> {\n    path.file_name().map(|e| e.to_string_lossy().to_lowercase())\n}\n\n#[cfg(test)]\nmod tests {\n    use std::fs;\n\n    use tempfile::TempDir;\n\n    use super::IGNORE_FILE;\n    use crate::{\n        config::Config,\n        language::{languages::Languages, LanguageType},\n    };\n\n    const FILE_CONTENTS: &[u8] = b\"fn main() {}\";\n    const FILE_NAME: &str = \"main.rs\";\n    const IGNORE_PATTERN: &str = \"*.rs\";\n    const LANGUAGE: &LanguageType = &LanguageType::Rust;\n\n    #[test]\n    fn ignore_directory_with_extension() {\n        let mut languages = Languages::new();\n        let tmp_dir = TempDir::new().expect(\"Couldn't create temp dir\");\n        let path_name = tmp_dir.path().join(\"directory.rs\");\n\n        fs::create_dir(path_name).expect(\"Couldn't create directory.rs within temp\");\n\n        super::get_all_files(\n            &[tmp_dir.into_path().to_str().unwrap()],\n            &[],\n            &mut languages,\n            &Config::default(),\n        );\n\n        assert!(languages.get(LANGUAGE).is_none());\n    }\n\n    #[test]\n    fn hidden() {\n        let dir = TempDir::new().expect(\"Couldn't create temp dir.\");\n        let mut config = Config::default();\n        let mut languages = Languages::new();\n\n        fs::write(dir.path().join(\".hidden.rs\"), FILE_CONTENTS).unwrap();\n\n        super::get_all_files(\n            &[dir.path().to_str().unwrap()],\n            &[],\n            &mut languages,\n            &config,\n        );\n\n        assert!(languages.get(LANGUAGE).is_none());\n\n        config.hidden = Some(true);\n\n        super::get_all_files(\n            &[dir.path().to_str().unwrap()],\n            &[],\n            &mut languages,\n            &config,\n        );\n\n        assert!(languages.get(LANGUAGE).is_some());\n    }\n\n    #[test]\n    fn no_ignore_implies_dot() {\n        let dir = TempDir::new().expect(\"Couldn't create temp dir.\");\n        let mut config = Config::default();\n        let mut languages = Languages::new();\n\n        fs::write(dir.path().join(\".ignore\"), IGNORE_PATTERN).unwrap();\n        fs::write(dir.path().join(FILE_NAME), FILE_CONTENTS).unwrap();\n\n        super::get_all_files(\n            &[dir.path().to_str().unwrap()],\n            &[],\n            &mut languages,\n            &config,\n        );\n\n        assert!(languages.get(LANGUAGE).is_none());\n\n        config.no_ignore = Some(true);\n\n        super::get_all_files(\n            &[dir.path().to_str().unwrap()],\n            &[],\n            &mut languages,\n            &config,\n        );\n\n        assert!(languages.get(LANGUAGE).is_some());\n    }\n\n    #[test]\n    fn no_ignore_implies_vcs_gitignore() {\n        let dir = TempDir::new().expect(\"Couldn't create temp dir.\");\n        let mut config = Config::default();\n        let mut languages = Languages::new();\n\n        git2::Repository::init(dir.path()).expect(\"Couldn't create git repo.\");\n\n        fs::write(dir.path().join(\".gitignore\"), IGNORE_PATTERN).unwrap();\n        fs::write(dir.path().join(FILE_NAME), FILE_CONTENTS).unwrap();\n\n        super::get_all_files(\n            &[dir.path().to_str().unwrap()],\n            &[],\n            &mut languages,\n            &config,\n        );\n\n        assert!(languages.get(LANGUAGE).is_none());\n\n        config.no_ignore = Some(true);\n\n        super::get_all_files(\n            &[dir.path().to_str().unwrap()],\n            &[],\n            &mut languages,\n            &config,\n        );\n\n        assert!(languages.get(LANGUAGE).is_some());\n    }\n\n    #[test]\n    fn no_ignore_parent() {\n        let parent_dir = TempDir::new().expect(\"Couldn't create temp dir.\");\n        let child_dir = parent_dir.path().join(\"child/\");\n        let mut config = Config::default();\n        let mut languages = Languages::new();\n\n        fs::create_dir_all(&child_dir)\n            .unwrap_or_else(|_| panic!(\"Couldn't create {:?}\", child_dir));\n        fs::write(parent_dir.path().join(\".ignore\"), IGNORE_PATTERN)\n            .expect(\"Couldn't create .gitignore.\");\n        fs::write(child_dir.join(FILE_NAME), FILE_CONTENTS).expect(\"Couldn't create child.rs\");\n\n        super::get_all_files(\n            &[child_dir.as_path().to_str().unwrap()],\n            &[],\n            &mut languages,\n            &config,\n        );\n\n        assert!(languages.get(LANGUAGE).is_none());\n\n        config.no_ignore_parent = Some(true);\n\n        super::get_all_files(\n            &[child_dir.as_path().to_str().unwrap()],\n            &[],\n            &mut languages,\n            &config,\n        );\n\n        assert!(languages.get(LANGUAGE).is_some());\n    }\n\n    #[test]\n    fn no_ignore_dot() {\n        let dir = TempDir::new().expect(\"Couldn't create temp dir.\");\n        let mut config = Config::default();\n        let mut languages = Languages::new();\n\n        fs::write(dir.path().join(\".ignore\"), IGNORE_PATTERN).unwrap();\n        fs::write(dir.path().join(FILE_NAME), FILE_CONTENTS).unwrap();\n\n        super::get_all_files(\n            &[dir.path().to_str().unwrap()],\n            &[],\n            &mut languages,\n            &config,\n        );\n\n        assert!(languages.get(LANGUAGE).is_none());\n\n        config.no_ignore_dot = Some(true);\n\n        super::get_all_files(\n            &[dir.path().to_str().unwrap()],\n            &[],\n            &mut languages,\n            &config,\n        );\n\n        assert!(languages.get(LANGUAGE).is_some());\n    }\n\n    #[test]\n    fn no_ignore_dot_still_vcs_gitignore() {\n        let dir = TempDir::new().expect(\"Couldn't create temp dir.\");\n        let mut config = Config::default();\n        let mut languages = Languages::new();\n\n        git2::Repository::init(dir.path()).expect(\"Couldn't create git repo.\");\n\n        fs::write(dir.path().join(\".gitignore\"), IGNORE_PATTERN).unwrap();\n        fs::write(dir.path().join(FILE_NAME), FILE_CONTENTS).unwrap();\n\n        config.no_ignore_dot = Some(true);\n\n        super::get_all_files(\n            &[dir.path().to_str().unwrap()],\n            &[],\n            &mut languages,\n            &config,\n        );\n\n        assert!(languages.get(LANGUAGE).is_none());\n    }\n\n    #[test]\n    fn no_ignore_dot_includes_custom_ignore() {\n        let dir = TempDir::new().expect(\"Couldn't create temp dir.\");\n        let mut config = Config::default();\n        let mut languages = Languages::new();\n\n        fs::write(dir.path().join(IGNORE_FILE), IGNORE_PATTERN).unwrap();\n        fs::write(dir.path().join(FILE_NAME), FILE_CONTENTS).unwrap();\n\n        super::get_all_files(\n            &[dir.path().to_str().unwrap()],\n            &[],\n            &mut languages,\n            &config,\n        );\n\n        assert!(languages.get(LANGUAGE).is_none());\n\n        config.no_ignore_dot = Some(true);\n\n        super::get_all_files(\n            &[dir.path().to_str().unwrap()],\n            &[],\n            &mut languages,\n            &config,\n        );\n\n        assert!(languages.get(LANGUAGE).is_some());\n    }\n\n    #[test]\n    fn no_ignore_vcs_gitignore() {\n        let dir = TempDir::new().expect(\"Couldn't create temp dir.\");\n        let mut config = Config::default();\n        let mut languages = Languages::new();\n\n        git2::Repository::init(dir.path()).expect(\"Couldn't create git repo.\");\n\n        fs::write(dir.path().join(\".gitignore\"), IGNORE_PATTERN).unwrap();\n        fs::write(dir.path().join(FILE_NAME), FILE_CONTENTS).unwrap();\n\n        super::get_all_files(\n            &[dir.path().to_str().unwrap()],\n            &[],\n            &mut languages,\n            &config,\n        );\n\n        assert!(languages.get(LANGUAGE).is_none());\n\n        config.no_ignore_vcs = Some(true);\n\n        super::get_all_files(\n            &[dir.path().to_str().unwrap()],\n            &[],\n            &mut languages,\n            &config,\n        );\n\n        assert!(languages.get(LANGUAGE).is_some());\n    }\n\n    #[test]\n    fn no_ignore_vcs_gitignore_still_dot() {\n        let dir = TempDir::new().expect(\"Couldn't create temp dir.\");\n        let mut config = Config::default();\n        let mut languages = Languages::new();\n\n        fs::write(dir.path().join(\".ignore\"), IGNORE_PATTERN).unwrap();\n        fs::write(dir.path().join(FILE_NAME), FILE_CONTENTS).unwrap();\n\n        config.no_ignore_vcs = Some(true);\n\n        super::get_all_files(\n            &[dir.path().to_str().unwrap()],\n            &[],\n            &mut languages,\n            &config,\n        );\n\n        assert!(languages.get(LANGUAGE).is_none());\n    }\n\n    #[test]\n    fn no_ignore_vcs_gitexclude() {\n        let dir = TempDir::new().expect(\"Couldn't create temp dir.\");\n        let mut config = Config::default();\n        let mut languages = Languages::new();\n\n        git2::Repository::init(dir.path()).expect(\"Couldn't create git repo.\");\n\n        fs::write(dir.path().join(\".git/info/exclude\"), IGNORE_PATTERN).unwrap();\n        fs::write(dir.path().join(FILE_NAME), FILE_CONTENTS).unwrap();\n\n        super::get_all_files(\n            &[dir.path().to_str().unwrap()],\n            &[],\n            &mut languages,\n            &config,\n        );\n\n        assert!(languages.get(LANGUAGE).is_none());\n\n        config.no_ignore_vcs = Some(true);\n\n        super::get_all_files(\n            &[dir.path().to_str().unwrap()],\n            &[],\n            &mut languages,\n            &config,\n        );\n\n        assert!(languages.get(LANGUAGE).is_some());\n    }\n\n    #[test]\n    fn custom_ignore() {\n        let dir = TempDir::new().expect(\"Couldn't create temp dir.\");\n        let config = Config::default();\n        let mut languages = Languages::new();\n\n        git2::Repository::init(dir.path()).expect(\"Couldn't create git repo.\");\n\n        fs::write(dir.path().join(IGNORE_FILE), IGNORE_PATTERN).unwrap();\n        fs::write(dir.path().join(FILE_NAME), FILE_CONTENTS).unwrap();\n\n        super::get_all_files(\n            &[dir.path().to_str().unwrap()],\n            &[],\n            &mut languages,\n            &config,\n        );\n\n        assert!(languages.get(LANGUAGE).is_none());\n\n        fs::remove_file(dir.path().join(IGNORE_FILE)).unwrap();\n\n        super::get_all_files(\n            &[dir.path().to_str().unwrap()],\n            &[],\n            &mut languages,\n            &config,\n        );\n\n        assert!(languages.get(LANGUAGE).is_some());\n    }\n}\n"
  },
  {
    "path": "src/utils/macros.rs",
    "content": "#![allow(unused_macros)]\n\nmacro_rules! opt_warn {\n    ($option:expr, $message:expr) => {\n        match $option {\n            Some(result) => result,\n            None => {\n                warn!($message);\n                continue;\n            }\n        }\n    };\n}\n\nmacro_rules! rs_warn {\n    ($result:expr, $message: expr) => {\n        match $result {\n            Ok(result) => result,\n            Err(error) => {\n                warn!(\"{}\", error);\n                continue;\n            }\n        }\n    };\n}\n\nmacro_rules! opt_error {\n    ($option:expr, $message:expr) => {\n        match $option {\n            Some(result) => result,\n            None => {\n                error!($message);\n                continue;\n            }\n        }\n    };\n}\n\nmacro_rules! rs_error {\n    ($result:expr) => {\n        match $result {\n            Ok(result) => result,\n            Err(error) => {\n                error!(\"{}\", error);\n                continue;\n            }\n        }\n    };\n}\n\nmacro_rules! opt_ret_warn {\n    ($option:expr, $message:expr) => {\n        match $option {\n            Some(result) => result,\n            None => {\n                warn!($message);\n                return None;\n            }\n        }\n    };\n}\n\nmacro_rules! rs_ret_warn {\n    ($result:expr, $message: expr) => {\n        match $result {\n            Ok(result) => result,\n            Err(error) => {\n                warn!(\"{}\", error);\n                return None;\n            }\n        }\n    };\n}\n\nmacro_rules! opt_ret_error {\n    ($option:expr, $message:expr) => {\n        match $option {\n            Some(result) => result,\n            None => {\n                error!($message);\n                return None;\n            }\n        }\n    };\n}\n\nmacro_rules! rs_ret_error {\n    ($result:expr) => {\n        match $result {\n            Ok(result) => result,\n            Err(error) => {\n                error!(\"{}\", error);\n                return None;\n            }\n        }\n    };\n}\n\nmacro_rules! debug {\n    ($fmt:expr) => (if cfg!(debug_assertions) {println!($fmt)});\n    ($fmt:expr, $($arg:tt)*) => (if cfg!(debug_assertions) {println!($fmt, $($arg)*)});\n}\n"
  },
  {
    "path": "src/utils/mod.rs",
    "content": "#[macro_use]\nmod macros;\npub(crate) mod ext;\npub mod fs;\n"
  },
  {
    "path": "tests/accuracy.rs",
    "content": "extern crate ignore;\nextern crate regex;\nextern crate tokei;\n\nuse std::fs;\n\nuse once_cell::sync::Lazy;\nuse regex::Regex;\nuse tokei::{Config, Languages};\n\nstatic LINES: Lazy<Regex> = Lazy::new(|| Regex::new(r\"\\d+ lines\").unwrap());\nstatic CODE: Lazy<Regex> = Lazy::new(|| Regex::new(r\"\\d+ code\").unwrap());\nstatic COMMENTS: Lazy<Regex> = Lazy::new(|| Regex::new(r\"\\d+ comments\").unwrap());\nstatic BLANKS: Lazy<Regex> = Lazy::new(|| Regex::new(r\"\\d+ blanks\").unwrap());\n\nmacro_rules! get_digit {\n    ($regex:expr, $text:expr) => {{\n        let matched = $regex.find(&$text).expect(\"Couldn't find category\");\n        matched\n            .as_str()\n            .split_whitespace()\n            .next()\n            .unwrap()\n            .parse::<usize>()\n            .unwrap()\n    }};\n}\n\nmod config {\n    use tokei::*;\n\n    /*\n    #[test]\n    fn extension_change() {\n        use std::collections::HashMap;\n        let mut languages = Languages::new();\n        let config = Config {\n            languages: {\n                let mut map = HashMap::new();\n                let mut config = LanguageConfig::new();\n                config.extensions(vec![String::from(\"cpp\")]);\n                map.insert(LanguageType::C, config);\n\n                Some(map)\n            },\n            ..Config::default()\n        };\n\n        languages.get_statistics(&[\"tests/data/cpp.cpp\"], &[], &config);\n\n        if languages.len() != 1 {\n            panic!(\"wrong languages detected: expected just C, found {:?}\",\n                   languages.into_iter().collect::<Vec<_>>());\n        }\n\n        let (name, _) = languages.into_iter().next().unwrap();\n\n        assert_eq!(LanguageType::C, name);\n    }\n    */\n\n    #[test]\n    fn treating_comments_as_code() {\n        let mut languages = Languages::new();\n        let config = Config {\n            treat_doc_strings_as_comments: Some(true),\n            ..Config::default()\n        };\n\n        languages.get_statistics(&[\"tests/data/python.py\"], &[], &config);\n\n        if languages.len() != 1 {\n            panic!(\n                \"wrong languages detected: expected just Python, found {:?}\",\n                languages.into_iter().collect::<Vec<_>>()\n            );\n        }\n\n        let (_, language) = languages.into_iter().next().unwrap();\n\n        assert_eq!(language.lines(), 15);\n        assert_eq!(language.blanks, 3);\n        assert_eq!(language.comments, 7);\n        assert_eq!(language.code, 5);\n    }\n}\n\ninclude!(concat!(env!(\"OUT_DIR\"), \"/tests.rs\"));\n"
  },
  {
    "path": "tests/data/Daml.daml",
    "content": "-- ! 42 lines 24 code 9 comments 9 blanks\n\n{-\nThis code is derived from https://github.com/digital-asset/ex-secure-daml-infra/blob/master/daml/BobTrigger.daml\n-- Haskell/DAML support nested comments\n{- including nested \nblock comments\n-}\n-}\n\n\nmodule BobTrigger where\n\n-- import DA.Action\nimport DA.Foldable\nimport DA.Next.Map (Map)\nimport Daml.Trigger\nimport Main\n\n(--$) :: a -> b -> String\na --$ b = \"Not a comment\"\n\nrejectTrigger : Trigger () = Trigger with\n  initialize = \\_ -> ()\n  updateState = \\_ _ () -> ()\n  rule = rejectRule\n  registeredTemplates = AllInDar\n  heartbeat = None\n  testNotAComment = 1\n    --$ 2 == \"Not a comment\"\n  \n\nrejectRule : Party -> ACS -> Time -> Map CommandId [Command] -> () -> TriggerA ()\nrejectRule p acs _ _ _ = do\n  let assets = getContracts @Asset acs\n  let bobAssets = filter (\\(_,a) -> a.owner == p) assets\n  let configs = getContracts @DonorConfig acs\n  let Some (_,bobConfig) = find (\\(_,c) -> c.owner == p) configs\n\n  forA_ bobAssets $ \\(_cid, c) -> do\n    debug \"Ran rejectRule\"\n    emitCommands [exerciseCmd _cid Give with newOwner = bobConfig.donateTo] [toAnyContractId _cid]\n"
  },
  {
    "path": "tests/data/Dockerfile",
    "content": "# 17 lines 7 code 3 comments 7 blanks\n\nFROM netbsd:7.0.2\n\nMAINTAINER Somebody version: 2.2\n\nRUN curl -sSf https://static.rust-lang.org/rustup.sh | sh -s -- -y\n\n# this part is important\nVOLUME [\"/project\"]\nWORKDIR \"/project\"\n\nRUN sh -c 'echo \"Hello World\" > /dev/null'\nRUN cargo install tokei # not counted\n\n# now you do your part\n\n"
  },
  {
    "path": "tests/data/MSBuild.csproj",
    "content": "<!-- 12 lines 10 code 1 comments 1 blanks -->\n<Project Sdk=\"Microsoft.NET.Sdk\">\n  <PropertyGroup>\n    <OutputType>Exe</OutputType>\n    <TargetFramework>netcoreapp2.0</TargetFramework>\n    <LangVersion>Latest</LangVersion>\n  </PropertyGroup>\n\n  <ItemGroup>\n    <PackageReference Include=\"Microsoft.CodeAnalysis\" Version=\"2.3.2\" />\n  </ItemGroup>\n</Project>\n"
  },
  {
    "path": "tests/data/Makefile",
    "content": "# 24 lines 11 code 5 comments 8 blanks\n\n##\t\t\t\t     ##\n## IMPORTANT COMMENT ##\n##\t\t\t\t\t ##\n\nall: hello\n\nhello: main.o factorial.o hello.o\n    g++ main.o factorial.o hello.o -o hello\n\n# main.o is my favorite\nmain.o: main.cpp\n    g++ -c main.cpp\n\nfactorial.o: factorial.cpp\n    g++ -c factorial.cpp\n\nhello.o: hello.cpp\n    g++ -c hello.cpp\n\nclean:\n    rm *o hello #not counted\n\n"
  },
  {
    "path": "tests/data/Modelica.mo",
    "content": "// 21 lines 13 code 5 comments 3 blanks\nblock Add \"Output the sum of the two inputs\"\n  extends Interfaces.SI2SO;\n\n/* \nparameter section\n*/\n  parameter Real k1=+1 \"Gain of input signal 1\";\n  parameter Real k2=+1 \"Gain of input signal 2\";\n\n// equation section\nequation \n  y = k1*u1 + k2*u2;\n  annotation (\n    Documentation(info=\"<html>\n<p>\nSome documentation.\n</p>\n</html>\"));\n\nend Add;\n"
  },
  {
    "path": "tests/data/NuGet.Config",
    "content": "<!-- 24 lines 13 code 8 comments 3 blanks -->\n\n<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<configuration>\n    <!-- defaultPushSource key works like the 'defaultPushSource' key of NuGet.Config files. -->\n    <!-- This can be used by administrators to prevent accidental publishing of packages to nuget.org. -->\n    <config>\n        <add key=\"defaultPushSource\" value=\"https://contoso.com/packages/\" />\n    </config>\n\n    <!-- Default Package Sources; works like the 'packageSources' section of NuGet.Config files. -->\n    <!-- This collection cannot be deleted or modified but can be disabled/enabled by users. -->\n    <packageSources>\n        <add key=\"Contoso Package Source\" value=\"https://contoso.com/packages/\" />\n        <add key=\"nuget.org\" value=\"https://api.nuget.org/v3/index.json\" />\n    </packageSources>\n\n    <!-- Default Package Sources that are disabled by default. -->\n    <!-- Works like the 'disabledPackageSources' section of NuGet.Config files. -->\n    <!-- Sources cannot be modified or deleted either but can be enabled/disabled by users. -->\n    <disabledPackageSources>\n        <add key=\"nuget.org\" value=\"true\" />\n    </disabledPackageSources>\n</configuration>\n"
  },
  {
    "path": "tests/data/PKGBUILD",
    "content": "# 24 lines 19 code 3 comments 2 blanks\n# Maintainer: \t  Andy 'Blocktronics' Herbert <blocktronics.org>\n# Aur Maintainer: Wanesty <github.com/Wanesty/aurpkg>\n\npkgname=moebius-bin\npkgver=1.0.29\npkgrel=1\nepoch=1\npkgdesc=\"Modern ANSI & ASCII Art Editor\"\narch=('x86_64')\nurl=\"https://github.com/blocktronics/moebius\"\nlicense=('Apache')\ndepends=('gtk3' 'libnotify' 'libxss' 'libxtst' 'xdg-utils' 'libappindicator-gtk3')\nmakedepends=('libarchive')\nconflicts=('moebius')\nsource=(\"https://github.com/blocktronics/moebius/releases/download/$pkgver/Moebius.rpm\") \nsha256sums=(69aaa1e42e287ed78c8e73971dae3df23ae4fa00e3416ea0fc262b7d147fefec)\nnoextract=(\"Moebius.rpm\")\n\npackage() {\n\tbsdtar -C \"${pkgdir}\" -xvf \"$srcdir/Moebius.rpm\"\n\tmkdir \"$pkgdir/usr/bin\"\n\tln -s \"/opt/Moebius/moebius\" \"$pkgdir/usr/bin/moebius\"\n}"
  },
  {
    "path": "tests/data/Rakefile",
    "content": "# 10 lines 4 code 2 comments 4 blanks\n\n# this is a rakefile\n\ntask default: %w[test]\n\ntask :test do # not counted\n  ruby \"test/unittest.rb\"\nend\n\n"
  },
  {
    "path": "tests/data/SConstruct",
    "content": "#!python\n# 10 lines 3 code 3 comments 4 blanks\n\n# this is a comment\n\nProgram('cpp.cpp') # this is a line-ending comment\n\nenv = Environment(CCFLAGS='-O3')\nenv.Append(CCFLAGS='-O3')\n\n"
  },
  {
    "path": "tests/data/Snakefile",
    "content": "# 67 lines 50 code 4 comments 13 blanks\n\"\"\"\nA sample Snakefile for testing line counting\n\"\"\"\n\nSAMPLES = [\"A\", \"B\"]\n\n\n# This is a\n# multiline\n# comment\nrule all:\n    input:\n        \"plots/quals.svg\"\n\n\n'''Sometimes even some\ncomments in single quote\nfences.'''\nrule bwa_map:\n    input:\n        \"data/genome.fa\",  # Inline comments are also supported\n        \"data/samples/{sample}.fastq\"\n    output:\n        \"mapped_reads/{sample}.bam\"\n    shell:\n        \"bwa mem {input} | samtools view -Sb - > {output}\"\n\n\nrule samtools_sort:\n    input:\n        \"mapped_reads/{sample}.bam\"\n    output:\n        \"sorted_reads/{sample}.bam\"\n    shell:\n        \"samtools sort -T sorted_reads/{wildcards.sample} \"\n        \"-O bam {input} > {output}\"\n\n\nrule samtools_index:\n    input:\n        \"sorted_reads/{sample}.bam\"\n    output:\n        \"sorted_reads/{sample}.bam.bai\"\n    shell:\n        \"samtools index {input}\"\n\n\nrule bcftools_call:\n    input:\n        fa=\"data/genome.fa\",\n        bam=expand(\"sorted_reads/{sample}.bam\", sample=SAMPLES),\n        bai=expand(\"sorted_reads/{sample}.bam.bai\", sample=SAMPLES)\n    output:\n        \"calls/all.vcf\"\n    shell:\n        \"bcftools mpileup -f {input.fa} {input.bam} | \"\n        \"bcftools call -mv - > {output}\"\n\n\nrule plot_quals:\n    input:\n        \"calls/all.vcf\"\n    output:\n        \"plots/quals.svg\"\n    script:\n        \"scripts/plot-quals.py\"\n"
  },
  {
    "path": "tests/data/Tera.tera",
    "content": "{# 42 lines 26 code 11 comments 5 blanks #}\n<!DOCTYPE html>\n<html>\n    <head>\n        <meta charset=\"utf-8\" />\n        <meta name=\"viewport\" content=\"width=device-width\" />\n        <title></title>\n        link\n        <style>\nbody {\n    background-color: pink;\n}\n        </style>{# #}\n        {# comment #}\n    </head>\n    <body>\n        body\n    </body>\n    <!-- Normal Comment-->\n\n    <nav class=\"navbar navbar-default navbar-fixed-top navbar-custom\">\n        <div id=\"modalSearch\" class=\"modal fade\" role=\"dialog\"> </div>\n    </nav>\n\n    <!--\n        document.write(\"Multi-line and Code comment!\");{# comment #}\n        //-->\n\n        <!--[if IE 8]>\n            IE Special comment\n        <![endif]-->\n        <script>\nlet x = 5;\n        </script>\n        {#\n        multi-line-comment #}\n        \n          {% if error %}\n          <div class=\"flash {{ error.category }}\">{{ error.content }}</div>{# comment #}\n          {% endif %}\n\n</html>\n"
  },
  {
    "path": "tests/data/abnf.abnf",
    "content": "; 11 lines 3 code 5 comments 3 blanks\n; comment line 0\n; comment line 1\n\nALPHA          =  %x41-5A / %x61-7A   ; A-Z / a-z\n\nBIT            =  \"0\" / \"1\"\n\nCHAR           =  %x01-7F\n                    ; any 7-bit US-ASCII character,\n                    ;  excluding NUL\n"
  },
  {
    "path": "tests/data/alloy.als",
    "content": "// 18 lines 10 code 3 comments 5 blanks\n\nsig Node {\n    edge: set Node\n}\n\n------------------------------------------------------------------------\n\npred self_loop[n: Node] {\n  n in n.edge\n}\n\npred all_self_loop {\n  all n: Node | self_loop[n]\n}\n\n/* Comments started by /* don't nest */\nrun all_self_loop\n"
  },
  {
    "path": "tests/data/apl.apl",
    "content": "⍝ 10 lines 3 code 3 comments 4 blanks\n\n\n256=2*8\n\n⍝ Comment\n⊃¨ ' '(≠⊆⊢) 'A Programming Language'\n\n⍝ A magic square of length ⊢\nMS ← (⍳-∘⌈÷∘2)(⊣⊖⌽),⍨⍴∘⍳×⍨\n"
  },
  {
    "path": "tests/data/arduino.ino",
    "content": "// 23 lines 13 code 6 comments 4 blanks\n\nint led = 13;\n\nvoid setup() {\n    // setup println()\n    Serial.begin(155200);\n    // Init LED pin\n    pinMode(led, OUTPUT);\n}\n\n/**\n * Blink the LED\n*/\nvoid loop() {\n    Serial.println(\"LED ON!\");\n    digitalWrite(led, HIGH);\n    delay(1000);\n\n    Serial.println(\"LED OFF!\");\n    digitalWrite(led, LOW);\n    delay(1000);\n}\n"
  },
  {
    "path": "tests/data/arturo.art",
    "content": "; 8 lines 3 code 3 comments 2 blanks\n; this is a comment\n; this is another comment\n\na1: 2\na2: 3.14 ; pi\n\na3: 213213      ; another number\n"
  },
  {
    "path": "tests/data/asciidoc.adoc",
    "content": "// 20 lines 5 code 8 comments 7 blanks\n\n= AsciiDoc title\n\nA simple paragraph.\n\n// single line comment\n\nThat should end before a paragraph.\n\n////\nmulti\n= Line\n\ncomment\n////\n\n== Nested title\n\nNested titles and paragraphs are fine, too.\n"
  },
  {
    "path": "tests/data/asn1.asn1",
    "content": "-- 34 lines 16 code 11 comments 7 blanks\nPKCS-12 {\n    iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs-12(12)\n        modules(0) pkcs-12(1) }\n\n    -- PKCS #12 v1.1 ASN.1 Module\n    -- Revised October 27, 2012\n\n    -- This module has been checked for conformance with the ASN.1 standard\n    -- by the OSS ASN.1 Tools\n\n    DEFINITIONS IMPLICIT TAGS ::=\n\n    BEGIN\n\n    PFX ::= SEQUENCE {\n        version INTEGER {v3(3)}(v3,...),\n        authSafe OCTET STRING,\n        macData MacData /* \" \" */ OPTIONAL\n    }\n\n    /*\n     * Multi line\n     *\n     */\n\n    MacData ::= SEQUENCE {\n        mac OBJECT IDENTIFIER,\n        macSalt OCTET STRING,\n        iterations INTEGER DEFAULT 1\n        -- Note: The default is for historical reasons and its use is\n        -- deprecated.\n    }\nEND\n"
  },
  {
    "path": "tests/data/ats.dats",
    "content": "//! 42 lines 25 code 9 comments 8 blanks\n\n(*************\n    Reference:\n    https://github.com/ats-lang/ats-lang.github.io/blob/master/DOCUMENT/ATS2TUTORIAL/CODE/chap_stream_vt.dats\n**************)\n#include \"share/atspre_staload.hats\"\n\n/* Lazy-evaluated integer iterator */\nfun from\n(n: int): stream_vt(int) =\n  $ldelay(stream_vt_cons(n, from(n + 1)))\n\n// Lazy-evaluated prime finder\nfun sieve\n(ns: stream_vt(int))\n: stream_vt(int) = $ldelay(\n  let\n    val ns_con = !ns\n    val- @stream_vt_cons(n0, ns1) = ns_con\n    val n0_val = n0\n    val ns1_val = ns1\n\n    val () =\n      (ns1 := sieve(stream_vt_filter_cloptr<int>(ns1_val, lam x => x mod n0_val > 0)))\n    \n    prval () = fold@(ns_con)\n  in\n    ns_con\n  end\n  ,\n  ~ns\n)\n\n// Test run for finding the 1000-th prime number\nval thePrimes = sieve(from(2))\nval p1000 = (steam_vt_drop_exn(thePrimes, 1000)).head()\nval () = println!(\"p1000 = \", p1000)\n\nimplement main0 () = {}\n\n(* End of file *)\n"
  },
  {
    "path": "tests/data/awk.awk",
    "content": "#!/bin/awk -f\n# 5 lines 1 code 3 comments 1 blanks\n\n# This is a comment\n{ print $0 }\n"
  },
  {
    "path": "tests/data/ballerina.bal",
    "content": "// 40 lines 29 code 6 comments 5 blanks\n\n# Returns Bob's response to someone talking to him.\n#\n# + input - whatever is said to Bob\n# + return - Bob's response\npublic function hey(string input) returns string {\n    string trimmed = input.trim();\n    boolean silent = isSilence(trimmed);\n    boolean asking = isQuestion(trimmed);\n    boolean yelling = isYelling(trimmed);\n\n    match [silent, yelling, asking] {\n        [true, _, _] => {\n            return \"Fine. Be that way!\";\n        }\n        [_, true, true] => {\n            return \"Calm down, I know what I'm doing!\";\n        }\n        [_, true, false] => {\n            return \"Whoa, chill out!\";\n        }\n        [_, false, true] => {\n            return \"Sure.\";\n        }\n        _ => {\n            return \"Whatever.\";\n        }\n    }\n}\n\nisolated function isSilence(string input) returns boolean => input.length() == 0;\n\nisolated function isQuestion(string input) returns boolean => input.endsWith(\"?\");\n\nfunction isYelling(string input) returns boolean {\n    // contains an uppercase letter and does not contain a lowercase letter\n    return input.includesMatch(re `\\p{Lu}`)\n        && !input.includesMatch(re `\\p{Ll}`);\n}\n"
  },
  {
    "path": "tests/data/bazel.bzl",
    "content": "# 18 lines 13 code 3 comments 2 blanks\n\n# build hello-greet\ncc_library(\n    name = \"hello-greet\",\n    srcs = [\"hello-greet.cc\"],\n    hdrs = [\"hello-greet.h\"],\n)\n\n# build hello-world\ncc_binary(\n    name = \"hello-world\",\n    srcs = [\"hello-world.cc\"],\n    deps = [\n        \":hello-greet\",\n        \"//lib:hello-time\",\n    ],\n)"
  },
  {
    "path": "tests/data/bean.bean",
    "content": "; 27 lines 13 code 6 comments 8 blanks\n\noption \"operating_currency\" \"EUR\"\n\n2002-01-01 commodity EUR\n    name: \"Euro\"\n    asset-class: \"cash\"\n\n\n; open accounts initially\n2020-09-01 open Equity:Opening-Balances\n2020-09-01 open Assets:Cash                 EUR\n2020-09-01 open Expenses:Food               EUR\n\n; put initial money on account\n2020-09-01 pad Assets:Cash Equity:Opening-Balances\n\n; verifying starting balance\n2020-09-02 balance Assets:Cash  81.7 EUR\n\n; transferring money\n2020-09-03 * \"transfer of money\"\n  Assets:Cash   -17.7 EUR\n  Expenses:Food\n\n; validating changed balance\n2020-09-04 balance  Assets:Cash  64 EUR\n"
  },
  {
    "path": "tests/data/bicep.bicep",
    "content": "//! 50 lines 35 code 8 comments 7 blanks\n/*\nBicep is a declarative language, which means the elements can appear in any order. Unlike imperative languages, the order of elements doesn't affect how deployment is processed.\nThis means you can define resources, variables, and parameters in any order you like.\n*/\n\nmetadata description = 'Creates a storage account and a web app'\n\n@description('The prefix to use for the storage account name.')\n@minLength(3)\n@maxLength(11)\nparam storagePrefix string\n\nparam storageSKU string = 'Standard_LRS'\nparam location string = resourceGroup().location\n\nvar uniqueStorageName = '${storagePrefix}${uniqueString(resourceGroup().id)}'\n\nvar objectExmaple = {\n    name: 'John'\n    age: 30\n    address: '''\n        1 Microsoft Way\n        Redmond, WA 98052\n    '''\n}\n\n// Create a storage account\nresource stg 'Microsoft.Storage/storageAccounts@2022-09-01' = {\n    name: uniqueStorageName\n    location: location\n    sku: {\n        name: storageSKU\n    }\n    kind: 'StorageV2'\n    properties: {\n        supportsHttpsTrafficOnly: true\n    }\n}\n\n// Use a module to deploy a web app\n// Modules are a way to encapsulate and reuse resources in Bicep\nmodule webModule './webApp.bicep' = {\n    name: 'webDeploy'\n    params: {\n        skuName: 'S1'\n        location: location\n        personalInfo: objectExmaple\n    }\n}\n"
  },
  {
    "path": "tests/data/bitbake.bb",
    "content": "# 23 lines 13 code 5 comments 5 blanks\n#\n# This file was derived from the 'Hello World!' example recipe in the\n# Yocto Project Development Manual.\n#\n\nSUMMARY = \"Simple helloworld application\"\nSECTION = \"examples\"\nLICENSE = \"MIT\"\nLIC_FILES_CHKSUM = \"file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302\"\n\nSRC_URI = \"file://helloworld.c\"\n\nS = \"${WORKDIR}\"\n\ndo_compile() {\n\t     ${CC} helloworld.c -o helloworld\n}\n\ndo_install() {\n\t     install -d ${D}${bindir}\n\t     install -m 0755 helloworld ${D}${bindir}\n}\n"
  },
  {
    "path": "tests/data/bqn.bqn",
    "content": "# 10 lines 3 code 3 comments 4 blanks\n\n\n256=2⋆8\n\n# Comment\n<⟜'a'⊸/\"Big Questions Notation\"\n\n# A magic square of length ⊢\nMS ← (↕-⌈∘÷⟜2)(⊣⌽˘⌾⍉⌽˘)⋈˜⥊⟜↕×˜\n"
  },
  {
    "path": "tests/data/brightscript.brs",
    "content": "' 26 lines 10 code 13 comments 3 blanks\n' /**\n'  * @member difference\n'  * @memberof module:rodash\n'  * @instance\n'  * @description Return a new array of items from the first which are not in the second.\n'  * @param {Array} first\n'  * @param {Array} second\n'  * @example\n\nREM  * difference = _.difference([1,2], [2])\nREM  * ' => [1]\nREM  *\nREM  */\n\nFunction rodash_difference_(first, second)\n  result = []\n  for each f in first\n    result.push(f) 'Push array\n    for each s in second\n      if m.equal(s,f) then result.pop()\n    end for\n  end for\n\n  return result\nEnd Function"
  },
  {
    "path": "tests/data/c.c",
    "content": "// 50 lines 33 code 8 comments 9 blanks\r\n\r\n/* /* we can't nest block comments in c, but we can start one */\r\nint main(void) {\r\n\tchar *start = \"/*\";\r\n\r\n\tint x = 1;\r\n\tx += 2; // end of line comment */\r\n}\r\n\r\nvoid foo() {\r\n\tchar *esc = \"\\\"/*escaped quotes in a string and block comment*/\\\"\";\r\n\tfunc1();\r\n\tfunc2();\r\n\tchar *next_line =\r\n\t\t\"*/ /*string on new line\\\r\n\t\tcontinued to another line\\\r\n\t\tbar();\\\r\n\t\t*/\";\r\n\r\n\tchar *next_line2 = \"line1\\\r\n\t\t// not a real comment\\\r\n\t\tline3*/\";\r\n\r\n\t/* Block comment\r\n\t// line comment in a block comment\r\n\tend block comment*/\r\n\r\n\tchar *late_start = // \"\r\n\t\t\"wow\\\r\n\t\tthat's pretty neat\";\r\n\r\n\tchar *late_start2 = /* \" */\r\n\t\t\"*/ still just a string\"; // but this is a line comment\r\n}\r\n\r\nvoid foobar() {\r\n\tint a = 4; // /*\r\n    int b = 5;\r\n    int c = 6; // */\r\n}\r\n\r\n/*\\\r\n / comment\r\n\\*/\r\nstruct Point {\r\n    int x;\r\n    int y;\r\n    int z;\r\n};\r\n"
  },
  {
    "path": "tests/data/c3.c3",
    "content": "/* 8 lines 6 code 1 comments 1 blanks */\n\nfn int main() {\n    int x = 5; /* Multline line comment */\n    io::printn(\"Hello, world!\");\n    int y = 4; // Single line comment.\n    return 0;\n}\n"
  },
  {
    "path": "tests/data/cairo.cairo",
    "content": "//! 51 lines 32 code 13 comments 6 blanks\n//! ```rust\n//! fn main () {\n//!     // Comment\n//!\n//!     println!(\"Hello World!\");\n//! }\n//! ```\n\n/// The main function\nfn main() {\n    let x: ByteArray = \"\\\"/*##\\\"\\\"##\\'\\'\";\n    // comment\n    loop {\n        if x.len() >= 2 && x[0] == '*' && x[1] == '/' { // found the */\n            break;\n        }\n    }\n}\n\nfn foo<T, +Drop<T>>(name: T) {\n    let this_ends = 'a \"\\'test/\"*.';\n    call1();\n    call2();\n    let this_does_not =  // a // nested // comment \" //\n    ///\"*/another /*test\n    call3();\n//*/\";\n}\n\nfn call1() {}\nfn call2() {}\nfn call3() {}\n\nfn foobar() {\n    let does_not_start: ByteArray =  // \"\n    \"until here,\n        test/*\n        test\"; // a quote: \"\n    let also_doesnt_start =\n        /// \" */\n        'until here,\n        test,'; // another quote: \"\n}\n\nfn foo2() {\n    let a = 4; // ///\n    let b = '5';\n    let c = 6; // ///\n}\n\n"
  },
  {
    "path": "tests/data/cangjie.cj",
    "content": "/* 50 lines 34 code 8 comments 8 blanks */\n\n/* /* we can nest block*/ comments in cangjie */\nmain(): Unit {\n    let start: String = \"/*\"\n\n    var x: Int64 = 1\n    x += 2 // end of line comment */\n}\n\nfunc foo(): Unit {\n    let esc: String = \"\\\"/*escaped quotes in a string and block comment*/\\\"\";\n    let next_line: String = \"\"\"\n\t\t*/ /*string on new line\n\t\tcontinued to another line\n\t\tbar();\n\t\t*/\"\n\"\"\"\n\n    /* Block comment\n    // line comment in a block comment\n    end block comment*/\n    let late_start: String = // \"\n        ##\"wow\\\n\t\tthat's pretty neat\"##\n\n    let late_start2: String = /* \" */\n        \"*/ still just a string\" // but this is a line comment\n}\n\nfunc foobar(): Unit {\n    let a: Int64 = 4 // /*\n    let b: Int64 = 5\n    let c: Int64 = 6 // */\n}\n\n/* \\\n / comment\n\\*/\nstruct Point {\n    let x: Int64\n    let y: Int64\n    let z: Int64\n\n    public init(x: Int64, y: Int64, z: Int64) {\n        this.x = x\n        this.y = y\n        this.z = z\n    }\n}\n"
  },
  {
    "path": "tests/data/chapel.chpl",
    "content": "// chapel 24 lines 9 code 9 comments 6 blanks\n\n// Tidy line comment\n\n/* Tidy block\n   comment.\n*/\n\n// Cheeky line comments /*\n// */\n\n/* Cheeky // block comments */\n\n// Calculate a factorial\nproc factorial(n: int): int {\n    var x = 1; // this will eventually be returned\n    for i in 1..n {\n        x *= i;\n    }\n    return x;\n}\n\nwriteln(\"// this isn't a comment\");\nwriteln('/* this is also not a comment */');\n"
  },
  {
    "path": "tests/data/cil.cil",
    "content": "; 20 lines 15 code 3 comments 2 blanks\n;============= etcd_t ==============\n(allow etcd_t proc_sysctl_t (dir (search)))\n(allow etcd_t proc_sysctl_t (file (read open)))\n(allow etcd_t procfs_t (dir (search getattr)))\n(allow etcd_t procfs_t (lnk_file (read)))\n(allow etcd_t self (dir (read open search)))\n(allow etcd_t self (fifo_file (write read)))\n\n;============= kernel_t ==============\n(allow kernel_t bin_t (dir (search)))\n(allow kernel_t bin_t (file (read execute_no_trans open map execute)))\n(allow kernel_t debugfs_t (dir (search)))\n(allow kernel_t device_t (blk_file (create setattr)))\n(allow kernel_t device_t (chr_file (write create setattr)))\n(allow kernel_t self (capability (dac_override mknod)))\n(allow kernel_t self (dir (write add_name search)))\n(allow kernel_t self (file (write create open)))\n\n(filecon \"/.extra(/.*)?\" any (system_u object_r extra_t (systemLow systemLow)))\n"
  },
  {
    "path": "tests/data/circom.circom",
    "content": "// 34 lines 23 code 7 comments 4 blanks\npragma circom 2.0.8;\n\n/*\n * Sum an array of non-zero values.\n */\nfunction sum(values, size) {\n  var sum = 0;\n  for (var i = 0; i < size; i++) {\n    assert(values[i] != 0);\n    sum += values[i];\n  }\n  log(\"sum = \", sum);\n  return sum;\n}\n\n/*\n * Ensure x is a solution to x^5 - 2x^4 + 5x - 4 = 0.\n */\ntemplate Polynomial() {\n    signal input x;\n    signal x2;\n    signal x4;\n    signal x5;\n    signal output y;\n\n    x2 <== x * x;\n    x4 <== x2 * x2;\n    x5 <== x4 * x;\n    y <== x5 - 2 * x4 + 5 * x - 4;      // y = x^5 - 2 * x^4 + 5x - 4.\n    y === 0;                            // Ensure that y = 0.\n}\n\ncomponent main = Polynomial();\n"
  },
  {
    "path": "tests/data/clojure.clj",
    "content": "; 19 lines 13 code 3 comments 3 blanks\n\n(ns clojure)\n\n; Below is a function\n(defn a-fn\n  \"Docstring with a column ;\"\n  [a b]\n  (+ 1 1))\n\n(defn a-fn2\n  ;\"Not a doc\"\n  \"Doc doc again\"\n  [a b] ; a and b right?\n  (let [multiline \"I'm\n  a multline\n  ; string\n  \"]\n       (str multline a b)))\n"
  },
  {
    "path": "tests/data/clojurec.cljc",
    "content": "; 19 lines 13 code 3 comments 3 blanks\n\n(ns clojure)\n\n; Below is a function\n(defn a-fn\n  \"Docstring with a column ;\"\n  [a b]\n  (+ 1 1))\n\n(defn a-fn2\n  ;\"Not a doc\"\n  \"Doc doc again\"\n  [a b] ; a and b right?\n  (let [multiline \"I'm\n  a multline\n  ; string\n  \"]\n       (str multline a b)))\n"
  },
  {
    "path": "tests/data/clojurescript.cljs",
    "content": "; 19 lines 13 code 3 comments 3 blanks\n\n(ns clojure)\n\n; Below is a function\n(defn a-fn\n  \"Docstring with a column ;\"\n  [a b]\n  (+ 1 1))\n\n(defn a-fn2\n  ;\"Not a doc\"\n  \"Doc doc again\"\n  [a b] ; a and b right?\n  (let [multiline \"I'm\n  a multline\n  ; string\n  \"]\n       (str multline a b)))\n"
  },
  {
    "path": "tests/data/cmake.cmake",
    "content": "# 25 lines 16 code 3 comments 6 blanks\n\nSET(_POSSIBLE_XYZ_INCLUDE include include/xyz)\nSET(_POSSIBLE_XYZ_EXECUTABLE xyz)\nSET(_POSSIBLE_XYZ_LIBRARY XYZ)\n\n# this is a comment\nIF(XYZ_FIND_VERSION_MAJOR AND XYZ_FIND_VERSION_MINOR)\n  SET(_POSSIBLE_SUFFIXES \"${XYZ_FIND_VERSION_MAJOR}${XYZ_FIND_VERSION_MINOR}\" \"${XYZ_FIND_VERSION_MAJOR}.${XYZ_FIND_VERSION_MINOR}\" \"-${XYZ_FIND_VERSION_MAJOR}.${XYZ_FIND_VERSION_MINOR}\") # not counted\nELSE(XYZ_FIND_VERSION_MAJOR AND XYZ_FIND_VERSION_MINOR)\n  SET(_POSSIBLE_SUFFIXES \"67\" \"92\" \"352.9\" \"0.0.8z\")\nENDIF(XYZ_FIND_VERSION_MAJOR AND XYZ_FIND_VERSION_MINOR)\n\nFOREACH(_SUFFIX ${_POSSIBLE_SUFFIXES})\n  LIST(APPEND _POSSIBLE_XYZ_INCLUDE \"include/XYZ${_SUFFIX}\")\n  LIST(APPEND _POSSIBLE_XYZ_EXECUTABLE \"XYZ${_SUFFIX}\")\n  LIST(APPEND _POSSIBLE_XYZ_LIBRARY \"XYZ${_SUFFIX}\")\nENDFOREACH(_SUFFIX) # not counted\n\nFIND_PROGRAM(XYZ_EXECUTABLE\n  NAMES ${_POSSIBLE_XYZ_EXECUTABLE}\n)\n\n# this is also a comment\n\n"
  },
  {
    "path": "tests/data/codeql.ql",
    "content": "//! 40 lines 17 code 15 comments 8 blanks\n\n/** \n * @name fu \n * @description bar \n *\n * Rerum similique consequatur non dolor sit. Autem doloribus sed in sint\n * ratione sit voluptates at. Nihil ut fugiat ab ut aliquid consequatur sunt\n * ullam. Adipisci voluptatem hic dicta.\n */\n\n// asdf\n\nimport cpp\nprivate import test.foo.bar.baz\n\n/**\n * Another comment.\n */\nclass C extends Expr {\n    C () {\n        // single comment\n        not this.test() and\n        not this.what()\n    }\n\n    private predicate what() {\n        /* TODO */\n        this.isAbstract()\n    }\n\n    predicate test() { this = \"what\" }\n}\n\nfrom Function f\nwhere \n    f.getName() = \"function\" and /* inline comment */\n    f.getArgument(0).asExpr() instanceof FooBar\nselect f, \"function\"\n\n"
  },
  {
    "path": "tests/data/cogent.cogent",
    "content": "-- 7 lines 2 code 2 comments 3 blanks\n\ntype A -- uncounted comment\n\n-- comment\n\ntype B\n"
  },
  {
    "path": "tests/data/cpp.cpp",
    "content": "/* 46 lines 37 code 3 comments 6 blanks */\n\n#include <stdio.h>\n\n// bubble_sort_function\nvoid bubble_sort(int a[10], int n) {\n  int t;\n  int j = n;\n  int s = 1;\n  while (s > 0) {\n    s = 0;\n    int i = 1;\n    while (i < j) {\n      if (a[i] < a[i - 1]) {\n        t = a[i];\n        a[i] = a[i - 1];\n        a[i - 1] = t;\n        s = 1;\n      }\n      i++;\n    }\n    j--;\n  }\n}\n\nint main() {\n  int a[] = {4, 65, 2, -31, 0, 99, 2, 83, 782, 1};\n  int n = 10;\n  int i = 0;\n\n  printf(R\"(Before sorting:\\n\\n\" )\");\n  // Single line comment\n  while (i < n) {\n    printf(\"%d \", a[i]);\n    i++;\n  }\n\n  bubble_sort(a, n);\n\n  printf(\"\\n\\nAfter sorting:\\n\\n\");\n  i = 0;\n  while (i < n) {\n    printf(\"%d \", a[i]);\n    i++;\n  }\n}\n"
  },
  {
    "path": "tests/data/cppm.cppm",
    "content": "/* 46 lines 37 code 3 comments 6 blanks */\n\n#include <stdio.h>\n\n// bubble_sort_function\nvoid bubble_sort(int a[10], int n) {\n  int t;\n  int j = n;\n  int s = 1;\n  while (s > 0) {\n    s = 0;\n    int i = 1;\n    while (i < j) {\n      if (a[i] < a[i - 1]) {\n        t = a[i];\n        a[i] = a[i - 1];\n        a[i - 1] = t;\n        s = 1;\n      }\n      i++;\n    }\n    j--;\n  }\n}\n\nint main() {\n  int a[] = {4, 65, 2, -31, 0, 99, 2, 83, 782, 1};\n  int n = 10;\n  int i = 0;\n\n  printf(R\"(Before sorting:\\n\\n\" )\");\n  // Single line comment\n  while (i < n) {\n    printf(\"%d \", a[i]);\n    i++;\n  }\n\n  bubble_sort(a, n);\n\n  printf(\"\\n\\nAfter sorting:\\n\\n\");\n  i = 0;\n  while (i < n) {\n    printf(\"%d \", a[i]);\n    i++;\n  }\n}\n"
  },
  {
    "path": "tests/data/crystal.cr",
    "content": "# 20 lines 14 code 2 comments 4 blanks\nx = 3\nif x < 2\n  p = \"Smaller\"\nelse\n  p = \"Bigger\"\nend\n\nmultiline_string = \"first line\nsecond line\"\n\nheredoc = <<-SOME\nhello\nSOME\n\n# testing.\nwhile x > 2 && x < 10\n  x += 1\nend\n\n"
  },
  {
    "path": "tests/data/csharp.cs",
    "content": "// 26 lines 14 code 9 comments 3 blanks\nnamespace Ns\n{\n    /*\n\n    multi-line comment\n\n    */\n    public class Cls\n    {\n        private const string BasePath = @\"a:\\\";\n\n        [Fact]\n        public void MyTest()\n        {\n            // Arrange.\n            Foo();\n\n            // Act.\n            Bar();\n\n            // Assert.\n            Baz();\n        }\n    }\n}\n"
  },
  {
    "path": "tests/data/cuda.cu",
    "content": "/* 7 lines 4 code 2 comments 1 blanks */\n\n// add vector\n__host__ void add(const int* a, const int* b, int* c) {\n    int i = threadIdx.x;\n    c[i] = a[i] + b[i];\n}\n"
  },
  {
    "path": "tests/data/cue.cue",
    "content": "// 12 lines 8 code 2 comments 2 blanks\n\n// A documentation comment\nmap: {\n\tnormal: \"normal string\" // inline comment (not counted)\n\n\tcontent: \"\"\"\n\tMulti-line string\n\t\"\"\"\n\traw: #\"A newline is \\#n written as \"\\n\".\"#\n\tbyte: '\\U0001F604'\n}\n"
  },
  {
    "path": "tests/data/cython.pyx",
    "content": "# 29 lines, 21 code, 3 comments, 5 blanks\n\n\ndef add(x, y):\n    '''\n    Hello World\n    # Real Second line\n    Second line\n    '''\n    string = \"Hello World  #\\\n    \"\n    y += len(string)\n    # Add the two numbers.\n    x + y\n\n\ncdef add2(x, y):\n    \"\"\"\n    Hello World\n    # Real Second line\n    Second line\n\n    Note that docstring lines are counted as code\n    \"\"\"\n\n    string = \"Hello World\"\n    y += len(string)\n    # Add the two numbers.\n    x + y\n"
  },
  {
    "path": "tests/data/d.d",
    "content": "/* 8 lines 5 code 1 comments 2 blanks */\n\nvoid main() {\n    auto x = 5; /+ a /+ nested +/ comment /* +/\n    writefln(\"hello\");\n    auto y = 4; // */\n}\n\n"
  },
  {
    "path": "tests/data/d2.d2",
    "content": "# 15 lines 4 code 6 comments 5 blanks\n\n# Comments start with a hash character and continue until the next newline or EOF.\nx -> y\n\nx -> y # I am at the end\n\n'#x' -> \"#y\"\n\n\"\"\"\nThis is a\nblock comment\n\"\"\"\n\ny -> z\n"
  },
  {
    "path": "tests/data/dhall.dhall",
    "content": "-- 16 lines 9 code 5 comments 2 blanks\n{- A comment within the interior of a multi-line literal counts as part of the\n   literal\n-}\n\n''\n-- Hello\n{- world -}\n''\n{ some = \"thing\"\n\n, keys = [\"can\"\n, \"have\",\n-- wait for it\n\"lists\"]\n}\n"
  },
  {
    "path": "tests/data/dreammaker.dm",
    "content": "// 17 lines 7 code 6 comments 4 blanks\n/*\n * /* Hello! */\n */\n\n/mob\n    // I can rely on this file to exist on disk.\n    var/some_file = './/dreammaker.dm'\n\n/mob/Login()\n    // Not counted. /* */ \n    world << \"// Say hello to [src]!\"\n\n    src << browse({\"\n    /*<a href=\"https://google.com\">Link</a>*/\n    \"}, \"window=google\")\n\n"
  },
  {
    "path": "tests/data/dust.dust",
    "content": "{! 10 lines 2 code 5 comments 3 blanks !}\n\n{! All Dust comments are multiline comments.  And there's no quoting\n   comment openers and closers.  Instead there are escape sequences\n   for producing literal brackets in template output: {~lb} outputs a\n   left-bracket.!} <h1>Hello\n\nworld!</h1>{! More comments !}\n\n<h2>~{lb}Goodbye, world~{rb}</h2>\n"
  },
  {
    "path": "tests/data/ebuild.ebuild",
    "content": "# 16 lines 9 code 2 comments 5 blanks\n\n# test comment\n\nEAPI=8\n\nDESCRIPTION=\"ebuild file\"\nHOMEPAGE=\"https://foo.example.org/\"\nSRC_URI=\"ftp://foo.example.org/${P}.tar.gz\"\n\nLICENSE=\"MIT\"\nSLOT=\"0\"\n\nsrc_compile() {\n\t:\n}\n"
  },
  {
    "path": "tests/data/edgeql.edgeql",
    "content": "# 28 lines 21 code 3 comments 4 blanks\n\nselect User {\n    name,\n    friends: {\n        name\n    },\n    has_i := .friends.name ilike '%i%',\n    has_o := .friends.name ilike '%o%',\n} filter .has_i or .has_o;\n\nselect <User>{} ?? User {name};\n\n# update the user with the name 'Alice Smith'\nwith module example\nupdate User\nfilter .name = 'Alice Smith'\nset {\n    name := 'Alice J. Smith'\n};\n\n# update all users whose name is 'Bob'\nwith module example\nupdate User\nfilter .name like 'Bob%'\nset {\n    name := User.name ++ '*'\n};\n"
  },
  {
    "path": "tests/data/edn.edn",
    "content": "; 11 lines 6 code 2 comments 3 blanks\n\n; Just some random data\n\n {:a [\n      1]\n\n  :b 1 ; this doesn't count as a comment\n  :c {1 1\n      2 2}\n  :d [1 2 3]}\n"
  },
  {
    "path": "tests/data/eight.8th",
    "content": "\\ 22 lines 9 code 8 comments 5 blanks\n\n(* multiline comments\n  (* a nested \n     comment *)\n *\n *)\n\n-- here's a single line comment\n\"Hello, \" var, foo -- line ending comment\n\n\\ here's another single line comment\n\"!\" var, bar \\ a different line ending comment\n\n: hello   \\ s --\n  foo @ s:<+ \n  bar @ s:+\n  . cr\n;\n\n\"World\" hello\nbye\n"
  },
  {
    "path": "tests/data/elvish.elv",
    "content": "# 16 lines, 9 code, 5 blanks, 2 comments\necho \"This is a\nmultiline string\n# with a hash\nin it.\"\n\necho 'This is a single-quoted string.'\n\n# This is a comment.\n\nuse re\n\nedit:after-readline = [\n  [line]{ print \"\\e]2;\"$line\"\\a\" > /dev/tty }\n]\n\n"
  },
  {
    "path": "tests/data/emacs_dev_env.ede",
    "content": ";; 16 lines 6 code 7 comments 3 blanks\n\n;; This is an EDE Project file\n\n;; Object ede-proj-project\n;; EDE project file.\n(ede-proj-project \"ede-proj-project\"\n  :name \"my-proj\"\n  :version \"1.0.0\"\n  :file \"Project.ede\"\n  :targets (list\n))\n\n;; Local Variables:\n;; mode: emacs-lisp\n;; End:\n"
  },
  {
    "path": "tests/data/emacs_lisp.el",
    "content": ";; 21 lines 11 code 6 comments 4 blanks\n\n                                        ; This is a comment line\n;; This too!\n;;; This 3!\n;;;; This 4!\n\n(setq some-global-var nil)              ;Comment\n\n;;;###autoload\n(defun some-fn ()\n  \"Some function.\"\n  (interactive)\n  (message \"I am some function\"))\n\n(defun fundamental-mode ()\n  \"Major mode not specialized for anything in particular.\nOther major modes are defined by comparison with this one.\"\n  (interactive)\n  (kill-all-local-variables)\n  (run-mode-hooks))\n"
  },
  {
    "path": "tests/data/emojicode.🍇",
    "content": "💭 24 lines 10 code 10 comments 4 blanks\n\n📘\n  This package is neat.\n📘\n\n💭🔜 Comment! 💭🔜 nested? 🔚💭\n\n📗\n  Simple docstring\n  with a quote 🔤\n📗\n🐇👨‍🚀🍇\n  💭 More quotes! 🔤\n\n  🐇❗👋 name🔡 🍇\n    😀 🍪 🔤Hello there, 🔤 name 🔤! ❌🔤\n      💭 no comment here🔤 🍪❗\n  🍉\n🍉\n\n🏁 🍇\n  👋🐇👨‍🚀 🔤Karen🔤❗\n🍉\n"
  },
  {
    "path": "tests/data/esdl.esdl",
    "content": "# 20 lines 13 code 4 comments 3 blanks\n\n# no module block\ntype default::Movie {\n    required property title -> str;\n    # the year of release\n    property year -> int64;\n    required link director -> default::Person;\n    required multi link actors -> default::Person;\n}\n\ntype default::Person {\n    required property first_name -> str;\n    required property last_name -> str;\n}\n\nabstract link friends_base {\n    # declare a specific title for the link\n    annotation title := 'Close contacts';\n}\n"
  },
  {
    "path": "tests/data/example.umpl",
    "content": "! 68 lines 58 code 2 comments 8 blanks\r\n\r\ncreate yum with 5\r\ncreate num with ((plus 7 6 6 yum))>\r\n(num)>\r\n((plus 1 num))> ! prints 17, the greater than signifies that this should be the printed, the bang symbol signifies a comment, numbers must be in hexadecimal but will be printed in decimal\r\n\r\npotato 😂3 ⧼ ! to create a function we use the potato keyword, functions must be nameed with a single emoji, we put the number of args and then ⧼⧽ is where the code goes\r\n    loop ⧼\r\n        (` `)>\r\n        if {$3} ⧼\r\n            break\r\n        ⧽ else ⧼\r\n            continue\r\n        ⧽\r\n    ⧽\r\n    potato 😂 1 ⧼\r\n      ((minus 1 10))> ! prints -15\r\n    ⧽\r\n    create final with ((add $1 $2))< ! ! we use the create keyword to create a variable and the with keyword to assign a value to the variable, to refrence the args from a function use $1 for the first arg and so on\r\n    ! adds both arg1 and arg2, the less than signifies that this should not be the printed and sets it to the final variable\r\n    return: ! we use (:) colon whe returning nothing\r\n⧽\r\n((new 😂 10 1 0x1A))> ! we use the new keyword to call a function followed by function names, prints 32, when the first digit of number is a letre prefix with 0x\r\n\r\nlist it with [((not true))> 0] ! creates a list with the values  1,2 (in hex), list can only have two elements and you cant declare recursive lists like: [0 [1 1]], but if you have an identiifer (d) that points to a list you can do: [9 d]\r\n((set-with it.first 2))<! sets the first element of the it list to 2, we can also use something like addwith mulitply with  which will add  or multiple by the variable to/by the value\r\n((set-with it.second 4))< ! sets the second element of the it list to 4 and print its\r\n\r\ncreate bool with true\r\ncreate other-bool with ((not bool))< ! creates a variable called bool and sets it to true, then creates a variable called other-bool and sets it to the opposite of bool, when we call a function in a variable defenition we must use an expression to call the function\r\n(other-bool)> ! prints false\r\n((set-with other-bool true))< ! we set other-bool to true\r\n\r\nif {other-bool} ⧼ ! the {} must evalte to a boolean, we can also use eq, ne lt, le etc\r\n  (`hello`)> ! the bactick (`) is used to encapsulate a string\r\n⧽ else ⧼\r\n    (`by`)>\r\n⧽\r\n\r\nif {true} ⧼ ! there is now else, use nested if elses, condotins must be in {} and we can use nested () within the \r\n  ((minus 1 10))> ! prints -15\r\n⧽ else ⧼\r\n  ((plus 1 10))> ! prints 17 \r\n⧽\r\n\r\nloop ⧼\r\n    potato 😂 1 ⧼\r\n    ((minus 1 10))> ! prints -15\r\n    ⧽\r\n  create inputs with ((input `continue?`))<\r\n  if {((eq inputs `y`))<} ⧼ ! there is now else, use nested if elses, condotins must be in {} and we can use nested () within the \r\n    potato 😂 1 ⧼\r\n      ((minus 1 10))> ! prints -15\r\n      ⧽\r\n    continue\r\n  ⧽ else ⧼\r\n      potato 😂 1 ⧼\r\n        ((minus 1 10))> ! prints -15\r\n      ⧽\r\n    break ! what the if/esle do can be nothing if you dont pu anything in the parenthesis\r\n  ⧽\r\n  ((inputs `continue?`))<\r\n⧽\r\n((new 😂 10 190 0x1A))> ! we use the new keyword to call a function followed by function names, prints 32, when the first digit of number is a letre prefix with 0x\r\n\r\n((set-with bool (input `ads`)))>\r\ncreate inputs with ((input `continue?`))<\r\n"
  },
  {
    "path": "tests/data/factor.factor",
    "content": "! 14 lines, 5 code, 6 comments, 3 blanks\n\n/* we can use some dependencies */\nUSING: math multiline sequences ;\n\n! this is a vocabulary\nIN: my-vocab\n\n! this comment describes this function\n: add ( x y -- z )\n    \"Hello World  !\\\n    \" length /*\n        Add the three numbers.\n    */ + + ;\n"
  },
  {
    "path": "tests/data/fennel.fnl",
    "content": ";; 18 lines 8 code 5 comments 5 blanks\n\n; this is a ; single comment\n;;;; this is also a single comment ;;;;;;\n\n                  ; \"this is a comment too!\"\n\n(local variable \"I ;am a ;variable!\")\n\n; (print \"\\\"I am commented out!\\\"\")\n(print \"\\\"Hello world!\\\"\") ; this is an ; end of line comment\n(print \"This is not a comment: ;\")\n(print \"This is a\n  multiline string\")\n\n(fn somefn [x]\n  (print \"I am some function.\")\n  (print \"My parameter is \" (string.format \"\\\"%s\\\"\" x)))\n"
  },
  {
    "path": "tests/data/flatbuffers.fbs",
    "content": "// 34 lines 21 code 6 comments 7 blanks\n\ninclude \"another_schema.fbs\";\n\nnamespace Example;\n\n// one line comment\nenum PhoneType: byte {\n    MOBILE,\n    HOME,\n    WORK\n}\n\n/* block comment\n   another line\n   end */\ntable PhoneNumber {\n    number: string;\n    type: PhoneType;\n}\n\n/// documentation comment\ntable Person {\n    name: string;\n    id: int32;\n    email: string;\n    phones: [PhoneNumber]; // a stray quote \"\n}\n\ntable AddressBook {\n    people: /* a block comment inside code */ [Person];\n}\n\nroot_type AddressBook; /* block /* comments cannot be nested (except the start comment) */\n"
  },
  {
    "path": "tests/data/forgecfg.cfg",
    "content": "# 79 lines 20 code 40 comments 19 blanks\n\n# Configuration file\n\n~CONFIG_VERSION: 0.4.0\n\n##########################################################################################################\n# advanced\n#--------------------------------------------------------------------------------------------------------#\n# Advanced config options to change the way JEI functions.\n##########################################################################################################\n\nadvanced {\n    # Move the JEI search bar to the bottom center of the screen. [default: false]\n    B:centerSearchBarEnabled=true\n    B:debugModeEnabled=false\n\n    # Choose if JEI should give ingredients direct to the inventory (inventory) or pick them up with the mouse (mouse_pickup).\n    # [Default: mouse_pickup]\n    # [Valid: [inventory, mouse_pickup]]\n    S:giveMode=inventory\n\n    # The maximum width of the ingredient list. [range: 4 ~ 100, default: 100]\n    I:maxColumns=100\n\n    # The maximum height of the recipe GUI. [range: 175 ~ 5000, default: 350]\n    I:maxRecipeGuiHeight=350\n\n    # How the mod name should be formatted in the tooltip for JEI GUIs. Leave blank to disable. [Default: blue italic] [Valid: [black, dark_blue, dark_green, dark_aqua, dark_red, dark_purple, gold, gray, dark_gray, blue, green, aqua, red, light_purple, yellow, white, obfuscated, bold, strikethrough, underline, italic]]\n    S:modNameFormat=blue italic\n\n    # Enable JEI memory usage optimizations. [default: true]\n    B:optimizeMemoryUsage=true\n}\n\n\n##########################################################################################################\n# search\n#--------------------------------------------------------------------------------------------------------#\n# Options relating to the search bar.\n##########################################################################################################\n\nsearch {\n    # Search mode for Colors (prefix: ^)\n    # [Default: disabled]\n    # [Valid: [enabled, require_prefix, disabled]]\n    S:colorSearchMode=DISABLED\n\n    # Search mode for Creative Tab Names (prefix: %)\n    # [Default: disabled]\n    # [Valid: [enabled, require_prefix, disabled]]\n    S:creativeTabSearchMode=DISABLED\n\n    # Search mode for Mod Names (prefix: @)\n    # [Default: require_prefix]\n    # [Valid: [enabled, require_prefix, disabled]]\n    S:modNameSearchMode=require_prefix\n\n    # Search mode for Ore Dictionary Names (prefix: $)\n    # [Default: disabled]\n    # [Valid: [enabled, require_prefix, disabled]]\n    S:oreDictSearchMode=require_prefix\n\n    # Search mode for resources ids (prefix: &)\n    # [Default: disabled]\n    # [Valid: [enabled, require_prefix, disabled]]\n    S:resourceIdSearchMode=enabled\n\n    # Search mode for Tooltips (prefix: #)\n    # [Default: enabled]\n    # [Valid: [enabled, require_prefix, disabled]]\n    S:tooltipSearchMode=enabled\n}\n\n\nsearchadvancedtooltips {\n    # config.jei.searchAdvancedTooltips.search.comment [default: false]\n    B:search=false\n}\n"
  },
  {
    "path": "tests/data/fsharp.fs",
    "content": "(* 15 lines 6 code 5 comments 4 blanks *)\n\n// Comment\n\nlet foo = (*\n    Comment\n*)\n5\n\nlet bar = \"(*\n    Code\n*)\"\n\nlet baz = @\"a:\\\"\n// Comment\n"
  },
  {
    "path": "tests/data/fstar.fst",
    "content": "(* 11 lines 3 code 5 comments 3 blanks *)\n\nmodule Hello\n\n(* multi\n   line\n   comment *)\nopen FStar.IO // uncounted comment\n\n// single line comment\nlet main = print_string \"Hello, F*!\\n\" (* uncounted comment *)\n"
  },
  {
    "path": "tests/data/ftl.ftl",
    "content": "<#-- 10 lines 5 code 3 comments 2 blanks -->\n<#ftl output_format=\"plainText\"/>\n\n<#-- Define the print macro -->\n<#macro print text>\n${text}\n</#macro>\n\n<#-- Print \"Hello world\" -->\n<@print \"Hello world\"/>\n"
  },
  {
    "path": "tests/data/futhark.fut",
    "content": "-- 8 lines 2 code 3 comments 3 blanks\n\n-- this is a test file\n\n-- add two\nlet f (x: i32) = x + 2\n\nlet main = f -- eta expand\n"
  },
  {
    "path": "tests/data/gas.S",
    "content": "// 67 lines 46 code 10 comments 11 blanks\n#include \"config.h\"\n\n/* BIG FAT WARNING GOES HERE */\n#define dbg(char) \\\n\tmov \\char, %ax ;\\\n\tout %ax, $0xe9 ;\\\n#endif\n\n.align 16\n.code16\n.section .resettext, \"xa\", @progbits\nreset_vector:\n\tcli\n\tjmp switch_modes\n\n.section .text\nswitch_modes:\n\t/*\n\t * The developer manual builds up the GDT, but since it should get\n\t * cached by the CPU, we can just have it in the flash.\n\t */\n\tmov %cs, %ax\n\tmov %ax, %ds\n\n\t/* Enable protected mode (PE) */\n\tmov %cr0, %eax\n\tor $1, %al\n\tmov %eax, %cr0\n\n\tljmpl $0x8,$protected_mode\n\n.code32\nprotected_mode:\n\tmov $0x10, %ax\n\tmov %ax, %ds\n\tmov %ax, %es\n\tmov %ax, %fs\n\tmov %ax, %gs\n\tmov %ax, %ss\n\n\ngdt_desc:\n\t.word egdt - gdt - 1\n\t.long gdt\n\n.align 8\ngdt:\n// 0 segment\n.long 0, 0\n\n// code (0x8)\n.word 0xffff\t// limit 15:0\n.word 0x0\t// base 15:0\n.byte 0x0\t// base 23:16\n.byte 0x9b\t// present, ring 0, executable, !conforming, readable, accessed\n.byte 0xcf\t// granularity size, limit[19:16] = f\n.byte 0x0\t// base 31:24\n\n// data (0x10)\n.word 0xffff\t// limit 15:0\n.word 0x0\t// base 15:0\n.byte 0x0\t// base 23:16\n.byte 0x93\t// present, priv=0, !executable, stack down, writable, accessed\n.byte 0xcf\t// granularity=1 size=1, limit 19:16 = f\n.byte 0x0\t// base 31:24\negdt:\n"
  },
  {
    "path": "tests/data/gdb.gdb",
    "content": "# 15 lines 7 code 5 comments 3 blanks\n#\n# This is a comment line. We don't have multi-comment lines\n#\n\nmacro define offsetof(_type, _memb)       ((long)(&((_type *)0)->_memb))\n\nbreak foo\ncontinue\n\n# Let's have something print when a breakpoint is hit.\ncommands 2\n  p i\n  continue\nend\n"
  },
  {
    "path": "tests/data/gdshader.gdshader",
    "content": "// 24 lines 6 code 13 comments 5 blanks\n/* \ntest\ntest\ntest\n*/ \n\n/* /** */\n\nvoid fragment() {\n    // there is not any string in gdshader\n    int x, b, c;\n    float p, q, r;\n    if(b == c) {\n\n    }\n    /**\n    //sdsadasdasdasd /*\n    **/\n\n    /* // odpwopdw\n    // */\n}\n\n"
  },
  {
    "path": "tests/data/gherkin.feature",
    "content": "# 13 lines 8 code 3 comments 2 blanks\nFeature: Guess the word\n\n  # The first example has two steps\n  Scenario: Maker starts a game\n    When the Maker starts a game\n    Then the Maker waits for a Breaker to join\n\n  # The second example has three steps\n  Scenario: Breaker joins a game\n    Given the Maker has started a game with the word \"silky\"\n    When the Breaker joins the Maker's game\n    Then the Breaker must guess a word with 5 characters\n"
  },
  {
    "path": "tests/data/gleam.gleam",
    "content": "//// 34 lines 24 code 4 comments 6 blanks\n\nimport gleam/option.{Option, None}\nimport gleam/io\n\npub type LoadedBool {\n  Yup\n  AlsoYup\n}\n\npub external type Person\n\npub opaque type Cat {\n  Cat(\n    name: String,\n    age: Int,\n    is_cute: LoadedBool,\n    owner: Some(Person),\n  )\n}\n\npub fn main() {\n  let jane = // Here is a quote \"\n    new_kitten(called: \"Jane\") \n  let kira = new_kitten(called: \"Kira\")\n  io.println(\"Two kitties!\")\n}\n\n/// A new baby kitten\n///\nfn new_kitten(called name: String) -> Cat {\n  // No owner yet!\n  Cat(name: name, age: 0, is_cute: Yup, owner: None)\n}\n"
  },
  {
    "path": "tests/data/glimmer_js.gjs",
    "content": "// 27 lines, 18 code, 6 comments, 3 blanks\nimport { helper } from '@ember/component/helper';\nimport { modifier } from 'ember-modifier';\n\n// A single-line comment\nconst plusOne = helper(([num]) => num + 1);\n\n/**\n * A multi-line comment\n */\nconst setScrollPosition = modifier((element, [position]) => {\n  element.scrollTop = position\n});\n\n<template>\n  <!-- A HTML-like comment -->\n  <div class=\"scroll-container\" {{setScrollPosition @scrollPos}}>\n    {{#each @items as |item index|}}\n      Item #{{plusOne index}}: {{item}}\n    {{/each}}\n  </div>\n  <style>\n    div {\n      background-color: #E04E39;\n    }\n  </style>\n</template>\n"
  },
  {
    "path": "tests/data/glimmer_ts.gts",
    "content": "// 18 lines, 10 code, 6 comments, 2 blanks\nimport type { TemplateOnlyComponent } from '@glimmer/component';\n\n// A single-line comment\nconst localVariable = 'foo';\n\n/**\n * A multi-line comment\n */\nconst Greet: TemplateOnlyComponent<{ name: string }> = <template>\n  <!-- A HTML-like comment -->\n  <p>Hello, {{@name}}! {{localVariable}}</p>\n  <style>\n    p {\n      background-color: #E04E39;\n    }\n  </style>\n</template>\n"
  },
  {
    "path": "tests/data/gml.gml",
    "content": "/* 17 lines 5 code 9 comments 3 blanks */\n\n/* here's a comment */\n\n/* \n    this is also\n    ...a comment!\n*/ \n\nvar a = 0;\n// @function b(c)\n// hi!\nfunction b(c) {\n    d = 5;\n    // how are you?\n}\ne = \"good how r u\";"
  },
  {
    "path": "tests/data/go.go",
    "content": "// 37 lines 24 code 6 comments 7 blanks\n\n// Package main is a test file.\npackage main\n\nimport (\n\t\"errors\"\n)\n\n/* /**/\nfunc main() {\n\tstart := \"/*\"\n\n\tfor {\n\t\tif len(start) >= 2 && start[1] == '*' && start[0] == '/' { // found the */\n\t\t\tbreak\n\t\t}\n\t}\n\n\tif err := Foo(42, start); err != nil {\n\t\tpanic(err)\n\t}\n}\n\n// Foo is a function. /* nested comment */\nfunc Foo(\n\t// first\n\ta int,\n\ts string, /* second */\n) (err error) {\n\tm := `a\nmultiline\nstring`\n\treturn errors.New(m)\n}\n\n// end of file\n"
  },
  {
    "path": "tests/data/gohtml.gohtml",
    "content": "<!-- 41 lines 20 code 14 comments 7 blanks -->\n<!DOCTYPE html>\n<html>\n  <head>\n    <meta charset=\"utf-8\" />\n    <meta name=\"viewport\" content=\"width=device-width\" />\n    <title>{{ .title }}</title>\n  </head>\n\n  <body>\n    <nav class=\"navbar navbar-default navbar-fixed-top navbar-custom\">\n      {{/* GoHTML comment */}}\n      <div id=\"modalSearch\" class=\"modal fade\" role=\"dialog\"> </div>\n    </nav>\n\n    <!-- HTML single line Comment-->\n    <main>\n      <article>\n        <h1>{{ .title }}</h1>\n        <p>{{ .text }}</p>\n      </article>\n    </main>\n\n    {{ template \"footer\" . }}\n\n  </body>\n\n  {{/*\n    GoHTML\n    multi line\n    comment\n  */}}\n\n  <!--\n          document.write(\"Multi-line and Code comment!\");\n  //-->\n\n  <!--[if IE 8]>\n          IE Special comment\n  <![endif]-->\n</html>\n"
  },
  {
    "path": "tests/data/graphql.gql",
    "content": "# 89 lines 71 code 3 comments 15 blanks\n\n\"\"\"\nA simple GraphQL schema which is well described. This is not a comment.\nSee: https://facebook.github.io/graphql/June2018/#sec-Descriptions\n\"\"\"\ntype Query {\n  \"\"\"\n  Translates a string from a given language into a different language.\n  \"\"\"\n  translate(\n    \"The original language that `text` is provided in.\"\n    fromLanguage: Language\n\n    \"The translated language to be returned.\"\n    toLanguage: Language\n\n    \"The text to be translated.\"\n    text: String\n  ): String\n}\n\n\"\"\"\nThe set of languages supported by `translate`.\n\"\"\"\nenum Language {\n  \"English\"\n  EN\n\n  \"French\"\n  FR\n\n  \"Chinese\"\n  CH\n}\n\n# Comment the query and use \"quotes\" inside the comment\nquery withFragments($expandedInfo: Boolean) {\n  user(id: \"3bd5a1cbed10e\") {\n    id # Insignificant comment\n\n    ... @include(if: $expandedInfo) {\n      firstName\n      lastName\n      birthday\n    }\n\n    friends(first: 10) {\n      ...friendFields\n    }\n\n    profiles(\n      handles: [\n        \"zuck\",\n        \"cocacola\",\n        \"#hashed#hash#inside\"\n      ]\n    ) {\n      handle\n\n      ... on User {\n        friends {\n          count\n        }\n      }\n\n      ... on Page {\n        likers {\n          count\n        }\n      }\n    }\n  }\n}\n\nfragment friendFields on User {\n  id\n  firstName\n  profilePic(size: 50)\n}\n\n# A simple GraphQL type definition\ntype User {\n  id: ID\n  firstName: String\n  lastName: String\n  birthday: Date\n}\n\n"
  },
  {
    "path": "tests/data/gwion.gw",
    "content": "#! 10 lines 8 code 1 comments 1 blanks\nclass C {\n  var int i;\n  var float f;\n  var Object o;\n  operator void @dtor () { <<< \"dtor\" >>>; }\n}\n\nvar C c;\n<<< c >>>;\n"
  },
  {
    "path": "tests/data/haml.haml",
    "content": "-# 18 lines 11 code 2 comments 5 blanks\n\n%section.container\n\n  - @posts.each do |post|\n    -# Ruby comment\n    %h1= post.title\n\n    %h2= post.subtitle\n\n    .content\n      = post.content\n\n    /\n      HTML comment. Not detected as of now.\n      %div\n        %span\n          This is all wrapped in a comment\n"
  },
  {
    "path": "tests/data/hcl.tf",
    "content": "# 22 lines 11 code 7 comments 4 blanks\nvariable \"foo\" \"bar\" {\n  default = \"yes\"\n}\n\n/* We like\n   multiple lines\n*/\n\nlocals {\n  // this\n  this = \"that\"\n\n  # list\n  more = [\"foo\", \"bar\"]\n\n  /* map */\n  map = {\n    yep  = \"nope\" # bad comment\n    nope = \"yep\"\n  }\n}\n"
  },
  {
    "path": "tests/data/headache.ha",
    "content": "// 13 lines 9 code 3 comments 1 blanks\n/* //The Headache cat program */\n// Implementation of Unix Cat in Headache\n\nvoid main() {\n    char x;\n    read x; //read from stdin\n    while(x){\n        @x; //print char x\n        read x; //read again from stdin\n        if(x - (255 as char)){} else x = 0; //necessary for terminal emulation\n    }\n}"
  },
  {
    "path": "tests/data/hex0.hex0",
    "content": "# 154 lines 89 code 34 comments 31 blanks\n# SPDX-FileCopyrightText: 2017 Jeremiah Orians <jeremiah@pdp10.guru>\n# SPDX-FileCopyrightText: 2023 Andrius Štikonas <andrius@stikonas.eu>\n#\n# SPDX-License-Identifier: GPL-3.0-or-later\n\n## ELF Header\n#:ELF_base\n7F 45 4C 46        ## e_ident[EI_MAG0-3] ELF's magic number\n\n02                 ## e_ident[EI_CLASS] Indicating 64 bit\n01                 ## e_ident[EI_DATA] Indicating little endianness\n01                 ## e_ident[EI_VERSION] Indicating original elf\n\n03                 ## e_ident[EI_OSABI] Set at 3 because FreeBSD is strict\n00                 ## e_ident[EI_ABIVERSION] Set at 0 because none cares\n\n00 00 00 00 00 00 00 ## e_ident[EI_PAD]\n02 00              ## e_type Indicating Executable\n3E 00              ## e_machine Indicating AMD64\n01 00 00 00        ## e_version Indicating original elf\n\n78 00 60 00 00 00 00 00 ## e_entry Address of the entry point (Number of bytes this header is + Base Address)\n40 00 00 00 00 00 00 00 ## e_phoff Address of program header table\n00 00 00 00 00 00 00 00 ## e_shoff Address of section header table\n\n00 00 00 00        ## e_flags\n40 00              ## e_ehsize Indicating our 64 Byte header\n\n38 00              ## e_phentsize size of a program header table\n01 00              ## e_phnum number of entries in program table\n\n00 00              ## e_shentsize size of a section header table\n00 00              ## e_shnum number of entries in section table\n\n00 00              ## e_shstrndx index of the section names\n\n## Program Header\n#:ELF_program_headers\n01 00 00 00             ## p_type\n01 00 00 00             ## p_flags: PF-X = 1\n00 00 00 00 00 00 00 00 ## p_offset\n\n00 00 60 00 00 00 00 00 ## p_vaddr\n00 00 60 00 00 00 00 00 ## p_physaddr\n\nE5 00 00 00 00 00 00 00 ## p_filesz\nE5 00 00 00 00 00 00 00 ## p_memsz\n\n01 00 00 00 00 00 00 00 ## Required alignment\n\n#:ELF_text\n\n# Where the ELF Header is going to hit\n# Simply jump to _start\n# Our main function\n#:_start (0x600078)\n\t58                  ; pop_rax         # Get the number of arguments\n\t5F                  ; pop_rdi         # Get the program name\n\t5F                  ; pop_rdi         # Get the actual input name\n\t31F6                ; xor_esi,esi     # prepare read_only, rsi = 0\n\t6A 02               ; push !2         # prepare syscall number\n\t58                  ; pop_rax         # the syscall number for open()\n\t99                  ; cdq             # Extra sure, rdx = 0\n\t0F05                ; syscall         # Now open that damn file\n\t5F                  ; pop_rdi         # Get the actual output name\n\t50                  ; push_rax        # Preserve the file pointer we were given\n\t66BE 4102           ; mov_si, @577    # Prepare file as O_WRONLY|O_CREAT|O_TRUNC\n\t66BA C001           ; mov_dx, @448    # Prepare file as RWX for owner only (700 in octal)\n\t6A 02               ; push !2         # prepare syscall number\n\t58                  ; pop_rax         # the syscall number for open()\n\t0F05                ; syscall         # Now open that damn file\n\t93                  ; xchg_ebx,eax    # Preserve the file pointer we were given\n\t99                  ; cdq             # rdx = 0 since file descriptor is nonnegative\n\tFFC2                ; inc_edx         # rdx = 1 (count for read/write)\n\n#:loop_reset_all (0x600096)\n\t31ED                ; xor_ebp,ebp     # ebp = 0 (no prior hex val)\n\n# Comment tracking is done with ecx.\n# ecx is decremented if we hit a\n# comment (';' or '#') and reset\n# if we hit a new-line.\n#:loop_reset_comment (0x600098)\n\t52                  ; push_rdx\n\t59                  ; pop_rcx         # Set no current comment\n#:loop_add_comment (0x60009A)\n\tFFC9                ; dec_ecx\n#:loop (0x60009C)\n\n\t# Read a byte\n\t5F                  ; pop_rdi         # Get infile\n\t54                  ; push_rsp\n\t5E                  ; pop_rsi         # Set buffer\n\t# rdx is already set to 1.\n\t31C0                ; xor_eax,eax     # Set read syscall in rax\n\t51                  ; push_rcx        # Save comment tracking\n\t0F05                ; syscall         # Do the actual read\n\t59                  ; pop_rcx         # Restore comment tracking\n\t57                  ; push_rdi        # Re-save infile\n\t85C0                ; test_eax,eax    # Check what we got\n\t75 06               ; jne !cont       # No EOF\n\n\t# Exit successfully\n\tB0 3C               ; mov_al, !60     # Set exit syscall in rax\n\t31FF                ; xor_edi,edi     # Set return success (rdi = 0)\n\t0F05                ; syscall         # Exit\n\n#:cont (0x6000B0)\n\t8A06                ; mov_al,[rsi]    # Move prog byte in eax\n\n\t# New line check\n\t3C 0A               ; cmp_al, !10     # Check new-line\n\t74 E2               ; je !loop_reset_comment # If new-line, end comment handling\n\n\t# In comment check\n\t85C9                ; test_ecx,ecx    # Skip byte if we are in a comment\n\t75 E2               ; jne !loop\n\n\t# Start comment check\n\t3C 23               ; cmp_al, !35     # Start of '#' comment\n\t74 DC               ; je !loop_add_comment\n\n\t3C 3B               ; cmp_al, !59     # Start of ';' comment\n\t74 D8               ; je !loop_add_comment\n\n\t# Start of hex str to int\n\t2C 30               ; sub_al, !48     # Subtract ascii '0' from al\n\t2C 0A               ; sub_al, !10     # Check for value in '0'-'9'\n\t72 08               ; jb !write       # We have hex value, write it\n\n\t2C 07               ; sub_al, !7      # Subtract ('A'-'0') from al\n\t24 DF               ; and_al, !0xDF   # Remove lower case bit\n\t3C 07               ; cmp_al, !7      # Check for value 'A'-'F'\n\t73 CC               ; jae !loop       # We have hex value, write it\n\n#:write (0x6000D0)\n\tC1E5 04             ; shl_ebp, !4     # Shift up existing hex digit\n\t04 0A               ; add_al, !10     # Finish converting ascii to raw value\n\t01C5                ; add_ebp,eax     # Combine the hex digits\n\n\t# Check if this is first digit in hex val\n\tF7DB                ; neg_ebx         # Flip sign of r10 to indicate we got a digit\n\t7C C1               ; jl !loop        # Negative -> first digit, get another one\n\n\t# We have both digits in low byte of ebp, good to write\n\t892E                ; mov_[rsi],ebp   # Move edge to buffer\n\t89DF                ; mov_edi,ebx     # Move outfile to rdi\n\tB0 01               ; mov_al, !1      # Set write syscall in rax\n\t0F05                ; syscall         # Do the write\n\tEB B1               ; jmp !loop_reset_all # Start a fresh byte\n\n#:ELF_end (0x6000E5)\n\n"
  },
  {
    "path": "tests/data/hex1.hex1",
    "content": "# 589 lines 387 code 91 comments 111 blanks\n# SPDX-FileCopyrightText: 2016 Jeremiah Orians <jeremiah@pdp10.guru>\n# SPDX-FileCopyrightText: 2017 Jan Nieuwenhuizen <janneke@gnu.org>\n#\n# SPDX-License-Identifier: GPL-3.0-or-later\n\n## ELF Header\n# :ELF_base\n7F 45 4C 46        ## e_ident[EI_MAG0-3] ELF's magic number\n\n02                 ## e_ident[EI_CLASS] Indicating 64 bit\n01                 ## e_ident[EI_DATA] Indicating little endianness\n01                 ## e_ident[EI_VERSION] Indicating original elf\n\n03                 ## e_ident[EI_OSABI] Set at 3 because FreeBSD is strict\n00                 ## e_ident[EI_ABIVERSION] Set at 0 because none cares\n\n00 00 00 00 00 00 00 ## e_ident[EI_PAD]\n02 00              ## e_type Indicating Executable\n3E 00              ## e_machine Indicating AMD64\n01 00 00 00        ## e_version Indicating original elf\n\n78 00 60 00 00 00 00 00 ## e_entry Address of the entry point (Number of bytes this header is + Base Address)\n40 00 00 00 00 00 00 00 ## e_phoff Address of program header table\n00 00 00 00 00 00 00 00 ## e_shoff Address of section header table\n\n00 00 00 00        ## e_flags\n40 00              ## e_ehsize Indicating our 64 Byte header\n\n38 00              ## e_phentsize size of a program header table\n01 00              ## e_phnum number of entries in program table\n\n00 00              ## e_shentsize size of a section header table\n00 00              ## e_shnum number of entries in section table\n\n00 00              ## e_shstrndx index of the section names\n\n## Program Header\n# :ELF_program_headers\n01 00 00 00             ## p_type\n07 00 00 00             ## ph_flags: PF-X|PF-W|PF-R = 7\n00 00 00 00 00 00 00 00 ## p_offset\n\n00 00 60 00 00 00 00 00 ## p_vaddr\n00 00 60 00 00 00 00 00 ## p_physaddr\n\nEF 05 00 00 00 00 00 00 ## p_filesz\nEF 05 00 00 00 00 00 00 ## p_memsz\n\n01 00 00 00 00 00 00 00 ## Required alignment\n\n# :ELF_text\n\n# Where the ELF Header is going to hit\n# Simply jump to _start\n# Our main function\n\n\t# Register usage:\n\t# RAX, RDX, RSI, RDI => Temps\n\t# R15 => Flag\n\t# R14 => High bits\n\t# R13 => IP\n\t# R12 => MALLOC\n\t# R11 => HEAD\n\n\t# Struct format: (size 24)\n\t# NEXT => 0\n\t# TARGET => 8\n\t# NAME => 16\n\n# :_start\n\t48C7C7 00000000             ; mov_rdi, %0                 # Get current pointer\n\tE8 %w                       ; call %malloc                # Get current HEAP\n\t4889C7                      ; mov_rdi,rax                 # Using current\n\t4989C4                      ; mov_r12,rax                 # Setup MALLOC\n\t4881C7 00008000             ; add_rdi, %8388608           # Create space for temp [8MB]\n\tE8 %w                       ; call %malloc                # Give ourselves 8192000 bytes to work with\n\n\t4C8925 %T                   ; mov_[rip+DWORD],r12 %scratch # Allocate space for scratch area\n\t4981C4 00080000             ; add_r12, %0x800             # 2 KiB of scratch\n\n\t58                          ; pop_rax                     # Get the number of arguments\n\t5F                          ; pop_rdi                     # Get the program name\n\t5F                          ; pop_rdi                     # Get the actual input name\n\t48C7C6 00000000             ; mov_rsi, %0                 # prepare read_only\n\t48C7C0 02000000             ; mov_rax, %2                 # the syscall number for open()\n\t0F05                        ; syscall                     # Now open that damn file\n\t4989C1                      ; mov_r9,rax                  # Preserve the file pointer we were given\n\n\t5F                          ; pop_rdi                     # Get the actual output name\n\t48C7C6 41020000             ; mov_rsi, %577               # Prepare file as O_WRONLY|O_CREAT|O_TRUNC\n\t48C7C2 C0010000             ; mov_rdx, %448               # Prepare file as RWX for owner only (700 in octal)\n\t48C7C0 02000000             ; mov_rax, %2                 # the syscall number for open()\n\t0F05                        ; syscall                     # Now open that damn file\n\t4883F8 00                   ; cmp_rax, !0                 # Check for missing output\n\t0F8F %R                     ; jg %_start_out              # Have real input\n\t48C7C0 01000000             ; mov_rax, %1                 # Use stdout\n\n:R # :_start_out\n\t4989C2                      ; mov_r10,rax                 # Preserve the file pointer we were given\n\n\tE8 %H                       ; call %ClearScratch          # Zero scratch\n\t49C7C7 FFFFFFFF             ; mov_r15, %-1                # Our flag for byte processing\n\t49C7C6 00000000             ; mov_r14, %0                 # temp storage for the sum\n\t49C7C5 00006000             ; mov_r13, %0x00600000        # Our starting IP\n\t49C7C3 00000000             ; mov_r11, %0                 # HEAD = NULL\n\tE8 %a                       ; call %First_pass            # Process it\n\n\t# rewind input file\n\t4C89CF                      ; mov_rdi,r9                  # Using our input file\n\t48C7C6 00000000             ; mov_rsi, %0                 # Offset Zero\n\t48C7C2 00000000             ; mov_rdx, %0                 # Whence Zero\n\t48C7C0 08000000             ; mov_rax, %8                 # lseek\n\t4153                        ; push_r11                    # Protect HEAD\n\t0F05                        ; syscall\n\t415B                        ; pop_r11                     # Restore HEAD\n\n\t49C7C7 FFFFFFFF             ; mov_r15, %-1                # Our flag for byte processing\n\t49C7C6 00000000             ; mov_r14, %0                 # temp storage for the sum\n\t49C7C5 00006000             ; mov_r13, %0x00600000        # Our starting IP\n\tE8 %k                       ; call %Second_pass           # Process it\n\n\tE9 %v                       ; jmp %Done\n\n:a # :First_pass\n\tE8 %x                       ; call %Read_byte\n\n\t# Deal with EOF\n\t4883F8 FC                   ; cmp_rax, !-4\n\t0F84 %i                     ; je %First_pass_done\n\n\t# Check for :\n\t4883F8 3A                   ; cmp_rax, !0x3A\n\t0F85 %b                     ; jne %First_pass_0\n\n\t# Deal with label\n\tE9 %C                       ; jmp %StoreLabel\n\n:b # :First_pass_0\n\t# Check for !\n\t4883F8 21                   ; cmp_rax, !0x21\n\t0F84 %h                     ; je %First_pass_pointer\n\n\t# Check for @\n\t4883F8 40                   ; cmp_rax, !0x40\n\t0F84 %h                     ; je %First_pass_pointer\n\n\t# Check for $\n\t4883F8 24                   ; cmp_rax, !0x24\n\t0F84 %h                     ; je %First_pass_pointer\n\n\t# Check for %\n\t4883F8 25                   ; cmp_rax, !0x25\n\t0F84 %h                     ; je %First_pass_pointer\n\n\t# Check for &\n\t4883F8 26                   ; cmp_rax, !0x26\n\t0F84 %h                     ; je %First_pass_pointer\n\n\t# Deal with everything else\n\tE8 %j                       ; call %hex                   # Process our char\n\n\t# Deal with EOF\n\t4883F8 FC                   ; cmp_rax, !-4\n\t0F84 %i                     ; je %First_pass_done\n\n\t# deal with -1 values\n\t4883F8 00                   ; cmp_rax, !0\n\t0F8C %a                     ; jl %First_pass\n\n\t# deal with toggle\n\t4983FF 00                   ; cmp_r15, !0\n\t0F84 %c                     ; je %First_pass_1\n\t4983C5 01                   ; add_r13, !1                 # Increment IP\n\n:c # :First_pass_1\n\t49F7D7                      ; not_r15\n\tE9 %a                       ; jmp %First_pass\n\n:d # :Update_Pointer\n\t# Check for !\n\t4883F8 21                   ; cmp_rax, !0x21\n\t0F84 %g                     ; je %Update_Pointer_1\n\n\t# Check for @\n\t4883F8 40                   ; cmp_rax, !0x40\n\t0F84 %f                     ; je %Update_Pointer_2\n\n\t# Check for $\n\t4883F8 24                   ; cmp_rax, !0x24\n\t0F84 %f                     ; je %Update_Pointer_2\n\n\t# Check for %\n\t4883F8 25                   ; cmp_rax, !0x25\n\t0F84 %e                     ; je %Update_Pointer_4\n\n\t# Check for &\n\t4883F8 26                   ; cmp_rax, !0x26\n\t0F84 %e                     ; je %Update_Pointer_4\n\n\t# deal with bad input\n\tE8 %Q                       # call %fail\n\n:e # :Update_Pointer_4\n\t4983C5 02                   ; add_r13, !2                 # Increment IP\n:f # :Update_Pointer_2\n\t4983C5 01                   ; add_r13, !1                 # Increment IP\n:g # :Update_Pointer_1\n\t4983C5 01                   ; add_r13, !1                 # Increment IP\n\tC3                          ; ret\n\n:h # :First_pass_pointer\n\t# Deal with Pointer to label\n\tE8 %d                       ; call %Update_Pointer        # Increment IP\n\t488B1D %T                   ; mov_rbx,[rip+DWORD] %scratch # Using scratch\n\tE8 %A                       ; call %consume_token         # Read token\n\tE8 %H                       ; call %ClearScratch          # Throw away token\n\t4883F8 3E                   ; cmp_rax, !0x3E              # check for '>'\n\t0F85 %a                     ; jne %First_pass             # Loop again\n\n\t# Deal with %label>label case\n\t488B1D %T                   ; mov_rbx,[rip+DWORD] %scratch # Write to scratch\n\tE8 %A                       ; call %consume_token         # get token\n\tE8 %H                       ; call %ClearScratch          # Clean up after ourselves\n\tE9 %a                       ; jmp %First_pass             # Loop again\n\n:i # :First_pass_done\n\tC3                          ; ret\n\n:j # :hex\n\t# deal with EOF\n\t4883F8 FC                   ; cmp_rax, !-4\n\t0F84 %n                     ; je %EOF\n\t# deal with line comments starting with #\n\t4883F8 23                   ; cmp_rax, !0x23\n\t0F84 %s                     ; je %ascii_comment\n\t# deal with line comments starting with ;\n\t4883F8 3B                   ; cmp_rax, !0x3B\n\t0F84 %s                     ; je %ascii_comment\n\t# deal all ascii less than 0\n\t4883F8 30                   ; cmp_rax, !0x30\n\t0F8C %r                     ; jl %ascii_other\n\t# deal with 0-9\n\t4883F8 3A                   ; cmp_rax, !0x3A\n\t0F8C %o                     ; jl %ascii_num\n\t# deal with all ascii less than A\n\t4883F8 41                   ; cmp_rax, !0x41\n\t0F8C %r                     ; jl %ascii_other\n\t# deal with A-F\n\t4883F8 47                   ; cmp_rax, !0x47\n\t0F8C %q                     ; jl %ascii_high\n\t# deal with all ascii less than a\n\t4883F8 61                   ; cmp_rax, !0x61\n\t0F8C %r                     ; jl %ascii_other\n\t# deal with a-f\n\t4883F8 67                   ; cmp_rax, !0x67\n\t0F8C %p                     ; jl %ascii_low\n\t# The rest that remains needs to be ignored\n\tE9 %r                       ; jmp %ascii_other\n\n:k # :Second_pass\n\tE8 %x                       ; call %Read_byte\n\n\t# Deal with EOF\n\t4883F8 FC                   ; cmp_rax, !-4\n\t0F84 %m                     ; je %Second_pass_done\n\n\t# Simply drop the label\n\t4883F8 3A                   ; cmp_rax, !0x3A\n\t0F85 %l                     ; jne %Second_pass_0\n\n\t488B1D %T                   ; mov_rbx,[rip+DWORD] %scratch # Using scratch\n\tE8 %A                       ; call %consume_token         # Read token\n\tE8 %H                       ; call %ClearScratch          # Throw away token\n\n\tE9 %k                       ; jmp %Second_pass\n\n:l # :Second_pass_0\n\t# Deal with % pointer\n\t4883F8 25                   ; cmp_rax, !0x25\n\t0F84 %L                     ; je %StorePointer_rel4\n\n\t# Deal with @ pointer\n\t4883F8 40                   ; cmp_rax, !0x40\n\t0F84 %M                     ; je %StorePointer_rel2\n\n\t# Deal with ! pointer\n\t4883F8 21                   ; cmp_rax, !0x21\n\t0F84 %N                     ; je %StorePointer_rel1\n\n\t# Deal with & pointer\n\t4883F8 26                   ; cmp_rax, !0x26\n\t0F84 %O                     ; je %StorePointer_abs4\n\n\t# Deal with $ pointer\n\t4883F8 24                   ; cmp_rax, !0x24\n\t0F84 %P                     ; je %StorePointer_abs2\n\n# :Second_pass_1\n\t# Deal with everything else\n\tE8 %j                       ; call %hex                   # Process our char\n\n\t# Deal with EOF\n\t4883F8 FC                   ; cmp_rax, !-4\n\t0F84 %m                     ; je %Second_pass_done\n\n\t# deal with -1 values\n\t4883F8 00                   ; cmp_rax, !0\n\t0F8C %k                     ; jl %Second_pass\n\n\t# deal with toggle\n\t4983FF 00                   ; cmp_r15, !0\n\t0F84 %u                     ; je %print\n\n\t# process first byte of pair\n\t4989C6                      ; mov_r14,rax\n\t49C7C7 00000000             ; mov_r15, %0\n\tE9 %k                       ; jmp %Second_pass\n\n:m # :Second_pass_done\n:n # :EOF\n\tC3                          ; ret\n\n:o # :ascii_num\n\t83E8 30                     ; sub_rax, !0x30\n\tC3                          ; ret\n:p # :ascii_low\n\t83E8 57                     ; sub_rax, !0x57\n\tC3                          ; ret\n:q # :ascii_high\n\t83E8 37                     ; sub_rax, !0x37\n\tC3                          ; ret\n:r # :ascii_other\n\t48C7C0 FFFFFFFF             ; mov_rax, %-1\n\tC3                          ; ret\n:s # :ascii_comment\n\tE8 %x                       ; call %Read_byte\n\t4883F8 0D                   ; cmp_rax, !0x0D\n\t0F84 %t                     ; je %ascii_comment_cr\n\t4883F8 0A                   ; cmp_rax, !0x0A\n\t0F85 %s                     ; jne %ascii_comment\n:t # :ascii_comment_cr\n\t48C7C0 FFFFFFFF             ; mov_rax, %-1\n\tC3                          ; ret\n\n# process second byte of pair\n:u # :print\n\t# update the sum and store in output\n\t49C1E6 04                   ; shl_r14, !4\n\t4C01F0                      ; add_rax,r14\n\n\t# flip the toggle\n\t49F7D7                      ; not_r15\n\n\t# Print our first Hex\n\t48C7C2 01000000             ; mov_rdx, %1                 # set the size of chars we want\n\tE8 %z                       ; call %print_chars\n\n\t4983C5 01                   ; add_r13, !1                 # Increment IP\n\tE9 %k                       ; jmp %Second_pass\n\n:v # :Done\n\t# program completed Successfully\n\t48C7C7 00000000             ; mov_rdi, %0                 # All is well\n\t48C7C0 3C000000             ; mov_rax, %0x3C              # put the exit syscall number in eax\n\t0F05                        ; syscall                     # Call it a good day\n\n\n# Malloc isn't actually required if the program being built fits in the initial memory\n# However, it doesn't take much to add it.\n# Requires a value in RDI\n:w # :malloc\n\t48C7C0 0C000000             ; mov_rax, %12                # the Syscall # for SYS_BRK\n\t4153                        ; push_r11                    # Protect r11\n\t0F05                        ; syscall                     # call the Kernel\n\t415B                        ; pop_r11                     # Restore r11\n\tC3                          ; ret\n\n\n:x # :Read_byte\n\t# Attempt to read 1 byte from STDIN\n\t48C7C2 01000000             ; mov_rdx, %1                 # set the size of chars we want\n\t488D35 %S                   ; lea_rsi,[rip+DWORD] %write  # Where to put it\n\t4C89CF                      ; mov_rdi,r9                  # Where are we reading from\n\t48C7C0 00000000             ; mov_rax, %0                 # the syscall number for read\n\t4153                        ; push_r11                    # Protect r11\n\t0F05                        ; syscall                     # call the Kernel\n\t415B                        ; pop_r11                     # Restore r11\n\n\t4885C0                      ; test_rax,rax                # check what we got\n\t0F84 %y                     ; je %Read_byte_1             # Got EOF call it done\n\n\t# load byte\n\t8A05 %S                     ; mov_al,[rip+DWORD] %write   # load char\n\t480FB6C0                    ; movzx_rax,al                # We have to zero extend it to use it\n\tC3                          ; ret\n\n# Deal with EOF\n:y # :Read_byte_1\n\t48C7C0 FCFFFFFF             ; mov_rax, %-4                # Put EOF in rax\n\tC3                          ; ret\n\n:z # :print_chars\n\t50                          ; push_rax                    # Push address of chars onto stack\n\t4889E6                      ; mov_rsi,rsp                 # What we are writing\n\t4C89D7                      ; mov_rdi,r10                 # Write to target file\n\t48C7C0 01000000             ; mov_rax, %1                 # the syscall number for write\n\t4153                        ; push_r11                    # Protect HEAD\n\t0F05                        ; syscall                     # call the Kernel\n\t415B                        ; pop_r11                     # Restore HEAD\n\t58                          ; pop_rax                     # deallocate stack\n\tC3                          ; ret\n\n\t# Receives pointer in RBX\n\t# Writes out char and updates RBX\n:A # :consume_token\n\tE8 %x                       ; call %Read_byte             # Consume_token\n\n\t# Check for \\t\n\t4883F8 09                   ; cmp_rax, !0x09\n\t0F84 %B                     ; je %consume_token_done\n\n\t# Check for \\n\n\t4883F8 0A                   ; cmp_rax, !0x0A\n\t0F84 %B                     ; je %consume_token_done\n\n\t# Check for ' '\n\t4883F8 20                   ; cmp_rax, !0x20\n\t0F84 %B                     ; je %consume_token_done\n\n\t# Check for '>'\n\t4883F8 3E                   ; cmp_rax, !0x3E\n\t0F84 %B                     ; je %consume_token_done\n\n\t# Looks like we are still reading token\n\t8803                        ; mov_[rbx],al                # Store char\n\t4883C3 01                   ; add_rbx, !1                 # Point to next spot\n\tE9 %A                       ; jmp %consume_token          # loop until done\n\n:B # :consume_token_done\n\t48C7C1 00000000             ; mov_rcx, %0                 # Pad with nulls\n\t48890B                      ; mov_[rbx],rcx\n\t4883C3 08                   ; add_rbx, !8\n\tC3                          ; ret\n\n:C # :StoreLabel\n\t4C89E0                      ; mov_rax,r12                 # ENTRY\n\t4981C4 18000000             ; add_r12, %24                # CALLOC\n\t4C8968 08                   ; mov_[rax+BYTE],r13 !8       # ENTRY->TARGET = IP\n\t4C8918                      ; mov_[rax],r11               # ENTRY->NEXT = JUMP_TABLE\n\t4989C3                      ; mov_r11,rax                 # JUMP_TABLE = ENTRY\n\t4D8963 10                   ; mov_[r11+BYTE],r12 !16      # ENTRY->NAME = TOKEN\n\t4C89E3                      ; mov_rbx,r12                 # Write Starting after struct\n\tE8 %A                       ; call %consume_token         # Collect whole string\n\t4989DC                      ; mov_r12,rbx                 # Update HEAP\n\tE9 %a                       ; jmp %First_pass\n\n:D # :GetTarget\n\t488B3D %T                   ; mov_rdi,[rip+DWORD] %scratch # Reset scratch\n\t4C89D9                      ; mov_rcx,r11                 # Grab JUMP_TABLE\n\t488B71 10                   ; mov_rsi,[rcx+BYTE] !16      # I->NAME\n:E # :GetTarget_loop\n\t8A06                        ; mov_al,[rsi]                # I->NAME[0]\n\t8A1F                        ; mov_bl,[rdi]                # scratch[0]\n\t480FB6DB                    ; movzx_rbx,bl                # Zero extend\n\t480FB6C0                    ; movzx_rax,al                # Zero extend\n\t38D8                        ; cmp_al,bl                   # IF TOKEN == I->NAME\n\t0F85 %F                     ; jne %GetTarget_miss         # Oops\n\n\t4883C6 01                   ; add_rsi, !1\n\t4881C7 01000000             ; add_rdi, %1\n\t3C 00                       ; cmp_al, !0\n\t0F85 %E                     ; jne %GetTarget_loop         # Loop until\n\tE9 %G                       ; jmp %GetTarget_done         # Match\n\n\t# Miss\n:F # :GetTarget_miss\n\t488B09                      ; mov_rcx,[rcx]               # I = I->NEXT\n\t4883F9 00                   ; cmp_rcx, !0                 # IF NULL == I\n\t0F84 %Q                     ; je %fail                    # Abort hard\n\n\t488B71 10                   ; mov_rsi,[rcx+BYTE] !16      # I->NAME\n\t488B3D %T                   ; mov_rdi,[rip+DWORD] %scratch # Reset scratch\n\tE9 %E                       ; jmp %GetTarget_loop\n\n:G # :GetTarget_done\n\t488B41 08                   ; mov_rax,[rcx+BYTE] !8       # Get address\n\tC3                          ; ret\n\n:H # :ClearScratch\n\t50                          ; push_rax                    # Protect against changes\n\t53                          ; push_rbx                    # And overwrites\n\t51                          ; push_rcx                    # While we work\n\t488B1D %T                   ; mov_rbx,[rip+DWORD] %scratch # Where our scratch is\n\t48C7C0 00000000             ; mov_rax, %0                 # Using null\n\n:I # :ClearScratch_loop\n\t488B0B                      ; mov_rcx,[rbx]               # Get current value\n\t8803                        ; mov_[rbx],al                # Because we want null\n\t4883C3 01                   ; add_rbx, !1                 # Increment\n\t4883F9 00                   ; cmp_rcx, !0                 # Check if we hit null\n\t0F85 %I                     ; jne %ClearScratch_loop      # Keep looping\n\n\t59                          ; pop_rcx                     # Don't Forget to\n\t5B                          ; pop_rbx                     # Restore Damage\n\t58                          ; pop_rax                     # Entirely\n\tC3                          ; ret\n\n:J # :StorePointer\n\tE8 %d                       ; call %Update_Pointer        # Increment IP\n\t488B1D %T                   ; mov_rbx,[rip+DWORD] %scratch # Write to scratch\n\tE8 %A                       ; call %consume_token         # get token\n\t50                          ; push_rax                    # Protect base_sep_p\n\t488B05 %T                   ; mov_rax,[rip+DWORD] %scratch # Pointer to scratch\n\tE8 %D                       ; call %GetTarget             # Get address of pointer\n\tE8 %H                       ; call %ClearScratch          # Clean up after ourselves\n\t4C89EA                      ; mov_rdx,r13                 # base = IP\n\t5B                          ; pop_rbx                     # Restore base_sep_p\n\t4883FB 3E                   ; cmp_rbx, !0x3E              # If base_sep_p == '>'\n\t0F85 %K                     ; jne %StorePointer_done      # If not\n\n\t# Deal with %label>label case\n\t50                          ; push_rax                    # We need to preserve main target\n\t488B1D %T                   ; mov_rbx,[rip+DWORD] %scratch # Write to scratch\n\tE8 %A                       ; call %consume_token         # get token\n\t488B05 %T                   ; mov_rax,[rip+DWORD] %scratch # Pointer to scratch\n\tE8 %D                       ; call %GetTarget             # Get address of pointer\n\tE8 %H                       ; call %ClearScratch          # Clean up after ourselves\n\t4889C2                      ; mov_rdx,rax                 # Use our new base\n\t58                          ; pop_rax                     # Restore main target\n\n:K # :StorePointer_done\n\tC3                          ; ret\n\n:L # :StorePointer_rel4\n\tE8 %J                       ; call %StorePointer          # Do Common\n\t4829D0                      ; sub_rax,rdx                 # target - ip\n\t48C7C2 04000000             ; mov_rdx, %4                 # set the size of chars we want\n\tE8 %z                       ; call %print_chars\n\tE8 %H                       ; call %ClearScratch          # Clean up after ourselves\n\tE9 %k                       ; jmp %Second_pass\n\n:M # :StorePointer_rel2\n\tE8 %J                       ; call %StorePointer          # Do Common\n\t4829D0                      ; sub_rax,rdx                 # target - ip\n\t48C7C2 02000000             ; mov_rdx, %2                 # set the size of chars we want\n\tE8 %z                       ; call %print_chars\n\tE8 %H                       ; call %ClearScratch          # Clean up after ourselves\n\tE9 %k                       ; jmp %Second_pass\n\n:N # :StorePointer_rel1\n\tE8 %J                       ; call %StorePointer          # Do Common\n\t4829D0                      ; sub_rax,rdx                 # target - ip\n\t48C7C2 01000000             ; mov_rdx, %1                 # set the size of chars we want\n\tE8 %z                       ; call %print_chars\n\tE8 %H                       ; call %ClearScratch          # Clean up after ourselves\n\tE9 %k                       ; jmp %Second_pass\n\n:O # :StorePointer_abs4\n\tE8 %J                       ; call %StorePointer          # Do Common\n\t48C7C2 04000000             ; mov_rdx, %4                 # set the size of chars we want\n\tE8 %z                       ; call %print_chars\n\tE8 %H                       ; call %ClearScratch          # Clean up after ourselves\n\tE9 %k                       ; jmp %Second_pass\n\n:P # :StorePointer_abs2\n\tE8 %J                       ; call %StorePointer          # Do Common\n\t48C7C2 02000000             ; mov_rdx, %2                 # set the size of chars we want\n\tE8 %z                       ; call %print_chars\n\tE8 %H                       ; call %ClearScratch          # Clean up after ourselves\n\tE9 %k                       ; jmp %Second_pass\n\n:Q # :fail\n\t# Some shit went wrong\n\t48C7C7 01000000             ; mov_rdi, %1                 # All is wrong\n\t48C7C0 3C000000             ; mov_rax, %0x3C              # put the exit syscall number in eax\n\t0F05                        ; syscall                     # Call it a good day\n\n\n:S # :write\n\t00000000                    ; NULL\n\t00000000                    ; NULL\n\n:T # :scratch\n\t00000000                    ; NULL\n\t00000000                    ; NULL\n\n# :ELF_end\n\n"
  },
  {
    "path": "tests/data/hex2.hex2",
    "content": "# 110 lines 78 code 7 comments 25 blanks\n# SPDX-FileCopyrightText: 2019 Jeremiah Orians <jeremiah@pdp10.guru>\n#\n# SPDX-License-Identifier: GPL-3.0-or-later\n\n## ELF Header\n\n:ELF_base\n7F 45 4C 46        ## e_ident[EI_MAG0-3] ELF's magic number\n\n02                 ## e_ident[EI_CLASS] Indicating 64 bit\n01                 ## e_ident[EI_DATA] Indicating little endianness\n01                 ## e_ident[EI_VERSION] Indicating original elf\n\n03                 ## e_ident[EI_OSABI] Set at 3 because FreeBSD is strict\n00                 ## e_ident[EI_ABIVERSION] Set at 0 because none cares\n\n00 00 00 00 00 00 00 ## e_ident[EI_PAD]\n02 00              ## e_type Indicating Executable\n3E 00              ## e_machine Indicating AMD64\n01 00 00 00        ## e_version Indicating original elf\n\n&_start 00 00 00 00 ## e_entry Address of the entry point (Number of bytes this header is + Base Address)\n%ELF_program_headers>ELF_base 00 00 00 00 ## e_phoff Address of program header table\n00 00 00 00 00 00 00 00 ## e_shoff Address of section header table\n\n00 00 00 00        ## e_flags\n40 00              ## e_ehsize Indicating our 64 Byte header\n\n38 00              ## e_phentsize size of a program header table\n01 00              ## e_phnum number of entries in program table\n\n00 00              ## e_shentsize size of a section header table\n00 00              ## e_shnum number of entries in section table\n\n00 00              ## e_shstrndx index of the section names\n\n## Program Header\n:ELF_program_headers\n01 00 00 00             ## p_type\n07 00 00 00             ## ph_flags: PF-X|PF-W|PF-R = 7\n00 00 00 00 00 00 00 00 ## p_offset\n\n&ELF_base 00 00 00 00 ## p_vaddr\n&ELF_base 00 00 00 00 ## p_physaddr\n\n%ELF_end>ELF_base 00 00 00 00 ## p_filesz\n%ELF_end>ELF_base 00 00 00 00 ## p_memsz\n\n01 00 00 00 00 00 00 00 ## Required alignment\n\n:ELF_text\n\n:_start\n\t58                          ; pop_rax                     # Get the number of arguments\n\t5F                          ; pop_rdi                     # Get the program name\n\t5F                          ; pop_rdi                     # Get the actual output name\n\t48C7C6 41020000             ; mov_rsi, %577               # Prepare file as O_WRONLY|O_CREAT|O_TRUNC\n\t48C7C2 80010000             ; mov_rdx, %384               # Prepare file as RW for owner only (600 in octal)\n\t48C7C0 02000000             ; mov_rax, %2                 # the syscall number for open()\n\t0F05                        ; syscall                     # Now open that file\n\t4989C7                      ; mov_r15,rax                 # Preserve the file pointer we were given\n\n\t48C7C0 0C000000             ; mov_rax, %12                # the Syscall # for SYS_BRK\n\t48C7C7 00000000             ; mov_rdi, %0                 # Get current brk\n\t0F05                        ; syscall                     # Let the kernel do the work\n\t4989C6                      ; mov_r14,rax                 # Set our malloc pointer\n\n\t48C7C0 0C000000             ; mov_rax, %12                # the Syscall # for SYS_BRK\n\t4C89F7                      ; mov_r14,rax                 # Using current pointer\n\t4881C7 00001000             ; add_rdi, %0x100000          # Allocate 1MB\n\t0F05                        ; syscall                     # Let the kernel do the work\n\n:core\n\t5F                          ; pop_rdi                     # Get the actual input name\n\t4883FF 00                   ; cmp_rdi, !0                 # Check for null string\n\t0F84 %done                  ; je %done                    # Hit null be done\n\n\t48C7C6 00000000             ; mov_rsi, %0                 # prepare read_only\n\t48C7C2 00000000             ; mov_rdx, %0                 # prevent any interactions\n\t48C7C0 02000000             ; mov_rax, %2                 # the syscall number for open()\n\t0F05                        ; syscall                     # Now open that damn file\n\t4989C5                      ; mov_r13,rax                 # Protect INPUT\n:keep\n\t48C7C2 00001000             ; mov_rdx, %0x100000          # set the size of chars we want\n\t4C89F6                      ; mov_rsi,r14                 # Where to put it\n\t4C89EF                      ; mov_rdi,r13                 # Where are we reading from\n\t48C7C0 00000000             ; mov_rax, %0                 # the syscall number for read\n\t0F05                        ; syscall                     # call the Kernel\n\t50                          ; push_rax                    # Protect the number of bytes read\n\n\t4889C2                      ; mov_rdx,rax                 # Number of bytes to write\n\t4C89F6                      ; mov_rsi,r14                 # What we are writing\n\t4C89FF                      ; mov_rdi,r15                 # Write to target file\n\t48C7C0 01000000             ; mov_rax, %1                 # the syscall number for write\n\t0F05                        ; syscall                     # call the Kernel\n\n\t58                          ; pop_rax                     # Get bytes read\n\t483D 00001000               ; cmp_rax, %0x100000          # Check if buffer was fully used\n\t0F84 %keep                  ; je %keep                    # Keep looping if was full\n\tE9 %core                    ; jmp %core                   # Otherwise move to next file\n\n:done\n\t# program completed Successfully\n\t48C7C7 00000000             ; mov_rdi, %0                 # All is well\n\t48C7C0 3C000000             ; mov_rax, %0x3C              # put the exit syscall number in eax\n\t0F05                        ; syscall                     # Call it a good day\n\n:ELF_end\n\n"
  },
  {
    "path": "tests/data/hicad.mac",
    "content": "REM 10 lines 4 code 3 comments 3 blanks\nSTART  59\n\nREM Comment on a line\n%XY:=42\n\nrem This is also a comment\nIF FOO= \"foo\" GOTO 10\n\nEND\n"
  },
  {
    "path": "tests/data/hledger.hledger",
    "content": "# 18 lines 6 code 10 comments 2 blanks\n# a comment\n; another comment\n\n; ^ a blank line\ncomment\naccount assets             ; Declare valid account names and display order.\na block comment\nend comment\n\naccount assets:savings     ; A subaccount. This one represents a bank account.\naccount assets:checking    ; Another. Note, 2+ spaces after the account name.\naccount assets:receivable  ; Accounting type is inferred from english names,\naccount passifs            ; or declared with a \"type\" tag, type:L\naccount expenses           ; type:X\n                           ; A follow-on comment line, indented.\naccount expenses:rent      ; Expense and revenue categories are also accounts.\n                           ; Subaccounts inherit their parent's type.\n"
  },
  {
    "path": "tests/data/hpp.hpp",
    "content": "/* 21 lines 11 code 5 comments 5 blanks */\n#ifndef TEST_H\n#define TEST_H\n\n#include <iostream>\n\n//Some definitions\nextern int out;\nvoid foo();\n\n/*\n *  Templated function\n */\ntemplate<typename T>\nvoid print_value(T& t)\n{\n    std::cout<<t;\n}\n\n\n#endif \n"
  },
  {
    "path": "tests/data/html.html",
    "content": "<!-- 46 lines 23 code 19 comments 4 blanks -->\n<!DOCTYPE html>\n<html>\n    <head>\n        <meta charset=\"utf-8\" />\n        <meta name=\"viewport\" content=\"width=device-width\" />\n        <title></title>\n        link\n        <style>\n/*\n\nCSS multi-line comment\n\n */\nbody {\n    background-color: pink;\n}\n        </style>\n    </head>\n    <body>\n        body\n    </body>\n    <!-- Normal Comment-->\n\n    <nav class=\"navbar navbar-default navbar-fixed-top navbar-custom\">\n        <div id=\"modalSearch\" class=\"modal fade\" role=\"dialog\"> </div>\n    </nav>\n\n    <!--\n        document.write(\"Multi-line and Code comment!\");\n        //-->\n\n        <!--[if IE 8]>\n            IE Special comment\n        <![endif]-->\n        <script>\n            /*\n\n            Javascript multi-line comment\n\n             */\nlet x = 5;\n// Javascript single line comment\n        </script>\n\n</html>\n"
  },
  {
    "path": "tests/data/janet.janet",
    "content": "# 17 lines 12 code 3 comments 2 blanks\n\n# Below is a function\n(defn a-fn\n  \"Docstring with a hash #\"\n  [a b]\n  (+ 1 1))\n\n(defn a-fn2\n  #\"Not a doc\"\n  \"String\"\n  [a b] # a and b right?\n  (let [multiline \"I'm\n  a multline\n  # string\n  \"]\n       (str multline a b)))\n"
  },
  {
    "path": "tests/data/java.java",
    "content": "/* 37 lines 23 code 5 comments 9 blanks */\n\n/*\n * Simple test class\n */\npublic class Test\n{\n    int j = 0; // Not counted\n    public static void main(String[] args)\n    {\n        Foo f = new Foo();\n        f.bar();\n        \n    }\n}\n\nclass Foo\n{\n    public void bar()\n    {\n      System.out.println(\"FooBar\"); //Not counted\n    }\n}\n\n// issues/915\npublic class BackSlash {\n    public void run()\n    {\n      \"\\\\\"; // 1 code + 2 blanks\n\n\n      \"\\\\\"; // 1 code + 3 blanks\n\n\n\n    }\n}\n"
  },
  {
    "path": "tests/data/javascript.js",
    "content": "// 33 lines, 14 code, 12 comments, 7 blanks\n\n/*\n * /* Nested comment\n * // single line comment\n * */\n\n/*\n\nfunction add(a, b) {\n  return a + b;\n}\n*/\n\nclass Rectangle {\n  constructor(width, height) {\n    this.width = width;\n    this.height = height;\n  }\n\n  get area() {\n    return this.calcArea();\n  }\n\n  calcArea() {\n    return this.width * this.height;\n  }\n}\n\nlet rect = new Rectangle(20, 20);\nconsole.log(rect.area); // 400\n\n// Comment\n"
  },
  {
    "path": "tests/data/jinja2.j2",
    "content": "{# 5 lines 1 code 2 comments 2 blanks #}\n\n{# test comment #}\n\n{{ testvar }}\n"
  },
  {
    "path": "tests/data/jq.jq",
    "content": "# 11 lines 3 code 5 comments 3 blanks\n\n# A function to perform arithmetic\ndef add_mul(adder; multiplier):\n  # comment character in quotes\n  \"# Result: \" + ((. + adder) * multiplier | tostring);\n\n# and demonstrate it\n10 | add_mul(5; 4)      # => \"# Result: 60\"\n\n# end of file\n"
  },
  {
    "path": "tests/data/jslt.jslt",
    "content": "// 126 lines 80 code 20 comments 26 blanks\n\n// https://github.com/schibsted/jslt/blob/master/examples/queens.jslt\n// ===========================================================================\n//  N-Queens problem solution in JSLT\n\n// board is n lists of length n\n//  0 => no queen\n//  1 => queen\n\n// queens(8) produces\n// [\n//   [ 1, 0, 0, 0, 0, 0, 0, 0 ],\n//   [ 0, 0, 0, 0, 1, 0, 0, 0 ],\n//   [ 0, 0, 0, 0, 0, 0, 0, 1 ],\n//   [ 0, 0, 0, 0, 0, 1, 0, 0 ],\n//   [ 0, 0, 1, 0, 0, 0, 0, 0 ],\n//   [ 0, 0, 0, 0, 0, 0, 1, 0 ],\n//   [ 0, 1, 0, 0, 0, 0, 0, 0 ],\n//   [ 0, 0, 0, 1, 0, 0, 0, 0 ]\n// ]\n\ndef queens(n)\n  solve(0, make-board($n))\n\ndef range(length, list)\n  if (size($list) < $length)\n    range($length, $list + [size($list)])\n  else\n    $list\n\ndef zeroes(length)\n  [for (range($length, [])) 0]\n\ndef make-board(n)\n  [for (range($n, [])) zeroes($n)]\n\ndef solve(row, board)\n  let n = size($board)\n  if ($row == $n)\n    $board\n  else\n    let tries = [for (range($n, []))\n      let newboard = place-queen($row, ., $board)\n      if (is-ok($newboard))\n        solve($row + 1, $newboard)\n      else\n        null]\n\n    filter($tries)[0]\n\ndef is-ok(board)\n  rows-ok($board) and cols-ok($board) and diagonals-ok($board)\n\ndef rows-ok(board)\n  all-ok([for ($board) sum(.) <= 1])\n\ndef cols-ok(board)\n  // 0, 1, 2, 3, ...\n  let indexes = range(size($board), [])\n\n  // list of columns instead of list of rows\n  let columns = [for ($indexes)\n    let col = (.)\n    [for ($board) .[$col]]\n  ]\n\n  rows-ok($columns)\n\ndef diagonals-ok(board)\n  let n = size($board)\n  let offsets = range($n - 1, [])[1 : ] // starts with 1\n\n  let diagonals-right = (\n    [diagonal-right($board, 0, 0)] +\n    [for ($offsets) diagonal-right($board, 0, .)] +\n    [for ($offsets) diagonal-right($board, ., 0)]\n  )\n\n  let diagonals-left = (\n    [diagonal-left($board, 0, $n - 1)] +\n    [for ($offsets) diagonal-left($board, ., $n - 1)] +\n    [for ($offsets) diagonal-left($board, 0, .)]\n  )\n\n  rows-ok($diagonals-right + $diagonals-left)\n\ndef diagonal-right(board, rowoff, coloff)\n  if ($rowoff >= size($board) or $coloff >= size($board))\n    []\n  else\n    [$board[$rowoff][$coloff]] + diagonal-right($board, $rowoff+1, $coloff+1)\n\ndef diagonal-left(board, rowoff, coloff)\n  if ($rowoff >= size($board) or $coloff < 0)\n    []\n  else\n    diagonal-left($board, $rowoff + 1, $coloff - 1) + [$board[$rowoff][$coloff]]\n\ndef sum(numbers)\n  if (not($numbers))\n    0\n  else\n    $numbers[0] + sum($numbers[1 : ])\n\ndef all-ok(booleans)\n  if (not($booleans))\n    true\n  else\n    $booleans[0] and all-ok($booleans[1 : ])\n\ndef place-queen(row, col, board)\n  let changerow = $board[$row]\n  let newrow = $changerow[ : $col] + [1] + $changerow[$col + 1 : ]\n\n  $board[ : $row] + [$newrow] + $board[$row + 1 : ]\n\ndef filter(array)\n  if (not($array))\n    []\n  else if ($array[0])\n    [$array[0]] + filter($array[1 : ])\n  else\n    filter($array[1 : ])\n\nqueens(8)\n"
  },
  {
    "path": "tests/data/jsonnet.jsonnet",
    "content": "// 13 lines 7 code 4 comments 2 blanks\n\n/* /**/ */\nlocal func(a, b) = {\n  // very useful\n  a: a,\n  b: b,\n  \n  # you forgot about me!\n  c: \" /* comment in a string! */ \", \n  e: ' // in another string ', // another after\n  f: ' # in a final string ', # comment after though\n}\n"
  },
  {
    "path": "tests/data/jupyter.ipynb",
    "content": "{\n \"cells\": [\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"# Cheat Sheet: Writing Python 2-3 compatible code\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"- **Copyright (c):** 2013-2015 Python Charmers Pty Ltd, Australia.\\n\",\n    \"- **Author:** Ed Schofield.\\n\",\n    \"- **Licence:** Creative Commons Attribution.\\n\",\n    \"\\n\",\n    \"A PDF version is here: http://python-future.org/compatible_idioms.pdf\\n\",\n    \"\\n\",\n    \"This notebook shows you idioms for writing future-proof code that is compatible with both versions of Python: 2 and 3. It accompanies Ed Schofield's talk at PyCon AU 2014, \\\"Writing 2/3 compatible code\\\". (The video is here: <http://www.youtube.com/watch?v=KOqk8j11aAI&t=10m14s>.)\\n\",\n    \"\\n\",\n    \"Minimum versions:\\n\",\n    \"\\n\",\n    \"  - Python 2: 2.6+\\n\",\n    \"  - Python 3: 3.3+\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"## Setup\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"The imports below refer to these ``pip``-installable packages on PyPI:\\n\",\n    \"\\n\",\n    \"    import future        # pip install future\\n\",\n    \"    import builtins      # pip install future\\n\",\n    \"    import past          # pip install future\\n\",\n    \"    import six           # pip install six\\n\",\n    \"\\n\",\n    \"The following scripts are also ``pip``-installable:\\n\",\n    \"\\n\",\n    \"    futurize             # pip install future\\n\",\n    \"    pasteurize           # pip install future\\n\",\n    \"\\n\",\n    \"See http://python-future.org and https://pythonhosted.org/six/ for more information.\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"## Essential syntax differences\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"### print\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 only:\\n\",\n    \"print 'Hello'\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 and 3:\\n\",\n    \"print('Hello')\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"To print multiple strings, import ``print_function`` to prevent Py2 from interpreting it as a tuple:\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 only:\\n\",\n    \"print 'Hello', 'Guido'\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 and 3:\\n\",\n    \"from __future__ import print_function    # (at top of module)\\n\",\n    \"\\n\",\n    \"print('Hello', 'Guido')\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 only:\\n\",\n    \"print >> sys.stderr, 'Hello'\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 and 3:\\n\",\n    \"from __future__ import print_function\\n\",\n    \"\\n\",\n    \"print('Hello', file=sys.stderr)\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 only:\\n\",\n    \"print 'Hello',\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 and 3:\\n\",\n    \"from __future__ import print_function\\n\",\n    \"\\n\",\n    \"print('Hello', end='')\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"### Raising exceptions\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 only:\\n\",\n    \"raise ValueError, \\\"dodgy value\\\"\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 and 3:\\n\",\n    \"raise ValueError(\\\"dodgy value\\\")\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"Raising exceptions with a traceback:\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 only:\\n\",\n    \"traceback = sys.exc_info()[2]\\n\",\n    \"raise ValueError, \\\"dodgy value\\\", traceback\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 3 only:\\n\",\n    \"raise ValueError(\\\"dodgy value\\\").with_traceback()\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 and 3: option 1\\n\",\n    \"from six import reraise as raise_\\n\",\n    \"# or\\n\",\n    \"from future.utils import raise_\\n\",\n    \"\\n\",\n    \"traceback = sys.exc_info()[2]\\n\",\n    \"raise_(ValueError, \\\"dodgy value\\\", traceback)\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 and 3: option 2\\n\",\n    \"from future.utils import raise_with_traceback\\n\",\n    \"\\n\",\n    \"raise_with_traceback(ValueError(\\\"dodgy value\\\"))\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"Exception chaining (PEP 3134):\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 3,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Setup:\\n\",\n    \"class DatabaseError(Exception):\\n\",\n    \"    pass\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 3 only\\n\",\n    \"class FileDatabase:\\n\",\n    \"    def __init__(self, filename):\\n\",\n    \"        try:\\n\",\n    \"            self.file = open(filename)\\n\",\n    \"        except IOError as exc:\\n\",\n    \"            raise DatabaseError('failed to open') from exc\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 16,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 and 3:\\n\",\n    \"from future.utils import raise_from\\n\",\n    \"\\n\",\n    \"class FileDatabase:\\n\",\n    \"    def __init__(self, filename):\\n\",\n    \"        try:\\n\",\n    \"            self.file = open(filename)\\n\",\n    \"        except IOError as exc:\\n\",\n    \"            raise_from(DatabaseError('failed to open'), exc)\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 17,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Testing the above:\\n\",\n    \"try:\\n\",\n    \"    fd = FileDatabase('non_existent_file.txt')\\n\",\n    \"except Exception as e:\\n\",\n    \"    assert isinstance(e.__cause__, IOError)    # FileNotFoundError on Py3.3+ inherits from IOError\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"### Catching exceptions\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 only:\\n\",\n    \"try:\\n\",\n    \"    ...\\n\",\n    \"except ValueError, e:\\n\",\n    \"    ...\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 and 3:\\n\",\n    \"try:\\n\",\n    \"    ...\\n\",\n    \"except ValueError as e:\\n\",\n    \"    ...\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"### Division\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"Integer division (rounding down):\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 only:\\n\",\n    \"assert 2 / 3 == 0\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 and 3:\\n\",\n    \"assert 2 // 3 == 0\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"\\\"True division\\\" (float division):\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 3 only:\\n\",\n    \"assert 3 / 2 == 1.5\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 and 3:\\n\",\n    \"from __future__ import division    # (at top of module)\\n\",\n    \"\\n\",\n    \"assert 3 / 2 == 1.5\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"\\\"Old division\\\" (i.e. compatible with Py2 behaviour):\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 only:\\n\",\n    \"a = b / c            # with any types\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 and 3:\\n\",\n    \"from past.utils import old_div\\n\",\n    \"\\n\",\n    \"a = old_div(b, c)    # always same as / on Py2\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"### Long integers\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"Short integers are gone in Python 3 and ``long`` has become ``int`` (without the trailing ``L`` in the ``repr``).\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 only\\n\",\n    \"k = 9223372036854775808L\\n\",\n    \"\\n\",\n    \"# Python 2 and 3:\\n\",\n    \"k = 9223372036854775808\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 only\\n\",\n    \"bigint = 1L\\n\",\n    \"\\n\",\n    \"# Python 2 and 3\\n\",\n    \"from builtins import int\\n\",\n    \"bigint = int(1)\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"To test whether a value is an integer (of any kind):\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 only:\\n\",\n    \"if isinstance(x, (int, long)):\\n\",\n    \"    ...\\n\",\n    \"\\n\",\n    \"# Python 3 only:\\n\",\n    \"if isinstance(x, int):\\n\",\n    \"    ...\\n\",\n    \"\\n\",\n    \"# Python 2 and 3: option 1\\n\",\n    \"from builtins import int    # subclass of long on Py2\\n\",\n    \"\\n\",\n    \"if isinstance(x, int):             # matches both int and long on Py2\\n\",\n    \"    ...\\n\",\n    \"\\n\",\n    \"# Python 2 and 3: option 2\\n\",\n    \"from past.builtins import long\\n\",\n    \"\\n\",\n    \"if isinstance(x, (int, long)):\\n\",\n    \"    ...\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"### Octal constants\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"0644     # Python 2 only\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"0o644    # Python 2 and 3\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"### Backtick repr\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"`x`      # Python 2 only\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"repr(x)  # Python 2 and 3\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"### Metaclasses\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"class BaseForm(object):\\n\",\n    \"    pass\\n\",\n    \"\\n\",\n    \"class FormType(type):\\n\",\n    \"    pass\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 only:\\n\",\n    \"class Form(BaseForm):\\n\",\n    \"    __metaclass__ = FormType\\n\",\n    \"    pass\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 3 only:\\n\",\n    \"class Form(BaseForm, metaclass=FormType):\\n\",\n    \"    pass\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 and 3:\\n\",\n    \"from six import with_metaclass\\n\",\n    \"# or\\n\",\n    \"from future.utils import with_metaclass\\n\",\n    \"\\n\",\n    \"class Form(with_metaclass(FormType, BaseForm)):\\n\",\n    \"    pass\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"## Strings and bytes\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"### Unicode (text) string literals\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"If you are upgrading an existing Python 2 codebase, it may be preferable to mark up all string literals as unicode explicitly with ``u`` prefixes:\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 only\\n\",\n    \"s1 = 'The Zen of Python'\\n\",\n    \"s2 = u'きたないのよりきれいな方がいい\\\\n'\\n\",\n    \"\\n\",\n    \"# Python 2 and 3\\n\",\n    \"s1 = u'The Zen of Python'\\n\",\n    \"s2 = u'きたないのよりきれいな方がいい\\\\n'\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"The ``futurize`` and ``python-modernize`` tools do not currently offer an option to do this automatically.\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"If you are writing code for a new project or new codebase, you can use this idiom to make all string literals in a module unicode strings:\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 and 3\\n\",\n    \"from __future__ import unicode_literals    # at top of module\\n\",\n    \"\\n\",\n    \"s1 = 'The Zen of Python'\\n\",\n    \"s2 = 'きたないのよりきれいな方がいい\\\\n'\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"See http://python-future.org/unicode_literals.html for more discussion on which style to use.\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"### Byte-string literals\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 only\\n\",\n    \"s = 'This must be a byte-string'\\n\",\n    \"\\n\",\n    \"# Python 2 and 3\\n\",\n    \"s = b'This must be a byte-string'\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"To loop over a byte-string with possible high-bit characters, obtaining each character as a byte-string of length 1:\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 only:\\n\",\n    \"for bytechar in 'byte-string with high-bit chars like \\\\xf9':\\n\",\n    \"    ...\\n\",\n    \"\\n\",\n    \"# Python 3 only:\\n\",\n    \"for myint in b'byte-string with high-bit chars like \\\\xf9':\\n\",\n    \"    bytechar = bytes([myint])\\n\",\n    \"\\n\",\n    \"# Python 2 and 3:\\n\",\n    \"from builtins import bytes\\n\",\n    \"for myint in bytes(b'byte-string with high-bit chars like \\\\xf9'):\\n\",\n    \"    bytechar = bytes([myint])\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"As an alternative, ``chr()`` and ``.encode('latin-1')`` can be used to convert an int into a 1-char byte string:\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 3 only:\\n\",\n    \"for myint in b'byte-string with high-bit chars like \\\\xf9':\\n\",\n    \"    char = chr(myint)    # returns a unicode string\\n\",\n    \"    bytechar = char.encode('latin-1')\\n\",\n    \"\\n\",\n    \"# Python 2 and 3:\\n\",\n    \"from builtins import bytes, chr\\n\",\n    \"for myint in bytes(b'byte-string with high-bit chars like \\\\xf9'):\\n\",\n    \"    char = chr(myint)    # returns a unicode string\\n\",\n    \"    bytechar = char.encode('latin-1')    # forces returning a byte str\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"### basestring\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 only:\\n\",\n    \"a = u'abc'\\n\",\n    \"b = 'def'\\n\",\n    \"assert (isinstance(a, basestring) and isinstance(b, basestring))\\n\",\n    \"\\n\",\n    \"# Python 2 and 3: alternative 1\\n\",\n    \"from past.builtins import basestring    # pip install future\\n\",\n    \"\\n\",\n    \"a = u'abc'\\n\",\n    \"b = b'def'\\n\",\n    \"assert (isinstance(a, basestring) and isinstance(b, basestring))\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 and 3: alternative 2: refactor the code to avoid considering\\n\",\n    \"# byte-strings as strings.\\n\",\n    \"\\n\",\n    \"from builtins import str\\n\",\n    \"a = u'abc'\\n\",\n    \"b = b'def'\\n\",\n    \"c = b.decode()\\n\",\n    \"assert isinstance(a, str) and isinstance(c, str)\\n\",\n    \"# ...\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"### unicode\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 only:\\n\",\n    \"templates = [u\\\"blog/blog_post_detail_%s.html\\\" % unicode(slug)]\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 and 3: alternative 1\\n\",\n    \"from builtins import str\\n\",\n    \"templates = [u\\\"blog/blog_post_detail_%s.html\\\" % str(slug)]\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 and 3: alternative 2\\n\",\n    \"from builtins import str as text\\n\",\n    \"templates = [u\\\"blog/blog_post_detail_%s.html\\\" % text(slug)]\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"### StringIO\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 only:\\n\",\n    \"from StringIO import StringIO\\n\",\n    \"# or:\\n\",\n    \"from cStringIO import StringIO\\n\",\n    \"\\n\",\n    \"# Python 2 and 3:\\n\",\n    \"from io import BytesIO     # for handling byte strings\\n\",\n    \"from io import StringIO    # for handling unicode strings\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"## Imports relative to a package\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"Suppose the package is:\\n\",\n    \"\\n\",\n    \"    mypackage/\\n\",\n    \"        __init__.py\\n\",\n    \"        submodule1.py\\n\",\n    \"        submodule2.py\\n\",\n    \"        \\n\",\n    \"and the code below is in ``submodule1.py``:\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 only: \\n\",\n    \"import submodule2\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 and 3:\\n\",\n    \"from . import submodule2\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 and 3:\\n\",\n    \"# To make Py2 code safer (more like Py3) by preventing\\n\",\n    \"# implicit relative imports, you can also add this to the top:\\n\",\n    \"from __future__ import absolute_import\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"## Dictionaries\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"heights = {'Fred': 175, 'Anne': 166, 'Joe': 192}\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"### Iterating through ``dict`` keys/values/items\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"Iterable dict keys:\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 only:\\n\",\n    \"for key in heights.iterkeys():\\n\",\n    \"    ...\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 and 3:\\n\",\n    \"for key in heights:\\n\",\n    \"    ...\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"Iterable dict values:\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 only:\\n\",\n    \"for value in heights.itervalues():\\n\",\n    \"    ...\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Idiomatic Python 3\\n\",\n    \"for value in heights.values():    # extra memory overhead on Py2\\n\",\n    \"    ...\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 8,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 and 3: option 1\\n\",\n    \"from builtins import dict\\n\",\n    \"\\n\",\n    \"heights = dict(Fred=175, Anne=166, Joe=192)\\n\",\n    \"for key in heights.values():    # efficient on Py2 and Py3\\n\",\n    \"    ...\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 and 3: option 2\\n\",\n    \"from builtins import itervalues\\n\",\n    \"# or\\n\",\n    \"from six import itervalues\\n\",\n    \"\\n\",\n    \"for key in itervalues(heights):\\n\",\n    \"    ...\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"Iterable dict items:\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 only:\\n\",\n    \"for (key, value) in heights.iteritems():\\n\",\n    \"    ...\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 and 3: option 1\\n\",\n    \"for (key, value) in heights.items():    # inefficient on Py2    \\n\",\n    \"    ...\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 and 3: option 2\\n\",\n    \"from future.utils import viewitems\\n\",\n    \"\\n\",\n    \"for (key, value) in viewitems(heights):   # also behaves like a set\\n\",\n    \"    ...\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 and 3: option 3\\n\",\n    \"from future.utils import iteritems\\n\",\n    \"# or\\n\",\n    \"from six import iteritems\\n\",\n    \"\\n\",\n    \"for (key, value) in iteritems(heights):\\n\",\n    \"    ...\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"### dict keys/values/items as a list\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"dict keys as a list:\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 only:\\n\",\n    \"keylist = heights.keys()\\n\",\n    \"assert isinstance(keylist, list)\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 and 3:\\n\",\n    \"keylist = list(heights)\\n\",\n    \"assert isinstance(keylist, list)\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"dict values as a list:\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 only:\\n\",\n    \"heights = {'Fred': 175, 'Anne': 166, 'Joe': 192}\\n\",\n    \"valuelist = heights.values()\\n\",\n    \"assert isinstance(valuelist, list)\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 and 3: option 1\\n\",\n    \"valuelist = list(heights.values())    # inefficient on Py2\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 and 3: option 2\\n\",\n    \"from builtins import dict\\n\",\n    \"\\n\",\n    \"heights = dict(Fred=175, Anne=166, Joe=192)\\n\",\n    \"valuelist = list(heights.values())\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 and 3: option 3\\n\",\n    \"from future.utils import listvalues\\n\",\n    \"\\n\",\n    \"valuelist = listvalues(heights)\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 and 3: option 4\\n\",\n    \"from future.utils import itervalues\\n\",\n    \"# or\\n\",\n    \"from six import itervalues\\n\",\n    \"\\n\",\n    \"valuelist = list(itervalues(heights))\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"dict items as a list:\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 and 3: option 1\\n\",\n    \"itemlist = list(heights.items())    # inefficient on Py2\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 and 3: option 2\\n\",\n    \"from future.utils import listitems\\n\",\n    \"\\n\",\n    \"itemlist = listitems(heights)\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 and 3: option 3\\n\",\n    \"from future.utils import iteritems\\n\",\n    \"# or\\n\",\n    \"from six import iteritems\\n\",\n    \"\\n\",\n    \"itemlist = list(iteritems(heights))\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"## Custom class behaviour\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"### Custom iterators\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 only\\n\",\n    \"class Upper(object):\\n\",\n    \"    def __init__(self, iterable):\\n\",\n    \"        self._iter = iter(iterable)\\n\",\n    \"    def next(self):          # Py2-style\\n\",\n    \"        return self._iter.next().upper()\\n\",\n    \"    def __iter__(self):\\n\",\n    \"        return self\\n\",\n    \"\\n\",\n    \"itr = Upper('hello')\\n\",\n    \"assert itr.next() == 'H'     # Py2-style\\n\",\n    \"assert list(itr) == list('ELLO')\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 and 3: option 1\\n\",\n    \"from builtins import object\\n\",\n    \"\\n\",\n    \"class Upper(object):\\n\",\n    \"    def __init__(self, iterable):\\n\",\n    \"        self._iter = iter(iterable)\\n\",\n    \"    def __next__(self):      # Py3-style iterator interface\\n\",\n    \"        return next(self._iter).upper()  # builtin next() function calls\\n\",\n    \"    def __iter__(self):\\n\",\n    \"        return self\\n\",\n    \"\\n\",\n    \"itr = Upper('hello')\\n\",\n    \"assert next(itr) == 'H'      # compatible style\\n\",\n    \"assert list(itr) == list('ELLO')\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 and 3: option 2\\n\",\n    \"from future.utils import implements_iterator\\n\",\n    \"\\n\",\n    \"@implements_iterator\\n\",\n    \"class Upper(object):\\n\",\n    \"    def __init__(self, iterable):\\n\",\n    \"        self._iter = iter(iterable)\\n\",\n    \"    def __next__(self):                  # Py3-style iterator interface\\n\",\n    \"        return next(self._iter).upper()  # builtin next() function calls\\n\",\n    \"    def __iter__(self):\\n\",\n    \"        return self\\n\",\n    \"\\n\",\n    \"itr = Upper('hello')\\n\",\n    \"assert next(itr) == 'H'\\n\",\n    \"assert list(itr) == list('ELLO')\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"### Custom ``__str__`` methods\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 only:\\n\",\n    \"class MyClass(object):\\n\",\n    \"    def __unicode__(self):\\n\",\n    \"        return 'Unicode string: \\\\u5b54\\\\u5b50'\\n\",\n    \"    def __str__(self):\\n\",\n    \"        return unicode(self).encode('utf-8')\\n\",\n    \"\\n\",\n    \"a = MyClass()\\n\",\n    \"print(a)    # prints encoded string\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 11,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [\n    {\n     \"name\": \"stdout\",\n     \"output_type\": \"stream\",\n     \"text\": [\n      \"Unicode string: 孔子\\n\"\n     ]\n    }\n   ],\n   \"source\": [\n    \"# Python 2 and 3:\\n\",\n    \"from future.utils import python_2_unicode_compatible\\n\",\n    \"\\n\",\n    \"@python_2_unicode_compatible\\n\",\n    \"class MyClass(object):\\n\",\n    \"    def __str__(self):\\n\",\n    \"        return u'Unicode string: \\\\u5b54\\\\u5b50'\\n\",\n    \"\\n\",\n    \"a = MyClass()\\n\",\n    \"print(a)    # prints string encoded as utf-8 on Py2\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"### Custom ``__nonzero__`` vs ``__bool__`` method:\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 only:\\n\",\n    \"class AllOrNothing(object):\\n\",\n    \"    def __init__(self, l):\\n\",\n    \"        self.l = l\\n\",\n    \"    def __nonzero__(self):\\n\",\n    \"        return all(self.l)\\n\",\n    \"\\n\",\n    \"container = AllOrNothing([0, 100, 200])\\n\",\n    \"assert not bool(container)\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 and 3:\\n\",\n    \"from builtins import object\\n\",\n    \"\\n\",\n    \"class AllOrNothing(object):\\n\",\n    \"    def __init__(self, l):\\n\",\n    \"        self.l = l\\n\",\n    \"    def __bool__(self):\\n\",\n    \"        return all(self.l)\\n\",\n    \"\\n\",\n    \"container = AllOrNothing([0, 100, 200])\\n\",\n    \"assert not bool(container)\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"## Lists versus iterators\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"### xrange\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 only:\\n\",\n    \"for i in xrange(10**8):\\n\",\n    \"    ...\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 and 3: forward-compatible\\n\",\n    \"from builtins import range\\n\",\n    \"for i in range(10**8):\\n\",\n    \"    ...\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 and 3: backward-compatible\\n\",\n    \"from past.builtins import xrange\\n\",\n    \"for i in xrange(10**8):\\n\",\n    \"    ...\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"### range\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 only\\n\",\n    \"mylist = range(5)\\n\",\n    \"assert mylist == [0, 1, 2, 3, 4]\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 and 3: forward-compatible: option 1\\n\",\n    \"mylist = list(range(5))            # copies memory on Py2\\n\",\n    \"assert mylist == [0, 1, 2, 3, 4]\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 and 3: forward-compatible: option 2\\n\",\n    \"from builtins import range\\n\",\n    \"\\n\",\n    \"mylist = list(range(5))\\n\",\n    \"assert mylist == [0, 1, 2, 3, 4]\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 and 3: option 3\\n\",\n    \"from future.utils import lrange\\n\",\n    \"\\n\",\n    \"mylist = lrange(5)\\n\",\n    \"assert mylist == [0, 1, 2, 3, 4]\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 and 3: backward compatible\\n\",\n    \"from past.builtins import range\\n\",\n    \"\\n\",\n    \"mylist = range(5)\\n\",\n    \"assert mylist == [0, 1, 2, 3, 4]\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"### map\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 only:\\n\",\n    \"mynewlist = map(f, myoldlist)\\n\",\n    \"assert mynewlist == [f(x) for x in myoldlist]\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 and 3: option 1\\n\",\n    \"# Idiomatic Py3, but inefficient on Py2\\n\",\n    \"mynewlist = list(map(f, myoldlist))\\n\",\n    \"assert mynewlist == [f(x) for x in myoldlist]\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 and 3: option 2\\n\",\n    \"from builtins import map\\n\",\n    \"\\n\",\n    \"mynewlist = list(map(f, myoldlist))\\n\",\n    \"assert mynewlist == [f(x) for x in myoldlist]\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 and 3: option 3\\n\",\n    \"try:\\n\",\n    \"    import itertools.imap as map\\n\",\n    \"except ImportError:\\n\",\n    \"    pass\\n\",\n    \"\\n\",\n    \"mynewlist = list(map(f, myoldlist))    # inefficient on Py2\\n\",\n    \"assert mynewlist == [f(x) for x in myoldlist]\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 and 3: option 4\\n\",\n    \"from future.utils import lmap\\n\",\n    \"\\n\",\n    \"mynewlist = lmap(f, myoldlist)\\n\",\n    \"assert mynewlist == [f(x) for x in myoldlist]\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 and 3: option 5\\n\",\n    \"from past.builtins import map\\n\",\n    \"\\n\",\n    \"mynewlist = map(f, myoldlist)\\n\",\n    \"assert mynewlist == [f(x) for x in myoldlist]\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"### imap\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 only:\\n\",\n    \"from itertools import imap\\n\",\n    \"\\n\",\n    \"myiter = imap(func, myoldlist)\\n\",\n    \"assert isinstance(myiter, iter)\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 3 only:\\n\",\n    \"myiter = map(func, myoldlist)\\n\",\n    \"assert isinstance(myiter, iter)\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 and 3: option 1\\n\",\n    \"from builtins import map\\n\",\n    \"\\n\",\n    \"myiter = map(func, myoldlist)\\n\",\n    \"assert isinstance(myiter, iter)\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 and 3: option 2\\n\",\n    \"try:\\n\",\n    \"    import itertools.imap as map\\n\",\n    \"except ImportError:\\n\",\n    \"    pass\\n\",\n    \"\\n\",\n    \"myiter = map(func, myoldlist)\\n\",\n    \"assert isinstance(myiter, iter)\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"### zip, izip\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"As above with ``zip`` and ``itertools.izip``.\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"### filter, ifilter\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"As above with ``filter`` and ``itertools.ifilter`` too.\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"## Other builtins\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"### File IO with open()\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": true\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 only\\n\",\n    \"f = open('myfile.txt')\\n\",\n    \"data = f.read()              # as a byte string\\n\",\n    \"text = data.decode('utf-8')\\n\",\n    \"\\n\",\n    \"# Python 2 and 3: alternative 1\\n\",\n    \"from io import open\\n\",\n    \"f = open('myfile.txt', 'rb')\\n\",\n    \"data = f.read()              # as bytes\\n\",\n    \"text = data.decode('utf-8')  # unicode, not bytes\\n\",\n    \"\\n\",\n    \"# Python 2 and 3: alternative 2\\n\",\n    \"from io import open\\n\",\n    \"f = open('myfile.txt', encoding='utf-8')\\n\",\n    \"text = f.read()    # unicode, not bytes\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"### reduce()\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 only:\\n\",\n    \"assert reduce(lambda x, y: x+y, [1, 2, 3, 4, 5]) == 1+2+3+4+5\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 and 3:\\n\",\n    \"from functools import reduce\\n\",\n    \"\\n\",\n    \"assert reduce(lambda x, y: x+y, [1, 2, 3, 4, 5]) == 1+2+3+4+5\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"### raw_input()\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 only:\\n\",\n    \"name = raw_input('What is your name? ')\\n\",\n    \"assert isinstance(name, str)    # native str\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 and 3:\\n\",\n    \"from builtins import input\\n\",\n    \"\\n\",\n    \"name = input('What is your name? ')\\n\",\n    \"assert isinstance(name, str)    # native str on Py2 and Py3\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"### input()\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 only:\\n\",\n    \"input(\\\"Type something safe please: \\\")\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 and 3\\n\",\n    \"from builtins import input\\n\",\n    \"eval(input(\\\"Type something safe please: \\\"))\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"Warning: using either of these is **unsafe** with untrusted input.\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"### file()\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 only:\\n\",\n    \"f = file(pathname)\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 and 3:\\n\",\n    \"f = open(pathname)\\n\",\n    \"\\n\",\n    \"# But preferably, use this:\\n\",\n    \"from io import open\\n\",\n    \"f = open(pathname, 'rb')   # if f.read() should return bytes\\n\",\n    \"# or\\n\",\n    \"f = open(pathname, 'rt')   # if f.read() should return unicode text\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"### exec\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": true\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 only:\\n\",\n    \"exec 'x = 10'\\n\",\n    \"\\n\",\n    \"# Python 2 and 3:\\n\",\n    \"exec('x = 10')\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": true\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 only:\\n\",\n    \"g = globals()\\n\",\n    \"exec 'x = 10' in g\\n\",\n    \"\\n\",\n    \"# Python 2 and 3:\\n\",\n    \"g = globals()\\n\",\n    \"exec('x = 10', g)\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": true\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 only:\\n\",\n    \"l = locals()\\n\",\n    \"exec 'x = 10' in g, l\\n\",\n    \"\\n\",\n    \"# Python 2 and 3:\\n\",\n    \"exec('x = 10', g, l)\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"But note that Py3's `exec()` is less powerful (and less dangerous) than Py2's `exec` statement.\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"### execfile()\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 only:\\n\",\n    \"execfile('myfile.py')\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 and 3: alternative 1\\n\",\n    \"from past.builtins import execfile\\n\",\n    \"\\n\",\n    \"execfile('myfile.py')\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 and 3: alternative 2\\n\",\n    \"exec(compile(open('myfile.py').read()))\\n\",\n    \"\\n\",\n    \"# This can sometimes cause this:\\n\",\n    \"#     SyntaxError: function ... uses import * and bare exec ...\\n\",\n    \"# See https://github.com/PythonCharmers/python-future/issues/37\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"### unichr()\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 only:\\n\",\n    \"assert unichr(8364) == '€'\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 3 only:\\n\",\n    \"assert chr(8364) == '€'\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 and 3:\\n\",\n    \"from builtins import chr\\n\",\n    \"assert chr(8364) == '€'\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"### intern()\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 only:\\n\",\n    \"intern('mystring')\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 3 only:\\n\",\n    \"from sys import intern\\n\",\n    \"intern('mystring')\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 and 3: alternative 1\\n\",\n    \"from past.builtins import intern\\n\",\n    \"intern('mystring')\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 and 3: alternative 2\\n\",\n    \"from six.moves import intern\\n\",\n    \"intern('mystring')\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 and 3: alternative 3\\n\",\n    \"from future.standard_library import install_aliases\\n\",\n    \"install_aliases()\\n\",\n    \"from sys import intern\\n\",\n    \"intern('mystring')\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 and 3: alternative 2\\n\",\n    \"try:\\n\",\n    \"    from sys import intern\\n\",\n    \"except ImportError:\\n\",\n    \"    pass\\n\",\n    \"intern('mystring')\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"### apply()\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"args = ('a', 'b')\\n\",\n    \"kwargs = {'kwarg1': True}\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 only:\\n\",\n    \"apply(f, args, kwargs)\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 and 3: alternative 1\\n\",\n    \"f(*args, **kwargs)\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 and 3: alternative 2\\n\",\n    \"from past.builtins import apply\\n\",\n    \"apply(f, args, kwargs)\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"### chr()\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 only:\\n\",\n    \"assert chr(64) == b'@'\\n\",\n    \"assert chr(200) == b'\\\\xc8'\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 3 only: option 1\\n\",\n    \"assert chr(64).encode('latin-1') == b'@'\\n\",\n    \"assert chr(0xc8).encode('latin-1') == b'\\\\xc8'\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 and 3: option 1\\n\",\n    \"from builtins import chr\\n\",\n    \"\\n\",\n    \"assert chr(64).encode('latin-1') == b'@'\\n\",\n    \"assert chr(0xc8).encode('latin-1') == b'\\\\xc8'\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 3 only: option 2\\n\",\n    \"assert bytes([64]) == b'@'\\n\",\n    \"assert bytes([0xc8]) == b'\\\\xc8'\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 and 3: option 2\\n\",\n    \"from builtins import bytes\\n\",\n    \"\\n\",\n    \"assert bytes([64]) == b'@'\\n\",\n    \"assert bytes([0xc8]) == b'\\\\xc8'\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"### cmp()\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 only:\\n\",\n    \"assert cmp('a', 'b') < 0 and cmp('b', 'a') > 0 and cmp('c', 'c') == 0\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 and 3: alternative 1\\n\",\n    \"from past.builtins import cmp\\n\",\n    \"assert cmp('a', 'b') < 0 and cmp('b', 'a') > 0 and cmp('c', 'c') == 0\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 and 3: alternative 2\\n\",\n    \"cmp = lambda(x, y): (x > y) - (x < y)\\n\",\n    \"assert cmp('a', 'b') < 0 and cmp('b', 'a') > 0 and cmp('c', 'c') == 0\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"### reload()\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 only:\\n\",\n    \"reload(mymodule)\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 and 3\\n\",\n    \"from imp import reload\\n\",\n    \"reload(mymodule)\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"## Standard library\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"### dbm modules\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 only\\n\",\n    \"import anydbm\\n\",\n    \"import whichdb\\n\",\n    \"import dbm\\n\",\n    \"import dumbdbm\\n\",\n    \"import gdbm\\n\",\n    \"\\n\",\n    \"# Python 2 and 3: alternative 1\\n\",\n    \"from future import standard_library\\n\",\n    \"standard_library.install_aliases()\\n\",\n    \"\\n\",\n    \"import dbm\\n\",\n    \"import dbm.ndbm\\n\",\n    \"import dbm.dumb\\n\",\n    \"import dbm.gnu\\n\",\n    \"\\n\",\n    \"# Python 2 and 3: alternative 2\\n\",\n    \"from future.moves import dbm\\n\",\n    \"from future.moves.dbm import dumb\\n\",\n    \"from future.moves.dbm import ndbm\\n\",\n    \"from future.moves.dbm import gnu\\n\",\n    \"\\n\",\n    \"# Python 2 and 3: alternative 3\\n\",\n    \"from six.moves import dbm_gnu\\n\",\n    \"# (others not supported)\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"### commands / subprocess modules\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 only\\n\",\n    \"from commands import getoutput, getstatusoutput\\n\",\n    \"\\n\",\n    \"# Python 2 and 3\\n\",\n    \"from future import standard_library\\n\",\n    \"standard_library.install_aliases()\\n\",\n    \"\\n\",\n    \"from subprocess import getoutput, getstatusoutput\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"### subprocess.check_output()\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2.7 and above\\n\",\n    \"from subprocess import check_output\\n\",\n    \"\\n\",\n    \"# Python 2.6 and above: alternative 1\\n\",\n    \"from future.moves.subprocess import check_output\\n\",\n    \"\\n\",\n    \"# Python 2.6 and above: alternative 2\\n\",\n    \"from future import standard_library\\n\",\n    \"standard_library.install_aliases()\\n\",\n    \"\\n\",\n    \"from subprocess import check_output\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"### collections: Counter, OrderedDict, ChainMap\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 6,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2.7 and above\\n\",\n    \"from collections import Counter, OrderedDict, ChainMap\\n\",\n    \"\\n\",\n    \"# Python 2.6 and above: alternative 1\\n\",\n    \"from future.backports import Counter, OrderedDict, ChainMap\\n\",\n    \"\\n\",\n    \"# Python 2.6 and above: alternative 2\\n\",\n    \"from future import standard_library\\n\",\n    \"standard_library.install_aliases()\\n\",\n    \"\\n\",\n    \"from collections import Counter, OrderedDict, ChainMap\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"### StringIO module\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 only\\n\",\n    \"from StringIO import StringIO\\n\",\n    \"from cStringIO import StringIO\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 and 3\\n\",\n    \"from io import BytesIO\\n\",\n    \"# and refactor StringIO() calls to BytesIO() if passing byte-strings\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"### http module\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 only:\\n\",\n    \"import httplib\\n\",\n    \"import Cookie\\n\",\n    \"import cookielib\\n\",\n    \"import BaseHTTPServer\\n\",\n    \"import SimpleHTTPServer\\n\",\n    \"import CGIHttpServer\\n\",\n    \"\\n\",\n    \"# Python 2 and 3 (after ``pip install future``):\\n\",\n    \"import http.client\\n\",\n    \"import http.cookies\\n\",\n    \"import http.cookiejar\\n\",\n    \"import http.server\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"### xmlrpc module\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 only:\\n\",\n    \"import DocXMLRPCServer\\n\",\n    \"import SimpleXMLRPCServer\\n\",\n    \"\\n\",\n    \"# Python 2 and 3 (after ``pip install future``):\\n\",\n    \"import xmlrpc.server\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 only:\\n\",\n    \"import xmlrpclib\\n\",\n    \"\\n\",\n    \"# Python 2 and 3 (after ``pip install future``):\\n\",\n    \"import xmlrpc.client\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"### html escaping and entities\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 and 3:\\n\",\n    \"from cgi import escape\\n\",\n    \"\\n\",\n    \"# Safer (Python 2 and 3, after ``pip install future``):\\n\",\n    \"from html import escape\\n\",\n    \"\\n\",\n    \"# Python 2 only:\\n\",\n    \"from htmlentitydefs import codepoint2name, entitydefs, name2codepoint\\n\",\n    \"\\n\",\n    \"# Python 2 and 3 (after ``pip install future``):\\n\",\n    \"from html.entities import codepoint2name, entitydefs, name2codepoint\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"### html parsing\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 only:\\n\",\n    \"from HTMLParser import HTMLParser\\n\",\n    \"\\n\",\n    \"# Python 2 and 3 (after ``pip install future``)\\n\",\n    \"from html.parser import HTMLParser\\n\",\n    \"\\n\",\n    \"# Python 2 and 3 (alternative 2):\\n\",\n    \"from future.moves.html.parser import HTMLParser\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"### urllib module\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"``urllib`` is the hardest module to use from Python 2/3 compatible code. You may like to use Requests (http://python-requests.org) instead.\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 only:\\n\",\n    \"from urlparse import urlparse\\n\",\n    \"from urllib import urlencode\\n\",\n    \"from urllib2 import urlopen, Request, HTTPError\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 2,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 3 only:\\n\",\n    \"from urllib.parse import urlparse, urlencode\\n\",\n    \"from urllib.request import urlopen, Request\\n\",\n    \"from urllib.error import HTTPError\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 and 3: easiest option\\n\",\n    \"from future.standard_library import install_aliases\\n\",\n    \"install_aliases()\\n\",\n    \"\\n\",\n    \"from urllib.parse import urlparse, urlencode\\n\",\n    \"from urllib.request import urlopen, Request\\n\",\n    \"from urllib.error import HTTPError\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 and 3: alternative 2\\n\",\n    \"from future.standard_library import hooks\\n\",\n    \"\\n\",\n    \"with hooks():\\n\",\n    \"    from urllib.parse import urlparse, urlencode\\n\",\n    \"    from urllib.request import urlopen, Request\\n\",\n    \"    from urllib.error import HTTPError\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 and 3: alternative 3\\n\",\n    \"from future.moves.urllib.parse import urlparse, urlencode\\n\",\n    \"from future.moves.urllib.request import urlopen, Request\\n\",\n    \"from future.moves.urllib.error import HTTPError\\n\",\n    \"# or\\n\",\n    \"from six.moves.urllib.parse import urlparse, urlencode\\n\",\n    \"from six.moves.urllib.request import urlopen\\n\",\n    \"from six.moves.urllib.error import HTTPError\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 and 3: alternative 4\\n\",\n    \"try:\\n\",\n    \"    from urllib.parse import urlparse, urlencode\\n\",\n    \"    from urllib.request import urlopen, Request\\n\",\n    \"    from urllib.error import HTTPError\\n\",\n    \"except ImportError:\\n\",\n    \"    from urlparse import urlparse\\n\",\n    \"    from urllib import urlencode\\n\",\n    \"    from urllib2 import urlopen, Request, HTTPError\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"### Tkinter\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 only:\\n\",\n    \"import Tkinter\\n\",\n    \"import Dialog\\n\",\n    \"import FileDialog\\n\",\n    \"import ScrolledText\\n\",\n    \"import SimpleDialog\\n\",\n    \"import Tix  \\n\",\n    \"import Tkconstants\\n\",\n    \"import Tkdnd   \\n\",\n    \"import tkColorChooser\\n\",\n    \"import tkCommonDialog\\n\",\n    \"import tkFileDialog\\n\",\n    \"import tkFont\\n\",\n    \"import tkMessageBox\\n\",\n    \"import tkSimpleDialog\\n\",\n    \"import ttk\\n\",\n    \"\\n\",\n    \"# Python 2 and 3 (after ``pip install future``):\\n\",\n    \"import tkinter\\n\",\n    \"import tkinter.dialog\\n\",\n    \"import tkinter.filedialog\\n\",\n    \"import tkinter.scrolledtext\\n\",\n    \"import tkinter.simpledialog\\n\",\n    \"import tkinter.tix\\n\",\n    \"import tkinter.constants\\n\",\n    \"import tkinter.dnd\\n\",\n    \"import tkinter.colorchooser\\n\",\n    \"import tkinter.commondialog\\n\",\n    \"import tkinter.filedialog\\n\",\n    \"import tkinter.font\\n\",\n    \"import tkinter.messagebox\\n\",\n    \"import tkinter.simpledialog\\n\",\n    \"import tkinter.ttk\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"### socketserver\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 only:\\n\",\n    \"import SocketServer\\n\",\n    \"\\n\",\n    \"# Python 2 and 3 (after ``pip install future``):\\n\",\n    \"import socketserver\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"### copy_reg, copyreg\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 only:\\n\",\n    \"import copy_reg\\n\",\n    \"\\n\",\n    \"# Python 2 and 3 (after ``pip install future``):\\n\",\n    \"import copyreg\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"### configparser\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 only:\\n\",\n    \"from ConfigParser import ConfigParser\\n\",\n    \"\\n\",\n    \"# Python 2 and 3 (after ``pip install future``):\\n\",\n    \"from configparser import ConfigParser\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"### queue\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 only:\\n\",\n    \"from Queue import Queue, heapq, deque\\n\",\n    \"\\n\",\n    \"# Python 2 and 3 (after ``pip install future``):\\n\",\n    \"from queue import Queue, heapq, deque\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"### repr, reprlib\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 only:\\n\",\n    \"from repr import aRepr, repr\\n\",\n    \"\\n\",\n    \"# Python 2 and 3 (after ``pip install future``):\\n\",\n    \"from reprlib import aRepr, repr\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"### UserDict, UserList, UserString\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 only:\\n\",\n    \"from UserDict import UserDict\\n\",\n    \"from UserList import UserList\\n\",\n    \"from UserString import UserString\\n\",\n    \"\\n\",\n    \"# Python 3 only:\\n\",\n    \"from collections import UserDict, UserList, UserString\\n\",\n    \"\\n\",\n    \"# Python 2 and 3: alternative 1\\n\",\n    \"from future.moves.collections import UserDict, UserList, UserString\\n\",\n    \"\\n\",\n    \"# Python 2 and 3: alternative 2\\n\",\n    \"from six.moves import UserDict, UserList, UserString\\n\",\n    \"\\n\",\n    \"# Python 2 and 3: alternative 3\\n\",\n    \"from future.standard_library import install_aliases\\n\",\n    \"install_aliases()\\n\",\n    \"from collections import UserDict, UserList, UserString\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"### itertools: filterfalse, zip_longest\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {\n    \"collapsed\": false\n   },\n   \"outputs\": [],\n   \"source\": [\n    \"# Python 2 only:\\n\",\n    \"from itertools import ifilterfalse, izip_longest\\n\",\n    \"\\n\",\n    \"# Python 3 only:\\n\",\n    \"from itertools import filterfalse, zip_longest\\n\",\n    \"\\n\",\n    \"# Python 2 and 3: alternative 1\\n\",\n    \"from future.moves.itertools import filterfalse, zip_longest\\n\",\n    \"\\n\",\n    \"# Python 2 and 3: alternative 2\\n\",\n    \"from six.moves import filterfalse, zip_longest\\n\",\n    \"\\n\",\n    \"# Python 2 and 3: alternative 3\\n\",\n    \"from future.standard_library import install_aliases\\n\",\n    \"install_aliases()\\n\",\n    \"from itertools import filterfalse, zip_longest\"\n   ]\n  }\n ],\n \"metadata\": {\n  \"kernelspec\": {\n   \"display_name\": \"Python 3\",\n   \"language\": \"python\",\n   \"name\": \"python3\"\n  },\n  \"language_info\": {\n   \"codemirror_mode\": {\n    \"name\": \"ipython\",\n    \"version\": 3\n   },\n   \"file_extension\": \".py\",\n   \"mimetype\": \"text/x-python\",\n   \"name\": \"python\",\n   \"nbconvert_exporter\": \"python\",\n   \"pygments_lexer\": \"ipython3\",\n   \"version\": \"3.4.3\"\n  }\n },\n \"nbformat\": 4,\n \"nbformat_minor\": 0\n}\n"
  },
  {
    "path": "tests/data/justfile",
    "content": "# 215 lines 154 code 11 comments 50 blanks\nset shell := [\"sh\", \"-c\"]\nset windows-shell := [\"powershell.exe\", \"-NoLogo\", \"-Command\"]\nset allow-duplicate-recipes\nset positional-arguments\nset dotenv-load\nset export\n\nalias s := serve\n\nbt := '0'\n\nexport RUST_BACKTRACE_1 := bt\n\nlog := \"warn\"\n\nexport JUST_LOG := (log + \"ing\" + `grep loop /etc/networks | cut -f2`)\n\ntmpdir  := `mktemp`\nversion := \"0.2.7\"\ntardir  := tmpdir / \"awesomesauce-\" + version\nfoo1    := / \"tmp\"\nfoo2_3  := \"a/\"\ntarball := tardir + \".tar.gz\"\n\nexport RUST_BACKTRACE_2 := \"1\"\nstring-with-tab             := \"\\t\"\nstring-with-newline         := \"\\n\"\nstring-with-carriage-return := \"\\r\"\nstring-with-double-quote    := \"\\\"\"\nstring-with-slash           := \"\\\\\"\nstring-with-no-newline      := \"\\\n\"\n\n# Newlines in variables\nsingle := '\nhello\n'\n\ndouble := \"\ngoodbye\n\"\nescapes := '\\t\\n\\r\\\"\\\\'\n\n# this string will evaluate to `foo\\nbar\\n`\nx := '''\n  foo\n  bar\n'''\n\n# this string will evaluate to `abc\\n  wuv\\nbar\\n`\ny := \"\"\"\n  abc\n    wuv\n  xyz\n\"\"\"\n\nfor:\n  for file in `ls .`; do \\\n    echo $file; \\\n  done\n\nserve:\n  touch {{tmpdir}}/file\n\n# This backtick evaluates the command `echo foo\\necho bar\\n`, which produces the value `foo\\nbar\\n`.\nstuff := ```\n    echo foo\n    echo bar\n  ```\n\n\nan_arch := trim(lowercase(justfile())) + arch()\ntrim_end := trim_end(\"99.99954%   \")\nhome_dir := replace(env_var('HOME') / \"yep\", 'yep', '')\nquoted := quote(\"some things beyond\\\"$()^%#@!|-+=_*&'`\")\nsmartphone := trim_end_match('blah.txt', 'txt')\nmuseum := trim_start_match(trim_start(trim_end_matches('   yep_blah.txt.txt', '.txt')), 'yep_')\nwater := trim_start_matches('ssssssoup.txt', 's')\ncongress := uppercase(os())\nfam := os_family()\npath_1 := absolute_path('test')\npath_2 := '/tmp/subcommittee.txt'\next_z := extension(path_2)\nexe_name := file_name(just_executable())\na_stem := file_stem(path_2)\na_parent := parent_directory(path_2)\nsans_ext := without_extension(path_2)\ncamera := join('tmp', 'dir1', 'dir2', path_2)\ncleaned := clean('/tmp/blah/..///thing.txt')\nid__path := '/tmp' / sha256('blah') / sha256_file(justfile())\n_another_var := env_var_or_default(\"HOME\", justfile_directory())\npython := `which python`\n\nexists := if path_exists(just_executable()) =~ '^/User' { uuid() } else { 'yeah' }\n\nfoo   := if env_var(\"_\") == \"/usr/bin/env\" { `touch /tmp/a_file` } else { \"dummy-value\" }\nfoo_b := if \"hello\" == \"goodbye\" { \"xyz\" } else { if \"no\" == \"no\" { \"yep\"} else { error(\"123\") } }\nfoo_c := if \"hello\" == \"goodbye\" {\n  \"xyz\"\n} else if \"a\" == \"a\" {\n  \"abc\"\n} else {\n  \"123\"\n}\n\nbar:\n  @echo {{foo}}\n\n\nbar2 foo_stuff:\n  echo {{ if foo_stuff == \"bar\" { \"hello\" } else { \"goodbye\" } }}\n\nexecutable:\n  @echo The executable is at: {{just_executable()}}\n\n\nrustfmt:\n  find {{invocation_directory()}} -name \\*.rs -exec rustfmt {} \\;\n\ntest:\n  echo \"{{home_dir}}\"\n\n\nlinewise:\n  Write-Host \"Hello, world!\"\n\nserve2:\n  @echo \"Starting server with database $DATABASE_ADDRESS on port $SERVER_PORT…\"\n\n\nshebang := if os() == 'windows' {\n  'powershell.exe'\n} else {\n  '/usr/bin/env pwsh'\n}\n\nshebang:\n\t#!{{shebang}}\n\t$PSV = $PSVersionTable.PSVersion | % {\"$_\" -split \"\\.\" }\n\t$psver = $PSV[0] + \".\" + $PSV[1]\n\tif ($PSV[2].Length -lt 4) {\n\t\t$psver += \".\" + $PSV[2] + \" Core\"\n\t} else {\n\t\t$psver += \" Desktop\"\n\t}\n\techo \"PowerShell $psver\"\n\n@foo:\n  echo bar\n\n@test5 *args='':\n  bash -c 'while (( \"$#\" )); do echo - $1; shift; done' -- \"$@\"\n\ntest2 $RUST_BACKTRACE=\"1\":\n  # will print a stack trace if it crashes\n  cargo test\n\n\nnotify m=\"\":\n\tkeybase chat send --topic-type \"chat\" --channel <channel> <team> \"upd(<repo>): {{m}}\"\n\n# Sample project script 2\nscript2 *ARGS:\n    {{ python }} script2.py {{ ARGS }}\n\nbraces:\n  echo 'I {{{{LOVE}} curly braces!'\n\n_braces2:\n  echo '{{'I {{LOVE}} curly braces!'}}'\n\n_braces3:\n  echo 'I {{ \"{{\" }}LOVE}} curly braces!'\n\nfoo2:\n  -@cat foo\n  echo 'Done!'\n\ntest3 target tests=path_1:\n  @echo 'Testing {{target}}:{{tests}}…'\n  ./test --tests {{tests}} {{target}}\n\ntest4 triple=(an_arch + \"-unknown-unknown\") input=(an_arch / \"input.dat\"):\n  ./test {{triple}}\n\nvariadic $VAR1_1 VAR2 VAR3 VAR4=(\"a\") +$FLAGS='-q': foo2 braces\n  cargo test {{FLAGS}}\n\ntime:\n  @-date +\"%H:%S\"\n  -cat /tmp/nonexistent_file.txt\n  @echo \"finished\"\n\njustwords:\n  grep just \\\n    --text /usr/share/dict/words \\\n    > /tmp/justwords\n\n# Subsequent dependencies\n# https://just.systems/man/en/chapter_37.html\n# To test, run `$ just -f test-suite.just b`\na:\n  echo 'A!'\n\nb: a && d\n  echo 'B start!'\n  just -f {{justfile()}} c\n  echo 'B end!'\n\nc:\n  echo 'C!'\n\nd:\n  echo 'D!'\n"
  },
  {
    "path": "tests/data/k.k",
    "content": "// 8 lines 2 code 4 comments 2 blanks\n/suduko solver\n\n/ initial state\nx:.:'\"200370009009200007001004002050000800008000900006000040900100500800007600400089001\"\n\n/ breadth search all solutions (p:row col box for each position)\n*(,x)(,/{@[x;y;:;]'&~in[!10]x*|/p[;y]=p,:3/:_(p:9\\:!81)%3}')/&~x\n"
  },
  {
    "path": "tests/data/kaem.kaem",
    "content": "# 43 lines 2 code 33 comments 8 blanks \n#! /usr/bin/env bash\n# Mes --- Maxwell Equations of Software\n# Copyright © 2017 Jan Nieuwenhuizen <janneke@gnu.org>\n# Copyright © 2017 Jeremiah Orians\n#\n# This file is part of Mes.\n#\n# Mes is free software; you can redistribute it and/or modify it\n# under the terms of the GNU General Public License as published by\n# the Free Software Foundation; either version 3 of the License, or (at\n# your option) any later version.\n#\n# Mes is distributed in the hope that it will be useful, but\n# WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with Mes.  If not, see <http://www.gnu.org/licenses/>.\n\n\n\n# Can also be run by kaem or any other shell of your personal choice\n# To run in kaem simply: kaem --verbose --strict\n# Warning all binaries prior to the use of blood-elf will not be readable by\n# Objdump, you may need to use ndism or gdb to view the assembly in the binary.\n\n\n\n###############################################\n# Phase-0 Build hex0 from bootstrapped binary #\n###############################################\n./bootstrap-seeds/POSIX/AMD64/hex0-seed ./AMD64/hex0_AMD64.hex0 ./AMD64/artifact/hex0\n# hex0 should have the exact same checksum as hex0-seed as they are both supposed\n# to be built from hex0_amd64.hex0 and by definition must be identical\n\n#########################################\n# Phase-0b Build minimal kaem from hex0 #\n#########################################\n./AMD64/artifact/hex0 ./AMD64/kaem-minimal.hex0 ./AMD64/artifact/kaem-0\n# for checksum validation reasons\n\n"
  },
  {
    "path": "tests/data/kakoune_script.kak",
    "content": "# 13 lines, 8 code, 2 comments, 3 blanks\nhook global BufCreate (.*/)?(kakrc|.*.kak) %{\n  set-option buffer filetype kak\n}\n\necho \"This is a string\".\necho 'This is a\nmultiline string\n# with a hash\nin it.'\n\n# This is a comment.\n\n"
  },
  {
    "path": "tests/data/koka.kk",
    "content": "// 29 lines 11 code 12 comments 6 blanks\n\n/*---------------------------------------------------------------------------\n  Copyright 2020-2021, Microsoft Research, Daan Leijen.\n\n  This is free software; you can redistribute it and/or modify it under the\n  terms of the Apache License, Version 2.0. A copy of the License can be\n  found in the LICENSE file at the root of this distribution.\n---------------------------------------------------------------------------*/\n\n/* Run processes.\n*/\nmodule std/os/process\n\nimport std/os/path\n\nextern import {\n  c file \"process-inline.c\"\n}\n\n// Run a command in the shell and return its output as a string.\npub extern run-system-read( cmd : string ) : io error<string> {\n  c \"kk_os_run_command_error\"\n}\n\n// Run a command in the shell\npub extern run-system( cmd : string ) : io int {\n  c \"kk_os_run_system_prim\"\n}\n"
  },
  {
    "path": "tests/data/ksh.ksh",
    "content": "#!/bin/ksh\n# 17 lines, 11 code, 4 comments, 2 blanks\n\n# first comment\nfiles=\"/etc/passwd /etc/group /etc/hosts\"\nfor f in $files; do\n    if [ ! -f $f ]\n    then\n        echo \"$f file missing!\"\n    fi\ndone\n\n# second comment\nfor f in $(ls /tmp/*)\ndo\n    print \"Full file path in /tmp dir : $f\"\ndone\n"
  },
  {
    "path": "tests/data/kvlanguage.kv",
    "content": "# 22 lines 17 code 3 comments 2 blanks\n\n\n#:kivy 2.0.0\n#:import C kivy.utils.get_color_from_hex\n#:import KeypadButton keypadbutton\n#:include keypadbutton.kv\n#:set name value\n<DynamicWidgetClass>:\n    color: C('#27272A')\n# general comment\n<Keypad@GridLayout>:\n    width: self.minimum_width\n    height: self.minimum_height\n    size_hint: None, None\n    cols: 3\n    spacing: 6\n    KeypadButton:\n        text: '1'\n        disabled: root.disabled\n        on_press: root.dispatch('on_key_pressed', self.key_val)\n    # a final comment\n"
  },
  {
    "path": "tests/data/lalrpop.lalrpop",
    "content": "// 37 lines 26 code 3 comments 8 blanks\nuse crate::ast::{ExprSymbol, Opcode};\nuse crate::tok9::Tok;\n\ngrammar<'input>(input: &'input str);\n\n// line comment\npub Expr: Box<ExprSymbol<'input>> = { // comment 1\n    Expr r##\"verbatim2\"## Factor => Box::new(ExprSymbol::Op(<>)),\n    Factor, // comment 2\n};\n\nFactor: Box<ExprSymbol<'input>> = { // comment 3\n    Factor \"FactorOp\" Term => Box::new(ExprSymbol::Op(<>)),\n    Term,\n};\n\n// comment 4\n\nTerm: Box<ExprSymbol<'input>> = {\n    r#\"verbatim\"# => Box::new(ExprSymbol::NumSymbol(<>)),\n    \"(\" <Expr> \")\"\n};\n\nextern {\n    type Location = usize;\n    type Error = ();\n\n    enum Tok<'input> {\n        r#\"verbatim\"# => Tok::NumSymbol(<&'input str>),\n        \"FactorOp\" => Tok::FactorOp(<Opcode>),\n        r##\"verbatim2\"## => Tok::ExprOp(<Opcode>),\n        \"(\" => Tok::ParenOpen,\n        \")\" => Tok::ParenClose,\n    }\n}\n\n"
  },
  {
    "path": "tests/data/linguafranca.lf",
    "content": "// 36 lines 16 code 9 comments 11 blanks\n\ntarget Rust;\n\n// A C style comment\nimport KeyboardEvents from \"KeyboardEvents.lf\";\n\n/* A block comment */\n  # a python like comment\n\nmain reactor Snake(grid_side: usize(32),\n                   food_limit: u32(2)) {\n\n    // counts as 2 lines of Rust code and one blank\n    preamble {=\n        use crate::snakes::*;\n\n        use rand::prelude::*;\n    =}\n\n    /// rust doc comment\n    keyboard = new KeyboardEvents();\n\n    // T\n    state snake: CircularSnake ({= CircularSnake::new(grid_side) =});\n    state grid: SnakeGrid ({= SnakeGrid::new(grid_side, &snake) =});\n    state food_on_grid: u32(0);\n\n\n    // 1 line of rust code\n    reaction(shutdown) {=\n        // comment in Rust\n\n        println!(\"New high score: {}\", self.snake.len());\n    =}\n}\n"
  },
  {
    "path": "tests/data/liquid.liquid",
    "content": "{% comment %} 24 lines 19 code 1 comments 4 blanks {% endcomment %}\n\n{% paginate collection.products by 20 %}\n\n<ul id=\"product-collection\">\n    {% for product in collection.products %}\n    <li class=\"singleproduct clearfix\">\n      <div class=\"small\">\n        <div class=\"prodimage\"><a href=\"{{product.url}}\"><img src=\"{{ product.featured_image | product_img_url: 'small' }}\" /></a></div>\n      </div>\n      <div class=\"description\">\n        <h3><a href=\"{{product.url}}\">{{product.title}}</a></h3>\n        <p>{{ product.description | strip_html | truncatewords: 35 }}</p>\n      <p class=\"money\">{{ product.price_min | money }}{% if product.price_varies %} - {{ product.price_max | money }}{% endif %}</p>\n     </div>\n    </li>\n    {% endfor %}\n</ul>\n\n<div id=\"pagination\">\n  {{ paginate | default_pagination }}\n</div>\n\n{% endpaginate %}\n"
  },
  {
    "path": "tests/data/livescript.ls",
    "content": "# 28 lines, 10 code, 12 comments, 6 blanks\n\n/*\n * /* Nested comment\n * #  single line comment\n * */\n\n/*\n\nadd = (a, b) ->\n  return a + b\n*/\n\nhello = ->\n  console.log 'hello, world!'\n\n\"hello!\" |> capitalize |> console.log\n\n# Easy listing of implicit objects\ntable1 =\n  * id: 1\n    name: 'george'\n  * id: 2\n    name: 'mike'  # comment\n  * id: 3\n    name: 'donald'\n\n# Comment\n"
  },
  {
    "path": "tests/data/llvm.ll",
    "content": "; 21 lines 17 code 1 comments 3 blanks\ndefine i32 @add1(i32 %a, i32 %b) {\nentry:\n  %tmp1 = add i32 %a, %b\n  ret i32 %tmp1\n}\n\ndefine i32 @add2(i32 %a, i32 %b) {\nentry:\n  %tmp1 = icmp eq i32 %a, 0\n  br i1 %tmp1, label %done, label %recurse\n\nrecurse:\n  %tmp2 = sub i32 %a, 1\n  %tmp3 = add i32 %b, 1\n  %tmp4 = call i32 @add2(i32 %tmp2, i32 %tmp3)\n  ret i32 %tmp4\n\ndone:\n  ret i32 %b\n}"
  },
  {
    "path": "tests/data/logtalk.lgt",
    "content": "/*\n\tTest file for the Logtalk programming language\n\t(copied by the author from a Logtalk distribution example)\n\n\t65 lines 27 code 18 comments 20 blanks\n*/\n\n\n% Alf believes he is the only survivor of his species; no point in\n% defining a class if there is only going to be a single instance:\n\n% a prototype, which is also a stand-alone object\n\n:- object(alf).\n\n\t% prototypes declare predicates for themselves (and derived prototypes)\n\t:- public([\n\t\tname/1, planet/1, stomachs/1, favorite_food/1, chases/1, motto/1\n\t]).\n\n\tname('Gordon Shumway').\n\tplanet('Melmac').\n\tstomachs(8).\n\tfavorite_food(cats).\n\tchases('Lucky').\n\tmotto('Are you going to finish that sandwich?').\n\n:- end_object.\n\n\n% later on, Alf finds out that his best friend, Skip, and his\n% girlfriend, Rhonda, also survived Melmac's explosion; as they\n% are all melmacians, they share most attributes (and add some\n% of their own):\n\n% \"skip\", a derived prototype from \"alf\", its parent prototype\n\n:- object(skip,\n\textends(alf)).\n\n\t:- public(best_friend/1).\n\n\tbest_friend(alf).\n\tname('Skip').\n\t% still longing for a nice cat to eat since Melmac exploded\n\tchases(_) :-\n\t\tfail.\n\n:- end_object.\n\n\n% \"rhonda\" is also a prototype derived from \"alf\"\n\n:- object(rhonda,\n\textends(alf)).\n\n\t:- public(boyfriend/1).\n\n\tboyfriend(alf).\n\tname('Rhonda').\n\t% still longing for a nice cat to eat since Melmac exploded\n\tchases(_) :-\n\t\tfail.\n\n:- end_object.\n"
  },
  {
    "path": "tests/data/lolcode.lol",
    "content": "BTW 26 lines 11 code 9 comments 6 blanks\nHAI 1.3\n\nBTW TEST!\nI HAS A MSG ITZ \"BYE! OBTW\"\nHOW IZ I PRINT_HELLO\n    I HAS A MSG ITZ \"BTW Hello, World!\" BTW OBTW\n    BTW MORE COMMENTS!\n    VISIBLE MSG  BTW TLDR\nIF U SAY SO\n\nI HAS A MSG ITZ \"Hello OBTW BTW TLDR\"\nI IZ PRINT_HELLO MKAY\n\nOBTW\na longer test\nasd TLDR\n\nOBTW TLDR\n\nI IZ PRINT_HELLO MKAY  OBTW should be valid\nfoo bar\nTLDR\n\nVISIBLE MSG\nKTHXBYE\n"
  },
  {
    "path": "tests/data/m1.m1",
    "content": "# 280 lines 197 code 47 comments 36 blanks\n## Copyright (C) 2016 Jeremiah Orians\n## This file is part of stage0.\n##\n## stage0 is free software: you can redistribute it and/or modify\n## it under the terms of the GNU General Public License as published by\n## the Free Software Foundation, either version 3 of the License, or\n## (at your option) any later version.\n##\n## stage0 is distributed in the hope that it will be useful,\n## but WITHOUT ANY WARRANTY; without even the implied warranty of\n## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n## GNU General Public License for more details.\n##\n## You should have received a copy of the GNU General Public License\n## along with stage0.  If not, see <http://www.gnu.org/licenses/>.\n\n# M2-Planet standards\nDEFINE NULL 00000000\n\n# Registers\nDEFINE R0 0\nDEFINE R1 1\nDEFINE R2 2\nDEFINE R3 3\nDEFINE R4 4\nDEFINE R5 5\nDEFINE R6 6\nDEFINE R7 7\nDEFINE R8 8\nDEFINE R9 9\nDEFINE R10 A\nDEFINE R11 B\nDEFINE R12 C\nDEFINE BP C\nDEFINE R13 D\nDEFINE SP D\nDEFINE R14 E\nDEFINE LR E\nDEFINE R15 F\nDEFINE PC F\n\n# Register masks for push/pop16\nDEFINE {R0} 0100\nDEFINE {R1} 0200\nDEFINE {R2} 0400\nDEFINE {R3} 0800\nDEFINE {R4} 1000\nDEFINE {R8} 0001\nDEFINE {R9} 0002\nDEFINE {R10} 0004\nDEFINE {R11} 0008\nDEFINE {BP} 0010\nDEFINE {LR} 0040\n\n# Bitshift constants\nDEFINE NO_SHIFT 0\nDEFINE LEFT 1\nDEFINE RIGHT 3\nDEFINE ARITH_RIGHT 5\n\n# LOAD/STORE\nDEFINE HALF_MEMORY E1\nDEFINE MEMORY E5\nDEFINE NO_OFFSET B0\nDEFINE STORE32 08\nDEFINE STORE16 0C\nDEFINE STORE8 0C\nDEFINE LOAD32 09\nDEFINE LOADU8 0\nDEFINE LOADS8 D0\nDEFINE LOADS16 F0\nDEFINE LOAD 0D\nDEFINE LOADI8_ALWAYS 0A0E3\nDEFINE LOADI8_G 0A0C3\nDEFINE LOADI8_GE 0A0A3\nDEFINE LOADI8_EQUAL 0A003\nDEFINE LOADI8_NE 0A013\nDEFINE LOADI8_LE 0A0D3\nDEFINE LOADI8_L 0A0B3\nDEFINE LOADI8_HI 0A083\nDEFINE LOADI8_HS 0A023\nDEFINE LOADI8_LS 0A093\nDEFINE LOADI8_LO 0A033\n\n# JUMP/BRANCH\nDEFINE JUMP_ALWAYS EA\nDEFINE JUMP_EQUAL 0A\nDEFINE JUMP_NE 1A\nDEFINE CALL_ALWAYS EB\nDEFINE CALL_REG_ALWAYS FF2FE1\nDEFINE RETURN FF2FE1\n\n# Data movement\nDEFINE MOVE_ALWAYS A0E1\nDEFINE MVN_ALWAYS 0E0E1\nDEFINE MVN_LT 0E0B1\nDEFINE MVNI8_EQUAL 0E003\nDEFINE PUSH_ALWAYS 2DE9\nDEFINE POP_ALWAYS BDE8\n\n# Arithmetic/logic\nDEFINE AUX_ALWAYS E1\nDEFINE IMM_ALWAYS E3\nDEFINE ARITH_ALWAYS E2\nDEFINE ARITH_GE A2\nDEFINE ARITH_LT B2\nDEFINE ARITH_NE 12\nDEFINE ARITH2_ALWAYS E0\nDEFINE ARITH2_GE A0\nDEFINE ADC 0A\nDEFINE ADCS 0B\nDEFINE ADD 08\nDEFINE ADDS 09\nDEFINE AND 00\nDEFINE CMP 005\nDEFINE CMPI8 005\nDEFINE MUL 0\nDEFINE MULS 1\nDEFINE OR 08\nDEFINE SHIFT A0\nDEFINE SUB 04\nDEFINE RSUB 06\nDEFINE XOR 02\n\n# SYSCALL\nDEFINE SYSCALL_ALWAYS 000000EF\n\n## Copyright (C) 2016 Jeremiah Orians\n## This file is part of M2-Planet.\n##\n## M2-Planet is free software: you can redistribute it and/or modify\n## it under the terms of the GNU General Public License as published by\n## the Free Software Foundation, either version 3 of the License, or\n## (at your option) any later version.\n##\n## M2-Planet is distributed in the hope that it will be useful,\n## but WITHOUT ANY WARRANTY; without even the implied warranty of\n## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n## GNU General Public License for more details.\n##\n## You should have received a copy of the GNU General Public License\n## along with M2-Planet.  If not, see <http://www.gnu.org/licenses/>.\n\n:_start\n\t'0' SP BP NO_SHIFT MOVE_ALWAYS  ; Setup Base Pointer\n\n\t;; Prepare argv\n\t!4 R0 ADD BP ARITH_ALWAYS       ; ARGV_address = BP + 4\n\t{R0} PUSH_ALWAYS                ; Put argv on the stack\n\n\t;; Prepare envp\n\t'0' BP R0 NO_SHIFT MOVE_ALWAYS  ; Address we need to load from\n\t!0 R0 LOAD32 R0 MEMORY          ; Get ARGC\n\t!2 R0 ADD R0 ARITH_ALWAYS       ; OFFSET = ARGC + 2\n\t'0' R0 R0 '1' MOVE_ALWAYS       ; OFFSET = OFFSET * WORDSIZE\n\t'0' R0 R0 ADD BP ARITH2_ALWAYS  ; ENVP_address = BP + OFFSET\n\t{R0} PUSH_ALWAYS                ; Put envp on the stack\n\n\t;; Stack offset\n\t!4 BP ADD BP ARITH_ALWAYS       ; Fix BP\n\n\t^~FUNCTION___init_malloc CALL_ALWAYS ; Setup for malloc\n\n\t^~FUNCTION___init_io CALL_ALWAYS ; Setup for FILE*\n\n\t^~FUNCTION_main CALL_ALWAYS     ; Jump right into main\n\n\t{R1} POP_ALWAYS                 ; Fix stack\n\t{R1} POP_ALWAYS                 ; Fix stack\n\t{R1} POP_ALWAYS                 ; Fix stack\n\t{R0} PUSH_ALWAYS                ; put return on the stack\n\t{R0} PUSH_ALWAYS                ; So that _exit will have it\n\t{R0} PUSH_ALWAYS                ; So that _exit will have it\n\n:FUNCTION_exit\n\t^~FUNCTION___kill_io CALL_ALWAYS\n\n:FUNCTION__exit\n\t!4 R0 SUB R12 ARITH_ALWAYS\n\t!0 R0 LOAD32 R0 MEMORY\n\t!1 R7 LOADI8_ALWAYS\n\tSYSCALL_ALWAYS                  ; exit\n\n# Unsigned Divide\n:divide\n\t{R4} PUSH_ALWAYS                ; Protect R4\n\t{R3} PUSH_ALWAYS                ; Protect R3\n\t{R2} PUSH_ALWAYS                ; Protect R2\n\n\t'0' R0 R3 NO_SHIFT MOVE_ALWAYS  ; MOV R3,R0\n\t'0' R1 R2 NO_SHIFT MOVE_ALWAYS  ; MOV R2,R1\n\n\t!0 R0 LOADI8_ALWAYS             ; MOV R0,#0\n\t!0 CMPI8 R2 IMM_ALWAYS          ; CMP R2,#0\n\t!1 R0 SUB R0 ARITH_LT           ; SUBLT R0,R0,#1\n\t!0 CMPI8 R3 IMM_ALWAYS          ; CMP R3,#0\n\t!0 R3 RSUB R3 ARITH_LT          ; RSBLT R3,R3,#0\n\t'0' R0 R0 MVN_LT                ; MVNLT R0,R0\n\t'0' R0 R4 NO_SHIFT MOVE_ALWAYS  ; MOV R4,R0\n\n\t!32 R0 LOADI8_ALWAYS            ; MOV  R0,#32.\n\t!0 R1 LOADI8_ALWAYS             ; MOV  R1,#0\n:divide_loop\n\t'0' R2 R2 ADDS R2 ARITH2_ALWAYS ; ADDS R2,R2,R2\n\t'0' R1 R1 ADCS R1 ARITH2_ALWAYS ; ADCS R1,R1,R1\n\t'0' R3 CMP R1 AUX_ALWAYS        ; CMP  R1,R3\n\t'0' R3 R1 SUB R1 ARITH2_GE      ; SUBGE  R1,R1,R3\n\t!1 R2 ADD R2 ARITH_GE           ; ADDGE  R2,R2,#1\n\t!1 R0 SUB R0 ARITH_ALWAYS       ; SUB  R0,R0,#1\n\t!0 CMPI8 R0 IMM_ALWAYS          ; CMP  R0,#0\n\t^~divide_loop JUMP_NE           ; BNE  loop\n\n\t'0' R2 R0 NO_SHIFT MOVE_ALWAYS  ; MOV R0,R2\n\n\t{R2} POP_ALWAYS                 ; Restore R2\n\t{R3} POP_ALWAYS                 ; Restore R3\n\t{R4} POP_ALWAYS                 ; Restore R4\n\t'1' LR RETURN\n\n# Signed Divide\n:divides\n\t{R4} PUSH_ALWAYS                ; Protect R4\n\t{R3} PUSH_ALWAYS                ; Protect R3\n\t{R2} PUSH_ALWAYS                ; Protect R2\n\n\t'0' R0 R3 NO_SHIFT MOVE_ALWAYS  ; MOV R3,R0\n\t'0' R1 R2 NO_SHIFT MOVE_ALWAYS  ; MOV R2,R1\n\n\t!0 R0 LOADI8_ALWAYS             ; MOV R0,#0\n\t!0 CMPI8 R2 IMM_ALWAYS          ; CMP R2,#0\n\t!0 R2 RSUB R2 ARITH_LT          ; RSBLT R2,R2,#0\n\t!1 R0 SUB R0 ARITH_LT           ; SUBLT R0,R0,#1\n\t!0 CMPI8 R3 IMM_ALWAYS          ; CMP R3,#0\n\t!0 R3 RSUB R3 ARITH_LT          ; RSBLT R3,R3,#0\n\t'0' R0 R0 MVN_LT                ; MVNLT R0,R0\n\t'0' R0 R4 NO_SHIFT MOVE_ALWAYS  ; MOV R4,R0\n\n\t!32 R0 LOADI8_ALWAYS            ; MOV  R0,#32.\n\t!0 R1 LOADI8_ALWAYS             ; MOV  R1,#0\n:divides_loop\n\t'0' R2 R2 ADDS R2 ARITH2_ALWAYS ; ADDS R2,R2,R2\n\t'0' R1 R1 ADCS R1 ARITH2_ALWAYS ; ADCS R1,R1,R1\n\t'0' R3 CMP R1 AUX_ALWAYS        ; CMP  R1,R3\n\t'0' R3 R1 SUB R1 ARITH2_GE      ; SUBGE  R1,R1,R3\n\t!1 R2 ADD R2 ARITH_GE           ; ADDGE  R2,R2,#1\n\t!1 R0 SUB R0 ARITH_ALWAYS       ; SUB  R0,R0,#1\n\t!0 CMPI8 R0 IMM_ALWAYS          ; CMP  R0,#0\n\t^~divides_loop JUMP_NE          ; BNE  loop\n\n\t!0 CMPI8 R4 IMM_ALWAYS          ; CMP R4,#0\n\t!0 R2 RSUB R2 ARITH_NE          ; RSBNE R2,R2,#0\n\t'0' R2 R0 NO_SHIFT MOVE_ALWAYS  ; MOV R0,R2\n\n\t{R2} POP_ALWAYS                 ; Restore R2\n\t{R3} POP_ALWAYS                 ; Restore R3\n\t{R4} POP_ALWAYS                 ; Restore R4\n\t'1' LR RETURN\n\n# Unsigned Modulus\n:modulus\n\t{LR} PUSH_ALWAYS                ; Prepare to leverage divide\n\t^~divide CALL_ALWAYS            ; Use divide\n\t'0' R1 R0 NO_SHIFT MOVE_ALWAYS  ; MOV R0,R1\n\t{LR} POP_ALWAYS                 ; Prepare for return\n\t'1' LR RETURN\n\n# Signed Modulus\n:moduluss\n\t{LR} PUSH_ALWAYS                ; Prepare to leverage divide\n\t^~divides CALL_ALWAYS           ; Use divides\n\t'0' R1 R0 NO_SHIFT MOVE_ALWAYS  ; MOV R0,R1\n\t{LR} POP_ALWAYS                 ; Prepare for return\n\t'1' LR RETURN\n\n:GLOBAL__envp\n\tNULL\n\n:mystring\n\"this is my string\\n\"\n"
  },
  {
    "path": "tests/data/m4.m4",
    "content": "dnl 7 lines 3 code 1 blanks 3 comments\nThe builtin `dnl' stands for “Discard to Next Line”:\ndnl this line is not emitted\nOther text is emitted\n\nYou can also make comments with `#' # this is a comment\n# This is a comment, too\n"
  },
  {
    "path": "tests/data/menhir.mly",
    "content": "// 47 lines 31 code 7 comments 9 blanks\n\n(* Example from the menhir development with instrumented comments.\n * (* Note: nested C style comments are not allowed. *)\n * https://gitlab.inria.fr/fpottier/menhir/-/tree/master/demos/calc-alias *)\n\n%token<int> INT  \"42\"\n%token PLUS       \"+\"\n%token MINUS      \"-\"\n%token TIMES      \"*\"\n%token DIV        \"/\"\n%token LPAREN     \"(\"\n%token RPAREN     \")\"\n%token EOL\n\n(* Token aliases can be used throughout the rest of the grammar. E.g.,\n   they can be used in precedence declarations: *)\n\n%left \"+\" \"-\"       /* lowest \" precedence */\n%left \"*\" \"/\"       /* medium precedence */\n%nonassoc UMINUS    // highest \"precedence\"\n\n%start <int> main\n\n%%\n\nmain:\n| e = expr EOL\n    { e }\n\n(* Token aliases can also be used inside rules: *)\n\nexpr:\n| i = \"42\"\n    { i }\n| \"(\" e = expr \")\"\n    { e }\n| e1 = expr \"+\" e2 = expr\n    { e1 + e2 }\n| e1 = expr \"-\" e2 = expr\n    { e1 - e2 }\n| e1 = expr \"*\" e2 = expr\n    { e1 * e2 }\n| e1 = expr \"/\" e2 = expr\n    { e1 / e2 }\n| \"-\" e = expr %prec UMINUS\n    { - e }\n"
  },
  {
    "path": "tests/data/meson.build",
    "content": "# 12 lines 6 code 2 comments 4 blanks\n\nproject('xyz', 'c',\n        meson_version : '>=0.30.0') # not counted\n\nxyz_gen = '''\n# comment inside\nprint(\"This is generated source.\")\n'''\n\n# this is a comment\n\n"
  },
  {
    "path": "tests/data/metal.metal",
    "content": "/* 32 lines 21 code 5 comments 6 blanks */\n#include <metal_stdlib>\n\n// comment\nstruct Uniforms {\n    float2 extent;\n};\n\nstruct VertexIn {\n    float2 position [[attribute(0)]];\n};\n\nstruct VertexOut {\n    float2 position [[position]];\n};\n\n/*\n    multi-line comment\n*/\n\nvertex VertexOut vs_main(\n    VertexIn in [[stage_in]]\n) {\n    VertexOut out;\n    return out;\n}\n\nfragment float4 fs_main(\n    VertexOut in [[stage_in]]\n) {\n    return float4(0.0);\n}\n"
  },
  {
    "path": "tests/data/mlatu.mlt",
    "content": "// 22 lines 14 code 3 comments 5 blanks\n\ndefine divisible (Int, Int -> Bool +Fail) { (%) 0 (=) }\n\n// Here's a random comment that is definitely useful\n\ndefine fizzbuzz (Int -> String) {\n  -> n;\n  do (with (+Fail)) { n 5 divisible n 3 divisible }\n  if { if { \"FizzBuzz\" } else { \"Fizz\" } }\n  else { if { \"Buzz\" } else { n show } }\n}\n\ndefine fizzbuzzes (Int, Int -> +IO) {\n  -> c, m;\n  c fizzbuzz println (c < m)\n  if { (c + 1) m fizzbuzzes }\n  else {} // We don't need anything here\n}\n\n1 100 fizzbuzzes\n// Comment at end\n"
  },
  {
    "path": "tests/data/moduledef.def",
    "content": "; 17 lines 9 code 6 comments 2 blanks\n;\n; Definition file of KERNEL32.dll\n; Automatic generated by gendef\n; written by Kai Tietz 2008\n;\nLIBRARY \"KERNEL32.dll\"\n\nEXPORTS\n\"BaseThreadInitThunk;@4\"\nInterlockedPushListSList@8\nAcquireSRWLockExclusive@4\nAcquireSRWLockShared@4\nActivateActCtx@8\nAddAtomA@4\nAddAtomW@4\n\n"
  },
  {
    "path": "tests/data/mojo.mojo",
    "content": "# 21 lines 15 code 3 comments 3 blanks\n\n\n'''\nThis is a docstring.\n# It has multiple lines.\nThis is the end of the docstring.\n'''\ndef main():\n    # Hello Mojo!\n    string = \"Hello Mojo!\"\n    # The following line prints the string \"Hello Mojo!\"\n    print(string)\n\n    \"\"\"\n    This piece of code prints \n    the numbers \"9\", \"6\", and \"3\".\n    'Here is a quote.'\n    \"\"\"\n    for x in range(9, 0, -3):\n        print(x)\n"
  },
  {
    "path": "tests/data/monkeyc.mc",
    "content": "// 69 lines 41 code 18 comments 10 blanks\n// Slightly modified template from the \"Garmin.monkey-c\" VS Code extension.\nimport Toybox.Application;\nimport Toybox.Graphics;\nimport Toybox.Lang;\nimport Toybox.System;\nimport Toybox.WatchUi;\n\nclass WatchFaceView extends WatchUi.WatchFace {\n\n    function initialize() {\n        WatchFace.initialize();\n    }\n\n    // Load your resources here\n    function onLayout(dc as Dc) as Void {\n        setLayout(Rez.Layouts.WatchFace(dc));\n    }\n\n    /*\n       Called when this View is brought to the foreground. Restore\n       the state of this View and prepare it to be shown. This includes\n       loading resources into memory.\n    */\n    function onShow() as Void {\n    }\n\n    // Update the view\n    function onUpdate(dc as Dc) as Void {\n        // Get the current time and format it correctly\n        var timeFormat = \"$1$:$2$\";\n        var clockTime = System.getClockTime();\n        var hours = clockTime.hour;\n        if (!System.getDeviceSettings().is24Hour) {\n            if (hours > 12) {\n                hours = hours - 12;\n            }\n        } else {\n            if (getApp().getProperty(\"UseMilitaryFormat\")) {\n                timeFormat = \"$1$$2$\";\n                hours = hours.format(\"%02d\");\n            }\n        }\n        var timeString = Lang.format(timeFormat, [hours, clockTime.min.format(\"%02d\")]);\n\n        // Update the view\n        var view = View.findDrawableById(\"TimeLabel\") as Text;\n        view.setColor(getApp().getProperty(\"ForegroundColor\") as Number);\n        view.setText(timeString);\n\n        View.onUpdate(dc); // Call the parent onUpdate function to redraw the layout\n    }\n\n    /* \n       Called when this View is removed from the screen. Save the\n       state of this View here. This includes freeing resources from\n       memory.\n    */\n    function onHide() as Void {\n    }\n\n    // The user has just looked at their watch. Timers and animations may be started here.\n    function onExitSleep() as Void {\n    }\n\n    // Terminate any active timers and prepare for slow updates.\n    function onEnterSleep() as Void {\n    }\n}"
  },
  {
    "path": "tests/data/nextflow.nf",
    "content": "/* 18 lines 10 code 5 comments 3 blanks */\n\n/*\nNextflow - hello\n*/\n\n// comment\ncheers = Channel.from 'Bonjour', 'Ciao', 'Hello', 'Hola'\n\nprocess sayHello {\n  echo true\n  input: \n    val x from cheers\n  script:\n    \"\"\"\n    echo '$x world!'\n    \"\"\"\n}\n"
  },
  {
    "path": "tests/data/nqp.nqp",
    "content": "# 24 lines 14 code 8 comments 2 blanks\n=begin\nRegex methods and functions\n=end\n\n=begin item match\nMatch C<$text> against C<$regex>.  If the C<$global> flag is\ngiven, then return an array of all non-overlapping matches.\n=end item\n\nsub match ($text, $regex, :$global?) {\n    my $match := $text ~~ $regex;\n    if $global {\n        my @matches;\n        while $match {\n            nqp::push(@matches, $match);\n            $match := $match.parse($text, :rule($regex), :c($match.to));\n        }\n        @matches;\n    }\n    else {\n        $match;\n    }\n}\n"
  },
  {
    "path": "tests/data/odin.odin",
    "content": "// 29 lines 17 code 7 comments 5 blanks\nimport \"core:fmt\"\n\n/*\n * Calculates the next number in the Collatz sequence\n *\n * If `x` is divisible by two, the result is `x` divided by two\n * If `x` is not divisible by two, the result is `x` multiplied by three plus one\n */\ncollatz :: inline proc(x: int) -> int {\n\tif x & 1 == 0 do return x >> 1;\n\telse do return x * 3 + 1;\n}\n\nsteps :: proc(x: int) -> int {\n\tcount := 0;\n\n\ty := x;\n\tfor y != 1 {\n\t\ty = collatz(y);\n\t\tcount += 1;\n\t}\n\n\treturn count;\n}\n\nmain :: proc() {\n\tfmt.println(steps(42)); // 8\n}"
  },
  {
    "path": "tests/data/open_policy_agent.rego",
    "content": "# 13 lines 8 code 3 comments 2 blanks\n\npackage application.authz\n\n# Only owner can update the pet's information\n# Ownership information is provided as part of OPA's input\ndefault allow = false\nallow {\n    input.method == \"PUT\"\n    some petid\n    input.path = [\"pets\", petid]\n    input.user == input.owner\n}\n"
  },
  {
    "path": "tests/data/openscad.scad",
    "content": "//! 34 lines 15 code 16 comments 3 blanks\n// https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/Commented_Example_Projects\n// The idea is to twist a translated circle:\n// -\n/*\n\tlinear_extrude(height = 10, twist = 360, scale = 0)\n\ttranslate([1,0])\n\tcircle(r = 1);\n*/\n\nmodule horn(height = 10, radius = 6,\n\t\t\ttwist = 720, $fn = 50)\n{\n\t// A centered circle translated by 1xR and\n\t// twisted by 360° degrees, covers a 2x(2xR) space.\n\t// -\n\tradius = radius/4;\n\t// De-translate.\n\t// -\n\ttranslate([-radius,0])\n\t// The actual code.\n\t// -\n\tlinear_extrude(height = height, twist = twist,\n\t\t\t\t   scale=0, $fn = $fn)\n\ttranslate([radius,0])\n\tcircle(r=radius);\n}\n\ntranslate([3,0])\nmirror()\nhorn();\n\ntranslate([-3,0])\nhorn();\n"
  },
  {
    "path": "tests/data/opentype.fea",
    "content": "# 54 lines 24 code 24 comments 6 blanks\nlanguagesystem DFLT dflt;\nlanguagesystem latn dflt;\nlanguagesystem latn DEU;\nlanguagesystem latn TRK;\nlanguagesystem cyrl dflt;\n\nfeature smcp {\n    sub [a-z] by [A.sc-Z.sc];\n    # Since all the rules in this feature are of the same type, they will be grouped in a single lookup.\n    # Since no script or language keyword has been specified yet,\n    # the lookup will be registered for this feature under all the language systems.\n} smcp;\n\nfeature liga {\n    sub f f by f_f;\n    sub f i by f_i;\n    sub f l by f_l;\n    # Since all the rules in this feature are of the same type, they will be\n    # grouped in a single lookup.\n    # Since no script or language keyword has been specified yet,\n    # the lookup will be registered for this feature under all the language systems.\n\n    script latn;\n        language dflt;\n        # lookupflag 0;      (implicit)\n            sub c t by c_t;\n            sub c s by c_s;\n        # The rules above will be placed in a lookup that is registered for all\n        # the specified languages for the script latn, but not any other scripts.\n\n        language DEU;\n        # script latn;       (stays the same)\n        # lookupflag 0;      (stays the same)\n            sub c h by c_h;\n            sub c k by c_k;\n        # The rules above will be placed in a lookup that is registered only\n        # under the script 'latn', 'language DEU'.\n\n        language TRK;\n        # This will inherit both the top level default rules - the rules defined\n        # before the first 'script' statement, and the script-level default\n        # rules for 'latn: all the lookups of this feature defined after the\n        # 'script latn' statement, and before the 'language DEU' statement.\n        # If 'TRK' were not named here, it would not inherit the default rules\n        # for the script 'latn'.\n} liga;\n\nfeature kern {\n    pos a y -150;\n    # [more pos statements]\n    # All the rules in this feature will be grouped in a single lookup\n    # that is registered under all the languagesystems.\n} kern;\n"
  },
  {
    "path": "tests/data/org_mode.org",
    "content": "# 13 lines 7 code 2 comments 4 blanks\n\n#+TITLE: This is the title, not a comment\n\n# This is comment\n\nSome text\n\n* Heading 1\n:PROPERTIES:\n:CUSTOM_ID: heading-1\n:END:\nText under heading 1\n"
  },
  {
    "path": "tests/data/pan.pan",
    "content": "# 21 lines 11 code 4 comments 6 blanks\n\n# Pan example code, see https://quattor-pan.readthedocs.io/en/stable/pan-book/index.html\n\nprefix \"/system/aii/osinstall/ks\";\n\"clearpart\" = append(\"vdb\");\n\"ignoredisk\" = list(); # no disks to ignore\n\nprefix \"/system/blockdevices\";\n\"physical_devs/vdb/label\" = \"msdos\";\n\"partitions/vdb1\" = dict(\n    \"holding_dev\", \"vdb\",\n);\n\n\"files/{/srv/elasticsearch}\" = dict('size', 0);\n\n# To facilitate adding other partitions at a later stage, a\n# logical volume will be created\n\"volume_groups/vg1/device_list\" = append(\"partitions/vdb1\");\n\"logical_volumes\" = lvs_add('vg1', dict(\"elasticsearch\", -1));\n\n"
  },
  {
    "path": "tests/data/pcss.pcss",
    "content": "/* 14 lines 6 code 5 comments 3 blanks */\n\n.foo {\n  color: #f00;\n\n  &.bar {\n    background: url(\"foobar.jpg\");\n  }\n}\n\n// inline comments are allowed by some PostCSS syntaxes\n/*\n * block comments are standard\n */\n"
  },
  {
    "path": "tests/data/pest.pest",
    "content": "// 9 lines 4 code 3 comments 2 blanks\nalpha = { 'a'..'z' | 'A'..'Z' }\ndigit = { '0'..'9' }\n\nident = { (alpha | digit)+ }\n\nident_list = _{ !digit ~ ident ~ (\" \" ~ ident)+ }\n          // ^\n          // ident_list rule is silent which means it produces no tokens"
  },
  {
    "path": "tests/data/phix.e",
    "content": "/* 40 lines 25 code 8 comments 7 blanks */\n\n-- copied from cpp, not necessarily idiomatic Euphoria code\n\ninclude std/sequence.e\n\n-- bubble_sort_function\npublic function bubble_sort(sequence a)\n    integer t = 0\n    integer j = length(a)\n    integer s = 1\n    while s > 0 do\n        s = 0\n        integer i = 2\n        while i <= j do\n            if a[i] < a[i - 1] then\n                t = a[i]\n                a[i] = a[i - 1]\n                a[i - 1] = t\n                s = 1\n            end if\n            i += 1\n        end while\n        j -= 1\n    end while\n    return a\nend function\n\nsequence a = {4, 65, 2, -31, 0, 99, 2, 83, 782, 1}\n\n-- Single line comment\n? {\"Before:\", a}\n\na = bubble_sort(a)\n\n/* multi\n * line\n * comment\n */\n? {\"After:\", a, equal(a, {-31,0,1,2,2,4,65,83,99,782})}"
  },
  {
    "path": "tests/data/plantuml.puml",
    "content": "' 35 lines 10 code 13 comments 12 blanks\n' plantuml line comments must start at the beginning of a line.\n' plantuml block comments must either start on a newline or start and end on the same line as they start.\n' strings cannot span multiple lines.\n' single quotes are a valid string wrapper '', but not when they are the first non-whitespace characters on a line\n\n' comment at start uml\n@startuml\n\n!include <C4/C4_Container>\n\n'' this is also a comment\n\nrectangle \"this is a string\" as r\n/' this\nis a multi-line\ncomment '/\n\n/' this is also a multi-line comment '/\nContainer(C, \"This is some more text\", \"text\")\n\nnode n /' this is a multi-line comment at the end of a line '/\n\n/' this is /' a valid '/ block comment '/\n\ncomponent \"'this is not a comment\"\n\n/' this is a multi-line comment at the start of a line '/ interface i\n\nboundary \"/' this is not a multi-line comment '/\"\n\nSystem(s, \"this is /' not part of a comment\", \"'/ this is also not part of a comment\", \"/' '/ /' neither is this\")\n\n' comment after start uml\n@enduml\n"
  },
  {
    "path": "tests/data/pofile.po",
    "content": "# 14 lines 6 code 5 comments 3 blanks\n\n#: lib/error.c:116\nmsgid \"Unknown system error\"\nmsgstr \"Error desconegut del sistema\"\n\n#: disk-utils/addpart.c:15\n#, c-format\nmsgid \" %s <disk device> <partition number> <start> <length>\\n\"\nmsgstr \" %s <périphérique disque> <numéro de partition> <début> <longueur>\\n\"\n\n#: disk-utils/addpart.c:19\nmsgid \"Tell the kernel about the existence of a specified partition.\\n\"\nmsgstr \"Informer le noyau de l’existence d’une partition indiquée.\\n\"\n"
  },
  {
    "path": "tests/data/pofile_pot.pot",
    "content": "# 17 lines 8 code 5 comments 4 blanks\n\n#: disk-utils/addpart.c:60 disk-utils/delpart.c:61 disk-utils/resizepart.c:101\nmsgid \"invalid partition number argument\"\nmsgstr \"\"\n\n#: disk-utils/addpart.c:61\nmsgid \"invalid start argument\"\nmsgstr \"\"\n\n#: disk-utils/addpart.c:62 disk-utils/resizepart.c:111\nmsgid \"invalid length argument\"\nmsgstr \"\"\n\n#: disk-utils/addpart.c:63\nmsgid \"failed to add partition\"\nmsgstr \"\"\n"
  },
  {
    "path": "tests/data/poke.pk",
    "content": "/* 4 lines 2 code 1 comments 1 blanks */\n\nvar N = 3;\nfun getoff = offset<uint<64>,B>: { return 2#B; }\n"
  },
  {
    "path": "tests/data/pony.pony",
    "content": "// 12 lines 7 code 3 comments 2 blanks\n\n/* com-\n    -ment */\n\nactor Main\n    \"\"\"\n        Some\n        Docs\n    \"\"\"\n    new create(env: Env) =>\n        env.out.print(\"Hello, world.\")\n"
  },
  {
    "path": "tests/data/postcss.sss",
    "content": "// 27 lines 18 code 4 comments 5 blanks\n\n/**\nmulti-line\n*/\ndiv {\n  width: calc(99.9% * 1/3 -  (30px - 30px * 1/3)); \n}\n\ndiv:nth-child(1n) {\n  float: left; \n  margin-right: 30px; \n  clear: none; \n}\n\ndiv:last-child {\n  margin-right: 0; \n}\n\ndiv:nth-child(3n) {\n  margin-right: 0; \n  float: right; \n}\n\ndiv:nth-child(3n + 1) {\n  clear: both; \n}\n"
  },
  {
    "path": "tests/data/powershell.ps1",
    "content": "# 17 lines 9 code 4 comments 4 blanks\n\n<#\nTest\n#>\n\n'a' + \"b\"\n\nWrite-Host @\"\n    Name: $name\n    Address: $address\n\"@\n\n$template = @'\n    Name: {0}\n    Address: {0}\n'@\n"
  },
  {
    "path": "tests/data/pug.pug",
    "content": "//- 13 lines, 8 code, 3 comments, 2 blanks\n\ndoctype html\n// this comment will be translated to an HTML comment\n//- this comment will be excluded from the generated HTML\n\nhtml\n  head\n    title Hello, World!\n  body\n    p\n      | Hello,\n      | World!\n"
  },
  {
    "path": "tests/data/puppet.pp",
    "content": "# 18 lines 14 code 3 comments 1 blanks\nclass example::class(\n  $param1,\n  $param2=2,\n  $param3=undef,  # pass this one\n) {\n  # comments are really simple\n  some::resource {\n    'bar':\n      param1 => param2,\n      # comments here too\n      param3 => param4;\n  }\n\n  some::other::resource {\n    'baz':\n  }\n}\n"
  },
  {
    "path": "tests/data/pyret.arr",
    "content": "# 22 lines 9 code 8 comments 5 blanks\n\nfun single-quote():\n  doc: \"this is a documentation string\"\n  'foo'\nend\n\n#|\n  Hello, this is a multiline message\n|#\n\n# This is a line message\n\nfun double-quotes():\n  \"bar\"\nend\n\nnested = #|\n  doesn't start yet\n  or yet\n|#\n\"nested\""
  },
  {
    "path": "tests/data/python.py",
    "content": "# 15 lines, 10 code, 2 comments, 3 blanks\n\n\ndef add(x, y):\n    \"\"\"\n    Hello World\n    # Real Second line\n    Second line\n    \"\"\"\n\n    string = \"Hello World  #\\\n    \"\n    y += len(string)\n    # Add the two numbers.\n    x + y\n"
  },
  {
    "path": "tests/data/q.q",
    "content": "// 14 lines 5 code 5 comments 4 blanks\n\n/calc nav for sets of portfolios,ETFs,indices,..\n\n/one day of ([]time;sym;price) sorted by time\nn:10000000;S:-10000?`4\nt:([]time:09:30:00.0+til n;sym:n?S;price:n?1.0)\n\n/calc price deltas once\n\\t update deltas price by sym from`t\n\n/for each portfolio\na:([sym:-100?S]weight:100?1.0)\n\\t r:select time,sums price*weight from t ij a\n"
  },
  {
    "path": "tests/data/qml.qml",
    "content": "// 20 lines 11 code 5 comments 4 blanks\n\nimport QtQuick 2.7\nimport QtQuick.Controls 2.0\n\nApplicationWindow {\n    visible: true\n\n    /*\n     * Multiline comment\n     */\n    Text {\n        text: \"string type 1\"\n    }\n\n    // comment\n    function testfunc() {\n        console.log('string type 2');\n    }\n}\n"
  },
  {
    "path": "tests/data/racket.rkt",
    "content": ";;; 40 lines 15 code 14 comments 11 blanks\n#lang racket ; defines the language we are using\n\n;;; Comments\n\n;; Single line comments start with a semicolon\n\n#| Block comments\n   can span multiple lines and...\n    #|\n        they can be nested!\n    |#\n|#\n\n;; S-expression comments discard the following expression\n;; since this is syntax-aware, tokei counts this as code\n#; (this expression is discarded)\n\n;; Constant\n(define %pi 3.14159265358979323846)\n\n#| This is a block comment |#\n(define (degrees->radians deg)\n  (* deg (/ %pi 180)))\n\n;; Function\n(define (sq x) (* x x))\n\n(define (sum xs)\n  \"Sum list of elements.\"\n  (foldl + 0 xs)) ; comment\n\n(define (sum-upto n)\n  (/ (* n (+ 1 n)) 2))\n\n(define (test-sums n)\n  (= (sum-upto n)\n     (sum (range (+ 1 n)))))\n\n(test-sums 100)\n"
  },
  {
    "path": "tests/data/raku.raku",
    "content": "# 49 lines 37 code 6 comments 6 blanks\n\n=begin pod\n\n=begin DESCRIPTION\n\n=head1 Test file for Tokei\n\n=end DESCRIPTION\n\n=begin code :lang<raku>\n\nsay 'Hello World';\n\n=end code\n\n=end pod\n\n#| Fibonacci with multiple dispatch\nmulti sub fib (0 --> 0) {}\nmulti sub fib (1 --> 1) {}\nmulti sub fib (\\n where * > 1) {\n    fib(n - 1) + fib(n - 2)\n}\n\n#|{\nRole shape\nfor printing area of different shapes\n}\nrole Shape {\n    method area { ... }\n\n    method print_area {\n        say \"Area of {self.^name} is {self.area}.\";\n    }\n}\n\nclass Rectangle does Shape {\n    has $.width is required;   #= Width of rectangle\n    has $.height is required;  #= Height of rectangle\n\n    method area {\n        #`(\n        area of rectangle:\n        width times height\n         )\n        $!width × $!height\n    }\n}\n"
  },
  {
    "path": "tests/data/razor.cshtml",
    "content": "@* 55 lines 35 code 15 comments 5 blanks *@\n@page \"/\"\n@using Microsoft.AspNetCore.Components.Web\n@namespace temp.Pages\n@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers\n\n@{\n    // foo\n    string foo = \"bar\";\n\n    /*\n    * bar\n    */\n    string bar = \"foo\";\n}\n\n<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"utf-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n    <base href=\"~/\" />\n    <link rel=\"stylesheet\" href=\"css/bootstrap/bootstrap.min.css\" />\n    <link href=\"css/site.css\" rel=\"stylesheet\" />\n    <link href=\"temp.styles.css\" rel=\"stylesheet\" />\n    <link rel=\"icon\" type=\"image/png\" href=\"favicon.png\"/>\n    <component type=\"typeof(HeadOutlet)\" render-mode=\"ServerPrerendered\" />\n</head>\n<body>\n    @*\n    \n    multi-line comment\n    \n    *@\n    <component type=\"typeof(App)\" render-mode=\"ServerPrerendered\" />\n\n    <div id=\"blazor-error-ui\">\n        <environment include=\"Staging,Production\">\n            An error has occurred. This application may no longer respond until reloaded.\n        </environment>\n        <!--\n        \n        different multi-line comment\n        \n        -->\n        <environment include=\"Development\">\n            An unhandled exception has occurred. See browser dev tools for details.\n        </environment>\n        <a href=\"\" class=\"reload\">Reload</a>\n        <a class=\"dismiss\">🗙</a>\n    </div>\n\n    <script src=\"_framework/blazor.server.js\"></script>\n</body>\n</html>\n"
  },
  {
    "path": "tests/data/razorcomponent.razor",
    "content": "@* 45 lines 16 code 21 comments 8 blanks *@\n@page \"/counter\"\n\n@{\n    // foo\n    string foo = \"bar\";\n\n    /*\n    * bar\n    */\n    string bar = \"foo\";\n}\n\n<PageTitle>Counter</PageTitle>\n\n@*\n\nmulti-line comment\n\n*@\n<h1>Counter</h1>\n\n<p role=\"status\">Current count: @currentCount</p>\n\n<!--\n\ndifferent multi-line comment\n\n-->\n<button class=\"btn btn-primary\" @onclick=\"IncrementCount\">Click me</button>\n\n@code {\n    /*\n    \n    C# style multi-line comment\n    \n    */\n    private int currentCount = 0;\n\n    private void IncrementCount()\n    {\n        // increment the count\n        currentCount++;\n    }\n}\n"
  },
  {
    "path": "tests/data/redscript.reds",
    "content": "// 75 lines 47 code 20 comments 8 blanks\n\n// redscript allows line comments\n/* as well as block comments */\n\n// it supports global functions\nfunc add2(x: Int32, y: Int32) -> Int32 {\n  return x + y;\n}\n\n// functions without a type annotation default to Void return type\nfunc tutorial() {\n  let x: Int32 = 10;\n  // compiler can infer types for local variables, y will be Int32\n  let y = 20;\n  // it supports arithmetic\n  let sum = x + y + 13;\n  // as well as mutation\n  let mutable = 0;\n  mutable += 10;\n  // numbers with decimal points default to type Float\n  let num = 10.0;\n  // you can cast between some types\n  let uint: Uint8 = Cast(10);\n  // array literals\n  let arr = [1, 2, 3];\n  // array iteration\n  for item in arr {\n    // logging and string operations\n    Log(\"at \" + ToString(item));\n  }\n}\n\n// you can define your own classes\npublic class IntTuple {\n  let fst: Int32;\n  let snd: Int32;\n\n  // you can define static member functions\n  public static func Create(fst: Int32, snd: Int32) -> ref<IntTuple> {\n    let tuple = new IntTuple();\n    tuple.fst = fst;\n    tuple.snd = snd;\n    return tuple;\n  }\n \n  public func Swap() {\n    let tmp = this.fst;\n    this.fst = this.snd;\n    this.snd = tmp;\n  }\n}\n\n// you can replace existing in-game methods by specifying the class they belong to\n@replaceMethod(CraftingSystem)\nprivate final func ProcessCraftSkill(xpAmount: Int32, craftedItem: StatsObjectID) {\n  // instantiate a class using the new operator\n  let xpEvent = new ExperiencePointsEvent();\n  xpEvent.amount = xpAmount * 100;\n  xpEvent.type = gamedataProficiencyType.Crafting;\n  GetPlayer(this.GetGameInstance()).QueueEvent(xpEvent);\n}\n\n// you can add new methods to existing classes as well\n// they are visible to other code using the class\n@addMethod(BackpackMainGameController)\nprivate final func DisassembleAllJunkItems() -> Void {\n  let items = this.m_InventoryManager.GetPlayerItemsByType(gamedataItemType.Gen_Junk);\n  let i = 0;\n  for item in items {\n    ItemActionsHelper.DisassembleItem(this.m_player, InventoryItemData.GetID(item));\n  };\n  // some methods require CName literals, they need to be prefixed with the n letter\n  this.PlaySound(n\"Item\", n\"OnBuy\");\n}"
  },
  {
    "path": "tests/data/renpy.rpy",
    "content": "# 32 lines 8 code 9 comments 15 blanks\n\n# Declare characters used by this game. The color argument colorizes the\n# name of the character.\n\ndefine e = Character(\"Eileen\")\n\n\n# The game starts here.\n\nlabel start:\n\n    # Show a background. This uses a placeholder by default, but you can\n    # add a file (named either \"bg room.png\" or \"bg room.jpg\") to the\n    # images directory to show it.\n\n    scene bg room\n\n\n    show eileen happy\n\n    # These display lines of dialogue.\n\n    e \"You've created a new Ren'Py game.\"\n\n    e 'Once you add a story, pictures, and music, you can release it to the world!'\n\n    e `Testing, testing`\n\n    # This ends the game.\n\n    return\n"
  },
  {
    "path": "tests/data/roc.roc",
    "content": "# 36 lines 18 code 10 comments 8 blanks\nmodule [square]\n# this is a comment\n# this is another comment\n\na1 = 1\na2 = 3.14159 # pi\n\nexpect\n    # simple check\n    a1 == 1\n\nexpect\n    a2 |> Num.toStr == \"3.14159\"\n\n## Compute the square\nsquare = \\x ->\n    s = x * x\n\n    # the line above is blank\n    s\n\nexpect square 3 == 9\n\n## \"\"\"\n## this is not a multiline string,\n## it's a doc comment\n## \"\"\"\nmultilineString =\n    \"\"\"\n    # this line is not a comment, it's actually code\n\n    The line above is not blank, it's actually code\n    \"\"\"\n\nexpect multilineString |> Str.toUtf8 |> List.first == Ok '#'\n"
  },
  {
    "path": "tests/data/ron.ron",
    "content": "// 157 lines 137 code 7 comments 13 blanks\n\n#![enable(implicit_some)]\nContainer(\n    transform: (\n        id: \"background\",\n        anchor: Middle,\n        stretch: XY( x_margin: 0., y_margin: 0., keep_aspect_ratio: false),\n        width: 20.,\n        height: 20.,\n    ),\n    background: SolidColor(0.03, 0.03, 0.03, 1.0),\n    children: [\n\n        Container(\n            transform: (\n                id: \"container_start\",\n                y: 180,\n                width: 755.,\n                height: 170.,\n                anchor: Middle,\n            ),\n            background: SolidColor(1.0, 0.65, 0.0, 1.0),\n            children: [\n\n                // Complex Button\n                Button(\n                    transform: (\n                        id: \"start\",\n                        width: 750.,\n                        height: 165.,\n                        tab_order: 1,\n                        anchor: Middle,\n                        mouse_reactive: true,\n                    ),\n                    button: (\n                        text: \"START GAME\",\n                        font: File(\"font/square.ttf\", (\"TTF\", ())),\n                        font_size: 75.,\n                        normal_text_color: (1.0, 0.65, 0., 1.0), // ffa500\n                        // hover_text_color: (1.0, 0.65, 0., 1.0),\n                        // press_text_color: (1.0, 0.65, 0., 1.0),\n                        normal_image: SolidColor(0., 0., 0., 1.),\n                        hover_image: SolidColor(0.1, 0.1, 0.1, 1.),\n                        press_image: SolidColor(0.15, 0.15, 0.15, 1.),\n                    )\n                ),\n            ]\n        ),\n\n\n        Container(\n            transform: (\n                id: \"container_load\",\n                y: 0,\n                width: 755.,\n                height: 170.,\n                anchor: Middle,\n            ),\n            background: SolidColor(1.0, 0.65, 0.0, 1.0),\n            children: [\n\n                // Complex Button\n                Button(\n                    transform: (\n                        id: \"load\",\n                        width: 750.,\n                        height: 165.,\n                        tab_order: 3,\n                        anchor: Middle,\n                        mouse_reactive: true,\n                    ),\n                    button: (\n                        text: \"LOAD GAME\",\n                        font: File(\"font/square.ttf\", (\"TTF\", ())),\n                        font_size: 75.,\n                        normal_text_color: (1.0, 0.65, 0., 1.0), // ffa500\n                        normal_image: SolidColor(0., 0., 0., 1.),\n                        hover_image: SolidColor(0.1, 0.1, 0.1, 1.),\n                        press_image: SolidColor(0.15, 0.15, 0.15, 1.),\n                    )\n                ),\n            ]\n        ),\n\n\n        Container(\n            transform: (\n                id: \"container_options\",\n                y: -180,\n                width: 755.,\n                height: 170.,\n                anchor: Middle,\n            ),\n            background: SolidColor(1.0, 0.65, 0.0, 1.0),\n            children: [\n\n                // Complex Button\n                Button(\n                    transform: (\n                        id: \"options\",\n                        width: 750.,\n                        height: 165.,\n                        tab_order: 3,\n                        anchor: Middle,\n                        mouse_reactive: true,\n                    ),\n                    button: (\n                        text: \"OPTIONS\",\n                        font: File(\"font/square.ttf\", (\"TTF\", ())),\n                        font_size: 75.,\n                        normal_text_color: (1.0, 0.65, 0., 1.0), // ffa500\n                        normal_image: SolidColor(0., 0., 0., 1.),\n                        hover_image: SolidColor(0.1, 0.1, 0.1, 1.),\n                        press_image: SolidColor(0.15, 0.15, 0.15, 1.),\n                    )\n                ),\n            ]\n        ),\n\n\n        Container(\n            transform: (\n                id: \"container_credits\",\n                y: -360,\n                width: 755.,\n                height: 170.,\n                anchor: Middle,\n            ),\n            background: SolidColor(1.0, 0.65, 0.0, 1.0),\n            children: [\n\n                // Complex Button\n                Button(\n                    transform: (\n                        id: \"credits\",\n                        width: 750.,\n                        height: 165.,\n                        tab_order: 3,\n                        anchor: Middle,\n                        mouse_reactive: true,\n                    ),\n                    button: (\n                        text: \"CREDITS\",\n                        font: File(\"font/square.ttf\", (\"TTF\", ())),\n                        font_size: 75.,\n                        normal_text_color: (1.0, 0.65, 0., 1.0), // ffa500\n                        normal_image: SolidColor(0., 0., 0., 1.),\n                        hover_image: SolidColor(0.1, 0.1, 0.1, 1.),\n                        press_image: SolidColor(0.15, 0.15, 0.15, 1.),\n                    )\n                ),\n            ]\n        ),\n\n    ],\n)\n"
  },
  {
    "path": "tests/data/rpmspec.spec",
    "content": "# 42 lines 22 code 4 comments 16 blanks\n\nName:           example\nVersion:        0.0.1\nRelease:        1%{?dist}\nSummary:        an example specfile\n\nGroup:          \nURL:            \nSource0:        \n\n# test comments for requirements\nBuildRequires:  \nRequires:       \n\n%description\n\n\n%prep\n%setup -q\n\n\n# build the project\n%build\n%configure\nmake build\n\n\n# install the files here\n%install\nmake install\n\n\n%clean\n\n\n%files\n%defattr(-,root,root,-)\n%doc\n\n\n%changelog\n"
  },
  {
    "path": "tests/data/ruby.rb",
    "content": "# 20 lines 9 code 8 comments 3 blanks\nx = 3\nif x < 2\n  p = \"Smaller\"\nelse\n  p = \"Bigger\"\nend\n\n=begin\n  Comments\n  Comments\n  Comments\n  Comments\n=end\n\n# testing.\nwhile x > 2 and x < 10\n  x += 1\nend\n\n"
  },
  {
    "path": "tests/data/ruby_env",
    "content": "#!/usr/bin/env ruby\n# 11 lines 3 code 6 comments 2 blanks\n\n=begin\n  Comment that only counts if detected as ruby\n  Comments\n=end\n\nwhile x > 2 and x < 10\n  x += 1\nend"
  },
  {
    "path": "tests/data/ruby_html.erb",
    "content": "<!-- 34 lines 21 code 8 comments 5 blanks -->\n<!DOCTYPE html>\n<html>\n  <head>\n    <meta charset=\"utf-8\" />\n    <meta name=\"viewport\" content=\"width=device-width\" />\n    <title><%= title %></title>\n  </head>\n\n  <body>\n    <nav class=\"navbar navbar-default navbar-fixed-top navbar-custom\">\n      <%# ruby comment %>\n      <div id=\"modalSearch\" class=\"modal fade\" role=\"dialog\"> </div>\n    </nav>\n\n    <!-- HTML single line Comment-->\n    <main>\n      <article>\n        <h1><%= header %></h1>\n        <p><%= text %></p>\n      </article>\n    </main>\n\n    <%= template \"footer\" %>\n  </body>\n\n  <!--\n          document.write(\"Multi-line and Code comment!\");\n  //-->\n\n  <!--[if IE 8]>\n          IE Special comment\n  <![endif]-->\n</html>\n"
  },
  {
    "path": "tests/data/rust.rs",
    "content": "//! 48 lines 36 code 6 comments 6 blanks\n//! ```rust\n//! fn main () {\n//!     // Comment\n//!\n//!     println!(\"Hello World!\");\n//! }\n//! ```\n\n/* /**/ */\nfn main() {\n    let start = r##\"/*##\\\"\n\\\"##;\n    // comment\n    loop {\n        if x.len() >= 2 && x[0] == '*' && x[1] == '/' { // found the */\n            break;\n        }\n    }\n}\n\nfn foo<'a, 'b>(name: &'b str) {\n    let this_ends = \"a \\\"test/*.\";\n    call1();\n    call2();\n    let this_does_not = /* a /* nested */ comment \" */\n        \"*/another /*test\n            call3();\n            */\";\n}\n\nfn foobar() {\n    let does_not_start = // \"\n        \"until here,\n        test/*\n        test\"; // a quote: \"\n    let also_doesnt_start = /* \" */\n        \"until here,\n        test,*/\n        test\"; // another quote: \"\n}\n\nfn foo() {\n    let a = 4; // /*\n    let b = 5;\n    let c = 6; // */\n}\n\n"
  },
  {
    "path": "tests/data/scheme.scm",
    "content": ";;; 26 lines 14 code 4 comments 8 blanks\n\n(import (srfi srfi-1)) ; for reduce\n\n;; Constant\n(define %pi 3.14159265358979323846)\n\n#| This is a block comment |#\n(define (degrees->radians deg)\n  (* deg (/ %pi 180)))\n\n;; Function\n(define (sq x) (* x x))\n\n(define (sum xs)\n  \"Sum list of elements.\"\n  (reduce + 0 xs)) ; comment\n\n(define (sum-upto n)\n  (/ (* n (1+ n)) 2))\n\n(define (test-sums n)\n  (= (sum-upto n)\n     (sum (iota (1+ n)))))\n\n(test-sums 100)\n"
  },
  {
    "path": "tests/data/shaderlab.shader",
    "content": "// 43 lines 31 code 8 comments 4 blanks\nShader \"Custom/Sample shader\"\n{\n    Properties\n    {\n        _MainTex (\"Texture\", 2D) = \"white\" {}\n    }\n    SubShader\n    {\n        Tags { \"Queue\"=\"Transparent\" \"RenderType\"=\"Transparent\" }\n\n        // blending\n        Blend SrcAlpha OneMinusSrcAlpha\n        /*\n\n          multi-line comment\n\n        */\n        Pass\n        {\n            CGPROGRAM\n            #pragma vertex vert\n\n            struct appdata\n            {\n                float4 vertex : POSITION;\n                float2 uv : TEXCOORD0;\n            };\n\n            sampler2D _MainTex;\n\n            // vertex\n            v2f vert (appdata v)\n            {\n                v2f o;\n                o.vertex = UnityObjectToClipPos(v.vertex);\n                o.uv = TRANSFORM_TEX(v.uv, _MainTex);\n                return o;\n            }\n            ENDCG\n        }\n    }\n}\n"
  },
  {
    "path": "tests/data/slang.slang",
    "content": "// 15 lines 8 code 5 comments 2 blanks\n\nTexture2D<float4> in_tex;\nRWTexture2D<float4> out_tex;\n\n// Blit compute shader\n[shader(\"compute\")]\n[numthreads(8, 8, 1)]\nvoid main(uint2 id: SV_DispatchThreadID) {\n  /*\n    Perform the blit\n  */\n  out_tex[id] = in_tex[id];\n  return;\n}\n"
  },
  {
    "path": "tests/data/slint.slint",
    "content": "// 26 lines 21 code 2 comments 3 blanks\n\ncomponent MyButton inherits Text {\n    color: black;\n    // ...\n}\n\nexport component MyApp inherits Window {\n    preferred-width: 200px;\n    preferred-height: 100px;\n    Rectangle {\n        width: 200px;\n        height: 100px;\n        background: green;\n        \n    }/* */\n    MyButton { \n        x:0;y:0;    // hello\n        text: \"hello\";\n    }\n    MyButton {  // world\n        y:0;\n        x: 50px;\n        text: \"world\";\n    }\n}"
  },
  {
    "path": "tests/data/solidity.sol",
    "content": "// 14 lines 6 code 7 comments 1 blanks\npragma solidity >=0.4.22 <0.6.0;\n\n// Comment line\ncontract Foo {\n    /*\n     Comment line\n     Comment line\n     Comment line\n     */\n    function foo(address bar) public {\n         require(bar != 0);\n    }\n}\n"
  },
  {
    "path": "tests/data/sql.sql",
    "content": "-- 12 lines 4 code 5 comments 3 blanks\r\n\r\n\r\nSELECT * FROM Users\r\nWHERE FirstName is not null; -- select rows where the user has a first name\r\n\r\n/* this is the beginning of a block comment\r\n\tinsert a new user into the Users table \r\n\t-- line comment in a block comment\r\n*/\r\nINSERT INTO Users (FirstName, LastName)\r\nVALUES (\"John\", \"Does\");"
  },
  {
    "path": "tests/data/srecode.srt",
    "content": ";; 37 lines 23 code 2 comments 12 blanks\n\nset escape_start \"$\"\nset escape_end \"$\"\nset mode \"srecode-template-mode\"\nset priority \"70\"\n\nset comment_start  \";;\"\nset comment_end    \"\"\nset comment_prefix \";;\"\n\nset SEPARATOR \"----\"\n\nset DOLLAR \"$\"\n\ncontext file\n\nprompt MAJORMODE \"Major Mode for templates: \" read srecode-read-major-mode-name\nprompt START \"Escape Start Characters: \" default \"{{\"\nprompt END \"Escape End Characters: \" default \"}}\"\n\ntemplate empty :file :user :time :srt\n\"Insert a skeleton for a template file.\"\n----\n$>:filecomment$\n\nset mode \"$?MAJORMODE$\"\nset escape_start \"$?START$\"\nset escape_end \"$?END$\"\n\ncontext file\n\n$^$\n\n\n;; end\n----\n"
  },
  {
    "path": "tests/data/stan.stan",
    "content": "// 142 lines 123 code 17 comments 2 blanks\n// Example code from https://github.com/TheEconomist/us-potus-model/blob/85be55ae7b0bc68cb155a9ca975e155837eb4851/scripts/model/poll_model_2020.stan\ndata{\n  int N_national_polls;    // Number of polls\n  int N_state_polls;    // Number of polls\n  int T;    // Number of days\n  int S;    // Number of states (for which at least 1 poll is available) + 1\n  int P;    // Number of pollsters\n  int M;    // Number of poll modes\n  int Pop;    // Number of poll populations\n  int<lower = 1, upper = S + 1> state[N_state_polls]; // State index\n  int<lower = 1, upper = T> day_state[N_state_polls];   // Day index\n  int<lower = 1, upper = T> day_national[N_national_polls];   // Day index\n  int<lower = 1, upper = P> poll_state[N_state_polls];  // Pollster index\n  int<lower = 1, upper = P> poll_national[N_national_polls];  // Pollster index\n  int<lower = 1, upper = M> poll_mode_state[N_state_polls];  // Poll mode index\n  int<lower = 1, upper = M> poll_mode_national[N_national_polls];  // Poll mode index\n  int<lower = 1, upper = Pop> poll_pop_state[N_state_polls];  // Poll mode index\n  int<lower = 1, upper = Pop> poll_pop_national[N_national_polls];  // Poll mode index\n  int n_democrat_national[N_national_polls];\n  int n_two_share_national[N_national_polls];\n  int n_democrat_state[N_state_polls];\n  int n_two_share_state[N_state_polls];\n  vector<lower = 0, upper = 1.0>[N_national_polls] unadjusted_national;\n  vector<lower = 0, upper = 1.0>[N_state_polls] unadjusted_state;\n  // cov_matrix[S] ss_cov_mu_b_walk;\n  // cov_matrix[S] ss_cov_mu_b_T;\n  // cov_matrix[S] ss_cov_poll_bias;\n  //*** prior input\n  vector[S] mu_b_prior;\n  vector[S] state_weights;\n  real sigma_c;\n  real sigma_m;\n  real sigma_pop;\n  real sigma_measure_noise_national;\n  real sigma_measure_noise_state;\n  real sigma_e_bias;\n  // covariance matrix and scales\n  cov_matrix[S] state_covariance_0;\n  real random_walk_scale;\n  real mu_b_T_scale;\n  real polling_bias_scale;\n}\ntransformed data {\n  real national_cov_matrix_error_sd = sqrt(transpose(state_weights) * state_covariance_0 * state_weights);\n  cholesky_factor_cov[S] cholesky_ss_cov_poll_bias;\n  cholesky_factor_cov[S] cholesky_ss_cov_mu_b_T;\n  cholesky_factor_cov[S] cholesky_ss_cov_mu_b_walk;\n  // scale covariance\n  matrix[S, S] ss_cov_poll_bias = state_covariance_0 * square(polling_bias_scale/national_cov_matrix_error_sd);\n  matrix[S, S] ss_cov_mu_b_T = state_covariance_0 * square(mu_b_T_scale/national_cov_matrix_error_sd);\n  matrix[S, S] ss_cov_mu_b_walk = state_covariance_0 * square(random_walk_scale/national_cov_matrix_error_sd);\n  // transformation\n  cholesky_ss_cov_poll_bias = cholesky_decompose(ss_cov_poll_bias);\n  cholesky_ss_cov_mu_b_T = cholesky_decompose(ss_cov_mu_b_T);\n  cholesky_ss_cov_mu_b_walk = cholesky_decompose(ss_cov_mu_b_walk);\n}\nparameters {\n  vector[S] raw_mu_b_T;\n  matrix[S, T] raw_mu_b;\n  vector[P] raw_mu_c;\n  vector[M] raw_mu_m;\n  vector[Pop] raw_mu_pop;\n  real<offset=0, multiplier=0.02> mu_e_bias;\n  real<lower = 0, upper = 1> rho_e_bias;\n  vector[T] raw_e_bias;\n  vector[N_national_polls] raw_measure_noise_national;\n  vector[N_state_polls] raw_measure_noise_state;\n  vector[S] raw_polling_bias;\n  real mu_b_T_model_estimation_error;\n}\ntransformed parameters {\n  //*** parameters\n  matrix[S, T] mu_b;\n  vector[P] mu_c;\n  vector[M] mu_m;\n  vector[Pop] mu_pop;\n  vector[T] e_bias;\n  vector[S] polling_bias = cholesky_ss_cov_poll_bias * raw_polling_bias;\n  vector[T] national_mu_b_average;\n  real national_polling_bias_average = transpose(polling_bias) * state_weights;\n  real sigma_rho;\n  //*** containers\n  vector[N_state_polls] logit_pi_democrat_state;\n  vector[N_national_polls] logit_pi_democrat_national;\n  //*** construct parameters\n  mu_b[:,T] = cholesky_ss_cov_mu_b_T * raw_mu_b_T + mu_b_prior;  // * mu_b_T_model_estimation_error\n  for (i in 1:(T-1)) mu_b[:, T - i] = cholesky_ss_cov_mu_b_walk * raw_mu_b[:, T - i] + mu_b[:, T + 1 - i];\n  national_mu_b_average = transpose(mu_b) * state_weights;\n  mu_c = raw_mu_c * sigma_c;\n  mu_m = raw_mu_m * sigma_m;\n  mu_pop = raw_mu_pop * sigma_pop;\n  e_bias[1] = raw_e_bias[1] * sigma_e_bias;\n  sigma_rho = sqrt(1-square(rho_e_bias)) * sigma_e_bias;\n  for (t in 2:T) e_bias[t] = mu_e_bias + rho_e_bias * (e_bias[t - 1] - mu_e_bias) + raw_e_bias[t] * sigma_rho;\n  //*** fill pi_democrat\n  for (i in 1:N_state_polls){\n    logit_pi_democrat_state[i] =\n      mu_b[state[i], day_state[i]] +\n      mu_c[poll_state[i]] +\n      mu_m[poll_mode_state[i]] +\n      mu_pop[poll_pop_state[i]] +\n      unadjusted_state[i] * e_bias[day_state[i]] +\n      raw_measure_noise_state[i] * sigma_measure_noise_state +\n      polling_bias[state[i]];\n  }\n  logit_pi_democrat_national =\n    national_mu_b_average[day_national] +\n    mu_c[poll_national] +\n    mu_m[poll_mode_national] +\n    mu_pop[poll_pop_national] +\n    unadjusted_national .* e_bias[day_national] +\n    raw_measure_noise_national * sigma_measure_noise_national +\n    national_polling_bias_average;\n}\n\nmodel {\n  //*** priors\n  raw_mu_b_T ~ std_normal();\n  //mu_b_T_model_estimation_error ~ scaled_inv_chi_square(7, 1);\n  to_vector(raw_mu_b) ~ std_normal();\n  raw_mu_c ~ std_normal();\n  raw_mu_m ~ std_normal();\n  raw_mu_pop ~ std_normal();\n  mu_e_bias ~ normal(0, 0.02);\n  rho_e_bias ~ normal(0.7, 0.1);\n  raw_e_bias ~ std_normal();\n  raw_measure_noise_national ~ std_normal();\n  raw_measure_noise_state ~ std_normal();\n  raw_polling_bias ~ std_normal();\n  //*** likelihood\n  n_democrat_state ~ binomial_logit(n_two_share_state, logit_pi_democrat_state);\n  n_democrat_national ~ binomial_logit(n_two_share_national, logit_pi_democrat_national);\n}\n\ngenerated quantities {\n  matrix[T, S] predicted_score;\n  for (s in 1:S){\n    //predicted_score[1:T, s] = inv_logit(mu_a[1:T] + to_vector(mu_b[s, 1:T]));\n    predicted_score[1:T, s] = inv_logit(to_vector(mu_b[s, 1:T]));\n  }\n}\n"
  },
  {
    "path": "tests/data/stata.do",
    "content": "* 16 lines 6 code 7 comments 3 blanks\n* This is a comment\n**** Any number of * symbol\n\nuse \"foo.dta\", replace\ngen x = 1*2\ngen x2 = 1/2\n/*\nHere's a comment block\n*/\n\nif c(username) == \"foobar\" {\n    global FOO 1\n}\n\n// Finally another symbol for comment"
  },
  {
    "path": "tests/data/stratego.str",
    "content": "// 24 lines 12 code 6 comments 6 blanks\nmodule stratego\n\nstrategies\n\n/** // */\nmain =\n  !\"/* \"\n  ; id // */\n\nfoo =\n  ?'a'\n\nrules\n\nfoobar: \"//\" -> \"\\\\\" // \" '\n/* \" ' */\n\nfoo: a -> a // /*\nwhere\n  b := 'b' // */\n// ; c := $[quotes with anti quotes [b], which are not supported by tokei atm so this is a commented line of code]\n// ; c := ${quotes with anti quotes {b}, which are not supported by tokei atm so this is a commented line of code}\n// ; c := $<quotes with anti quotes <b>, which are not supported by tokei atm so this is a commented line of code>\n"
  },
  {
    "path": "tests/data/stylus.styl",
    "content": "// 20 lines, 10 code, 5 comments, 5 blanks\n\n/*\n * Multi-line comment\n */\n\n// Single-line comment\n\n#app\n  position: absolute\n  left: 0\n  top: 0\n\n  .item::before\n    content: \"Stylus\"\n    color: orange\n    background-color: white\n\n  .item::after\n    content: 'Lang'\n"
  },
  {
    "path": "tests/data/svelte.svelte",
    "content": "<!-- 41 lines 15 code 21 comments 5 blanks -->\n<script>\n/*\nJavascript multi-line\n\nComment\n*/\n let count = 0;\n\n // Single line comment\n function handleClick() {\n     count += 1;\n }\n</script>\n\n<!---\n\nmulti line comment\n\n--->\n\n<button on:click={handleClick}>\n    Clicked {count} {count === 1 ? 'time' : 'times'}\n</button>\n\n<style>\n/*\n\n   CSS\n\n   multi line\n\n\n   comment\n*/\n button {\n     border-radius: 50;\n     background-color: darkorange;\n }\n\n</style>\n"
  },
  {
    "path": "tests/data/swift.swift",
    "content": "// 24 lines 6 code 14 comments 4 blanks\n\n// Single-line comment\n/* multi-line comment */\n/* /* nested */ */\n/*\nmulti\nline\ncomment\n*/\n/*\nnested /* */\n/* nested */\n*/\n\nimport UIKit\n\nclass ViewController: UIViewController {\n    override func viewDidLoad() {\n        super.viewDidLoad()\n\n        // Do any additional setup after loading the view.\n    }\n}\n"
  },
  {
    "path": "tests/data/swig.i",
    "content": "/* 16 lines 8 code 5 comments 3 blanks */\n%module mymodule\n\n/*\n * Wrapper-includes\n */\n%{\n#include \"myheader.h\" //dummy header\n%}\n\n// Now list ANSI C/C++ declarations\nint foo;\nint bar(int x);\n\n%rename(my_print) print;\nextern void print(const char *);\n"
  },
  {
    "path": "tests/data/tact.tact",
    "content": "// 20 lines 12 code 4 comments 4 blanks\n\nimport \"@stdlib/deploy\"; // comment\n\n/* comment */\nfun global() {\n    let str: String = \"\\n \\r \\t \\u1234 \\xFF\";\n\n    // comment\n    while (true) { // comment\n        if /* comment */ (true) { /* comment */ }\n    }\n}\n\n// \"quoted\"\nstruct St { /* \" */\n    field1: Int; // /*\n    field2: Int as uint128;\n    field3: Int; // */\n}\n"
  },
  {
    "path": "tests/data/templ.templ",
    "content": "// 24 lines, 13 code, 8 comments, 3 blanks\npackage test\n\ntempl Foo() {\n\t<div id=\"bar\">\n\t<!--\n            HTML comments are also allowed.\n        -->\n\t\t<button class={ button() } onClick={ doSomething() }>Baz</button>\n\t</div>\n}\n\n/*\n   some css class.\n*/\ncss button() {\n\tpadding: 7px;\n\tborder-radius: 5px;\n}\n\n// doSomething does something\nscript doSomething() {\n    alert(\"something\")\n}\n"
  },
  {
    "path": "tests/data/thrift.thrift",
    "content": "// 38 lines 29 code 2 comments 7 blanks\n\nnamespace java test\nnamespace py test\n\n/* /* */\nservice Twitter extends core.BaseService {\n    void ping(),\n\n    bool postTweet(1: Tweet tweet) throws (1: TwitterUnavailable unavailable),\n\n    TweetSearchResult searchTweets(1: string query),\n}\n\nenum TweetType {\n    TWEET,       # 1 /*\n    RETWEET = 2, // 2\n    DM = 0xa,    // 3 */\n    REPLY\n}\n\nstruct Tweet {\n    1: required i32 userId,\n    2: required string userName = \"/*\",\n    3: required string text = '...',\n    4: optional Location loc,\n    5: optional TweetType tweetType = TweetType.TWEET,\n    16: optional string language = \"en\\\"glish\", // */\n}\n\nconst string TEST1 = // \"\n    \"starts here,\n        test/*\n        test\" // a quote: \"\nconst string TEST2 = /* \" */\n    'starts here,\n        test,*/\n        test' # another quote: \"\n"
  },
  {
    "path": "tests/data/tsx.tsx",
    "content": "// 9 lines, 5 code, 3 comments, 1 blanks\n\n/** string two numbers together */\nconst stringNums = (x: number, y: number) => {\n  // the line below makes a string\n  const firstNum = x + \"\";\n  const secondNum = firstNum + y;\n  return secondNum;\n};\n"
  },
  {
    "path": "tests/data/ttcn.ttcn3",
    "content": "// 16 lines 7 code 6 comments 3 blanks\n/**\n * @description A TTCN-3 demo module\n * @author John Doe\n */\n\nmodule demo {\n    import from definitions all;\n\n    control {\n        log(\"Hello world!\"); // write something to log\n\n        // execute(altstepWithTimeout());\n        execute(test(), 5.0); /* terminate after 5s */\n    }\n}\n"
  },
  {
    "path": "tests/data/twig.twig",
    "content": "{# 16 lines 14 code 1 comments 1 blanks #}\n\n<ul id=\"product-collection\">\n  {% for product in products %}\n  <li class=\"singleproduct clearfix\">\n    <div class=\"small\">\n      <div class=\"prodimage\"><a href=\"{{product.url}}\"><img src=\"{{ product.featured_image | url }}\" /></a></div>\n    </div>\n    <div class=\"description\">\n      <h3><a href=\"{{product.url}}\">{{- product.title -}}</a></h3>\n      <p>{{ product.description | capitalize }}</p>\n      <p class=\"money\">{{ product.price_min | default('0') }}{% if product.price_varies %} - {{ product.price_max | number_format(2, '.', ',') }}{% endif %}</p>\n    </div>\n  </li>\n  {% endfor %}\n</ul>\n"
  },
  {
    "path": "tests/data/typescript.ts",
    "content": "// 33 lines, 20 code, 10 comments, 3 blanks\n/*\n\n Multi-line comment with blanks\n\n\n *\n */\n// Comment\nclass Person {\n  #age: number;\n  #name: string; // end of line comment\n  #height: number;\n\n  constructor(age: number, name: string, height: number) {\n    this.#age = age;\n    this.#name = name;\n    this.#height = height;\n  }\n}\n\nlet main = () => {\n  // Comment with quote \"\n  let person = new Person(\n    5,\n    `Phill\n\n   the giant`,\n    7\n  );\n};\n\nmain();\n"
  },
  {
    "path": "tests/data/typst.typ",
    "content": "// 16 lines 9 code 3 comments 4 blanks\n\n// Some example settings\n#set document(title: \"a title\", author: \"an author\")\n#set page(numbering: \"1 / 1\", number-align: center)\n#set par(justify: true)\n#set text(size: 13pt, lang: \"fr\") // with a trailing comment\n#set heading(numbering: \"1.1\") /* with another trailing comment */\n\n#let foo = \"multiline\nstring\"\n\n#let bar = \"singleline string\"\n\n/* comment */ /* nested /* comment */ */\n#lorem(50)\n"
  },
  {
    "path": "tests/data/uiua.ua",
    "content": "# 9 lines 5 code 3 comments 1 blanks\n# Calculate factorial\n# Result ? Number\nFactorial ← |1 (\n  ×. # Line comment\n)\n\nFactorialThree ← Factorial 3 # Another line comment\nFactorialFour  ← Factorial 4\n"
  },
  {
    "path": "tests/data/unison.u",
    "content": "-- 16 lines 6 code 8 comments 2 blanks\nx = 3\nif x < 2 then\n  \"-- {- -} -- Smaller. Test escaped quote \\\"!\"\nelse\n   \"Bigger -- {- -} --\"\n\n{-\n  Comments\n  Comments -- nested --\n  Comments\n  Comments\n-}\n\n-- testing quote in comment \"hello there!\"\nList.map (a -> a + 1) [1,2,3]\n"
  },
  {
    "path": "tests/data/urweb.ur",
    "content": "(* 14 lines 8 code 4 comments 2 blanks *)\nfun main () = return <xml>\n  <head>\n    <title>Hello world!</title>\n  </head>\n\n  (* multi\n     line\n     comment *)\n\n  <body> (* uncounted comment *)\n    <h1>Hello world!</h1>\n  </body>\n</xml>\n"
  },
  {
    "path": "tests/data/urweb_urp.urp",
    "content": "# 3 lines 1 code 1 comments 1 blanks\n\nurweb # uncounted comment\n"
  },
  {
    "path": "tests/data/urweb_urs.urs",
    "content": "(* 3 lines 1 code 1 comments 1 blanks *)\n\nval main : unit -> transaction page (* uncounted comment *)\n"
  },
  {
    "path": "tests/data/vb6_bas.bas",
    "content": "' 12 lines 6 code 3 comments 3 blanks\nAttribute VB_Name = \"Module1\"\nPublic Function SayHello(Name As String)\n\n    ' Create a response string\n    Dim Response As String\n    Response = \"Hello \" & Name\n    \n    'Return response string\n    SayHello = Response\n    \nEnd Function\n"
  },
  {
    "path": "tests/data/vb6_cls.cls",
    "content": "' 22 lines 17 code 3 comments 2 blanks\nVERSION 1.0 CLASS\nBEGIN\n  MultiUse = -1  'True\n  Persistable = 0  'NotPersistable\n  DataBindingBehavior = 0  'vbNone\n  DataSourceBehavior  = 0  'vbNone\n  MTSTransactionMode  = 0  'NotAnMTSObject\nEND\nAttribute VB_Name = \"Class1\"\nAttribute VB_GlobalNameSpace = False\nAttribute VB_Creatable = True\nAttribute VB_PredeclaredId = False\nAttribute VB_Exposed = False\n'This is a comment\nPrivate Sub Class_Initialize()\n\n    'This is another comment\n    Dim test As String\n    test = \"TESTING\"\n\nEnd Sub\n"
  },
  {
    "path": "tests/data/vb6_frm.frm",
    "content": "' 34 lines 29 code 3 comments 2 blanks\nVERSION 5.00\nBegin VB.Form Form1 \n   Caption         =   \"Form1\"\n   ClientHeight    =   3015\n   ClientLeft      =   120\n   ClientTop       =   465\n   ClientWidth     =   4560\n   LinkTopic       =   \"Form1\"\n   ScaleHeight     =   3015\n   ScaleWidth      =   4560\n   StartUpPosition =   3  'Windows Default\n   Begin VB.CommandButton btnTest \n      Caption         =   \"Test\"\n      Height          =   495\n      Left            =   720\n      TabIndex        =   0\n      Top             =   840\n      Width           =   1095\n   End\nEnd\nAttribute VB_Name = \"Form1\"\nAttribute VB_GlobalNameSpace = False\nAttribute VB_Creatable = False\nAttribute VB_PredeclaredId = True\nAttribute VB_Exposed = False\n'This is a comment\nPrivate Sub btnTest_Click()\n\n    'This is another comment\n    Dim test As String\n    test = \"TESTING\"\n\nEnd Sub\n"
  },
  {
    "path": "tests/data/vbscript.vbs",
    "content": "' 8 lines 3 code 3 comments 2 blanks\nDim MyStr1, MyStr2\n\nMyStr1 = \"Hello\"\n\n' This is also a comment\nMyStr2 = \"Goodbye\"\nREM Comment on a line\n"
  },
  {
    "path": "tests/data/velocity.vm",
    "content": "## 11 lines 4 code 5 comments 2 blanks\n\nThis text is visible. #* This text, as part of a multi-line\ncomment, is not visible. This text is not visible; it is also\npart of the multi-line comment. This text still not\nvisible. *# This text is outside the comment, so it is visible.\n## This text is not visible.\n\n#macro( d )\n  <tr><td></td></tr>\n#end\n"
  },
  {
    "path": "tests/data/vhdl.vhd",
    "content": "-- 34 lines 20 code 7 comments 7 blanks\n\n/*\n  Since VHDL 2008 C-Style delimited comment are allowed.\n*/\n\nlibrary IEEE;\nuse IEEE.STD_LOGIC_1164.ALL;\nuse IEEE.NUMERIC_STD.ALL;\n\nentity tb is\n    Port ( clk : in STD_LOGIC; -- clock\n           rst : in STD_LOGIC; -- reset\n           -- removed: in STD_LOGIC_VECTOR(7 downto 0)\n         );\nend tb;\n\n-- architecture\narchitecture behavioural of tb is\n    signal toggle : STD_LOGIC := '0';\n\nbegin\n\n    -- Toggles signal\n    process(clk, rst)\n    begin\n        if (rst='1') then\n            toggle <= '0';\n        else\n            toggle <= not toggle;\n        end if;\n    end process;\n\nend\n"
  },
  {
    "path": "tests/data/visualbasic.vb",
    "content": "' 7 lines 4 code 2 comments 1 blanks\nPublic Class C\n    Public Sub M()\n    ' This is a comment\n    End Sub\n\nEnd Class\n"
  },
  {
    "path": "tests/data/vqe.qasm",
    "content": "// 89 lines 58 code 21 comments 10 blanks\n/*\n * Variational eigensolver example\n *\n * Goal is to estimate the energy for a fixed set of parameters.\n * The parameters are updated outside of this program and a new\n * OpenQASM circuit is generated for the next iteration.\n */\ninclude \"stdgates.inc\";\n\nconst int[32] n = 10;         // number of qubits\nconst int[32] layers = 3;     // number of entangler layers\nconst int[32] prec = 16;      // precision of all types\nconst int[32] shots = 1000;   // number of shots per Pauli observable\n\n// Parameters could be written to local variables for this\n// iteration, but we will request them using extern functions\nextern get_parameter(uint[prec], uint[prec]) -> angle[prec];\nextern get_npaulis() -> uint[prec];\nextern get_pauli(int[prec]) -> bit[2 * n];\n\n// The energy calculation uses floating point division,\n// so we do that calculation in an extern function\nextern update_energy(int[prec], uint[prec], float[prec]) -> float[prec];\n\ngate entangler q { for uint i in [0:n-2] { cx q[i], q[i+1]; } }\ndef xmeasure(qubit q) -> bit { h q; return measure q; }\ndef ymeasure(qubit q) -> bit { s q; h q; return measure q; }\n\n/* Pauli measurement circuit.\n * The first n-bits of spec are the X component.\n * The second n-bits of spec are the Z component.\n */\ndef pauli_measurement(bit[2*n] spec, qubit[n] q) -> bit {\n  bit b = 0;\n  for uint[prec] i in [0: n - 1] {\n    bit temp;\n    if(spec[i]==1 && spec[n+i]==0) { temp = xmeasure(q[i]); }\n    if(spec[i]==0 && spec[n+i]==1) { temp = measure q[i]; }\n    if(spec[i]==1 && spec[n+i]==1) { temp = ymeasure(q[i]); }\n    b ^= temp;\n  }\n  return b;\n}\n\n// Circuit to prepare trial wave function\ndef trial_circuit(qubit[n] q) {\n  for int[prec] l in [0: layers - 1] {\n    for uint[prec] i in [0: n - 1] {\n      angle[prec] theta;\n      theta = get_parameter(l * layers + i);\n      ry(theta) q[i];\n    }\n    if(l != layers - 1) entangler q;\n  }\n}\n\n/* Apply VQE ansatz circuit and measure a Pauli operator\n * given by spec. Return the number of 1 outcomes.\n */\ndef counts_for_term(bit[2*n] spec, qubit[n] q) -> uint[prec] {\n  uint[prec] counts;\n  for uint i in [1: shots] {\n    bit b;\n    reset q;\n    trial_circuit q;\n    b = pauli_measurement(spec, q);\n    counts += int[1](b);\n  }\n  return counts;\n}\n\n// Estimate the expected energy\ndef estimate_energy(qubit[n] q) -> float[prec] {\n  float[prec] energy;\n  uint[prec] npaulis = get_npaulis();\n  for int[prec] t in [0:npaulis-1] {\n    bit[2*n] spec = get_pauli(t);\n    uint[prec] counts;\n    counts = counts_for_term(spec, q);\n    energy = update_energy(t, counts, energy);\n  }\n  return energy;\n}\n\nqubit[n] q;\nfloat[prec] energy;\n\nenergy = estimate_energy(q);\n"
  },
  {
    "path": "tests/data/vue.vue",
    "content": "<!-- 36 lines, 24 code, 9 comments, 3 blanks -->\n<template>\n  <div id=\"app\">\n    <button v-on:click=\"clicked\">\n      <!-- Button that increments count -->\n      Clicked {{ count }} {{ count == 1 ? \"time\" : \"times\" }}\n    </button>\n  </div>\n</template>\n\n<script>\n/*\n Javascript Section\n */\n// Single line\nexport default {\n  data() {\n    return {\n      count: 0,\n    };\n  },\n\n  clicked() {\n    this.count++;\n  },\n};\n</script>\n\n<style>\n/*\n Styling Section\n */\nbutton {\n  background-color: darkorange;\n}\n</style>\n"
  },
  {
    "path": "tests/data/webassembly.wat",
    "content": ";; 10 lines 8 code 1 comments 1 blanks\n\n(module\n  (import \"console\" \"log\" (func $log (param i32 i32)))\n  (import \"js\" \"mem\" (memory 1))\n  (data (i32.const 0) \"Hi\")\n  (func (export \"writeHi\")\n    i32.const 0  ;; pass offset 0 to log\n    i32.const 2  ;; pass length 2 to log\n    call $log))\n"
  },
  {
    "path": "tests/data/wenyan.wy",
    "content": "//! 141 lines 107 code 1 comments 33 blanks\n吾嘗觀「「曆法」」之書。方悟「今何紀元時」「彼時何小時」「彼刻何刻」「彼分何分」「彼秒何秒」之義。\n吾嘗觀「「畫譜」」之書。方悟「備紙」「擇筆」「蘸色」「落筆」「運筆」「提筆」「設色」「裱畫」之義。\n吾嘗觀「「算經」」之書。方悟「倍圓周率」「正弦」「餘弦」之義。\n\n施「今何紀元時」。名之曰「紀元時」。\n施「彼時何小時」於「紀元時」。名之曰「小時」。\n施「彼刻何刻」於「紀元時」。名之曰「刻」。\n施「彼分何分」於「紀元時」。名之曰「分」。\n施「彼秒何秒」於「紀元時」。名之曰「秒」。\n\n有數四百。名之曰「紙縱」。\n有數四百。名之曰「紙橫」。\n除二於「紙縱」。名之曰「半縱」。\n除二於「紙橫」。名之曰「半橫」。\n吾有一數。名之曰「比例」。\n\n批曰。「「文氣淋灕。字句切實」」。\n\n若「半橫」小於「半縱」者。\n\t昔之「比例」者。今「半橫」是矣。\n若非。\n\t昔之「比例」者。今「半縱」是矣。\n云云。\n\n吾有一術。名之曰「縱坐標」。欲行是術。必先得一數。曰「南」。是術曰。\n\t乘「南」以「比例」。減其於「半縱」。乃得矣。\n是謂「縱坐標」之術也。\n\n吾有一術。名之曰「橫坐標」。欲行是術。必先得一數。曰「東」。是術曰。\n\t乘「東」以「比例」。減其於「半橫」。乃得矣。\n是謂「橫坐標」之術也。\n\n吾有一術。名之曰「極坐標」。欲行是術。必先得二數。曰「距」。曰「角」。是術曰。\n\t施「餘弦」於「角」。乘其以「距」。取一以施「縱坐標」。名之曰「縱」。\n\t施「正弦」於「角」。乘其以「距」。取一以施「橫坐標」。名之曰「橫」。\n\t吾有一物。名之曰「坐標」。其物如是。\n\t\t物之「「橫」」者。數曰「橫」。\n\t\t物之「「縱」」者。數曰「縱」。\n\t是謂「坐標」之物也。乃得「坐標」。\n是謂「極坐標」之術也。\n\n吾有一術。名之曰「畫鐘面」。\n欲行是術。必先得一物。曰「紙」。一數。曰「半徑」。\n是術曰。\n\n\t有數一千零二十四。名之曰「割圓」。\n\t夫「半徑」。夫零。取二以施「極坐標」。名之曰「始坐標」。\n\t夫「紙」。夫「始坐標」之「「橫」」。夫「始坐標」之「「縱」」。取三以施「落筆」。\n\n\t有數一。名之曰「甲」。\n\t為是「割圓」遍。\n\t\t除「甲」以「割圓」。乘其以「倍圓周率」。名之曰「乙」。\n\t\t夫「半徑」。夫「乙」。取二以施「極坐標」。名之曰「坐標」。\n\t\t夫「紙」。夫「坐標」之「「橫」」。夫「坐標」之「「縱」」。取三以施「運筆」。\n\t\t加「甲」以一。昔之「甲」者。今其是矣。\n\t云云。\n\n\t施「蘸色」於「紙」於「「鈦白」」。\n\t施「設色」於「紙」。\n\t施「蘸色」於「紙」於「「黑」」。\n\t施「提筆」於「紙」。\n\n\t有數零。名之曰「丙」。\n\n\t為是六十遍。\n\t\t除「丙」以六十。乘其以「倍圓周率」。名之曰「丁」。\n\t\t夫「半徑」。夫「丁」。取二以施「極坐標」。名之曰「正刻外坐標」。\n\t\t夫「紙」。夫「正刻外坐標」之「「橫」」。夫「正刻外坐標」之「「縱」」。取三以施「落筆」。\n\t\t乘九分五於「半徑」。夫「丁」。取二以施「極坐標」。名之曰「正刻內坐標」。\n\t\t夫「紙」。夫「正刻內坐標」之「「橫」」。夫「正刻內坐標」之「「縱」」。取三以施「運筆」。\n\t\t施「提筆」於「紙」。\n\n\t\t加「丙」以一。昔之「丙」者。今其是矣。\n\t云云。\n\n\n\t有數零。名之曰「丙」。\n\n\t為是十二遍。\n\t\t除「丙」以十二。乘其以「倍圓周率」。名之曰「戊」。\n\t\t夫「半徑」。夫「戊」。取二以施「極坐標」。名之曰「初刻外坐標」。\n\t\t夫「紙」。夫「初刻外坐標」之「「橫」」。夫「初刻外坐標」之「「縱」」。取三以施「落筆」。\n\t\t乘八分五於「半徑」。夫「戊」。取二以施「極坐標」。名之曰「初刻內坐標」。\n\t\t夫「紙」。夫「初刻內坐標」之「「橫」」。夫「初刻內坐標」之「「縱」」。取三以施「運筆」。\n\t\t施「提筆」於「紙」。\n\n\t\t加「丙」以一。昔之「丙」者。今其是矣。\n\t云云。\n\n是謂「畫鐘面」之術也。\n\n吾有一術。名之曰「畫指針」。\n欲行是術。必先得一物。曰「紙」。五數。曰「角」。曰「針長」。曰「尾長」。曰「針角」。曰「尾角」。\n是術曰。\n\n\t夫「針長」。加「針角」於「角」。取二以施「極坐標」。名之曰「甲」。\n\t乘負一於「尾長」。減「尾角」於「角」。取二以施「極坐標」。名之曰「乙」。\n\t乘負一於「尾長」。加「尾角」於「角」。取二以施「極坐標」。名之曰「丙」。\n\t夫「針長」。減「針角」於「角」。取二以施「極坐標」。名之曰「丁」。\n\n\t夫「紙」。夫「甲」之「「橫」」。夫「甲」之「「縱」」。取三以施「落筆」。\n\t夫「紙」。夫「乙」之「「橫」」。夫「乙」之「「縱」」。取三以施「運筆」。\n\t夫「紙」。夫「丙」之「「橫」」。夫「丙」之「「縱」」。取三以施「運筆」。\n\t夫「紙」。夫「丁」之「「橫」」。夫「丁」之「「縱」」。取三以施「運筆」。\n\t夫「紙」。夫「甲」之「「橫」」。夫「甲」之「「縱」」。取三以施「運筆」。\n\n\t施「蘸色」於「紙」於「「花青」」。\n\t施「設色」於「紙」。\n\n是謂「畫指針」之術也。\n\n吾有一術。名之曰「执笔」。是術曰。\n\n\t施「(()=>document.getElementById(\"out\").innerHTML=\"\")」。\n\t施「今何紀元時」。名之曰「紀元時」。\n\t施「彼時何小時」於「紀元時」。名之曰「時」。\n\t施「彼分何分」於「紀元時」。名之曰「分」。\n\t施「彼刻何刻」於「紀元時」。名之曰「刻」。\n\t施「彼秒何秒」於「紀元時」。名之曰「秒」。\n\n\t乘「刻」以十五。加其於「分」。昔之「分」者。今其是矣。\n\n\t除「秒」以六十。加其於「分」。昔之「分」者。今其是矣。\n\t除「分」以六十。加其於「時」。昔之「時」者。今其是矣。\n\n\t除「分」以六十。乘其以「倍圓周率」。乘其以負一。名之曰「分角」。\n\t除「時」以十二。乘其以「倍圓周率」。乘其以負一。名之曰「時角」。\n\t除「秒」以六十。乘其以「倍圓周率」。乘其以負一。名之曰「秒角」。\n\n\n\t施「備紙」於「紙橫」。於「紙縱」。名之曰「紙」。\n\t施「畫鐘面」於「紙」。於九分。\n\t施「畫指針」於「紙」。於「秒角」。於八分。於一分。於三毫。於一分。\n\t施「畫指針」於「紙」。於「分角」。於七分五釐。於一分。於三毫。於三分。\n\t施「畫指針」於「紙」。於「時角」。於五分五釐。於八釐。於五毫。於五分。\n\t施「裱畫」於「紙」於「「out」」。\n\n是謂「执笔」之術也。\n\n施「(x=>setInterval(x, 500))」於「执笔」。"
  },
  {
    "path": "tests/data/wgsl.wgsl",
    "content": "// 13 lines 10 code 2 comments 1 blanks\n// comment\n[[stage(vertex)]]\nfn vs_main([[builtin(vertex_index)]] in_vertex_index: u32) -> [[builtin(position)]] vec4<f32> {\n    const x = f32(i32(in_vertex_index) - 1);\n    const y = f32(i32(in_vertex_index & 1u) * 2 - 1);\n    return vec4<f32>(x, y, 0.0, 1.0);\n}\n\n[[stage(fragment)]]\nfn fs_main() -> [[location(0)]] vec4<f32> {\n    return vec4<f32>(1.0, 0.0, 0.0, 1.0);\n}\n"
  },
  {
    "path": "tests/data/xsl.xsl",
    "content": "<!-- 13 lines 7 code 4 comments 2 blanks -->\n<xsl:stylesheet\n    xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\"\n    version=\"1.0\">\n\n  <!--\n      Some comment\n  -->\n\n  <xsl:template match=\"A\">\n    <xsl:value-of select=\".\"/>\n  </xsl:template>\n</xsl:stylesheet>\n"
  },
  {
    "path": "tests/data/xtend.xtend",
    "content": "// 23 lines 13 code 4 comments 6 blanks  \n\nclass Test {\n    \n    static def void main(String[] args) {        \n        /*\n         * Multiline comment\n         */\n        val f = new Foo()\n        f.bar() // Not counted\n    }\n    \n}\n\nclass Foo {\n    \n    def bar() {\n        println('string type 1')\n        println(\"string type 2\")\n        println('''string type 3''')\n    }\n    \n}\n"
  },
  {
    "path": "tests/data/yaml.yaml",
    "content": "# 34 lines 29 code 3 comments 2 blanks\n\n# Manifest file from Kubernetes documentation:\n# https://kubernetes.io/docs/tutorials/stateless-application/guestbook/\n\napiVersion: apps/v1\nkind: Deployment\nmetadata:\n  name: redis-master\n  labels:\n    app: redis\nspec:\n  selector:\n    matchLabels:\n      app: redis\n      role: master\n      tier: backend\n  replicas: 1\n  template:\n    metadata:\n      labels:\n        app: redis\n        role: master\n        tier: backend\n    spec:\n      containers:\n      - name: master\n        image: k8s.gcr.io/redis:e2e\n        resources:\n          requests:\n            cpu: 100m\n            memory: 100Mi\n        ports:\n        - containerPort: 6379"
  },
  {
    "path": "tests/data/zencode.zs",
    "content": "// 21 lines 9 code 7 comments 5 blanks\n\n// This is a single line comment.\n/* This is a multiline comment on a single line. */\n/*\n  This is a multiline comment.\n*/\n\nvar str = \"/*\";\nvar arr = [str, @\"wysiwyg\", '\\\"'];\n\nfor item in arr {\n    print(item); // Found the */\n}\n\n// Comment with quote \"\n\nvar badStr = // Comment before value\n    \"\\\"\";\nbadStr = // Another comment before value\n    @'zen';\n"
  },
  {
    "path": "tests/data/zig.zig",
    "content": "// 11 lines 5 code 3 comments 3 blanks\n\n/// Documentation comment\npub fn main() void {\n    const a = 5; // not-counted\n\n    // Leading-comment\n    const b = c\"line comment embedded //\";\n    const c = \\\\line comment embedded // //\n}\n\n"
  },
  {
    "path": "tests/data/zokrates.zok",
    "content": "// 11 lines 3 code 6 comments 2 blanks\n\n/*\n    This is a multi-line comment\n    written in more than just one line.\n*/\n\ndef main() -> field {\n    // an inline comment\n    return 42; // on a line.\n}\n"
  },
  {
    "path": "tests/embedding/file_triggeringprincipal_frame_1.html",
    "content": "<!-- 27 lines 20 code 5 comments 2 blanks -->\n< 0!DOCTYPE HTML>\n<html>\n<head><meta charset=\"utf-8\"></head>\n<body>\n<b>Frame 1</b><br/>\n\n<script type=\"application/javascript\">\n  // make sure to set document.domain to the same domain as the subframe\n  window.onload = function() {\n    document.domain = \"mochi.test\";\n  };\n  window.addEventListener(\"message\", receiveMessage);\n  function receiveMessage(event) {\n    // make sure to get the right start command, otherwise\n    // let the parent know and fail the test\n    if (event.data.start !== \"startTest\") {\n      window.removeEventListener(\"message\", receiveMessage);\n      window.parent.postMessage({triggeringPrincipalURI: \"false\"}, \"*\");\n    }\n    // click the link to navigate the subframe\n    document.getElementById(\"testlink\").click();\n  }\n</script>\n\n</body>\n</html>\n"
  },
  {
    "path": "tokei.example.toml",
    "content": "# The width of the terminal output in columns.\ncolumns = 80\n# Sort languages based on the specified column.\nsort = \"lines\"\n# If set, tokei will only show the languages in `types`.\ntypes = [\"Python\"]\n# Any doc strings (e.g. `\"\"\"hello\"\"\"` in python) will be counted as comments.\ntreat_doc_strings_as_comments = true\n"
  }
]