[
  {
    "path": ".config/insta.yaml",
    "content": "behavior:\n  output: minimal\n\ntest:\n  runner: nextest\n"
  },
  {
    "path": ".config/nextest.toml",
    "content": "experimental = [\"setup-scripts\", \"wrapper-scripts\"]\n\n[scripts.setup.stop-tmux]\ncommand = \"sh -c 'tmux kill-session -t skim_e2e || true'\"\n[scripts.setup.start-tmux]\ncommand = \"tmux new-session -d -s skim_e2e -n skim_e2e\"\n\n# Valgrind wrapper for memory leak detection\n[scripts.wrapper.valgrind]\ncommand = [\n    \"valgrind\",\n    \"--leak-check=full\",\n    \"--show-leak-kinds=all\",\n    \"--track-origins=yes\",\n    \"--error-exitcode=1\",\n    \"--suppressions=.config/valgrind.supp\"\n]\n\n[profile.default]\nfail-fast = false\nretries = 9\n[[profile.default.scripts]]\nplatform = \"cfg(unix)\"\nsetup = [\"stop-tmux\", \"start-tmux\"]\n[profile.default.junit]\npath = \"junit.xml\"\n\n# Valgrind profile for memory leak detection\n# Usage: cargo nextest run --profile valgrind --features test-utils\n#\n# Note: Valgrind can detect memory leaks but does NOT detect dangling threads.\n# For thread leak detection, use ThreadSanitizer instead (see below).\n[profile.valgrind]\nfail-fast = false\nretries = 2\ntest-threads = 1  # Run tests serially to avoid interleaved valgrind output\n[[profile.valgrind.scripts]]\nplatform = \"cfg(unix)\"\nsetup = [\"stop-tmux\", \"start-tmux\"]\nrun-wrapper = \"valgrind\"\n\n# ThreadSanitizer profile for detecting data races and thread issues\n# Usage:\n#   1. First build with sanitizer (rebuilds stdlib and all deps):\n#      RUSTFLAGS=\"-Zsanitizer=thread\" cargo +nightly build --tests --features test-utils -Zbuild-std --target x86_64-unknown-linux-gnu\n#   2. Then run tests:\n#      TSAN_OPTIONS=\"detect_deadlocks=1\" cargo +nightly nextest run --profile tsan --features test-utils --target x86_64-unknown-linux-gnu\n#\n# Note: ThreadSanitizer can detect:\n#   - Data races (concurrent unsynchronized access to memory)\n#   - Deadlocks (with TSAN_OPTIONS=detect_deadlocks=1)\n#   - Thread leaks (threads not joined before program exit)\n#\n# Requirements:\n#   - Rust nightly (for -Zsanitizer and -Zbuild-std flags)\n#   - The -Zbuild-std flag rebuilds the standard library with ThreadSanitizer\n#     instrumentation to avoid ABI mismatch errors\n#\n# Important: This takes a long time on first build as it recompiles everything\n# including the standard library with ThreadSanitizer instrumentation.\n#\n# Environment variables:\n#   TSAN_OPTIONS=\"detect_deadlocks=1 second_deadlock_stack=1\"\n[profile.tsan]\nfail-fast = false\nretries = 3\ntest-threads = 1  # TSan requires running tests serially\n[[profile.tsan.scripts]]\nplatform = \"cfg(unix)\"\nsetup = [\"stop-tmux\", \"start-tmux\"]\n"
  },
  {
    "path": ".config/valgrind.supp",
    "content": "# Valgrind suppressions for skim tests\n# This file suppresses known false positives from Rust stdlib and system libraries\n\n# Rust std allocations that are intentionally not freed at program exit\n{\n   rust_std_exit_cleanup\n   Memcheck:Leak\n   ...\n   fun:*std*\n}\n\n# Thread-local storage cleanup\n{\n   thread_local_cleanup\n   Memcheck:Leak\n   ...\n   fun:pthread_create*\n}\n\n# Tokio runtime allocations\n{\n   tokio_runtime\n   Memcheck:Leak\n   ...\n   fun:*tokio*runtime*\n}\n\n# Crossterm/terminal allocations\n{\n   crossterm_terminal\n   Memcheck:Leak\n   ...\n   fun:*crossterm*\n}\n\n# Libc thread initialization\n{\n   libc_thread_init\n   Memcheck:Leak\n   match-leak-kinds: possible\n   ...\n   fun:calloc\n   fun:allocate_dtv\n}\n\n# DL allocations\n{\n   dl_init\n   Memcheck:Leak\n   match-leak-kinds: possible\n   ...\n   fun:*dl_*\n}\n"
  },
  {
    "path": ".dockerignore",
    "content": "target/\n.github/\nbin/\nplugin/\nman/\nshell/\n*.md\ninstall"
  },
  {
    "path": ".envrc",
    "content": "use flake\n"
  },
  {
    "path": ".githooks/pre-commit",
    "content": "set -xeuo pipefail\n\ncargo fmt --check --all\ncargo clippy --all-targets --features test-utils -- -Dwarnings\ncargo check --no-default-features\n"
  },
  {
    "path": ".github/CONTRIBUTING.md",
    "content": "# Contributor Guide\n\n## Running tests\n\nAll tests can be run by using [cargo-nextest](https://nexte.st/), which can be installed using `cargo install cargo-nextest` of following the instructions on the website.\nYou will need `tmux` to run some integration tests.\n\nYou can then run `cargo nextest run --release --features test-utils`, which should automatically build a release binary, run the unit tests and the integration tests.\n\n\nMost integration tests use [cargo insta](https://insta.rs). If you need to add some tests or re-review them, you will need to install it, and run tests with `cargo insta test --features test-utils --tests --review`, which will let you review snapshots.\n\nNote: you can run the tests without `--release`, but expect more flaky tests since the timings will be looser. I would advise testing manually any debug test failure if you have doubts. However, the tests won't run without the `test-utils` feature, used to create test backends.\n\nNote2: A dockerfile is available if you want to run the tests inside docker. There is little to no cache, so the test will need to rebuild most of the application after each change.\nTo use it, build the image with `docker build -f test.dockerfile . -t skim-test` then run it using `docker run --rm -it skim-test`.\n\n## Submitting code\n\nTo avoid using up CI minutes uselessly, make sure that :\n- You run `cargo clippy` and `cargo fmt` before pushing any code to an open PR.\n- Your PR's title respects [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/).\n\nNot respecting these guidelines could end up consuming all our minutes and preventing us from testing and releasing any new code until the end of the month.\n\nNote: a git pre-commit hook is available in .githooks/pre-commit which will make the clippy & fmt checks. To use it, run `git config core.hooksPath \".githooks\"`.\n\n## Vibe Coding guidelines\n\nAny code generated partially or completely using LLMs will be treated the same way as if you wrote it yourself.\n\nThis means that you are expected to understand if fully and are responsible for it.\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "content": "---\nname: Bug report\nabout: Report a bug encountered using `skim`\ntitle: \"[BUG] xxx\"\nlabels: bug\nassignees: LoricAndre\n\n---\n\n**Describe the bug**\nA clear and concise description of what the bug is.\n\n**To Reproduce**\nSteps to reproduce the behavior:\n1. Go to '...'\n2. Click on '....'\n3. Scroll down to '....'\n4. See error\n\n**Expected behavior**\nA clear and concise description of what you expected to happen.\n\n**Screenshots**\nIf applicable, add screenshots to help explain your problem.\n\n**Environment (please complete the following information):**\n - OS (`uname -a`): \n - `skim` version (`sk --version`): \n- Shell and version: \n - Variables (`env | grep '^SKIM'`): \n\n**Additional context**\nAdd any other context about the problem here.\n"
  },
  {
    "path": ".github/dependabot.yml",
    "content": "version: 2\nupdates:\n  - package-ecosystem: \"cargo\"\n    directory: \"/\"\n    schedule:\n      interval: \"weekly\"\n    commit-message:\n      prefix: chore(deps)\n      prefix-development: chore(dev-deps)\n"
  },
  {
    "path": ".github/pr-title-checker-config.json",
    "content": "{\n  \"LABEL\": {\n    \"name\": \"invalid-title\",\n    \"color\": \"B60205\"\n  },\n  \"CHECKS\": {\n    \"prefixes\": [\n      \"feat: \",\n      \"feature: \",\n      \"fix: \",\n      \"bugfix: \",\n      \"perf: \",\n      \"refactor: \",\n      \"test: \",\n      \"tests: \",\n      \"build: \",\n      \"ci: \",\n      \"doc: \",\n      \"docs: \",\n      \"style: \",\n      \"chore: \",\n      \"other: \"\n    ],\n    \"regexp\": \"^\\\\w+(\\\\([a-z_-]+\\\\))?: \",\n    \"regexpFlags\": \"\",\n    \"ignoreLabels\": [\n      \"skip-title-check\"\n    ]\n  },\n  \"MESSAGES\": {\n    \"success\": \"PR title is valid\",\n    \"failure\": \"PR title is invalid\",\n    \"notice\": \"\"\n  }\n}\n"
  },
  {
    "path": ".github/pull_request_template.md",
    "content": "## Checklist\n\n- [ ] The title of my PR follows [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/)\n- [ ] I have updated the documentation (`README.md`, comments, `src/manpage.rs` and/or `src/options.rs` if applicable) \n- [ ] I have added unit tests\n- [ ] I have added [integration tests](https://github.com/skim-rs/skim/tree/master/tests)\n- [ ] I have linked all related issues or PRs\n\n## Description of the changes\n\n_Note_: [codecov](https://codecov.io) runs on the PR on this repo, but feel free to ignore it.\n"
  },
  {
    "path": ".github/workflows/pr.yml",
    "content": "name: \"Pull Requests\"\non:\n  pull_request_target:\n    types:\n      - opened\n      - edited\n      - synchronize\n      - labeled\n      - unlabeled\n\njobs:\n  check:\n    runs-on: ubuntu-latest\n    permissions:\n      pull-requests: write\n    steps:\n      - uses: thehanimo/pr-title-checker@v1.4.3\n        with:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n          pass_on_octokit_error: false\n          configuration_path: .github/pr-title-checker-config.json\n  generate-files:\n    runs-on: ubuntu-22.04\n    permissions:\n      contents: write\n    steps:\n      - uses: actions/create-github-app-token@v1\n        id: app-token\n        with:\n          app-id: ${{ vars.SKIM_RS_BOT_APP_ID }}\n          private-key: ${{ secrets.SKIM_RS_BOT_PRIVATE_KEY }}\n      - name: Checkout Git repo\n        uses: actions/checkout@v6\n        with:\n          fetch-depth: 0\n          ref: ${{github.event.pull_request.head.ref}}\n          repository: ${{github.event.pull_request.head.repo.full_name}}\n          token: ${{ steps.app-token.outputs.token }}\n      - run: rustup toolchain install\n      - name: Cache\n        uses: Swatinem/rust-cache@v2\n      - name: Generate files\n        run: |\n          cargo run -- --man > man/man1/sk.1\n          cargo run -- --shell bash > shell/completion.bash\n          cargo run -- --shell zsh > shell/completion.zsh\n          cargo run -- --shell fish > shell/completion.fish\n          cargo run -- --shell nushell > shell/completion.nu\n      - name: Push modified files\n        run: |\n          git branch -v\n          git config user.email \"skim-bot@skim-rs.github.io\"\n          git config user.name \"Skim bot\"\n          git commit -am 'chore: generate completions & manpage' || exit 0\n          git push\n"
  },
  {
    "path": ".github/workflows/publish.yml",
    "content": "name: Publish cargo crate\n\non:\n  workflow_call:\n    inputs:\n      plan:\n        required: true\n        type: string\n\njobs:\n  publish:\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout repository\n        uses: actions/checkout@v6\n      - run: rustup toolchain install\n      - name: Cache\n        uses: Swatinem/rust-cache@v2\n      - name: Login\n        run: cargo login ${CRATES_IO_TOKEN}\n        env:\n          CRATES_IO_TOKEN: ${{ secrets.CRATES_IO_TOKEN }}\n      - name: Publish\n        run: cargo publish\n"
  },
  {
    "path": ".github/workflows/release.yml",
    "content": "# This file was autogenerated by dist: https://axodotdev.github.io/cargo-dist\n#\n# Copyright 2022-2024, axodotdev\n# SPDX-License-Identifier: MIT or Apache-2.0\n#\n# CI that:\n#\n# * checks for a Git Tag that looks like a release\n# * builds artifacts with dist (archives, installers, hashes)\n# * uploads those artifacts to temporary workflow zip\n# * on success, uploads the artifacts to a GitHub Release\n#\n# Note that the GitHub Release will be created with a generated\n# title/body based on your changelogs.\n\nname: Release\npermissions:\n  \"contents\": \"write\"\n\n# This task will run whenever you push a git tag that looks like a version\n# like \"1.0.0\", \"v0.1.0-prerelease.1\", \"my-app/0.1.0\", \"releases/v1.0.0\", etc.\n# Various formats will be parsed into a VERSION and an optional PACKAGE_NAME, where\n# PACKAGE_NAME must be the name of a Cargo package in your workspace, and VERSION\n# must be a Cargo-style SemVer Version (must have at least major.minor.patch).\n#\n# If PACKAGE_NAME is specified, then the announcement will be for that\n# package (erroring out if it doesn't have the given version or isn't dist-able).\n#\n# If PACKAGE_NAME isn't specified, then the announcement will be for all\n# (dist-able) packages in the workspace with that version (this mode is\n# intended for workspaces with only one dist-able package, or with all dist-able\n# packages versioned/released in lockstep).\n#\n# If you push multiple tags at once, separate instances of this workflow will\n# spin up, creating an independent announcement for each one. However, GitHub\n# will hard limit this to 3 tags per commit, as it will assume more tags is a\n# mistake.\n#\n# If there's a prerelease-style suffix to the version, then the release(s)\n# will be marked as a prerelease.\non:\n  pull_request:\n  push:\n    tags:\n      - '**[0-9]+.[0-9]+.[0-9]+*'\n\njobs:\n  # Run 'dist plan' (or host) to determine what tasks we need to do\n  plan:\n    runs-on: \"ubuntu-22.04\"\n    outputs:\n      val: ${{ steps.plan.outputs.manifest }}\n      tag: ${{ !github.event.pull_request && github.ref_name || '' }}\n      tag-flag: ${{ !github.event.pull_request && format('--tag={0}', github.ref_name) || '' }}\n      publishing: ${{ !github.event.pull_request }}\n    env:\n      GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n    steps:\n      - uses: actions/checkout@v4\n        with:\n          persist-credentials: false\n          submodules: recursive\n      - name: Install dist\n        # we specify bash to get pipefail; it guards against the `curl` command\n        # failing. otherwise `sh` won't catch that `curl` returned non-0\n        shell: bash\n        run: \"curl --proto '=https' --tlsv1.2 -LsSf https://github.com/axodotdev/cargo-dist/releases/download/v0.30.3/cargo-dist-installer.sh | sh\"\n      - name: Cache dist\n        uses: actions/upload-artifact@v4\n        with:\n          name: cargo-dist-cache\n          path: ~/.cargo/bin/dist\n      # sure would be cool if github gave us proper conditionals...\n      # so here's a doubly-nested ternary-via-truthiness to try to provide the best possible\n      # functionality based on whether this is a pull_request, and whether it's from a fork.\n      # (PRs run on the *source* but secrets are usually on the *target* -- that's *good*\n      # but also really annoying to build CI around when it needs secrets to work right.)\n      - id: plan\n        run: |\n          dist ${{ (!github.event.pull_request && format('host --steps=create --tag={0}', github.ref_name)) || 'plan' }} --output-format=json > plan-dist-manifest.json\n          echo \"dist ran successfully\"\n          cat plan-dist-manifest.json\n          echo \"manifest=$(jq -c \".\" plan-dist-manifest.json)\" >> \"$GITHUB_OUTPUT\"\n      - name: \"Upload dist-manifest.json\"\n        uses: actions/upload-artifact@v4\n        with:\n          name: artifacts-plan-dist-manifest\n          path: plan-dist-manifest.json\n\n  custom-test:\n    uses: ./.github/workflows/test.yml\n    secrets: inherit\n\n  # Build and packages all the platform-specific things\n  build-local-artifacts:\n    name: build-local-artifacts (${{ join(matrix.targets, ', ') }})\n    # Let the initial task tell us to not run (currently very blunt)\n    needs:\n      - plan\n      - custom-test\n    if: ${{ fromJson(needs.plan.outputs.val).ci.github.artifacts_matrix.include != null && (needs.plan.outputs.publishing == 'true' || fromJson(needs.plan.outputs.val).ci.github.pr_run_mode == 'upload') }}\n    strategy:\n      fail-fast: false\n      # Target platforms/runners are computed by dist in create-release.\n      # Each member of the matrix has the following arguments:\n      #\n      # - runner: the github runner\n      # - dist-args: cli flags to pass to dist\n      # - install-dist: expression to run to install dist on the runner\n      #\n      # Typically there will be:\n      # - 1 \"global\" task that builds universal installers\n      # - N \"local\" tasks that build each platform's binaries and platform-specific installers\n      matrix: ${{ fromJson(needs.plan.outputs.val).ci.github.artifacts_matrix }}\n    runs-on: ${{ matrix.runner }}\n    container: ${{ matrix.container && matrix.container.image || null }}\n    env:\n      GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n      BUILD_MANIFEST_NAME: target/distrib/${{ join(matrix.targets, '-') }}-dist-manifest.json\n    steps:\n      - name: enable windows longpaths\n        run: |\n          git config --global core.longpaths true\n      - uses: actions/checkout@v4\n        with:\n          persist-credentials: false\n          submodules: recursive\n      - name: Install Rust non-interactively if not already installed\n        if: ${{ matrix.container }}\n        run: |\n          if ! command -v cargo > /dev/null 2>&1; then\n            curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y\n            echo \"$HOME/.cargo/bin\" >> $GITHUB_PATH\n          fi\n      - name: Install dist\n        run: ${{ matrix.install_dist.run }}\n      # Get the dist-manifest\n      - name: Fetch local artifacts\n        uses: actions/download-artifact@v4\n        with:\n          pattern: artifacts-*\n          path: target/distrib/\n          merge-multiple: true\n      - name: Install dependencies\n        run: |\n          ${{ matrix.packages_install }}\n      - name: Build artifacts\n        run: |\n          # Actually do builds and make zips and whatnot\n          dist build ${{ needs.plan.outputs.tag-flag }} --print=linkage --output-format=json ${{ matrix.dist_args }} > dist-manifest.json\n          echo \"dist ran successfully\"\n      - id: cargo-dist\n        name: Post-build\n        # We force bash here just because github makes it really hard to get values up\n        # to \"real\" actions without writing to env-vars, and writing to env-vars has\n        # inconsistent syntax between shell and powershell.\n        shell: bash\n        run: |\n          # Parse out what we just built and upload it to scratch storage\n          echo \"paths<<EOF\" >> \"$GITHUB_OUTPUT\"\n          dist print-upload-files-from-manifest --manifest dist-manifest.json >> \"$GITHUB_OUTPUT\"\n          echo \"EOF\" >> \"$GITHUB_OUTPUT\"\n\n          cp dist-manifest.json \"$BUILD_MANIFEST_NAME\"\n      - name: \"Upload artifacts\"\n        uses: actions/upload-artifact@v4\n        with:\n          name: artifacts-build-local-${{ join(matrix.targets, '_') }}\n          path: |\n            ${{ steps.cargo-dist.outputs.paths }}\n            ${{ env.BUILD_MANIFEST_NAME }}\n\n  # Build and package all the platform-agnostic(ish) things\n  build-global-artifacts:\n    needs:\n      - plan\n      - build-local-artifacts\n    runs-on: \"ubuntu-22.04\"\n    env:\n      GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n      BUILD_MANIFEST_NAME: target/distrib/global-dist-manifest.json\n    steps:\n      - uses: actions/checkout@v4\n        with:\n          persist-credentials: false\n          submodules: recursive\n      - name: Install cached dist\n        uses: actions/download-artifact@v4\n        with:\n          name: cargo-dist-cache\n          path: ~/.cargo/bin/\n      - run: chmod +x ~/.cargo/bin/dist\n      # Get all the local artifacts for the global tasks to use (for e.g. checksums)\n      - name: Fetch local artifacts\n        uses: actions/download-artifact@v4\n        with:\n          pattern: artifacts-*\n          path: target/distrib/\n          merge-multiple: true\n      - id: cargo-dist\n        shell: bash\n        run: |\n          dist build ${{ needs.plan.outputs.tag-flag }} --output-format=json \"--artifacts=global\" > dist-manifest.json\n          echo \"dist ran successfully\"\n\n          # Parse out what we just built and upload it to scratch storage\n          echo \"paths<<EOF\" >> \"$GITHUB_OUTPUT\"\n          jq --raw-output \".upload_files[]\" dist-manifest.json >> \"$GITHUB_OUTPUT\"\n          echo \"EOF\" >> \"$GITHUB_OUTPUT\"\n\n          cp dist-manifest.json \"$BUILD_MANIFEST_NAME\"\n      - name: \"Upload artifacts\"\n        uses: actions/upload-artifact@v4\n        with:\n          name: artifacts-build-global\n          path: |\n            ${{ steps.cargo-dist.outputs.paths }}\n            ${{ env.BUILD_MANIFEST_NAME }}\n  # Determines if we should publish/announce\n  host:\n    needs:\n      - plan\n      - build-local-artifacts\n      - build-global-artifacts\n    # Only run if we're \"publishing\", and only if plan, local and global didn't fail (skipped is fine)\n    if: ${{ always() && needs.plan.result == 'success' && needs.plan.outputs.publishing == 'true' && (needs.build-global-artifacts.result == 'skipped' || needs.build-global-artifacts.result == 'success') && (needs.build-local-artifacts.result == 'skipped' || needs.build-local-artifacts.result == 'success') }}\n    env:\n      GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n    runs-on: \"ubuntu-22.04\"\n    outputs:\n      val: ${{ steps.host.outputs.manifest }}\n    steps:\n      - uses: actions/checkout@v4\n        with:\n          persist-credentials: false\n          submodules: recursive\n      - name: Install cached dist\n        uses: actions/download-artifact@v4\n        with:\n          name: cargo-dist-cache\n          path: ~/.cargo/bin/\n      - run: chmod +x ~/.cargo/bin/dist\n      # Fetch artifacts from scratch-storage\n      - name: Fetch artifacts\n        uses: actions/download-artifact@v4\n        with:\n          pattern: artifacts-*\n          path: target/distrib/\n          merge-multiple: true\n      - id: host\n        shell: bash\n        run: |\n          dist host ${{ needs.plan.outputs.tag-flag }} --steps=upload --steps=release --output-format=json > dist-manifest.json\n          echo \"artifacts uploaded and released successfully\"\n          cat dist-manifest.json\n          echo \"manifest=$(jq -c \".\" dist-manifest.json)\" >> \"$GITHUB_OUTPUT\"\n      - name: \"Upload dist-manifest.json\"\n        uses: actions/upload-artifact@v4\n        with:\n          # Overwrite the previous copy\n          name: artifacts-dist-manifest\n          path: dist-manifest.json\n      # Create a GitHub Release while uploading all files to it\n      - name: \"Download GitHub Artifacts\"\n        uses: actions/download-artifact@v4\n        with:\n          pattern: artifacts-*\n          path: artifacts\n          merge-multiple: true\n      - name: Cleanup\n        run: |\n          # Remove the granular manifests\n          rm -f artifacts/*-dist-manifest.json\n      - name: Create GitHub Release\n        env:\n          PRERELEASE_FLAG: \"${{ fromJson(steps.host.outputs.manifest).announcement_is_prerelease && '--prerelease' || '' }}\"\n          ANNOUNCEMENT_TITLE: \"${{ fromJson(steps.host.outputs.manifest).announcement_title }}\"\n          ANNOUNCEMENT_BODY: \"${{ fromJson(steps.host.outputs.manifest).announcement_github_body }}\"\n          RELEASE_COMMIT: \"${{ github.sha }}\"\n        run: |\n          # Write and read notes from a file to avoid quoting breaking things\n          echo \"$ANNOUNCEMENT_BODY\" > $RUNNER_TEMP/notes.txt\n\n          gh release create \"${{ needs.plan.outputs.tag }}\" --target \"$RELEASE_COMMIT\" $PRERELEASE_FLAG --title \"$ANNOUNCEMENT_TITLE\" --notes-file \"$RUNNER_TEMP/notes.txt\" artifacts/*\n\n  custom-publish:\n    needs:\n      - plan\n      - host\n    if: ${{ !fromJson(needs.plan.outputs.val).announcement_is_prerelease || fromJson(needs.plan.outputs.val).publish_prereleases }}\n    uses: ./.github/workflows/publish.yml\n    with:\n      plan: ${{ needs.plan.outputs.val }}\n    secrets: inherit\n    # publish jobs get escalated permissions\n    permissions:\n      \"id-token\": \"write\"\n      \"packages\": \"write\"\n\n  announce:\n    needs:\n      - plan\n      - host\n      - custom-publish\n    # use \"always() && ...\" to allow us to wait for all publish jobs while\n    # still allowing individual publish jobs to skip themselves (for prereleases).\n    # \"host\" however must run to completion, no skipping allowed!\n    if: ${{ always() && needs.host.result == 'success' && (needs.custom-publish.result == 'skipped' || needs.custom-publish.result == 'success') }}\n    runs-on: \"ubuntu-22.04\"\n    env:\n      GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n    steps:\n      - uses: actions/checkout@v4\n        with:\n          persist-credentials: false\n          submodules: recursive\n"
  },
  {
    "path": ".github/workflows/test.yml",
    "content": "name: Build & Test\n\non:\n  workflow_dispatch:\n  workflow_call:\n    inputs:\n      plan:\n        required: false\n        type: string\n  # pull_request: # No need to trigger on PR, cargo-dist already does\n  push:\n    branches:\n    - master\n\nconcurrency:\n  group: ${{ github.workflow }}-${{ github.ref }}\n  cancel-in-progress: true\n\njobs:\n  nextest:\n    runs-on: ${{matrix.os}}\n    strategy:\n      matrix:\n        build: [linux, macos]\n        include:\n        - build: linux\n          os: ubuntu-latest\n          target: x86_64-unknown-linux-musl\n        - build: macos\n          os: macos-latest\n          target: x86_64-apple-darwin\n    steps:\n      - name: \"[linux] Install dependencies\"\n        run: |\n          sudo apt-get install tmux\n          tmux -V\n          locale\n        if: runner.os == 'Linux'\n      - name: \"[macos] Install dependencies\"\n        run: |\n          brew install tmux\n          tmux -V\n          locale\n        if: runner.os == 'macOS'\n        env:\n          HOMEBREW_NO_AUTO_UPDATE: 1\n      - name: Checkout repository\n        uses: actions/checkout@v4\n        with:\n          fetch-depth: 1\n      - run: rustup toolchain install\n      - uses: taiki-e/install-action@v2\n        with:\n          tool: nextest@0.9\n      - uses: taiki-e/install-action@v2\n        with:\n          tool: cargo-llvm-cov@0.8\n      - name: Cache\n        uses: Swatinem/rust-cache@v2\n      - name: Run doctests\n        run: cargo test --doc\n      - name: Run tests\n        # Do not use `--all-targets` to avoid running benches\n        run: cargo llvm-cov nextest --release --features test-utils --bins --lib --examples --tests --codecov --output-path codecov.json\n        env:\n          LC_ALL: en_US.UTF-8\n          TERM: xterm-256color\n      - name: Upload coverage reports to Codecov\n        uses: codecov/codecov-action@v5\n        with:\n          token: ${{ secrets.CODECOV_TOKEN }}\n          files: codecov.json\n          fail_ci_if_error: false\n        continue-on-error: true\n      - name: Upload test results to Codecov\n        uses: codecov/codecov-action@v5\n        with:\n          report_type: test_results\n          token: ${{ secrets.CODECOV_TOKEN }}\n          files: target/nextest/default/junit.xml\n          fail_ci_if_error: false\n        continue-on-error: true\n\n  clippy:\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout repository\n        uses: actions/checkout@v6\n        with:\n          fetch-depth: 1\n      - run: rustup toolchain install\n      - name: Cache\n        uses: Swatinem/rust-cache@v2\n      - name: Clippy\n        run: cargo clippy\n\n  rustfmt:\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout repository\n        uses: actions/checkout@v6\n      - run: rustup toolchain install\n      - name: Check formatting\n        run: |\n          cargo fmt --all -- --check\n\n  sonarqube:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v6\n        with:\n          fetch-depth: 0  # Shallow clones should be disabled for a better relevancy of analysis\n      - name: SonarQube Scan\n        uses: SonarSource/sonarqube-scan-action@v6\n        env:\n          SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}\n\n  build-no-default-features:\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout repository\n        uses: actions/checkout@v6\n      - run: rustup toolchain install\n      - name: Cache\n        uses: Swatinem/rust-cache@v2\n      - name: Build without any feature\n        run: |\n          cargo build --no-default-features\n"
  },
  {
    "path": ".gitignore",
    "content": "# Compiled files\n*.o\n*.so\n*.rlib\n*.dll\n\n# Executables\n*.exe\n\n# Generated by Cargo\n/target/\n\n/bin/sk\n.idea/\n.ropeproject/\nbench_data.txt\n.direnv\n\n# Coverage\nlcov.info\ncodecov.json\n*.profraw\n\nbenches/fixtures/*.txt\n\n# Profiling\nprofile.json.gz\nperf.data.old\nperf.data\nflamegraph.svg\ncachegrind.out.*\n\n/scripts/data/\n"
  },
  {
    "path": ".rustfmt.toml",
    "content": "max_width = 120"
  },
  {
    "path": "AGENTS.md",
    "content": "# Skim Agent Guidelines\n\n## Build/Test/Lint Commands\n- Build: `cargo build [--release]`\n- Run: `cargo run [--release]`\n- Test (all): `cargo nextest --features test-utils`\n- Test (single): `cargo nextest test_name --features test-utils`\n- Integration/E2E tests: `cargo nextest --tests --features test-utils` (will need tmux under the hood)\n- Memory leak detection: `cargo nextest run --profile valgrind --features test-utils`\n- Thread leak/race detection: \n  1. Build: `RUSTFLAGS=\"-Zsanitizer=thread\" cargo +nightly build --tests --features test-utils -Zbuild-std --target x86_64-unknown-linux-gnu`\n  2. Run: `TSAN_OPTIONS=\"detect_deadlocks=1\" cargo +nightly nextest run --profile tsan --features test-utils --target x86_64-unknown-linux-gnu`\n- Lint: `cargo clippy`\n- Format: `cargo fmt` (check only: `cargo fmt --check`)\n\n## Code Style\n- Format with 120 char line width (defined in .rustfmt.toml)\n- Use standard Rust naming conventions (snake_case for functions/variables, CamelCase for types)\n- Organize imports by standard library, external crates, then internal modules\n- Prefer Option/Result types for error handling over panicking\n- Use proper error propagation with `?` operator\n- Document public API with rustdoc comments\n- Use meaningful type annotations, especially for public functions\n- Follow the existing structure for new modules (see src/engine/ or src/model/)\n- Implement relevant traits (SkimItem, etc.) for new types when needed\n\n## Project Structure\n- Core functionality in `skim/src/`\n- Common utilities in `skim-common/`\n- Task automation in `xtask/`\n\n\n## Testing\n\nThis application can be tested by :\n- creating a new `tmux` session in the background (`tmux new-session -s <session name> -d`)\n- creating a new named tmux window in that session : `tmux new-window -d -P -F '#I' -n <window name> -t <session name>` and configuring the pane naming using `tmux set-window-option -t <window name> pane-base-index 0`\n- sending the command to run and input using `tmux send-keys -t <window name> <keys>`\n- when ready, capturing the window using `tmux capture-pane -b <window name> -t <window name>.0` and then saving the capture to a file using `tmux save-buffer -b <window name> <output file>`\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "# Changelog\n\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## [4.0.0] - 2026-03-10\n\n### Changes\n\n`sk` is now beating `fzf` in interactive matching for time, peak memory usage and CPU usage ! See the benchmarks below for details, including a brief explanation of the benchmark itself.\n\nThis release brings multiple breaking changes, please read the following if you have doubts about the update.\n\n#### Default to the Arinae matcher\n\nThe biggest change of them all is that the default algorithm is now `Arinae`, skim's latest and most performant algorithm, featuring typo-resistance. If you want to keep using `SkimV2`, pass `--algo skim_v2` and please take the time to open an issue explaining why if possible.\nThis also makes the `--scheme` option available to the default matcher, allowing for more refined matching scenarii.\n\n#### Default to non-typo resistant\n\nTypo-resistant behavior is now disable by default, add `--typos` to your skim invocation or `SKIM_DEFAULT_OPTIONS` to get it back. Unless you were using the `frizbee`, `fzy` or `arinae` matcher, this should not impact you.\n\n#### Removal of the `skim_v1` algorithm\n\nThe `SkimV1` algorithm, skim's initial algorithm, has been deprecated for years now, and it has now been removed.\n\n#### SkimItem index (library only)\n\n`SkimItem::get_index` and `SkimItem::set_index` are gone, and all index handling is now done internally.\n\n### Benchmarks\n\nThis benchmarks runs the interactive interface in a tmux session, and waits for the UI to stabilize.\nIt uses a 10 million path-like ASCII items input file, and the query `test`.\n\n```\n=== Results: sk v4.0.0 [baseline] ===\nCompleted runs: 50 / 50\nAverage items matched: 2895782 / 10000000  (min: 2895782, max: 2895782)\nAverage time: 3.827s  (min: 3.576s, max: 4.090s)\nAverage items/second: 2615767  (min: 2445033, max: 2796365)\nAverage peak memory usage: 1589.2 MB  (min: 1518.6 MB, max: 1661.2 MB)\nAverage peak CPU usage: 528.9%  (min: 457.0%, max: 740.0%)\n\n=== Results: sk v3.7.0 ===\nCompleted runs: 50 / 50\nAverage items matched: 2895782 / 10000000  (min: 2895782, max: 2895782) +0.0%\nAverage time: 3.930s  (min: 3.565s, max: 4.226s)  +2.7%\nAverage items/second: 2548674  (min: 2366263, max: 2804816)  -2.6%\nAverage peak memory usage: 1618.8 MB  (min: 1539.1 MB, max: 1680.6 MB) +1.9%\nAverage peak CPU usage: 696.8%  (min: 608.0%, max: 875.0%)  +31.7%\n\n=== Results: fzf 0.70.0 ===\nCompleted runs: 50 / 50\nAverage items matched: 2895782 / 10000000  (min: 2895782, max: 2895782) +0.0%\nAverage time: 5.421s  (min: 4.814s, max: 6.111s)  +41.7%\nAverage items/second: 1848269  (min: 1636444, max: 2077385)  -29.3%\nAverage peak memory usage: 2015.3 MB  (min: 1860.7 MB, max: 2173.9 MB) +26.8%\nAverage peak CPU usage: 1301.1%  (min: 1229.0%, max: 1431.0%)  +146.0%\n\n=== Comparison Summary (vs baseline: sk v4.0.0) ===\nBinary      Avg time     Δ time  Avg rate     Δ rate\n------------------------------------------------------------------------------\nsk v4.0.0   3.827s    baseline   2615767     baseline\nsk v3.7.0   3.930s      +2.7%    2548674      -2.6%\nfzf 0.70.0  5.421s     +41.7%    1848269     -29.3%\n```\n\n### 🚀 Features\n\n- [**breaking**] Internally compute indexes at match time (removes get/set_index) (#1001)\n- [**breaking**] Use Arinae as default algorithm\n\n### ⚙️ Miscellaneous Tasks\n\n- [**breaking**] Default to disabled typos\n- Use python for bench script for comparison\n\n## [3.7.0] - 2026-03-08\n\nThis adds a new library API: `Skim::run_items`. Using this, you don't need to send the items, the library handles it for you.\n\nInstead of writing this:\n\n```rust\nuse skim::prelude::*;\nlet (rx, tx) = unbounded();\n\n// We need MySkimItem to override the set_index/get_index methods\nstruct MySkimItem {\n    value: String,\n    index: usize\n}\n\nimpl SkimItem for MySkimItem {\n    // Implement the default members\n}\n\ntx.send(MySkimItem { value: String::from(\"foo\"), index: 0 });\ntx.send(MySkimItem { value: String::from(\"bar\"), index: 1 });\n\nlet res = Skim::run_with(options, Some(rx));\n```\n\nYou can simply write this:\n```rust\nuse skim::prelude::*;\n\nlet res = Skim::run_with(options, [\"foo\", \"bar\"]);\n```\n\nIt will automatically convert any iterator of <impl SkimItem> by adding an `index` field and then send it, before running skim.\n\n### 🚀 Features\n\n- Add `scheme` for better filepath matching in Arinae\n- Add Skim::run_items API\n\n### 🐛 Bug Fixes\n\n- Use sum of scores in And engine\n- Correctly init rank for and engine\n\n### ⚙️ Miscellaneous Tasks\n\n- Add gungraun benchmark\n- Review snapshots\n\n## [3.6.2] - 2026-03-04\n\n### ⚙️ Miscellaneous Tasks\n\n- Refactor app layout computations to take them out of the hot loop (#996)\n- Allow using flag or env vars for both log level and file\n\n## [3.6.1] - 2026-03-03\n\n### ⚙️ Miscellaneous Tasks\n\n- Switch allocator to mimalloc for ~20% perf in some cases\n- Add bench plot scripts\n\n## [3.6.0] - 2026-03-02\n\nThis version adds the Arinae algorithm as an option.\n\nArinae is designed to become skim's default algorithm in the future.\n\nTechnically, it uses Smith-Waterman and a modified Levenshtein distance with affine gaps for scoring, as well as multiple optimizations (the main ones being a loose prefilter and checks for early dismissal of paths that cannot lead to the best match). It also forbids typos on the first char of the query.\n\nIn practice, it should feel close to FZY's scoring with typos disabled, but with a more natural behavior regarding typos as Frizbee or other algorithms.\n\nThese other algorithms usually work by allowing a set number of typos using 3D matrices for computations, the max-typos value being set based on the length of the query. In practice, that meant that tes will match exactly, but test will allow one typo, meaning that typing a single character will change the filtered items completely. This algorithm will instead penalize typos, not block them completely.\n\nThis algorithm does not aim to revolution anything, but it aims at making typo-resistant fuzzy matching feel more like an actual alternative to the current options (namely FZF and FZY), while maintaining per-item performance at least as good as the current algorithms.\n\n### 🚀 Features\n\n- Merge ranks in AndOr engine matcher\n- Add Arinae algorithm (#990)\n- *(shell)* Colored history widgets & remove perl completely (#994)\n\n### 🐛 Bug Fixes\n\n- Make sure we drop Skim before returning the output\n\n### 💼 Other\n\n- Drop flake-utils, add formatter (#992)\n\n### New Contributors\n* @faukah made their first contribution in [#992](https://github.com/skim-rs/skim/pull/992)\n\n## [3.5.0] - 2026-02-22\n\n### 🚀 Features\n\n- Add fzy matcher and `--typos`/`--no-typos` flag (#987)\n\n### 🐛 Bug Fixes\n\n- Correctly bind uppercase keys\n- More precision on AndOr matches (closes #526)\n- Respect the and & or priority\n\n### ⚙️ Miscellaneous Tasks\n\n- *(dep)* Remove unused dependency 'beef' (#986)\n- *(dep)* Frizbee 0.8.1 (#985)\n- Add partial bench to measure the time it takes to setup skim\n- Pin dependencies to the latest exact version\n\n### New Contributors\n* @bitfehler made their first contribution in [#985](https://github.com/skim-rs/skim/pull/985)\n* @Sisyphus1813 made their first contribution in [#983](https://github.com/skim-rs/skim/pull/983)\n\n## [3.4.0] - 2026-02-19\n\n### 🚀 Features\n\n- Allow setting delimiters in `+` expansions (closes #935)\n- Add set-header action (closes #768)\n- Add `--print-current`, `--output-format` (closes #981)\n- Add --ellipsis option\n- Back to stable rust (#980)\n\n### 🐛 Bug Fixes\n\n- *(ci)* Do not run benches\n\n### 💼 Other\n\n- Add filter\n\n### 📚 Documentation\n\n- Benchmarks\n\n### ⚙️ Miscellaneous Tasks\n\n- Add rust benchmark\n\n## [3.3.0] - 2026-02-18\n\n### 🚀 Features\n\n- Use a separate thread pool for Matcher runs (#961)\n- Event-driven re-render (#949)\n- Allow run_with to be run within a tokio runtime (#979)\n\n### 🐛 Bug Fixes\n\n- Possible None unwrap if ansi enabled but not in item\n\n### ⚙️ Miscellaneous Tasks\n\n- Unify filter mode & squeeze more perf (#974)\n- Refactor Skim into its own file\n\n### New Contributors\n* @figsoda made their first contribution in [#979](https://github.com/skim-rs/skim/pull/979)\n\n## [3.2.0] - 2026-02-13\n\n### 🚀 Features\n\n- Further reduce DefaultSkimItem size (#967)\n\n### ⚙️ Miscellaneous Tasks\n\n- Enhance PR template [skip ci]\n\n## [3.1.1] - 2026-02-13\n\n### 🐛 Bug Fixes\n\n- Republish crate\n\n## [3.1.0] - 2026-02-13\n\n### 🚀 Features\n\n- *(cli)* Add SKIM_OPTIONS_FILE (#972)\n- Add set-preview-cmd action to change preview (#969)\n\n## [3.0.1] - 2026-02-12\n\n### 🐛 Bug Fixes\n\n- Restart_matcher race condition (closes #970)\n\n### 📚 Documentation\n\n- Add link to sqlite_skim (#971) [skip ci]\n\n### New Contributors\n* @tzachar made their first contribution in [#971](https://github.com/skim-rs/skim/pull/971)\n\n## [3.0.0] - 2026-02-12\n\n### Changes\n\n#### For everyone\n\nMemory usage was optimized, bringing a ~25% reduction in memory usage in our benches. Thanks to @kimono-koans for the initial idea and help with this.\n\n#### For CLI users\n\n- Added a `:pty` preview-window flag that will make the preview run in a PTY. This allows for more interactive commands and more complicated display, including running `sk` itself inside `sk`'s preview (run `SKIM_DEFAULT_OPTIONS='--preview \"sk\" --preview-window \":pty\"' sk` for the extreme version).\n    Note: the `pty` preview does not take input (for now). For instance, scrolling in paged output will not work.\n- Readded `--sync` functionality after it was broken in v1\n\n#### For library users\n\n- A more fine-grained control over skim's event loop is now possible using `tokio` and the new methods on `Skim`. Check them out if you want to interact with the skim instance while it is running.\n- [**breaking**] The `SkimOptionsBuilder`'s setter methods are back to taking raw `&str`s instead of `String`s. This should match the behavior pre-v1, sorry for the extra work that some of you already put in to migrate.\n\n### 🚀 Features\n\n- Interactive pty preview & concurrency optimizations (#952)\n- Optimize match_item to use ref and not Arc (#962)\n- Reduce DefaultSkimItem memory footprint by around 25% by default (#966)\n- [**breaking**] Use smarter setters, remove the need for Some(...) and String::from() in setters\n- Readd `--sync` functionality\n- *(lib)* Add fine-grained control over skim's event loop (#968)\n\n### 🐛 Bug Fixes\n\n- Manually patch modifiers (closes #945)\n- Implement cursor_pos_from_tty ourselves (#963)\n- Run preview on move after selection\n\n### 📚 Documentation\n\n- Specify nightly version in README\n- Update nightly version [skip ci]\n- Detail `pty` preview window flag [skip ci]\n\n### ⚙️ Miscellaneous Tasks\n\n- Ignore coverage files [skip ci]\n- Add coverage flag to README\n- Test matchers\n- Update frizbee and nightly version\n- *(ci)* Make codecov less aggressive\n\n### New Contributors\n* @kimono-koans made their first contribution in [#962](https://github.com/skim-rs/skim/pull/962)\n\n## [2.0.2] - 2026-02-02\n\n### 🐛 Bug Fixes\n\n- Fix preview_fn\n\n### ⚙️ Miscellaneous Tasks\n\n- Update interactive mode examples (closes #943)\n\n## [2.0.1] - 2026-02-01\n\n### 🐛 Bug Fixes\n\n- *(linux)* Run preview in a PTY (closes #894) (#897)\n\n## [2.0.0] - 2026-01-31\n\n### 🚀 Features\n\n- [**breaking**] **library** Send & receive items in batches (#938)\n\tThis should not affect TUI users other than in improving the app's performance\n\n### ⚙️ Miscellaneous Tasks\n\n- Add valgrind and thread sanitizer test profiles [skip ci]\n\n## [1.11.2] - 2026-01-29\n\n### 🐛 Bug Fixes\n\n- Always make ctrl-d abort (closes #941)\n\n## [1.11.1] - 2026-01-29\n\n### 🐛 Bug Fixes\n\n- Do not enter TUI early if the matcher needs restarting before ingestion done (closes #940)\n\n### ⚙️ Miscellaneous Tasks\n\n- Add nix flake for dev [skip ci]\n- Reduce FPS for better performance\n\n## [1.11.0] - 2026-01-27\n\n### 🚀 Features\n\n- Add custom action for lib usage (closes #537)\n\n## [1.10.0] - 2026-01-27\n\n### 🚀 Features\n\n- Add `--normalize` to ignore accents etc. when matching (closes #453) (#914)\n\n### 🐛 Bug Fixes\n\n- *(frizbee)* Correctly compute max_typos\n- Make `-1` reset highlights (#937)\n- Always display item_list (closes #939)\n\n## [1.9.1] - 2026-01-27\n\n### 🐛 Bug Fixes\n\n- Clear screen when not in fullscreenon bash & fish (#936)\n\n### New Contributors\n* @phanen made their first contribution in [#936](https://github.com/skim-rs/skim/pull/936)\n\n## [1.9.0] - 2026-01-26\n\n### 🚀 Features\n\n- *(frizbee)* Adaptive max_typos value\n- *(theme)* Add catppuccin themes\n\n## [1.8.1] - 2026-01-26\n\n### 🐛 Bug Fixes\n\n- Correctly flush buffered stderr\n\n## [1.8.0] - 2026-01-26\n\n### 🚀 Features\n\n- Parse ansi codes in prompt\n\n### 🐛 Bug Fixes\n\n- Header and header-lines order in reverse layout\n- Correctly overlay header styles\n- Make select-1 and exit-0 work again (closes #916) (#933)\n- Fix cursor blinking (closes #932) (credits to @lilydjwg)\n\n### 🧪 Testing\n\n- Review snapshots\n\n### ⚙️ Miscellaneous Tasks\n\n- Add matrix room to readme\n\n## [1.7.2] - 2026-01-25\n\n### 🐛 Bug Fixes\n\n- Correct cursor position when using reverse and border (closes #928)\n\n## [1.7.1] - 2026-01-25\n\n### 🐛 Bug Fixes\n\n- Y cursor pos in reverse mode (closes #931)\n\n## [1.7.0] - 2026-01-25\n\n### 🚀 Features\n\n- Add borders to all widgets (#930)\n\n### 🐛 Bug Fixes\n\n- Correctly merge base styles\n- Correctly display all header lines\n- Correctly toggle prompt on ToggleInteractive (closes #925)\n- Fix printf sometimes replacing recursively\n- Interrupt the reader thread when stopping\n- Replace {n} with an empty string when no item is selected\n- Revert case-insensitive action_chain\n- Re-enable query/cmd-query distinction and switching\n- Correctly compute character width for cursor display (closes #929)\n\n### ⚙️ Miscellaneous Tasks\n\n- Cleanup changelog [skip ci]\n\n## [1.6.0] - 2026-01-23\n\n### 🚀 Features\n\n- Add `--remote` flag to call remote (`--listen`) instances (#915)\n\n### 🐛 Bug Fixes\n\n- Make no-sort work again\n\n### 🧪 Testing\n\n- Remove insta_ prefixes after finalizing tests migration\n\n## [1.5.4] - 2026-01-23\n\n### 🐛 Bug Fixes\n\n- Do not override {} with {q} in interactive mode\n- Remove unnecessary clone in printf\n- Correctly merge styles & do not reset them by default (#918)\n- Translate frizbee's byte indices into char indices\n\n### 📚 Documentation\n\n- Customize man page\n\n## [1.5.3] - 2026-01-22\n\n### 🐛 Bug Fixes\n\n- Quote expanded items independently (#910)\n- Escape last `;` in env var value before passing to tmux (#912)\n\n### New Contributors\n* @mathieu-lemay made their first contribution in [#912](https://github.com/skim-rs/skim/pull/912)\n\n## [1.5.2] - 2026-01-22\n\n### 🐛 Bug Fixes\n\n- Ignore `{+}` expressions when splitting action chains (closes #910)\n- Strip ansi from expanded items (#910)\n\n## [1.5.1] - 2026-01-22\n\n### 🐛 Bug Fixes\n\n- Correctly expand `{+}` to current when no items are selected (cl… (#913)\n\n## [1.5.0] - 2026-01-22\n\n### 🚀 Features\n\n- Add `set-query` action to update the input (closes #657) (#907)\n\n### 🐛 Bug Fixes\n\n- Make case option work with non-ascii input (closes #454)\n\n### ⚙️ Miscellaneous Tasks\n\n- Fix tests link in PR template [skip ci]\n\n## [1.4.0] - 2026-01-21\n\n### 🚀 Features\n\n- Split-match (#906)\n\n### 📚 Documentation\n\n- Reflect need for nightly rust in install section [skip ci]\n\n## [1.3.2] - 2026-01-21\n\n### 🐛 Bug Fixes\n\n- Better spinner debounce behavior to avoid flickering (closes #904)\n\n### 📚 Documentation\n\n- Update README install section\n- Add details to interactive mode in manpage (closes #805) (#816)\n\n### 🧪 Testing\n\n- Use insta for applicable integration tests, making them cross-p… (#903)\n\n## [1.3.1] - 2026-01-21\n\n### 🐛 Bug Fixes\n\n- Allow layout to override reverse (closes #901)\n\n### 🧪 Testing\n\n- Allow multiple bench runs for better consistency\n- More reproducible and more precise bench [skip ci]\n\n### ⚙️ Miscellaneous Tasks\n\n- Optimized release builds\n\n## [1.3.0] - 2026-01-20\n\n### 🚀 Features\n\n- Typo resistant matcher using frizbee from blink.cmp (#891)\n\n## [1.2.0] - 2026-01-20\n\n### 🚀 Features\n\n- Add no-strip-ansi flag (#898)\n\n### 🐛 Bug Fixes\n\n- Run preview in a PTY (closes #894)\n\n## [1.1.2] - 2026-01-20\n\n### 🐛 Bug Fixes\n\n- Half page down scrolls down\n- Use ansi-stripped raw item in preview expansion\n\n## [1.1.1] - 2026-01-19\n\n### 🐛 Bug Fixes\n\n- Use item text in printf\n- Parse ansi codes in header\n- Use item output for fields\n\n### 🧪 Testing\n\n- Fix preview_nul\n\n### ⚙️ Miscellaneous Tasks\n\n- Update crossterm version requirement to pass crates.io publish checks\n\n## [1.1.0] - 2026-01-19\n\n### 🚀 Features\n\n- Wrap items\n\n### 🐛 Bug Fixes\n\n- Delete outside char boundaries\n- Preview on large binaries does not hang or mangle the tui\n\n### 🧪 Testing\n\n- Fix wrap test (#896)\n\n## [1.0.1] - 2026-01-19\n\n### 🐛 Bug Fixes\n\n- Disable compact_matcher feature\n\n## [1.0.0-pre11] - 2026-01-17\n\n### 🐛 Bug Fixes\n\n- Always use cursor/selector colors (#892)\n\n### 🧪 Testing\n\n- Fix flaky tests\n\n### ⚙️ Miscellaneous Tasks\n\n- *(changelog)* Ignore release commits\n\n## [1.0.0-pre10] - 2026-01-17\n\n### 🐛 Bug Fixes\n\n- Only expand selection in {+} for commands\n\n### ⚙️ Miscellaneous Tasks\n\n- Add pointer/marker as aliases for selector/multi-selector\n\n## [1.0.0-pre9] - 2026-01-16\n\n### 🐛 Bug Fixes\n\n- Matcher race condition at startup\n\n## [1.0.0-pre8] - 2026-01-16\n\n### 🚀 Features\n\n- Add print-header flag (and readd print-score) (closes #470)\n\n### 🐛 Bug Fixes\n\n- *(ui)* Use current highlight for the current item (closes #889) (#890)\n\n### 🧪 Testing\n\n- Remove useless listen tests\n\n## [1.0.0-pre7] - 2026-01-16\n\n### 🚀 Features\n\n- Add `listen` flag (closes #719)\n\n### 🐛 Bug Fixes\n\n- Fix listen flag on macos (#888)\n- Correctly parse wrap arg in preview options\n\n### 🧪 Testing\n\n- Add tests for listen flag\n\n## [1.0.0-pre6] - 2026-01-15\n\n### 🚀 Features\n\n- Add cycle flag (closes #553)\n- Add disabled flag (closes #500)\n- Add nushell completion support (closes #459)\n- Add --shell-bindings flag to get bindings at runtime\n\n### 🐛 Bug Fixes\n\n- Disable completions without cli feature\n- Fix build without default features\n\n### ⚙️ Miscellaneous Tasks\n\n- Add exhaustive_match macro for enum building from str\n\n## [1.0.0-pre5] - 2026-01-15\n\n### 🚀 Features\n\n- *(ui)* Add selector and multi-selector options to set the itemlist icons\n- *(ui)* Allow setting modifiers (closes #871)\n\n## [1.0.0-pre4] - 2026-01-14\n\n### 🚀 Features\n\n- 120 FPS\n\n### 🐛 Bug Fixes\n\n- *(cmd)* [**breaking**] Always use `sh` for all command executions\n\n### ⚙️ Miscellaneous Tasks\n\n- Regenerate CHANGELOG.md\n\n## [1.0.0-pre3] - 2026-01-14\n\n### 🐛 Bug Fixes\n\n- Fix terminal height management\n\n### ⚙️ Miscellaneous Tasks\n\n- Release v1.0.0-pre3\n\n## [1.0.0-pre2] - 2026-01-14\n\n### 🚀 Features\n\n- *(ci)* Add crates.io publish to release CI\n\n### 🐛 Bug Fixes\n\n- Manually acquire cursor pos (closes #885) (#886)\n\n### ⚙️ Miscellaneous Tasks\n\n- Remove unneeded deps (#884)\n- Release\n\n## [1.0.0-pre1] - 2026-01-13\n\n### 🚀 Features\n\n- *(ui)* [**breaking**] Ratatui migration (#864)\n\n### ⚙️ Miscellaneous Tasks\n\n- Remove workspace (#883)\n\n### New Contributors\n* @rusty-snake made their first contribution in [#872](https://github.com/skim-rs/skim/pull/872)\n* @peccu made their first contribution in [#845](https://github.com/skim-rs/skim/pull/845)\n* @azarmadr made their first contribution in [#841](https://github.com/skim-rs/skim/pull/841)\n\n## [0.20.5] - 2025-08-09\n\n### 🐛 Bug Fixes\n\n- Compile without the cli feature (#834)\n\n### ⚙️ Miscellaneous Tasks\n\n- *(release)* Release (#835)\n\n## [0.20.4] - 2025-08-02\n\n### 🚀 Features\n\n- *(e2e)* Add Dockerfile to run E2E\n\n### 🐛 Bug Fixes\n\n- *(options)* Allow border to be used without args\n- *(ci)* Fetch whole history to avoid PR recreation\n\n### ⚙️ Miscellaneous Tasks\n\n- *(ci)* Revert to a more vanilla release-plz config\n- Remove unreleased section from changelog\n- *(release)* Release (#831)\n\n## [0.20.3] - 2025-07-27\n\n### ⚙️ Miscellaneous Tasks\n\n- *(release)* Release (#826)\n\n## [0.20.2] - 2025-06-29\n\n### 📚 Documentation\n\n- *(e2e)* Add contributing section (#817)\n\n### ⚙️ Miscellaneous Tasks\n\n- *(release)* Release (#818)\n\n### New Contributors\n* @azat made their first contribution in [#783](https://github.com/skim-rs/skim/pull/783)\n\n## [0.20.1] - 2025-06-21\n\n### 🐛 Bug Fixes\n\n- Min-query-length in interactive mode (#814)\n\n### ⚙️ Miscellaneous Tasks\n\n- *(release)* Release (#815)\n\n## [0.20.0] - 2025-06-21\n\n### 🚀 Features\n\n- *(ui)* Respect NO_COLOR environment variable (#804)\n\n### ⚙️ Miscellaneous Tasks\n\n- *(release)* Release (#813)\n\n### New Contributors\n* @saidelmark made their first contribution in [#804](https://github.com/skim-rs/skim/pull/804)\n\n## [0.19.0] - 2025-06-21\n\n### 🚀 Features\n\n- Add min query length option (#806)\n\n### ⚙️ Miscellaneous Tasks\n\n- *(release)* Release (#811)\n\n## [0.18.0] - 2025-05-30\n\n### 🚀 Features\n\n- *(shell)* Improve shell completion with dynamic generation (#790)\n\n### 🐛 Bug Fixes\n\n- *(ci)* Remove version from pr name\n\n### 📚 Documentation\n\n- *(contributing)* Refine guidelines for GPT-assisted development\n- Improve theming documentation (#788)\n- Improve wording in README and options.rs (#789)\n\n### ⚙️ Miscellaneous Tasks\n\n- Generate changelog\n- *(release)* Release (#792)\n\n## [0.17.3] - 2025-05-20\n\n### 🐛 Bug Fixes\n\n- *(shell)* Fix zsh tmux args in key bindings (#777)\n- *(shell)* Remove duplocate tmux height arg fixes #776 (#778)\n\n### 💼 Other\n\n- Set keybinding right before printing special character (#774)\n\n### ⚙️ Miscellaneous Tasks\n\n- Generate changelog using git cliff\n- *(release)* Release v0.17.3 (#782)\n\n### New Contributors\n* @ajeetdsouza made their first contribution in [#774](https://github.com/skim-rs/skim/pull/774)\n\n## [0.17.2] - 2025-05-04\n\n### 🐛 Bug Fixes\n\n- *(tmux)* Force sh as shell for tmux mode (#765)\n- *(ci)* Remove release commits filter\n\n### ⚙️ Miscellaneous Tasks\n\n- *(ci)* Remove temp workflow\n- *(release)* Release v0.17.2 (#766)\n\n## [0.17.1] - 2025-05-04\n\n### 🚀 Features\n\n- *(ci)* Manually update versions\n\n### 🐛 Bug Fixes\n\n- *(cargo)* Fix tuikit re-export\n- *(ci)* More generic pr name\n- *(ci)* Split release pr and gh release\n- *(cargo)* Fix tuikit readme path\n- *(ci)* Fix broken ci after migration\n\n### 🧪 Testing\n\n- *(ci)* Show context\n- *(ci)* Test trigger (#761)\n\n### ⚙️ Miscellaneous Tasks\n\n- *(ci)* Only release after merge\n- Release (#760)\n- *(cargo)* Update to 2024 edition (#764)\n- *(ci)* Update dependencies\n\n## [0.17.0] - 2025-05-04\n\n### 🐛 Bug Fixes\n\n- Fix local dependencies\n\n## [common-v0.1.0] - 2025-05-04\n\n### 🚀 Features\n\n- *(tui)* Add tuikit as workspace member and update (#741)\n- *(shell)* Readd completions (#726) (#739)\n\n### 🐛 Bug Fixes\n\n- *(cargo)* Fix workspace packages\n- *(ci)* Remove leftover package\n- *(ci)* Add metadata to common package\n\n### ⚙️ Miscellaneous Tasks\n\n- *(tuikit)* Bring skim-rs/tuikit#43 (#743)\n- *(ci)* Back to manifest release\n- *(ci)* Readd manifest manually\n- *(ci)* Revert action\n- *(ci)* Use linked changelog\n- *(ci)* Disable skim prefix in tag\n- *(ci)* Test without extra packages\n- *(ci)* Readd all components\n- *(ci)* Release every package at the same version\n- *(ci)* Release whole workspace at once\n- *(ci)* Update manifest\n- *(ci)* Readd all packages as well as root\n- *(ci)* Better handling of packages in release\n- *(ci)* Unlink versions\n- *(ci)* Set package names\n- *(ci)* Explicitely set root component\n- *(ci)* Explicitely set last release sha\n- *(ci)* Use previous versions for packages\n- *(ci)* Migrate to release-plz\n- *(ci)* Update release-plz changelog format\n- *(ci)* Update release-plz changelog format\n- *(ci)* Split release actions\n- Release (#756)\n- *(ci)* Do not publish extra packages\n- *(ci)* Release on all commits\n- *(ci)* Make local packages publishable\n\n## [0.16.2] - 2025-04-26\n\n### 🚀 Features\n\n- *(zsh)* [**breaking**] Sort history items by timestamp\n\n### 🐛 Bug Fixes\n\n- *(tmux)* Check if TMUX is set (closes #734) (#736)\n- *(filter)* Fix broken pipe while writing results to locked stdout (closes #733) (#737)\n\n### 📚 Documentation\n\n- *(tmux)* Add note about env var (#732)\n- *(tmux)* Fix docs formatting\n\n### 🧪 Testing\n\n- *(ci)* Try a simpler release-please config\n\n### ⚙️ Miscellaneous Tasks\n\n- Move changelog to subdir (#740)\n- *(master)* Release 0.16.2 (#738)\n\n## [0.16.1] - 2025-03-06\n\n### 🐛 Bug Fixes\n\n- Hasten deprecation of expect after #703\n\n### ⚙️ Miscellaneous Tasks\n\n- Manually update release-please manifest after release\n- *(master)* Release 0.16.1 (#712)\n\n## [0.16.0] - 2025-01-23\n\n### 🚀 Features\n\n- Add preview callback (#407)\n\n### 🐛 Bug Fixes\n\n- *(docs)* Fix README lib example\n- *(term)* Clamp height option (#690)\n\n### 📚 Documentation\n\n- *(readme)* Correct fzf library statement in README (#679)\n\n### 🧪 Testing\n\n- *(ci)* Test previous fixes\n- *(ci)* Test previous fixes\n- *(ci)* Try removing the packages altogether\n\n### ⚙️ Miscellaneous Tasks\n\n- Remove lazy_static (#687)\n- Fix clippy warning in rust 1.84 (#688)\n- *(ci)* Try to fix release-please on extra packages\n- *(ci)* Do not search commits on e2e & xtask\n- *(ci)* Try releasing as 0.1.0\n- Release master (#672)\n- Release master (#691)\n\n### New Contributors\n* @alexxbb made their first contribution in [#407](https://github.com/skim-rs/skim/pull/407)\n* @alexandregv made their first contribution in [#679](https://github.com/skim-rs/skim/pull/679)\n\n## [0.15.7] - 2024-12-27\n\n### 🐛 Bug Fixes\n\n- Remove atty (#671)\n\n### ⚙️ Miscellaneous Tasks\n\n- Release master (#670)\n\n### New Contributors\n* @gallois made their first contribution in [#671](https://github.com/skim-rs/skim/pull/671)\n\n## [0.15.6] - 2024-12-26\n\n### 🐛 Bug Fixes\n\n- Fix non-functional vim plugin (#659)\n- Update rank to follow the readded index tiebreak (#669)\n\n### ⚙️ Miscellaneous Tasks\n\n- Release master (#656)\n\n### New Contributors\n* @egrieco made their first contribution\n* @dotdash made their first contribution in [#659](https://github.com/skim-rs/skim/pull/659)\n\n## [0.15.5] - 2024-12-04\n\n### 🐛 Bug Fixes\n\n- Revert README overwrite\n- Fix --tmux quoting (#643)\n\n### 📚 Documentation\n\n- Missing backtick in install commands (#646)\n- Add note about fuzziness of interactive examples (fixes #543)\n\n### ⚙️ Miscellaneous Tasks\n\n- Release master (#647)\n- Fix release-please config\n- Fix release config\n- Release master (#655)\n\n### New Contributors\n* @genskyff made their first contribution in [#646](https://github.com/skim-rs/skim/pull/646)\n\n## [0.15.4] - 2024-12-01\n\n### 🐛 Bug Fixes\n\n- Fix token permissions for release file\n- Clippy pedantic on lib.rs\n\n### ⚙️ Miscellaneous Tasks\n\n- Cargo fmt\n- Release master (#642)\n\n## [0.15.3] - 2024-12-01\n\n### 🐛 Bug Fixes\n\n- Fix missing var in CI\n- Clippy pedantic on main.rs\n\n### ⚙️ Miscellaneous Tasks\n\n- Remove cli feature from skim\n- Cargo fmt\n- Release master (#641)\n\n## [0.15.2] - 2024-12-01\n\n### 🐛 Bug Fixes\n\n- Do not run tests in release workflow\n- Make item module public (closes #568)\n\n### ⚙️ Miscellaneous Tasks\n\n- Release master (#640)\n\n### New Contributors\n* @skim-rs-bot[bot] made their first contribution in [#640](https://github.com/skim-rs/skim/pull/640)\n\n## [0.15.1] - 2024-12-01\n\n### 🐛 Bug Fixes\n\n- Fix ci\n- Fix urls in cargo.toml\n\n### ⚙️ Miscellaneous Tasks\n\n- Generate files in PR (#638)\n- Fix push\n- Test push with explicit ref\n- Use cache for xtask\n- Simplify release ci\n- Use PAT for release-please to trigger downstream ci\n- Use gh app for token\n- Use gh app for push\n- Manually use gh app for push\n- Skip ci on modified files\n- Use token in checkout\n- Exit success when nothing to commit\n- Avoid duplicate test runs\n- Cleanup\n- Release master (#639)\n\n## [0.15.0] - 2024-12-01\n\n### 🚀 Features\n\n- *(tui)* Add info hidden (#630)\n\n### 🐛 Bug Fixes\n\n- *(ci)* Fix clippy os\n- *(ci)* Set release-please path\n- Undo sk-tmux deprecation\n- *(ci)* Release-please permissions on job level\n- *(ci)* Use subpath for release-please outputs\n- *(ci)* Remove needs in release-please condition\n- *(ci)* Use different syntax for conditions\n- *(ci)* Add intermediary step for release\n- *(ci)* Use release-please in workspace root\n- *(ci)* Test with different release-please config\n- *(ci)* Set skim version\n- *(ci)* Set skim changelog path\n- *(ci)* Use absolute path for changelog\n- *(ci)* Do not bump major\n- *(ci)* Bump minor for feat\n- *(ci)* Use correct tag\n- *(ci)* Remove string from cond\n- *(ci)* Fix templating\n- *(ci)* Fix extra dot\n- *(ci)* Use stable toolchain\n- *(ci)* Remove extra modules\n- *(ci)* Skip extra packages\n- *(ci)* Replace underscore with dashes\n- Set toolchain\n\n### 🧪 Testing\n\n- Migrate e2e to rust (#629)\n- *(ci)* Try downgrading cargo.toml\n- *(ci)* Test with crate root\n- *(ci)* Test with subpath\n- *(ci)* Add debug\n- *(ci)* Fix dash in test\n- *(ci)* Check for string\n\n### ⚙️ Miscellaneous Tasks\n\n- Readd crate to release-please\n- Fix release-please target branch\n- Fix condition\n- Release master (#632)\n- Release master (#633)\n- Cleanup failed releases\n- Release master (#634)\n- Release master (#635)\n- Release master (#636)\n- Release master (#637)\n\n### New Contributors\n* @github-actions[bot] made their first contribution in [#637](https://github.com/skim-rs/skim/pull/637)\n\n## [0.14.3] - 2024-11-28\n\n### 🚀 Features\n\n- Readd index tiebreak (#609)\n- [**breaking**] Do not check for expect before printing the argument of accept… (#625)\n- Add `--tmux` flag (deprecates sk-tmux, fixes #596) (#603)\n\n### 🐛 Bug Fixes\n\n- Allow combined multiple args (fixes #622) (#623)\n\n### 📚 Documentation\n\n- Update changelog from github releases (#620)\n- Link all PRs, issues, commits and authors in CHANGELOG (#621)\n- Add fzf-lua and nu_plugin_skim to the README (#626)\n\n### ⚙️ Miscellaneous Tasks\n\n- Bump unicode-width from 0.1.14 to 0.2.0 (#616)\n- Bump nix from 0.25.1 to 0.29.0 (#614)\n- Bump env_logger from 0.9.3 to 0.11.5 (#615)\n- Improve PR ci (#617)\n- Remove ci dir (#627)\n\n### New Contributors\n* @khafatech made their first contribution in [#605](https://github.com/skim-rs/skim/pull/605)\n* @praveenperera made their first contribution in [#621](https://github.com/skim-rs/skim/pull/621)\n\n## [0.13.0] - 2024-11-25\n\n### 🚀 Features\n\n- Allow more flexibility for use as a library (#613)\n\n### ⚙️ Miscellaneous Tasks\n\n- Add pull request template (#608)\n\n## [0.12.0] - 2024-11-24\n\n### 🚀 Features\n\n- Add reload action (#604)\n\n## [0.11.12] - 2024-11-24\n\n### 🐛 Bug Fixes\n\n- Remove index tiebreak from shell bindings (#611)\n\n### ⚙️ Miscellaneous Tasks\n\n- Remove some platform-specific quirkinesses from e2e (#602)\n\n### New Contributors\n* @crodjer made their first contribution in [#413](https://github.com/skim-rs/skim/pull/413)\n\n## [0.11.11] - 2024-11-22\n\n### 💼 Other\n\n- Readd version arg (#606)\n\n## [0.11.1] - 2024-11-21\n\n### 🐛 Bug Fixes\n\n- Fix github publish action\n\n## [0.11.0] - 2024-11-20\n\n### 🚀 Features\n\n- Use clap & derive for options, manpage & completions (#586)\n\n### 💼 Other\n\n- \"Package Managers\": add Portage\n- Remove unuseful entries (#382)\n\n### 📚 Documentation\n\n- *(discord)* Discord invitation link\n\n### ⚙️ Miscellaneous Tasks\n\n- Fix clippy\n- Remove atty (#587)\n- Remove bitflags (#579)\n\n### New Contributors\n* @LoricAndre made their first contribution in [#586](https://github.com/skim-rs/skim/pull/586)\n* @otto-dev made their first contribution in [#468](https://github.com/skim-rs/skim/pull/468)\n* @jgarte made their first contribution in [#487](https://github.com/skim-rs/skim/pull/487)\n* @iamb4uc made their first contribution in [#560](https://github.com/skim-rs/skim/pull/560)\n* @hellux made their first contribution in [#563](https://github.com/skim-rs/skim/pull/563)\n* @reneegyllensvaan made their first contribution in [#461](https://github.com/skim-rs/skim/pull/461)\n* @jirutka made their first contribution in [#449](https://github.com/skim-rs/skim/pull/449)\n* @rspencer01 made their first contribution in [#433](https://github.com/skim-rs/skim/pull/433)\n* @marcoieni made their first contribution in [#382](https://github.com/skim-rs/skim/pull/382)\n* @ymnejmi made their first contribution in [#551](https://github.com/skim-rs/skim/pull/551)\n* @sisrfeng made their first contribution\n* @vitaly-zdanevich made their first contribution\n\n## [0.10.2] - 2022-11-08\n\n### 🐛 Bug Fixes\n\n- Print version from Cargo.toml with latest clap\n\n### New Contributors\n* @anthraxx made their first contribution\n\n## [0.10.0] - 2022-10-28\n\n### ⚙️ Miscellaneous Tasks\n\n- Update deps and fix lots of clippy lints\n\n### New Contributors\n* @yazgoo made their first contribution in [#472](https://github.com/skim-rs/skim/pull/472)\n* @EdenEast made their first contribution\n* @grant0417 made their first contribution\n* @mgttlinger made their first contribution\n* @TD-Sky made their first contribution\n* @dependabot[bot] made their first contribution\n* @io12 made their first contribution\n* @terror made their first contribution\n* @PCouaillier made their first contribution\n* @sweenu made their first contribution\n\n## [0.9.4] - 2021-02-15\n\n### 💼 Other\n\n- Update\n\n### ⚙️ Miscellaneous Tasks\n\n- *(cargo)* Fix documentation link\n\n### New Contributors\n* @x4121 made their first contribution\n* @Mephistophiles made their first contribution\n* @n8henrie made their first contribution\n* @marcusbuffett made their first contribution\n* @mb720 made their first contribution\n* @pickfire made their first contribution\n* @sirwindfield made their first contribution\n\n## [0.9.3] - 2020-11-02\n\n### 🐛 Bug Fixes\n\n- Ansi parse error for multi-bytes string\n\n## [0.9.1] - 2020-10-20\n\n### 🚀 Features\n\n- Support initial scroll for preview window\n\n### 🐛 Bug Fixes\n\n- Ansi merge fragments (typo)\n- Tiebreak should contains score by default\n- Reduce flickering of preview window\n- Multiple preview options won't merge\n- Clippy\n- Pre-select-items select '' by default\n- Preview's scroll could be 0\n\n## [0.9.0] - 2020-10-18\n\n### 🚀 Features\n\n- Unicode spinner\n- Implement `--keep-right`\n- Support skip-to-pattern\n\n### 🐛 Bug Fixes\n\n- Orderedvec won't preserve insertion order\n- Upgrade fuzzy-matcher to fix wrong matching indices\n- Ensure the matching range is within bound\n- Some options are broken (introduced by 08bc067)\n- Do no auto scroll for customized items\n- Multiple selection (regression in 1d72fca)\n\n### 💼 Other\n\n- Ansi color were not shown for DefaultSkimItem\n\n### 🚜 Refactor\n\n- Demangle lib and bin implementations\n- Separate MatchResult from MatchedItem\n\n### New Contributors\n* @pkubik made their first contribution\n* @wucke13 made their first contribution\n\n## [0.8.2] - 2020-06-26\n\n### 🐛 Bug Fixes\n\n- Preview's fields should based on orig text\n\n### 💼 Other\n\n- Move filter function to binary\n- Exit gracefully on SIGPIPE error(see PR#279)\n- Handle print0 parameters correctly in filter mode\n\n### 🚜 Refactor\n\n- DefaultSkimItem now accept string\n\n### New Contributors\n* @marsam made their first contribution\n* @caixiangyue made their first contribution\n* @emmanueltouzery made their first contribution\n* @BlindingDark made their first contribution\n* @aldhsu made their first contribution\n\n## [0.8.0] - 2020-02-23\n\n### 🚀 Features\n\n- Support left click event on selection list\n\n### 🐛 Bug Fixes\n\n- Ensure screen is rendered with item\n\n### 💼 Other\n\n- \"enter\" key not printed with expect keys\n- Support case insensitive in exact mode\n- Case insensitive + refactor engine\n\n## [0.7.0] - 2020-01-15\n\n### 💼 Other\n\n- *(src/ansi.rs)* Use pattern match to destruct Option wrapper.\n\n### 📚 Documentation\n\n- Add installation instructions for arch linux\n\n### ⚙️ Miscellaneous Tasks\n\n- Update derive_builder to 0.9\n\n### New Contributors\n* @ammgws made their first contribution\n* @alexreg made their first contribution\n* @cireu made their first contribution\n\n## [0.6.7] - 2019-05-31\n\n### 💼 Other\n\n- Use as a library: remove extraneous line in example code.\n- Remove extraneous line.\n- Remove extraneous line.\n- Add crates.io svg.\n\n### New Contributors\n* @chmp made their first contribution\n* @ngirard made their first contribution\n\n## [0.6.5] - 2019-04-01\n\n### 🐛 Bug Fixes\n\n- Wrong matches on empty lines\n\n## [0.6.3] - 2019-03-25\n\n### 🐛 Bug Fixes\n\n- Number of matched items not show correctly\n- Matcher is slow to kill\n\n## [0.6.2] - 2019-03-19\n\n### 🚀 Features\n\n- Header-lines\n\n### 🐛 Bug Fixes\n\n- Compilation error of examples\n\n## [0.6.0] - 2019-03-17\n\n### 💼 Other\n\n- Rotate mode\n\n## [0.5.3] - 2019-02-20\n\n### 💼 Other\n\n- Create new variable for lines used by skim\n- Update usage string.\n- Return slice instead of new vector\n- Draw status after query\n- Return early if possible\n\n### New Contributors\n* @dfreese made their first contribution\n* @lilydjwg made their first contribution\n* @RemiliaForever made their first contribution\n* @bennyyip made their first contribution\n* @Konfekt made their first contribution\n* @Lompik made their first contribution\n* @light4 made their first contribution\n\n## [0.3.0] - 2017-09-21\n\n### 🐛 Bug Fixes\n\n- Main window did not earse correctly\n- Some lines now shown if too long\n- Skim cannot show empty lines\n- Alternate screen is not switched off on exit\n- Ansi color not shown correctly in main area\n- Toggle will panic if there is no item matched\n\n### New Contributors\n* @tiziano88 made their first contribution\n* @supermarin made their first contribution\n\n## [0.2.1-beta.2] - 2017-01-19\n\n### 🚜 Refactor\n\n- Use filter_map instead of map then filter\n\n### New Contributors\n* @anchepiece made their first contribution\n* @brookst made their first contribution\n* @SirVer made their first contribution\n* @akiradeveloper made their first contribution\n\n## [0.2.0] - 2017-01-03\n\n### 🐛 Bug Fixes\n\n- Model will not redraw from the 1 line\n- Reader: reader and sender will lock each other.\n\n### New Contributors\n* @leoyvens made their first contribution\n* @mohamedhayibor made their first contribution\n\n## [0.1.1-rc2] - 2016-07-19\n\n### 🐛 Bug Fixes\n\n- #4 exit with non-zero status on cancel.\n- Fields result in incorrect output with ANSI enabled.\n\n### 💼 Other\n\n- Remove debug code\n\n## [0.1-alpha] - 2016-07-01\n\n### New Contributors\n* @lotabout made their first contribution\n* @ made their first contribution\n\n<!-- generated by git-cliff -->\n"
  },
  {
    "path": "Cargo.toml",
    "content": "[package]\nname = \"skim\"\nversion = \"4.0.0\"\nauthors = [\"Loric ANDRE\", \"Zhang Jinzhou <lotabout@gmail.com>\"]\ndescription = \"Fuzzy Finder in rust!\"\ndocumentation = \"https://docs.rs/skim\"\nhomepage = \"https://github.com/skim-rs/skim\"\nrepository = \"https://github.com/skim-rs/skim\"\nreadme = \"README.md\"\nkeywords = [\"fuzzy\", \"menu\", \"util\"]\nlicense = \"MIT\"\nedition = \"2024\"\n\n[profile.release]\nlto = true\ncodegen-units = 1\nopt-level = 2\nstrip = true\n\n# Kept for compatibility\n[profile.dist]\ninherits = \"release\"\n\n[profile.release-debug]\ninherits = \"release\"\ndebug = true\nstrip = false\n\n\n[lib]\nname = \"skim\"\npath = \"src/lib.rs\"\n\n[[bin]]\nname = \"sk\"\npath = \"src/bin/main.rs\"\nrequired-features = [\"cli\"]\n\n[dependencies]\nassert_enum_variants = \"=0.1.2\"\nclap = { version = \"=4.6.0\" , optional = true, features = [\"cargo\", \"derive\", \"unstable-markdown\"] }\nclap_complete = { version = \"=4.6.0\", optional = true }\nclap_mangen = { version = \"=0.2.33\", optional = true }\nderive_builder = \"=0.20.2\"\nenv_logger = { version = \"=0.11.9\", optional = true, features = [\"humantime\"] }\nindexmap = \"=2.13.0\"\nlog = \"=0.4.29\"\nnix = { version = \"=0.31.2\", features = [\"fs\", \"poll\"] }\nrand = \"=0.10.0\"\nrayon = \"=1.11.0\"\nregex = \"=1.12.3\"\nshell-quote = \"=0.7.2\"\nshlex = { version = \"=1.3.0\", optional = true }\ntokio = { version = \"=1.50.0\", features = [\"macros\", \"net\", \"rt-multi-thread\", \"sync\", \"time\", \"tokio-macros\"] }\nwhich = \"=8.0.2\"\nratatui = \"=0.30.0\"\ncolor-eyre = \"=0.6.5\"\nansi-to-tui = \"=8.0.1\"\nfutures = \"=0.3.32\"\ntokio-util = \"=0.7.18\"\nthiserror = \"=2.0.18\"\ntempfile = \"=3.27.0\"\ncrossterm = { version = \">=0.0.0\", features = [\"event-stream\", \"use-dev-tty\", \"libc\"] }\nthread_local = \"=1.1.9\"\nmemchr = \"=2.8.0\"\nclap_complete_nushell = \"=4.5.10\"\ninterprocess = { version = \"=2.4.0\", features = [\"tokio\"] }\nserde = { version = \"=1.0.228\", features = [\"derive\"] }\nron = \"=0.12.0\"\nfrizbee = { version = \"=0.8.3\" }\nroff = \"=1.0.0\"\nunicode-display-width = \"=0.3.0\"\nunicode-normalization = \"=0.1.25\"\nderive_more = { version = \"=2.1.1\", features = [\"debug\", \"eq\"] }\nportable-pty = \"=0.9.0\"\ntui-term = \"=0.3.2\"\nkanal = \"=0.1.1\"\nmimalloc = { version = \"0.1.48\", features = [\"v3\"] }\ngungraun = { version = \"0.17.2\", optional = true }\n\n[features]\ndefault = [\"cli\"]\n# Everyting needed to use skim as a cli (argument parsing, shell integrations...)\ncli = [\"dep:clap\", \"dep:clap_complete\", \"dep:shlex\", \"dep:env_logger\", \"dep:clap_mangen\"]\n# Enable test utilities (e.g., Tui::new_for_test)\ntest-utils = []\n# Enable gungraun (Valgrind-based) benchmarks\ngungraun = [\"dep:gungraun\"]\n\n[dev-dependencies]\ncriterion = { version = \"0.8.2\", features = [\"async_tokio\"] }\ninsta = \"1.46\"\n\n[lints.rust]\nunexpected_cfgs = { level = \"warn\", check-cfg = ['cfg(feature, values(\"test-utils\", \"gungraun\"))', 'cfg(coverage, coverage_nightly)'] }\n\n[[bench]]\nname = \"read_and_match\"\nharness = false\n\n[[bench]]\nname = \"filter\"\nharness = false\n\n[[bench]]\nname = \"partial\"\nharness = false\n\n[[bench]]\nname = \"matcher_micro\"\nharness = false\n\n[[bench]]\nname = \"gungraun\"\nharness = false\nrequired-features = [\"gungraun\"]\n"
  },
  {
    "path": "LICENSE",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2016 Jinzhou Zhang\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 all\ncopies 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 THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "<p align=\"center\">\n  <a href=\"https://crates.io/crates/skim\">\n    <img src=\"https://img.shields.io/crates/v/skim.svg\" alt=\"Crates.io\" />\n  </a>\n  <a href=\"https://github.com/skim-rs/skim/actions?query=workflow%3A%22Build+%26+Test%22+event%3Apush\">\n    <img src=\"https://github.com/skim-rs/skim/actions/workflows/test.yml/badge.svg?event=push\" alt=\"Build & Test\" />\n  </a>\n  <a href=\"https://codecov.io/gh/skim-rs/skim\" >\n    <img src=\"https://codecov.io/gh/skim-rs/skim/graph/badge.svg?token=RtIDgmDdzX\" alt=\"codecov badge\" />\n  </a>\n  <a href=\"https://repology.org/project/skim-fuzzy-finder/versions\">\n    <img src=\"https://repology.org/badge/tiny-repos/skim-fuzzy-finder.svg\" alt=\"Packaging status\" />\n  </a>\n  <a href=\"https://discord.gg/23PuxttufP\">\n    <img alt=\"Skim Discord\" src=\"https://img.shields.io/discord/1031830957432504361?label=&color=7389d8&labelColor=6a7ec2&logoColor=ffffff&logo=discord\" />\n  </a>\n  <a href=\"https://matrix.to/#/#skim:matrix.org\">\n    <img alt=\"Skim Matrix room\" src=\"https://img.shields.io/badge/matrix-%23000000?style=flat&logo=matrix&logoColor=white\" />\n  </a>\n  <a href=\"https://ratatui.rs\">\n    <img alt=\"Built with Ratatui\" src=\"https://ratatui.rs/built-with-ratatui/badge.svg\" />\n  </a>\n</p>\n\n> Life is short, skim!\n\nWe spend so much of our time navigating through files, lines, and commands. That's where Skim comes in!\nIt's a powerful fuzzy finder designed to make your workflow faster and more efficient.\n\n[![skim demo](https://asciinema.org/a/pIfwazaM0mTHA8F7qRbjrqOnm.svg)](https://asciinema.org/a/pIfwazaM0mTHA8F7qRbjrqOnm)\n\nSkim provides a single executable called `sk`. Think of it as a smarter alternative to tools like\n`grep` - once you try it, you'll wonder how you ever lived without it!\n\n# Table of contents\n\n- [Installation](#installation)\n   * [Package Managers](#package-managers)\n   * [Manually](#manually)\n- [Usage](#usage)\n   * [As Vim plugin](#as-vim-plugin)\n   * [As filter](#as-filter)\n   * [As Interactive Interface](#as-interactive-interface)\n   * [Shell Bindings](#shell-bindings)\n   * [Key Bindings](#key-bindings)\n   * [Search Syntax](#search-syntax)\n   * [exit code](#exit-code)\n- [Tools compatible with `skim`](#tools-compatible-with-skim)\n   * [fzf-lua neovim plugin](#fzf-lua-neovim-plugin)\n   * [nu_plugin_skim](#nu_plugin_skim)\n- [Customization](#customization)\n   * [Keymap](#keymap)\n   * [Sort Criteria](#sort-criteria)\n   * [Color Scheme](#color-scheme)\n   * [Misc](#misc)\n- [Advanced Topics](#advanced-topics)\n   * [Interactive mode](#interactive-mode)\n      + [How does it work?](#how-does-it-work)\n   * [Executing external programs](#executing-external-programs)\n   * [Algorithms](#algorithms)\n   * [Preview Window](#preview-window)\n      + [How does it work?](#how-does-it-work-1)\n   * [Fields support](#fields-support)\n   * [Use as a library](#use-as-a-library)\n   * [Benchmarks](#benchmarks)\n- [FAQ](#faq)\n   * [How to ignore files?](#how-to-ignore-files)\n   * [Some files are not shown in Vim plugin](#some-files-are-not-shown-in-vim-plugin)\n- [Differences from fzf](#differences-from-fzf)\n- [How to contribute](#how-to-contribute)\n- [Troubleshooting](#troubleshooting)\n   * [No line feed issues with nix, FreeBSD, termux](#no-line-feed-issues-with-nix-freebsd-termux)\n\n# Installation\n\nThe skim project contains several components:\n\n1. `sk` executable - the core program\n2. Vim/Nvim plugin - to call `sk` inside Vim/Nvim. Check [skim.vim](https://github.com/skim-rs/skim/blob/master/plugin/skim.vim) for Vim support.\n\n## Package Managers\n\n| OS             | Package Manager   | Command                      |\n| -------------- | ----------------- | ---------------------------- |\n| macOS          | Homebrew          | `brew install sk`            |\n| macOS          | MacPorts          | `sudo port install skim`     |\n| Alpine         | apk               | `apk add skim`               |\n| Arch           | pacman            | `pacman -S skim`             |\n| Fedora         | COPR              |   see below                  |\n| Gentoo         | Portage           | `emerge --ask app-misc/skim` |\n| Guix           | guix              | `guix install skim`          |\n| Void           | XBPS              | `xbps-install -S skim`       |\n\n<a href=\"https://repology.org/project/skim-fuzzy-finder/versions\">\n    <img src=\"https://repology.org/badge/vertical-allrepos/skim-fuzzy-finder.svg?columns=4\" alt=\"Packaging status\">\n</a>\n\n### Fedora\nUp to date Fedora packages are provided via an unofficial community-maintained COPR repository.\n```bash\nsudo dnf copr enable sisyphus1813/skim\nsudo dnf install skim\n```\n\n\n## Manually\n\nAny of the following applies:\n\n- Using the install script:\n    ```sh\n    # Always check the content of the script before running it !\n    $ curl --proto '=https' --tlsv1.2 -LsSf https://github.com/skim-rs/skim/releases/latest/download/skim-installer.sh | sh\n    ```\n- Using Binary: Simply [download the sk executable](https://github.com/skim-rs/skim/releases) directly.\n- Install from [crates.io](https://crates.io/): `cargo install skim`\n- Build Manually:\n    ```sh\n    $ git clone --depth 1 git@github.com:skim-rs/skim.git ~/.skim\n    $ cd ~/.skim\n    $ cargo build --release\n    $ # Add the resulting `target/release/sk` executable to your PATH\n    ```\n\nYou will then have access to:\n\n- The man page, which you can either write to the correct path or run `man --local-file <(sk --man)`\n- The shell completions (and optional keybinds), using `source <(sk --shell \\<shell> \\[--shell-bindings])`, see below for details\n\n# Usage\n\nSkim can be used either as a general filter (similar to `grep`) or as an interactive\ninterface for running commands.\n\n## As Vim plugin (on neovim, checkout [fzf-lua](https://github.com/ibhagwan/fzf-lua) with the skim profile)\n\nVia vim-plug (recommended):\n\nInstall skim, then :\n\n```vim\nPlug 'skim-rs/skim'\n```\n\n\n## As filter\n\nHere are some examples to get you started:\n\n```bash\n# directly invoke skim\nsk\n\n# Or pipe some input to it (press TAB key to select multiple items when -m is enabled)\nvim $(find . -name \"*.rs\" | sk -m)\n```\nThis last command lets you select files with the \".rs\" extension and opens\nyour selections in Vim - a great time-saver for developers!\n\n## As Interactive Interface\n\n`skim` can invoke other commands dynamically. Normally you would want to\nintegrate it with [grep](https://www.gnu.org/software/grep/),\n[ack](https://github.com/petdance/ack2),\n[ag](https://github.com/ggreer/the_silver_searcher), or\n[rg](https://github.com/BurntSushi/ripgrep) for searching contents in a\nproject directory:\n\n```sh\n# works with grep\nsk --ansi -i -c 'grep -rI --color=always --line-number {q} .'\n# works with ack\nsk --ansi -i -c 'ack --color {q}'\n# works with ag\nsk --ansi -i -c 'ag --color {q}'\n# works with rg\nsk --ansi -i -c 'rg --color=always --line-number {q}'\n```\n\n> **Note**: In these examples, `{q}` will be literally expanded to the current input query (wrapped in single quotes).\n> This means these examples will search for the exact query string, not fuzzily.\n> For fuzzy searching, pipe the command output into `sk` without using interactive mode.\n\n![interactive mode demo](https://cloud.githubusercontent.com/assets/1527040/21603930/655d859a-d1db-11e6-9fec-c25099d30a12.gif)\n\n## Shell Bindings\n\nBindings for Fish, Bash and Zsh are available in the `shell` directory:\n- `completion.{shell}` contains the completion scripts for `sk` cli usage\n- `key-bindings.{shell}` contains key-binds and shell integrations:\n    - `ctrl-t` to select a file through `sk`\n    - `ctrl-r` to select an history entry through `sk`\n    - `alt-c`  to `cd` into a directory selected through `sk`\n    - (not available in `fish`) `**` to complete file paths, for example `ls **<tab>` will show a `sk` widget to select a folder\n\nTo enable these features, source the `key-bindings.{shell}` file and set up completions according to your shell's documentation or see below.\n\n### Shell Completions\n\nYou can generate shell completions for your preferred shell using the `--shell` flag with one of the supported shells: `bash`, `zsh`, `fish`, `powershell`, or `elvish`:\n\n> **Note:** While PowerShell completions are supported, Windows is not supported for now.\n\n#### Option 1: Source directly in your current shell session\n\n```sh\n# For bash\nsource <(sk --shell bash)\n\n# For zsh\nsource <(sk --shell zsh)\n\n# For fish\nsk --shell fish | source\n```\n\n#### Option 2: Save to a file to be loaded automatically on shell startup\n\n```sh\n# For bash, add to ~/.bashrc\necho 'source <(sk --shell bash)' >> ~/.bashrc  # Or save to ~/.bash_completion\n\n# For zsh, add to ~/.zshrc\nsk --shell zsh > ~/.zfunc/_sk  # Create ~/.zfunc directory and add to fpath in ~/.zshrc\n\n# For fish, add to ~/.config/fish/completions/\nsk --shell fish > ~/.config/fish/completions/sk.fish\n```\n\n## Key Bindings\n\nSome commonly used key bindings:\n\n| Key               | Action                                     |\n|------------------:|--------------------------------------------|\n| Enter             | Accept (select current one and quit)       |\n| ESC/Ctrl-G        | Abort                                      |\n| Ctrl-P/Up         | Move cursor up                             |\n| Ctrl-N/Down       | Move cursor Down                           |\n| TAB               | Toggle selection and move down (with `-m`) |\n| Shift-TAB         | Toggle selection and move up (with `-m`)   |\n\nFor a complete list of key bindings, refer to the [man\npage](https://github.com/skim-rs/skim/blob/master/man/man1/sk.1) (`man sk`).\n\n## Search Syntax\n\n`skim` borrows `fzf`'s syntax for matching items:\n\n| Token    | Match type                 | Description                       |\n|----------|----------------------------|-----------------------------------|\n| `text`   | fuzzy-match                | items that match `text`           |\n| `^music` | prefix-exact-match         | items that start with `music`     |\n| `.mp3$`  | suffix-exact-match         | items that end with `.mp3`        |\n| `'wild`  | exact-match (quoted)       | items that include `wild`         |\n| `!fire`  | inverse-exact-match        | items that do not include `fire`  |\n| `!.mp3$` | inverse-suffix-exact-match | items that do not end with `.mp3` |\n\n`skim` also supports the combination of tokens.\n\n- Whitespace has the meaning of `AND`. With the term `src main`, `skim` will search\n    for items that match **both** `src` and `main`.\n- ` | ` means `OR` (note the spaces around `|`). With the term `.md$ |\n    .markdown$`, `skim` will search for items ends with either `.md` or\n    `.markdown`.\n- `OR` has higher precedence. For example, `readme .md$ | .markdown$` is interpreted as\n    `readme AND (.md$ OR .markdown$)`.\n\n- When using the `--split-match` option, each part around spaces or `|` will be matched in a split way:\n    - If the option's value (defaulting to `:`) is absent from the query, do a normal match\n    - If it is present, match everything before to everything before it in the items, and everything after it (including potential other occurrences of the delimiter) to the part after it in the items. This is particularly useful when piping in input from `rg` to match on both file name and content.\n\nIf you prefer using regular expressions, `skim` offers a `regex` mode:\n\n```sh\nsk --regex\n```\n\nYou can switch to `regex` mode dynamically by pressing `Ctrl-R` (Rotate Mode).\n\n## exit code\n\n| Exit Code | Meaning                             |\n|-----------|-------------------------------------|\n| 0         | Exited normally                     |\n| 1         | No Match found                      |\n| 130       | Aborted by Ctrl-C/Ctrl-G/ESC/etc... |\n\n# Tools compatible with `skim`\n\nThese tools are or aim to be compatible with `skim`:\n\n## [fzf-lua neovim plugin](https://github.com/ibhagwan/fzf-lua)\n\nA [neovim](https://neovim.io) plugin allowing fzf and skim to be used in a to navigate your code.\n\nInstall it with your package manager, following the README. For instance, with `lazy.nvim`:\n\n```lua\n{\n  \"ibhagwan/fzf-lua\",\n  -- enable `sk` support instead of the default `fzf`\n  opts = {'skim'}\n}\n```\n\n## [nu_plugin_skim](https://github.com/idanarye/nu_plugin_skim)\n\nA [nushell](https://www.nushell.sh/) plugin to allow for better interaction between skim and nushell.\n\nFollowing the instruction in the plugin's README, you can install it with cargo:\n```nu\ncargo install nu_plugin_skim\nplugin add ~/.cargo/bin/nu_plugin_skim\n```\n\n## [sqlite extension](https://github.com/tzachar/sqlite_skim)\n\nAn `sqlite` loadable module which enables a `skim_score` function in SQL\nqueries.\n\n# Customization\n\nThe doc here is only a preview, please check the man page (`man sk`) for a full\nlist of options.\n\n## Keymap\n\nSpecify the bindings with comma separated pairs (no space allowed). For example:\n\n```sh\nsk --bind 'alt-a:select-all,alt-d:deselect-all'\n```\n\nAdditionally, use `+` to concatenate actions, such as `execute-silent(echo {} | pbcopy)+abort`.\n\nSee the _KEY BINDINGS_ section of the man page for details.\n\n## Sort Criteria\n\nThere are five sort keys for results: `score, index, begin, end, length`. You can\nspecify how the records are sorted by `sk --tiebreak score,index,-begin` or any\nother order you want.\n\n## Color Scheme\n\nYou probably have your own aesthetic preferences! Fortunately, you aren't\nlimited to the default appearance - Skim supports comprehensive customization of its color scheme.\n\n```sh\n--color=[BASE_SCHEME][,COLOR:ANSI]\n```\n\nSkim also respects the `NO_COLOR` environment variable. Set it to anything and `sk` (and many other terminal apps) will disable all colored output. See [no-color.org](https://no-color.org/) for more details.\n\n### Available Base Color Schemes\n\nSkim comes with several built-in color schemes that you can use as a starting point:\n\n```sh\nsk --color=dark      # Default dark theme (256 colors)\nsk --color=light     # Light theme (256 colors)\nsk --color=16        # Simple 16-color theme\nsk --color=bw        # Minimal black & white theme (no colors, just styles)\nsk --color=none      # Minimal black & white theme (no colors, no styles)\nsk --color=molokai   # Molokai-inspired theme (256 colors)\n```\n\n### Customizing Colors\n\nYou can customize individual UI elements by specifying color values after the base scheme:\n\n```sh\nsk --color=light,fg:232,bg:255,current_bg:116,info:27\n```\n\nColors can be specified in several ways:\n\n- ANSI colors (0-255): `sk --color=fg:232,bg:255`\n- RGB hex values: `sk --color=fg:#FF0000` (red text)\n\n### Available Color Customization Options\n\nThe following UI elements can be customized:\n\n| Element            | Description                                 | Example                        |\n|--------------------|---------------------------------------------|--------------------------------|\n| `fg`               | Normal text foreground color                | `--color=fg:232`               |\n| `bg`               | Normal text background color                | `--color=bg:255`               |\n| `matched`          | Matched text in search results              | `--color=matched:108`          |\n| `matched_bg`       | Background of matched text                  | `--color=matched_bg:0`         |\n| `current`          | Current line foreground color               | `--color=current:254`          |\n| `current_bg`       | Current line background color               | `--color=current_bg:236`       |\n| `current_match`    | Matched text in current line                | `--color=current_match:151`    |\n| `current_match_bg` | Background of matched text in current line  | `--color=current_match_bg:236` |\n| `spinner`          | Progress indicator color                    | `--color=spinner:148`          |\n| `info`             | Information line color                      | `--color=info:144`             |\n| `prompt`           | Prompt color                                | `--color=prompt:110`           |\n| `cursor`           | Cursor color                                | `--color=cursor:161`           |\n| `selected`         | Selected item marker color                  | `--color=selected:168`         |\n| `header`           | Header text color                           | `--color=header:109`           |\n| `border`           | Border color for preview/layout             | `--color=border:59`            |\n\n### Examples\n\n```sh\n# Use light theme but change the current line background\nsk --color=light,current_bg:24\n\n# Custom theme with multiple colors\nsk --color=dark,matched:#00FF00,current:#FFFFFF,current_bg:#000080\n\n# High contrast theme\nsk --color=fg:232,bg:255,matched:160,current:255,current_bg:20\n```\n\nFor more details, check the man page (`man sk`).\n\n## Misc\n\n- `--ansi`: to parse ANSI color codes (e.g., `\\e[32mABC`) of the data source\n- `--regex`: use the query as regular expression to match the data source\n\n# Advanced Topics\n\n## Interactive mode\n\nIn **interactive mode**, you can invoke a command dynamically. Try it out:\n\n```sh\nsk --ansi -i -c 'rg --color=always --line-number {q}'\n```\n\n### How does it work?\n\n![How Skim's interactive mode works](https://user-images.githubusercontent.com/1527040/53381293-461ce380-39ab-11e9-8e86-7c3bbfd557bc.png)\n\n- Skim  accepts two kinds of sources: Command output or piped input\n- Skim has two kinds of prompts: A query prompt to specify the query pattern and a\n    command prompt to specify the \"arguments\" of the command\n- `-c` is used to specify the command to execute and defaults to `SKIM_DEFAULT_COMMAND`\n- `-i` tells skim to open command prompt on startup, which will show `c>` by default.\n\nTo further narrow down the results returned by the command, press\n`Ctrl-Q` to toggle interactive mode.\n\n## Executing external programs\n\nYou can configure key bindings to start external processes without leaving Skim (`execute`, `execute-silent`).\n\n```sh\n# Press F1 to open the file with less without leaving skim\n# Press CTRL-Y to copy the line to clipboard and aborts skim (requires pbcopy)\nsk --bind 'f1:execute(less -f {}),ctrl-y:execute-silent(echo {} | pbcopy)+abort'\n```\n\n## Algorithms\n\nSkim offers multiple algorithms, check the help or manpage for an exhaustive list. Among them are:\n- `skim_v2`, the default algorithm, loosely based on `fzf`'s algorithm\n- `frizbee`([crate](https://crates.io/frizbee), the typo-resistant algorithm used in the [blink.cmp](https://github.com/saghen/blink.cmp) neovim plugin\n- `fzy`, based on [fzy](https://github.com/jhawthorn/fzy/)'s algorithm expanded for basic typo-resistance\n- `arinae`, skim's newest algorithm, designed in-house with typo-resistance in mind, expanding on all the above to make typo-resistant matching feel more natural while keeping the per-item performance up to the best standards\n\n## Preview Window\n\nThis is a great feature of fzf that skim borrows. For example, we use 'ag' to\nfind the matched lines, and once we narrow down to the target lines, we want to\nfinally decide which lines to pick by checking the context around the line.\n`grep` and `ag` have the option `--context`, and skim can make use of `--context` for\na better preview window. For example:\n\n```sh\nsk --ansi -i -c 'ag --color {q}' --preview \"preview.sh {}\"\n```\n\n(Note that [preview.sh](https://github.com/junegunn/fzf.vim/blob/master/bin/preview.sh) is a script to print the context given filename:lines:columns)\n\nYou get things like this:\n\n![preview demo](https://user-images.githubusercontent.com/1527040/30677573-0cee622e-9ebf-11e7-8316-c741324ecb3a.png)\n\n### How does it work?\n\nIf the preview command is given by the `--preview` option, skim will replace the\n`{}` with the current highlighted line surrounded by single quotes, call the\ncommand to get the output, and print the output on the preview window.\n\nSometimes you don't need the whole line for invoking the command. In this case\nyou can use `{}`, `{1..}`, `{..3}` or `{1..5}` to select the fields. The\nsyntax is explained in the section [Fields Support](#filds-support).\n\nLastly, you might want to configure the position of preview window with `--preview-window`:\n- `--preview-window up:30%` to put the window in the up position with height\n    30% of the total height of skim.\n- `--preview-window left:10:wrap` to specify the `wrap` allows the preview\n    window to wrap the output of the preview command.\n- `--preview-window wrap:hidden` to hide the preview window at startup, later\n    it can be shown by the action `toggle-preview`.\n\n## Fields support\n\nNormally only plugin users need to understand this.\n\nFor example, you have the data source with the format:\n\n```sh\n<filename>:<line number>:<column number>\n```\n\nHowever, you want to search `<filename>` only when typing in queries. That\nmeans when you type `21`, you want to find a `<filename>` that contains `21`,\nbut not matching line number or column number.\n\nYou can use `sk --delimiter ':' --nth 1` to achieve this.\n\nYou can also use `--with-nth` to re-arrange the order of fields.\n\n**Range Syntax**\n\n- `<num>` -- to specify the `num`-th fields, starting with 1.\n- `start..` -- starting from the `start`-th fields and the rest.\n- `..end` -- starting from the `0`-th field, all the way to `end`-th field,\n    including `end`.\n- `start..end` -- starting from `start`-th field, all the way to `end`-th\n    field, including `end`.\n\n## Use as a library\n\nSkim can be used as a library in your Rust crates.\n\nFirst, add skim into your `Cargo.toml`:\n\n```toml\n[dependencies]\nskim = { version = \"<version>\", default-features = false, features = [..] }\n```\n\n_Note on features_:\n    - the `cli` feature is required to use skim as a cli, it *should* not be needed when using it as a library.\n\n### Basic usage\n\nThen try to run this simple example:\n\n```rust\nextern crate skim;\nuse skim::prelude::*;\nuse std::io::Cursor;\n\npub fn main() {\n    let options = SkimOptionsBuilder::default()\n        .height(\"50%\")\n        .multi(true)\n        .build()\n        .unwrap();\n\n    let input = \"aaaaa\\nbbbb\\nccc\".to_string();\n\n    // `SkimItemReader` is a helper to turn any `BufRead` into a stream of `SkimItem`\n    // `SkimItem` was implemented for `AsRef<str>` by default\n    let item_reader = SkimItemReader::default();\n    let items = item_reader.of_bufread(Cursor::new(input));\n\n    // `run_with` would read and show items from the stream\n    let selected_items = Skim::run_with(&options, Some(items))\n        .map(|out| out.selected_items)\n        .unwrap_or_else(|| Vec::new());\n\n    for item in selected_items.iter() {\n        println!(\"{}\", item.output());\n    }\n}\n```\n\n### Fine-grained usage\n\nYou can also gain fine-grained usage of skim as a library using `tokio` and async code, allowing you to dynamically interact with\n\n\n### Internal workings\n\nGiven an `Option<SkimItemReceiver>`, skim will read items accordingly, do its\njob and bring us back the user selection including the selected items, the\nquery, etc. Note that:\n\n- `SkimItemReceiver` is `crossbeam::channel::Receiver<Arc<dyn SkimItem>>`\n- If it is none, it will invoke the given command and read items from command output\n- Otherwise, it will read the items from the (crossbeam) channel.\n\nTrait `SkimItem` is provided to customize how a line could be displayed,\ncompared and previewed. It is implemented by default for `AsRef<str>`\n\nPlus, `SkimItemReader` is a helper to convert a `BufRead` into\n`SkimItemReceiver` (we can easily turn a `File` or `String` into `BufRead`),\nso that you could deal with strings or files easily.\n\nCheck out more examples under the [examples/](https://github.com/skim-rs/skim/tree/master/skim/examples) directory.\n\n## Benchmarks\n\nThis benchmarks runs the interactive interface in a tmux session, and waits for the UI to stabilize.\nIt uses a 10 million path-like ASCII items input file, and the query test.\n\n```\n=== Results: sk v4.0.0 [baseline] ===\nCompleted runs: 50 / 50\nAverage items matched: 2895782 / 10000000  (min: 2895782, max: 2895782)\nAverage time: 3.827s  (min: 3.576s, max: 4.090s)\nAverage items/second: 2615767  (min: 2445033, max: 2796365)\nAverage peak memory usage: 1589.2 MB  (min: 1518.6 MB, max: 1661.2 MB)\nAverage peak CPU usage: 528.9%  (min: 457.0%, max: 740.0%)\n\n=== Results: sk v3.7.0 ===\nCompleted runs: 50 / 50\nAverage items matched: 2895782 / 10000000  (min: 2895782, max: 2895782) +0.0%\nAverage time: 3.930s  (min: 3.565s, max: 4.226s)  +2.7%\nAverage items/second: 2548674  (min: 2366263, max: 2804816)  -2.6%\nAverage peak memory usage: 1618.8 MB  (min: 1539.1 MB, max: 1680.6 MB) +1.9%\nAverage peak CPU usage: 696.8%  (min: 608.0%, max: 875.0%)  +31.7%\n\n=== Results: fzf 0.70.0 ===\nCompleted runs: 50 / 50\nAverage items matched: 2895782 / 10000000  (min: 2895782, max: 2895782) +0.0%\nAverage time: 5.421s  (min: 4.814s, max: 6.111s)  +41.7%\nAverage items/second: 1848269  (min: 1636444, max: 2077385)  -29.3%\nAverage peak memory usage: 2015.3 MB  (min: 1860.7 MB, max: 2173.9 MB) +26.8%\nAverage peak CPU usage: 1301.1%  (min: 1229.0%, max: 1431.0%)  +146.0%\n\n=== Comparison Summary (vs baseline: sk v4.0.0) ===\nBinary      Avg time     Δ time  Avg rate     Δ rate\n------------------------------------------------------------------------------\nsk v4.0.0   3.827s    baseline   2615767     baseline\nsk v3.7.0   3.930s      +2.7%    2548674      -2.6%\nfzf 0.70.0  5.421s     +41.7%    1848269     -29.3%\n```\n\nTL;DR: sk v4.0.0 is ~30% faster than fzf 0.70.0 with a third of the CPU usage and less memory usage\n\n# FAQ\n\n## How to ignore files?\n\nSkim invokes `find .` to fetch a list of files for filtering. You can override\nthis by setting the environment variable `SKIM_DEFAULT_COMMAND`. For example:\n\n```sh\n$ SKIM_DEFAULT_COMMAND=\"fd --type f || git ls-tree -r --name-only HEAD || rg --files || find .\"\n$ sk\n```\n\nYou could put it in your `.bashrc` or `.zshrc` if you like it to be default.\n\n## Some files are not shown in Vim plugin\n\nIf you use the Vim plugin and execute the `:SK` command, you may find some\nof your files not shown.\n\nAs described in [#3](https://github.com/skim-rs/skim/issues/3), in the Vim\nplugin, `SKIM_DEFAULT_COMMAND` is set to the command by default:\n\n```vim\nlet $SKIM_DEFAULT_COMMAND = \"git ls-tree -r --name-only HEAD || rg --files || ag -l -g \\\"\\\" || find .\"\n```\n\nThis means files not recognized by git won't be shown. You can either override the\ndefault with `let $SKIM_DEFAULT_COMMAND = ''` or locate the missing files by\nyourself.\n\n# Differences from fzf\n\n[fzf](https://github.com/junegunn/fzf) is a command-line fuzzy finder written\nin Go and [skim](https://github.com/skim-rs/skim) tries to implement a new one\nin Rust!\n\nThis project is written from scratch. Some decisions of implementation are\ndifferent from fzf. For example:\n\n1. `skim` has an interactive mode.\n2. `skim` supports pre-selection.\n3. The fuzzy search algorithm is different.\n\nMore generally, `skim`'s maintainers allow themselves some freedom of implementation.\nThe goal is to keep `skim` as feature-full as `fzf` is, but the command flags might differ.\n\n# How to contribute\n\n[Create new issues](https://github.com/skim-rs/skim/issues/new) if you encounter any bugs\nor have any ideas. Pull requests are warmly welcomed.\n\n# Troubleshooting\n\nTo troubleshoot what's happening, you can set the environment variable `SKIM_LOG` or the flag `--log-level` to either `debug` or even `trace`, and set the environment variable `SKIM_LOG_FILE` or the flag `--log-file` to a path. You can then read those logs during or after the execution to better understand what's happening. Don't hesitate to add those logs to an issue if you need help.\n\n## No line feed issues with nix, FreeBSD, termux\n\nIf you encounter display issues like:\n\n```bash\n$ for n in {1..10}; do echo \"$n\"; done | sk\n  0/10 0/0.> 10/10  10  9  8  7  6  5  4  3  2> 1\n```\n\nFor example\n\n- https://github.com/skim-rs/skim/issues/412\n- https://github.com/skim-rs/skim/issues/455\n\nYou need to set TERMINFO or TERMINFO_DIRS to the path of a correct terminfo database path\n\nFor example, with termux, you can add this in your bashrc:\n\n```\nexport TERMINFO=/data/data/com.termux/files/usr/share/terminfo\n```\n\n# Benchmarks\n\n## Shell script\n\nThe `bench.py` script is available to benchmark the code against other versions or fzf using `tmux` and querying the output. This is by no means a precise or foolproof way of running benchmarks, but it has the added benefit of allowing us to benchmark against `fzf` and of giving us resource metrics.\n\nYou can use it directly using `./bench.py <binary> -n <number of items> -r <number of runs>`, or generate the data using `./bench.py -g <output file> -n <number of items>`, then `./bench.py <binary> -f <file> -r <number of runs>`\n\n### Criterion benchmarks\n\nCriterion benchmarks are available to measure skim's performance more precisely.\nTo run them, you need to generate input data using `./bench.py -g benches/fixtures/10M.txt -n 10000000 && ./bench.py -g benches/fixtures/1M.txt -n 1000000`, then run `cargo bench -j 1`.\n\nThese will run for several minutes.\n"
  },
  {
    "path": "bench.py",
    "content": "#!/usr/bin/env python3\n\"\"\"\nBenchmark script to measure ingestion + matching rate in skim interactive mode.\nThis measures how fast skim can ingest items and display matched results.\n\nUsage: bench.py [BINARY_PATH ...] [-n|--num-items NUM] [-q|--query QUERY]\n                [-r|--runs RUNS] [-w|--warmup N] [-f|--file FILE]\n                [-g|--generate-file FILE] [-j|--json] [-- EXTRA_ARGS...]\n\nArguments:\n  BINARY_PATH ...          One or more paths to binaries (default: ./target/release/sk)\n                           When multiple are given they run in round-robin and the\n                           first is used as the baseline for +/- comparisons.\n  -n, --num-items NUM      Number of items to generate (default: 1000000)\n  -q, --query QUERY        Query string to search (default: \"test\")\n  -r, --runs RUNS          Number of benchmark runs per binary (default: 1)\n  -w, --warmup N           Number of warmup runs per binary (default: 1)\n  -f, --file FILE          Use existing file as input instead of generating\n  -g, --generate-file FILE Generate test data to file and exit\n  -j, --json               Output results as JSON\n  --                       Pass remaining arguments to the binary\n\nExamples:\n  ./bench.py                                         # Use defaults\n  ./bench.py ./target/release/sk -n 500000 -q foo\n  ./bench.py ./old/sk ./new/sk -r 5                 # Compare two binaries\n  ./bench.py -r 5                                    # Run 5 times and show average\n  ./bench.py -f input.txt -q search                 # Use existing file\n  ./bench.py -g testdata.txt -n 2000000             # Generate file and exit\n\"\"\"\n\nimport json\nimport math\nimport os\nimport random\nimport subprocess\nimport sys\nimport tempfile\nimport time\n\n# ---------------------------------------------------------------------------\n# Defaults\n# ---------------------------------------------------------------------------\nDEFAULT_BINARY = \"./target/release/sk\"\nDEFAULT_NUM_ITEMS = 1_000_000\nDEFAULT_QUERY = \"test\"\nDEFAULT_RUNS = 1\nDEFAULT_WARMUP = 1\n\n# Stability / timeout tuning (mirrors bench.sh values)\nREQUIRED_STABLE_S = 5.0  # seconds the matched count must be unchanged\nMAX_WAIT_S = 60.0  # hard timeout per run\nCHECK_INTERVAL_S = 0.05  # polling interval\n\n\n# ---------------------------------------------------------------------------\n# Test-data generation\n# ---------------------------------------------------------------------------\n\nWORDS = [\n    \"home\",\n    \"usr\",\n    \"etc\",\n    \"var\",\n    \"opt\",\n    \"tmp\",\n    \"dev\",\n    \"proc\",\n    \"sys\",\n    \"lib\",\n    \"bin\",\n    \"sbin\",\n    \"boot\",\n    \"mnt\",\n    \"media\",\n    \"src\",\n    \"test\",\n    \"config\",\n    \"data\",\n    \"logs\",\n    \"cache\",\n    \"backup\",\n    \"docs\",\n    \"images\",\n    \"videos\",\n    \"audio\",\n    \"downloads\",\n    \"uploads\",\n    \"temp\",\n    \"shared\",\n]\n\n\ndef generate_test_data(output_file: str, num_items: int) -> None:\n    rng = random.Random()\n    with open(output_file, \"w\") as fh:\n        for i in range(1, num_items + 1):\n            depth = rng.randint(2, 10)\n            parts = [rng.choice(WORDS) for _ in range(depth)]\n            fh.write(\"/\".join(parts) + f\"_{i}\\n\")\n\n\n# ---------------------------------------------------------------------------\n# Argument parsing  (no argparse – stdlib only, but argparse IS stdlib…\n# however, to keep the spirit of \"no dependencies\" we use manual parsing\n# since argparse is a stdlib module, not a third-party dep. We use it.)\n# ---------------------------------------------------------------------------\n\n\ndef parse_args(argv):\n    \"\"\"Return (binaries, opts, extra_args).\"\"\"\n    import argparse\n\n    # Split off everything after \"--\"\n    extra_args = []\n    if \"--\" in argv:\n        sep_idx = argv.index(\"--\")\n        extra_args = argv[sep_idx + 1 :]\n        argv = argv[:sep_idx]\n\n    parser = argparse.ArgumentParser(\n        description=\"Skim benchmark script\",\n        add_help=True,\n    )\n    parser.add_argument(\n        \"binaries\",\n        nargs=\"*\",\n        metavar=\"BINARY_PATH\",\n        help=\"Path(s) to binary (default: ./target/release/sk)\",\n    )\n    parser.add_argument(\"-n\", \"--num-items\", type=int, default=DEFAULT_NUM_ITEMS)\n    parser.add_argument(\"-q\", \"--query\", default=DEFAULT_QUERY)\n    parser.add_argument(\"-r\", \"--runs\", type=int, default=DEFAULT_RUNS)\n    parser.add_argument(\"-w\", \"--warmup\", type=int, default=DEFAULT_WARMUP)\n    parser.add_argument(\"-f\", \"--file\", default=\"\")\n    parser.add_argument(\"-g\", \"--generate-file\", default=\"\")\n    parser.add_argument(\"-j\", \"--json\", action=\"store_true\")\n\n    opts = parser.parse_args(argv)\n\n    if not opts.binaries:\n        opts.binaries = [DEFAULT_BINARY]\n\n    if opts.file and opts.generate_file:\n        parser.error(\"Cannot use both --file and --generate-file\")\n\n    return opts.binaries, opts, extra_args\n\n\n# ---------------------------------------------------------------------------\n# Resource monitor (background thread)\n# ---------------------------------------------------------------------------\n\nimport threading\n\n\nclass ResourceMonitor(threading.Thread):\n    \"\"\"Sample CPU and RSS of *pid* every 50 ms until the process exits.\"\"\"\n\n    def __init__(self, pid: int):\n        super().__init__(daemon=True)\n        self.pid = pid\n        self.peak_mem_kb: int = 0  # RSS in kB\n        self.peak_cpu: float = 0.0  # %CPU\n\n    def run(self):\n        while True:\n            try:\n                result = subprocess.run(\n                    [\"ps\", \"-p\", str(self.pid), \"-o\", \"rss=,%cpu=\"],\n                    capture_output=True,\n                    text=True,\n                )\n                line = result.stdout.strip()\n                if not line:\n                    break\n                parts = line.split()\n                if len(parts) >= 2:\n                    try:\n                        mem = int(parts[0])\n                        cpu = float(parts[1])\n                        if mem > self.peak_mem_kb:\n                            self.peak_mem_kb = mem\n                        if cpu > self.peak_cpu:\n                            self.peak_cpu = cpu\n                    except ValueError:\n                        pass\n            except Exception:\n                break\n            time.sleep(0.05)\n\n\n# ---------------------------------------------------------------------------\n# Single run\n# ---------------------------------------------------------------------------\n\n\ndef _find_sk_pid(pane_pid: int, binary_path: str) -> int:\n    \"\"\"Try for up to 5 s to find the sk child PID under *pane_pid*.\"\"\"\n    for _ in range(50):\n        time.sleep(0.1)\n        try:\n            result = subprocess.run(\n                [\"pgrep\", \"-P\", str(pane_pid), \"-f\", binary_path],\n                capture_output=True,\n                text=True,\n            )\n            pids = result.stdout.strip().splitlines()\n            if pids:\n                return int(pids[0])\n        except Exception:\n            pass\n    return 0\n\n\ndef run_once(\n    binary_path: str,\n    query: str,\n    tmp_file: str,\n    num_items: int,\n    extra_args: list,\n    run_index: int,\n    session_suffix: str,\n) -> dict:\n    \"\"\"\n    Execute one benchmark run against *binary_path*.\n    Returns a dict with keys: elapsed_s, rate, matched, peak_mem_kb, peak_cpu,\n    completed.\n    \"\"\"\n    session_name = f\"skim_bench_{os.getpid()}_{session_suffix}_{run_index}\"\n    status_fd, status_file = tempfile.mkstemp(prefix=\"skim_bench_status_\")\n    os.close(status_fd)\n\n    env = os.environ.copy()\n    env[\"SHELL\"] = \"/bin/sh\"\n    env.pop(\"HISTFILE\", None)\n    env.pop(\"FZF_DEFAULT_OPTS\", None)\n    env.pop(\"SKIM_DEFAULT_OPTIONS\", None)\n\n    try:\n        # Create tmux session\n        subprocess.run(\n            [\"tmux\", \"new-session\", \"-s\", session_name, \"-d\"],\n            check=True,\n            env=env,\n            capture_output=True,\n        )\n\n        # Clear env vars inside the session\n        for cmd in [\n            \"unset HISTFILE\",\n            \"unset FZF_DEFAULT_OPTS\",\n            \"unset SKIM_DEFAULT_OPTIONS\",\n        ]:\n            subprocess.run(\n                [\"tmux\", \"send-keys\", \"-t\", session_name, cmd, \"Enter\"],\n                check=True,\n                capture_output=True,\n            )\n        time.sleep(0.1)\n\n        # Build the command string\n        extra_str = \" \".join(extra_args)\n        cmd_str = f\"cat {tmp_file} | {binary_path} --query '{query}' {extra_str}\"\n        subprocess.run(\n            [\"tmux\", \"send-keys\", \"-t\", session_name, cmd_str, \"Enter\"],\n            check=True,\n            capture_output=True,\n        )\n\n        start_ns = time.perf_counter_ns()\n\n        # Locate sk PID for resource monitoring\n        pane_pid = 0\n        try:\n            r = subprocess.run(\n                [\"tmux\", \"list-panes\", \"-t\", session_name, \"-F\", \"#{pane_pid}\"],\n                capture_output=True,\n                text=True,\n            )\n            pane_pid = int(r.stdout.strip().splitlines()[0])\n        except Exception:\n            pass\n\n        sk_pid = 0\n        monitor = None\n        if pane_pid:\n            sk_pid = _find_sk_pid(pane_pid, binary_path)\n        if sk_pid:\n            monitor = ResourceMonitor(sk_pid)\n            monitor.start()\n\n        # Poll for matcher completion\n        completed = False\n        matched_count = 0\n        prev_matched_count = -1\n        stable_start: float = 0.0\n        end_ns = 0\n        loop_start = time.monotonic()\n\n        while True:\n            time.sleep(CHECK_INTERVAL_S)\n\n            now = time.monotonic()\n            if now - loop_start >= MAX_WAIT_S:\n                break\n\n            # Early exit if sk process is gone\n            if sk_pid:\n                try:\n                    os.kill(sk_pid, 0)\n                except ProcessLookupError:\n                    break\n\n            # Capture tmux pane\n            try:\n                subprocess.run(\n                    [\n                        \"tmux\",\n                        \"capture-pane\",\n                        \"-b\",\n                        f\"status-{session_name}\",\n                        \"-t\",\n                        session_name,\n                    ],\n                    capture_output=True,\n                )\n                subprocess.run(\n                    [\n                        \"tmux\",\n                        \"save-buffer\",\n                        \"-b\",\n                        f\"status-{session_name}\",\n                        status_file,\n                    ],\n                    capture_output=True,\n                )\n            except Exception:\n                continue\n\n            # Parse \"matched/total\" from status line\n            try:\n                with open(status_file) as fh:\n                    content = fh.read()\n            except OSError:\n                continue\n\n            import re\n\n            m = re.search(r\"(\\d+)/(\\d+)\", content)\n            if not m:\n                continue\n\n            mc = int(m.group(1))\n            total = int(m.group(2))\n\n            if total == num_items:\n                if mc != prev_matched_count:\n                    prev_matched_count = mc\n                    matched_count = mc\n                    stable_start = time.monotonic()\n                    end_ns = time.perf_counter_ns()\n                elif stable_start > 0:\n                    if time.monotonic() - stable_start >= REQUIRED_STABLE_S:\n                        completed = True\n                        break\n\n        if end_ns == 0:\n            end_ns = time.perf_counter_ns()\n\n        # Exit skim\n        subprocess.run(\n            [\"tmux\", \"send-keys\", \"-t\", session_name, \"Escape\"],\n            capture_output=True,\n        )\n        time.sleep(0.1)\n\n        # Wait for monitor\n        if monitor is not None:\n            monitor.join(timeout=2.0)\n\n        elapsed_s = (end_ns - start_ns) / 1e9\n        rate = num_items / elapsed_s if elapsed_s > 0 else 0\n\n        peak_mem_kb = monitor.peak_mem_kb if monitor and monitor.peak_mem_kb else 0\n        peak_cpu = monitor.peak_cpu if monitor and monitor.peak_cpu else 0.0\n\n        return {\n            \"elapsed_s\": elapsed_s,\n            \"rate\": rate,\n            \"matched\": matched_count,\n            \"peak_mem_kb\": peak_mem_kb if peak_mem_kb else None,\n            \"peak_cpu\": peak_cpu if peak_cpu else None,\n            \"completed\": completed,\n        }\n\n    finally:\n        subprocess.run(\n            [\"tmux\", \"kill-session\", \"-t\", session_name],\n            capture_output=True,\n        )\n        try:\n            os.unlink(status_file)\n        except OSError:\n            pass\n\n\n# ---------------------------------------------------------------------------\n# Aggregate statistics\n# ---------------------------------------------------------------------------\n\n\ndef _avg(values):\n    vals = [v for v in values if v is not None]\n    return sum(vals) / len(vals) if vals else None\n\n\ndef _min(values):\n    vals = [v for v in values if v is not None]\n    return min(vals) if vals else None\n\n\ndef _max(values):\n    vals = [v for v in values if v is not None]\n    return max(vals) if vals else None\n\n\ndef aggregate(results: list) -> dict:\n    completed_results = [r for r in results if r[\"completed\"]]\n    times = [r[\"elapsed_s\"] for r in completed_results]\n    rates = [r[\"rate\"] for r in completed_results]\n    matched = [r[\"matched\"] for r in completed_results]\n    mems = [r[\"peak_mem_kb\"] for r in completed_results]\n    cpus = [r[\"peak_cpu\"] for r in completed_results]\n    completed = len(completed_results)\n\n    return {\n        \"completed\": completed,\n        \"runs\": len(results),\n        \"avg_time\": _avg(times),\n        \"min_time\": _min(times),\n        \"max_time\": _max(times),\n        \"avg_rate\": _avg(rates),\n        \"min_rate\": _min(rates),\n        \"max_rate\": _max(rates),\n        \"avg_matched\": _avg(matched),\n        \"min_matched\": _min(matched),\n        \"max_matched\": _max(matched),\n        \"avg_mem\": _avg(mems),\n        \"min_mem\": _min(mems),\n        \"max_mem\": _max(mems),\n        \"avg_cpu\": _avg(cpus),\n        \"min_cpu\": _min(cpus),\n        \"max_cpu\": _max(cpus),\n    }\n\n\n# ---------------------------------------------------------------------------\n# Formatting helpers\n# ---------------------------------------------------------------------------\n\n\ndef _pct(baseline, value):\n    \"\"\"Return a +/-XX.X% string comparing *value* to *baseline*.\"\"\"\n    if baseline is None or value is None or baseline == 0:\n        return \"\"\n    diff = (value - baseline) / abs(baseline) * 100\n    sign = \"+\" if diff >= 0 else \"\"\n    return f\"{sign}{diff:.1f}%\"\n\n\ndef _fmt_mem(kb):\n    if kb is None:\n        return None\n    return kb / 1024  # MB\n\n\ndef _fmt_optional(value, fmt):\n    if value is None:\n        return \"N/A\"\n    return fmt.format(value)\n\n\n# ---------------------------------------------------------------------------\n# Output\n# ---------------------------------------------------------------------------\n\n\ndef print_human(\n    binary_label: str,\n    agg: dict,\n    num_items: int,\n    baseline: dict | None = None,\n    is_baseline: bool = False,\n):\n    tag = \" [baseline]\" if is_baseline else \"\"\n    print(f\"\\n=== Results: {binary_label}{tag} ===\")\n    print(f\"Completed runs: {agg['completed']} / {agg['runs']}\")\n\n    def cmp(key, baseline_key=None):\n        \"\"\"Format comparison vs baseline for a given aggregate key.\"\"\"\n        bk = baseline_key or key\n        if baseline is None or is_baseline:\n            return \"\"\n        return \"  \" + _pct(baseline.get(bk), agg.get(key))\n\n    # Items matched\n    avg_m = _fmt_optional(agg[\"avg_matched\"], \"{:.0f}\")\n    min_m = _fmt_optional(agg[\"min_matched\"], \"{:.0f}\")\n    max_m = _fmt_optional(agg[\"max_matched\"], \"{:.0f}\")\n    print(\n        f\"Average items matched: {avg_m} / {num_items}\"\n        f\"  (min: {min_m}, max: {max_m})\"\n        f\"{cmp('avg_matched')}\"\n    )\n\n    # Time\n    avg_t = _fmt_optional(agg[\"avg_time\"], \"{:.3f}s\")\n    min_t = _fmt_optional(agg[\"min_time\"], \"{:.3f}s\")\n    max_t = _fmt_optional(agg[\"max_time\"], \"{:.3f}s\")\n    # Lower time is better, so flip sign for display\n    time_cmp = \"\"\n    if (\n        baseline\n        and not is_baseline\n        and baseline.get(\"avg_time\")\n        and agg.get(\"avg_time\")\n    ):\n        diff = (\n            (agg[\"avg_time\"] - baseline[\"avg_time\"]) / abs(baseline[\"avg_time\"]) * 100\n        )\n        sign = \"+\" if diff >= 0 else \"\"\n        time_cmp = f\"  {sign}{diff:.1f}%\"\n    print(f\"Average time: {avg_t}  (min: {min_t}, max: {max_t}){time_cmp}\")\n\n    # Rate\n    avg_r = _fmt_optional(agg[\"avg_rate\"], \"{:.0f}\")\n    min_r = _fmt_optional(agg[\"min_rate\"], \"{:.0f}\")\n    max_r = _fmt_optional(agg[\"max_rate\"], \"{:.0f}\")\n    print(\n        f\"Average items/second: {avg_r}  (min: {min_r}, max: {max_r}){cmp('avg_rate')}\"\n    )\n\n    # Memory\n    if agg[\"avg_mem\"] is not None:\n        avg_mb = _fmt_mem(agg[\"avg_mem\"])\n        min_mb = _fmt_mem(agg[\"min_mem\"])\n        max_mb = _fmt_mem(agg[\"max_mem\"])\n        print(\n            f\"Average peak memory usage: {avg_mb:.1f} MB\"\n            f\"  (min: {min_mb:.1f} MB, max: {max_mb:.1f} MB)\"\n            f\"{cmp('avg_mem')}\"\n        )\n\n    # CPU\n    if agg[\"avg_cpu\"] is not None:\n        avg_c = _fmt_optional(agg[\"avg_cpu\"], \"{:.1f}%\")\n        min_c = _fmt_optional(agg[\"min_cpu\"], \"{:.1f}%\")\n        max_c = _fmt_optional(agg[\"max_cpu\"], \"{:.1f}%\")\n        print(\n            f\"Average peak CPU usage: {avg_c}\"\n            f\"  (min: {min_c}, max: {max_c})\"\n            f\"{cmp('avg_cpu')}\"\n        )\n\n\ndef print_json_multi(binaries: list, aggregates: list, num_items: int, runs: int):\n    output = []\n    for binary, agg in zip(binaries, aggregates):\n        entry = {\n            \"binary\": binary,\n            \"num_items\": num_items,\n            \"runs\": runs,\n            \"completed_runs\": agg[\"completed\"],\n            \"items_matched\": {\n                \"avg\": agg[\"avg_matched\"],\n                \"min\": agg[\"min_matched\"],\n                \"max\": agg[\"max_matched\"],\n            },\n            \"time_s\": {\n                \"avg\": agg[\"avg_time\"],\n                \"min\": agg[\"min_time\"],\n                \"max\": agg[\"max_time\"],\n            },\n            \"items_per_second\": {\n                \"avg\": agg[\"avg_rate\"],\n                \"min\": agg[\"min_rate\"],\n                \"max\": agg[\"max_rate\"],\n            },\n            \"peak_memory_kb\": {\n                \"avg\": agg[\"avg_mem\"],\n                \"min\": agg[\"min_mem\"],\n                \"max\": agg[\"max_mem\"],\n            },\n            \"peak_cpu\": {\n                \"avg\": agg[\"avg_cpu\"],\n                \"min\": agg[\"min_cpu\"],\n                \"max\": agg[\"max_cpu\"],\n            },\n        }\n        output.append(entry)\n    print(json.dumps(output if len(output) > 1 else output[0]))\n\n\n# ---------------------------------------------------------------------------\n# Main\n# ---------------------------------------------------------------------------\n\n\ndef main():\n    import re  # ensure import at top of main scope for run_once\n\n    binaries, opts, extra_args = parse_args(sys.argv[1:])\n\n    num_items = opts.num_items\n    query = opts.query\n    runs = opts.runs\n    warmup = opts.warmup\n    input_file = opts.file\n    generate_file = opts.generate_file\n    as_json = opts.json\n\n    # ---- generate-file mode ------------------------------------------------\n    if generate_file:\n        print(f\"Generating {num_items} items to {generate_file}...\", file=sys.stderr)\n        generate_test_data(generate_file, num_items)\n        print(f\"Generated {num_items} items successfully\", file=sys.stderr)\n        return\n\n    # ---- prepare input data ------------------------------------------------\n    cleanup_input = False\n    if input_file:\n        if not os.path.isfile(input_file):\n            print(f\"Error: Input file '{input_file}' not found\", file=sys.stderr)\n            sys.exit(1)\n        tmp_file = input_file\n        with open(input_file) as fh:\n            num_items = sum(1 for _ in fh)\n        print(f\"Using input file with {num_items} items\", file=sys.stderr)\n    else:\n        fd, tmp_file = tempfile.mkstemp(prefix=\"skim_bench_input_\")\n        os.close(fd)\n        cleanup_input = True\n        print(\"Generating test data...\", file=sys.stderr)\n        generate_test_data(tmp_file, num_items)\n\n    try:\n        # ---- header ---------------------------------------------------------\n        binary_list = \", \".join(binaries)\n        print(f\"=== Skim Ingestion + Matching Benchmark ===\", file=sys.stderr)\n        print(\n            f\"Binaries: {binary_list} | Items: {num_items} | \"\n            f\"Query: '{query}' | Warmup: {warmup} | Runs: {runs} (per binary)\",\n            file=sys.stderr,\n        )\n        if input_file:\n            print(f\"Input file: {input_file}\", file=sys.stderr)\n        if extra_args:\n            print(f\"Extra args: {' '.join(extra_args)}\", file=sys.stderr)\n\n        # ---- warmup (results discarded) -------------------------------------\n        if warmup > 0:\n            print(f\"\\n=== Warmup ({warmup} run(s) per binary) ===\", file=sys.stderr)\n            for bi, binary in enumerate(binaries):\n                for wu in range(1, warmup + 1):\n                    print(\n                        f\"  Warmup {wu}/{warmup} — {binary} ...\",\n                        file=sys.stderr,\n                    )\n                    run_once(\n                        binary_path=binary,\n                        query=query,\n                        tmp_file=tmp_file,\n                        num_items=num_items,\n                        extra_args=extra_args,\n                        run_index=wu,\n                        session_suffix=f\"warmup_b{bi}\",\n                    )\n\n        # ---- run benchmark in round-robin -----------------------------------\n        # all_results[i] = list of per-run dicts for binaries[i]\n        all_results = [[] for _ in binaries]\n\n        for run_num in range(1, runs + 1):\n            for bi, binary in enumerate(binaries):\n                label = f\"[{os.path.basename(binary)}]\"\n                if runs > 1 or len(binaries) > 1:\n                    print(\n                        f\"\\n=== Run {run_num}/{runs} — binary {bi + 1}/{len(binaries)}: {binary} ===\",\n                        file=sys.stderr,\n                    )\n\n                result = run_once(\n                    binary_path=binary,\n                    query=query,\n                    tmp_file=tmp_file,\n                    num_items=num_items,\n                    extra_args=extra_args,\n                    run_index=run_num,\n                    session_suffix=f\"b{bi}\",\n                )\n                all_results[bi].append(result)\n\n                if runs > 1 or len(binaries) > 1:\n                    status = \"COMPLETED\" if result[\"completed\"] else \"TIMEOUT\"\n                    print(f\"Status: {status}\", file=sys.stderr)\n                    print(\n                        f\"Items matched: {result['matched']} / {num_items}\",\n                        file=sys.stderr,\n                    )\n                    print(f\"Total time: {result['elapsed_s']:.3f}s\", file=sys.stderr)\n                    print(f\"Items/second: {result['rate']:.0f}\", file=sys.stderr)\n                    if result[\"peak_mem_kb\"]:\n                        print(\n                            f\"Peak memory usage: {result['peak_mem_kb'] / 1024:.1f} MB\",\n                            file=sys.stderr,\n                        )\n                    if result[\"peak_cpu\"]:\n                        print(\n                            f\"Peak CPU usage: {result['peak_cpu']:.1f}%\",\n                            file=sys.stderr,\n                        )\n\n        # ---- aggregate ------------------------------------------------------\n        aggregates = [aggregate(all_results[i]) for i in range(len(binaries))]\n\n        # ---- output ---------------------------------------------------------\n        if as_json:\n            print_json_multi(binaries, aggregates, num_items, runs)\n        else:\n            baseline_agg = aggregates[0]\n            for i, (binary, agg) in enumerate(zip(binaries, aggregates)):\n                print_human(\n                    binary_label=binary,\n                    agg=agg,\n                    num_items=num_items,\n                    baseline=baseline_agg if len(binaries) > 1 else None,\n                    is_baseline=(i == 0),\n                )\n\n            # Summary comparison table when multiple binaries\n            if len(binaries) > 1:\n                print(f\"\\n=== Comparison Summary (vs baseline: {binaries[0]}) ===\")\n                header = f\"{'Binary':<40} {'Avg time':>12} {'Δ time':>10} {'Avg rate':>14} {'Δ rate':>10}\"\n                print(header)\n                print(\"-\" * len(header))\n                for i, (binary, agg) in enumerate(zip(binaries, aggregates)):\n                    t = (\n                        f\"{agg['avg_time']:.3f}s\"\n                        if agg[\"avg_time\"] is not None\n                        else \"N/A\"\n                    )\n                    r = (\n                        f\"{agg['avg_rate']:.0f}\"\n                        if agg[\"avg_rate\"] is not None\n                        else \"N/A\"\n                    )\n                    if i == 0:\n                        dt = \"baseline\"\n                        dr = \"baseline\"\n                    else:\n                        dt = _pct(baseline_agg[\"avg_time\"], agg[\"avg_time\"])\n                        dr = _pct(baseline_agg[\"avg_rate\"], agg[\"avg_rate\"])\n                    name = os.path.basename(binary) if len(binary) > 40 else binary\n                    print(f\"{name:<40} {t:>12} {dt:>10} {r:>14} {dr:>10}\")\n\n    finally:\n        if cleanup_input:\n            try:\n                os.unlink(tmp_file)\n            except OSError:\n                pass\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "benches/filter.rs",
    "content": "use std::fs;\n\nuse criterion::{Criterion, criterion_group, criterion_main};\n\nuse skim::Typos;\nuse skim::helper::item::DefaultSkimItem;\nuse skim::prelude::*;\n\nconst CHUNK_SIZE: usize = 1024;\nfn load_lines(file: &str) -> Vec<String> {\n    let data = fs::read_to_string(format!(\"benches/fixtures/{file}\")).expect(\"{file} missing\");\n    data.lines().map(|l| l.to_string()).collect()\n}\n\nfn prepare(file: &str, opt_builder: &mut SkimOptionsBuilder) -> (SkimOptions, SkimItemReceiver) {\n    let lines = load_lines(file);\n    let opts = opt_builder.build().unwrap();\n    let (tx, rx) = unbounded();\n    let mut chunk_size = 0;\n    let mut chunk = Vec::new();\n    for line in lines {\n        if chunk_size >= CHUNK_SIZE {\n            tx.send(chunk).unwrap();\n            chunk_size = 0;\n            chunk = Vec::new();\n        }\n        chunk.push(Arc::new(DefaultSkimItem::from(line)) as Arc<dyn SkimItem>);\n    }\n    tx.send(chunk).unwrap();\n    (opts, rx)\n}\n\nfn criterion_benchmark_10m(c: &mut Criterion) {\n    c.bench_function(\"filter_10M_regex\", |b| {\n        b.iter_batched(\n            || prepare(\"10M.txt\", SkimOptionsBuilder::default().filter(\"test\").regex(true)),\n            |(opts, rx)| Skim::run_with(opts, Some(rx)),\n            criterion::BatchSize::SmallInput,\n        );\n    });\n    c.bench_function(\"filter_10M_frizbee\", |b| {\n        b.iter_batched(\n            || {\n                prepare(\n                    \"10M.txt\",\n                    SkimOptionsBuilder::default()\n                        .filter(\"test\")\n                        .algorithm(FuzzyAlgorithm::Frizbee)\n                        .typos(Typos::Disabled),\n                )\n            },\n            |(opts, rx)| Skim::run_with(opts, Some(rx)),\n            criterion::BatchSize::SmallInput,\n        );\n    });\n    c.bench_function(\"filter_10M_frizbee_typos\", |b| {\n        b.iter_batched(\n            || {\n                prepare(\n                    \"10M.txt\",\n                    SkimOptionsBuilder::default()\n                        .filter(\"test\")\n                        .algorithm(FuzzyAlgorithm::Frizbee)\n                        .typos(Typos::Smart),\n                )\n            },\n            |(opts, rx)| Skim::run_with(opts, Some(rx)),\n            criterion::BatchSize::SmallInput,\n        );\n    });\n    c.bench_function(\"filter_10M_clangd\", |b| {\n        b.iter_batched(\n            || {\n                prepare(\n                    \"10M.txt\",\n                    SkimOptionsBuilder::default()\n                        .filter(\"test\")\n                        .algorithm(FuzzyAlgorithm::Clangd),\n                )\n            },\n            |(opts, rx)| Skim::run_with(opts, Some(rx)),\n            criterion::BatchSize::SmallInput,\n        );\n    });\n    c.bench_function(\"filter_10M_fzy\", |b| {\n        b.iter_batched(\n            || {\n                prepare(\n                    \"10M.txt\",\n                    SkimOptionsBuilder::default()\n                        .filter(\"test\")\n                        .algorithm(FuzzyAlgorithm::Fzy)\n                        .typos(Typos::Disabled),\n                )\n            },\n            |(opts, rx)| Skim::run_with(opts, Some(rx)),\n            criterion::BatchSize::SmallInput,\n        );\n    });\n    c.bench_function(\"filter_10M_fzy_typos\", |b| {\n        b.iter_batched(\n            || {\n                prepare(\n                    \"10M.txt\",\n                    SkimOptionsBuilder::default()\n                        .filter(\"test\")\n                        .algorithm(FuzzyAlgorithm::Fzy)\n                        .typos(Typos::Smart),\n                )\n            },\n            |(opts, rx)| Skim::run_with(opts, Some(rx)),\n            criterion::BatchSize::SmallInput,\n        );\n    });\n    c.bench_function(\"filter_10M_arinae\", |b| {\n        b.iter_batched(\n            || {\n                prepare(\n                    \"10M.txt\",\n                    SkimOptionsBuilder::default()\n                        .filter(\"test\")\n                        .algorithm(FuzzyAlgorithm::Arinae)\n                        .typos(Typos::Disabled),\n                )\n            },\n            |(opts, rx)| Skim::run_with(opts, Some(rx)),\n            criterion::BatchSize::SmallInput,\n        );\n    });\n    c.bench_function(\"filter_10M_arinae_typos\", |b| {\n        b.iter_batched(\n            || {\n                prepare(\n                    \"10M.txt\",\n                    SkimOptionsBuilder::default()\n                        .filter(\"test\")\n                        .algorithm(FuzzyAlgorithm::Arinae)\n                        .typos(Typos::Smart),\n                )\n            },\n            |(opts, rx)| Skim::run_with(opts, Some(rx)),\n            criterion::BatchSize::SmallInput,\n        );\n    });\n}\n\nfn criterion_benchmark_1m(c: &mut Criterion) {\n    c.bench_function(\"filter_1M_regex\", |b| {\n        b.iter_batched(\n            || prepare(\"1M.txt\", SkimOptionsBuilder::default().filter(\"test\").regex(true)),\n            |(opts, rx)| Skim::run_with(opts, Some(rx)),\n            criterion::BatchSize::SmallInput,\n        );\n    });\n    c.bench_function(\"filter_1M_frizbee\", |b| {\n        b.iter_batched(\n            || {\n                prepare(\n                    \"1M.txt\",\n                    SkimOptionsBuilder::default()\n                        .filter(\"test\")\n                        .algorithm(FuzzyAlgorithm::Frizbee)\n                        .typos(Typos::Disabled),\n                )\n            },\n            |(opts, rx)| Skim::run_with(opts, Some(rx)),\n            criterion::BatchSize::SmallInput,\n        );\n    });\n    c.bench_function(\"filter_1M_frizbee_typos\", |b| {\n        b.iter_batched(\n            || {\n                prepare(\n                    \"1M.txt\",\n                    SkimOptionsBuilder::default()\n                        .filter(\"test\")\n                        .algorithm(FuzzyAlgorithm::Frizbee)\n                        .typos(Typos::Smart),\n                )\n            },\n            |(opts, rx)| Skim::run_with(opts, Some(rx)),\n            criterion::BatchSize::SmallInput,\n        );\n    });\n    c.bench_function(\"filter_1M_clangd\", |b| {\n        b.iter_batched(\n            || {\n                prepare(\n                    \"1M.txt\",\n                    SkimOptionsBuilder::default()\n                        .filter(\"test\")\n                        .algorithm(FuzzyAlgorithm::Clangd),\n                )\n            },\n            |(opts, rx)| Skim::run_with(opts, Some(rx)),\n            criterion::BatchSize::SmallInput,\n        );\n    });\n    c.bench_function(\"filter_1M_fzy\", |b| {\n        b.iter_batched(\n            || {\n                prepare(\n                    \"1M.txt\",\n                    SkimOptionsBuilder::default()\n                        .filter(\"test\")\n                        .algorithm(FuzzyAlgorithm::Fzy)\n                        .typos(Typos::Disabled),\n                )\n            },\n            |(opts, rx)| Skim::run_with(opts, Some(rx)),\n            criterion::BatchSize::SmallInput,\n        );\n    });\n    c.bench_function(\"filter_1M_fzy_typos\", |b| {\n        b.iter_batched(\n            || {\n                prepare(\n                    \"1M.txt\",\n                    SkimOptionsBuilder::default()\n                        .filter(\"test\")\n                        .algorithm(FuzzyAlgorithm::Fzy)\n                        .typos(Typos::Smart),\n                )\n            },\n            |(opts, rx)| Skim::run_with(opts, Some(rx)),\n            criterion::BatchSize::SmallInput,\n        );\n    });\n    c.bench_function(\"filter_1M_arinae\", |b| {\n        b.iter_batched(\n            || {\n                prepare(\n                    \"1M.txt\",\n                    SkimOptionsBuilder::default()\n                        .filter(\"test\")\n                        .algorithm(FuzzyAlgorithm::Arinae)\n                        .typos(Typos::Disabled),\n                )\n            },\n            |(opts, rx)| Skim::run_with(opts, Some(rx)),\n            criterion::BatchSize::SmallInput,\n        );\n    });\n    c.bench_function(\"filter_1M_arinae_typos\", |b| {\n        b.iter_batched(\n            || {\n                prepare(\n                    \"1M.txt\",\n                    SkimOptionsBuilder::default()\n                        .filter(\"test\")\n                        .algorithm(FuzzyAlgorithm::Arinae)\n                        .typos(Typos::Smart),\n                )\n            },\n            |(opts, rx)| Skim::run_with(opts, Some(rx)),\n            criterion::BatchSize::SmallInput,\n        );\n    });\n\n    c.bench_function(\"filter_1M_andor\", |b| {\n        b.iter_batched(\n            || prepare(\"1M.txt\", SkimOptionsBuilder::default().filter(\"boot foo | mnt foo\")),\n            |(opts, rx)| Skim::run_with(opts, Some(rx)),\n            criterion::BatchSize::SmallInput,\n        );\n    });\n}\n\ncriterion_group!(\n    name = benches_10m;\n    config = Criterion::default().sample_size(10);\n    targets = criterion_benchmark_10m\n);\ncriterion_group!(\n    name = benches_1m;\n    config = Criterion::default().sample_size(100);\n    targets = criterion_benchmark_1m\n);\ncriterion_main!(benches_1m, benches_10m);\n"
  },
  {
    "path": "benches/gungraun.rs",
    "content": "use gungraun::{library_benchmark, library_benchmark_group, main};\nuse std::fs;\nuse std::hint::black_box;\n\nuse skim::CaseMatching;\nuse skim::fuzzy_matcher::FuzzyMatcher;\nuse skim::fuzzy_matcher::arinae::ArinaeMatcher;\nuse skim::fuzzy_matcher::frizbee::FrizbeeMatcher;\nuse skim::prelude::SkimMatcherV2;\n\nfn load_lines() -> Vec<String> {\n    let data = fs::read_to_string(\"benches/fixtures/1M.txt\").expect(\"1M.txt missing\");\n    data.lines().map(|l| l.to_string()).collect()\n}\n\n#[inline(always)]\nfn bench_matcher(m: impl FuzzyMatcher, lines: Vec<String>) -> u64 {\n    let mut count = 0u64;\n    for line in &lines {\n        if m.fuzzy_indices(line, \"test\").is_some() {\n            count += 1;\n        }\n    }\n    count\n}\n\n#[library_benchmark]\nfn skim_v2() -> u64 {\n    bench_matcher(SkimMatcherV2::default().smart_case(), black_box(load_lines()))\n}\n#[library_benchmark]\nfn frizbee() -> u64 {\n    bench_matcher(\n        FrizbeeMatcher::default().case(CaseMatching::Smart).max_typos(Some(0)),\n        black_box(load_lines()),\n    )\n}\n#[library_benchmark]\nfn frizbee_typos() -> u64 {\n    bench_matcher(\n        FrizbeeMatcher::default().case(CaseMatching::Smart).max_typos(Some(1)),\n        black_box(load_lines()),\n    )\n}\n#[library_benchmark]\nfn arinae() -> u64 {\n    bench_matcher(\n        ArinaeMatcher::new(CaseMatching::Smart, false, false),\n        black_box(load_lines()),\n    )\n}\n#[library_benchmark]\nfn arinae_typos() -> u64 {\n    bench_matcher(\n        ArinaeMatcher::new(CaseMatching::Smart, true, false),\n        black_box(load_lines()),\n    )\n}\n\nlibrary_benchmark_group!(\n    name = benches,\n    benchmarks = [skim_v2, frizbee, frizbee_typos, arinae, arinae_typos]\n);\n\nmain!(library_benchmark_groups = benches);\n"
  },
  {
    "path": "benches/matcher_micro.rs",
    "content": "//! Microbenchmark that isolates the fuzzy matcher DP from all other overhead\n//! (I/O, threading, sorting).\n\nuse std::fs;\n\nuse criterion::{Criterion, criterion_group, criterion_main};\n\nuse skim::CaseMatching;\nuse skim::fuzzy_matcher::FuzzyMatcher;\nuse skim::fuzzy_matcher::arinae::ArinaeMatcher;\nuse skim::fuzzy_matcher::frizbee::FrizbeeMatcher;\nuse skim::prelude::SkimMatcherV2;\n\nfn load_lines() -> Vec<String> {\n    let data = fs::read_to_string(\"benches/fixtures/100K.txt\").expect(\"100K.txt missing\");\n    data.lines().map(|l| l.to_string()).collect()\n}\n\nfn bench_matcher(c: &mut Criterion) {\n    let lines = load_lines();\n\n    c.bench_function(\"micro_skim_v2\", |b| {\n        let m = SkimMatcherV2::default().smart_case();\n        b.iter(|| {\n            let mut count = 0u64;\n            for line in &lines {\n                if m.fuzzy_indices(line, \"test\").is_some() {\n                    count += 1;\n                }\n            }\n            count\n        });\n    });\n    c.bench_function(\"micro_frizbee\", |b| {\n        let m = FrizbeeMatcher::default().case(CaseMatching::Smart).max_typos(Some(0));\n        b.iter(|| {\n            let mut count = 0u64;\n            for line in &lines {\n                if m.fuzzy_indices(line, \"test\").is_some() {\n                    count += 1;\n                }\n            }\n            count\n        });\n    });\n    c.bench_function(\"micro_typos_frizbee\", |b| {\n        let m = FrizbeeMatcher::default().case(CaseMatching::Smart).max_typos(Some(1));\n        b.iter(|| {\n            let mut count = 0u64;\n            for line in &lines {\n                if m.fuzzy_indices(line, \"test\").is_some() {\n                    count += 1;\n                }\n            }\n            count\n        });\n    });\n    c.bench_function(\"micro_arinae\", |b| {\n        let m = ArinaeMatcher::new(CaseMatching::Smart, false, false);\n        b.iter(|| {\n            let mut count = 0u64;\n            for line in &lines {\n                if m.fuzzy_indices(line, \"test\").is_some() {\n                    count += 1;\n                }\n            }\n            count\n        });\n    });\n    c.bench_function(\"micro_arinae_range\", |b| {\n        let m = ArinaeMatcher::new(CaseMatching::Smart, false, false);\n        b.iter(|| {\n            let mut count = 0u64;\n            for line in &lines {\n                if m.fuzzy_match_range(line, \"test\").is_some() {\n                    count += 1;\n                }\n            }\n            count\n        });\n    });\n    c.bench_function(\"micro_arinae_score\", |b| {\n        let m = ArinaeMatcher::new(CaseMatching::Smart, false, false);\n        b.iter(|| {\n            let mut count = 0u64;\n            for line in &lines {\n                if m.fuzzy_match(line, \"test\").is_some() {\n                    count += 1;\n                }\n            }\n            count\n        });\n    });\n    c.bench_function(\"micro_typos_arinae\", |b| {\n        let m = ArinaeMatcher::new(CaseMatching::Smart, true, false);\n        b.iter(|| {\n            let mut count = 0u64;\n            for line in &lines {\n                if m.fuzzy_indices(line, \"test\").is_some() {\n                    count += 1;\n                }\n            }\n            count\n        });\n    });\n    c.bench_function(\"micro_typos_arinae_range\", |b| {\n        let m = ArinaeMatcher::new(CaseMatching::Smart, true, false);\n        b.iter(|| {\n            let mut count = 0u64;\n            for line in &lines {\n                if m.fuzzy_match_range(line, \"test\").is_some() {\n                    count += 1;\n                }\n            }\n            count\n        });\n    });\n    c.bench_function(\"micro_typos_arinae_score\", |b| {\n        let m = ArinaeMatcher::new(CaseMatching::Smart, true, false);\n        b.iter(|| {\n            let mut count = 0u64;\n            for line in &lines {\n                if m.fuzzy_match(line, \"test\").is_some() {\n                    count += 1;\n                }\n            }\n            count\n        });\n    });\n}\n\ncriterion_group!(benches, bench_matcher);\ncriterion_main!(benches);\n"
  },
  {
    "path": "benches/partial.rs",
    "content": "use std::io::{BufWriter, Stderr};\n\nuse clap::Parser as _;\nuse criterion::{Criterion, criterion_group, criterion_main};\n\nuse ratatui::prelude::CrosstermBackend;\nuse skim::prelude::*;\n\nfn criterion_benchmark(c: &mut Criterion) {\n    c.bench_function(\"parse_options\", |b| {\n        b.iter(|| SkimOptions::parse_from(Vec::<&str>::new()));\n    });\n    c.bench_function(\"init\", |b| {\n        b.iter_batched(\n            || SkimOptions::default().build(),\n            |options: SkimOptions| Skim::<CrosstermBackend<BufWriter<Stderr>>>::init(options, None),\n            criterion::BatchSize::SmallInput,\n        );\n    });\n    c.bench_function(\"init_with_source\", |b| {\n        b.iter_batched(\n            || {\n                let (_tx, rx) = bounded(8);\n                (SkimOptions::default().build(), rx)\n            },\n            |input: (SkimOptions, SkimItemReceiver)| {\n                Skim::<CrosstermBackend<BufWriter<Stderr>>>::init(input.0, Some(input.1))\n            },\n            criterion::BatchSize::SmallInput,\n        );\n    });\n    c.bench_function(\"start\", |b| {\n        b.iter_batched(\n            || Skim::<CrosstermBackend<BufWriter<Stderr>>>::init(SkimOptions::default().build(), None).unwrap(),\n            |mut skim: Skim| skim.start(),\n            criterion::BatchSize::SmallInput,\n        );\n    });\n\n    c.bench_function(\"full_setup\", |b| {\n        b.iter(|| {\n            let mut options = SkimOptions::default().build();\n            if let Some(ref filter_query) = options.filter\n                && options.query.is_none()\n            {\n                options.query = Some(filter_query.clone());\n            }\n            let mut skim = Skim::init(options, None).unwrap();\n\n            skim.start();\n\n            if skim.should_enter() {\n                skim.init_tui().unwrap();\n            }\n        });\n    });\n}\n\ncriterion_group!(\n    name = benches;\n    config = Criterion::default().sample_size(100);\n    targets = criterion_benchmark\n);\ncriterion_main!(benches);\n"
  },
  {
    "path": "benches/read_and_match.rs",
    "content": "use color_eyre::eyre::{Ok, Result};\nuse criterion::{Criterion, criterion_group, criterion_main};\n\nuse skim::prelude::*;\n\nasync fn wait_until_done(mut opts: SkimOptions) -> Result<SkimOutput> {\n    opts.cmd = Some(String::from(\"cat benches/fixtures/10M.txt\"));\n    let mut skim = Skim::init(opts, None)?;\n    skim.start();\n    skim.init_tui()?;\n    skim.enter().await?;\n    while !skim.tick().await? {\n        if skim.reader_done() && skim.matcher_stopped() {\n            skim.event_sender().send(Event::Action(Action::Accept(None))).await?;\n        }\n    }\n    Ok(skim.output())\n}\n\nfn criterion_benchmark(c: &mut Criterion) {\n    c.bench_function(\"default\", |b| {\n        let rt = tokio::runtime::Runtime::new().unwrap();\n        b.to_async(rt)\n            .iter(async || wait_until_done(SkimOptions::default()).await);\n    });\n    c.bench_function(\"query\", |b| {\n        let rt = tokio::runtime::Runtime::new().unwrap();\n        b.to_async(rt)\n            .iter(async || wait_until_done(SkimOptionsBuilder::default().query(\"test\").build().unwrap()).await);\n    });\n    c.bench_function(\"query_frizbee\", |b| {\n        let rt = tokio::runtime::Runtime::new().unwrap();\n        b.to_async(rt).iter(async || {\n            wait_until_done(\n                SkimOptionsBuilder::default()\n                    .query(\"test\")\n                    .algorithm(FuzzyAlgorithm::Frizbee)\n                    .no_typos(true)\n                    .build()\n                    .unwrap(),\n            )\n            .await\n        });\n    });\n    c.bench_function(\"query_ari\", |b| {\n        let rt = tokio::runtime::Runtime::new().unwrap();\n        b.to_async(rt).iter(async || {\n            wait_until_done(\n                SkimOptionsBuilder::default()\n                    .query(\"test\")\n                    .algorithm(FuzzyAlgorithm::Arinae)\n                    .no_typos(true)\n                    .build()\n                    .unwrap(),\n            )\n            .await\n        });\n    });\n    c.bench_function(\"query_frizbee_typos\", |b| {\n        let rt = tokio::runtime::Runtime::new().unwrap();\n        b.to_async(rt).iter(async || {\n            wait_until_done(\n                SkimOptionsBuilder::default()\n                    .query(\"test\")\n                    .algorithm(FuzzyAlgorithm::Frizbee)\n                    .build()\n                    .unwrap(),\n            )\n            .await\n        });\n    });\n    c.bench_function(\"query_ari_typos\", |b| {\n        let rt = tokio::runtime::Runtime::new().unwrap();\n        b.to_async(rt).iter(async || {\n            wait_until_done(\n                SkimOptionsBuilder::default()\n                    .query(\"test\")\n                    .algorithm(FuzzyAlgorithm::Arinae)\n                    .build()\n                    .unwrap(),\n            )\n            .await\n        });\n    });\n    c.bench_function(\"typing\", |b| {\n        let rt = tokio::runtime::Runtime::new().unwrap();\n        b.to_async(rt).iter(async || {\n            let mut skim = Skim::init(SkimOptionsBuilder::default().cmd(\"cat bench_data.txt\").build()?, None)?;\n            skim.start();\n            skim.init_tui()?;\n            skim.enter().await?;\n            let s = skim.event_sender();\n            let mut sent = false;\n            let mut done_since = 0;\n            let mut done = false;\n            while !skim.tick().await? {\n                if skim.reader_done() && skim.matcher_stopped() {\n                    if done {\n                        done_since += 1;\n                    } else {\n                        done_since = 1;\n                    }\n                    if sent && done_since > 50 {\n                        s.send(Event::Action(Action::Accept(None))).await?;\n                    } else if !sent {\n                        s.send(Event::Action(Action::AddChar('t'))).await?;\n                        s.send(Event::Action(Action::AddChar('e'))).await?;\n                        s.send(Event::Action(Action::AddChar('s'))).await?;\n                        s.send(Event::Action(Action::AddChar('t'))).await?;\n                        sent = true;\n                    }\n                    done = true;\n                } else {\n                    done = false;\n                }\n            }\n            Ok(skim.output())\n        });\n    });\n}\n\ncriterion_group!(\n    name = benches;\n    config = Criterion::default().sample_size(10);\n    targets = criterion_benchmark\n);\ncriterion_main!(benches);\n"
  },
  {
    "path": "bin/sk-tmux",
    "content": "#!/usr/bin/env bash\n\n# The MIT License (MIT)\n#\n# Copyright (c) 2019 Jinzhou Zhang\n# Copyright (c) 2016 Junegunn Choi\n#\n# Permission is hereby granted, free of charge, to any person obtaining a copy\n# of this software and associated documentation files (the \"Software\"), to deal\n# in the Software without restriction, including without limitation the rights\n# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n# copies of the Software, and to permit persons to whom the Software is\n# furnished to do so, subject to the following conditions:\n#\n# The above copyright notice and this permission notice shall be included in\n# all copies or substantial portions of the Software.\n#\n# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n# THE SOFTWARE.\n#\n# Modified by Jinzhouz Zhang\n\n# sk-tmux: starts sk in a tmux pane\n# usage: sk-tmux [LAYOUT OPTIONS] [--] [SK OPTIONS]\n\necho \"[WRN] This script is deprecated in favor of \\`sk --tmux\\` and will be removed in a later release\" >&2\n\nfail() {\n  >&2 echo \"$1\"\n  exit 2\n}\n\nsk=\"$(command -v sk 2> /dev/null)\" || sk=\"$(dirname \"$0\")/sk\"\n[[ -x \"$sk\" ]] || fail 'sk executable not found'\n\ntmux_args=()\nargs=()\nopt=\"\"\nskip=\"\"\nswap=\"\"\nclose=\"\"\nterm=\"\"\n[[ -n \"$LINES\" ]] && lines=$LINES || lines=$(tput lines) || lines=$(tmux display-message -p \"#{pane_height}\")\n[[ -n \"$COLUMNS\" ]] && columns=$COLUMNS || columns=$(tput cols) || columns=$(tmux display-message -p \"#{pane_width}\")\n\nhelp() {\n  >&2 echo 'usage: sk-tmux [LAYOUT OPTIONS] [--] [SK OPTIONS]\n\n  LAYOUT OPTIONS:\n    (default layout: -d 50%)\n\n    Popup window (requires tmux 3.2 or above):\n      -p [WIDTH[%][,HEIGHT[%]]]  (default: 50%)\n      -w WIDTH[%]\n      -h HEIGHT[%]\n      -x COL\n      -y ROW\n\n    Split pane:\n      -u [HEIGHT[%]]             Split above (up)\n      -d [HEIGHT[%]]             Split below (down)\n      -l [WIDTH[%]]              Split left\n      -r [WIDTH[%]]              Split right\n'\n  exit\n}\n\nwhile [[ $# -gt 0 ]]; do\n  arg=\"$1\"\n  shift\n  [[ -z \"$skip\" ]] && case \"$arg\" in\n    -)\n      term=1\n      ;;\n    --help)\n      help\n      ;;\n    --version)\n      echo \"sk-tmux (with sk $(\"$sk\" --version))\"\n      exit\n      ;;\n    -p*|-w*|-h*|-x*|-y*|-d*|-u*|-r*|-l*)\n      if [[ \"$arg\" =~ ^-[pwhxy] ]]; then\n        [[ \"$opt\" =~ \"-E\" ]] || opt=\"-E\"\n      elif [[ \"$arg\" =~ ^.[lr] ]]; then\n        opt=\"-h\"\n        if [[ \"$arg\" =~ ^.l ]]; then\n          opt=\"$opt -d\"\n          swap=\"; swap-pane -D ; select-pane -L\"\n          close=\"; tmux swap-pane -D\"\n        fi\n      else\n        opt=\"\"\n        if [[ \"$arg\" =~ ^.u ]]; then\n          opt=\"$opt -d\"\n          swap=\"; swap-pane -D ; select-pane -U\"\n          close=\"; tmux swap-pane -D\"\n        fi\n      fi\n      if [[ ${#arg} -gt 2 ]]; then\n        size=\"${arg:2}\"\n      else\n        if [[ \"$1\" =~ ^[0-9%,]+$ ]] || [[ \"$1\" =~ ^[A-Z]$ ]]; then\n          size=\"$1\"\n          shift\n        else\n          continue\n        fi\n      fi\n\n      if [[ \"$arg\" =~ ^-p ]]; then\n        if [[ -n \"$size\" ]]; then\n          w=${size%%,*}\n          h=${size##*,}\n          opt=\"$opt -w$w -h$h\"\n        fi\n      elif [[ \"$arg\" =~ ^-[whxy] ]]; then\n        opt=\"$opt ${arg:0:2}$size\"\n      elif [[ \"$size\" =~ %$ ]]; then\n        size=${size:0:((${#size}-1))}\n        if [[ -n \"$swap\" ]]; then\n          opt=\"$opt -p $(( 100 - size ))\"\n        else\n          opt=\"$opt -p $size\"\n        fi\n      else\n        if [[ -n \"$swap\" ]]; then\n          if [[ \"$arg\" =~ ^.l ]]; then\n            max=$columns\n          else\n            max=$lines\n          fi\n          size=$(( max - size ))\n          [[ $size -lt 0 ]] && size=0\n          opt=\"$opt -l $size\"\n        else\n          opt=\"$opt -l $size\"\n        fi\n      fi\n      ;;\n    --)\n      # \"--\" can be used to separate sk-tmux options from sk options to\n      # avoid conflicts\n      skip=1\n      tmux_args=(\"${args[@]}\")\n      args=()\n      continue\n      ;;\n    *)\n      args+=(\"$arg\")\n      ;;\n  esac\n  [[ -n \"$skip\" ]] && args+=(\"$arg\")\ndone\n\nif [[ -z \"$TMUX\" ]]; then\n  \"$sk\" \"${args[@]}\"\n  exit $?\nfi\n\n# --height option is not allowed\nargs=(\"${args[@]}\" \"--no-height\")\n\n# Handle zoomed tmux pane without popup options by moving it to a temp window\nif [[ ! \"$opt\" =~ \"-E\" ]] && tmux list-panes -F '#F' | grep -q Z; then\n  zoomed_without_popup=1\n  original_window=$(tmux display-message -p \"#{window_id}\")\n  tmp_window=$(tmux new-window -d -P -F \"#{window_id}\" \"bash -c 'while :; do for c in \\\\| / - '\\\\;' do sleep 0.2; printf \\\"\\\\r\\$c sk-tmux is running\\\\r\\\"; done; done'\")\n  tmux swap-pane -t $tmp_window \\; select-window -t $tmp_window\nfi\n\nset -e\n\n# Clean up named pipes on exit\nid=$RANDOM\nargsf=\"${TMPDIR:-/tmp}/sk-args-$id\"\nfifo1=\"${TMPDIR:-/tmp}/sk-fifo1-$id\"\nfifo2=\"${TMPDIR:-/tmp}/sk-fifo2-$id\"\nfifo3=\"${TMPDIR:-/tmp}/sk-fifo3-$id\"\ntmux_win_opts=( $(tmux show-window-options remain-on-exit \\; show-window-options synchronize-panes | sed '/ off/d; s/^/set-window-option /; s/$/ \\\\;/') )\ncleanup() {\n  \\rm -f $argsf $fifo1 $fifo2 $fifo3\n\n  # Restore tmux window options\n  if [[ \"${#tmux_win_opts[@]}\" -gt 0 ]]; then\n    eval \"tmux ${tmux_win_opts[*]}\"\n  fi\n\n  # Remove temp window if we were zoomed without popup options\n  if [[ -n \"$zoomed_without_popup\" ]]; then\n    tmux display-message -p \"#{window_id}\" > /dev/null\n    tmux swap-pane -t $original_window \\; \\\n      select-window -t $original_window \\; \\\n      kill-window -t $tmp_window \\; \\\n      resize-pane -Z\n  fi\n\n  if [ $# -gt 0 ]; then\n    trap - EXIT\n    exit 130\n  fi\n}\ntrap 'cleanup 1' SIGUSR1\ntrap 'cleanup' EXIT\n\nenvs=\"export TERM=$TERM \"\n[[ \"$opt\" =~ \"-E\" ]] && SKIM_DEFAULT_OPTIONS=\"--margin 0,1 $SKIM_DEFAULT_OPTIONS\"\n[[ -n \"$SKIM_DEFAULT_OPTIONS\"    ]] && envs=\"$envs SKIM_DEFAULT_OPTIONS=$(printf %q \"$SKIM_DEFAULT_OPTIONS\")\"\n[[ -n \"$SKIM_DEFAULT_COMMAND\" ]] && envs=\"$envs SKIM_DEFAULT_COMMAND=$(printf %q \"$SKIM_DEFAULT_COMMAND\")\"\necho \"$envs;\" > \"$argsf\"\n\n# Build arguments to sk\nopts=$(printf \"%q \" \"${args[@]}\")\n\npppid=$$\necho -n \"trap 'kill -SIGUSR1 -$pppid' EXIT SIGINT SIGTERM;\" >> $argsf\nclose=\"; trap - EXIT SIGINT SIGTERM $close\"\n\nexport TMUX=$(cut -d , -f 1,2 <<< \"$TMUX\")\nmkfifo -m o+w $fifo2\nif [[ \"$opt\" =~ \"-E\" ]]; then\n  cat $fifo2 &\n  if [[ -n \"$term\" ]] || [[ -t 0 ]]; then\n    cat <<< \"\\\"$sk\\\" $opts > $fifo2; out=\\$? $close; exit \\$out\" >> $argsf\n  else\n    mkfifo $fifo1\n    cat <<< \"\\\"$sk\\\" $opts < $fifo1 > $fifo2; out=\\$? $close; exit \\$out\" >> $argsf\n    cat <&0 > $fifo1 &\n  fi\n  tmux popup -d \"$PWD\" \"${tmux_args[@]}\" $opt \"bash $argsf\" > /dev/null 2>&1\n  exit $?\nfi\n\nmkfifo -m o+w $fifo3\nif [[ -n \"$term\" ]] || [[ -t 0 ]]; then\n  cat <<< \"\\\"$sk\\\" $opts > $fifo2; echo \\$? > $fifo3 $close\" >> $argsf\nelse\n  mkfifo $fifo1\n  cat <<< \"\\\"$sk\\\" $opts < $fifo1 > $fifo2; echo \\$? > $fifo3 $close\" >> $argsf\n  cat <&0 > $fifo1 &\nfi\n\ntmux set-window-option synchronize-panes off \\;\\\n  set-window-option remain-on-exit off \\;\\\n  split-window -c \"$PWD\" $opt \"${tmux_args[@]}\" \"bash -c 'exec -a sk bash $argsf'\" $swap \\\n  > /dev/null 2>&1 || { \"$sk\" \"${args[@]}\"; exit $?; }\ncat $fifo2\nexit \"$(cat $fifo3)\"\n"
  },
  {
    "path": "cliff.toml",
    "content": "# git-cliff ~ configuration file\n# https://git-cliff.org/docs/configuration\n\n\n[changelog]\n# A Tera template to be rendered for each release in the changelog.\n# See https://keats.github.io/tera/docs/#introduction\nbody = \"\"\"\n{%- macro remote_url() -%}\n  https://github.com/{{ remote.github.owner }}/{{ remote.github.repo }}\n{%- endmacro -%}\n\n{% if version %}\\\n    ## [{{ version | trim_start_matches(pat=\"v\") }}] - {{ timestamp | date(format=\"%Y-%m-%d\") }}\n{% else %}\\\n    ## [unreleased]\n{% endif %}\\\n{% for group, commits in commits | group_by(attribute=\"group\") %}\n    ### {{ group | striptags | trim | upper_first }}\n    {% for commit in commits %}\n        - {% if commit.scope %}*({{ commit.scope }})* {% endif %}\\\n            {% if commit.breaking %}[**breaking**] {% endif %}\\\n            {{ commit.message | upper_first }}\\\n    {% endfor %}\n{% endfor %}\n\n{%- if github.contributors | filter(attribute=\"is_first_time\", value=true) | length != 0 %}\n    ### New Contributors\n{%- endif -%}\n\n{% for contributor in github.contributors | filter(attribute=\"is_first_time\", value=true) %}\n  * @{{ contributor.username }} made their first contribution\n    {%- if contributor.pr_number %} in \\\n      [#{{ contributor.pr_number }}]({{ self::remote_url() }}/pull/{{ contributor.pr_number }}) \\\n    {%- endif %}\n{%- endfor %}\\n\n\n{%- if github.contributors | filter(attribute=\"is_first_time\", value=true) | length != 0 %}{% raw %}\\n{% endraw -%}{% endif %}\n\n\"\"\"\nheader = \"\"\"\n# Changelog\n\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\"\"\"\n# Remove leading and trailing whitespaces from the changelog's body.\nfooter = \"<!-- generated by git-cliff -->\"\ntrim = true\n# Render body even when there are no releases to process.\nrender_always = true\n# An array of regex based postprocessors to modify the changelog.\npostprocessors = [\n    # Replace the placeholder <REPO> with a URL.\n    #{ pattern = '<REPO>', replace = \"https://github.com/orhun/git-cliff\" },\n]\n# render body even when there are no releases to process\n# render_always = true\n# output file path\n# output = \"test.md\"\n\n[git]\n# Parse commits according to the conventional commits specification.\n# See https://www.conventionalcommits.org\nconventional_commits = true\n# Exclude commits that do not match the conventional commits specification.\nfilter_unconventional = true\n# Require all commits to be conventional.\n# Takes precedence over filter_unconventional.\nrequire_conventional = false\n# Split commits on newlines, treating each line as an individual commit.\nsplit_commits = false\n# An array of regex based parsers to modify commit messages prior to further processing.\ncommit_preprocessors = [\n    # Allow commits named `type!(scope): desc` and treat them like `type(scope)!: desc`\n    { pattern = '(.*)!\\((.*)\\):(.*)', replace = \"$1($2)!:$3\" },\n]\n# Prevent commits that are breaking from being excluded by commit parsers.\nprotect_breaking_commits = false\n# An array of regex based parsers for extracting data from the commit message.\n# Assigns commits to groups.\n# Optionally sets the commit's scope and can decide to exclude commits from further processing.\ncommit_parsers = [\n    { message = \"^feat\", group = \"<!-- 0 -->🚀 Features\" },\n    { message = \"^fix\", group = \"<!-- 1 -->🐛 Bug Fixes\" },\n    { message = \"^doc\", group = \"<!-- 3 -->📚 Documentation\" },\n    { message = \"^perf\", group = \"<!-- 4 -->⚡ Performance\" },\n    { message = \"^refactor\", group = \"<!-- 2 -->🚜 Refactor\" },\n    { message = \"^style\", group = \"<!-- 5 -->🎨 Styling\" },\n    { message = \"^test\", group = \"<!-- 6 -->🧪 Testing\" },\n    { message = \"^chore\\\\(release\\\\): prepare for\", skip = true },\n    { message = \"^chore\\\\(deps.*\\\\)\", skip = true },\n    { message = \"^chore\\\\(pr\\\\)\", skip = true },\n    { message = \"^chore\\\\(pull\\\\)\", skip = true },\n    { message = \"^chore|^ci\", group = \"<!-- 7 -->⚙️ Miscellaneous Tasks\" },\n    { body = \".*security\", group = \"<!-- 8 -->🛡️ Security\" },\n    { message = \"^revert\", group = \"<!-- 9 -->◀️ Revert\" },\n    { message = \"^release\", skip = true },\n    { message = \".*\", group = \"<!-- 10 -->💼 Other\" },\n]\n# Exclude commits that are not matched by any commit parser.\nfilter_commits = false\n# Fail on a commit that is not matched by any commit parser.\nfail_on_unmatched_commit = false\n# An array of link parsers for extracting external references, and turning them into URLs, using regex.\nlink_parsers = []\n# Include only the tags that belong to the current branch.\nuse_branch_tags = false\n# Order releases topologically instead of chronologically.\ntopo_order = false\n# Order commits topologically instead of chronologically.\ntopo_order_commits = true\n# Order of commits in each group/release within the changelog.\n# Allowed values: newest, oldest\nsort_commits = \"oldest\"\n# Process submodules commits\nrecurse_submodules = false\n"
  },
  {
    "path": "codecov.yml",
    "content": "# Make Codecov statuses informational so coverage is visible but won't fail PRs\ncoverage:\n  status:\n    default_rules:\n      flag_coverage_not_uploaded_behavior: pass\n    project:\n      default:\n        target: auto\n        threshold: 0\n        informational: true\n    patch:\n      default:\n        target: auto\n        threshold: 0\n        informational: true\n"
  },
  {
    "path": "dist-workspace.toml",
    "content": "[workspace]\nmembers = [\"cargo:.\"]\n\n# Config for 'dist'\n[dist]\n# The preferred dist version to use in CI (Cargo.toml SemVer syntax)\ncargo-dist-version = \"0.30.3\"\n# CI backends to support\nci = \"github\"\n# The installers to generate for each app\ninstallers = [\"shell\"]\n# Target platforms to build apps for (Rust target-triple syntax)\ntargets = [\"aarch64-apple-darwin\", \"aarch64-unknown-linux-gnu\", \"x86_64-apple-darwin\", \"x86_64-unknown-linux-gnu\", \"x86_64-unknown-linux-musl\"]\n# Path that installers should place binaries in\ninstall-path = \"CARGO_HOME\"\n# Whether to install an updater program\ninstall-updater = false\ninclude = [\"./man/\", \"./shell/\"]\nplan-jobs = [\"./test\"]\npublish-jobs = [\"./publish\"]\npublish-prereleases = true\n"
  },
  {
    "path": "examples/ansi.rs",
    "content": "extern crate skim;\nuse skim::{prelude::*, reader::CommandCollector};\n\npub fn main() {\n    env_logger::init();\n\n    let glogm = \"git log --oneline --color=always | head -n10\";\n\n    let options = SkimOptionsBuilder::default()\n        .height(\"50%\")\n        .cmd(glogm)\n        .preview(\"echo {}\")\n        .multi(true)\n        .reverse(true)\n        .cmd_collector(Rc::new(RefCell::new(SkimItemReader::new(\n            SkimItemReaderOption::default().ansi(true),\n        ))) as Rc<RefCell<dyn CommandCollector>>)\n        .build()\n        .unwrap();\n\n    log::debug!(\"Options: ansi {}\", options.ansi);\n\n    let selected_items = Skim::run_with(options, None)\n        .map(|out| out.selected_items)\n        .unwrap_or_default();\n\n    for item in selected_items.iter() {\n        println!(\"selected: {}\", item.output());\n    }\n}\n"
  },
  {
    "path": "examples/async.rs",
    "content": "use skim::{Skim, prelude::SkimOptionsBuilder};\n\n#[tokio::main]\nasync fn main() {\n    let options = SkimOptionsBuilder::default().build().unwrap();\n    Skim::run_with(options, None).unwrap();\n}\n"
  },
  {
    "path": "examples/base.rs",
    "content": "use skim::prelude::*;\n\nfn main() -> color_eyre::Result<()> {\n    let opts = SkimOptionsBuilder::default().multi(true).reverse(true).build()?;\n    let res = Skim::run_items(opts, [\"hello\", \"world\"])?;\n\n    for item in res.selected_items {\n        println!(\"Selected {} (id {})\", item.output(), item.rank.index);\n    }\n\n    Ok(())\n}\n"
  },
  {
    "path": "examples/cmd_collector.rs",
    "content": "extern crate skim;\nuse reader::CommandCollector;\nuse skim::prelude::*;\n\nstruct BasicSkimItem {\n    value: String,\n}\n\nimpl SkimItem for BasicSkimItem {\n    fn text(&self) -> Cow<'_, str> {\n        Cow::Borrowed(&self.value)\n    }\n}\n\nstruct BasicCmdCollector {\n    pub items: Vec<String>,\n}\n\nimpl CommandCollector for BasicCmdCollector {\n    fn invoke(&mut self, _cmd: &str, _components_to_stop: Arc<AtomicUsize>) -> (SkimItemReceiver, Sender<i32>) {\n        let (tx, rx) = unbounded();\n        let (tx_interrupt, _rx_interrupt) = unbounded();\n        let mut batch = Vec::new();\n        while let Some(value) = self.items.pop() {\n            let item = BasicSkimItem { value };\n            batch.push(Arc::from(item) as Arc<dyn SkimItem>);\n        }\n        if !batch.is_empty() {\n            tx.send(batch).unwrap();\n        }\n\n        (rx, tx_interrupt)\n    }\n}\n\nfn main() {\n    let cmd_collector = BasicCmdCollector {\n        items: vec![String::from(\"foo\"), String::from(\"bar\"), String::from(\"baz\")],\n    };\n    let options = SkimOptionsBuilder::default()\n        .cmd_collector(Rc::from(RefCell::from(cmd_collector)))\n        .build()\n        .unwrap();\n\n    let selected_items = Skim::run_with(options, None)\n        .map(|out| out.selected_items)\n        .unwrap_or_default();\n\n    for item in selected_items.iter() {\n        println!(\"{}\", item.output());\n    }\n}\n"
  },
  {
    "path": "examples/custom_action_keybinding.rs",
    "content": "extern crate skim;\nuse crossterm::event::{KeyCode, KeyEvent, KeyModifiers};\nuse skim::prelude::*;\nuse skim::tui::event::{Action, ActionCallback, Event};\nuse std::io::Cursor;\n\n/// This example demonstrates how to bind custom action callbacks to keyboard shortcuts.\n///\n/// It shows how to:\n/// 1. Create custom action callbacks (both sync and async)\n/// 2. Bind them to specific key combinations\n/// 3. Use them interactively in skim\nfn main() {\n    // Create a synchronous callback that adds a prefix to the query.\n    // Use `new_sync` for plain closures that do not need to await anything.\n    let add_prefix_callback = ActionCallback::new_sync(|app: &mut skim::tui::App| {\n        // Get current query and add prefix\n        let current_query = app.input.value.clone();\n\n        // Clear the line first, then add new content\n        let mut events = vec![Event::Action(Action::UnixLineDiscard)];\n\n        let prefix = \"TODO: \";\n        for ch in prefix.chars() {\n            events.push(Event::Action(Action::AddChar(ch)));\n        }\n\n        // Add back the original query\n        for ch in current_query.chars() {\n            events.push(Event::Action(Action::AddChar(ch)));\n        }\n\n        Ok(events)\n    });\n\n    // Create an async callback that selects all and exits.\n    // Use `new` for async closures or blocks that may await futures.\n    let select_all_callback = ActionCallback::new(|app: &mut skim::tui::App| {\n        let count = app.item_pool.len();\n        async move {\n            // Async work could go here (e.g. HTTP requests, file I/O, …).\n            Ok(vec![\n                Event::Action(Action::SelectAll),\n                Event::Action(Action::Accept(Some(format!(\"Selected {count} items\")))),\n            ])\n        }\n    });\n\n    // Build basic options\n    let mut options = SkimOptionsBuilder::default()\n        .multi(true)\n        .prompt(\"Select> \")\n        .header(\"<C-p>: add prefix to prompt\\t<C-a>: select all and exit with count\")\n        .build()\n        .unwrap();\n\n    // Now manually add custom keybindings to the keymap\n    // We can access the keymap directly since it's public\n\n    // Bind Ctrl-P to add prefix\n    options.keymap.insert(\n        KeyEvent::new(KeyCode::Char('p'), KeyModifiers::CONTROL),\n        vec![Action::Custom(add_prefix_callback)],\n    );\n\n    // Bind Ctrl-A to select all with message\n    options.keymap.insert(\n        KeyEvent::new(KeyCode::Char('a'), KeyModifiers::CONTROL),\n        vec![Action::Custom(select_all_callback)],\n    );\n\n    // Create sample items\n    let items = [\n        \"Write documentation\",\n        \"Fix bug #123\",\n        \"Implement feature X\",\n        \"Review pull request\",\n        \"Update dependencies\",\n        \"Refactor module Y\",\n        \"Add unit tests\",\n        \"Optimize performance\",\n    ];\n\n    let item_reader = SkimItemReader::default();\n    let input = items.join(\"\\n\");\n    let item_source = item_reader.of_bufread(Cursor::new(input));\n\n    // Run skim with our custom keybindings\n    if let Ok(output) = Skim::run_with(options, Some(item_source)) {\n        println!(\"output: {output:?}\");\n    } else {\n        println!(\"\\nAborted!\");\n    }\n}\n"
  },
  {
    "path": "examples/custom_item.rs",
    "content": "extern crate skim;\nuse skim::prelude::*;\n\nstruct MyItem {\n    inner: String,\n}\n\nimpl SkimItem for MyItem {\n    fn text(&self) -> Cow<'_, str> {\n        Cow::Borrowed(&self.inner)\n    }\n\n    fn preview(&self, _context: PreviewContext) -> ItemPreview {\n        if self.inner.starts_with(\"color\") {\n            ItemPreview::AnsiText(format!(\"\\x1b[31mhello:\\x1b[m\\n{}\", self.inner))\n        } else {\n            ItemPreview::Text(format!(\"hello:\\n{}\", self.inner))\n        }\n    }\n}\n\nfn main() {\n    let options = SkimOptionsBuilder::default()\n        .height(\"50%\")\n        .multi(true)\n        .preview(\"\") // preview should be specified to enable preview window\n        .build()\n        .unwrap();\n\n    env_logger::init();\n\n    let (tx_item, rx_item): (SkimItemSender, SkimItemReceiver) = unbounded();\n    let _ = tx_item.send(vec![\n        Arc::new(MyItem {\n            inner: \"color aaaa\".to_string(),\n        }) as Arc<dyn SkimItem>,\n        Arc::new(MyItem {\n            inner: \"bbbb\".to_string(),\n        }) as Arc<dyn SkimItem>,\n        Arc::new(MyItem {\n            inner: \"ccc\".to_string(),\n        }) as Arc<dyn SkimItem>,\n    ]);\n    drop(tx_item); // so that skim could know when to stop waiting for more items.\n\n    let selected_items = Skim::run_with(options, Some(rx_item))\n        .map(|out| out.selected_items)\n        .unwrap_or_default();\n\n    for item in selected_items.iter() {\n        println!(\"{}\", item.output());\n    }\n}\n"
  },
  {
    "path": "examples/custom_keybinding_actions.rs",
    "content": "extern crate skim;\nuse crossterm::event::{KeyCode, KeyModifiers};\nuse skim::prelude::*;\n\n// No action is actually performed on your filesystem!\n// This example only produce friendly print statements!\n\nfn fake_delete_item(item: &str) {\n    println!(\"Deleting item `{item}`...\");\n}\n\nfn fake_create_item(item: &str) {\n    println!(\"Creating a new item `{item}`...\");\n}\n\nfn main() {\n    // Note: `accept` is a keyword used define custom actions.\n    // For full list of accepted keywords see `parse_event` in `src/event.rs`.\n    // `delete` and `create` are arbitrary keywords used for this example.\n    let options = SkimOptionsBuilder::default()\n        .multi(true)\n        .bind(vec![\"bs:abort\".into(), \"enter:accept\".into()])\n        .build()\n        .unwrap();\n\n    if let Ok(out) = Skim::run_with(options, None) {\n        match (out.final_key.code, out.final_key.modifiers) {\n            // Delete each selected item\n            (KeyCode::Backspace, KeyModifiers::NONE) => {\n                out.selected_items.iter().for_each(|i| fake_delete_item(&i.text()))\n            }\n            // Create a new item based on the query\n            (KeyCode::Enter, KeyModifiers::NONE) => fake_create_item(out.query.as_ref()),\n            _ => (),\n        }\n    };\n}\n"
  },
  {
    "path": "examples/downcast.rs",
    "content": "extern crate skim;\nuse skim::prelude::*;\n\n/// This example illustrates downcasting custom structs that implement\n/// `SkimItem` after calling `Skim::run_with`.\n\n#[derive(Debug, Clone)]\nstruct Item {\n    text: String,\n}\n\nimpl SkimItem for Item {\n    fn text(&self) -> Cow<'_, str> {\n        Cow::Borrowed(&self.text)\n    }\n\n    fn preview(&self, _context: PreviewContext) -> ItemPreview {\n        ItemPreview::Text(self.text.to_owned())\n    }\n}\n\npub fn main() {\n    let options = SkimOptionsBuilder::default()\n        .height(\"50%\")\n        .multi(true)\n        .preview(\"\")\n        .build()\n        .unwrap();\n\n    let (tx, rx): (SkimItemSender, SkimItemReceiver) = unbounded();\n\n    tx.send(vec![\n        Arc::new(Item { text: \"a\".into() }) as Arc<dyn SkimItem>,\n        Arc::new(Item { text: \"b\".into() }) as Arc<dyn SkimItem>,\n        Arc::new(Item { text: \"c\".into() }) as Arc<dyn SkimItem>,\n    ])\n    .unwrap();\n\n    drop(tx);\n\n    let selected_items = Skim::run_with(options, Some(rx))\n        .map(|out| out.selected_items)\n        .unwrap_or_default()\n        .iter()\n        .map(|selected_item| selected_item.downcast_item::<Item>().unwrap().to_owned())\n        .collect::<Vec<Item>>();\n\n    for item in selected_items {\n        println!(\"{item:?}\");\n    }\n}\n"
  },
  {
    "path": "examples/fine-grain.rs",
    "content": "extern crate skim;\nuse color_eyre::Result;\nuse skim::prelude::*;\n\n#[tokio::main(flavor = \"current_thread\")]\npub async fn main() -> Result<()> {\n    let options = SkimOptionsBuilder::default().height(\"50%\").multi(true).build()?;\n\n    let (tx_item, rx_item): (SkimItemSender, SkimItemReceiver) = unbounded();\n    let mut skim = Skim::init(options, Some(rx_item))?;\n\n    skim.start();\n    skim.init_tui()?;\n\n    let event_tx = skim.event_sender();\n\n    skim.enter().await?;\n\n    let output = skim\n        .run_until(async move {\n            for i in 1..=10 {\n                let _ = event_tx.try_send(Event::ClearItems);\n                let _ = tx_item.send(vec![Arc::new(format!(\"item {i}\")) as Arc<dyn SkimItem>]);\n                tokio::time::sleep(std::time::Duration::from_secs(1)).await;\n            }\n        })\n        .await?;\n\n    for item in output.selected_items.iter() {\n        println!(\"{}\", item.output());\n    }\n\n    Ok(())\n}\n"
  },
  {
    "path": "examples/fuzzy_matcher_fz.rs",
    "content": "use skim::fuzzy_matcher::FuzzyMatcher;\nuse skim::fuzzy_matcher::clangd::ClangdMatcher;\nuse skim::fuzzy_matcher::skim::SkimMatcherV2;\nuse std::env;\nuse std::io::{self, BufRead};\nuse std::process::exit;\n\ntype IndexType = usize;\n\npub fn main() {\n    let args: Vec<String> = env::args().collect();\n\n    // arg parsing (manually)\n    let mut arg_iter = args.iter().skip(1);\n    let mut pattern = \"\".to_string();\n    let mut algorithm = Some(\"skim\");\n\n    while let Some(arg) = arg_iter.next() {\n        if arg == \"--algo\" {\n            algorithm = arg_iter.next().map(String::as_ref);\n        } else {\n            pattern = arg.to_string();\n        }\n    }\n\n    if pattern.is_empty() {\n        eprintln!(\"Usage: echo <piped_input> | fz --algo [skim|clangd] <pattern>\");\n        exit(1);\n    }\n\n    let matcher: Box<dyn FuzzyMatcher> = match algorithm {\n        Some(\"skim\") | Some(\"skim_v2\") => Box::new(SkimMatcherV2::default()),\n        Some(\"clangd\") => Box::new(ClangdMatcher::default()),\n        _ => panic!(\"Algorithm not supported: {:?}\", algorithm),\n    };\n\n    let stdin = io::stdin();\n    for line in stdin.lock().lines() {\n        if let Ok(line) = line\n            && let Some((score, indices)) = matcher.fuzzy_indices(&line, &pattern)\n        {\n            println!(\"{:8}: {}\", score, wrap_matches(&line, &indices));\n        }\n    }\n}\n\nfn wrap_matches(line: &str, indices: &[IndexType]) -> String {\n    let mut ret = String::new();\n    let mut peekable = indices.iter().peekable();\n    let ansi_invert: &str = str::from_utf8(&[27, b'[', b'7', b'm']).unwrap();\n    let ansi_reset: &str = str::from_utf8(&[27, b'[', b'0', b'm']).unwrap();\n    for (idx, ch) in line.chars().enumerate() {\n        let next_id = **peekable.peek().unwrap_or(&&(line.len() as IndexType));\n        if next_id == (idx as IndexType) {\n            ret.push_str(format!(\"{}{}{}\", ansi_invert, ch, ansi_reset).as_str());\n            peekable.next();\n        } else {\n            ret.push(ch);\n        }\n    }\n\n    ret\n}\n"
  },
  {
    "path": "examples/multiple_runs.rs",
    "content": "use skim::{Skim, prelude::SkimOptionsBuilder};\n\n// Hint: use `ps -T -p $(pgrep -f target/debug/examples/multiple_runs)` to watch threads while the\n// different invocations run, and make sure none is leaking through\nfn main() {\n    for i in 0..3 {\n        let opts = SkimOptionsBuilder::default()\n            .header(format!(\"run {i}\"))\n            .cmd(\"cat benches/fixtures/10M.txt\")\n            .build()\n            .unwrap();\n        let res = Skim::run_with(opts, None).unwrap();\n        #[cfg(target_os = \"linux\")]\n        unsafe {\n            nix::libc::malloc_trim(0);\n        }\n        println!(\n            \"run {i}: {:?}, sleeping for 5 secs\",\n            res.selected_items.first().map(|x| x.output())\n        );\n        std::thread::sleep(std::time::Duration::from_secs(5));\n    }\n}\n"
  },
  {
    "path": "examples/nth.rs",
    "content": "extern crate skim;\nuse skim::prelude::*;\nuse std::io::Cursor;\n\n/// `nth` option is supported by SkimItemReader.\n/// In the example below, with `nth=2` set, only `123` could be matched.\npub fn main() {\n    let input = \"foo 123\";\n\n    let options = SkimOptionsBuilder::default().query(\"f\").build().unwrap();\n    let item_reader = SkimItemReader::new(SkimItemReaderOption::default().nth(vec![\"2\"].into_iter()).build());\n\n    let items = item_reader.of_bufread(Cursor::new(input));\n    let selected_items = Skim::run_with(options, Some(items))\n        .map(|out| out.selected_items)\n        .unwrap_or_default();\n\n    for item in selected_items.iter() {\n        println!(\"{}\", item.output());\n    }\n}\n"
  },
  {
    "path": "examples/option_builder.rs",
    "content": "extern crate skim;\nuse skim::prelude::*;\nuse std::io::Cursor;\n\npub fn main() {\n    let item_reader = SkimItemReader::default();\n\n    //==================================================\n    // first run\n    let options = SkimOptionsBuilder::default().height(\"50%\").multi(true).build().unwrap();\n    let input = \"aaaaa\\nbbbb\\nccc\";\n    let items = item_reader.of_bufread(Cursor::new(input));\n    let selected_items = Skim::run_with(options, Some(items))\n        .map(|out| out.selected_items)\n        .unwrap_or_default();\n\n    for item in selected_items.iter() {\n        println!(\"{}\", item.output());\n    }\n\n    //==================================================\n    // second run\n    let options = SkimOptionsBuilder::default().height(\"50%\").multi(true).build().unwrap();\n    let input = \"11111\\n22222\\n333333333\";\n    let items = item_reader.of_bufread(Cursor::new(input));\n    let selected_items = Skim::run_with(options, Some(items))\n        .map(|out| out.selected_items)\n        .unwrap_or_default();\n\n    for item in selected_items.iter() {\n        println!(\"{}\", item.output());\n    }\n}\n"
  },
  {
    "path": "examples/preview_callback.rs",
    "content": "use std::io::Cursor;\n\nuse skim::prelude::*;\n\npub fn main() {\n    env_logger::init();\n    let options = SkimOptionsBuilder::default()\n        .multi(true)\n        .preview_fn(PreviewCallback::from(|items: Vec<Arc<dyn SkimItem>>| {\n            items.iter().map(|s| s.text().to_ascii_uppercase()).collect::<Vec<_>>()\n        }))\n        .build()\n        .unwrap();\n    let item_reader = SkimItemReader::default();\n\n    let input = \"aaaaa\\nbbbb\\nccc\";\n    let items = item_reader.of_bufread(Cursor::new(input));\n    let selected_items = Skim::run_with(options, Some(items))\n        .map(|out| out.selected_items)\n        .unwrap_or_default();\n\n    for item in selected_items.iter() {\n        println!(\"{}\", item.output());\n    }\n}\n"
  },
  {
    "path": "examples/receiver_multi.rs",
    "content": "use std::sync::Arc;\n\nuse skim::prelude::*;\n\nfn main() {\n    let (sender, receiver): (SkimItemSender, SkimItemReceiver) = unbounded();\n    let mut batch = Vec::new();\n    for num in 1..=8 {\n        batch.push(Arc::new(format!(\"Option {num}\")) as Arc<dyn SkimItem>);\n    }\n    sender.send(batch).unwrap();\n    drop(sender); // bug replicates even without this\n\n    let _ = Skim::run_with(\n        SkimOptionsBuilder::default().multi(true).build().unwrap(),\n        Some(receiver),\n    );\n}\n"
  },
  {
    "path": "examples/sample.rs",
    "content": "extern crate skim;\nuse skim::prelude::*;\n\npub fn main() {\n    let options = SkimOptions::default();\n\n    let selected_items = Skim::run_with(options, None)\n        .map(|out| out.selected_items)\n        .unwrap_or_default();\n\n    for item in selected_items.iter() {\n        println!(\"{}\", item.output());\n    }\n}\n"
  },
  {
    "path": "examples/selector.rs",
    "content": "extern crate skim;\nuse skim::prelude::*;\n\nstruct BasicSelector {\n    pub pat: String,\n}\n\nimpl Selector for BasicSelector {\n    fn should_select(&self, _index: usize, item: &dyn SkimItem) -> bool {\n        item.text().contains(&self.pat)\n    }\n}\n\npub fn main() {\n    let selector = BasicSelector {\n        pat: String::from(\"examples\"),\n    };\n    let options = SkimOptionsBuilder::default()\n        .multi(true)\n        .selector(Rc::from(selector))\n        .query(\"skim/\")\n        .build()\n        .unwrap();\n\n    let selected_items = Skim::run_with(options, None)\n        .map(|out| out.selected_items)\n        .unwrap_or_default();\n\n    for item in selected_items.iter() {\n        println!(\"{}\", item.output());\n    }\n}\n"
  },
  {
    "path": "examples/tick.rs",
    "content": "use skim::prelude::*;\n\n#[tokio::main]\npub async fn main() -> color_eyre::eyre::Result<()> {\n    let opts = SkimOptionsBuilder::default().cmd(\"cat bench_data.txt\").build()?;\n\n    println!(\"START\");\n    let mut skim = Skim::init(opts, None)?;\n    skim.start();\n    skim.init_tui()?;\n    skim.enter().await?;\n    while !skim.tick().await? {\n        if skim.reader_done() && skim.matcher_stopped() {\n            skim.event_sender()\n                .send(Event::Action(Action::Accept(Some(String::from(\"Done\")))))\n                .await?;\n        }\n    }\n    println!(\"DONE: {:?}\", skim.output());\n    color_eyre::eyre::Ok(())\n}\n"
  },
  {
    "path": "flake.nix",
    "content": "{\n  description = \"Nix flake for skim development\";\n\n  inputs.nixpkgs.url = \"https://channels.nixos.org/nixpkgs-unstable/nixexprs.tar.xz\";\n\n  outputs = inputs: let\n    inherit (inputs.nixpkgs) lib;\n    systems = lib.systems.flakeExposed;\n    eachSystem = lib.genAttrs systems;\n    pkgsFor = inputs.nixpkgs.legacyPackages;\n  in {\n    devShells = eachSystem (system: {\n      default = pkgsFor.${system}.mkShellNoCC {\n        packages = with pkgsFor.${system}; [\n          cargo-nextest\n          cargo-insta\n          cargo-llvm-cov\n          cargo-edit\n          cargo-public-api\n          git-cliff\n          libclang\n          binutils\n          tmux\n          rustup\n          just\n          hyperfine\n          uv\n          valgrind\n          python313Packages.matplotlib\n          python313Packages.requests\n        ];\n        shellHook = let\n          pkgs = pkgsFor.${system};\n        in ''\n          export LIBCLANG_PATH=\"${pkgs.libclang.lib}/lib\"\n          export LD_LIBRARY_PATH=\"${pkgs.valgrind.out}/lib:$LD_LIBRARY_PATH\"\n        '';\n      };\n    });\n\n    formatter = eachSystem (system: pkgsFor.${system}.nixfmt);\n  };\n}\n"
  },
  {
    "path": "justfile",
    "content": "bump-version version:\n    sed -i 's/^version = \".*\"/version = \"{{ version }}\"/' ./Cargo.toml\n\ngenerate-files:\n    SKIM_DEFAULT_OPTIONS= cargo run -- --man > ./man/man1/sk.1\n    SKIM_DEFAULT_OPTIONS= cargo run -- --shell bash > ./shell/completion.bash\n    SKIM_DEFAULT_OPTIONS= cargo run -- --shell zsh > ./shell/completion.zsh\n    SKIM_DEFAULT_OPTIONS= cargo run -- --shell fish > ./shell/completion.fish\n    SKIM_DEFAULT_OPTIONS= cargo run -- --shell nushell > ./shell/completion.nu\n\nchangelog version:\n    git cliff -p CHANGELOG.md -t 'v{{ version }}' -u\n\nrelease version: (bump-version version) generate-files (changelog version) test\n    cargo generate-lockfile\n    echo '{{ version }}' > shell/version.txt\n    git add CHANGELOG.md Cargo.lock Cargo.toml man/ shell/\n    git commit -m 'release: v{{ version }}'\n    git tag 'v{{ version }}'\n    read -p \"Press any key to confirm pushing tag v{{ version }}\"\n    git push\n    git push --tags\n\nauto-release:\n    git switch master\n    git pull\n    just release $(git cliff --bumped-version | sed 's/v\\(.*\\)/\\1/')\n\ntest target=\"\":\n    cargo test --doc\n    -cargo nextest run --features test-utils {{ target }}\n    tmux kill-session -t skim_e2e\n"
  },
  {
    "path": "man/man1/sk-tmux.1",
    "content": ".ig\nThe MIT License (MIT)\n\nCopyright (c) 2019 Jinzhou Zhang\nCopyright (c) 2017 Junegunn Choi\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..\n.TH sk-tmux 1 \"Oct 2018\" \"sk 0.10.4\" \"sk-tmux - open sk in tmux split pane\"\n\n.SH NAME\nsk-tmux - open sk in tmux split pane\n\n.SH SYNOPSIS\n.B sk-tmux [-u|-d [HEIGHT[%]]] [-l|-r [WIDTH[%]]] [--] [sk OPTIONS]\n\n.SH DESCRIPTION\nsk-tmux is a wrapper script for sk that opens sk in a tmux split pane. It is\ndesigned to work just like sk except that it does not take up the whole\nscreen. You can safely use sk-tmux instead of sk in your scripts as the extra\noptions will be silently ignored if you're not on tmux.\n\n.SH OPTIONS\n.SS Layout\n\n(default: \\fB-d 50%\\fR)\n\n.TP\n.B \"-u [height[%]]\"\nSplit above (up)\n.TP\n.B \"-d [height[%]]\"\nSplit below (down)\n.TP\n.B \"-l [width[%]]\"\nSplit left\n.TP\n.B \"-r [width[%]]\"\nSplit right\n"
  },
  {
    "path": "man/man1/sk.1",
    "content": ".ie \\n(.g .ds Aq \\(aq\n.el .ds Aq '\n.TH sk 1  \"sk 4.0.0\" \n.ie \\n(.g .ds Aq \\(aq\n.el .ds Aq '\n.SH NAME\nsk \\- Fuzzy Finder in rust!\n.ie \\n(.g .ds Aq \\(aq\n.el .ds Aq '\n.SH SYNOPSIS\n\\fBsk\\fR [\\fB\\-\\-tac\\fR] [\\fB\\-\\-min\\-query\\-length\\fR] [\\fB\\-\\-no\\-sort\\fR] [\\fB\\-t\\fR|\\fB\\-\\-tiebreak\\fR] [\\fB\\-n\\fR|\\fB\\-\\-nth\\fR] [\\fB\\-\\-with\\-nth\\fR] [\\fB\\-d\\fR|\\fB\\-\\-delimiter\\fR] [\\fB\\-e\\fR|\\fB\\-\\-exact\\fR] [\\fB\\-\\-regex\\fR] [\\fB\\-\\-algo\\fR] [\\fB\\-\\-case\\fR] [\\fB\\-\\-typos\\fR] [\\fB\\-\\-no\\-typos\\fR] [\\fB\\-\\-normalize\\fR] [\\fB\\-\\-split\\-match\\fR] [\\fB\\-\\-last\\-match\\fR] [\\fB\\-\\-scheme\\fR] [\\fB\\-b\\fR|\\fB\\-\\-bind\\fR] [\\fB\\-m\\fR|\\fB\\-\\-multi\\fR] [\\fB\\-\\-no\\-multi\\fR] [\\fB\\-\\-no\\-mouse\\fR] [\\fB\\-c\\fR|\\fB\\-\\-cmd\\fR] [\\fB\\-i\\fR|\\fB\\-\\-interactive\\fR] [\\fB\\-I \\fR] [\\fB\\-\\-color\\fR] [\\fB\\-\\-no\\-hscroll\\fR] [\\fB\\-\\-keep\\-right\\fR] [\\fB\\-\\-skip\\-to\\-pattern\\fR] [\\fB\\-\\-no\\-clear\\-if\\-empty\\fR] [\\fB\\-\\-no\\-clear\\-start\\fR] [\\fB\\-\\-no\\-clear\\fR] [\\fB\\-\\-show\\-cmd\\-error\\fR] [\\fB\\-\\-cycle\\fR] [\\fB\\-\\-disabled\\fR] [\\fB\\-\\-layout\\fR] [\\fB\\-\\-reverse\\fR] [\\fB\\-\\-height\\fR] [\\fB\\-\\-no\\-height\\fR] [\\fB\\-\\-min\\-height\\fR] [\\fB\\-\\-margin\\fR] [\\fB\\-p\\fR|\\fB\\-\\-prompt\\fR] [\\fB\\-\\-cmd\\-prompt\\fR] [\\fB\\-\\-selector\\fR] [\\fB\\-\\-multi\\-selector\\fR] [\\fB\\-\\-ansi\\fR] [\\fB\\-\\-tabstop\\fR] [\\fB\\-\\-info\\fR] [\\fB\\-\\-no\\-info\\fR] [\\fB\\-\\-inline\\-info\\fR] [\\fB\\-\\-header\\fR] [\\fB\\-\\-header\\-lines\\fR] [\\fB\\-\\-border\\fR] [\\fB\\-\\-wrap\\fR] [\\fB\\-\\-history\\fR] [\\fB\\-\\-history\\-size\\fR] [\\fB\\-\\-cmd\\-history\\fR] [\\fB\\-\\-cmd\\-history\\-size\\fR] [\\fB\\-\\-preview\\fR] [\\fB\\-\\-preview\\-window\\fR] [\\fB\\-q\\fR|\\fB\\-\\-query\\fR] [\\fB\\-\\-cmd\\-query\\fR] [\\fB\\-\\-read0\\fR] [\\fB\\-\\-print0\\fR] [\\fB\\-\\-print\\-query\\fR] [\\fB\\-\\-print\\-cmd\\fR] [\\fB\\-\\-print\\-score\\fR] [\\fB\\-\\-print\\-header\\fR] [\\fB\\-\\-print\\-current\\fR] [\\fB\\-\\-output\\-format\\fR] [\\fB\\-\\-no\\-strip\\-ansi\\fR] [\\fB\\-1\\fR|\\fB\\-\\-select\\-1\\fR] [\\fB\\-0\\fR|\\fB\\-\\-exit\\-0\\fR] [\\fB\\-\\-sync\\fR] [\\fB\\-\\-pre\\-select\\-n\\fR] [\\fB\\-\\-pre\\-select\\-pat\\fR] [\\fB\\-\\-pre\\-select\\-items\\fR] [\\fB\\-\\-pre\\-select\\-file\\fR] [\\fB\\-f\\fR|\\fB\\-\\-filter\\fR] [\\fB\\-\\-shell\\fR] [\\fB\\-\\-shell\\-bindings\\fR] [\\fB\\-\\-man\\fR] [\\fB\\-\\-listen\\fR] [\\fB\\-\\-remote\\fR] [\\fB\\-\\-tmux\\fR] [\\fB\\-\\-log\\-level\\fR] [\\fB\\-\\-log\\-file\\fR] [\\fB\\-\\-expect\\fR] [\\fB\\-h\\fR|\\fB\\-\\-help\\fR] [\\fB\\-V\\fR|\\fB\\-\\-version\\fR] \n.ie \\n(.g .ds Aq \\(aq\n.el .ds Aq '\n.SH OPTIONS\n.TP\n\\fB\\-h\\fR, \\fB\\-\\-help\\fR\nPrint help (see a summary with \\*(Aq\\-h\\*(Aq)\n.TP\n\\fB\\-V\\fR, \\fB\\-\\-version\\fR\nPrint version\n.SH SEARCH\n.TP\n\\fB\\-\\-tac\\fR\nShow results in reverse order\n\nOften used in combination with \\-\\-no\\-sort\n.TP\n\\fB\\-\\-min\\-query\\-length\\fR \\fI<MIN_QUERY_LENGTH>\\fR\nMinimum query length to start showing results\n\nOnly show results when the query is at least this many characters long\n.TP\n\\fB\\-\\-no\\-sort\\fR\nDo not sort the results\n\nOften used in combination with \\-\\-tac Example: history | sk \\-\\-tac \\-\\-no\\-sort\n.TP\n\\fB\\-t\\fR, \\fB\\-\\-tiebreak\\fR \\fI<TIEBREAK>\\fR [default: score,begin,end]\nComma\\-separated list of sort criteria to apply when the scores are tied.\n\n* **score**: Score of the fuzzy match algorithm\n\n    \\- Each criterion could be negated, e.g. (\\-index)\n    \\- Each criterion should appear only once in the list\n.br\n\n.br\n\\fIPossible values:\\fR\n.RS 14\n.IP \\(bu 2\nscore\n.IP \\(bu 2\n\\-score\n.IP \\(bu 2\nbegin\n.IP \\(bu 2\n\\-begin\n.IP \\(bu 2\nend\n.IP \\(bu 2\n\\-end\n.IP \\(bu 2\nlength\n.IP \\(bu 2\n\\-length\n.IP \\(bu 2\nindex\n.IP \\(bu 2\n\\-index\n.IP \\(bu 2\npathname\n.IP \\(bu 2\n\\-pathname\n.RE\n.TP\n\\fB\\-n\\fR, \\fB\\-\\-nth\\fR \\fI<NTH>\\fR [default: ]\nFields to be matched\n\nA field index expression can be a non\\-zero integer or a range expression (`[BEGIN]..[END]`).\n`\\-\\-nth` and `\\-\\-with\\-nth` take a comma\\-separated list of field index expressions.\n\n**Examples:**\n    1      The 1st field\n    2      The 2nd field\n    \\-1     The last field\n    \\-2     The 2nd to last field\n    3..5   From the 3rd field to the 5th field\n    2..    From the 2nd field to the last field\n    ..\\-3   From the 1st field to the 3rd to the last field\n    ..     All the fields\n.TP\n\\fB\\-\\-with\\-nth\\fR \\fI<WITH_NTH>\\fR [default: ]\nFields to be transformed\n\nSee nth for the details\n.TP\n\\fB\\-d\\fR, \\fB\\-\\-delimiter\\fR \\fI<DELIMITER>\\fR [default: [\\\\t\\\\n ]+]\nDelimiter between fields\n\nIn regex format, default to AWK\\-style. Escape sequences like \\\\x00, \\\\t, \\\\n are supported.\n.TP\n\\fB\\-e\\fR, \\fB\\-\\-exact\\fR\nRun in exact mode\n.TP\n\\fB\\-\\-regex\\fR\nStart in regex mode instead of fuzzy\\-match\n.TP\n\\fB\\-\\-algo\\fR \\fI<ALGORITHM>\\fR [default: arinae]\nFuzzy matching algorithm\n\narinae (ari) Latest algorithm\n`skim_v2` Legacy skim algorithm\nclangd  Used in clangd for keyword completion\nfzy     Algorithm from fzy (<https://github.com/jhawthorn/fzy>)\n.br\n\n.br\n\\fIPossible values:\\fR\n.RS 14\n.IP \\(bu 2\nskim_v2: Improved skim fuzzy matching algorithm (v2)\n.IP \\(bu 2\nclangd: Clangd fuzzy matching algorithm\n.IP \\(bu 2\nfzy: Fzy matching algorithm (https://github.com/jhawthorn/fzy)\n.IP \\(bu 2\nfrizbee: Frizbee matching algorithm, typo resistant\n.IP \\(bu 2\narinae: Arinae: typo\\-resistant & natural algorithm, default\n.RE\n.TP\n\\fB\\-\\-case\\fR \\fI<CASE>\\fR [default: smart]\nCase sensitivity\n\nDetermines whether or not to ignore case while matching Note: this is not used for the Frizbee matcher, it uses a penalty system to favor case\\-sensitivity without enforcing it\n.br\n\n.br\n\\fIPossible values:\\fR\n.RS 14\n.IP \\(bu 2\nrespect: Case\\-sensitive matching\n.IP \\(bu 2\nignore: Case\\-insensitive matching\n.IP \\(bu 2\nsmart: Smart case: case\\-insensitive unless query contains uppercase\n.RE\n.TP\n\\fB\\-\\-typos\\fR [\\fI<TYPOS>\\fR] [default: disabled]\nEnable typo\\-tolerant matching\n\nWhen passed without a value (\\-\\-typos), uses adaptive formula (pattern_length / 4). When passed with a value (e.g. \\-\\-typos=2), uses that exact number as the maximum allowed typos. \\-\\-typos=0 explicitly disables typo tolerance. Applies to both fzy and frizbee matchers.\n.TP\n\\fB\\-\\-no\\-typos\\fR\nDisable typo\\-resistant matching\n.TP\n\\fB\\-\\-normalize\\fR\nNormalize unicode characters\n\nWhen set, normalize accents and other unicode diacritics/others\n.TP\n\\fB\\-\\-split\\-match\\fR [\\fI<SPLIT_MATCH>...\\fR]\nEnable split matching and set delimiter\n\nSplit matching runs the matcher in splits: foo:bar will match all items matching foo, then :, then bar if the delimiter is present, or match normally if not.\n.TP\n\\fB\\-\\-last\\-match\\fR\nHighlight the last match found, not the first one This makes tiebreak more pertinent on path items where we want to prioritize a match on the last parts\n.TP\n\\fB\\-\\-scheme\\fR \\fI<SCHEME>\\fR [default: default]\n\n.br\n\\fIPossible values:\\fR\n.RS 14\n.IP \\(bu 2\ndefault: Default scheme, no modifications to the options\n.IP \\(bu 2\npath: Path scheme: will find the furthest match in the item and set pathname as the main tiebreak\n.IP \\(bu 2\nhistory: History scheme: will force index as the first tiebreak\n.RE\n.SH INTERFACE\n.TP\n\\fB\\-b\\fR, \\fB\\-\\-bind\\fR [\\fI<BIND>...\\fR] [default: ]\nComma separated list of bindings\n\nYou can customize key bindings of sk with `\\-\\-bind` option which takes a  comma\\-separated  list  of\nkey binding expressions. Each key binding expression follows the following format: `<key>:<action>`\nSee the [KEYBINDS] section for details\n\n**Example**: `sk \\-\\-bind=ctrl\\-j:accept,ctrl\\-k:kill\\-line`\n\n## Multiple actions can be chained using + separator.\n\n**Example**: `sk \\-\\-bind \\*(Aqctrl\\-a:select\\-all+accept\\*(Aq`\n\n# Special behaviors\n\nWith `execute(...)` and `reload(...)` action, you can execute arbitrary commands without leaving sk.\nFor example, you can turn sk into a simple file browser by binding enter key to less command like follows:\n\n```bash\nsk \\-\\-bind \"enter:execute(less {})\"\n```\n\nNote: if no argument is supplied to reload, the default command is run.\n\nYou can use the same placeholder expressions as in \\-\\-preview.\n\nsk  switches  to  the  alternate screen when executing a command. However, if the command is ex‐\npected to complete quickly, and you are not interested in its output, you might want to use exe‐\ncute\\-silent instead, which silently executes the command without the  switching.  Note  that  sk\nwill  not  be  responsive  until the command is complete. For asynchronous execution, start your\ncommand as a background process (i.e. appending &).\n\nWith if\\-query\\-empty and if\\-query\\-not\\-empty action, you could specify the action to  execute  de‐\npends on the query condition. For example:\n\n`sk \\-\\-bind \\*(Aqctrl\\-d:if\\-query\\-empty(abort)+delete\\-char\\*(Aq`\n\nIf  the query is empty, skim will execute abort action, otherwise execute delete\\-char action. It\nis equal to ‘delete\\-char/eof‘.\n.TP\n\\fB\\-m\\fR, \\fB\\-\\-multi\\fR\nEnable multiple selection\n\nUses Tab and S\\-Tab by default for selection\n.TP\n\\fB\\-\\-no\\-multi\\fR\nDisable multiple selection\n.TP\n\\fB\\-\\-no\\-mouse\\fR\nDisable mouse\n.TP\n\\fB\\-c\\fR, \\fB\\-\\-cmd\\fR \\fI<CMD>\\fR\nCommand to invoke dynamically in interactive mode\n\nWill be invoked using sh \\-c\n.TP\n\\fB\\-i\\fR, \\fB\\-\\-interactive\\fR\nStart skim in interactive mode\n\nIn interactive mode, sk will run the command specified by \\-\\-cmd option and display the results.\n.TP\n\\fB\\-I\\fR \\fI<REPLSTR>\\fR [default: {}]\nReplace replstr with the selected item in commands\n.TP\n\\fB\\-\\-color\\fR \\fI<COLOR>\\fR\nSet color theme\n\nFormat: [BASE][,COLOR:ANSI[:ATTR1:ATTR2:..]]\nSee [THEME] section for details\n.TP\n\\fB\\-\\-no\\-hscroll\\fR\nDisable horizontal scroll\n.TP\n\\fB\\-\\-keep\\-right\\fR\nKeep the right end of the line visible on overflow\n\nEffective only when the query string is empty\n.TP\n\\fB\\-\\-skip\\-to\\-pattern\\fR \\fI<SKIP_TO_PATTERN>\\fR\nShow the matched pattern at the line start\n\nLine  will  start  with  the  start of the matched pattern. Effective only when the query\nstring is empty. Was designed to skip showing starts of paths of rg/grep results.\n\ne.g. sk \\-i \\-c \"rg {q} \\-\\-color=always\" \\-\\-skip\\-to\\-pattern \\*(Aq[^/]*:\\*(Aq \\-\\-ansi\n.TP\n\\fB\\-\\-no\\-clear\\-if\\-empty\\fR\nDo not clear previous line if the command returns an empty result\n\nDo not clear previous items if new command returns empty result. This might be useful  to\nreduce flickering when typing new commands and the half\\-complete commands are not valid.\n\nThis is not the default behavior because similar use cases for grep and rg have already been op‐\ntimized where empty query results actually mean \"empty\" and previous results should be\ncleared.\n.TP\n\\fB\\-\\-no\\-clear\\-start\\fR\nDo not clear items on start\n.TP\n\\fB\\-\\-no\\-clear\\fR\nDo not clear screen on exit\n\nDo not clear finder interface on exit. If skim was started in full screen mode, it will not switch back to the original  screen, so you\\*(Aqll have to manually run tput rmcup to return. This option can be used to avoid flickering of the screen when your application needs to start skim multiple times in order.\n.TP\n\\fB\\-\\-show\\-cmd\\-error\\fR\nShow error message if command fails\n.TP\n\\fB\\-\\-cycle\\fR\nCycle the results by wrapping around when scrolling\n.TP\n\\fB\\-\\-disabled\\fR\nDisable matching entirely\n.SH LAYOUT\n.TP\n\\fB\\-\\-layout\\fR \\fI<LAYOUT>\\fR [default: default]\nSet layout\n.br\n\n.br\n\\fIPossible values:\\fR\n.RS 14\n.IP \\(bu 2\ndefault: Display from the bottom of the screen\n.IP \\(bu 2\nreverse: Display from the top of the screen\n.IP \\(bu 2\nreverse\\-list: Display from the top of the screen, prompt at the bottom\n.RE\n.TP\n\\fB\\-\\-reverse\\fR\nShorthand for reverse layout\n.TP\n\\fB\\-\\-height\\fR \\fI<HEIGHT>\\fR [default: 100%]\nHeight of skim\\*(Aqs window\n\nCan either be a row count or a percentage\n.TP\n\\fB\\-\\-no\\-height\\fR\nDisable height (force full screen)\n.TP\n\\fB\\-\\-min\\-height\\fR \\fI<MIN_HEIGHT>\\fR [default: 10]\nMinimum height of skim\\*(Aqs window\n\nUseful when the height is set as a percentage\nIgnored when \\-\\-height is not specified\n.TP\n\\fB\\-\\-margin\\fR \\fI<MARGIN>\\fR [default: 0]\nScreen margin\n\nFor each side, can be either a row count or a percentage of the terminal size\n\nFormat can be one of:\n    \\- TRBL\n    \\- TB,RL\n    \\- T,RL,B\n    \\- T,R,B,L\nExample: 1,10%\n.TP\n\\fB\\-p\\fR, \\fB\\-\\-prompt\\fR \\fI<PROMPT>\\fR [default: > ]\nSet prompt\n.TP\n\\fB\\-\\-cmd\\-prompt\\fR \\fI<CMD_PROMPT>\\fR [default: c> ]\nSet prompt in command mode\n.TP\n\\fB\\-\\-selector\\fR \\fI<SELECTOR_ICON>\\fR [default: >]\nSet selected item icon\n.TP\n\\fB\\-\\-multi\\-selector\\fR \\fI<MULTI_SELECT_ICON>\\fR [default: >]\nSet selected item icon\n.SH DISPLAY\n.TP\n\\fB\\-\\-ansi\\fR\nParse ANSI color codes in input strings\n\nWhen using skim as a library, this has no effect and ansi parsing should be enabled by manually injecting a cmd_collector like so:\n\n  use skim::prelude::*;\n  \n  let _options = SkimOptionsBuilder::default()\n    .cmd(\"ls \\-\\-color\")\n    .cmd_collector(Rc::new(RefCell::new(SkimItemReader::new(\n      SkimItemReaderOption::default().ansi(true),\n      ))) as Rc<RefCell<dyn CommandCollector>>)\n    .build()\n    .unwrap();\n\n.TP\n\\fB\\-\\-tabstop\\fR \\fI<TABSTOP>\\fR [default: 8]\nNumber of spaces that make up a tab\n.TP\n\\fB\\-\\-info\\fR \\fI<INFO>\\fR [default: default]\nSet matching result count display position\n\n  \\- hidden: do not display info\n  \\- inline: display info in the same row as the input\n  \\- default: display info in a dedicated row above the input\n.br\n\n.br\n\\fIPossible values:\\fR\n.RS 14\n.IP \\(bu 2\ndefault\n.IP \\(bu 2\ninline\n.IP \\(bu 2\nhidden\n.RE\n.TP\n\\fB\\-\\-no\\-info\\fR\nAlias for \\-\\-info=hidden\n.TP\n\\fB\\-\\-inline\\-info\\fR\nAlias for \\-\\-info=inline\n.TP\n\\fB\\-\\-header\\fR \\fI<HEADER>\\fR\nSet header, displayed next to the info\n\nThe  given  string  will  be printed as the sticky header. The lines are displayed in the given order from top to bottom regardless of \\-\\-layout option, and  are  not  affected  by \\-\\-with\\-nth. ANSI color codes are processed even when \\-\\-ansi is not set.\n.TP\n\\fB\\-\\-header\\-lines\\fR \\fI<HEADER_LINES>\\fR [default: 0]\nNumber of lines of the input treated as header\n\nThe  first N lines of the input are treated as the sticky header. When \\-\\-with\\-nth is set, the lines are transformed just like the other lines that follow.\n.TP\n\\fB\\-\\-border\\fR [\\fI<BORDER>...\\fR]\nDraw borders around the UI components\n.br\n\n.br\n\\fIPossible values:\\fR\n.RS 14\n.IP \\(bu 2\nplain\n.IP \\(bu 2\nrounded\n.IP \\(bu 2\ndouble\n.IP \\(bu 2\nthick\n.IP \\(bu 2\nlight\\-double\\-dashed\n.IP \\(bu 2\nheavy\\-double\\-dashed\n.IP \\(bu 2\nlight\\-triple\\-dashed\n.IP \\(bu 2\nheavy\\-triple\\-dashed\n.IP \\(bu 2\nlight\\-quadruple\\-dashed\n.IP \\(bu 2\nheavy\\-quadruple\\-dashed\n.IP \\(bu 2\nquadrant\\-inside\n.IP \\(bu 2\nquadrant\\-outside\n.RE\n.TP\n\\fB\\-\\-wrap\\fR\nWrap items in the item list\n.TP\n\\fB\\-\\-tmux\\fR [\\fI<TMUX>...\\fR]\nRun in a tmux popup\n\nFormat: `sk \\-\\-tmux <center|top|bottom|left|right>[,SIZE[%]][,SIZE[%]]`\n\nDepending on the direction, the order and behavior of the sizes varies:\n\nDefault: center,50%\n.SH HISTORY\n.TP\n\\fB\\-\\-history\\fR \\fI<HISTORY_FILE>\\fR\nHistory file\n\nLoad search history from the specified file and update the file on completion.\n\nWhen enabled, CTRL\\-N and CTRL\\-P are automatically remapped to next\\-history and previous\\-history.\n.TP\n\\fB\\-\\-history\\-size\\fR \\fI<HISTORY_SIZE>\\fR [default: 1000]\nMaximum number of query history entries to keep\n.TP\n\\fB\\-\\-cmd\\-history\\fR \\fI<CMD_HISTORY_FILE>\\fR\nCommand history file\n\nLoad command query history from the specified file and update the file on completion.\n\nWhen enabled, CTRL\\-N and CTRL\\-P are automatically remapped to next\\-history and previous\\-history.\n.TP\n\\fB\\-\\-cmd\\-history\\-size\\fR \\fI<CMD_HISTORY_SIZE>\\fR [default: 1000]\nMaximum number of query history entries to keep\n.SH PREVIEW\n.TP\n\\fB\\-\\-preview\\fR \\fI<PREVIEW>\\fR\nPreview command\n\nExecute the given command for the current line and display the result on the preview window. {} in the command\nis the placeholder that is replaced to the single\\-quoted string of the current line. To transform the replace‐\nment string, specify field index expressions between the braces (See FIELD INDEX EXPRESSION for the details).\n\n**Examples**:\n\n```bash\nsk \\-\\-preview=\\*(Aqhead \\-$LINES {}\\*(Aq\nls \\-l | sk \\-\\-preview=\"echo user={3} when={\\-4..\\-2}; cat {\\-1}\" \\-\\-header\\-lines=1\n.TP\n\\fB\\-\\-preview\\-window\\fR \\fI<PREVIEW_WINDOW>\\fR [default: right:50%]\nPreview window layout\n\nFormat: [up|down|left|right][:SIZE[%]][:hidden][:[no]wrap][:[no]pty][:+SCROLL[\\-OFFSET]]\n\nDetermine  the  layout of the preview window. If the argument ends with: hidden, the preview window will be hidden by default until toggle\\-preview action is triggered. Long lines are truncated by default. Line wrap can be enabled with :wrap flag. For more interactive commands or previews that draw complex interfaces, the preview can use a PTY with the :pty flag.\n\nNote: the preview will run in a PTY (interactive session) on linux and when wrap is unset\n\nIf size is given as 0, preview window will not be visible, but sk will still execute the command in the background.\n\n+SCROLL[\\-OFFSET] determines the initial scroll offset of the preview window. SCROLL can be either a  numeric  integer or  a  single\\-field index expression that refers to a numeric integer. The optional \\-OFFSET part is for adjusting the base offset so that you can see the text above it. It should be given as a numeric integer (\\-INTEGER), or as a denom‐ inator form (\\-/INTEGER) for specifying a fraction of the preview window height.\n\nExamples:\n\n  # Non\\-default scroll window positions and sizes\n  sk \\-\\-preview=\"head {}\" \\-\\-preview\\-window=up:30%\n  sk \\-\\-preview=\"file {}\" \\-\\-preview\\-window=down:2\n  \n  # Initial scroll offset is set to the line number of each line of\n  # git grep output *minus* 5 lines (\\-5)\n  git grep \\-\\-line\\-number \\*(Aq\\*(Aq |\n    sk \\-\\-delimiter:  \\-\\-preview \\*(Aqnl {1}\\*(Aq \\-\\-preview\\-window +{2}\\-5\n  \n              # Preview with bat, matching line in the middle of the window (\\-/2)\n              git grep \\-\\-line\\-number \\*(Aq\\*(Aq |\n                sk \\-\\-delimiter : \\\\\n                    \\-\\-preview \\*(Aqbat \\-\\-style=numbers \\-\\-color=always \\-\\-highlight\\-line {2} {1}\\*(Aq \\\\\n                    \\-\\-preview\\-window +{2}\\-/2\n\n.SH SCRIPTING\n.TP\n\\fB\\-q\\fR, \\fB\\-\\-query\\fR \\fI<QUERY>\\fR\nInitial query\n.TP\n\\fB\\-\\-cmd\\-query\\fR \\fI<CMD_QUERY>\\fR\nInitial query in interactive mode\n.TP\n\\fB\\-\\-read0\\fR\nRead input delimited by ASCII NUL(\\\\0) characters\n.TP\n\\fB\\-\\-print0\\fR\nPrint output delimited by ASCII NUL(\\\\0) characters\n.TP\n\\fB\\-\\-print\\-query\\fR\nPrint the query as the first line\n.TP\n\\fB\\-\\-print\\-cmd\\fR\nPrint the command as the first line (after print\\-query)\n.TP\n\\fB\\-\\-print\\-score\\fR\nPrint the score after each item\n.TP\n\\fB\\-\\-print\\-header\\fR\nPrint the header as the first line (after print\\-score)\n.TP\n\\fB\\-\\-print\\-current\\fR\nPrint the current (highlighted) item as the first line (after print\\-header)\n.TP\n\\fB\\-\\-output\\-format\\fR \\fI<OUTPUT_FORMAT>\\fR\nSet the output format If set, overrides all print_ options Will be expanded the same way as preview or commands\n.TP\n\\fB\\-\\-no\\-strip\\-ansi\\fR\nPrint the ANSI codes, making the output exactly match the input even when \\-\\-ansi is on\n.TP\n\\fB\\-1\\fR, \\fB\\-\\-select\\-1\\fR\nDo not enter the TUI if the query passed in \\-q matches only one item and return it\n.TP\n\\fB\\-0\\fR, \\fB\\-\\-exit\\-0\\fR\nDo not enter the TUI if the query passed in \\-q does not match any item\n.TP\n\\fB\\-\\-sync\\fR\nSynchronous search for multi\\-staged filtering\n\nSynchronous search for multi\\-staged filtering. If specified, skim will launch ncurses finder only after the input stream is complete. e.g. sk \\-\\-multi | sk \\-\\-sync\n.TP\n\\fB\\-\\-pre\\-select\\-n\\fR \\fI<PRE_SELECT_N>\\fR [default: 0]\nPre\\-select the first n items in multi\\-selection mode\n.TP\n\\fB\\-\\-pre\\-select\\-pat\\fR \\fI<PRE_SELECT_PAT>\\fR [default: ]\nPre\\-select the matched items in multi\\-selection mode\n\nCheck the doc for the detailed syntax: https://docs.rs/regex/1.4.1/regex/\n.TP\n\\fB\\-\\-pre\\-select\\-items\\fR \\fI<PRE_SELECT_ITEMS>\\fR [default: ]\nPre\\-select the items separated by newline character\n\nExample: \\*(Aqitem1\\\\nitem2\\*(Aq\n.TP\n\\fB\\-\\-pre\\-select\\-file\\fR \\fI<PRE_SELECT_FILE>\\fR\nPre\\-select the items read from this file\n.TP\n\\fB\\-f\\fR, \\fB\\-\\-filter\\fR \\fI<FILTER>\\fR\nQuery for filter mode\n.TP\n\\fB\\-\\-shell\\fR \\fI<SHELL>\\fR\nGenerate shell completion script\n\nGenerate completion script for the specified shell: bash, zsh, fish, etc. The output can be directly sourced or saved to a file for automatic loading. Examples: source <(sk \\-\\-shell bash) (immediate use) sk \\-\\-shell bash >> ~/.bash_completion (persistent use)\n\nSupported shells: bash, zsh, fish, powershell, elvish\n\nNote: While PowerShell completions are supported, Windows is not supported for now.\n.br\n\n.br\n\\fIPossible values:\\fR\n.RS 14\n.IP \\(bu 2\nbash: Bourne Again SHell\n.IP \\(bu 2\nelvish: Elvish shell\n.IP \\(bu 2\nfish: Friendly Interactive SHell\n.IP \\(bu 2\nnushell: Nushell (nu)\n.IP \\(bu 2\npower\\-shell: PowerShell\n.IP \\(bu 2\nzsh: Zsh\n.RE\n.TP\n\\fB\\-\\-shell\\-bindings\\fR\nGenerate shell key bindings \\- only for bash, zsh and fish\n\nGenerate key bindings script after the shell completions See the shell option for more details\n.TP\n\\fB\\-\\-man\\fR\nGenerate man page and output it to stdout\n.TP\n\\fB\\-\\-listen\\fR [\\fI<LISTEN>...\\fR]\nRun an IPC socket with optional name (defaults to sk)\n\nThe socket expects Actions in Ron format (similar to Rust code), see ./src/tui/event.rs for all possible Actions To write to it, see the \\-\\-remote option or the man page\n.TP\n\\fB\\-\\-remote\\fR [\\fI<REMOTE>...\\fR]\nSend commands to an IPC socket with optional name (defaults to sk)\n\nThe commands are read from stdin, one per line, in the same format as the actions in the bind flag. They can also be chained using + as a separator. All other arguments will be ignored\n.TP\n\\fB\\-\\-log\\-level\\fR \\fI<LOG_LEVEL>\\fR\nSet the log level\n.TP\n\\fB\\-\\-log\\-file\\fR \\fI<LOG_FILE>\\fR\nPipe log output to a file\n.SH DEPRECATED\n.TP\n\\fB\\-\\-expect\\fR \\fI<EXPECT>\\fR [default: ]\nDeprecated, kept for compatibility purposes. See accept() bind instead\n.ie \\n(.g .ds Aq \\(aq\n.el .ds Aq '\n.SH MODES\n\n.SS \"Normal mode\"\n\n.br\nIn normal mode, sk reads the input from stdin and displays the results interactively,\n.br\nand the query is then used to fuzzily filter among the input lines.\n.br\n\n.SS \"Interactive mode\"\n\n.br\nInteractive mode is a special mode that allows you to run a command interactively and display\n.br\nthe results. It is enabled by the `\\-\\-interactive` (or `\\-i`) option or by binding the\n.br\n`toggle\\-interactive` action (default: <ctrl\\-q>).\n.br\nThe command is specified with the `\\-\\-cmd` option.\n.br\n\n.br\nExample: `sk \\-\\-cmd \\*(Aqrg {} \\-\\-color=always\\*(Aq \\-\\-interactive` will use `rg` to search for the query\n.br\nin the current directory and display the results interactively.\n.br\n\n.SH SEARCH\n\n.br\nBy default, skim will start in `extended search`, giving some characters will have meaning.\n.br\nExample: `^test rs$ | sh$` will match items starting with test and ending with rs or sh.\n.br\n\n.SS \"AND (space)\"\nA space between terms will act as an \\*(Aqand\\*(Aq operator and will filter for items matching all terms.\n.br\n\n.SS \"OR (|)\"\nA vertical bar between terms will act as an \\*(Aqor\\*(Aq operator and will filter for items matching one of the terms.\n.br\n\n.SS \"Exact match (')\"\n\n.br\nIf a term is prefixed by a `\\*(Aq`, sk will search for exact occurrences of that term.\n.br\nExact search can be enabled by default by the `\\-\\-exact` command\\-line flag. In exact mode, `\\*(Aq` will disable exact matching for that term.\n.br\n\n.SS \"Anchored match (^|$)\"\nIf a term is prefixed by a `^` (resp. suffixed by a `$`), sk will search for matches starting (resp. ending) with that exact term.\n.br\n\n.SS \"Negation (!)\"\nIf a term is prefixed by `!`, sk will exclude the items that match this term.\n.br\n\n.SH KEYBINDS\n\n.br\nKeybinds can be set by the `\\-\\-bind` option, which takes a comma\\-separated list of [key]:[action[+action2].\n.br\nActions can take arguments, specified either between parentheses `reload(ls)` or after a colon `reload:ls`\n.br\n\n.SS \"Available keys (aliases in parentheses)\"\n\n.br\n* ctrl\\-[a\\-z]\n.br\n* ctrl\\-space\n.br\n* ctrl\\-alt\\-[a\\-z]\n.br\n* alt\\-[a\\-zA\\-Z]\n.br\n* alt\\-[0\\-9]\n.br\n* f[1\\-12]\n.br\n* enter\n.br\n* space\n.br\n* bspace      (bs)\n.br\n* alt\\-up\n.br\n* alt\\-down\n.br\n* alt\\-left\n.br\n* alt\\-right\n.br\n* alt\\-enter\n.br\n* alt\\-space\n.br\n* alt\\-bspace  (alt\\-bs)\n.br\n* alt\\-/\n.br\n* tab\n.br\n* btab        (shift\\-tab)\n.br\n* esc\n.br\n* del\n.br\n* up\n.br\n* down\n.br\n* left\n.br\n* right\n.br\n* home\n.br\n* end\n.br\n* pgup\n.br\n* pgdn\n.br\n* shift\\-up\n.br\n* shift\\-down\n.br\n* shift\\-left\n.br\n* shift\\-right\n.br\n* alt\\-shift\\-up\n.br\n* alt\\-shift\\-down\n.br\n* alt\\-shift\\-left\n.br\n* alt\\-shift\\-right\n.br\n* any single character\n.br\n\n.SS \"Actions[:default keys][*notes]\"\n\n.br\n* abort: ctrl\\-c  ctrl\\-q  esc\n.br\n* accept(...): enter *the argument will be printed when the binding is triggered*\n.br\n* append\\-and\\-select\n.br\n* backward\\-char: ctrl\\-b  left\n.br\n* backward\\-delete\\-char: ctrl\\-h  bspace\n.br\n* backward\\-delete\\-char/eof\n.br\n* backward\\-kill\\-word: alt\\-bs\n.br\n* backward\\-word: alt\\-b   shift\\-left\n.br\n* beginning\\-of\\-line: ctrl\\-a  home\n.br\n* clear\\-screen: ctrl\\-l\n.br\n* delete\\-char: del\n.br\n* delete\\-char/eof: ctrl\\-d\n.br\n* deselect\\-all\n.br\n* down: ctrl\\-j  ctrl\\-n  down\n.br\n* end\\-of\\-line: ctrl\\-e  end\n.br\n* execute(...): *arg will be a command, see COMMAND EXPANSION for details\n.br\n* execute\\-silent(...): *arg will be a command, see COMMAND EXPANSION for details\n.br\n* forward\\-char: ctrl\\-f  right\n.br\n* forward\\-word: alt\\-f   shift\\-right\n.br\n* if\\-non\\-matched\n.br\n* if\\-query\\-empty\n.br\n* if\\-query\\-not\\-empty\n.br\n* ignore\n.br\n* kill\\-line\n.br\n* kill\\-word: alt\\-d\n.br\n* next\\-history: ctrl\\-n with `\\-\\-history` or `\\-\\-cmd\\-history`\n.br\n* page\\-down: pgdn\n.br\n* page\\-up: pgup\n.br\n* half\\-page\\-down\n.br\n* half\\-page\\-up\n.br\n* preview\\-up: shift\\-up\n.br\n* preview\\-down: shift\\-down\n.br\n* preview\\-left\n.br\n* preview\\-right\n.br\n* preview\\-page\\-down\n.br\n* preview\\-page\\-up\n.br\n* previous\\-history: ctrl\\-p with `\\-\\-history` or `\\-\\-cmd\\-history`\n.br\n* redraw\n.br\n* refresh\\-cmd\n.br\n* refresh\\-preview\n.br\n* reload(...)\n.br\n* select\\-all\n.br\n* select\\-row\n.br\n* set\\-preview\\-cmd(...): *arg will be a expanded expression, see COMMAND EXPANSION for details\n.br\n* set\\-query(...): *arg will be a expanded expression, see COMMAND EXPANSION for details\n.br\n* toggle\n.br\n* toggle\\-all\n.br\n* toggle+down: ctrl\\-i  tab\n.br\n* toggle\\-in: (\\-\\-layout=reverse ? toggle+up:  toggle+down)\n.br\n* toggle\\-interactive\n.br\n* toggle\\-out: (\\-\\-layout=reverse ? toggle+down:  toggle+up)\n.br\n* toggle\\-preview\n.br\n* toggle\\-preview\\-wrap\n.br\n* toggle\\-sort\n.br\n* toggle+up: btab    shift\\-tab\n.br\n* top\n.br\n* unix\\-line\\-discard: ctrl\\-u\n.br\n* unix\\-word\\-rubout: ctrl\\-w\n.br\n* up: ctrl\\-k  ctrl\\-p  up\n.br\n* yank: ctrl\\-y\n.br\n\n.SH \"COMMAND EXPANSION\"\n\n.br\nIn the `preview` flag, `execute`, `reload`, `set\\-query`... binds, sk will expand placeholders:\n.br\n* {} (or \\-\\-replstr if used) will be expanded to the current item.\n.br\n* {q} (or {cq} for legacy reasons) will be expanded to the current query input.\n.br\n* {+} will be expanded to either the currently selected items in multi\\-select mode, or the current\n.br\n item in single\\-select.\n.br\n* {n} will be expanded to the index of the current item.\n.br\n* {+n} will be expanded to the index(es) of the corresponding {+} item(s).\n.br\n* {FIELD_INDEX_EXPRESSION} will be expanded to the field index expression run against the current\n.br\n item.\n.br\n* {+FIELD_INDEX_EXPRESSION} will be expanded to the field index expression run against the {+}\n.br\n item(s).\n.br\n\n.SS \"Field index expression\"\n\n.br\nskim will expand some expressions between {..}.\n.br\nIt will expand to the corresponding fields, separated by the `\\-\\-delimiter|\\-d` option (see there for details).\n.br\n* `{n}` will be the n\\-th field.\n.br\n* `{n..m}` will be the fields from n to m, inclusive, separated by a space\n.br\n* `{\\-n}` will be the n\\-th, starting from the end, \\-1 will be the last field etc.\n.br\n\n.SH \"ENVIRONMENT VARIABLES\"\n\n.SS SKIM_DEFAULT_COMMAND\nIf set, skim will collect items with this command if no input is piped in (defaults to `find .` if not set)\n.br\n\n.SS SKIM_DEFAULT_OPTIONS\nWill be parsed and used as default options. Example: `\\-\\-reverse \\-\\-multi`\n.br\n\n.SS SKIM_OPTIONS_FILE\n\n.br\nIf the variable is set to the path of an existing file, the contents of this file will be parsed and used as default options.\n.br\nIt supports `#` as a comment start, which can be escaped using `##`.\n.br\nExample:\n.br\n```\n.br\n# Preview\n.br\n\\-\\-preview \\*(Aqecho {}\\*(Aq\n.br\n\\-\\-preview\\-window \\*(Aqleft:30%\\*(Aq # Preview window\n.br\n\\-\\-reverse\n.br\n\\-\\-prompt \\*(Aq## \\*(Aq\n.br\n```\n.br\n\n.SS NO_COLOR\nIf set to a non\\-empty value, will disable coloring\n.br\n\n.SH THEME\n\n.br\nAvailable themes:\n.br\n    * none: base color scheme\n.br\n    * molokai: molokai 256color\n.br\n    * light: light 256color\n.br\n    * 16: dark base16 theme\n.br\n    * bw: black & white theme\n.br\n    * dark | default: dark 256color, default value\n.br\n    * all 4 catppuccin variants:\n.br\n        * catppuccin\\-latte\n.br\n        * catppuccin\\-macchiato\n.br\n        * catppuccin\\-frappe\n.br\n        * catppuccin\\-mocha\n.br\n\n.br\nAvailable color names:\n.br\n    * normal (or empty string): normal text\n.br\n    * matched (or hl): matched text\n.br\n    * current (or fg+): current line foreground\n.br\n    * bg+: current line background (special case, always sets background)\n.br\n    * current_match (or hl+): matched text in current line\n.br\n    * query: query text\n.br\n    * spinner: spinner character\n.br\n    * info: info text (match count)\n.br\n    * prompt: prompt text\n.br\n    * cursor (or pointer): cursor/pointer\n.br\n    * selected (or marker): selected item marker\n.br\n    * header: header text\n.br\n    * border: border lines\n.br\n\n.br\nAdding `\\-fg`, `_fg`, `\\-bg`, `_bg`, `\\-underline`, `_underline` sets the corresponding part of\n.br\nthe color. For instance, `normal\\-fg` (or simply `fg`) will set the foreground normal color.\n.br\n\n.br\nColor formats:\n.br\n    * 0\\-255: ANSI terminal color\n.br\n    * #rrggbb: 24\\-bit color\n.br\n\n.br\nAvailable attrs:\n.br\n    * x | regular: resets the modifiers, use it before the others\n.br\n    * b | bold\n.br\n    * u | underline\n.br\n    * c | crossed\\-out\n.br\n    * d | dim\n.br\n    * i | italic\n.br\n    * r | reverse\n.br\n\n.br\nExample: `\\-\\-color \\*(Aq16,normal\\-fg:0+bold,matched\\-fg:#ffffff+u,cursor\\-bg:#deadbe\\*(Aq` will start with the\n.br\n base 16 theme and override it with a bold ANSI color 0 foreground (black), a hex ffffff (full\n.br\n white) underlined foreground for matched parts and a #deadbe (pale rose, apparently) cursor background.\n.br\n\n.SH LISTEN/REMOTE\n\n.br\nskim can be controlled from other processes, using the `\\-\\-listen` (and optionally `\\-\\-remote`) flags.\n.br\n\n.br\nTo achieve this, run the server instance using `sk \\-\\-listen optional_address` (the address defaults to `sk`).\n.br\nIt will then start listening on a named socket for instructions.\n.br\n\n.br\nTo send instructions, you can use `sk \\-\\-remote optional_address` or any other tool that allows us to interact with such sockets,\n.br\nsuch as `socat` on linux: `echo \\*(AqToggleIn\\*(Aq | socat \\-u STDIN ABSTRACT\\-CONNECT:optional_address`. Instructions correspond to skim\\*(Aqs Actions and need to be sent in Ron format.\n.br\nWhen using `sk \\-\\-remote`, pipe in action chains (see the KEYBINDS section), for instance `echo \\*(Aqup+select\\-row\\*(Aq | sk \\-\\-remote optional_address`\n.br\n\n.SH \"EXIT CODES\"\n\n.br\n* 0: success\n.br\n* 1: no match\n.br\n* 130: interrupt (ctrl\\-c or esc)\n.br\n* others: error\n.br\n\n.ie \\n(.g .ds Aq \\(aq\n.el .ds Aq '\n.SH VERSION\nv4.0.0\n"
  },
  {
    "path": "plugin/skim.vim",
    "content": "\" Copyright (c) 2017 Junegunn Choi\n\"\n\" MIT License\n\"\n\" Permission is hereby granted, free of charge, to any person obtaining\n\" a copy of this software and associated documentation files (the\n\" \"Software\"), to deal in the Software without restriction, including\n\" without limitation the rights to use, copy, modify, merge, publish,\n\" distribute, sublicense, and/or sell copies of the Software, and to\n\" permit persons to whom the Software is furnished to do so, subject to\n\" the following conditions:\n\"\n\" The above copyright notice and this permission notice shall be\n\" included in all copies or substantial portions of the Software.\n\"\n\" THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n\" EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n\" MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n\" NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n\" LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n\" OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n\" WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\nif exists('g:loaded_skim')\n  finish\nendif\nlet g:loaded_skim = 1\n\nif empty($SKIM_DEFAULT_COMMAND)\n    let $SKIM_DEFAULT_COMMAND = \"fd --type f || git ls-tree -r --name-only HEAD || rg --files || ag -l -g \\\"\\\" || find .\"\nendif\n\nlet s:is_win = has('win32') || has('win64')\nif s:is_win && &shellslash\n  set noshellslash\n  let s:base_dir = expand('<sfile>:h:h')\n  set shellslash\nelse\n  let s:base_dir = expand('<sfile>:h:h')\nendif\nif s:is_win\n  let s:term_marker = '&::SKIM'\n\n  function! s:skim_call(fn, ...)\n    let shellslash = &shellslash\n    try\n      set noshellslash\n      return call(a:fn, a:000)\n    finally\n      let &shellslash = shellslash\n    endtry\n  endfunction\n\n  \" Use utf-8 for skim.vim commands\n  \" Return array of shell commands for cmd.exe\n  function! s:enc_to_cp(str)\n    if !has('iconv')\n      return a:str\n    endif\n    if !exists('s:codepage')\n      let s:codepage = libcallnr('kernel32.dll', 'GetACP', 0)\n    endif\n    return iconv(a:str, &encoding, 'cp'.s:codepage)\n  endfunction\n  function! s:wrap_cmds(cmds)\n    return map([\n      \\ '@echo off',\n      \\ 'setlocal enabledelayedexpansion']\n    \\ + (has('gui_running') ? ['set TERM= > nul'] : [])\n    \\ + (type(a:cmds) == type([]) ? a:cmds : [a:cmds])\n    \\ + ['endlocal'],\n    \\ '<SID>enc_to_cp(v:val.\"\\r\")')\n  endfunction\nelse\n  let s:term_marker = \";#SKIM\"\n\n  function! s:skim_call(fn, ...)\n    return call(a:fn, a:000)\n  endfunction\n\n  function! s:wrap_cmds(cmds)\n    return a:cmds\n  endfunction\n\n  function! s:enc_to_cp(str)\n    return a:str\n  endfunction\nendif\n\nfunction! s:shellesc_cmd(arg)\n  let escaped = substitute(a:arg, '[&|<>()@^]', '^&', 'g')\n  let escaped = substitute(escaped, '%', '%%', 'g')\n  let escaped = substitute(escaped, '\"', '\\\\^&', 'g')\n  let escaped = substitute(escaped, '\\(\\\\\\+\\)\\(\\\\^\\)', '\\1\\1\\2', 'g')\n  return '^\"'.substitute(escaped, '\\(\\\\\\+\\)$', '\\1\\1', '').'^\"'\nendfunction\n\nfunction! skim#shellescape(arg, ...)\n  let shell = get(a:000, 0, s:is_win ? 'cmd.exe' : 'sh')\n  if shell =~# 'cmd.exe$'\n    return s:shellesc_cmd(a:arg)\n  endif\n  return s:skim_call('shellescape', a:arg)\nendfunction\n\nfunction! s:skim_getcwd()\n  return s:skim_call('getcwd')\nendfunction\n\nfunction! s:skim_fnamemodify(fname, mods)\n  return s:skim_call('fnamemodify', a:fname, a:mods)\nendfunction\n\nfunction! s:skim_expand(fmt)\n  return s:skim_call('expand', a:fmt, 1)\nendfunction\n\nfunction! s:skim_tempname()\n  return s:skim_call('tempname')\nendfunction\n\nlet s:layout_keys = ['window', 'tmux', 'up', 'down', 'left', 'right']\nlet s:skim_rs = s:base_dir.'/bin/sk'\nlet s:skim_tmux = s:base_dir.'/bin/sk-tmux'\n\nlet s:cpo_save = &cpo\nset cpo&vim\n\nfunction! s:popup_support()\n  return has('nvim') ? has('nvim-0.4') : has('popupwin') && has('patch-8.2.191')\nendfunction\n\nfunction! s:default_layout()\n  return s:popup_support()\n        \\ ? { 'window' : { 'width': 0.9, 'height': 0.8, 'highlight': 'Normal' } }\n        \\ : { 'down': '~40%' }\nendfunction\n\nfunction! skim#exec()\n  if !exists('s:exec')\n    if executable(s:skim_rs)\n      let s:exec = s:skim_rs\n    elseif executable('sk')\n      let s:exec = 'sk'\n    else\n      redraw\n      throw 'skim executable not found'\n    endif\n  endif\n  return s:exec\nendfunction\n\nfunction! s:tmux_enabled()\n  if has('gui_running') || !exists('$TMUX')\n    return 0\n  endif\n\n  if exists('s:tmux')\n    return s:tmux\n  endif\n\n  let s:tmux = 0\n  if !executable(s:skim_tmux)\n    if executable('sk-tmux')\n      let s:skim_tmux = 'sk-tmux'\n    else\n      return 0\n    endif\n  endif\n\n  let output = system('tmux -V')\n  let s:tmux = !v:shell_error && output >= 'tmux 1.7'\n  return s:tmux\nendfunction\n\nfunction! s:escape(path)\n  let path = fnameescape(a:path)\n  return s:is_win ? escape(path, '$') : path\nendfunction\n\nfunction! s:error(msg)\n  echohl ErrorMsg\n  echom a:msg\n  echohl None\nendfunction\n\nfunction! s:warn(msg)\n  echohl WarningMsg\n  echom a:msg\n  echohl None\nendfunction\n\nfunction! s:has_any(dict, keys)\n  for key in a:keys\n    if has_key(a:dict, key)\n      return 1\n    endif\n  endfor\n  return 0\nendfunction\n\nfunction! s:open(cmd, target)\n  if stridx('edit', a:cmd) == 0 && s:skim_fnamemodify(a:target, ':p') ==# s:skim_expand('%:p')\n    return\n  endif\n  execute a:cmd s:escape(a:target)\nendfunction\n\nfunction! s:common_sink(action, lines) abort\n  if len(a:lines) < 2\n    return\n  endif\n  let key = remove(a:lines, 0)\n  let Cmd = get(a:action, key, 'e')\n  if type(Cmd) == type(function('call'))\n    return Cmd(a:lines)\n  endif\n  if len(a:lines) > 1\n    augroup skim_swap\n      autocmd SwapExists * let v:swapchoice='o'\n            \\| call s:warn('skim: E325: swap file exists: '.s:skim_expand('<afile>'))\n    augroup END\n  endif\n  try\n    let empty = empty(s:skim_expand('%')) && line('$') == 1 && empty(getline(1)) && !&modified\n    \" Preserve the current working directory in case it's changed during\n    \" the execution (e.g. `set autochdir` or `autocmd BufEnter * lcd ...`)\n    let cwd = exists('w:skim_pushd') ? w:skim_pushd.dir : expand('%:p:h')\n    for item in a:lines\n      if item[0] != '~' && item !~ (s:is_win ? '^[A-Z]:\\' : '^/')\n        let item = join([cwd, item], (s:is_win ? '\\' : '/'))\n      endif\n      if empty\n        execute 'e' s:escape(item)\n        let empty = 0\n      else\n        call s:open(Cmd, item)\n      endif\n      if !has('patch-8.0.0177') && !has('nvim-0.2') && exists('#BufEnter')\n            \\ && isdirectory(item)\n        doautocmd BufEnter\n      endif\n    endfor\n  catch /^Vim:Interrupt$/\n  finally\n    silent! autocmd! skim_swap\n  endtry\nendfunction\n\nfunction! s:get_color(attr, ...)\n  let gui = !s:is_win && !has('win32unix') && has('termguicolors') && &termguicolors\n  let fam = gui ? 'gui' : 'cterm'\n  let pat = gui ? '^#[a-f0-9]\\+' : '^[0-9]\\+$'\n  for group in a:000\n    let code = synIDattr(synIDtrans(hlID(group)), a:attr, fam)\n    if code =~? pat\n      return code\n    endif\n  endfor\n  return ''\nendfunction\n\nfunction! s:defaults()\n  let rules = copy(get(g:, 'skim_colors', {}))\n  let colors = join(map(items(filter(map(rules, 'call(\"s:get_color\", v:val)'), '!empty(v:val)')), 'join(v:val, \":\")'), ',')\n  return empty(colors) ? '' : skim#shellescape('--color='.colors)\nendfunction\n\nfunction! s:validate_layout(layout)\n  for key in keys(a:layout)\n    if index(s:layout_keys, key) < 0\n      throw printf('Invalid entry in g:skim_layout: %s (allowed: %s)%s',\n            \\ key, join(s:layout_keys, ', '), key == 'options' ? '. Use $SKIM_DEFAULT_OPTIONS.' : '')\n    endif\n  endfor\n  return a:layout\nendfunction\n\nfunction! s:evaluate_opts(options)\n  return type(a:options) == type([]) ?\n        \\ join(map(copy(a:options), 'skim#shellescape(v:val)')) : a:options\nendfunction\n\n\" [name string,] [opts dict,] [fullscreen boolean]\nfunction! skim#wrap(...)\n  let args = ['', {}, 0]\n  let expects = map(copy(args), 'type(v:val)')\n  let tidx = 0\n  for arg in copy(a:000)\n    let tidx = index(expects, type(arg) == 6 ? type(0) : type(arg), tidx)\n    if tidx < 0\n      throw 'Invalid arguments (expected: [name string] [opts dict] [fullscreen boolean])'\n    endif\n    let args[tidx] = arg\n    let tidx += 1\n    unlet arg\n  endfor\n  let [name, opts, bang] = args\n\n  if len(name)\n    let opts.name = name\n  end\n\n  \" Layout: g:skim_layout (and deprecated g:skim_height)\n  if bang\n    for key in s:layout_keys\n      if has_key(opts, key)\n        call remove(opts, key)\n      endif\n    endfor\n  elseif !s:has_any(opts, s:layout_keys)\n    if !exists('g:skim_layout') && exists('g:skim_height')\n      let opts.down = g:skim_height\n    else\n      let opts = extend(opts, s:validate_layout(get(g:, 'skim_layout', s:default_layout())))\n    endif\n  endif\n\n  \" Interactive commands should use --cmd-history for query history\n  let history_option = '--history'\n  if index(opts['options'], '-i') != -1\n    let history_option = '--cmd-history'\n  endif\n\n  \" Colors: g:skim_colors\n  let opts.options = s:defaults() .' '. s:evaluate_opts(get(opts, 'options', ''))\n\n  \" History: g:skim_history_dir\n  if len(name) && len(get(g:, 'skim_history_dir', ''))\n    let dir = s:skim_expand(g:skim_history_dir)\n    if !isdirectory(dir)\n      call mkdir(dir, 'p')\n    endif\n    let history = skim#shellescape(dir.'/'.name)\n    let opts.options = join([history_option, history, opts.options])\n  endif\n\n  \" Action: g:skim_action\n  if !s:has_any(opts, ['sink', 'sink*'])\n    let opts._action = get(g:, 'skim_action', s:default_action)\n    let opts.options .= ' --expect='.join(keys(opts._action), ',')\n    function! opts.sink(lines) abort\n      return s:common_sink(self._action, a:lines)\n    endfunction\n    let opts['sink*'] = remove(opts, 'sink')\n  endif\n\n  return opts\nendfunction\n\nfunction! s:use_sh()\n  let [shell, shellslash, shellcmdflag, shellxquote] = [&shell, &shellslash, &shellcmdflag, &shellxquote]\n  if s:is_win\n    set shell=cmd.exe\n    set noshellslash\n    let &shellcmdflag = has('nvim') ? '/s /c' : '/c'\n    let &shellxquote = has('nvim') ? '\"' : '('\n  else\n    set shell=sh\n  endif\n  return [shell, shellslash, shellcmdflag, shellxquote]\nendfunction\n\nfunction! skim#run(...) abort\ntry\n  let [shell, shellslash, shellcmdflag, shellxquote] = s:use_sh()\n\n  let dict   = exists('a:1') ? copy(a:1) : {}\n  let temps  = { 'result': s:skim_tempname() }\n  let optstr = s:evaluate_opts(get(dict, 'options', ''))\n  try\n    let skim_exec = skim#shellescape(skim#exec())\n  catch\n    throw v:exception\n  endtry\n\n  if !has_key(dict, 'dir')\n    let dict.dir = s:skim_getcwd()\n  endif\n  if has('win32unix') && has_key(dict, 'dir')\n    let dict.dir = fnamemodify(dict.dir, ':p')\n  endif\n\n  if has_key(dict, 'source')\n    let source = dict.source\n    let type = type(source)\n    if type == 1\n      let prefix = '( '.source.' )|'\n    elseif type == 3\n      let temps.input = s:skim_tempname()\n      call writefile(map(source, '<SID>enc_to_cp(v:val)'), temps.input)\n      let prefix = (s:is_win ? 'type ' : 'cat ').skim#shellescape(temps.input).'|'\n    else\n      throw 'Invalid source type'\n    endif\n  else\n    let prefix = ''\n  endif\n\n  let prefer_tmux = get(g:, 'skim_prefer_tmux', 0) || has_key(dict, 'tmux')\n  let use_height = has_key(dict, 'down') && !has('gui_running') &&\n        \\ !(has('nvim') || s:is_win || has('win32unix') || s:present(dict, 'up', 'left', 'right', 'window')) &&\n        \\ executable('tput') && filereadable('/dev/tty')\n  let has_vim8_term = has('terminal') && has('patch-8.0.995')\n  let has_nvim_term = has('nvim-0.2.1') || has('nvim') && !s:is_win\n  let use_term = has_nvim_term ||\n    \\ has_vim8_term && !has('win32unix') && (has('gui_running') || s:is_win || !use_height && s:present(dict, 'down', 'up', 'left', 'right', 'window'))\n  let use_tmux = (has_key(dict, 'tmux') || (!use_height && !use_term || prefer_tmux) && !has('win32unix') && s:splittable(dict)) && s:tmux_enabled()\n  if prefer_tmux && use_tmux\n    let use_height = 0\n    let use_term = 0\n  endif\n  if use_height\n    let height = s:calc_size(&lines, dict.down, dict)\n    let optstr .= ' --height='.height\n  elseif use_term\n    let optstr .= ' --no-height'\n  endif\n  let command = prefix.(use_tmux ? s:skim_tmux(dict) : skim_exec).' '.optstr.' > '.temps.result\n\n  if use_term\n    return s:execute_term(dict, command, temps)\n  endif\n\n  let lines = use_tmux ? s:execute_tmux(dict, command, temps)\n                 \\ : s:execute(dict, command, use_height, temps)\n  call s:callback(dict, lines)\n  return lines\nfinally\n  let [&shell, &shellslash, &shellcmdflag, &shellxquote] = [shell, shellslash, shellcmdflag, shellxquote]\nendtry\nendfunction\n\nfunction! s:present(dict, ...)\n  for key in a:000\n    if !empty(get(a:dict, key, ''))\n      return 1\n    endif\n  endfor\n  return 0\nendfunction\n\nfunction! s:skim_tmux(dict)\n  let size = get(a:dict, 'tmux', '')\n  if empty(size)\n    for o in ['up', 'down', 'left', 'right']\n      if s:present(a:dict, o)\n        let spec = a:dict[o]\n        if (o == 'up' || o == 'down') && spec[0] == '~'\n          let size = '-'.o[0].s:calc_size(&lines, spec, a:dict)\n        else\n          \" Legacy boolean option\n          let size = '-'.o[0].(spec == 1 ? '' : substitute(spec, '^\\~', '', ''))\n        endif\n        break\n      endif\n    endfor\n  endif\n  return printf('LINES=%d COLUMNS=%d %s %s %s --',\n    \\ &lines, &columns, skim#shellescape(s:skim_tmux), size, (has_key(a:dict, 'source') ? '' : '-'))\nendfunction\n\nfunction! s:splittable(dict)\n  return s:present(a:dict, 'up', 'down') && &lines > 15 ||\n        \\ s:present(a:dict, 'left', 'right') && &columns > 40\nendfunction\n\nfunction! s:pushd(dict)\n  if s:present(a:dict, 'dir')\n    let cwd = s:skim_getcwd()\n    let w:skim_pushd = {\n    \\   'command': haslocaldir() ? 'lcd' : (exists(':tcd') && haslocaldir(-1) ? 'tcd' : 'cd'),\n    \\   'origin': cwd,\n    \\   'bufname': bufname('')\n    \\ }\n    execute 'lcd' s:escape(a:dict.dir)\n    let cwd = s:skim_getcwd()\n    let w:skim_pushd.dir = cwd\n    let a:dict.pushd = w:skim_pushd\n    return cwd\n  endif\n  return ''\nendfunction\n\naugroup skim_popd\n  autocmd!\n  autocmd WinEnter * call s:dopopd()\naugroup END\n\nfunction! s:dopopd()\n  if !exists('w:skim_pushd')\n    return\n  endif\n\n  \" Note: We temporarily change the working directory to 'dir' entry\n  \" of options dictionary (set to the current working directory if not given)\n  \" before running skim.\n  \"\n  \" e.g. call skim#run({'dir': '/tmp', 'source': 'ls', 'sink': 'e'})\n  \"\n  \" After processing the sink function, we restore the current working\n  \" directory using a heuristic: we only change directory if the current\n  \" working directory matches 'dir' entry. This handles most cases correctly,\n  \" though it may not restore the directory if the sink function explicitly\n  \" changed to the 'dir' path.\n  if s:skim_getcwd() ==# w:skim_pushd.dir && (!&autochdir || w:skim_pushd.bufname ==# bufname(''))\n    execute w:skim_pushd.command s:escape(w:skim_pushd.origin)\n  endif\n  unlet! w:skim_pushd\nendfunction\n\nfunction! s:xterm_launcher()\n  let fmt = 'xterm -T \"[skim]\" -bg \"%s\" -fg \"%s\" -geometry %dx%d+%d+%d -e bash -ic %%s'\n  if has('gui_macvim')\n    let fmt .= '&& osascript -e \"tell application \\\"MacVim\\\" to activate\"'\n  endif\n  return printf(fmt,\n    \\ escape(synIDattr(hlID(\"Normal\"), \"bg\"), '#'), escape(synIDattr(hlID(\"Normal\"), \"fg\"), '#'),\n    \\ &columns, &lines/2, getwinposx(), getwinposy())\nendfunction\nunlet! s:launcher\nif s:is_win || has('win32unix')\n  let s:launcher = '%s'\nelse\n  let s:launcher = function('s:xterm_launcher')\nendif\n\nfunction! s:exit_handler(code, command, ...)\n  if a:code == 130\n    return 0\n  elseif has('nvim') && a:code == 129\n    \" When deleting the terminal buffer while skim is still running,\n    \" Nvim sends SIGHUP.\n    return 0\n  elseif a:code > 1\n    call s:error('Error running ' . a:command)\n    if !empty(a:000)\n      sleep\n    endif\n    return 0\n  endif\n  return 1\nendfunction\n\nfunction! s:execute(dict, command, use_height, temps) abort\n  call s:pushd(a:dict)\n  if has('unix') && !a:use_height\n    silent! !clear 2> /dev/null\n  endif\n  let escaped = (a:use_height || s:is_win) ? a:command : escape(substitute(a:command, '\\n', '\\\\n', 'g'), '%#!')\n  if has('gui_running')\n    let Launcher = get(a:dict, 'launcher', get(g:, 'Skim_launcher', get(g:, 'skim_launcher', s:launcher)))\n    let fmt = type(Launcher) == 2 ? call(Launcher, []) : Launcher\n    if has('unix')\n      let escaped = \"'\".substitute(escaped, \"'\", \"'\\\"'\\\"'\", 'g').\"'\"\n    endif\n    let command = printf(fmt, escaped)\n  else\n    let command = escaped\n  endif\n  if s:is_win\n    let batchfile = s:skim_tempname().'.bat'\n    call writefile(s:wrap_cmds(command), batchfile)\n    let command = batchfile\n    let a:temps.batchfile = batchfile\n    if has('nvim')\n      let skim = {}\n      let skim.dict = a:dict\n      let skim.temps = a:temps\n      function! skim.on_exit(job_id, exit_status, event) dict\n        call s:pushd(self.dict)\n        let lines = s:collect(self.temps)\n        call s:callback(self.dict, lines)\n      endfunction\n      let cmd = 'start /wait cmd /c '.command\n      call jobstart(cmd, skim)\n      return []\n    endif\n  elseif has('win32unix') && $TERM !=# 'cygwin'\n    let shellscript = s:skim_tempname()\n    call writefile([command], shellscript)\n    let command = 'cmd.exe /C '.skim#shellescape('set \"TERM=\" & start /WAIT sh -c '.shellscript)\n    let a:temps.shellscript = shellscript\n  endif\n  if a:use_height\n    let stdin = has_key(a:dict, 'source') ? '' : '< /dev/tty'\n    call system(printf('tput cup %d > /dev/tty; tput cnorm > /dev/tty; %s %s 2> /dev/tty', &lines, command, stdin))\n  else\n    execute 'silent !'.command\n  endif\n  let exit_status = v:shell_error\n  redraw!\n  return s:exit_handler(exit_status, command) ? s:collect(a:temps) : []\nendfunction\n\nfunction! s:execute_tmux(dict, command, temps) abort\n  let command = a:command\n  let cwd = s:pushd(a:dict)\n  if len(cwd)\n    \" -c '#{pane_current_path}' is only available on tmux 1.9 or above\n    let command = join(['cd', skim#shellescape(cwd), '&&', command])\n  endif\n\n  call system(command)\n  let exit_status = v:shell_error\n  redraw!\n  return s:exit_handler(exit_status, command) ? s:collect(a:temps) : []\nendfunction\n\nfunction! s:calc_size(max, val, dict)\n  let val = substitute(a:val, '^\\~', '', '')\n  if val =~ '%$'\n    let size = a:max * str2nr(val[:-2]) / 100\n  else\n    let size = min([a:max, str2nr(val)])\n  endif\n\n  let srcsz = -1\n  if type(get(a:dict, 'source', 0)) == type([])\n    let srcsz = len(a:dict.source)\n  endif\n\n  let opts = $SKIM_DEFAULT_OPTIONS.' '.s:evaluate_opts(get(a:dict, 'options', ''))\n  if opts =~ 'preview'\n    return size\n  endif\n  let margin = match(opts, '--inline-info\\|--info[^-]\\{-}inline') > match(opts, '--no-inline-info\\|--info[^-]\\{-}\\(default\\|hidden\\)') ? 1 : 2\n  let margin += stridx(opts, '--border') > stridx(opts, '--no-border') ? 2 : 0\n  if stridx(opts, '--header') > stridx(opts, '--no-header')\n    let margin += len(split(opts, \"\\n\"))\n  endif\n  return srcsz >= 0 ? min([srcsz + margin, size]) : size\nendfunction\n\nfunction! s:getpos()\n  return {'tab': tabpagenr(), 'win': winnr(), 'winid': win_getid(), 'cnt': winnr('$'), 'tcnt': tabpagenr('$')}\nendfunction\n\nfunction! s:split(dict)\n  let directions = {\n  \\ 'up':    ['topleft', 'resize', &lines],\n  \\ 'down':  ['botright', 'resize', &lines],\n  \\ 'left':  ['vertical topleft', 'vertical resize', &columns],\n  \\ 'right': ['vertical botright', 'vertical resize', &columns] }\n  let ppos = s:getpos()\n  let is_popup = 0\n  try\n    if s:present(a:dict, 'window')\n      if type(a:dict.window) == type({})\n        if !s:popup_support()\n          throw 'Nvim 0.4+ or Vim 8.2.191+ with popupwin feature is required for pop-up window'\n        end\n        call s:popup(a:dict.window)\n        let is_popup = 1\n      else\n        execute 'keepalt' a:dict.window\n      endif\n    elseif !s:splittable(a:dict)\n      execute (tabpagenr()-1).'tabnew'\n    else\n      for [dir, triple] in items(directions)\n        let val = get(a:dict, dir, '')\n        if !empty(val)\n          let [cmd, resz, max] = triple\n          if (dir == 'up' || dir == 'down') && val[0] == '~'\n            let sz = s:calc_size(max, val, a:dict)\n          else\n            let sz = s:calc_size(max, val, {})\n          endif\n          execute cmd sz.'new'\n          execute resz sz\n          return [ppos, {}, is_popup]\n        endif\n      endfor\n    endif\n    return [ppos, is_popup ? {} : { '&l:wfw': &l:wfw, '&l:wfh': &l:wfh }, is_popup]\n  finally\n    if !is_popup\n      setlocal winfixwidth winfixheight\n    endif\n  endtry\nendfunction\n\nfunction! s:execute_term(dict, command, temps) abort\n  let winrest = winrestcmd()\n  let pbuf = bufnr('')\n  let [ppos, winopts, is_popup] = s:split(a:dict)\n  call s:use_sh()\n  let b:skim = a:dict\n  let skim = { 'buf': bufnr(''), 'pbuf': pbuf, 'ppos': ppos, 'dict': a:dict, 'temps': a:temps,\n            \\ 'winopts': winopts, 'winrest': winrest, 'lines': &lines,\n            \\ 'columns': &columns, 'command': a:command }\n  function! skim.switch_back(inplace)\n    if a:inplace && bufnr('') == self.buf\n      if bufexists(self.pbuf)\n        execute 'keepalt b' self.pbuf\n      endif\n      \" No other listed buffer\n      if bufnr('') == self.buf\n        enew\n      endif\n    endif\n  endfunction\n  function! skim.on_exit(id, code, ...)\n    if s:getpos() == self.ppos \" {'window': 'enew'}\n      for [opt, val] in items(self.winopts)\n        execute 'let' opt '=' val\n      endfor\n      call self.switch_back(1)\n    else\n      if bufnr('') == self.buf\n        \" We use close instead of bd! since Vim does not close the split when\n        \" there's no other listed buffer (nvim +'set nobuflisted')\n        close\n      endif\n      silent! execute 'tabnext' self.ppos.tab\n      silent! execute self.ppos.win.'wincmd w'\n    endif\n\n    if bufexists(self.buf)\n      execute 'bd!' self.buf\n    endif\n\n    if &lines == self.lines && &columns == self.columns && s:getpos() == self.ppos\n      execute self.winrest\n    endif\n\n    if !s:exit_handler(a:code, self.command, 1)\n      return\n    endif\n\n    call s:pushd(self.dict)\n    let lines = s:collect(self.temps)\n    call s:callback(self.dict, lines)\n    call self.switch_back(s:getpos() == self.ppos)\n  endfunction\n\n  try\n    call s:pushd(a:dict)\n    if s:is_win\n      let skim.temps.batchfile = s:skim_tempname().'.bat'\n      call writefile(s:wrap_cmds(a:command), skim.temps.batchfile)\n      let command = skim.temps.batchfile\n    else\n      let command = a:command\n    endif\n    let command .= s:term_marker\n    if has('nvim')\n      call termopen(command, skim)\n    else\n      let term_opts = {'exit_cb': function(skim.on_exit)}\n      if is_popup\n        let term_opts.hidden = 1\n      else\n        let term_opts.curwin = 1\n      endif\n      let skim.buf = term_start([&shell, &shellcmdflag, command], term_opts)\n      if is_popup && exists('#TerminalWinOpen')\n        doautocmd <nomodeline> TerminalWinOpen\n      endif\n      if !has('patch-8.0.1261') && !s:is_win\n        call term_wait(skim.buf, 20)\n      endif\n    endif\n  finally\n    call s:dopopd()\n  endtry\n  setlocal nospell bufhidden=wipe nobuflisted nonumber\n  setf skim\n  startinsert\n  return []\nendfunction\n\nfunction! s:collect(temps) abort\n  try\n    return filereadable(a:temps.result) ? readfile(a:temps.result) : []\n  finally\n    for tf in values(a:temps)\n      silent! call delete(tf)\n    endfor\n  endtry\nendfunction\n\nfunction! s:callback(dict, lines) abort\n  let popd = has_key(a:dict, 'pushd')\n  if popd\n    let w:skim_pushd = a:dict.pushd\n  endif\n\n  try\n    if has_key(a:dict, 'sink')\n      for line in a:lines\n        if type(a:dict.sink) == 2\n          call a:dict.sink(line)\n        else\n          execute a:dict.sink s:escape(line)\n        endif\n      endfor\n    endif\n    if has_key(a:dict, 'sink*')\n      call a:dict['sink*'](a:lines)\n    endif\n  catch\n    if stridx(v:exception, ':E325:') < 0\n      echoerr v:exception\n    endif\n  endtry\n\n  \" We may have opened a new window or tab\n  if popd\n    let w:skim_pushd = a:dict.pushd\n    call s:dopopd()\n  endif\nendfunction\n\nif has('nvim')\n  function s:create_popup(hl, opts) abort\n    let buf = nvim_create_buf(v:false, v:true)\n    let opts = extend({'relative': 'editor', 'style': 'minimal'}, a:opts)\n    let border = has_key(opts, 'border') ? remove(opts, 'border') : []\n    let win = nvim_open_win(buf, v:true, opts)\n    call setwinvar(win, '&winhighlight', 'NormalFloat:'..a:hl)\n    call setwinvar(win, '&colorcolumn', '')\n    if !empty(border)\n      call nvim_buf_set_lines(buf, 0, -1, v:true, border)\n    endif\n    return buf\n  endfunction\nelse\n  function! s:create_popup(hl, opts) abort\n    let is_frame = has_key(a:opts, 'border')\n    let s:popup_create = {buf -> popup_create(buf, #{\n      \\ line: a:opts.row,\n      \\ col: a:opts.col,\n      \\ minwidth: a:opts.width,\n      \\ minheight: a:opts.height,\n      \\ zindex: 50 - is_frame,\n    \\ })}\n    if is_frame\n      let id = s:popup_create('')\n      call setwinvar(id, '&wincolor', a:hl)\n      call setbufline(winbufnr(id), 1, a:opts.border)\n      execute 'autocmd BufWipeout * ++once call popup_close('..id..')'\n      return winbufnr(id)\n    else\n      autocmd TerminalOpen * ++once call s:popup_create(str2nr(expand('<abuf>')))\n    endif\n  endfunction\nendif\n\nfunction! s:popup(opts) abort\n  \" Support ambiwidth == 'double'\n  let ambidouble = &ambiwidth == 'double' ? 2 : 1\n\n  \" Size and position\n  let width = min([max([8, a:opts.width > 1 ? a:opts.width : float2nr(&columns * a:opts.width)]), &columns])\n  let width += width % ambidouble\n  let height = min([max([4, a:opts.height > 1 ? a:opts.height : float2nr(&lines * a:opts.height)]), &lines - has('nvim')])\n  let row = float2nr(get(a:opts, 'yoffset', 0.5) * (&lines - height))\n  let col = float2nr(get(a:opts, 'xoffset', 0.5) * (&columns - width))\n\n  \" Managing the differences\n  let row = min([max([0, row]), &lines - has('nvim') - height])\n  let col = min([max([0, col]), &columns - width])\n  let row += !has('nvim')\n  let col += !has('nvim')\n\n  \" Border style\n  let style = tolower(get(a:opts, 'border', 'rounded'))\n  if !has_key(a:opts, 'border') && !get(a:opts, 'rounded', 1)\n    let style = 'sharp'\n  endif\n\n  if style =~ 'vertical\\|left\\|right'\n    let mid = style == 'vertical' ? '│' .. repeat(' ', width - 2 * ambidouble) .. '│' :\n            \\ style == 'left'     ? '│' .. repeat(' ', width - 1 * ambidouble)\n            \\                     :        repeat(' ', width - 1 * ambidouble) .. '│'\n    let border = repeat([mid], height)\n    let shift = { 'row': 0, 'col': style == 'right' ? 0 : 2, 'width': style == 'vertical' ? -4 : -2, 'height': 0 }\n  elseif style =~ 'horizontal\\|top\\|bottom'\n    let hor = repeat('─', width / ambidouble)\n    let mid = repeat(' ', width)\n    let border = style == 'horizontal' ? [hor] + repeat([mid], height - 2) + [hor] :\n               \\ style == 'top'        ? [hor] + repeat([mid], height - 1)\n               \\                       :         repeat([mid], height - 1) + [hor]\n    let shift = { 'row': style == 'bottom' ? 0 : 1, 'col': 0, 'width': 0, 'height': style == 'horizontal' ? -2 : -1 }\n  else\n    let edges = style == 'sharp' ? ['┌', '┐', '└', '┘'] : ['╭', '╮', '╰', '╯']\n    let bar = repeat('─', width / ambidouble - 2)\n    let top = edges[0] .. bar .. edges[1]\n    let mid = '│' .. repeat(' ', width - 2 * ambidouble) .. '│'\n    let bot = edges[2] .. bar .. edges[3]\n    let border = [top] + repeat([mid], height - 2) + [bot]\n    let shift = { 'row': 1, 'col': 2, 'width': -4, 'height': -2 }\n  endif\n\n  let highlight = get(a:opts, 'highlight', 'Comment')\n  let frame = s:create_popup(highlight, {\n    \\ 'row': row, 'col': col, 'width': width, 'height': height, 'border': border\n  \\ })\n  call s:create_popup('Normal', {\n    \\ 'row': row + shift.row, 'col': col + shift.col, 'width': width + shift.width, 'height': height + shift.height\n  \\ })\n  if has('nvim')\n    execute 'autocmd BufWipeout <buffer> bwipeout '..frame\n  endif\nendfunction\n\nlet s:default_action = {\n  \\ 'enter': 'edit',\n  \\ 'ctrl-t': 'tab split',\n  \\ 'ctrl-x': 'split',\n  \\ 'ctrl-v': 'vsplit' }\n\nfunction! s:shortpath()\n  let short = fnamemodify(getcwd(), ':~:.')\n  if !has('win32unix')\n    let short = pathshorten(short)\n  endif\n  let slash = (s:is_win && !&shellslash) ? '\\' : '/'\n  return empty(short) ? '~'.slash : short . (short =~ escape(slash, '\\').'$' ? '' : slash)\nendfunction\n\nfunction! s:cmd(bang, ...) abort\n  let args = copy(a:000)\n  let opts = { 'options': ['--multi'] }\n  if len(args) && isdirectory(expand(args[-1]))\n    let opts.dir = substitute(substitute(remove(args, -1), '\\\\\\([\"'']\\)', '\\1', 'g'), '[/\\\\]*$', '/', '')\n    if s:is_win && !&shellslash\n      let opts.dir = substitute(opts.dir, '/', '\\\\', 'g')\n    endif\n    let prompt = opts.dir\n  else\n    let prompt = s:shortpath()\n  endif\n  let prompt = strwidth(prompt) < &columns - 20 ? prompt : '> '\n  call extend(opts.options, ['--prompt', prompt])\n  call extend(opts.options, args)\n  call skim#run(skim#wrap('SKIM', opts, a:bang))\nendfunction\n\ncommand! -nargs=* -complete=dir -bang SK call s:cmd(<bang>0, <f-args>)\n\nlet &cpo = s:cpo_save\nunlet s:cpo_save\n"
  },
  {
    "path": "rust-toolchain.toml",
    "content": "[toolchain]\nchannel = \"stable\"\nprofile = \"default\"\ncomponents = [\"rust-analyzer\"]\n"
  },
  {
    "path": "shell/LICENSE",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2016 Jinzhou Zhang\nCopyright (c) 2016 Junegunn Choi\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": "shell/README.md",
    "content": "This directory contains the shell bindings for skim. \n\nNote that the content in this directory is copied from\n[fzf](https://github.com/junegunn/fzf/tree/master/shell) and modified to fit\nskim.\n\nThe scripts in this directory will not be maintained by skim.\n\nBut you are welcome to submit PR for update the scripts to the fzf's latest\nscripts. Thanks!\n"
  },
  {
    "path": "shell/completion.bash",
    "content": "_sk() {\n    local i cur prev opts cmd\n    COMPREPLY=()\n    if [[ \"${BASH_VERSINFO[0]}\" -ge 4 ]]; then\n        cur=\"$2\"\n    else\n        cur=\"${COMP_WORDS[COMP_CWORD]}\"\n    fi\n    prev=\"$3\"\n    cmd=\"\"\n    opts=\"\"\n\n    for i in \"${COMP_WORDS[@]:0:COMP_CWORD}\"\n    do\n        case \"${cmd},${i}\" in\n            \",$1\")\n                cmd=\"sk\"\n                ;;\n            *)\n                ;;\n        esac\n    done\n\n    case \"${cmd}\" in\n        sk)\n            opts=\"-t -n -d -e -b -m -c -i -I -p -q -1 -0 -f -x -h -V --tac --min-query-length --no-sort --tiebreak --nth --with-nth --delimiter --exact --regex --algo --case --typos --no-typos --normalize --split-match --last-match --scheme --bind --multi --no-multi --no-mouse --cmd --interactive --color --no-hscroll --keep-right --skip-to-pattern --no-clear-if-empty --no-clear-start --no-clear --show-cmd-error --cycle --disabled --layout --reverse --height --no-height --min-height --margin --prompt --cmd-prompt --selector --multi-selector --ansi --tabstop --ellipsis --info --no-info --inline-info --header --header-lines --border --wrap --history --history-size --cmd-history --cmd-history-size --preview --preview-window --query --cmd-query --read0 --print0 --print-query --print-cmd --print-score --print-header --print-current --output-format --no-strip-ansi --select-1 --exit-0 --sync --pre-select-n --pre-select-pat --pre-select-items --pre-select-file --filter --shell --shell-bindings --man --listen --remote --tmux --log-level --log-file --flags --extended --literal --hscroll-off --filepath-word --jump-labels --no-bold --phony --tail --style --no-color --padding --border-label --border-label-pos --highlight-line --wrap-sign --no-multi-line --raw --track --gap --gap-line --freeze-left --freeze-right --scroll-off --gutter --gutter-raw --marker-multi-line --scrollbar --no-scrollbar --list-border --list-label --list-label-pos --no-input --info-command --separator --no-separator --ghost --input-border --input-label --input-label-pos --preview-label --preview-label-pos --header-first --header-border --header-lines-border --footer --footer-border --footer-label --footer-label-pos --with-shell --expect --help --version\"\n            if [[ ${cur} == -* || ${COMP_CWORD} -eq 1 ]] ; then\n                COMPREPLY=( $(compgen -W \"${opts}\" -- \"${cur}\") )\n                return 0\n            fi\n            case \"${prev}\" in\n                --min-query-length)\n                    COMPREPLY=($(compgen -f \"${cur}\"))\n                    return 0\n                    ;;\n                --tiebreak)\n                    COMPREPLY=($(compgen -W \"score -score begin -begin end -end length -length index -index pathname -pathname\" -- \"${cur}\"))\n                    return 0\n                    ;;\n                -t)\n                    COMPREPLY=($(compgen -W \"score -score begin -begin end -end length -length index -index pathname -pathname\" -- \"${cur}\"))\n                    return 0\n                    ;;\n                --nth)\n                    COMPREPLY=($(compgen -f \"${cur}\"))\n                    return 0\n                    ;;\n                -n)\n                    COMPREPLY=($(compgen -f \"${cur}\"))\n                    return 0\n                    ;;\n                --with-nth)\n                    COMPREPLY=($(compgen -f \"${cur}\"))\n                    return 0\n                    ;;\n                --delimiter)\n                    COMPREPLY=($(compgen -f \"${cur}\"))\n                    return 0\n                    ;;\n                -d)\n                    COMPREPLY=($(compgen -f \"${cur}\"))\n                    return 0\n                    ;;\n                --algo)\n                    COMPREPLY=($(compgen -W \"skim_v2 clangd fzy frizbee arinae\" -- \"${cur}\"))\n                    return 0\n                    ;;\n                --case)\n                    COMPREPLY=($(compgen -W \"respect ignore smart\" -- \"${cur}\"))\n                    return 0\n                    ;;\n                --typos)\n                    COMPREPLY=($(compgen -f \"${cur}\"))\n                    return 0\n                    ;;\n                --split-match)\n                    COMPREPLY=($(compgen -f \"${cur}\"))\n                    return 0\n                    ;;\n                --scheme)\n                    COMPREPLY=($(compgen -W \"default path history\" -- \"${cur}\"))\n                    return 0\n                    ;;\n                --bind)\n                    COMPREPLY=($(compgen -f \"${cur}\"))\n                    return 0\n                    ;;\n                -b)\n                    COMPREPLY=($(compgen -f \"${cur}\"))\n                    return 0\n                    ;;\n                --cmd)\n                    COMPREPLY=($(compgen -f \"${cur}\"))\n                    return 0\n                    ;;\n                -c)\n                    COMPREPLY=($(compgen -f \"${cur}\"))\n                    return 0\n                    ;;\n                -I)\n                    COMPREPLY=($(compgen -f \"${cur}\"))\n                    return 0\n                    ;;\n                --color)\n                    COMPREPLY=($(compgen -f \"${cur}\"))\n                    return 0\n                    ;;\n                --skip-to-pattern)\n                    COMPREPLY=($(compgen -f \"${cur}\"))\n                    return 0\n                    ;;\n                --layout)\n                    COMPREPLY=($(compgen -W \"default reverse reverse-list\" -- \"${cur}\"))\n                    return 0\n                    ;;\n                --height)\n                    COMPREPLY=($(compgen -f \"${cur}\"))\n                    return 0\n                    ;;\n                --min-height)\n                    COMPREPLY=($(compgen -f \"${cur}\"))\n                    return 0\n                    ;;\n                --margin)\n                    COMPREPLY=($(compgen -f \"${cur}\"))\n                    return 0\n                    ;;\n                --prompt)\n                    COMPREPLY=($(compgen -f \"${cur}\"))\n                    return 0\n                    ;;\n                -p)\n                    COMPREPLY=($(compgen -f \"${cur}\"))\n                    return 0\n                    ;;\n                --cmd-prompt)\n                    COMPREPLY=($(compgen -f \"${cur}\"))\n                    return 0\n                    ;;\n                --selector)\n                    COMPREPLY=($(compgen -f \"${cur}\"))\n                    return 0\n                    ;;\n                --multi-selector)\n                    COMPREPLY=($(compgen -f \"${cur}\"))\n                    return 0\n                    ;;\n                --tabstop)\n                    COMPREPLY=($(compgen -f \"${cur}\"))\n                    return 0\n                    ;;\n                --ellipsis)\n                    COMPREPLY=($(compgen -f \"${cur}\"))\n                    return 0\n                    ;;\n                --info)\n                    COMPREPLY=($(compgen -W \"default inline hidden\" -- \"${cur}\"))\n                    return 0\n                    ;;\n                --header)\n                    COMPREPLY=($(compgen -f \"${cur}\"))\n                    return 0\n                    ;;\n                --header-lines)\n                    COMPREPLY=($(compgen -f \"${cur}\"))\n                    return 0\n                    ;;\n                --border)\n                    COMPREPLY=($(compgen -W \"plain rounded double thick light-double-dashed heavy-double-dashed light-triple-dashed heavy-triple-dashed light-quadruple-dashed heavy-quadruple-dashed quadrant-inside quadrant-outside\" -- \"${cur}\"))\n                    return 0\n                    ;;\n                --history)\n                    COMPREPLY=($(compgen -f \"${cur}\"))\n                    return 0\n                    ;;\n                --history-size)\n                    COMPREPLY=($(compgen -f \"${cur}\"))\n                    return 0\n                    ;;\n                --cmd-history)\n                    COMPREPLY=($(compgen -f \"${cur}\"))\n                    return 0\n                    ;;\n                --cmd-history-size)\n                    COMPREPLY=($(compgen -f \"${cur}\"))\n                    return 0\n                    ;;\n                --preview)\n                    COMPREPLY=($(compgen -f \"${cur}\"))\n                    return 0\n                    ;;\n                --preview-window)\n                    COMPREPLY=($(compgen -f \"${cur}\"))\n                    return 0\n                    ;;\n                --query)\n                    COMPREPLY=($(compgen -f \"${cur}\"))\n                    return 0\n                    ;;\n                -q)\n                    COMPREPLY=($(compgen -f \"${cur}\"))\n                    return 0\n                    ;;\n                --cmd-query)\n                    COMPREPLY=($(compgen -f \"${cur}\"))\n                    return 0\n                    ;;\n                --output-format)\n                    COMPREPLY=($(compgen -f \"${cur}\"))\n                    return 0\n                    ;;\n                --pre-select-n)\n                    COMPREPLY=($(compgen -f \"${cur}\"))\n                    return 0\n                    ;;\n                --pre-select-pat)\n                    COMPREPLY=($(compgen -f \"${cur}\"))\n                    return 0\n                    ;;\n                --pre-select-items)\n                    COMPREPLY=($(compgen -f \"${cur}\"))\n                    return 0\n                    ;;\n                --pre-select-file)\n                    COMPREPLY=($(compgen -f \"${cur}\"))\n                    return 0\n                    ;;\n                --filter)\n                    COMPREPLY=($(compgen -f \"${cur}\"))\n                    return 0\n                    ;;\n                -f)\n                    COMPREPLY=($(compgen -f \"${cur}\"))\n                    return 0\n                    ;;\n                --shell)\n                    COMPREPLY=($(compgen -W \"bash elvish fish nushell power-shell zsh\" -- \"${cur}\"))\n                    return 0\n                    ;;\n                --listen)\n                    COMPREPLY=($(compgen -f \"${cur}\"))\n                    return 0\n                    ;;\n                --remote)\n                    COMPREPLY=($(compgen -f \"${cur}\"))\n                    return 0\n                    ;;\n                --tmux)\n                    COMPREPLY=($(compgen -f \"${cur}\"))\n                    return 0\n                    ;;\n                --log-level)\n                    COMPREPLY=($(compgen -f \"${cur}\"))\n                    return 0\n                    ;;\n                --log-file)\n                    COMPREPLY=($(compgen -f \"${cur}\"))\n                    return 0\n                    ;;\n                --flags)\n                    COMPREPLY=($(compgen -W \"no-preview-pty show-score show-index\" -- \"${cur}\"))\n                    return 0\n                    ;;\n                --hscroll-off)\n                    COMPREPLY=($(compgen -f \"${cur}\"))\n                    return 0\n                    ;;\n                --jump-labels)\n                    COMPREPLY=($(compgen -f \"${cur}\"))\n                    return 0\n                    ;;\n                --tail)\n                    COMPREPLY=($(compgen -f \"${cur}\"))\n                    return 0\n                    ;;\n                --style)\n                    COMPREPLY=($(compgen -f \"${cur}\"))\n                    return 0\n                    ;;\n                --padding)\n                    COMPREPLY=($(compgen -f \"${cur}\"))\n                    return 0\n                    ;;\n                --border-label)\n                    COMPREPLY=($(compgen -f \"${cur}\"))\n                    return 0\n                    ;;\n                --border-label-pos)\n                    COMPREPLY=($(compgen -f \"${cur}\"))\n                    return 0\n                    ;;\n                --wrap-sign)\n                    COMPREPLY=($(compgen -f \"${cur}\"))\n                    return 0\n                    ;;\n                --gap)\n                    COMPREPLY=($(compgen -f \"${cur}\"))\n                    return 0\n                    ;;\n                --gap-line)\n                    COMPREPLY=($(compgen -f \"${cur}\"))\n                    return 0\n                    ;;\n                --freeze-left)\n                    COMPREPLY=($(compgen -f \"${cur}\"))\n                    return 0\n                    ;;\n                --freeze-right)\n                    COMPREPLY=($(compgen -f \"${cur}\"))\n                    return 0\n                    ;;\n                --scroll-off)\n                    COMPREPLY=($(compgen -f \"${cur}\"))\n                    return 0\n                    ;;\n                --gutter)\n                    COMPREPLY=($(compgen -f \"${cur}\"))\n                    return 0\n                    ;;\n                --gutter-raw)\n                    COMPREPLY=($(compgen -f \"${cur}\"))\n                    return 0\n                    ;;\n                --marker-multi-line)\n                    COMPREPLY=($(compgen -f \"${cur}\"))\n                    return 0\n                    ;;\n                --scrollbar)\n                    COMPREPLY=($(compgen -f \"${cur}\"))\n                    return 0\n                    ;;\n                --list-border)\n                    COMPREPLY=($(compgen -f \"${cur}\"))\n                    return 0\n                    ;;\n                --list-label)\n                    COMPREPLY=($(compgen -f \"${cur}\"))\n                    return 0\n                    ;;\n                --list-label-pos)\n                    COMPREPLY=($(compgen -f \"${cur}\"))\n                    return 0\n                    ;;\n                --info-command)\n                    COMPREPLY=($(compgen -f \"${cur}\"))\n                    return 0\n                    ;;\n                --separator)\n                    COMPREPLY=($(compgen -f \"${cur}\"))\n                    return 0\n                    ;;\n                --ghost)\n                    COMPREPLY=($(compgen -f \"${cur}\"))\n                    return 0\n                    ;;\n                --input-border)\n                    COMPREPLY=($(compgen -f \"${cur}\"))\n                    return 0\n                    ;;\n                --input-label)\n                    COMPREPLY=($(compgen -f \"${cur}\"))\n                    return 0\n                    ;;\n                --input-label-pos)\n                    COMPREPLY=($(compgen -f \"${cur}\"))\n                    return 0\n                    ;;\n                --preview-label)\n                    COMPREPLY=($(compgen -f \"${cur}\"))\n                    return 0\n                    ;;\n                --preview-label-pos)\n                    COMPREPLY=($(compgen -f \"${cur}\"))\n                    return 0\n                    ;;\n                --header-border)\n                    COMPREPLY=($(compgen -f \"${cur}\"))\n                    return 0\n                    ;;\n                --header-lines-border)\n                    COMPREPLY=($(compgen -f \"${cur}\"))\n                    return 0\n                    ;;\n                --footer)\n                    COMPREPLY=($(compgen -f \"${cur}\"))\n                    return 0\n                    ;;\n                --footer-border)\n                    COMPREPLY=($(compgen -f \"${cur}\"))\n                    return 0\n                    ;;\n                --footer-label)\n                    COMPREPLY=($(compgen -f \"${cur}\"))\n                    return 0\n                    ;;\n                --footer-label-pos)\n                    COMPREPLY=($(compgen -f \"${cur}\"))\n                    return 0\n                    ;;\n                --with-shell)\n                    COMPREPLY=($(compgen -f \"${cur}\"))\n                    return 0\n                    ;;\n                --expect)\n                    COMPREPLY=($(compgen -f \"${cur}\"))\n                    return 0\n                    ;;\n                *)\n                    COMPREPLY=()\n                    ;;\n            esac\n            COMPREPLY=( $(compgen -W \"${opts}\" -- \"${cur}\") )\n            return 0\n            ;;\n    esac\n}\n\nif [[ \"${BASH_VERSINFO[0]}\" -eq 4 && \"${BASH_VERSINFO[1]}\" -ge 4 || \"${BASH_VERSINFO[0]}\" -gt 4 ]]; then\n    complete -F _sk -o nosort -o bashdefault -o default sk\nelse\n    complete -F _sk -o bashdefault -o default sk\nfi\n"
  },
  {
    "path": "shell/completion.fish",
    "content": "complete -c sk -l min-query-length -d 'Minimum query length to start showing results' -r\ncomplete -c sk -s t -l tiebreak -d 'Comma-separated list of sort criteria to apply when the scores are tied.' -r -f -a \"score\\t''\n-score\\t''\nbegin\\t''\n-begin\\t''\nend\\t''\n-end\\t''\nlength\\t''\n-length\\t''\nindex\\t''\n-index\\t''\npathname\\t''\n-pathname\\t''\"\ncomplete -c sk -s n -l nth -d 'Fields to be matched' -r\ncomplete -c sk -l with-nth -d 'Fields to be transformed' -r\ncomplete -c sk -s d -l delimiter -d 'Delimiter between fields' -r\ncomplete -c sk -l algo -d 'Fuzzy matching algorithm' -r -f -a \"skim_v2\\t'Improved skim fuzzy matching algorithm (v2)'\nclangd\\t'Clangd fuzzy matching algorithm'\nfzy\\t'Fzy matching algorithm (https://github.com/jhawthorn/fzy)'\nfrizbee\\t'Frizbee matching algorithm, typo resistant'\narinae\\t'Arinae: typo-resistant & natural algorithm, default'\"\ncomplete -c sk -l case -d 'Case sensitivity' -r -f -a \"respect\\t'Case-sensitive matching'\nignore\\t'Case-insensitive matching'\nsmart\\t'Smart case: case-insensitive unless query contains uppercase'\"\ncomplete -c sk -l typos -d 'Enable typo-tolerant matching' -r\ncomplete -c sk -l split-match -d 'Enable split matching and set delimiter' -r\ncomplete -c sk -l scheme -r -f -a \"default\\t'Default scheme, no modifications to the options'\npath\\t'Path scheme: will find the furthest match in the item and set pathname as the main tiebreak'\nhistory\\t'History scheme: will force index as the first tiebreak'\"\ncomplete -c sk -s b -l bind -d 'Comma separated list of bindings' -r\ncomplete -c sk -s c -l cmd -d 'Command to invoke dynamically in interactive mode' -r\ncomplete -c sk -s I -d 'Replace replstr with the selected item in commands' -r\ncomplete -c sk -l color -d 'Set color theme' -r\ncomplete -c sk -l skip-to-pattern -d 'Show the matched pattern at the line start' -r\ncomplete -c sk -l layout -d 'Set layout' -r -f -a \"default\\t'Display from the bottom of the screen'\nreverse\\t'Display from the top of the screen'\nreverse-list\\t'Display from the top of the screen, prompt at the bottom'\"\ncomplete -c sk -l height -d 'Height of skim\\'s window' -r\ncomplete -c sk -l min-height -d 'Minimum height of skim\\'s window' -r\ncomplete -c sk -l margin -d 'Screen margin' -r\ncomplete -c sk -s p -l prompt -d 'Set prompt' -r\ncomplete -c sk -l cmd-prompt -d 'Set prompt in command mode' -r\ncomplete -c sk -l selector -d 'Set selected item icon' -r\ncomplete -c sk -l multi-selector -d 'Set selected item icon' -r\ncomplete -c sk -l tabstop -d 'Number of spaces that make up a tab' -r\ncomplete -c sk -l ellipsis -d 'The characters used to display truncated lines' -r\ncomplete -c sk -l info -d 'Set matching result count display position' -r -f -a \"default\\t''\ninline\\t''\nhidden\\t''\"\ncomplete -c sk -l header -d 'Set header, displayed next to the info' -r\ncomplete -c sk -l header-lines -d 'Number of lines of the input treated as header' -r\ncomplete -c sk -l border -d 'Draw borders around the UI components' -r -f -a \"plain\\t''\nrounded\\t''\ndouble\\t''\nthick\\t''\nlight-double-dashed\\t''\nheavy-double-dashed\\t''\nlight-triple-dashed\\t''\nheavy-triple-dashed\\t''\nlight-quadruple-dashed\\t''\nheavy-quadruple-dashed\\t''\nquadrant-inside\\t''\nquadrant-outside\\t''\"\ncomplete -c sk -l history -d 'History file' -r\ncomplete -c sk -l history-size -d 'Maximum number of query history entries to keep' -r\ncomplete -c sk -l cmd-history -d 'Command history file' -r\ncomplete -c sk -l cmd-history-size -d 'Maximum number of query history entries to keep' -r\ncomplete -c sk -l preview -d 'Preview command' -r\ncomplete -c sk -l preview-window -d 'Preview window layout' -r\ncomplete -c sk -s q -l query -d 'Initial query' -r\ncomplete -c sk -l cmd-query -d 'Initial query in interactive mode' -r\ncomplete -c sk -l output-format -d 'Set the output format If set, overrides all print_ options Will be expanded the same way as preview or commands' -r\ncomplete -c sk -l pre-select-n -d 'Pre-select the first n items in multi-selection mode' -r\ncomplete -c sk -l pre-select-pat -d 'Pre-select the matched items in multi-selection mode' -r\ncomplete -c sk -l pre-select-items -d 'Pre-select the items separated by newline character' -r\ncomplete -c sk -l pre-select-file -d 'Pre-select the items read from this file' -r\ncomplete -c sk -s f -l filter -d 'Query for filter mode' -r\ncomplete -c sk -l shell -d 'Generate shell completion script' -r -f -a \"bash\\t'Bourne Again SHell'\nelvish\\t'Elvish shell'\nfish\\t'Friendly Interactive SHell'\nnushell\\t'Nushell (nu)'\npower-shell\\t'PowerShell'\nzsh\\t'Zsh'\"\ncomplete -c sk -l listen -d 'Run an IPC socket with optional name (defaults to sk)' -r\ncomplete -c sk -l remote -d 'Send commands to an IPC socket with optional name (defaults to sk)' -r\ncomplete -c sk -l tmux -d 'Run in a tmux popup' -r\ncomplete -c sk -l log-level -d 'Set the log level' -r\ncomplete -c sk -l log-file -d 'Pipe log output to a file' -r\ncomplete -c sk -l flags -d 'Feature flags' -r -f -a \"no-preview-pty\\t'Disable preview PTY on linux'\nshow-score\\t'Display the item\\'s match score before its value in the item list (for matcher debugging)'\nshow-index\\t'Display the item\\'s index before its value in the item list'\"\ncomplete -c sk -l hscroll-off -r\ncomplete -c sk -l jump-labels -r\ncomplete -c sk -l tail -r\ncomplete -c sk -l style -r\ncomplete -c sk -l padding -r\ncomplete -c sk -l border-label -r\ncomplete -c sk -l border-label-pos -r\ncomplete -c sk -l wrap-sign -r\ncomplete -c sk -l gap -r\ncomplete -c sk -l gap-line -r\ncomplete -c sk -l freeze-left -r\ncomplete -c sk -l freeze-right -r\ncomplete -c sk -l scroll-off -r\ncomplete -c sk -l gutter -r\ncomplete -c sk -l gutter-raw -r\ncomplete -c sk -l marker-multi-line -r\ncomplete -c sk -l scrollbar -r\ncomplete -c sk -l list-border -r\ncomplete -c sk -l list-label -r\ncomplete -c sk -l list-label-pos -r\ncomplete -c sk -l info-command -r\ncomplete -c sk -l separator -r\ncomplete -c sk -l ghost -r\ncomplete -c sk -l input-border -r\ncomplete -c sk -l input-label -r\ncomplete -c sk -l input-label-pos -r\ncomplete -c sk -l preview-label -r\ncomplete -c sk -l preview-label-pos -r\ncomplete -c sk -l header-border -r\ncomplete -c sk -l header-lines-border -r\ncomplete -c sk -l footer -r\ncomplete -c sk -l footer-border -r\ncomplete -c sk -l footer-label -r\ncomplete -c sk -l footer-label-pos -r\ncomplete -c sk -l with-shell -r\ncomplete -c sk -l expect -d 'Deprecated, kept for compatibility purposes. See accept() bind instead' -r\ncomplete -c sk -l tac -d 'Show results in reverse order'\ncomplete -c sk -l no-sort -d 'Do not sort the results'\ncomplete -c sk -s e -l exact -d 'Run in exact mode'\ncomplete -c sk -l regex -d 'Start in regex mode instead of fuzzy-match'\ncomplete -c sk -l no-typos -d 'Disable typo-resistant matching'\ncomplete -c sk -l normalize -d 'Normalize unicode characters'\ncomplete -c sk -l last-match -d 'Highlight the last match found, not the first one This makes tiebreak more pertinent on path items where we want to prioritize a match on the last parts'\ncomplete -c sk -s m -l multi -d 'Enable multiple selection'\ncomplete -c sk -l no-multi -d 'Disable multiple selection'\ncomplete -c sk -l no-mouse -d 'Disable mouse'\ncomplete -c sk -s i -l interactive -d 'Start skim in interactive mode'\ncomplete -c sk -l no-hscroll -d 'Disable horizontal scroll'\ncomplete -c sk -l keep-right -d 'Keep the right end of the line visible on overflow'\ncomplete -c sk -l no-clear-if-empty -d 'Do not clear previous line if the command returns an empty result'\ncomplete -c sk -l no-clear-start -d 'Do not clear items on start'\ncomplete -c sk -l no-clear -d 'Do not clear screen on exit'\ncomplete -c sk -l show-cmd-error -d 'Show error message if command fails'\ncomplete -c sk -l cycle -d 'Cycle the results by wrapping around when scrolling'\ncomplete -c sk -l disabled -d 'Disable matching entirely'\ncomplete -c sk -l reverse -d 'Shorthand for reverse layout'\ncomplete -c sk -l no-height -d 'Disable height (force full screen)'\ncomplete -c sk -l ansi -d 'Parse ANSI color codes in input strings'\ncomplete -c sk -l no-info -d 'Alias for --info=hidden'\ncomplete -c sk -l inline-info -d 'Alias for --info=inline'\ncomplete -c sk -l wrap -d 'Wrap items in the item list'\ncomplete -c sk -l read0 -d 'Read input delimited by ASCII NUL(\\\\0) characters'\ncomplete -c sk -l print0 -d 'Print output delimited by ASCII NUL(\\\\0) characters'\ncomplete -c sk -l print-query -d 'Print the query as the first line'\ncomplete -c sk -l print-cmd -d 'Print the command as the first line (after print-query)'\ncomplete -c sk -l print-score -d 'Print the score after each item'\ncomplete -c sk -l print-header -d 'Print the header as the first line (after print-score)'\ncomplete -c sk -l print-current -d 'Print the current (highlighted) item as the first line (after print-header)'\ncomplete -c sk -l no-strip-ansi -d 'Print the ANSI codes, making the output exactly match the input even when --ansi is on'\ncomplete -c sk -s 1 -l select-1 -d 'Do not enter the TUI if the query passed in -q matches only one item and return it'\ncomplete -c sk -s 0 -l exit-0 -d 'Do not enter the TUI if the query passed in -q does not match any item'\ncomplete -c sk -l sync -d 'Synchronous search for multi-staged filtering'\ncomplete -c sk -l shell-bindings -d 'Generate shell key bindings - only for bash, zsh and fish'\ncomplete -c sk -l man -d 'Generate man page and output it to stdout'\ncomplete -c sk -s x -l extended\ncomplete -c sk -l literal\ncomplete -c sk -l filepath-word\ncomplete -c sk -l no-bold\ncomplete -c sk -l phony\ncomplete -c sk -l no-color\ncomplete -c sk -l highlight-line\ncomplete -c sk -l no-multi-line\ncomplete -c sk -l raw\ncomplete -c sk -l track\ncomplete -c sk -l no-scrollbar\ncomplete -c sk -l no-input\ncomplete -c sk -l no-separator\ncomplete -c sk -l header-first\ncomplete -c sk -s h -l help -d 'Print help (see more with \\'--help\\')'\ncomplete -c sk -s V -l version -d 'Print version'\n"
  },
  {
    "path": "shell/completion.nu",
    "content": "module completions {\n\n  def \"nu-complete sk tiebreak\" [] {\n    [ \"score\" \"-score\" \"begin\" \"-begin\" \"end\" \"-end\" \"length\" \"-length\" \"index\" \"-index\" \"pathname\" \"-pathname\" ]\n  }\n\n  def \"nu-complete sk algorithm\" [] {\n    [ \"skim_v2\" \"clangd\" \"fzy\" \"frizbee\" \"arinae\" ]\n  }\n\n  def \"nu-complete sk case\" [] {\n    [ \"respect\" \"ignore\" \"smart\" ]\n  }\n\n  def \"nu-complete sk scheme\" [] {\n    [ \"default\" \"path\" \"history\" ]\n  }\n\n  def \"nu-complete sk layout\" [] {\n    [ \"default\" \"reverse\" \"reverse-list\" ]\n  }\n\n  def \"nu-complete sk info\" [] {\n    [ \"default\" \"inline\" \"hidden\" ]\n  }\n\n  def \"nu-complete sk border\" [] {\n    [ \"plain\" \"rounded\" \"double\" \"thick\" \"light-double-dashed\" \"heavy-double-dashed\" \"light-triple-dashed\" \"heavy-triple-dashed\" \"light-quadruple-dashed\" \"heavy-quadruple-dashed\" \"quadrant-inside\" \"quadrant-outside\" ]\n  }\n\n  def \"nu-complete sk shell\" [] {\n    [ \"bash\" \"elvish\" \"fish\" \"nushell\" \"power-shell\" \"zsh\" ]\n  }\n\n  def \"nu-complete sk flags\" [] {\n    [ \"no-preview-pty\" \"show-score\" \"show-index\" ]\n  }\n\n  # Fuzzy Finder in rust!\n  export extern sk [\n    --tac                     # Show results in reverse order\n    --min-query-length: string # Minimum query length to start showing results\n    --no-sort                 # Do not sort the results\n    --tiebreak(-t): string@\"nu-complete sk tiebreak\" # Comma-separated list of sort criteria to apply when the scores are tied.\n    --nth(-n): string         # Fields to be matched\n    --with-nth: string        # Fields to be transformed\n    --delimiter(-d): string   # Delimiter between fields\n    --exact(-e)               # Run in exact mode\n    --regex                   # Start in regex mode instead of fuzzy-match\n    --algo: string@\"nu-complete sk algorithm\" # Fuzzy matching algorithm\n    --case: string@\"nu-complete sk case\" # Case sensitivity\n    --typos: string           # Enable typo-tolerant matching\n    --no-typos                # Disable typo-resistant matching\n    --normalize               # Normalize unicode characters\n    --split-match: string     # Enable split matching and set delimiter\n    --last-match              # Highlight the last match found, not the first one This makes tiebreak more pertinent on path items where we want to prioritize a match on the last parts\n    --scheme: string@\"nu-complete sk scheme\"\n    --bind(-b): string        # Comma separated list of bindings\n    --multi(-m)               # Enable multiple selection\n    --no-multi                # Disable multiple selection\n    --no-mouse                # Disable mouse\n    --cmd(-c): string         # Command to invoke dynamically in interactive mode\n    --interactive(-i)         # Start skim in interactive mode\n    -I: string                # Replace replstr with the selected item in commands\n    --color: string           # Set color theme\n    --no-hscroll              # Disable horizontal scroll\n    --keep-right              # Keep the right end of the line visible on overflow\n    --skip-to-pattern: string # Show the matched pattern at the line start\n    --no-clear-if-empty       # Do not clear previous line if the command returns an empty result\n    --no-clear-start          # Do not clear items on start\n    --no-clear                # Do not clear screen on exit\n    --show-cmd-error          # Show error message if command fails\n    --cycle                   # Cycle the results by wrapping around when scrolling\n    --disabled                # Disable matching entirely\n    --layout: string@\"nu-complete sk layout\" # Set layout\n    --reverse                 # Shorthand for reverse layout\n    --height: string          # Height of skim's window\n    --no-height               # Disable height (force full screen)\n    --min-height: string      # Minimum height of skim's window\n    --margin: string          # Screen margin\n    --prompt(-p): string      # Set prompt\n    --cmd-prompt: string      # Set prompt in command mode\n    --selector: string        # Set selected item icon\n    --multi-selector: string  # Set selected item icon\n    --ansi                    # Parse ANSI color codes in input strings\n    --tabstop: string         # Number of spaces that make up a tab\n    --ellipsis: string        # The characters used to display truncated lines\n    --info: string@\"nu-complete sk info\" # Set matching result count display position\n    --no-info                 # Alias for --info=hidden\n    --inline-info             # Alias for --info=inline\n    --header: string          # Set header, displayed next to the info\n    --header-lines: string    # Number of lines of the input treated as header\n    --border: string@\"nu-complete sk border\" # Draw borders around the UI components\n    --wrap                    # Wrap items in the item list\n    --history: string         # History file\n    --history-size: string    # Maximum number of query history entries to keep\n    --cmd-history: string     # Command history file\n    --cmd-history-size: string # Maximum number of query history entries to keep\n    --preview: string         # Preview command\n    --preview-window: string  # Preview window layout\n    --query(-q): string       # Initial query\n    --cmd-query: string       # Initial query in interactive mode\n    --read0                   # Read input delimited by ASCII NUL(\\0) characters\n    --print0                  # Print output delimited by ASCII NUL(\\0) characters\n    --print-query             # Print the query as the first line\n    --print-cmd               # Print the command as the first line (after print-query)\n    --print-score             # Print the score after each item\n    --print-header            # Print the header as the first line (after print-score)\n    --print-current           # Print the current (highlighted) item as the first line (after print-header)\n    --output-format: string   # Set the output format If set, overrides all print_ options Will be expanded the same way as preview or commands\n    --no-strip-ansi           # Print the ANSI codes, making the output exactly match the input even when --ansi is on\n    --select-1(-1)            # Do not enter the TUI if the query passed in -q matches only one item and return it\n    --exit-0(-0)              # Do not enter the TUI if the query passed in -q does not match any item\n    --sync                    # Synchronous search for multi-staged filtering\n    --pre-select-n: string    # Pre-select the first n items in multi-selection mode\n    --pre-select-pat: string  # Pre-select the matched items in multi-selection mode\n    --pre-select-items: string # Pre-select the items separated by newline character\n    --pre-select-file: string # Pre-select the items read from this file\n    --filter(-f): string      # Query for filter mode\n    --shell: string@\"nu-complete sk shell\" # Generate shell completion script\n    --shell-bindings          # Generate shell key bindings - only for bash, zsh and fish\n    --man                     # Generate man page and output it to stdout\n    --listen: string          # Run an IPC socket with optional name (defaults to sk)\n    --remote: string          # Send commands to an IPC socket with optional name (defaults to sk)\n    --tmux: string            # Run in a tmux popup\n    --log-level: string       # Set the log level\n    --log-file: string        # Pipe log output to a file\n    --flags: string@\"nu-complete sk flags\" # Feature flags\n    --extended(-x)\n    --literal\n    --hscroll-off: string\n    --filepath-word\n    --jump-labels: string\n    --no-bold\n    --phony\n    --tail: string\n    --style: string\n    --no-color\n    --padding: string\n    --border-label: string\n    --border-label-pos: string\n    --highlight-line\n    --wrap-sign: string\n    --no-multi-line\n    --raw\n    --track\n    --gap: string\n    --gap-line: string\n    --freeze-left: string\n    --freeze-right: string\n    --scroll-off: string\n    --gutter: string\n    --gutter-raw: string\n    --marker-multi-line: string\n    --scrollbar: string\n    --no-scrollbar\n    --list-border: string\n    --list-label: string\n    --list-label-pos: string\n    --no-input\n    --info-command: string\n    --separator: string\n    --no-separator\n    --ghost: string\n    --input-border: string\n    --input-label: string\n    --input-label-pos: string\n    --preview-label: string\n    --preview-label-pos: string\n    --header-first\n    --header-border: string\n    --header-lines-border: string\n    --footer: string\n    --footer-border: string\n    --footer-label: string\n    --footer-label-pos: string\n    --with-shell: string\n    --expect: string          # Deprecated, kept for compatibility purposes. See accept() bind instead\n    --help(-h)                # Print help (see more with '--help')\n    --version(-V)             # Print version\n  ]\n\n}\n\nexport use completions *\n"
  },
  {
    "path": "shell/completion.zsh",
    "content": "#compdef sk\n\nautoload -U is-at-least\n\n_sk() {\n    typeset -A opt_args\n    typeset -a _arguments_options\n    local ret=1\n\n    if is-at-least 5.2; then\n        _arguments_options=(-s -S -C)\n    else\n        _arguments_options=(-s -C)\n    fi\n\n    local context curcontext=\"$curcontext\" state line\n    _arguments \"${_arguments_options[@]}\" : \\\n'--min-query-length=[Minimum query length to start showing results]:MIN_QUERY_LENGTH:_default' \\\n'*-t+[Comma-separated list of sort criteria to apply when the scores are tied.]:TIEBREAK:(score -score begin -begin end -end length -length index -index pathname -pathname)' \\\n'*--tiebreak=[Comma-separated list of sort criteria to apply when the scores are tied.]:TIEBREAK:(score -score begin -begin end -end length -length index -index pathname -pathname)' \\\n'*-n+[Fields to be matched]:NTH:_default' \\\n'*--nth=[Fields to be matched]:NTH:_default' \\\n'*--with-nth=[Fields to be transformed]:WITH_NTH:_default' \\\n'-d+[Delimiter between fields]:DELIMITER:_default' \\\n'--delimiter=[Delimiter between fields]:DELIMITER:_default' \\\n'--algo=[Fuzzy matching algorithm]:ALGORITHM:((skim_v2\\:\"Improved skim fuzzy matching algorithm (v2)\"\nclangd\\:\"Clangd fuzzy matching algorithm\"\nfzy\\:\"Fzy matching algorithm (https\\://github.com/jhawthorn/fzy)\"\nfrizbee\\:\"Frizbee matching algorithm, typo resistant\"\narinae\\:\"Arinae\\: typo-resistant & natural algorithm, default\"))' \\\n'--case=[Case sensitivity]:CASE:((respect\\:\"Case-sensitive matching\"\nignore\\:\"Case-insensitive matching\"\nsmart\\:\"Smart case\\: case-insensitive unless query contains uppercase\"))' \\\n'--typos=[Enable typo-tolerant matching]::TYPOS:_default' \\\n'--split-match=[Enable split matching and set delimiter]::SPLIT_MATCH:_default' \\\n'--scheme=[]:SCHEME:((default\\:\"Default scheme, no modifications to the options\"\npath\\:\"Path scheme\\: will find the furthest match in the item and set pathname as the main tiebreak\"\nhistory\\:\"History scheme\\: will force index as the first tiebreak\"))' \\\n'*-b+[Comma separated list of bindings]::BIND:_default' \\\n'*--bind=[Comma separated list of bindings]::BIND:_default' \\\n'-c+[Command to invoke dynamically in interactive mode]:CMD:_default' \\\n'--cmd=[Command to invoke dynamically in interactive mode]:CMD:_default' \\\n'-I+[Replace replstr with the selected item in commands]:REPLSTR:_default' \\\n'--color=[Set color theme]:COLOR:_default' \\\n'--skip-to-pattern=[Show the matched pattern at the line start]:SKIP_TO_PATTERN:_default' \\\n'--layout=[Set layout]:LAYOUT:((default\\:\"Display from the bottom of the screen\"\nreverse\\:\"Display from the top of the screen\"\nreverse-list\\:\"Display from the top of the screen, prompt at the bottom\"))' \\\n'--height=[Height of skim'\\''s window]:HEIGHT:_default' \\\n'--min-height=[Minimum height of skim'\\''s window]:MIN_HEIGHT:_default' \\\n'--margin=[Screen margin]:MARGIN:_default' \\\n'-p+[Set prompt]:PROMPT:_default' \\\n'--prompt=[Set prompt]:PROMPT:_default' \\\n'--cmd-prompt=[Set prompt in command mode]:CMD_PROMPT:_default' \\\n'--selector=[Set selected item icon]:SELECTOR_ICON:_default' \\\n'--multi-selector=[Set selected item icon]:MULTI_SELECT_ICON:_default' \\\n'--tabstop=[Number of spaces that make up a tab]:TABSTOP:_default' \\\n'--ellipsis=[The characters used to display truncated lines]:ELLIPSIS:_default' \\\n'--info=[Set matching result count display position]:INFO:(default inline hidden)' \\\n'--header=[Set header, displayed next to the info]:HEADER:_default' \\\n'--header-lines=[Number of lines of the input treated as header]:HEADER_LINES:_default' \\\n'--border=[Draw borders around the UI components]::BORDER:(plain rounded double thick light-double-dashed heavy-double-dashed light-triple-dashed heavy-triple-dashed light-quadruple-dashed heavy-quadruple-dashed quadrant-inside quadrant-outside)' \\\n'--history=[History file]:HISTORY_FILE:_default' \\\n'--history-size=[Maximum number of query history entries to keep]:HISTORY_SIZE:_default' \\\n'--cmd-history=[Command history file]:CMD_HISTORY_FILE:_default' \\\n'--cmd-history-size=[Maximum number of query history entries to keep]:CMD_HISTORY_SIZE:_default' \\\n'--preview=[Preview command]:PREVIEW:_default' \\\n'--preview-window=[Preview window layout]:PREVIEW_WINDOW:_default' \\\n'-q+[Initial query]:QUERY:_default' \\\n'--query=[Initial query]:QUERY:_default' \\\n'--cmd-query=[Initial query in interactive mode]:CMD_QUERY:_default' \\\n'--output-format=[Set the output format If set, overrides all print_ options Will be expanded the same way as preview or commands]:OUTPUT_FORMAT:_default' \\\n'--pre-select-n=[Pre-select the first n items in multi-selection mode]:PRE_SELECT_N:_default' \\\n'--pre-select-pat=[Pre-select the matched items in multi-selection mode]:PRE_SELECT_PAT:_default' \\\n'--pre-select-items=[Pre-select the items separated by newline character]:PRE_SELECT_ITEMS:_default' \\\n'--pre-select-file=[Pre-select the items read from this file]:PRE_SELECT_FILE:_default' \\\n'-f+[Query for filter mode]:FILTER:_default' \\\n'--filter=[Query for filter mode]:FILTER:_default' \\\n'--shell=[Generate shell completion script]:SHELL:((bash\\:\"Bourne Again SHell\"\nelvish\\:\"Elvish shell\"\nfish\\:\"Friendly Interactive SHell\"\nnushell\\:\"Nushell (nu)\"\npower-shell\\:\"PowerShell\"\nzsh\\:\"Zsh\"))' \\\n'--listen=[Run an IPC socket with optional name (defaults to sk)]::LISTEN:_default' \\\n'--remote=[Send commands to an IPC socket with optional name (defaults to sk)]::REMOTE:_default' \\\n'--tmux=[Run in a tmux popup]::TMUX:_default' \\\n'--log-level=[Set the log level]:LOG_LEVEL:_default' \\\n'--log-file=[Pipe log output to a file]:LOG_FILE:_default' \\\n'*--flags=[Feature flags]:FLAGS:((no-preview-pty\\:\"Disable preview PTY on linux\"\nshow-score\\:\"Display the item'\\''s match score before its value in the item list (for matcher debugging)\"\nshow-index\\:\"Display the item'\\''s index before its value in the item list\"))' \\\n'--hscroll-off=[]:HSCROLL_OFF:_default' \\\n'--jump-labels=[]:JUMP_LABELS:_default' \\\n'--tail=[]:TAIL:_default' \\\n'--style=[]:STYLE:_default' \\\n'--padding=[]:PADDING:_default' \\\n'--border-label=[]:BORDER_LABEL:_default' \\\n'--border-label-pos=[]:BORDER_LABEL_POS:_default' \\\n'--wrap-sign=[]:WRAP_SIGN:_default' \\\n'--gap=[]:GAP:_default' \\\n'--gap-line=[]:GAP_LINE:_default' \\\n'--freeze-left=[]:FREEZE_LEFT:_default' \\\n'--freeze-right=[]:FREEZE_RIGHT:_default' \\\n'--scroll-off=[]:SCROLL_OFF:_default' \\\n'--gutter=[]:GUTTER:_default' \\\n'--gutter-raw=[]:GUTTER_RAW:_default' \\\n'--marker-multi-line=[]:MARKER_MULTI_LINE:_default' \\\n'--scrollbar=[]:SCROLLBAR:_default' \\\n'--list-border=[]:LIST_BORDER:_default' \\\n'--list-label=[]:LIST_LABEL:_default' \\\n'--list-label-pos=[]:LIST_LABEL_POS:_default' \\\n'--info-command=[]:INFO_COMMAND:_default' \\\n'--separator=[]:SEPARATOR:_default' \\\n'--ghost=[]:GHOST:_default' \\\n'--input-border=[]:INPUT_BORDER:_default' \\\n'--input-label=[]:INPUT_LABEL:_default' \\\n'--input-label-pos=[]:INPUT_LABEL_POS:_default' \\\n'--preview-label=[]:PREVIEW_LABEL:_default' \\\n'--preview-label-pos=[]:PREVIEW_LABEL_POS:_default' \\\n'--header-border=[]:HEADER_BORDER:_default' \\\n'--header-lines-border=[]:HEADER_LINES_BORDER:_default' \\\n'--footer=[]:FOOTER:_default' \\\n'--footer-border=[]:FOOTER_BORDER:_default' \\\n'--footer-label=[]:FOOTER_LABEL:_default' \\\n'--footer-label-pos=[]:FOOTER_LABEL_POS:_default' \\\n'--with-shell=[]:WITH_SHELL:_default' \\\n'--expect=[Deprecated, kept for compatibility purposes. See accept() bind instead]:EXPECT:_default' \\\n'--tac[Show results in reverse order]' \\\n'--no-sort[Do not sort the results]' \\\n'-e[Run in exact mode]' \\\n'--exact[Run in exact mode]' \\\n'--regex[Start in regex mode instead of fuzzy-match]' \\\n'(--typos --typos)--no-typos[Disable typo-resistant matching]' \\\n'--normalize[Normalize unicode characters]' \\\n'--last-match[Highlight the last match found, not the first one This makes tiebreak more pertinent on path items where we want to prioritize a match on the last parts]' \\\n'-m[Enable multiple selection]' \\\n'--multi[Enable multiple selection]' \\\n'(-m --multi)--no-multi[Disable multiple selection]' \\\n'--no-mouse[Disable mouse]' \\\n'-i[Start skim in interactive mode]' \\\n'--interactive[Start skim in interactive mode]' \\\n'--no-hscroll[Disable horizontal scroll]' \\\n'--keep-right[Keep the right end of the line visible on overflow]' \\\n'--no-clear-if-empty[Do not clear previous line if the command returns an empty result]' \\\n'--no-clear-start[Do not clear items on start]' \\\n'--no-clear[Do not clear screen on exit]' \\\n'--show-cmd-error[Show error message if command fails]' \\\n'--cycle[Cycle the results by wrapping around when scrolling]' \\\n'--disabled[Disable matching entirely]' \\\n'--reverse[Shorthand for reverse layout]' \\\n'--no-height[Disable height (force full screen)]' \\\n'--ansi[Parse ANSI color codes in input strings]' \\\n'--no-info[Alias for --info=hidden]' \\\n'--inline-info[Alias for --info=inline]' \\\n'--wrap[Wrap items in the item list]' \\\n'--read0[Read input delimited by ASCII NUL(\\\\0) characters]' \\\n'--print0[Print output delimited by ASCII NUL(\\\\0) characters]' \\\n'--print-query[Print the query as the first line]' \\\n'--print-cmd[Print the command as the first line (after print-query)]' \\\n'--print-score[Print the score after each item]' \\\n'--print-header[Print the header as the first line (after print-score)]' \\\n'--print-current[Print the current (highlighted) item as the first line (after print-header)]' \\\n'--no-strip-ansi[Print the ANSI codes, making the output exactly match the input even when --ansi is on]' \\\n'-1[Do not enter the TUI if the query passed in -q matches only one item and return it]' \\\n'--select-1[Do not enter the TUI if the query passed in -q matches only one item and return it]' \\\n'-0[Do not enter the TUI if the query passed in -q does not match any item]' \\\n'--exit-0[Do not enter the TUI if the query passed in -q does not match any item]' \\\n'--sync[Synchronous search for multi-staged filtering]' \\\n'--shell-bindings[Generate shell key bindings - only for bash, zsh and fish]' \\\n'--man[Generate man page and output it to stdout]' \\\n'-x[]' \\\n'--extended[]' \\\n'--literal[]' \\\n'--filepath-word[]' \\\n'--no-bold[]' \\\n'--phony[]' \\\n'--no-color[]' \\\n'--highlight-line[]' \\\n'--no-multi-line[]' \\\n'--raw[]' \\\n'--track[]' \\\n'--no-scrollbar[]' \\\n'--no-input[]' \\\n'--no-separator[]' \\\n'--header-first[]' \\\n'-h[Print help (see more with '\\''--help'\\'')]' \\\n'--help[Print help (see more with '\\''--help'\\'')]' \\\n'-V[Print version]' \\\n'--version[Print version]' \\\n&& ret=0\n}\n\n(( $+functions[_sk_commands] )) ||\n_sk_commands() {\n    local commands; commands=()\n    _describe -t commands 'sk commands' commands \"$@\"\n}\n\nif [ \"$funcstack[1]\" = \"_sk\" ]; then\n    _sk \"$@\"\nelse\n    compdef _sk sk\nfi\n"
  },
  {
    "path": "shell/key-bindings.bash",
    "content": "# skim key bindings for bash\n#\n# - $SKIM_TMUX_OPTS\n# - $SKIM_CTRL_T_COMMAND\n# - $SKIM_CTRL_T_OPTS\n# - $SKIM_CTRL_R_OPTS\n# - $SKIM_CTRL_R_IDX_COLOR  (default: \\033[2m, set NO_COLOR to disable)\n# - $SKIM_ALT_C_COMMAND\n# - $SKIM_ALT_C_OPTS\n# - $SKIM_COMPLETION_TRIGGER (default: '**')\n# - $SKIM_COMPLETION_OPTS    (default: empty)\n\n# Key bindings\n# ------------\n__skim_select__() {\n\tlocal cmd=\"${SKIM_CTRL_T_COMMAND:-\"command find -L . -mindepth 1 \\\\( -path '*/\\\\.*' -o -fstype 'sysfs' -o -fstype 'devfs' -o -fstype 'devtmpfs' -o -fstype 'proc' \\\\) -prune \\\n    -o -type f -print \\\n    -o -type d -print \\\n    -o -type l -print 2> /dev/null | cut -b3-\"}\"\n\teval \"$cmd\" | SKIM_DEFAULT_OPTIONS=\"--reverse $SKIM_DEFAULT_OPTIONS $SKIM_CTRL_T_OPTS\" $(__skimcmd) -m \"$@\" | while read -r item; do\n\t\tprintf '%q ' \"$item\"\n\tdone\n\techo\n}\n\nif [[ $- =~ i ]]; then\n\n\t__skimcmd() {\n\t\t[ -n \"$TMUX_PANE\" ] && { [ \"${SKIM_TMUX:-0}\" != 0 ] || [ -n \"$SKIM_TMUX_OPTS\" ]; } &&\n\t\t\techo \"sk --tmux=${SKIM_TMUX_OPTS:-center,${SKIM_TMUX_HEIGHT:-40%}}\" || echo \"sk\"\n\t}\n\n\tskim-file-widget() {\n\t\tlocal selected=\"$(__skim_select__)\"\n\t\tREADLINE_LINE=\"${READLINE_LINE:0:$READLINE_POINT}$selected${READLINE_LINE:$READLINE_POINT}\"\n\t\tREADLINE_POINT=$((READLINE_POINT + ${#selected}))\n\t}\n\n\t__skim_cd__() {\n\t\tlocal cmd dir\n\t\tcmd=\"${SKIM_ALT_C_COMMAND:-\"command find -L . -mindepth 1 \\\\( -path '*/\\\\.*' -o -fstype 'sysfs' -o -fstype 'devfs' -o -fstype 'devtmpfs' -o -fstype 'proc' \\\\) -prune \\\n    -o -type d -print 2> /dev/null | cut -b3-\"}\"\n\t\tdir=$(eval \"$cmd\" | SKIM_DEFAULT_OPTIONS=\"--reverse $SKIM_DEFAULT_OPTIONS $SKIM_ALT_C_OPTS\" $(__skimcmd) --no-multi)\n\t\tprintf 'cd %q' \"$dir\"\n\t}\n\n\t__skim_history__() {\n\t\tlocal output\n\t\tlocal c_idx='' c_reset='' ansi_opt=''\n\t\tif [[ ! -v NO_COLOR ]]; then\n\t\t\tc_idx=\"${SKIM_CTRL_R_IDX_COLOR:-\\033[2m}\"\n\t\t\tc_reset='\\033[0m'\n\t\t\tansi_opt='--ansi'\n\t\tfi\n\t\toutput=$(\n\t\t\tbuiltin fc -lnr -2147483648 |\n\t\t\t\tlast_hist=$(HISTTIMEFORMAT='' builtin history 1) awk -v last_hist=\"$last_hist\" -v c_idx=\"$c_idx\" -v c_reset=\"$c_reset\" '\n        BEGIN { HISTCMD = last_hist + 1; cmd = \"\"; idx = 0 }\n        /^\\t/ {\n          if (cmd != \"\" && !seen[cmd]++) printf \"%s%d%s\\t%s\\0\", c_idx, HISTCMD - idx, c_reset, cmd\n          idx++; cmd = substr($0, 2); sub(/^[ *]/, \"\", cmd); next\n        }\n        { cmd = cmd \"\\n\" $0 }\n        END { if (cmd != \"\" && !seen[cmd]++) printf \"%s%d%s\\t%s\\0\", c_idx, HISTCMD - idx, c_reset, cmd }\n      ' |\n\t\t\t\tSKIM_DEFAULT_OPTIONS=\"$SKIM_DEFAULT_OPTIONS -n2..,.. --bind=ctrl-r:toggle-sort $SKIM_CTRL_R_OPTS --no-multi --read0 $ansi_opt\" $(__skimcmd) --query \"$READLINE_LINE\"\n\t\t) || return\n\t\techo -e \"\\033[0m\"\n\t\tREADLINE_LINE=${output#*$'\\t'}\n\t\tif [ -z \"$READLINE_POINT\" ]; then\n\t\t\techo \"$READLINE_LINE\"\n\t\telse\n\t\t\tREADLINE_POINT=0x7fffffff\n\t\tfi\n\t}\n\n\t# Required to refresh the prompt after skim\n\tbind -m emacs-standard '\"\\er\": redraw-current-line'\n\n\tbind -m vi-command '\"\\C-z\": emacs-editing-mode'\n\tbind -m vi-insert '\"\\C-z\": emacs-editing-mode'\n\tbind -m emacs-standard '\"\\C-z\": vi-editing-mode'\n\n\tif [ \"${BASH_VERSINFO[0]}\" -lt 4 ]; then\n\t\t# CTRL-T - Paste the selected file path into the command line\n\t\tbind -m emacs-standard '\"\\C-t\": \" \\C-b\\C-k \\C-u`__skim_select__`\\e\\C-e\\er\\C-a\\C-y\\C-h\\C-e\\e \\C-y\\ey\\C-x\\C-x\\C-f\"'\n\t\tbind -m vi-command '\"\\C-t\": \"\\C-z\\C-t\\C-z\"'\n\t\tbind -m vi-insert '\"\\C-t\": \"\\C-z\\C-t\\C-z\"'\n\n\t\t# CTRL-R - Paste the selected command from history into the command line\n\t\tbind -m emacs-standard '\"\\C-r\": \"\\C-e \\C-u\\C-y\\ey\\C-u\"$(__skim_history__)\"\\e\\C-e\\er\"'\n\t\tbind -m vi-command '\"\\C-r\": \"\\C-z\\C-r\\C-z\"'\n\t\tbind -m vi-insert '\"\\C-r\": \"\\C-z\\C-r\\C-z\"'\n\telse\n\t\t# CTRL-T - Paste the selected file path into the command line\n\t\tbind -m emacs-standard -x '\"\\C-t\": skim-file-widget'\n\t\tbind -m vi-command -x '\"\\C-t\": skim-file-widget'\n\t\tbind -m vi-insert -x '\"\\C-t\": skim-file-widget'\n\n\t\t# CTRL-R - Paste the selected command from history into the command line\n\t\tbind -m emacs-standard -x '\"\\C-r\": __skim_history__'\n\t\tbind -m vi-command -x '\"\\C-r\": __skim_history__'\n\t\tbind -m vi-insert -x '\"\\C-r\": __skim_history__'\n\tfi\n\n\t# ALT-C - cd into the selected directory\n\tbind -m emacs-standard '\"\\ec\": \" \\C-b\\C-k \\C-u`__skim_cd__`\\e\\C-e\\er\\C-m\\C-y\\C-h\\e \\C-y\\ey\\C-x\\C-x\\C-d\"'\n\tbind -m vi-command '\"\\ec\": \"\\C-z\\ec\\C-z\"'\n\tbind -m vi-insert '\"\\ec\": \"\\C-z\\ec\\C-z\"'\n\n\t# Completion\n\n\tif ! declare -f _skim_compgen_path >/dev/null; then\n\t\t_skim_compgen_path() {\n\t\t\techo \"$1\"\n\t\t\tcommand find -L \"$1\" \\\n\t\t\t\t-name .git -prune -o -name .hg -prune -o -name .svn -prune -o \\( -type d -o -type f -o -type l \\) \\\n\t\t\t\t-a -not -path \"$1\" -print 2>/dev/null | sed 's@^\\./@@'\n\t\t}\n\tfi\n\n\tif ! declare -f _skim_compgen_dir >/dev/null; then\n\t\t_skim_compgen_dir() {\n\t\t\tcommand find -L \"$1\" \\\n\t\t\t\t-name .git -prune -o -name .hg -prune -o -name .svn -prune -o -type d \\\n\t\t\t\t-a -not -path \"$1\" -print 2>/dev/null | sed 's@^\\./@@'\n\t\t}\n\tfi\n\n\t###########################################################\n\n\t__skim_comprun() {\n\t\tif [ \"$(type -t _skim_comprun 2>&1)\" = function ]; then\n\t\t\t_skim_comprun \"$@\"\n\t\telif [ -n \"$TMUX_PANE\" ] && { [ \"${SKIM_TMUX:-0}\" != 0 ] || [ -n \"$SKIM_TMUX_OPTS\" ]; }; then\n\t\t\tshift\n\t\t\tsk --tmux=${SKIM_TMUX_OPTS:-center,${SKIM_TMUX_HEIGHT:-40%}} \"$@\"\n\t\telse\n\t\t\tshift\n\t\t\tsk \"$@\"\n\t\tfi\n\t}\n\n\t__skim_orig_completion_filter() {\n\t\tsed 's/^\\(.*-F\\) *\\([^ ]*\\).* \\([^ ]*\\)$/export _skim_orig_completion_\\3=\"\\1 %s \\3 #\\2\"; [[ \"\\1\" = *\" -o nospace \"* ]] \\&\\& [[ ! \"$__skim_nospace_commands\" = *\" \\3 \"* ]] \\&\\& __skim_nospace_commands=\"$__skim_nospace_commands \\3 \";/' |\n\t\t\tawk -F= '{OFS = FS} {gsub(/[^A-Za-z0-9_= ;]/, \"_\", $1);}1'\n\t}\n\n\t_skim_handle_dynamic_completion() {\n\t\tlocal cmd orig_var orig ret orig_cmd orig_complete\n\t\tcmd=\"$1\"\n\t\tshift\n\t\torig_cmd=\"$1\"\n\t\torig_var=\"_skim_orig_completion_$cmd\"\n\t\torig=\"${!orig_var##*#}\"\n\t\tif [ -n \"$orig\" ] && type \"$orig\" >/dev/null 2>&1; then\n\t\t\t$orig \"$@\"\n\t\telif [ -n \"$_skim_completion_loader\" ]; then\n\t\t\torig_complete=$(complete -p \"$orig_cmd\" 2>/dev/null)\n\t\t\t_completion_loader \"$@\"\n\t\t\tret=$?\n\t\t\t# _completion_loader may not have updated completion for the command\n\t\t\tif [ \"$(complete -p \"$orig_cmd\" 2>/dev/null)\" != \"$orig_complete\" ]; then\n\t\t\t\teval \"$(complete | command grep \" -F.* $orig_cmd$\" | __skim_orig_completion_filter)\"\n\t\t\t\tif [[ \"$__skim_nospace_commands\" = *\" $orig_cmd \"* ]]; then\n\t\t\t\t\teval \"${orig_complete/ -F / -o nospace -F }\"\n\t\t\t\telse\n\t\t\t\t\teval \"$orig_complete\"\n\t\t\t\tfi\n\t\t\tfi\n\t\t\treturn $ret\n\t\tfi\n\t}\n\n\t__skim_generic_path_completion() {\n\t\tlocal cur base dir leftover matches trigger cmd\n\t\tcmd=\"${COMP_WORDS[0]//[^A-Za-z0-9_=]/_}\"\n\t\tCOMPREPLY=()\n\t\ttrigger=${SKIM_COMPLETION_TRIGGER-'**'}\n\t\tcur=\"${COMP_WORDS[COMP_CWORD]}\"\n\t\tif [[ \"$cur\" == *\"$trigger\" ]]; then\n\t\t\tbase=${cur:0:${#cur}-${#trigger}}\n\t\t\teval \"base=$base\"\n\n\t\t\t[[ $base = *\"/\"* ]] && dir=\"$base\"\n\t\t\twhile true; do\n\t\t\t\tif [ -z \"$dir\" ] || [ -d \"$dir\" ]; then\n\t\t\t\t\tleftover=${base/#\"$dir\"/}\n\t\t\t\t\tleftover=${leftover/#\\//}\n\t\t\t\t\t[ -z \"$dir\" ] && dir='.'\n\t\t\t\t\t[ \"$dir\" != \"/\" ] && dir=\"${dir/%\\//}\"\n\t\t\t\t\tmatches=$(eval \"$1 $(printf %q \"$dir\")\" | SKIM_DEFAULT_OPTIONS=\"--reverse $SKIM_DEFAULT_OPTIONS $SKIM_COMPLETION_OPTS $2\" __skim_comprun \"$4\" -q \"$leftover\" | while read -r item; do\n\t\t\t\t\t\tprintf \"%q$3 \" \"$item\"\n\t\t\t\t\tdone)\n\t\t\t\t\tmatches=${matches% }\n\t\t\t\t\t[[ -z \"$3\" ]] && [[ \"$__skim_nospace_commands\" = *\" ${COMP_WORDS[0]} \"* ]] && matches=\"$matches \"\n\t\t\t\t\tif [ -n \"$matches\" ]; then\n\t\t\t\t\t\tCOMPREPLY=(\"$matches\")\n\t\t\t\t\telse\n\t\t\t\t\t\tCOMPREPLY=(\"$cur\")\n\t\t\t\t\tfi\n\t\t\t\t\t# To redraw line after skim closes (printf '\\e[5n')\n\t\t\t\t\tbind '\"\\e[0n\": redraw-current-line'\n\t\t\t\t\tprintf '\\e[5n'\n\t\t\t\t\treturn 0\n\t\t\t\tfi\n\t\t\t\tdir=$(dirname \"$dir\")\n\t\t\t\t[[ \"$dir\" =~ /$ ]] || dir=\"$dir\"/\n\t\t\tdone\n\t\telse\n\t\t\tshift\n\t\t\tshift\n\t\t\tshift\n\t\t\t_skim_handle_dynamic_completion \"$cmd\" \"$@\"\n\t\tfi\n\t}\n\n\t_skim_complete() {\n\t\t# Split arguments around --\n\t\tlocal args rest str_arg i sep\n\t\targs=(\"$@\")\n\t\tsep=\n\t\tfor i in \"${!args[@]}\"; do\n\t\t\tif [[ \"${args[$i]}\" = -- ]]; then\n\t\t\t\tsep=$i\n\t\t\t\tbreak\n\t\t\tfi\n\t\tdone\n\t\tif [[ -n \"$sep\" ]]; then\n\t\t\tstr_arg=\n\t\t\trest=(\"${args[@]:$((sep + 1)):${#args[@]}}\")\n\t\t\targs=(\"${args[@]:0:$sep}\")\n\t\telse\n\t\t\tstr_arg=$1\n\t\t\targs=()\n\t\t\tshift\n\t\t\trest=(\"$@\")\n\t\tfi\n\n\t\tlocal cur selected trigger cmd post\n\t\tpost=\"$(caller 0 | awk '{print $2}')_post\"\n\t\ttype -t \"$post\" >/dev/null 2>&1 || post=cat\n\n\t\tcmd=\"${COMP_WORDS[0]//[^A-Za-z0-9_=]/_}\"\n\t\ttrigger=${SKIM_COMPLETION_TRIGGER-'**'}\n\t\tcur=\"${COMP_WORDS[COMP_CWORD]}\"\n\t\tif [[ \"$cur\" == *\"$trigger\" ]]; then\n\t\t\tcur=${cur:0:${#cur}-${#trigger}}\n\n\t\t\tselected=$(SKIM_DEFAULT_OPTIONS=\"--reverse $SKIM_DEFAULT_OPTIONS $SKIM_COMPLETION_OPTS $str_arg\" __skim_comprun \"${rest[0]}\" \"${args[@]}\" -q \"$cur\" | $post | tr '\\n' ' ')\n\t\t\tselected=${selected% } # Strip trailing space not to repeat \"-o nospace\"\n\t\t\tif [ -n \"$selected\" ]; then\n\t\t\t\tCOMPREPLY=(\"$selected\")\n\t\t\telse\n\t\t\t\tCOMPREPLY=(\"$cur\")\n\t\t\tfi\n\t\t\t# To redraw line after skim closes (printf '\\e[5n')\n\t\t\tbind '\"\\e[0n\": redraw-current-line'\n\t\t\tprintf '\\e[5n'\n\t\t\techo -e \"\\033[0m\"\n\t\t\treturn 0\n\t\telse\n\t\t\t_skim_handle_dynamic_completion \"$cmd\" \"${rest[@]}\"\n\t\tfi\n\t}\n\n\t_skim_path_completion() {\n\t\t__skim_generic_path_completion _skim_compgen_path \"-m\" \"\" \"$@\"\n\t}\n\n\t# Deprecated. No file only completion.\n\t_skim_file_completion() {\n\t\t_skim_path_completion \"$@\"\n\t}\n\n\t_skim_dir_completion() {\n\t\t__skim_generic_path_completion _skim_compgen_dir \"\" \"/\" \"$@\"\n\t}\n\n\t_skim_complete_kill() {\n\t\tlocal trigger=${SKIM_COMPLETION_TRIGGER-'**'}\n\t\tlocal cur=\"${COMP_WORDS[COMP_CWORD]}\"\n\t\tif [[ -z \"$cur\" ]]; then\n\t\t\tCOMP_WORDS[$COMP_CWORD]=$trigger\n\t\telif [[ \"$cur\" != *\"$trigger\" ]]; then\n\t\t\treturn 1\n\t\tfi\n\n\t\t_skim_proc_completion \"$@\"\n\t}\n\n\t_skim_proc_completion() {\n\t\t_skim_complete -m --preview 'echo {}' --preview-window down:3:wrap --min-height 15 -- \"$@\" < <(\n\t\t\tcommand ps -ef | sed 1d\n\t\t)\n\t}\n\n\t_skim_proc_completion_post() {\n\t\tawk '{print $2}'\n\t}\n\n\t_skim_host_completion() {\n\t\t_skim_complete --no-multi -- \"$@\" < <(\n\t\t\tcommand cat <(command tail -n +1 ~/.ssh/config ~/.ssh/config.d/* /etc/ssh/ssh_config 2>/dev/null | command grep -i '^\\s*host\\(name\\)\\? ' | awk '{for (i = 2; i <= NF; i++) print $1 \" \" $i}' | command grep -v '[*?]') \\\n\t\t\t\t<(command grep -oE '^[[a-z0-9.,:-]+' ~/.ssh/known_hosts | tr ',' '\\n' | tr -d '[' | awk '{ print $1 \" \" $1 }') \\\n\t\t\t\t<(command grep -v '^\\s*\\(#\\|$\\)' /etc/hosts | command grep -Fv '0.0.0.0') |\n\t\t\t\tawk '{if (length($2) > 0) {print $2}}' | sort -u\n\t\t)\n\t}\n\n\t_skim_var_completion() {\n\t\t_skim_complete -m -- \"$@\" < <(\n\t\t\tdeclare -xp | sed 's/=.*//' | sed 's/.* //'\n\t\t)\n\t}\n\n\t_skim_alias_completion() {\n\t\t_skim_complete -m -- \"$@\" < <(\n\t\t\talias | sed 's/=.*//' | sed 's/.* //'\n\t\t)\n\t}\n\n\td_cmds=\"${SKIM_COMPLETION_DIR_COMMANDS:-cd pushd rmdir}\"\n\ta_cmds=\"\n  awk cat diff diff3\n  emacs emacsclient ex file ftp g++ gcc gvim head hg java\n  javac ld less more mvim nvim patch perl python ruby\n  sed sftp sort source tail tee uniq vi view vim wc xdg-open\n  basename bunzip2 bzip2 chmod chown curl cp dirname du\n  find git grep gunzip gzip hg jar\n  ln ls mv open rm rsync scp\n  svn tar unzip zip\"\n\n\t# Preserve existing completion\n\teval \"$(complete |\n\t\tsed -E '/-F/!d; / _skim/d; '\"/ ($(echo $d_cmds $a_cmds | sed 's/ /|/g; s/+/\\\\+/g'))$/\"'!d' |\n\t\t__skim_orig_completion_filter)\"\n\n\tif type _completion_loader >/dev/null 2>&1; then\n\t\t_skim_completion_loader=1\n\tfi\n\n\t__skim_defc() {\n\t\tlocal cmd func opts orig_var orig def\n\t\tcmd=\"$1\"\n\t\tfunc=\"$2\"\n\t\topts=\"$3\"\n\t\torig_var=\"_skim_orig_completion_${cmd//[^A-Za-z0-9_]/_}\"\n\t\torig=\"${!orig_var}\"\n\t\tif [ -n \"$orig\" ]; then\n\t\t\tprintf -v def \"$orig\" \"$func\"\n\t\t\teval \"$def\"\n\t\telse\n\t\t\tcomplete -F \"$func\" $opts \"$cmd\"\n\t\tfi\n\t}\n\n\t# Anything\n\tfor cmd in $a_cmds; do\n\t\t__skim_defc \"$cmd\" _skim_path_completion \"-o default -o bashdefault\"\n\tdone\n\n\t# Directory\n\tfor cmd in $d_cmds; do\n\t\t__skim_defc \"$cmd\" _skim_dir_completion \"-o nospace -o dirnames\"\n\tdone\n\n\t# Kill completion (supports empty completion trigger)\n\tcomplete -F _skim_complete_kill -o default -o bashdefault kill\n\n\tunset cmd d_cmds a_cmds\n\n\t_skim_setup_completion() {\n\t\tlocal kind fn cmd\n\t\tkind=$1\n\t\tfn=_skim_${1}_completion\n\t\tif [[ $# -lt 2 ]] || ! type -t \"$fn\" >/dev/null; then\n\t\t\techo \"usage: ${FUNCNAME[0]} path|dir|var|alias|host|proc COMMANDS...\"\n\t\t\treturn 1\n\t\tfi\n\t\tshift\n\t\teval \"$(complete -p \"$@\" 2>/dev/null | grep -v \"$fn\" | __skim_orig_completion_filter)\"\n\t\tfor cmd in \"$@\"; do\n\t\t\tcase \"$kind\" in\n\t\t\tdir) __skim_defc \"$cmd\" \"$fn\" \"-o nospace -o dirnames\" ;;\n\t\t\tvar) __skim_defc \"$cmd\" \"$fn\" \"-o default -o nospace -v\" ;;\n\t\t\talias) __skim_defc \"$cmd\" \"$fn\" \"-a\" ;;\n\t\t\t*) __skim_defc \"$cmd\" \"$fn\" \"-o default -o bashdefault\" ;;\n\t\t\tesac\n\t\tdone\n\t}\n\n\t# Environment variables / Aliases / Hosts\n\t_skim_setup_completion 'var' export unset\n\t_skim_setup_completion 'alias' unalias\n\t_skim_setup_completion 'host' ssh telnet\n\nfi\n"
  },
  {
    "path": "shell/key-bindings.fish",
    "content": "#!/bin/fish\n# skim key bindings for fish\n#\n# - $SKIM_TMUX_OPTS\n# - $SKIM_CTRL_T_COMMAND\n# - $SKIM_CTRL_T_OPTS\n# - $SKIM_CTRL_R_OPTS\n# - $SKIM_ALT_C_COMMAND\n# - $SKIM_ALT_C_OPTS\n# - $SKIM_COMPLETION_TRIGGER (default: '**')\n# - $SKIM_COMPLETION_OPTS    (default: empty)\n\n# Key bindings\n# ------------\nfunction skim_key_bindings\n\n  # Store current token in $dir as root for the 'find' command\n  function skim-file-widget -d \"List files and folders\"\n    set -l commandline (__skim_parse_commandline)\n    set -l dir $commandline[1]\n    set -l skim_query $commandline[2]\n\n    # \"-path \\$dir'*/\\\\.*'\" matches hidden files/folders inside $dir but not\n    # $dir itself, even if hidden.\n    test -n \"$SKIM_CTRL_T_COMMAND\"; or set -l SKIM_CTRL_T_COMMAND \"\n    command find -L \\$dir -mindepth 1 \\\\( -path \\$dir'*/\\\\.*' -o -fstype 'sysfs' -o -fstype 'devfs' -o -fstype 'devtmpfs' \\\\) -prune \\\n    -o -type f -print \\\n    -o -type d -print \\\n    -o -type l -print 2> /dev/null | sed 's@^\\./@@'\"\n\n    begin\n      set -lx SKIM_DEFAULT_OPTIONS \"--reverse $SKIM_DEFAULT_OPTIONS $SKIM_CTRL_T_OPTS\"\n      eval \"$SKIM_CTRL_T_COMMAND | \"(__skimcmd)' -m --query \"'$skim_query'\"' | while read -l r; set result $result $r; end\n    end\n    if [ -z \"$result\" ]\n      commandline -f repaint\n      return\n    else\n      # Remove last token from commandline.\n      commandline -t \"\"\n    end\n    for i in $result\n      commandline -it -- (string escape $i)\n      commandline -it -- ' '\n    end\n    commandline -f repaint\n  end\n\n  function skim-history-widget -d \"Show command history\"\n    begin\n      set -lx SKIM_DEFAULT_OPTIONS \"$SKIM_DEFAULT_OPTIONS --bind=ctrl-r:toggle-sort $SKIM_CTRL_R_OPTS --no-multi\"\n\n      set -l FISH_MAJOR (echo $version | cut -f1 -d.)\n      set -l FISH_MINOR (echo $version | cut -f2 -d.)\n\n      # history's -z flag is needed for multi-line support.\n      # history's -z flag was added in fish 2.4.0, so don't use it for versions\n      # before 2.4.0.\n      if [ \"$FISH_MAJOR\" -gt 2 -o \\( \"$FISH_MAJOR\" -eq 2 -a \"$FISH_MINOR\" -ge 4 \\) ];\n        history -z | eval (__skimcmd) --read0 --print0 -q '(commandline)' | read -lz result\n        and commandline -- $result\n      else\n        history | eval (__skimcmd) -q '(commandline)' | read -l result\n        and commandline -- $result\n      end\n    end\n    commandline -f repaint\n  end\n\n  function skim-cd-widget -d \"Change directory\"\n    set -l commandline (__skim_parse_commandline)\n    set -l dir $commandline[1]\n    set -l skim_query $commandline[2]\n\n    test -n \"$SKIM_ALT_C_COMMAND\"; or set -l SKIM_ALT_C_COMMAND \"\n    command find -L \\$dir -mindepth 1 \\\\( -path \\$dir'*/\\\\.*' -o -fstype 'sysfs' -o -fstype 'devfs' -o -fstype 'devtmpfs' \\\\) -prune \\\n    -o -type d -print 2> /dev/null | sed 's@^\\./@@'\"\n    begin\n      set -lx SKIM_DEFAULT_OPTIONS \"--reverse $SKIM_DEFAULT_OPTIONS $SKIM_ALT_C_OPTS\"\n      eval \"$SKIM_ALT_C_COMMAND | \"(__skimcmd)' --no-multi --query \"'$skim_query'\"' | read -l result\n\n      if [ -n \"$result\" ]\n        cd $result\n\n        # Remove last token from commandline.\n        commandline -t \"\"\n      end\n    end\n\n    commandline -f repaint\n  end\n\n  function __skimcmd\n    test -n \"$SKIM_TMUX\"; or set SKIM_TMUX 0\n    test -n \"$SKIM_TMUX_HEIGHT\"; or set SKIM_TMUX_HEIGHT 40%\n    if [ -n \"$SKIM_TMUX_OPTS\" ]\n      echo \"sk --tmux=$SKIM_TMUX_OPTS \"\n    else if [ $SKIM_TMUX -eq 1 ]\n      echo \"sk --tmux=center,$SKIM_TMUX_HEIGHT\"\n    else\n      echo \"sk\"\n    end\n  end\n\n  bind \\ct skim-file-widget\n  bind \\cr skim-history-widget\n  bind \\ec skim-cd-widget\n\n  if bind -M insert > /dev/null 2>&1\n    bind -M insert \\ct skim-file-widget\n    bind -M insert \\cr skim-history-widget\n    bind -M insert \\ec skim-cd-widget\n  end\n\n  function __skim_parse_commandline -d 'Parse the current command line token and return split of existing filepath and rest of token'\n    # eval is used to do shell expansion on paths\n    set -l commandline (eval \"printf '%s' \"(commandline -t))\n\n    if [ -z $commandline ]\n      # Default to current directory with no --query\n      set dir '.'\n      set skim_query ''\n    else\n      set dir (__skim_get_dir $commandline)\n\n      if [ \"$dir\" = \".\" -a (string sub -l 1 -- $commandline) != '.' ]\n        # if $dir is \".\" but commandline is not a relative path, this means no file path found\n        set skim_query $commandline\n      else\n        # Also remove trailing slash after dir, to \"split\" input properly\n        set skim_query (string replace -r \"^$dir/?\" -- '' \"$commandline\")\n      end\n    end\n\n    echo $dir\n    echo $skim_query\n  end\n\n  function __skim_get_dir -d 'Find the longest existing filepath from input string'\n    set dir $argv\n\n    # Strip all trailing slashes. Ignore if $dir is root dir (/)\n    if [ (string length -- $dir) -gt 1 ]\n      set dir (string replace -r '/*$' -- '' $dir)\n    end\n\n    # Iteratively check if dir exists and strip tail end of path\n    while [ ! -d \"$dir\" ]\n      # If path is absolute, this can keep going until ends up at /\n      # If path is relative, this can keep going until entire input is consumed, dirname returns \".\"\n      set dir (dirname -- \"$dir\")\n    end\n\n    echo $dir\n  end\nend\n"
  },
  {
    "path": "shell/key-bindings.zsh",
    "content": "# skim key bindings for zsh\n#\n# - $SKIM_TMUX_OPTS\n# - $SKIM_CTRL_T_COMMAND\n# - $SKIM_CTRL_T_OPTS\n# - $SKIM_CTRL_R_OPTS\n# - $SKIM_CTRL_R_IDX_COLOR   (default: \\033[2m, set NO_COLOR to disable)\n# - $SKIM_CTRL_R_DATE_COLOR  (default: \\033[32m, set NO_COLOR to disable)\n# - $SKIM_ALT_C_COMMAND\n# - $SKIM_ALT_C_OPTS\n# - $SKIM_COMPLETION_TRIGGER (default: '**')\n# - $SKIM_COMPLETION_OPTS    (default: empty)\n\n# Key bindings\n# ------------\n\nif 'zmodload' 'zsh/parameter' 2>'/dev/null' && (( ${+options} )); then\n  __skim_key_bindings_options=\"options=(${(j: :)${(kv)options[@]}})\"\n  __skim_completion_options=\"options=(${(j: :)${(kv)options[@]}})\"\nelse\n  () {\n    __skim_key_bindings_options=\"setopt\"\n    'local' '__skim_opt'\n    for __skim_opt in \"${(@)${(@f)$(set -o)}%% *}\"; do\n      if [[ -o \"$__skim_opt\" ]]; then\n        __skim_key_bindings_options+=\" -o $__skim_opt\"\n      else\n        __skim_key_bindings_options+=\" +o $__skim_opt\"\n      fi\n    done\n  }\n  () {\n    'local' '__skim_opt'\n    __skim_completion_options=\"setopt\"\n    for __skim_opt in \"${(@)${(@f)$(set -o)}%% *}\"; do\n      if [[ -o \"$__skim_opt\" ]]; then\n        __skim_completion_options+=\" -o $__skim_opt\"\n      else\n        __skim_completion_options+=\" +o $__skim_opt\"\n      fi\n    done\n  }\nfi\n\n'emulate' 'zsh' '-o' 'no_aliases'\n\n{\n\n[[ -o interactive ]] || return 0\n\n# CTRL-T - Paste the selected file path(s) into the command line\n__fsel() {\n  local cmd=\"${SKIM_CTRL_T_COMMAND:-\"command find -L . -mindepth 1 \\\\( -path '*/\\\\.*' -o -fstype 'sysfs' -o -fstype 'devfs' -o -fstype 'devtmpfs' -o -fstype 'proc' \\\\) -prune \\\n    -o -type f -print \\\n    -o -type d -print \\\n    -o -type l -print 2> /dev/null | cut -b3-\"}\"\n  setopt localoptions pipefail no_aliases 2> /dev/null\n  REPORTTIME_=$REPORTTIME\n  unset REPORTTIME\n  eval \"$cmd\" | SKIM_DEFAULT_OPTIONS=\"--reverse $SKIM_DEFAULT_OPTIONS $SKIM_CTRL_T_OPTS\" $(__skimcmd) -m \"$@\" | while read item; do\n    echo -n \"${(q)item} \"\n  done\n  local ret=$?\n  echo\n  if ! [ -z $REPORTTIME_ ]; then\n      REPORTTIME=$REPORTTIME_\n  fi\n  unset REPORTTIME_\n  return $ret\n}\n\n__skimcmd() {\n  [ -n \"$TMUX_PANE\" ] && { [ \"${SKIM_TMUX:-0}\" != 0 ] || [ -n \"$SKIM_TMUX_OPTS\" ]; } &&\n    echo \"sk --tmux=${SKIM_TMUX_OPTS:-center,${SKIM_TMUX_HEIGHT:-40%}}\" || echo \"sk\"\n}\n\nskim-file-widget() {\n  LBUFFER=\"${LBUFFER}$(__fsel)\"\n  local ret=$?\n  zle reset-prompt\n  return $ret\n}\nzle     -N   skim-file-widget\nbindkey '^T' skim-file-widget\n\n# Ensure precmds are run after cd\nskim-redraw-prompt() {\n  local precmd\n  for precmd in $precmd_functions; do\n    $precmd\n  done\n  zle reset-prompt\n}\nzle -N skim-redraw-prompt\n\n# ALT-C - cd into the selected directory\nskim-cd-widget() {\n  local cmd=\"${SKIM_ALT_C_COMMAND:-\"command find -L . -mindepth 1 \\\\( -path '*/\\\\.*' -o -fstype 'sysfs' -o -fstype 'devfs' -o -fstype 'devtmpfs' -o -fstype 'proc' \\\\) -prune \\\n    -o -type d -print 2> /dev/null | cut -b3-\"}\"\n  setopt localoptions pipefail no_aliases 2> /dev/null\n  REPORTTIME_=$REPORTTIME\n  unset REPORTTIME\n  local dir=\"$(eval \"$cmd\" | SKIM_DEFAULT_OPTIONS=\"--reverse $SKIM_DEFAULT_OPTIONS $SKIM_ALT_C_OPTS\" $(__skimcmd) --no-multi)\"\n  if ! [ -z $REPORTTIME_ ]; then\n      REPORTTIME=$REPORTTIME_\n  fi\n  unset REPORTTIME_\n  if [[ -z \"$dir\" ]]; then\n    zle redisplay\n    return 0\n  fi\n  if [ -z \"$BUFFER\" ]; then\n    BUFFER=\"cd ${(q)dir}\"\n    zle accept-line\n  else\n    print -sr \"cd ${(q)dir}\"\n    cd \"$dir\"\n  fi\n  local ret=$?\n  unset dir # ensure this doesn't end up appearing in prompt expansion\n  zle skim-redraw-prompt\n  tput cnorm\n  return $ret\n}\nzle     -N    skim-cd-widget\nbindkey '\\ec' skim-cd-widget\n\n# CTRL-R - Paste the selected command from history into the command line\nskim-history-widget() {\n  local selected num\n  setopt localoptions noglobsubst noposixbuiltins pipefail no_aliases 2> /dev/null\n  local c_idx='' c_date='' c_reset='' ansi_opt=''\n  if [[ ! -v NO_COLOR ]]; then\n    c_idx=\"${SKIM_CTRL_R_IDX_COLOR:-\\033[2m}\"\n    c_date=\"${SKIM_CTRL_R_DATE_COLOR:-\\033[32m}\"\n    c_reset='\\033[0m'\n    ansi_opt='--ansi'\n  fi\n  local awk_filter='{ cmd=$0; sub(/^[ \\t]*[0-9]+\\**[ \\t]+/, \"\", cmd); if (!seen[cmd]++) { idx=$1; sub(idx, c_idx idx c_reset); print } }'\n  local n=2 fc_opts=''\n  if [[ -o extended_history ]]; then\n    local today=$(date +%Y-%m-%d)\n    # For today's commands, replace date ($2) with \"today\", otherwise remove time ($3).\n    # And filter out duplicates.\n    awk_filter='{\n      cmd = $0; sub(/^[ \\t]*[0-9]+\\**[ \\t]+[^ \\t]+[ \\t]+[^ \\t]+[ \\t]+/, \"\", cmd)\n      if (!seen[cmd]++) {\n        time = $3; date = $2; idx = $1\n        if (date == today) sub(date \" \" time \"  \", c_date \"today \" time c_reset \"\\t\")\n        else sub(date \" \" time \"  \", c_date date c_reset \"\\t\")\n        sub(idx, c_idx idx c_reset)\n        print\n      }\n    }'\n    fc_opts='-i'\n    n=3\n  fi\n  selected=( $(fc -rl $fc_opts 1 | awk -v c_idx=\"$c_idx\" -v c_date=\"$c_date\" -v c_reset=\"$c_reset\" -v today=\"$today\" \"$awk_filter\" |\n    SKIM_DEFAULT_OPTIONS=\"$SKIM_DEFAULT_OPTIONS -n$n..,.. --bind=ctrl-r:toggle-sort $SKIM_CTRL_R_OPTS --query=${(qqq)LBUFFER} --no-multi $ansi_opt --tabstop=20\" $(__skimcmd)) )\n  local ret=$?\n  if [ -n \"$selected\" ]; then\n    num=$selected[1]\n    if [ -n \"$num\" ]; then\n      zle vi-fetch-history -n $num\n    fi\n  fi\n  zle reset-prompt\n  tput cnorm\n  return $ret\n}\nzle     -N   skim-history-widget\nbindkey '^R' skim-history-widget\n\n# Completion\n# To use custom commands instead of find, override _skim_compgen_{path,dir}\nif ! declare -f _skim_compgen_path > /dev/null; then\n  _skim_compgen_path() {\n    echo \"$1\"\n    command find -L \"$1\" \\\n      -name .git -prune -o -name .hg -prune -o -name .svn -prune -o \\( -type d -o -type f -o -type l \\) \\\n      -a -not -path \"$1\" -print 2> /dev/null | sed 's@^\\./@@'\n  }\nfi\n\nif ! declare -f _skim_compgen_dir > /dev/null; then\n  _skim_compgen_dir() {\n    command find -L \"$1\" \\\n      -name .git -prune -o -name .hg -prune -o -name .svn -prune -o -type d \\\n      -a -not -path \"$1\" -print 2> /dev/null | sed 's@^\\./@@'\n  }\nfi\n\n###########################################################\n\n__skim_comprun() {\n  if [[ \"$(type _skim_comprun 2>&1)\" =~ function ]]; then\n    _skim_comprun \"$@\"\n  elif [ -n \"$TMUX_PANE\" ] && { [ \"${SKIM_TMUX:-0}\" != 0 ] || [ -n \"$SKIM_TMUX_OPTS\" ]; }; then\n    shift\n    if [ -n \"$SKIM_TMUX_OPTS\" ]; then\n      sk --tmux=${(Q)${(Z+n+)SKIM_TMUX_OPTS}} \"$@\"\n    else\n      sk --tmux=${SKIM_TMUX_OPTS:-center,${SKIM_TMUX_HEIGHT:-40%}} \"$@\"\n    fi\n  else\n    shift\n    sk \"$@\"\n  fi\n}\n\n# Extract the name of the command. e.g. foo=1 bar baz**<tab>\n__skim_extract_command() {\n  local token tokens\n  tokens=(${(z)1})\n  for token in $tokens; do\n    token=${(Q)token}\n    if [[ \"$token\" =~ [[:alnum:]] && ! \"$token\" =~ \"=\" ]]; then\n      echo \"$token\"\n      return\n    fi\n  done\n  echo \"${tokens[1]}\"\n}\n\n__skim_generic_path_completion() {\n  local base lbuf cmd compgen skim_opts suffix tail dir leftover matches\n  base=$1\n  lbuf=$2\n  cmd=$(__skim_extract_command \"$lbuf\")\n  compgen=$3\n  skim_opts=$4\n  suffix=$5\n  tail=$6\n\n  setopt localoptions nonomatch\n  eval \"base=$base\"\n  [[ $base = *\"/\"* ]] && dir=\"$base\"\n  while [ 1 ]; do\n    if [[ -z \"$dir\" || -d ${dir} ]]; then\n      leftover=${base/#\"$dir\"}\n      leftover=${leftover/#\\/}\n      [ -z \"$dir\" ] && dir='.'\n      [ \"$dir\" != \"/\" ] && dir=\"${dir/%\\//}\"\n      matches=$(eval \"$compgen $(printf %q \"$dir\")\" | SKIM_DEFAULT_OPTIONS=\"--reverse $SKIM_DEFAULT_OPTIONS $SKIM_COMPLETION_OPTS\" __skim_comprun \"$cmd\" ${(Q)${(Z+n+)skim_opts}} -q \"$leftover\" | while read item; do\n        echo -n \"${(q)item}$suffix \"\n      done)\n      matches=${matches% }\n      if [ -n \"$matches\" ]; then\n        LBUFFER=\"$lbuf$matches$tail\"\n      fi\n      zle reset-prompt\n      break\n    fi\n    dir=$(dirname \"$dir\")\n    dir=${dir%/}/\n  done\n}\n\n_skim_path_completion() {\n  __skim_generic_path_completion \"$1\" \"$2\" _skim_compgen_path \\\n    \"-m\" \"\" \" \"\n}\n\n_skim_dir_completion() {\n  __skim_generic_path_completion \"$1\" \"$2\" _skim_compgen_dir \\\n    \"\" \"/\" \"\"\n}\n\n_skim_feed_fifo() (\n  command rm -f \"$1\"\n  mkfifo \"$1\"\n  cat <&0 > \"$1\" &\n)\n\n_skim_complete() {\n  setopt localoptions ksh_arrays\n  # Split arguments around --\n  local args rest str_arg i sep\n  args=(\"$@\")\n  sep=\n  for i in {0..${#args[@]}}; do\n    if [[ \"${args[$i]}\" = -- ]]; then\n      sep=$i\n      break\n    fi\n  done\n  if [[ -n \"$sep\" ]]; then\n    str_arg=\n    rest=(\"${args[@]:$((sep + 1)):${#args[@]}}\")\n    args=(\"${args[@]:0:$sep}\")\n  else\n    str_arg=$1\n    args=()\n    shift\n    rest=(\"$@\")\n  fi\n\n  local fifo lbuf cmd matches post\n  fifo=\"${TMPDIR:-/tmp}/skim-complete-fifo-$$\"\n  lbuf=${rest[0]}\n  cmd=$(__skim_extract_command \"$lbuf\")\n  post=\"${funcstack[1]}_post\"\n  type $post > /dev/null 2>&1 || post=cat\n\n  _skim_feed_fifo \"$fifo\"\n  matches=$(SKIM_DEFAULT_OPTIONS=\"--reverse $SKIM_DEFAULT_OPTIONS $SKIM_COMPLETION_OPTS $str_arg\" __skim_comprun \"$cmd\" \"${args[@]}\" -q \"${(Q)prefix}\" < \"$fifo\" | $post | tr '\\n' ' ')\n  if [ -n \"$matches\" ]; then\n    LBUFFER=\"$lbuf$matches\"\n  fi\n  zle reset-prompt\n  command rm -f \"$fifo\"\n}\n\n_skim_complete_telnet() {\n  _skim_complete --no-multi -- \"$@\" < <(\n    command grep -v '^\\s*\\(#\\|$\\)' /etc/hosts | command grep -Fv '0.0.0.0' |\n        awk '{if (length($2) > 0) {print $2}}' | sort -u\n  )\n}\n\n_skim_complete_ssh() {\n  _skim_complete --no-multi -- \"$@\" < <(\n    setopt localoptions nonomatch\n    command cat <(command tail -n +1 ~/.ssh/config ~/.ssh/config.d/* /etc/ssh/ssh_config 2> /dev/null | command grep -i '^\\s*host\\(name\\)\\? ' | awk '{for (i = 2; i <= NF; i++) print $1 \" \" $i}' | command grep -v '[*?]') \\\n        <(command grep -oE '^[[a-z0-9.,:-]+' ~/.ssh/known_hosts | tr ',' '\\n' | tr -d '[' | awk '{ print $1 \" \" $1 }') \\\n        <(command grep -v '^\\s*\\(#\\|$\\)' /etc/hosts | command grep -Fv '0.0.0.0') |\n        awk '{if (length($2) > 0) {print $2}}' | sort -u\n  )\n}\n\n_skim_complete_export() {\n  _skim_complete -m -- \"$@\" < <(\n    declare -xp | sed 's/=.*//' | sed 's/.* //'\n  )\n}\n\n_skim_complete_unset() {\n  _skim_complete -m -- \"$@\" < <(\n    declare -xp | sed 's/=.*//' | sed 's/.* //'\n  )\n}\n\n_skim_complete_unalias() {\n  _skim_complete --no-multi -- \"$@\" < <(\n    alias | sed 's/=.*//'\n  )\n}\n\n_skim_complete_kill() {\n  _skim_complete -m --preview 'echo {}' --preview-window down:3:wrap --min-height 15 -- \"$@\" < <(\n    command ps -ef | sed 1d\n  )\n}\n\n_skim_complete_kill_post() {\n  awk '{print $2}'\n}\n\nskim-completion() {\n  local tokens cmd prefix trigger tail matches lbuf d_cmds\n  setopt localoptions noshwordsplit noksh_arrays noposixbuiltins\n\n  # http://zsh.sourceforge.net/FAQ/zshfaq03.html\n  # http://zsh.sourceforge.net/Doc/Release/Expansion.html#Parameter-Expansion-Flags\n  tokens=(${(z)LBUFFER})\n  if [ ${#tokens} -lt 1 ]; then\n    zle ${skim_default_completion:-expand-or-complete}\n    return\n  fi\n\n  cmd=$(__skim_extract_command \"$LBUFFER\")\n\n  # Explicitly allow for empty trigger.\n  trigger=${SKIM_COMPLETION_TRIGGER-'**'}\n  [ -z \"$trigger\" -a ${LBUFFER[-1]} = ' ' ] && tokens+=(\"\")\n\n  # When the trigger starts with ';', it becomes a separate token\n  if [[ ${LBUFFER} = *\"${tokens[-2]}${tokens[-1]}\" ]]; then\n    tokens[-2]=\"${tokens[-2]}${tokens[-1]}\"\n    tokens=(${tokens[0,-2]})\n  fi\n\n  lbuf=$LBUFFER\n  tail=${LBUFFER:$(( ${#LBUFFER} - ${#trigger} ))}\n  # Kill completion (do not require trigger sequence)\n  if [ \"$cmd\" = kill -a ${LBUFFER[-1]} = ' ' ]; then\n    tail=$trigger\n    tokens+=$trigger\n    lbuf=\"$lbuf$trigger\"\n  fi\n\n  # Trigger sequence given\n  if [ ${#tokens} -gt 1 -a \"$tail\" = \"$trigger\" ]; then\n    d_cmds=(${=SKIM_COMPLETION_DIR_COMMANDS:-cd pushd rmdir})\n\n    [ -z \"$trigger\"      ] && prefix=${tokens[-1]} || prefix=${tokens[-1]:0:-${#trigger}}\n    [ -n \"${tokens[-1]}\" ] && lbuf=${lbuf:0:-${#tokens[-1]}}\n\n    if eval \"type _skim_complete_${cmd} > /dev/null\"; then\n      prefix=\"$prefix\" eval _skim_complete_${cmd} ${(q)lbuf}\n    elif [ ${d_cmds[(i)$cmd]} -le ${#d_cmds} ]; then\n      _skim_dir_completion \"$prefix\" \"$lbuf\"\n    else\n      _skim_path_completion \"$prefix\" \"$lbuf\"\n    fi\n    tput cnorm\n  # Fall back to default completion\n  else\n    zle ${skim_default_completion:-expand-or-complete}\n  fi\n}\n\n[ -z \"$skim_default_completion\" ] && {\n  binding=$(bindkey '^I')\n  [[ $binding =~ 'undefined-key' ]] || skim_default_completion=$binding[(s: :w)2]\n  unset binding\n}\n\nzle     -N   skim-completion\nbindkey '^I' skim-completion\n\n} always {\n  eval $__skim_key_bindings_options\n  'unset' '__skim_key_bindings_options'\n  eval $__skim_completion_options\n  'unset' '__skim_completion_options'\n}\n"
  },
  {
    "path": "shell/version.txt",
    "content": "4.0.0\n"
  },
  {
    "path": "sonar-project.properties",
    "content": "sonar.projectKey=skim-rs_skim\nsonar.organization=skim\n\n\n# This is the name and version displayed in the SonarCloud UI.\n#sonar.projectName=skim\n#sonar.projectVersion=1.0\n\n\n# Path is relative to the sonar-project.properties file. Replace \"\\\" by \"/\" on Windows.\n#sonar.sources=.\n\n# Encoding of the source code. Default is default system encoding\n#sonar.sourceEncoding=UTF-8\n"
  },
  {
    "path": "src/bin/main.rs",
    "content": "//! Command-line interface for skim fuzzy finder.\n//!\n//! This binary provides the `sk` command-line tool for fuzzy finding and filtering.\n\nextern crate clap;\nextern crate env_logger;\nextern crate log;\nextern crate shlex;\nextern crate skim;\n\nuse crate::Event;\nuse color_eyre::Result;\nuse color_eyre::eyre::eyre;\nuse derive_builder::Builder;\nuse interprocess::bound_util::RefWrite;\nuse interprocess::local_socket::ToNsName as _;\nuse interprocess::local_socket::traits::Stream as _;\nuse log::trace;\nuse skim::binds::parse_action_chain;\nuse skim::reader::CommandCollector;\nuse skim::tui::event::Action;\nuse std::fs::File;\nuse std::io::{BufReader, BufWriter, IsTerminal, Write};\nuse std::{env, io};\n\nuse skim::prelude::*;\n\nfn init_logger(opts: &SkimOptions) {\n    let target = if let Some(ref log_file) = opts.log_file.as_ref().or(std::env::var(\"SKIM_LOG_FILE\").ok().as_ref()) {\n        env_logger::Target::Pipe(Box::new(File::create(log_file).expect(\"Failed to create log file\")))\n    } else {\n        env_logger::Target::Stdout\n    };\n\n    let env_var = \"SKIM_LOG\";\n\n    let format = |buf: &mut env_logger::fmt::Formatter, record: &log::Record<'_>| {\n        writeln!(\n            buf,\n            \"[{} {} {} ({}:{})] [{}/{:?}] {}\",\n            buf.timestamp_nanos(),\n            record.level().as_str(),\n            record.module_path().unwrap_or(\"sk\"),\n            record.file().unwrap_or_default(),\n            record.line().unwrap_or_default(),\n            std::thread::current().name().unwrap_or(\"?\"),\n            std::thread::current().id(),\n            record.args()\n        )\n    };\n\n    if let Some(level) = opts.log_level {\n        env_logger::builder()\n            .filter_level(level)\n            .parse_env(env_var)\n            .target(target)\n            .format(format)\n            .init();\n    } else {\n        env_logger::builder()\n            .parse_env(env_var)\n            .target(target)\n            .format(format)\n            .init();\n    };\n}\n\n//------------------------------------------------------------------------------\nfn main() -> Result<()> {\n    let mut opts = SkimOptions::from_env().unwrap_or_else(|e| {\n        e.exit();\n    });\n    color_eyre::install()?;\n    init_logger(&opts);\n\n    // Build the options after setting the log target\n    opts = opts.build();\n    trace!(\"Command line: {:?}\", std::env::args());\n\n    // Shell completion scripts\n    if let Some(shell) = opts.shell {\n        // Generate completion script directly to stdout\n        skim::shell::generate_completions(&shell);\n        if opts.shell_bindings {\n            skim::shell::generate_key_bindings(&shell);\n        }\n        return Ok(());\n    }\n    // Man page\n    if opts.man {\n        crate::manpage::generate(&mut std::io::stdout())?;\n        return Ok(());\n    }\n\n    if let Some(remote) = opts.remote {\n        let ns_name = remote\n            .to_ns_name::<interprocess::local_socket::GenericNamespaced>()\n            .unwrap();\n        let stream = interprocess::local_socket::Stream::connect(ns_name)?;\n        let mut action_chain = String::new();\n        loop {\n            action_chain.clear();\n            let len = std::io::stdin().read_line(&mut action_chain)?;\n            log::debug!(\"Got line {} from stdin\", action_chain.trim());\n            if len == 0 {\n                break;\n            }\n            let actions = parse_action_chain(action_chain.trim())?;\n            for act in actions {\n                stream\n                    .as_write()\n                    .write_all(format!(\"{}\\n\", ron::ser::to_string(&act)?).as_bytes())?;\n                log::debug!(\"Sent action {act:?} to listener\");\n            }\n        }\n        return Ok(());\n    }\n\n    match sk_main(opts) {\n        Ok(exit_code) => std::process::exit(exit_code),\n        Err(err) => match err.downcast_ref::<clap::error::Error>() {\n            Some(e) => e.exit(),\n            None => Err(eyre!(err)),\n        },\n    }\n}\n\nfn sk_main(mut opts: SkimOptions) -> Result<i32> {\n    let reader_opts = SkimItemReaderOption::from_options(&opts);\n    let cmd_collector = Rc::new(RefCell::new(SkimItemReader::new(reader_opts)));\n    opts.cmd_collector = cmd_collector.clone() as Rc<RefCell<dyn CommandCollector>>;\n\n    let cmd_history = opts.cmd_history.clone();\n    let cmd_history_size = opts.cmd_history_size;\n    let cmd_history_file = opts.cmd_history_file.clone();\n\n    let query_history = opts.query_history.clone();\n    let history_size = opts.history_size;\n    let history_file = opts.history_file.clone();\n    //------------------------------------------------------------------------------\n    let bin_options = BinOptions {\n        print_query: opts.print_query,\n        print_cmd: opts.print_cmd,\n        print_score: opts.print_score,\n        print_header: opts.print_header,\n        print_current: opts.print_current,\n        output_ending: String::from(if opts.print0 { \"\\0\" } else { \"\\n\" }),\n        strip_ansi: opts.ansi && !opts.no_strip_ansi,\n        output_format: opts.output_format.clone(),\n        delimiter: opts.delimiter.clone(),\n        replstr: opts.replstr.clone(),\n    };\n\n    //------------------------------------------------------------------------------\n    // output\n\n    let Some(result) = (if opts.tmux.is_some() && env::var(\"TMUX\").is_ok() {\n        crate::tmux::run_with(&opts)\n    } else {\n        // read from pipe or command\n        let rx_item = if io::stdin().is_terminal() || (opts.interactive && opts.cmd.is_some()) {\n            None\n        } else {\n            let rx_item = cmd_collector.borrow().of_bufread(BufReader::new(std::io::stdin()));\n            Some(rx_item)\n        };\n        Some(Skim::run_with(opts, rx_item)?)\n    }) else {\n        return Ok(135);\n    };\n    log::debug!(\"result: {result:?}\");\n\n    if result.is_abort {\n        return Ok(130);\n    }\n\n    // Output\n    if let Some(ref output_format) = bin_options.output_format {\n        print!(\n            \"{}{}\",\n            skim::printf(\n                output_format,\n                &bin_options.delimiter,\n                &bin_options.replstr,\n                &result.selected_items.iter(),\n                &result.current,\n                &result.query,\n                &result.cmd,\n                true\n            ),\n            bin_options.output_ending\n        );\n    } else {\n        if bin_options.print_query {\n            print!(\"{}{}\", result.query, bin_options.output_ending);\n        }\n\n        if bin_options.print_cmd {\n            print!(\"{}{}\", result.cmd, bin_options.output_ending);\n        }\n\n        if bin_options.print_header {\n            print!(\"{}{}\", result.header, bin_options.output_ending);\n        }\n\n        if bin_options.print_current {\n            if let Some(ref current) = result.current {\n                print!(\"{}{}\", current.output(), bin_options.output_ending);\n            } else {\n                print!(\"{}\", bin_options.output_ending);\n            }\n        }\n\n        if let Event::Action(Action::Accept(Some(accept_key))) = result.final_event {\n            print!(\"{}{}\", accept_key, bin_options.output_ending);\n        }\n\n        for item in &result.selected_items {\n            if bin_options.strip_ansi {\n                print!(\n                    \"{}{}\",\n                    skim::helper::item::strip_ansi(&item.output()).0,\n                    bin_options.output_ending\n                );\n            } else {\n                print!(\"{}{}\", item.output(), bin_options.output_ending);\n            }\n            if bin_options.print_score {\n                print!(\"{}{}\", item.rank.score, bin_options.output_ending);\n            }\n        }\n    }\n    std::io::stdout().flush()?;\n\n    //------------------------------------------------------------------------------\n    // write the history with latest item\n    if let Some(file) = history_file {\n        let limit = history_size;\n        write_history_to_file(&query_history, &result.query, limit, &file)?;\n    }\n\n    if let Some(file) = cmd_history_file {\n        let limit = cmd_history_size;\n        write_history_to_file(&cmd_history, &result.cmd, limit, &file)?;\n    }\n\n    Ok(i32::from(result.selected_items.is_empty()))\n}\n\nfn write_history_to_file(\n    orig_history: &[String],\n    latest: &str,\n    limit: usize,\n    filename: &str,\n) -> Result<(), std::io::Error> {\n    if orig_history.last().map(String::as_str) == Some(latest) {\n        // no point of having at the end of the history 5x the same command...\n        return Ok(());\n    }\n    let additional_lines = usize::from(!latest.trim().is_empty());\n    let start_index = if orig_history.len() + additional_lines > limit {\n        orig_history.len() + additional_lines - limit\n    } else {\n        0\n    };\n\n    let mut history = orig_history[start_index..].to_vec();\n    history.push(latest.to_string());\n\n    let file = File::create(filename)?;\n    let mut file = BufWriter::new(file);\n    file.write_all(history.join(\"\\n\").as_bytes())?;\n    Ok(())\n}\n\n/// Options specific to the binary/CLI mode\n#[derive(Builder)]\n#[allow(missing_docs)]\npub struct BinOptions {\n    output_ending: String,\n    print_query: bool,\n    print_cmd: bool,\n    print_score: bool,\n    print_header: bool,\n    print_current: bool,\n    strip_ansi: bool,\n    output_format: Option<String>,\n    delimiter: regex::Regex,\n    replstr: String,\n}\n"
  },
  {
    "path": "src/binds.rs",
    "content": "//! Key binding configuration and parsing.\n//!\n//! This module provides utilities for parsing and managing keyboard shortcuts\n//! and their associated actions in skim.\n\nuse std::{\n    collections::HashMap,\n    ops::{Deref, DerefMut},\n};\n\nuse color_eyre::Result;\nuse color_eyre::eyre::eyre;\nuse crossterm::event::{KeyCode, KeyEvent, KeyModifiers};\n\nuse crate::tui::event::{self, Action};\n\n/// A map of key events to their associated actions\n#[derive(Clone, Debug)]\npub struct KeyMap(pub HashMap<KeyEvent, Vec<Action>>);\n\nimpl Deref for KeyMap {\n    type Target = HashMap<KeyEvent, Vec<Action>>;\n\n    fn deref(&self) -> &Self::Target {\n        &self.0\n    }\n}\nimpl DerefMut for KeyMap {\n    fn deref_mut(&mut self) -> &mut Self::Target {\n        &mut self.0\n    }\n}\n\nimpl From<&str> for KeyMap {\n    fn from(value: &str) -> Self {\n        parse_keymaps(value.split(','))\n    }\n}\n\nimpl Default for KeyMap {\n    fn default() -> Self {\n        get_default_key_map()\n    }\n}\n\nimpl KeyMap {\n    /// Adds keymaps from the source, parsing them using `parse_keymap`\n    pub fn add_keymaps<'a, T>(&mut self, source: T)\n    where\n        T: Iterator<Item = &'a str>,\n    {\n        for map in source {\n            if let Ok((key, action_chain)) = parse_keymap(map) {\n                self.bind(key, action_chain)\n                    .unwrap_or_else(|err| debug!(\"Failed to bind key {map}: {err}\"));\n            } else {\n                debug!(\"Failed to parse key: {map}\");\n            }\n        }\n    }\n    fn bind(&mut self, key: &str, action_chain: Vec<Action>) -> Result<()> {\n        let key = parse_key(key)?;\n\n        // remove the key for existing keymap;\n        let _ = self.remove(&key);\n        self.entry(key).or_insert(action_chain);\n        Ok(())\n    }\n}\n\n/// Returns the default key bindings for skim\n#[rustfmt::skip]\n#[must_use]\npub fn get_default_key_map() -> KeyMap {\n    let mut ret = HashMap::new();\n\n    ret.insert(KeyEvent::new(KeyCode::Down, KeyModifiers::NONE), vec![Action::Down(1)]);\n    ret.insert(KeyEvent::new(KeyCode::Up, KeyModifiers::NONE), vec![Action::Up(1)]);\n    ret.insert(KeyEvent::new(KeyCode::PageUp, KeyModifiers::NONE), vec![Action::PageUp(1)]);\n    ret.insert(KeyEvent::new(KeyCode::PageDown, KeyModifiers::NONE), vec![Action::PageDown(1)]);\n    ret.insert(KeyEvent::new(KeyCode::End, KeyModifiers::NONE), vec![Action::EndOfLine]);\n    ret.insert(KeyEvent::new(KeyCode::Home, KeyModifiers::NONE), vec![Action::BeginningOfLine]);\n    ret.insert(KeyEvent::new(KeyCode::Delete, KeyModifiers::NONE), vec![Action::DeleteChar]);\n    ret.insert(KeyEvent::new(KeyCode::Tab, KeyModifiers::NONE), vec![Action::Toggle, Action::Down(1)]);\n    ret.insert(KeyEvent::new(KeyCode::BackTab, KeyModifiers::all()), vec![Action::Toggle, Action::Up(1)]);\n    ret.insert(KeyEvent::new(KeyCode::Esc, KeyModifiers::NONE), vec![Action::Abort]);\n    ret.insert(KeyEvent::new(KeyCode::Enter, KeyModifiers::NONE), vec![Action::Accept(None)]);\n    ret.insert(KeyEvent::new(KeyCode::Left, KeyModifiers::NONE), vec![Action::BackwardChar]);\n    ret.insert(KeyEvent::new(KeyCode::Right, KeyModifiers::NONE), vec![Action::ForwardChar]);\n    ret.insert(KeyEvent::new(KeyCode::Backspace, KeyModifiers::NONE), vec![Action::BackwardDeleteChar]);\n\n\n    ret.insert(KeyEvent::new(KeyCode::Left, KeyModifiers::SHIFT), vec![Action::BackwardWord]);\n    ret.insert(KeyEvent::new(KeyCode::Right, KeyModifiers::SHIFT), vec![Action::ForwardWord]);\n    ret.insert(KeyEvent::new(KeyCode::Up, KeyModifiers::SHIFT), vec![Action::PreviewUp(1)]);\n    ret.insert(KeyEvent::new(KeyCode::Down, KeyModifiers::SHIFT), vec![Action::PreviewDown(1)]);\n    ret.insert(KeyEvent::new(KeyCode::Tab, KeyModifiers::SHIFT), vec![Action::Toggle, Action::Up(1)]);\n    ret.insert(KeyEvent::new(KeyCode::BackTab, KeyModifiers::SHIFT), vec![Action::Toggle, Action::Up(1)]);\n    ret.insert(KeyEvent::new(KeyCode::Home, KeyModifiers::SHIFT), vec![Action::BeginningOfLine]);\n\n\n    ret.insert(KeyEvent::new(KeyCode::Left, KeyModifiers::CONTROL), vec![Action::BackwardWord]);\n    ret.insert(KeyEvent::new(KeyCode::Right, KeyModifiers::CONTROL), vec![Action::ForwardWord]);\n\n    ret.insert(KeyEvent::new(KeyCode::Char('a'), KeyModifiers::CONTROL), vec![Action::BeginningOfLine]);\n    ret.insert(KeyEvent::new(KeyCode::Char('b'), KeyModifiers::CONTROL), vec![Action::BackwardChar]);\n    ret.insert(KeyEvent::new(KeyCode::Char('c'), KeyModifiers::CONTROL), vec![Action::Abort]);\n    ret.insert(KeyEvent::new(KeyCode::Char('d'), KeyModifiers::CONTROL), vec![Action::Abort]);\n    ret.insert(KeyEvent::new(KeyCode::Char('e'), KeyModifiers::CONTROL), vec![Action::EndOfLine]);\n    ret.insert(KeyEvent::new(KeyCode::Char('f'), KeyModifiers::CONTROL), vec![Action::ForwardChar]);\n    ret.insert(KeyEvent::new(KeyCode::Char('g'), KeyModifiers::CONTROL), vec![Action::Abort]);\n    ret.insert(KeyEvent::new(KeyCode::Char('h'), KeyModifiers::CONTROL), vec![Action::BackwardDeleteChar]);\n    ret.insert(KeyEvent::new(KeyCode::Char('j'), KeyModifiers::CONTROL), vec![Action::Down(1)]);\n    ret.insert(KeyEvent::new(KeyCode::Char('k'), KeyModifiers::CONTROL), vec![Action::Up(1)]);\n    ret.insert(KeyEvent::new(KeyCode::Char('l'), KeyModifiers::CONTROL), vec![Action::ClearScreen]);\n    ret.insert(KeyEvent::new(KeyCode::Char('n'), KeyModifiers::CONTROL), vec![Action::Down(1)]);\n    ret.insert(KeyEvent::new(KeyCode::Char('p'), KeyModifiers::CONTROL), vec![Action::Up(1)]);\n    ret.insert(KeyEvent::new(KeyCode::Char('q'), KeyModifiers::CONTROL), vec![Action::ToggleInteractive]);\n    ret.insert(KeyEvent::new(KeyCode::Char('r'), KeyModifiers::CONTROL), vec![Action::RotateMode]);\n    ret.insert(KeyEvent::new(KeyCode::Char('u'), KeyModifiers::CONTROL), vec![Action::UnixLineDiscard]);\n    ret.insert(KeyEvent::new(KeyCode::Char('w'), KeyModifiers::CONTROL), vec![Action::UnixWordRubout]);\n    ret.insert(KeyEvent::new(KeyCode::Char('y'), KeyModifiers::CONTROL), vec![Action::Yank]);\n\n\n    ret.insert(KeyEvent::new(KeyCode::Backspace, KeyModifiers::ALT), vec![Action::BackwardKillWord]);\n\n    ret.insert(KeyEvent::new(KeyCode::Char('b'), KeyModifiers::ALT), vec![Action::BackwardWord]);\n    ret.insert(KeyEvent::new(KeyCode::Char('d'), KeyModifiers::ALT), vec![Action::KillWord]);\n    ret.insert(KeyEvent::new(KeyCode::Char('f'), KeyModifiers::ALT), vec![Action::ForwardWord]);\n    ret.insert(KeyEvent::new(KeyCode::Char('h'), KeyModifiers::ALT), vec![Action::ScrollLeft(1)]);\n    ret.insert(KeyEvent::new(KeyCode::Char('l'), KeyModifiers::ALT), vec![Action::ScrollRight(1)]);\n\n    KeyMap(ret)\n}\n\n/// Parses a key str into a crossterm `KeyEvent`\n///\n/// # Errors\n/// Returns an error if the key string is empty, contains an unknown modifier,\n/// or does not correspond to a recognised key name.\npub fn parse_key(key: &str) -> Result<KeyEvent> {\n    if key.is_empty() {\n        return Err(eyre!(\"Cannot parse empty key\"));\n    }\n    let parts = key.split('-').collect::<Vec<&str>>();\n    let mut mods = KeyModifiers::NONE;\n\n    if parts.len() > 1 {\n        let mod_strs = &parts[..parts.len() - 1];\n        for mod_str in mod_strs {\n            mods |= match *mod_str {\n                \"ctrl\" => KeyModifiers::CONTROL,\n                \"alt\" => KeyModifiers::ALT,\n                \"shift\" => KeyModifiers::SHIFT,\n                s => return Err(eyre!(\"Failed to parse {} as key modifier\", s)),\n            }\n        }\n    }\n    let key = parts.last().unwrap_or(&\"\").to_string();\n\n    let keycode: KeyCode;\n    if key.len() == 1 {\n        let char = key.chars().next().unwrap_or_default();\n        if char.is_uppercase() {\n            mods |= KeyModifiers::SHIFT;\n            keycode = KeyCode::Char(char.to_ascii_lowercase());\n        } else {\n            keycode = KeyCode::Char(char);\n        }\n    } else if let Some(f) = key.strip_prefix('f') {\n        let f_index = f.parse::<u8>()?;\n        keycode = KeyCode::F(f_index);\n    } else {\n        keycode = match key.as_str() {\n            \"space\" => KeyCode::Char(' '),\n            \"enter\" => KeyCode::Enter,\n            \"bspace\" | \"bs\" => KeyCode::Backspace,\n            \"up\" => KeyCode::Up,\n            \"down\" => KeyCode::Down,\n            \"left\" => KeyCode::Left,\n            \"right\" => KeyCode::Right,\n            \"tab\" => KeyCode::Tab,\n            \"btab\" => KeyCode::BackTab,\n            \"esc\" => KeyCode::Esc,\n            \"home\" => KeyCode::Home,\n            \"end\" => KeyCode::End,\n            \"pgup\" => KeyCode::PageUp,\n            \"pgdown\" => KeyCode::PageDown,\n            \"change\" => KeyCode::F(255),\n            s => return Err(eyre!(\"Unknown key {}\", s)),\n        }\n    }\n\n    debug!(\"parsed key {keycode:?} and mods {mods:?}\");\n\n    Ok(KeyEvent::new(keycode, mods))\n}\n\n/// Parse an iterator of keymaps into a `KeyMap`\npub fn parse_keymaps<'a, T>(maps: T) -> KeyMap\nwhere\n    T: Iterator<Item = &'a str>,\n{\n    let mut res = KeyMap::default();\n    res.add_keymaps(maps);\n    res\n}\n\n/// Parses an action chain, separated by '+'s into the corresponding actions\n///\n/// # Errors\n/// Returns an error if the action chain is empty or contains only unknown actions.\npub fn parse_action_chain(action_chain: &str) -> Result<Vec<Action>> {\n    let mut actions: Vec<Action> = vec![];\n    let mut split = action_chain.split('+');\n\n    while let Some(mut s) = split.next().map(String::from) {\n        if (s.starts_with(\"if-\") || s.ends_with('{'))\n            && let Some(otherwise) = split.next()\n        {\n            s += &(String::from(\"+\") + otherwise);\n        }\n        if let Some(act) = event::parse_action(&s) {\n            actions.push(act);\n        }\n    }\n    if actions.is_empty() {\n        Err(eyre!(\"Empty action chain or unknown action `{}`\", action_chain))\n    } else {\n        Ok(actions)\n    }\n}\n\n/// Parse a single keymap and return the key and action(s)\n///\n/// # Errors\n/// Returns an error if the string is empty, missing a colon separator, or the\n/// action chain cannot be parsed.\npub fn parse_keymap(key_action: &str) -> Result<(&str, Vec<Action>)> {\n    if key_action.is_empty() {\n        return Err(eyre!(\"Got an empty keybind, skipping\"));\n    }\n    debug!(\"got key_action: {key_action:?}\");\n    let (key, action_chain) = key_action\n        .split_once(':')\n        .ok_or(eyre!(\"Failed to parse {} as key and action\", key_action))?;\n    debug!(\"parsed key_action: {key:?}: {action_chain:?}\");\n    Ok((key, parse_action_chain(action_chain)?))\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use event::Action::*;\n    #[test]\n    fn test_parse_action_chain() {\n        let parsed = parse_action_chain(\n            \"execute-silent:1 {}+execute-silent:2 {+}+execute-silent:3 {+n}+reload+if-query-empty:reload+up\",\n        );\n        assert!(parsed.is_ok());\n        let res = parsed.unwrap();\n        assert_eq!(\n            res,\n            vec![\n                ExecuteSilent(\"1 {}\".into()),\n                ExecuteSilent(\"2 {+}\".into()),\n                ExecuteSilent(\"3 {+n}\".into()),\n                Reload(None),\n                IfQueryEmpty(\"reload\".into(), Some(\"up\".into())),\n            ]\n        );\n    }\n    #[test]\n    fn test_parse_key() {\n        assert_eq!(\n            parse_key(\"a\").unwrap(),\n            KeyEvent::new(KeyCode::Char('a'), KeyModifiers::empty())\n        );\n\n        assert_eq!(\n            parse_key(\"A\").unwrap(),\n            KeyEvent::new(KeyCode::Char('a'), KeyModifiers::SHIFT)\n        );\n\n        assert_eq!(\n            parse_key(\"alt-a\").unwrap(),\n            KeyEvent::new(KeyCode::Char('a'), KeyModifiers::ALT)\n        );\n\n        assert_eq!(\n            parse_key(\"alt-A\").unwrap(),\n            KeyEvent::new(KeyCode::Char('a'), KeyModifiers::ALT | KeyModifiers::SHIFT)\n        );\n        assert_eq!(\n            parse_key(\"alt-shift-a\").unwrap(),\n            KeyEvent::new(KeyCode::Char('a'), KeyModifiers::ALT | KeyModifiers::SHIFT)\n        );\n\n        assert_eq!(\n            parse_key(\"ctrl-a\").unwrap(),\n            KeyEvent::new(KeyCode::Char('a'), KeyModifiers::CONTROL)\n        );\n\n        assert_eq!(\n            parse_key(\"ctrl-A\").unwrap(),\n            KeyEvent::new(KeyCode::Char('a'), KeyModifiers::CONTROL | KeyModifiers::SHIFT)\n        );\n        assert_eq!(\n            parse_key(\"ctrl-shift-a\").unwrap(),\n            KeyEvent::new(KeyCode::Char('a'), KeyModifiers::CONTROL | KeyModifiers::SHIFT)\n        );\n\n        assert_eq!(\n            parse_key(\"f10\").unwrap(),\n            KeyEvent::new(KeyCode::F(10), KeyModifiers::empty())\n        );\n\n        assert_eq!(\n            parse_key(\"space\").unwrap(),\n            KeyEvent::new(KeyCode::Char(' '), KeyModifiers::empty())\n        );\n        assert_eq!(\n            parse_key(\"enter\").unwrap(),\n            KeyEvent::new(KeyCode::Enter, KeyModifiers::empty())\n        );\n        assert_eq!(\n            parse_key(\"bspace\").unwrap(),\n            KeyEvent::new(KeyCode::Backspace, KeyModifiers::empty())\n        );\n        assert_eq!(\n            parse_key(\"bs\").unwrap(),\n            KeyEvent::new(KeyCode::Backspace, KeyModifiers::empty())\n        );\n        assert_eq!(\n            parse_key(\"up\").unwrap(),\n            KeyEvent::new(KeyCode::Up, KeyModifiers::empty())\n        );\n        assert_eq!(\n            parse_key(\"down\").unwrap(),\n            KeyEvent::new(KeyCode::Down, KeyModifiers::empty())\n        );\n        assert_eq!(\n            parse_key(\"left\").unwrap(),\n            KeyEvent::new(KeyCode::Left, KeyModifiers::empty())\n        );\n        assert_eq!(\n            parse_key(\"right\").unwrap(),\n            KeyEvent::new(KeyCode::Right, KeyModifiers::empty())\n        );\n        assert_eq!(\n            parse_key(\"tab\").unwrap(),\n            KeyEvent::new(KeyCode::Tab, KeyModifiers::empty())\n        );\n        assert_eq!(\n            parse_key(\"btab\").unwrap(),\n            KeyEvent::new(KeyCode::BackTab, KeyModifiers::empty())\n        );\n        assert_eq!(\n            parse_key(\"esc\").unwrap(),\n            KeyEvent::new(KeyCode::Esc, KeyModifiers::empty())\n        );\n        assert_eq!(\n            parse_key(\"home\").unwrap(),\n            KeyEvent::new(KeyCode::Home, KeyModifiers::empty())\n        );\n        assert_eq!(\n            parse_key(\"end\").unwrap(),\n            KeyEvent::new(KeyCode::End, KeyModifiers::empty())\n        );\n        assert_eq!(\n            parse_key(\"pgup\").unwrap(),\n            KeyEvent::new(KeyCode::PageUp, KeyModifiers::empty())\n        );\n        assert_eq!(\n            parse_key(\"pgdown\").unwrap(),\n            KeyEvent::new(KeyCode::PageDown, KeyModifiers::empty())\n        );\n        assert_eq!(\n            parse_key(\"change\").unwrap(),\n            KeyEvent::new(KeyCode::F(255), KeyModifiers::empty())\n        );\n    }\n}\n"
  },
  {
    "path": "src/engine/all.rs",
    "content": "use std::fmt::{Display, Error, Formatter};\nuse std::sync::Arc;\n\nuse crate::item::RankBuilder;\nuse crate::{MatchEngine, MatchRange, MatchResult, SkimItem};\n\n//------------------------------------------------------------------------------\n#[derive(Debug)]\npub struct MatchAllEngine {\n    rank_builder: Arc<RankBuilder>,\n}\n\nimpl MatchAllEngine {\n    pub fn builder() -> Self {\n        Self {\n            rank_builder: Default::default(),\n        }\n    }\n\n    pub fn rank_builder(mut self, rank_builder: Arc<RankBuilder>) -> Self {\n        self.rank_builder = rank_builder;\n        self\n    }\n\n    pub fn build(self) -> Self {\n        self\n    }\n}\n\nimpl MatchEngine for MatchAllEngine {\n    fn match_item(&self, item: &dyn SkimItem) -> Option<MatchResult> {\n        let item_text = item.text();\n        Some(MatchResult {\n            rank: self.rank_builder.build_rank(0, 0, 0, &item_text),\n            matched_range: MatchRange::ByteRange(0, 0),\n        })\n    }\n}\n\nimpl Display for MatchAllEngine {\n    fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {\n        write!(f, \"Noop\")\n    }\n}\n"
  },
  {
    "path": "src/engine/andor.rs",
    "content": "use std::fmt::{Display, Error, Formatter};\n\nuse crate::fuzzy_matcher::MatchIndices;\nuse crate::{MatchEngine, MatchRange, MatchResult, SkimItem};\n\n//------------------------------------------------------------------------------\n// OrEngine, a combinator\npub struct OrEngine {\n    engines: Vec<Box<dyn MatchEngine>>,\n}\n\nimpl OrEngine {\n    pub fn builder() -> Self {\n        Self { engines: vec![] }\n    }\n\n    pub fn engines(mut self, mut engines: Vec<Box<dyn MatchEngine>>) -> Self {\n        self.engines.append(&mut engines);\n        self\n    }\n\n    pub fn build(self) -> Self {\n        self\n    }\n}\n\nimpl MatchEngine for OrEngine {\n    fn match_item(&self, item: &dyn SkimItem) -> Option<MatchResult> {\n        let result = self\n            .engines\n            .iter()\n            .map(|e| e.match_item(item))\n            .max_by_key(|res| res.as_ref().map(|matched| matched.rank.score));\n\n        result?\n    }\n}\n\nimpl Display for OrEngine {\n    fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {\n        write!(\n            f,\n            \"(Or: {})\",\n            self.engines\n                .iter()\n                .map(|e| format!(\"{e}\"))\n                .collect::<Vec<_>>()\n                .join(\", \")\n        )\n    }\n}\n\n//------------------------------------------------------------------------------\n// AndEngine, a combinator\npub struct AndEngine {\n    engines: Vec<Box<dyn MatchEngine>>,\n}\n\nimpl AndEngine {\n    pub fn builder() -> Self {\n        Self { engines: vec![] }\n    }\n\n    pub fn engines(mut self, mut engines: Vec<Box<dyn MatchEngine>>) -> Self {\n        self.engines.append(&mut engines);\n        self\n    }\n\n    pub fn build(self) -> Self {\n        self\n    }\n\n    fn merge_matched_items(items: Vec<MatchResult>, text: &str) -> MatchResult {\n        let mut ranges = MatchIndices::new();\n        let mut rank = crate::Rank {\n            score: 0,\n            begin: i32::MAX,\n            end: i32::MIN,\n            ..items[0].rank\n        };\n        for item in items {\n            match item.matched_range {\n                MatchRange::ByteRange(..) => {\n                    ranges.extend(item.range_char_indices(text));\n                }\n                MatchRange::Chars(vec) => {\n                    ranges.extend(vec.iter().copied());\n                }\n            }\n            rank.score = rank.score.saturating_add(item.rank.score);\n            rank.begin = rank.begin.min(item.rank.begin);\n            rank.end = rank.end.max(item.rank.end);\n        }\n\n        ranges.sort_unstable();\n        ranges.dedup();\n        MatchResult {\n            rank,\n            matched_range: MatchRange::Chars(ranges),\n        }\n    }\n}\n\nimpl MatchEngine for AndEngine {\n    fn match_item(&self, item: &dyn SkimItem) -> Option<MatchResult> {\n        // mock\n        let mut results = vec![];\n        for engine in &self.engines {\n            let result = engine.match_item(item)?;\n            results.push(result);\n        }\n\n        if results.is_empty() {\n            None\n        } else {\n            Some(Self::merge_matched_items(results, &item.text()))\n        }\n    }\n}\n\nimpl Display for AndEngine {\n    fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {\n        write!(\n            f,\n            \"(And: {})\",\n            self.engines\n                .iter()\n                .map(|e| format!(\"{e}\"))\n                .collect::<Vec<_>>()\n                .join(\", \")\n        )\n    }\n}\n"
  },
  {
    "path": "src/engine/exact.rs",
    "content": "use crate::engine::util::{contains_upper, regex_match};\nuse crate::item::RankBuilder;\nuse crate::{CaseMatching, MatchEngine, MatchRange, MatchResult, SkimItem};\nuse regex::{Regex, escape};\nuse std::cmp::min;\nuse std::fmt::{Display, Error, Formatter};\nuse std::sync::Arc;\n\n//------------------------------------------------------------------------------\n// Exact engine\n#[derive(Debug, Copy, Clone, Default)]\npub struct ExactMatchingParam {\n    pub prefix: bool,\n    pub postfix: bool,\n    pub inverse: bool,\n    pub case: CaseMatching,\n    __non_exhaustive: bool,\n}\n\n#[derive(Debug)]\npub struct ExactEngine {\n    #[allow(dead_code)]\n    query: String,\n    query_regex: Option<Regex>,\n    rank_builder: Arc<RankBuilder>,\n    inverse: bool,\n}\n\nimpl ExactEngine {\n    pub fn builder(query: &str, param: ExactMatchingParam) -> Self {\n        let case_sensitive = match param.case {\n            CaseMatching::Respect => true,\n            CaseMatching::Ignore => false,\n            CaseMatching::Smart => contains_upper(query),\n        };\n\n        let mut query_builder = String::new();\n        if !case_sensitive {\n            query_builder.push_str(\"(?i)\");\n        }\n\n        if param.prefix {\n            query_builder.push('^');\n        }\n\n        query_builder.push_str(&escape(query));\n\n        if param.postfix {\n            query_builder.push('$');\n        }\n\n        let query_regex = if query.is_empty() {\n            None\n        } else {\n            Regex::new(&query_builder).ok()\n        };\n\n        ExactEngine {\n            query: query.to_string(),\n            query_regex,\n            rank_builder: Default::default(),\n            inverse: param.inverse,\n        }\n    }\n\n    pub fn rank_builder(mut self, rank_builder: Arc<RankBuilder>) -> Self {\n        self.rank_builder = rank_builder;\n        self\n    }\n\n    pub fn build(self) -> Self {\n        self\n    }\n}\n\nimpl MatchEngine for ExactEngine {\n    fn match_item(&self, item: &dyn SkimItem) -> Option<MatchResult> {\n        let mut matched_result = None;\n        let item_text = item.text();\n        let default_range = [(0, item_text.len())];\n        for &(start, end) in item.get_matching_ranges().unwrap_or(&default_range) {\n            let start = min(start, item_text.len());\n            let end = min(end, item_text.len());\n            if self.query_regex.is_none() {\n                matched_result = Some((0, 0));\n                break;\n            }\n\n            matched_result =\n                regex_match(&item_text[start..end], self.query_regex.as_ref()).map(|(s, e)| (s + start, e + start));\n\n            if self.inverse {\n                matched_result = matched_result.xor(Some((0, 0)));\n            }\n\n            if matched_result.is_some() {\n                break;\n            }\n        }\n\n        let (begin, end) = matched_result?;\n        let score = i32::try_from(end - begin).unwrap_or(i32::MAX);\n        Some(MatchResult {\n            rank: self.rank_builder.build_rank(score, begin, end, &item_text),\n            matched_range: MatchRange::ByteRange(begin, end),\n        })\n    }\n}\n\nimpl Display for ExactEngine {\n    fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {\n        write!(\n            f,\n            \"(Exact|{}{})\",\n            if self.inverse { \"!\" } else { \"\" },\n            self.query_regex.as_ref().map_or(\"\", regex::Regex::as_str)\n        )\n    }\n}\n"
  },
  {
    "path": "src/engine/factory.rs",
    "content": "use regex::Regex;\n\nuse crate::engine::all::MatchAllEngine;\nuse crate::engine::andor::{AndEngine, OrEngine};\nuse crate::engine::exact::{ExactEngine, ExactMatchingParam};\nuse crate::engine::fuzzy::{FuzzyAlgorithm, FuzzyEngine};\nuse crate::engine::regexp::RegexEngine;\nuse crate::item::RankBuilder;\nuse crate::{CaseMatching, MatchEngine, MatchEngineFactory, Typos};\nuse std::sync::{Arc, LazyLock};\n\nstatic RE_OR_WITH_SPACES: LazyLock<Regex> = LazyLock::new(|| Regex::new(r\" *\\|+ *\").unwrap());\n\n//------------------------------------------------------------------------------\n// Exact engine factory\n/// Factory for creating exact or fuzzy match engines based on configuration\npub struct ExactOrFuzzyEngineFactory {\n    exact_mode: bool,\n    fuzzy_algorithm: FuzzyAlgorithm,\n    rank_builder: Arc<RankBuilder>,\n    typos: Typos,\n    filter_mode: bool,\n    last_match: bool,\n}\n\nimpl ExactOrFuzzyEngineFactory {\n    /// Creates a new builder with default settings\n    #[must_use]\n    pub fn builder() -> Self {\n        Self {\n            exact_mode: false,\n            fuzzy_algorithm: FuzzyAlgorithm::SkimV2,\n            rank_builder: Default::default(),\n            typos: Typos::Disabled,\n            filter_mode: false,\n            last_match: false,\n        }\n    }\n\n    /// Sets whether to use exact matching mode\n    #[must_use]\n    pub fn exact_mode(mut self, exact_mode: bool) -> Self {\n        self.exact_mode = exact_mode;\n        self\n    }\n\n    /// Sets the fuzzy matching algorithm to use\n    #[must_use]\n    pub fn fuzzy_algorithm(mut self, fuzzy_algorithm: FuzzyAlgorithm) -> Self {\n        self.fuzzy_algorithm = fuzzy_algorithm;\n        self\n    }\n\n    /// Sets the rank builder for scoring matches\n    #[must_use]\n    pub fn rank_builder(mut self, rank_builder: Arc<RankBuilder>) -> Self {\n        self.rank_builder = rank_builder;\n        self\n    }\n\n    /// Sets the typo tolerance configuration\n    ///\n    /// - `Typos::Disabled`: no typo tolerance\n    /// - `Typos::Smart`: adaptive typo tolerance (`pattern_length` / 4)\n    /// - `Typos::Fixed(n)`: exactly n typos allowed\n    #[must_use]\n    pub fn typos(mut self, typos: Typos) -> Self {\n        self.typos = typos;\n        self\n    }\n\n    /// Sets filter mode (skips per-character match indices for faster matching)\n    #[must_use]\n    pub fn filter_mode(mut self, filter_mode: bool) -> Self {\n        self.filter_mode = filter_mode;\n        self\n    }\n\n    /// When true, prefer the last (rightmost) occurrence on tied scores\n    #[must_use]\n    pub fn last_match(mut self, last_match: bool) -> Self {\n        self.last_match = last_match;\n        self\n    }\n\n    /// Builds the factory (currently a no-op, returns self)\n    #[must_use]\n    pub fn build(self) -> Self {\n        self\n    }\n}\n\nimpl MatchEngineFactory for ExactOrFuzzyEngineFactory {\n    fn create_engine_with_case(&self, query: &str, case: CaseMatching) -> Box<dyn MatchEngine> {\n        // 'abc => match exact \"abc\"\n        // ^abc => starts with \"abc\"\n        // abc$ => ends with \"abc\"\n        // ^abc$ => match exact \"abc\"\n        // !^abc => items not starting with \"abc\"\n        // !abc$ => items not ending with \"abc\"\n        // !^abc$ => not \"abc\"\n\n        let mut query = query;\n        let mut exact = self.exact_mode;\n        let mut param = ExactMatchingParam::default();\n        param.case = case;\n\n        if query.starts_with('\\'') {\n            exact = !exact;\n            query = &query[1..];\n        }\n\n        if query.starts_with('!') {\n            query = &query[1..];\n            exact = true;\n            param.inverse = true;\n        }\n\n        if query.is_empty() {\n            // if only \"!\" was provided, will still show all items\n            return Box::new(\n                MatchAllEngine::builder()\n                    .rank_builder(self.rank_builder.clone())\n                    .build(),\n            );\n        }\n\n        if query.starts_with('^') {\n            query = &query[1..];\n            exact = true;\n            param.prefix = true;\n        }\n\n        if query.ends_with('$') {\n            query = &query[..(query.len() - 1)];\n            exact = true;\n            param.postfix = true;\n        }\n\n        if exact {\n            Box::new(\n                ExactEngine::builder(query, param)\n                    .rank_builder(self.rank_builder.clone())\n                    .build(),\n            )\n        } else {\n            Box::new(\n                FuzzyEngine::builder()\n                    .query(query)\n                    .algorithm(self.fuzzy_algorithm)\n                    .case(case)\n                    .typos(self.typos)\n                    .filter_mode(self.filter_mode)\n                    .last_match(self.last_match)\n                    .rank_builder(self.rank_builder.clone())\n                    .build(),\n            )\n        }\n    }\n}\n\n//------------------------------------------------------------------------------\n/// Factory for creating AND/OR composite match engines\npub struct AndOrEngineFactory {\n    inner: Box<dyn MatchEngineFactory>,\n}\n\nimpl AndOrEngineFactory {\n    /// Creates a new AND/OR engine factory wrapping another factory\n    pub fn new(factory: impl MatchEngineFactory + 'static) -> Self {\n        Self {\n            inner: Box::new(factory),\n        }\n    }\n\n    fn parse_andor(&self, query: &str, case: CaseMatching) -> Box<dyn MatchEngine> {\n        if query.trim().is_empty() {\n            return self.inner.create_engine_with_case(query, case);\n        }\n        let and_engines = RE_OR_WITH_SPACES\n            .replace_all(&Self::mask_escape_space(query), \"|\")\n            .split(' ')\n            .filter_map(|and_term| {\n                if and_term.is_empty() {\n                    return None;\n                }\n                let or_engines = and_term\n                    .split('|')\n                    .filter_map(|term| {\n                        if term.is_empty() {\n                            return None;\n                        }\n                        debug!(\"Creating Or engine for {term}\");\n                        Some(\n                            self.inner\n                                .create_engine_with_case(&Self::unmask_escape_space(term), case),\n                        )\n                    })\n                    .collect::<Vec<_>>();\n                debug!(\"Building or matcher engine from Ors\");\n                if or_engines.len() == 1 {\n                    return Some(or_engines.into_iter().next().unwrap());\n                }\n                Some(Box::new(OrEngine::builder().engines(or_engines).build()) as Box<dyn MatchEngine>)\n            })\n            .collect();\n        debug!(\"Creating and matcher engine from Ors\");\n        Box::new(AndEngine::builder().engines(and_engines).build())\n    }\n\n    fn mask_escape_space(string: &str) -> String {\n        string.replace(\"\\\\ \", \"\\0\")\n    }\n\n    fn unmask_escape_space(string: &str) -> String {\n        string.replace('\\0', \" \")\n    }\n}\n\nimpl MatchEngineFactory for AndOrEngineFactory {\n    fn create_engine_with_case(&self, query: &str, case: CaseMatching) -> Box<dyn MatchEngine> {\n        self.parse_andor(query, case)\n    }\n}\n\n//------------------------------------------------------------------------------\n/// Factory for creating regex-based match engines\npub struct RegexEngineFactory {\n    rank_builder: Arc<RankBuilder>,\n}\n\nimpl RegexEngineFactory {\n    /// Creates a new builder with default settings\n    #[must_use]\n    pub fn builder() -> Self {\n        Self {\n            rank_builder: Default::default(),\n        }\n    }\n\n    /// Sets the rank builder for scoring matches\n    #[must_use]\n    pub fn rank_builder(mut self, rank_builder: Arc<RankBuilder>) -> Self {\n        self.rank_builder = rank_builder;\n        self\n    }\n\n    /// Builds the factory (currently a no-op, returns self)\n    #[must_use]\n    pub fn build(self) -> Self {\n        self\n    }\n}\n\nimpl MatchEngineFactory for RegexEngineFactory {\n    fn create_engine_with_case(&self, query: &str, case: CaseMatching) -> Box<dyn MatchEngine> {\n        Box::new(\n            RegexEngine::builder(query, case)\n                .rank_builder(self.rank_builder.clone())\n                .build(),\n        )\n    }\n}\n\n#[cfg(test)]\nmod test {\n    #[test]\n    fn test_engine_factory() {\n        use super::*;\n        let exact_or_fuzzy = ExactOrFuzzyEngineFactory::builder().build();\n        let x = exact_or_fuzzy.create_engine(\"'abc\");\n        assert_eq!(format!(\"{x}\"), \"(Exact|(?i)abc)\");\n\n        let x = exact_or_fuzzy.create_engine(\"^abc\");\n        assert_eq!(format!(\"{x}\"), \"(Exact|(?i)^abc)\");\n\n        let x = exact_or_fuzzy.create_engine(\"abc$\");\n        assert_eq!(format!(\"{x}\"), \"(Exact|(?i)abc$)\");\n\n        let x = exact_or_fuzzy.create_engine(\"^abc$\");\n        assert_eq!(format!(\"{x}\"), \"(Exact|(?i)^abc$)\");\n\n        let x = exact_or_fuzzy.create_engine(\"!abc\");\n        assert_eq!(format!(\"{x}\"), \"(Exact|!(?i)abc)\");\n\n        let x = exact_or_fuzzy.create_engine(\"!^abc\");\n        assert_eq!(format!(\"{x}\"), \"(Exact|!(?i)^abc)\");\n\n        let x = exact_or_fuzzy.create_engine(\"!abc$\");\n        assert_eq!(format!(\"{x}\"), \"(Exact|!(?i)abc$)\");\n\n        let x = exact_or_fuzzy.create_engine(\"!^abc$\");\n        assert_eq!(format!(\"{x}\"), \"(Exact|!(?i)^abc$)\");\n\n        let regex_factory = RegexEngineFactory::builder();\n        let and_or_factory = AndOrEngineFactory::new(exact_or_fuzzy);\n\n        let x = and_or_factory.create_engine(\"'abc | def ^gh ij | kl mn\");\n        assert_eq!(\n            format!(\"{x}\"),\n            \"(And: (Or: (Exact|(?i)abc), (Fuzzy: def)), (Exact|(?i)^gh), (Or: (Fuzzy: ij), (Fuzzy: kl)), (Fuzzy: mn))\"\n        );\n\n        let x = regex_factory.create_engine(\"'abc | def ^gh ij | kl mn\");\n        assert_eq!(format!(\"{x}\"), \"(Regex: 'abc | def ^gh ij | kl mn)\");\n\n        let x = and_or_factory.create_engine(\"readme .md$ | .markdown$\");\n        assert_eq!(\n            format!(\"{x}\"),\n            \"(And: (Fuzzy: readme), (Or: (Exact|(?i)\\\\.md$), (Exact|(?i)\\\\.markdown$)))\"\n        );\n    }\n}\n"
  },
  {
    "path": "src/engine/fuzzy.rs",
    "content": "use std::cmp::min;\nuse std::fmt::{Display, Error, Formatter};\nuse std::sync::Arc;\n\nuse crate::fuzzy_matcher::MatchIndices;\nuse crate::fuzzy_matcher::arinae::ArinaeMatcher;\nuse crate::fuzzy_matcher::frizbee::FrizbeeMatcher;\nuse crate::fuzzy_matcher::{FuzzyMatcher, clangd::ClangdMatcher, fzy::FzyMatcher, skim::SkimMatcherV2};\n\nuse crate::item::RankBuilder;\nuse crate::{CaseMatching, MatchEngine, Typos};\nuse crate::{MatchRange, MatchResult, SkimItem};\n\n//------------------------------------------------------------------------------\n/// Fuzzy matching algorithm to use\n#[derive(Debug, Copy, Clone, Default, PartialEq)]\n#[cfg_attr(feature = \"cli\", derive(clap::ValueEnum))]\n#[cfg_attr(feature = \"cli\", clap(rename_all = \"snake_case\"))]\npub enum FuzzyAlgorithm {\n    /// Improved skim fuzzy matching algorithm (v2)\n    SkimV2,\n    /// Clangd fuzzy matching algorithm\n    Clangd,\n    /// Fzy matching algorithm (<https://github.com/jhawthorn/fzy>)\n    Fzy,\n    /// Frizbee matching algorithm, typo resistant\n    Frizbee,\n    /// Arinae: typo-resistant & natural algorithm, default\n    #[cfg_attr(feature = \"cli\", clap(alias = \"ari\"))]\n    #[default]\n    Arinae,\n}\n\nconst BYTES_1M: usize = 1024 * 1024 * 1024;\n\n//------------------------------------------------------------------------------\n// Fuzzy engine\n#[derive(Default)]\npub struct FuzzyEngineBuilder {\n    query: String,\n    case: CaseMatching,\n    algorithm: FuzzyAlgorithm,\n    rank_builder: Arc<RankBuilder>,\n    /// Typo tolerance configuration:\n    /// - `Typos::Disabled`: no typo tolerance\n    /// - `Typos::Smart`: adaptive (`pattern_length` / 4)\n    /// - `Typos::Fixed(n)`: exactly n typos allowed\n    typos: Typos,\n    /// When true, use `fuzzy_match_range` instead of `fuzzy_indices` to avoid\n    /// per-character index computation (useful in filter mode where highlighting\n    /// is not needed).\n    filter_mode: bool,\n    /// When true, prefer the last (rightmost) occurrence on tied scores.\n    last_match: bool,\n}\n\nimpl FuzzyEngineBuilder {\n    pub fn query(mut self, query: &str) -> Self {\n        self.query = query.to_string();\n        self\n    }\n\n    pub fn case(mut self, case: CaseMatching) -> Self {\n        self.case = case;\n        self\n    }\n\n    pub fn algorithm(mut self, algorithm: FuzzyAlgorithm) -> Self {\n        self.algorithm = algorithm;\n        self\n    }\n\n    pub fn rank_builder(mut self, rank_builder: Arc<RankBuilder>) -> Self {\n        self.rank_builder = rank_builder;\n        self\n    }\n\n    pub fn typos(mut self, typos: Typos) -> Self {\n        self.typos = typos;\n        self\n    }\n\n    pub fn filter_mode(mut self, filter_mode: bool) -> Self {\n        self.filter_mode = filter_mode;\n        self\n    }\n\n    pub fn last_match(mut self, last_match: bool) -> Self {\n        self.last_match = last_match;\n        self\n    }\n\n    /// Compute the effective `max_typos` for the given query.\n    ///\n    /// - `Typos::Disabled` → `None` (no typo tolerance)\n    /// - `Typos::Smart` → adaptive: `Some(query.chars().count() / 4)`\n    /// - `Typos::Fixed(n)` → `Some(n)`\n    fn effective_max_typos(&self) -> Option<usize> {\n        match self.typos {\n            Typos::Disabled => None,\n            Typos::Smart => Some(self.query.chars().count().saturating_div(4)),\n            Typos::Fixed(n) => Some(n),\n        }\n    }\n\n    #[allow(deprecated)]\n    pub fn build(self) -> FuzzyEngine {\n        #[allow(unused_mut)]\n        let mut algorithm = self.algorithm;\n        let max_typos = self.effective_max_typos();\n        let matcher: Box<dyn FuzzyMatcher> = match algorithm {\n            FuzzyAlgorithm::SkimV2 => {\n                let matcher = SkimMatcherV2::default().element_limit(BYTES_1M);\n                let matcher = match self.case {\n                    CaseMatching::Respect => matcher.respect_case(),\n                    CaseMatching::Ignore => matcher.ignore_case(),\n                    CaseMatching::Smart => matcher.smart_case(),\n                };\n                debug!(\"Initialized SkimV2 algorithm\");\n                Box::new(matcher)\n            }\n            FuzzyAlgorithm::Clangd => {\n                let matcher = ClangdMatcher::default();\n                let matcher = match self.case {\n                    CaseMatching::Respect => matcher.respect_case(),\n                    CaseMatching::Ignore => matcher.ignore_case(),\n                    CaseMatching::Smart => matcher.smart_case(),\n                };\n                debug!(\"Initialized Clangd algorithm\");\n                Box::new(matcher)\n            }\n            FuzzyAlgorithm::Frizbee => Box::new(FrizbeeMatcher::default().case(self.case).max_typos(max_typos)),\n            FuzzyAlgorithm::Fzy => {\n                let matcher = FzyMatcher::default().max_typos(max_typos);\n                let matcher = match self.case {\n                    CaseMatching::Respect => matcher.respect_case(),\n                    CaseMatching::Ignore => matcher.ignore_case(),\n                    CaseMatching::Smart => matcher.smart_case(),\n                };\n                debug!(\"Initialized Fzy algorithm (max_typos: {max_typos:?})\");\n                Box::new(matcher)\n            }\n            FuzzyAlgorithm::Arinae => {\n                let matcher = ArinaeMatcher::new(self.case, !matches!(self.typos, Typos::Disabled), self.last_match);\n                debug!(\"Initialized Arinae algorithm\");\n                Box::new(matcher)\n            }\n        };\n\n        FuzzyEngine {\n            matcher,\n            query: self.query,\n            rank_builder: self.rank_builder,\n            filter_mode: self.filter_mode,\n        }\n    }\n}\n\n/// The fuzzy matching engine\npub struct FuzzyEngine {\n    query: String,\n    matcher: Box<dyn FuzzyMatcher>,\n    rank_builder: Arc<RankBuilder>,\n    filter_mode: bool,\n}\n\nimpl FuzzyEngine {\n    /// Returns a default builder for chaining\n    #[must_use]\n    pub fn builder() -> FuzzyEngineBuilder {\n        FuzzyEngineBuilder::default()\n    }\n}\n\nimpl MatchEngine for FuzzyEngine {\n    fn match_item(&self, item: &dyn SkimItem) -> Option<MatchResult> {\n        let item_text = item.text();\n        let default_range = [(0, item_text.len())];\n\n        if self.filter_mode {\n            // Fast path: use fuzzy_match_range to avoid per-character index computation\n            let mut best: Option<(i64, usize, usize)> = None;\n            for &(start, end) in item.get_matching_ranges().unwrap_or(&default_range) {\n                let start = min(start, item_text.len());\n                let end = min(end, item_text.len());\n\n                let result = if self.query.is_empty() {\n                    Some((0i64, 0, 0))\n                } else if item_text[start..end].is_empty() {\n                    None\n                } else {\n                    self.matcher\n                        .fuzzy_match_range(&item_text[start..end], &self.query)\n                        .map(|(s, b, e)| {\n                            let offset = if start != 0 {\n                                item_text[..start].chars().count()\n                            } else {\n                                0\n                            };\n                            (s, b + offset, e + offset)\n                        })\n                };\n\n                if result.is_some() {\n                    best = result;\n                    break;\n                }\n            }\n\n            let (score, begin, end) = best?;\n            Some(MatchResult {\n                rank: self\n                    .rank_builder\n                    .build_rank(i32::try_from(score).unwrap_or(i32::MAX), begin, end, &item_text),\n                matched_range: MatchRange::ByteRange(begin, end),\n            })\n        } else {\n            let mut matched_result = None;\n            for &(start, end) in item.get_matching_ranges().unwrap_or(&default_range) {\n                let start = min(start, item_text.len());\n                let end = min(end, item_text.len());\n\n                let result = if self.query.is_empty() {\n                    Some((0i64, MatchIndices::new()))\n                } else if item_text[start..end].is_empty() {\n                    None\n                } else {\n                    self.matcher.fuzzy_indices(&item_text[start..end], &self.query)\n                };\n\n                matched_result = result.map(|(s, vec)| {\n                    if start != 0 {\n                        let start_char = item_text[..start].chars().count();\n                        (s, vec.iter().map(|x| x + start_char).collect::<MatchIndices>())\n                    } else {\n                        (s, vec)\n                    }\n                });\n\n                if matched_result.is_some() {\n                    break;\n                }\n            }\n\n            let (score, matched_indices) = matched_result?;\n            let begin = *matched_indices.first().unwrap_or(&0);\n            let end = *matched_indices.last().unwrap_or(&0);\n            let matched_range = MatchRange::Chars(matched_indices);\n\n            Some(MatchResult {\n                rank: self\n                    .rank_builder\n                    .build_rank(i32::try_from(score).unwrap_or(i32::MAX), begin, end, &item_text),\n                matched_range,\n            })\n        }\n    }\n}\n\nimpl Display for FuzzyEngine {\n    fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {\n        write!(f, \"(Fuzzy: {})\", self.query)\n    }\n}\n"
  },
  {
    "path": "src/engine/mod.rs",
    "content": "pub mod all;\npub mod andor;\npub mod exact;\npub mod factory;\npub mod fuzzy;\npub mod normalized;\npub mod regexp;\npub mod split;\nmod util;\n"
  },
  {
    "path": "src/engine/normalized.rs",
    "content": "//! Normalized match engine for matching with Unicode normalization (removing diacritics).\n//!\n//! This engine wraps another engine and normalizes both the query and item text before matching,\n//! then maps the results back to the original text.\n\nuse std::borrow::Cow;\nuse std::fmt::{Display, Error, Formatter};\n\nuse crate::engine::util::{map_byte_range_to_original, map_char_indices_to_original};\nuse crate::engine::util::{normalize_with_byte_mapping, normalize_with_char_mapping};\nuse crate::{CaseMatching, MatchEngine, MatchEngineFactory, MatchRange, MatchResult, SkimItem};\n\n/// Engine that normalizes text before matching\npub struct NormalizedEngine {\n    /// The underlying engine to match normalized text\n    inner: Box<dyn MatchEngine>,\n}\n\nimpl NormalizedEngine {\n    /// Creates a new normalized match engine\n    pub fn new(inner: Box<dyn MatchEngine>) -> Self {\n        Self { inner }\n    }\n}\n\nimpl MatchEngine for NormalizedEngine {\n    fn match_item(&self, item: &dyn SkimItem) -> Option<MatchResult> {\n        let item_text = item.text();\n\n        // Normalize the item text\n        let (normalized_text, char_mapping) = normalize_with_char_mapping(&item_text);\n        let (_, byte_mapping) = normalize_with_byte_mapping(&item_text);\n\n        // Create a wrapper item with normalized text\n        let normalized_item: &dyn SkimItem = &NormalizedItem(normalized_text);\n\n        // Match using the inner engine\n        let mut result = self.inner.match_item(normalized_item)?;\n\n        // Map the matched range back to the original text\n        result.matched_range = match result.matched_range {\n            MatchRange::Chars(indices) => MatchRange::Chars(map_char_indices_to_original(&indices, &char_mapping)),\n            MatchRange::ByteRange(start, end) => {\n                let (orig_start, orig_end) = map_byte_range_to_original(start, end, &byte_mapping, &item_text);\n                MatchRange::ByteRange(orig_start, orig_end)\n            }\n        };\n\n        Some(result)\n    }\n}\n\nimpl Display for NormalizedEngine {\n    fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {\n        write!(f, \"(Normalized: {})\", self.inner)\n    }\n}\n\n/// Simple string wrapper implementing `SkimItem` for normalized matching\nstruct NormalizedItem(String);\n\nimpl SkimItem for NormalizedItem {\n    fn text(&self) -> Cow<'_, str> {\n        Cow::Borrowed(&self.0)\n    }\n}\n\n//------------------------------------------------------------------------------\n// NormalizedEngineFactory - wraps another factory and handles normalization\n\n/// Factory that handles normalization by wrapping another engine factory\npub struct NormalizedEngineFactory {\n    inner: Box<dyn MatchEngineFactory>,\n}\n\nimpl NormalizedEngineFactory {\n    /// Creates a new normalized engine factory\n    pub fn new(inner: impl MatchEngineFactory + 'static) -> Self {\n        Self { inner: Box::new(inner) }\n    }\n}\n\nimpl MatchEngineFactory for NormalizedEngineFactory {\n    fn create_engine_with_case(&self, query: &str, case: CaseMatching) -> Box<dyn MatchEngine> {\n        // Normalize the query\n        let (normalized_query, _) = normalize_with_char_mapping(query);\n\n        // Create the inner engine with the normalized query\n        let inner_engine = self.inner.create_engine_with_case(&normalized_query, case);\n\n        // Wrap it in a NormalizedEngine\n        Box::new(NormalizedEngine::new(inner_engine))\n    }\n}\n"
  },
  {
    "path": "src/engine/regexp.rs",
    "content": "use std::fmt::{Display, Error, Formatter};\nuse std::sync::Arc;\n\nuse regex::Regex;\n\nuse crate::engine::util::regex_match;\nuse crate::item::RankBuilder;\nuse crate::{CaseMatching, MatchEngine};\nuse crate::{MatchRange, MatchResult, SkimItem};\nuse std::cmp::min;\n\n//------------------------------------------------------------------------------\n// Regular Expression engine\n#[derive(Debug)]\npub struct RegexEngine {\n    query_regex: Option<Regex>,\n    rank_builder: Arc<RankBuilder>,\n}\n\nimpl RegexEngine {\n    pub fn builder(query: &str, case: CaseMatching) -> Self {\n        let mut query_builder = String::new();\n\n        match case {\n            CaseMatching::Ignore => query_builder.push_str(\"(?i)\"),\n            CaseMatching::Respect | CaseMatching::Smart => {}\n        }\n\n        query_builder.push_str(query);\n\n        RegexEngine {\n            query_regex: Regex::new(&query_builder).ok(),\n            rank_builder: Default::default(),\n        }\n    }\n\n    pub fn rank_builder(mut self, rank_builder: Arc<RankBuilder>) -> Self {\n        self.rank_builder = rank_builder;\n        self\n    }\n\n    pub fn build(self) -> Self {\n        self\n    }\n}\n\nimpl MatchEngine for RegexEngine {\n    fn match_item(&self, item: &dyn SkimItem) -> Option<MatchResult> {\n        let mut matched_result = None;\n        let item_text = item.text();\n        let default_range = [(0, item_text.len())];\n        for &(start, end) in item.get_matching_ranges().unwrap_or(&default_range) {\n            let start = min(start, item_text.len());\n            let end = min(end, item_text.len());\n            if self.query_regex.is_none() {\n                matched_result = Some((0, 0));\n                break;\n            }\n\n            matched_result =\n                regex_match(&item_text[start..end], self.query_regex.as_ref()).map(|(s, e)| (s + start, e + start));\n\n            if matched_result.is_some() {\n                break;\n            }\n        }\n\n        let (begin, end) = matched_result?;\n        let score = i32::try_from(end - begin).unwrap_or(i32::MAX);\n\n        Some(MatchResult {\n            rank: self.rank_builder.build_rank(score, begin, end, &item_text),\n            matched_range: MatchRange::ByteRange(begin, end),\n        })\n    }\n}\n\nimpl Display for RegexEngine {\n    fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {\n        write!(\n            f,\n            \"(Regex: {})\",\n            self.query_regex\n                .as_ref()\n                .map_or(String::new(), |re| re.as_str().to_string())\n        )\n    }\n}\n"
  },
  {
    "path": "src/engine/split.rs",
    "content": "//! Split match engine for matching against different parts of items based on a delimiter.\n//!\n//! This engine splits both the query and item text on a delimiter character, then matches\n//! the query parts against the corresponding item parts.\n\nuse crate::fuzzy_matcher::MatchIndices;\nuse crate::{MatchEngine, MatchEngineFactory, MatchRange, MatchResult, SkimItem};\nuse std::fmt::{Display, Error, Formatter};\n\n/// Engine that matches by splitting query and item on a delimiter\npub struct SplitMatchEngine {\n    /// The engine to match the \"before delimiter\" part\n    before_engine: Box<dyn MatchEngine>,\n    /// The engine to match the \"after delimiter\" part  \n    after_engine: Box<dyn MatchEngine>,\n    /// The delimiter character used for splitting\n    delimiter: char,\n}\n\nimpl SplitMatchEngine {\n    /// Creates a new split match engine\n    pub fn new(before_engine: Box<dyn MatchEngine>, after_engine: Box<dyn MatchEngine>, delimiter: char) -> Self {\n        Self {\n            before_engine,\n            after_engine,\n            delimiter,\n        }\n    }\n}\n\nimpl MatchEngine for SplitMatchEngine {\n    fn match_item(&self, item: &dyn SkimItem) -> Option<MatchResult> {\n        let text = item.text();\n\n        // Find the delimiter in the item text (by char position)\n        let delimiter_char_idx = text.chars().position(|c| c == self.delimiter)?;\n\n        // Get byte position for slicing\n        let delimiter_byte_pos = text.char_indices().nth(delimiter_char_idx).map(|(i, _)| i)?;\n\n        let text_before = &text[..delimiter_byte_pos];\n        let text_after = &text[delimiter_byte_pos + self.delimiter.len_utf8()..];\n\n        // Create wrapper items for each part\n        let before_item: &dyn SkimItem = &StringItem(text_before.to_string());\n        let after_item: &dyn SkimItem = &StringItem(text_after.to_string());\n\n        // Match both parts\n        let before_result = self.before_engine.match_item(before_item)?;\n        let after_result = self.after_engine.match_item(after_item)?;\n\n        // Combine the results - use rank from first result (like AndEngine does)\n        let rank = before_result.rank;\n\n        let mut combined_indices: MatchIndices = match before_result.matched_range {\n            MatchRange::Chars(indices) => indices,\n            MatchRange::ByteRange(start, end) => {\n                // Convert byte range to char indices for the before part\n                text_before\n                    .char_indices()\n                    .enumerate()\n                    .filter(|(_, (byte_idx, _))| *byte_idx >= start && *byte_idx < end)\n                    .map(|(char_idx, _)| char_idx)\n                    .collect()\n            }\n        };\n\n        // Offset for the \"after\" part: delimiter_char_idx + 1 (to skip the delimiter)\n        let offset = delimiter_char_idx + 1;\n\n        let after_indices: MatchIndices = match after_result.matched_range {\n            MatchRange::Chars(indices) => indices.into_iter().map(|i| i + offset).collect(),\n            MatchRange::ByteRange(start, end) => {\n                // Convert byte range to char indices for the after part\n                text_after\n                    .char_indices()\n                    .enumerate()\n                    .filter(|(_, (byte_idx, _))| *byte_idx >= start && *byte_idx < end)\n                    .map(|(char_idx, _)| char_idx + offset)\n                    .collect()\n            }\n        };\n\n        combined_indices.extend(after_indices);\n        combined_indices.sort_unstable();\n        combined_indices.dedup();\n\n        Some(MatchResult {\n            rank,\n            matched_range: MatchRange::Chars(combined_indices),\n        })\n    }\n}\n\nimpl Display for SplitMatchEngine {\n    fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {\n        write!(\n            f,\n            \"(Split[{}]: {} | {})\",\n            self.delimiter, self.before_engine, self.after_engine\n        )\n    }\n}\n\n/// Simple string wrapper implementing `SkimItem` for split matching\nstruct StringItem(String);\n\nimpl SkimItem for StringItem {\n    fn text(&self) -> std::borrow::Cow<'_, str> {\n        std::borrow::Cow::Borrowed(&self.0)\n    }\n}\n\n//------------------------------------------------------------------------------\n// SplitMatchEngineFactory - wraps another factory and handles split matching\n\n/// Factory that handles split matching by wrapping another engine factory\npub struct SplitMatchEngineFactory {\n    inner: Box<dyn MatchEngineFactory>,\n    delimiter: char,\n}\n\nimpl SplitMatchEngineFactory {\n    /// Creates a new split match engine factory\n    pub fn new(inner: impl MatchEngineFactory + 'static, delimiter: char) -> Self {\n        Self {\n            inner: Box::new(inner),\n            delimiter,\n        }\n    }\n}\n\nimpl MatchEngineFactory for SplitMatchEngineFactory {\n    fn create_engine_with_case(&self, query: &str, case: crate::CaseMatching) -> Box<dyn MatchEngine> {\n        // Check if the query contains the delimiter\n        if let Some(delimiter_pos) = query.find(self.delimiter) {\n            let query_before = &query[..delimiter_pos];\n            let query_after = &query[delimiter_pos + self.delimiter.len_utf8()..];\n\n            // Create engines for each part using the inner factory\n            let before_engine = self.inner.create_engine_with_case(query_before, case);\n            let after_engine = self.inner.create_engine_with_case(query_after, case);\n\n            Box::new(SplitMatchEngine::new(before_engine, after_engine, self.delimiter))\n        } else {\n            // No delimiter in query, pass through to inner factory\n            self.inner.create_engine_with_case(query, case)\n        }\n    }\n}\n"
  },
  {
    "path": "src/engine/util.rs",
    "content": "use crate::fuzzy_matcher::MatchIndices;\nuse regex::Regex;\nuse unicode_normalization::UnicodeNormalization;\n\n/// Normalize a string and return a mapping from normalized char indices to original char indices.\n///\n/// Returns (`normalized_string`, mapping) where mapping[i] gives the original char index\n/// for the i-th character in the normalized string.\npub fn normalize_with_char_mapping(s: &str) -> (String, Vec<usize>) {\n    let mut normalized = String::new();\n    let mut mapping = Vec::new();\n\n    for (orig_char_idx, orig_char) in s.chars().enumerate() {\n        // Decompose this character into NFD form\n        for decomposed_char in orig_char.nfd() {\n            if !unicode_normalization::char::is_combining_mark(decomposed_char) {\n                normalized.push(decomposed_char);\n                mapping.push(orig_char_idx);\n            }\n        }\n    }\n\n    (normalized, mapping)\n}\n\n/// Map character indices from normalized string back to original string.\n///\n/// Given indices into a normalized string and the char mapping from `normalize_with_char_mapping`,\n/// returns the corresponding indices in the original string.\npub fn map_char_indices_to_original(normalized_indices: &[usize], char_mapping: &[usize]) -> MatchIndices {\n    normalized_indices\n        .iter()\n        .filter_map(|&idx| char_mapping.get(idx).copied())\n        .collect()\n}\n\n/// Normalize a string and return a mapping from normalized byte positions to original byte positions.\n///\n/// Returns (`normalized_string`, `byte_mapping`) where `byte_mapping`[i] gives the original byte position\n/// for the i-th byte in the normalized string.\npub fn normalize_with_byte_mapping(s: &str) -> (String, Vec<usize>) {\n    let mut normalized = String::new();\n    let mut byte_mapping = Vec::new();\n\n    for (orig_byte_pos, orig_char) in s.char_indices() {\n        // Decompose this character into NFD form\n        for decomposed_char in orig_char.nfd() {\n            if !unicode_normalization::char::is_combining_mark(decomposed_char) {\n                let char_start = normalized.len();\n                normalized.push(decomposed_char);\n                // Map each byte of the decomposed char to the original byte position\n                for _ in char_start..normalized.len() {\n                    byte_mapping.push(orig_byte_pos);\n                }\n            }\n        }\n    }\n\n    (normalized, byte_mapping)\n}\n\n/// Map a byte range from normalized string back to original string.\n///\n/// Given a (start, end) byte range in a normalized string and the byte mapping,\n/// returns the corresponding (start, end) byte range in the original string.\npub fn map_byte_range_to_original(\n    normalized_start: usize,\n    normalized_end: usize,\n    byte_mapping: &[usize],\n    original_str: &str,\n) -> (usize, usize) {\n    if byte_mapping.is_empty() || normalized_start >= byte_mapping.len() {\n        return (0, 0);\n    }\n\n    let orig_start = byte_mapping[normalized_start];\n\n    // For the end, we need to find the end of the original character\n    // that contains the last byte of the normalized range\n    let orig_end = if normalized_end > 0 && normalized_end <= byte_mapping.len() {\n        let last_byte_orig_pos = byte_mapping[normalized_end - 1];\n        // Find the end of the character at this position in the original string\n        original_str[last_byte_orig_pos..]\n            .chars()\n            .next()\n            .map_or(original_str.len(), |c| last_byte_orig_pos + c.len_utf8())\n    } else if normalized_end >= byte_mapping.len() {\n        original_str.len()\n    } else {\n        orig_start\n    };\n\n    (orig_start, orig_end)\n}\n\npub fn regex_match(choice: &str, pattern: Option<&Regex>) -> Option<(usize, usize)> {\n    let pat = pattern?;\n    let mat = pat.find(choice)?;\n    Some((mat.start(), mat.end()))\n}\n\npub fn contains_upper(string: &str) -> bool {\n    for ch in string.chars() {\n        if ch.is_uppercase() {\n            return true;\n        }\n    }\n    false\n}\n"
  },
  {
    "path": "src/field.rs",
    "content": "//! Field extraction and parsing utilities.\n//!\n//! This module provides utilities for parsing field ranges and extracting\n//! fields from text based on delimiters.\n\nuse regex::Regex;\nuse std::{\n    cmp::{max, min},\n    sync::LazyLock,\n};\n\nstatic FIELD_RANGE: LazyLock<Regex> =\n    LazyLock::new(|| Regex::new(r\"^(?P<left>-?\\d+)?(?P<sep>\\.\\.)?(?P<right>-?\\d+)?$\").unwrap());\n\n/// Represents a range of fields to extract from text\n#[derive(PartialEq, Eq, Clone, Debug)]\npub enum FieldRange {\n    /// A single field at the given index\n    Single(i32),\n    /// All fields from the start up to and including the given index\n    LeftInf(i32),\n    /// All fields from the given index to the end\n    RightInf(i32),\n    /// Fields between two indices (inclusive)\n    Both(i32, i32),\n}\n\nimpl FieldRange {\n    /// Parses a field range from a string (e.g., \"1\", \"1..\", \"..10\", \"1..10\")\n    #[allow(clippy::should_implement_trait)]\n    pub fn from_str(range: &str) -> Option<FieldRange> {\n        use self::FieldRange::{Both, LeftInf, RightInf, Single};\n\n        // \"1\", \"1..\", \"..10\", \"1..10\", etc.\n        let opt_caps = FIELD_RANGE.captures(range);\n        if let Some(caps) = opt_caps {\n            let opt_left = caps.name(\"left\").map(|s| s.as_str().parse().unwrap_or(1));\n            let opt_right = caps.name(\"right\").map(|s| s.as_str().parse().unwrap_or(-1));\n            let opt_sep = caps.name(\"sep\").map(|s| s.as_str().to_string());\n\n            match (opt_left, opt_right) {\n                (None, None) => Some(RightInf(0)),\n                (Some(left), None) => {\n                    match opt_sep {\n                        None => Some(Single(left)),      // 1\n                        Some(_) => Some(RightInf(left)), // 1..\n                    }\n                }\n                (None, Some(right)) => {\n                    match opt_sep {\n                        None => Some(Single(right)),     // 1 (should not happen)\n                        Some(_) => Some(LeftInf(right)), // ..1 (should not happen)\n                    }\n                }\n                (Some(left), Some(right)) => Some(Both(left, right)), // 1..3\n            }\n        } else {\n            None\n        }\n    }\n\n    /// Converts a field range to an index pair (left, right).\n    ///\n    /// For example, 1..3 => (0, 4). Note that field range is inclusive while\n    /// the output index will exclude the right end.\n    #[must_use]\n    pub fn to_index_pair(&self, length: usize) -> Option<(usize, usize)> {\n        use self::FieldRange::{Both, LeftInf, RightInf, Single};\n        match *self {\n            Single(num) => {\n                let num = FieldRange::translate_neg(num, length);\n                if num == 0 || num > length {\n                    None\n                } else {\n                    Some((num - 1, num))\n                }\n            }\n            LeftInf(right) => {\n                let right = FieldRange::translate_neg(right, length);\n                if length == 0 || right == 0 {\n                    None\n                } else {\n                    let right = min(right, length);\n                    Some((0, right))\n                }\n            }\n            RightInf(left) => {\n                let left = FieldRange::translate_neg(left, length);\n                if length == 0 || left > length {\n                    None\n                } else {\n                    let left = max(left, 1);\n                    Some((left - 1, length))\n                }\n            }\n            Both(left, right) => {\n                let left = FieldRange::translate_neg(left, length);\n                let right = FieldRange::translate_neg(right, length);\n                if length == 0 || right == 0 || left > right || left > length {\n                    None\n                } else {\n                    Some((max(left, 1) - 1, min(right, length)))\n                }\n            }\n        }\n    }\n\n    fn translate_neg(idx: i32, length: usize) -> usize {\n        let len = i32::try_from(length).unwrap_or(i32::MAX);\n        let idx = if idx < 0 { idx + len + 1 } else { idx };\n        max(0, idx).unsigned_abs() as usize\n    }\n}\n\n// (\"|\", \"a|b||c\") -> [(0, 2), (2, 4), (4, 5), (5, 6)]\n// explain: split to [\"a|\", \"b|\", \"|\", \"c\"]\nfn get_ranges_by_delimiter(delimiter: &Regex, text: &str) -> Vec<(usize, usize)> {\n    let mut ranges = Vec::new();\n    let mut last = 0;\n    for mat in delimiter.find_iter(text) {\n        ranges.push((last, mat.start()));\n        last = mat.end();\n    }\n    ranges.push((last, text.len()));\n    ranges\n}\n\n/// Extracts a substring from text based on a field range and delimiter.\n///\n/// For example, with delimiter = `Regex::new(\",\").unwrap()`, text \"a,b,c\", and field Single(2),\n/// this returns \"b\". Note that this is different from `to_index_pair`, it uses delimiters.\n#[must_use]\npub fn get_string_by_field<'a>(delimiter: &Regex, text: &'a str, field: &FieldRange) -> Option<&'a str> {\n    let ranges = get_ranges_by_delimiter(delimiter, text);\n\n    if let Some((start, stop)) = field.to_index_pair(ranges.len()) {\n        let &(begin, _) = &ranges[start];\n        let &(_, end) = ranges.get(stop - 1).unwrap_or(&(text.len(), 0));\n        Some(&text[begin..end])\n    } else {\n        None\n    }\n}\n\n/// Extracts a substring from text by parsing a range string and using a delimiter\n#[must_use]\npub fn get_string_by_range<'a>(delimiter: &Regex, text: &'a str, range: &str) -> Option<&'a str> {\n    FieldRange::from_str(range).and_then(|field| get_string_by_field(delimiter, text, &field))\n}\n\n/// Parses matching fields and returns a vector of byte ranges.\n///\n/// Given delimiter `,`, text: \"a,b,c\", and fields &[Single(2), LeftInf(2)],\n/// this returns [(2, 4), (0, 4)].\n#[must_use]\npub fn parse_matching_fields(delimiter: &Regex, text: &str, fields: &[FieldRange]) -> Vec<(usize, usize)> {\n    let ranges = get_ranges_by_delimiter(delimiter, text);\n\n    let mut ret = Vec::new();\n    for field in fields {\n        if let Some((start, stop)) = field.to_index_pair(ranges.len()) {\n            let &(begin, _) = &ranges[start];\n            let &(end, _) = ranges.get(stop).unwrap_or(&(text.len(), 0));\n            ret.push((begin, end));\n        }\n    }\n    ret\n}\n\n/// Extracts the specified fields from text using the delimiter\n#[must_use]\npub fn parse_transform_fields(delimiter: &Regex, text: &str, fields: &[FieldRange]) -> String {\n    let ranges = get_ranges_by_delimiter(delimiter, text);\n\n    let mut ret = String::new();\n    for field in fields {\n        if let Some((start, stop)) = field.to_index_pair(ranges.len()) {\n            let &(begin, _) = &ranges[start];\n            let &(end, _) = ranges.get(stop).unwrap_or(&(text.len(), 0));\n            ret.push_str(&text[begin..end]);\n        }\n    }\n    ret\n}\n\n#[cfg(test)]\nmod test {\n    use super::FieldRange::*;\n    #[test]\n    fn test_parse_range() {\n        assert_eq!(FieldRange::from_str(\"1\"), Some(Single(1)));\n        assert_eq!(FieldRange::from_str(\"-1\"), Some(Single(-1)));\n\n        assert_eq!(FieldRange::from_str(\"1..\"), Some(RightInf(1)));\n        assert_eq!(FieldRange::from_str(\"-1..\"), Some(RightInf(-1)));\n\n        assert_eq!(FieldRange::from_str(\"..1\"), Some(LeftInf(1)));\n        assert_eq!(FieldRange::from_str(\"..-1\"), Some(LeftInf(-1)));\n\n        assert_eq!(FieldRange::from_str(\"1..3\"), Some(Both(1, 3)));\n        assert_eq!(FieldRange::from_str(\"-1..-3\"), Some(Both(-1, -3)));\n\n        assert_eq!(FieldRange::from_str(\"..\"), Some(RightInf(0)));\n        assert_eq!(FieldRange::from_str(\"a..\"), None);\n        assert_eq!(FieldRange::from_str(\"..b\"), None);\n        assert_eq!(FieldRange::from_str(\"a..b\"), None);\n    }\n\n    use regex::Regex;\n\n    #[test]\n    fn test_parse_field_range() {\n        assert_eq!(Single(0).to_index_pair(10), None);\n        assert_eq!(Single(1).to_index_pair(10), Some((0, 1)));\n        assert_eq!(Single(10).to_index_pair(10), Some((9, 10)));\n        assert_eq!(Single(11).to_index_pair(10), None);\n        assert_eq!(Single(-1).to_index_pair(10), Some((9, 10)));\n        assert_eq!(Single(-10).to_index_pair(10), Some((0, 1)));\n        assert_eq!(Single(-11).to_index_pair(10), None);\n\n        assert_eq!(LeftInf(0).to_index_pair(10), None);\n        assert_eq!(LeftInf(1).to_index_pair(10), Some((0, 1)));\n        assert_eq!(LeftInf(8).to_index_pair(10), Some((0, 8)));\n        assert_eq!(LeftInf(10).to_index_pair(10), Some((0, 10)));\n        assert_eq!(LeftInf(11).to_index_pair(10), Some((0, 10)));\n        assert_eq!(LeftInf(-1).to_index_pair(10), Some((0, 10)));\n        assert_eq!(LeftInf(-8).to_index_pair(10), Some((0, 3)));\n        assert_eq!(LeftInf(-9).to_index_pair(10), Some((0, 2)));\n        assert_eq!(LeftInf(-10).to_index_pair(10), Some((0, 1)));\n        assert_eq!(LeftInf(-11).to_index_pair(10), None);\n\n        assert_eq!(RightInf(0).to_index_pair(10), Some((0, 10)));\n        assert_eq!(RightInf(1).to_index_pair(10), Some((0, 10)));\n        assert_eq!(RightInf(8).to_index_pair(10), Some((7, 10)));\n        assert_eq!(RightInf(10).to_index_pair(10), Some((9, 10)));\n        assert_eq!(RightInf(11).to_index_pair(10), None);\n        assert_eq!(RightInf(-1).to_index_pair(10), Some((9, 10)));\n        assert_eq!(RightInf(-8).to_index_pair(10), Some((2, 10)));\n        assert_eq!(RightInf(-9).to_index_pair(10), Some((1, 10)));\n        assert_eq!(RightInf(-10).to_index_pair(10), Some((0, 10)));\n        assert_eq!(RightInf(-11).to_index_pair(10), Some((0, 10)));\n\n        assert_eq!(Both(0, 0).to_index_pair(10), None);\n        assert_eq!(Both(0, 1).to_index_pair(10), Some((0, 1)));\n        assert_eq!(Both(0, 10).to_index_pair(10), Some((0, 10)));\n        assert_eq!(Both(0, 11).to_index_pair(10), Some((0, 10)));\n        assert_eq!(Both(1, -1).to_index_pair(10), Some((0, 10)));\n        assert_eq!(Both(1, -9).to_index_pair(10), Some((0, 2)));\n        assert_eq!(Both(1, -10).to_index_pair(10), Some((0, 1)));\n        assert_eq!(Both(1, -11).to_index_pair(10), None);\n        assert_eq!(Both(-9, -9).to_index_pair(10), Some((1, 2)));\n        assert_eq!(Both(-9, -8).to_index_pair(10), Some((1, 3)));\n        assert_eq!(Both(-9, 0).to_index_pair(10), None);\n        assert_eq!(Both(-9, 1).to_index_pair(10), None);\n        assert_eq!(Both(-9, 2).to_index_pair(10), Some((1, 2)));\n        assert_eq!(Both(-1, 0).to_index_pair(10), None);\n        assert_eq!(Both(11, 20).to_index_pair(10), None);\n        assert_eq!(Both(-11, -11).to_index_pair(10), None);\n    }\n\n    #[test]\n    fn test_parse_transform_fields() {\n        // delimiter is \",\"\n        let re = Regex::new(\",\").unwrap();\n\n        assert_eq!(\n            super::parse_transform_fields(&re, \"A,B,C,D,E,F\", &[Single(2), Single(4), Single(-1), Single(-7)]),\n            \"B,D,F\"\n        );\n\n        assert_eq!(\n            super::parse_transform_fields(&re, \"A,B,C,D,E,F\", &[LeftInf(3), LeftInf(-6), LeftInf(-7)]),\n            \"A,B,C,A,\"\n        );\n\n        assert_eq!(\n            super::parse_transform_fields(\n                &re,\n                \"A,B,C,D,E,F\",\n                &[RightInf(5), RightInf(-2), RightInf(-1), RightInf(8)]\n            ),\n            \"E,FE,FF\"\n        );\n\n        assert_eq!(\n            super::parse_transform_fields(\n                &re,\n                \"A,B,C,D,E,F\",\n                &[Both(3, 3), Both(-9, 2), Both(6, 10), Both(-9, -5)]\n            ),\n            \"C,A,B,FA,B,\"\n        );\n    }\n\n    #[test]\n    fn test_parse_matching_fields() {\n        // delimiter is \",\"\n        let re = Regex::new(\",\").unwrap();\n\n        // bytes:3  3  3 3\n        //       中,华,人,民,E,F\",\n\n        assert_eq!(\n            super::parse_matching_fields(&re, \"中,华,人,民,E,F\", &[Single(2), Single(4), Single(-1), Single(-7)]),\n            vec![(4, 8), (12, 16), (18, 19)]\n        );\n\n        assert_eq!(\n            super::parse_matching_fields(&re, \"中,华,人,民,E,F\", &[LeftInf(3), LeftInf(-6), LeftInf(-7)]),\n            vec![(0, 12), (0, 4)]\n        );\n\n        assert_eq!(\n            super::parse_matching_fields(\n                &re,\n                \"中,华,人,民,E,F\",\n                &[RightInf(5), RightInf(-2), RightInf(-1), RightInf(7)]\n            ),\n            vec![(16, 19), (16, 19), (18, 19)]\n        );\n\n        assert_eq!(\n            super::parse_matching_fields(\n                &re,\n                \"中,华,人,民,E,F\",\n                &[Both(3, 3), Both(-8, 2), Both(6, 10), Both(-8, -5)]\n            ),\n            vec![(8, 12), (0, 8), (18, 19), (0, 8)]\n        );\n    }\n\n    use super::*;\n\n    #[test]\n    fn test_null_delimiter() {\n        // Test with null byte delimiter\n        let re = Regex::new(\"\\x00\").unwrap();\n        let text = \"a\\x00b\\x00c\";\n\n        // Test field extraction\n        assert_eq!(get_string_by_field(&re, text, &Single(1)), Some(\"a\"));\n        assert_eq!(get_string_by_field(&re, text, &Single(2)), Some(\"b\"));\n        assert_eq!(get_string_by_field(&re, text, &Single(3)), Some(\"c\"));\n\n        // Test matching fields - ranges include the delimiter after the field\n        // text bytes: a(0), \\0(1), b(2), \\0(3), c(4)\n        // Field 2 is \"b\" at byte 2, range includes delimiter at byte 3, so (2, 4)\n        assert_eq!(parse_matching_fields(&re, text, &[Single(2)]), vec![(2, 4)]);\n\n        // Field 1 is \"a\" at byte 0, range includes delimiter at byte 1, so (0, 2)\n        // Field 3 is \"c\" at byte 4, no delimiter after it, so (4, 5)\n        assert_eq!(\n            parse_matching_fields(&re, text, &[Single(1), Single(3)]),\n            vec![(0, 2), (4, 5)]\n        );\n    }\n\n    #[test]\n    fn test_get_string_by_field() {\n        // delimiter is \",\"\n        let re = Regex::new(\",\").unwrap();\n        let text = \"a,b,c,\";\n        assert_eq!(get_string_by_field(&re, text, &Single(0)), None);\n        assert_eq!(get_string_by_field(&re, text, &Single(1)), Some(\"a\"));\n        assert_eq!(get_string_by_field(&re, text, &Single(2)), Some(\"b\"));\n        assert_eq!(get_string_by_field(&re, text, &Single(3)), Some(\"c\"));\n        assert_eq!(get_string_by_field(&re, text, &Single(4)), Some(\"\"));\n        assert_eq!(get_string_by_field(&re, text, &Single(5)), None);\n        assert_eq!(get_string_by_field(&re, text, &Single(6)), None);\n        assert_eq!(get_string_by_field(&re, text, &Single(-1)), Some(\"\"));\n        assert_eq!(get_string_by_field(&re, text, &Single(-2)), Some(\"c\"));\n        assert_eq!(get_string_by_field(&re, text, &Single(-3)), Some(\"b\"));\n        assert_eq!(get_string_by_field(&re, text, &Single(-4)), Some(\"a\"));\n        assert_eq!(get_string_by_field(&re, text, &Single(-5)), None);\n        assert_eq!(get_string_by_field(&re, text, &Single(-6)), None);\n\n        assert_eq!(get_string_by_field(&re, text, &LeftInf(0)), None);\n        assert_eq!(get_string_by_field(&re, text, &LeftInf(1)), Some(\"a\"));\n        assert_eq!(get_string_by_field(&re, text, &LeftInf(2)), Some(\"a,b\"));\n        assert_eq!(get_string_by_field(&re, text, &LeftInf(3)), Some(\"a,b,c\"));\n        assert_eq!(get_string_by_field(&re, text, &LeftInf(4)), Some(\"a,b,c,\"));\n        assert_eq!(get_string_by_field(&re, text, &LeftInf(5)), Some(\"a,b,c,\"));\n        assert_eq!(get_string_by_field(&re, text, &LeftInf(-5)), None);\n        assert_eq!(get_string_by_field(&re, text, &LeftInf(-4)), Some(\"a\"));\n        assert_eq!(get_string_by_field(&re, text, &LeftInf(-3)), Some(\"a,b\"));\n        assert_eq!(get_string_by_field(&re, text, &LeftInf(-2)), Some(\"a,b,c\"));\n        assert_eq!(get_string_by_field(&re, text, &LeftInf(-1)), Some(\"a,b,c,\"));\n\n        assert_eq!(get_string_by_field(&re, text, &RightInf(0)), Some(\"a,b,c,\"));\n        assert_eq!(get_string_by_field(&re, text, &RightInf(1)), Some(\"a,b,c,\"));\n        assert_eq!(get_string_by_field(&re, text, &RightInf(2)), Some(\"b,c,\"));\n        assert_eq!(get_string_by_field(&re, text, &RightInf(3)), Some(\"c,\"));\n        assert_eq!(get_string_by_field(&re, text, &RightInf(4)), Some(\"\"));\n        assert_eq!(get_string_by_field(&re, text, &RightInf(5)), None);\n        assert_eq!(get_string_by_field(&re, text, &RightInf(-5)), Some(\"a,b,c,\"));\n        assert_eq!(get_string_by_field(&re, text, &RightInf(-4)), Some(\"a,b,c,\"));\n        assert_eq!(get_string_by_field(&re, text, &RightInf(-3)), Some(\"b,c,\"));\n        assert_eq!(get_string_by_field(&re, text, &RightInf(-2)), Some(\"c,\"));\n        assert_eq!(get_string_by_field(&re, text, &RightInf(-1)), Some(\"\"));\n\n        assert_eq!(get_string_by_field(&re, text, &Both(0, 0)), None);\n        assert_eq!(get_string_by_field(&re, text, &Both(0, 1)), Some(\"a\"));\n        assert_eq!(get_string_by_field(&re, text, &Both(0, 2)), Some(\"a,b\"));\n        assert_eq!(get_string_by_field(&re, text, &Both(0, 3)), Some(\"a,b,c\"));\n        assert_eq!(get_string_by_field(&re, text, &Both(0, 4)), Some(\"a,b,c,\"));\n        assert_eq!(get_string_by_field(&re, text, &Both(0, 5)), Some(\"a,b,c,\"));\n        assert_eq!(get_string_by_field(&re, text, &Both(1, 1)), Some(\"a\"));\n        assert_eq!(get_string_by_field(&re, text, &Both(1, 2)), Some(\"a,b\"));\n        assert_eq!(get_string_by_field(&re, text, &Both(1, 3)), Some(\"a,b,c\"));\n        assert_eq!(get_string_by_field(&re, text, &Both(1, 4)), Some(\"a,b,c,\"));\n        assert_eq!(get_string_by_field(&re, text, &Both(1, 5)), Some(\"a,b,c,\"));\n        assert_eq!(get_string_by_field(&re, text, &Both(2, 5)), Some(\"b,c,\"));\n        assert_eq!(get_string_by_field(&re, text, &Both(3, 5)), Some(\"c,\"));\n        assert_eq!(get_string_by_field(&re, text, &Both(4, 5)), Some(\"\"));\n        assert_eq!(get_string_by_field(&re, text, &Both(5, 5)), None);\n        assert_eq!(get_string_by_field(&re, text, &Both(6, 5)), None);\n        assert_eq!(get_string_by_field(&re, text, &Both(2, 3)), Some(\"b,c\"));\n        assert_eq!(get_string_by_field(&re, text, &Both(3, 3)), Some(\"c\"));\n        assert_eq!(get_string_by_field(&re, text, &Both(4, 3)), None);\n    }\n}\n"
  },
  {
    "path": "src/fuzzy_matcher/arinae/algo.rs",
    "content": "//! Arinae's algo itself\n\nuse std::cell::RefCell;\n\nuse thread_local::ThreadLocal;\n\nuse crate::fuzzy_matcher::{IndexType, MatchIndices};\n\nuse super::banding::{compute_banding, typo_vband_row};\nuse super::constants::{\n    CONSECUTIVE_BONUS, GAP_EXTEND, GAP_OPEN, MATCH_BONUS, MAX_PAT_LEN, MISMATCH_PENALTY, TYPO_PENALTY,\n};\nuse super::{Atom, CELL_ZERO, Cell, Dir, SWMatrix, Score};\n\n/// Core cell scoring kernel shared by both score-only and full DP.\n///\n/// Computes the best score and direction for a single DP cell from its\n/// three neighbours (diagonal, up, left). The caller is responsible for\n/// fetching the neighbour values from whatever storage layout it uses.\n///\n/// Returns `(best_score, direction)`. The direction is `Dir::None` when\n/// `best_score <= 0`.\n///\n/// This function is written in a branchless style: all scoring arithmetic\n/// uses `bool as Score` multipliers and `max` instead of if/else, and the\n/// final direction is selected via a branchless cascade of conditional moves.\n#[inline(always)]\n#[allow(clippy::too_many_arguments)]\n#[allow(clippy::fn_params_excessive_bools)]\nfn compute_cell<const ALLOW_TYPOS: bool>(\n    is_match: bool,\n    is_first: bool,\n    bonus_j: Score,\n    diag_score: Score,\n    diag_was_diag: bool,\n    up_score: Score,\n    left_score: Score,\n    left_was_diag: bool,\n) -> (Score, Dir) {\n    // --- Bonus (branchless) ---\n    // consecutive bonus added when diag_was_diag, first-char multiplier doubles the bonus.\n    // `bool as Score` is 0 or 1 — no branch.\n    let bonus = (bonus_j + CONSECUTIVE_BONUS * Score::from(diag_was_diag)) * (1 + Score::from(is_first));\n\n    // --- DIAGONAL (branchless) ---\n    // Match path: diag_score + MATCH_BONUS + bonus, masked by is_match.\n    // Mismatch path (typos only): diag_score - MISMATCH_PENALTY, masked by !is_match.\n    let match_val = (diag_score + MATCH_BONUS + bonus) * Score::from(is_match);\n    let mismatch_val = if ALLOW_TYPOS {\n        (diag_score - MISMATCH_PENALTY) * Score::from(!is_match)\n    } else {\n        0\n    };\n    let diag_val = match_val + mismatch_val;\n\n    // --- UP (skip pattern char, typos only — const-generic elides entirely) ---\n    let up_val = if ALLOW_TYPOS { up_score - TYPO_PENALTY } else { 0 };\n\n    // --- LEFT (skip choice char, branchless gap penalty) ---\n    // GAP_OPEN when left_was_diag, GAP_EXTEND otherwise.\n    // pen = GAP_EXTEND + (GAP_OPEN - GAP_EXTEND) * left_was_diag\n    let left_val = left_score - (GAP_EXTEND + (GAP_OPEN - GAP_EXTEND) * Score::from(left_was_diag));\n\n    // --- Best score (branchless max chain) ---\n    let best = diag_val.max(up_val).max(left_val);\n\n    // --- Direction (branchless select) ---\n    // We encode direction as a u8 and build it without branches.\n    // Priority: Diag > Up > Left > None (when best <= 0).\n    //\n    // Start with Left (2), override with Up if up wins, override with Diag\n    // if diag wins, override with None if best <= 0.\n    // For exact mode (ALLOW_TYPOS=false), Diag is only valid when is_match.\n    let diag_wins = if ALLOW_TYPOS {\n        diag_val >= up_val && diag_val >= left_val\n    } else {\n        is_match && diag_val >= left_val\n    };\n    let up_wins = ALLOW_TYPOS && !diag_wins && up_val >= left_val;\n\n    // Branchless cascade: select dir as integer.\n    // Dir encoding: None=0, Diag=1, Up=2, Left=3.\n    // Base is Left(3); subtract 1 if Up wins, subtract 2 if Diag wins.\n    let dir_bits: u8 = Dir::Left as u8 - u8::from(up_wins) - u8::from(diag_wins) * 2;\n    // If best <= 0, force Dir::None (0) — achieved by ANDing with all-zeros.\n    let positive = best > 0;\n    // When positive: dir_bits; when not: 0 (Dir::None).\n    let dir_val = dir_bits & u8::from(positive).wrapping_neg();\n\n    // SAFETY: dir_val is in 0..=3 because of the construction above.\n    let dir: Dir = unsafe { std::mem::transmute(dir_val) };\n\n    (best, dir)\n}\n\n// ---------------------------------------------------------------------------\n// Full DP with traceback — packed Cell (u32 = score + dir)\n// ---------------------------------------------------------------------------\n\n/// Full DP for byte slices using packed cells.\n///\n/// Implements two pruning strategies:\n///\n/// 1. **Row-range banding** – for each row `i` only compute columns\n///    `j_lo..=j_hi` that can participate in a valid alignment.\n///    - Exact mode: bounded by precomputed first/last match columns.\n///    - Typo mode: bounded by diagonal ± bandwidth.\n///\n/// 2. **Interpair max-score pruning** – after processing a row, if no\n///    column produced a non-zero score, all active alignments for this\n///    and subsequent rows are dead (since UP/LEFT can only propagate\n///    existing scores). We track this and allow early termination.\n#[allow(clippy::too_many_lines)]\npub(super) fn full_dp<const ALLOW_TYPOS: bool, const COMPUTE_INDICES: bool, C: Atom>(\n    cho: &[C],\n    pat: &[C],\n    bonuses: &[Score],\n    respect_case: bool,\n    full_buf: &ThreadLocal<RefCell<SWMatrix>>,\n    indices_buf: &ThreadLocal<RefCell<MatchIndices>>,\n    use_last_match: bool,\n) -> Option<(Score, MatchIndices)> {\n    let n = pat.len();\n    let m = cho.len();\n\n    let banding = compute_banding::<ALLOW_TYPOS, C>(pat, cho, respect_case)?;\n    let j_start = banding.j_first; // earliest match — skip columns before this\n\n    // Column offset: the matrix stores only columns from j_start onward.\n    // Matrix column 0 is the left wall (all zeros); matrix column `jm`\n    // corresponds to original 1-indexed column `j = jm + j_start - 1`.\n    let col_off = j_start - 1; // subtract from original j to get matrix col\n    let mcols = m - col_off + 1; // matrix columns: 0 ..= (m - col_off)\n\n    let mut buf = full_buf\n        .get_or(|| RefCell::new(SWMatrix::zero(n + 1, mcols)))\n        .borrow_mut();\n    buf.resize(n + 1, mcols);\n\n    // Hoist pointer and stride before initialization to use raw access.\n    let base_ptr = buf.data.as_mut_ptr();\n    let cols = buf.cols;\n\n    // Initialize row 0 to CELL_ZERO (all-zero bytes: score=0, dir=None=0).\n    // Column 0 of each subsequent row is also CELL_ZERO.\n    // SAFETY: base_ptr points to a valid allocation of (n+1)*cols Cells.\n    unsafe {\n        // Row 0: mcols contiguous Cells starting at base_ptr.\n        std::ptr::write_bytes(base_ptr, 0, mcols);\n        // Column 0 of rows 1..=n: one Cell per row, stride = cols.\n        for i in 1..=n {\n            *base_ptr.add(i * cols) = CELL_ZERO;\n        }\n    }\n\n    // base_ptr and cols already set above\n\n    // Pre-extract row bounds once (avoids repeated unwrap inside the loop).\n    // For exact mode we copy the arrays out; for typo mode these are unused.\n    let (row_lo_arr, row_hi_arr) = if ALLOW_TYPOS {\n        ([0usize; MAX_PAT_LEN], [0usize; MAX_PAT_LEN])\n    } else {\n        let (lo, hi) = banding.row_bounds.as_ref().unwrap();\n        (*lo, *hi)\n    };\n\n    // Hoist invariant pointers outside the row loop.\n    let cho_ptr = cho.as_ptr();\n    let bonuses_ptr = bonuses.as_ptr();\n\n    for i in 1..=n {\n        let pi = pat[i - 1];\n        let is_first = i == 1;\n\n        // --- Compute column bounds for this row (original 1-indexed space) ---\n        let (j_lo, j_hi) = typo_vband_row(i, m, banding.bandwidth, banding.j_first);\n\n        if j_lo > j_hi || j_lo > m {\n            // Entire row is outside the band. Only zero the cells the next\n            // row's Diag (reads [i][jm-1]) and Up (reads [i][jm]) will touch.\n            // Peek at the next row's bounds to limit work.\n            if i < n {\n                let (nj_lo, nj_hi) = if ALLOW_TYPOS {\n                    typo_vband_row(i + 1, m, banding.bandwidth, banding.j_first)\n                } else {\n                    (row_lo_arr[i], row_hi_arr[i])\n                };\n                let nj_lo = nj_lo.max(j_start);\n                if nj_lo <= nj_hi && nj_lo <= m {\n                    let next_mat_lo = nj_lo - col_off;\n                    let next_mat_hi = (nj_hi - col_off).min(mcols - 1);\n                    // Diag reads jm-1, Up reads jm → need [next_mat_lo-1 .. next_mat_hi].\n                    let zero_lo = next_mat_lo.saturating_sub(1);\n                    let zero_hi = next_mat_hi.min(mcols - 1);\n                    // SAFETY: row i is within the allocated matrix.\n                    unsafe {\n                        let row_ptr = base_ptr.add(i * cols);\n                        for k in zero_lo..=zero_hi {\n                            *row_ptr.add(k) = CELL_ZERO;\n                        }\n                    }\n                }\n            }\n            continue;\n        }\n\n        // Convert to matrix-local column indices (safe: j_lo >= j_start here).\n        let mat_col_lo = j_lo - col_off;\n        let mat_col_hi = j_hi - col_off;\n        let jm_max = mcols - 1; // last valid matrix column\n\n        // Zero only the boundary cells that Diag/Left/Up moves will read:\n        // - Cell at mat_col_lo-1: read by Left at mat_col_lo and Diag from next row.\n        // - Cell at mat_col_hi+1: read by Up from next row at mat_col_hi+1 (if in next band).\n        // SAFETY: indices are within the row's allocation.\n        unsafe {\n            let row_ptr = base_ptr.add(i * cols);\n            if mat_col_lo > 1 {\n                *row_ptr.add(mat_col_lo - 1) = CELL_ZERO;\n            }\n            if mat_col_hi < jm_max {\n                *row_ptr.add(mat_col_hi + 1) = CELL_ZERO;\n            }\n        }\n\n        // Get prev_row as immutable slice, cur_row as mutable slice.\n        // SAFETY: i >= 1 so rows i-1 and i are distinct; each row is\n        // cols-aligned inside the contiguous data vec. base_ptr/cols are\n        // hoisted outside the loop.\n        let (prev_row, cur_row) = unsafe {\n            let pr = std::slice::from_raw_parts(base_ptr.add((i - 1) * cols), cols);\n            let cr = std::slice::from_raw_parts_mut(base_ptr.add(i * cols), cols);\n            (pr, cr)\n        };\n\n        // Hoist raw pointers for unchecked access inside the hot loop.\n        let prev_ptr = prev_row.as_ptr();\n        let cur_ptr = cur_row.as_mut_ptr();\n\n        for j in j_lo..=j_hi {\n            let jm = j - col_off; // matrix column\n            // SAFETY: j and jm are inside the band and within array bounds.\n            let cj = unsafe { *cho_ptr.add(j - 1) };\n            let is_match = pi.eq(cj, respect_case);\n\n            // Fetch neighbour values from the matrix.\n            let diag_cell = unsafe { *prev_ptr.add(jm - 1) };\n            let up_score = if ALLOW_TYPOS {\n                let up_cell = unsafe { *prev_ptr.add(jm) };\n                up_cell.score()\n            } else {\n                0\n            };\n            let left_cell = unsafe { *cur_ptr.add(jm - 1) };\n\n            let (best, dir) = compute_cell::<ALLOW_TYPOS>(\n                is_match,\n                is_first,\n                unsafe { *bonuses_ptr.add(j - 1) },\n                diag_cell.score(),\n                diag_cell.is_diag(),\n                up_score,\n                left_cell.score(),\n                left_cell.is_diag(),\n            );\n\n            unsafe {\n                *cur_ptr.add(jm) = Cell::new(best, dir);\n            }\n        }\n    }\n\n    // --- Find best score in the last row (row n) ---\n    // Moved out of the inner loop to eliminate the `i == n` branch per cell.\n    let mut best_score: Score = 0;\n    let mut best_j = 0usize; // stored in original 1-indexed space\n    {\n        let (last_j_lo_raw, last_j_hi) = if ALLOW_TYPOS {\n            typo_vband_row(n, m, banding.bandwidth, banding.j_first)\n        } else {\n            (row_lo_arr[n - 1], row_hi_arr[n - 1])\n        };\n        let last_j_lo = last_j_lo_raw.max(j_start);\n        let last_row_ptr = unsafe { base_ptr.add(n * cols) };\n        if use_last_match {\n            // Iterate left-to-right with `>=` so that every column tying the\n            // running best overwrites best_j — the last (rightmost) tie wins.\n            for j in last_j_lo..=last_j_hi {\n                let jm = j - col_off;\n                let s = unsafe { (*last_row_ptr.add(jm)).score() };\n                let better = s >= best_score && s > 0;\n                best_score = if better { s } else { best_score };\n                best_j = if better { j } else { best_j };\n            }\n        } else {\n            for j in last_j_lo..=last_j_hi {\n                let jm = j - col_off;\n                let s = unsafe { (*last_row_ptr.add(jm)).score() };\n                // Branchless max: update best_score and best_j together.\n                let better = s > best_score;\n                // Use conditional moves instead of a branch.\n                best_score = if better { s } else { best_score };\n                best_j = if better { j } else { best_j };\n            }\n        }\n    }\n\n    if best_score <= 0 {\n        return None;\n    }\n\n    if COMPUTE_INDICES {\n        // Traceback — j walks in original 1-indexed space, convert to matrix\n        // column for buf access; output indices in original 0-indexed space.\n        // Reuse a thread-local Vec to avoid per-call allocation.\n        let indices_ref_cell = indices_buf.get_or(|| RefCell::new(Vec::new()));\n        let mut indices_ref = indices_ref_cell.borrow_mut();\n        indices_ref.clear();\n        let mut i = n;\n        let mut j = best_j;\n        let mut true_matches = 0usize;\n\n        while i > 0 && j >= j_start {\n            let jm = j - col_off;\n            // SAFETY: jm and i are within the matrix bounds established above.\n            let cell_val = unsafe { *base_ptr.add(i * cols).add(jm) };\n            match cell_val.dir() {\n                Dir::Diag => {\n                    if pat[i - 1].eq(cho[j - 1], respect_case) {\n                        indices_ref.push((j - 1) as IndexType);\n                        true_matches += 1;\n                    }\n                    i -= 1;\n                    j -= 1;\n                }\n                Dir::Up => {\n                    i -= 1;\n                }\n                Dir::Left => {\n                    j -= 1;\n                }\n                Dir::None => break,\n            }\n        }\n\n        if true_matches < banding.min_true_matches {\n            return None;\n        }\n\n        // Traceback produces indices in reverse order; reverse is O(n)\n        // vs sort_unstable's O(n log n).\n        indices_ref.reverse();\n\n        // Move ownership out of the thread-local buffer by cloning the vec's\n        // contents into a fresh Vec (cheap since MatchIndices is Vec<usize>),\n        // but avoid an extra clone by using `to_vec()` which reallocates once.\n        let out = indices_ref.to_vec();\n        Some((best_score, out))\n    } else {\n        Some((best_score, Vec::default()))\n    }\n}\n\n// ---------------------------------------------------------------------------\n// Range DP — full matrix, minimal traceback (begin + end only)\n// ---------------------------------------------------------------------------\n\n/// Full matrix DP followed by a traceback that only records the first and\n/// last matched positions (not every index). Used by `fuzzy_match_range` to\n/// avoid allocating and populating the full index vec when only the span is\n/// needed.\n#[allow(clippy::too_many_lines)]\npub(super) fn range_dp<const ALLOW_TYPOS: bool, C: Atom>(\n    cho: &[C],\n    pat: &[C],\n    bonuses: &[Score],\n    respect_case: bool,\n    full_buf: &ThreadLocal<RefCell<SWMatrix>>,\n    use_last_match: bool,\n) -> Option<(Score, usize, usize)> {\n    let n = pat.len();\n    let m = cho.len();\n\n    let banding = compute_banding::<ALLOW_TYPOS, C>(pat, cho, respect_case)?;\n    let j_start = banding.j_first;\n    let col_off = j_start - 1;\n    let mcols = m - col_off + 1;\n\n    let mut buf = full_buf\n        .get_or(|| RefCell::new(SWMatrix::zero(n + 1, mcols)))\n        .borrow_mut();\n    buf.resize(n + 1, mcols);\n\n    let base_ptr = buf.data.as_mut_ptr();\n    let cols = buf.cols;\n\n    // Initialize row 0 to CELL_ZERO (all-zero bytes: score=0, dir=None=0).\n    // Column 0 of each subsequent row is also CELL_ZERO.\n    // SAFETY: base_ptr points to a valid allocation of (n+1)*cols Cells.\n    unsafe {\n        std::ptr::write_bytes(base_ptr, 0, mcols);\n        for i in 1..=n {\n            *base_ptr.add(i * cols) = CELL_ZERO;\n        }\n    }\n\n    let (row_lo_arr, row_hi_arr) = if ALLOW_TYPOS {\n        ([0usize; MAX_PAT_LEN], [0usize; MAX_PAT_LEN])\n    } else {\n        let (lo, hi) = banding.row_bounds.as_ref().unwrap();\n        (*lo, *hi)\n    };\n\n    let cho_ptr = cho.as_ptr();\n    let bonuses_ptr = bonuses.as_ptr();\n    let mut dead_rows = 0u32;\n\n    for i in 1..=n {\n        let pi = pat[i - 1];\n        let is_first = i == 1;\n\n        let (j_lo, j_hi) = if ALLOW_TYPOS {\n            typo_vband_row(i, m, banding.bandwidth, banding.j_first)\n        } else {\n            (row_lo_arr[i - 1], row_hi_arr[i - 1])\n        };\n        let j_lo = j_lo.max(j_start);\n\n        if j_lo > j_hi || j_lo > m {\n            if i < n {\n                let (nj_lo, nj_hi) = if ALLOW_TYPOS {\n                    typo_vband_row(i + 1, m, banding.bandwidth, banding.j_first)\n                } else {\n                    (row_lo_arr[i], row_hi_arr[i])\n                };\n                let nj_lo = nj_lo.max(j_start);\n                if nj_lo <= nj_hi && nj_lo <= m {\n                    let next_mat_lo = nj_lo - col_off;\n                    let next_mat_hi = (nj_hi - col_off).min(mcols - 1);\n                    let zero_lo = next_mat_lo.saturating_sub(1);\n                    let zero_hi = next_mat_hi.min(mcols - 1);\n                    unsafe {\n                        let row_ptr = base_ptr.add(i * cols);\n                        for k in zero_lo..=zero_hi {\n                            *row_ptr.add(k) = CELL_ZERO;\n                        }\n                    }\n                }\n            }\n            dead_rows += 1;\n            if dead_rows >= 2 {\n                return None;\n            }\n            continue;\n        }\n\n        let mat_col_lo = j_lo - col_off;\n        let mat_col_hi = j_hi - col_off;\n        let jm_max = mcols - 1;\n\n        unsafe {\n            let row_ptr = base_ptr.add(i * cols);\n            if mat_col_lo > 1 {\n                *row_ptr.add(mat_col_lo - 1) = CELL_ZERO;\n            }\n            if mat_col_hi < jm_max {\n                *row_ptr.add(mat_col_hi + 1) = CELL_ZERO;\n            }\n        }\n\n        let (prev_row, cur_row) = unsafe {\n            let pr = std::slice::from_raw_parts(base_ptr.add((i - 1) * cols), cols);\n            let cr = std::slice::from_raw_parts_mut(base_ptr.add(i * cols), cols);\n            (pr, cr)\n        };\n\n        let prev_ptr = prev_row.as_ptr();\n        let cur_ptr = cur_row.as_mut_ptr();\n\n        let mut row_positive = false;\n        for j in j_lo..=j_hi {\n            let jm = j - col_off;\n            let cj = unsafe { *cho_ptr.add(j - 1) };\n            let is_match = pi.eq(cj, respect_case);\n\n            let diag_cell = unsafe { *prev_ptr.add(jm - 1) };\n            let up_score = if ALLOW_TYPOS {\n                let up_cell = unsafe { *prev_ptr.add(jm) };\n                up_cell.score()\n            } else {\n                0\n            };\n            let left_cell = unsafe { *cur_ptr.add(jm - 1) };\n\n            let (best, dir) = compute_cell::<ALLOW_TYPOS>(\n                is_match,\n                is_first,\n                unsafe { *bonuses_ptr.add(j - 1) },\n                diag_cell.score(),\n                diag_cell.is_diag(),\n                up_score,\n                left_cell.score(),\n                left_cell.is_diag(),\n            );\n\n            row_positive |= best > 0;\n            unsafe {\n                *cur_ptr.add(jm) = Cell::new(best, dir);\n            }\n        }\n\n        if row_positive {\n            dead_rows = 0;\n        } else {\n            dead_rows += 1;\n            if dead_rows >= 2 {\n                return None;\n            }\n        }\n    }\n\n    // Find best score in the last row.\n    let mut best_score: Score = 0;\n    let mut best_j = 0usize;\n    {\n        let (last_j_lo, last_j_hi) = if ALLOW_TYPOS {\n            typo_vband_row(n, m, banding.bandwidth, banding.j_first)\n        } else {\n            (row_lo_arr[n - 1], row_hi_arr[n - 1])\n        };\n        let last_j_lo = last_j_lo.max(j_start);\n        if last_j_lo <= last_j_hi && last_j_lo <= m {\n            let last_row_ptr = unsafe { base_ptr.add(n * cols) };\n            for j in last_j_lo..=last_j_hi {\n                let jm = j - col_off;\n                let s = unsafe { (*last_row_ptr.add(jm)).score() };\n                // When use_last_match: `>=` keeps overwriting best_j so the\n                // rightmost tied column wins. Otherwise strict `>` means the\n                // leftmost (first) column with the best score wins.\n                let better = if use_last_match {\n                    s >= best_score && s > 0\n                } else {\n                    s > best_score\n                };\n                best_score = if better { s } else { best_score };\n                best_j = if better { j } else { best_j };\n            }\n        }\n    }\n\n    if best_score <= 0 {\n        return None;\n    }\n\n    // Minimal traceback: walk back until we can go no further, recording\n    // only the final j (which becomes `begin`). `end` is best_j - 1.\n    let end_0 = best_j - 1; // 0-indexed end\n    let mut i = n;\n    let mut j = best_j;\n    let mut true_matches = 0usize;\n\n    while i > 0 && j >= j_start {\n        let jm = j - col_off;\n        let cell_val = unsafe { *base_ptr.add(i * cols).add(jm) };\n        match cell_val.dir() {\n            Dir::Diag => {\n                if pat[i - 1].eq(cho[j - 1], respect_case) {\n                    true_matches += 1;\n                }\n                i -= 1;\n                j -= 1;\n            }\n            Dir::Up => {\n                i -= 1;\n            }\n            Dir::Left => {\n                j -= 1;\n            }\n            Dir::None => break,\n        }\n    }\n\n    if true_matches < banding.min_true_matches {\n        return None;\n    }\n\n    // `j` after traceback is one step before the first matched column;\n    // the first match is at `j` (0-indexed: `j` since j is 1-indexed here\n    // but we stepped past it). We need the earliest index that was recorded.\n    // After the loop, j points to the column just before the alignment start,\n    // so begin = j (0-indexed) because the first Diag step decremented j before\n    // breaking. Re-scan the last row of the traceback to find begin precisely:\n    // We track the last diagonal j we visited.\n    let begin_0 = j; // j is 1-indexed after the last decrement; 0-indexed = j\n\n    Some((best_score, begin_0, end_0))\n}\n"
  },
  {
    "path": "src/fuzzy_matcher/arinae/atom.rs",
    "content": "//! Byte/Char helpers\nuse super::Score;\nuse super::constants::SEPARATOR_TABLE;\nuse memchr::memchr;\n\npub(super) trait Atom: PartialEq + Into<char> + Copy {\n    #[inline(always)]\n    fn eq(self, other: Self, respect_case: bool) -> bool\n    where\n        Self: PartialEq + Sized,\n    {\n        if respect_case {\n            self == other\n        } else {\n            self.eq_ignore_case(other)\n        }\n    }\n    fn eq_ignore_case(self, other: Self) -> bool;\n    fn is_lowercase(self) -> bool;\n\n    /// Return the index of the first occurrence of `self` in `haystack`,\n    /// or `None` if not found.\n    ///\n    /// Implementations may override this with a SIMD-backed search (e.g.\n    /// `memchr` for `u8` in case-sensitive mode).\n    #[inline(always)]\n    fn find_first_in(self, haystack: &[Self], respect_case: bool) -> Option<usize> {\n        haystack.iter().position(|&c| self.eq(c, respect_case))\n    }\n    /// Return the word-separator bonus for this character, or `0` if it is not\n    /// a separator.  Uses a table lookup — a single bounds check replaces\n    /// several branches and the returned value encodes both *whether* the\n    /// character is a separator and *how much* bonus it carries.\n    #[inline(always)]\n    fn separator_bonus(self) -> Score {\n        let ch = self.into() as usize;\n        // For ch < 128 we do a table lookup; for ch >= 128 we return 0.\n        // The `get` returns None for out-of-range, and `copied().unwrap_or(0)` is\n        // typically compiled as a conditional move (branchless).\n        SEPARATOR_TABLE.get(ch).copied().unwrap_or(0)\n    }\n}\n\nimpl Atom for u8 {\n    #[inline(always)]\n    fn eq_ignore_case(self, b: Self) -> bool {\n        self.eq_ignore_ascii_case(&b)\n    }\n    #[inline(always)]\n    fn is_lowercase(self) -> bool {\n        self.is_ascii_lowercase()\n    }\n\n    /// Case-sensitive search uses SIMD-backed `memchr`; case-insensitive\n    /// falls back to the generic scalar loop.\n    #[inline(always)]\n    fn find_first_in(self, haystack: &[Self], respect_case: bool) -> Option<usize> {\n        if respect_case {\n            memchr(self, haystack)\n        } else {\n            // Case-insensitive: compare lowercase. Also try the uppercase variant\n            // so a single `memchr` can be used for each case variant.\n            let lo = self.to_ascii_lowercase();\n            let hi = self.to_ascii_uppercase();\n            if lo == hi {\n                // No case distinction for this byte (digit, symbol, etc.).\n                memchr(lo, haystack)\n            } else {\n                // Check both variants and return the earliest occurrence.\n                let p_lo = memchr(lo, haystack);\n                let p_hi = memchr(hi, haystack);\n                match (p_lo, p_hi) {\n                    (None, x) | (x, None) => x,\n                    (Some(a), Some(b)) => Some(a.min(b)),\n                }\n            }\n        }\n    }\n}\nimpl Atom for char {\n    #[inline(always)]\n    fn eq_ignore_case(self, b: Self) -> bool {\n        self.to_lowercase().eq(b.to_lowercase())\n    }\n    #[inline(always)]\n    fn is_lowercase(self) -> bool {\n        self.is_lowercase()\n    }\n}\n"
  },
  {
    "path": "src/fuzzy_matcher/arinae/banding.rs",
    "content": "//! Banding utils\n//! Banding is the process of calculating the pertinent parts of the matrix to our specific\n//! computation to avoid computing every cell\n\nuse super::atom::Atom;\nuse super::constants::{MAX_PAT_LEN, TYPO_BAND_SLACK};\nuse super::helpers::{compute_last_match_cols, compute_row_col_bounds, find_first_char};\n\n/// Precomputed banding information shared by both score-only and full DP.\npub(super) struct BandingInfo {\n    /// Per-row column bounds (only present in exact mode).\n    pub(super) row_bounds: Option<([usize; MAX_PAT_LEN], [usize; MAX_PAT_LEN])>,\n    /// 1-indexed column of the first match of `pat[0]` in `cho`.\n    pub(super) j_first: usize,\n    /// Bandwidth for typo-mode diagonal banding (0 in exact mode).\n    pub(super) bandwidth: usize,\n    /// Minimum number of true (non-substitution) matches to accept.\n    pub(super) min_true_matches: usize,\n}\n\n/// Compute banding information for the DP. Returns `None` if the pattern\n/// cannot possibly match (e.g. a pattern character has no occurrence).\npub(super) fn compute_banding<const ALLOW_TYPOS: bool, C: Atom>(\n    pat: &[C],\n    cho: &[C],\n    respect_case: bool,\n) -> Option<BandingInfo> {\n    let n = pat.len();\n    let m = cho.len();\n    let row_bounds;\n    let j_first;\n\n    if ALLOW_TYPOS {\n        j_first = find_first_char(pat, cho, respect_case)?;\n        row_bounds = None;\n    } else {\n        let fm = compute_first_match_cols(pat, cho, respect_case)?;\n        let lm = compute_last_match_cols(pat, cho, respect_case)?;\n        j_first = fm[0];\n        row_bounds = Some(compute_row_col_bounds(n, m, &fm, &lm));\n    }\n\n    let bandwidth = if ALLOW_TYPOS { n + TYPO_BAND_SLACK } else { 0 };\n    let min_true_matches = if ALLOW_TYPOS { n.div_ceil(2) } else { 0 };\n\n    Some(BandingInfo {\n        row_bounds,\n        j_first,\n        bandwidth,\n        min_true_matches,\n    })\n}\n\n/// Row-major V-shaped band: compute column bounds at row `i`.\n///\n/// The result is an upper triangle starting at the diagonal (j ~ i + `j_first` - 1)\n#[inline(always)]\npub(super) fn typo_vband_row(i: usize, m: usize, bandwidth: usize, j_first: usize) -> (usize, usize) {\n    let j = i + j_first - 1;\n    let lo = j.saturating_sub(bandwidth).max(j_first);\n\n    (lo, m)\n}\n\n/// For exact (non-typo) mode, compute the earliest column (1-indexed) at which\n/// each pattern character can first be matched. This tightens the diagonal\n/// lower bound so we never compute cells that cannot participate in a valid\n/// alignment.\n///\n/// Returns `None` if any pattern character has no match in the choice (the\n/// subsequence check should have caught this, but we guard anyway).\nfn compute_first_match_cols<C: Atom>(pat: &[C], cho: &[C], respect_case: bool) -> Option<[usize; MAX_PAT_LEN]> {\n    let n = pat.len();\n    // Patterns longer than MAX_PAT_LEN cannot be handled by the stack-allocated\n    // banding arrays.  Return None so the caller skips this choice gracefully.\n    if n > MAX_PAT_LEN {\n        return None;\n    }\n    let mut first = [0usize; MAX_PAT_LEN];\n    let mut start = 0usize; // search from this choice index onward\n    for i in 0..n {\n        let found = cho[start..].iter().position(|&c| pat[i].eq(c, respect_case));\n        match found {\n            Some(pos) => {\n                first[i] = start + pos + 1; // 1-indexed column\n                start = start + pos + 1; // next char must be strictly after\n            }\n            None => return None,\n        }\n    }\n    Some(first)\n}\n"
  },
  {
    "path": "src/fuzzy_matcher/arinae/constants.rs",
    "content": "// ---------------------------------------------------------------------------\n// Scoring constants\n// ---------------------------------------------------------------------------\nuse super::Score;\n\n/// Points awarded for each correctly matched character.\npub(super) const MATCH_BONUS: Score = 18;\n\n/// Extra bonus when the match is at position 0 of the choice string.\npub(super) const START_OF_STRING_BONUS: Score = 16;\n\n/// Extra bonus for a camelCase transition.\npub(super) const CAMEL_CASE_BONUS: Score = 6;\n\n/// Bonus for each additional consecutive matched character.\npub(super) const CONSECUTIVE_BONUS: Score = 11;\n\n/// Cost to open a gap (skip characters in choice).\npub(super) const GAP_OPEN: Score = 6;\n\n/// Cost to extend a gap by one more character.\npub(super) const GAP_EXTEND: Score = 4;\n\npub(super) const TYPO_PENALTY: Score = 10;\n\n/// Penalty for aligning a pattern char to a different choice char (typos only).\npub(super) const MISMATCH_PENALTY: Score = 16;\n\n/// Maximum pattern length supported by the banding arrays (stack-allocated).\npub(super) const MAX_PAT_LEN: usize = 32;\n\n/// Bandwidth for typo-mode banding. In typo mode we allow diagonal moves\n/// (match/mismatch) plus UP (skip pattern char) and LEFT (skip choice char),\n/// so the optimal path can wander off the main diagonal. A bandwidth of\n/// `n + TYPO_BAND_SLACK` columns around the diagonal is generous enough\n/// to capture all viable alignments while still pruning far-off cells.\npub(super) const TYPO_BAND_SLACK: usize = 4;\n\n/// Per-separator bonus lookup table. Each entry holds the `Score` awarded when\n/// a matched character immediately follows that ASCII codepoint. Non-separator\n/// characters (and all non-ASCII codepoints) map to `0`.\n///\n/// Different separators can carry different bonuses — for example, `/` and `\\`\n/// delimit path components (high bonus), while `_` or `-` delimit sub-words\n/// (standard bonus).  Entries that are `0` are not considered separators.\npub(super) const SEPARATOR_TABLE: [Score; 128] = {\n    let mut t = [0 as Score; 128];\n    t[b' ' as usize] = 16; // space\n    t[b'-' as usize] = 10; // hyphen / kebab-case\n    t[b'.' as usize] = 12; // dot (file extensions, domain names)\n    t[b'/' as usize] = 16; // forward slash (path separator — higher bonus)\n    t[b'\\\\' as usize] = 16; // backslash (Windows path separator — higher bonus)\n    t[b'_' as usize] = 12; // underscore / snake_case\n    t\n};\n"
  },
  {
    "path": "src/fuzzy_matcher/arinae/helpers.rs",
    "content": "//! &[dyn Atom] manipulation helpers\n\nuse super::Atom;\nuse super::constants::MAX_PAT_LEN;\n\n/// Find the 1-indexed column of the first occurrence of `pat[0]` in `cho`.\n///\n/// Returns `None` if `pat[0]` is not found anywhere (caller should return\n/// `None`). The position defines the start of the V-shaped banding envelope.\n/// Uses SIMD-backed `find_first_in` for `u8` slices.\n#[inline(always)]\npub(super) fn find_first_char<C: Atom>(pat: &[C], cho: &[C], respect_case: bool) -> Option<usize> {\n    pat[0].find_first_in(cho, respect_case).map(|idx| idx + 1) // 1-indexed\n}\n\n/// Compute the last column (1-indexed) at which each pattern character can be\n/// matched, scanning from the end. Used to tighten the diagonal upper bound.\npub(super) fn compute_last_match_cols<C: Atom>(\n    pat: &[C],\n    cho: &[C],\n    respect_case: bool,\n) -> Option<[usize; MAX_PAT_LEN]> {\n    let n = pat.len();\n    // Patterns longer than MAX_PAT_LEN cannot be handled by the stack-allocated\n    // banding arrays.  Return None so the caller skips this choice gracefully.\n    if n > MAX_PAT_LEN {\n        return None;\n    }\n    let m = cho.len();\n    let mut last = [0usize; MAX_PAT_LEN];\n    let mut end = m; // search up to this choice index (exclusive)\n    for i in (0..n).rev() {\n        let found = cho[..end].iter().rposition(|&c| pat[i].eq(c, respect_case));\n        match found {\n            Some(pos) => {\n                last[i] = pos + 1; // 1-indexed column\n                end = pos; // previous char must be strictly before\n            }\n            None => return None,\n        }\n    }\n    Some(last)\n}\n\n/// For the **row-major** full DP (outer loop over rows), compute per-row\n/// column bounds `(j_lo, j_hi)` accounting for cross-row Diag reads.\n///\n/// Row `i` (1-indexed) matches pattern char `i-1`. The Diag move at\n/// `(i, j)` reads `buf[i-1][j-1]`, so row `i-1` must have computed\n/// column `j-1`. We expand each row's upper bound to satisfy the next\n/// row's lower-bound Diag dependency, and each row's lower bound to\n/// satisfy the previous row's upper-bound Diag dependency.\npub(super) fn compute_row_col_bounds(\n    n: usize,\n    m: usize,\n    first_match: &[usize; MAX_PAT_LEN],\n    last_match: &[usize; MAX_PAT_LEN],\n) -> ([usize; MAX_PAT_LEN], [usize; MAX_PAT_LEN]) {\n    let mut lo = [0usize; MAX_PAT_LEN];\n    let mut hi = [0usize; MAX_PAT_LEN];\n\n    // Start with the raw first/last match bounds.\n    lo[..n].copy_from_slice(&first_match[..n]);\n    hi[..n].copy_from_slice(&last_match[..n]);\n\n    // Forward pass: row i's upper bound must extend so that row i+1 can\n    // read Diag at (i+1, j_lo[i+1]) → needs buf[i][j_lo[i+1]-1].\n    // Also, LEFT propagation within row i+1 starts at j_lo[i+1], but\n    // score flows from row i via Diag, so row i must reach j_lo[i+1]-1.\n    for i in 0..n.saturating_sub(1) {\n        let next_lo = lo[i + 1];\n        if next_lo > 1 {\n            hi[i] = hi[i].max(next_lo - 1);\n        }\n    }\n\n    // Backward pass: row i's lower bound can't be later than row i-1's\n    // upper bound + 1 (Diag from (i-1, hi[i-1]) can reach (i, hi[i-1]+1)).\n    // This is rarely binding but ensures consistency.\n    for i in 1..n {\n        lo[i] = lo[i].min(hi[i - 1] + 1);\n    }\n\n    // Clamp to valid range.\n    for i in 0..n {\n        lo[i] = lo[i].max(1).min(m);\n        hi[i] = hi[i].max(lo[i]).min(m);\n    }\n\n    (lo, hi)\n}\n"
  },
  {
    "path": "src/fuzzy_matcher/arinae/matrix.rs",
    "content": "//! Base structs for the matching algorithm: Cell & `SWMatrix`\n\nuse super::Score;\n\n/// Direction the optimal path took to reach a cell.\n#[derive(Clone, Copy, Debug, PartialEq, Eq)]\n#[repr(u8)]\n#[allow(dead_code)] // variants are constructed via transmute from bits\npub(super) enum Dir {\n    /// No valid path (score == 0).\n    ///\n    /// Assigned tag 0 so that `Cell::new(0, Dir::None)` encodes as all-zero\n    /// bits, allowing boundary rows/columns to be bulk-zeroed with\n    /// `write_bytes(0)` instead of a scalar loop.\n    None = 0,\n    /// Diagonal: match or mismatch (came from [i-1][j-1])\n    Diag = 1,\n    /// Up: gap in choice (came from [i-1][j], skip pattern char)\n    Up = 2,\n    /// Left: gap in pattern (came from [i][j-1], skip choice char)\n    Left = 3,\n}\n\n/// Packed cell stored as a `u32`: bits [15:0] = score (as u16 bitcast from\n/// i16), bits [17:16] = direction tag.  This gives 4 bytes per cell with no\n/// padding and enables branchless direction extraction via bitmask.\n#[derive(Copy, Clone)]\npub(super) struct Cell(u32);\n\npub(super) const CELL_ZERO: Cell = Cell::new(0, Dir::None);\n\nimpl std::fmt::Debug for Cell {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        f.debug_struct(\"Cell\")\n            .field(\"score\", &self.score())\n            .field(\"dir\", &self.dir())\n            .finish()\n    }\n}\n\nimpl Cell {\n    #[inline(always)]\n    pub(super) const fn new(score: Score, dir: Dir) -> Cell {\n        // Store score as u16 bits in low 16 bits, dir in bits 16-17.\n        Cell((score.cast_unsigned() as u32) | ((dir as u32) << 16))\n    }\n    #[inline(always)]\n    pub(super) fn score(self) -> Score {\n        // Truncation is intentional: low 16 bits store the score as a bitcast i16.\n        #[allow(clippy::cast_possible_truncation)]\n        let low16 = self.0 as u16;\n        low16.cast_signed()\n    }\n    #[inline(always)]\n    pub(super) fn dir(self) -> Dir {\n        // SAFETY: Dir has repr(u8) with values 0..=3 and we only ever store\n        // valid Dir values in bits 16-17. Truncation from u32 to u8 is intentional.\n        #[allow(clippy::cast_possible_truncation)]\n        let tag = (self.0 >> 16) as u8 & 0x3;\n        unsafe { std::mem::transmute(tag) }\n    }\n    /// Branchless check: true when dir == Diag (tag 1).\n    #[inline(always)]\n    pub(super) fn is_diag(self) -> bool {\n        (self.0 >> 16) & 0x3 == 1\n    }\n}\n\n#[derive(Default, Debug)]\npub(super) struct SWMatrix {\n    pub(super) data: Vec<Cell>,\n    pub(super) cols: usize,\n    pub(super) rows: usize,\n}\n\nimpl SWMatrix {\n    pub fn zero(rows: usize, cols: usize) -> Self {\n        let mut res = SWMatrix::default();\n        res.resize(rows, cols);\n        res\n    }\n    pub fn resize(&mut self, rows: usize, cols: usize) {\n        let needed = rows * cols;\n        if needed > self.data.len() {\n            self.data.resize(needed, CELL_ZERO);\n        }\n        self.rows = rows;\n        self.cols = cols;\n    }\n}\n"
  },
  {
    "path": "src/fuzzy_matcher/arinae/mod.rs",
    "content": "//! Arinae fuzzy matching algorithm.\n//!\n//! Uses a Smith-Waterman local alignment approach with affine gap penalties\n//! and context-sensitive bonuses.\n//!\n//! ## Key design choices\n//!\n//! - **Single score per cell** (u16 saturating) plus a 2-bit direction tag\n//!   for traceback. Gap open vs extend is tracked via the direction tag.\n//! - **Semi-global alignment**: the pattern must be fully consumed, but\n//!   alignment can start/end at any position in the choice string.\n//!\n//!\n//! ## Pruning strategies\n//!\n//! - **Row-range banding**: each DP cell is only computed when the row/column\n//!   pair falls within the feasible alignment band. In exact mode the band is\n//!   derived from precomputed first/last match columns for each pattern\n//!   character; in typo mode a diagonal ± bandwidth envelope is used.\n//! - **Interpair max-score pruning**: after processing a column (score-only)\n//!   or row (full DP), if all cells are zero for several consecutive\n//!   iterations, the alignment is dead and we terminate early.\n\n#![allow(clippy::inline_always)]\n\nmod algo;\nmod atom;\nmod banding;\nmod constants;\nmod helpers;\nmod matrix;\nmod prefilter;\n#[cfg(test)]\nmod tests;\n\nuse std::cell::RefCell;\n\nuse thread_local::ThreadLocal;\n\nuse self::algo::{full_dp, range_dp};\nuse self::atom::Atom;\nuse self::constants::{CAMEL_CASE_BONUS, START_OF_STRING_BONUS};\nuse self::prefilter::cheap_typo_prefilter;\n\nuse self::matrix::{CELL_ZERO, Cell, Dir, SWMatrix};\nuse crate::{\n    CaseMatching,\n    fuzzy_matcher::{FuzzyMatcher, MatchIndices, ScoreType},\n};\n\ntype Score = i16;\n\nfn precompute_bonuses<C: Atom>(cho: &[C], buf: &mut Vec<Score>) {\n    // Reset length (O(1), no deallocation) then fill with fresh values.\n    buf.clear();\n    // The first character always gets START_OF_STRING_BONUS.\n    // Subsequent characters get a bonus based on the previous character:\n    //   - separator_bonus() when the previous char is a separator (the exact\n    //     bonus depends on the separator — see SEPARATOR_TABLE in constants.rs),\n    //   - CAMEL_CASE_BONUS when transitioning from lowercase to non-lowercase.\n    // Using a safe iterator lets the compiler auto-vectorise the loop.\n    let bonus_iter = std::iter::once(START_OF_STRING_BONUS).chain(cho.windows(2).map(|w| {\n        let prev = w[0];\n        let cur = w[1];\n        prev.separator_bonus() + CAMEL_CASE_BONUS * Score::from(prev.is_lowercase() && !cur.is_lowercase())\n    }));\n    buf.extend(bonus_iter);\n}\n\n/// Arinae fuzzy matcher: Smith-Waterman local alignment with affine gap\n/// penalties and context-sensitive bonuses.\n#[derive(Debug, Default)]\npub struct ArinaeMatcher {\n    pub(crate) case: CaseMatching,\n    pub(crate) allow_typos: bool,\n    pub(crate) use_last_match: bool,\n\n    full_buf: ThreadLocal<RefCell<SWMatrix>>,\n    indices_buf: ThreadLocal<RefCell<MatchIndices>>,\n    #[allow(clippy::type_complexity)]\n    char_buf: ThreadLocal<RefCell<(Vec<char>, Vec<char>)>>,\n    bonus_buf: ThreadLocal<RefCell<Vec<Score>>>,\n}\n\nimpl ArinaeMatcher {\n    /// Create a new `ArinaeMatcher` with the given settings.\n    #[must_use]\n    pub fn new(case: CaseMatching, allow_typos: bool, use_last_match: bool) -> Self {\n        Self {\n            case,\n            allow_typos,\n            use_last_match,\n            ..Default::default()\n        }\n    }\n\n    #[inline(always)]\n    fn respect_case<C: Atom>(&self, pattern: &[C]) -> bool {\n        self.case == CaseMatching::Respect\n            || (self.case == CaseMatching::Smart && !pattern.iter().all(|b| b.is_lowercase()))\n    }\n\n    /// Dispatch to `full_dp` with the appropriate const generics.\n    /// Assumes prefilters and bonuses have already been computed.\n    fn dispatch_dp<C: Atom>(\n        &self,\n        cho: &[C],\n        pat: &[C],\n        bonuses: &[Score],\n        respect_case: bool,\n        compute_indices: bool,\n    ) -> Option<(ScoreType, MatchIndices)> {\n        #[rustfmt::skip]\n        let res = match (self.allow_typos, compute_indices) {\n            (true, true)   => full_dp::<true , true , _>(cho, pat, bonuses, respect_case, &self.full_buf, &self.indices_buf, self.use_last_match),\n            (true, false)  => full_dp::<true , false, _>(cho, pat, bonuses, respect_case, &self.full_buf, &self.indices_buf, self.use_last_match),\n            (false, true)  => full_dp::<false, true , _>(cho, pat, bonuses, respect_case, &self.full_buf, &self.indices_buf, self.use_last_match),\n            (false, false) => full_dp::<false, false, _>(cho, pat, bonuses, respect_case, &self.full_buf, &self.indices_buf, self.use_last_match),\n        };\n        res.map(|(s, idx)| (ScoreType::from(s), idx))\n    }\n\n    /// Generic helper: run full DP over slices of Atom.\n    /// If `compute_indices` is true, returns the matched indices; otherwise\n    /// returns a single-element vec containing the 1-indexed end column.\n    fn match_slices<C: Atom>(&self, cho: &[C], pat: &[C], compute_indices: bool) -> Option<(ScoreType, MatchIndices)> {\n        if pat.is_empty() {\n            return Some((0, MatchIndices::new()));\n        }\n        if cho.is_empty() {\n            return None;\n        }\n\n        let respect_case = self.respect_case(pat);\n\n        // Prefilter for typo mode.\n        // In exact mode (non-typo) we skip is_subsequence here: compute_banding\n        // calls compute_first_match_cols which already validates the subsequence\n        // and returns None if any pattern character is absent — no redundant scan.\n        if self.allow_typos && !cheap_typo_prefilter(pat, cho, respect_case) {\n            return None;\n        }\n\n        // Prepare bonuses\n        let mut bonus_buf = self.bonus_buf.get_or(|| RefCell::new(Vec::new())).borrow_mut();\n        precompute_bonuses(cho, &mut bonus_buf);\n\n        self.dispatch_dp(cho, pat, &bonus_buf, respect_case, compute_indices)\n    }\n\n    fn run(&self, choice: &str, pattern: &str, compute_indices: bool) -> Option<(ScoreType, MatchIndices)> {\n        if pattern.is_empty() {\n            return Some((0, MatchIndices::new()));\n        }\n        if choice.is_empty() {\n            return None;\n        }\n\n        // Fast path for ASCII matching\n        if choice.is_ascii() && pattern.is_ascii() {\n            let cho = choice.as_bytes();\n            let pat = pattern.as_bytes();\n            return self.match_slices(cho, pat, compute_indices);\n        }\n\n        let mut bufs = self\n            .char_buf\n            .get_or(|| RefCell::new((Vec::new(), Vec::new())))\n            .borrow_mut();\n        let (ref mut pat_buf, ref mut cho_buf) = *bufs;\n        pat_buf.clear();\n        pat_buf.extend(pattern.chars());\n        cho_buf.clear();\n        cho_buf.extend(choice.chars());\n\n        let respect_case = self.respect_case(pat_buf);\n\n        // Prefilter for typo mode only (see match_slices for rationale).\n        if self.allow_typos && !cheap_typo_prefilter(pat_buf, cho_buf, respect_case) {\n            return None;\n        }\n\n        let mut bonus_buf = self.bonus_buf.get_or(|| RefCell::new(Vec::new())).borrow_mut();\n        precompute_bonuses(cho_buf, &mut bonus_buf);\n\n        // Call dispatch_dp directly to avoid double-borrowing bonus_buf.\n        self.dispatch_dp(cho_buf, pat_buf, &bonus_buf, respect_case, compute_indices)\n    }\n\n    /// Run the DP and return `(score, begin, end)` without collecting all indices.\n    ///\n    /// Uses the full matrix (for traceback) but only records the first and last\n    /// matched columns instead of the full index list. Avoids the allocation and\n    /// work of `fuzzy_indices` when only the range is needed.\n    fn run_range(&self, choice: &str, pattern: &str) -> Option<(ScoreType, usize, usize)> {\n        if pattern.is_empty() {\n            return Some((0, 0, 0));\n        }\n        if choice.is_empty() {\n            return None;\n        }\n\n        let range = if choice.is_ascii() && pattern.is_ascii() {\n            let cho = choice.as_bytes();\n            let pat = pattern.as_bytes();\n            let respect_case = self.respect_case(pat);\n            // Exact mode: compute_banding validates the subsequence implicitly.\n            if self.allow_typos && !cheap_typo_prefilter(pat, cho, respect_case) {\n                return None;\n            }\n            let mut bonus_buf = self.bonus_buf.get_or(|| RefCell::new(Vec::new())).borrow_mut();\n            precompute_bonuses(cho, &mut bonus_buf);\n            if self.allow_typos {\n                range_dp::<true, _>(cho, pat, &bonus_buf, respect_case, &self.full_buf, self.use_last_match)\n            } else {\n                range_dp::<false, _>(cho, pat, &bonus_buf, respect_case, &self.full_buf, self.use_last_match)\n            }\n        } else {\n            let mut bufs = self\n                .char_buf\n                .get_or(|| RefCell::new((Vec::new(), Vec::new())))\n                .borrow_mut();\n            let (ref mut pat_buf, ref mut cho_buf) = *bufs;\n            pat_buf.clear();\n            pat_buf.extend(pattern.chars());\n            cho_buf.clear();\n            cho_buf.extend(choice.chars());\n            let respect_case = self.respect_case(pat_buf);\n            // Exact mode: compute_banding validates the subsequence implicitly.\n            if self.allow_typos && !cheap_typo_prefilter(pat_buf, cho_buf, respect_case) {\n                return None;\n            }\n            let mut bonus_buf = self.bonus_buf.get_or(|| RefCell::new(Vec::new())).borrow_mut();\n            precompute_bonuses(cho_buf, &mut bonus_buf);\n            if self.allow_typos {\n                range_dp::<true, _>(\n                    cho_buf,\n                    pat_buf,\n                    &bonus_buf,\n                    respect_case,\n                    &self.full_buf,\n                    self.use_last_match,\n                )\n            } else {\n                range_dp::<false, _>(\n                    cho_buf,\n                    pat_buf,\n                    &bonus_buf,\n                    respect_case,\n                    &self.full_buf,\n                    self.use_last_match,\n                )\n            }\n        };\n        range.map(|(s, b, e)| (ScoreType::from(s), b, e))\n    }\n}\n\n// ---------------------------------------------------------------------------\n// FuzzyMatcher trait implementation\n// ---------------------------------------------------------------------------\n\nimpl FuzzyMatcher for ArinaeMatcher {\n    fn fuzzy_match(&self, choice: &str, pattern: &str) -> Option<ScoreType> {\n        let result = self.run(choice, pattern, false);\n        result.map(|x| x.0)\n    }\n\n    fn fuzzy_match_range(&self, choice: &str, pattern: &str) -> Option<(ScoreType, usize, usize)> {\n        self.run_range(choice, pattern)\n    }\n\n    fn fuzzy_indices(&self, choice: &str, pattern: &str) -> Option<(ScoreType, MatchIndices)> {\n        self.run(choice, pattern, true)\n    }\n}\n"
  },
  {
    "path": "src/fuzzy_matcher/arinae/prefilter.rs",
    "content": "//! Prefilters running before the algo to optimize performance on unmatchable items\n\nuse super::Atom;\nuse super::constants::MAX_PAT_LEN;\n\n/// Cheap prefilter for typo-tolerant matching.\n///\n/// Rejects choices that clearly cannot produce a positive score in the DP.\n/// The prefilter is intentionally lenient — false positives are fine (the DP\n/// will reject them), but false negatives lose valid matches.\n///\n/// Strategy:\n///   1. The first pattern character must appear somewhere in the choice at\n///      position `j_first` (anchoring the alignment).\n///   2. Of the remaining `n - 1` pattern characters, at least\n///      `floor((n - 1) / 2)` must also appear (unordered, as a multiset) in\n///      `choice[j_first..]` — the window the DP actually examines.\n///\n/// Scoping the tail check to `choice[j_first..]` is strictly correct: the\n/// typo-mode DP band starts at `j_first` for every row (bandwidth = n + 4\n/// always exceeds n - 1, so the left clamp always hits `j_first`). Any tail\n/// character that only exists before `j_first` can never contribute a true\n/// diagonal match in the DP; counting it would be a false positive.\n///\n/// We use a multiset frequency check rather than an ordered greedy scan.\n/// An ordered scan causes false negatives when a greedily-consumed character\n/// advances the cursor past positions where later characters could still match.\n///\n/// For the ASCII (`u8`) path the tail frequency table is built in a single\n/// O(m) sequential pass over the window, then queried in O(n). For the `char`\n/// path we fall back to a small O(n) linear-search table seeded from the\n/// tail, queried via a scalar scan of the window — still a single O(m) pass.\npub(super) fn cheap_typo_prefilter<C: Atom>(pattern: &[C], choice: &[C], respect_case: bool) -> bool {\n    let n = pattern.len();\n    let m = choice.len();\n\n    // A pattern much longer than the choice cannot match.\n    if n > m + 2 {\n        return false;\n    }\n\n    // The first pattern character must be present in the choice.\n    // Use the SIMD-backed find_first_in (memchr for u8, scalar for char).\n    // j_first is 0-indexed; the DP window is choice[j_first..].\n    let first = pattern[0];\n    let Some(j_first) = first.find_first_in(choice, respect_case) else {\n        return false;\n    };\n\n    if n == 1 {\n        return true;\n    }\n\n    let min_tail = (n - 1) / 2;\n    if min_tail == 0 {\n        return true;\n    }\n\n    // Tail frequency check scoped to choice[j_first..].\n    // Build a frequency table over the window in one pass, then consume\n    // entries as we walk the tail pattern characters.\n    let window = &choice[j_first..];\n    tail_freq_check(pattern, window, respect_case, min_tail)\n}\n\n/// Multiset frequency check: count how many of `pattern[1..]` can be\n/// satisfied (one-for-one) by characters in `window`, and return `true`\n/// as soon as `min_tail` matches are reached.\n///\n/// Builds a frequency table over `window` in a single O(|window|) pass,\n/// then walks the tail in O(n). Total cost: O(m + n) with a single\n/// sequential read of `window` — optimal cache behaviour.\n#[inline(always)]\nfn tail_freq_check<C: Atom>(pattern: &[C], window: &[C], respect_case: bool, min_tail: usize) -> bool {\n    // We need a per-character frequency table for the window.\n    // Use a small stack-allocated array of (char_value, count) pairs keyed on\n    // the PATTERN tail characters (at most MAX_PAT_LEN - 1 = 15 entries).\n    // We build it in two passes:\n    //   Pass 1 (O(n)): collect distinct tail chars into the table with count=0.\n    //   Pass 2 (O(m)): scan window and increment matching table entries.\n    //   Pass 3 (O(n)): walk the tail, decrement table entries, count matches.\n\n    const MAX_TAIL: usize = MAX_PAT_LEN - 1;\n    let tail = &pattern[1..];\n    let tail_len = tail.len().min(MAX_TAIL);\n\n    // Table of (pattern_char, available_count).  At most MAX_TAIL distinct chars.\n    // Seed every slot with the first tail char and count=0 so the array is fully\n    // initialised; only entries 0..table_len are ever consulted.\n    let placeholder = tail[0];\n    let mut table: [(C, u8); MAX_TAIL] = [(placeholder, 0); MAX_TAIL];\n    let mut table_len = 0usize;\n\n    // Pass 1: populate table with distinct tail chars (count = 0).\n    for &pi in &tail[..tail_len] {\n        if !table[..table_len].iter().any(|&(c, _)| pi.eq(c, respect_case)) {\n            table[table_len] = (pi, 0);\n            table_len += 1;\n        }\n    }\n\n    // Pass 2: scan window, increment table counts (saturate at 255).\n    for &c in window {\n        if let Some(entry) = table[..table_len]\n            .iter_mut()\n            .find(|(tc, _)| Atom::eq(*tc, c, respect_case))\n        {\n            entry.1 = entry.1.saturating_add(1);\n        }\n    }\n\n    // Pass 3: walk the tail, consume from table, count matches.\n    let mut matched = 0usize;\n    for &pi in &tail[..tail_len] {\n        if let Some(entry) = table[..table_len]\n            .iter_mut()\n            .find(|(tc, _)| Atom::eq(pi, *tc, respect_case))\n            && entry.1 > 0\n        {\n            entry.1 -= 1;\n            matched += 1;\n            if matched >= min_tail {\n                return true;\n            }\n        }\n    }\n\n    false\n}\n"
  },
  {
    "path": "src/fuzzy_matcher/arinae/tests.rs",
    "content": "use super::*;\nuse crate::fuzzy_matcher::FuzzyMatcher;\n\nfn matcher() -> ArinaeMatcher {\n    ArinaeMatcher::default()\n}\n\nfn matcher_typos() -> ArinaeMatcher {\n    ArinaeMatcher {\n        allow_typos: true,\n        ..Default::default()\n    }\n}\n\nfn score(choice: &str, pattern: &str) -> Option<i64> {\n    matcher().fuzzy_match(choice, pattern)\n}\n\nfn score_typos(choice: &str, pattern: &str) -> Option<i64> {\n    matcher_typos().fuzzy_match(choice, pattern)\n}\n\nfn indices(choice: &str, pattern: &str) -> Option<MatchIndices> {\n    matcher().fuzzy_indices(choice, pattern).map(|(_, v)| v)\n}\n\n// ----- Basic matching -----\n\n#[test]\nfn empty_pattern_always_matches() {\n    assert_eq!(score(\"anything\", \"\"), Some(0));\n    assert_eq!(score(\"\", \"\"), Some(0));\n}\n\n#[test]\nfn empty_choice_never_matches() {\n    assert!(score(\"\", \"a\").is_none());\n}\n\n#[test]\nfn exact_match_scores_positive() {\n    assert!(score(\"hello\", \"hello\").unwrap() > 0);\n}\n\n#[test]\nfn no_match_returns_none() {\n    assert!(score(\"abc\", \"xyz\").is_none());\n}\n\n#[test]\nfn subsequence_match() {\n    assert!(score(\"axbycz\", \"abc\").is_some());\n    let idx = indices(\"axbycz\", \"abc\").unwrap();\n    assert_eq!(idx.as_slice(), &[0, 2, 4]);\n}\n\n// ----- Scoring quality -----\n\n#[test]\nfn contiguous_beats_scattered() {\n    let contiguous = score(\"ab\", \"ab\").unwrap();\n    let scattered = score(\"axb\", \"ab\").unwrap();\n    assert!(\n        contiguous > scattered,\n        \"contiguous={contiguous} should beat scattered={scattered}\"\n    );\n}\n\n#[test]\nfn fewer_gaps_beats_more_gaps() {\n    let one_gap = score(\"abxc\", \"abc\").unwrap();\n    let two_gaps = score(\"axbxc\", \"abc\").unwrap();\n    assert!(one_gap > two_gaps, \"one_gap={one_gap} should beat two_gaps={two_gaps}\");\n}\n\n#[test]\nfn word_start_bonus() {\n    let boundary = score(\"src/reader.rs\", \"reader\").unwrap();\n    let stitched = score(\"src/tui/header.rs\", \"reader\").unwrap();\n    assert!(\n        boundary > stitched,\n        \"word-boundary={boundary} should beat stitched={stitched}\"\n    );\n}\n\n#[test]\nfn start_of_string_bonus() {\n    let at_start = score(\"abc\", \"a\").unwrap();\n    let at_mid = score(\"xabc\", \"a\").unwrap();\n    assert!(at_start > at_mid, \"start={at_start} should beat mid={at_mid}\");\n}\n\n#[test]\nfn consecutive_match_preferred() {\n    let consecutive = score(\"foobar\", \"oob\").unwrap();\n    let spread = score(\"oxoxb\", \"oob\").unwrap();\n    assert!(\n        consecutive > spread,\n        \"consecutive={consecutive} should beat spread={spread}\"\n    );\n}\n\n#[test]\nfn camel_case_bonus() {\n    let camel = score(\"FooBar\", \"fb\").unwrap();\n    let flat = score(\"foobar\", \"fb\").unwrap();\n    assert!(camel > flat, \"camel={camel} should beat flat={flat}\");\n}\n\n// ----- Case sensitivity -----\n\n#[test]\nfn smart_case_insensitive_lowercase_pattern() {\n    let m = ArinaeMatcher {\n        case: CaseMatching::Smart,\n        allow_typos: false,\n        ..Default::default()\n    };\n    assert!(m.fuzzy_match(\"FooBar\", \"foobar\").is_some());\n}\n\n#[test]\nfn smart_case_sensitive_uppercase_pattern() {\n    let m = ArinaeMatcher {\n        case: CaseMatching::Smart,\n        allow_typos: false,\n        ..Default::default()\n    };\n    assert!(m.fuzzy_match(\"foobar\", \"FooBar\").is_none());\n    assert!(m.fuzzy_match(\"FooBar\", \"FooBar\").is_some());\n}\n\n#[test]\nfn respect_case() {\n    let m = ArinaeMatcher {\n        case: CaseMatching::Respect,\n        allow_typos: false,\n        ..Default::default()\n    };\n    assert!(m.fuzzy_match(\"abc\", \"ABC\").is_none());\n    assert!(m.fuzzy_match(\"ABC\", \"ABC\").is_some());\n}\n\n#[test]\nfn ignore_case() {\n    let m = ArinaeMatcher {\n        case: CaseMatching::Ignore,\n        allow_typos: false,\n        ..Default::default()\n    };\n    assert!(m.fuzzy_match(\"abc\", \"ABC\").is_some());\n}\n\n// ----- Typo tolerance -----\n\n#[test]\nfn no_typos_rejects_mismatch() {\n    assert!(score(\"hxllo\", \"hello\").is_none());\n}\n\n#[test]\nfn typos_accepts_mismatch() {\n    assert!(score_typos(\"hxllo\", \"hello\").is_some());\n}\n\n#[test]\nfn no_typos_rejects_transposition() {\n    assert!(score(\"hlelo\", \"hello\").is_none());\n}\n\n#[test]\nfn typos_accepts_transposition() {\n    assert!(score_typos(\"hlelo\", \"hello\").is_some());\n}\n\n#[test]\nfn exact_match_same_with_and_without_typos() {\n    let with = score_typos(\"hello\", \"hello\").unwrap();\n    let without = score(\"hello\", \"hello\").unwrap();\n    assert_eq!(\n        with, without,\n        \"exact match score should be identical regardless of typo flag\"\n    );\n}\n\n#[test]\nfn typo_match_scores_less_than_exact() {\n    let exact = score_typos(\"hello\", \"hello\").unwrap();\n    let typo = score_typos(\"hxllo\", \"hello\").unwrap();\n    assert!(exact > typo, \"exact={exact} should beat typo={typo}\");\n}\n\n// ----- Traceback correctness -----\n\n#[test]\nfn indices_exact_match() {\n    let idx = indices(\"hello\", \"hello\").unwrap();\n    assert_eq!(idx.as_slice(), &[0, 1, 2, 3, 4]);\n}\n\n#[test]\nfn transposition_matches() {\n    let result = matcher_typos().fuzzy_indices(\"abdc\", \"abcd\");\n    assert!(result.is_some(), \"transposed input should match with typos\");\n    let (score_trans, _) = result.unwrap();\n\n    let (score_exact, _) = matcher_typos().fuzzy_indices(\"abcd\", \"abcd\").unwrap();\n    assert!(\n        score_exact > score_trans,\n        \"exact={score_exact} should beat transposed={score_trans}\"\n    );\n}\n\n// ----- Reader ranking regression -----\n\n#[test]\nfn reader_ranking() {\n    let pattern = \"reader\";\n    let dense = score(\"src/reader.rs\", pattern).unwrap();\n    let sparse = score(\n        \"tests/snapshots/normalize__insta_normalize_accented_item_unaccented_query.snap\",\n        pattern,\n    )\n    .unwrap_or(0);\n    assert!(dense > sparse, \"dense={dense} should beat sparse={sparse}\");\n}\n\n// ----- Ordering sanity -----\n\n#[test]\nfn ordering_ab() {\n    use crate::fuzzy_matcher::util::assert_order;\n    let m = ArinaeMatcher {\n        case: CaseMatching::Ignore,\n        allow_typos: false,\n        ..Default::default()\n    };\n    assert_order(&m, \"ab\", &[\"ab\", \"aoo_boo\", \"acb\"]);\n}\n\n#[test]\nfn ordering_print() {\n    use crate::fuzzy_matcher::util::assert_order;\n    let m = ArinaeMatcher {\n        case: CaseMatching::Ignore,\n        allow_typos: false,\n        ..Default::default()\n    };\n    assert_order(&m, \"print\", &[\"printf\", \"sprintf\"]);\n}\n\n// ----- Score-only vs full DP consistency -----\n\n#[test]\nfn score_only_matches_full_dp() {\n    let m = ArinaeMatcher {\n        case: CaseMatching::Ignore,\n        allow_typos: true,\n        ..Default::default()\n    };\n    let cases = [\n        (\"hello world\", \"hlo\"),\n        (\"src/reader.rs\", \"reader\"),\n        (\"FooBar\", \"fb\"),\n        (\"axbycz\", \"abc\"),\n        (\"hxllo\", \"hello\"),\n    ];\n    for (choice, pattern) in &cases {\n        let score_only = m.fuzzy_match(choice, pattern);\n        let full = m.fuzzy_indices(choice, pattern).map(|(s, _)| s);\n        assert_eq!(\n            score_only, full,\n            \"score mismatch for ({choice}, {pattern}): score_only={score_only:?} full={full:?}\"\n        );\n    }\n}\n\n// ----- Non-ASCII fallback -----\n\n#[test]\nfn non_ascii_matching() {\n    let m = matcher();\n    assert!(m.fuzzy_match(\"café\", \"café\").is_some());\n    assert!(m.fuzzy_match(\"naïve\", \"naive\").is_none());\n}\n\n// Regression test: all valid subsequences must be returned in --no-typos mode.\n// grep '.*t.*e.*s.*t' should give the same results as arinae with pattern 'test'.\n#[test]\nfn all_subsequences_must_match() {\n    let m = matcher();\n    let cases = [\n        // Bug 1: full_dp tracked best_j across all rows instead of only the\n        // last row, so traceback started at the wrong cell.\n        \"audio/audio/bin/temp/usr/uploads/mnt/cache/media_3445258\",\n        \"audio/audio/audio/docs/cache/temp/downloads/backup/shared/data_9591740\",\n        // Bug 2: min_true_matches was enforced in exact mode, but the true-count\n        // bookkeeping is corrupted by tiebreaking when a character coincidentally\n        // matches at a column where diag_score=0 (fresh local alignment start).\n        // In exact mode every row increment requires a true match, so score > 0\n        // at row n already guarantees n true matches; the threshold is not needed.\n        \"audio/audio/audio/opt/media/sys/sys/backup/etc_744357\",\n        \"audio/audio/audio/temp/shared/uploads/downloads/config/home/mnt_9037278\",\n        \"audio/audio/opt/cache/usr/usr/var/temp_1579492\",\n    ];\n    for choice in &cases {\n        assert!(\n            m.fuzzy_match(choice, \"test\").is_some(),\n            \"fuzzy_match should match subsequence 'test' in {choice:?}\",\n        );\n        assert!(\n            m.fuzzy_indices(choice, \"test\").is_some(),\n            \"fuzzy_indices should match subsequence 'test' in {choice:?}\",\n        );\n    }\n}\n#[test]\nfn score_and_full_dp_same() {\n    let cases = [(\"dist-workspace.toml\", \"tst\")];\n    let m = matcher_typos();\n    for (choice, pat) in cases {\n        assert_eq!(\n            m.fuzzy_indices(choice, pat).map(|(s, _)| s),\n            m.fuzzy_match_range(choice, pat).map(|(s, _, _)| s)\n        );\n    }\n}\n\n// Verify that fuzzy_match_range returns scores consistent with fuzzy_indices\n// and that begin/end are within the span of the full index list.\n#[test]\nfn range_consistent_with_indices() {\n    let cases = [\n        (\"hello\", \"hello\"),\n        (\"axbycz\", \"abc\"),\n        (\"src/reader.rs\", \"reader\"),\n        (\"FooBar\", \"fb\"),\n        (\"dist-workspace.toml\", \"tst\"),\n    ];\n    let matchers = [matcher(), matcher_typos()];\n    for m in &matchers {\n        for &(choice, pattern) in &cases {\n            let range = m.fuzzy_match_range(choice, pattern);\n            let full = m.fuzzy_indices(choice, pattern);\n            match (range, full) {\n                (None, None) => {}\n                (Some((rs, rb, re)), Some((fs, fidx))) => {\n                    assert_eq!(rs, fs, \"score mismatch for ({choice}, {pattern})\");\n                    let fbegin = fidx.first().copied().unwrap_or_default();\n                    let fend = fidx.last().copied().unwrap_or_default();\n                    assert_eq!(\n                        rb, fbegin,\n                        \"begin mismatch for ({choice}, {pattern}): range={rb} indices={fbegin}\"\n                    );\n                    assert_eq!(\n                        re, fend,\n                        \"end mismatch for ({choice}, {pattern}): range={re} indices={fend}\"\n                    );\n                }\n                _ => panic!(\"range/indices disagreement for ({choice}, {pattern})\"),\n            }\n        }\n    }\n}\n\n// ----- Prefilter regression tests -----\n\n/// Extending a typo-tolerant match with an additional character must not cause\n/// the candidate to be incorrectly rejected.\n///\n/// \"fobara\" matches `src/fuzzy_matcher/arinae/algo.rs` via the typo-tolerant\n/// DP (score 91). Typing one more character to form \"fobaral\" should continue\n/// to match — the `a`, `r`, `a` subsequence exists in the choice string and\n/// satisfies the prefilter threshold (`min_tail` = 3).\n///\n/// The old prefilter used a greedy ordered scan that consumed `o` at position 28,\n/// locking the cursor past all four `a` occurrences (at positions 11, 18, 22, 25),\n/// causing a false negative. The correct approach is an unordered frequency check.\n#[test]\nfn typo_prefilter_no_false_negative_on_extension() {\n    let choice = \"src/fuzzy_matcher/arinae/algo.rs\";\n    // Both the shorter and the extended pattern must match.\n    assert!(\n        score_typos(choice, \"fobara\").is_some(),\n        \"\\\"fobara\\\" should match \\\"{choice}\\\"\"\n    );\n    assert!(\n        score_typos(choice, \"fobaral\").is_some(),\n        \"\\\"fobaral\\\" should match \\\"{choice}\\\" (regression: greedy prefilter scan false negative)\"\n    );\n}\n\n#[test]\nfn use_last_match_prefers_later_occurrence() {\n    // \"man/man1/sk.1\" contains \"man\" at indices 0..=2 and again at 4..=6.\n    // With use_last_match=true, the matcher should highlight the second one.\n    let m = ArinaeMatcher {\n        use_last_match: true,\n        ..Default::default()\n    };\n    let (_, got) = m.fuzzy_indices(\"man/man1/sk.1\", \"man\").expect(\"should match\");\n    assert_eq!(got, vec![4, 5, 6], \"expected second 'man' (indices 4,5,6), got {got:?}\");\n}\n\n#[test]\nfn no_use_last_match_prefers_first_occurrence() {\n    // \"man/man1/sk.1\" contains \"man\" at indices 0..=2 and again at 4..=6.\n    // With use_last_match=true, the matcher should highlight the second one.\n    let m = ArinaeMatcher::default();\n    let (_, got) = m.fuzzy_indices(\"man/man1/sk.1\", \"man\").expect(\"should match\");\n    assert_eq!(got, vec![0, 1, 2], \"expected second 'man' (indices 0,1,2), got {got:?}\");\n}\n"
  },
  {
    "path": "src/fuzzy_matcher/clangd.rs",
    "content": "//! The fuzzy matching algorithm used in clangd.\n//! https://github.com/llvm-mirror/clang-tools-extra/blob/master/clangd/FuzzyMatch.cpp\n//!\n//! # Example:\n//! ```\n//! use skim::fuzzy_matcher::FuzzyMatcher;\n//! use skim::fuzzy_matcher::clangd::ClangdMatcher;\n//!\n//! let matcher = ClangdMatcher::default();\n//!\n//! assert_eq!(None, matcher.fuzzy_match(\"abc\", \"abx\"));\n//! assert!(matcher.fuzzy_match(\"axbycz\", \"abc\").is_some());\n//! assert!(matcher.fuzzy_match(\"axbycz\", \"xyz\").is_some());\n//!\n//! let (score, indices) = matcher.fuzzy_indices(\"axbycz\", \"abc\").unwrap();\n//! assert_eq!(indices, [0, 2, 4]);\n//!\n//! ```\n//!\n//! Algorithm modified from\n//! https://github.com/llvm-mirror/clang-tools-extra/blob/master/clangd/FuzzyMatch.cpp\n//! Also check: https://github.com/lewang/flx/issues/98\nuse crate::fuzzy_matcher::util::{CharRole, CharType, char_equal, char_role, char_type_of, cheap_matches};\nuse crate::fuzzy_matcher::{FuzzyMatcher, IndexType, MatchIndices, ScoreType};\nuse std::cell::RefCell;\nuse std::cmp::max;\nuse thread_local::ThreadLocal;\n\n#[derive(Eq, PartialEq, Debug, Copy, Clone)]\nenum CaseMatching {\n    Respect,\n    Ignore,\n    Smart,\n}\n\n#[derive(Debug)]\n/// Fuzzy matcher using the clangd algorithm\npub struct ClangdMatcher {\n    case: CaseMatching,\n\n    use_cache: bool,\n\n    c_cache: ThreadLocal<RefCell<Vec<char>>>, // vector to store the characters of choice\n    p_cache: ThreadLocal<RefCell<Vec<char>>>, // vector to store the characters of pattern\n}\n\nimpl Default for ClangdMatcher {\n    fn default() -> Self {\n        Self {\n            case: CaseMatching::Ignore,\n            use_cache: true,\n            c_cache: ThreadLocal::new(),\n            p_cache: ThreadLocal::new(),\n        }\n    }\n}\n\nimpl ClangdMatcher {\n    /// Sets the matcher to ignore case when matching\n    #[must_use]\n    pub fn ignore_case(mut self) -> Self {\n        self.case = CaseMatching::Ignore;\n        self\n    }\n\n    /// Sets the matcher to use smart case (case insensitive unless pattern contains uppercase)\n    #[must_use]\n    pub fn smart_case(mut self) -> Self {\n        self.case = CaseMatching::Smart;\n        self\n    }\n\n    /// Sets the matcher to respect case when matching\n    #[must_use]\n    pub fn respect_case(mut self) -> Self {\n        self.case = CaseMatching::Respect;\n        self\n    }\n\n    /// Enables or disables caching for improved performance\n    #[must_use]\n    pub fn use_cache(mut self, use_cache: bool) -> Self {\n        self.use_cache = use_cache;\n        self\n    }\n\n    fn contains_upper(string: &str) -> bool {\n        for ch in string.chars() {\n            if ch.is_uppercase() {\n                return true;\n            }\n        }\n\n        false\n    }\n\n    fn is_case_sensitive(&self, pattern: &str) -> bool {\n        match self.case {\n            CaseMatching::Respect => true,\n            CaseMatching::Ignore => false,\n            CaseMatching::Smart => Self::contains_upper(pattern),\n        }\n    }\n}\n\nimpl FuzzyMatcher for ClangdMatcher {\n    fn fuzzy_indices(&self, choice: &str, pattern: &str) -> Option<(ScoreType, MatchIndices)> {\n        let case_sensitive = self.is_case_sensitive(pattern);\n\n        let mut choice_chars = self.c_cache.get_or(|| RefCell::new(Vec::new())).borrow_mut();\n        let mut pattern_chars = self.p_cache.get_or(|| RefCell::new(Vec::new())).borrow_mut();\n\n        choice_chars.clear();\n        for char in choice.chars() {\n            choice_chars.push(char);\n        }\n\n        pattern_chars.clear();\n        for char in pattern.chars() {\n            pattern_chars.push(char);\n        }\n\n        cheap_matches(&choice_chars, &pattern_chars, case_sensitive)?;\n\n        let num_pattern_chars = pattern_chars.len();\n        let num_choice_chars = choice_chars.len();\n\n        let dp = build_graph(&choice_chars, &pattern_chars, false, case_sensitive);\n\n        // search backwards for the matched indices\n        let mut indices_reverse = Vec::with_capacity(num_pattern_chars);\n        let cell = dp[num_pattern_chars][num_choice_chars];\n\n        let (mut last_action, score) = if cell.hit > cell.missed {\n            (Action::Match, cell.hit)\n        } else {\n            (Action::Miss, cell.missed)\n        };\n\n        let mut row = num_pattern_chars;\n        let mut col = num_choice_chars;\n\n        while row > 0 || col > 0 {\n            if last_action == Action::Match {\n                indices_reverse.push((col - 1) as IndexType);\n            }\n\n            let cell = &dp[row][col];\n            if last_action == Action::Match {\n                last_action = cell.last_action_match;\n                row -= 1;\n                col -= 1;\n            } else {\n                last_action = cell.last_action_miss;\n                col -= 1;\n            }\n        }\n\n        if !self.use_cache {\n            // drop the allocated memory\n            self.c_cache.get().map(|cell| cell.replace(vec![]));\n            self.p_cache.get().map(|cell| cell.replace(vec![]));\n        }\n\n        indices_reverse.reverse();\n        Some((\n            adjust_score(score, num_choice_chars),\n            MatchIndices::from(indices_reverse),\n        ))\n    }\n\n    fn fuzzy_match(&self, choice: &str, pattern: &str) -> Option<ScoreType> {\n        let case_sensitive = self.is_case_sensitive(pattern);\n\n        let mut choice_chars = self.c_cache.get_or(|| RefCell::new(Vec::new())).borrow_mut();\n        let mut pattern_chars = self.p_cache.get_or(|| RefCell::new(Vec::new())).borrow_mut();\n\n        choice_chars.clear();\n        for char in choice.chars() {\n            choice_chars.push(char);\n        }\n\n        pattern_chars.clear();\n        for char in pattern.chars() {\n            pattern_chars.push(char);\n        }\n\n        cheap_matches(&choice_chars, &pattern_chars, case_sensitive)?;\n\n        let num_pattern_chars = pattern_chars.len();\n        let num_choice_chars = choice_chars.len();\n\n        let dp = build_graph(&choice_chars, &pattern_chars, true, case_sensitive);\n\n        let cell = dp[num_pattern_chars & 1][num_choice_chars];\n        let score = max(cell.hit, cell.missed);\n\n        if !self.use_cache {\n            // drop the allocated memory\n            self.c_cache.get().map(|cell| cell.replace(vec![]));\n            self.p_cache.get().map(|cell| cell.replace(vec![]));\n        }\n\n        Some(adjust_score(score, num_choice_chars))\n    }\n}\n\n/// fuzzy match `line` with `pattern`, returning the score and indices of matches\n#[must_use]\npub fn fuzzy_indices(line: &str, pattern: &str) -> Option<(ScoreType, MatchIndices)> {\n    ClangdMatcher::default().ignore_case().fuzzy_indices(line, pattern)\n}\n\n/// fuzzy match `line` with `pattern`, returning the score(the larger the better) on match\n#[must_use]\npub fn fuzzy_match(line: &str, pattern: &str) -> Option<ScoreType> {\n    ClangdMatcher::default().ignore_case().fuzzy_match(line, pattern)\n}\n\n// checkout https://github.com/llvm-mirror/clang-tools-extra/blob/master/clangd/FuzzyMatch.cpp\n// for the description\nfn build_graph(line: &[char], pattern: &[char], compressed: bool, case_sensitive: bool) -> Vec<Vec<Score>> {\n    let num_line_chars = line.len();\n    let num_pattern_chars = pattern.len();\n    let max_rows = if compressed { 2 } else { num_pattern_chars + 1 };\n\n    let mut dp: Vec<Vec<Score>> = Vec::with_capacity(max_rows);\n\n    for _ in 0..max_rows {\n        dp.push(vec![Score::default(); num_line_chars + 1]);\n    }\n\n    dp[0][0].missed = 0;\n\n    // first line\n    for (idx, &ch) in line.iter().enumerate() {\n        dp[0][idx + 1] = Score {\n            missed: dp[0][idx].missed - skip_penalty(idx, ch, Action::Miss),\n            last_action_miss: Action::Miss,\n            hit: AWFUL_SCORE,\n            last_action_match: Action::Miss,\n        };\n    }\n\n    // build the matrix\n    let mut pat_prev_ch = '\\0';\n    for (pat_idx, &pat_ch) in pattern.iter().enumerate() {\n        let current_row_idx = if compressed { (pat_idx + 1) & 1 } else { pat_idx + 1 };\n        let prev_row_idx = if compressed { pat_idx & 1 } else { pat_idx };\n\n        let mut line_prev_ch = '\\0';\n        for (line_idx, &line_ch) in line.iter().enumerate() {\n            if line_idx < pat_idx {\n                line_prev_ch = line_ch;\n                continue;\n            }\n\n            // what if we skip current line character?\n            // we need to calculate the cases where the pre line character is matched/missed\n            let pre_miss = &dp[current_row_idx][line_idx];\n            let mut match_missed = pre_miss.hit;\n            let mut miss_missed = pre_miss.missed;\n            if pat_idx < num_pattern_chars - 1 {\n                match_missed -= skip_penalty(line_idx, line_ch, Action::Match);\n                miss_missed -= skip_penalty(line_idx, line_ch, Action::Miss);\n            }\n\n            let (missed, last_action_miss) = if match_missed > miss_missed {\n                (match_missed, Action::Match)\n            } else {\n                (miss_missed, Action::Miss)\n            };\n\n            // what if we want to match current line character?\n            // so we need to calculate the cases where the pre pattern character is matched/missed\n            let pre_match = &dp[prev_row_idx][line_idx];\n            let match_hit = if allow_match(pat_ch, line_ch, case_sensitive) {\n                pre_match.hit\n                    + match_bonus(\n                        pat_idx,\n                        pat_ch,\n                        pat_prev_ch,\n                        line_idx,\n                        line_ch,\n                        line_prev_ch,\n                        Action::Match,\n                    )\n            } else {\n                AWFUL_SCORE\n            };\n\n            let miss_hit = if allow_match(pat_ch, line_ch, case_sensitive) {\n                pre_match.missed\n                    + match_bonus(\n                        pat_idx,\n                        pat_ch,\n                        pat_prev_ch,\n                        line_idx,\n                        line_ch,\n                        line_prev_ch,\n                        Action::Match,\n                    )\n            } else {\n                AWFUL_SCORE\n            };\n\n            let (hit, last_action_match) = if match_hit > miss_hit {\n                (match_hit, Action::Match)\n            } else {\n                (miss_hit, Action::Miss)\n            };\n\n            dp[current_row_idx][line_idx + 1] = Score {\n                last_action_miss,\n                last_action_match,\n                missed,\n                hit,\n            };\n\n            line_prev_ch = line_ch;\n        }\n\n        pat_prev_ch = pat_ch;\n    }\n\n    dp\n}\n\nfn adjust_score(score: ScoreType, num_line_chars: usize) -> ScoreType {\n    // line width will affect 10 scores\n    // Cast through u32 to avoid precision-loss lint (character counts fit in u32).\n    let width = f64::from(u32::try_from(num_line_chars + 1).unwrap_or(u32::MAX));\n    let adjustment = width.ln().floor();\n    // SAFETY: adjustment = floor(ln(usize + 1)) ≤ floor(ln(u32::MAX + 1)) ≈ 22, always fits in i64.\n    #[allow(clippy::cast_possible_truncation)]\n    let adjustment_i = adjustment as ScoreType;\n    score - adjustment_i\n}\n\nconst AWFUL_SCORE: ScoreType = -(1 << 30);\n\n#[derive(Debug, PartialEq, Clone, Copy)]\nenum Action {\n    Miss,\n    Match,\n}\n\n#[derive(Debug, Clone, Copy)]\nstruct Score {\n    pub last_action_miss: Action,\n    pub last_action_match: Action,\n    pub missed: ScoreType,\n    pub hit: ScoreType,\n}\n\nimpl Default for Score {\n    fn default() -> Self {\n        Self {\n            last_action_miss: Action::Miss,\n            last_action_match: Action::Miss,\n            missed: AWFUL_SCORE,\n            hit: AWFUL_SCORE,\n        }\n    }\n}\n\nfn skip_penalty(_ch_idx: usize, ch: char, last_action: Action) -> ScoreType {\n    let mut score = 1;\n    if last_action == Action::Match {\n        // Non-consecutive match.\n        score += 3;\n    }\n\n    if char_type_of(ch) == CharType::NonWord {\n        // skip separator\n        score += 6;\n    }\n\n    score\n}\n\nfn allow_match(pat_ch: char, line_ch: char, case_sensitive: bool) -> bool {\n    char_equal(pat_ch, line_ch, case_sensitive)\n}\n\nfn match_bonus(\n    pat_idx: usize,\n    pat_ch: char,\n    pat_prev_ch: char,\n    line_idx: usize,\n    line_ch: char,\n    line_prev_ch: char,\n    last_action: Action,\n) -> ScoreType {\n    let mut score = 10;\n    let pat_role = char_role(pat_prev_ch, pat_ch);\n    let line_role = char_role(line_prev_ch, line_ch);\n\n    // Bonus: pattern so far is a (case-insensitive) prefix of the word.\n    if pat_idx == line_idx {\n        score += 10;\n    }\n\n    // Bonus: case match\n    if pat_ch == line_ch {\n        score += 8;\n    }\n\n    // Bonus: match header\n    if line_role == CharRole::Head {\n        score += 9;\n    }\n\n    // Bonus: a Head in the pattern aligns with one in the word.\n    if pat_role == CharRole::Head && line_role == CharRole::Head {\n        score += 10;\n    }\n\n    // Penalty: matching inside a segment (and previous char wasn't matched).\n    if line_role == CharRole::Tail && pat_idx > 0 && last_action == Action::Miss {\n        score -= 30;\n    }\n\n    // Penalty: a Head in the pattern matches in the middle of a word segment.\n    if pat_role == CharRole::Head && line_role == CharRole::Tail {\n        score -= 10;\n    }\n\n    // Penalty: matching the first pattern character in the middle of a segment.\n    if pat_idx == 0 && line_role == CharRole::Tail {\n        score -= 40;\n    }\n\n    score\n}\n\n#[allow(dead_code)]\nfn print_dp(line: &str, pattern: &str, dp: &[Vec<Score>]) {\n    let num_line_chars = line.chars().count();\n    let num_pattern_chars = pattern.chars().count();\n\n    print!(\"\\t\");\n    for (idx, ch) in line.chars().enumerate() {\n        print!(\"\\t\\t{}/{}\", idx + 1, ch);\n    }\n\n    for (row_num, row) in dp.iter().enumerate().take(num_pattern_chars + 1) {\n        print!(\"\\n{row_num}\\t\");\n        for cell in row.iter().take(num_line_chars + 1) {\n            print!(\n                \"({},{})/({},{})\\t\",\n                cell.missed,\n                if cell.last_action_miss == Action::Miss {\n                    'X'\n                } else {\n                    'O'\n                },\n                cell.hit,\n                if cell.last_action_match == Action::Miss {\n                    'X'\n                } else {\n                    'O'\n                }\n            );\n        }\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use crate::fuzzy_matcher::util::{assert_order, wrap_matches};\n\n    fn wrap_fuzzy_match(line: &str, pattern: &str) -> Option<String> {\n        let (_score, indices) = fuzzy_indices(line, pattern)?;\n        Some(wrap_matches(line, &indices))\n    }\n\n    #[test]\n    fn test_match_or_not() {\n        assert_eq!(None, fuzzy_match(\"abcdefaghi\", \"中\"));\n        assert_eq!(None, fuzzy_match(\"abc\", \"abx\"));\n        assert!(fuzzy_match(\"axbycz\", \"abc\").is_some());\n        assert!(fuzzy_match(\"axbycz\", \"xyz\").is_some());\n\n        assert_eq!(\"[a]x[b]y[c]z\", &wrap_fuzzy_match(\"axbycz\", \"abc\").unwrap());\n        assert_eq!(\"a[x]b[y]c[z]\", &wrap_fuzzy_match(\"axbycz\", \"xyz\").unwrap());\n        assert_eq!(\"[H]ello, [世]界\", &wrap_fuzzy_match(\"Hello, 世界\", \"H世\").unwrap());\n    }\n\n    #[test]\n    fn test_match_quality() {\n        let matcher = ClangdMatcher::default();\n        // case\n        assert_order(&matcher, \"monad\", &[\"monad\", \"Monad\", \"mONAD\"]);\n\n        // initials\n        assert_order(&matcher, \"ab\", &[\"ab\", \"aoo_boo\", \"acb\"]);\n        assert_order(&matcher, \"CC\", &[\"CamelCase\", \"camelCase\", \"camelcase\"]);\n        assert_order(&matcher, \"cC\", &[\"camelCase\", \"CamelCase\", \"camelcase\"]);\n        assert_order(\n            &matcher,\n            \"cc\",\n            &[\"camel case\", \"camelCase\", \"camelcase\", \"CamelCase\", \"camel ace\"],\n        );\n        assert_order(\n            &matcher,\n            \"Da.Te\",\n            &[\"Data.Text\", \"Data.Text.Lazy\", \"Data.Aeson.Encoding.text\"],\n        );\n        assert_order(&matcher, \"foobar.h\", &[\"foobar.h\", \"foo/bar.h\"]);\n        // prefix\n        assert_order(&matcher, \"is\", &[\"isIEEE\", \"inSuf\"]);\n        // shorter\n        assert_order(&matcher, \"ma\", &[\"map\", \"many\", \"maximum\"]);\n        assert_order(&matcher, \"print\", &[\"printf\", \"sprintf\"]);\n        // score(PRINT) = kMinScore\n        assert_order(&matcher, \"ast\", &[\"ast\", \"AST\", \"INT_FAST16_MAX\"]);\n        // score(PRINT) > kMinScore\n        assert_order(&matcher, \"Int\", &[\"int\", \"INT\", \"PRINT\"]);\n    }\n}\n"
  },
  {
    "path": "src/fuzzy_matcher/frizbee.rs",
    "content": "//! Matcher using <https://crates.io/crates/frizbee>\nuse frizbee::{Scoring, smith_waterman::SmithWatermanMatcher};\n\nuse crate::{\n    CaseMatching,\n    fuzzy_matcher::{FuzzyMatcher, MatchIndices, ScoreType},\n};\n\nconst RESPECT_CASE_BONUS: u16 = 10000;\n\n/// Matcher using frizbee,\n/// the same one that `blink.cmp` uses in neovim\n/// credits to @saghen\n#[derive(Default)]\npub struct FrizbeeMatcher {\n    case: CaseMatching,\n    max_typos: Option<u16>,\n}\n\nimpl FrizbeeMatcher {\n    /// Set the max typos to use\n    #[must_use]\n    pub fn max_typos(mut self, typos: Option<usize>) -> Self {\n        self.max_typos = Some(typos.map_or(0, |x| u16::try_from(x).unwrap_or(u16::MAX)));\n        self\n    }\n    /// Set the case, will be converted to a `matching_case_bonus`\n    #[must_use]\n    pub fn case(mut self, case: CaseMatching) -> Self {\n        self.case = case;\n        self\n    }\n}\n\nimpl FuzzyMatcher for FrizbeeMatcher {\n    fn fuzzy_indices(&self, choice: &str, pattern: &str) -> Option<(ScoreType, MatchIndices)> {\n        let scoring = Scoring {\n            matching_case_bonus: match self.case {\n                CaseMatching::Respect => RESPECT_CASE_BONUS,\n                CaseMatching::Ignore => 0,\n                CaseMatching::Smart => {\n                    if pattern.chars().any(char::is_uppercase) {\n                        RESPECT_CASE_BONUS\n                    } else {\n                        0\n                    }\n                }\n            },\n            ..Default::default()\n        };\n        let mut matcher = SmithWatermanMatcher::new(pattern.as_bytes(), &scoring);\n        matcher\n            .match_haystack_indices(choice.as_bytes(), 0, self.max_typos)\n            .and_then(|(m, mut indices)| {\n                debug!(\"{choice}: {m} ({})\", scoring.matching_case_bonus);\n                if m > scoring.matching_case_bonus.saturating_mul(\n                    pattern\n                        .chars()\n                        .count()\n                        .saturating_sub(self.max_typos.unwrap_or(0).into())\n                        .try_into()\n                        .unwrap(),\n                ) {\n                    indices.reverse();\n                    Some((m.into(), MatchIndices::from(indices)))\n                } else {\n                    None\n                }\n            })\n    }\n    fn fuzzy_match(&self, choice: &str, pattern: &str) -> Option<i64> {\n        let scoring = Scoring {\n            matching_case_bonus: match self.case {\n                CaseMatching::Respect => RESPECT_CASE_BONUS,\n                CaseMatching::Ignore => 0,\n                CaseMatching::Smart => {\n                    if pattern.chars().any(char::is_uppercase) {\n                        RESPECT_CASE_BONUS\n                    } else {\n                        0\n                    }\n                }\n            },\n            ..Default::default()\n        };\n        let mut matcher = SmithWatermanMatcher::new(pattern.as_bytes(), &scoring);\n        matcher\n            .match_haystack(choice.as_bytes(), self.max_typos)\n            .map(ScoreType::from)\n    }\n}\n"
  },
  {
    "path": "src/fuzzy_matcher/fzy.rs",
    "content": "//! Fuzzy matching algorithm based on fzy by John Hawthorn.\n//! https://github.com/jhawthorn/fzy\n//!\n//! This implements fzy's scoring algorithm which treats fuzzy matching as a\n//! modified edit-distance problem using dynamic programming (Needleman-Wunsch style).\n//! It uses two DP matrices:\n//! - `M[i][j]`: the best possible score using the first `i` chars of the needle\n//!   and the first `j` chars of the haystack.\n//! - `D[i][j]`: the best score that *ends with a match* at position `(i, j)`.\n//!\n//! This separation enables affine gap penalties: a constant cost to open a gap\n//! and a linear cost for extending it, plus a bonus for consecutive matches.\n//!\n//! All scoring uses integer arithmetic with a ×200 scaling factor for performance.\n//! The original fzy float constants map as follows:\n//! - -0.005 → -1\n//! - -0.01  → -2\n//! - 0.6    → 120\n//! - 0.7    → 140\n//! - 0.8    → 160\n//! - 0.9    → 180\n//! - 1.0    → 200\n//! - -1.5   → -300\n//!\n//! # Example:\n//! ```\n//! use skim::fuzzy_matcher::FuzzyMatcher;\n//! use skim::fuzzy_matcher::fzy::FzyMatcher;\n//!\n//! let matcher = FzyMatcher::default();\n//!\n//! assert_eq!(None, matcher.fuzzy_match(\"abc\", \"abx\"));\n//! assert!(matcher.fuzzy_match(\"axbycz\", \"abc\").is_some());\n//!\n//! let (score, indices) = matcher.fuzzy_indices(\"axbycz\", \"abc\").unwrap();\n//! assert_eq!(indices, [0, 2, 4]);\n//! ```\n\nuse std::cell::RefCell;\n\nuse thread_local::ThreadLocal;\n\nuse crate::fuzzy_matcher::util::cheap_matches;\nuse crate::fuzzy_matcher::{FuzzyMatcher, IndexType, MatchIndices, ScoreType};\n\n// ---------------------------------------------------------------------------\n// Score constants (from fzy's config.def.h, scaled ×200 to integer)\n// ---------------------------------------------------------------------------\n\n/// Sentinel for \"impossible\" / uninitialized DP cells.\n/// Uses `i64::MIN / 2` so that adding a penalty never overflows.\nconst SCORE_MIN: i64 = i64::MIN / 2;\n\n/// Score for an exact-length match (`needle.len()` == `haystack.len()`).\nconst SCORE_MAX: i64 = i64::MAX / 2;\n\nconst SCORE_GAP_LEADING: i64 = -1; // -0.005 × 200\nconst SCORE_GAP_TRAILING: i64 = -1; // -0.005 × 200\nconst SCORE_GAP_INNER: i64 = -2; // -0.01  × 200\n\nconst SCORE_MATCH_CONSECUTIVE: i64 = 200; // 1.0 × 200\nconst SCORE_MATCH_SLASH: i64 = 180; // 0.9 × 200\nconst SCORE_MATCH_WORD: i64 = 160; // 0.8 × 200\nconst SCORE_MATCH_CAPITAL: i64 = 140; // 0.7 × 200\nconst SCORE_MATCH_DOT: i64 = 120; // 0.6 × 200\n\n/// Penalty applied when a typo is used (substitution or needle-char deletion).\nconst SCORE_TYPO: i64 = -300; // -1.5 × 200\n\n/// Maximum haystack length we will score.\nconst MATCH_MAX_LEN: usize = 1024;\n\n/// Conversion factor from internal ×200 scores to skim's ×1000 convention.\n/// `internal_score` × `SCORE_TO_SKIM` = `skim_score`\nconst SCORE_TO_SKIM: i64 = 5; // 1000 / 200\n\n// ---------------------------------------------------------------------------\n// Bonus computation\n// ---------------------------------------------------------------------------\n\n#[inline]\nfn bonus_index(ch: char) -> usize {\n    if ch.is_ascii_uppercase() {\n        2\n    } else {\n        usize::from(ch.is_ascii_lowercase() || ch.is_ascii_digit())\n    }\n}\n\n#[inline]\nfn compute_bonus(prev_ch: char, ch: char) -> i64 {\n    match bonus_index(ch) {\n        0 => 0,\n        1 => match prev_ch {\n            '/' => SCORE_MATCH_SLASH,\n            '-' | '_' | ' ' => SCORE_MATCH_WORD,\n            '.' => SCORE_MATCH_DOT,\n            _ => 0,\n        },\n        2 => match prev_ch {\n            '/' => SCORE_MATCH_SLASH,\n            '-' | '_' | ' ' => SCORE_MATCH_WORD,\n            '.' => SCORE_MATCH_DOT,\n            c if c.is_ascii_lowercase() => SCORE_MATCH_CAPITAL,\n            _ => 0,\n        },\n        _ => unreachable!(),\n    }\n}\n\n// ---------------------------------------------------------------------------\n// Core DP matching (no typos)\n// ---------------------------------------------------------------------------\n\nfn precompute_bonus(haystack: &[char]) -> Vec<i64> {\n    let mut bonuses = Vec::with_capacity(haystack.len());\n    let mut prev = '/';\n    for &ch in haystack {\n        bonuses.push(compute_bonus(prev, ch));\n        prev = ch;\n    }\n    bonuses\n}\n\n#[inline]\nfn is_match(\n    needle: &[char],\n    haystack: &[char],\n    lower_needle: &[char],\n    lower_haystack: &[char],\n    case_sensitive: bool,\n    i: usize,\n    j: usize,\n) -> bool {\n    if case_sensitive {\n        needle[i] == haystack[j]\n    } else {\n        lower_needle[i] == lower_haystack[j]\n    }\n}\n\n/// Core fzy scoring without typos.\n#[allow(clippy::too_many_lines)]\nfn fzy_score(\n    needle: &[char],\n    haystack: &[char],\n    case_sensitive: bool,\n    positions: Option<&mut Vec<IndexType>>,\n) -> Option<i64> {\n    let n = needle.len();\n    let m = haystack.len();\n\n    if n == 0 || m > MATCH_MAX_LEN || n > m {\n        return None;\n    }\n\n    if n == m {\n        if let Some(pos) = positions {\n            pos.clear();\n            pos.extend(0..n);\n        }\n        return Some(SCORE_MAX);\n    }\n\n    let lower_needle: Vec<char> = needle.iter().map(char::to_ascii_lowercase).collect();\n    let lower_haystack: Vec<char> = haystack.iter().map(char::to_ascii_lowercase).collect();\n    let match_bonus = precompute_bonus(haystack);\n\n    if positions.is_some() {\n        // Full matrix for position recovery\n        let mut d_matrix: Vec<Vec<i64>> = vec![vec![SCORE_MIN; m]; n];\n        let mut m_matrix: Vec<Vec<i64>> = vec![vec![SCORE_MIN; m]; n];\n\n        // Row 0\n        {\n            let mut prev_score = SCORE_MIN;\n            let gap = if n == 1 { SCORE_GAP_TRAILING } else { SCORE_GAP_INNER };\n            for j in 0..m {\n                if is_match(needle, haystack, &lower_needle, &lower_haystack, case_sensitive, 0, j) {\n                    let score = i64::try_from(j).unwrap_or(i64::MAX) * SCORE_GAP_LEADING + match_bonus[j];\n                    d_matrix[0][j] = score;\n                    prev_score = score;\n                    m_matrix[0][j] = score;\n                } else {\n                    prev_score += gap;\n                    m_matrix[0][j] = prev_score;\n                }\n            }\n        }\n\n        // Rows 1..n-1\n        for i in 1..n {\n            let mut prev_score = SCORE_MIN;\n            let gap_score = if i == n - 1 {\n                SCORE_GAP_TRAILING\n            } else {\n                SCORE_GAP_INNER\n            };\n            for j in 0..m {\n                if is_match(needle, haystack, &lower_needle, &lower_haystack, case_sensitive, i, j) {\n                    let mut score = SCORE_MIN;\n                    if j > 0 {\n                        let prev_m = m_matrix[i - 1][j - 1];\n                        let prev_d = d_matrix[i - 1][j - 1];\n                        score = i64::max(prev_m + match_bonus[j], prev_d + SCORE_MATCH_CONSECUTIVE);\n                    }\n                    d_matrix[i][j] = score;\n                    prev_score = i64::max(score, prev_score + gap_score);\n                    m_matrix[i][j] = prev_score;\n                } else {\n                    prev_score += gap_score;\n                    m_matrix[i][j] = prev_score;\n                }\n            }\n        }\n\n        let final_score = m_matrix[n - 1][m - 1];\n\n        // Backtrace\n        if let Some(pos) = positions {\n            pos.clear();\n            pos.resize(n, 0);\n            let mut match_required = false;\n            let mut j = m - 1;\n            for i in (0..n).rev() {\n                loop {\n                    if d_matrix[i][j] != SCORE_MIN && (match_required || d_matrix[i][j] == m_matrix[i][j]) {\n                        match_required =\n                            i > 0 && j > 0 && m_matrix[i][j] == d_matrix[i - 1][j - 1] + SCORE_MATCH_CONSECUTIVE;\n                        pos[i] = j;\n                        j = j.saturating_sub(1);\n                        break;\n                    }\n                    if j == 0 {\n                        break;\n                    }\n                    j -= 1;\n                }\n            }\n        }\n\n        Some(final_score)\n    } else {\n        // Score-only: rolling single row\n        let mut d_row = vec![SCORE_MIN; m];\n        let mut m_row = vec![SCORE_MIN; m];\n\n        for i in 0..n {\n            let mut prev_score = SCORE_MIN;\n            let gap_score = if i == n - 1 {\n                SCORE_GAP_TRAILING\n            } else {\n                SCORE_GAP_INNER\n            };\n            let mut prev_d = SCORE_MIN;\n            let mut prev_m = SCORE_MIN;\n\n            for j in 0..m {\n                let old_d = d_row[j];\n                let old_m = m_row[j];\n\n                if is_match(needle, haystack, &lower_needle, &lower_haystack, case_sensitive, i, j) {\n                    let score = if i == 0 {\n                        i64::try_from(j).unwrap_or(i64::MAX) * SCORE_GAP_LEADING + match_bonus[j]\n                    } else if j > 0 {\n                        i64::max(prev_m + match_bonus[j], prev_d + SCORE_MATCH_CONSECUTIVE)\n                    } else {\n                        SCORE_MIN\n                    };\n                    d_row[j] = score;\n                    prev_score = i64::max(score, prev_score + gap_score);\n                    m_row[j] = prev_score;\n                } else {\n                    d_row[j] = SCORE_MIN;\n                    prev_score += gap_score;\n                    m_row[j] = prev_score;\n                }\n\n                prev_d = old_d;\n                prev_m = old_m;\n            }\n        }\n\n        Some(m_row[m - 1])\n    }\n}\n\n// ---------------------------------------------------------------------------\n// Typo-tolerant pre-filter (allocation-free)\n// ---------------------------------------------------------------------------\n\n/// Fast subsequence check allowing up to `max_typos` needle characters to be\n/// unmatched. Returns `true` if the needle can plausibly match the haystack.\n///\n/// Uses a greedy forward scan: for each needle char, try to find it in the\n/// remaining haystack. If not found, consume a typo. If we exceed `max_typos`,\n/// return false.\n///\n/// This is a heuristic pre-filter — it may return true for some candidates\n/// that the full DP will reject, but it will never return false for a valid\n/// match. The key property is that it's O(n + m) with zero allocations.\n///\n/// `lower_pattern` should be the pre-lowercased pattern (avoids repeated\n/// lowercasing of the same pattern across calls). Haystack chars are\n/// lowercased inline.\n#[inline]\nfn can_match_with_typos(\n    choice: &[char],\n    pattern: &[char],\n    lower_pattern: &[char],\n    case_sensitive: bool,\n    max_typos: usize,\n) -> bool {\n    let n = pattern.len();\n    let m = choice.len();\n\n    // Quick length check: we need at least (n - max_typos) haystack chars\n    if n > m + max_typos {\n        return false;\n    }\n\n    let mut typos_used = 0;\n    let mut j = 0; // position in haystack\n\n    for i in 0..n {\n        // Try to find pattern[i] in remaining haystack\n        let saved_j = j;\n        let mut found = false;\n        while j < m {\n            let matches = if case_sensitive {\n                pattern[i] == choice[j]\n            } else {\n                lower_pattern[i] == choice[j].to_ascii_lowercase()\n            };\n            if matches {\n                j += 1;\n                found = true;\n                break;\n            }\n            j += 1;\n        }\n        if !found {\n            // Needle char not found — treat as typo (deletion of this needle char).\n            // Restore j so that subsequent needle chars can still match\n            // remaining haystack characters (needle deletion skips only the\n            // needle char, not haystack chars).\n            j = saved_j;\n            typos_used += 1;\n            if typos_used > max_typos {\n                return false;\n            }\n        }\n    }\n\n    true\n}\n\n// ---------------------------------------------------------------------------\n// Typo-tolerant scoring with rolling rows\n// ---------------------------------------------------------------------------\n\n/// Thread-local DP buffers to avoid per-call allocation in the typo path.\n#[derive(Debug, Default)]\nstruct TypoDpBuffers {\n    /// Per-typo-layer D and M rolling rows: `[t][cur_or_prev][j]`\n    /// Flattened as: `[(t_max+1) * 2 * m_cap]` for D and M each.\n    d_buf: Vec<i64>,\n    m_buf: Vec<i64>,\n    /// For the full-matrix path (positions needed), we store full D and M:\n    /// `[(t_max+1) * n * m]` each.\n    d_full: Vec<i64>,\n    m_full: Vec<i64>,\n}\n\n/// Score-only typo-tolerant fzy matching using rolling rows.\n///\n/// For each typo layer `t` we maintain two rows (current and previous needle row)\n/// of D and M values. This is `O((t_max+1)` * n * m) time but only\n/// `O((t_max+1)` * m) space.\n#[allow(clippy::too_many_arguments)]\nfn fzy_score_typos_rolling(\n    needle: &[char],\n    haystack: &[char],\n    lower_needle: &[char],\n    lower_haystack: &[char],\n    match_bonus: &[i64],\n    case_sensitive: bool,\n    max_typos: usize,\n    bufs: &mut TypoDpBuffers,\n) -> Option<i64> {\n    let n = needle.len();\n    let m = haystack.len();\n    let t_max = max_typos;\n\n    // We need two rows (prev, cur) per typo layer for both D and M.\n    // Layout: [(t_max+1) * 2 * m] — row index is (t * 2 + row_parity) * m + j\n    let row_size = m;\n    let layer_size = 2 * row_size;\n    let total = (t_max + 1) * layer_size;\n\n    bufs.d_buf.clear();\n    bufs.d_buf.resize(total, SCORE_MIN);\n    bufs.m_buf.clear();\n    bufs.m_buf.resize(total, SCORE_MIN);\n\n    let d = &mut bufs.d_buf;\n    let m_arr = &mut bufs.m_buf;\n\n    // Index helpers\n    let ri = |t: usize, parity: usize, j: usize| -> usize { t * layer_size + parity * row_size + j };\n\n    for i in 0..n {\n        let cur = i & 1;\n        let prev = 1 - cur;\n        let gap_score = if i == n - 1 {\n            SCORE_GAP_TRAILING\n        } else {\n            SCORE_GAP_INNER\n        };\n\n        for t in 0..=t_max {\n            let mut prev_score = SCORE_MIN;\n\n            for j in 0..m {\n                let matched = if case_sensitive {\n                    needle[i] == haystack[j]\n                } else {\n                    lower_needle[i] == lower_haystack[j]\n                };\n\n                let mut d_val = SCORE_MIN;\n\n                // --- Exact match ---\n                if matched {\n                    if i == 0 {\n                        d_val = i64::try_from(j).unwrap_or(i64::MAX) * SCORE_GAP_LEADING + match_bonus[j];\n                    } else if j > 0 {\n                        let pm = m_arr[ri(t, prev, j - 1)];\n                        let pd = d[ri(t, prev, j - 1)];\n                        if pm != SCORE_MIN {\n                            d_val = i64::max(d_val, pm + match_bonus[j]);\n                        }\n                        if pd != SCORE_MIN {\n                            d_val = i64::max(d_val, pd + SCORE_MATCH_CONSECUTIVE);\n                        }\n                    }\n                }\n\n                // --- Substitution typo: consume both needle[i] and haystack[j] ---\n                if !matched && t > 0 {\n                    if i == 0 {\n                        d_val = i64::max(\n                            d_val,\n                            i64::try_from(j).unwrap_or(i64::MAX) * SCORE_GAP_LEADING + SCORE_TYPO,\n                        );\n                    } else if j > 0 {\n                        let pm = m_arr[ri(t - 1, prev, j - 1)];\n                        if pm != SCORE_MIN {\n                            d_val = i64::max(d_val, pm + SCORE_TYPO);\n                        }\n                    }\n                }\n\n                d[ri(t, cur, j)] = d_val;\n\n                // --- Compute M from D and gap ---\n                if d_val == SCORE_MIN {\n                    prev_score += gap_score;\n                } else {\n                    prev_score = i64::max(d_val, prev_score + gap_score);\n                }\n\n                // --- Needle deletion: skip needle[i], use a typo ---\n                // M[t][i][j] can come from M[t-1][i-1][j] + SCORE_TYPO\n                if t > 0 {\n                    let del_from = if i == 0 {\n                        // Deleting first needle char\n                        i64::try_from(j).unwrap_or(i64::MAX) * SCORE_GAP_LEADING + SCORE_TYPO\n                    } else {\n                        let pv = m_arr[ri(t - 1, prev, j)];\n                        if pv == SCORE_MIN { SCORE_MIN } else { pv + SCORE_TYPO }\n                    };\n                    if del_from != SCORE_MIN {\n                        prev_score = i64::max(prev_score, del_from);\n                    }\n                }\n\n                m_arr[ri(t, cur, j)] = prev_score;\n            }\n        }\n    }\n\n    // Find best score across all typo layers\n    let final_row = (n - 1) & 1;\n    let mut best = SCORE_MIN;\n    for t in 0..=t_max {\n        let s = m_arr[ri(t, final_row, m - 1)];\n        if s > best {\n            best = s;\n        }\n    }\n\n    if best == SCORE_MIN { None } else { Some(best) }\n}\n\n/// Full-matrix typo-tolerant scoring for position recovery.\n#[allow(clippy::too_many_lines)]\n#[allow(clippy::too_many_arguments)]\nfn fzy_score_typos_full(\n    needle: &[char],\n    haystack: &[char],\n    lower_needle: &[char],\n    lower_haystack: &[char],\n    match_bonus: &[i64],\n    case_sensitive: bool,\n    max_typos: usize,\n    positions: &mut Vec<IndexType>,\n    bufs: &mut TypoDpBuffers,\n) -> Option<i64> {\n    let n = needle.len();\n    let m = haystack.len();\n    let t_max = max_typos;\n\n    let layer_size = n * m;\n    let total = (t_max + 1) * layer_size;\n\n    bufs.d_full.clear();\n    bufs.d_full.resize(total, SCORE_MIN);\n    bufs.m_full.clear();\n    bufs.m_full.resize(total, SCORE_MIN);\n\n    let d_flat = &mut bufs.d_full;\n    let m_flat = &mut bufs.m_full;\n\n    let idx = |t: usize, i: usize, j: usize| -> usize { t * layer_size + i * m + j };\n\n    // Fill DP\n    for t in 0..=t_max {\n        for i in 0..n {\n            let gap_score = if i == n - 1 {\n                SCORE_GAP_TRAILING\n            } else {\n                SCORE_GAP_INNER\n            };\n            let mut prev_score = SCORE_MIN;\n\n            for j in 0..m {\n                let matched = if case_sensitive {\n                    needle[i] == haystack[j]\n                } else {\n                    lower_needle[i] == lower_haystack[j]\n                };\n\n                let mut d_val = SCORE_MIN;\n\n                // Exact match\n                if matched {\n                    if i == 0 {\n                        d_val = i64::try_from(j).unwrap_or(i64::MAX) * SCORE_GAP_LEADING + match_bonus[j];\n                    } else if j > 0 {\n                        let pm = m_flat[idx(t, i - 1, j - 1)];\n                        let pd = d_flat[idx(t, i - 1, j - 1)];\n                        if pm != SCORE_MIN {\n                            d_val = i64::max(d_val, pm + match_bonus[j]);\n                        }\n                        if pd != SCORE_MIN {\n                            d_val = i64::max(d_val, pd + SCORE_MATCH_CONSECUTIVE);\n                        }\n                    }\n                }\n\n                // Substitution typo\n                if !matched && t > 0 {\n                    if i == 0 {\n                        d_val = i64::max(\n                            d_val,\n                            i64::try_from(j).unwrap_or(i64::MAX) * SCORE_GAP_LEADING + SCORE_TYPO,\n                        );\n                    } else if j > 0 {\n                        let pm = m_flat[idx(t - 1, i - 1, j - 1)];\n                        if pm != SCORE_MIN {\n                            d_val = i64::max(d_val, pm + SCORE_TYPO);\n                        }\n                    }\n                }\n\n                d_flat[idx(t, i, j)] = d_val;\n\n                if d_val == SCORE_MIN {\n                    prev_score += gap_score;\n                } else {\n                    prev_score = i64::max(d_val, prev_score + gap_score);\n                }\n\n                // Needle deletion\n                if t > 0 {\n                    let del_from = if i == 0 {\n                        i64::try_from(j).unwrap_or(i64::MAX) * SCORE_GAP_LEADING + SCORE_TYPO\n                    } else {\n                        let pv = m_flat[idx(t - 1, i - 1, j)];\n                        if pv == SCORE_MIN { SCORE_MIN } else { pv + SCORE_TYPO }\n                    };\n                    if del_from != SCORE_MIN {\n                        prev_score = i64::max(prev_score, del_from);\n                    }\n                }\n\n                m_flat[idx(t, i, j)] = prev_score;\n            }\n        }\n    }\n\n    // Find best score and typo layer\n    let mut best_score = SCORE_MIN;\n    let mut best_t = 0;\n    for t in 0..=t_max {\n        let s = m_flat[idx(t, n - 1, m - 1)];\n        if s > best_score {\n            best_score = s;\n            best_t = t;\n        }\n    }\n\n    if best_score == SCORE_MIN {\n        return None;\n    }\n\n    // Backtrace for positions\n    positions.clear();\n    let mut cur_t = best_t;\n    let mut j = m - 1;\n    let mut rev_positions: Vec<Option<usize>> = Vec::with_capacity(n);\n\n    for i in (0..n).rev() {\n        loop {\n            let cur_m = m_flat[idx(cur_t, i, j)];\n\n            // Check needle deletion (exact integer comparison — no epsilon needed)\n            if cur_t > 0 {\n                let del_from = if i == 0 {\n                    i64::try_from(j).unwrap_or(i64::MAX) * SCORE_GAP_LEADING + SCORE_TYPO\n                } else {\n                    let pv = m_flat[idx(cur_t - 1, i - 1, j)];\n                    if pv == SCORE_MIN { SCORE_MIN } else { pv + SCORE_TYPO }\n                };\n                if del_from != SCORE_MIN && cur_m == del_from {\n                    rev_positions.push(None);\n                    cur_t -= 1;\n                    break;\n                }\n            }\n\n            // Check match/substitution at (i, j) (exact integer comparison)\n            let d_val = d_flat[idx(cur_t, i, j)];\n            if d_val != SCORE_MIN && d_val == cur_m {\n                rev_positions.push(Some(j));\n                j = j.saturating_sub(1);\n                break;\n            }\n\n            // Gap — haystack[j] skipped\n            if j == 0 {\n                rev_positions.push(None);\n                break;\n            }\n            j -= 1;\n        }\n    }\n\n    rev_positions.reverse();\n    for p in rev_positions.iter().flatten() {\n        positions.push(*p);\n    }\n\n    Some(best_score)\n}\n\n// ---------------------------------------------------------------------------\n// Score conversion\n// ---------------------------------------------------------------------------\n\n/// Convert internal ×200-scaled integer score to skim's `ScoreType` (×1000 convention).\n#[inline]\nfn internal_to_skim_score(score: i64) -> ScoreType {\n    if score == SCORE_MAX {\n        ScoreType::MAX / 2\n    } else if score == SCORE_MIN {\n        ScoreType::MIN / 2\n    } else {\n        score * SCORE_TO_SKIM\n    }\n}\n\n// ---------------------------------------------------------------------------\n// Public matcher struct\n// ---------------------------------------------------------------------------\n\n#[derive(Eq, PartialEq, Debug, Copy, Clone)]\nenum CaseMatching {\n    Respect,\n    Ignore,\n    Smart,\n}\n\n/// Fuzzy matcher using the fzy algorithm.\n///\n/// This is a clean reimplementation of the scoring algorithm from\n/// [fzy](https://github.com/jhawthorn/fzy) by John Hawthorn.\n///\n/// Supports optional typo tolerance via [`max_typos`](Self::max_typos).\n#[derive(Debug)]\npub struct FzyMatcher {\n    case: CaseMatching,\n    use_cache: bool,\n    max_typos: Option<usize>,\n    c_cache: ThreadLocal<RefCell<Vec<char>>>,\n    p_cache: ThreadLocal<RefCell<Vec<char>>>,\n    lc_cache: ThreadLocal<RefCell<Vec<char>>>,\n    lp_cache: ThreadLocal<RefCell<Vec<char>>>,\n    typo_bufs: ThreadLocal<RefCell<TypoDpBuffers>>,\n}\n\nimpl Default for FzyMatcher {\n    fn default() -> Self {\n        Self {\n            case: CaseMatching::Ignore,\n            use_cache: true,\n            max_typos: None,\n            c_cache: ThreadLocal::new(),\n            p_cache: ThreadLocal::new(),\n            lc_cache: ThreadLocal::new(),\n            lp_cache: ThreadLocal::new(),\n            typo_bufs: ThreadLocal::new(),\n        }\n    }\n}\n\nimpl FzyMatcher {\n    /// Sets the matcher to ignore case when matching.\n    #[must_use]\n    pub fn ignore_case(mut self) -> Self {\n        self.case = CaseMatching::Ignore;\n        self\n    }\n\n    /// Sets the matcher to use smart case.\n    #[must_use]\n    pub fn smart_case(mut self) -> Self {\n        self.case = CaseMatching::Smart;\n        self\n    }\n\n    /// Sets the matcher to respect case exactly.\n    #[must_use]\n    pub fn respect_case(mut self) -> Self {\n        self.case = CaseMatching::Respect;\n        self\n    }\n\n    /// Enables or disables thread-local caching.\n    #[must_use]\n    pub fn use_cache(mut self, use_cache: bool) -> Self {\n        self.use_cache = use_cache;\n        self\n    }\n\n    /// Sets the maximum number of typos allowed during matching.\n    ///\n    /// - `None` (default): strict subsequence matching with no typos.\n    /// - `Some(n)`: allows up to `n` typos.\n    #[must_use]\n    pub fn max_typos(mut self, max_typos: Option<usize>) -> Self {\n        self.max_typos = max_typos;\n        self\n    }\n\n    fn contains_upper(string: &str) -> bool {\n        string.chars().any(char::is_uppercase)\n    }\n\n    fn is_case_sensitive(&self, pattern: &str) -> bool {\n        match self.case {\n            CaseMatching::Respect => true,\n            CaseMatching::Ignore => false,\n            CaseMatching::Smart => Self::contains_upper(pattern),\n        }\n    }\n}\n\nimpl FuzzyMatcher for FzyMatcher {\n    fn fuzzy_indices(&self, choice: &str, pattern: &str) -> Option<(ScoreType, MatchIndices)> {\n        let case_sensitive = self.is_case_sensitive(pattern);\n\n        let mut choice_chars = self.c_cache.get_or(|| RefCell::new(Vec::new())).borrow_mut();\n        let mut pattern_chars = self.p_cache.get_or(|| RefCell::new(Vec::new())).borrow_mut();\n\n        choice_chars.clear();\n        choice_chars.extend(choice.chars());\n        pattern_chars.clear();\n        pattern_chars.extend(pattern.chars());\n\n        match self.max_typos {\n            None => {\n                cheap_matches(&choice_chars, &pattern_chars, case_sensitive)?;\n                let mut positions = Vec::with_capacity(pattern_chars.len());\n                let s = fzy_score(&pattern_chars, &choice_chars, case_sensitive, Some(&mut positions))?;\n                Some((internal_to_skim_score(s), MatchIndices::from(positions)))\n            }\n            Some(max_t) => {\n                // Fast path: try exact subsequence match first\n                if cheap_matches(&choice_chars, &pattern_chars, case_sensitive).is_some() {\n                    let mut positions = Vec::with_capacity(pattern_chars.len());\n                    if let Some(s) = fzy_score(&pattern_chars, &choice_chars, case_sensitive, Some(&mut positions)) {\n                        return Some((internal_to_skim_score(s), MatchIndices::from(positions)));\n                    }\n                }\n\n                if max_t == 0 {\n                    return None;\n                }\n\n                // Slow path: typo-tolerant matching\n                let n = pattern_chars.len();\n                let m = choice_chars.len();\n\n                if n == 0 || m > MATCH_MAX_LEN || n > m + max_t {\n                    return None;\n                }\n\n                // Compute lowercase pattern (small, fixed size) for prefilter\n                let mut lower_pattern = self.lp_cache.get_or(|| RefCell::new(Vec::new())).borrow_mut();\n                lower_pattern.clear();\n                lower_pattern.extend(pattern_chars.iter().map(char::to_ascii_lowercase));\n\n                if !can_match_with_typos(&choice_chars, &pattern_chars, &lower_pattern, case_sensitive, max_t) {\n                    return None;\n                }\n\n                // Only compute lowercase choice after prefilter passes\n                let mut lower_choice = self.lc_cache.get_or(|| RefCell::new(Vec::new())).borrow_mut();\n                lower_choice.clear();\n                lower_choice.extend(choice_chars.iter().map(char::to_ascii_lowercase));\n\n                let match_bonus = precompute_bonus(&choice_chars);\n                let mut bufs = self\n                    .typo_bufs\n                    .get_or(|| RefCell::new(TypoDpBuffers::default()))\n                    .borrow_mut();\n                let mut positions = Vec::with_capacity(n);\n                let s = fzy_score_typos_full(\n                    &pattern_chars,\n                    &choice_chars,\n                    &lower_pattern,\n                    &lower_choice,\n                    &match_bonus,\n                    case_sensitive,\n                    max_t,\n                    &mut positions,\n                    &mut bufs,\n                )?;\n\n                if !self.use_cache {\n                    self.lc_cache.get().map(|cell| cell.replace(vec![]));\n                    self.lp_cache.get().map(|cell| cell.replace(vec![]));\n                }\n\n                Some((internal_to_skim_score(s), MatchIndices::from(positions)))\n            }\n        }\n    }\n\n    fn fuzzy_match(&self, choice: &str, pattern: &str) -> Option<ScoreType> {\n        let case_sensitive = self.is_case_sensitive(pattern);\n\n        let mut choice_chars = self.c_cache.get_or(|| RefCell::new(Vec::new())).borrow_mut();\n        let mut pattern_chars = self.p_cache.get_or(|| RefCell::new(Vec::new())).borrow_mut();\n\n        choice_chars.clear();\n        choice_chars.extend(choice.chars());\n        pattern_chars.clear();\n        pattern_chars.extend(pattern.chars());\n\n        match self.max_typos {\n            None => {\n                cheap_matches(&choice_chars, &pattern_chars, case_sensitive)?;\n                let s = fzy_score(&pattern_chars, &choice_chars, case_sensitive, None)?;\n                Some(internal_to_skim_score(s))\n            }\n            Some(max_t) => {\n                // Fast path: try exact subsequence match first\n                if cheap_matches(&choice_chars, &pattern_chars, case_sensitive).is_some()\n                    && let Some(s) = fzy_score(&pattern_chars, &choice_chars, case_sensitive, None)\n                {\n                    return Some(internal_to_skim_score(s));\n                }\n\n                if max_t == 0 {\n                    return None;\n                }\n\n                // Slow path: typo-tolerant matching\n                let n = pattern_chars.len();\n                let m = choice_chars.len();\n\n                if n == 0 || m > MATCH_MAX_LEN || n > m + max_t {\n                    return None;\n                }\n\n                // Compute lowercase pattern (small, fixed size) for prefilter\n                let mut lower_pattern = self.lp_cache.get_or(|| RefCell::new(Vec::new())).borrow_mut();\n                lower_pattern.clear();\n                lower_pattern.extend(pattern_chars.iter().map(char::to_ascii_lowercase));\n\n                if !can_match_with_typos(&choice_chars, &pattern_chars, &lower_pattern, case_sensitive, max_t) {\n                    return None;\n                }\n\n                // Only compute lowercase choice after prefilter passes\n                let mut lower_choice = self.lc_cache.get_or(|| RefCell::new(Vec::new())).borrow_mut();\n                lower_choice.clear();\n                lower_choice.extend(choice_chars.iter().map(char::to_ascii_lowercase));\n\n                let match_bonus = precompute_bonus(&choice_chars);\n                let mut bufs = self\n                    .typo_bufs\n                    .get_or(|| RefCell::new(TypoDpBuffers::default()))\n                    .borrow_mut();\n                let s = fzy_score_typos_rolling(\n                    &pattern_chars,\n                    &choice_chars,\n                    &lower_pattern,\n                    &lower_choice,\n                    &match_bonus,\n                    case_sensitive,\n                    max_t,\n                    &mut bufs,\n                )?;\n\n                if !self.use_cache {\n                    self.lc_cache.get().map(|cell| cell.replace(vec![]));\n                    self.lp_cache.get().map(|cell| cell.replace(vec![]));\n                }\n\n                Some(internal_to_skim_score(s))\n            }\n        }\n    }\n}\n\n// ---------------------------------------------------------------------------\n// Convenience free functions\n// ---------------------------------------------------------------------------\n\n/// Fuzzy match `choice` against `pattern` using the fzy algorithm, returning\n/// the score and matched character indices.\n#[must_use]\npub fn fuzzy_indices(choice: &str, pattern: &str) -> Option<(ScoreType, MatchIndices)> {\n    FzyMatcher::default().ignore_case().fuzzy_indices(choice, pattern)\n}\n\n/// Fuzzy match `choice` against `pattern` using the fzy algorithm, returning\n/// only the score.\n#[must_use]\npub fn fuzzy_match(choice: &str, pattern: &str) -> Option<ScoreType> {\n    FzyMatcher::default().ignore_case().fuzzy_match(choice, pattern)\n}\n\n// ---------------------------------------------------------------------------\n// Tests\n// ---------------------------------------------------------------------------\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use crate::fuzzy_matcher::util::{assert_order, wrap_matches};\n\n    fn wrap_fuzzy_match(choice: &str, pattern: &str) -> Option<String> {\n        let (_score, indices) = fuzzy_indices(choice, pattern)?;\n        Some(wrap_matches(choice, &indices))\n    }\n\n    #[test]\n    fn test_no_match() {\n        assert_eq!(None, fuzzy_match(\"abc\", \"abx\"));\n        assert_eq!(None, fuzzy_match(\"abc\", \"d\"));\n        assert_eq!(None, fuzzy_match(\"\", \"a\"));\n    }\n\n    #[test]\n    fn test_has_match() {\n        assert!(fuzzy_match(\"axbycz\", \"abc\").is_some());\n        assert!(fuzzy_match(\"axbycz\", \"xyz\").is_some());\n        assert!(fuzzy_match(\"abc\", \"abc\").is_some());\n    }\n\n    #[test]\n    fn test_exact_match_is_max() {\n        let matcher = FzyMatcher::default().ignore_case();\n        let score = matcher.fuzzy_match(\"abc\", \"abc\").unwrap();\n        assert!(score > 1_000_000);\n    }\n\n    #[test]\n    fn test_match_indices() {\n        assert_eq!(\"[a]x[b]y[c]z\", &wrap_fuzzy_match(\"axbycz\", \"abc\").unwrap());\n        assert_eq!(\"a[x]b[y]c[z]\", &wrap_fuzzy_match(\"axbycz\", \"xyz\").unwrap());\n    }\n\n    #[test]\n    fn test_consecutive_bonus() {\n        let matcher = FzyMatcher::default().ignore_case();\n        let consecutive = matcher.fuzzy_match(\"foobar\", \"foo\").unwrap();\n        let scattered = matcher.fuzzy_match(\"fxoxo\", \"foo\").unwrap();\n        assert!(\n            consecutive > scattered,\n            \"consecutive={consecutive} > scattered={scattered}\"\n        );\n    }\n\n    #[test]\n    fn test_word_boundary_bonus() {\n        let matcher = FzyMatcher::default().ignore_case();\n        let boundary = matcher.fuzzy_match(\"foo_bar_baz\", \"fbb\").unwrap();\n        let inner = matcher.fuzzy_match(\"fooobarbaz\", \"fbb\").unwrap();\n        assert!(boundary > inner, \"boundary={boundary} > inner={inner}\");\n    }\n\n    #[test]\n    fn test_path_separator_bonus() {\n        let matcher = FzyMatcher::default().ignore_case();\n        let path = matcher.fuzzy_match(\"src/lib/foo.rs\", \"foo\").unwrap();\n        let no_path = matcher.fuzzy_match(\"srcxlibxfoo.rs\", \"foo\").unwrap();\n        assert!(path > no_path, \"path={path} > no_path={no_path}\");\n    }\n\n    #[test]\n    fn test_camel_case_bonus() {\n        let matcher = FzyMatcher::default().ignore_case();\n        let camel = matcher.fuzzy_match(\"FooBarBaz\", \"fbb\").unwrap();\n        let no_camel = matcher.fuzzy_match(\"foobarbaz\", \"fbb\").unwrap();\n        assert!(camel > no_camel, \"camel={camel} > no_camel={no_camel}\");\n    }\n\n    #[test]\n    fn test_shorter_match_preferred() {\n        let matcher = FzyMatcher::default().ignore_case();\n        let short = matcher.fuzzy_match(\"ab\", \"ab\").unwrap();\n        let long = matcher.fuzzy_match(\"axxxxxxb\", \"ab\").unwrap();\n        assert!(short > long, \"short={short} > long={long}\");\n    }\n\n    #[test]\n    fn test_match_quality_ordering() {\n        let matcher = FzyMatcher::default();\n        assert_order(&matcher, \"monad\", &[\"monad\", \"Monad\", \"mONAD\"]);\n        assert_order(&matcher, \"ab\", &[\"ab\", \"aoo_boo\", \"acb\"]);\n        assert_order(&matcher, \"ma\", &[\"map\", \"many\", \"maximum\"]);\n    }\n\n    #[test]\n    fn test_unicode_match() {\n        let matcher = FzyMatcher::default().ignore_case();\n        let result = matcher.fuzzy_indices(\"Hello, 世界\", \"H世\");\n        assert!(result.is_some());\n        let (_, indices) = result.unwrap();\n        assert_eq!(indices.as_slice(), &[0, 7]);\n    }\n\n    #[test]\n    fn test_smart_case() {\n        let matcher = FzyMatcher::default().smart_case();\n        assert!(matcher.fuzzy_match(\"FooBar\", \"foobar\").is_some());\n        assert!(matcher.fuzzy_match(\"foobar\", \"FooBar\").is_none());\n        assert!(matcher.fuzzy_match(\"FooBar\", \"FooBar\").is_some());\n    }\n\n    #[test]\n    fn test_respect_case() {\n        let matcher = FzyMatcher::default().respect_case();\n        assert!(matcher.fuzzy_match(\"abc\", \"ABC\").is_none());\n        assert!(matcher.fuzzy_match(\"ABC\", \"ABC\").is_some());\n    }\n\n    #[test]\n    fn test_long_haystack() {\n        let matcher = FzyMatcher::default().ignore_case();\n        let long = \"a\".repeat(MATCH_MAX_LEN + 1);\n        assert_eq!(None, matcher.fuzzy_match(&long, \"a\"));\n    }\n\n    // -----------------------------------------------------------------------\n    // Typo-tolerant matching tests\n    // -----------------------------------------------------------------------\n\n    #[test]\n    fn test_typo_no_typos_behaves_like_default() {\n        let strict = FzyMatcher::default().ignore_case();\n        let typo0 = FzyMatcher::default().ignore_case().max_typos(Some(0));\n\n        assert!(strict.fuzzy_match(\"axbycz\", \"abc\").is_some());\n        assert!(typo0.fuzzy_match(\"axbycz\", \"abc\").is_some());\n\n        assert!(strict.fuzzy_match(\"abc\", \"abx\").is_none());\n        assert!(typo0.fuzzy_match(\"abc\", \"abx\").is_none());\n    }\n\n    #[test]\n    fn test_typo_substitution_single() {\n        let matcher = FzyMatcher::default().ignore_case().max_typos(Some(1));\n        assert!(matcher.fuzzy_match(\"abc\", \"abx\").is_some(), \"substitution: 'x' for 'c'\");\n    }\n\n    #[test]\n    fn test_typo_substitution_returns_none_when_too_many_typos() {\n        let matcher = FzyMatcher::default().ignore_case().max_typos(Some(1));\n        assert!(\n            matcher.fuzzy_match(\"abc\", \"ayx\").is_none(),\n            \"2 typos needed but only 1 allowed\"\n        );\n\n        let matcher2 = FzyMatcher::default().ignore_case().max_typos(Some(2));\n        assert!(matcher2.fuzzy_match(\"abc\", \"ayx\").is_some(), \"2 typos allowed\");\n    }\n\n    #[test]\n    fn test_typo_needle_deletion() {\n        let matcher = FzyMatcher::default().ignore_case().max_typos(Some(1));\n        assert!(matcher.fuzzy_match(\"abd\", \"abcd\").is_some(), \"needle deletion of 'c'\");\n\n        let strict = FzyMatcher::default().ignore_case();\n        assert!(strict.fuzzy_match(\"abd\", \"abcd\").is_none());\n    }\n\n    #[test]\n    fn test_typo_exact_match_scores_higher_than_typo_match() {\n        let matcher = FzyMatcher::default().ignore_case().max_typos(Some(1));\n        let exact = matcher.fuzzy_match(\"abc\", \"abc\").unwrap();\n        let typo = matcher.fuzzy_match(\"axc\", \"abc\").unwrap();\n        assert!(exact > typo, \"exact ({exact}) > typo ({typo})\");\n    }\n\n    #[test]\n    fn test_typo_subsequence_beats_typo() {\n        let matcher = FzyMatcher::default().ignore_case().max_typos(Some(1));\n        let subseq = matcher.fuzzy_match(\"axbycz\", \"abc\").unwrap();\n        let typo = matcher.fuzzy_match(\"abx\", \"abc\").unwrap();\n        assert!(subseq > typo, \"subsequence ({subseq}) > typo ({typo})\");\n    }\n\n    #[test]\n    fn test_typo_indices_substitution() {\n        let matcher = FzyMatcher::default().ignore_case().max_typos(Some(1));\n        let result = matcher.fuzzy_indices(\"abx\", \"abc\");\n        assert!(result.is_some());\n        let (_, indices) = result.unwrap();\n        assert_eq!(indices.as_slice(), &[0, 1, 2]);\n    }\n\n    #[test]\n    fn test_typo_indices_needle_deletion() {\n        let matcher = FzyMatcher::default().ignore_case().max_typos(Some(1));\n        let result = matcher.fuzzy_indices(\"abd\", \"abcd\");\n        assert!(result.is_some());\n        let (_, indices) = result.unwrap();\n        // 'a'→0, 'b'→1, 'c' deleted (no index), 'd'→2\n        assert_eq!(indices.as_slice(), &[0, 1, 2]);\n    }\n\n    #[test]\n    fn test_typo_max_typos_none_is_zero_overhead() {\n        let default = FzyMatcher::default().ignore_case();\n        let explicit_none = FzyMatcher::default().ignore_case().max_typos(None);\n\n        let choices = [\"foobar\", \"axbycz\", \"src/lib/foo.rs\", \"FooBarBaz\"];\n        let pattern = \"foo\";\n\n        for choice in &choices {\n            assert_eq!(\n                default.fuzzy_match(choice, pattern),\n                explicit_none.fuzzy_match(choice, pattern),\n                \"max_typos(None) should match default for '{choice}'\"\n            );\n        }\n    }\n\n    #[test]\n    fn test_typo_realistic_filename() {\n        let matcher = FzyMatcher::default().ignore_case().max_typos(Some(1));\n        let result = matcher.fuzzy_match(\"controller\", \"controllr\");\n        assert!(\n            result.is_some(),\n            \"should match 'controller' with needle 'controllr' (1 typo)\"\n        );\n    }\n\n    #[test]\n    fn test_typo_two_typos() {\n        let matcher = FzyMatcher::default().ignore_case().max_typos(Some(2));\n        assert!(matcher.fuzzy_match(\"abc\", \"xyz\").is_none());\n        assert!(matcher.fuzzy_match(\"abc\", \"axz\").is_some());\n    }\n\n    #[test]\n    fn test_typo_empty_pattern() {\n        let matcher = FzyMatcher::default().ignore_case().max_typos(Some(1));\n        assert_eq!(None, matcher.fuzzy_match(\"abc\", \"\"));\n    }\n\n    #[test]\n    fn test_typo_pattern_longer_than_haystack() {\n        let matcher = FzyMatcher::default().ignore_case().max_typos(Some(1));\n        assert!(matcher.fuzzy_match(\"ab\", \"abc\").is_some(), \"delete 'c' from needle\");\n        assert!(matcher.fuzzy_match(\"a\", \"abc\").is_none());\n\n        let matcher2 = FzyMatcher::default().ignore_case().max_typos(Some(2));\n        assert!(matcher2.fuzzy_match(\"a\", \"abc\").is_some());\n    }\n}\n"
  },
  {
    "path": "src/fuzzy_matcher/mod.rs",
    "content": "//! Fuzzy matching algorithms and implementations.\n//!\n//! This module provides different fuzzy matching algorithms including\n//! skim's own algorithm and clangd's algorithm for matching text patterns.\n\n/// Arinae fuzzy matching algorithm (Smith-Waterman with affine gaps)\npub mod arinae;\n/// Clangd fuzzy matching algorithm\npub mod clangd;\npub mod frizbee;\n/// Fzy fuzzy matching algorithm\npub mod fzy;\n/// Skim fuzzy matching algorithm\npub mod skim;\nmod util;\n\npub(crate) type IndexType = usize;\npub(crate) type ScoreType = i64;\n\npub(crate) type MatchIndices = Vec<IndexType>;\n\n/// Trait for fuzzy matching text patterns against choices\npub trait FuzzyMatcher: Send + Sync {\n    /// fuzzy match choice with pattern, and return the score & matched indices of characters\n    fn fuzzy_indices(&self, choice: &str, pattern: &str) -> Option<(i64, MatchIndices)>;\n\n    /// fuzzy match choice with pattern, and return the score of matching\n    fn fuzzy_match(&self, choice: &str, pattern: &str) -> Option<i64> {\n        self.fuzzy_indices(choice, pattern).map(|(score, _)| score)\n    }\n\n    /// Fuzzy match and return (score, `begin_char_index`, `end_char_index`) without\n    /// computing per-character match indices. This avoids the Vec allocation and\n    /// traceback that `fuzzy_indices` requires, making it much faster for ranking.\n    ///\n    /// `begin` is the character index of the first matched pattern character,\n    /// `end` is the character index of the last matched pattern character.\n    ///\n    /// Default implementation falls back to `fuzzy_indices`.\n    fn fuzzy_match_range(&self, choice: &str, pattern: &str) -> Option<(i64, usize, usize)> {\n        self.fuzzy_indices(choice, pattern).map(|(score, indices)| {\n            let begin = indices.first().copied().unwrap_or(0);\n            let end = indices.last().copied().unwrap_or(0);\n            (score, begin, end)\n        })\n    }\n}\n"
  },
  {
    "path": "src/fuzzy_matcher/skim.rs",
    "content": "//! The fuzzy matching algorithm used by skim\n//!\n//! # Example:\n//! ```\n//! use skim::fuzzy_matcher::FuzzyMatcher;\n//! use skim::fuzzy_matcher::skim::SkimMatcherV2;\n//!\n//! let matcher = SkimMatcherV2::default();\n//! assert_eq!(None, matcher.fuzzy_match(\"abc\", \"abx\"));\n//! assert!(matcher.fuzzy_match(\"axbycz\", \"abc\").is_some());\n//! assert!(matcher.fuzzy_match(\"axbycz\", \"xyz\").is_some());\n//!\n//! let (score, indices) = matcher.fuzzy_indices(\"axbycz\", \"abc\").unwrap();\n//! assert_eq!(indices, [0, 2, 4]);\n//! ```\n\n#![allow(deprecated)]\n\nuse std::cell::RefCell;\nuse std::cmp::max;\nuse std::fmt::Formatter;\n\nuse thread_local::ThreadLocal;\n\nuse super::skim::Movement::{Match, Skip};\nuse super::util::{char_equal, cheap_matches};\nuse super::{FuzzyMatcher, IndexType, MatchIndices, ScoreType};\n\n#[derive(Copy, Clone, Debug)]\n/// Configuration for skim's scoring algorithm\npub struct SkimScoreConfig {\n    /// Score for each matched character\n    pub score_match: i32,\n    /// Penalty for starting a gap (unmatched characters)\n    pub gap_start: i32,\n    /// Penalty for extending a gap\n    pub gap_extension: i32,\n\n    /// The first character in the typed pattern usually has more significance\n    /// than the rest so it's important that it appears at special positions where\n    /// bonus points are given. e.g. \"to-go\" vs. \"ongoing\" on \"og\" or on \"ogo\".\n    /// The amount of the extra bonus should be limited so that the gap penalty is\n    /// still respected.\n    pub bonus_first_char_multiplier: i32,\n\n    /// We prefer matches at the beginning of a word, but the bonus should not be\n    /// too great to prevent the longer acronym matches from always winning over\n    /// shorter fuzzy matches. The bonus point here was specifically chosen that\n    /// the bonus is cancelled when the gap between the acronyms grows over\n    /// 8 characters, which is approximately the average length of the words found\n    /// in web2 dictionary and my file system.\n    pub bonus_head: i32,\n\n    /// Just like `bonus_head`, but its breakage of word is not that strong, so it should\n    /// be slighter less then `bonus_head`\n    pub bonus_break: i32,\n\n    /// Edge-triggered bonus for matches in camelCase words.\n    /// Compared to word-boundary case, they don't accompany single-character gaps\n    /// (e.g. `FooBar` vs. foo-bar), so we deduct bonus point accordingly.\n    pub bonus_camel: i32,\n\n    /// Minimum bonus point given to characters in consecutive chunks.\n    /// Note that bonus points for consecutive matches shouldn't have needed if we\n    /// used fixed match score as in the original algorithm.\n    pub bonus_consecutive: i32,\n\n    /// Skim will match case-sensitively if the pattern contains ASCII upper case,\n    /// If case of case insensitive match, the penalty will be given to case mismatch\n    pub penalty_case_mismatch: i32,\n}\n\nimpl Default for SkimScoreConfig {\n    fn default() -> Self {\n        let score_match = 16;\n        let gap_start = -3;\n        let gap_extension = -1;\n        let bonus_first_char_multiplier = 2;\n\n        Self {\n            score_match,\n            gap_start,\n            gap_extension,\n            bonus_first_char_multiplier,\n            bonus_head: score_match / 2,\n            bonus_break: score_match / 2 + gap_extension,\n            bonus_camel: score_match / 2 + 2 * gap_extension,\n            bonus_consecutive: -(gap_start + gap_extension),\n            penalty_case_mismatch: gap_extension * 2,\n        }\n    }\n}\n\n#[derive(Debug, Copy, Clone, PartialEq)]\nenum Movement {\n    Match,\n    Skip,\n}\n\n/// Inner state of the score matrix\n// Implementation detail: tried to pad to 16B\n// will store the m and p matrix together\n#[derive(Clone, Debug)]\nstruct MatrixCell {\n    pub m_move: Movement,\n    pub m_score: i32,\n    pub p_move: Movement,\n    pub p_score: i32, // The max score of align pattern[..i] & choice[..j]\n\n    // temporary fields (make use the rest of the padding)\n    pub matched: bool,\n    pub bonus: i32,\n}\n\nconst MATRIX_CELL_NEG_INFINITY: i32 = i16::MIN as i32;\n\nimpl Default for MatrixCell {\n    fn default() -> Self {\n        Self {\n            m_move: Skip,\n            m_score: MATRIX_CELL_NEG_INFINITY,\n            p_move: Skip,\n            p_score: MATRIX_CELL_NEG_INFINITY,\n            matched: false,\n            bonus: 0,\n        }\n    }\n}\n\nimpl MatrixCell {\n    pub fn reset(&mut self) {\n        self.m_move = Skip;\n        self.m_score = MATRIX_CELL_NEG_INFINITY;\n        self.p_move = Skip;\n        self.p_score = MATRIX_CELL_NEG_INFINITY;\n        self.bonus = 0;\n        self.matched = false;\n    }\n}\n\n/// Simulate a 1-D vector as 2-D matrix\nstruct ScoreMatrix<'a> {\n    matrix: &'a mut [MatrixCell],\n    pub rows: usize,\n    pub cols: usize,\n}\n\nimpl<'a> ScoreMatrix<'a> {\n    /// given a matrix, extend it to be (rows x cols) and fill in as `init_val`\n    pub fn new(matrix: &'a mut Vec<MatrixCell>, rows: usize, cols: usize) -> Self {\n        matrix.resize(rows * cols, MatrixCell::default());\n        ScoreMatrix { matrix, rows, cols }\n    }\n\n    #[inline]\n    fn get_index(&self, row: usize, col: usize) -> usize {\n        row * self.cols + col\n    }\n\n    fn get_row(&self, row: usize) -> &[MatrixCell] {\n        let start = row * self.cols;\n        &self.matrix[start..start + self.cols]\n    }\n}\n\nimpl std::ops::Index<(usize, usize)> for ScoreMatrix<'_> {\n    type Output = MatrixCell;\n\n    fn index(&self, index: (usize, usize)) -> &Self::Output {\n        &self.matrix[self.get_index(index.0, index.1)]\n    }\n}\n\nimpl std::ops::IndexMut<(usize, usize)> for ScoreMatrix<'_> {\n    fn index_mut(&mut self, index: (usize, usize)) -> &mut Self::Output {\n        &mut self.matrix[self.get_index(index.0, index.1)]\n    }\n}\n\nimpl std::fmt::Debug for ScoreMatrix<'_> {\n    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {\n        let _ = writeln!(f, \"M score:\");\n        for row in 0..self.rows {\n            for col in 0..self.cols {\n                let cell = &self[(row, col)];\n                write!(\n                    f,\n                    \"{:4}/{}  \",\n                    if cell.m_score == MATRIX_CELL_NEG_INFINITY {\n                        -999\n                    } else {\n                        cell.m_score\n                    },\n                    match cell.m_move {\n                        Match => 'M',\n                        Skip => 'S',\n                    }\n                )?;\n            }\n            writeln!(f)?;\n        }\n\n        let _ = writeln!(f, \"P score:\");\n        for row in 0..self.rows {\n            for col in 0..self.cols {\n                let cell = &self[(row, col)];\n                write!(\n                    f,\n                    \"{:4}/{}  \",\n                    if cell.p_score == MATRIX_CELL_NEG_INFINITY {\n                        -999\n                    } else {\n                        cell.p_score\n                    },\n                    match cell.p_move {\n                        Match => 'M',\n                        Skip => 'S',\n                    }\n                )?;\n            }\n            writeln!(f)?;\n        }\n\n        Ok(())\n    }\n}\n\n/// We categorize characters into types:\n///\n/// - Empty(E): the start of string\n/// - Upper(U): the ascii upper case\n/// - lower(L): the ascii lower case & other unicode characters\n/// - number(N): ascii number\n/// - hard separator(S): clearly separate the content: ` ` `/` `\\` `|` `(` `)` `[` `]` `{` `}`\n/// - soft separator(s): other ascii punctuation, e.g. `!` `\"` `#` `$`, ...\n#[derive(Debug, PartialEq, Copy, Clone)]\nenum CharType {\n    Empty,\n    Upper,\n    Lower,\n    Number,\n    HardSep,\n    SoftSep,\n}\n\nimpl CharType {\n    pub fn of(ch: char) -> Self {\n        match ch {\n            '\\0' => CharType::Empty,\n            ' ' | '/' | '\\\\' | '|' | '(' | ')' | '[' | ']' | '{' | '}' => CharType::HardSep,\n            '!'..='\\'' | '*'..='.' | ':'..='@' | '^'..='`' | '~' => CharType::SoftSep,\n            '0'..='9' => CharType::Number,\n            'A'..='Z' => CharType::Upper,\n            _ => CharType::Lower,\n        }\n    }\n}\n\n/// Ref: <https://github.com/llvm-mirror/clang-tools-extra/blob/master/clangd/FuzzyMatch.cpp>\n///\n///\n/// ```text\n/// +-----------+--------------+-------+\n/// | Example   | Chars | Type | Role  |\n/// +-----------+--------------+-------+\n/// | (f)oo     | ^fo   | Ell  | Head  |\n/// | (F)oo     | ^Fo   | EUl  | Head  |\n/// | Foo/(B)ar | /Ba   | SUl  | Head  |\n/// | Foo/(b)ar | /ba   | Sll  | Head  |\n/// | Foo.(B)ar | .Ba   | SUl  | Break |\n/// | Foo(B)ar  | oBa   | lUl  | Camel |\n/// | 123(B)ar  | 3Ba   | nUl  | Camel |\n/// | F(o)oBar  | Foo   | Ull  | Tail  |\n/// | H(T)TP    | HTT   | UUU  | Tail  |\n/// | others    |       |      | Tail  |\n/// +-----------+--------------+-------+\n#[derive(Debug, PartialEq, Copy, Clone)]\nenum CharRole {\n    Head,\n    Tail,\n    Camel,\n    Break,\n}\n\nimpl CharRole {\n    pub fn of_type(prev: CharType, cur: CharType) -> Self {\n        match (prev, cur) {\n            (CharType::Empty | CharType::HardSep, _) => CharRole::Head,\n            (CharType::SoftSep, _) => CharRole::Break,\n            (CharType::Lower | CharType::Number, CharType::Upper) => CharRole::Camel,\n            _ => CharRole::Tail,\n        }\n    }\n}\n\n#[derive(Eq, PartialEq, Debug, Copy, Clone)]\nenum CaseMatching {\n    Respect,\n    Ignore,\n    Smart,\n}\n\n/// Fuzzy matching is a sub problem is sequence alignment.\n/// Specifically what we'd like to implement is sequence alignment with affine gap penalty.\n/// Ref: <https://www.cs.cmu.edu>/~ckingsf/bioinfo-lectures/gaps.pdf\n///\n/// Given `pattern`(i) and `choice`(j), we'll maintain 2 score matrix:\n///\n/// ```text\n/// M[i][j] = match(i, j) + max(M[i-1][j-1] + consecutive, P[i-1][j-1])\n/// M[i][j] = -infinity if p[i][j] do not match\n///\n/// M[i][j] means the score of best alignment of p[..=i] and c[..=j] ending with match/mismatch e.g.:\n///\n/// c: [.........]b\n/// p: [.........]b\n///\n/// So that p[..=i-1] and c[..=j-1] could be any alignment\n///\n/// P[i][j] = max(M[i][j-k]-gap(k)) for k in 1..j\n///\n/// P[i][j] means the score of best alignment of p[..=i] and c[..=j] where c[j] is not matched.\n/// So that we need to search through all the previous matches, and calculate the gap.\n///\n///   (j-k)--.   j\n/// c: [....]bcdef\n/// p: [....]b----\n///          i\n/// ```\n///\n/// Note that the above is O(n^3) in the worst case. However the above algorithm uses a general gap\n/// penalty, but we use affine gap: `gap = gap_start + k * gap_extend` where:\n/// - u: the cost of starting of gap\n/// - v: the cost of extending a gap by one more space.\n///\n/// So that we could optimize the algorithm by:\n///\n/// ```text\n/// P[i][j] = max(gap_start + gap_extend + M[i][j-1], gap_extend + P[i][j-1])\n/// ```\n///\n/// Besides, since we are doing fuzzy matching, we'll prefer some pattern over others.\n/// So we'll calculate in-place bonus for each character. e.g. bonus for camel cases.\n///\n/// In summary:\n///\n/// ```text\n/// B[j] = in_place_bonus_of(j)\n/// M[i][j] = match(i, j) + max(M[i-1][j-1] + consecutive, P[i-1][j-1])\n/// M[i][j] = -infinity if p[i] and c[j] do not match\n/// P[i][j] = max(gap_start + gap_extend + M[i][j-1], gap_extend + P[i][j-1])\n/// ```\n#[derive(Debug)]\npub struct SkimMatcherV2 {\n    debug: bool,\n\n    score_config: SkimScoreConfig,\n    element_limit: usize,\n    case: CaseMatching,\n    use_cache: bool,\n\n    m_cache: ThreadLocal<RefCell<Vec<MatrixCell>>>,\n    c_cache: ThreadLocal<RefCell<Vec<char>>>, // vector to store the characters of choice\n    p_cache: ThreadLocal<RefCell<Vec<char>>>, // vector to store the characters of pattern\n}\n\nimpl Default for SkimMatcherV2 {\n    fn default() -> Self {\n        Self {\n            debug: false,\n            score_config: SkimScoreConfig::default(),\n            element_limit: 0,\n            case: CaseMatching::Smart,\n            use_cache: true,\n\n            m_cache: ThreadLocal::new(),\n            c_cache: ThreadLocal::new(),\n            p_cache: ThreadLocal::new(),\n        }\n    }\n}\n\nimpl SkimMatcherV2 {\n    /// Sets the scoring configuration for the matcher\n    #[must_use]\n    pub fn score_config(mut self, score_config: SkimScoreConfig) -> Self {\n        self.score_config = score_config;\n        self\n    }\n\n    /// Sets the maximum number of elements to process\n    #[must_use]\n    pub fn element_limit(mut self, elements: usize) -> Self {\n        self.element_limit = elements;\n        self\n    }\n\n    /// Sets the matcher to ignore case when matching\n    #[must_use]\n    pub fn ignore_case(mut self) -> Self {\n        self.case = CaseMatching::Ignore;\n        self\n    }\n\n    /// Sets the matcher to use smart case (case insensitive unless pattern contains uppercase)\n    #[must_use]\n    pub fn smart_case(mut self) -> Self {\n        self.case = CaseMatching::Smart;\n        self\n    }\n\n    /// Sets the matcher to respect case when matching\n    #[must_use]\n    pub fn respect_case(mut self) -> Self {\n        self.case = CaseMatching::Respect;\n        self\n    }\n\n    /// Enables or disables caching for improved performance\n    #[must_use]\n    pub fn use_cache(mut self, use_cache: bool) -> Self {\n        self.use_cache = use_cache;\n        self\n    }\n\n    /// Enables or disables debug mode\n    #[must_use]\n    pub fn debug(mut self, debug: bool) -> Self {\n        self.debug = debug;\n        self\n    }\n\n    /// Build the score matrix using the algorithm described above\n    fn build_score_matrix(\n        &self,\n        m: &mut ScoreMatrix,\n        choice: &[char],\n        pattern: &[char],\n        first_match_indices: &[usize],\n        compressed: bool,\n        case_sensitive: bool,\n    ) {\n        let mut in_place_bonuses = vec![0; m.cols];\n\n        self.build_in_place_bonus(choice, &mut in_place_bonuses);\n\n        // need to reset M[row][first_match] & M[i][j-1]\n        m[(0, 0)].reset();\n        for i in 1..m.rows {\n            m[(i, first_match_indices[i - 1])].reset();\n        }\n\n        for j in 0..m.cols {\n            // p[0][j]: the score of best alignment of p[] and c[..=j] where c[j] is not matched\n            m[(0, j)].reset();\n            m[(0, j)].p_score = self.score_config.gap_extension;\n        }\n\n        // update the matrix;\n        for (i, &p_ch) in pattern.iter().enumerate() {\n            let row = Self::adjust_row_idx(i + 1, compressed);\n            let row_prev = Self::adjust_row_idx(i, compressed);\n            let to_skip = first_match_indices[i];\n\n            // Pre-calculate base indices to reduce repeated index calculations\n            let row_base = row * m.cols;\n            let row_prev_base = row_prev * m.cols;\n\n            for (j, &c_ch) in choice[to_skip..].iter().enumerate() {\n                let col = to_skip + j + 1;\n                let col_prev = to_skip + j;\n\n                // Use pre-calculated bases to reduce index calculations\n                let idx_cur = row_base + col;\n                let idx_last = row_base + col_prev;\n                let idx_prev = row_prev_base + col_prev;\n\n                // Cache in_place_bonus lookup to avoid repeated array access\n                let in_place_bonus = in_place_bonuses[col];\n\n                // update M matrix\n                // M[i][j] = match(i, j) + max(M[i-1][j-1], P[i-1][j-1])\n                if let Some(cur_match_score) = self.calculate_match_score(c_ch, p_ch, case_sensitive) {\n                    let prev_cell = &m.matrix[idx_prev];\n                    let prev_match_score = prev_cell.m_score;\n                    let prev_skip_score = prev_cell.p_score;\n\n                    let prev_match_bonus = m.matrix[idx_last].bonus;\n\n                    let consecutive_bonus = max(\n                        prev_match_bonus,\n                        max(in_place_bonus, self.score_config.bonus_consecutive),\n                    );\n                    m.matrix[idx_last].bonus = consecutive_bonus;\n\n                    let score_match = prev_match_score + consecutive_bonus;\n                    let score_skip = prev_skip_score + in_place_bonus;\n\n                    let cur_cell = &mut m.matrix[idx_cur];\n                    if score_match >= score_skip {\n                        cur_cell.m_score = score_match + i32::from(cur_match_score);\n                        cur_cell.m_move = Movement::Match;\n                    } else {\n                        cur_cell.m_score = score_skip + i32::from(cur_match_score);\n                        cur_cell.m_move = Movement::Skip;\n                    }\n                } else {\n                    let cur_cell = &mut m.matrix[idx_cur];\n                    cur_cell.m_score = MATRIX_CELL_NEG_INFINITY;\n                    cur_cell.m_move = Movement::Skip;\n                    cur_cell.bonus = 0;\n                }\n\n                // update P matrix\n                // P[i][j] = max(gap_start + gap_extend + M[i][j-1], gap_extend + P[i][j-1])\n                let last_cell = &m.matrix[idx_last];\n                let prev_match_score =\n                    self.score_config.gap_start + self.score_config.gap_extension + last_cell.m_score;\n                let prev_skip_score = self.score_config.gap_extension + last_cell.p_score;\n\n                let cur_cell = &mut m.matrix[idx_cur];\n                if prev_match_score >= prev_skip_score {\n                    cur_cell.p_score = prev_match_score;\n                    cur_cell.p_move = Movement::Match;\n                } else {\n                    cur_cell.p_score = prev_skip_score;\n                    cur_cell.p_move = Movement::Skip;\n                }\n            }\n        }\n    }\n\n    /// check bonus for start of camel case, etc.\n    fn build_in_place_bonus(&self, choice: &[char], b: &mut [i32]) {\n        let mut prev_ch = '\\0';\n        for (j, &c_ch) in choice.iter().enumerate() {\n            let prev_ch_type = CharType::of(prev_ch);\n            let ch_type = CharType::of(c_ch);\n            b[j + 1] = self.in_place_bonus(prev_ch_type, ch_type);\n            prev_ch = c_ch;\n        }\n\n        if b.len() > 1 {\n            b[1] *= self.score_config.bonus_first_char_multiplier;\n        }\n    }\n\n    /// In case we don't need to backtrack the matching indices, we could use only 2 rows for the\n    /// matrix, this function could be used to rotate accessing these two rows.\n    fn adjust_row_idx(row_idx: usize, compressed: bool) -> usize {\n        if compressed { row_idx & 1 } else { row_idx }\n    }\n\n    /// Calculate the matching score of the characters\n    /// return None if not matched.\n    fn calculate_match_score(&self, c: char, p: char, case_sensitive: bool) -> Option<u16> {\n        if !char_equal(c, p, case_sensitive) {\n            return None;\n        }\n\n        let score = self.score_config.score_match;\n        let mut bonus = 0;\n\n        // penalty on case mismatch\n        if !case_sensitive && p != c {\n            bonus += self.score_config.penalty_case_mismatch;\n        }\n\n        Some(u16::try_from(max(0, score + bonus)).unwrap_or(u16::MAX))\n    }\n\n    #[inline]\n    fn in_place_bonus(&self, prev_char_type: CharType, char_type: CharType) -> i32 {\n        match CharRole::of_type(prev_char_type, char_type) {\n            CharRole::Head => self.score_config.bonus_head,\n            CharRole::Camel => self.score_config.bonus_camel,\n            CharRole::Break => self.score_config.bonus_break,\n            CharRole::Tail => 0,\n        }\n    }\n\n    fn contains_upper(string: &str) -> bool {\n        string.chars().any(char::is_uppercase)\n    }\n\n    /// Performs fuzzy matching with full algorithm and returns score and indices\n    ///\n    /// # Panics\n    /// Panics if the last row of the DP matrix is empty (should not happen for non-empty patterns).\n    pub fn fuzzy(&self, choice: &str, pattern: &str, with_pos: bool) -> Option<(ScoreType, Vec<IndexType>)> {\n        if pattern.is_empty() {\n            return Some((0, Vec::new()));\n        }\n\n        let case_sensitive = match self.case {\n            CaseMatching::Respect => true,\n            CaseMatching::Ignore => false,\n            CaseMatching::Smart => Self::contains_upper(pattern),\n        };\n\n        let compressed = !with_pos;\n\n        // initialize the score matrix\n        let mut m = self.m_cache.get_or(|| RefCell::new(Vec::new())).borrow_mut();\n        let mut choice_chars = self.c_cache.get_or(|| RefCell::new(Vec::new())).borrow_mut();\n        let mut pattern_chars = self.p_cache.get_or(|| RefCell::new(Vec::new())).borrow_mut();\n\n        choice_chars.clear();\n        choice_chars.reserve(choice.chars().count());\n        choice_chars.extend(choice.chars());\n\n        pattern_chars.clear();\n        pattern_chars.reserve(pattern.chars().count());\n        pattern_chars.extend(pattern.chars());\n\n        let first_match_indices = cheap_matches(&choice_chars, &pattern_chars, case_sensitive)?;\n\n        let cols = choice_chars.len() + 1;\n        let num_char_pattern = pattern_chars.len();\n        let rows = if compressed { 2 } else { num_char_pattern + 1 };\n\n        if self.element_limit > 0 && self.element_limit < rows * cols {\n            return self.simple_match(\n                &choice_chars,\n                &pattern_chars,\n                &first_match_indices,\n                case_sensitive,\n                with_pos,\n            );\n        }\n\n        let mut m = ScoreMatrix::new(&mut m, rows, cols);\n        self.build_score_matrix(\n            &mut m,\n            &choice_chars,\n            &pattern_chars,\n            &first_match_indices,\n            compressed,\n            case_sensitive,\n        );\n        let first_col_of_last_row = first_match_indices[first_match_indices.len() - 1];\n        let last_row = m.get_row(Self::adjust_row_idx(num_char_pattern, compressed));\n        let (pat_idx, &MatrixCell { m_score, .. }) = last_row[first_col_of_last_row..]\n            .iter()\n            .enumerate()\n            .max_by_key(|&(_, x)| x.m_score)\n            .map(|(idx, cell)| (idx + first_col_of_last_row, cell))\n            .expect(\"fuzzy_matcher failed to iterate over last_row\");\n\n        let mut positions = if with_pos {\n            Vec::with_capacity(num_char_pattern)\n        } else {\n            Vec::new()\n        };\n        if with_pos {\n            let mut i = m.rows - 1;\n            let mut j = pat_idx;\n            let mut track_m = true;\n            let mut current_move = Match;\n            let first_col_first_row = first_match_indices[0];\n            while i > 0 && j > first_col_first_row {\n                if current_move == Match {\n                    positions.push((j - 1) as IndexType);\n                }\n\n                let cell = &m[(i, j)];\n                current_move = if track_m { cell.m_move } else { cell.p_move };\n                if track_m {\n                    i -= 1;\n                }\n\n                j -= 1;\n\n                track_m = match current_move {\n                    Match => true,\n                    Skip => false,\n                };\n            }\n            positions.reverse();\n        }\n\n        if self.debug {\n            println!(\"Matrix:\\n{m:?}\");\n        }\n\n        if !self.use_cache {\n            // drop the allocated memory\n            self.m_cache.get().map(|cell| cell.replace(vec![]));\n            self.c_cache.get().map(|cell| cell.replace(vec![]));\n            self.p_cache.get().map(|cell| cell.replace(vec![]));\n        }\n\n        Some((ScoreType::from(m_score), positions))\n    }\n\n    /// Performs simple pattern matching for cases where the full algorithm isn't needed\n    pub fn simple_match(\n        &self,\n        choice: &[char],\n        pattern: &[char],\n        first_match_indices: &[usize],\n        case_sensitive: bool,\n        with_pos: bool,\n    ) -> Option<(ScoreType, Vec<IndexType>)> {\n        if pattern.is_empty() {\n            return Some((0, Vec::new()));\n        } else if pattern.len() == 1 {\n            let match_idx = first_match_indices[0];\n            let prev_ch = if match_idx > 0 { choice[match_idx - 1] } else { '\\0' };\n            let prev_ch_type = CharType::of(prev_ch);\n            let ch_type = CharType::of(choice[match_idx]);\n            let in_place_bonus = self.in_place_bonus(prev_ch_type, ch_type);\n            return Some((ScoreType::from(in_place_bonus), vec![match_idx as IndexType]));\n        }\n\n        let mut start_idx = first_match_indices[0];\n        let end_idx = first_match_indices[first_match_indices.len() - 1];\n\n        let mut pattern_iter = pattern.iter().rev().peekable();\n        for (idx, &c) in choice[start_idx..=end_idx].iter().enumerate().rev() {\n            match pattern_iter.peek() {\n                Some(&&p) => {\n                    if char_equal(c, p, case_sensitive) {\n                        let _ = pattern_iter.next();\n                        start_idx = idx;\n                    }\n                }\n                None => break,\n            }\n        }\n\n        Some(self.calculate_score_with_pos(choice, pattern, start_idx, end_idx, case_sensitive, with_pos))\n    }\n\n    fn calculate_score_with_pos(\n        &self,\n        choice: &[char],\n        pattern: &[char],\n        start_idx: usize,\n        end_idx: usize,\n        case_sensitive: bool,\n        with_pos: bool,\n    ) -> (ScoreType, Vec<IndexType>) {\n        let mut pos = Vec::new();\n\n        let choice_iter = choice[start_idx..=end_idx].iter().enumerate();\n        let mut pattern_iter = pattern.iter().enumerate().peekable();\n\n        // unfortunately we could not get the the character before the first character's(for performance)\n        // so we tread them as NonWord\n        let mut prev_ch = '\\0';\n\n        let mut score: i32 = 0;\n        let mut in_gap = false;\n        let mut prev_match_bonus = 0;\n\n        for (c_idx, &c) in choice_iter {\n            let op = pattern_iter.peek();\n            if op.is_none() {\n                break;\n            }\n\n            let prev_ch_type = CharType::of(prev_ch);\n            let ch_type = CharType::of(c);\n            let in_place_bonus = self.in_place_bonus(prev_ch_type, ch_type);\n\n            let (_p_idx, &p) = *op.unwrap();\n\n            if let Some(match_score) = self.calculate_match_score(c, p, case_sensitive) {\n                if with_pos {\n                    pos.push((c_idx + start_idx) as IndexType);\n                }\n\n                score += i32::from(match_score);\n\n                let consecutive_bonus = max(\n                    prev_match_bonus,\n                    max(in_place_bonus, self.score_config.bonus_consecutive),\n                );\n                prev_match_bonus = consecutive_bonus;\n\n                if !in_gap {\n                    score += consecutive_bonus;\n                }\n\n                in_gap = false;\n                let _ = pattern_iter.next();\n            } else {\n                if !in_gap {\n                    score += self.score_config.gap_start;\n                }\n\n                score += self.score_config.gap_extension;\n                in_gap = true;\n                prev_match_bonus = 0;\n            }\n\n            prev_ch = c;\n        }\n\n        (ScoreType::from(score), pos)\n    }\n}\n\nimpl FuzzyMatcher for SkimMatcherV2 {\n    fn fuzzy_indices(&self, choice: &str, pattern: &str) -> Option<(ScoreType, MatchIndices)> {\n        self.fuzzy(choice, pattern, true)\n            .map(|(s, v)| (s, MatchIndices::from(v)))\n    }\n\n    fn fuzzy_match(&self, choice: &str, pattern: &str) -> Option<ScoreType> {\n        self.fuzzy(choice, pattern, false).map(|(score, _)| score)\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use crate::fuzzy_matcher::util::{assert_order, wrap_matches};\n\n    use super::*;\n\n    fn wrap_fuzzy_match(matcher: &dyn FuzzyMatcher, line: &str, pattern: &str) -> Option<String> {\n        let (score, indices) = matcher.fuzzy_indices(line, pattern)?;\n        println!(\"score: {score:?}, indices: {indices:?}\");\n        Some(wrap_matches(line, &indices))\n    }\n\n    #[test]\n    fn test_match_or_not() {\n        let matcher = SkimMatcherV2::default();\n        assert_eq!(Some(0), matcher.fuzzy_match(\"\", \"\"));\n        assert_eq!(Some(0), matcher.fuzzy_match(\"abcdefaghi\", \"\"));\n        assert_eq!(None, matcher.fuzzy_match(\"\", \"a\"));\n        assert_eq!(None, matcher.fuzzy_match(\"abcdefaghi\", \"中\"));\n        assert_eq!(None, matcher.fuzzy_match(\"abc\", \"abx\"));\n        assert!(matcher.fuzzy_match(\"axbycz\", \"abc\").is_some());\n        assert!(matcher.fuzzy_match(\"axbycz\", \"xyz\").is_some());\n\n        assert_eq!(\"[a]x[b]y[c]z\", &wrap_fuzzy_match(&matcher, \"axbycz\", \"abc\").unwrap());\n        assert_eq!(\"a[x]b[y]c[z]\", &wrap_fuzzy_match(&matcher, \"axbycz\", \"xyz\").unwrap());\n        assert_eq!(\n            \"[H]ello, [世]界\",\n            &wrap_fuzzy_match(&matcher, \"Hello, 世界\", \"H世\").unwrap()\n        );\n    }\n\n    #[test]\n    fn test_match_quality() {\n        let matcher = SkimMatcherV2::default().ignore_case();\n\n        // initials\n        assert_order(&matcher, \"ab\", &[\"ab\", \"aoo_boo\", \"acb\"]);\n        assert_order(&matcher, \"CC\", &[\"CamelCase\", \"camelCase\", \"camelcase\"]);\n        assert_order(&matcher, \"cC\", &[\"camelCase\", \"CamelCase\", \"camelcase\"]);\n        assert_order(\n            &matcher,\n            \"cc\",\n            &[\"camel case\", \"camelCase\", \"CamelCase\", \"camelcase\", \"camel ace\"],\n        );\n        assert_order(\n            &matcher,\n            \"Da.Te\",\n            &[\"Data.Text\", \"Data.Text.Lazy\", \"Data.Aeson.Encoding.text\"],\n        );\n        // prefix\n        assert_order(&matcher, \"is\", &[\"isIEEE\", \"inSuf\"]);\n        // shorter\n        assert_order(&matcher, \"ma\", &[\"map\", \"many\", \"maximum\"]);\n        assert_order(&matcher, \"print\", &[\"printf\", \"sprintf\"]);\n        // score(PRINT) = kMinScore\n        assert_order(&matcher, \"ast\", &[\"ast\", \"AST\", \"INT_FAST16_MAX\"]);\n        // score(PRINT) > kMinScore\n        assert_order(&matcher, \"Int\", &[\"int\", \"INT\", \"PRINT\"]);\n    }\n\n    fn simple_match(\n        matcher: &SkimMatcherV2,\n        choice: &str,\n        pattern: &str,\n        case_sensitive: bool,\n        with_pos: bool,\n    ) -> Option<(ScoreType, Vec<IndexType>)> {\n        let choice: Vec<char> = choice.chars().collect();\n        let pattern: Vec<char> = pattern.chars().collect();\n        let first_match_indices = cheap_matches(&choice, &pattern, case_sensitive)?;\n        matcher.simple_match(&choice, &pattern, &first_match_indices, case_sensitive, with_pos)\n    }\n\n    #[test]\n    fn test_match_or_not_simple() {\n        let matcher = SkimMatcherV2::default();\n        assert_eq!(\n            simple_match(&matcher, \"axbycz\", \"xyz\", false, true).unwrap().1,\n            vec![1, 3, 5]\n        );\n\n        assert_eq!(simple_match(&matcher, \"\", \"\", false, false), Some((0, vec![])));\n        assert_eq!(\n            simple_match(&matcher, \"abcdefaghi\", \"\", false, false),\n            Some((0, vec![]))\n        );\n        assert_eq!(simple_match(&matcher, \"\", \"a\", false, false), None);\n        assert_eq!(simple_match(&matcher, \"abcdefaghi\", \"中\", false, false), None);\n        assert_eq!(simple_match(&matcher, \"abc\", \"abx\", false, false), None);\n        assert_eq!(\n            simple_match(&matcher, \"axbycz\", \"abc\", false, true).unwrap().1,\n            vec![0, 2, 4]\n        );\n        assert_eq!(\n            simple_match(&matcher, \"axbycz\", \"xyz\", false, true).unwrap().1,\n            vec![1, 3, 5]\n        );\n        assert_eq!(\n            simple_match(&matcher, \"Hello, 世界\", \"H世\", false, true).unwrap().1,\n            vec![0, 7]\n        );\n    }\n\n    #[test]\n    fn test_match_or_not_v2() {\n        let matcher = SkimMatcherV2::default().debug(true);\n\n        assert_eq!(matcher.fuzzy_match(\"\", \"\"), Some(0));\n        assert_eq!(matcher.fuzzy_match(\"abcdefaghi\", \"\"), Some(0));\n        assert_eq!(matcher.fuzzy_match(\"\", \"a\"), None);\n        assert_eq!(matcher.fuzzy_match(\"abcdefaghi\", \"中\"), None);\n        assert_eq!(matcher.fuzzy_match(\"abc\", \"abx\"), None);\n        assert!(matcher.fuzzy_match(\"axbycz\", \"abc\").is_some());\n        assert!(matcher.fuzzy_match(\"axbycz\", \"xyz\").is_some());\n\n        assert_eq!(&wrap_fuzzy_match(&matcher, \"axbycz\", \"abc\").unwrap(), \"[a]x[b]y[c]z\");\n        assert_eq!(&wrap_fuzzy_match(&matcher, \"axbycz\", \"xyz\").unwrap(), \"a[x]b[y]c[z]\");\n        assert_eq!(\n            &wrap_fuzzy_match(&matcher, \"Hello, 世界\", \"H世\").unwrap(),\n            \"[H]ello, [世]界\"\n        );\n    }\n\n    #[test]\n    fn test_case_option_v2() {\n        let matcher = SkimMatcherV2::default().ignore_case();\n        assert!(matcher.fuzzy_match(\"aBc\", \"abc\").is_some());\n        assert!(matcher.fuzzy_match(\"aBc\", \"aBc\").is_some());\n        assert!(matcher.fuzzy_match(\"aBc\", \"aBC\").is_some());\n\n        let matcher = SkimMatcherV2::default().respect_case();\n        assert!(matcher.fuzzy_match(\"aBc\", \"abc\").is_none());\n        assert!(matcher.fuzzy_match(\"aBc\", \"aBc\").is_some());\n        assert!(matcher.fuzzy_match(\"aBc\", \"aBC\").is_none());\n\n        let matcher = SkimMatcherV2::default().smart_case();\n        assert!(matcher.fuzzy_match(\"aBc\", \"abc\").is_some());\n        assert!(matcher.fuzzy_match(\"aBc\", \"aBc\").is_some());\n        assert!(matcher.fuzzy_match(\"aBc\", \"aBC\").is_none());\n    }\n\n    #[test]\n    fn test_matcher_quality_v2() {\n        let matcher = SkimMatcherV2::default();\n        assert_order(&matcher, \"ab\", &[\"ab\", \"aoo_boo\", \"acb\"]);\n        assert_order(\n            &matcher,\n            \"cc\",\n            &[\"camel case\", \"camelCase\", \"CamelCase\", \"camelcase\", \"camel ace\"],\n        );\n        assert_order(\n            &matcher,\n            \"Da.Te\",\n            &[\"Data.Text\", \"Data.Text.Lazy\", \"Data.Aeson.Encoding.Text\"],\n        );\n        assert_order(&matcher, \"is\", &[\"isIEEE\", \"inSuf\"]);\n        assert_order(&matcher, \"ma\", &[\"map\", \"many\", \"maximum\"]);\n        assert_order(&matcher, \"print\", &[\"printf\", \"sprintf\"]);\n        assert_order(&matcher, \"ast\", &[\"ast\", \"AST\", \"INT_FAST16_MAX\"]);\n        assert_order(&matcher, \"int\", &[\"int\", \"INT\", \"PRINT\"]);\n    }\n\n    #[test]\n    fn test_reuse_should_not_affect_indices() {\n        let matcher = SkimMatcherV2::default();\n        let pattern = \"139\";\n        for num in 0..10000 {\n            let choice = num.to_string();\n            if let Some((_score, indices)) = matcher.fuzzy_indices(&choice, pattern) {\n                assert_eq!(indices.len(), 3);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/fuzzy_matcher/util.rs",
    "content": "use super::{FuzzyMatcher, IndexType, ScoreType};\n\npub fn cheap_matches(choice: &[char], pattern: &[char], case_sensitive: bool) -> Option<Vec<usize>> {\n    let mut first_match_indices = vec![];\n    let mut pattern_iter = pattern.iter().peekable();\n    for (idx, &c) in choice.iter().enumerate() {\n        match pattern_iter.peek() {\n            Some(&&p) => {\n                if char_equal(c, p, case_sensitive) {\n                    first_match_indices.push(idx);\n                    let _ = pattern_iter.next();\n                }\n            }\n            None => break,\n        }\n    }\n\n    if pattern_iter.peek().is_none() {\n        Some(first_match_indices)\n    } else {\n        None\n    }\n}\n\n/// Given 2 character, check if they are equal (considering ascii case)\n/// e.g. ('a', 'A', true) => false\n/// e.g. ('a', 'A', false) => true\n#[inline]\npub fn char_equal(a: char, b: char, case_sensitive: bool) -> bool {\n    if case_sensitive {\n        a == b\n    } else {\n        let a_lower = a.to_lowercase();\n        let mut b_lower = b.to_lowercase();\n        for a_n in a_lower {\n            let Some(b_n) = b_lower.next() else {\n                return false;\n            };\n            if a_n != b_n {\n                return false;\n            }\n        }\n        true\n    }\n}\n\n#[derive(Debug, PartialEq)]\npub enum CharType {\n    NonWord,\n    Lower,\n    Upper,\n    Number,\n}\n\n#[inline]\npub fn char_type_of(ch: char) -> CharType {\n    if ch.is_lowercase() {\n        CharType::Lower\n    } else if ch.is_uppercase() {\n        CharType::Upper\n    } else if ch.is_numeric() {\n        CharType::Number\n    } else {\n        CharType::NonWord\n    }\n}\n\n#[derive(Debug, PartialEq)]\npub enum CharRole {\n    Tail,\n    Head,\n}\n\n// checkout https://github.com/llvm-mirror/clang-tools-extra/blob/master/clangd/FuzzyMatch.cpp\n// The Role can be determined from the Type of a character and its neighbors:\n//\n//   Example  | Chars | Type | Role\n//   ---------+--------------+-----\n//   F(o)oBar | Foo   | Ull  | Tail\n//   Foo(B)ar | oBa   | lUl  | Head\n//   (f)oo    | ^fo   | Ell  | Head\n//   H(T)TP   | HTT   | UUU  | Tail\n//\n//      Curr= Empty Lower Upper Separ\n// Prev=Empty 0x00, 0xaa, 0xaa, 0xff, // At start, Lower|Upper->Head\n// Prev=Lower 0x00, 0x55, 0xaa, 0xff, // In word, Upper->Head;Lower->Tail\n// Prev=Upper 0x00, 0x55, 0x59, 0xff, // Ditto, but U(U)U->Tail\n// Prev=Separ 0x00, 0xaa, 0xaa, 0xff, // After separator, like at start\npub fn char_role(prev: char, cur: char) -> CharRole {\n    use self::CharRole::{Head, Tail};\n    use self::CharType::{Lower, NonWord, Upper};\n    match (char_type_of(prev), char_type_of(cur)) {\n        (Lower | NonWord, Upper) | (NonWord, Lower) => Head,\n        _ => Tail,\n    }\n}\n\n#[allow(dead_code)]\npub fn assert_order(matcher: &dyn FuzzyMatcher, pattern: &str, choices: &[&'static str]) {\n    let result = filter_and_sort(matcher, pattern, choices);\n\n    if result != choices {\n        // debug print\n        println!(\"pattern: {pattern}\");\n        for &choice in choices {\n            if let Some((score, indices)) = matcher.fuzzy_indices(choice, pattern) {\n                println!(\"{}: {:?}\", score, wrap_matches(choice, &indices));\n            } else {\n                println!(\"NO MATCH for {choice}\");\n            }\n        }\n    }\n\n    assert_eq!(result, choices);\n}\n\n#[allow(dead_code)]\npub fn filter_and_sort(matcher: &dyn FuzzyMatcher, pattern: &str, lines: &[&'static str]) -> Vec<&'static str> {\n    let mut lines_with_score: Vec<(ScoreType, &'static str)> = lines\n        .iter()\n        .filter_map(|&s| matcher.fuzzy_match(s, pattern).map(|score| (score, s)))\n        .collect();\n    lines_with_score.sort_by_key(|(score, _)| -score);\n    lines_with_score.into_iter().map(|(_, string)| string).collect()\n}\n\n#[allow(dead_code)]\npub fn wrap_matches(line: &str, indices: &[IndexType]) -> String {\n    let mut ret = String::new();\n    let mut peekable = indices.iter().peekable();\n    for (idx, ch) in line.chars().enumerate() {\n        let next_id = **peekable.peek().unwrap_or(&&(line.len() as IndexType));\n        if next_id == (idx as IndexType) {\n            ret.push_str(format!(\"[{ch}]\").as_str());\n            peekable.next();\n        } else {\n            ret.push(ch);\n        }\n    }\n\n    ret\n}\n"
  },
  {
    "path": "src/helper/item.rs",
    "content": "//! Skim item helpers\n//! Including the `DefaultSkimItem`\nuse crate::field::{FieldRange, parse_matching_fields, parse_transform_fields};\nuse crate::tui::util::merge_styles;\nuse crate::{DisplayContext, SkimItem};\nuse ansi_to_tui::IntoText;\nuse ratatui::text::{Line, Span};\nuse regex::Regex;\nuse std::borrow::Cow;\n\n//------------------------------------------------------------------------------\n/// An item will store everything that one line input will need to be operated and displayed.\n///\n/// What's special about an item?\n/// The simplest version of an item is a line of string, but things are getting more complex:\n/// - The conversion of lower/upper case is slow in rust, because it involds unicode.\n/// - We may need to interpret the ANSI codes in the text.\n/// - The text can be transformed and limited while searching.\n///\n/// About the ANSI, we made assumption that it is linewise, that means no ANSI codes will affect\n/// more than one line.\n#[derive(Debug)]\npub struct DefaultSkimItem {\n    /// The text that will be shown on screen.\n    text: Box<str>,\n\n    /// Metadata containing miscellaneous fields when special options are used\n    metadata: Option<Box<DefaultSkimItemMetadata>>,\n}\n\n/// Additional metadata for a `SkimItem`\n#[derive(Debug)]\npub struct DefaultSkimItemMetadata {\n    /// The text that will be output when user press `enter`\n    /// `Some(..)` => the original input is transformed, could not output `text` directly\n    /// `None` => that it is safe to output `text` directly\n    orig_text: Option<Box<str>>,\n\n    /// The text stripped of all ansi sequences, used for matching\n    /// Will be Some when ANSI is enabled, None otherwise\n    stripped_text: Option<Box<str>>,\n\n    /// A mapping of positions from stripped text to original text.\n    /// Each element is (`byte_position`, `char_position`) in the original raw text.\n    /// Will be empty if ansi is disabled.\n    ansi_info: Option<Vec<(usize, usize)>>,\n\n    // The ranges on which to perform matching\n    matching_ranges: Option<Vec<(usize, usize)>>,\n}\n\nimpl DefaultSkimItem {\n    /// Create a new `DefaultSkimItem` from text\n    #[must_use]\n    pub fn new(\n        orig_text: &str,\n        ansi_enabled: bool,\n        trans_fields: &[FieldRange],\n        matching_fields: &[FieldRange],\n        delimiter: &Regex,\n    ) -> Self {\n        let using_transform_fields = !trans_fields.is_empty();\n        let contains_ansi = Self::contains_ansi_escape(orig_text);\n\n        //        transformed | ANSI             | output\n        //------------------------------------------------------\n        //                    +- T -> trans+ANSI | ANSI\n        //                    |                  |\n        //      +- T -> trans +- F -> trans      | orig\n        // orig |                                |\n        //      +- F -> orig  +- T -> ANSI     ==| ANSI\n        //                    |                  |\n        //                    +- F -> orig       | orig\n\n        let (mut orig_text, mut temp_text): (Option<String>, Box<str>) = match (using_transform_fields, ansi_enabled) {\n            (true, true) => {\n                let transformed = parse_transform_fields(delimiter, orig_text, trans_fields);\n                (Some(orig_text.into()), Box::from(transformed))\n            }\n            (true, false) => {\n                let transformed = parse_transform_fields(delimiter, &escape_ansi(orig_text), trans_fields);\n                (Some(orig_text.into()), Box::from(transformed))\n            }\n            (false, false) if contains_ansi => (None, escape_ansi(orig_text).into()),\n            (false, true | false) => (None, Box::from(orig_text)),\n        };\n\n        // Keep track of whether we have null bytes for special handling\n        let has_null_bytes = temp_text.contains('\\0');\n\n        // Preserve original text with null bytes for output if needed\n        if has_null_bytes && orig_text.is_none() {\n            orig_text = Some(temp_text.to_string());\n        }\n\n        // Strip null bytes from text used for display and matching\n        // Null bytes are control characters that cause rendering issues (zero-width)\n        // They are preserved in orig_text for output\n        if has_null_bytes {\n            temp_text = temp_text.to_string().replace('\\0', \"\").into_boxed_str();\n        }\n\n        let (stripped_text, ansi_info) = if ansi_enabled && contains_ansi {\n            let (stripped, info) = strip_ansi(&temp_text);\n            (Some(stripped), Some(info))\n        } else {\n            (None, None)\n        };\n\n        // Calculate matching ranges on text WITHOUT null bytes (after stripping)\n        // This ensures the byte positions match the actual text used for matching\n        let matching_ranges = if matching_fields.is_empty() {\n            None\n        } else {\n            // Use stripped text for matching ranges when ANSI is enabled\n            let text_for_matching = if let Some(stripped) = stripped_text.as_ref() {\n                stripped\n            } else {\n                temp_text.as_ref()\n            };\n\n            // Parse the original text with null bytes to determine field boundaries\n            // Then extract those fields, strip null bytes, and recalculate positions\n            // When has_null_bytes is true, orig_text was set to Some above, so unwrap is safe.\n            let orig_text_for_fields = if has_null_bytes {\n                orig_text.as_deref().unwrap_or(text_for_matching)\n            } else {\n                text_for_matching\n            };\n\n            if has_null_bytes {\n                // Extract each field from the original text (with null bytes)\n                // then strip null bytes and build new ranges in the cleaned text\n                let mut adjusted_ranges = Vec::new();\n\n                for field in matching_fields {\n                    // Get the field text from original (with null bytes)\n                    if let Some(field_text) = crate::field::get_string_by_field(delimiter, orig_text_for_fields, field)\n                    {\n                        // Strip null bytes from this field\n                        let cleaned_field = field_text.replace('\\0', \"\");\n\n                        // Find this cleaned field in the cleaned full text\n                        if let Some(pos) = text_for_matching.find(&cleaned_field) {\n                            adjusted_ranges.push((pos, pos + cleaned_field.len()));\n                        }\n                    }\n                }\n                Some(adjusted_ranges)\n            } else {\n                Some(parse_matching_fields(delimiter, text_for_matching, matching_fields))\n            }\n        };\n\n        let metadata =\n            if orig_text.is_some() || stripped_text.is_some() || ansi_info.is_some() || matching_ranges.is_some() {\n                Some(Box::new(DefaultSkimItemMetadata {\n                    orig_text: orig_text.map(std::string::String::into_boxed_str),\n                    stripped_text: stripped_text.map(std::string::String::into_boxed_str),\n                    ansi_info,\n                    matching_ranges,\n                }))\n            } else {\n                None\n            };\n\n        DefaultSkimItem {\n            text: temp_text,\n            metadata,\n        }\n    }\n\n    fn contains_ansi_escape(s: &str) -> bool {\n        s.contains('\\x1b')\n    }\n\n    /// Getter for `stripped_text` stored in the metadata\n    #[must_use]\n    pub fn stripped_text(&self) -> Option<&str> {\n        if let Some(meta) = &self.metadata\n            && let Some(stripped_text) = &meta.stripped_text\n        {\n            Some(stripped_text.as_ref())\n        } else {\n            None\n        }\n    }\n\n    /// Getter for `orig_text` stored in metadata\n    #[must_use]\n    pub fn orig_text(&self) -> Option<&str> {\n        if let Some(meta) = &self.metadata\n            && let Some(orig) = &meta.orig_text\n        {\n            Some(orig.as_ref())\n        } else {\n            None\n        }\n    }\n\n    /// Getter for `ansi_info` stored in metadata\n    #[must_use]\n    pub fn ansi_info(&self) -> Option<&Vec<(usize, usize)>> {\n        if let Some(meta) = &self.metadata\n            && let Some(info) = &meta.ansi_info\n        {\n            Some(info)\n        } else {\n            None\n        }\n    }\n\n    /// Getter for `matching_ranges` stored in metadata\n    #[must_use]\n    pub fn matching_ranges(&self) -> Option<&[(usize, usize)]> {\n        if let Some(meta) = &self.metadata {\n            meta.matching_ranges.as_ref().map(|v| v.as_ref() as &[(usize, usize)])\n        } else {\n            None\n        }\n    }\n}\n\nimpl DefaultSkimItem {\n    /// Get the display text (with ANSI codes if present) for rendering purposes\n    #[inline]\n    #[allow(dead_code)]\n    #[must_use]\n    pub fn get_display_text(&self) -> &str {\n        &self.text\n    }\n}\n\nimpl From<String> for DefaultSkimItem {\n    fn from(value: String) -> Self {\n        Self {\n            text: Box::from(value),\n            metadata: None,\n        }\n    }\n}\n\nimpl SkimItem for DefaultSkimItem {\n    #[inline]\n    fn text(&self) -> Cow<'_, str> {\n        // Return stripped text for matching when ANSI is enabled\n        if let Some(stripped) = self.stripped_text() {\n            Cow::Borrowed(stripped)\n        } else {\n            Cow::Borrowed(&self.text)\n        }\n    }\n\n    fn output(&self) -> Cow<'_, str> {\n        if let Some(orig) = self.orig_text() {\n            Cow::Borrowed(orig)\n        } else {\n            Cow::Borrowed(&self.text)\n        }\n    }\n\n    fn get_matching_ranges(&self) -> Option<&[(usize, usize)]> {\n        // Return matching ranges if present in metadata\n        self.matching_ranges()\n    }\n\n    // The display function handles ANSI stripping, field highlighting, and match\n    // rendering in a single pass; splitting it would require duplicating context handling.\n    #[allow(clippy::too_many_lines)]\n    fn display(&self, context: DisplayContext) -> Line<'_> {\n        // If we have ANSI info, we need to handle ANSI codes properly and map matches\n        if self.ansi_info().is_some() {\n            // Parse the ANSI text using ansi-to-tui to get proper styled spans\n            let text_bytes = self.text.as_bytes().to_vec();\n            let Ok(parsed_text) = text_bytes.into_text() else {\n                // Fallback to plain text if parsing fails\n                return context.to_line(Cow::Borrowed(&self.text));\n            };\n\n            // Extract all spans from the parsed text (should be a single line)\n            let all_spans: Vec<Span> = parsed_text.lines.into_iter().flat_map(|line| line.spans).collect();\n\n            // Now apply highlighting based on matched positions\n            // We need to map match positions from stripped text to original text\n            match context.matches {\n                crate::Matches::CharIndices(ref indices) => {\n                    // Indices are already in stripped text coordinates (same as parsed ANSI text)\n                    // No need to remap since both matching and ANSI parsing strip the codes\n                    let highlight_positions: std::collections::HashSet<usize> = indices.iter().copied().collect();\n\n                    // Apply highlighting to characters at those positions\n                    let mut new_spans = Vec::new();\n                    let mut char_idx = 0;\n\n                    for span in all_spans {\n                        let mut current_content = String::new();\n                        let mut highlighted_content = String::new();\n                        let base_style = span.style;\n\n                        for ch in span.content.chars() {\n                            if highlight_positions.contains(&char_idx) {\n                                // Flush normal content if any\n                                if !current_content.is_empty() {\n                                    // Combine ANSI style with context base_style\n                                    new_spans.push(Span::styled(\n                                        current_content.clone(),\n                                        merge_styles(context.base_style, base_style),\n                                    ));\n                                    current_content.clear();\n                                }\n                                highlighted_content.push(ch);\n                            } else {\n                                // Flush highlighted content if any\n                                if !highlighted_content.is_empty() {\n                                    // Combine styles: use highlight bg, preserve ANSI fg and modifiers\n                                    new_spans.push(Span::styled(\n                                        highlighted_content.clone(),\n                                        merge_styles(base_style, context.matched_style),\n                                    ));\n                                    highlighted_content.clear();\n                                }\n                                current_content.push(ch);\n                            }\n                            char_idx += 1;\n                        }\n\n                        // Flush remaining content\n                        if !current_content.is_empty() {\n                            // Combine ANSI style with context base_style\n                            new_spans.push(Span::styled(\n                                current_content,\n                                merge_styles(context.base_style, base_style),\n                            ));\n                        }\n                        if !highlighted_content.is_empty() {\n                            // Combine styles: use highlight bg, preserve ANSI fg and modifiers\n                            new_spans.push(Span::styled(\n                                highlighted_content,\n                                merge_styles(base_style, context.matched_style),\n                            ));\n                        }\n                    }\n\n                    Line::from(new_spans)\n                }\n                crate::Matches::CharRange(start, end) => {\n                    // Positions are already in stripped text coordinates (same as parsed ANSI text)\n                    // No need to remap since both matching and ANSI parsing strip the codes\n\n                    // Apply highlighting to the range\n                    let mut new_spans = Vec::new();\n                    let mut char_idx = 0;\n\n                    for span in all_spans {\n                        let mut before = String::new();\n                        let mut highlighted = String::new();\n                        let mut after = String::new();\n                        let base_style = span.style;\n\n                        for ch in span.content.chars() {\n                            if char_idx < start {\n                                before.push(ch);\n                            } else if char_idx < end {\n                                highlighted.push(ch);\n                            } else {\n                                after.push(ch);\n                            }\n                            char_idx += 1;\n                        }\n\n                        if !before.is_empty() {\n                            // Combine ANSI style with context base_style\n                            new_spans.push(Span::styled(before, merge_styles(context.base_style, base_style)));\n                        }\n                        if !highlighted.is_empty() {\n                            // Combine ANSI style with context matched_style\n                            new_spans.push(Span::styled(\n                                highlighted,\n                                merge_styles(base_style, context.matched_style),\n                            ));\n                        }\n                        if !after.is_empty() {\n                            // Combine ANSI style with context base_style\n                            new_spans.push(Span::styled(after, merge_styles(context.base_style, base_style)));\n                        }\n                    }\n\n                    Line::from(new_spans)\n                }\n                crate::Matches::ByteRange(start, end) => {\n                    // Convert byte positions to char positions in stripped text\n                    let stripped = self.stripped_text().unwrap();\n                    let char_start = stripped.get(0..start).map_or(0, |s| s.chars().count());\n                    let char_end = stripped\n                        .get(0..end)\n                        .map_or(stripped.chars().count(), |s| s.chars().count());\n\n                    // Apply highlighting to the range\n                    let mut new_spans = Vec::new();\n                    let mut char_idx = 0;\n\n                    for span in all_spans {\n                        let mut before = String::new();\n                        let mut highlighted = String::new();\n                        let mut after = String::new();\n                        let base_style = span.style;\n\n                        for ch in span.content.chars() {\n                            if char_idx < char_start {\n                                before.push(ch);\n                            } else if char_idx < char_end {\n                                highlighted.push(ch);\n                            } else {\n                                after.push(ch);\n                            }\n                            char_idx += 1;\n                        }\n\n                        if !before.is_empty() {\n                            // Combine ANSI style with context base_style\n                            new_spans.push(Span::styled(before, merge_styles(context.base_style, base_style)));\n                        }\n                        if !highlighted.is_empty() {\n                            // Combine ANSI style with context matched_style\n                            new_spans.push(Span::styled(\n                                highlighted,\n                                merge_styles(base_style, context.matched_style),\n                            ));\n                        }\n                        if !after.is_empty() {\n                            // Combine ANSI style with context base_style\n                            new_spans.push(Span::styled(after, merge_styles(context.base_style, base_style)));\n                        }\n                    }\n\n                    Line::from(new_spans)\n                }\n                crate::Matches::None => Line::from(all_spans),\n            }\n        } else {\n            // No ANSI mapping needed, use text as-is\n            context.to_line(Cow::Borrowed(&self.text))\n        }\n    }\n}\n\n/// Strip ANSI escape sequences from a string\n///\n/// This function removes all ANSI escape codes (CSI sequences, OSC sequences, etc.)\n/// from the input string, leaving only the visible text.\n///\n/// Returns the stripped string as well as a mapping of positions. Each element in the\n/// mapping vector is a tuple `(byte_position, char_position)` where:\n/// - `byte_position`: The byte offset in the original raw string\n/// - `char_position`: The character index in the original raw string\n///\n/// For the character at position `i` in the stripped string:\n/// - `mapping[i].0` gives its byte position in the original string\n/// - `mapping[i].1` gives its character index in the original string\n///\n/// Examples of ANSI codes that are stripped:\n/// - `\\x1b[31m` (set foreground color to red)\n/// - `\\x1b[01;32m` (bold green)\n/// - `\\x1b[0m` (reset)\n/// - `\\x1b]0;title\\x07` (OSC sequences)\n#[must_use]\npub fn strip_ansi(text: &str) -> (String, Vec<(usize, usize)>) {\n    let mut result = String::with_capacity(text.len());\n    let mut index_mapping = Vec::new();\n    let mut chars = text.char_indices().peekable();\n    let mut char_idx = 0;\n\n    while let Some((byte_pos, ch)) = chars.next() {\n        if ch == '\\x1b' {\n            // ESC sequence detected\n            if let Some(&(_, next_ch)) = chars.peek() {\n                match next_ch {\n                    '[' => {\n                        // CSI sequence: ESC [ ... (ending with a letter)\n                        chars.next(); // consume '['\n                        char_idx += 1;\n                        while let Some(&(_, c)) = chars.peek() {\n                            chars.next();\n                            char_idx += 1;\n                            if c.is_ascii_alphabetic() {\n                                break;\n                            }\n                        }\n                    }\n                    ']' => {\n                        // OSC sequence: ESC ] ... (ending with BEL or ESC \\)\n                        chars.next(); // consume ']'\n                        char_idx += 1;\n                        while let Some((_, c)) = chars.next() {\n                            char_idx += 1;\n                            if c == '\\x07' {\n                                // BEL\n                                break;\n                            }\n                            if c == '\\x1b'\n                                && let Some(&(_, '\\\\')) = chars.peek()\n                            {\n                                chars.next(); // consume '\\'\n                                char_idx += 1;\n                                break;\n                            }\n                        }\n                    }\n                    '(' | ')' | '#' | '%' => {\n                        // Other escape sequences\n                        chars.next(); // consume the next char\n                        char_idx += 1;\n                        chars.next(); // and one more\n                        char_idx += 1;\n                    }\n                    _ => {\n                        // Unknown escape sequence, consume next char\n                        chars.next();\n                        char_idx += 1;\n                    }\n                }\n            }\n        } else {\n            result.push(ch);\n            index_mapping.push((byte_pos, char_idx));\n        }\n        char_idx += 1;\n    }\n\n    (result, index_mapping)\n}\n\n/// Replace the ANSI ESC code by a ?\n///\n/// Unsafe: bytes are parsed back from the original string or b'?'\n/// No risk associated\nfn escape_ansi(raw: &str) -> String {\n    unsafe { String::from_utf8_unchecked(raw.bytes().map(|b| if b == 27 { b'?' } else { b }).collect()) }\n}\n\n#[cfg(test)]\nmod test {\n    use super::*;\n\n    #[test]\n    fn test_strip_ansi() {\n        // Test basic ANSI color codes\n        // \"\\x1b[31mred\\x1b[0m\" has chars at positions: 0=ESC, 1=[, 2=3, 3=1, 4=m, 5=r, 6=e, 7=d, 8=ESC, 9=[, 10=0, 11=m\n        let (text, mapping) = strip_ansi(\"\\x1b[31mred\\x1b[0m\");\n        assert_eq!(text, \"red\");\n        assert_eq!(mapping, vec![(5, 5), (6, 6), (7, 7)]);\n\n        let (text, mapping) = strip_ansi(\"\\x1b[01;32mgreen\\x1b[0m\");\n        assert_eq!(text, \"green\");\n        assert_eq!(mapping, vec![(8, 8), (9, 9), (10, 10), (11, 11), (12, 12)]);\n\n        let (text, mapping) = strip_ansi(\"\\x1b[01;34mblue\\x1b[0m\");\n        assert_eq!(text, \"blue\");\n        assert_eq!(mapping, vec![(8, 8), (9, 9), (10, 10), (11, 11)]);\n\n        // Test text without ANSI codes\n        let (text, mapping) = strip_ansi(\"plain text\");\n        assert_eq!(text, \"plain text\");\n        assert_eq!(\n            mapping,\n            vec![\n                (0, 0),\n                (1, 1),\n                (2, 2),\n                (3, 3),\n                (4, 4),\n                (5, 5),\n                (6, 6),\n                (7, 7),\n                (8, 8),\n                (9, 9)\n            ]\n        );\n\n        // Test multiple ANSI sequences\n        let (text, mapping) = strip_ansi(\"\\x1b[31mred\\x1b[0m and \\x1b[32mgreen\\x1b[0m\");\n        assert_eq!(text, \"red and green\");\n        assert_eq!(\n            mapping,\n            vec![\n                (5, 5),\n                (6, 6),\n                (7, 7),\n                (12, 12),\n                (13, 13),\n                (14, 14),\n                (15, 15),\n                (16, 16),\n                (22, 22),\n                (23, 23),\n                (24, 24),\n                (25, 25),\n                (26, 26)\n            ]\n        );\n\n        // Test ANSI codes in the middle of text\n        let (text, mapping) = strip_ansi(\"be\\x1b[01;34mf\\x1b[0more\");\n        assert_eq!(text, \"before\");\n        assert_eq!(mapping, vec![(0, 0), (1, 1), (10, 10), (15, 15), (16, 16), (17, 17)]);\n\n        // Test real ls --color output\n        let (text, mapping) = strip_ansi(\"\\x1b[01;32mbench.sh\\x1b[0m\");\n        assert_eq!(text, \"bench.sh\");\n        assert_eq!(\n            mapping,\n            vec![\n                (8, 8),\n                (9, 9),\n                (10, 10),\n                (11, 11),\n                (12, 12),\n                (13, 13),\n                (14, 14),\n                (15, 15)\n            ]\n        );\n\n        let (text, mapping) = strip_ansi(\"\\x1b[01;34mbin\\x1b[0m\");\n        assert_eq!(text, \"bin\");\n        assert_eq!(mapping, vec![(8, 8), (9, 9), (10, 10)]);\n\n        // Test with multi-byte UTF-8 characters to verify byte vs char position difference\n        // \"😀\" is 4 bytes but 1 char - when followed by ANSI codes, byte and char positions diverge\n        let (text, mapping) = strip_ansi(\"😀\\x1b[32mtext\\x1b[0m\");\n        assert_eq!(text, \"😀text\");\n        // Original: \"😀\\x1b[32mtext\\x1b[0m\"\n        // byte positions: 😀=0-3, \\x1b=4, [=5, 3=6, 2=7, m=8, t=9, e=10, x=11, t=12, \\x1b=13, [=14, 0=15, m=16\n        // char positions: 😀=0, \\x1b=1, [=2, 3=3, 2=4, m=5, t=6, e=7, x=8, t=9, \\x1b=10, [=11, 0=12, m=13\n        // After stripping: \"😀text\"\n        // stripped[0]='😀' -> (byte=0, char=0)\n        // stripped[1]='t' -> (byte=9, char=6) <- Here byte and char positions differ!\n        assert_eq!(mapping, vec![(0, 0), (9, 6), (10, 7), (11, 8), (12, 9)]);\n    }\n\n    #[test]\n    fn test_ansi_matching_and_display() {\n        use crate::{DisplayContext, Matches, SkimItem};\n        use ratatui::style::{Color, Style};\n        use regex::Regex;\n\n        // Create an item with ANSI codes\n        let input = \"\\x1b[32mgreen\\x1b[0m text\";\n        let delimiter = Regex::new(r\"\\s+\").unwrap();\n        let item = DefaultSkimItem::new(\n            input,\n            true, // ansi_enabled\n            &[],\n            &[],\n            &delimiter,\n        );\n\n        // text() should return stripped text for matching\n        assert_eq!(item.text(), \"green text\");\n\n        // Verify we have ANSI info\n        assert!(item.ansi_info().is_some());\n\n        // Create a match context as if we matched \"text\" (positions 6-10 in stripped string)\n        let context = DisplayContext {\n            score: 100,\n            matches: Matches::CharRange(6, 10),\n            container_width: 80,\n            base_style: Style::default(),\n            matched_style: Style::default().fg(Color::Yellow),\n        };\n\n        // display() should map the match positions back to the original ANSI text\n        let line = item.display(context);\n\n        // The line should have the original ANSI codes intact\n        // We can't easily verify the exact ANSI codes in the output, but we can check\n        // that it's not empty and has multiple spans (original text + highlighted match)\n        assert!(!line.spans.is_empty());\n    }\n\n    #[test]\n    fn test_ansi_char_indices_mapping() {\n        use crate::{DisplayContext, Matches, SkimItem};\n        use ratatui::style::{Color, Style};\n        use regex::Regex;\n\n        // Create an item with ANSI codes: \"😀\\x1b[32mtext\\x1b[0m\"\n        let input = \"😀\\x1b[32mtext\\x1b[0m\";\n        let delimiter = Regex::new(r\"\\s+\").unwrap();\n        let item = DefaultSkimItem::new(\n            input,\n            true, // ansi_enabled\n            &[],\n            &[],\n            &delimiter,\n        );\n\n        // text() should return \"😀text\"\n        assert_eq!(item.text(), \"😀text\");\n\n        // Match indices 1,2 in stripped text (the 't' and 'e')\n        let context = DisplayContext {\n            score: 100,\n            matches: Matches::CharIndices(vec![1, 2]),\n            container_width: 80,\n            base_style: Style::default(),\n            matched_style: Style::default().fg(Color::Yellow),\n        };\n\n        // display() should map these to positions 6,7 in original text\n        let line = item.display(context);\n        assert!(!line.spans.is_empty());\n    }\n\n    #[test]\n    fn test_text_returns_stripped() {\n        use crate::SkimItem;\n        use regex::Regex;\n\n        let delimiter = Regex::new(r\"\\s+\").unwrap();\n\n        // Test with ANSI enabled\n        let item_ansi = DefaultSkimItem::new(\n            \"\\x1b[31mred\\x1b[0m\",\n            true, // ansi_enabled\n            &[],\n            &[],\n            &delimiter,\n        );\n        assert_eq!(\n            item_ansi.text(),\n            \"red\",\n            \"text() should return stripped text when ANSI is enabled\"\n        );\n\n        // Test with ANSI disabled\n        let item_no_ansi = DefaultSkimItem::new(\n            \"\\x1b[31mred\\x1b[0m\",\n            false, // ansi_enabled\n            &[],\n            &[],\n            &delimiter,\n        );\n        assert_eq!(\n            item_no_ansi.text(),\n            \"?[31mred?[0m\",\n            \"text() should return text with ? when ANSI is disabled\"\n        );\n    }\n\n    #[test]\n    fn test_highlighting_applied() {\n        use crate::{DisplayContext, Matches, SkimItem};\n        use ratatui::style::{Color, Style};\n        use regex::Regex;\n\n        let delimiter = Regex::new(r\"\\s+\").unwrap();\n\n        // Create item with ANSI codes: \"\\x1b[32mgreen\\x1b[0m\"\n        let item = DefaultSkimItem::new(\n            \"\\x1b[32mgreen\\x1b[0m\",\n            true, // ansi_enabled\n            &[],\n            &[],\n            &delimiter,\n        );\n\n        // Create display context with yellow background highlight for character 0 (the 'g')\n        let context = DisplayContext {\n            score: 100,\n            matches: Matches::CharIndices(vec![0]),\n            container_width: 80,\n            base_style: Style::default(),\n            matched_style: Style::default().bg(Color::Yellow),\n        };\n\n        let line = item.display(context);\n\n        // The line should have spans with highlighting\n        // At least one span should have the yellow background\n        let has_highlight = line.spans.iter().any(|span| span.style.bg == Some(Color::Yellow));\n        assert!(has_highlight, \"Highlighted character should have yellow background\");\n\n        // The green foreground from ANSI should be preserved in at least one span\n        let has_green_fg = line.spans.iter().any(|span| span.style.fg == Some(Color::Green));\n        assert!(has_green_fg, \"ANSI green foreground should be preserved\");\n    }\n\n    #[test]\n    fn test_char_range_highlighting() {\n        use crate::{DisplayContext, Matches, SkimItem};\n        use ratatui::style::{Color, Style};\n        use regex::Regex;\n\n        let delimiter = Regex::new(r\"\\s+\").unwrap();\n\n        // Create item with ANSI codes: \"\\x1b[32mgreen\\x1b[0m\"\n        let item = DefaultSkimItem::new(\n            \"\\x1b[32mgreen\\x1b[0m\",\n            true, // ansi_enabled\n            &[],\n            &[],\n            &delimiter,\n        );\n\n        // Create display context with yellow background highlight for characters 1-3 ('re')\n        let context = DisplayContext {\n            score: 100,\n            matches: Matches::CharRange(1, 3),\n            container_width: 80,\n            base_style: Style::default(),\n            matched_style: Style::default().bg(Color::Yellow),\n        };\n\n        let line = item.display(context);\n\n        // Should have multiple spans: before, highlighted, after\n        assert!(line.spans.len() >= 2, \"Should have multiple spans for highlighting\");\n\n        // At least one span should have the yellow background (the highlighted portion)\n        let has_highlight = line.spans.iter().any(|span| span.style.bg == Some(Color::Yellow));\n        assert!(has_highlight, \"Highlighted characters should have yellow background\");\n\n        // The green foreground from ANSI should be preserved\n        let has_green_fg = line.spans.iter().any(|span| span.style.fg == Some(Color::Green));\n        assert!(has_green_fg, \"ANSI green foreground should be preserved\");\n    }\n\n    #[test]\n    fn test_byte_range_highlighting() {\n        use crate::{DisplayContext, Matches, SkimItem};\n        use ratatui::style::{Color, Style};\n        use regex::Regex;\n\n        let delimiter = Regex::new(r\"\\s+\").unwrap();\n\n        // Create item with ANSI codes: \"\\x1b[32mgreen\\x1b[0m\"\n        let item = DefaultSkimItem::new(\n            \"\\x1b[32mgreen\\x1b[0m\",\n            true, // ansi_enabled\n            &[],\n            &[],\n            &delimiter,\n        );\n\n        // Create display context with yellow background highlight for bytes 1-3 ('re' in stripped text)\n        let context = DisplayContext {\n            score: 100,\n            matches: Matches::ByteRange(1, 3),\n            container_width: 80,\n            base_style: Style::default(),\n            matched_style: Style::default().bg(Color::Yellow),\n        };\n\n        let line = item.display(context);\n\n        // Should have multiple spans for highlighting\n        assert!(!line.spans.is_empty(), \"Should have spans\");\n\n        // At least one span should have the yellow background (the highlighted portion)\n        let has_highlight = line.spans.iter().any(|span| span.style.bg == Some(Color::Yellow));\n        assert!(has_highlight, \"Highlighted bytes should have yellow background\");\n\n        // The green foreground from ANSI should be preserved\n        let has_green_fg = line.spans.iter().any(|span| span.style.fg == Some(Color::Green));\n        assert!(has_green_fg, \"ANSI green foreground should be preserved\");\n    }\n\n    #[test]\n    fn test_matching_with_ansi_basic() {\n        use crate::SkimItem;\n        use regex::Regex;\n\n        let delimiter = Regex::new(r\"\\s+\").unwrap();\n\n        // Create item with ANSI codes: \"\\x1b[32mgreen_text\\x1b[0m\"\n        let item = DefaultSkimItem::new(\n            \"\\x1b[32mgreen_text\\x1b[0m\",\n            true, // ansi_enabled\n            &[],\n            &[], // no matching fields restriction\n            &delimiter,\n        );\n\n        // text() should return stripped text \"green_text\"\n        assert_eq!(item.text(), \"green_text\");\n\n        // With no matching_fields, get_matching_ranges should return None (match whole text)\n        assert!(item.get_matching_ranges().is_none());\n\n        // Verify the stripped_text and ansi_info are populated correctly\n        assert!(item.stripped_text().is_some());\n        assert!(item.ansi_info().is_some());\n        assert_eq!(item.stripped_text().unwrap(), \"green_text\");\n    }\n\n    #[test]\n    fn test_null_delimiter_with_matching_fields() {\n        use crate::SkimItem;\n        use crate::field::FieldRange;\n        use regex::Regex;\n\n        // Test with null byte delimiter and matching_fields\n        let delimiter = Regex::new(\"\\x00\").unwrap();\n        let text = \"a\\x00b\\x00c\";\n\n        // Create item with matching field 2\n        let item = DefaultSkimItem::new(\n            text,\n            false,                    // no ansi\n            &[],                      // no transform fields\n            &[FieldRange::Single(2)], // match field 2\n            &delimiter,\n        );\n\n        // text() should return text with null bytes stripped for display\n        assert_eq!(item.text(), \"abc\");\n\n        // get_matching_ranges should return the range for field 2 in the stripped text\n        let ranges = item.get_matching_ranges().expect(\"Should have matching ranges\");\n        assert_eq!(ranges.len(), 1, \"Should have one matching range\");\n\n        // Field 2 is \"b\" which is at position 1 in the stripped text \"abc\"\n        assert_eq!(ranges[0], (1, 2), \"Field 2 should be at position 1-2 in stripped text\");\n\n        // Verify the substring matches what we expect\n        let stripped_text = item.text();\n        let field_text = &stripped_text[ranges[0].0..ranges[0].1];\n        assert_eq!(field_text, \"b\", \"Field text should be 'b'\");\n    }\n}\n"
  },
  {
    "path": "src/helper/item_reader.rs",
    "content": "//! Helper utilities for converting input sources into skim item streams.\n\nuse std::error::Error;\nuse std::io::{BufRead, BufReader};\nuse std::process::{Child, Command, Stdio};\nuse std::sync::Arc;\nuse std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};\nuse std::thread;\nuse std::time::{Duration, Instant};\n\nuse regex::Regex;\n\nuse crate::field::FieldRange;\nuse crate::helper::item::DefaultSkimItem;\nuse crate::reader::CommandCollector;\nuse crate::{SkimItem, SkimItemReceiver, SkimItemSender, SkimOptions};\n\nconst DELIMITER_STR: &str = r\"[\\t\\n ]+\";\nconst READ_BUFFER_SIZE: usize = 1024;\nconst ITEMS_BUFFER_SIZE: usize = 1024;\nconst SEND_TIMEOUT_MS: u64 = 100; // Send items if we haven't sent anything in 100ms\n\npub enum CollectorInput {\n    Pipe(Box<dyn BufRead + Send>),\n    Command(String),\n}\n\n/// Options for configuring how items are read and parsed\n#[derive(Debug)]\npub struct SkimItemReaderOption {\n    buf_size: usize,\n    use_ansi_color: bool,\n    transform_fields: Vec<FieldRange>,\n    matching_fields: Vec<FieldRange>,\n    delimiter: Regex,\n    line_ending: u8,\n    show_error: bool,\n}\n\nimpl Default for SkimItemReaderOption {\n    fn default() -> Self {\n        Self {\n            buf_size: READ_BUFFER_SIZE,\n            line_ending: b'\\n',\n            use_ansi_color: false,\n            transform_fields: Vec::new(),\n            matching_fields: Vec::new(),\n            delimiter: Regex::new(DELIMITER_STR).unwrap(),\n            show_error: false,\n        }\n    }\n}\n\nimpl SkimItemReaderOption {\n    /// Creates reader options from skim options\n    #[must_use]\n    pub fn from_options(options: &SkimOptions) -> Self {\n        Self {\n            buf_size: READ_BUFFER_SIZE,\n            line_ending: if options.read0 { b'\\0' } else { b'\\n' },\n            use_ansi_color: options.ansi,\n            transform_fields: options\n                .with_nth\n                .iter()\n                .filter_map(|f| if f.is_empty() { None } else { FieldRange::from_str(f) })\n                .collect(),\n            matching_fields: options\n                .nth\n                .iter()\n                .filter_map(|f| if f.is_empty() { None } else { FieldRange::from_str(f) })\n                .collect(),\n            delimiter: options.delimiter.clone(),\n            show_error: options.show_cmd_error,\n        }\n    }\n\n    /// Sets the buffer size for reading\n    #[must_use]\n    pub fn buf_size(mut self, buf_size: usize) -> Self {\n        self.buf_size = buf_size;\n        self\n    }\n\n    /// Sets the line ending character (default: '\\n')\n    #[must_use]\n    pub fn line_ending(mut self, line_ending: u8) -> Self {\n        self.line_ending = line_ending;\n        self\n    }\n\n    /// Enables or disables ANSI color code parsing\n    #[must_use]\n    pub fn ansi(mut self, enable: bool) -> Self {\n        self.use_ansi_color = enable;\n        self\n    }\n\n    /// Sets the field delimiter regex\n    #[must_use]\n    pub fn delimiter(mut self, delimiter: Regex) -> Self {\n        self.delimiter = delimiter;\n        self\n    }\n\n    /// Sets the fields to display (transform) from the input\n    #[must_use]\n    pub fn with_nth<'a, T>(mut self, with_nth: T) -> Self\n    where\n        T: Iterator<Item = &'a str>,\n    {\n        self.transform_fields = with_nth.filter_map(FieldRange::from_str).collect();\n        self\n    }\n\n    /// Sets the transform fields directly\n    #[must_use]\n    pub fn transform_fields(mut self, transform_fields: Vec<FieldRange>) -> Self {\n        self.transform_fields = transform_fields;\n        self\n    }\n\n    /// Sets the fields to use for matching\n    #[must_use]\n    pub fn nth<'a, T>(mut self, nth: T) -> Self\n    where\n        T: Iterator<Item = &'a str>,\n    {\n        self.matching_fields = nth.filter_map(FieldRange::from_str).collect();\n        self\n    }\n\n    /// Sets the matching fields directly\n    #[must_use]\n    pub fn matching_fields(mut self, matching_fields: Vec<FieldRange>) -> Self {\n        self.matching_fields = matching_fields;\n        self\n    }\n\n    /// Enables reading null-terminated lines instead of newline-terminated\n    #[must_use]\n    pub fn read0(mut self, enable: bool) -> Self {\n        if enable {\n            self.line_ending = b'\\0';\n        } else {\n            self.line_ending = b'\\n';\n        }\n        self\n    }\n\n    /// Sets whether to show command errors\n    #[must_use]\n    pub fn show_error(mut self, show_error: bool) -> Self {\n        self.show_error = show_error;\n        self\n    }\n\n    /// Builds the options (currently a no-op, returns self)\n    #[must_use]\n    pub fn build(self) -> Self {\n        self\n    }\n\n    /// Returns true if no field transformations or ANSI parsing is needed\n    #[must_use]\n    pub fn is_simple(&self) -> bool {\n        !self.use_ansi_color && self.matching_fields.is_empty() && self.transform_fields.is_empty()\n    }\n}\n\n/// Reader for converting various input sources into streams of skim items\npub struct SkimItemReader {\n    option: Arc<SkimItemReaderOption>,\n}\n\nimpl Default for SkimItemReader {\n    fn default() -> Self {\n        Self {\n            option: Arc::new(Default::default()),\n        }\n    }\n}\n\nimpl SkimItemReader {\n    /// Creates a new item reader with the given options\n    #[must_use]\n    pub fn new(option: SkimItemReaderOption) -> Self {\n        Self {\n            option: Arc::new(option),\n        }\n    }\n\n    /// Sets the reader options\n    #[must_use]\n    pub fn option(mut self, option: SkimItemReaderOption) -> Self {\n        self.option = Arc::new(option);\n        self\n    }\n}\n\nimpl SkimItemReader {\n    /// Converts a `BufRead` source into a stream of skim items\n    pub fn of_bufread(&self, source: impl BufRead + Send + 'static) -> SkimItemReceiver {\n        if self.option.is_simple() {\n            self.raw_bufread(source)\n        } else {\n            self.read_and_collect_from_command(Arc::new(AtomicUsize::new(0)), CollectorInput::Pipe(Box::new(source)))\n                .0\n        }\n    }\n\n    /// Helper function that contains the common logic for reading lines from a `BufRead` source\n    /// and converting them into `SkimItems`.\n    fn read_lines_into_items(\n        mut source: impl BufRead + Send + 'static,\n        tx_item: &SkimItemSender,\n        option: &Arc<SkimItemReaderOption>,\n        transform_fields: &[FieldRange],\n        matching_fields: &[FieldRange],\n    ) {\n        let mut buffer = Vec::with_capacity(option.buf_size);\n        let mut items_to_send = Vec::with_capacity(ITEMS_BUFFER_SIZE);\n        let mut last_send_time = Instant::now();\n        let send_timeout = Duration::from_millis(SEND_TIMEOUT_MS);\n\n        loop {\n            buffer.clear();\n\n            // start reading\n            match source.read_until(option.line_ending, &mut buffer) {\n                Ok(0) => break,\n                Ok(_) => {\n                    // Strip line endings\n                    if buffer.ends_with(b\"\\r\\n\") {\n                        buffer.pop();\n                        buffer.pop();\n                    } else if buffer.ends_with(&[option.line_ending]) {\n                        buffer.pop();\n                    }\n\n                    let Ok(line) = std::str::from_utf8(&buffer) else {\n                        continue;\n                    };\n\n                    trace!(\"got item {line}\");\n\n                    let raw_item = DefaultSkimItem::new(\n                        line,\n                        option.use_ansi_color,\n                        transform_fields,\n                        matching_fields,\n                        &option.delimiter,\n                    );\n                    items_to_send.push(Arc::new(raw_item) as Arc<dyn SkimItem>);\n                }\n                Err(err) => {\n                    trace!(\"Got {err:?} when reading, skipping\");\n                } // String not UTF8 or other error, skip.\n            }\n\n            // Send batched items if buffer is full OR timeout has elapsed\n            let should_send = items_to_send.len() == ITEMS_BUFFER_SIZE\n                || (!items_to_send.is_empty() && last_send_time.elapsed() >= send_timeout);\n\n            if should_send {\n                let batch = std::mem::replace(&mut items_to_send, Vec::with_capacity(ITEMS_BUFFER_SIZE));\n                match tx_item.send(batch) {\n                    Ok(()) => {\n                        last_send_time = Instant::now();\n                    }\n                    Err(e) => {\n                        warn!(\"Failed to send items: {e:?}\");\n                        break;\n                    }\n                }\n            }\n        }\n\n        // Send remaining items\n        if !items_to_send.is_empty() {\n            let _ = tx_item.send(items_to_send);\n        }\n    }\n\n    /// helper: convert bufread into `SkimItemReceiver`\n    fn raw_bufread(&self, source: impl BufRead + Send + 'static) -> SkimItemReceiver {\n        let (tx_item, rx_item): (SkimItemSender, SkimItemReceiver) = kanal::bounded(1024 * 1024);\n        let option = self.option.clone();\n\n        thread::spawn(move || {\n            Self::read_lines_into_items(source, &tx_item, &option, &[], &[]);\n        });\n\n        rx_item\n    }\n\n    /// `components_to_stop` == 0 => all the threads have been stopped\n    /// return (`channel_for_receive_item`, `channel_to_stop_command`)\n    fn read_and_collect_from_command(\n        &self,\n        components_to_stop: Arc<AtomicUsize>,\n        input: CollectorInput,\n    ) -> (SkimItemReceiver, crate::prelude::Sender<i32>) {\n        let send_error = self.option.show_error;\n        let (command, source) = match input {\n            CollectorInput::Pipe(pipe) => (None, pipe),\n            CollectorInput::Command(cmd) => get_command_output(&cmd, send_error).expect(\"command not found\"),\n        };\n\n        let (tx_interrupt, rx_interrupt) = crate::prelude::bounded(8);\n        let (tx_item, rx_item): (SkimItemSender, SkimItemReceiver) = crate::prelude::bounded(1024 * 1024);\n\n        let started = Arc::new(AtomicBool::new(false));\n        let started_clone = started.clone();\n        let components_to_stop_clone = components_to_stop.clone();\n        let tx_item_clone = tx_item.clone();\n        // listening to close signal and kill command if needed\n        thread::spawn(move || {\n            debug!(\"collector: command killer start\");\n            components_to_stop_clone.fetch_add(1, Ordering::SeqCst);\n            started_clone.store(true, Ordering::SeqCst); // notify parent that it is started\n\n            let _ = rx_interrupt.recv();\n            if let Some(mut child) = command {\n                // clean up resources\n                let _ = child.kill();\n                let _ = child.wait();\n\n                if send_error {\n                    let has_error = child\n                        .try_wait()\n                        .map(|os| os.is_none_or(|s| !s.success()))\n                        .unwrap_or(false);\n                    if has_error {\n                        trace!(\"collector: sending error\");\n                        let output = child.wait_with_output().expect(\"could not retrieve error message\");\n                        let error_text = String::from_utf8_lossy(&output.stderr).to_string();\n                        let error_items: Vec<Arc<dyn SkimItem>> = error_text\n                            .lines()\n                            .map(|line| {\n                                Arc::new(DefaultSkimItem::new(\n                                    line,\n                                    false,\n                                    &[],\n                                    &[],\n                                    &Regex::new(DELIMITER_STR).unwrap(),\n                                )) as Arc<dyn SkimItem>\n                            })\n                            .collect();\n                        let _ = tx_item_clone.send(error_items);\n                    }\n                }\n            }\n\n            components_to_stop_clone.fetch_sub(1, Ordering::SeqCst);\n            debug!(\"collector: command killer stop\");\n        });\n\n        while !started.load(Ordering::SeqCst) {\n            // busy waiting for the thread to start. (components_to_stop is added)\n        }\n\n        let started = Arc::new(AtomicBool::new(false));\n        let started_clone = started.clone();\n        let tx_interrupt_clone = tx_interrupt.clone();\n        let option = self.option.clone();\n        let transform_fields = option.transform_fields.clone();\n        let matching_fields = option.matching_fields.clone();\n\n        thread::spawn(move || {\n            debug!(\"collector: command collector start\");\n            components_to_stop.fetch_add(1, Ordering::SeqCst);\n            started_clone.store(true, Ordering::SeqCst); // notify parent that it is started\n\n            Self::read_lines_into_items(source, &tx_item, &option, &transform_fields, &matching_fields);\n\n            let _ = tx_interrupt_clone.send(1); // ensure the waiting thread will exit\n            components_to_stop.fetch_sub(1, Ordering::SeqCst);\n            debug!(\"collector: command collector stop\");\n        });\n\n        while !started.load(Ordering::SeqCst) {\n            // busy waiting for the thread to start. (components_to_stop is added)\n        }\n\n        (rx_item, tx_interrupt)\n    }\n}\n\nimpl CommandCollector for SkimItemReader {\n    fn invoke(\n        &mut self,\n        cmd: &str,\n        components_to_stop: Arc<AtomicUsize>,\n    ) -> (SkimItemReceiver, crate::prelude::Sender<i32>) {\n        self.read_and_collect_from_command(components_to_stop, CollectorInput::Command(cmd.to_string()))\n    }\n}\n\ntype CommandOutput = (Option<Child>, Box<dyn BufRead + Send>);\n\nfn get_command_output(cmd: &str, send_error: bool) -> Result<CommandOutput, Box<dyn Error>> {\n    let (reader, writer) = std::io::pipe()?;\n    let mut sh = Command::new(\"sh\");\n    let command = sh.arg(\"-c\").arg(cmd).stdout(writer.try_clone()?);\n    if send_error {\n        trace!(\"redirecting stderr to the output\");\n        command.stderr(writer);\n    } else {\n        command.stderr(Stdio::null());\n    }\n\n    Ok((command.spawn().ok(), Box::new(BufReader::new(reader))))\n}\n"
  },
  {
    "path": "src/helper/macros.rs",
    "content": "/// Macro for exhaustive matching that ensures all enum variants are covered\n///\n/// This macro wraps a match expression and automatically extracts enum variants\n/// from the match arms to check exhaustiveness at compile time using `assert_enum_variants!`.\n///\n/// # Example with Option<Type>\n///\n/// ```rust\n/// use skim::exhaustive_match;\n///\n/// enum Status { Active, Inactive, Pending }\n/// enum Output { Success, Failure, Unknown }\n///\n/// fn check(status: Status) -> Option<Output> {\n///     exhaustive_match! {\n///         status => Option<Output>;\n///         {\n///             Status::Active => Some(Success),\n///             Status::Inactive => Some(Failure),\n///             Status::Pending => Some(Unknown),\n///         }\n///         default _ => None\n///     }\n/// }\n/// ```\n///\n/// # Example with plain Type\n///\n/// ```rust\n/// use skim::exhaustive_match;\n///\n/// enum KeyCode { Enter, Char(char) }\n///\n/// fn parse(key: &str) -> KeyCode {\n///     exhaustive_match! {\n///         key => KeyCode;\n///         {\n///             \"enter\" => Enter,\n///             \"space\" => Char(' '),\n///         }\n///         default s => panic!(\"Unknown key {}\", s)\n///     }\n/// }\n/// ```\n#[macro_export]\nmacro_rules! exhaustive_match {\n    // Version with Option<Type> in header (with Some() wrapper and default case)\n    (\n        $expr:expr => Option<$return_ty:ty>;\n        {\n            $($pattern:pat => Some($variant:ident $($rest:tt)*)),+ $(,)?\n        }\n        default $default_pattern:pat => $default:expr\n    ) => {{\n        use $return_ty::*;\n        // Assert that all variants are accounted for at compile time\n        ::assert_enum_variants::assert_enum_variants!($return_ty, { $($variant),+ });\n\n        // Perform the actual match\n        match $expr {\n            $($pattern => Some($variant $($rest)*),)+\n            $default_pattern => $default\n        }\n    }};\n\n    // Version with plain Type in header (direct variant usage, with default)\n    (\n        $expr:expr => $return_ty:ty;\n        {\n            $($pattern:pat => $variant:ident $(($($args:tt)*))? ),+ $(,)?\n        }\n        default $default_pattern:pat => $default:expr\n    ) => {{\n        use $return_ty::*;\n        // Assert that all variants are accounted for at compile time\n        ::assert_enum_variants::assert_enum_variants!($return_ty, { $($variant),+ });\n\n        // Perform the actual match\n        match $expr {\n            $($pattern => $variant $(($($args)*))? ,)+\n            $default_pattern => $default\n        }\n    }};\n\n    // Version with plain Type in header (direct variant usage, no default)\n    (\n        $expr:expr => $return_ty:ty;\n        {\n            $($pattern:pat => $variant:ident $(($($args:tt)*))? ),+ $(,)?\n        }\n    ) => {{\n        use $return_ty::*;\n        // Assert that all variants are accounted for at compile time\n        ::assert_enum_variants::assert_enum_variants!($return_ty, { $($variant),+ });\n\n        // Perform the actual match\n        match $expr {\n            $($pattern => $variant $(($($args)*))? ),+\n        }\n    }};\n}\n"
  },
  {
    "path": "src/helper/mod.rs",
    "content": "//! Skim helpers\npub mod item;\npub(crate) mod item_reader;\n#[macro_use]\npub(crate) mod macros;\npub(crate) mod selector;\n"
  },
  {
    "path": "src/helper/selector.rs",
    "content": "use std::collections::HashSet;\n\nuse regex::Regex;\n\nuse crate::{Selector, SkimItem};\n\n/// Default implementation of the selector trait for pre-selecting items\n#[derive(Debug, Default)]\npub struct DefaultSkimSelector {\n    first_n: usize,\n    regex: Option<Regex>,\n    preset: Option<HashSet<String>>,\n}\n\nimpl DefaultSkimSelector {\n    /// Selects the first N items\n    #[must_use]\n    pub fn first_n(mut self, first_n: usize) -> Self {\n        trace!(\"select first_n: {first_n}\");\n        self.first_n = first_n;\n        self\n    }\n\n    /// Selects items whose text matches any of the preset strings\n    #[must_use]\n    pub fn preset(mut self, preset: impl IntoIterator<Item = String>) -> Self {\n        if self.preset.is_none() {\n            self.preset = Some(HashSet::new());\n        }\n\n        if let Some(set) = self.preset.as_mut() {\n            set.extend(preset);\n        }\n        self\n    }\n\n    /// Selects items whose text matches the given regex pattern\n    #[must_use]\n    pub fn regex(mut self, regex: &str) -> Self {\n        trace!(\"select regex: {regex}\");\n        if !regex.is_empty() {\n            self.regex = Regex::new(regex).ok();\n        }\n        self\n    }\n}\n\nimpl Selector for DefaultSkimSelector {\n    fn should_select(&self, index: usize, item: &dyn SkimItem) -> bool {\n        if self.first_n > index {\n            return true;\n        }\n\n        if self.preset.is_some()\n            && self\n                .preset\n                .as_ref()\n                .is_some_and(|preset| preset.contains(item.text().as_ref()))\n        {\n            return true;\n        }\n\n        if self.regex.is_some() && self.regex.as_ref().is_some_and(|re| re.is_match(&item.text())) {\n            return true;\n        }\n\n        false\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    pub fn test_first_n() {\n        let selector = DefaultSkimSelector::default().first_n(10);\n        assert!(selector.should_select(0, &\"item\"));\n        assert!(selector.should_select(1, &\"item\"));\n        assert!(selector.should_select(2, &\"item\"));\n        assert!(selector.should_select(9, &\"item\"));\n        assert!(!selector.should_select(10, &\"item\"));\n    }\n\n    #[test]\n    pub fn test_preset() {\n        let selector = DefaultSkimSelector::default().preset(vec![\"a\".to_string(), \"b\".to_string(), \"c\".to_string()]);\n        assert!(selector.should_select(0, &\"a\"));\n        assert!(selector.should_select(0, &\"b\"));\n        assert!(selector.should_select(0, &\"c\"));\n        assert!(!selector.should_select(0, &\"d\"));\n    }\n\n    #[test]\n    pub fn test_regex() {\n        let selector = DefaultSkimSelector::default().regex(\"^[0-9]\");\n        assert!(selector.should_select(0, &\"1\"));\n        assert!(selector.should_select(0, &\"2\"));\n        assert!(selector.should_select(0, &\"3\"));\n        assert!(selector.should_select(0, &\"1a\"));\n        assert!(!selector.should_select(0, &\"a\"));\n    }\n\n    #[test]\n    pub fn test_all_together() {\n        let selector = DefaultSkimSelector::default()\n            .first_n(1)\n            .regex(\"b\")\n            .preset(vec![\"c\".to_string()]);\n        assert!(selector.should_select(0, &\"a\"));\n        assert!(selector.should_select(1, &\"b\"));\n        assert!(selector.should_select(2, &\"c\"));\n        assert!(!selector.should_select(3, &\"d\"));\n    }\n}\n"
  },
  {
    "path": "src/item.rs",
    "content": "//! Item representation and management.\n//!\n//! This module provides the core item types used by skim, including ranked items,\n//! item pools for efficient storage, and ranking criteria for sorting matches.\nuse std::cmp::min;\nuse std::default::Default;\nuse std::hash::Hash;\nuse std::ops::Deref;\nuse std::sync::Arc;\nuse std::sync::atomic::{AtomicUsize, Ordering};\n\n#[cfg(feature = \"cli\")]\nuse clap::ValueEnum;\n#[cfg(feature = \"cli\")]\nuse clap::builder::PossibleValue;\n\nuse crate::spinlock::{SpinLock, SpinLockGuard};\nuse crate::{MatchRange, Rank, SkimItem};\nuse tokio::sync::Notify;\n\n//------------------------------------------------------------------------------\n\n/// Builder for creating rank values based on configurable criteria\n#[derive(Debug)]\npub struct RankBuilder {\n    criterion: Vec<RankCriteria>,\n}\n\nimpl Default for RankBuilder {\n    fn default() -> Self {\n        Self {\n            criterion: vec![RankCriteria::Score, RankCriteria::Begin, RankCriteria::End],\n        }\n    }\n}\n\nimpl RankBuilder {\n    /// Creates a new rank builder with the given criteria\n    #[must_use]\n    pub fn new(mut criterion: Vec<RankCriteria>) -> Self {\n        if !criterion.contains(&RankCriteria::Score) && !criterion.contains(&RankCriteria::NegScore) {\n            criterion.insert(0, RankCriteria::Score);\n        }\n\n        criterion.dedup();\n        Self { criterion }\n    }\n\n    /// Returns the tiebreak criteria slice.\n    #[must_use]\n    pub fn criteria(&self) -> &[RankCriteria] {\n        &self.criterion\n    }\n\n    /// Computes the byte offset of the first character after the last path separator\n    /// (`/` or `\\`) in `text`.  Returns `0` when no separator is present.\n    fn path_name_offset(text: &str) -> i32 {\n        text.rfind(['/', '\\\\'])\n            .map_or(0, |pos| i32::try_from(pos).unwrap_or(i32::MAX).saturating_add(1))\n    }\n\n    /// Builds a `Rank` from raw match measurements.\n    ///\n    /// The values are stored as-is; the tiebreak ordering and sign-flipping are\n    /// applied lazily by [`Rank::sort_key`] at comparison time.\n    /// The `index` will be overridden later\n    #[must_use]\n    pub fn build_rank(&self, score: i32, begin: usize, end: usize, item_text: &str) -> Rank {\n        Rank {\n            score,\n            begin: i32::try_from(begin).unwrap_or(i32::MAX),\n            end: i32::try_from(end).unwrap_or(i32::MAX),\n            length: i32::try_from(item_text.len()).unwrap_or(i32::MAX),\n            index: Default::default(),\n            path_name_offset: Self::path_name_offset(item_text),\n        }\n    }\n}\n\nimpl Rank {\n    /// Computes the ordered sort key for this rank given a slice of tiebreak criteria.\n    ///\n    /// Each criterion maps to one slot in the returned `[i32; 5]` array. Values are\n    /// sign-flipped where necessary so that the array compares lexicographically with\n    /// the \"best\" match sorting first (ascending order).\n    #[must_use]\n    pub fn sort_key(&self, criteria: &[RankCriteria]) -> [i32; 5] {\n        let mut key = [0i32; 5];\n        for (priority, criterion) in criteria.iter().take(5).enumerate() {\n            key[priority] = match criterion {\n                RankCriteria::Score => -self.score,\n                RankCriteria::NegScore => self.score,\n                RankCriteria::Begin => self.begin,\n                RankCriteria::NegBegin => -self.begin,\n                RankCriteria::End => self.end,\n                RankCriteria::NegEnd => -self.end,\n                RankCriteria::Length => self.length,\n                RankCriteria::NegLength => -self.length,\n                RankCriteria::Index => self.index,\n                RankCriteria::NegIndex => -self.index,\n                // PathName: prefer matches that fall within the filename portion (i.e. at or\n                // after the last path separator).  `path_name_offset - begin` is <= 0 when the\n                // match starts inside the filename, and positive when it starts in a directory\n                // component.  Lower values sort first, so filename matches rank higher.\n                RankCriteria::PathName => self.path_name_offset - self.begin,\n                RankCriteria::NegPathName => self.begin - self.path_name_offset,\n            };\n        }\n        key\n    }\n}\n\n//------------------------------------------------------------------------------\n/// An item that has been matched against a query\n#[derive(Clone)]\npub struct MatchedItem {\n    /// The underlying skim item\n    pub item: Arc<dyn SkimItem>,\n    /// Raw match measurements\n    pub rank: Rank,\n    /// The tiebreak criteria used to derive sort order from `rank`\n    pub rank_builder: Arc<RankBuilder>,\n    /// Range of characters that matched the pattern\n    pub matched_range: Option<MatchRange>,\n}\n\nimpl std::fmt::Debug for MatchedItem {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        f.debug_struct(\"MatchedItem\")\n            .field(\"item\", &self.item.text())\n            .field(\"rank\", &self.rank)\n            .field(\"sort_key\", &self.rank.sort_key(self.rank_builder.criteria()))\n            .field(\"matched_range\", &self.matched_range)\n            .finish()\n    }\n}\n\nimpl Hash for MatchedItem {\n    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {\n        state.write_i32(self.rank.index);\n        self.text().hash(state);\n    }\n}\n\nimpl Deref for MatchedItem {\n    type Target = Arc<dyn SkimItem>;\n\n    fn deref(&self) -> &Self::Target {\n        &self.item\n    }\n}\n\nimpl MatchedItem {\n    /// Merge two sorted `Vec<MatchedItem>` lists into one, preserving sort order by rank.\n    ///\n    /// Both input lists must already be sorted by the same tiebreak criteria (ascending).\n    /// The merge is O(n+m).\n    #[must_use]\n    pub fn sorted_merge(existing: Vec<MatchedItem>, incoming: Vec<MatchedItem>) -> Vec<MatchedItem> {\n        if existing.is_empty() {\n            return incoming;\n        }\n        if incoming.is_empty() {\n            return existing;\n        }\n\n        // Fast path: if all existing <= all incoming, we can append without merging.\n        #[allow(clippy::missing_panics_doc)]\n        if existing.last().unwrap() <= incoming.first().unwrap() {\n            let mut out = existing;\n            out.extend(incoming);\n            return out;\n        }\n\n        // Fast path: if all incoming <= all existing, prepend without complex merge.\n        #[allow(clippy::missing_panics_doc)]\n        if incoming.last().unwrap() <= existing.first().unwrap() {\n            let mut out = incoming;\n            out.extend(existing);\n            return out;\n        }\n\n        // Merge using direct next values to avoid Peekable overhead.\n        let mut merged = Vec::with_capacity(existing.len() + incoming.len());\n        let mut a = existing.into_iter();\n        let mut b = incoming.into_iter();\n        let mut a_next = a.next();\n        let mut b_next = b.next();\n\n        loop {\n            #[allow(clippy::missing_panics_doc)]\n            match (&a_next, &b_next) {\n                (Some(av), Some(bv)) => {\n                    if av <= bv {\n                        // take a_next\n                        merged.push(a_next.take().unwrap());\n                        a_next = a.next();\n                    } else {\n                        merged.push(b_next.take().unwrap());\n                        b_next = b.next();\n                    }\n                }\n                (Some(_), None) => {\n                    merged.push(a_next.take().unwrap());\n                    merged.extend(a);\n                    break;\n                }\n                (None, Some(_)) => {\n                    merged.push(b_next.take().unwrap());\n                    merged.extend(b);\n                    break;\n                }\n                (None, None) => break,\n            }\n        }\n\n        merged\n    }\n\n    /// Merge `incoming` into an already-sorted `existing` vector in-place.\n    ///\n    /// This function chooses between two strategies:\n    /// - If `incoming` is small (few items), insert them one-by-one using binary\n    ///   search to find the insertion point. This is O(m log n) for m incoming\n    ///   items and is faster when m << n.\n    /// - Otherwise, fall back to the linear two-way merge which is O(n+m).\n    ///\n    /// `existing` must be sorted according to the same ordering used by\n    /// `MatchedItem::cmp`.\n    pub fn merge_into_sorted(existing: &mut Vec<MatchedItem>, incoming: Vec<MatchedItem>) {\n        // Heuristic threshold: for small incoming batches, prefer binary-insert.\n        // This avoids allocating a new vector and copying the entire existing\n        // list when we only need to insert a few new items.\n        const SMALL_INSERT_THRESHOLD: usize = 256;\n\n        if incoming.is_empty() {\n            return;\n        }\n\n        if incoming.len() <= SMALL_INSERT_THRESHOLD {\n            // Insert each incoming item into the existing sorted vector.\n            // For small m this is typically faster than allocating a new\n            // buffer and performing a full linear merge.\n            for item in incoming {\n                let pos = existing.binary_search_by(|e| e.cmp(&item)).unwrap_or_else(|p| p);\n                existing.insert(pos, item);\n            }\n        } else {\n            // For larger incoming batches, perform the linear two-way merge\n            // which is O(n+m) and avoids the O(n*m) cost of repeated inserts.\n            let old = std::mem::take(existing);\n            *existing = MatchedItem::sorted_merge(old, incoming);\n        }\n    }\n}\n\nimpl MatchedItem {\n    /// Downcast the `MatchedItem` to the corresponding `SkimItem` struct\n    #[must_use]\n    pub fn downcast_item<T: SkimItem>(&self) -> Option<&T> {\n        (*self.item).as_any().downcast_ref::<T>()\n    }\n}\n\nuse std::cmp::Ordering as CmpOrd;\n\nimpl PartialEq for MatchedItem {\n    fn eq(&self, other: &Self) -> bool {\n        self.text().eq(&other.text()) && self.rank.index.eq(&other.rank.index)\n    }\n}\n\nimpl std::cmp::Eq for MatchedItem {}\n\nimpl PartialOrd for MatchedItem {\n    fn partial_cmp(&self, other: &Self) -> Option<CmpOrd> {\n        Some(self.cmp(other))\n    }\n}\n\nimpl Ord for MatchedItem {\n    fn cmp(&self, other: &Self) -> CmpOrd {\n        let criteria = self.rank_builder.criteria();\n        self.rank.sort_key(criteria).cmp(&other.rank.sort_key(criteria))\n    }\n}\n\n//------------------------------------------------------------------------------\nconst ITEM_POOL_CAPACITY: usize = 16384;\n\n/// Thread-safe pool for storing and managing items efficiently\npub struct ItemPool {\n    /// Total number of items in the pool\n    length: AtomicUsize,\n    /// The main pool of items\n    pool: SpinLock<Vec<Arc<dyn SkimItem>>>,\n    /// Number of items that were taken\n    taken: AtomicUsize,\n\n    /// Reserved first N lines as header\n    reserved_items: SpinLock<Vec<Arc<dyn SkimItem>>>,\n    /// Number of lines to reserve as header\n    lines_to_reserve: usize,\n    /// Reverse the order of items (--tac flag)\n    tac: bool,\n\n    /// Notified whenever new items are appended to the pool (async path).\n    ///\n    /// Listeners (e.g. the TUI event loop) can `await` this to wake up\n    /// immediately when items arrive instead of waiting for the next\n    /// periodic tick.\n    pub items_available: Arc<Notify>,\n}\n\nimpl Default for ItemPool {\n    fn default() -> Self {\n        Self {\n            length: AtomicUsize::new(0),\n            pool: SpinLock::new(Vec::with_capacity(ITEM_POOL_CAPACITY)),\n            taken: AtomicUsize::new(0),\n            reserved_items: SpinLock::new(Vec::new()),\n            lines_to_reserve: 0,\n            tac: false,\n            items_available: Arc::new(Notify::new()),\n        }\n    }\n}\n\nimpl ItemPool {\n    /// Creates a new empty item pool\n    #[must_use]\n    pub fn new() -> Self {\n        Self::default()\n    }\n\n    /// Creates a new item pool from skim options\n    #[must_use]\n    pub fn from_options(options: &crate::SkimOptions) -> Self {\n        Self {\n            length: AtomicUsize::new(0),\n            pool: SpinLock::new(Vec::with_capacity(ITEM_POOL_CAPACITY)),\n            taken: AtomicUsize::new(0),\n            reserved_items: SpinLock::new(Vec::new()),\n            lines_to_reserve: options.header_lines,\n            tac: options.tac,\n            items_available: Arc::new(Notify::new()),\n        }\n    }\n\n    /// Returns the total number of items in the pool\n    pub fn len(&self) -> usize {\n        self.length.load(Ordering::SeqCst)\n    }\n\n    /// Returns true if the pool contains no items\n    pub fn is_empty(&self) -> bool {\n        self.len() == 0\n    }\n\n    /// Returns the number of items that have not been taken yet\n    pub fn num_not_taken(&self) -> usize {\n        self.length.load(Ordering::SeqCst) - self.taken.load(Ordering::SeqCst)\n    }\n\n    /// Returns the number of items that have been taken\n    pub fn num_taken(&self) -> usize {\n        self.taken.load(Ordering::SeqCst)\n    }\n\n    /// Clears all items from the pool and resets counters\n    pub fn clear(&self) {\n        let mut items = self.pool.lock();\n        items.clear();\n        let mut header_items = self.reserved_items.lock();\n        header_items.clear();\n        self.taken.store(0, Ordering::SeqCst);\n        self.length.store(0, Ordering::SeqCst);\n    }\n\n    /// Resets the taken counter without clearing items\n    pub fn reset(&self) {\n        // lock to ensure consistency\n        let _items = self.pool.lock();\n\n        self.taken.store(0, Ordering::SeqCst);\n    }\n\n    /// append the items and return the `new_size` of the pool\n    pub fn append(&self, mut items: Vec<Arc<dyn SkimItem>>) -> usize {\n        let len = items.len();\n        trace!(\"item pool, append {len} items\");\n        let mut pool = self.pool.lock();\n        let mut header_items = self.reserved_items.lock();\n\n        let to_reserve = self.lines_to_reserve - header_items.len();\n        if to_reserve > 0 {\n            let to_reserve = min(to_reserve, items.len());\n            // Split items: first part goes to header, rest to main pool\n            let remaining = items.split_off(to_reserve);\n\n            // Header items are always in input order, regardless of tac\n            header_items.extend(items);\n\n            if self.tac {\n                // For --tac, prepend non-header items (newest items go to front)\n                for item in remaining {\n                    pool.insert(0, item);\n                }\n            } else {\n                pool.extend(remaining);\n            }\n        } else if self.tac {\n            // For --tac, prepend items (newest items go to front)\n            for item in items {\n                pool.insert(0, item);\n            }\n        } else {\n            pool.extend(items);\n        }\n        self.length.store(pool.len(), Ordering::SeqCst);\n        trace!(\"item pool, done append {len} items, total: {}\", pool.len());\n        let new_len = pool.len();\n        drop(pool);\n        drop(header_items);\n        // Wake any listener that is waiting for new items (e.g. the event loop\n        // or the filter-mode loop) so it can restart the matcher immediately\n        // instead of waiting for the next periodic tick.\n        self.items_available.notify_one();\n\n        new_len\n    }\n\n    /// Takes items from the pool, copying new items since last take and releasing lock immediately\n    pub fn take(&self) -> Vec<Arc<dyn SkimItem>> {\n        let guard = self.pool.lock();\n        let taken = self.taken.swap(guard.len(), Ordering::SeqCst);\n        // Copy the new items out so we can release the lock immediately\n        let items = guard[taken..].to_vec();\n        drop(guard); // Explicitly release lock\n        items\n    }\n\n    /// Returns a copy of the reserved header items\n    pub fn reserved(&self) -> Vec<Arc<dyn SkimItem>> {\n        let guard = self.reserved_items.lock();\n        guard.clone()\n    }\n}\n\n/// Guard for accessing a slice of items from the pool\npub struct ItemPoolGuard<'a, T: Sized + 'a> {\n    guard: SpinLockGuard<'a, Vec<T>>,\n    start: usize,\n}\n\nimpl<T: Sized> Deref for ItemPoolGuard<'_, T> {\n    type Target = [T];\n\n    fn deref(&self) -> &[T] {\n        &self.guard[self.start..]\n    }\n}\n\n//------------------------------------------------------------------------------\n/// Criteria for ranking and sorting matched items\n#[derive(Debug, PartialEq, Eq, Clone, Copy)]\npub enum RankCriteria {\n    /// Sort by match score (lower is better)\n    Score,\n    /// Sort by match score (higher is better)\n    NegScore,\n    /// Sort by beginning position of match\n    Begin,\n    /// Sort by beginning position of match (reversed)\n    NegBegin,\n    /// Sort by ending position of match\n    End,\n    /// Sort by ending position of match (reversed)\n    NegEnd,\n    /// Sort by item length\n    Length,\n    /// Sort by item length (reversed)\n    NegLength,\n    /// Sort by item index\n    Index,\n    /// Sort by item index (reversed)\n    NegIndex,\n    /// Give a bonus to matches that are after the last path separator (`/` or `\\`)\n    PathName,\n    /// Give a bonus to matches that are after the last path separator (reversed)\n    NegPathName,\n}\n\n#[cfg(feature = \"cli\")]\nimpl ValueEnum for RankCriteria {\n    fn value_variants<'a>() -> &'a [Self] {\n        use RankCriteria::{\n            Begin, End, Index, Length, NegBegin, NegEnd, NegIndex, NegLength, NegPathName, NegScore, PathName, Score,\n        };\n        &[\n            Score,\n            NegScore,\n            Begin,\n            NegBegin,\n            End,\n            NegEnd,\n            Length,\n            NegLength,\n            Index,\n            NegIndex,\n            PathName,\n            NegPathName,\n        ]\n    }\n\n    fn to_possible_value(&self) -> Option<clap::builder::PossibleValue> {\n        use RankCriteria::{\n            Begin, End, Index, Length, NegBegin, NegEnd, NegIndex, NegLength, NegPathName, NegScore, PathName, Score,\n        };\n        Some(match self {\n            Score => PossibleValue::new(\"score\"),\n            Begin => PossibleValue::new(\"begin\"),\n            End => PossibleValue::new(\"end\"),\n            NegScore => PossibleValue::new(\"-score\"),\n            NegBegin => PossibleValue::new(\"-begin\"),\n            NegEnd => PossibleValue::new(\"-end\"),\n            Length => PossibleValue::new(\"length\"),\n            NegLength => PossibleValue::new(\"-length\"),\n            Index => PossibleValue::new(\"index\"),\n            NegIndex => PossibleValue::new(\"-index\"),\n            PathName => PossibleValue::new(\"pathname\"),\n            NegPathName => PossibleValue::new(\"-pathname\"),\n        })\n    }\n}\n"
  },
  {
    "path": "src/lib.rs",
    "content": "//! Skim is a fuzzy finder library for Rust.\n//!\n//! It provides a fast and customizable way to filter and select items interactively,\n//! similar to fzf. Skim can be used as a library or as a command-line tool.\n//!\n//! # Examples\n//!\n//! ```no_run\n//! use skim::prelude::*;\n//!\n//! let options = SkimOptionsBuilder::default()\n//!     .height(\"50%\")\n//!     .multi(true)\n//!     .build()\n//!     .unwrap();\n//!\n//! let output = Skim::run_items(\n//!         options,\n//!         [\"awk\", \"bash\", \"csh\", \"dash\", \"fish\", \"ksh\", \"zsh\"]\n//!     ).unwrap();\n//! ```\n\n#![warn(missing_docs)]\n#![warn(clippy::pedantic)]\n#![allow(clippy::default_trait_access, clippy::struct_excessive_bools)]\n\n#[macro_use]\nextern crate log;\n\n#[global_allocator]\nstatic GLOBAL_ALLOCATOR: mimalloc::MiMalloc = mimalloc::MiMalloc;\n\nuse std::any::Any;\nuse std::borrow::Cow;\nuse std::fmt::Display;\nuse std::sync::Arc;\n\nuse crate::fuzzy_matcher::MatchIndices;\nuse ratatui::{\n    style::Style,\n    text::{Line, Span},\n};\n\npub use crate::engine::fuzzy::FuzzyAlgorithm;\npub use crate::item::RankCriteria;\npub use crate::options::SkimOptions;\npub use crate::output::SkimOutput;\npub use crate::skim::*;\npub use crate::skim_item::SkimItem;\nuse crate::tui::Size;\npub use util::printf;\n\npub mod binds;\nmod engine;\npub mod field;\npub mod fuzzy_matcher;\npub mod helper;\npub mod item;\npub mod matcher;\npub mod options;\nmod output;\npub mod prelude;\npub mod reader;\nmod skim;\nmod skim_item;\npub mod spinlock;\npub mod theme;\npub mod tmux;\npub mod tui;\nmod util;\n\n#[cfg(feature = \"cli\")]\npub mod manpage;\n#[cfg(feature = \"cli\")]\npub mod shell;\n\n//------------------------------------------------------------------------------\n/// Trait for downcasting to concrete types from trait objects\npub trait AsAny {\n    /// Returns a reference to the value as `Any`\n    fn as_any(&self) -> &dyn Any;\n    /// Returns a mutable reference to the value as `Any`\n    fn as_any_mut(&mut self) -> &mut dyn Any;\n}\n\nimpl<T: Any> AsAny for T {\n    fn as_any(&self) -> &dyn Any {\n        self\n    }\n\n    fn as_any_mut(&mut self) -> &mut dyn Any {\n        self\n    }\n}\n\n//------------------------------------------------------------------------------\n// Display Context\n#[derive(Default, Debug, Clone)]\n/// Represents how a query matches an item\npub enum Matches {\n    /// No matches\n    #[default]\n    None,\n    /// Matches at specific character indices\n    CharIndices(MatchIndices),\n    /// Matches in a character range (start, end)\n    CharRange(usize, usize),\n    /// Matches in a byte range (start, end)\n    ByteRange(usize, usize),\n}\n\n#[derive(Default, Clone)]\n/// Context information for displaying an item\npub struct DisplayContext {\n    /// The match score for this item\n    pub score: i32,\n    /// Where the query matched in the item\n    pub matches: Matches,\n    /// The width of the container to display in\n    pub container_width: usize,\n    /// The base style to apply to non-matched portions\n    pub base_style: Style,\n    /// The style to apply to matched portions\n    pub matched_style: Style,\n}\n\nimpl DisplayContext {\n    /// Converts the context and text into a styled `Line` with highlighted matches\n    ///\n    /// # Panics\n    ///\n    /// Panics if the byte ranges in `Matches::ByteRange` do not align with valid UTF-8 boundaries.\n    #[must_use]\n    pub fn to_line(self, cow: Cow<str>) -> Line {\n        let text: String = cow.into_owned();\n\n        // Combine base_style with match style for highlighted text\n        // Match style takes precedence for fg, but inherits bg from base if not set\n        match &self.matches {\n            Matches::CharIndices(indices) => {\n                let mut res = Line::default();\n                let mut chars = text.chars();\n                let mut prev_index = 0;\n                for &index in indices {\n                    let span_content = chars.by_ref().take(index - prev_index);\n                    res.push_span(Span::styled(span_content.collect::<String>(), self.base_style));\n                    let highlighted_char = chars.next().unwrap_or_default().to_string();\n\n                    res.push_span(Span::styled(\n                        highlighted_char,\n                        self.base_style.patch(self.matched_style),\n                    ));\n                    prev_index = index + 1;\n                }\n                res.push_span(Span::styled(chars.collect::<String>(), self.base_style));\n                res\n            }\n            // AnsiString::from((context.text, indices, context.highlight_attr)),\n            #[allow(clippy::cast_possible_truncation)]\n            Matches::CharRange(start, end) => {\n                let mut chars = text.chars();\n                let mut res = Line::default();\n                res.push_span(Span::styled(\n                    chars.by_ref().take(*start).collect::<String>(),\n                    self.base_style,\n                ));\n                let highlighted_text = chars.by_ref().take(*end - *start).collect::<String>();\n\n                res.push_span(Span::styled(\n                    highlighted_text,\n                    self.base_style.patch(self.matched_style),\n                ));\n                res.push_span(Span::styled(chars.collect::<String>(), self.base_style));\n                res\n            }\n            Matches::ByteRange(start, end) => {\n                let mut bytes = text.bytes();\n                let mut res = Line::default();\n                res.push_span(Span::styled(\n                    String::from_utf8(bytes.by_ref().take(*start).collect()).unwrap(),\n                    self.base_style,\n                ));\n                let highlighted_bytes = bytes.by_ref().take(*end - *start).collect();\n                let highlighted_text = String::from_utf8(highlighted_bytes).unwrap();\n\n                res.push_span(Span::styled(\n                    highlighted_text,\n                    self.base_style.patch(self.matched_style),\n                ));\n                res.push_span(Span::styled(\n                    String::from_utf8(bytes.collect()).unwrap(),\n                    self.base_style,\n                ));\n                res\n            }\n            Matches::None => Line::from(vec![Span::styled(text, self.base_style)]),\n        }\n    }\n}\n\n//------------------------------------------------------------------------------\n// Preview Context\n\n/// Context information for generating item previews\npub struct PreviewContext<'a> {\n    /// The current search query\n    pub query: &'a str,\n    /// The current command query (for interactive mode)\n    pub cmd_query: &'a str,\n    /// Width of the preview window\n    pub width: usize,\n    /// Height of the preview window\n    pub height: usize,\n    /// Index of the current item\n    pub current_index: usize,\n    /// Text of the current selection\n    pub current_selection: &'a str,\n    /// selected item indices (may or may not include current item)\n    pub selected_indices: &'a [usize],\n    /// selected item texts (may or may not include current item)\n    pub selections: &'a [&'a str],\n}\n\n//------------------------------------------------------------------------------\n// Preview\n\n/// Position and scroll information for preview display\n#[derive(Default, Copy, Clone, Debug)]\npub struct PreviewPosition {\n    /// Horizontal scroll position\n    pub h_scroll: Size,\n    /// Horizontal offset\n    pub h_offset: Size,\n    /// Vertical scroll position\n    pub v_scroll: Size,\n    /// Vertical offset\n    pub v_offset: Size,\n}\n\n/// Defines how an item should be previewed\npub enum ItemPreview {\n    /// execute the command and print the command's output\n    Command(String),\n    /// Display the prepared text(lines)\n    Text(String),\n    /// Display the colored text(lines)\n    AnsiText(String),\n    /// Execute a command and display output with position\n    CommandWithPos(String, PreviewPosition),\n    /// Display text with position\n    TextWithPos(String, PreviewPosition),\n    /// Display ANSI-colored text with position\n    AnsiWithPos(String, PreviewPosition),\n    /// Use global command settings to preview the item\n    Global,\n}\n\n//==============================================================================\n// A match engine will execute the matching algorithm\n\n/// Case sensitivity mode for matching\n#[derive(Eq, PartialEq, Debug, Copy, Clone, Default)]\n#[cfg_attr(feature = \"cli\", derive(clap::ValueEnum), clap(rename_all = \"snake_case\"))]\npub enum CaseMatching {\n    /// Case-sensitive matching\n    Respect,\n    /// Case-insensitive matching\n    Ignore,\n    /// Smart case: case-insensitive unless query contains uppercase\n    #[default]\n    Smart,\n}\n\n/// Typo tolerance configuration for fuzzy matching\n///\n/// Controls how many character mismatches (typos) are allowed when matching.\n#[derive(Eq, PartialEq, Debug, Copy, Clone, Default)]\npub enum Typos {\n    /// No typo tolerance — query must match exactly\n    #[default]\n    Disabled,\n    /// Adaptive typo tolerance — allows `pattern_length / 4` typos\n    Smart,\n    /// Fixed typo tolerance — allows exactly `n` typos\n    Fixed(usize),\n}\n\nimpl From<usize> for Typos {\n    fn from(n: usize) -> Self {\n        match n {\n            0 => Typos::Disabled,\n            n => Typos::Fixed(n),\n        }\n    }\n}\n\n/// Represents the range of a match in an item\n#[derive(PartialEq, Eq, Clone, Debug)]\npub enum MatchRange {\n    /// Range of bytes (start, end)\n    ByteRange(usize, usize),\n    /// Individual character indices that matched\n    Chars(MatchIndices),\n}\n\n/// Rank stores the raw match measurements used for sorting results.\n///\n/// Named fields preserve the semantic meaning of each value. The actual\n/// sort key (taking into account the user-configured tiebreak criteria and\n/// their direction) is computed lazily via [`Rank::sort_key`] rather than\n/// being baked in at construction time.\n#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]\npub struct Rank {\n    /// Raw fuzzy/exact match score (higher is a better match)\n    pub score: i32,\n    /// Index of the first matched character (0-based)\n    pub begin: i32,\n    /// Index of the last matched character (0-based)\n    pub end: i32,\n    /// Length of the item text in bytes\n    pub length: i32,\n    /// Ordinal position of the item in the input stream\n    pub index: i32,\n    /// Byte offset of the first character after the last path separator (`/` or `\\`).\n    /// Equal to `0` when the item text contains no path separator.\n    pub path_name_offset: i32,\n}\n\n/// Result of matching a query against an item\n#[derive(Clone, Debug)]\npub struct MatchResult {\n    /// The rank/score of this match\n    pub rank: Rank,\n    /// The range where the match occurred\n    pub matched_range: MatchRange,\n}\n\nimpl MatchResult {\n    #[must_use]\n    /// Converts the match range to character indices\n    pub fn range_char_indices(&self, text: &str) -> MatchIndices {\n        match &self.matched_range {\n            &MatchRange::ByteRange(start, end) => {\n                let first = text[..start].chars().count();\n                let last = first + text[start..end].chars().count();\n                (first..last).collect()\n            }\n            MatchRange::Chars(vec) => vec.clone(),\n        }\n    }\n}\n\n/// A matching engine that can match queries against items\npub trait MatchEngine: Sync + Send + Display {\n    /// Matches an item against the query, returning a result if matched\n    fn match_item(&self, item: &dyn SkimItem) -> Option<MatchResult>;\n}\n\n/// Factory for creating match engines\npub trait MatchEngineFactory {\n    /// Creates a match engine with explicit case sensitivity\n    fn create_engine_with_case(&self, query: &str, case: CaseMatching) -> Box<dyn MatchEngine>;\n    /// Creates a match engine with default case sensitivity\n    fn create_engine(&self, query: &str) -> Box<dyn MatchEngine> {\n        self.create_engine_with_case(query, CaseMatching::default())\n    }\n}\n\n//------------------------------------------------------------------------------\n// Preselection\n\n/// A selector that determines whether an item should be \"pre-selected\" in multi-selection mode\npub trait Selector {\n    /// Returns true if the item at the given index should be pre-selected\n    fn should_select(&self, index: usize, item: &dyn SkimItem) -> bool;\n}\n\n//------------------------------------------------------------------------------\n/// Sender for streaming items to skim\npub type SkimItemSender = kanal::Sender<Vec<Arc<dyn SkimItem>>>;\n/// Receiver for streaming items to skim\npub type SkimItemReceiver = kanal::Receiver<Vec<Arc<dyn SkimItem>>>;\n"
  },
  {
    "path": "src/manpage.rs",
    "content": "//! Provides what's needed to generate skim's man page\nuse std::io::Write;\n\nuse clap::CommandFactory;\nuse clap_mangen::Man;\nuse color_eyre::eyre::Result;\nuse roff::{Inline, Roff};\n\nuse crate::SkimOptions;\n\nconst THEME_SECTION: &str = \"\nAvailable themes:\n    * none: base color scheme\n    * molokai: molokai 256color\n    * light: light 256color\n    * 16: dark base16 theme\n    * bw: black & white theme\n    * dark | default: dark 256color, default value\n    * all 4 catppuccin variants:\n        * catppuccin-latte\n        * catppuccin-macchiato\n        * catppuccin-frappe\n        * catppuccin-mocha\n\nAvailable color names:\n    * normal (or empty string): normal text\n    * matched (or hl): matched text\n    * current (or fg+): current line foreground\n    * bg+: current line background (special case, always sets background)\n    * current_match (or hl+): matched text in current line\n    * query: query text\n    * spinner: spinner character\n    * info: info text (match count)\n    * prompt: prompt text\n    * cursor (or pointer): cursor/pointer\n    * selected (or marker): selected item marker\n    * header: header text\n    * border: border lines\n\nAdding `-fg`, `_fg`, `-bg`, `_bg`, `-underline`, `_underline` sets the corresponding part of\nthe color. For instance, `normal-fg` (or simply `fg`) will set the foreground normal color.\n\nColor formats:\n    * 0-255: ANSI terminal color\n    * #rrggbb: 24-bit color\n\nAvailable attrs:\n    * x | regular: resets the modifiers, use it before the others\n    * b | bold\n    * u | underline\n    * c | crossed-out\n    * d | dim\n    * i | italic\n    * r | reverse\n\nExample: `--color '16,normal-fg:0+bold,matched-fg:#ffffff+u,cursor-bg:#deadbe'` will start with the\n base 16 theme and override it with a bold ANSI color 0 foreground (black), a hex ffffff (full\n white) underlined foreground for matched parts and a #deadbe (pale rose, apparently) cursor background.\n\";\n\nconst EXIT_CODES_SECTION: &str = \"\n* 0: success\n* 1: no match\n* 130: interrupt (ctrl-c or esc)\n* others: error\n\";\n\nconst NORMAL_MODE_SS: &str = \"\nIn normal mode, sk reads the input from stdin and displays the results interactively,\nand the query is then used to fuzzily filter among the input lines.\n\";\n\nconst INTERACTIVE_MODE_SS: &str = \"\nInteractive mode is a special mode that allows you to run a command interactively and display\nthe results. It is enabled by the `--interactive` (or `-i`) option or by binding the\n`toggle-interactive` action (default: <ctrl-q>).\nThe command is specified with the `--cmd` option.\n\nExample: `sk --cmd 'rg {} --color=always' --interactive` will use `rg` to search for the query\nin the current directory and display the results interactively.\n\";\n\nconst KEYS_SS: &str = \"\n* ctrl-[a-z]\n* ctrl-space\n* ctrl-alt-[a-z]\n* alt-[a-zA-Z]\n* alt-[0-9]\n* f[1-12]\n* enter\n* space\n* bspace      (bs)\n* alt-up\n* alt-down\n* alt-left\n* alt-right\n* alt-enter\n* alt-space\n* alt-bspace  (alt-bs)\n* alt-/\n* tab\n* btab        (shift-tab)\n* esc\n* del\n* up\n* down\n* left\n* right\n* home\n* end\n* pgup\n* pgdn\n* shift-up\n* shift-down\n* shift-left\n* shift-right\n* alt-shift-up\n* alt-shift-down\n* alt-shift-left\n* alt-shift-right\n* any single character\n\";\nconst ACTIONS_SS: &str = \"\n* abort: ctrl-c  ctrl-q  esc\n* accept(...): enter *the argument will be printed when the binding is triggered*\n* append-and-select\n* backward-char: ctrl-b  left\n* backward-delete-char: ctrl-h  bspace\n* backward-delete-char/eof\n* backward-kill-word: alt-bs\n* backward-word: alt-b   shift-left\n* beginning-of-line: ctrl-a  home\n* clear-screen: ctrl-l\n* delete-char: del\n* delete-char/eof: ctrl-d\n* deselect-all\n* down: ctrl-j  ctrl-n  down\n* end-of-line: ctrl-e  end\n* execute(...): *arg will be a command, see COMMAND EXPANSION for details\n* execute-silent(...): *arg will be a command, see COMMAND EXPANSION for details\n* forward-char: ctrl-f  right\n* forward-word: alt-f   shift-right\n* if-non-matched\n* if-query-empty\n* if-query-not-empty\n* ignore\n* kill-line\n* kill-word: alt-d\n* next-history: ctrl-n with `--history` or `--cmd-history`\n* page-down: pgdn\n* page-up: pgup\n* half-page-down\n* half-page-up\n* preview-up: shift-up\n* preview-down: shift-down\n* preview-left\n* preview-right\n* preview-page-down\n* preview-page-up\n* previous-history: ctrl-p with `--history` or `--cmd-history`\n* redraw\n* refresh-cmd\n* refresh-preview\n* reload(...)\n* select-all\n* select-row\n* set-preview-cmd(...): *arg will be a expanded expression, see COMMAND EXPANSION for details\n* set-query(...): *arg will be a expanded expression, see COMMAND EXPANSION for details\n* toggle\n* toggle-all\n* toggle+down: ctrl-i  tab\n* toggle-in: (--layout=reverse ? toggle+up:  toggle+down)\n* toggle-interactive\n* toggle-out: (--layout=reverse ? toggle+down:  toggle+up)\n* toggle-preview\n* toggle-preview-wrap\n* toggle-sort\n* toggle+up: btab    shift-tab\n* top\n* unix-line-discard: ctrl-u\n* unix-word-rubout: ctrl-w\n* up: ctrl-k  ctrl-p  up\n* yank: ctrl-y\n\";\n\nconst REMOTE_SECTION: &str = \"\nskim can be controlled from other processes, using the `--listen` (and optionally `--remote`) flags.\n\nTo achieve this, run the server instance using `sk --listen optional_address` (the address defaults to `sk`).\nIt will then start listening on a named socket for instructions.\n\nTo send instructions, you can use `sk --remote optional_address` or any other tool that allows us to interact with such sockets,\nsuch as `socat` on linux: `echo 'ToggleIn' | socat -u STDIN ABSTRACT-CONNECT:optional_address`. Instructions correspond to skim's Actions and need to be sent in Ron format.\nWhen using `sk --remote`, pipe in action chains (see the KEYBINDS section), for instance `echo 'up+select-row' | sk --remote optional_address`\n\";\n\nfn parse_str(src: &str) -> Vec<Inline> {\n    let mut res = Vec::new();\n    for line in src.lines() {\n        res.push(Inline::Roman(line.to_string()));\n        res.push(Inline::LineBreak);\n    }\n    res\n}\n\nfn section(c: &mut Roff, name: &str, content: &str) {\n    c.control(\"SH\", [name]);\n    c.text(parse_str(content));\n}\n\nfn subsection(c: &mut Roff, name: &str, content: &str) {\n    c.control(\"SS\", [name]);\n    c.text(parse_str(content));\n}\n\n/// Generate skim's manpage and write it to the writer\n///\n/// # Errors\n///\n/// Returns an error if writing to the output fails.\n// The manpage generator writes many sections sequentially; splitting it would add\n// indirection without meaningful simplification.\n#[allow(clippy::too_many_lines)]\npub fn generate<W>(w: &mut W) -> Result<()>\nwhere\n    W: Write,\n{\n    let base = Man::new(SkimOptions::command());\n    let mut custom = Roff::default();\n\n    // Render normal sections, as would mangen do\n    base.render_title(w)?;\n    base.render_name_section(w)?;\n    base.render_synopsis_section(w)?;\n\n    // Render options\n    base.render_options_section(w)?;\n\n    // Add custom sections\n    section(&mut custom, \"MODES\", \"\");\n    subsection(&mut custom, \"Normal mode\", NORMAL_MODE_SS);\n    subsection(&mut custom, \"Interactive mode\", INTERACTIVE_MODE_SS);\n    section(\n        &mut custom,\n        \"SEARCH\",\n        \"\nBy default, skim will start in `extended search`, giving some characters will have meaning.\nExample: `^test rs$ | sh$` will match items starting with test and ending with rs or sh.\n\",\n    );\n    subsection(\n        &mut custom,\n        \"AND (space)\",\n        \"A space between terms will act as an 'and' operator and will filter for items matching all terms.\",\n    );\n    subsection(\n        &mut custom,\n        \"OR (|)\",\n        \"A vertical bar between terms will act as an 'or' operator and will filter for items matching one of the terms.\",\n    );\n    subsection(\n        &mut custom,\n        \"Exact match (')\",\n        \"\nIf a term is prefixed by a `'`, sk will search for exact occurrences of that term.\nExact search can be enabled by default by the `--exact` command-line flag. In exact mode, `'` will disable exact matching for that term.\n\",\n    );\n    subsection(\n        &mut custom,\n        \"Anchored match (^|$)\",\n        \"If a term is prefixed by a `^` (resp. suffixed by a `$`), sk will search for matches starting (resp. ending) with that exact term.\",\n    );\n    subsection(\n        &mut custom,\n        \"Negation (!)\",\n        \"If a term is prefixed by `!`, sk will exclude the items that match this term.\",\n    );\n\n    section(\n        &mut custom,\n        \"KEYBINDS\",\n        \"\nKeybinds can be set by the `--bind` option, which takes a comma-separated list of [key]:[action[+action2].\nActions can take arguments, specified either between parentheses `reload(ls)` or after a colon `reload:ls`\n\",\n    );\n    subsection(&mut custom, \"Available keys (aliases in parentheses)\", KEYS_SS);\n    subsection(&mut custom, \"Actions[:default keys][*notes]\", ACTIONS_SS);\n\n    section(\n        &mut custom,\n        \"COMMAND EXPANSION\",\n        \"\nIn the `preview` flag, `execute`, `reload`, `set-query`... binds, sk will expand placeholders:\n* {} (or --replstr if used) will be expanded to the current item.\n* {q} (or {cq} for legacy reasons) will be expanded to the current query input.\n* {+} will be expanded to either the currently selected items in multi-select mode, or the current\n item in single-select.\n* {n} will be expanded to the index of the current item.\n* {+n} will be expanded to the index(es) of the corresponding {+} item(s).\n* {FIELD_INDEX_EXPRESSION} will be expanded to the field index expression run against the current\n item.\n* {+FIELD_INDEX_EXPRESSION} will be expanded to the field index expression run against the {+}\n item(s).\n\",\n    );\n    subsection(\n        &mut custom,\n        \"Field index expression\",\n        \"\nskim will expand some expressions between {..}.\nIt will expand to the corresponding fields, separated by the `--delimiter|-d` option (see there for details).\n* `{n}` will be the n-th field.\n* `{n..m}` will be the fields from n to m, inclusive, separated by a space\n* `{-n}` will be the n-th, starting from the end, -1 will be the last field etc.\n\",\n    );\n\n    section(&mut custom, \"ENVIRONMENT VARIABLES\", \"\");\n    subsection(\n        &mut custom,\n        \"SKIM_DEFAULT_COMMAND\",\n        \"If set, skim will collect items with this command if no input is piped in (defaults to `find .` if not set)\",\n    );\n    subsection(\n        &mut custom,\n        \"SKIM_DEFAULT_OPTIONS\",\n        \"Will be parsed and used as default options. Example: `--reverse --multi`\",\n    );\n    subsection(\n        &mut custom,\n        \"SKIM_OPTIONS_FILE\",\n        \"\nIf the variable is set to the path of an existing file, the contents of this file will be parsed and used as default options.\nIt supports `#` as a comment start, which can be escaped using `##`.\nExample:\n```\n# Preview\n--preview 'echo {}'\n--preview-window 'left:30%' # Preview window\n--reverse\n--prompt '## '\n```\n\",\n    );\n\n    subsection(\n        &mut custom,\n        \"NO_COLOR\",\n        \"If set to a non-empty value, will disable coloring\",\n    );\n\n    section(&mut custom, \"THEME\", THEME_SECTION);\n\n    section(&mut custom, \"LISTEN/REMOTE\", REMOTE_SECTION);\n\n    section(&mut custom, \"EXIT CODES\", EXIT_CODES_SECTION);\n\n    custom.to_writer(w)?;\n\n    // Finish with mangen version section\n    base.render_version_section(w)?;\n    Ok(())\n}\n"
  },
  {
    "path": "src/matcher.rs",
    "content": "//! This module contains the matching coordinator\nuse rayon::ThreadPool;\nuse std::rc::Rc;\nuse std::sync::Arc;\nuse std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};\n\nuse rayon::prelude::*;\n\nuse crate::engine::normalized::NormalizedEngineFactory;\nuse crate::engine::split::SplitMatchEngineFactory;\nuse crate::item::{ItemPool, MatchedItem, RankBuilder};\nuse crate::prelude::{AndOrEngineFactory, ExactOrFuzzyEngineFactory, RegexEngineFactory};\nuse crate::{CaseMatching, MatchEngineFactory, SkimOptions};\n\n//==============================================================================\n/// Control handle for a running matcher operation.\n///\n/// Provides methods to check status, retrieve results, and stop the matcher.\npub struct MatcherControl {\n    stopped: Arc<AtomicBool>,\n    interrupt: Arc<AtomicBool>,\n    processed: Arc<AtomicUsize>,\n    matched: Arc<AtomicUsize>,\n}\n\nimpl Default for MatcherControl {\n    fn default() -> Self {\n        Self {\n            stopped: Arc::new(AtomicBool::new(true)),\n            interrupt: Arc::new(AtomicBool::new(false)),\n            processed: Default::default(),\n            matched: Default::default(),\n        }\n    }\n}\n\nimpl MatcherControl {\n    /// Returns the number of items that have been processed so far.\n    #[must_use]\n    pub fn get_num_processed(&self) -> usize {\n        self.processed.load(Ordering::Relaxed)\n    }\n\n    /// Returns the number of items that have matched so far.\n    #[must_use]\n    pub fn get_num_matched(&self) -> usize {\n        self.matched.load(Ordering::Relaxed)\n    }\n\n    /// Signals the matcher to stop processing.\n    pub fn kill(&mut self) {\n        self.interrupt.store(true, Ordering::Relaxed);\n    }\n\n    /// Returns true if the matcher has stopped (either completed or killed).\n    #[must_use]\n    pub fn stopped(&self) -> bool {\n        self.stopped.load(Ordering::Relaxed)\n    }\n}\n\nimpl Drop for MatcherControl {\n    fn drop(&mut self) {\n        self.kill();\n    }\n}\n\n//==============================================================================\n/// The main matcher that coordinates fuzzy/exact matching of items against a query.\npub struct Matcher {\n    engine_factory: Rc<dyn MatchEngineFactory>,\n    case_matching: CaseMatching,\n    /// The rank builder shared with all engines; used to attach criteria to `MatchedItem`s.\n    pub rank_builder: Arc<RankBuilder>,\n}\n\nimpl Matcher {\n    /// Creates a new Matcher builder with the given engine factory.\n    pub fn builder(engine_factory: Rc<dyn MatchEngineFactory>) -> Self {\n        Self {\n            engine_factory,\n            case_matching: CaseMatching::default(),\n            rank_builder: Arc::new(RankBuilder::default()),\n        }\n    }\n\n    /// Sets the case matching mode (smart, ignore, or respect).\n    #[must_use]\n    pub fn case(mut self, case_matching: CaseMatching) -> Self {\n        self.case_matching = case_matching;\n        self\n    }\n\n    /// Sets the rank builder (carries tiebreak criteria).\n    #[must_use]\n    pub fn rank_builder(mut self, rank_builder: Arc<RankBuilder>) -> Self {\n        self.rank_builder = rank_builder;\n        self\n    }\n\n    /// Finalizes the builder and returns the configured Matcher.\n    #[must_use]\n    pub fn build(self) -> Self {\n        self\n    }\n\n    /// Creates a `MatchEngineFactory` from the given options.\n    ///\n    /// This is useful when you need the factory directly (e.g., for filter mode)\n    /// without creating a full Matcher instance.\n    #[must_use]\n    pub fn create_engine_factory(options: &SkimOptions) -> Rc<dyn MatchEngineFactory> {\n        Self::create_engine_factory_with_builder(options).0\n    }\n\n    /// Creates a `MatchEngineFactory` and the associated `RankBuilder` from the given options.\n    ///\n    /// Returns both so callers can attach the builder to `MatchedItem`s for lazy sort-key\n    /// computation.\n    #[must_use]\n    pub fn create_engine_factory_with_builder(options: &SkimOptions) -> (Rc<dyn MatchEngineFactory>, Arc<RankBuilder>) {\n        if options.regex {\n            let regex_factory = RegexEngineFactory::builder();\n            let factory: Rc<dyn MatchEngineFactory> = if options.normalize {\n                Rc::new(NormalizedEngineFactory::new(regex_factory))\n            } else {\n                Rc::new(regex_factory)\n            };\n            (factory, Arc::new(RankBuilder::default()))\n        } else {\n            let rank_builder = Arc::new(RankBuilder::new(options.tiebreak.clone()));\n            log::debug!(\"Creating matcher for algo {:?}\", options.algorithm);\n            let fuzzy_engine_factory = ExactOrFuzzyEngineFactory::builder()\n                .fuzzy_algorithm(options.algorithm)\n                .exact_mode(options.exact)\n                .typos(options.typos)\n                .filter_mode(options.filter.is_some())\n                .last_match(options.last_match)\n                .rank_builder(rank_builder.clone())\n                .build();\n\n            // If split_match is enabled, wrap the fuzzy factory with SplitMatchEngineFactory\n            // Then wrap with AndOrEngineFactory so that queries like \"foo:bar baz:qux\" work\n            let andor_factory = if let Some(delimiter) = options.split_match {\n                let split_factory = SplitMatchEngineFactory::new(fuzzy_engine_factory, delimiter);\n                AndOrEngineFactory::new(split_factory)\n            } else {\n                AndOrEngineFactory::new(fuzzy_engine_factory)\n            };\n\n            // Wrap with NormalizedEngineFactory if normalization is requested\n            let factory: Rc<dyn MatchEngineFactory> = if options.normalize {\n                Rc::new(NormalizedEngineFactory::new(andor_factory))\n            } else {\n                Rc::new(andor_factory)\n            };\n            (factory, rank_builder)\n        }\n    }\n\n    /// Creates a Matcher configured from the given `SkimOptions`.\n    #[must_use]\n    pub fn from_options(options: &SkimOptions) -> Self {\n        let (engine_factory, rank_builder) = Self::create_engine_factory_with_builder(options);\n        Matcher::builder(engine_factory)\n            .case(options.case)\n            .rank_builder(rank_builder)\n            .build()\n    }\n\n    /// Returns the case matching setting for this matcher.\n    #[must_use]\n    pub fn case_matching(&self) -> CaseMatching {\n        self.case_matching\n    }\n\n    /// Returns a reference to the engine factory.\n    #[must_use]\n    pub fn engine_factory(&self) -> &Rc<dyn MatchEngineFactory> {\n        &self.engine_factory\n    }\n\n    /// Runs the matcher on items from the pool in a background thread.\n    ///\n    /// The callback is invoked when matching is complete with the matched items.\n    /// Returns a `MatcherControl` that can be used to monitor progress or stop the matcher.\n    pub fn run<C>(\n        &self,\n        query: &str,\n        item_pool: &Arc<ItemPool>,\n        thread_pool: &ThreadPool,\n        callback: C,\n    ) -> MatcherControl\n    where\n        C: Fn(Vec<MatchedItem>) + Send + 'static,\n    {\n        let matcher_engine = self.engine_factory.create_engine_with_case(query, self.case_matching);\n        debug!(\"engine: {matcher_engine}\");\n        let stopped = Arc::new(AtomicBool::new(false));\n        let stopped_clone = stopped.clone();\n        let interrupt = Arc::new(AtomicBool::new(false));\n        let interrupt_clone = interrupt.clone();\n        let processed = Arc::new(AtomicUsize::new(0));\n        let processed_clone = processed.clone();\n        let matched = Arc::new(AtomicUsize::new(0));\n        let matched_clone = matched.clone();\n        let rank_builder = self.rank_builder.clone();\n\n        // Take items synchronously before spawning to avoid a race condition:\n        // if we took items inside the spawned closure, a subsequent restart_matcher()\n        // could call kill() + reset() before the old closure runs, causing the old\n        // closure to re-take items that should belong to the new matcher.\n        let start = item_pool.num_taken();\n        let items = item_pool.take();\n        let total = items.len();\n        trace!(\"matcher start, total: {total}\");\n\n        thread_pool.spawn(move || {\n            // Process items in parallel using chunk-based accounting to minimize\n            // atomic contention. Each rayon work unit processes a chunk of items,\n            // updating the shared `processed` and `matched` counters only once per\n            // chunk instead of once per item. The interrupt flag is also checked\n            // only once per chunk to amortize the atomic load.\n            //\n            // `with_min_len` ensures rayon doesn't split work into chunks smaller\n            // than CHUNK_SIZE, keeping the overhead of the parallel iterator low\n            // relative to the actual matching work.\n            const CHUNK_SIZE: usize = 512;\n\n            let matched_items: Vec<MatchedItem> = items\n                .into_par_iter()\n                .with_min_len(CHUNK_SIZE)\n                .enumerate()\n                .fold(\n                    || (Vec::new(), 0usize, 0usize), // (local_matches, local_processed, local_matched)\n                    |(mut local_matches, mut local_processed, mut local_matched), (index, item)| {\n                        // Check interrupt once at the start of each chunk boundary.\n                        // The fold processes items sequentially within each rayon work unit,\n                        // so checking every CHUNK_SIZE items amortizes the atomic load.\n                        if local_processed % CHUNK_SIZE == 0 && interrupt.load(Ordering::Relaxed) {\n                            return (local_matches, local_processed, local_matched);\n                        }\n\n                        local_processed += 1;\n\n                        if let Some(match_result) = matcher_engine.match_item(item.as_ref()) {\n                            local_matched += 1;\n                            let mut rank = match_result.rank;\n                            rank.index = i32::try_from(index + start).unwrap_or(i32::MAX);\n                            local_matches.push(MatchedItem {\n                                item,\n                                rank,\n                                rank_builder: rank_builder.clone(),\n                                matched_range: Some(match_result.matched_range),\n                            });\n                        }\n\n                        // Flush counters periodically so the UI sees progress updates.\n                        if local_processed % CHUNK_SIZE == 0 {\n                            processed.fetch_add(CHUNK_SIZE, Ordering::Relaxed);\n                            if local_matched > 0 {\n                                matched.fetch_add(local_matched, Ordering::Relaxed);\n                                local_matched = 0;\n                            }\n                        }\n\n                        (local_matches, local_processed, local_matched)\n                    },\n                )\n                .map(|(local_matches, local_processed, local_matched)| {\n                    // Flush any remaining counts that didn't hit a chunk boundary.\n                    let remainder = local_processed % CHUNK_SIZE;\n                    if remainder > 0 {\n                        processed.fetch_add(remainder, Ordering::Relaxed);\n                    }\n                    if local_matched > 0 {\n                        matched.fetch_add(local_matched, Ordering::Relaxed);\n                    }\n                    local_matches\n                })\n                .reduce(Vec::new, |mut a, mut b| {\n                    // Merge per-thread result vectors. Always extend the larger one\n                    // to avoid unnecessary reallocations.\n                    if a.len() >= b.len() {\n                        a.extend(b);\n                        a\n                    } else {\n                        b.extend(a);\n                        b\n                    }\n                });\n\n            if !interrupt.load(Ordering::SeqCst) {\n                trace!(\"matcher stop, total matched: {}\", matched_items.len());\n                callback(matched_items);\n            }\n            stopped.store(true, Ordering::Relaxed);\n        });\n\n        MatcherControl {\n            stopped: stopped_clone,\n            interrupt: interrupt_clone,\n            matched: matched_clone,\n            processed: processed_clone,\n        }\n    }\n}\n"
  },
  {
    "path": "src/options.rs",
    "content": "//! Configuration options for skim.\n//!\n//! This module provides the `SkimOptions` struct and builder for configuring\n//! all aspects of skim's behavior, including search, display, layout, and interaction settings.\n\nuse std::cell::RefCell;\nuse std::rc::Rc;\n\nuse crossterm::event::{KeyCode, KeyEvent, KeyModifiers};\nuse derive_builder::Builder;\nuse regex::Regex;\n\nuse crate::binds::KeyMap;\nuse crate::item::RankCriteria;\nuse crate::prelude::SkimItemReader;\nuse crate::reader::CommandCollector;\nuse crate::tui::BorderType;\nuse crate::tui::PreviewCallback;\nuse crate::tui::event::Action;\nuse crate::tui::options::{PreviewLayout, TuiLayout};\nuse crate::tui::statusline::InfoDisplay;\nuse crate::util::read_file_lines;\nuse crate::{CaseMatching, FuzzyAlgorithm, Selector, Typos};\n\n#[cfg(feature = \"cli\")]\n/// Custom value parser for delimiter that handles escape sequences\nfn parse_delimiter_value(s: &str) -> Result<Regex, String> {\n    let unescaped = crate::util::unescape_delimiter(s);\n    Regex::new(&unescaped).map_err(|e| format!(\"Invalid regex delimiter: {e}\"))\n}\n\n#[cfg(feature = \"cli\")]\n/// Custom value parser for typo tolerance\n///\n/// - `\"smart\"` → `Typos::Smart` (adaptive: `pattern_length` / 4)\n/// - `\"disabled\"` → `Typos::Disabled`\n/// - `\"N\"` (N >= 0) → `Typos::Fixed(N)`\nfn parse_typos(s: &str) -> Result<Typos, String> {\n    if s.eq_ignore_ascii_case(\"smart\") {\n        Ok(Typos::Smart)\n    } else if s.eq_ignore_ascii_case(\"disabled\") {\n        Ok(Typos::Disabled)\n    } else {\n        s.parse::<usize>()\n            .map(Typos::from)\n            .map_err(|_| format!(\"Invalid typos value '{s}': expected 'smart', 'disabled' or a non-negative integer\"))\n    }\n}\n\n/// The options for `--scheme`\n#[derive(Debug, Clone, Default, PartialEq, Eq)]\n#[cfg_attr(feature = \"cli\", derive(clap::ValueEnum))]\npub enum MatchScheme {\n    /// Default scheme, no modifications to the options\n    #[default]\n    Default,\n    /// Path scheme: will find the furthest match in the item and set `pathname` as the main\n    /// tiebreak\n    Path,\n    /// History scheme: will force `index` as the first tiebreak\n    History,\n}\n\n/// sk - fuzzy finder in Rust\n///\n/// sk is a general purpose command-line fuzzy finder.\n#[allow(missing_docs)] // derive_builder seems to have issues with doc comments ?\n#[derive(Builder)]\n#[builder(build_fn(name = \"final_build\"), setter(into, strip_option))]\n#[builder(default)]\n#[cfg_attr(feature = \"cli\", derive(clap::Parser))]\n#[cfg_attr(\n    feature = \"cli\",\n    command(name = \"sk\", args_override_self = true, verbatim_doc_comment, version, about)\n)]\n#[derive(derive_more::Debug)]\npub struct SkimOptions {\n    //  --- Search ---\n    /// Show results in reverse order\n    ///\n    /// Often used in combination with --no-sort\n    #[cfg_attr(feature = \"cli\", arg(long, help_heading = \"Search\"))]\n    pub tac: bool,\n\n    /// Minimum query length to start showing results\n    ///\n    /// Only show results when the query is at least this many characters long\n    #[cfg_attr(feature = \"cli\", arg(long, help_heading = \"Search\"))]\n    pub min_query_length: Option<usize>,\n\n    /// Do not sort the results\n    ///\n    /// Often used in combination with --tac\n    /// Example: `history | sk --tac --no-sort`\n    #[cfg_attr(feature = \"cli\", arg(long, help_heading = \"Search\"))]\n    pub no_sort: bool,\n\n    /// Comma-separated list of sort criteria to apply when the scores are tied.\n    ///\n    /// * **score**: Score of the fuzzy match algorithm\n    ///\n    ///     - Each criterion could be negated, e.g. (-index)\n    ///     - Each criterion should appear only once in the list\n    #[cfg_attr(\n        feature = \"cli\",\n        arg(\n            short,\n            long,\n            default_value = \"score,begin,end\",\n            value_enum,\n            value_delimiter = ',',\n            help_heading = \"Search\",\n            allow_hyphen_values = true,\n            verbatim_doc_comment\n        )\n    )]\n    pub tiebreak: Vec<RankCriteria>,\n\n    /// Fields to be matched\n    ///\n    /// A field index expression can be a non-zero integer or a range expression (`[BEGIN]..[END]`).\n    /// `--nth` and `--with-nth` take a comma-separated list of field index expressions.\n    ///\n    /// **Examples:**\n    ///     1      The 1st field\n    ///     2      The 2nd field\n    ///     -1     The last field\n    ///     -2     The 2nd to last field\n    ///     3..5   From the 3rd field to the 5th field\n    ///     2..    From the 2nd field to the last field\n    ///     ..-3   From the 1st field to the 3rd to the last field\n    ///     ..     All the fields\n    #[cfg_attr(\n        feature = \"cli\",\n        arg(\n            short,\n            long,\n            default_value = \"\",\n            help_heading = \"Search\",\n            verbatim_doc_comment,\n            value_delimiter = ',',\n            allow_hyphen_values = true,\n        )\n    )]\n    pub nth: Vec<String>,\n\n    /// Fields to be transformed\n    ///\n    /// See **nth** for the details\n    #[cfg_attr(\n        feature = \"cli\",\n        arg(long, default_value = \"\", help_heading = \"Search\", value_delimiter = ',')\n    )]\n    pub with_nth: Vec<String>,\n\n    /// Delimiter between fields\n    ///\n    /// In regex format, default to AWK-style. Escape sequences like \\x00, \\t, \\n are supported.\n    #[cfg_attr(\n        feature = \"cli\",\n        arg(short, long, default_value = r\"[\\t\\n ]+\", value_parser = parse_delimiter_value, help_heading = \"Search\")\n    )]\n    pub delimiter: Regex,\n\n    /// Run in exact mode\n    #[cfg_attr(feature = \"cli\", arg(short, long, help_heading = \"Search\"))]\n    pub exact: bool,\n\n    /// Start in regex mode instead of fuzzy-match\n    #[cfg_attr(feature = \"cli\", arg(long, help_heading = \"Search\"))]\n    pub regex: bool,\n\n    /// Fuzzy matching algorithm\n    ///\n    /// arinae (ari) Latest algorithm\n    /// `skim_v2` Legacy skim algorithm\n    /// clangd  Used in clangd for keyword completion\n    /// fzy     Algorithm from fzy (<https://github.com/jhawthorn/fzy>)\n    #[cfg_attr(\n        feature = \"cli\",\n        arg(\n            long = \"algo\",\n            value_enum,\n            default_value = \"arinae\",\n            help_heading = \"Search\",\n            verbatim_doc_comment\n        )\n    )]\n    pub algorithm: FuzzyAlgorithm,\n\n    /// Case sensitivity\n    ///\n    /// Determines whether or not to ignore case while matching\n    /// Note: this is not used for the Frizbee matcher, it uses a penalty system to favor\n    /// case-sensitivity without enforcing it\n    #[cfg_attr(\n        feature = \"cli\",\n        arg(long, default_value = \"smart\", value_enum, help_heading = \"Search\")\n    )]\n    pub case: CaseMatching,\n\n    /// Enable typo-tolerant matching\n    ///\n    /// When passed without a value (`--typos`), uses adaptive formula (`pattern_length` / 4).\n    /// When passed with a value (e.g. `--typos=2`), uses that exact number as the\n    /// maximum allowed typos. `--typos=0` explicitly disables typo tolerance.\n    /// Applies to both fzy and frizbee matchers.\n    #[cfg_attr(\n        feature = \"cli\",\n        arg(long, default_value = \"disabled\", default_missing_value = \"smart\", num_args = 0..=1, value_parser = parse_typos, overrides_with = \"no_typos\", help_heading = \"Search\")\n    )]\n    pub typos: Typos,\n\n    /// Disable typo-resistant matching\n    #[cfg_attr(\n        feature = \"cli\",\n        arg(long, conflicts_with = \"typos\", conflicts_with = \"typos\", help_heading = \"Search\")\n    )]\n    pub no_typos: bool,\n\n    /// Normalize unicode characters\n    ///\n    /// When set, normalize accents and other unicode diacritics/others\n    #[cfg_attr(feature = \"cli\", arg(long, help_heading = \"Search\"))]\n    pub normalize: bool,\n\n    /// Enable split matching and set delimiter\n    ///\n    /// Split matching runs the matcher in splits: `foo:bar` will match all items matching `foo`, then\n    /// `:`, then `bar` if the delimiter is present, or match normally if not.\n    #[cfg_attr(\n        feature = \"cli\",\n        arg(\n            long,\n            default_missing_value = \":\",\n            help_heading = \"Search\",\n            num_args=0..\n        )\n    )]\n    pub split_match: Option<char>,\n\n    /// Highlight the last match found, not the first one\n    /// This makes tiebreak more pertinent on path items where we want to prioritize a match on the\n    /// last parts\n    #[cfg_attr(feature = \"cli\", arg(long, help_heading = \"Search\"))]\n    pub last_match: bool,\n\n    #[cfg_attr(feature = \"cli\", arg(long, help_heading = \"Search\", default_value = \"default\"))]\n    scheme: Option<MatchScheme>,\n\n    //  --- Interface ---\n    /// Comma separated list of bindings\n    ///\n    /// You can customize key bindings of sk with `--bind` option which takes a  comma-separated  list  of\n    /// key binding expressions. Each key binding expression follows the following format: `<key>:<action>`\n    /// See the [KEYBINDS] section for details\n    ///\n    /// **Example**: `sk --bind=ctrl-j:accept,ctrl-k:kill-line`\n    ///\n    /// ## Multiple actions can be chained using + separator.\n    ///\n    /// **Example**: `sk --bind 'ctrl-a:select-all+accept'`\n    ///\n    /// # Special behaviors\n    ///\n    /// With `execute(...)` and `reload(...)` action, you can execute arbitrary commands without leaving sk.\n    /// For example, you can turn sk into a simple file browser by binding enter key to less command like follows:\n    ///\n    /// ```bash\n    /// sk --bind \"enter:execute(less {})\"\n    /// ```\n    ///\n    /// Note: if no argument is supplied to reload, the default command is run.\n    ///\n    /// You can use the same placeholder expressions as in --preview.\n    ///\n    /// sk  switches  to  the  alternate screen when executing a command. However, if the command is ex‐\n    /// pected to complete quickly, and you are not interested in its output, you might want to use exe‐\n    /// cute-silent instead, which silently executes the command without the  switching.  Note  that  sk\n    /// will  not  be  responsive  until the command is complete. For asynchronous execution, start your\n    /// command as a background process (i.e. appending &).\n    ///\n    /// With if-query-empty and if-query-not-empty action, you could specify the action to  execute  de‐\n    /// pends on the query condition. For example:\n    ///\n    /// `sk --bind 'ctrl-d:if-query-empty(abort)+delete-char'`\n    ///\n    /// If  the query is empty, skim will execute abort action, otherwise execute delete-char action. It\n    /// is equal to ‘delete-char/eof‘.\n    #[cfg_attr(\n        feature = \"cli\",\n        arg(short, long, help_heading = \"Interface\", verbatim_doc_comment, default_value = \"\", num_args=0..)\n    )]\n    pub bind: Vec<String>,\n\n    /// Enable multiple selection\n    ///\n    /// Uses Tab and S-Tab by default for selection\n    #[cfg_attr(\n        feature = \"cli\",\n        arg(short, long, overrides_with = \"no_multi\", help_heading = \"Interface\")\n    )]\n    pub multi: bool,\n\n    /// Disable multiple selection\n    #[cfg_attr(feature = \"cli\", arg(long, conflicts_with = \"multi\", help_heading = \"Interface\"))]\n    pub no_multi: bool,\n\n    /// Disable mouse\n    #[cfg_attr(feature = \"cli\", arg(long, help_heading = \"Interface\"))]\n    pub no_mouse: bool,\n\n    /// Command to invoke dynamically in interactive mode\n    ///\n    /// Will be invoked using `sh -c`\n    #[cfg_attr(feature = \"cli\", arg(short, long, help_heading = \"Interface\"))]\n    pub cmd: Option<String>,\n\n    /// Start skim in interactive mode\n    ///\n    /// In interactive mode, sk will run the command specified by `--cmd` option and display the\n    /// results.\n    #[cfg_attr(feature = \"cli\", arg(short, long, help_heading = \"Interface\"))]\n    pub interactive: bool,\n\n    /// Replace replstr with the selected item in commands\n    #[cfg_attr(feature = \"cli\", arg(short = 'I', default_value = \"{}\", help_heading = \"Interface\"))]\n    pub replstr: String,\n\n    /// Set color theme\n    ///\n    /// Format: [BASE][,COLOR:ANSI[:ATTR1:ATTR2:..]]\n    /// See [THEME] section for details\n    #[cfg_attr(feature = \"cli\", arg(long, help_heading = \"Interface\", verbatim_doc_comment))]\n    pub color: Option<String>,\n\n    /// Disable horizontal scroll\n    #[cfg_attr(feature = \"cli\", arg(long, help_heading = \"Interface\"))]\n    pub no_hscroll: bool,\n\n    /// Keep the right end of the line visible on overflow\n    ///\n    /// Effective only when the query string is empty\n    #[cfg_attr(feature = \"cli\", arg(long, help_heading = \"Interface\"))]\n    pub keep_right: bool,\n\n    /// Show the matched pattern at the line start\n    ///\n    /// Line  will  start  with  the  start of the matched pattern. Effective only when the query\n    /// string is empty. Was designed to skip showing starts of paths of rg/grep results.\n    ///\n    /// e.g. sk -i -c \"rg {q} --color=always\" --skip-to-pattern '[^/]*:' --ansi\n    #[cfg_attr(feature = \"cli\", arg(long, help_heading = \"Interface\", verbatim_doc_comment))]\n    pub skip_to_pattern: Option<String>,\n\n    /// Do not clear previous line if the command returns an empty result\n    ///\n    /// Do not clear previous items if new command returns empty result. This might be useful  to\n    /// reduce flickering when typing new commands and the half-complete commands are not valid.\n    ///\n    /// This is not the default behavior because similar use cases for grep and rg have already been op‐\n    /// timized where empty query results actually mean \"empty\" and previous results should be\n    /// cleared.\n    #[cfg_attr(feature = \"cli\", arg(long, help_heading = \"Interface\", verbatim_doc_comment))]\n    pub no_clear_if_empty: bool,\n\n    /// Do not clear items on start\n    #[cfg_attr(feature = \"cli\", arg(long, help_heading = \"Interface\"))]\n    pub no_clear_start: bool,\n\n    /// Do not clear screen on exit\n    ///\n    /// Do not clear finder interface on exit. If skim was started in full screen mode, it will not switch back to the\n    /// original  screen, so you'll have to manually run tput rmcup to return. This option can be used to avoid\n    /// flickering of the screen when your application needs to start skim multiple times in order.\n    #[cfg_attr(feature = \"cli\", arg(long, help_heading = \"Interface\"))]\n    pub no_clear: bool,\n\n    /// Show error message if command fails\n    #[cfg_attr(feature = \"cli\", arg(long, help_heading = \"Interface\"))]\n    pub show_cmd_error: bool,\n\n    /// Cycle the results by wrapping around when scrolling\n    #[cfg_attr(feature = \"cli\", arg(long, help_heading = \"Interface\"))]\n    pub cycle: bool,\n\n    /// Disable matching entirely\n    #[cfg_attr(feature = \"cli\", arg(long, help_heading = \"Interface\"))]\n    pub disabled: bool,\n\n    //  --- Layout ---\n    /// Set layout\n    ///\n    #[cfg_attr(\n        feature = \"cli\",\n        arg(long, help_heading = \"Layout\", verbatim_doc_comment, default_value = \"default\")\n    )]\n    pub layout: TuiLayout,\n\n    /// Shorthand for reverse layout\n    #[cfg_attr(feature = \"cli\", arg(long, help_heading = \"Layout\", overrides_with = \"layout\"))]\n    pub reverse: bool,\n\n    /// Height of skim's window\n    ///\n    /// Can either be a row count or a percentage\n    #[cfg_attr(feature = \"cli\", arg(long, default_value = \"100%\", help_heading = \"Layout\"))]\n    pub height: String,\n\n    /// Disable height (force full screen)\n    #[cfg_attr(feature = \"cli\", arg(long, help_heading = \"Layout\"))]\n    pub no_height: bool,\n\n    /// Minimum height of skim's window\n    ///\n    /// Useful when the height is set as a percentage\n    /// Ignored when --height is not specified\n    #[cfg_attr(\n        feature = \"cli\",\n        arg(long, default_value = \"10\", help_heading = \"Layout\", verbatim_doc_comment)\n    )]\n    pub min_height: String,\n\n    /// Screen margin\n    ///\n    /// For each side, can be either a row count or a percentage of the terminal size\n    ///\n    /// Format can be one of:\n    ///     - TRBL\n    ///     - TB,RL\n    ///     - T,RL,B\n    ///     - T,R,B,L\n    /// Example: 1,10%\n    #[cfg_attr(\n        feature = \"cli\",\n        arg(long, default_value = \"0\", help_heading = \"Layout\", verbatim_doc_comment)\n    )]\n    pub margin: String,\n\n    /// Set prompt\n    #[cfg_attr(feature = \"cli\", arg(long, short, default_value = \"> \", help_heading = \"Layout\"))]\n    pub prompt: String,\n\n    /// Set prompt in command mode\n    #[cfg_attr(feature = \"cli\", arg(long, default_value = \"c> \", help_heading = \"Layout\"))]\n    pub cmd_prompt: String,\n\n    /// Set selected item icon\n    #[cfg_attr(\n        feature = \"cli\",\n        arg(long = \"selector\", alias = \"pointer\", default_value = \">\", help_heading = \"Layout\")\n    )]\n    pub selector_icon: String,\n\n    /// Set selected item icon\n    #[cfg_attr(\n        feature = \"cli\",\n        arg(\n            long = \"multi-selector\",\n            alias = \"marker\",\n            default_value = \">\",\n            help_heading = \"Layout\"\n        )\n    )]\n    pub multi_select_icon: String,\n\n    //  --- Display ---\n    /// Parse ANSI color codes in input strings\n    ///\n    /// When using skim as a library, this has no effect and ansi parsing should\n    /// be enabled by manually injecting a `cmd_collector` like so:\n    /// ```rust\n    /// use skim::prelude::*;\n    ///\n    /// let _options = SkimOptionsBuilder::default()\n    ///   .cmd(\"ls --color\")\n    ///   .cmd_collector(Rc::new(RefCell::new(SkimItemReader::new(\n    ///     SkimItemReaderOption::default().ansi(true),\n    ///     ))) as Rc<RefCell<dyn CommandCollector>>)\n    ///   .build()\n    ///   .unwrap();\n    /// ```\n    #[cfg_attr(feature = \"cli\", arg(long, help_heading = \"Display\"))]\n    pub ansi: bool,\n\n    /// Number of spaces that make up a tab\n    #[cfg_attr(feature = \"cli\", arg(long, default_value = \"8\", help_heading = \"Display\"))]\n    pub tabstop: usize,\n\n    /// The characters used to display truncated lines\n    #[cfg_attr(\n        feature = \"cli\",\n        arg(long, hide = true, allow_hyphen_values = true, default_value = \"..\")\n    )]\n    pub ellipsis: String,\n\n    /// Set matching result count display position\n    ///\n    ///   - hidden: do not display info\n    ///   - inline: display info in the same row as the input\n    ///   - default: display info in a dedicated row above the input\n    #[cfg_attr(\n        feature = \"cli\",\n        arg(\n            long,\n            help_heading = \"Display\",\n            value_enum,\n            default_value = \"default\",\n            verbatim_doc_comment\n        )\n    )]\n    pub info: InfoDisplay,\n\n    /// Alias for --info=hidden\n    #[cfg_attr(feature = \"cli\", arg(long, help_heading = \"Display\"))]\n    pub no_info: bool,\n\n    /// Alias for --info=inline\n    #[cfg_attr(feature = \"cli\", arg(long, help_heading = \"Display\"))]\n    pub inline_info: bool,\n\n    /// Set header, displayed next to the info\n    ///\n    /// The  given  string  will  be printed as the sticky header. The lines are displayed in the\n    /// given order from top to bottom regardless of --layout option, and  are  not  affected  by\n    /// --with-nth. ANSI color codes are processed even when --ansi is not set.\n    #[cfg_attr(feature = \"cli\", arg(long, help_heading = \"Display\"))]\n    pub header: Option<String>,\n\n    /// Number of lines of the input treated as header\n    ///\n    /// The  first N lines of the input are treated as the sticky header. When `--with-nth` is set,\n    /// the lines are transformed just like the other lines that follow.\n    #[cfg_attr(feature = \"cli\", arg(long, default_value = \"0\", help_heading = \"Display\"))]\n    pub header_lines: usize,\n\n    /// Draw borders around the UI components\n    ///\n    #[cfg_attr(\n        feature = \"cli\",\n        arg(long, default_missing_value = \"plain\", help_heading = \"Display\", num_args=0..)\n    )]\n    #[debug(skip)]\n    pub border: Option<BorderType>,\n\n    /// Wrap items in the item list\n    #[cfg_attr(feature = \"cli\", arg(long = \"wrap\", help_heading = \"Display\"))]\n    pub wrap_items: bool,\n\n    //  --- History ---\n    /// History file\n    ///\n    /// Load search history from the specified file and update the file on completion.\n    ///\n    /// When enabled, CTRL-N and CTRL-P are automatically remapped\n    /// to next-history and previous-history.\n    #[cfg_attr(feature = \"cli\", arg(long = \"history\", help_heading = \"History\"))]\n    pub history_file: Option<String>,\n\n    /// Maximum number of query history entries to keep\n    #[cfg_attr(feature = \"cli\", arg(long, default_value = \"1000\", help_heading = \"History\"))]\n    pub history_size: usize,\n\n    /// Command history file\n    ///\n    /// Load command query history from the specified file and update the file on completion.\n    ///\n    /// When enabled, CTRL-N and CTRL-P are automatically remapped\n    /// to next-history and previous-history.\n    #[cfg_attr(feature = \"cli\", arg(long = \"cmd-history\", help_heading = \"History\"))]\n    pub cmd_history_file: Option<String>,\n\n    /// Maximum number of query history entries to keep\n    #[cfg_attr(feature = \"cli\", arg(long, default_value = \"1000\", help_heading = \"History\"))]\n    pub cmd_history_size: usize,\n\n    //  --- Preview ---\n    /// Preview command\n    ///\n    /// Execute the given command for the current line and display the result on the preview window. {} in the command\n    /// is the placeholder that is replaced to the single-quoted string of the current line. To transform the replace‐\n    /// ment string, specify field index expressions between the braces (See FIELD INDEX EXPRESSION for the details).\n    ///\n    /// **Examples**:\n    ///\n    /// ```bash\n    /// sk --preview='head -$LINES {}'\n    /// ls -l | sk --preview=\"echo user={3} when={-4..-2}; cat {-1}\" --header-lines=1\n    #[cfg_attr(feature = \"cli\", arg(long, help_heading = \"Preview\", verbatim_doc_comment))]\n    pub preview: Option<String>,\n\n    /// Preview window layout\n    ///\n    /// Format: [up|down|left|right][:SIZE[%]][:hidden][:[no]wrap][:[no]pty][:+SCROLL[-OFFSET]]\n    ///\n    /// Determine  the  layout of the preview window. If the argument ends with: hidden, the preview window will be hidden by\n    /// default until toggle-preview action is triggered. Long lines are truncated by default.\n    /// Line wrap can be enabled with `:wrap` flag.\n    /// For more interactive commands or previews that draw complex interfaces, the preview can use a PTY with the `:pty` flag.\n    ///\n    /// Note: the preview will run in a PTY (interactive session) on linux and when `wrap` is unset\n    ///\n    /// If size is given as 0, preview window will not be visible, but sk will still execute the command in the background.\n    ///\n    /// +SCROLL[-OFFSET] determines the initial scroll offset of the preview window. SCROLL can be either a  numeric  integer\n    /// or  a  single-field index expression that refers to a numeric integer. The optional -OFFSET part is for adjusting the\n    /// base offset so that you can see the text above it. It should be given as a numeric integer (-INTEGER), or as a denom‐\n    /// inator form (-/INTEGER) for specifying a fraction of the preview window height.\n    ///\n    /// **Examples**:\n    /// ```bash\n    /// # Non-default scroll window positions and sizes\n    /// sk --preview=\"head {}\" --preview-window=up:30%\n    /// sk --preview=\"file {}\" --preview-window=down:2\n    ///\n    /// # Initial scroll offset is set to the line number of each line of\n    /// # git grep output *minus* 5 lines (-5)\n    /// git grep --line-number '' |\n    ///   sk --delimiter:  --preview 'nl {1}' --preview-window +{2}-5\n    ///\n    ///             # Preview with bat, matching line in the middle of the window (-/2)\n    ///             git grep --line-number '' |\n    ///               sk --delimiter : \\\n    ///                   --preview 'bat --style=numbers --color=always --highlight-line {2} {1}' \\\n    ///                   --preview-window +{2}-/2\n    /// ```\n    #[cfg_attr(\n        feature = \"cli\",\n        arg(\n            long,\n            default_value = \"right:50%\",\n            help_heading = \"Preview\",\n            allow_hyphen_values = true\n        )\n    )]\n    pub preview_window: PreviewLayout,\n\n    //  --- Scripting ---\n    /// Initial query\n    #[cfg_attr(feature = \"cli\", arg(long, short, help_heading = \"Scripting\"))]\n    pub query: Option<String>,\n\n    /// Initial query in interactive mode\n    #[cfg_attr(feature = \"cli\", arg(long, help_heading = \"Scripting\"))]\n    pub cmd_query: Option<String>,\n\n    /// Read input delimited by ASCII NUL(\\\\0) characters\n    #[cfg_attr(feature = \"cli\", arg(long, help_heading = \"Scripting\"))]\n    pub read0: bool,\n\n    /// Print output delimited by ASCII NUL(\\\\0) characters\n    #[cfg_attr(feature = \"cli\", arg(long, help_heading = \"Scripting\"))]\n    pub print0: bool,\n\n    /// Print the query as the first line\n    #[cfg_attr(feature = \"cli\", arg(long, help_heading = \"Scripting\"))]\n    pub print_query: bool,\n\n    /// Print the command as the first line (after print-query)\n    #[cfg_attr(feature = \"cli\", arg(long, help_heading = \"Scripting\"))]\n    pub print_cmd: bool,\n\n    /// Print the score after each item\n    #[cfg_attr(feature = \"cli\", arg(long, help_heading = \"Scripting\"))]\n    pub print_score: bool,\n\n    /// Print the header as the first line (after print-score)\n    #[cfg_attr(feature = \"cli\", arg(long, help_heading = \"Scripting\"))]\n    pub print_header: bool,\n\n    /// Print the current (highlighted) item as the first line (after print-header)\n    #[cfg_attr(feature = \"cli\", arg(long, help_heading = \"Scripting\"))]\n    pub print_current: bool,\n\n    /// Set the output format\n    /// If set, overrides all `print_` options\n    /// Will be expanded the same way as preview or commands\n    #[cfg_attr(feature = \"cli\", arg(long, help_heading = \"Scripting\"))]\n    pub output_format: Option<String>,\n\n    /// Print the ANSI codes, making the output exactly match the input even when `--ansi` is on\n    #[cfg_attr(feature = \"cli\", arg(long, help_heading = \"Scripting\", requires = \"ansi\"))]\n    pub no_strip_ansi: bool,\n\n    /// Do not enter the TUI if the query passed in `-q` matches only one item and return it\n    #[cfg_attr(feature = \"cli\", arg(long, short = '1', help_heading = \"Scripting\"))]\n    pub select_1: bool,\n\n    /// Do not enter the TUI if the query passed in `-q` does not match any item\n    #[cfg_attr(feature = \"cli\", arg(long, short = '0', help_heading = \"Scripting\"))]\n    pub exit_0: bool,\n\n    /// Synchronous search for multi-staged filtering\n    ///\n    /// Synchronous search for multi-staged filtering. If specified,\n    /// skim will launch ncurses finder only after the input stream is complete.\n    /// e.g. `sk --multi | sk --sync`\n    #[cfg_attr(feature = \"cli\", arg(long, help_heading = \"Scripting\"))]\n    pub sync: bool,\n\n    /// Pre-select the first n items in multi-selection mode\n    #[cfg_attr(feature = \"cli\", arg(long, default_value = \"0\", help_heading = \"Scripting\"))]\n    pub pre_select_n: usize,\n\n    /// Pre-select the matched items in multi-selection mode\n    ///\n    /// Check the doc for the detailed syntax:\n    /// <https://docs.rs/regex/1.4.1/regex>/\n    #[cfg_attr(feature = \"cli\", arg(long, default_value = \"\", help_heading = \"Scripting\"))]\n    pub pre_select_pat: String,\n\n    /// Pre-select the items separated by newline character\n    ///\n    /// Example: 'item1\\nitem2'\n    #[cfg_attr(feature = \"cli\", arg(long, default_value = \"\", help_heading = \"Scripting\"))]\n    pub pre_select_items: String,\n\n    /// Pre-select the items read from this file\n    #[cfg_attr(feature = \"cli\", arg(long, help_heading = \"Scripting\"))]\n    pub pre_select_file: Option<String>,\n\n    /// Query for filter mode\n    #[cfg_attr(feature = \"cli\", arg(long, short, help_heading = \"Scripting\"))]\n    pub filter: Option<String>,\n\n    /// Generate shell completion script\n    ///\n    /// Generate completion script for the specified shell: bash, zsh, fish, etc.\n    /// The output can be directly sourced or saved to a file for automatic loading.\n    /// Examples: `source <(sk --shell bash)` (immediate use)\n    ///          `sk --shell bash >> ~/.bash_completion` (persistent use)\n    ///\n    /// Supported shells: bash, zsh, fish, powershell, elvish\n    ///\n    /// Note: While `PowerShell` completions are supported, Windows is not supported for now.\n    #[cfg(feature = \"cli\")]\n    #[cfg_attr(\n        feature = \"cli\",\n        arg(long, value_name = \"SHELL\", help_heading = \"Scripting\", value_enum)\n    )]\n    pub shell: Option<crate::shell::Shell>,\n\n    /// Generate shell key bindings - only for bash, zsh and fish\n    ///\n    /// Generate key bindings script after the shell completions\n    /// See the `shell` option for more details\n    #[cfg(feature = \"cli\")]\n    #[cfg_attr(feature = \"cli\", arg(long, help_heading = \"Scripting\", requires = \"shell\"))]\n    pub shell_bindings: bool,\n\n    /// Generate man page and output it to stdout\n    #[cfg(feature = \"cli\")]\n    #[cfg_attr(feature = \"cli\", arg(long, help_heading = \"Scripting\"))]\n    pub man: bool,\n\n    /// Run an IPC socket with optional name (defaults to `sk`)\n    ///\n    /// The socket expects Actions in Ron format (similar to Rust code), see `./src/tui/event.rs` for all possible Actions\n    /// To write to it, see the `--remote` option or the man page\n    #[cfg_attr(feature = \"cli\", arg(long, help_heading = \"Scripting\", default_missing_value = \"sk\", num_args=0..))]\n    pub listen: Option<String>,\n\n    /// Send commands to an IPC socket with optional name (defaults to `sk`)\n    ///\n    /// The commands are read from stdin, one per line, in the same format as the actions in the\n    /// bind flag. They can also be chained using `+` as a separator.\n    /// All other arguments will be ignored\n    #[cfg_attr(feature = \"cli\", arg(long, help_heading = \"Scripting\", default_missing_value = \"sk\", num_args=0..))]\n    pub remote: Option<String>,\n\n    /// Run in a tmux popup\n    ///\n    /// Format: `sk --tmux <center|top|bottom|left|right>[,SIZE[%]][,SIZE[%]]`\n    ///\n    /// Depending on the direction, the order and behavior of the sizes varies:\n    ///\n    /// Default: center,50%\n    #[cfg_attr(feature = \"cli\", arg(long, verbatim_doc_comment, help_heading = \"Display\", default_missing_value = \"center,50%\", num_args=0..))]\n    pub tmux: Option<String>,\n\n    /// Set the log level\n    #[cfg_attr(feature = \"cli\", arg(long, help_heading = \"Scripting\"))]\n    pub log_level: Option<log::LevelFilter>,\n\n    /// Pipe log output to a file\n    #[cfg_attr(feature = \"cli\", arg(long, help_heading = \"Scripting\"))]\n    pub log_file: Option<String>,\n\n    /// Feature flags\n    #[cfg_attr(feature = \"cli\", arg(long, hide = true, help_heading = \"Scripting\"))]\n    pub flags: Vec<FeatureFlag>,\n\n    // FZF compatibility args\n    #[cfg_attr(feature = \"cli\", arg(short = 'x', long, hide = true))]\n    #[builder(setter(skip))]\n    extended: bool,\n    #[cfg_attr(feature = \"cli\", arg(long, hide = true))]\n    #[builder(setter(skip))]\n    literal: bool,\n    #[cfg_attr(feature = \"cli\", arg(long, hide = true, default_value = \"10\"))]\n    #[builder(setter(skip))]\n    hscroll_off: usize,\n    #[cfg_attr(feature = \"cli\", arg(long, hide = true))]\n    #[builder(setter(skip))]\n    filepath_word: bool,\n    #[cfg_attr(feature = \"cli\", arg(long, hide = true, default_value = \"\"))]\n    #[builder(setter(skip))]\n    jump_labels: String,\n    #[cfg_attr(feature = \"cli\", arg(long, hide = true))]\n    #[builder(setter(skip))]\n    no_bold: bool,\n    #[cfg_attr(feature = \"cli\", arg(long, hide = true))]\n    #[builder(setter(skip))]\n    phony: bool,\n    #[cfg_attr(feature = \"cli\", arg(long, hide = true))]\n    #[builder(setter(skip))]\n    tail: Option<usize>,\n    #[cfg_attr(feature = \"cli\", arg(long, hide = true))]\n    #[builder(setter(skip))]\n    style: Option<String>,\n    #[cfg_attr(feature = \"cli\", arg(long, hide = true))]\n    #[builder(setter(skip))]\n    no_color: bool,\n    #[cfg_attr(feature = \"cli\", arg(long, hide = true))]\n    #[builder(setter(skip))]\n    padding: Option<String>,\n    #[cfg_attr(feature = \"cli\", arg(long, hide = true))]\n    #[builder(setter(skip))]\n    border_label: Option<String>,\n    #[cfg_attr(feature = \"cli\", arg(long, hide = true))]\n    #[builder(setter(skip))]\n    border_label_pos: Option<String>,\n    #[cfg_attr(feature = \"cli\", arg(long, hide = true))]\n    #[builder(setter(skip))]\n    highlight_line: bool,\n    #[cfg_attr(feature = \"cli\", arg(long, hide = true))]\n    #[builder(setter(skip))]\n    wrap_sign: Option<String>,\n    #[cfg_attr(feature = \"cli\", arg(long, hide = true))]\n    #[builder(setter(skip))]\n    no_multi_line: bool,\n    #[cfg_attr(feature = \"cli\", arg(long, hide = true))]\n    #[builder(setter(skip))]\n    raw: bool,\n    #[cfg_attr(feature = \"cli\", arg(long, hide = true))]\n    #[builder(setter(skip))]\n    track: bool,\n    #[cfg_attr(feature = \"cli\", arg(long, hide = true))]\n    #[builder(setter(skip))]\n    gap: Option<usize>,\n    #[cfg_attr(feature = \"cli\", arg(long, hide = true))]\n    #[builder(setter(skip))]\n    gap_line: Option<String>,\n    #[cfg_attr(feature = \"cli\", arg(long, hide = true, default_value = \"0\"))]\n    #[builder(setter(skip))]\n    freeze_left: usize,\n    #[cfg_attr(feature = \"cli\", arg(long, hide = true, default_value = \"0\"))]\n    #[builder(setter(skip))]\n    freeze_right: usize,\n    #[cfg_attr(feature = \"cli\", arg(long, hide = true, default_value = \"0\"))]\n    #[builder(setter(skip))]\n    scroll_off: usize,\n    #[cfg_attr(feature = \"cli\", arg(long, hide = true))]\n    #[builder(setter(skip))]\n    gutter: Option<String>,\n    #[cfg_attr(feature = \"cli\", arg(long, hide = true))]\n    #[builder(setter(skip))]\n    gutter_raw: Option<String>,\n    #[cfg_attr(feature = \"cli\", arg(long, hide = true))]\n    #[builder(setter(skip))]\n    marker_multi_line: Option<String>,\n    #[cfg_attr(feature = \"cli\", arg(long, hide = true))]\n    #[builder(setter(skip))]\n    scrollbar: Option<String>,\n    #[cfg_attr(feature = \"cli\", arg(long, hide = true))]\n    #[builder(setter(skip))]\n    no_scrollbar: bool,\n    #[cfg_attr(feature = \"cli\", arg(long, hide = true))]\n    #[builder(setter(skip))]\n    list_border: Option<String>,\n    #[cfg_attr(feature = \"cli\", arg(long, hide = true))]\n    #[builder(setter(skip))]\n    list_label: Option<String>,\n    #[cfg_attr(feature = \"cli\", arg(long, hide = true))]\n    #[builder(setter(skip))]\n    list_label_pos: Option<String>,\n    #[cfg_attr(feature = \"cli\", arg(long, hide = true))]\n    #[builder(setter(skip))]\n    no_input: bool,\n    #[cfg_attr(feature = \"cli\", arg(long, hide = true))]\n    #[builder(setter(skip))]\n    info_command: Option<String>,\n    #[cfg_attr(feature = \"cli\", arg(long, hide = true))]\n    #[builder(setter(skip))]\n    separator: Option<String>,\n    #[cfg_attr(feature = \"cli\", arg(long, hide = true))]\n    #[builder(setter(skip))]\n    no_separator: bool,\n    #[cfg_attr(feature = \"cli\", arg(long, hide = true))]\n    #[builder(setter(skip))]\n    ghost: Option<String>,\n    #[cfg_attr(feature = \"cli\", arg(long, hide = true))]\n    #[builder(setter(skip))]\n    input_border: Option<String>,\n    #[cfg_attr(feature = \"cli\", arg(long, hide = true))]\n    #[builder(setter(skip))]\n    input_label: Option<String>,\n    #[cfg_attr(feature = \"cli\", arg(long, hide = true))]\n    #[builder(setter(skip))]\n    input_label_pos: Option<String>,\n    #[cfg_attr(feature = \"cli\", arg(long, hide = true))]\n    #[builder(setter(skip))]\n    preview_label: Option<String>,\n    #[cfg_attr(feature = \"cli\", arg(long, hide = true))]\n    #[builder(setter(skip))]\n    preview_label_pos: Option<String>,\n    #[cfg_attr(feature = \"cli\", arg(long, hide = true))]\n    #[builder(setter(skip))]\n    header_first: bool,\n    #[cfg_attr(feature = \"cli\", arg(long, hide = true))]\n    #[builder(setter(skip))]\n    header_border: Option<String>,\n    #[cfg_attr(feature = \"cli\", arg(long, hide = true))]\n    #[builder(setter(skip))]\n    header_lines_border: Option<String>,\n    #[cfg_attr(feature = \"cli\", arg(long, hide = true))]\n    #[builder(setter(skip))]\n    footer: Option<String>,\n    #[cfg_attr(feature = \"cli\", arg(long, hide = true))]\n    #[builder(setter(skip))]\n    footer_border: Option<String>,\n    #[cfg_attr(feature = \"cli\", arg(long, hide = true))]\n    #[builder(setter(skip))]\n    footer_label: Option<String>,\n    #[cfg_attr(feature = \"cli\", arg(long, hide = true))]\n    #[builder(setter(skip))]\n    footer_label_pos: Option<String>,\n    #[cfg_attr(feature = \"cli\", arg(long, hide = true))]\n    #[builder(setter(skip))]\n    with_shell: Option<String>,\n\n    /// Deprecated, kept for compatibility purposes. See `accept()` bind instead.\n    #[cfg_attr(feature = \"cli\", arg(long, help_heading = \"Deprecated\", default_value = \"\"))]\n    expect: String,\n\n    /// Command collector for reading items from commands\n    #[cfg_attr(feature = \"cli\", clap(skip = Rc::new(RefCell::new(SkimItemReader::default())) as Rc<RefCell<dyn CommandCollector>>))]\n    #[builder(setter(into = false))]\n    #[debug(skip)]\n    pub cmd_collector: Rc<RefCell<dyn CommandCollector>>,\n    /// Query history entries loaded from history file\n    #[cfg_attr(feature = \"cli\", clap(skip))]\n    pub query_history: Vec<String>,\n    /// Command history entries loaded from cmd history file\n    #[cfg_attr(feature = \"cli\", clap(skip))]\n    pub cmd_history: Vec<String>,\n    /// Selector for pre-selecting items\n    #[cfg_attr(feature = \"cli\", clap(skip))]\n    #[builder(setter(into = false))]\n    #[debug(skip)]\n    pub selector: Option<Rc<dyn Selector>>,\n    /// Preview Callback\n    ///\n    /// Used to define a function or closure for the preview window, instead of a shell command.\n    ///\n    /// The function will take a `Vec<Arc<dyn SkimItem>>>` containing the currently selected items\n    /// and return a Vec<String> with the lines to display in UTF-8\n    #[cfg_attr(feature = \"cli\", clap(skip))]\n    #[debug(skip)]\n    pub preview_fn: Option<PreviewCallback>,\n\n    /// The internal (parsed) keymap\n    #[cfg_attr(feature = \"cli\", clap(skip))]\n    pub keymap: KeyMap,\n}\n\nimpl Default for SkimOptions {\n    #[allow(clippy::too_many_lines)]\n    fn default() -> Self {\n        Self {\n            split_match: None,\n            no_strip_ansi: false,\n            wrap_items: false,\n            listen: None,\n            remote: None,\n            print_header: false,\n            print_current: false,\n            disabled: false,\n            tac: Default::default(),\n            min_query_length: Default::default(),\n            no_sort: Default::default(),\n            tiebreak: vec![RankCriteria::Score, RankCriteria::Begin, RankCriteria::End],\n            nth: Default::default(),\n            with_nth: Default::default(),\n            delimiter: Regex::new(r\"[\\t\\n ]+\").unwrap(),\n            exact: Default::default(),\n            regex: Default::default(),\n            algorithm: Default::default(),\n            case: Default::default(),\n            typos: Typos::Disabled,\n            no_typos: false,\n            normalize: false,\n            last_match: false,\n            bind: Default::default(),\n            multi: Default::default(),\n            no_multi: Default::default(),\n            no_mouse: Default::default(),\n            cmd: Default::default(),\n            interactive: Default::default(),\n            replstr: String::from(\"{}\"),\n            color: Default::default(),\n            no_hscroll: Default::default(),\n            keep_right: Default::default(),\n            skip_to_pattern: Default::default(),\n            no_clear_if_empty: Default::default(),\n            no_clear_start: Default::default(),\n            no_clear: Default::default(),\n            show_cmd_error: Default::default(),\n            layout: TuiLayout::default(),\n            reverse: Default::default(),\n            height: String::from(\"100%\"),\n            no_height: Default::default(),\n            min_height: String::from(\"10\"),\n            margin: Default::default(),\n            prompt: String::from(\"> \"),\n            cmd_prompt: String::from(\"c> \"),\n            selector_icon: String::from(\">\"),\n            multi_select_icon: String::from(\">\"),\n            ansi: Default::default(),\n            tabstop: 8,\n            info: Default::default(),\n            no_info: Default::default(),\n            inline_info: Default::default(),\n            header: Default::default(),\n            header_lines: Default::default(),\n            history_file: Default::default(),\n            history_size: 1000,\n            cmd_history_file: Default::default(),\n            cmd_history_size: 1000,\n            preview: Default::default(),\n            preview_window: PreviewLayout::default(),\n            query: Default::default(),\n            cmd_query: Default::default(),\n            read0: Default::default(),\n            print0: Default::default(),\n            print_query: Default::default(),\n            print_cmd: Default::default(),\n            print_score: Default::default(),\n            output_format: Default::default(),\n            select_1: Default::default(),\n            exit_0: Default::default(),\n            sync: Default::default(),\n            pre_select_n: Default::default(),\n            pre_select_pat: Default::default(),\n            pre_select_items: Default::default(),\n            pre_select_file: Default::default(),\n            filter: Default::default(),\n            tmux: Default::default(),\n            log_file: Default::default(),\n            extended: Default::default(),\n            literal: Default::default(),\n            cycle: Default::default(),\n            hscroll_off: 10,\n            filepath_word: Default::default(),\n            jump_labels: String::from(\"abcdefghijklmnopqrstuvwxyz\"),\n            border: Default::default(),\n            no_bold: Default::default(),\n            phony: Default::default(),\n            scheme: Default::default(),\n            tail: Default::default(),\n            style: Default::default(),\n            no_color: Default::default(),\n            padding: Default::default(),\n            border_label: Default::default(),\n            border_label_pos: Default::default(),\n            highlight_line: Default::default(),\n            wrap_sign: Default::default(),\n            no_multi_line: Default::default(),\n            raw: Default::default(),\n            track: Default::default(),\n            gap: Default::default(),\n            gap_line: Default::default(),\n            freeze_left: Default::default(),\n            freeze_right: Default::default(),\n            scroll_off: Default::default(),\n            gutter: Default::default(),\n            gutter_raw: Default::default(),\n            marker_multi_line: Default::default(),\n            ellipsis: Default::default(),\n            scrollbar: Default::default(),\n            no_scrollbar: Default::default(),\n            list_border: Default::default(),\n            list_label: Default::default(),\n            list_label_pos: Default::default(),\n            no_input: Default::default(),\n            info_command: Default::default(),\n            separator: Default::default(),\n            no_separator: Default::default(),\n            ghost: Default::default(),\n            input_border: Default::default(),\n            input_label: Default::default(),\n            input_label_pos: Default::default(),\n            preview_label: Default::default(),\n            preview_label_pos: Default::default(),\n            header_first: Default::default(),\n            header_border: Default::default(),\n            header_lines_border: Default::default(),\n            footer: Default::default(),\n            footer_border: Default::default(),\n            footer_label: Default::default(),\n            footer_label_pos: Default::default(),\n            with_shell: Default::default(),\n            expect: Default::default(),\n            cmd_collector: Rc::new(RefCell::new(SkimItemReader::default())) as Rc<RefCell<dyn CommandCollector>>,\n            query_history: Default::default(),\n            cmd_history: Default::default(),\n            selector: Default::default(),\n            preview_fn: Default::default(),\n            keymap: Default::default(),\n            #[cfg(feature = \"cli\")]\n            shell: Default::default(),\n            #[cfg(feature = \"cli\")]\n            man: false,\n            #[cfg(feature = \"cli\")]\n            shell_bindings: false,\n            flags: Default::default(),\n            log_level: Default::default(),\n        }\n    }\n}\n\nimpl SkimOptionsBuilder {\n    /// Builds the `SkimOptions` from the builder\n    ///\n    /// # Errors\n    ///\n    /// Returns an error if any required fields are missing.\n    pub fn build(&mut self) -> Result<SkimOptions, SkimOptionsBuilderError> {\n        self.final_build().map(SkimOptions::build)\n    }\n}\n\nimpl SkimOptions {\n    /// Finalizes the options by applying defaults and initializing components\n    #[must_use]\n    pub fn build(mut self) -> Self {\n        if self.no_height {\n            self.height = String::from(\"100%\");\n        }\n\n        self.keymap = self.bind.iter().fold(KeyMap::default(), |mut res, part| {\n            res.add_keymaps(part.split(','));\n            res\n        });\n\n        if self.reverse {\n            self.layout = TuiLayout::Reverse;\n        }\n        if self.history_file.is_some() || self.cmd_history_file.is_some() {\n            self.init_histories();\n            self.keymap.insert(\n                KeyEvent::new(KeyCode::Char('p'), KeyModifiers::CONTROL),\n                vec![Action::PreviousHistory],\n            );\n            self.keymap.insert(\n                KeyEvent::new(KeyCode::Char('n'), KeyModifiers::CONTROL),\n                vec![Action::NextHistory],\n            );\n        }\n        if self.inline_info {\n            self.info = InfoDisplay::Inline;\n        }\n        if self.no_info {\n            self.info = InfoDisplay::Hidden;\n        }\n        if self.no_typos {\n            self.typos = Typos::Disabled;\n        }\n\n        if let Some(ref filter_query) = self.filter\n            && self.query.is_none()\n        {\n            self.query = Some(filter_query.clone());\n        }\n\n        match self.scheme {\n            None | Some(MatchScheme::Default) => (),\n            Some(MatchScheme::Path) => {\n                self.last_match = true;\n                self.tiebreak.insert(0, RankCriteria::PathName);\n                self.tiebreak.insert(0, RankCriteria::Score);\n            }\n            Some(MatchScheme::History) => self.tiebreak.insert(0, RankCriteria::Index),\n        }\n\n        self\n    }\n    /// Initializes history from configured history files\n    pub fn init_histories(&mut self) {\n        if let Some(histfile) = &self.history_file {\n            self.query_history.extend(read_file_lines(histfile).unwrap_or_default());\n        }\n\n        if let Some(cmd_histfile) = &self.cmd_history_file {\n            self.cmd_history\n                .extend(read_file_lines(cmd_histfile).unwrap_or_default());\n        }\n    }\n    #[cfg(feature = \"cli\")]\n    /// Merges `SKIM_DEFAULT_OPTIONS` with the app's args\n    ///\n    /// # Errors\n    ///\n    /// Returns an error if argument parsing fails.\n    ///\n    /// # Panics\n    ///\n    /// Panics if the process was invoked with no arguments (which should never happen in practice).\n    pub fn from_env() -> Result<Self, clap::Error> {\n        use clap::Parser;\n        use std::env;\n\n        let mut args = Vec::new();\n\n        args.push(\n            env::args()\n                .next()\n                .expect(\"there should be at least one arg: the application name\"),\n        );\n        if let Ok(opts_file) = env::var(\"SKIM_OPTIONS_FILE\")\n            && let Ok(content) = std::fs::read(opts_file)\n        {\n            let mut in_comment = false;\n            let mut pending_comment = false;\n            let without_comments = content\n                .iter()\n                .filter_map(|b| match (in_comment, pending_comment, *b) {\n                    (_, _, b'\\n') => {\n                        in_comment = false;\n                        pending_comment = false;\n                        Some(b' ')\n                    }\n                    (true, _, _) => {\n                        pending_comment = false;\n                        None\n                    }\n                    (_, true, b'#') => {\n                        pending_comment = false;\n                        Some(b'#')\n                    }\n                    (_, false, b'#') => {\n                        pending_comment = true;\n                        None\n                    }\n                    (false, true, _) => {\n                        pending_comment = false;\n                        in_comment = true;\n                        None\n                    }\n                    (false, false, x) => Some(x),\n                })\n                .collect::<Vec<_>>();\n            let parsed = String::from_utf8_lossy(&without_comments);\n            args.extend(shlex::split(&parsed).unwrap_or_default());\n        }\n        args.extend(\n            env::var(\"SKIM_DEFAULT_OPTIONS\")\n                .ok()\n                .and_then(|val| shlex::split(&val))\n                .unwrap_or_default(),\n        );\n        for arg in env::args().skip(1) {\n            args.push(arg);\n        }\n\n        Self::try_parse_from(args)\n    }\n}\n\n/// Feature flags\n#[derive(Copy, Clone, Debug, Eq, PartialEq)]\n#[cfg_attr(feature = \"cli\", derive(clap::ValueEnum))]\npub enum FeatureFlag {\n    /// Disable preview PTY on linux\n    NoPreviewPty,\n    /// Display the item's match score before its value in the item list (for matcher debugging)\n    ShowScore,\n    /// Display the item's index before its value in the item list\n    ShowIndex,\n}\n\n#[allow(unused_macros)]\nmacro_rules! feature_flag {\n    ($options:ident, $name:ident) => {\n        (std::env::var(stringify!(SKIM_FLAG_$name).replace(' ', \"\")).is_ok_and(|x| x.len() > 0)\n            || $options.flags.contains(&crate::options::FeatureFlag::$name))\n    };\n}\n#[allow(unused_imports)]\npub(crate) use feature_flag;\n"
  },
  {
    "path": "src/output.rs",
    "content": "use crate::item::MatchedItem;\nuse crate::tui::Event;\n\n/// Output from running skim, containing the final selection and state\n#[derive(Debug)]\npub struct SkimOutput {\n    /// The final event that makes skim accept/quit.\n    /// Was designed to determine if skim quit or accept.\n    /// Typically there are only two options: `Event::EvActAbort` | `Event::EvActAccept`\n    pub final_event: Event,\n\n    /// quick pass for judging if skim aborts.\n    pub is_abort: bool,\n\n    /// The final key that makes skim accept/quit.\n    /// Note that it might be `Key::Null` if it is triggered by skim.\n    pub final_key: crossterm::event::KeyEvent,\n\n    /// The query\n    pub query: String,\n\n    /// The command query\n    pub cmd: String,\n\n    /// The selected items.\n    pub selected_items: Vec<MatchedItem>,\n\n    /// The current item\n    pub current: Option<MatchedItem>,\n\n    /// The header\n    pub header: String,\n}\n"
  },
  {
    "path": "src/prelude.rs",
    "content": "//! Convenience re-exports of commonly used types.\n//!\n//! This module provides a convenient way to import all the commonly used\n//! skim types and traits with a single `use skim::prelude::*;` statement.\n\npub use crate::engine::{\n    factory::*,\n    fuzzy::{FuzzyAlgorithm, FuzzyEngine},\n};\npub use crate::fuzzy_matcher::skim::SkimMatcherV2;\npub use crate::helper::item_reader::{SkimItemReader, SkimItemReaderOption};\npub use crate::helper::selector::DefaultSkimSelector;\npub use crate::options::{SkimOptions, SkimOptionsBuilder};\npub use crate::output::SkimOutput;\npub use crate::reader::CommandCollector;\npub use crate::tui::{Event, PreviewCallback, event::Action};\npub use crate::*;\npub use kanal::{Receiver, Sender, bounded, unbounded};\npub use std::borrow::Cow;\npub use std::cell::RefCell;\npub use std::rc::Rc;\npub use std::sync::Arc;\npub use std::sync::atomic::{AtomicUsize, Ordering};\n"
  },
  {
    "path": "src/reader.rs",
    "content": "//! Reader is used for reading items from datasource (e.g. stdin or command output)\n//!\n//! After reading in a line, reader will save an item into the pool(items)\nuse crate::item::ItemPool;\nuse crate::options::SkimOptions;\nuse crate::prelude::{Sender, SkimItemReader};\nuse crate::spinlock::SpinLock;\nuse crate::{SkimItem, SkimItemReceiver};\nuse std::cell::RefCell;\nuse std::rc::Rc;\nuse std::sync::Arc;\nuse std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};\n\n/// Trait for collecting items from command output\npub trait CommandCollector {\n    /// execute the `cmd` and produce a\n    /// - skim item producer\n    /// - a channel sender, any message send would mean to terminate the `cmd` process (for now).\n    ///\n    /// Internally, the command collector may start several threads(components), the collector\n    /// should add `1` on every thread creation and sub `1` on thread termination. reader would use\n    /// this information to determine whether the collector had stopped or not.\n    fn invoke(\n        &mut self,\n        cmd: &str,\n        components_to_stop: Arc<AtomicUsize>,\n    ) -> (SkimItemReceiver, crate::prelude::Sender<i32>);\n}\n\n/// Handle for controlling a running reader\npub struct ReaderControl {\n    tx_interrupt: Sender<i32>,\n    tx_interrupt_cmd: Option<Sender<i32>>,\n    components_to_stop: Arc<AtomicUsize>,\n    items: Arc<SpinLock<Vec<Arc<dyn SkimItem>>>>,\n}\n\nimpl ReaderControl {\n    /// Kills the reader and waits for all components to stop\n    pub fn kill(&mut self) {\n        debug!(\n            \"kill reader, components before: {}\",\n            self.components_to_stop.load(Ordering::SeqCst)\n        );\n\n        let _ = self.tx_interrupt_cmd.clone().map(|tx| tx.send(1));\n        let _ = self.tx_interrupt.send(1);\n        while self.components_to_stop.load(Ordering::SeqCst) != 0 {}\n    }\n\n    /// Takes all items collected so far\n    #[must_use]\n    pub fn take(&self) -> Vec<Arc<dyn SkimItem>> {\n        let mut items = self.items.lock();\n        let mut ret = Vec::with_capacity(items.len());\n        ret.append(&mut items);\n        ret\n    }\n\n    /// Returns true if the reader has finished and no items remain\n    #[must_use]\n    pub fn is_done(&self) -> bool {\n        let items = self.items.lock();\n        self.components_to_stop.load(Ordering::SeqCst) == 0 && items.is_empty()\n    }\n}\n\nimpl Drop for ReaderControl {\n    fn drop(&mut self) {\n        self.kill();\n    }\n}\n\n/// Reader for streaming items from commands or other sources\npub struct Reader {\n    cmd_collector: Rc<RefCell<dyn CommandCollector>>,\n    rx_item: Option<SkimItemReceiver>,\n}\n\nimpl Reader {\n    /// Creates a new reader from skim options\n    #[must_use]\n    pub fn from_options(options: &SkimOptions) -> Self {\n        Self {\n            cmd_collector: options.cmd_collector.clone(),\n            rx_item: None,\n        }\n    }\n\n    /// Sets the item source (if None, will use command collector)\n    #[must_use]\n    pub fn source(mut self, rx_item: Option<SkimItemReceiver>) -> Self {\n        self.rx_item = rx_item;\n        self\n    }\n\n    /// Starts the reader and returns a control handle\n    pub fn run(&mut self, app_tx: Sender<Vec<Arc<dyn SkimItem>>>, cmd: &str) -> ReaderControl {\n        let components_to_stop: Arc<AtomicUsize> = Arc::new(AtomicUsize::new(0));\n        let items = Arc::new(SpinLock::new(Vec::new()));\n\n        let (rx_item, tx_interrupt_cmd) = self.rx_item.take().map_or_else(\n            || {\n                let components_to_stop_clone = components_to_stop.clone();\n                let (rx_item, tx_interrupt_cmd) = self.cmd_collector.borrow_mut().invoke(cmd, components_to_stop_clone);\n                (rx_item, Some(tx_interrupt_cmd))\n            },\n            |rx| (rx, None),\n        );\n\n        let components_to_stop_clone = components_to_stop.clone();\n        let tx_interrupt = collect_items(components_to_stop_clone, rx_item, move |items| _ = app_tx.send(items));\n\n        ReaderControl {\n            tx_interrupt,\n            tx_interrupt_cmd,\n            components_to_stop,\n            items,\n        }\n    }\n\n    /// Starts collecting items and sending them to the pool directly\n    /// Returns a control handle\n    pub fn collect(&mut self, item_pool: Arc<ItemPool>, cmd: &str) -> ReaderControl {\n        let components_to_stop: Arc<AtomicUsize> = Arc::new(AtomicUsize::new(0));\n        let items = Arc::new(SpinLock::new(Vec::new()));\n\n        let (rx_item, tx_interrupt_cmd) = self.rx_item.take().map_or_else(\n            || {\n                let components_to_stop_clone = components_to_stop.clone();\n                let (rx_item, tx_interrupt_cmd) = self.cmd_collector.borrow_mut().invoke(cmd, components_to_stop_clone);\n                (rx_item, Some(tx_interrupt_cmd))\n            },\n            |rx| (rx, None),\n        );\n\n        let components_to_stop_clone = components_to_stop.clone();\n        let tx_interrupt = collect_items(components_to_stop_clone, rx_item, move |items| {\n            item_pool.append(items);\n        });\n        debug!(\"collect: started ({components_to_stop:?} components)\");\n\n        ReaderControl {\n            tx_interrupt,\n            tx_interrupt_cmd,\n            components_to_stop,\n            items,\n        }\n    }\n}\n\nimpl Default for Reader {\n    fn default() -> Self {\n        Self {\n            cmd_collector: Rc::new(RefCell::new(SkimItemReader::default())) as Rc<RefCell<dyn CommandCollector>>,\n            rx_item: Default::default(),\n        }\n    }\n}\n\nfn collect_items<F>(components_to_stop: Arc<AtomicUsize>, rx_item: SkimItemReceiver, callback: F) -> Sender<i32>\nwhere\n    F: Fn(Vec<Arc<dyn SkimItem>>) + Send + 'static,\n{\n    let (tx_interrupt, rx_interrupt) = crate::prelude::bounded(8);\n\n    let started = Arc::new(AtomicBool::new(false));\n    let started_clone = started.clone();\n    std::thread::spawn(move || {\n        debug!(\"collect_item start\");\n        components_to_stop.fetch_add(1, Ordering::SeqCst);\n        started_clone.store(true, Ordering::SeqCst); // notify parent that it is started\n\n        loop {\n            if let Ok(Some(msg)) = rx_interrupt.try_recv() {\n                debug!(\"interrupt: {msg}\");\n                break;\n            }\n            match rx_item.try_recv() {\n                Ok(Some(items)) => {\n                    trace!(\"collect_item: got {} items\", items.len());\n                    callback(items);\n                }\n                Ok(None) => {\n                    std::thread::sleep(std::time::Duration::from_millis(10));\n                }\n                Err(_) => {\n                    break;\n                }\n            }\n        }\n\n        components_to_stop.fetch_sub(1, Ordering::SeqCst);\n        debug!(\"collect_item stop\");\n    });\n\n    while !started.load(Ordering::SeqCst) {\n        // busy waiting for the thread to start. (components_to_stop is added)\n    }\n\n    tx_interrupt\n}\n"
  },
  {
    "path": "src/shell.rs",
    "content": "//! Provides helpers to easily generate shell completions\nuse clap::CommandFactory;\n\nuse crate::SkimOptions;\n\n/// Available shells for completion generation\n#[derive(Clone, clap::ValueEnum, PartialEq, Debug)]\npub enum Shell {\n    /// Bourne Again `SHell`\n    Bash,\n    /// Elvish shell\n    Elvish,\n    /// Friendly Interactive `SHell`\n    Fish,\n    /// Nushell (nu)\n    Nushell,\n    /// `PowerShell`\n    PowerShell,\n    /// Zsh\n    Zsh,\n}\n\n/// Generate the completion and write it to stdout\npub fn generate_completions(sh: &Shell) {\n    use Shell::{Bash, Elvish, Fish, Nushell, PowerShell, Zsh};\n    let output = &mut std::io::stdout();\n    let cmd = &mut SkimOptions::command();\n    let bin_name = \"sk\";\n\n    if *sh == Nushell {\n        clap_complete::generate(clap_complete_nushell::Nushell, cmd, bin_name, output);\n    } else {\n        let clap_shell: clap_complete::Shell = match sh {\n            Bash => clap_complete::Shell::Bash,\n            Elvish => clap_complete::Shell::Elvish,\n            Fish => clap_complete::Shell::Fish,\n            PowerShell => clap_complete::Shell::PowerShell,\n            Zsh => clap_complete::Shell::Zsh,\n            Nushell => unreachable!(),\n        };\n        clap_complete::generate(clap_shell, cmd, bin_name, output);\n    }\n}\n\n/// Generate the key-bindings script and write it to stdout\npub fn generate_key_bindings(sh: &Shell) {\n    use Shell::{Bash, Fish, Zsh};\n    let binds_script = match sh {\n        Bash => include_str!(\"../shell/key-bindings.bash\"),\n        Zsh => include_str!(\"../shell/key-bindings.zsh\"),\n        Fish => include_str!(\"../shell/key-bindings.fish\"),\n        _ => \"\",\n    };\n    if !binds_script.is_empty() {\n        println!(\"{binds_script}\");\n    }\n}\n"
  },
  {
    "path": "src/skim.rs",
    "content": "//! Module containing skim's entry point\nuse std::env;\nuse std::io::{BufWriter, Stderr};\nuse std::sync::Arc;\nuse std::time::Duration;\n\nuse color_eyre::eyre::Result;\nuse color_eyre::eyre::{self, OptionExt};\nuse crossterm::event::{KeyCode, KeyEvent, KeyModifiers};\nuse tokio::{runtime::Handle, select, task::block_in_place};\n\nuse crate::reader::{Reader, ReaderControl};\nuse crate::tui::{App, Event, Size, Tui, event::Action};\nuse crate::{SkimItem, SkimItemReceiver, SkimOptions, SkimOutput};\n\n/// Main entry point for running skim\npub struct Skim<Backend = ratatui::backend::CrosstermBackend<BufWriter<Stderr>>>\nwhere\n    Backend: ratatui::backend::Backend,\n    Backend::Error: Send + Sync + 'static,\n{\n    app: App,\n    tui: Option<Tui<Backend>>,\n    height: Size,\n    reader: Reader,\n    reader_done: bool,\n    initial_cmd: String,\n    reader_control: Option<ReaderControl>,\n    matcher_interval: Option<tokio::time::Interval>,\n    listener: Option<interprocess::local_socket::tokio::Listener>,\n    final_event: Event,\n    final_key: KeyEvent,\n}\n\nimpl Skim {\n    /// Run skim, collecting items from the source and using options\n    ///\n    /// # Params\n    ///\n    /// - options: the \"complex\" options that control how skim behaves\n    /// - source: a stream of items to be passed to skim for filtering.\n    ///   If None is given, skim will invoke the command given to fetch the items.\n    ///\n    /// # Returns\n    ///\n    /// - None: on internal errors.\n    /// - `SkimOutput`: the collected key, event, query, selected items, etc.\n    ///\n    /// # Errors\n    ///\n    /// Returns an error if skim initialization or the TUI loop fails.\n    ///\n    /// # Panics\n    ///\n    /// Panics if the tui fails to initialize\n    pub fn run_with(options: SkimOptions, source: Option<SkimItemReceiver>) -> Result<SkimOutput> {\n        trace!(\"running skim\");\n        let mut skim = Self::init(options, source)?;\n\n        skim.start();\n\n        if skim.should_enter() {\n            skim.init_tui()?;\n            let task = async {\n                skim.enter().await?;\n                skim.run().await?;\n                eyre::Ok(())\n            };\n\n            if let Ok(handle) = Handle::try_current() {\n                block_in_place(|| handle.block_on(task))?;\n            } else {\n                let rt = tokio::runtime::Runtime::new()?;\n                rt.block_on(task)?;\n            }\n        } else {\n            // We didn't enter\n            skim.final_event = Event::Action(Action::Accept(None));\n        }\n        let output = skim.output();\n        debug!(\"output: {output:?}\");\n\n        Ok(output)\n    }\n\n    /// Run skim with a Vec of items\n    ///\n    /// # Errors\n    ///\n    /// Returns an error if sending items to the channel or running skim fails.\n    ///\n    /// ```no_run\n    /// use skim::prelude::*;\n    ///\n    /// let opts = SkimOptionsBuilder::default().multi(true).build().unwrap();\n    /// Skim::run_items(opts, [\"foo\", \"bar\"]);\n    /// ```\n    pub fn run_items<I, T>(options: SkimOptions, items: I) -> Result<SkimOutput>\n    where\n        I: IntoIterator<Item = T>,\n        T: SkimItem,\n    {\n        const BATCH_SIZE: usize = 1024;\n        let (tx, rx) = crate::prelude::unbounded();\n        let mut batch: Vec<Arc<dyn SkimItem>> = Vec::with_capacity(BATCH_SIZE);\n        for item in items {\n            if batch.len() == 1024 {\n                tx.send(batch)?;\n                batch = Vec::with_capacity(BATCH_SIZE);\n            }\n            batch.push(Arc::new(item) as Arc<dyn SkimItem>);\n        }\n        tx.send(batch)?;\n        Self::run_with(options, Some(rx))\n    }\n\n    /// Initialize the TUI with the default crossterm backend, but do not enter it yet\n    ///\n    /// # Errors\n    ///\n    /// Returns an error if the TUI backend cannot be initialized.\n    pub fn init_tui(&mut self) -> Result<()> {\n        self.tui = Some(Tui::new_with_height(self.height)?);\n        Ok(())\n    }\n}\n\nimpl<Backend: ratatui::backend::Backend + 'static> Skim<Backend>\nwhere\n    Backend::Error: Send + Sync + 'static,\n{\n    /// Initialize skim, without starting anything yet\n    ///\n    /// # Errors\n    ///\n    /// Returns an error if parsing the height or other options fails.\n    pub fn init(options: SkimOptions, source: Option<SkimItemReceiver>) -> Result<Self> {\n        const SKIM_DEFAULT_COMMAND: &str = \"find .\";\n        let height = Size::try_from(options.height.as_str())?;\n\n        // application state\n        // Initialize theme from options\n        let theme = Arc::new(crate::theme::ColorTheme::init_from_options(&options));\n        let reader = Reader::from_options(&options).source(source);\n        let default_command = String::from(match env::var(\"SKIM_DEFAULT_COMMAND\").as_deref() {\n            Err(_) | Ok(\"\") => SKIM_DEFAULT_COMMAND,\n            Ok(v) => v,\n        });\n        let cmd = options.cmd.clone().unwrap_or(default_command);\n\n        let app = App::from_options(options, theme.clone(), cmd.clone());\n\n        //------------------------------------------------------------------------------\n        // reader\n        // In interactive mode, expand all placeholders ({}, {q}, etc) with initial query (empty or from --query)\n        let initial_cmd = if app.options.interactive && app.options.cmd.is_some() {\n            let expanded = app.expand_cmd(&cmd, true);\n            log::debug!(\"Interactive mode: initial_cmd = {expanded:?} (from template {cmd:?})\");\n            expanded\n        } else {\n            cmd.clone()\n        };\n        Ok(Self {\n            app,\n            height,\n            reader,\n            reader_done: false,\n            initial_cmd,\n            tui: None,\n            reader_control: None,\n            matcher_interval: None,\n            listener: None,\n            final_event: Event::Quit,\n            final_key: KeyEvent::new(KeyCode::Enter, KeyModifiers::empty()),\n        })\n    }\n\n    /// Start the reader and matcher, but do not enter the TUI yet\n    pub fn start(&mut self) {\n        debug!(\"Starting reader with initial_cmd: {:?}\", self.initial_cmd);\n        self.reader_control = Some(self.reader.collect(self.app.item_pool.clone(), &self.initial_cmd));\n        self.app.restart_matcher(true);\n    }\n\n    /// Handle a reload event by killing the current reader, clearing items, and starting a new reader.\n    ///\n    /// This encapsulates the reload logic from the main event loop so it can\n    /// be reused by test harnesses without reimplementing it.\n    pub fn handle_reload(&mut self, new_cmd: &str) {\n        debug!(\"reloading with cmd {new_cmd}\");\n        // Kill the current reader\n        if let Some(rc) = self.reader_control.as_mut() {\n            rc.kill();\n        }\n        // Clear items\n        self.app.item_pool.clear();\n        // Clear displayed items unless no_clear_if_empty is set\n        if !self.app.options.no_clear_if_empty {\n            self.app.item_list.clear();\n        }\n        self.app.restart_matcher(true);\n        // Start a new reader with the new command\n        self.reader_control = Some(self.reader.collect(self.app.item_pool.clone(), new_cmd));\n        self.reader_done = false;\n    }\n\n    /// Check if the reader has finished and restart the matcher if needed.\n    ///\n    /// This encapsulates the reader-status check from the main event loop\n    /// so it can be reused by test harnesses.\n    ///\n    /// Returns `true` if the reader has completed.\n    pub fn check_reader(&mut self) -> bool {\n        if self\n            .reader_control\n            .as_ref()\n            .is_some_and(super::reader::ReaderControl::is_done)\n            && !self.reader_done\n        {\n            self.reader_done = true;\n            self.app.restart_matcher(false);\n            true\n        } else {\n            false\n        }\n    }\n\n    /// Returns `true` if the reader is done (has finished producing items).\n    pub fn reader_done(&self) -> bool {\n        self.reader_done\n            && self\n                .reader_control\n                .as_ref()\n                .is_none_or(super::reader::ReaderControl::is_done)\n    }\n\n    /// Returns `true` if the matcher is stopped\n    pub fn matcher_stopped(&self) -> bool {\n        self.app.matcher_control.stopped()\n    }\n\n    /// Initialize the TUI with a caller-provided instance.\n    ///\n    /// Use this instead of [`init_tui()`](Skim::init_tui) when you need a\n    /// non-default backend (e.g. `TestBackend` for snapshot tests).\n    pub fn init_tui_with(&mut self, tui: Tui<Backend>) {\n        self.tui = Some(tui);\n    }\n\n    /// Returns a shared reference to the application state.\n    pub fn app(&self) -> &App {\n        &self.app\n    }\n\n    /// Returns a mutable reference to the application state.\n    pub fn app_mut(&mut self) -> &mut App {\n        &mut self.app\n    }\n\n    /// Returns a shared reference to the TUI.\n    ///\n    /// # Panics\n    ///\n    /// Panics if the TUI has not been initialized yet.\n    pub fn tui_ref(&self) -> &Tui<Backend> {\n        self.tui.as_ref().expect(\"TUI needs to be initialized before access\")\n    }\n\n    /// Returns a mutable reference to the TUI.\n    ///\n    /// # Panics\n    ///\n    /// Panics if the TUI has not been initialized yet.\n    pub fn tui_mut(&mut self) -> &mut Tui<Backend> {\n        self.tui.as_mut().expect(\"TUI needs to be initialized before access\")\n    }\n\n    /// Returns mutable references to both the app and the TUI simultaneously.\n    ///\n    /// This is useful when you need to call `app.handle_event(tui, ...)` or\n    /// `tui.draw(|frame| frame.render_widget(app, ...))`, which require\n    /// disjoint mutable borrows of both fields.\n    ///\n    /// # Panics\n    ///\n    /// Panics if the TUI has not been initialized yet.\n    pub fn app_and_tui(&mut self) -> (&mut App, &mut Tui<Backend>) {\n        (\n            &mut self.app,\n            self.tui.as_mut().expect(\"TUI needs to be initialized before access\"),\n        )\n    }\n\n    /// Returns a shared reference to the final event that caused skim to quit.\n    pub fn final_event(&self) -> &Event {\n        &self.final_event\n    }\n\n    /// Returns a clone of the TUI event sender.\n    ///\n    /// Use this to send events (e.g. [`Event::Render`], [`Event::Action`])\n    /// to the running skim instance from outside the event loop. The sender\n    /// is cheap to clone and can be moved into async blocks or other tasks.\n    ///\n    /// Must be called after [`init_tui()`](Skim::init_tui).\n    ///\n    /// # Panics\n    ///\n    /// Panics if `init_tui` has not been called before this method.\n    pub fn event_sender(&self) -> tokio::sync::mpsc::Sender<Event> {\n        self.tui\n            .as_ref()\n            .expect(\"TUI needs to be initialized using Skim::init_tui before getting the event sender\")\n            .event_tx\n            .clone()\n    }\n\n    /// Enter the TUI\n    ///\n    /// # Errors\n    ///\n    /// Returns an error if the TUI cannot be entered or the input listener fails.\n    ///\n    /// # Panics\n    ///\n    /// Panics if `init_tui` has not been called before this method.\n    ///\n    /// # Async\n    ///\n    /// This will call `init_listener`, which requires a tokio runtime\n    /// Though it is not technically async code, this is a good hint.\n    #[allow(clippy::unused_async)]\n    pub async fn enter(&mut self) -> Result<()> {\n        debug!(\"Entering TUI\");\n        self.init_listener()?;\n        self.tui\n            .as_mut()\n            .expect(\"TUI needs to be initialized using Skim::init_tui before entering\")\n            .enter()\n    }\n\n    /// Checks read-0 select-1, filter, and sync to wait and returns whether or not we should enter\n    ///\n    /// # Panics\n    ///\n    /// Panics if `start` has not been called before this method.\n    pub fn should_enter(&mut self) -> bool {\n        let reader_control = self\n            .reader_control\n            .as_ref()\n            .expect(\"reader_control needs to be initialized using Skim::start\");\n        let app = &mut self.app;\n\n        // Filter mode: wait for all items to be read and matched, then return without entering TUI\n        if app.options.filter.is_some() {\n            trace!(\"filter mode: waiting for all items to be processed\");\n            loop {\n                let matcher_stopped = app.matcher_control.stopped();\n                let reader_done = reader_control.is_done();\n                if matcher_stopped && reader_done && app.item_pool.num_not_taken() == 0 {\n                    break;\n                }\n                std::thread::sleep(Duration::from_millis(1));\n                app.restart_matcher(false);\n            }\n            app.item_list.items = app.item_list.processed_items.lock().take().unwrap_or_default().items;\n            debug!(\"filter mode: matched {} items\", app.item_list.items.len());\n            return false;\n        }\n\n        // Deal with read-0 / select-1\n        let min_items_before_enter = if app.options.exit_0 {\n            1\n        } else if app.options.select_1 {\n            2\n        } else if app.options.sync {\n            usize::MAX\n        } else {\n            0\n        };\n        if min_items_before_enter > 0 || app.options.sync {\n            trace!(\n                \"checking matcher, stopped: {}, processed: {}, matched: {}/{}, pool: {}, query: {}, reader_control_done: {}\",\n                app.matcher_control.stopped(),\n                app.matcher_control.get_num_processed(),\n                app.matcher_control.get_num_matched(),\n                min_items_before_enter,\n                app.item_pool.num_not_taken(),\n                app.input.value,\n                reader_control.is_done()\n            );\n            while app.matcher_control.get_num_matched() < min_items_before_enter\n                && (!app.matcher_control.stopped() || !reader_control.is_done())\n            {\n                trace!(\"still waiting\");\n                std::thread::sleep(Duration::from_millis(1));\n                app.restart_matcher(false);\n            }\n            trace!(\n                \"checked matcher, stopped: {}, processed: {}, pool: {}, query: {}, reader_control_done: {}\",\n                app.matcher_control.stopped(),\n                app.matcher_control.get_num_processed(),\n                app.item_pool.num_not_taken(),\n                app.input.value,\n                reader_control.is_done()\n            );\n            trace!(\n                \"checking for matched item count before entering: {}/{min_items_before_enter}\",\n                app.matcher_control.get_num_matched()\n            );\n            if app.matcher_control.get_num_matched() == min_items_before_enter - 1 {\n                app.item_list.items = app.item_list.processed_items.lock().take().unwrap_or_default().items;\n                debug!(\"early exit, result: {:?}\", app.results());\n                return false;\n            }\n        }\n        true\n    }\n\n    /// Initialize the IPC socket listener\n    /// This needs to be called from an async context despite being sync\n    fn init_listener(&mut self) -> Result<()> {\n        if let Some(socket_name) = &self.app.options.listen {\n            self.listener = Some(\n                interprocess::local_socket::ListenerOptions::new()\n                    .name(interprocess::local_socket::ToNsName::to_ns_name::<\n                        interprocess::local_socket::GenericNamespaced,\n                    >(socket_name.to_owned())?)\n                    .create_tokio()?,\n            );\n        }\n        Ok(())\n    }\n\n    /// Capture `self` and extract the output\n    /// This will perform cleanup\n    ///\n    /// # Panics\n    ///\n    /// Panics if the selected items or current item cannot be retrieved.\n    pub fn output(mut self) -> SkimOutput {\n        if let Some(mut rc) = self.reader_control.take() {\n            rc.kill();\n        }\n\n        // Extract final_key and is_abort from final_event\n        let is_abort = !matches!(&self.final_event, Event::Action(Action::Accept(_)));\n\n        let selected_items = self.app.results();\n\n        let cmd = if self.app.options.interactive {\n            self.app.input.to_string()\n        } else if self.app.options.cmd_query.is_some() {\n            self.app.options.cmd_query.clone().unwrap()\n        } else {\n            self.initial_cmd.clone()\n        };\n        let query = self.app.input.to_string();\n        let current = self.app.item_list.selected();\n        let header = self.app.header.header.clone();\n        let final_event = self.final_event.clone();\n        let final_key = self.final_key;\n\n        drop(self);\n\n        SkimOutput {\n            final_event,\n            is_abort,\n            final_key,\n            query,\n            cmd,\n            selected_items,\n            current,\n            header,\n        }\n    }\n\n    /// Returns true if skim has finished (the user accepted or aborted)\n    pub fn should_quit(&self) -> bool {\n        self.app.should_quit\n    }\n\n    /// Process a single event loop iteration.\n    ///\n    /// This awaits the next event from the TUI, matcher, or IPC listener,\n    /// processes it, and returns. Use this in your own event loop when you\n    /// need fine-grained control over the application lifecycle.\n    ///\n    /// Returns `Ok(true)` if skim should quit, `Ok(false)` to continue.\n    ///\n    /// # Errors\n    ///\n    /// Returns an error if the TUI cannot produce the next event or if event handling fails.\n    ///\n    /// # Panics\n    ///\n    /// Panics if `init_tui` has not been called before this method.\n    ///\n    /// # Example\n    ///\n    /// ```ignore\n    /// while !skim.tick().await? {\n    ///     // do your own work between ticks\n    /// }\n    /// ```\n    pub async fn tick(&mut self) -> Result<bool> {\n        let matcher_interval = &mut self.matcher_interval;\n        let items_available = self.app.item_pool.items_available.clone();\n        select! {\n            event = self.tui.as_mut().expect(\"TUI should be initialized before the event loop can start\").next() => {\n                let evt = event.ok_or_eyre(\"Could not acquire next event\")?;\n\n                if let Event::Key(k) = &evt {\n                  self.final_key.clone_from(k);\n                } else {\n                  self.final_event = evt.clone();\n                }\n\n\n                // Handle reload event separately\n                if let Event::Reload(new_cmd) = &evt {\n                    self.handle_reload(&new_cmd.clone());\n                } else {\n                    self.app.handle_event(self.tui.as_mut().expect(\"TUI should be initialized before handling events\"), &evt)?;\n                }\n\n                // Check reader status and update\n                self.check_reader();\n            }\n            () = async {\n                match matcher_interval {\n                    Some(interval) => { interval.tick().await; },\n                    None => std::future::pending::<()>().await,\n                }\n            } => {\n              self.app.restart_matcher(false);\n            }\n            // Wake immediately when new items arrive in the pool so the matcher\n            // can pick them up without waiting for the next periodic interval.\n            () = items_available.notified() => {\n                self.app.restart_matcher(false);\n            }\n            Ok(stream) = async {\n                match &self.listener {\n                    Some(l) => interprocess::local_socket::traits::tokio::Listener::accept(l).await,\n                    None => std::future::pending().await,\n                }\n            } => {\n                debug!(\"Listener accepted a connection\");\n                let event_tx_clone_ipc = self.tui.as_ref().expect(\"TUI should be initialized before listening\").event_tx.clone();\n                tokio::spawn(async move {\n                    use tokio::io::AsyncBufReadExt;\n                    let reader = tokio::io::BufReader::new(stream);\n                    let mut lines = reader.lines();\n                    while let Ok(Some(line)) = lines.next_line().await {\n                        debug!(\"listener: got {line}\");\n                        if let Ok(act) = ron::from_str::<Action>(&line) {\n                            debug!(\"listener: parsed into action {act:?}\");\n                            _ = event_tx_clone_ipc.try_send(Event::Action(act));\n                        }\n                    }\n                });\n            }\n        }\n\n        Ok(self.app.should_quit)\n    }\n\n    /// Run the event loop on the current task until skim quits.\n    ///\n    /// This is a convenience wrapper around [`tick()`](Self::tick) that loops\n    /// until the user accepts or aborts. Use `tick()` directly if you need\n    /// to interleave your own logic between iterations.\n    ///\n    /// # Errors\n    ///\n    /// Returns an error if any tick in the event loop fails.\n    pub async fn run(&mut self) -> Result<()> {\n        self.matcher_interval = Some(tokio::time::interval(Duration::from_millis(10)));\n        trace!(\"Starting event loop\");\n        loop {\n            if self.tick().await? {\n                break Ok(());\n            }\n        }\n    }\n\n    /// Spawn the event loop and run a user-provided future concurrently.\n    ///\n    /// This consumes `self`, spawns the event loop as a local task, and runs\n    /// `user_task` alongside it. When the user accepts or aborts in the TUI,\n    /// the event loop completes and the [`SkimOutput`] is returned — regardless\n    /// of whether `user_task` has finished.\n    ///\n    /// Use this when you need to send items or do other work concurrently\n    /// while the TUI is running.\n    ///\n    /// # Errors\n    ///\n    /// Returns an error if the event loop or the task join fails.\n    ///\n    /// # Example\n    ///\n    /// ```ignore\n    /// let output = skim.run_until(async {\n    ///     for i in 1..=10 {\n    ///         tx.send(vec![Arc::new(format!(\"item {i}\"))]);\n    ///         tokio::time::sleep(Duration::from_millis(100)).await;\n    ///     }\n    /// }).await?;\n    /// ```\n    pub async fn run_until<F: Future + 'static>(mut self, user_task: F) -> Result<SkimOutput> {\n        let local = tokio::task::LocalSet::new();\n        local\n            .run_until(async {\n                let handle = tokio::task::spawn_local(async move {\n                    self.run().await?;\n                    Ok(self.output())\n                });\n                tokio::task::spawn_local(user_task);\n                handle.await?\n            })\n            .await\n    }\n}\n"
  },
  {
    "path": "src/skim_item.rs",
    "content": "use ratatui::text::Line;\nuse std::{\n    borrow::Cow,\n    fmt::{Debug, Display},\n};\n\nuse crate::{AsAny, DisplayContext, ItemPreview, PreviewContext};\n\n/// A `SkimItem` defines what's been processed(fetched, matched, previewed and returned) by skim\n///\n/// # Downcast Example\n/// Skim will return the item back, but in `Arc<dyn SkimItem>` form. We might want a reference\n/// to the concrete type instead of trait object. Skim provide a somehow \"complicated\" way to\n/// `downcast` it back to the reference of the original concrete type.\n///\n/// ```rust\n/// use skim::prelude::*;\n///\n/// struct MyItem {}\n/// impl SkimItem for MyItem {\n///     fn text(&self) -> Cow<str> {\n///         unimplemented!()\n///     }\n/// }\n///\n/// impl MyItem {\n///     pub fn mutable(&mut self) -> i32 {\n///         1\n///     }\n///\n///     pub fn immutable(&self) -> i32 {\n///         0\n///     }\n/// }\n///\n/// let mut ret: Arc<dyn SkimItem> = Arc::new(MyItem{});\n/// let mutable: &mut MyItem = Arc::get_mut(&mut ret)\n///     .expect(\"item is referenced by others\")\n///     .as_any_mut() // cast to Any\n///     .downcast_mut::<MyItem>() // downcast to (mut) concrete type\n///     .expect(\"something wrong with downcast\");\n/// assert_eq!(mutable.mutable(), 1);\n///\n/// let immutable: &MyItem = (*ret).as_any() // cast to Any\n///     .downcast_ref::<MyItem>() // downcast to concrete type\n///     .expect(\"something wrong with downcast\");\n/// assert_eq!(immutable.immutable(), 0)\n/// ```\npub trait SkimItem: AsAny + Send + Sync + 'static {\n    /// The string to be used for matching (without color)\n    fn text(&self) -> Cow<'_, str>;\n\n    /// The content to be displayed on the item list, could contain ANSI properties\n    fn display(&self, context: DisplayContext) -> Line<'_> {\n        context.to_line(self.text())\n    }\n\n    /// Custom preview content, default to `ItemPreview::Global` which will use global preview\n    /// setting(i.e. the command set by `preview` option)\n    fn preview(&self, _context: PreviewContext) -> ItemPreview {\n        ItemPreview::Global\n    }\n\n    /// Get output text(after accept), default to `text()`\n    ///\n    /// Note that this function is intended to be used by the caller of skim and will not be used by\n    /// skim. And since skim will return the item back in `SkimOutput`, if string is not what you\n    /// want, you could still use `downcast` to retain the pointer to the original struct.\n    fn output(&self) -> Cow<'_, str> {\n        self.text()\n    }\n\n    /// Limit the matching ranges of the `get_text` of the item.\n    /// providing (`start_byte`, `end_byte`) of the range\n    fn get_matching_ranges(&self) -> Option<&[(usize, usize)]> {\n        None\n    }\n}\n\n//------------------------------------------------------------------------------\n// Implement SkimItem for raw strings\n\nimpl<T: AsRef<str> + Send + Sync + 'static> SkimItem for T {\n    fn text(&self) -> Cow<'_, str> {\n        Cow::Borrowed(self.as_ref())\n    }\n}\n\nimpl Display for dyn SkimItem {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        f.write_str(&self.text())\n    }\n}\nimpl Debug for dyn SkimItem {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        f.write_fmt(format_args!(\"SkimItem {{ text: {} }}\", self.text(),))\n    }\n}\n"
  },
  {
    "path": "src/spinlock.rs",
    "content": "//! `SpinLock` implemented using `AtomicBool`\n//! Just like Mutex except:\n//!\n//! 1. It uses CAS for locking, more efficient in low contention\n//! 2. Use `.lock()` instead of `.lock().unwrap()` to retrieve the guard.\n//! 3. It doesn't handle poison so data is still available on thread panic.\nuse std::cell::UnsafeCell;\nuse std::ops::Deref;\nuse std::ops::DerefMut;\nuse std::sync::atomic::AtomicBool;\nuse std::sync::atomic::Ordering;\n\n/// A spin lock that uses busy-waiting instead of OS blocking\n#[derive(Default)]\npub struct SpinLock<T: ?Sized> {\n    locked: AtomicBool,\n    data: UnsafeCell<T>,\n}\n\nunsafe impl<T: ?Sized + Send> Send for SpinLock<T> {}\nunsafe impl<T: ?Sized + Send> Sync for SpinLock<T> {}\n\n/// RAII guard for a spin lock, automatically releases the lock when dropped\npub struct SpinLockGuard<'a, T: ?Sized + 'a> {\n    // funny underscores due to how Deref/DerefMut currently work (they\n    // disregard field privacy).\n    __lock: &'a SpinLock<T>,\n}\n\nimpl<'a, T: ?Sized + 'a> SpinLockGuard<'a, T> {\n    /// Creates a new guard for the given lock\n    pub fn new(pool: &'a SpinLock<T>) -> SpinLockGuard<'a, T> {\n        Self { __lock: pool }\n    }\n}\n\nunsafe impl<T: ?Sized + Sync> Sync for SpinLockGuard<'_, T> {}\n\nimpl<T> SpinLock<T> {\n    /// Creates a new unlocked spin lock containing the given value\n    pub fn new(t: T) -> SpinLock<T> {\n        Self {\n            locked: AtomicBool::new(false),\n            data: UnsafeCell::new(t),\n        }\n    }\n}\n\nimpl<T: ?Sized> SpinLock<T> {\n    /// Acquires the lock, blocking the current thread until it succeeds\n    pub fn lock(&self) -> SpinLockGuard<'_, T> {\n        while self\n            .locked\n            .compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst)\n            .is_err()\n        {}\n        SpinLockGuard::new(self)\n    }\n}\n\nimpl<T: ?Sized> Deref for SpinLockGuard<'_, T> {\n    type Target = T;\n\n    fn deref(&self) -> &T {\n        unsafe { &*self.__lock.data.get() }\n    }\n}\n\nimpl<T: ?Sized> DerefMut for SpinLockGuard<'_, T> {\n    fn deref_mut(&mut self) -> &mut T {\n        unsafe { &mut *self.__lock.data.get() }\n    }\n}\n\nimpl<T: ?Sized> Drop for SpinLockGuard<'_, T> {\n    #[inline]\n    fn drop(&mut self) {\n        while self\n            .__lock\n            .locked\n            .compare_exchange(true, false, Ordering::SeqCst, Ordering::SeqCst)\n            .is_err()\n        {}\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use std::sync::Arc;\n    use std::sync::mpsc::channel;\n    use std::thread;\n\n    #[test]\n    fn smoke() {\n        let m = SpinLock::new(());\n        drop(m.lock());\n        drop(m.lock());\n    }\n\n    #[test]\n    fn lots_and_lots() {\n        const J: u32 = 1000;\n        const K: u32 = 3;\n\n        fn inc(m: &SpinLock<u32>) {\n            for _ in 0..J {\n                *m.lock() += 1;\n            }\n        }\n\n        let m = Arc::new(SpinLock::new(0));\n        let (tx, rx) = channel();\n        for _ in 0..K {\n            let tx2 = tx.clone();\n            let m2 = m.clone();\n            thread::spawn(move || {\n                inc(&m2);\n                tx2.send(()).unwrap();\n            });\n            let tx2 = tx.clone();\n            let m2 = m.clone();\n            thread::spawn(move || {\n                inc(&m2);\n                tx2.send(()).unwrap();\n            });\n        }\n\n        drop(tx);\n        for _ in 0..2 * K {\n            rx.recv().unwrap();\n        }\n        assert_eq!(*m.lock(), J * K * 2);\n    }\n\n    #[test]\n    fn test_mutex_unsized() {\n        let mutex = SpinLock::new([1, 2, 3]);\n        {\n            let b = &mut *mutex.lock();\n            b[0] = 4;\n            b[2] = 5;\n        }\n        let comp: &[i32] = &[4, 2, 5];\n        assert_eq!(&*mutex.lock(), comp);\n    }\n}\n"
  },
  {
    "path": "src/theme.rs",
    "content": "//! Handle the color theme\nuse ratatui::style::{Color, Modifier, Style};\n\nuse crate::options::SkimOptions;\n\n/// The color scheme of skim's UI\n///\n/// <pre>\n/// +----------------+\n/// | >selected line |  --> selected & normal(fg/bg) & matched\n/// |> current line  |  --> cursor & current & current_match\n/// |  normal line   |\n/// |\\ 8/10          |  --> spinner & info\n/// |> query         |  --> prompt & query\n/// +----------------+\n/// </pre>\n#[derive(Copy, Clone, Debug)]\npub struct ColorTheme {\n    /// Non-selected lines and general text\n    pub normal: Style,\n    /// Matched text on non-current lines\n    pub matched: Style,\n    /// Current line, non-matched text\n    pub current: Style,\n    /// Current line, matched text\n    pub current_match: Style,\n    /// Query text/input\n    pub query: Style,\n    /// Spinner\n    pub spinner: Style,\n    /// Info (outside of spinner)\n    pub info: Style,\n    /// Prompt prefix\n    pub prompt: Style,\n    /// Cursor/Selector/pointer (prefix of current item)\n    pub cursor: Style,\n    /// Multi-selector/marker (prefix of selected items)\n    pub selected: Style,\n    /// Header lines\n    pub header: Style,\n    /// Border\n    pub border: Style,\n}\n\nimpl Default for ColorTheme {\n    /// Theme defaults to Dark256\n    fn default() -> Self {\n        ColorTheme::dark256()\n    }\n}\n\n#[allow(dead_code)]\nimpl ColorTheme {\n    /// Setup the theme from the skim options\n    #[must_use]\n    pub fn init_from_options(options: &SkimOptions) -> ColorTheme {\n        // register\n        if let Some(color) = options.color.clone() {\n            ColorTheme::from_options(&color)\n        } else {\n            // Check for NO_COLOR environment variable\n            match std::env::var_os(\"NO_COLOR\") {\n                Some(no_color) if !no_color.is_empty() => ColorTheme::none(),\n                _ => ColorTheme::dark256(),\n            }\n        }\n    }\n\n    fn none() -> Self {\n        let def = Style::default();\n        Self {\n            spinner: Style::default().bold(),\n            normal: def,\n            matched: def,\n            current: def,\n            current_match: def,\n            query: def,\n            info: def,\n            prompt: def,\n            cursor: def,\n            selected: def,\n            header: def,\n            border: def,\n        }\n    }\n\n    fn bw() -> Self {\n        let base = ColorTheme::none();\n        ColorTheme {\n            matched: base.matched.underlined(),\n            current: base.current.reversed(),\n            current_match: base.current_match.reversed().underlined(),\n            ..base\n        }\n    }\n\n    fn default16() -> Self {\n        let base = ColorTheme::none();\n        ColorTheme {\n            matched: base.matched.fg(Color::Green),\n            current: base.current.fg(Color::Yellow),\n            current_match: base.current_match.fg(Color::Green),\n            spinner: base.spinner.fg(Color::Green),\n            info: base.info.fg(Color::White),\n            prompt: base.prompt.fg(Color::Blue),\n            cursor: base.cursor.fg(Color::Red),\n            selected: base.selected.fg(Color::Magenta),\n            header: base.header.fg(Color::Cyan),\n            border: base.border.fg(Color::Black),\n            ..base\n        }\n    }\n\n    fn dark256() -> Self {\n        let base = ColorTheme::none();\n        ColorTheme {\n            matched: base.matched.fg(Color::Indexed(108)).bg(Color::Indexed(0)),\n            current: base.current.bg(Color::Indexed(236)),\n            current_match: base.current_match.fg(Color::Indexed(151)).bg(Color::Indexed(236)),\n            spinner: base.spinner.fg(Color::Indexed(148)),\n            info: base.info.fg(Color::Indexed(144)),\n            prompt: base.prompt.fg(Color::Indexed(110)),\n            cursor: base.cursor.fg(Color::Indexed(161)),\n            selected: base.selected.fg(Color::Indexed(168)),\n            header: base.header.fg(Color::Indexed(109)),\n            border: base.border.fg(Color::Indexed(59)),\n            ..base\n        }\n    }\n\n    fn molokai256() -> Self {\n        let base = ColorTheme::none();\n        ColorTheme {\n            matched: base.matched.fg(Color::Indexed(234)).bg(Color::Indexed(186)),\n            current: base.current.bg(Color::Indexed(236)),\n            current_match: base.current_match.fg(Color::Indexed(234)).bg(Color::Indexed(186)),\n            spinner: base.spinner.fg(Color::Indexed(148)),\n            info: base.info.fg(Color::Indexed(144)),\n            prompt: base.prompt.fg(Color::Indexed(110)),\n            cursor: base.cursor.fg(Color::Indexed(161)),\n            selected: base.selected.fg(Color::Indexed(168)),\n            header: base.header.fg(Color::Indexed(109)),\n            border: base.border.fg(Color::Indexed(59)),\n            ..base\n        }\n    }\n\n    fn light256() -> Self {\n        let base = ColorTheme::none();\n        ColorTheme {\n            matched: base.matched.fg(Color::Indexed(0)).bg(Color::Indexed(220)),\n            current: base.current.bg(Color::Indexed(251)),\n            current_match: base.current_match.fg(Color::Indexed(66)).bg(Color::Indexed(251)),\n            spinner: base.spinner.fg(Color::Indexed(65)),\n            info: base.info.fg(Color::Indexed(101)),\n            prompt: base.prompt.fg(Color::Indexed(25)),\n            cursor: base.cursor.fg(Color::Indexed(161)),\n            selected: base.selected.fg(Color::Indexed(168)),\n            header: base.header.fg(Color::Indexed(31)),\n            border: base.border.fg(Color::Indexed(145)),\n            ..base\n        }\n    }\n\n    #[allow(unused_variables)]\n    fn catppuccin_mocha() -> Self {\n        let base = ColorTheme::none();\n        let text = Color::Rgb(205, 214, 244);\n        let subtext0 = Color::Rgb(166, 173, 200);\n        let subtext1 = Color::Rgb(186, 194, 222);\n        let overlay0 = Color::Rgb(108, 112, 134);\n        let surface0 = Color::Rgb(49, 50, 68);\n        let blue = Color::Rgb(137, 180, 250);\n        let red = Color::Rgb(243, 139, 168);\n        let lavender = Color::Rgb(180, 190, 254);\n        let sapphire = Color::Rgb(116, 199, 236);\n        Self {\n            normal: base.normal.fg(text),\n            matched: base.matched.fg(blue).underlined(),\n            current: base.current.bg(surface0),\n            current_match: base.current_match.fg(red).underlined(),\n            query: base.query.fg(text),\n            spinner: base.spinner.fg(subtext1).bold(),\n            info: base.info.fg(subtext1),\n            prompt: base.prompt.fg(lavender),\n            cursor: base.cursor.fg(red),\n            selected: base.selected.fg(red),\n            header: base.header.fg(subtext1),\n            border: base.header.fg(lavender),\n        }\n    }\n    #[allow(unused_variables)]\n    fn catppuccin_macchiato() -> Self {\n        let base = ColorTheme::none();\n        let text = Color::Rgb(202, 211, 245);\n        let subtext0 = Color::Rgb(165, 173, 203);\n        let subtext1 = Color::Rgb(184, 192, 224);\n        let overlay0 = Color::Rgb(110, 115, 141);\n        let surface0 = Color::Rgb(54, 58, 79);\n        let blue = Color::Rgb(138, 173, 244);\n        let red = Color::Rgb(237, 135, 150);\n        let lavender = Color::Rgb(183, 189, 248);\n        let sapphire = Color::Rgb(125, 196, 228);\n        Self {\n            normal: base.normal.fg(text),\n            matched: base.matched.fg(blue).underlined(),\n            current: base.current.bg(surface0),\n            current_match: base.current_match.fg(red).underlined(),\n            query: base.query.fg(text),\n            spinner: base.spinner.fg(subtext1).bold(),\n            info: base.info.fg(subtext1),\n            prompt: base.prompt.fg(lavender),\n            cursor: base.cursor.fg(red),\n            selected: base.selected.fg(red),\n            header: base.header.fg(subtext1),\n            border: base.header.fg(lavender),\n        }\n    }\n    #[allow(unused_variables)]\n    fn catppuccin_latte() -> Self {\n        let base = ColorTheme::none();\n        let text = Color::Rgb(76, 79, 105);\n        let subtext0 = Color::Rgb(108, 111, 133);\n        let subtext1 = Color::Rgb(92, 95, 119);\n        let overlay0 = Color::Rgb(156, 160, 176);\n        let surface0 = Color::Rgb(204, 208, 218);\n        let blue = Color::Rgb(30, 102, 245);\n        let red = Color::Rgb(210, 15, 57);\n        let lavender = Color::Rgb(114, 135, 253);\n        let sapphire = Color::Rgb(32, 159, 181);\n        Self {\n            normal: base.normal.fg(text),\n            matched: base.matched.fg(blue).underlined(),\n            current: base.current.bg(surface0),\n            current_match: base.current_match.fg(red).underlined(),\n            query: base.query.fg(text),\n            spinner: base.spinner.fg(subtext1).bold(),\n            info: base.info.fg(subtext1),\n            prompt: base.prompt.fg(lavender),\n            cursor: base.cursor.fg(red),\n            selected: base.selected.fg(red),\n            header: base.header.fg(subtext1),\n            border: base.header.fg(lavender),\n        }\n    }\n    #[allow(unused_variables)]\n    fn catppuccin_frappe() -> Self {\n        let base = ColorTheme::none();\n        let text = Color::Rgb(198, 208, 245);\n        let subtext0 = Color::Rgb(165, 173, 206);\n        let subtext1 = Color::Rgb(181, 191, 226);\n        let overlay0 = Color::Rgb(115, 121, 148);\n        let surface0 = Color::Rgb(65, 69, 89);\n        let blue = Color::Rgb(140, 170, 238);\n        let red = Color::Rgb(231, 130, 132);\n        let lavender = Color::Rgb(186, 187, 241);\n        let sapphire = Color::Rgb(133, 193, 220);\n        Self {\n            normal: base.normal.fg(text),\n            matched: base.matched.fg(blue).underlined(),\n            current: base.current.bg(surface0),\n            current_match: base.current_match.fg(red).underlined(),\n            query: base.query.fg(text),\n            spinner: base.spinner.fg(subtext1).bold(),\n            info: base.info.fg(subtext1),\n            prompt: base.prompt.fg(lavender),\n            cursor: base.cursor.fg(red),\n            selected: base.selected.fg(red),\n            header: base.header.fg(subtext1),\n            border: base.header.fg(lavender),\n        }\n    }\n\n    fn set_color(&mut self, name: &str, spec: &str) {\n        let spec_parts: Vec<_> = spec.split(&['+', ':']).collect();\n\n        // Compute modifiers\n        let mut modifier = Modifier::empty();\n        for part in spec_parts.iter().skip(1) {\n            if matches!(*part, \"x\" | \"regular\") {\n                modifier = Modifier::empty();\n            } else {\n                modifier |= match *part {\n                    \"b\" | \"bold\" => Modifier::BOLD,\n                    \"u\" | \"underlined\" => Modifier::UNDERLINED,\n                    \"c\" | \"crossed-out\" => Modifier::CROSSED_OUT,\n                    \"d\" | \"dim\" => Modifier::DIM,\n                    \"i\" | \"italic\" => Modifier::ITALIC,\n                    \"r\" | \"reverse\" => Modifier::REVERSED,\n                    m => {\n                        debug!(\"Unknown modifier '{m}'\");\n                        Modifier::empty()\n                    }\n                };\n            }\n        }\n        // Apply - check for layer suffixes (_fg, -fg, _bg, -bg, _u, -u, etc.)\n        let (component_name, layer) = if name.ends_with(\"_fg\") || name.ends_with(\"-fg\") {\n            (&name[..name.len() - 3], \"fg\")\n        } else if name.ends_with(\"_bg\") || name.ends_with(\"-bg\") {\n            (&name[..name.len() - 3], \"bg\")\n        } else if name.ends_with(\"_u\") || name.ends_with(\"-u\") {\n            (&name[..name.len() - 2], \"u\")\n        } else if name.ends_with(\"_underline\") || name.ends_with(\"-underline\") {\n            (&name[..name.len() - 10], \"underline\")\n        } else if name == \"bg\" {\n            (\"\", \"bg\")\n        } else {\n            (name, \"fg\")\n        };\n\n        let target_style = match component_name {\n            \"\" | \"normal\" => &mut self.normal,\n            \"matched\" | \"hl\" => &mut self.matched,\n            \"current\" | \"fg+\" | \"bg+\" => &mut self.current,\n            \"current_match\" | \"hl+\" => &mut self.current_match,\n            \"query\" => &mut self.query,\n            \"spinner\" => &mut self.spinner,\n            \"info\" => &mut self.info,\n            \"prompt\" => &mut self.prompt,\n            \"cursor\" | \"pointer\" => &mut self.cursor,\n            \"selected\" | \"marker\" => &mut self.selected,\n            \"header\" => &mut self.header,\n            \"border\" => &mut self.border,\n            _ => return,\n        };\n\n        // Handle color reset with `-1`\n        let raw_color = spec_parts[0];\n        // Compute color\n        let new_color = if raw_color.len() == 7 && raw_color.starts_with('#') {\n            // RGB Hex color\n            let r = u8::from_str_radix(&raw_color[1..3], 16).unwrap_or(255);\n            let g = u8::from_str_radix(&raw_color[3..5], 16).unwrap_or(255);\n            let b = u8::from_str_radix(&raw_color[5..7], 16).unwrap_or(255);\n            Some(Color::Rgb(r, g, b))\n        } else if raw_color == \"-1\" {\n            Some(Color::Reset)\n        } else {\n            raw_color.parse::<u8>().ok().map(Color::Indexed).or_else(|| {\n                if !raw_color.is_empty() {\n                    debug!(\"Unknown color '{}'\", spec_parts[0]);\n                }\n                None\n            })\n        };\n\n        let layer_override = if component_name == \"bg+\" { \"bg\" } else { layer };\n        set_style(target_style, layer_override, new_color, modifier);\n    }\n\n    fn from_options(color: &str) -> Self {\n        let mut theme = ColorTheme::dark256();\n        for pair in color.split(',') {\n            if let Some((name, spec)) = pair.split_once(':') {\n                theme.set_color(name, spec);\n            } else {\n                theme = match color {\n                    \"molokai\" => ColorTheme::molokai256(),\n                    \"light\" => ColorTheme::light256(),\n                    \"16\" => ColorTheme::default16(),\n                    \"bw\" => ColorTheme::bw(),\n                    \"none\" | \"empty\" => ColorTheme::none(),\n                    \"dark\" | \"default\" => ColorTheme::dark256(),\n                    \"catppuccin_mocha\" | \"catppuccin-mocha\" => ColorTheme::catppuccin_mocha(),\n                    \"catppuccin_macchiato\" | \"catppuccin-macchiato\" => ColorTheme::catppuccin_macchiato(),\n                    \"catppuccin_latte\" | \"catppuccin-latte\" => ColorTheme::catppuccin_latte(),\n                    \"catppuccin_frappe\" | \"catppuccin-frappe\" => ColorTheme::catppuccin_frappe(),\n                    t => {\n                        debug!(\"Unknown color theme '{t}'\");\n                        ColorTheme::dark256()\n                    }\n                };\n            }\n        }\n        theme\n    }\n}\n\nfn set_style(s: &mut Style, layer: &str, color: Option<Color>, modifier: Modifier) {\n    if let Some(c) = color {\n        *s = match layer {\n            \"fg\" => s.fg(c),\n            \"bg\" => s.bg(c),\n            \"u\" | \"underline\" => s.underline_color(c),\n            _ => *s,\n        }\n    }\n    *s = s.add_modifier(modifier);\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn test_base_themes() {\n        // Test that base themes have expected properties\n        let none = ColorTheme::none();\n        // Spinner should be bold even in none theme\n        assert!(none.spinner.add_modifier.contains(Modifier::BOLD));\n\n        let bw = ColorTheme::bw();\n        assert!(bw.matched.add_modifier.contains(Modifier::UNDERLINED));\n        assert!(bw.current.add_modifier.contains(Modifier::REVERSED));\n\n        let theme_16 = ColorTheme::default16();\n        assert_eq!(theme_16.matched.fg, Some(Color::Green));\n        assert_eq!(theme_16.matched.bg, None);\n\n        let dark = ColorTheme::dark256();\n        assert_eq!(dark.matched.fg, Some(Color::Indexed(108)));\n        assert_eq!(dark.matched.bg, Some(Color::Indexed(0)));\n\n        let molokai = ColorTheme::molokai256();\n        assert_eq!(molokai.matched.fg, Some(Color::Indexed(234)));\n        assert_eq!(molokai.matched.bg, Some(Color::Indexed(186)));\n\n        let light = ColorTheme::light256();\n        assert_eq!(light.matched.fg, Some(Color::Indexed(0)));\n        assert_eq!(light.matched.bg, Some(Color::Indexed(220)));\n    }\n\n    #[test]\n    fn test_from_options_base_themes() {\n        // Test base theme names\n        let dark = ColorTheme::from_options(\"dark\");\n        assert!(dark.matched.fg.is_some());\n\n        let molokai = ColorTheme::from_options(\"molokai\");\n        assert!(molokai.matched.fg.is_some());\n\n        let light = ColorTheme::from_options(\"light\");\n        assert!(light.matched.fg.is_some());\n\n        let theme_16 = ColorTheme::from_options(\"16\");\n        assert!(theme_16.matched.fg.is_some());\n\n        let bw = ColorTheme::from_options(\"bw\");\n        assert!(bw.matched.add_modifier.contains(Modifier::UNDERLINED));\n\n        // Test that \"none\" theme uses reset style (which may have default colors from terminal)\n        let none = ColorTheme::from_options(\"none\");\n        // Spinner should still be bold even in none theme\n        assert!(none.spinner.add_modifier.contains(Modifier::BOLD));\n    }\n\n    #[test]\n    fn test_ansi_color_parsing() {\n        // Test ANSI color (0-255)\n        let theme = ColorTheme::from_options(\"matched:108\");\n        assert_eq!(theme.matched.fg, Some(Color::Indexed(108)));\n\n        let theme = ColorTheme::from_options(\"prompt:25\");\n        assert_eq!(theme.prompt.fg, Some(Color::Indexed(25)));\n    }\n\n    #[test]\n    fn test_rgb_hex_color_parsing() {\n        // Test RGB hex color (#rrggbb)\n        let theme = ColorTheme::from_options(\"matched:#ff0000\");\n        assert_eq!(theme.matched.fg, Some(Color::Rgb(255, 0, 0)));\n\n        let theme = ColorTheme::from_options(\"prompt:#00ff00\");\n        assert_eq!(theme.prompt.fg, Some(Color::Rgb(0, 255, 0)));\n\n        let theme = ColorTheme::from_options(\"info:#0000ff\");\n        assert_eq!(theme.info.fg, Some(Color::Rgb(0, 0, 255)));\n    }\n\n    #[test]\n    fn test_color_with_modifiers() {\n        // Test color with bold modifier\n        let theme = ColorTheme::from_options(\"matched:108:bold\");\n        assert_eq!(theme.matched.fg, Some(Color::Indexed(108)));\n        assert!(theme.matched.add_modifier.contains(Modifier::BOLD));\n\n        // Test color with underline modifier\n        let theme = ColorTheme::from_options(\"matched:108:underlined\");\n        assert_eq!(theme.matched.fg, Some(Color::Indexed(108)));\n        assert!(theme.matched.add_modifier.contains(Modifier::UNDERLINED));\n\n        // Test color with multiple modifiers (using +)\n        let theme = ColorTheme::from_options(\"matched:108:bold:underlined\");\n        assert_eq!(theme.matched.fg, Some(Color::Indexed(108)));\n        assert!(theme.matched.add_modifier.contains(Modifier::BOLD));\n        assert!(theme.matched.add_modifier.contains(Modifier::UNDERLINED));\n    }\n\n    #[test]\n    fn test_modifier_shortcuts() {\n        // Test short modifier names\n        let theme = ColorTheme::from_options(\"matched:108:b\");\n        assert!(theme.matched.add_modifier.contains(Modifier::BOLD));\n\n        let theme = ColorTheme::from_options(\"matched:108:u\");\n        assert!(theme.matched.add_modifier.contains(Modifier::UNDERLINED));\n\n        let theme = ColorTheme::from_options(\"matched:108:i\");\n        assert!(theme.matched.add_modifier.contains(Modifier::ITALIC));\n\n        let theme = ColorTheme::from_options(\"matched:108:r\");\n        assert!(theme.matched.add_modifier.contains(Modifier::REVERSED));\n\n        let theme = ColorTheme::from_options(\"matched:108:d\");\n        assert!(theme.matched.add_modifier.contains(Modifier::DIM));\n\n        let theme = ColorTheme::from_options(\"matched:108:c\");\n        assert!(theme.matched.add_modifier.contains(Modifier::CROSSED_OUT));\n    }\n\n    #[test]\n    fn test_regular_modifier_reset() {\n        // Test that 'regular' or 'x' resets modifiers\n        let theme = ColorTheme::from_options(\"matched:108:x:bold\");\n        assert!(theme.matched.add_modifier.contains(Modifier::BOLD));\n        assert!(!theme.matched.add_modifier.contains(Modifier::ITALIC));\n\n        let theme = ColorTheme::from_options(\"matched:108:regular:underlined\");\n        assert!(theme.matched.add_modifier.contains(Modifier::UNDERLINED));\n    }\n\n    #[test]\n    fn test_multiple_color_components() {\n        // Test multiple color components separated by comma\n        let theme = ColorTheme::from_options(\"matched:108,prompt:25\");\n        assert_eq!(theme.matched.fg, Some(Color::Indexed(108)));\n        assert_eq!(theme.prompt.fg, Some(Color::Indexed(25)));\n\n        let theme = ColorTheme::from_options(\"matched:#ff0000:bold,prompt:#00ff00:underlined\");\n        assert_eq!(theme.matched.fg, Some(Color::Rgb(255, 0, 0)));\n        assert!(theme.matched.add_modifier.contains(Modifier::BOLD));\n        assert_eq!(theme.prompt.fg, Some(Color::Rgb(0, 255, 0)));\n        assert!(theme.prompt.add_modifier.contains(Modifier::UNDERLINED));\n    }\n\n    #[test]\n    fn test_component_name_aliases() {\n        // Test that aliases work correctly\n        let theme = ColorTheme::from_options(\"hl:108\");\n        assert_eq!(theme.matched.fg, Some(Color::Indexed(108)));\n\n        let theme = ColorTheme::from_options(\"fg+:254\");\n        assert_eq!(theme.current.fg, Some(Color::Indexed(254)));\n\n        let theme = ColorTheme::from_options(\"bg+:236\");\n        assert_eq!(theme.current.bg, Some(Color::Indexed(236)));\n\n        let theme = ColorTheme::from_options(\"hl+:151\");\n        // hl+ is an alias for current_match\n        assert_eq!(theme.current_match.fg, Some(Color::Indexed(151)));\n\n        let theme = ColorTheme::from_options(\"pointer:161\");\n        assert_eq!(theme.cursor.fg, Some(Color::Indexed(161)));\n\n        let theme = ColorTheme::from_options(\"marker:168\");\n        assert_eq!(theme.selected.fg, Some(Color::Indexed(168)));\n    }\n\n    #[test]\n    fn test_background_color() {\n        // Test setting background color explicitly\n        let theme = ColorTheme::from_options(\"matched_bg:0\");\n        assert_eq!(theme.matched.bg, Some(Color::Indexed(0)));\n\n        let theme = ColorTheme::from_options(\"matched-bg:236\");\n        assert_eq!(theme.matched.bg, Some(Color::Indexed(236)));\n    }\n\n    #[test]\n    fn test_base_theme_with_overrides() {\n        // Test that base theme can be overridden\n        let theme = ColorTheme::from_options(\"dark,matched:200\");\n        assert_eq!(theme.matched.fg, Some(Color::Indexed(200)));\n        // Other colors should still be from dark theme\n        assert!(theme.prompt.fg.is_some());\n    }\n\n    #[test]\n    fn test_all_component_names() {\n        // Test all valid component names with their specific colors\n        let theme = ColorTheme::from_options(\"normal:108\");\n        assert_eq!(theme.normal.fg, Some(Color::Indexed(108)));\n\n        let theme = ColorTheme::from_options(\"matched:109\");\n        assert_eq!(theme.matched.fg, Some(Color::Indexed(109)));\n\n        let theme = ColorTheme::from_options(\"current:110\");\n        assert_eq!(theme.current.fg, Some(Color::Indexed(110)));\n\n        let theme = ColorTheme::from_options(\"current_match:111\");\n        // current_match should now correctly set current_match.fg\n        assert_eq!(theme.current_match.fg, Some(Color::Indexed(111)));\n\n        let theme = ColorTheme::from_options(\"query:112\");\n        assert_eq!(theme.query.fg, Some(Color::Indexed(112)));\n\n        let theme = ColorTheme::from_options(\"spinner:113\");\n        assert_eq!(theme.spinner.fg, Some(Color::Indexed(113)));\n\n        let theme = ColorTheme::from_options(\"info:114\");\n        assert_eq!(theme.info.fg, Some(Color::Indexed(114)));\n\n        let theme = ColorTheme::from_options(\"prompt:115\");\n        assert_eq!(theme.prompt.fg, Some(Color::Indexed(115)));\n\n        let theme = ColorTheme::from_options(\"cursor:116\");\n        assert_eq!(theme.cursor.fg, Some(Color::Indexed(116)));\n\n        let theme = ColorTheme::from_options(\"selected:117\");\n        assert_eq!(theme.selected.fg, Some(Color::Indexed(117)));\n\n        let theme = ColorTheme::from_options(\"header:118\");\n        assert_eq!(theme.header.fg, Some(Color::Indexed(118)));\n\n        let theme = ColorTheme::from_options(\"border:119\");\n        assert_eq!(theme.border.fg, Some(Color::Indexed(119)));\n    }\n\n    #[test]\n    fn test_invalid_color_graceful_handling() {\n        // Test that invalid color values don't crash\n        // When color is invalid (not a number), it returns None and the color isn't set\n        // So the theme starts with dark256() and the invalid color spec doesn't change it\n        let theme = ColorTheme::from_options(\"matched:invalid\");\n        // Should remain the dark256 default since invalid color is ignored\n        assert_eq!(theme.matched.fg, Some(Color::Indexed(108)));\n        assert_eq!(theme.matched.bg, Some(Color::Indexed(0)));\n\n        // Invalid hex digits in #rrggbb format will use unwrap_or(255) fallback\n        // So \"#gggggg\" becomes Rgb(255, 255, 255) since 'gg' is invalid hex\n        let theme = ColorTheme::from_options(\"matched:#gggggg\");\n        assert_eq!(theme.matched.fg, Some(Color::Rgb(255, 255, 255)));\n        // But the background remains from dark256 theme since we only set fg\n        assert_eq!(theme.matched.bg, Some(Color::Indexed(0)));\n    }\n\n    #[test]\n    fn test_init_from_options() {\n        // Test initialization from SkimOptions\n        let opts = crate::options::SkimOptionsBuilder::default()\n            .color(\"matched:108\")\n            .build()\n            .unwrap();\n        let theme = ColorTheme::init_from_options(&opts);\n        assert_eq!(theme.matched.fg, Some(Color::Indexed(108)));\n    }\n\n    #[test]\n    fn test_complex_color_spec() {\n        // Test a complex real-world color specification\n        let theme =\n            ColorTheme::from_options(\"dark,matched:#00ff00:bold,prompt:#0000ff:underlined,current:#ffff00:italic\");\n        assert_eq!(theme.matched.fg, Some(Color::Rgb(0, 255, 0)));\n        assert!(theme.matched.add_modifier.contains(Modifier::BOLD));\n        assert_eq!(theme.prompt.fg, Some(Color::Rgb(0, 0, 255)));\n        assert!(theme.prompt.add_modifier.contains(Modifier::UNDERLINED));\n        assert_eq!(theme.current.fg, Some(Color::Rgb(255, 255, 0)));\n        assert!(theme.current.add_modifier.contains(Modifier::ITALIC));\n    }\n\n    #[test]\n    fn test_minus_one_color_reset() {\n        // `hl:-1:reverse` should not have foreground or background color, but keep the reverse modifier\n        let theme = ColorTheme::from_options(\"dark,hl:-1:reverse,hl-bg:-1,hl+:-1:bold,bg+:-1\");\n        assert_eq!(theme.matched.fg, Some(Color::Reset));\n        assert_eq!(theme.matched.bg, Some(Color::Reset));\n        assert!(theme.matched.add_modifier.contains(Modifier::REVERSED));\n        assert_eq!(theme.current_match.fg, Some(Color::Reset));\n        assert_ne!(theme.current_match.bg, Some(Color::Reset));\n        assert!(theme.current_match.add_modifier.contains(Modifier::BOLD));\n        let theme = ColorTheme::from_options(\"dark,prompt:-1:underlined\");\n        assert_eq!(theme.prompt.fg, Some(Color::Reset));\n        assert!(theme.prompt.add_modifier.contains(Modifier::UNDERLINED));\n        let theme = ColorTheme::from_options(\"dark,bg+:-1\");\n        assert_eq!(theme.current.bg, Some(Color::Reset));\n    }\n}\n"
  },
  {
    "path": "src/tmux.rs",
    "content": "//! Tmux integration utilities.\n//!\n//! This module provides functionality for running skim within tmux panes,\n//! allowing skim to be used as a tmux popup or split pane.\n\nuse std::{\n    borrow::Cow,\n    env,\n    fmt::Write as FmtWrite,\n    io::{BufRead as _, BufReader, BufWriter, IsTerminal as _, Write as _},\n    process::{Command, Stdio},\n    sync::{\n        Arc,\n        atomic::{AtomicBool, Ordering},\n    },\n    thread,\n};\n\nuse crossterm::event::{KeyCode, KeyEvent, KeyModifiers};\nuse nix::sys::stat::Mode;\nuse nix::unistd::mkfifo;\nuse rand::{RngExt as _, distr::Alphanumeric};\nuse which::which;\n\nuse crate::{\n    Rank, SkimItem, SkimOptions, SkimOutput,\n    item::{MatchedItem, RankBuilder},\n    tui::{Event, event::Action},\n};\n\n#[derive(Debug, PartialEq, Eq)]\nenum TmuxWindowDir {\n    Center,\n    Top,\n    Bottom,\n    Left,\n    Right,\n}\n\nimpl From<&str> for TmuxWindowDir {\n    fn from(value: &str) -> Self {\n        use TmuxWindowDir::{Bottom, Center, Left, Right, Top};\n        match value {\n            \"top\" => Top,\n            \"bottom\" => Bottom,\n            \"left\" => Left,\n            \"right\" => Right,\n            _ => Center, // includes \"center\" and all unknown values\n        }\n    }\n}\n\n#[derive(Debug, PartialEq, Eq)]\nstruct TmuxOptions<'a> {\n    width: &'a str,\n    height: &'a str,\n    x: &'a str,\n    y: &'a str,\n}\n\nstruct SkimTmuxOutput {\n    line: String,\n}\n\nimpl SkimItem for SkimTmuxOutput {\n    fn text(&self) -> Cow<'_, str> {\n        Cow::from(&self.line)\n    }\n}\n\nimpl<'a> From<&'a String> for TmuxOptions<'a> {\n    fn from(value: &'a String) -> Self {\n        let (raw_dir, size) = value.split_once(',').unwrap_or((value, \"50%\"));\n        let dir = TmuxWindowDir::from(raw_dir);\n        let (height, width) = if let Some((lhs, rhs)) = size.split_once(',') {\n            match dir {\n                TmuxWindowDir::Center | TmuxWindowDir::Left | TmuxWindowDir::Right => (rhs, lhs),\n                TmuxWindowDir::Top | TmuxWindowDir::Bottom => (lhs, rhs),\n            }\n        } else {\n            match dir {\n                TmuxWindowDir::Left | TmuxWindowDir::Right => (\"100%\", size),\n                TmuxWindowDir::Top | TmuxWindowDir::Bottom => (size, \"100%\"),\n                TmuxWindowDir::Center => (size, size),\n            }\n        };\n\n        let (x, y) = match dir {\n            TmuxWindowDir::Center => (\"C\", \"C\"),\n            TmuxWindowDir::Top => (\"C\", \"0%\"),\n            TmuxWindowDir::Bottom => (\"C\", \"100%\"),\n            TmuxWindowDir::Left => (\"0%\", \"C\"),\n            TmuxWindowDir::Right => (\"100%\", \"C\"),\n        };\n\n        Self { width, height, x, y }\n    }\n}\n\n/// Run skim in a tmux popup\n///\n/// This will extract the tmux options, then build a new sk command\n/// without them and send it to tmux in a popup.\n///\n/// # Panics\n///\n/// Panics if the temporary directory for IPC cannot be created.\n#[allow(clippy::too_many_lines)]\npub fn run_with(opts: &SkimOptions) -> Option<SkimOutput> {\n    // Create temp dir for downstream output\n    let temp_dir_name = format!(\n        \"sk-tmux-{}\",\n        &rand::rng()\n            .sample_iter(&Alphanumeric)\n            .take(8)\n            .map(char::from)\n            .collect::<String>(),\n    );\n    let temp_dir = std::env::temp_dir().join(&temp_dir_name);\n    std::fs::create_dir(&temp_dir)\n        .unwrap_or_else(|e| panic!(\"Failed to create temp dir {}: {}\", temp_dir.display(), e));\n\n    debug!(\"Created temp dir {}\", temp_dir.display());\n    let tmp_stdout = temp_dir.join(\"stdout\");\n    let tmp_stdin = temp_dir.join(\"stdin\");\n\n    let has_piped_input = !std::io::stdin().is_terminal();\n    let mut stdin_reader = BufReader::new(std::io::stdin());\n    let line_ending = if opts.read0 { b'\\0' } else { b'\\n' };\n\n    let stop_reading = Arc::new(AtomicBool::new(false));\n    let _stdin_handle = if has_piped_input {\n        debug!(\"Reading stdin and piping to fifo\");\n\n        // Create a named pipe (FIFO)\n        // This allows the nested skim to continuously read as data arrives\n        let stdin_path_str = tmp_stdin\n            .to_str()\n            .unwrap_or_else(|| panic!(\"Failed to convert stdin path to string\"));\n        mkfifo(stdin_path_str, Mode::S_IRUSR | Mode::S_IWUSR)\n            .unwrap_or_else(|e| panic!(\"Failed to create fifo {}: {}\", tmp_stdin.display(), e));\n\n        let tmp_stdin_clone = tmp_stdin.clone();\n        let stop_flag = Arc::clone(&stop_reading);\n        Some(thread::spawn(move || {\n            debug!(\"Opening fifo for writing (may block until reader starts)\");\n            let stdin_f = std::fs::File::create(tmp_stdin_clone.clone())\n                .unwrap_or_else(|e| panic!(\"Failed to open fifo {}: {}\", tmp_stdin_clone.display(), e));\n            debug!(\"Fifo opened for writing\");\n            let mut stdin_writer = BufWriter::new(stdin_f);\n            loop {\n                // Check if we should stop reading\n                if stop_flag.load(Ordering::Relaxed) {\n                    debug!(\"Stop signal received, exiting stdin reader thread\");\n                    break;\n                }\n\n                let mut buf = vec![];\n                match stdin_reader.read_until(line_ending, &mut buf) {\n                    Ok(0) => break,\n                    Ok(n) => {\n                        debug!(\"Read {n} bytes from stdin\");\n                        stdin_writer.write_all(&buf).unwrap();\n                    }\n                    Err(e) => panic!(\"Failed to read from stdin: {e}\"),\n                }\n            }\n            // Ensure all buffered data is written to the file\n            let _ = stdin_writer.flush();\n        }))\n    } else {\n        None\n    };\n\n    // Build args to send to downstream sk invocation\n    let mut tmux_shell_cmd = String::new();\n    let mut prev_is_tmux_flag = false;\n    let mut prev_is_output_format_flag = false;\n    // We keep argv[0] to use in the popup's command\n    for arg in std::env::args() {\n        debug!(\"Got arg {arg}\");\n        if prev_is_tmux_flag {\n            prev_is_tmux_flag = false;\n            if !arg.starts_with('-') {\n                continue;\n            }\n        } else if prev_is_output_format_flag {\n            prev_is_output_format_flag = false;\n            continue;\n        }\n        if arg == \"--tmux\" {\n            debug!(\"Found tmux arg, skipping this and the next\");\n            prev_is_tmux_flag = true;\n            continue;\n        } else if arg.starts_with(\"--tmux\") {\n            debug!(\"Found equal tmux arg, skipping\");\n            continue;\n        } else if arg == \"--output-format\" {\n            debug!(\"Found output format arg, skipping this and the next\");\n            prev_is_output_format_flag = true;\n            continue;\n        } else if arg.starts_with(\"--output-format\") {\n            debug!(\"Found equal output format arg, skipping\");\n            continue;\n        }\n        push_quoted_arg(&mut tmux_shell_cmd, &arg);\n    }\n    // Always add all --print-xxx flags to the child sk command so that the output\n    // is fully structured and can be parsed unconditionally below, regardless of\n    // which flags the user originally passed.\n    for flag in &[\n        \"--print-query\",\n        \"--print-cmd\",\n        \"--print-header\",\n        \"--print-current\",\n        \"--print-score\",\n    ] {\n        let _ = write!(tmux_shell_cmd, \" {flag}\");\n    }\n    tmux_shell_cmd = tmux_shell_cmd.replace(\"--output-format\", \"\");\n\n    if has_piped_input {\n        let _ = write!(tmux_shell_cmd, \" <{}\", tmp_stdin.display());\n    }\n    let _ = write!(tmux_shell_cmd, \" >{}\", tmp_stdout.display());\n\n    debug!(\"build cmd {}\", &tmux_shell_cmd);\n\n    // Run downstream sk in tmux\n    let raw_tmux_opts = &opts.tmux.clone().unwrap();\n    let tmux_opts = TmuxOptions::from(raw_tmux_opts);\n    let mut tmux_cmd = Command::new(which(\"tmux\").unwrap_or_else(|e| panic!(\"Failed to find tmux in path: {e}\")));\n\n    tmux_cmd\n        .arg(\"display-popup\")\n        .arg(\"-E\")\n        .args([\"-d\", std::env::current_dir().unwrap().to_str().unwrap()])\n        .args([\"-h\", tmux_opts.height])\n        .args([\"-w\", tmux_opts.width])\n        .args([\"-x\", tmux_opts.x])\n        .args([\"-y\", tmux_opts.y]);\n\n    for (name, value) in std::env::vars() {\n        if name.starts_with(\"SKIM\") || name == \"PATH\" || name.starts_with(\"RUST\") {\n            let value = sanitize_value(value);\n            debug!(\"adding {name} = {value} to the command's env\");\n            tmux_cmd.args([\"-e\", &format!(\"{name}={value}\")]);\n        }\n    }\n\n    tmux_cmd.args([\"sh\", \"-c\", &tmux_shell_cmd]);\n\n    debug!(\"tmux command: {tmux_cmd:?}\");\n\n    let status = tmux_cmd\n        .stdout(Stdio::null())\n        .stderr(Stdio::null())\n        .stdin(Stdio::null())\n        .status()\n        .unwrap_or_else(|e| panic!(\"Tmux invocation failed with {e}\"));\n\n    // Signal the stdin thread to stop and wait for it to exit\n    stop_reading.store(true, Ordering::Relaxed);\n\n    let output_ending = if opts.print0 { \"\\0\" } else { \"\\n\" };\n    let mut stdout_bytes = std::fs::read_to_string(tmp_stdout).unwrap_or_default();\n    stdout_bytes.pop();\n    let mut stdout = stdout_bytes.split(output_ending);\n    let _ = std::fs::remove_dir_all(temp_dir);\n\n    // The child sk process always runs with --print-query, --print-cmd, --print-header,\n    // and --print-score, so we always read those lines unconditionally.\n    let query_str = if status.success() {\n        stdout.next().unwrap_or_default()\n    } else {\n        \"\"\n    };\n\n    let command_str = if status.success() {\n        stdout.next().unwrap_or_default()\n    } else {\n        \"\"\n    };\n\n    let header = if status.success() {\n        stdout.next().unwrap_or_default()\n    } else {\n        \"\"\n    }\n    .to_string();\n\n    let current: Option<MatchedItem> = if status.success() {\n        let line = stdout.next().unwrap_or_default();\n        if line.is_empty() {\n            None\n        } else {\n            Some(MatchedItem {\n                item: Arc::new(SkimTmuxOutput { line: line.to_string() }),\n                rank: Rank::default(),\n                rank_builder: Arc::new(RankBuilder::default()),\n                matched_range: None,\n            })\n        }\n    } else {\n        None\n    };\n\n    let mut output_lines: Vec<MatchedItem> = vec![];\n    while let Some(line) = stdout.next() {\n        debug!(\"Adding output line: {line}\");\n        // --print-score is always enabled in the child, so every item is followed by its score.\n        let score: i32 = stdout.next().unwrap_or_default().parse().unwrap_or_default();\n        let item = MatchedItem {\n            item: Arc::new(SkimTmuxOutput { line: line.to_string() }),\n            rank: Rank {\n                score,\n                ..Default::default()\n            },\n            rank_builder: Arc::new(RankBuilder::default()),\n            matched_range: None,\n        };\n        output_lines.push(item);\n    }\n\n    let is_abort = !status.success();\n    let final_event = if is_abort {\n        Event::Action(Action::Abort)\n    } else {\n        Event::Action(Action::Accept(None))\n    };\n\n    let skim_output = SkimOutput {\n        final_event,\n        is_abort,\n        final_key: KeyEvent::new(KeyCode::Enter, KeyModifiers::empty()),\n        // Note: In tmux mode, the actual final key is not available since skim runs in a separate\n        // tmux popup process. Only the output text is captured. Use --expect with --bind to capture\n        // specific accept keys in the output if needed.\n        query: query_str.to_string(),\n        cmd: command_str.to_string(),\n        selected_items: output_lines,\n        current,\n        header,\n    };\n    Some(skim_output)\n}\n\nfn push_quoted_arg(args_str: &mut String, arg: &str) {\n    use shell_quote::{Bash, Fish, Quote as _, Sh, Zsh};\n    let shell_path = env::var(\"SHELL\").unwrap_or(String::from(\"/bin/sh\"));\n    let shell = shell_path.rsplit_once('/').unwrap_or((\"\", \"sh\")).1;\n    let quoted_arg: Vec<u8> = match shell {\n        \"zsh\" => Zsh::quote(arg),\n        \"bash\" => Bash::quote(arg),\n        \"fish\" => Fish::quote(arg),\n        _ => Sh::quote(arg),\n    };\n    let _ = write!(\n        args_str,\n        \" {}\",\n        String::from_utf8(quoted_arg).expect(\"Failed to parse quoted arg as utf8, this should not happen\")\n    );\n}\n\nfn sanitize_value(value: String) -> String {\n    if !value.ends_with(';') {\n        return value;\n    }\n\n    let mut value = value.clone();\n    value.replace_range(value.len() - 1.., \"\\\\;\");\n    value\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    fn check(input: &str, height: &str, width: &str, x: &str, y: &str) {\n        assert_eq!(\n            TmuxOptions::from(&String::from(input)),\n            TmuxOptions { width, height, x, y }\n        );\n    }\n\n    #[test]\n    fn tmux_options_default() {\n        check(\"\", \"50%\", \"50%\", \"C\", \"C\");\n    }\n    #[test]\n    fn tmux_options_center() {\n        let (x, y) = (\"C\", \"C\");\n        check(\"center\", \"50%\", \"50%\", x, y);\n        check(\"center,10\", \"10\", \"10\", x, y);\n        check(\"center,10,20\", \"20\", \"10\", x, y);\n        check(\"center,10%,20\", \"20\", \"10%\", x, y);\n        check(\"center,10%,20%\", \"20%\", \"10%\", x, y);\n    }\n    #[test]\n    fn tmux_options_top() {\n        let (x, y) = (\"C\", \"0%\");\n        check(\"top\", \"50%\", \"100%\", x, y);\n        check(\"top,10\", \"10\", \"100%\", x, y);\n        check(\"top,10,20\", \"10\", \"20\", x, y);\n        check(\"top,10%,20\", \"10%\", \"20\", x, y);\n        check(\"top,10%,20%\", \"10%\", \"20%\", x, y);\n    }\n    #[test]\n    fn tmux_options_bottom() {\n        let (x, y) = (\"C\", \"100%\");\n        check(\"bottom\", \"50%\", \"100%\", x, y);\n        check(\"bottom,10\", \"10\", \"100%\", x, y);\n        check(\"bottom,10,20\", \"10\", \"20\", x, y);\n        check(\"bottom,10%,20\", \"10%\", \"20\", x, y);\n        check(\"bottom,10%,20%\", \"10%\", \"20%\", x, y);\n    }\n    #[test]\n    fn tmux_options_left() {\n        let (x, y) = (\"0%\", \"C\");\n        check(\"left\", \"100%\", \"50%\", x, y);\n        check(\"left,10\", \"100%\", \"10\", x, y);\n        check(\"left,10,20\", \"20\", \"10\", x, y);\n        check(\"left,10%,20\", \"20\", \"10%\", x, y);\n        check(\"left,10%,20%\", \"20%\", \"10%\", x, y);\n    }\n    #[test]\n    fn tmux_options_right() {\n        let (x, y) = (\"100%\", \"C\");\n        check(\"right\", \"100%\", \"50%\", x, y);\n        check(\"right,10\", \"100%\", \"10\", x, y);\n        check(\"right,10,20\", \"20\", \"10\", x, y);\n        check(\"right,10%,20\", \"20\", \"10%\", x, y);\n        check(\"right,10%,20%\", \"20%\", \"10%\", x, y);\n    }\n\n    #[test]\n    fn test_sanitize_value() {\n        assert_eq!(sanitize_value(\"some-value\".to_string()), \"some-value\".to_string());\n        assert_eq!(sanitize_value(\"some-value;\".to_string()), \"some-value\\\\;\".to_string());\n        assert_eq!(sanitize_value(\"some-value;;\".to_string()), \"some-value;\\\\;\".to_string());\n        assert_eq!(\n            sanitize_value(\"some-value;;;\".to_string()),\n            \"some-value;;\\\\;\".to_string()\n        );\n        assert_eq!(sanitize_value(\"some-value;x\".to_string()), \"some-value;x\".to_string());\n        assert_eq!(\n            sanitize_value(\"some-value;x;\".to_string()),\n            \"some-value;x\\\\;\".to_string()\n        );\n    }\n}\n"
  },
  {
    "path": "src/tui/app.rs",
    "content": "use std::process::{Command, Stdio};\nuse std::rc::Rc;\nuse std::sync::Arc;\nuse std::sync::atomic::{AtomicBool, Ordering};\n\nuse crate::item::{ItemPool, MatchedItem};\nuse crate::matcher::{Matcher, MatcherControl};\nuse crate::prelude::ExactOrFuzzyEngineFactory;\nuse crate::tui::SkimRender;\nuse crate::tui::input::StatusInfo;\nuse crate::tui::layout::{AppLayout, LayoutTemplate};\nuse crate::tui::options::TuiLayout;\nuse crate::tui::statusline::InfoDisplay;\nuse crate::tui::widget::SkimWidget;\nuse crate::{ItemPreview, PreviewContext, SkimItem, SkimOptions};\nuse crate::{Rank, util};\n\nuse super::Event;\nuse super::Tui;\nuse super::event::Action;\nuse super::header::Header;\nuse super::item_list::ItemList;\nuse super::{input, preview};\nuse color_eyre::eyre::{Result, bail};\nuse crossterm::event::{KeyCode, KeyEvent, KeyModifiers, MouseEvent, MouseEventKind};\nuse input::Input;\nuse preview::Preview;\nuse ratatui::buffer::Buffer;\nuse ratatui::crossterm::event::KeyCode::Char;\nuse ratatui::layout::Rect;\nuse ratatui::prelude::Backend;\nuse ratatui::widgets::Widget;\nuse rayon::ThreadPool;\nuse std::sync::LazyLock;\n\nstatic NUM_THREADS: LazyLock<usize> = LazyLock::new(|| {\n    std::thread::available_parallelism()\n        .ok()\n        .map_or_else(|| 0, std::num::NonZero::get)\n});\n\nconst FRAME_TIME_MS: u128 = 1000 / 30;\nconst MATCHER_DEBOUNCE_MS: u128 = 200;\nconst HIDE_GRACE_MS: u128 = 500;\n\n/// Application state for skim's TUI\npub struct App {\n    /// Pool of items to be filtered\n    pub item_pool: Arc<ItemPool>,\n    /// Separate thread pool for use by skim\n    pub thread_pool: Arc<ThreadPool>,\n    /// Whether the application should quit\n    pub should_quit: bool,\n\n    /// Current cursor position (x, y)\n    pub cursor_pos: (u16, u16),\n    /// Control handle for the matcher thread\n    pub matcher_control: MatcherControl,\n    /// The matcher for filtering items\n    pub matcher: Matcher,\n    /// Register for yank/paste operations\n    pub yank_register: String,\n    /// Last time the matcher was restarted\n    pub last_matcher_restart: std::time::Instant,\n    /// Whether a matcher restart is pending\n    pub pending_matcher_restart: bool,\n    /// Whether or not we need a render on the next heartbeat\n    pub needs_render: Arc<AtomicBool>,\n    /// Time of the last render\n    pub last_render_timer: std::time::Instant,\n\n    /// Input field widget\n    pub input: Input,\n    /// Preview pane widget\n    pub preview: Preview,\n    /// Header widget\n    pub header: Header,\n    /// Item list widget\n    pub item_list: ItemList,\n    /// Color theme\n    pub theme: Arc<crate::theme::ColorTheme>,\n\n    /// Timer for tracking matcher activity\n    pub matcher_timer: std::time::Instant,\n\n    /// Last time spinner visibility changed\n    pub spinner_last_change: std::time::Instant,\n    /// Whether to show the spinner (controlled with debouncing)\n    pub show_spinner: bool,\n    /// Start time for spinner animation (set once at app creation, never reset)\n    pub spinner_start: std::time::Instant,\n\n    /// Query history navigation\n    pub query_history: Vec<String>,\n    /// Current position in query history\n    pub history_index: Option<usize>,\n    /// Saved input when navigating history\n    pub saved_input: String,\n\n    /// Command history navigation (for interactive mode)\n    pub cmd_history: Vec<String>,\n    /// Current position in command history\n    pub cmd_history_index: Option<usize>,\n    /// Saved command input when navigating history\n    pub saved_cmd_input: String,\n\n    /// Skim configuration options\n    pub options: SkimOptions,\n    /// The command being executed\n    pub cmd: String,\n    /// Pre-computed layout template built from options; rebuilt when options change.\n    pub layout_template: LayoutTemplate,\n    /// Concrete widget areas for the last rendered frame; updated in `render()`.\n    pub layout: AppLayout,\n    /// Last time preview was spawned (for debouncing)\n    pub last_preview_spawn: std::time::Instant,\n    /// Whether a preview run was debounced and needs to be retried\n    pub pending_preview_run: bool,\n    reader_timer: std::time::Instant,\n    items_just_updated: bool,\n}\n\nimpl Widget for &mut App {\n    fn render(self, area: Rect, buf: &mut Buffer) {\n        let mut res = SkimRender::default();\n        let has_border = self.options.border.is_some();\n\n        // Update header with reserved items (from --header-lines), then apply\n        // the pre-built template to the current terminal area.  The template\n        // encodes all option-derived constraints so this is a cheap set of\n        // rect splits with no option inspection.\n        self.header.set_header_lines(self.item_pool.reserved());\n        self.layout = self.layout_template.apply(area);\n\n        if let Some(header_area) = self.layout.header_area {\n            res |= self.header.render(header_area, buf);\n        }\n\n        if let Some(preview_area) = self.layout.preview_area {\n            res |= self.preview.render(preview_area, buf);\n        }\n\n        res |= self.item_list.render(self.layout.list_area, buf);\n\n        // Render the input after the item list so that the status shows correct information.\n        self.input.status_info = if self.options.info == InfoDisplay::Hidden {\n            None\n        } else {\n            Some(StatusInfo {\n                total: self.item_pool.len(),\n                matched: self.item_list.count(),\n                processed: self.matcher_control.get_num_processed(),\n                show_spinner: self.show_spinner,\n                matcher_mode: if self.options.regex {\n                    \"RE\".to_string()\n                } else {\n                    String::new()\n                },\n                multi_selection: self.options.multi,\n                selected: self.item_list.selection.len(),\n                current_item_idx: self.item_list.current,\n                hscroll_offset: i64::from(self.item_list.manual_hscroll),\n                start: Some(self.spinner_start),\n            })\n        };\n        res |= self.input.render(self.layout.input_area, buf);\n\n        // Cursor position needs to account for input border and title.\n        self.cursor_pos = (\n            self.layout.input_area.x + self.input.cursor_pos() + u16::from(has_border),\n            self.layout.input_area.y\n                + u16::from(!(self.options.layout == TuiLayout::Reverse && self.options.border.is_none())),\n        );\n        if res.run_preview {\n            self.pending_preview_run = true;\n        }\n    }\n}\n\nimpl Default for App {\n    fn default() -> Self {\n        let theme = Arc::new(crate::theme::ColorTheme::default());\n        let opts = SkimOptions::default();\n        let header = Header::from_options(&opts, theme.clone());\n        let layout_template = LayoutTemplate::from_options(&opts, header.height());\n        let layout = layout_template.apply(Rect::default());\n        Self {\n            input: Input::from_options(&opts, theme.clone()),\n            preview: Preview::from_options(&opts, theme.clone()),\n            header,\n            item_list: ItemList::from_options(&opts, theme.clone()),\n            thread_pool: Arc::new(\n                rayon::ThreadPoolBuilder::new()\n                    .num_threads(*NUM_THREADS)\n                    .build()\n                    .unwrap(),\n            ),\n            item_pool: Arc::default(),\n            theme,\n            should_quit: false,\n            cursor_pos: (0, 0),\n            matcher: Matcher::builder(Rc::new(ExactOrFuzzyEngineFactory::builder().build()))\n                .case(crate::CaseMatching::default())\n                .build(),\n            yank_register: String::new(),\n            matcher_control: MatcherControl::default(),\n            matcher_timer: std::time::Instant::now(),\n            last_matcher_restart: std::time::Instant::now(),\n            pending_matcher_restart: false,\n            needs_render: Arc::new(AtomicBool::new(true)),\n            last_render_timer: std::time::Instant::now()\n                .checked_sub(std::time::Duration::from_secs(1))\n                .unwrap(),\n            // spinner initial state\n            spinner_last_change: std::time::Instant::now(),\n            show_spinner: false,\n            spinner_start: std::time::Instant::now(),\n            query_history: Vec::new(),\n            history_index: None,\n            saved_input: String::new(),\n            cmd_history: Vec::new(),\n            cmd_history_index: None,\n            saved_cmd_input: String::new(),\n            options: opts,\n            cmd: String::new(),\n            layout_template,\n            layout,\n            last_preview_spawn: std::time::Instant::now(),\n            pending_preview_run: false,\n            reader_timer: std::time::Instant::now(),\n            items_just_updated: false,\n        }\n    }\n}\n\nimpl App {\n    /// Creates a new App from skim options.\n    ///\n    /// # Panics\n    ///\n    /// Panics if the Rayon thread pool cannot be built (system resource exhaustion).\n    #[must_use]\n    pub fn from_options(options: SkimOptions, theme: Arc<crate::theme::ColorTheme>, cmd: String) -> Self {\n        let header = Header::from_options(&options, theme.clone());\n        let layout_template = LayoutTemplate::from_options(&options, header.height());\n        let layout = layout_template.apply(Rect::default());\n        Self {\n            input: Input::from_options(&options, theme.clone()),\n            preview: Preview::from_options(&options, theme.clone()),\n            header,\n            thread_pool: Arc::new(\n                rayon::ThreadPoolBuilder::new()\n                    .num_threads(*NUM_THREADS)\n                    .build()\n                    .unwrap(),\n            ),\n            item_pool: Arc::new(ItemPool::from_options(&options)),\n            item_list: ItemList::from_options(&options, theme.clone()),\n            theme,\n            should_quit: false,\n            cursor_pos: (0, 0),\n            matcher: Matcher::from_options(&options),\n            yank_register: String::new(),\n            matcher_control: MatcherControl::default(),\n            reader_timer: std::time::Instant::now(),\n            matcher_timer: std::time::Instant::now(),\n            last_matcher_restart: std::time::Instant::now(),\n            pending_matcher_restart: false,\n            needs_render: Arc::new(AtomicBool::new(true)),\n            last_render_timer: std::time::Instant::now()\n                .checked_sub(std::time::Duration::from_secs(1))\n                .unwrap(),\n            // spinner initial state\n            spinner_last_change: std::time::Instant::now(),\n            show_spinner: false,\n            spinner_start: std::time::Instant::now(),\n            items_just_updated: false,\n            query_history: options.query_history.clone(),\n            history_index: None,\n            saved_input: String::new(),\n            cmd_history: options.cmd_history.clone(),\n            cmd_history_index: None,\n            saved_cmd_input: String::new(),\n            options,\n            cmd,\n            layout_template,\n            layout,\n            last_preview_spawn: std::time::Instant::now()\n                .checked_sub(std::time::Duration::from_secs(1))\n                .unwrap(),\n            pending_preview_run: false,\n        }\n    }\n\n    /// Rebuild the layout template for the given terminal dimensions.\n    ///\n    /// The template encodes all option-derived constraints; `apply` in render\n    /// is then a cheap set of rect splits with no option inspection.\n    /// Called on `Event::Resize(cols, rows)` and whenever a layout-affecting\n    /// option changes (e.g. `TogglePreview`).\n    pub fn resize(&mut self, cols: u16, rows: u16) {\n        self.layout_template = LayoutTemplate::from_options(&self.options, self.header.height());\n        self.layout = self.layout_template.apply(Rect::new(0, 0, cols, rows));\n    }\n}\n\nimpl App {\n    /// Calculate preview offset from offset expression (e.g., \"+123\", \"+{2}\", \"+{2}-2\")\n    fn calculate_preview_offset(&self, offset_expr: &str) -> u16 {\n        // Remove the leading '+'\n        let expr = offset_expr.trim_start_matches('+');\n\n        // Substitute field placeholders using printf\n        let substituted = self.expand_cmd(expr, true);\n        // Evaluate the expression (handle simple arithmetic like \"321-2\")\n        if let Some((left, right)) = substituted.split_once('-') {\n            let left_val = left.trim_matches(|x: char| !x.is_numeric()).parse::<u16>().unwrap_or(0);\n            let right_val = right\n                .trim_matches(|x: char| !x.is_numeric())\n                .parse::<u16>()\n                .unwrap_or(0);\n            left_val.saturating_sub(right_val)\n        } else if let Some((left, right)) = substituted.split_once('+') {\n            let left_val = left.trim_matches(|x: char| !x.is_numeric()).parse::<u16>().unwrap_or(0);\n            let right_val = right\n                .trim_matches(|x: char| !x.is_numeric())\n                .parse::<u16>()\n                .unwrap_or(0);\n            left_val.saturating_add(right_val)\n        } else {\n            substituted\n                .trim_matches(|x: char| !x.is_numeric())\n                .parse::<u16>()\n                .unwrap_or(0)\n        }\n    }\n\n    fn needs_render(&mut self) {\n        self.needs_render.store(true, Ordering::Relaxed);\n    }\n\n    /// Call after items are added or filtered (e.g., `Event::NewItem`, matcher completes)\n    fn on_items_updated(&mut self) {\n        self.pending_matcher_restart = true;\n        trace!(\"Got new items, len {}\", self.item_pool.len());\n        // mark reader activity and reset reader timer\n        self.reader_timer = std::time::Instant::now();\n        self.items_just_updated = true;\n    }\n\n    /// Call after selection changes (e.g., selection actions, `Event::Key`)\n    fn on_selection_changed() -> Vec<Event> {\n        vec![Event::RunPreview]\n    }\n\n    /// Call when query changes (e.g., `AddChar`, `BackwardDeleteChar`, etc.)\n    fn on_query_changed(&mut self) -> Vec<Event> {\n        // In interactive mode with --cmd, execute the command with {} substitution\n        if self.options.interactive && self.options.cmd.is_some() {\n            let expanded_cmd = self.expand_cmd(&self.cmd, true);\n            return vec![Event::Reload(expanded_cmd)];\n        }\n        self.restart_matcher_debounced();\n        vec![\n            Event::Key(KeyEvent::new(KeyCode::F(255), KeyModifiers::NONE)), // Send F255 which is the change bind\n            Event::RunPreview,\n        ]\n    }\n\n    fn update_spinner(&mut self) {\n        let matcher_running = !self.matcher_control.stopped();\n        let time_since_match = self.matcher_timer.elapsed();\n        let reading = self.item_pool.num_not_taken() != 0;\n\n        let should_show_spinner = reading || (matcher_running && time_since_match.as_millis() > MATCHER_DEBOUNCE_MS);\n\n        if should_show_spinner && !self.show_spinner {\n            self.toggle_spinner();\n        } else if !should_show_spinner && self.show_spinner {\n            // Hide spinner only after grace period to avoid flickering\n            if self.spinner_last_change.elapsed().as_millis() >= HIDE_GRACE_MS {\n                self.toggle_spinner();\n            }\n        }\n        if self.show_spinner {\n            self.needs_render.store(true, Ordering::Relaxed);\n        }\n    }\n\n    fn run_preview<B: Backend>(&mut self, tui: &mut Tui<B>) -> Result<()>\n    where\n        B::Error: Send + Sync + 'static,\n    {\n        // Debounce preview spawning to prevent overwhelming the system during rapid scrolling\n        const DEBOUNCE_MS: u64 = 50;\n        let now = std::time::Instant::now();\n        let elapsed = now.duration_since(self.last_preview_spawn);\n\n        if elapsed.as_millis() < u128::from(DEBOUNCE_MS) {\n            // Mark that we have a pending preview to run after the debounce period\n            self.pending_preview_run = true;\n            return Ok(());\n        }\n\n        self.pending_preview_run = false;\n        self.last_preview_spawn = now;\n\n        if let Some(preview_opt) = &self.options.preview\n            && let Some(item) = self.item_list.selected()\n        {\n            let selection: Vec<_> = self.item_list.selection.iter().map(|i| i.text().into_owned()).collect();\n            let selection_str: Vec<_> = selection.iter().map(std::string::String::as_str).collect();\n            let selected = self.item_list.selected();\n            let ctx = PreviewContext {\n                query: &self.input.value,\n                cmd_query: if self.options.interactive {\n                    &self.input.value\n                } else {\n                    self.options.cmd_query.as_deref().unwrap_or(&self.input.value)\n                },\n                width: self.preview.cols as usize,\n                height: self.preview.rows as usize,\n                current_index: selected\n                    .as_ref()\n                    .map(|i| i.rank.index.unsigned_abs() as usize)\n                    .unwrap_or_default(),\n                current_selection: &selected.map(|i| i.text().into_owned()).unwrap_or_default(),\n                selected_indices: &self\n                    .item_list\n                    .selection\n                    .iter()\n                    .map(|v| v.rank.index.unsigned_abs() as usize)\n                    .collect::<Vec<_>>(),\n                selections: &selection_str,\n            };\n            let preview = item.preview(ctx);\n            let preview_ready = !matches!(\n                preview,\n                ItemPreview::Global | ItemPreview::Command(_) | ItemPreview::CommandWithPos(_, _)\n            );\n            match preview {\n                ItemPreview::Command(cmd) => self.preview.spawn(tui, &self.expand_cmd(&cmd, true))?,\n                ItemPreview::Text(t) | ItemPreview::AnsiText(t) => {\n                    self.preview.content(&t.bytes().collect::<Vec<_>>())?;\n                }\n                ItemPreview::CommandWithPos(cmd, preview_position) => {\n                    // Execute command and apply position after content is ready\n                    self.preview.spawn(tui, &self.expand_cmd(&cmd, true))?;\n                    // Apply position offsets\n                    let v_scroll = match preview_position.v_scroll {\n                        crate::tui::Size::Fixed(n) => n,\n                        crate::tui::Size::Percent(p) => {\n                            u16::try_from(u32::from(self.preview.rows) * u32::from(p) / 100).unwrap_or(u16::MAX)\n                        }\n                    };\n                    let v_offset = match preview_position.v_offset {\n                        crate::tui::Size::Fixed(n) => n,\n                        crate::tui::Size::Percent(p) => {\n                            u16::try_from(u32::from(self.preview.rows) * u32::from(p) / 100).unwrap_or(u16::MAX)\n                        }\n                    };\n                    self.preview.scroll_y = v_scroll;\n                    self.preview.scroll_down(v_offset);\n\n                    let h_scroll = match preview_position.h_scroll {\n                        crate::tui::Size::Fixed(n) => n,\n                        crate::tui::Size::Percent(p) => {\n                            u16::try_from(u32::from(self.preview.cols) * u32::from(p) / 100).unwrap_or(u16::MAX)\n                        }\n                    };\n                    let h_offset = match preview_position.h_offset {\n                        crate::tui::Size::Fixed(n) => n,\n                        crate::tui::Size::Percent(p) => {\n                            u16::try_from(u32::from(self.preview.cols) * u32::from(p) / 100).unwrap_or(u16::MAX)\n                        }\n                    };\n                    self.preview.scroll_x = h_scroll.saturating_add(h_offset);\n                }\n                ItemPreview::TextWithPos(t, preview_position) | ItemPreview::AnsiWithPos(t, preview_position) => self\n                    .preview\n                    .content_with_position(&t.bytes().collect::<Vec<_>>(), preview_position)?,\n                ItemPreview::Global => self.preview.spawn(tui, &self.expand_cmd(preview_opt, true))?,\n            }\n            if preview_ready {\n                let _ = tui.event_tx.try_send(Event::PreviewReady);\n            }\n        } else if let Some(cb) = &self.options.preview_fn {\n            let selection: Vec<Arc<dyn SkimItem>>;\n            if self.options.multi {\n                selection = self.item_list.selection.iter().map(|i| i.item.clone()).collect();\n            } else if let Some(sel) = self.item_list.selected() {\n                selection = vec![sel.item];\n            } else {\n                selection = Vec::new();\n            }\n            self.preview.content(&cb(selection).join(\"\\n\").into_bytes())?;\n        }\n        Ok(())\n    }\n\n    /// Handles a TUI event and updates application state\n    ///\n    /// # Errors\n    ///\n    /// Returns an error if drawing, sending events, or spawning a preview fails.\n    #[allow(clippy::too_many_lines)]\n    pub fn handle_event<B: Backend>(&mut self, tui: &mut Tui<B>, event: &Event) -> Result<()>\n    where\n        B::Error: Send + Sync + 'static,\n    {\n        match event {\n            Event::Render => {\n                // Always render to avoid freezing, but the render function itself can optimize\n                tui.get_frame();\n                tui.draw(|f| {\n                    f.render_widget(&mut *self, f.area());\n                    f.set_cursor_position(self.cursor_pos);\n                })?;\n            }\n            Event::Heartbeat | Event::Tick => {\n                // Heartbeat is used for periodic UI updates\n                self.update_spinner();\n\n                if self.pending_matcher_restart {\n                    self.restart_matcher(true);\n                }\n                if self.needs_render.load(Ordering::Relaxed)\n                    && self.last_render_timer.elapsed().as_millis() > FRAME_TIME_MS\n                {\n                    debug!(\"Triggering render\");\n                    self.needs_render.store(false, Ordering::Relaxed);\n                    self.last_render_timer = std::time::Instant::now();\n                    tui.event_tx.try_send(Event::Render)?;\n                }\n\n                // Check if a debounced preview run needs to be executed\n                if self.pending_preview_run\n                    && let Err(e) = self.run_preview(tui)\n                {\n                    warn!(\"Heartbeat RunPreview: error {e:?}\");\n                }\n            }\n            Event::RunPreview => {\n                if let Err(e) = self.run_preview(tui) {\n                    warn!(\"RunPreview: error {e:?}\");\n                }\n            }\n            Event::Clear | Event::Redraw => {\n                tui.clear()?;\n            }\n            Event::Quit | Event::Close => {\n                tui.exit()?;\n                self.should_quit = true;\n            }\n            Event::PreviewReady => {\n                // Apply preview offset if configured\n                if let Some(offset_expr) = &self.options.preview_window.offset {\n                    let offset = self.calculate_preview_offset(offset_expr);\n                    self.preview.set_offset(offset);\n                }\n                self.needs_render();\n            }\n            Event::Error(msg) => {\n                tui.exit()?;\n                bail!(msg.to_owned());\n            }\n            Event::Action(act) => {\n                let events = self.handle_action(act)?;\n                for evt in events {\n                    tui.event_tx.try_send(evt)?;\n                }\n                tui.event_tx.try_send(Event::Render)?;\n            }\n            Event::Key(key) => {\n                let events = self.handle_key(key);\n                for evt in events {\n                    tui.event_tx.try_send(evt)?;\n                }\n            }\n            Event::Paste(text) => {\n                // Strip newlines/carriage returns from pasted text so they don't\n                // trigger Accept or get inserted as invisible characters.\n                let cleaned: String = text.chars().filter(|c| *c != '\\n' && *c != '\\r').collect();\n                if !cleaned.is_empty() {\n                    self.input.insert_str(&cleaned);\n                    let events = self.on_query_changed();\n                    for evt in events {\n                        tui.event_tx.try_send(evt)?;\n                    }\n                }\n            }\n            Event::Resize(cols, rows) => {\n                self.resize(*cols, *rows);\n                if let Err(e) = self.run_preview(tui) {\n                    warn!(\"error while rerunnig preview after resize: {e}\");\n                }\n            }\n            Event::Mouse(mouse_event) => {\n                self.handle_mouse(*mouse_event, tui)?;\n            }\n            Event::InvalidInput => {\n                warn!(\"Received invalid input\");\n            }\n            Event::ClearItems => {\n                self.item_pool.clear();\n                self.restart_matcher(true);\n            }\n            Event::AppendItems(items) => {\n                self.item_pool.append(items.to_owned());\n                self.restart_matcher(false);\n            }\n            Event::Reload(_) => {\n                unreachable!(\"Reload is handled by the TUI event loop in lib.rs\")\n            }\n        }\n\n        Ok(())\n    }\n    /// Handles new items received from the reader\n    pub fn handle_items(&mut self, items: Vec<Arc<dyn SkimItem>>) {\n        self.item_pool.append(items);\n        trace!(\"Got new items, len {}\", self.item_pool.len());\n        self.on_items_updated();\n    }\n    fn handle_key(&mut self, key: &KeyEvent) -> Vec<Event> {\n        debug!(\"key event: {key:?}\");\n\n        if let Some(act) = &self.options.keymap.get(key) {\n            debug!(\"{act:?}\");\n            return act.iter().map(|a| Event::Action(a.clone())).collect();\n        }\n        match key.modifiers {\n            KeyModifiers::CONTROL => {\n                if let Char('c') = key.code {\n                    return vec![Event::Quit];\n                }\n            }\n            KeyModifiers::NONE => {\n                if let Char(c) = key.code {\n                    return vec![Event::Action(Action::AddChar(c))];\n                }\n            }\n            KeyModifiers::SHIFT => {\n                if let Char(c) = key.code {\n                    return vec![Event::Action(Action::AddChar(c.to_uppercase().next().unwrap()))];\n                }\n            }\n            _ => (),\n        }\n        vec![]\n    }\n\n    #[allow(clippy::too_many_lines)]\n    fn handle_action(&mut self, act: &Action) -> Result<Vec<Event>> {\n        use Action::{\n            Abort, Accept, AddChar, AppendAndSelect, BackwardChar, BackwardDeleteChar, BackwardDeleteCharEof,\n            BackwardKillWord, BackwardWord, BeginningOfLine, Cancel, ClearScreen, Custom, DeleteChar, DeleteCharEof,\n            DeselectAll, Down, EndOfLine, Execute, ExecuteSilent, First, ForwardChar, ForwardWord, HalfPageDown,\n            HalfPageUp, IfNonMatched, IfQueryEmpty, IfQueryNotEmpty, Ignore, KillLine, KillWord, Last, NextHistory,\n            PageDown, PageUp, PreviewDown, PreviewLeft, PreviewPageDown, PreviewPageUp, PreviewRight, PreviewUp,\n            PreviousHistory, Redraw, RefreshCmd, RefreshPreview, Reload, RestartMatcher, RotateMode, ScrollLeft,\n            ScrollRight, Select, SelectAll, SelectRow, SetHeader, SetPreviewCmd, SetQuery, Toggle, ToggleAll, ToggleIn,\n            ToggleInteractive, ToggleOut, TogglePreview, TogglePreviewWrap, ToggleSort, Top, UnixLineDiscard,\n            UnixWordRubout, Up, Yank,\n        };\n        use ratatui::widgets::ListDirection::{BottomToTop, TopToBottom};\n        match act {\n            Abort | Accept(_) => {\n                self.should_quit = true;\n            }\n            AddChar(c) => {\n                self.input.insert(*c);\n                return Ok(self.on_query_changed());\n            }\n            AppendAndSelect => {\n                let value = self.input.value.clone();\n                let item: Arc<dyn SkimItem> = Arc::new(value);\n                let rank = Rank {\n                    index: i32::try_from(self.item_pool.len()).unwrap_or(i32::MAX),\n                    ..Default::default()\n                };\n                self.item_pool.append(vec![item.clone()]);\n                self.item_list.append(&mut vec![MatchedItem {\n                    item,\n                    rank,\n                    rank_builder: self.matcher.rank_builder.clone(),\n                    matched_range: None,\n                }]);\n                self.item_list.select_row(self.item_list.items.len() - 1);\n                self.restart_matcher_debounced();\n                return Ok(Self::on_selection_changed());\n            }\n            BackwardChar => {\n                self.input.move_cursor(-1);\n            }\n            BackwardDeleteChar => {\n                if self.input.delete(-1).is_some() {\n                    return Ok(self.on_query_changed());\n                }\n            }\n            BackwardDeleteCharEof => {\n                if self.input.is_empty() {\n                    self.should_quit = true;\n                    return Ok(vec![]);\n                }\n                self.input.delete(-1);\n                return Ok(self.on_query_changed());\n            }\n            BackwardKillWord => {\n                let deleted = self.input.delete_backward_word();\n                if !deleted.is_empty() {\n                    self.yank(deleted);\n                    return Ok(self.on_query_changed());\n                }\n            }\n            BackwardWord => {\n                self.input.move_cursor_backward_word();\n            }\n            BeginningOfLine => {\n                self.input.move_cursor_to(0);\n            }\n            Cancel => {\n                self.matcher_control.kill();\n                self.preview.kill();\n            }\n            ClearScreen => {\n                return Ok(vec![Event::Clear]);\n            }\n            DeleteChar => {\n                if self.input.delete(0).is_some() {\n                    return Ok(self.on_query_changed());\n                }\n            }\n            DeleteCharEof => {\n                if self.input.is_empty() {\n                    self.should_quit = true;\n                    return Ok(vec![]);\n                } else if self.input.delete(0).is_some() {\n                    return Ok(self.on_query_changed());\n                }\n            }\n            DeselectAll => {\n                if !self.item_list.selection.is_empty() {\n                    self.item_list.selection = Default::default();\n                    return Ok(Self::on_selection_changed());\n                }\n            }\n            Down(n) => {\n                match self.item_list.direction {\n                    TopToBottom => self.item_list.scroll_by(i32::from(*n)),\n                    BottomToTop => self.item_list.scroll_by(-i32::from(*n)),\n                }\n                return Ok(Self::on_selection_changed());\n            }\n            EndOfLine => {\n                self.input.move_to_end();\n            }\n            Execute(cmd) => {\n                let mut command = Command::new(\"sh\");\n                let expanded_cmd = self.expand_cmd(cmd, true);\n                debug!(\"execute: {expanded_cmd}\");\n                command.args([\"-c\", &expanded_cmd]);\n                let in_raw_mode = crossterm::terminal::is_raw_mode_enabled()?;\n                if in_raw_mode {\n                    crossterm::terminal::disable_raw_mode()?;\n                }\n                crossterm::execute!(\n                    std::io::stderr(),\n                    crossterm::terminal::LeaveAlternateScreen,\n                    crossterm::event::DisableMouseCapture\n                )?;\n                let _ = command.spawn().and_then(|mut c| c.wait());\n                if in_raw_mode {\n                    crossterm::terminal::enable_raw_mode()?;\n                }\n                crossterm::execute!(\n                    std::io::stderr(),\n                    crossterm::terminal::EnterAlternateScreen,\n                    crossterm::event::EnableMouseCapture\n                )?;\n                return Ok(vec![Event::Redraw]);\n            }\n            ExecuteSilent(cmd) => {\n                let mut command = Command::new(\"sh\");\n                let expanded_cmd = self.expand_cmd(cmd, true);\n                command.args([\"-c\", &expanded_cmd]);\n                command.stdout(Stdio::null());\n                command.stderr(Stdio::null());\n                let _ = command.spawn();\n            }\n            First | Top => {\n                // Jump to first item (considering reserved items)\n                self.item_list.jump_to_first();\n                return Ok(Self::on_selection_changed());\n            }\n            ForwardChar => {\n                self.input.move_cursor(1);\n            }\n            ForwardWord => {\n                self.input.move_cursor_forward_word();\n            }\n            IfQueryEmpty(then, otherwise) => {\n                let inner = crate::binds::parse_action_chain(then)?;\n                if self.input.is_empty() {\n                    return Ok(inner.iter().map(|e| Event::Action(e.to_owned())).collect());\n                } else if let Some(o) = otherwise {\n                    return Ok(crate::binds::parse_action_chain(o)?\n                        .iter()\n                        .map(|e| Event::Action(e.to_owned()))\n                        .collect());\n                }\n            }\n            IfQueryNotEmpty(then, otherwise) => {\n                let inner = crate::binds::parse_action_chain(then)?;\n                if !self.input.is_empty() {\n                    return Ok(inner.iter().map(|e| Event::Action(e.to_owned())).collect());\n                } else if let Some(o) = otherwise {\n                    return Ok(crate::binds::parse_action_chain(o)?\n                        .iter()\n                        .map(|e| Event::Action(e.to_owned()))\n                        .collect());\n                }\n            }\n            IfNonMatched(then, otherwise) => {\n                let inner = crate::binds::parse_action_chain(then)?;\n                if self.item_list.items.is_empty() {\n                    return Ok(inner.iter().map(|e| Event::Action(e.to_owned())).collect());\n                } else if let Some(o) = otherwise {\n                    return Ok(crate::binds::parse_action_chain(o)?\n                        .iter()\n                        .map(|e| Event::Action(e.to_owned()))\n                        .collect());\n                }\n            }\n            Ignore => (),\n            KillLine => {\n                let cursor = self.input.cursor_pos as usize;\n                let deleted = self.input.split_off(cursor);\n                self.yank(deleted);\n                return Ok(self.on_query_changed());\n            }\n            KillWord => {\n                let deleted = self.input.delete_forward_word();\n                self.yank(deleted);\n                return Ok(self.on_query_changed());\n            }\n            Last => {\n                // Jump to last item\n                self.item_list.jump_to_last();\n                return Ok(Self::on_selection_changed());\n            }\n            NextHistory => {\n                // Use cmd_history in interactive mode, query_history otherwise\n                let (history, history_index, saved_input) = if self.options.interactive {\n                    (\n                        &self.cmd_history,\n                        &mut self.cmd_history_index,\n                        &mut self.saved_cmd_input,\n                    )\n                } else {\n                    (&self.query_history, &mut self.history_index, &mut self.saved_input)\n                };\n\n                if history.is_empty() {\n                    return Ok(vec![]);\n                }\n\n                match *history_index {\n                    None => {\n                        // Already at most recent (current input), do nothing\n                    }\n                    Some(idx) => {\n                        if idx + 1 >= history.len() {\n                            // Move to most recent (restore saved input)\n                            self.input.value = saved_input.clone();\n                            self.input.move_to_end();\n                            *history_index = None;\n                        } else {\n                            // Move forward in history (toward more recent)\n                            let new_idx = idx + 1;\n                            self.input.value = history[new_idx].clone();\n                            self.input.move_to_end();\n                            *history_index = Some(new_idx);\n                        }\n                    }\n                }\n\n                return Ok(self.on_query_changed());\n            }\n            HalfPageDown(n) => {\n                let offset = i32::from(self.item_list.height) / 2;\n                if self.options.layout == TuiLayout::Default {\n                    self.item_list.scroll_by(-offset * n);\n                } else {\n                    self.item_list.scroll_by(offset * n);\n                }\n                return Ok(Self::on_selection_changed());\n            }\n            HalfPageUp(n) => {\n                let offset = i32::from(self.item_list.height) / 2;\n                if self.options.layout == TuiLayout::Default {\n                    self.item_list.scroll_by(offset * n);\n                } else {\n                    self.item_list.scroll_by(-offset * n);\n                }\n                return Ok(Self::on_selection_changed());\n            }\n            PageDown(n) => {\n                let offset = i32::from(self.item_list.height);\n                if self.options.layout == TuiLayout::Default {\n                    self.item_list.scroll_by(-offset * n);\n                } else {\n                    self.item_list.scroll_by(offset * n);\n                }\n                return Ok(Self::on_selection_changed());\n            }\n            PageUp(n) => {\n                let offset = i32::from(self.item_list.height);\n                if self.options.layout == TuiLayout::Default {\n                    self.item_list.scroll_by(offset * n);\n                } else {\n                    self.item_list.scroll_by(-offset * n);\n                }\n                return Ok(Self::on_selection_changed());\n            }\n            PreviewUp(n) => {\n                self.preview.scroll_up(u16::try_from(*n).unwrap_or(u16::MAX));\n                self.needs_render();\n            }\n            PreviewDown(n) => {\n                self.preview.scroll_down(u16::try_from(*n).unwrap_or(u16::MAX));\n                self.needs_render();\n            }\n            PreviewLeft(n) => {\n                self.preview.scroll_left(u16::try_from(*n).unwrap_or(u16::MAX));\n                self.needs_render();\n            }\n            PreviewRight(n) => {\n                self.preview.scroll_right(u16::try_from(*n).unwrap_or(u16::MAX));\n                self.needs_render();\n            }\n            PreviewPageUp(_n) => {\n                self.preview.page_up();\n                self.needs_render();\n            }\n            PreviewPageDown(_n) => {\n                self.preview.page_down();\n                self.needs_render();\n            }\n            PreviousHistory => {\n                // Use cmd_history in interactive mode, query_history otherwise\n                let (history, history_index, saved_input) = if self.options.interactive {\n                    (\n                        &self.cmd_history,\n                        &mut self.cmd_history_index,\n                        &mut self.saved_cmd_input,\n                    )\n                } else {\n                    (&self.query_history, &mut self.history_index, &mut self.saved_input)\n                };\n\n                if history.is_empty() {\n                    return Ok(vec![]);\n                }\n\n                match *history_index {\n                    None => {\n                        // Save current input and go to most recent history entry\n                        saved_input.clone_from(&self.input.value);\n                        let new_idx = history.len() - 1;\n                        self.input.value = history[new_idx].clone();\n                        self.input.move_to_end();\n                        *history_index = Some(new_idx);\n                    }\n                    Some(idx) => {\n                        if idx > 0 {\n                            // Move backward in history (toward older entries)\n                            let new_idx = idx - 1;\n                            self.input.value = history[new_idx].clone();\n                            self.input.move_to_end();\n                            *history_index = Some(new_idx);\n                        }\n                        // else: already at oldest, do nothing\n                    }\n                }\n\n                return Ok(self.on_query_changed());\n            }\n            Redraw => return Ok(vec![Event::Clear]),\n            Reload(Some(s)) => {\n                self.item_list.clear_selection();\n                return Ok(vec![Event::Reload(self.expand_cmd(s, true))]);\n            }\n            Reload(None) => {\n                self.item_list.clear_selection();\n                return Ok(vec![Event::Reload(self.cmd.clone())]);\n            }\n            RefreshCmd => {\n                // Refresh the command (reload in interactive mode)\n                if self.options.interactive {\n                    let expanded_cmd = self.expand_cmd(&self.cmd, true);\n                    return Ok(vec![Event::Reload(expanded_cmd)]);\n                }\n            }\n            RefreshPreview => {\n                return Ok(vec![Event::RunPreview]);\n            }\n            RestartMatcher => {\n                self.restart_matcher(true);\n            }\n            RotateMode => {\n                // Cycle through modes: fuzzy -> exact -> regex -> fuzzy\n                if self.options.regex {\n                    // regex -> fuzzy\n                    self.options.regex = false;\n                    self.options.exact = false;\n                } else if self.options.exact {\n                    // exact -> regex\n                    self.options.exact = false;\n                    self.options.regex = true;\n                } else {\n                    // fuzzy -> exact\n                    self.options.exact = true;\n                }\n                self.matcher = Matcher::from_options(&self.options);\n                self.restart_matcher(true);\n            }\n            ScrollLeft(n) => {\n                self.item_list.manual_hscroll = self.item_list.manual_hscroll.saturating_sub(*n);\n            }\n            ScrollRight(n) => {\n                self.item_list.manual_hscroll = self.item_list.manual_hscroll.saturating_add(*n);\n            }\n            SelectAll => {\n                self.item_list.select_all();\n                return Ok(Self::on_selection_changed());\n            }\n            SelectRow(row) => {\n                self.item_list.select_row(*row);\n                return Ok(Self::on_selection_changed());\n            }\n            Select => {\n                self.item_list.select();\n                return Ok(Self::on_selection_changed());\n            }\n            SetHeader(opt_header) => {\n                opt_header.clone_into(&mut self.options.header);\n                self.header = Header::from_options(&self.options, self.theme.clone());\n                // Rebuild the layout template so that the header area is\n                // included (or excluded) on the next render.\n                self.layout_template = LayoutTemplate::from_options(&self.options, self.header.height());\n            }\n            SetPreviewCmd(cmd) => {\n                self.options.preview = Some(cmd.to_owned());\n                return Ok(vec![Event::RunPreview]);\n            }\n            SetQuery(value) => {\n                self.input.value = self.expand_cmd(value, false);\n                self.input.move_to_end();\n                return Ok(self.on_query_changed());\n            }\n            Toggle => {\n                self.item_list.toggle();\n                return Ok(Self::on_selection_changed());\n            }\n            ToggleAll => {\n                self.item_list.toggle_all();\n                return Ok(Self::on_selection_changed());\n            }\n            ToggleIn => {\n                self.item_list.toggle();\n                match self.item_list.direction {\n                    TopToBottom => self.item_list.select_next(),\n                    BottomToTop => self.item_list.select_previous(),\n                }\n                return Ok(Self::on_selection_changed());\n            }\n            ToggleInteractive => {\n                self.options.interactive = !self.options.interactive;\n                self.input.switch_mode();\n                self.restart_matcher(true);\n            }\n            ToggleOut => {\n                self.item_list.toggle();\n                match self.item_list.direction {\n                    TopToBottom => self.item_list.select_previous(),\n                    BottomToTop => self.item_list.select_next(),\n                }\n                return Ok(Self::on_selection_changed());\n            }\n            TogglePreview => {\n                self.options.preview_window.hidden = !self.options.preview_window.hidden;\n                self.layout_template = LayoutTemplate::from_options(&self.options, self.header.height());\n                self.needs_render();\n            }\n            TogglePreviewWrap => {\n                self.preview.wrap = !self.preview.wrap;\n                self.needs_render();\n            }\n            ToggleSort => {\n                self.options.no_sort = !self.options.no_sort;\n                self.restart_matcher(true);\n            }\n            UnixLineDiscard => {\n                if !self.input.delete_to_beginning().is_empty() {\n                    return Ok(self.on_query_changed());\n                }\n            }\n            UnixWordRubout => {\n                if !self.input.delete_backward_to_whitespace().is_empty() {\n                    return Ok(self.on_query_changed());\n                }\n            }\n            Up(n) => {\n                match self.item_list.direction {\n                    TopToBottom => self.item_list.scroll_by(-i32::from(*n)),\n                    BottomToTop => self.item_list.scroll_by(i32::from(*n)),\n                }\n                return Ok(Self::on_selection_changed());\n            }\n            Yank => {\n                // Insert from yank register at cursor position\n                self.input.insert_str(&self.yank_register);\n                return Ok(self.on_query_changed());\n            }\n            Custom(cb) => {\n                return cb.call(self).map_err(|e| color_eyre::eyre::eyre!(\"{}\", e));\n            }\n        }\n        Ok(Vec::default())\n    }\n\n    /// Returns the selected items as results\n    pub fn results(&mut self) -> Vec<MatchedItem> {\n        if self.options.filter.is_some() {\n            // In filter mode, drain items to avoid cloning\n            self.item_list.items.drain(..).collect()\n        } else if self.options.multi && !self.item_list.selection.is_empty() {\n            self.item_list.selection.clone().into_iter().collect()\n        } else if let Some(sel) = self.item_list.selected() {\n            vec![sel]\n        } else {\n            vec![]\n        }\n    }\n\n    /// Restart the matcher to process items in the item pool.\n    ///\n    /// If `force` is true, the matcher will be restarted even if it's currently running.\n    /// If `force` is false, the matcher will only be restarted if there are new items\n    /// to process or if the previous matcher has completed.\n    pub fn restart_matcher(&mut self, force: bool) {\n        use crate::tui::item_list::{MergeStrategy, ProcessedItems};\n        // Check if query meets minimum length requirement\n        if let Some(min_length) = self.options.min_query_length\n            && !self.options.disabled\n        {\n            let query_to_check = &self.input.value;\n\n            if query_to_check.chars().count() < min_length {\n                // Query is too short, clear items and don't run matcher\n                self.matcher_control.kill();\n                self.item_list.items.clear();\n                self.item_list.current = 0;\n                self.item_list.offset = 0;\n                return;\n            }\n        }\n\n        let matcher_stopped = self.matcher_control.stopped();\n        if force || (matcher_stopped && self.item_pool.num_not_taken() > 0) {\n            trace!(\"restarting matcher, force={force}\");\n            // Reset debounce timer on any restart to prevent interference\n            self.last_matcher_restart = std::time::Instant::now();\n            self.pending_matcher_restart = false;\n            self.matcher_control.kill();\n            // record matcher start time for statusline spinner/progress\n            self.matcher_timer = std::time::Instant::now();\n            // In interactive mode, use empty query so all items are shown\n            // The input contains the command to execute, not a filter query\n            let query = if self.options.disabled {\n                \"\"\n            } else if self.options.interactive {\n                &input::Input::default()\n            } else {\n                &self.input\n            };\n            let item_pool = self.item_pool.clone();\n            let thread_pool = &self.thread_pool;\n            let processed_items = self.item_list.processed_items.clone();\n            let no_sort = self.options.no_sort;\n\n            if force {\n                self.item_pool.reset();\n            }\n\n            let needs_render = self.needs_render.clone();\n\n            self.matcher_control = self.matcher.run(query, &item_pool, thread_pool, move |mut matches| {\n                debug!(\"Got {} results from matcher, sending to item list...\", matches.len());\n\n                if !no_sort {\n                    matches.sort();\n                }\n\n                if force {\n                    // Full re-match: replace all results\n                    *processed_items.lock() = Some(ProcessedItems {\n                        items: matches,\n                        merge: MergeStrategy::Replace,\n                    });\n                } else {\n                    // Incremental: merge new matches into any unconsumed processed items,\n                    // and mark with merge strategy so the render loop merges with item_list.items\n                    let merge_strategy = if no_sort {\n                        MergeStrategy::Append\n                    } else {\n                        MergeStrategy::SortedMerge\n                    };\n                    let mut guard = processed_items.lock();\n                    if let Some(ref mut existing) = *guard {\n                        if no_sort {\n                            existing.items.extend(matches);\n                        } else {\n                            // Merge incoming matches into existing sorted list in-place.\n                            MatchedItem::merge_into_sorted(&mut existing.items, matches);\n                        }\n                    } else {\n                        *guard = Some(ProcessedItems {\n                            items: matches,\n                            merge: merge_strategy,\n                        });\n                    }\n                }\n                needs_render.store(true, Ordering::Relaxed);\n            });\n        }\n    }\n\n    fn yank(&mut self, contents: String) {\n        self.yank_register = contents;\n    }\n\n    /// Expand placeholders in a command string with current app state.\n    /// Replaces {}, {q}, {cq}, {n}, {+}, {+n}, and field patterns.\n    ///\n    /// Note: in command mode, the replstr is replaced by the current query\n    #[must_use]\n    pub fn expand_cmd(&self, cmd: &str, quote_args: bool) -> String {\n        util::printf(\n            cmd,\n            &self.options.delimiter,\n            &self.options.replstr,\n            &self.item_list.selection.iter(),\n            &self.item_list.selected(),\n            &self.input.value,\n            &self.input.value,\n            quote_args,\n        )\n    }\n\n    /// Restart matcher with debouncing to avoid excessive restarts during rapid typing\n    fn restart_matcher_debounced(&mut self) {\n        const DEBOUNCE_MS: u64 = 50;\n\n        if self.options.disabled {\n            return;\n        }\n\n        // If enough time has passed since last restart, restart immediately\n        if self.last_matcher_restart.elapsed().as_millis() > u128::from(DEBOUNCE_MS) {\n            debug!(\"restart_matcher_debounced: true\");\n            self.restart_matcher(true);\n        } else {\n            debug!(\"restart_matcher_debounced: false\");\n            self.pending_matcher_restart = true;\n        }\n    }\n\n    /// Handle mouse events\n    fn handle_mouse<B: Backend>(&mut self, mouse_event: MouseEvent, tui: &mut Tui<B>) -> Result<()>\n    where\n        B::Error: Send + Sync + 'static,\n    {\n        let mouse_pos = ratatui::layout::Position {\n            x: mouse_event.column,\n            y: mouse_event.row,\n        };\n\n        match mouse_event.kind {\n            MouseEventKind::ScrollUp => {\n                // Check if mouse is over preview area\n                if let Some(preview_area) = self.layout.preview_area\n                    && preview_area.contains(mouse_pos)\n                {\n                    // Scroll preview up\n                    for evt in self.handle_action(&Action::PreviewUp(3))? {\n                        tui.event_tx.try_send(evt)?;\n                    }\n                    return Ok(());\n                }\n                // Otherwise scroll item list up\n                for evt in self.handle_action(&Action::Up(1))? {\n                    tui.event_tx.try_send(evt)?;\n                }\n            }\n            MouseEventKind::ScrollDown => {\n                // Check if mouse is over preview area\n                if let Some(preview_area) = self.layout.preview_area\n                    && preview_area.contains(mouse_pos)\n                {\n                    // Scroll preview down\n                    for evt in self.handle_action(&Action::PreviewDown(3))? {\n                        tui.event_tx.try_send(evt)?;\n                    }\n                    return Ok(());\n                }\n                // Otherwise scroll item list down\n                for evt in self.handle_action(&Action::Down(1))? {\n                    tui.event_tx.try_send(evt)?;\n                }\n            }\n            _ => {\n                // Ignore other mouse events for now\n            }\n        }\n        tui.event_tx.try_send(Event::Render)?;\n        Ok(())\n    }\n    fn toggle_spinner(&mut self) {\n        self.show_spinner = !self.show_spinner;\n        self.spinner_last_change = std::time::Instant::now();\n        self.needs_render.store(true, Ordering::Relaxed);\n    }\n}\n"
  },
  {
    "path": "src/tui/backend.rs",
    "content": "use std::io::BufWriter;\nuse std::ops::{Deref, DerefMut};\nuse std::sync::Once;\n\nuse color_eyre::eyre::Result;\nuse crossterm::event::KeyEventKind;\nuse crossterm::event::{DisableBracketedPaste, EnableBracketedPaste};\nuse crossterm::event::{DisableMouseCapture, EnableMouseCapture};\nuse crossterm::terminal::{EnterAlternateScreen, LeaveAlternateScreen};\nuse crossterm::{self, cursor};\nuse futures::{FutureExt as _, StreamExt as _};\nuse ratatui::layout::Rect;\nuse ratatui::prelude::{Backend, CrosstermBackend};\nuse ratatui::{TerminalOptions, Viewport};\nuse tokio::sync::mpsc::{Receiver, Sender, channel};\nuse tokio::task::JoinHandle;\nuse tokio_util::sync::CancellationToken;\n\nuse super::util::cursor_pos_from_tty;\nuse super::{Event, Size};\n\nconst TICK_RATE: f64 = 12.;\nstatic PANIC_HOOK_SET: Once = Once::new();\n\n/// Terminal user interface handler for skim\npub struct Tui<B: Backend = ratatui::backend::CrosstermBackend<BufWriter<std::io::Stderr>>>\nwhere\n    B::Error: Send + Sync + 'static,\n{\n    /// The ratatui terminal instance\n    pub terminal: ratatui::Terminal<B>,\n    /// Background task handle for event polling\n    pub task: Option<JoinHandle<()>>,\n    /// Receiver for TUI events\n    pub event_rx: Receiver<Event>,\n    /// Sender for TUI events\n    pub event_tx: Sender<Event>,\n    /// Tick rate for updates (ticks per second)\n    pub tick_rate: f64,\n    /// Token for cancelling background tasks\n    pub cancellation_token: CancellationToken,\n    /// Whether running in fullscreen mode\n    pub is_fullscreen: bool,\n}\n\nimpl Tui {\n    /// Creates a TUI with the default backend (buffered stderr) and the specified height\n    ///\n    /// # Errors\n    ///\n    /// Returns an error if the TUI backend cannot be initialized.\n    pub fn new_with_height(height: Size) -> Result<Self> {\n        let backend = CrosstermBackend::new(std::io::BufWriter::new(std::io::stderr()));\n        Self::new_with_height_and_backend(backend, height)\n    }\n}\n\nimpl<B: Backend> Tui<B>\nwhere\n    B::Error: Send + Sync + 'static,\n{\n    /// Creates a new TUI with the specified backend and height\n    ///\n    /// # Errors\n    ///\n    /// Returns an error if the terminal size cannot be determined or setup fails.\n    ///\n    /// # Panics\n    ///\n    /// Panics if the terminal size cannot be read from the backend.\n    pub fn new_with_height_and_backend(backend: B, height: Size) -> Result<Self> {\n        let event_channel = channel(1024 * 1024);\n\n        let term_height = backend.size().expect(\"Failed to get terminal height\").height;\n        let lines = match height {\n            Size::Percent(100) => None,\n            Size::Fixed(lines) => Some(lines),\n            Size::Percent(p) => Some(term_height * p / 100),\n        };\n\n        let viewport = if let Some(mut height) = lines {\n            // Until https://github.com/crossterm-rs/crossterm/issues/919 is fixed, we need to do it ourselves\n            let cursor_pos = cursor_pos_from_tty()?;\n            let mut y = cursor_pos.1 - 1;\n            height = height.min(term_height);\n            if term_height - cursor_pos.1 < height {\n                let to_scroll = height - (term_height - cursor_pos.1) - 1;\n                crossterm::execute!(std::io::stderr(), crossterm::terminal::ScrollUp(to_scroll))?;\n                y = y.saturating_sub(to_scroll);\n            }\n            Viewport::Fixed(Rect::new(\n                0,\n                y,\n                backend.size().expect(\"Failed to get terminal width\").width - 1,\n                height,\n            ))\n        } else {\n            Viewport::Fullscreen\n        };\n\n        set_panic_hook();\n        Ok(Self {\n            terminal: ratatui::Terminal::with_options(backend, TerminalOptions { viewport })?,\n            task: None,\n            event_rx: event_channel.1,\n            event_tx: event_channel.0,\n            tick_rate: TICK_RATE,\n            cancellation_token: CancellationToken::default(),\n            is_fullscreen: lines.is_none(),\n        })\n    }\n\n    /// Creates a new TUI for testing with a fullscreen viewport.\n    ///\n    /// This constructor skips terminal-specific operations (cursor detection,\n    /// raw mode, scrolling) that don't work with `TestBackend`. Use this when\n    /// writing snapshot tests or other tests that need to render the UI.\n    ///\n    /// # Errors\n    ///\n    /// Returns an error if the terminal cannot be initialized with the given backend.\n    #[cfg(any(test, feature = \"test-utils\"))]\n    pub fn new_for_test(backend: B) -> Result<Self> {\n        let event_channel = channel(1024 * 1024);\n        Ok(Self {\n            terminal: ratatui::Terminal::new(backend)?,\n            task: None,\n            event_rx: event_channel.1,\n            event_tx: event_channel.0,\n            tick_rate: TICK_RATE,\n            cancellation_token: CancellationToken::default(),\n            is_fullscreen: true,\n        })\n    }\n\n    /// Enters the TUI by enabling raw mode and starting event handling\n    ///\n    /// # Errors\n    ///\n    /// Returns an error if enabling raw mode or mouse capture fails.\n    pub fn enter(&mut self) -> Result<()> {\n        crossterm::terminal::enable_raw_mode()?;\n        crossterm::execute!(std::io::stderr(), EnableMouseCapture, EnableBracketedPaste)?;\n        if self.is_fullscreen {\n            crossterm::execute!(std::io::stderr(), EnterAlternateScreen, cursor::Hide)?;\n        }\n        self.start();\n        Ok(())\n    }\n\n    /// Exits the TUI by stopping event handling and disabling raw mode\n    ///\n    /// # Errors\n    ///\n    /// Returns an error if disabling raw mode or mouse capture fails.\n    pub fn exit(&mut self) -> Result<()> {\n        self.stop();\n        if crossterm::terminal::is_raw_mode_enabled()? {\n            crossterm::execute!(\n                std::io::stderr(),\n                DisableMouseCapture,\n                DisableBracketedPaste,\n                LeaveAlternateScreen,\n                cursor::Show\n            )?;\n            crossterm::terminal::disable_raw_mode()?;\n        }\n        // When using the inline layout, we want to remove all previous output\n        //  -> reset cursor at the top of the drawing area\n        if !self.is_fullscreen {\n            self.clear()?;\n            let area = self.get_frame().area();\n            let orig = ratatui::layout::Position { x: area.x, y: area.y };\n            self.set_cursor_position(orig)?;\n        }\n        Ok(())\n    }\n    /// Stops the TUI event loop\n    /// Equivalent to `self.cancel()`\n    pub fn stop(&self) {\n        self.cancel();\n    }\n    /// Cancels all background tasks\n    pub fn cancel(&self) {\n        self.cancellation_token.cancel();\n    }\n    /// Starts the event loop for handling keyboard and timer events\n    pub fn start(&mut self) {\n        let tick_delay = std::time::Duration::from_secs_f64(1.0 / self.tick_rate);\n        let event_tx_clone = self.event_tx.clone();\n        let cancellation_token_clone = self.cancellation_token.clone();\n        if self.task.is_some() {\n            self.cancel();\n        }\n        self.task = Some(tokio::spawn(async move {\n            let mut reader = crossterm::event::EventStream::new();\n            let mut tick_interval = tokio::time::interval(tick_delay);\n            loop {\n                let tick_delay = tick_interval.tick();\n                let crossterm_event = reader.next().fuse();\n                tokio::select! {\n                    () = cancellation_token_clone.cancelled() => {\n                        break;\n                    }\n                    maybe_event = crossterm_event => {\n                      match maybe_event {\n                        Some(Ok(crossterm::event::Event::Key(key))) => {\n                          if key.kind == KeyEventKind::Press {\n                            _ = event_tx_clone.try_send(Event::Key(key));\n                          }\n                        }\n                        Some(Ok(crossterm::event::Event::Paste(text))) => {\n                          _ = event_tx_clone.try_send(Event::Paste(text));\n                        }\n                        Some(Ok(crossterm::event::Event::Mouse(mouse))) => {\n                          _ = event_tx_clone.try_send(Event::Mouse(mouse));\n                        }\n                        Some(Ok(crossterm::event::Event::Resize(cols, rows))) => {\n                          _ = event_tx_clone.try_send(Event::Resize(cols, rows));\n                          _ = event_tx_clone.try_send(Event::Render);\n                        }\n                        Some(Err(e)) => {\n                          _ = event_tx_clone.try_send(Event::Error(e.to_string()));\n                        }\n                        None | Some(Ok(_)) => {},\n                      }\n                    },\n                    _ = tick_delay => {\n                        _ = event_tx_clone.try_send(Event::Heartbeat);\n                    },\n                }\n            }\n        }));\n    }\n\n    /// Gets the next event from the event queue\n    pub async fn next(&mut self) -> Option<Event> {\n        self.event_rx.recv().await\n    }\n}\n\nimpl<B: Backend> Deref for Tui<B>\nwhere\n    B::Error: Send + Sync + 'static,\n{\n    type Target = ratatui::Terminal<B>;\n\n    fn deref(&self) -> &Self::Target {\n        &self.terminal\n    }\n}\n\nimpl<B: Backend> DerefMut for Tui<B>\nwhere\n    B::Error: Send + Sync + 'static,\n{\n    fn deref_mut(&mut self) -> &mut Self::Target {\n        &mut self.terminal\n    }\n}\n\nimpl<B: Backend> Drop for Tui<B>\nwhere\n    B::Error: Send + Sync + 'static,\n{\n    fn drop(&mut self) {\n        if let Some(t) = self.task.take() {\n            t.abort();\n        }\n        let _ = self.exit();\n    }\n}\n\nfn set_panic_hook() {\n    PANIC_HOOK_SET.call_once(|| {\n        let hook = std::panic::take_hook();\n        std::panic::set_hook(Box::new(move |panic_info| {\n            ratatui::restore(); // ignore any errors as we are already failing\n            hook(panic_info);\n        }));\n    });\n}\n"
  },
  {
    "path": "src/tui/event.rs",
    "content": "use std::future::Future;\nuse std::pin::Pin;\nuse std::sync::{Arc, Mutex};\n\nuse crate::exhaustive_match;\nuse crossterm::event::{KeyEvent, MouseEvent};\nuse derive_more::{Debug, Eq, PartialEq};\n\ntype BoxError = Box<dyn std::error::Error + Sync + Send>;\ntype BoxFuture<'a> = Pin<Box<dyn Future<Output = Result<Vec<Event>, BoxError>> + Send + 'a>>;\n\n/// Trait object stored inside [`ActionCallback`].\n///\n/// Having an explicit trait (rather than a bare `dyn Fn` type alias) allows\n/// Rust to correctly resolve the higher-ranked lifetime in the return type.\ntrait AsyncCallbackFn: Send {\n    fn call<'a>(&'a self, app: &'a mut crate::tui::App) -> BoxFuture<'a>;\n}\n\n/// Adapter that stores a concrete async closure and implements [`AsyncCallbackFn`].\nstruct AsyncFnWrapper<F>(F);\n\nimpl<F, Fut> AsyncCallbackFn for AsyncFnWrapper<F>\nwhere\n    F: for<'a> Fn(&'a mut crate::tui::App) -> Fut + Send,\n    Fut: Future<Output = Result<Vec<Event>, BoxError>> + Send + 'static,\n{\n    fn call<'a>(&'a self, app: &'a mut crate::tui::App) -> BoxFuture<'a> {\n        Box::pin((self.0)(app))\n    }\n}\n\n/// Adapter that stores a plain synchronous closure and implements [`AsyncCallbackFn`].\nstruct SyncFnWrapper<F>(F);\n\nimpl<F> AsyncCallbackFn for SyncFnWrapper<F>\nwhere\n    F: Fn(&mut crate::tui::App) -> Result<Vec<Event>, BoxError> + Send,\n{\n    fn call<'a>(&'a self, app: &'a mut crate::tui::App) -> BoxFuture<'a> {\n        Box::pin(std::future::ready((self.0)(app)))\n    }\n}\n\n/// A custom action callback that receives a mutable reference to the App.\n///\n/// The closure will be called with a mutable reference to App and should return\n/// a vec of events that will be processed after the callback completes.\n///\n/// Both sync and async closures are supported:\n/// - Use [`ActionCallback::new`] to wrap an **async** closure or block.\n/// - Use [`ActionCallback::new_sync`] to wrap a plain synchronous closure.\n#[derive(Clone)]\npub struct ActionCallback(Arc<Mutex<dyn AsyncCallbackFn>>);\n\nimpl std::fmt::Debug for ActionCallback {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        f.debug_struct(\"ActionCallback\").finish()\n    }\n}\n\nimpl ActionCallback {\n    /// Create a new action callback from an **async** closure or block.\n    ///\n    /// ```rust,ignore\n    /// ActionCallback::new(|app| async move {\n    ///     // async work here …\n    ///     Ok(vec![])\n    /// });\n    /// ```\n    pub fn new<F, Fut>(f: F) -> Self\n    where\n        F: for<'a> Fn(&'a mut crate::tui::App) -> Fut + Send + 'static,\n        Fut: Future<Output = Result<Vec<Event>, BoxError>> + Send + 'static,\n    {\n        Self(Arc::new(Mutex::new(AsyncFnWrapper(f))))\n    }\n\n    /// Create a new action callback from a plain **synchronous** closure.\n    ///\n    /// This is a convenience wrapper; the closure is lifted into an immediately-\n    /// resolving future so it integrates with the same async call site.\n    ///\n    /// ```rust,ignore\n    /// ActionCallback::new_sync(|app| {\n    ///     Ok(vec![Event::Action(Action::SelectAll)])\n    /// });\n    /// ```\n    pub fn new_sync<F>(f: F) -> Self\n    where\n        F: Fn(&mut crate::tui::App) -> Result<Vec<Event>, BoxError> + Send + 'static,\n    {\n        Self(Arc::new(Mutex::new(SyncFnWrapper(f))))\n    }\n\n    /// Call the callback with an App reference, driving the returned future to completion.\n    ///\n    /// Must be called from within a Tokio multi-thread runtime context.\n    pub(crate) fn call(&self, app: &mut crate::tui::App) -> Result<Vec<Event>, BoxError> {\n        let callback = self.0.lock().unwrap();\n        let fut = callback.call(app);\n        // We are inside a synchronous call stack that originates from an async\n        // tokio context.  `block_in_place` moves the current thread out of the\n        // async worker pool temporarily so we can block on the future without\n        // starving the runtime.\n        tokio::task::block_in_place(|| tokio::runtime::Handle::current().block_on(fut))\n    }\n}\n\n/// Events that can occur during skim's execution\n#[derive(Clone, Debug)]\npub enum Event {\n    /// Quit the application\n    Quit,\n    /// An error occurred\n    Error(String),\n    /// Close the application\n    Close,\n    /// Timer tick event\n    Tick,\n    /// Render the UI\n    Render,\n    /// A key was pressed\n    Key(KeyEvent),\n    /// Text was pasted (bracketed paste)\n    Paste(String),\n    /// A mouse event occurred\n    Mouse(MouseEvent),\n    /// Preview content is ready to display\n    PreviewReady,\n    /// Invalid input received\n    InvalidInput,\n    /// An action was triggered\n    Action(Action),\n    /// Append items to the pool\n    AppendItems(Vec<Arc<dyn crate::SkimItem>>),\n    /// Clear all items\n    ClearItems,\n    /// Clear the screen\n    Clear,\n    /// Heartbeat event\n    Heartbeat,\n    /// Run the preview command\n    RunPreview,\n    /// Redraw the screen\n    Redraw,\n    /// Reload with a new command\n    Reload(String),\n    /// Terminal was resized to (columns, rows)\n    Resize(u16, u16),\n}\n\n/// Actions that can be performed in skim\n#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]\npub enum Action {\n    /// Abort and exit with error\n    Abort,\n    /// Accept selection and exit with optional key\n    Accept(Option<String>),\n    /// Add a character to the query\n    AddChar(char),\n    /// Append to selection and select\n    AppendAndSelect,\n    /// Move cursor backward one character\n    BackwardChar,\n    /// Delete character before cursor\n    BackwardDeleteChar,\n    /// Delete character before cursor or exit if the query is empty\n    BackwardDeleteCharEof,\n    /// Delete word before cursor\n    BackwardKillWord,\n    /// Move cursor backward one word\n    BackwardWord,\n    /// Move cursor to beginning of line\n    BeginningOfLine,\n    /// Cancel current operation\n    Cancel,\n    /// Clear the screen\n    ClearScreen,\n    /// Delete character under cursor\n    DeleteChar,\n    /// Delete character or exit if empty\n    DeleteCharEof,\n    /// Deselect all items\n    DeselectAll,\n    /// Move selection down by N items\n    Down(u16),\n    /// Move cursor to end of line\n    EndOfLine,\n    /// Execute a command\n    Execute(String),\n    /// Execute a command silently\n    ExecuteSilent(String),\n    /// Jump to first item in list\n    First,\n    /// Move cursor forward one character\n    ForwardChar,\n    /// Move cursor forward one word\n    ForwardWord,\n    /// Execute action if query is empty\n    IfQueryEmpty(String, Option<String>),\n    /// Execute action if query is not empty\n    IfQueryNotEmpty(String, Option<String>),\n    /// Execute action if no items match\n    IfNonMatched(String, Option<String>),\n    /// Ignore the action\n    Ignore,\n    /// Delete from cursor to end of line\n    KillLine,\n    /// Delete word after cursor\n    KillWord,\n    /// Jump to last item in list\n    Last,\n    /// Move to next history entry\n    NextHistory,\n    /// Scroll down by half a page\n    HalfPageDown(i32),\n    /// Scroll up by half a page\n    HalfPageUp(i32),\n    /// Scroll down by a page\n    PageDown(i32),\n    /// Scroll up by a page\n    PageUp(i32),\n    /// Scroll preview up\n    PreviewUp(i32),\n    /// Scroll preview down\n    PreviewDown(i32),\n    /// Scroll preview left\n    PreviewLeft(i32),\n    /// Scroll preview right\n    PreviewRight(i32),\n    /// Scroll preview up by a page\n    PreviewPageUp(i32),\n    /// Scroll preview down by a page\n    PreviewPageDown(i32),\n    /// Move to previous history entry\n    PreviousHistory,\n    /// Redraw the screen\n    Redraw,\n    /// Refresh the command\n    RefreshCmd,\n    /// Refresh the preview\n    RefreshPreview,\n    /// Restart the matcher\n    RestartMatcher,\n    /// Reload with optional new command\n    Reload(Option<String>),\n    /// Rotate through matching modes\n    RotateMode,\n    /// Scroll item list left\n    ScrollLeft(i32),\n    /// Scroll item list right\n    ScrollRight(i32),\n    /// Select all items\n    SelectAll,\n    /// Select a specific row\n    SelectRow(usize),\n    /// Select current item\n    Select,\n    /// Set the header (or disable it on an empty value)\n    SetHeader(Option<String>),\n    /// Set the preview cmd and rerun preview\n    SetPreviewCmd(String),\n    /// Set the query to the expanded value\n    SetQuery(String),\n    /// Toggle selection of current item\n    Toggle,\n    /// Toggle selection of all items\n    ToggleAll,\n    /// Toggle and move in\n    ToggleIn,\n    /// Toggle interactive mode\n    ToggleInteractive,\n    /// Toggle and move out\n    ToggleOut,\n    /// Toggle preview visibility\n    TogglePreview,\n    /// Toggle preview line wrapping\n    TogglePreviewWrap,\n    /// Toggle sorting\n    ToggleSort,\n    /// Jump to first item in list (alias for First)\n    Top,\n    /// Discard line (unix-style)\n    UnixLineDiscard,\n    /// Delete word backward (unix-style)\n    UnixWordRubout,\n    /// Move selection up by N items\n    Up(u16),\n    /// Yank (paste)\n    Yank,\n    /// Custom action from lib\n    #[debug(\"custom\")]\n    #[eq(skip)]\n    #[partial_eq(skip)]\n    #[serde(skip)]\n    Custom(ActionCallback),\n}\n\n/// Parses an action string into an Action enum\n///\n/// # Panics\n///\n/// Panics if an `if-*` action is specified without its required argument.\n#[allow(clippy::too_many_lines)]\n#[must_use]\npub fn parse_action(raw_action: &str) -> Option<Action> {\n    let parts = raw_action.split_once([':', '(', ')']);\n    let action;\n    let mut arg = None;\n    match parts {\n        None => action = raw_action,\n        Some((act, \"\")) => action = act,\n        Some((act, a)) => {\n            action = act;\n            arg = Some(a.trim_end_matches(')').to_string());\n        }\n    }\n    debug!(\"parse_action: action={action}, arg={arg:?}\");\n\n    // Parse `if` chains\n    if action.starts_with(\"if-\") {\n        let then_arg;\n        let mut otherwise_arg = None;\n\n        let if_arg = arg.unwrap_or_else(|| panic!(\"no arg specified for event {action}\"));\n        if if_arg.contains('+') {\n            let split = if_arg.split_once('+');\n            match split {\n                Some((a, \"\")) => {\n                    then_arg = a.to_string();\n                }\n                Some((a, b)) => {\n                    then_arg = a.to_string();\n                    otherwise_arg = Some(b.to_string());\n                }\n                None => unreachable!(),\n            }\n        } else {\n            then_arg = if_arg.clone();\n        }\n        match action {\n            \"if-non-matched\" => Some(Action::IfNonMatched(then_arg, otherwise_arg)),\n            \"if-query-empty\" => Some(Action::IfQueryEmpty(then_arg, otherwise_arg)),\n            \"if-query-not-empty\" => Some(Action::IfQueryNotEmpty(then_arg, otherwise_arg)),\n            _ => None,\n        }\n    } else {\n        exhaustive_match! {\n            action => Option<Action>;\n            {\n                \"abort\" => Some(Abort),\n                \"accept\" => Some(Accept(arg)),\n                \"add-char\" => Some(AddChar(\n                    arg.unwrap_or_default()\n                        .chars()\n                        .next()\n                        .expect(\"add-char should have an argument\"),\n                )),\n                \"append-and-select\" => Some(AppendAndSelect),\n                \"backward-char\" => Some(BackwardChar),\n                \"backward-delete-char\" => Some(BackwardDeleteChar),\n                \"backward-delete-char/eof\" => Some(BackwardDeleteCharEof),\n                \"backward-kill-word\" => Some(BackwardKillWord),\n                \"backward-word\" => Some(BackwardWord),\n                \"beginning-of-line\" => Some(BeginningOfLine),\n                \"cancel\" => Some(Cancel),\n                \"clear-screen\" => Some(ClearScreen),\n                \"delete-char\" => Some(DeleteChar),\n                \"delete-char/eof\" => Some(DeleteCharEof),\n                \"deselect-all\" => Some(DeselectAll),\n                \"down\" => Some(Down(arg.and_then(|s| s.parse().ok()).unwrap_or(1))),\n                \"end-of-line\" => Some(EndOfLine),\n                \"execute\" => Some(Execute(arg.expect(\"execute event should have argument\"))),\n                \"execute-silent\" => Some(ExecuteSilent(arg.expect(\"execute-silent event should have argument\"))),\n                \"first\" => Some(First),\n                \"forward-char\" => Some(ForwardChar),\n                \"forward-word\" => Some(ForwardWord),\n                \"ignore\" => Some(Ignore),\n                \"kill-line\" => Some(KillLine),\n                \"kill-word\" => Some(KillWord),\n                \"last\" => Some(Last),\n                \"next-history\" => Some(NextHistory),\n                \"half-page-down\" => Some(HalfPageDown(arg.and_then(|s| s.parse().ok()).unwrap_or(1))),\n                \"half-page-up\" => Some(HalfPageUp(arg.and_then(|s| s.parse().ok()).unwrap_or(1))),\n                \"page-down\" => Some(PageDown(arg.and_then(|s| s.parse().ok()).unwrap_or(1))),\n                \"page-up\" => Some(PageUp(arg.and_then(|s| s.parse().ok()).unwrap_or(1))),\n                \"preview-up\" => Some(PreviewUp(arg.and_then(|s| s.parse().ok()).unwrap_or(1))),\n                \"preview-down\" => Some(PreviewDown(arg.and_then(|s| s.parse().ok()).unwrap_or(1))),\n                \"preview-left\" => Some(PreviewLeft(arg.and_then(|s| s.parse().ok()).unwrap_or(1))),\n                \"preview-right\" => Some(PreviewRight(arg.and_then(|s| s.parse().ok()).unwrap_or(1))),\n                \"preview-page-up\" => Some(PreviewPageUp(arg.and_then(|s| s.parse().ok()).unwrap_or(1))),\n                \"preview-page-down\" => Some(PreviewPageDown(arg.and_then(|s| s.parse().ok()).unwrap_or(1))),\n                \"previous-history\" => Some(PreviousHistory),\n                \"redraw\" => Some(Redraw),\n                \"refresh-cmd\" => Some(RefreshCmd),\n                \"refresh-preview\" => Some(RefreshPreview),\n                \"restart-matcher\" => Some(RestartMatcher),\n                \"reload\" => Some(Reload(arg.clone())),\n                \"rotate-mode\" => Some(RotateMode),\n                \"scroll-left\" => Some(ScrollLeft(arg.and_then(|s| s.parse().ok()).unwrap_or(1))),\n                \"scroll-right\" => Some(ScrollRight(arg.and_then(|s| s.parse().ok()).unwrap_or(1))),\n                \"select\" => Some(Select),\n                \"select-all\" => Some(SelectAll),\n                \"select-row\" => Some(SelectRow(arg.and_then(|s| s.parse().ok()).unwrap_or_default())),\n                \"set-header\" => Some(SetHeader(arg)),\n                \"set-preview-cmd\" => Some(SetPreviewCmd(arg.expect(\"set-preview-cmd action needs a value\"))),\n                \"set-query\" => Some(SetQuery(arg.expect(\"set-query action needs a value\"))),\n                \"toggle\" => Some(Toggle),\n                \"toggle-all\" => Some(ToggleAll),\n                \"toggle-in\" => Some(ToggleIn),\n                \"toggle-interactive\" => Some(ToggleInteractive),\n                \"toggle-out\" => Some(ToggleOut),\n                \"toggle-preview\" => Some(TogglePreview),\n                \"toggle-preview-wrap\" => Some(TogglePreviewWrap),\n                \"toggle-sort\" => Some(ToggleSort),\n                \"top\" => Some(Top),\n                \"unix-line-discard\" => Some(UnixLineDiscard),\n                \"unix-word-rubout\" => Some(UnixWordRubout),\n                \"up\" => Some(Up(arg.and_then(|s| s.parse().ok()).unwrap_or(1))),\n                \"yank\" => Some(Yank),\n                \"unreachable-if-non-matched\" => Some(IfNonMatched(Default::default(), None)),\n                \"unreachable-if-query-empty\" => Some(IfQueryEmpty(Default::default(), None)),\n                \"unreachable-if-query-not-empty\" => Some(IfQueryNotEmpty(Default::default(), None)),\n                \"custom-do-not-use-from-cli\" => Some(Custom(ActionCallback::new_sync(|_: &mut crate::tui::App| { Ok(Vec::new()) }))),\n            }\n            default _ => None\n        }\n    }\n}\n"
  },
  {
    "path": "src/tui/header.rs",
    "content": "//! Header display widget for skim's TUI.\n//!\n//! This module provides the header widget that displays static text above the item list.\nuse crate::DisplayContext;\nuse crate::SkimItem;\nuse crate::SkimOptions;\nuse crate::theme::ColorTheme;\nuse crate::tui::BorderType;\nuse crate::tui::options::TuiLayout;\nuse crate::tui::util::char_display_width;\nuse crate::tui::util::style_line;\nuse crate::tui::util::style_text;\nuse crate::tui::widget::{SkimRender, SkimWidget};\n\nuse ansi_to_tui::IntoText;\nuse ratatui::buffer::Buffer;\nuse ratatui::layout::Rect;\nuse ratatui::text::Text;\nuse ratatui::widgets::Widget;\nuse ratatui::widgets::{Block, Borders, Paragraph};\nuse std::cmp::max;\nuse std::sync::Arc;\n\n/// Header widget for displaying static text above the item list\n// The field named `header` in `Header` is intentional — it holds the static\n// header string that this widget displays. Renaming it would reduce clarity.\n#[allow(clippy::struct_field_names)]\n#[derive(Clone, Default)]\npub struct Header {\n    /// The static header string (from --header option), with expanded tabstop\n    pub header: String,\n    /// Dynamic header lines from input (from --header-lines option)\n    pub header_lines: Vec<Arc<dyn SkimItem>>,\n    /// Fixed number of rows reserved for dynamic header lines (`--header-lines`).\n    /// This is set once from the option and never changes, so that the layout\n    /// height is stable before the items actually arrive.\n    header_lines_count: u16,\n    /// The number of spaces to show before the header\n    indent_size: u16,\n    theme: Arc<ColorTheme>,\n    /// Border type, if borders are enabled\n    pub border: Option<BorderType>,\n    /// Whether to reverse the order of `header_lines` (for default/bottom-to-top layout)\n    reverse_lines: bool,\n    /// Reverse layout\n    reverse: bool,\n}\n\nimpl Header {\n    /// Sets the color theme for the header\n    #[must_use]\n    pub fn theme(mut self, theme: Arc<ColorTheme>) -> Self {\n        self.theme = theme;\n        self\n    }\n    /// Returns the total height (in rows) reserved for this header widget.\n    ///\n    /// This value is stable at construction time: it is derived purely from\n    /// `options.header` (static text) and `options.header_lines` (reserved-item\n    /// count), so the layout does not shift as items arrive at runtime.\n    #[must_use]\n    pub fn height(&self) -> u16 {\n        let static_lines = if self.header.is_empty() {\n            0\n        } else {\n            u16::try_from(self.header.lines().count()).unwrap_or(u16::MAX)\n        };\n        static_lines + self.header_lines_count\n    }\n\n    /// Sets the dynamic header lines from input (--header-lines)\n    pub fn set_header_lines(&mut self, items: Vec<Arc<dyn SkimItem>>) {\n        self.header_lines = items;\n        if self.reverse_lines {\n            self.header_lines.reverse();\n        }\n    }\n    fn header_text<'a>(&self) -> Text<'a> {\n        let mut res = self.header.into_text().unwrap(); //.unwrap_or(Text::from(self.header.clone()));\n        style_text(&mut res, self.theme.header);\n        res\n    }\n}\n\n/// Expands tab characters to spaces based on tabstop width and current position\nfn apply_tabstop(text: &str, tabstop: usize) -> String {\n    let mut result = String::new();\n    let mut current_width = 0;\n\n    for ch in text.chars() {\n        if ch == '\\t' {\n            let tab_width = tabstop - (current_width % tabstop);\n            result.push_str(&\" \".repeat(tab_width));\n            current_width += tab_width;\n        } else {\n            result.push(ch);\n            current_width += char_display_width(ch);\n        }\n    }\n\n    result\n}\n\nimpl SkimWidget for Header {\n    fn from_options(options: &SkimOptions, theme: Arc<ColorTheme>) -> Self {\n        let tabstop = max(1, options.tabstop);\n        let header = options.header.clone().unwrap_or_default();\n\n        // Expand tabs once during initialization\n        let expanded_header = apply_tabstop(&header, tabstop);\n\n        // In default layout (bottom-to-top), header_lines should be reversed\n        // to match the visual flow of the item list\n        let reverse_lines = options.layout == TuiLayout::Default;\n\n        Self {\n            header: expanded_header,\n            header_lines: Vec::new(),\n            header_lines_count: options\n                .header_lines\n                .try_into()\n                .expect(\"header_lines count overflows u16\"),\n            indent_size: (options.selector_icon.chars().count() + options.multi_select_icon.chars().count())\n                .try_into()\n                .expect(\"Failed to fit selector lens into an u16\"),\n            theme,\n            border: options.border,\n            reverse_lines,\n            reverse: options.layout == TuiLayout::Reverse,\n        }\n    }\n\n    fn render(&mut self, area: Rect, buf: &mut Buffer) -> SkimRender {\n        let block = if let Some(border_type) = self.border {\n            Block::default()\n                .borders(Borders::ALL)\n                .border_type(border_type.into())\n                .border_style(self.theme.border)\n        } else {\n            Block::default()\n        }\n        .padding(ratatui::widgets::Padding::left(self.indent_size));\n\n        let content_height = if self.header.is_empty() {\n            0\n        } else {\n            self.header_text().lines.len()\n        } + self.header_lines.len();\n\n        let container_height = if self.border.is_some() {\n            area.height - 2\n        } else {\n            area.height\n        };\n\n        // Combine static header with dynamic header_lines\n        let mut combined_header = if self.reverse && !self.header.is_empty() {\n            self.header_text()\n        } else {\n            let mut r = Text::default();\n            for _ in 0..(container_height.saturating_sub(content_height.try_into().unwrap())) {\n                r.push_line(\"\");\n            }\n            r\n        };\n\n        let display_context = DisplayContext {\n            base_style: self.theme.header,\n            ..Default::default()\n        };\n\n        for item in &self.header_lines {\n            let mut line = item.display(display_context.clone());\n            style_line(&mut line, self.theme.header);\n            combined_header.push_line(line);\n        }\n\n        // Add static header (from --header)\n        if !self.reverse && !self.header.is_empty() {\n            combined_header += self.header_text();\n        }\n\n        Paragraph::new(combined_header)\n            .style(self.theme.header)\n            .block(block)\n            .render(area, buf);\n\n        SkimRender::default()\n    }\n}\n"
  },
  {
    "path": "src/tui/input.rs",
    "content": "use std::fmt::Write as _;\nuse std::ops::{Deref, DerefMut};\nuse std::time::Instant;\n\nuse ansi_to_tui::IntoText;\nuse ratatui::{prelude::*, widgets::Widget};\nuse unicode_display_width::width as display_width;\n\nuse crate::helper::item::strip_ansi;\nuse crate::tui::BorderType;\nuse crate::tui::options::TuiLayout;\nuse crate::tui::statusline::InfoDisplay;\nuse crate::tui::util::style_line;\nuse crate::tui::widget::{SkimRender, SkimWidget};\nuse crate::{SkimOptions, theme::ColorTheme};\nuse std::sync::Arc;\n\nconst SPINNER_DURATION: u32 = 200;\nconst SPINNERS_UNICODE: [char; 10] = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];\n\n/// Status information to display in the input widget's title\n#[derive(Clone, Default)]\npub struct StatusInfo {\n    /// Total number of items\n    pub total: usize,\n    /// Number of matched items\n    pub matched: usize,\n    /// Number of processed items\n    pub processed: usize,\n    /// Whether the spinner should be shown (controlled by App with debouncing)\n    pub show_spinner: bool,\n    /// Current matcher mode (e.g., \"RE\" for regex)\n    pub matcher_mode: String,\n    /// Whether multi-selection mode is enabled\n    pub multi_selection: bool,\n    /// Number of selected items\n    pub selected: usize,\n    /// Index of the current item\n    pub current_item_idx: usize,\n    /// Horizontal scroll offset\n    pub hscroll_offset: i64,\n    /// Start time for calculating spinner animation\n    pub start: Option<Instant>,\n}\n\nimpl StatusInfo {\n    /// Build the left-aligned title string (spinner, matched/total, mode, progress, selection)\n    /// Used for Default info display mode (separate line)\n    pub fn left_title(&self) -> String {\n        let mut parts = String::new();\n\n        // Spinner\n        if self.show_spinner\n            && let Some(start) = self.start\n        {\n            let spinner_elapsed_ms = start.elapsed().as_millis();\n            let index =\n                ((spinner_elapsed_ms / u128::from(SPINNER_DURATION)) % (SPINNERS_UNICODE.len() as u128)) as usize;\n            parts.push(SPINNERS_UNICODE[index]);\n            parts.push(' ');\n        } else {\n            parts.push_str(\"  \");\n        }\n\n        // Matched/total\n        let _ = write!(parts, \"{}/{}\", self.matched, self.total);\n\n        // Matcher mode\n        if !self.matcher_mode.is_empty() {\n            let _ = write!(parts, \"/{}\", &self.matcher_mode);\n        }\n\n        // Progress percentage\n        if self.show_spinner && self.total > 0 && self.processed != self.total {\n            let pct = self.processed.saturating_mul(100) / self.total;\n            let _ = write!(parts, \" ({pct}%)\");\n        }\n\n        // Selection count\n        if self.multi_selection && self.selected > 0 {\n            let _ = write!(parts, \" [{}]\", self.selected);\n        }\n\n        parts\n    }\n\n    /// Get the inline separator character: spinner when active, '<' otherwise\n    /// Used for Inline info display mode\n    pub fn inline_separator(&self) -> char {\n        if self.show_spinner\n            && let Some(start) = self.start\n        {\n            let spinner_elapsed_ms = start.elapsed().as_millis();\n            let index =\n                ((spinner_elapsed_ms / u128::from(SPINNER_DURATION)) % (SPINNERS_UNICODE.len() as u128)) as usize;\n            SPINNERS_UNICODE[index]\n        } else {\n            '<'\n        }\n    }\n\n    /// Build the inline status string (matched/total, mode, progress, selection)\n    /// Used for Inline info display mode - does NOT include spinner prefix\n    pub fn inline_status(&self) -> String {\n        let mut parts = String::new();\n\n        // Matched/total\n        let _ = write!(parts, \"{}/{}\", self.matched, self.total);\n\n        // Matcher mode\n        if !self.matcher_mode.is_empty() {\n            let _ = write!(parts, \"/{}\", &self.matcher_mode);\n        }\n\n        // Progress percentage\n        if self.show_spinner && self.total > 0 {\n            let pct = self.processed.saturating_mul(100) / self.total;\n            let _ = write!(parts, \" ({pct}%)\");\n        }\n\n        // Selection count\n        if self.multi_selection && self.selected > 0 {\n            let _ = write!(parts, \" [{}]\", self.selected);\n        }\n\n        parts\n    }\n\n    /// Build the right-aligned title string (current index / hscroll)\n    pub fn right_title(&self) -> String {\n        format!(\"{}/{}\", self.current_item_idx, self.hscroll_offset)\n    }\n}\n\npub struct Input {\n    pub prompt: String,\n    /// see `alternate_value`\n    alternate_prompt: String,\n    pub value: String,\n    /// cmd query when in normal mode, query when in interactive mode\n    alternate_value: String,\n    pub cursor_pos: u16,\n    pub alternate_cursor_pos: u16,\n    pub theme: Arc<ColorTheme>,\n    /// Border type, if borders are enabled\n    pub border: Option<BorderType>,\n    /// Status information to display as the input's title\n    pub status_info: Option<StatusInfo>,\n    /// How to display the info/status (default, inline, or hidden)\n    pub info_display: InfoDisplay,\n    /// Whether layout is reversed (status goes below input instead of above)\n    pub reverse: bool,\n}\n\nimpl Default for Input {\n    fn default() -> Self {\n        Self {\n            prompt: String::from(\">\"),\n            alternate_prompt: String::from(\"c>\"),\n            value: String::default(),\n            alternate_value: String::default(),\n            cursor_pos: 0,\n            alternate_cursor_pos: 0,\n            theme: Arc::new(ColorTheme::default()),\n            border: None,\n            status_info: None,\n            info_display: InfoDisplay::Default,\n            reverse: false,\n        }\n    }\n}\n\nimpl Input {\n    pub fn insert(&mut self, c: char) {\n        self.value.insert(self.cursor_pos.into(), c);\n        // unwrap: len_utf8 < 4\n        self.move_cursor(c.len_utf8().try_into().unwrap());\n    }\n    pub fn insert_str(&mut self, s: &str) {\n        self.value.insert_str(self.cursor_pos as usize, s);\n        self.move_cursor(\n            s.chars()\n                .count()\n                .try_into()\n                .expect(\"Failed to fit inserted str len into an i32\"),\n        );\n    }\n    fn nchars(&self) -> usize {\n        self.value.chars().count()\n    }\n    pub fn delete(&mut self, offset: i32) -> Option<char> {\n        if self.value.is_empty() {\n            return None;\n        }\n        let new_pos = i32::from(self.cursor_pos) + offset;\n        if new_pos < 0 || usize::try_from(new_pos).map_or(true, |p| p >= self.value.len()) {\n            return None;\n        }\n        let pos = self.value.floor_char_boundary(new_pos.unsigned_abs() as usize);\n        let ch = self.value.remove(pos);\n        // Only move cursor if deleting backwards\n        if offset < 0 {\n            self.move_cursor(-1);\n        }\n        Some(ch)\n    }\n    pub fn move_cursor(&mut self, offset: i32) {\n        if offset == 0 {\n            return;\n        }\n        if offset < 0 {\n            let new_pos = (i32::from(self.cursor_pos) + offset).max(0).unsigned_abs() as usize;\n            self.move_cursor_to(u16::try_from(self.value.floor_char_boundary(new_pos)).unwrap_or(u16::MAX));\n        } else {\n            let new_pos = (i32::from(self.cursor_pos) + offset).unsigned_abs() as usize;\n            self.move_cursor_to(u16::try_from(self.value.ceil_char_boundary(new_pos)).unwrap_or(u16::MAX));\n        }\n    }\n    pub fn move_cursor_to(&mut self, pos: u16) {\n        if self.value.is_char_boundary(pos as usize) {\n            self.cursor_pos = u16::clamp(pos, 0, u16::try_from(self.value.len()).unwrap_or(u16::MAX));\n        } else {\n            warn!(\"Invalid cursor pos\");\n        }\n    }\n    pub fn move_to_end(&mut self) {\n        self.move_cursor_to(\n            self.value\n                .len()\n                .try_into()\n                .expect(\"Failed to fit input len into an u16\"),\n        );\n    }\n\n    /// Check if a character is a word character (alphanumeric only)\n    fn is_word_char(ch: char) -> bool {\n        ch.is_alphanumeric()\n    }\n\n    /// Find the position of the end of the next word (alphanumeric boundaries for deletion)\n    fn find_next_word_end(&self, start_pos: usize) -> usize {\n        let mut pos = start_pos;\n\n        // Skip any non-word characters\n        while pos < self.nchars() {\n            let ch = self.value.chars().nth(pos).unwrap();\n            if Self::is_word_char(ch) {\n                break;\n            }\n            pos += 1;\n        }\n\n        // Skip to the end of the word\n        while pos < self.nchars() {\n            let ch = self.value.chars().nth(pos).unwrap();\n            if !Self::is_word_char(ch) {\n                break;\n            }\n            pos += 1;\n        }\n\n        pos\n    }\n\n    /// Find the end of compound word (whitespace boundaries for cursor movement)\n    fn find_compound_word_end(&self, start_pos: usize) -> usize {\n        let mut pos = start_pos;\n\n        // Skip any whitespace\n        while pos < self.nchars() {\n            let ch = self.value.chars().nth(pos).unwrap();\n            if !ch.is_whitespace() {\n                break;\n            }\n            pos += 1;\n        }\n\n        // Skip to the end of the non-whitespace sequence (includes punctuation)\n        while pos < self.nchars() {\n            let ch = self.value.chars().nth(pos).unwrap();\n            if ch.is_whitespace() {\n                break;\n            }\n            pos += 1;\n        }\n\n        pos\n    }\n\n    /// Find the position of the start of the previous word (alphanumeric word boundaries)\n    fn find_prev_word_start(&self, start_pos: usize) -> usize {\n        if start_pos == 0 {\n            return 0;\n        }\n\n        let mut pos = start_pos;\n\n        // Move back at least one position\n        pos = pos.saturating_sub(1);\n\n        // Skip any non-word characters\n        while pos > 0 && !Self::is_word_char(self.value.chars().nth(pos).unwrap()) {\n            pos -= 1;\n        }\n\n        // Skip to the beginning of the word\n        while pos > 0 && Self::is_word_char(self.value.chars().nth(pos - 1).unwrap()) {\n            pos -= 1;\n        }\n\n        pos\n    }\n\n    /// Find the position to delete backward to (stops at non-word characters)\n    fn find_delete_backward_pos(&self, start_pos: usize) -> usize {\n        if start_pos == 0 {\n            return 0;\n        }\n\n        let mut pos = start_pos;\n\n        // Skip any non-word characters (whitespace, punctuation, etc.)\n        while pos > 0 {\n            let ch = self.value.chars().nth(pos - 1).unwrap();\n            if Self::is_word_char(ch) {\n                break;\n            }\n            pos -= 1;\n        }\n\n        // Skip back through word characters\n        while pos > 0 {\n            let ch = self.value.chars().nth(pos - 1).unwrap();\n            if !Self::is_word_char(ch) {\n                break;\n            }\n            pos -= 1;\n        }\n\n        pos\n    }\n\n    pub fn delete_backward_word(&mut self) -> String {\n        if self.cursor_pos == 0 {\n            return String::new();\n        }\n        // Delete back by alphanumeric word boundaries (for Alt+Backspace)\n        let start_pos = self.find_delete_backward_pos(self.cursor_pos as usize);\n        let deleted = self.value[start_pos..self.cursor_pos as usize].to_string();\n        self.value = format!(\n            \"{}{}\",\n            &self.value[..start_pos],\n            &self.value[self.cursor_pos as usize..]\n        );\n        self.cursor_pos = u16::try_from(start_pos).unwrap_or(u16::MAX);\n        deleted\n    }\n\n    pub fn delete_backward_to_whitespace(&mut self) -> String {\n        if self.cursor_pos == 0 {\n            return String::new();\n        }\n        // Unix word rubout: delete back to whitespace (for Ctrl+W)\n        let mut pos = self.cursor_pos as usize;\n\n        // Skip any trailing whitespace\n        while pos > 0 && self.value.chars().nth(pos - 1).unwrap_or_default().is_whitespace() {\n            pos -= 1;\n        }\n\n        // Delete back to next whitespace or start\n        while pos > 0 && !self.value.chars().nth(pos - 1).unwrap_or_default().is_whitespace() {\n            pos -= 1;\n        }\n\n        let deleted = self.value[pos..self.cursor_pos as usize].to_string();\n        self.value = format!(\"{}{}\", &self.value[..pos], &self.value[self.cursor_pos as usize..]);\n        self.cursor_pos = u16::try_from(pos).unwrap_or(u16::MAX);\n        deleted\n    }\n\n    pub fn delete_forward_word(&mut self) -> String {\n        if self.cursor_pos as usize >= self.value.len() {\n            return String::new();\n        }\n        let end_pos = self.find_next_word_end(self.cursor_pos as usize);\n        let deleted = self.value[self.cursor_pos as usize..end_pos].to_string();\n        self.value = format!(\"{}{}\", &self.value[..self.cursor_pos as usize], &self.value[end_pos..]);\n        deleted\n    }\n    pub fn move_cursor_forward_word(&mut self) {\n        let new_pos = self.find_compound_word_end(self.cursor_pos as usize);\n        self.cursor_pos = u16::try_from(new_pos).unwrap_or(u16::MAX);\n    }\n\n    pub fn move_cursor_backward_word(&mut self) {\n        let new_pos = self.find_prev_word_start(self.cursor_pos as usize);\n        self.cursor_pos = u16::try_from(new_pos).unwrap_or(u16::MAX);\n    }\n    pub fn delete_to_beginning(&mut self) -> String {\n        let deleted = self.value[..self.cursor_pos as usize].to_string();\n        self.value = self.value[self.cursor_pos as usize..].to_string();\n        self.cursor_pos = 0;\n        deleted\n    }\n    pub fn cursor_pos(&self) -> u16 {\n        (display_width(&self.value[..(self.cursor_pos as usize)]) + display_width(&strip_ansi(&self.prompt).0))\n            .try_into()\n            .expect(\"Failed to fit cursor char into an u16\")\n    }\n    pub fn switch_mode(&mut self) {\n        std::mem::swap(&mut self.prompt, &mut self.alternate_prompt);\n        std::mem::swap(&mut self.value, &mut self.alternate_value);\n        std::mem::swap(&mut self.cursor_pos, &mut self.alternate_cursor_pos);\n    }\n}\n\nimpl SkimWidget for Input {\n    fn from_options(options: &SkimOptions, theme: Arc<ColorTheme>) -> Self {\n        let mut res = Self {\n            theme,\n            border: options.border,\n            info_display: options.info.clone(),\n            reverse: options.layout == TuiLayout::Reverse,\n            ..Default::default()\n        };\n        if options.interactive {\n            res.prompt.clone_from(&options.cmd_prompt);\n            res.alternate_prompt.clone_from(&options.prompt);\n            res.value = options.cmd_query.clone().unwrap_or_default();\n            res.alternate_value = options.query.clone().unwrap_or_default();\n        } else {\n            res.prompt.clone_from(&options.prompt);\n            res.alternate_prompt.clone_from(&options.cmd_prompt);\n            res.value = options.query.clone().unwrap_or_default();\n            res.alternate_value = options.cmd_query.clone().unwrap_or_default();\n        }\n        res.cursor_pos = u16::try_from(res.value.len()).unwrap_or(u16::MAX);\n        res.alternate_cursor_pos = u16::try_from(res.alternate_value.len()).unwrap_or(u16::MAX);\n        res\n    }\n\n    fn render(&mut self, area: Rect, buf: &mut Buffer) -> SkimRender {\n        use ratatui::layout::Alignment;\n        use ratatui::text::{Line, Span};\n        use ratatui::widgets::Paragraph;\n        use ratatui::widgets::{Block, Borders};\n\n        let mut line = self.prompt.into_text().map_or_else(\n            |_| Line::from(self.prompt.as_str()),\n            |t| t.lines.into_iter().next().unwrap_or_default(),\n        );\n        style_line(&mut line, self.theme.prompt);\n        line.push_span(Span::styled(&self.value, self.theme.query));\n\n        let mut block = Block::default();\n\n        // Add borders if enabled\n        if let Some(border_type) = self.border {\n            block = block\n                .borders(Borders::ALL)\n                .border_type(border_type.into())\n                .border_style(self.theme.border);\n        }\n\n        // Handle different info display modes\n        match self.info_display {\n            InfoDisplay::Inline => {\n                // Inline mode: render status on the same line as input\n                // Format: prompt + value + \" \" + separator_char + \" \" + status + padding + right_status\n                // separator_char is spinner when active, '<' otherwise\n                if let Some(ref status) = self.status_info {\n                    let separator = status.inline_separator();\n                    let inline_status = status.inline_status();\n                    let right_status = status.right_title();\n\n                    // Calculate available width for padding\n                    // Format: \" X \" where X is separator (3 chars total)\n                    let prompt_width = display_width(&self.prompt);\n                    let value_width = display_width(&self.value);\n                    let separator_width = 4; // \"  X \" (2xspace + separator + space)\n                    let inline_status_width = display_width(&inline_status);\n                    let right_status_width = display_width(&right_status);\n\n                    let used_width =\n                        prompt_width + value_width + separator_width + inline_status_width + right_status_width;\n                    let available_width = u64::from(area.width);\n                    let padding_width = available_width.saturating_sub(used_width);\n\n                    line.push_span(Span::styled(format!(\"  {separator} \"), self.theme.info));\n                    line.push_span(Span::styled(inline_status, self.theme.info));\n                    line.push_span(Span::raw(\" \".repeat(padding_width.try_into().unwrap())));\n                    line.push_span(Span::styled(right_status, self.theme.info));\n\n                    Paragraph::new(line)\n                        .block(block)\n                        .style(self.theme.normal)\n                        .render(area, buf);\n                } else {\n                    // No status info, just render input\n                    Paragraph::new(line)\n                        .block(block)\n                        .style(self.theme.normal)\n                        .render(area, buf);\n                }\n            }\n            InfoDisplay::Default => {\n                // Default mode: render status as block title (separate line)\n                // In normal layout: status above input (title_top)\n                // In reverse layout: status below input (title_bottom)\n                if let Some(ref status) = self.status_info {\n                    let left_title = status.left_title();\n                    let right_title = status.right_title();\n\n                    if self.reverse {\n                        block = block\n                            .title_bottom(Line::from(left_title).style(self.theme.info).alignment(Alignment::Left))\n                            .title_bottom(\n                                Line::from(right_title)\n                                    .style(self.theme.info)\n                                    .alignment(Alignment::Right),\n                            );\n                    } else {\n                        block = block\n                            .title_top(Line::from(left_title).style(self.theme.info).alignment(Alignment::Left))\n                            .title_top(\n                                Line::from(right_title)\n                                    .style(self.theme.info)\n                                    .alignment(Alignment::Right),\n                            );\n                    }\n                }\n\n                Paragraph::new(line)\n                    .block(block)\n                    .style(self.theme.normal)\n                    .render(area, buf);\n            }\n            InfoDisplay::Hidden => {\n                // Hidden mode: no status displayed\n                Paragraph::new(line)\n                    .block(block)\n                    .style(self.theme.normal)\n                    .render(area, buf);\n            }\n        }\n\n        SkimRender::default()\n    }\n}\n\nimpl Deref for Input {\n    type Target = String;\n\n    fn deref(&self) -> &Self::Target {\n        &self.value\n    }\n}\n\nimpl DerefMut for Input {\n    fn deref_mut(&mut self) -> &mut Self::Target {\n        &mut self.value\n    }\n}\n"
  },
  {
    "path": "src/tui/item_list.rs",
    "content": "use std::{rc::Rc, sync::Arc};\n\nuse indexmap::IndexSet;\nuse ratatui::text::{Line, Span};\nuse ratatui::widgets::{Block, Borders, Clear, List, ListDirection, ListItem, ListState, StatefulWidget, Widget};\nuse regex::Regex;\nuse unicode_display_width::width as display_width;\n\nuse crate::options::feature_flag;\nuse crate::tui::util::char_display_width;\nuse crate::{\n    DisplayContext, MatchRange, Selector, SkimOptions,\n    item::MatchedItem,\n    spinlock::SpinLock,\n    theme::ColorTheme,\n    tui::BorderType,\n    tui::options::TuiLayout,\n    tui::util::wrap_text,\n    tui::widget::{SkimRender, SkimWidget},\n};\n\n/// How to apply processed items to the display list\n#[derive(Default, Clone, Copy)]\npub(crate) enum MergeStrategy {\n    /// Replace the entire item list (full re-match or first result)\n    #[default]\n    Replace,\n    /// Merge into existing list using sorted merge by rank\n    SortedMerge,\n    /// Append to existing list without sorting (for --no-sort)\n    Append,\n}\n\n/// Processed items ready for rendering\npub(crate) struct ProcessedItems {\n    pub(crate) items: Vec<MatchedItem>,\n    pub(crate) merge: MergeStrategy,\n}\n\nimpl Default for ProcessedItems {\n    fn default() -> Self {\n        Self {\n            items: Vec::new(),\n            merge: MergeStrategy::Replace,\n        }\n    }\n}\n\n/// Widget for displaying and managing the list of filtered items\npub struct ItemList {\n    pub(crate) items: Vec<MatchedItem>,\n    pub(crate) selection: IndexSet<MatchedItem>,\n    pub(crate) processed_items: Arc<SpinLock<Option<ProcessedItems>>>,\n    pub(crate) direction: ListDirection,\n    pub(crate) offset: usize,\n    pub(crate) current: usize,\n    pub(crate) height: u16,\n    pub(crate) theme: std::sync::Arc<crate::theme::ColorTheme>,\n    pub(crate) multi_select: bool,\n    reserved: usize,\n    no_hscroll: bool,\n    ellipsis: String,\n    keep_right: bool,\n    skip_to_pattern: Option<Regex>,\n    tabstop: usize,\n    selector: Option<Rc<dyn Selector>>,\n    pre_select_target: usize, // How many items we want to pre-select\n    no_clear_if_empty: bool,\n    interactive: bool,              // Whether we're in interactive mode\n    showing_stale_items: bool,      // True when displaying old items due to no_clear_if_empty\n    pub(crate) manual_hscroll: i32, // Manual horizontal scroll offset for ScrollLeft/ScrollRight\n    selector_icon: String,\n    multi_select_icon: String,\n    cycle: bool,\n    wrap: bool,\n    /// Border type, if borders are enabled\n    pub border: Option<BorderType>,\n    /// When true, prepend each item's match score to its display text\n    show_score: bool,\n    show_index: bool,\n}\n\nimpl Default for ItemList {\n    fn default() -> Self {\n        let processed_items = Arc::new(SpinLock::new(None));\n\n        Self {\n            processed_items,\n            direction: ListDirection::BottomToTop,\n            items: Default::default(),\n            selection: Default::default(),\n            offset: Default::default(),\n            current: Default::default(),\n            height: Default::default(),\n            theme: Arc::new(ColorTheme::default()),\n            multi_select: false,\n            reserved: 0,\n            no_hscroll: false,\n            ellipsis: String::from(\"..\"),\n            keep_right: false,\n            skip_to_pattern: None,\n            tabstop: 8,\n            selector: None,\n            pre_select_target: 0,\n            no_clear_if_empty: false,\n            interactive: false,\n            showing_stale_items: false,\n            manual_hscroll: 0,\n            selector_icon: String::from(\">\"),\n            multi_select_icon: String::from(\">\"),\n            cycle: false,\n            wrap: false,\n            border: None,\n            show_score: false,\n            show_index: false,\n        }\n    }\n}\n\nimpl ItemList {\n    fn cursor(&self) -> usize {\n        self.current\n    }\n\n    /// Returns the count of items for status display.\n    ///\n    /// This may differ from `items.len()` when `no_clear_if_empty` is active and showing stale items\n    #[must_use]\n    pub fn count(&self) -> usize {\n        if self.showing_stale_items { 0 } else { self.items.len() }\n    }\n\n    /// Returns the currently selected item, if any\n    #[must_use]\n    pub fn selected(&self) -> Option<MatchedItem> {\n        self.items.get(self.cursor()).cloned()\n    }\n\n    /// Appends new matched items to the list\n    pub fn append(&mut self, items: &mut Vec<MatchedItem>) {\n        self.items.append(items);\n        self.showing_stale_items = false;\n    }\n\n    /// Calculate the width to skip when using `skip_to_pattern`\n    /// Returns the actual skip width (not accounting for the ellipsis - that's handled in `apply_hscroll`)\n    fn calc_skip_width(&self, text: &str) -> usize {\n        if let Some(ref regex) = self.skip_to_pattern\n            && let Some(mat) = regex.find(text)\n        {\n            return display_width(&text[..mat.start()]).try_into().unwrap();\n        }\n        0\n    }\n\n    /// Calculate horizontal scroll offset for displaying a line with matches\n    /// Returns (shift, `full_width`, `has_left_overflow`, `has_right_overflow`)\n    fn calc_hscroll(\n        &self,\n        text: &str,\n        container_width: usize,\n        match_start_char: usize,\n        match_end_char: usize,\n    ) -> (usize, usize, bool, bool) {\n        // Calculate display width considering tab expansion\n        let full_width = text.chars().fold(0, |acc, ch| {\n            if ch == '\\t' {\n                acc + self.tabstop - (acc % self.tabstop)\n            } else {\n                acc + char_display_width(ch)\n            }\n        });\n\n        // Reserve space for ellipsis\n        let available_width = if container_width >= display_width(&self.ellipsis).try_into().unwrap() {\n            container_width\n        } else {\n            return (0, full_width, false, false);\n        };\n\n        let base_shift = if self.no_hscroll {\n            // No horizontal scroll: always start from beginning\n            0\n        } else if match_start_char == 0 && match_end_char == 0 {\n            // No match to center on (empty query or no matches)\n            let skip_width = self.calc_skip_width(text);\n            if skip_width > 0 {\n                // skip_to_pattern is set and found a match\n                skip_width\n            } else if self.keep_right {\n                // Show the right end\n                full_width.saturating_sub(available_width)\n            } else {\n                // Start from beginning\n                0\n            }\n        } else {\n            // Calculate shift to show the match\n            // Calculate display widths for match positions\n            let mut match_start_width = 0;\n            let mut match_end_width = 0;\n            let mut current_width = 0;\n            let mut found_start = false;\n            let mut found_end = false;\n\n            for (idx, ch) in text.chars().enumerate() {\n                if idx == match_start_char {\n                    match_start_width = current_width;\n                    found_start = true;\n                }\n                if idx == match_end_char {\n                    match_end_width = current_width;\n                    found_end = true;\n                    break;\n                }\n\n                if ch == '\\t' {\n                    current_width += self.tabstop - (current_width % self.tabstop);\n                } else {\n                    current_width += char_display_width(ch);\n                }\n            }\n\n            // If we didn't find the end, use the current width\n            if found_start && !found_end {\n                match_end_width = current_width;\n            }\n\n            let match_width = match_end_width.saturating_sub(match_start_width);\n\n            // Try to center the match, but ensure we show as much of it as possible\n            if match_width >= available_width {\n                // Match itself is too long, show from start of match\n                match_start_width\n            } else {\n                // Center the match in the available space\n                let desired_shift = match_start_width.saturating_sub((available_width - match_width) / 2);\n                // But don't shift more than necessary\n                let max_shift = full_width.saturating_sub(available_width);\n                desired_shift.min(max_shift)\n            }\n        };\n\n        // Apply manual horizontal scroll offset\n        // manual_hscroll can be positive (scroll right) or negative (scroll left)\n        // final_shift = base_shift + manual_hscroll\n        let proposed_shift = (i32::try_from(base_shift).unwrap_or(i32::MAX) + self.manual_hscroll)\n            .max(0)\n            .unsigned_abs() as usize;\n\n        // Only clamp if the text is actually wider than the container\n        // This allows skip_to_pattern to work even for short text\n        let shift = if full_width > available_width {\n            let max_shift = full_width.saturating_sub(available_width);\n            proposed_shift.min(max_shift)\n        } else {\n            proposed_shift\n        };\n\n        let has_left_overflow = shift > 0;\n        let has_right_overflow = shift + available_width < full_width;\n\n        (shift, full_width, has_left_overflow, has_right_overflow)\n    }\n\n    /// Apply horizontal scrolling to a line, adding ellipsis indicators as needed\n    /// Also expands tabs to spaces according to tabstop setting\n    fn apply_hscroll<'a>(\n        &'a self,\n        line: Line<'a>,\n        shift: usize,\n        container_width: usize,\n        full_width: usize,\n    ) -> Line<'a> {\n        let has_left_overflow = shift > 0;\n        let has_right_overflow = shift + container_width < full_width;\n\n        // Reserve space for overflow indicators\n        let left_indicator_width = if has_left_overflow {\n            display_width(&self.ellipsis).try_into().unwrap()\n        } else {\n            0\n        };\n        let right_indicator_width = if has_right_overflow {\n            display_width(&self.ellipsis).try_into().unwrap()\n        } else {\n            0\n        };\n        let content_width = container_width.saturating_sub(left_indicator_width + right_indicator_width);\n\n        // Extract the visible portion of the line while preserving styling\n        let mut result = Line::default();\n\n        // Add left indicator if needed\n        if has_left_overflow {\n            result.push_span(Span::raw(&self.ellipsis));\n        }\n\n        // Process spans to extract only the visible portion while preserving styles\n        let mut current_char_index = 0;\n        let mut current_width = 0;\n        let shift_char_start = self.char_index_at_width(&line, shift);\n        let shift_char_end = self.char_index_at_width(&line, shift + content_width);\n\n        for span in line.spans {\n            let span_text = span.content.as_ref();\n            let span_chars: Vec<char> = span_text.chars().collect();\n\n            let span_start_char = current_char_index;\n            let span_end_char = current_char_index + span_chars.len();\n\n            // Check if this span intersects with our visible range\n            if span_end_char > shift_char_start && span_start_char < shift_char_end {\n                // Calculate which part of this span is visible\n                let visible_start = shift_char_start.saturating_sub(span_start_char);\n\n                let visible_end = if span_end_char > shift_char_end {\n                    shift_char_end - span_start_char\n                } else {\n                    span_chars.len()\n                };\n\n                if visible_start < visible_end && visible_start < span_chars.len() {\n                    let visible_chars: String = span_chars[visible_start..visible_end.min(span_chars.len())]\n                        .iter()\n                        .collect();\n\n                    // Expand tabs to spaces and preserve styling\n                    let processed_chars = if visible_chars.contains('\\t') {\n                        self.expand_tabs(&visible_chars, current_width)\n                    } else {\n                        visible_chars\n                    };\n\n                    if !processed_chars.is_empty() {\n                        result.push_span(Span::styled(processed_chars, span.style));\n                    }\n                }\n            }\n\n            current_char_index += span_chars.len();\n            current_width += usize::try_from(display_width(span_text)).unwrap();\n        }\n\n        // Add right indicator if needed\n        if has_right_overflow {\n            result.push_span(Span::raw(&self.ellipsis));\n        }\n\n        result\n    }\n\n    fn char_index_at_width(&self, line: &Line<'_>, target_width: usize) -> usize {\n        let mut current_width = 0;\n        let mut char_index = 0;\n\n        for span in &line.spans {\n            for ch in span.content.chars() {\n                let ch_width = if ch == '\\t' {\n                    self.tabstop - (current_width % self.tabstop)\n                } else {\n                    char_display_width(ch)\n                };\n\n                if current_width >= target_width {\n                    return char_index;\n                }\n\n                current_width += ch_width;\n                char_index += 1;\n            }\n        }\n\n        char_index\n    }\n\n    fn expand_tabs(&self, text: &str, start_width: usize) -> String {\n        let mut result = String::new();\n        let mut current_width = start_width;\n\n        for ch in text.chars() {\n            if ch == '\\t' {\n                let tab_width = self.tabstop - (current_width % self.tabstop);\n                result.push_str(&\" \".repeat(tab_width));\n                current_width += tab_width;\n            } else {\n                result.push(ch);\n                current_width += char_display_width(ch);\n            }\n        }\n\n        result\n    }\n\n    /// Toggles the selection state of the item at the given index\n    pub fn toggle_at(&mut self, index: usize) {\n        if self.items.is_empty() {\n            return;\n        }\n        let item = &self.items[index];\n        trace!(\"Toggled item {} at index {}\", item.text(), index);\n        toggle_item(&mut self.selection, item);\n        trace!(\n            \"Selection is now {:#?}\",\n            self.selection.iter().map(|item| item.item.text()).collect::<Vec<_>>()\n        );\n    }\n    /// Toggles the selection state of the currently selected item\n    pub fn toggle(&mut self) {\n        self.toggle_at(self.cursor());\n    }\n    /// Toggles the selection state of all items\n    pub fn toggle_all(&mut self) {\n        for item in &self.items {\n            toggle_item(&mut self.selection, item);\n        }\n    }\n\n    /// Add row at cursor to selection\n    pub fn select(&mut self) {\n        debug!(\"{}\", self.cursor());\n        self.select_row(self.cursor());\n    }\n\n    /// Add row to selection\n    pub fn select_row(&mut self, index: usize) {\n        let item = self.items[index].clone();\n        self.selection.insert(item);\n    }\n    /// Selects all items\n    pub fn select_all(&mut self) {\n        for item in self.items.clone() {\n            self.selection.insert(item.clone());\n        }\n    }\n    /// Clears all selections\n    pub fn clear_selection(&mut self) {\n        self.selection.clear();\n    }\n    /// Clears all items from the list\n    pub fn clear(&mut self) {\n        self.items.clear();\n        self.selection.clear();\n        self.current = 0;\n        self.offset = 0;\n        self.showing_stale_items = false;\n    }\n    /// Scrolls the list by the given offset\n    pub fn scroll_by(&mut self, offset: i32) {\n        if self.reserved >= self.items.len() {\n            return;\n        }\n        let reserved = i32::try_from(self.reserved).unwrap_or(i32::MAX);\n        let total = i32::try_from(self.items.len()).unwrap_or(i32::MAX);\n        let mut new = i32::try_from(self.current).unwrap_or(i32::MAX) + offset;\n        if self.cycle {\n            let n = total - reserved;\n            new = reserved + (new + n - reserved) % n;\n        } else {\n            new = new\n                .min(i32::try_from(self.items.len()).unwrap_or(i32::MAX) - 1)\n                .max(i32::try_from(self.reserved).unwrap_or(i32::MAX));\n        }\n        self.current = new.max(0).unsigned_abs() as usize;\n        debug!(\"Scrolled to {}\", self.current);\n        debug!(\"Selection: {:?}\", self.selection);\n    }\n    /// Selects the previous item in the list\n    pub fn select_previous(&mut self) {\n        self.scroll_by(-1);\n    }\n    /// Selects the next item in the list\n    pub fn select_next(&mut self) {\n        self.scroll_by(1);\n    }\n    /// Jump to the first selectable item (respecting reserved header lines)\n    pub fn jump_to_first(&mut self) {\n        if self.items.len() > self.reserved {\n            self.current = self.reserved;\n        }\n    }\n    /// Jump to the last item in the list\n    pub fn jump_to_last(&mut self) {\n        if !self.items.is_empty() {\n            self.current = self.items.len().saturating_sub(1);\n        }\n    }\n}\n\nimpl SkimWidget for ItemList {\n    fn from_options(options: &SkimOptions, theme: Arc<ColorTheme>) -> Self {\n        use crate::helper::selector::DefaultSkimSelector;\n        use crate::util::read_file_lines;\n\n        let skip_to_pattern = options\n            .skip_to_pattern\n            .as_ref()\n            .and_then(|pattern| Regex::new(pattern).ok());\n\n        // Build the selector from options and calculate pre-select target\n        let (selector, pre_select_target) = if options.pre_select_n > 0\n            || !options.pre_select_pat.is_empty()\n            || !options.pre_select_items.is_empty()\n            || options.pre_select_file.is_some()\n            || options.selector.is_some()\n        {\n            if let Some(s) = options.selector.clone() {\n                // For custom selectors, use a very large target (pre-select all matching)\n                (Some(s), usize::MAX)\n            } else {\n                let mut preset_items: Vec<String> = options\n                    .pre_select_items\n                    .split('\\n')\n                    .filter(|s| !s.is_empty())\n                    .map(std::string::ToString::to_string)\n                    .collect();\n\n                if let Some(ref pre_select_file) = options.pre_select_file\n                    && let Ok(file_items) = read_file_lines(pre_select_file)\n                {\n                    preset_items.extend(file_items);\n                }\n\n                let selector = DefaultSkimSelector::default()\n                    .first_n(options.pre_select_n)\n                    .regex(&options.pre_select_pat)\n                    .preset(preset_items.clone());\n\n                // Only use a target for --pre-select-n\n                // For pattern/items, the selector always returns the same matches regardless of timing\n                let target = if options.pre_select_n > 0 {\n                    options.pre_select_n\n                } else {\n                    usize::MAX // No target - keep selecting matching items\n                };\n\n                (Some(Rc::new(selector) as Rc<dyn Selector>), target)\n            }\n        } else {\n            (None, 0)\n        };\n\n        let processed_items = Arc::new(SpinLock::new(None));\n\n        let interactive = options.interactive;\n        let no_clear_if_empty = options.no_clear_if_empty;\n        let multi_select = options.multi;\n\n        // Spawn background processing thread with the appropriate configuration\n        Self {\n            processed_items,\n            reserved: 0, // header_lines are now displayed in the Header widget, not ItemList\n            direction: match options.layout {\n                TuiLayout::Default => ratatui::widgets::ListDirection::BottomToTop,\n                TuiLayout::Reverse | TuiLayout::ReverseList => ratatui::widgets::ListDirection::TopToBottom,\n            },\n            current: 0,\n            theme,\n            multi_select,\n            no_hscroll: options.no_hscroll,\n            ellipsis: options.ellipsis.clone(),\n            keep_right: options.keep_right,\n            skip_to_pattern,\n            tabstop: options.tabstop.max(1),\n            selector,\n            pre_select_target,\n            no_clear_if_empty,\n            interactive,\n            showing_stale_items: false,\n            manual_hscroll: 0,\n            items: Default::default(),\n            selection: Default::default(),\n            offset: Default::default(),\n            height: Default::default(),\n            selector_icon: options.selector_icon.clone(),\n            multi_select_icon: options.multi_select_icon.clone(),\n            cycle: options.cycle,\n            wrap: options.wrap_items,\n            border: options.border,\n            show_score: feature_flag!(options, ShowScore),\n            show_index: feature_flag!(options, ShowIndex),\n        }\n    }\n\n    // The render function handles the full item list rendering pipeline; splitting\n    // it into smaller helpers would require passing many parameters between them.\n    #[allow(clippy::too_many_lines)]\n    fn render(&mut self, area: ratatui::prelude::Rect, buf: &mut ratatui::prelude::Buffer) -> SkimRender {\n        let this = &mut *self;\n\n        // Calculate inner area if borders are enabled\n        let inner_area = if this.border.is_some() {\n            ratatui::layout::Rect {\n                x: area.x + 1,\n                y: area.y + 1,\n                width: area.width.saturating_sub(2),\n                height: area.height.saturating_sub(2),\n            }\n        } else {\n            area\n        };\n\n        this.height = inner_area.height;\n        if this.current < this.offset {\n            this.offset = this.current;\n        } else if this.offset + inner_area.height as usize <= this.current {\n            this.offset = this.current - inner_area.height as usize + 1;\n        }\n        let initial_current = this.selected();\n\n        // Check for pre-processed items from background thread (non-blocking)\n        let items_updated = if let Some(processed) = this.processed_items.lock().take() {\n            debug!(\"Render: Got {} processed items\", processed.items.len());\n\n            // Check if items are empty or blank for no_clear_if_empty handling\n            let items_are_empty_or_blank =\n                processed.items.is_empty() || processed.items.iter().all(|item| item.item.text().trim().is_empty());\n\n            if this.interactive && this.no_clear_if_empty && items_are_empty_or_blank && !this.items.is_empty() {\n                debug!(\n                    \"no_clear_if_empty: keeping {} old items for display (new items are empty/blank)\",\n                    this.items.len()\n                );\n                this.showing_stale_items = true;\n            } else {\n                match processed.merge {\n                    MergeStrategy::Replace => {\n                        this.items = processed.items;\n                    }\n                    MergeStrategy::SortedMerge => {\n                        let existing = std::mem::take(&mut this.items);\n                        this.items = MatchedItem::sorted_merge(existing, processed.items);\n                    }\n                    MergeStrategy::Append => {\n                        this.items.extend(processed.items);\n                    }\n                }\n                this.showing_stale_items = false;\n\n                // Apply pre-selection only when new items arrive and only if we haven't reached target\n                // This runs once per item batch, not on every render\n                if this.multi_select\n                    && let Some(selector) = &this.selector\n                    && this.selection.len() < this.pre_select_target\n                {\n                    debug!(\n                        \"Applying pre-selection to {} items (currently {} selected, target {})\",\n                        this.items.len(),\n                        this.selection.len(),\n                        this.pre_select_target\n                    );\n                    for (index, item) in this.items.iter().enumerate() {\n                        if this.selection.len() >= this.pre_select_target {\n                            break;\n                        }\n                        let should_select = selector.should_select(index, item.item.as_ref());\n                        if should_select {\n                            debug!(\"Pre-selecting item[{}]: '{}'\", index, item.item.text());\n                            this.selection.insert(item.clone());\n                        }\n                    }\n                    debug!(\"Pre-selected {} items total\", this.selection.len());\n                }\n            }\n\n            true\n        } else {\n            false\n        };\n\n        let theme = &this.theme;\n        let selector_icon = &this.selector_icon;\n        let multi_select_icon = &this.multi_select_icon;\n        let wrap = &this.wrap;\n\n        let list = List::new(\n            this.items\n                .iter()\n                .enumerate()\n                .skip(this.offset)\n                .take(inner_area.height as usize)\n                .map(|(idx, item)| {\n                    let is_current = idx == this.current;\n                    let is_selected = this.selection.contains(item);\n\n                    // Reserve 2 characters for cursor indicators (\"> \" or \" >\")\n                    let container_width = (inner_area.width as usize)\n                        .saturating_sub(selector_icon.chars().count() + multi_select_icon.chars().count());\n\n                    // Get item text for hscroll calculation\n                    let item_text = item.item.text();\n\n                    // Calculate match positions for hscroll\n                    let (match_start_char, match_end_char) = match &item.matched_range {\n                        Some(MatchRange::Chars(matched_indices)) => {\n                            if matched_indices.is_empty() {\n                                (0, 0)\n                            } else {\n                                (matched_indices[0], matched_indices[matched_indices.len() - 1] + 1)\n                            }\n                        }\n                        Some(MatchRange::ByteRange(match_start, match_end)) => {\n                            let match_start_char = item_text[..*match_start].chars().count();\n                            let diff = item_text[*match_start..*match_end].chars().count();\n                            (match_start_char, match_start_char + diff)\n                        }\n                        None => (0, 0),\n                    };\n\n                    // Calculate horizontal scroll\n                    let (shift, full_width, _has_left, _has_right) =\n                        this.calc_hscroll(&item_text, container_width, match_start_char, match_end_char);\n\n                    // Get display content from item\n                    // Avoid cloning chars vector - use reference instead\n                    let matches = match &item.matched_range {\n                        Some(MatchRange::ByteRange(start, end)) => crate::Matches::ByteRange(*start, *end),\n                        Some(MatchRange::Chars(chars)) => crate::Matches::CharIndices(chars.clone()),\n                        None => crate::Matches::None,\n                    };\n\n                    let mut display_line = item.item.display(DisplayContext {\n                        score: item.rank.score,\n                        matches,\n                        container_width,\n                        base_style: if is_current { theme.current } else { theme.normal },\n                        matched_style: if is_current { theme.current_match } else { theme.matched },\n                    });\n\n                    if !wrap {\n                        // Apply horizontal scrolling to the display content\n                        display_line = this.apply_hscroll(display_line, shift, container_width, full_width);\n                    }\n\n                    // Prepend cursor indicators\n                    // Pre-allocate capacity to avoid reallocation\n                    let mut spans: Vec<Span> = Vec::with_capacity(3 + display_line.spans.len());\n                    spans.push(Span::styled(\n                        if is_current {\n                            selector_icon.to_owned()\n                        } else {\n                            str::repeat(\" \", selector_icon.chars().count())\n                        },\n                        theme.cursor,\n                    ));\n                    spans.push(Span::styled(\n                        if this.multi_select && is_selected {\n                            multi_select_icon.to_owned()\n                        } else {\n                            str::repeat(\" \", multi_select_icon.chars().count())\n                        },\n                        theme.selected,\n                    ));\n                    // Optionally prepend debug fields\n                    if this.show_score {\n                        let score = item.rank.score;\n                        spans.push(Span::styled(\n                            format!(\"[{score}] \"),\n                            if is_current { theme.current } else { theme.normal },\n                        ));\n                    }\n                    if this.show_index {\n                        let index = item.rank.index;\n                        spans.push(Span::styled(\n                            format!(\"[{index}] \"),\n                            if is_current { theme.current } else { theme.normal },\n                        ));\n                    }\n                    spans.extend(display_line.spans);\n\n                    if *wrap {\n                        wrap_text(ratatui::text::Text::from(Line::from(spans)), inner_area.width.into()).into()\n                    } else {\n                        Line::from(spans).into()\n                    }\n                })\n                .collect::<Vec<ListItem>>(),\n        )\n        .direction(this.direction)\n        .style(this.theme.normal);\n\n        Widget::render(Clear, area, buf);\n\n        // Render border if enabled\n        if let Some(border_type) = this.border {\n            let block = Block::default()\n                .borders(Borders::ALL)\n                .border_type(border_type.into())\n                .border_style(this.theme.border);\n            Widget::render(block, area, buf);\n        }\n\n        StatefulWidget::render(\n            list,\n            inner_area,\n            buf,\n            &mut ListState::default().with_selected(Some(this.current.saturating_sub(this.offset))),\n        );\n        let run_preview = if let Some(curr) = self.selected()\n            && let Some(prev) = initial_current\n        {\n            curr.text() != prev.text()\n        } else {\n            self.selected().is_some() != initial_current.is_some()\n        };\n        SkimRender {\n            items_updated,\n            run_preview,\n        }\n    }\n}\n\nfn toggle_item(sel: &mut IndexSet<MatchedItem>, item: &MatchedItem) {\n    if sel.contains(item) {\n        sel.shift_remove(item);\n    } else {\n        sel.insert(item.clone());\n    }\n}\n"
  },
  {
    "path": "src/tui/layout.rs",
    "content": "//! Layout computation for skim's TUI.\n//!\n//! The layout logic is split into two phases so that the expensive option-\n//! inspection work is done only once (at startup and on option changes) while\n//! the per-frame cost is reduced to a small number of cheap rect splits.\n//!\n//! * [`LayoutTemplate`] — built from [`SkimOptions`] and the static header\n//!   height.  Stores pre-computed constraints and flags; no terminal-size\n//!   dependency.  Stored on [`App`](super::App) and rebuilt only when options\n//!   that affect layout change (e.g. [`TogglePreview`]).\n//!\n//! * [`AppLayout`] — produced by [`LayoutTemplate::apply`] from a concrete\n//!   terminal [`Rect`].  Contains the final widget areas for one render frame.\n//!   Computed in `render()` and cached on `App` so that code between renders\n//!   (e.g. mouse hit-testing) can read it.\n\nuse ratatui::layout::{Constraint, Direction as RatatuiDirection, Layout, Rect};\n\nuse crate::SkimOptions;\nuse crate::tui::options::TuiLayout;\nuse crate::tui::statusline::InfoDisplay;\nuse crate::tui::{Direction, Size};\n\n// ---------------------------------------------------------------------------\n// LayoutTemplate\n// ---------------------------------------------------------------------------\n\n/// Orientation of the preview pane relative to the work area.\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\nenum PreviewPlacement {\n    /// Preview splits the full area horizontally (left / right of everything).\n    Left,\n    Right,\n    /// Preview splits the full area vertically (above / below everything).\n    Up,\n    Down,\n    /// No preview.\n    None,\n}\n\n/// Pre-computed layout descriptor built from [`SkimOptions`].\n///\n/// Contains everything needed to split a concrete [`Rect`] into widget areas,\n/// but contains no coordinates itself — those only appear in [`AppLayout`].\n/// Build with [`LayoutTemplate::from_options`] and store on\n/// [`App`](super::App); call [`LayoutTemplate::apply`] in every render.\n#[derive(Debug, Clone)]\npub struct LayoutTemplate {\n    /// Whether the header widget should be rendered at all.\n    show_header: bool,\n    /// Where the preview pane is placed relative to the rest of the UI.\n    preview_placement: PreviewPlacement,\n    /// Whether the work layout emits slots in reverse order `[input, header,\n    /// list]` instead of the default `[list, header, input]`.\n    work_layout_reversed: bool,\n    /// Pre-built [`Layout`] for carving the preview out of the full area\n    /// (step 1).  `None` when no preview is visible.\n    preview_layout: Option<Layout>,\n    /// Pre-built [`Layout`] for splitting the work area into three slots.\n    ///\n    /// When `work_layout_reversed` is `false` the slots map to\n    /// `[list, header, input]`; when `true` they map to\n    /// `[input, header, list]`.  Applied in step 2 of [`Self::apply`].\n    work_layout: Layout,\n}\n\nimpl LayoutTemplate {\n    /// Build a [`LayoutTemplate`] from [`SkimOptions`] and the static header\n    /// height (content rows only, excluding any border rows).\n    ///\n    /// `header_height` should come from\n    /// [`Header::height()`](super::header::Header::height), which returns a\n    /// value fixed at construction time from `options.header_lines` and the\n    /// line-count of `options.header`.\n    #[must_use]\n    pub fn from_options(options: &SkimOptions, header_height: u16) -> Self {\n        let has_border = options.border.is_some();\n\n        // Rows consumed by the input widget.\n        let input_rows: u16 = if has_border {\n            3 // 1 content + 2 border rows\n        } else {\n            1 + u16::from(options.info == InfoDisplay::Default)\n        };\n\n        // Rows consumed by the header widget.\n        let show_header = options.header.is_some() || options.header_lines > 0;\n        let header_rows: u16 = if show_header {\n            if has_border { header_height + 2 } else { header_height }\n        } else {\n            0\n        };\n\n        // Preview placement and layout.\n        let preview_visible =\n            (options.preview.is_some() || options.preview_fn.is_some()) && !options.preview_window.hidden;\n\n        let (preview_placement, preview_layout) = if preview_visible {\n            let pc = size_to_constraint(options.preview_window.size);\n            let placement = match options.preview_window.direction {\n                Direction::Left => PreviewPlacement::Left,\n                Direction::Right => PreviewPlacement::Right,\n                Direction::Up => PreviewPlacement::Up,\n                Direction::Down => PreviewPlacement::Down,\n            };\n            let layout = match placement {\n                PreviewPlacement::Left => Layout::new(RatatuiDirection::Horizontal, [pc, Constraint::Fill(1)]),\n                PreviewPlacement::Right => Layout::new(RatatuiDirection::Horizontal, [Constraint::Fill(1), pc]),\n                PreviewPlacement::Up => Layout::new(RatatuiDirection::Vertical, [pc, Constraint::Fill(1)]),\n                PreviewPlacement::Down => Layout::new(RatatuiDirection::Vertical, [Constraint::Fill(1), pc]),\n                PreviewPlacement::None => unreachable!(),\n            };\n            (placement, Some(layout))\n        } else {\n            (PreviewPlacement::None, None)\n        };\n\n        // Work-area layout: single 3-way vertical split into [list, header, input].\n        //\n        // For Default / ReverseList: slots are [list, header, input] top-to-bottom.\n        // For Reverse:               slots are [input, header, list] top-to-bottom.\n        let non_list_rows = input_rows + header_rows;\n        let work_layout_reversed = options.layout == TuiLayout::Reverse;\n        let work_layout = if show_header {\n            match options.layout {\n                TuiLayout::Default | TuiLayout::ReverseList => Layout::vertical([\n                    Constraint::Fill(1),\n                    Constraint::Length(header_rows),\n                    Constraint::Length(input_rows),\n                ]),\n                TuiLayout::Reverse => Layout::vertical([\n                    Constraint::Length(input_rows),\n                    Constraint::Length(header_rows),\n                    Constraint::Fill(1),\n                ]),\n            }\n        } else {\n            match options.layout {\n                TuiLayout::Default | TuiLayout::ReverseList => Layout::vertical([\n                    Constraint::Fill(1),\n                    Constraint::Length(0),\n                    Constraint::Length(non_list_rows),\n                ]),\n                TuiLayout::Reverse => Layout::vertical([\n                    Constraint::Length(non_list_rows),\n                    Constraint::Length(0),\n                    Constraint::Fill(1),\n                ]),\n            }\n        };\n\n        Self {\n            show_header,\n            preview_placement,\n            work_layout_reversed,\n            preview_layout,\n            work_layout,\n        }\n    }\n\n    /// Apply this template to a concrete terminal `area`, producing the\n    /// absolute [`AppLayout`] for one render frame.\n    #[must_use]\n    pub fn apply(&self, area: Rect) -> AppLayout {\n        // ── Step 1: carve out the preview from the full area ─────────────────\n        let (work_area, preview_area): (Rect, Option<Rect>) = match &self.preview_layout {\n            Some(layout) => {\n                let [a, b]: [Rect; 2] = layout.areas(area);\n                match self.preview_placement {\n                    // preview is the first segment for Left / Up\n                    PreviewPlacement::Left | PreviewPlacement::Up => (b, Some(a)),\n                    // preview is the second segment for Right / Down\n                    _ => (a, Some(b)),\n                }\n            }\n            None => (area, None),\n        };\n\n        // ── Step 2: split work_area into list / header / input in one pass ───\n        //\n        // Slots are [list, header, input] when `work_layout_reversed` is false,\n        // or [input, header, list] when true (Reverse layout).\n        let [slot0, slot1, slot2]: [Rect; 3] = self.work_layout.areas(work_area);\n\n        let (list_area, header_slot, input_area) = if self.work_layout_reversed {\n            (slot2, slot1, slot0)\n        } else {\n            (slot0, slot1, slot2)\n        };\n\n        let header_area = if self.show_header { Some(header_slot) } else { None };\n\n        AppLayout {\n            list_area,\n            input_area,\n            header_area,\n            preview_area,\n        }\n    }\n}\n\n// ---------------------------------------------------------------------------\n// AppLayout\n// ---------------------------------------------------------------------------\n\n/// Concrete widget areas for one render frame, produced by\n/// [`LayoutTemplate::apply`].\n///\n/// Cached on [`App`](super::App) after each render so that code between frames\n/// (e.g. mouse hit-testing in `handle_mouse`) can read the last known areas.\n#[derive(Debug, Clone, PartialEq, Eq)]\npub struct AppLayout {\n    /// Area for the item list widget.\n    pub list_area: Rect,\n    /// Area for the input / prompt widget.\n    pub input_area: Rect,\n    /// Area for the header widget (`None` when no header is shown).\n    pub header_area: Option<Rect>,\n    /// Area for the preview pane (`None` when preview is hidden or disabled).\n    pub preview_area: Option<Rect>,\n}\n\nimpl AppLayout {\n    /// Convenience wrapper: build a [`LayoutTemplate`] from `options` and\n    /// `header_height`, then immediately apply it to `area`.\n    ///\n    /// Prefer storing the [`LayoutTemplate`] and calling\n    /// [`LayoutTemplate::apply`] directly when the template can be reused\n    /// across frames.\n    #[must_use]\n    pub fn compute(area: Rect, options: &SkimOptions, header_height: u16) -> Self {\n        LayoutTemplate::from_options(options, header_height).apply(area)\n    }\n}\n\n// ---------------------------------------------------------------------------\n// Helper\n// ---------------------------------------------------------------------------\n\nfn size_to_constraint(size: Size) -> Constraint {\n    match size {\n        Size::Fixed(n) => Constraint::Length(n),\n        Size::Percent(p) => Constraint::Percentage(p),\n    }\n}\n\n// ---------------------------------------------------------------------------\n// Tests\n// ---------------------------------------------------------------------------\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use crate::options::SkimOptionsBuilder;\n    use crate::tui::options::PreviewLayout;\n\n    // A convenient 80×24 terminal area.\n    fn area() -> Rect {\n        Rect::new(0, 0, 80, 24)\n    }\n\n    // ── helpers ────────────────────────────────────────────────────────────\n\n    /// Assert that `rect` covers the full `area` width.\n    fn assert_full_width(rect: Rect, area: Rect, label: &str) {\n        assert_eq!(rect.x, area.x, \"{label}: x\");\n        assert_eq!(rect.width, area.width, \"{label}: width\");\n    }\n\n    /// Assert that two rects are vertically adjacent (b starts immediately\n    /// below a).\n    fn assert_vertically_adjacent(a: Rect, b: Rect, label: &str) {\n        assert_eq!(a.y + a.height, b.y, \"{label}: b should start right after a\");\n    }\n\n    /// Assert that two rects are horizontally adjacent (b starts immediately\n    /// to the right of a).\n    fn assert_horizontally_adjacent(a: Rect, b: Rect, label: &str) {\n        assert_eq!(a.x + a.width, b.x, \"{label}: b should start right after a\");\n    }\n\n    // Compute layout with no reserved-item header lines (header_height = 0\n    // unless the test needs something different).\n    fn compute(options: &SkimOptions) -> AppLayout {\n        AppLayout::compute(area(), options, 0)\n    }\n\n    fn compute_with_header_height(options: &SkimOptions, header_height: u16) -> AppLayout {\n        AppLayout::compute(area(), options, header_height)\n    }\n\n    fn opts() -> SkimOptionsBuilder {\n        SkimOptionsBuilder::default()\n    }\n\n    // ── Default layout ─────────────────────────────────────────────────────\n\n    #[test]\n    fn default_no_preview_no_header() {\n        // Simple case: just list + input (status line takes 1 extra row).\n        let options = opts().build().unwrap();\n        let layout = compute(&options);\n\n        // input = 2 rows (1 prompt + 1 status)\n        assert_eq!(layout.input_area.height, 2);\n        // list fills the rest\n        assert_eq!(layout.list_area.height, 24 - 2);\n        assert!(layout.header_area.is_none());\n        assert!(layout.preview_area.is_none());\n\n        // list is above input\n        assert_full_width(layout.list_area, area(), \"list\");\n        assert_full_width(layout.input_area, area(), \"input\");\n        assert_vertically_adjacent(layout.list_area, layout.input_area, \"list→input\");\n    }\n\n    #[test]\n    fn default_inline_info_no_header() {\n        // InfoDisplay::Inline → input uses only 1 row.\n        let options = opts().inline_info(true).build().unwrap();\n        let layout = compute(&options);\n\n        assert_eq!(layout.input_area.height, 1);\n        assert_eq!(layout.list_area.height, 23);\n    }\n\n    #[test]\n    fn default_hidden_info_no_header() {\n        // InfoDisplay::Hidden → input also uses only 1 row (just the prompt).\n        let options = opts().no_info(true).build().unwrap();\n        let layout = compute(&options);\n\n        assert_eq!(layout.input_area.height, 1);\n        assert_eq!(layout.list_area.height, 23);\n    }\n\n    #[test]\n    fn default_with_header() {\n        let options = opts().header(\"My Header\").build().unwrap();\n        // header_height = 1 line of static header text\n        let layout = compute_with_header_height(&options, 1);\n\n        // non-list = input(2) + header(1) = 3\n        assert_eq!(layout.list_area.height, 21);\n        assert_eq!(layout.header_area.unwrap().height, 1);\n        assert_eq!(layout.input_area.height, 2);\n\n        // Order: list | header | input (top to bottom)\n        let h = layout.header_area.unwrap();\n        assert_vertically_adjacent(layout.list_area, h, \"list→header\");\n        assert_vertically_adjacent(h, layout.input_area, \"header→input\");\n    }\n\n    #[test]\n    fn default_with_multiline_header() {\n        let options = opts().header(\"line1\\nline2\\nline3\").build().unwrap();\n        let layout = compute_with_header_height(&options, 3);\n\n        // non-list = input(2) + header(3) = 5\n        assert_eq!(layout.list_area.height, 19);\n        assert_eq!(layout.header_area.unwrap().height, 3);\n    }\n\n    #[test]\n    fn default_with_header_lines() {\n        // header_lines > 0 also triggers show_header\n        let options = opts().header_lines(2usize).build().unwrap();\n        let layout = compute_with_header_height(&options, 2);\n\n        assert_eq!(layout.header_area.unwrap().height, 2);\n        assert_eq!(layout.list_area.height, 24 - 2 - 2);\n    }\n\n    // ── Template can be reused across different areas ───────────────────────\n\n    #[test]\n    fn template_apply_different_areas() {\n        // A single template should produce consistent proportional results\n        // regardless of which concrete area it is applied to.\n        let options = opts().build().unwrap();\n        let template = LayoutTemplate::from_options(&options, 0);\n\n        let small = template.apply(Rect::new(0, 0, 40, 12));\n        let large = template.apply(Rect::new(0, 0, 160, 48));\n\n        // Both should have input = 2 rows, list = height - 2.\n        assert_eq!(small.input_area.height, 2);\n        assert_eq!(small.list_area.height, 10);\n        assert_eq!(large.input_area.height, 2);\n        assert_eq!(large.list_area.height, 46);\n    }\n\n    // ── Reverse layout ─────────────────────────────────────────────────────\n\n    #[test]\n    fn reverse_no_preview_no_header() {\n        let options = opts().layout(TuiLayout::Reverse).build().unwrap();\n        let layout = compute(&options);\n\n        // input is at the top\n        assert_eq!(layout.input_area.y, 0);\n        assert_eq!(layout.input_area.height, 2);\n        // list is below input\n        assert_eq!(layout.list_area.y, 2);\n        assert_eq!(layout.list_area.height, 22);\n\n        assert_vertically_adjacent(layout.input_area, layout.list_area, \"input→list\");\n    }\n\n    #[test]\n    fn reverse_with_header() {\n        // In Reverse layout: input on top, header below input, then list.\n        let options = opts().layout(TuiLayout::Reverse).header(\"hdr\").build().unwrap();\n        let layout = compute_with_header_height(&options, 2);\n\n        // non-list = input(2) + header(2) = 4\n        assert_eq!(layout.list_area.height, 20);\n        let h = layout.header_area.unwrap();\n        assert_eq!(h.height, 2);\n\n        // Order: input | header | list\n        assert_vertically_adjacent(layout.input_area, h, \"input→header\");\n        assert_vertically_adjacent(h, layout.list_area, \"header→list\");\n    }\n\n    // ── ReverseList layout ─────────────────────────────────────────────────\n\n    #[test]\n    fn reverse_list_no_preview_no_header() {\n        // ReverseList: items top-to-bottom (same split as Default), input at\n        // the bottom.\n        let options = opts().layout(TuiLayout::ReverseList).build().unwrap();\n        let layout = compute(&options);\n\n        assert_eq!(layout.input_area.height, 2);\n        assert_eq!(layout.list_area.height, 22);\n        assert_vertically_adjacent(layout.list_area, layout.input_area, \"list→input\");\n    }\n\n    #[test]\n    fn reverse_list_with_header() {\n        let options = opts().layout(TuiLayout::ReverseList).header(\"hdr\").build().unwrap();\n        let layout = compute_with_header_height(&options, 1);\n\n        // non-list = 2 + 1 = 3\n        assert_eq!(layout.list_area.height, 21);\n        let h = layout.header_area.unwrap();\n        assert_eq!(h.height, 1);\n        // Order: list | header | input\n        assert_vertically_adjacent(layout.list_area, h, \"list→header\");\n        assert_vertically_adjacent(h, layout.input_area, \"header→input\");\n    }\n\n    // ── Preview: Left / Right ───────────────────────────────────────────────\n\n    #[test]\n    fn default_preview_right_50_percent() {\n        let options = opts()\n            .preview(\"cat {}\")\n            .preview_window(PreviewLayout::from(\"right:50%\"))\n            .build()\n            .unwrap();\n        let layout = compute(&options);\n\n        let preview = layout.preview_area.unwrap();\n        // Preview is on the right: work_area = left half, preview = right half.\n        // 50% of 80 = 40.\n        assert_eq!(preview.width, 40);\n        assert_eq!(preview.x, 40);\n        // list and input share the same work-column width (40, not summed).\n        assert_eq!(layout.list_area.width, 40);\n        assert_eq!(layout.input_area.width, 40);\n        assert_horizontally_adjacent(layout.list_area, preview, \"list→preview\");\n    }\n\n    #[test]\n    fn default_preview_left_30_percent() {\n        let options = opts()\n            .preview(\"cat {}\")\n            .preview_window(PreviewLayout::from(\"left:30%\"))\n            .build()\n            .unwrap();\n        let layout = compute(&options);\n\n        let preview = layout.preview_area.unwrap();\n        // Preview on the left: 30% of 80 = 24.\n        assert_eq!(preview.width, 24);\n        assert_eq!(preview.x, 0);\n        // work_area starts after preview\n        assert_eq!(layout.list_area.x, 24);\n        assert_horizontally_adjacent(preview, layout.list_area, \"preview→list\");\n    }\n\n    #[test]\n    fn default_preview_right_fixed_20() {\n        let options = opts()\n            .preview(\"cat {}\")\n            .preview_window(PreviewLayout::from(\"right:20\"))\n            .build()\n            .unwrap();\n        let layout = compute(&options);\n\n        let preview = layout.preview_area.unwrap();\n        assert_eq!(preview.width, 20);\n        assert_eq!(layout.list_area.width, 60);\n    }\n\n    #[test]\n    fn reverse_preview_left() {\n        let options = opts()\n            .layout(TuiLayout::Reverse)\n            .preview(\"cat {}\")\n            .preview_window(PreviewLayout::from(\"left:40%\"))\n            .build()\n            .unwrap();\n        let layout = compute(&options);\n\n        let preview = layout.preview_area.unwrap();\n        // 40% of 80 = 32\n        assert_eq!(preview.width, 32);\n        // Input is at the top of work_area (Reverse)\n        assert_eq!(layout.input_area.y, 0);\n        assert_eq!(layout.input_area.x, 32);\n    }\n\n    // ── Preview: Up / Down ──────────────────────────────────────────────────\n\n    #[test]\n    fn default_preview_up_50_percent() {\n        let options = opts()\n            .preview(\"cat {}\")\n            .preview_window(PreviewLayout::from(\"up:50%\"))\n            .build()\n            .unwrap();\n        let layout = compute(&options);\n\n        let preview = layout.preview_area.unwrap();\n        // Preview is carved from the full area first: 50% of 24 = 12 rows.\n        assert_eq!(preview.height, 12);\n        // Preview is at the top (y = 0).\n        assert_eq!(preview.y, 0);\n        // work_area starts right after the preview.\n        assert_eq!(layout.list_area.y, 12);\n        // work_area height = 24 - 12 = 12; input = 2; list = 10.\n        assert_eq!(layout.list_area.height, 10);\n        // input is at the bottom of the work area.\n        assert_eq!(layout.input_area.y, 22);\n    }\n\n    #[test]\n    fn default_preview_down_50_percent() {\n        let options = opts()\n            .preview(\"cat {}\")\n            .preview_window(PreviewLayout::from(\"down:50%\"))\n            .build()\n            .unwrap();\n        let layout = compute(&options);\n\n        let preview = layout.preview_area.unwrap();\n        // Preview is carved from the full area: 50% of 24 = 12 rows at bottom.\n        assert_eq!(preview.height, 12);\n        // work_area is at the top (y = 0); preview starts after work_area.\n        assert_eq!(layout.list_area.y, 0);\n        assert_eq!(layout.list_area.height, 10);\n        // input is at the bottom of work_area (y = 10).\n        assert_eq!(layout.input_area.y, 10);\n        // Preview starts right after work_area (y = 12).\n        assert_eq!(preview.y, 12);\n        assert_vertically_adjacent(layout.input_area, preview, \"input→preview\");\n    }\n\n    #[test]\n    fn default_preview_up_fixed_8() {\n        let options = opts()\n            .preview(\"cat {}\")\n            .preview_window(PreviewLayout::from(\"up:8\"))\n            .build()\n            .unwrap();\n        let layout = compute(&options);\n\n        let preview = layout.preview_area.unwrap();\n        // Preview = 8 rows at top; work_area = 24 - 8 = 16 rows; list = 14 rows.\n        assert_eq!(preview.height, 8);\n        assert_eq!(preview.y, 0);\n        assert_eq!(layout.list_area.height, 14);\n        assert_full_width(preview, area(), \"preview\");\n    }\n\n    // ── Preview hidden ─────────────────────────────────────────────────────\n\n    #[test]\n    fn preview_hidden_produces_no_preview_area() {\n        let options = opts()\n            .preview(\"cat {}\")\n            .preview_window(PreviewLayout::from(\"right:50%:hidden\"))\n            .build()\n            .unwrap();\n        let layout = compute(&options);\n\n        assert!(layout.preview_area.is_none());\n        // Full width available to widgets.\n        assert_eq!(layout.list_area.width, 80);\n    }\n\n    #[test]\n    fn no_preview_command_produces_no_preview_area() {\n        // preview is None → no preview area even if preview_window is set.\n        let options = opts().preview_window(PreviewLayout::from(\"right:50%\")).build().unwrap();\n        let layout = compute(&options);\n\n        assert!(layout.preview_area.is_none());\n        assert_eq!(layout.list_area.width, 80);\n    }\n\n    // ── With borders ───────────────────────────────────────────────────────\n\n    #[test]\n    fn default_with_borders_no_header() {\n        let options = opts().border(crate::tui::BorderType::Plain).build().unwrap();\n        let layout = compute(&options);\n\n        // input = 3 rows (1 content + 2 border)\n        assert_eq!(layout.input_area.height, 3);\n        assert_eq!(layout.list_area.height, 21);\n        assert!(layout.header_area.is_none());\n    }\n\n    #[test]\n    fn default_with_borders_and_header() {\n        let options = opts()\n            .border(crate::tui::BorderType::Plain)\n            .header(\"hdr\")\n            .build()\n            .unwrap();\n        let layout = compute_with_header_height(&options, 2);\n\n        // input = 3, header = 2+2 = 4\n        assert_eq!(layout.input_area.height, 3);\n        let h = layout.header_area.unwrap();\n        assert_eq!(h.height, 4);\n        assert_eq!(layout.list_area.height, 24 - 3 - 4);\n    }\n\n    #[test]\n    fn reverse_with_borders() {\n        let options = opts()\n            .layout(TuiLayout::Reverse)\n            .border(crate::tui::BorderType::Plain)\n            .build()\n            .unwrap();\n        let layout = compute(&options);\n\n        // input at top (y = 0)\n        assert_eq!(layout.input_area.y, 0);\n        assert_eq!(layout.input_area.height, 3);\n        assert_eq!(layout.list_area.y, 3);\n        assert_eq!(layout.list_area.height, 21);\n    }\n\n    // ── Coverage / edge cases ──────────────────────────────────────────────\n\n    #[test]\n    fn all_areas_non_overlapping_default() {\n        // Ensure no area overlaps another for a complex configuration.\n        let options = opts()\n            .preview(\"cat {}\")\n            .preview_window(PreviewLayout::from(\"right:40%\"))\n            .header(\"hdr\")\n            .build()\n            .unwrap();\n        let layout = compute_with_header_height(&options, 2);\n\n        let preview = layout.preview_area.unwrap();\n        let header = layout.header_area.unwrap();\n\n        // Preview and work area must not overlap horizontally.\n        assert!(\n            layout.list_area.x + layout.list_area.width <= preview.x || preview.x + preview.width <= layout.list_area.x,\n            \"list and preview overlap\"\n        );\n\n        // Vertical areas within work column must not overlap.\n        let rects = [layout.list_area, header, layout.input_area];\n        for i in 0..rects.len() {\n            for j in (i + 1)..rects.len() {\n                let a = rects[i];\n                let b = rects[j];\n                let vertically_disjoint = a.y + a.height <= b.y || b.y + b.height <= a.y;\n                assert!(vertically_disjoint, \"rects[{i}] and rects[{j}] overlap vertically\");\n            }\n        }\n    }\n\n    #[test]\n    fn all_areas_non_overlapping_reverse() {\n        let options = opts()\n            .layout(TuiLayout::Reverse)\n            .inline_info(true)\n            .border(crate::tui::BorderType::Plain)\n            .preview(\"cat {}\")\n            .preview_window(PreviewLayout::from(\"left:25\"))\n            .header(\"hdr\")\n            .build()\n            .unwrap();\n        let layout = compute_with_header_height(&options, 1);\n\n        let preview = layout.preview_area.unwrap();\n        let header = layout.header_area.unwrap();\n\n        // Horizontally disjoint: preview on left, everything else on right.\n        assert_eq!(preview.x, 0);\n        assert_eq!(preview.width, 25);\n        assert_eq!(layout.list_area.x, 25);\n\n        // Vertical ordering within work column (Reverse): input, header, list.\n        assert_vertically_adjacent(layout.input_area, header, \"input→header\");\n        assert_vertically_adjacent(header, layout.list_area, \"header→list\");\n    }\n\n    #[test]\n    fn total_height_is_area_height_default() {\n        // Sum of all vertical regions must equal area.height.\n        let options = opts().header(\"hdr\").build().unwrap();\n        let layout = compute_with_header_height(&options, 3);\n        let total = layout.list_area.height + layout.header_area.unwrap().height + layout.input_area.height;\n        assert_eq!(total, area().height);\n    }\n\n    #[test]\n    fn total_width_is_area_width_with_right_preview() {\n        let options = opts()\n            .preview(\"cat {}\")\n            .preview_window(PreviewLayout::from(\"right:50%\"))\n            .build()\n            .unwrap();\n        let layout = compute(&options);\n        let total = layout.list_area.width + layout.preview_area.unwrap().width;\n        assert_eq!(total, area().width);\n    }\n\n    #[test]\n    fn total_height_is_area_height_with_down_preview() {\n        // With a Down preview the vertical space must still sum to area height.\n        let options = opts()\n            .inline_info(true)\n            .preview(\"cat {}\")\n            .preview_window(PreviewLayout::from(\"down:6\"))\n            .build()\n            .unwrap();\n        let layout = compute(&options);\n        let total = layout.preview_area.unwrap().height + layout.list_area.height + layout.input_area.height;\n        assert_eq!(total, area().height);\n    }\n\n    #[test]\n    fn reverse_list_with_header_and_preview_right() {\n        let options = opts()\n            .layout(TuiLayout::ReverseList)\n            .preview(\"cat {}\")\n            .preview_window(PreviewLayout::from(\"right:30%\"))\n            .header(\"hdr\")\n            .build()\n            .unwrap();\n        let layout = compute_with_header_height(&options, 1);\n\n        let preview = layout.preview_area.unwrap();\n        let header = layout.header_area.unwrap();\n\n        // Preview on the right\n        assert!(preview.x > 0);\n        // ReverseList: same vertical order as Default (list | header | input)\n        assert_vertically_adjacent(layout.list_area, header, \"list→header\");\n        assert_vertically_adjacent(header, layout.input_area, \"header→input\");\n        // All in the same x-column (work area left of preview)\n        assert_eq!(layout.list_area.x, layout.input_area.x);\n    }\n\n    #[test]\n    fn very_small_area() {\n        // Ensure the layout does not panic on a tiny terminal.\n        let tiny = Rect::new(0, 0, 20, 5);\n        let options = opts().header(\"hdr\").build().unwrap();\n        // Should not panic.\n        let layout = AppLayout::compute(tiny, &options, 1);\n        // input and header fit, list may have zero height but must exist.\n        assert_eq!(layout.list_area.width, 20);\n    }\n}\n"
  },
  {
    "path": "src/tui/mod.rs",
    "content": "//! Terminal UI components and rendering.\n//!\n//! This module provides the terminal user interface components for skim,\n//! including the application state, event handling, rendering widgets,\n//! and layout management.\n\nuse std::num::ParseIntError;\n\npub use app::App;\npub use event::Event;\npub use preview::PreviewCallback;\nuse thiserror::Error;\npub use widget::{SkimRender, SkimWidget};\nmod app;\nmod backend;\npub(crate) mod util;\npub use backend::Tui;\n/// Event handling and action definitions\npub mod event;\n/// Header display components\npub mod header;\nmod input;\n/// Item list display and management\npub mod item_list;\n/// Pre-computed widget layout areas\npub mod layout;\n/// TUI-specific options and configuration\npub mod options;\nmod preview;\n/// Status line display\npub mod statusline;\n/// Widget rendering utilities\npub mod widget;\n\n/// Represents a size value, either as a percentage or fixed value\n#[derive(Clone, Copy, Debug, PartialEq, Eq)]\npub enum Size {\n    /// Size as a percentage (0-100)\n    Percent(u16),\n    /// Fixed size in terminal cells\n    Fixed(u16),\n}\n\n/// Direction for movement or layout\n#[derive(PartialEq, Eq, Clone, Debug, Copy)]\npub enum Direction {\n    /// Upward direction\n    Up,\n    /// Downward direction\n    Down,\n    /// Left direction\n    Left,\n    /// Right direction\n    Right,\n}\n\nimpl TryFrom<&str> for Direction {\n    type Error = &'static str;\n    fn try_from(value: &str) -> Result<Self, Self::Error> {\n        match value.to_lowercase().as_str() {\n            \"up\" => Ok(Self::Up),\n            \"down\" => Ok(Self::Down),\n            \"left\" => Ok(Self::Left),\n            \"right\" => Ok(Self::Right),\n            _ => Err(\"Unknown direction {value}\"),\n        }\n    }\n}\n\n/// Error type for parsing size values\n#[derive(Error, Debug, PartialEq, Eq)]\npub enum SizeParseError {\n    /// Error parsing the size string\n    #[error(\"Error parsing {0}: {1:?}\")]\n    ParseError(String, ParseIntError),\n    /// Percentage value exceeds 100\n    #[error(\"Invalid percentage {0}\")]\n    InvalidPercent(u16),\n}\n\nimpl TryFrom<&str> for Size {\n    type Error = SizeParseError;\n\n    fn try_from(value: &str) -> Result<Self, Self::Error> {\n        if value.ends_with('%') {\n            let percent = value\n                .strip_suffix(\"%\")\n                .unwrap_or_default()\n                .parse::<u16>()\n                .map_err(|e| SizeParseError::ParseError(value.to_string(), e))?;\n            if percent > 100 {\n                return Err(SizeParseError::InvalidPercent(percent));\n            }\n            Ok(Self::Percent(percent))\n        } else {\n            Ok(Self::Fixed(\n                value\n                    .parse::<u16>()\n                    .map_err(|e| SizeParseError::ParseError(value.to_string(), e))?,\n            ))\n        }\n    }\n}\n\nimpl Default for Size {\n    fn default() -> Self {\n        Self::Percent(100)\n    }\n}\n\n/// This mirrors Ratatui's border type\n///\n/// We need it so that we can properly use `ValueEnum`\n#[derive(Default, Clone, Copy)]\n#[cfg_attr(feature = \"cli\", derive(clap::ValueEnum))]\n#[allow(missing_docs)]\npub enum BorderType {\n    #[default]\n    Plain,\n    Rounded,\n    Double,\n    Thick,\n\n    LightDoubleDashed,\n    HeavyDoubleDashed,\n\n    LightTripleDashed,\n    HeavyTripleDashed,\n\n    LightQuadrupleDashed,\n    HeavyQuadrupleDashed,\n\n    QuadrantInside,\n    QuadrantOutside,\n}\n\nimpl From<BorderType> for ratatui::widgets::BorderType {\n    fn from(val: BorderType) -> Self {\n        match val {\n            BorderType::Plain => ratatui::widgets::BorderType::Plain,\n            BorderType::Rounded => ratatui::widgets::BorderType::Rounded,\n            BorderType::Double => ratatui::widgets::BorderType::Double,\n            BorderType::Thick => ratatui::widgets::BorderType::Thick,\n            BorderType::LightDoubleDashed => ratatui::widgets::BorderType::LightDoubleDashed,\n            BorderType::HeavyDoubleDashed => ratatui::widgets::BorderType::HeavyDoubleDashed,\n            BorderType::LightTripleDashed => ratatui::widgets::BorderType::LightTripleDashed,\n            BorderType::HeavyTripleDashed => ratatui::widgets::BorderType::HeavyTripleDashed,\n            BorderType::LightQuadrupleDashed => ratatui::widgets::BorderType::LightQuadrupleDashed,\n            BorderType::HeavyQuadrupleDashed => ratatui::widgets::BorderType::HeavyQuadrupleDashed,\n            BorderType::QuadrantInside => ratatui::widgets::BorderType::QuadrantInside,\n            BorderType::QuadrantOutside => ratatui::widgets::BorderType::QuadrantOutside,\n        }\n    }\n}\n\n#[cfg(test)]\nmod size_test {\n    use super::*;\n    use std::num::IntErrorKind;\n    #[test]\n    fn fixed_success() {\n        assert_eq!(Size::try_from(\"10\"), Ok(Size::Fixed(10u16)));\n    }\n    #[test]\n    fn percent_success() {\n        assert_eq!(Size::try_from(\"10%\"), Ok(Size::Percent(10u16)));\n    }\n    #[test]\n    fn fixed_neg() {\n        let SizeParseError::ParseError(err_value, internal_error) = Size::try_from(\"-10\").unwrap_err() else {\n            panic!();\n        };\n        assert_eq!(internal_error.kind(), &IntErrorKind::InvalidDigit);\n        assert_eq!(err_value, String::from(\"-10\"));\n    }\n    #[test]\n    fn percent_neg() {\n        let SizeParseError::ParseError(err_value, internal_error) = Size::try_from(\"-10%\").unwrap_err() else {\n            panic!();\n        };\n        assert_eq!(internal_error.kind(), &IntErrorKind::InvalidDigit);\n        assert_eq!(err_value, String::from(\"-10%\"));\n    }\n    #[test]\n    fn percent_over_100() {\n        let SizeParseError::InvalidPercent(internal_error) = Size::try_from(\"110%\").unwrap_err() else {\n            panic!();\n        };\n        assert_eq!(internal_error, 110u16);\n    }\n    #[test]\n    fn fixed_invalid_char() {\n        let SizeParseError::ParseError(value, internal_error) = Size::try_from(\"1-0\").unwrap_err() else {\n            panic!();\n        };\n        assert_eq!(internal_error.kind(), &IntErrorKind::InvalidDigit);\n        assert_eq!(value, String::from(\"1-0\"));\n    }\n    #[test]\n    fn percent_invalid_char() {\n        let SizeParseError::ParseError(value, internal_error) = Size::try_from(\"1-0%\").unwrap_err() else {\n            panic!();\n        };\n        assert_eq!(internal_error.kind(), &IntErrorKind::InvalidDigit);\n        assert_eq!(value, String::from(\"1-0%\"));\n    }\n    #[test]\n    fn percent_empty() {\n        let SizeParseError::ParseError(value, internal_error) = Size::try_from(\"%\").unwrap_err() else {\n            panic!();\n        };\n        assert_eq!(internal_error.kind(), &IntErrorKind::Empty);\n        assert_eq!(value, String::from(\"%\"));\n    }\n}\n"
  },
  {
    "path": "src/tui/options.rs",
    "content": "use crate::tui::{Direction, Size};\n\n/// Layout configuration for the TUI\n#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]\n#[cfg_attr(feature = \"cli\", derive(clap::ValueEnum))]\npub enum TuiLayout {\n    /// Display from the bottom of the screen\n    #[default]\n    Default,\n    /// Display from the top of the screen\n    Reverse,\n    /// Display from the top of the screen, prompt at the bottom\n    ReverseList,\n}\n\n/// Configuration for the preview pane layout\n#[derive(Debug, Clone)]\npub struct PreviewLayout {\n    /// Direction where preview pane is positioned\n    pub direction: Direction,\n    /// Size of the preview pane\n    pub size: Size,\n    /// Whether the preview pane is hidden\n    pub hidden: bool,\n    /// Optional offset for preview position\n    pub offset: Option<String>,\n    /// Whether or not to wrap the preview contents\n    pub wrap: bool,\n    /// Whether or not to run the preview in a PTY\n    pub pty: bool,\n}\n\nimpl Default for PreviewLayout {\n    fn default() -> Self {\n        Self {\n            direction: Direction::Right,\n            size: Size::Percent(50),\n            hidden: false,\n            offset: None,\n            wrap: false,\n            pty: false,\n        }\n    }\n}\n\nimpl From<&str> for PreviewLayout {\n    fn from(value: &str) -> Self {\n        let mut res: Self = PreviewLayout::default();\n        // Parse the remainder which can be: size:offset:hidden, offset:hidden, size:hidden, etc.\n        let parts: Vec<&str> = value.split(':').collect();\n\n        for part in parts {\n            if part.is_empty() {\n                continue;\n            }\n\n            if part.starts_with('+') {\n                // This is an offset expression\n                res.offset = Some(part.to_string());\n            } else if part == \"hidden\" {\n                res.hidden = true;\n            } else if part == \"nohidden\" {\n                res.hidden = false;\n            } else if part == \"wrap\" {\n                res.wrap = true;\n            } else if part == \"nowrap\" {\n                res.wrap = false;\n            } else if part == \"pty\" {\n                res.pty = true;\n            } else if part == \"nopty\" {\n                res.pty = false;\n            } else {\n                // Try to parse as size\n                if let Ok(size) = part.try_into() {\n                    res.size = size;\n                }\n                if let Ok(dir) = part.try_into() {\n                    res.direction = dir;\n                }\n            }\n        }\n        res\n    }\n}\n\n// impl PreviewLayout {\n\n// }\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn test_preview_layout_direction_only() {\n        let layout = PreviewLayout::from(\"left\");\n        assert_eq!(layout.direction, Direction::Left);\n        assert_eq!(layout.size, Size::Percent(50)); // default\n        assert!(!layout.hidden);\n        assert_eq!(layout.offset, None);\n\n        let layout = PreviewLayout::from(\"right\");\n        assert_eq!(layout.direction, Direction::Right);\n\n        let layout = PreviewLayout::from(\"up\");\n        assert_eq!(layout.direction, Direction::Up);\n\n        let layout = PreviewLayout::from(\"down\");\n        assert_eq!(layout.direction, Direction::Down);\n    }\n\n    #[test]\n    fn test_preview_layout_with_size() {\n        let layout = PreviewLayout::from(\"left:30%\");\n        assert_eq!(layout.direction, Direction::Left);\n        assert_eq!(layout.size, Size::Percent(30));\n        assert!(!layout.hidden);\n        assert_eq!(layout.offset, None);\n\n        let layout = PreviewLayout::from(\"right:40\");\n        assert_eq!(layout.direction, Direction::Right);\n        assert_eq!(layout.size, Size::Fixed(40));\n    }\n\n    #[test]\n    fn test_preview_layout_with_offset() {\n        let layout = PreviewLayout::from(\"left:+123\");\n        assert_eq!(layout.direction, Direction::Left);\n        assert_eq!(layout.offset, Some(\"+123\".to_string()));\n\n        let layout = PreviewLayout::from(\"left:+{2}\");\n        assert_eq!(layout.direction, Direction::Left);\n        assert_eq!(layout.offset, Some(\"+{2}\".to_string()));\n\n        let layout = PreviewLayout::from(\"left:+{2}-2\");\n        assert_eq!(layout.direction, Direction::Left);\n        assert_eq!(layout.offset, Some(\"+{2}-2\".to_string()));\n    }\n\n    #[test]\n    fn test_preview_layout_with_size_and_offset() {\n        let layout = PreviewLayout::from(\"left:50%:+{2}\");\n        assert_eq!(layout.direction, Direction::Left);\n        assert_eq!(layout.size, Size::Percent(50));\n        assert_eq!(layout.offset, Some(\"+{2}\".to_string()));\n\n        let layout = PreviewLayout::from(\"right:40:+123\");\n        assert_eq!(layout.direction, Direction::Right);\n        assert_eq!(layout.size, Size::Fixed(40));\n        assert_eq!(layout.offset, Some(\"+123\".to_string()));\n    }\n\n    #[test]\n    fn test_preview_layout_with_hidden() {\n        let layout = PreviewLayout::from(\"left:hidden\");\n        assert_eq!(layout.direction, Direction::Left);\n        assert!(layout.hidden);\n\n        let layout = PreviewLayout::from(\"right:50%:hidden\");\n        assert_eq!(layout.direction, Direction::Right);\n        assert_eq!(layout.size, Size::Percent(50));\n        assert!(layout.hidden);\n    }\n\n    #[test]\n    fn test_preview_layout_complex() {\n        let layout = PreviewLayout::from(\"left:30%:+{2}-5:hidden\");\n        assert_eq!(layout.direction, Direction::Left);\n        assert_eq!(layout.size, Size::Percent(30));\n        assert_eq!(layout.offset, Some(\"+{2}-5\".to_string()));\n        assert!(layout.hidden);\n    }\n}\n"
  },
  {
    "path": "src/tui/preview.rs",
    "content": "use ansi_to_tui::IntoText;\nuse color_eyre::eyre::{Result, eyre};\nuse portable_pty::{PtyPair, PtySize, native_pty_system};\nuse ratatui::{\n    layout::Alignment,\n    prelude::Backend,\n    style::Stylize,\n    text::{Line, Text},\n    widgets::{Block, Borders, Clear, Paragraph, Widget},\n};\nuse tui_term::vt100;\nuse tui_term::widget::PseudoTerminal;\n\nuse std::io::Read;\nuse std::process::Command;\nuse std::sync::mpsc;\nuse std::sync::{Arc, RwLock};\nuse std::thread::JoinHandle;\n\nuse super::util::{find_csi_end, find_osc_end, handle_csi_query, handle_osc_query};\nuse super::widget::{SkimRender, SkimWidget};\nuse super::{BorderType, Direction, Event, Tui};\n\nuse crate::theme::ColorTheme;\nuse crate::{SkimItem, SkimOptions};\n\n// PreviewCallback for ratatui - returns Vec<String> instead of AnsiString\npub type PreviewCallbackFn = dyn Fn(Vec<Arc<dyn SkimItem>>) -> Vec<String> + Send + Sync + 'static;\nconst PREVIEW_MAX_BYTES: usize = 1024 * 1024;\nconst VT_SCROLLBACK: usize = 100_000;\n\n/// Preview content can be either parsed text or a terminal screen\npub(crate) enum PreviewContent {\n    /// Simple text content (for non-PTY previews and callbacks)\n    Text(Text<'static>),\n    /// Terminal screen (for PTY previews with cursor positioning)\n    Terminal(Arc<RwLock<vt100::Parser>>),\n}\n\nimpl Default for PreviewContent {\n    fn default() -> Self {\n        PreviewContent::Text(Text::default())\n    }\n}\n\n/// Callback function for generating preview content\n#[derive(Clone)]\npub struct PreviewCallback {\n    inner: Arc<PreviewCallbackFn>,\n}\n\nimpl<F> From<F> for PreviewCallback\nwhere\n    F: Fn(Vec<Arc<dyn SkimItem>>) -> Vec<String> + Send + Sync + 'static,\n{\n    fn from(func: F) -> Self {\n        Self { inner: Arc::new(func) }\n    }\n}\n\nimpl std::ops::Deref for PreviewCallback {\n    type Target = dyn Fn(Vec<Arc<dyn SkimItem>>) -> Vec<String> + Send + Sync + 'static;\n\n    fn deref(&self) -> &Self::Target {\n        &*self.inner\n    }\n}\n\npub struct Preview {\n    pub(crate) content: Arc<RwLock<PreviewContent>>,\n    pub cmd: String,\n    pub rows: u16,\n    pub cols: u16,\n    pub scroll_y: u16,\n    pub scroll_x: u16,\n    pub thread_handle: Option<JoinHandle<()>>,\n    /// Channel to signal thread interruption\n    interrupt_tx: Option<mpsc::Sender<()>>,\n    pub theme: Arc<ColorTheme>,\n    /// Border type, if borders are enabled\n    pub border: Option<BorderType>,\n    pub direction: Direction,\n    pub wrap: bool,\n    pty: Option<PtyPair>,\n    pty_child: Option<Box<dyn portable_pty::Child + Send + Sync>>,\n    pub total_lines: u16,\n}\n\nimpl Default for Preview {\n    fn default() -> Self {\n        Self {\n            content: Arc::new(RwLock::new(PreviewContent::Text(Text::default()))),\n            cmd: String::default(),\n            rows: 0,\n            cols: 0,\n            scroll_y: 0,\n            scroll_x: 0,\n            thread_handle: None,\n            interrupt_tx: None,\n            theme: Arc::new(ColorTheme::default()),\n            border: None,\n            direction: Direction::Right,\n            wrap: false,\n            pty: None,\n            pty_child: None,\n            total_lines: 0,\n        }\n    }\n}\n\nimpl Preview {\n    /// Convert a Size value to an actual offset based on preview dimensions\n    fn size_to_offset(&self, size: super::Size, is_vertical: bool) -> u16 {\n        match size {\n            super::Size::Fixed(n) => n,\n            super::Size::Percent(p) => {\n                let dimension = if is_vertical { self.rows } else { self.cols };\n                // Result is at most dimension (a u16), so truncation cannot occur.\n                u16::try_from(u32::from(dimension) * u32::from(p) / 100).unwrap_or(u16::MAX)\n            }\n        }\n    }\n\n    fn init_pty(&mut self) {\n        let pty_system = native_pty_system();\n        let cols = if self.wrap { self.cols } else { 1024 };\n        let pair = pty_system.openpty(PtySize {\n            rows: self.rows,\n            cols,\n            pixel_width: 0,\n            pixel_height: 0,\n        });\n        match pair {\n            Ok(p) => self.pty = Some(p),\n            Err(e) => warn!(\"failed to init preview pty: {e:?}\"),\n        }\n    }\n\n    /// Filter out terminal query sequences from the output and respond to them.\n    /// This prevents programs like delta from waiting for responses and timing out.\n    /// Returns the filtered output with query sequences removed.\n    fn filter_and_respond_to_queries(data: &[u8], writer: &mut Box<dyn std::io::Write + Send>) -> Vec<u8> {\n        let mut result = Vec::new();\n        let mut i = 0;\n\n        while i < data.len() {\n            if data[i] == b'\\x1b' && i + 1 < data.len() {\n                match data[i + 1] {\n                    b']' => {\n                        // OSC sequences: ESC ] ... ST (where ST is ESC \\ or BEL)\n                        if let Some(end) = find_osc_end(&data[i..]) {\n                            let seq = &data[i..i + end];\n                            handle_osc_query(seq, writer);\n                            i += end;\n                            continue;\n                        }\n                    }\n                    b'[' => {\n                        // CSI sequences: ESC [ ... final_byte\n                        if let Some(end) = find_csi_end(&data[i..]) {\n                            let seq = &data[i..i + end];\n                            if handle_csi_query(seq, writer) {\n                                // It was a query, filter it out\n                                i += end;\n                                continue;\n                            }\n                            // Not a query, keep it in output\n                        }\n                    }\n                    _ => {}\n                }\n            }\n            result.push(data[i]);\n            i += 1;\n        }\n\n        result\n    }\n\n    pub fn content(&mut self, content: &[u8]) -> Result<()> {\n        let text = content.to_owned().into_text()?;\n        let Ok(mut content) = self.content.write() else {\n            return Err(color_eyre::eyre::eyre!(\"Failed to acquire content for writing\"));\n        };\n        self.total_lines = text.lines.len().try_into().unwrap();\n        *content = PreviewContent::Text(text);\n        self.scroll_y = 0;\n        self.scroll_x = 0;\n        Ok(())\n    }\n\n    pub fn content_with_position(&mut self, content: &[u8], position: crate::PreviewPosition) -> Result<()> {\n        self.content(content).map(|()| {\n            // Apply position offsets\n            let v_scroll = self.size_to_offset(position.v_scroll, true);\n            let v_offset = self.size_to_offset(position.v_offset, true);\n            self.scroll_y = v_scroll.saturating_add(v_offset);\n\n            let h_scroll = self.size_to_offset(position.h_scroll, false);\n            let h_offset = self.size_to_offset(position.h_offset, false);\n            self.scroll_x = h_scroll.saturating_add(h_offset);\n        })\n    }\n\n    pub fn scroll_up(&mut self, lines: u16) {\n        self.scroll_y = self.scroll_y.saturating_sub(lines);\n    }\n\n    pub fn scroll_down(&mut self, lines: u16) {\n        trace!(\n            \"scrolling down by {lines} lines, ({} total, {} rows)\",\n            self.total_lines, self.rows\n        );\n        if self.total_lines > 0 {\n            self.scroll_y = self\n                .scroll_y\n                .saturating_add(lines)\n                .min(self.total_lines.saturating_sub(self.rows.saturating_sub(1)));\n        } else {\n            // We might not have the actual total_lines value\n            self.scroll_y = self.scroll_y.saturating_add(lines);\n        }\n    }\n\n    pub fn scroll_left(&mut self, cols: u16) {\n        self.scroll_x = self.scroll_x.saturating_sub(cols);\n    }\n\n    pub fn scroll_right(&mut self, cols: u16) {\n        self.scroll_x = self.scroll_x.saturating_add(cols);\n    }\n\n    pub fn set_offset(&mut self, offset: u16) {\n        self.scroll_y = offset.saturating_sub(1); // -1 because line numbers are 1-indexed\n    }\n\n    pub fn page_up(&mut self) {\n        let page_size = self.rows.saturating_sub(2); // Account for borders\n        self.scroll_up(page_size);\n    }\n\n    pub fn page_down(&mut self) {\n        let page_size = self.rows.saturating_sub(2); // Account for borders\n        self.scroll_down(page_size);\n    }\n    /// Kill the preview child process and interrupt the reader thread.\n    pub fn kill(&mut self) {\n        if let Some(tx) = self.interrupt_tx.take() {\n            let _ = tx.send(());\n        }\n\n        if let Some(mut child) = self.pty_child.take() {\n            trace!(\"killing pty child process\");\n            match child.try_wait() {\n                Ok(Some(status)) => {\n                    trace!(\"child already exited with status: {status:?}\");\n                }\n                Ok(None) => {\n                    trace!(\"child still running, sending kill signal\");\n                    if let Err(e) = child.kill() {\n                        trace!(\"failed to kill pty child: {e:?}\");\n                    }\n                }\n                Err(e) => {\n                    debug!(\"error checking child status: {e:?}\");\n                    let _ = child.kill();\n                }\n            }\n        }\n    }\n\n    #[allow(clippy::too_many_lines)]\n    pub fn spawn<B: Backend>(&mut self, tui: &mut Tui<B>, cmd: &str) -> Result<()>\n    where\n        B::Error: Send + Sync + 'static,\n    {\n        self.kill();\n        self.cmd = cmd.to_string();\n\n        // Reset scroll position and manual_scroll flag for new preview\n        self.scroll_y = 0;\n        self.scroll_x = 0;\n\n        let event_tx_clone = tui.event_tx.clone();\n        let content = self.content.clone();\n\n        if let Some(pty) = self.pty.take() {\n            // Ensure the PTY has the correct display dimensions before spawning.\n            // init_pty() creates PTYs with 1024 cols for non-wrap mode (for the vt100 parser's\n            // horizontal scrolling), but the child process needs to see the actual display size.\n            // render() only resizes when the area changes, so if spawn() is called twice at the\n            // same area size, the second PTY would still have 1024 cols.\n            if self.rows > 0 && self.cols > 0 {\n                let _ = pty.master.resize(PtySize {\n                    rows: self.rows,\n                    cols: self.cols,\n                    pixel_width: 0,\n                    pixel_height: 0,\n                });\n            }\n            trace!(\n                \"spawning preview cmd {cmd} in pty (rows={}, cols={})\",\n                self.rows, self.cols\n            );\n            self.init_pty();\n            trace!(\"initialized pty\");\n            let mut shell_cmd = portable_pty::CommandBuilder::new(\"/bin/sh\");\n            shell_cmd.env(\"ROWS\", self.rows.to_string());\n            shell_cmd.env(\"COLUMNS\", self.cols.to_string());\n            shell_cmd.env(\"PAGER\", \"\");\n            shell_cmd.arg(\"-c\");\n            if let Ok(cwd) = nix::unistd::getcwd() {\n                shell_cmd.cwd(cwd);\n            }\n            shell_cmd.arg(cmd);\n            self.pty_child = Some(pty.slave.spawn_command(shell_cmd).map_err(|e| {\n                warn!(\"{:#?}\", e.backtrace());\n                eyre!(Box::<dyn std::error::Error + Send + Sync + 'static>::from(e))\n            })?);\n\n            let mut reader = pty\n                .master\n                .try_clone_reader()\n                .map_err(|e| eyre!(Box::<dyn std::error::Error + Send + Sync + 'static>::from(e)))?;\n\n            // Get a writer to respond to terminal queries\n            let mut esc_writer = pty\n                .master\n                .take_writer()\n                .map_err(|e| eyre!(Box::<dyn std::error::Error + Send + Sync + 'static>::from(e)))?;\n\n            let (interrupt_tx, interrupt_rx) = mpsc::channel();\n            self.interrupt_tx = Some(interrupt_tx);\n\n            // Create vt100 parser for PTY output with large scrollback buffer\n            let cols = if self.wrap { self.cols } else { 1024 };\n            let parser = Arc::new(RwLock::new(vt100::Parser::new(self.rows, cols, VT_SCROLLBACK)));\n\n            // Update content to use the parser\n            if let Ok(mut c) = content.write() {\n                *c = PreviewContent::Terminal(parser.clone());\n            }\n\n            self.thread_handle = Some(std::thread::spawn(move || {\n                let mut buf = [0u8; 8192];\n                let mut unprocessed_buf = Vec::new();\n\n                trace!(\"preview reader thread started\");\n\n                loop {\n                    if interrupt_rx.try_recv().is_ok() {\n                        trace!(\"interrupt signal received, exiting\");\n                        return;\n                    }\n\n                    match reader.read(&mut buf) {\n                        Ok(0) => {\n                            trace!(\"reached EOF\");\n                            break;\n                        }\n                        Ok(size) => {\n                            trace!(\"read {size} bytes\");\n                            unprocessed_buf.extend_from_slice(&buf[..size]);\n\n                            // Filter out terminal query sequences and respond to them\n                            // This prevents programs like delta from waiting for responses\n                            let filtered = Self::filter_and_respond_to_queries(&unprocessed_buf, &mut esc_writer);\n\n                            if let Ok(mut parser_guard) = parser.write() {\n                                parser_guard.process(&filtered);\n                                parser_guard.screen_mut().set_scrollback(VT_SCROLLBACK);\n                            }\n\n                            // Clear the processed portion of the buffer\n                            unprocessed_buf.clear();\n\n                            // Check interrupt after processing\n                            if interrupt_rx.try_recv().is_ok() {\n                                trace!(\"interrupt signal received during read block, exiting\");\n                                return;\n                            }\n                        }\n                        Err(e) => {\n                            trace!(\"read error {e:?}\");\n                            break;\n                        }\n                    }\n                }\n\n                trace!(\"read complete\");\n                let _ = event_tx_clone.blocking_send(Event::PreviewReady);\n            }));\n        } else {\n            trace!(\"spawning preview cmd {cmd}\");\n            let mut shell_cmd = Command::new(\"/bin/sh\");\n            shell_cmd\n                .env(\"ROWS\", self.rows.to_string())\n                .env(\"COLUMNS\", self.cols.to_string())\n                .env(\"PAGER\", \"\")\n                .arg(\"-c\")\n                .arg(cmd);\n            if let Ok(cwd) = nix::unistd::getcwd() {\n                shell_cmd.current_dir(cwd);\n            }\n\n            let (interrupt_tx, interrupt_rx) = mpsc::channel();\n            self.interrupt_tx = Some(interrupt_tx);\n\n            self.thread_handle = Some(std::thread::spawn(move || {\n                if interrupt_rx.try_recv().is_ok() {\n                    return;\n                }\n\n                let try_out = shell_cmd.output();\n                if try_out.is_err() {\n                    println!(\"Shell cmd in error: {try_out:?}\");\n                    return;\n                }\n\n                let mut out = try_out.unwrap();\n\n                if interrupt_rx.try_recv().is_ok() {\n                    return;\n                }\n\n                if let Ok(mut c) = content.write() {\n                    if out.status.success() {\n                        out.stdout.resize(PREVIEW_MAX_BYTES.min(out.stdout.len()), 0);\n                        *c = PreviewContent::Text(out.stdout.into_text().unwrap_or_default());\n                    } else {\n                        *c = PreviewContent::Text(out.stderr.clone().into_text().unwrap_or_default());\n                    }\n                }\n\n                trace!(\"sending ready ping\");\n                let _ = event_tx_clone.blocking_send(Event::PreviewReady);\n            }));\n        }\n        Ok(())\n    }\n}\n\nimpl Drop for Preview {\n    fn drop(&mut self) {\n        if let Some(pty) = self.pty.take() {\n            let w = pty.master.take_writer();\n            drop(w);\n        }\n        self.kill();\n    }\n}\n\nimpl SkimWidget for Preview {\n    fn from_options(options: &SkimOptions, theme: Arc<ColorTheme>) -> Self {\n        #[cfg_attr(target_os = \"macos\", allow(unused_mut))]\n        let mut res = Self {\n            theme,\n            border: options.border,\n            direction: options.preview_window.direction,\n            wrap: options.preview_window.wrap,\n            content: Arc::new(RwLock::new(PreviewContent::default())),\n            cmd: Default::default(),\n            rows: 0,\n            cols: 0,\n            scroll_y: 0,\n            scroll_x: 0,\n            thread_handle: None,\n            interrupt_tx: None,\n            pty: None,\n            pty_child: None,\n            total_lines: 0,\n        };\n        #[cfg(target_os = \"linux\")]\n        if options.preview_window.pty {\n            res.init_pty();\n        }\n        res\n    }\n\n    fn render(&mut self, area: ratatui::prelude::Rect, buf: &mut ratatui::prelude::Buffer) -> SkimRender {\n        if self.rows != area.height || self.cols != area.width {\n            self.rows = area.height;\n            self.cols = area.width;\n            self.pty.as_ref().map(|p| {\n                p.master.resize(PtySize {\n                    rows: self.rows,\n                    cols: self.cols,\n                    ..Default::default()\n                })\n            });\n        }\n        let Ok(content) = self.content.try_read() else {\n            return SkimRender::default();\n        };\n\n        let mut block = Block::new().style(self.theme.normal).border_style(self.theme.border);\n\n        // Add borders based on direction and border setting\n        if let Some(border_type) = self.border {\n            block = block.borders(Borders::ALL).border_type(border_type.into());\n        } else {\n            // No border on preview itself - separator will be drawn between areas\n            match self.direction {\n                Direction::Up => block = block.borders(Borders::BOTTOM),\n                Direction::Down => block = block.borders(Borders::TOP),\n                Direction::Left => block = block.borders(Borders::RIGHT),\n                Direction::Right => block = block.borders(Borders::LEFT),\n            }\n        }\n\n        Clear.render(area, buf);\n\n        match &*content {\n            PreviewContent::Text(text) => {\n                // Calculate total lines in content\n                self.total_lines = text.lines.len().try_into().unwrap();\n\n                // Create paragraph with optional block\n                let mut paragraph = Paragraph::new(text.clone()).scroll((self.scroll_y, self.scroll_x));\n\n                // Enable wrapping if wrap is true\n                if self.wrap {\n                    paragraph = paragraph.wrap(ratatui::widgets::Wrap { trim: false });\n                }\n\n                // Add scroll position indicator at top-right if scrolled\n                if self.scroll_y > 0 && self.total_lines > 0 {\n                    let current_line = (self.scroll_y + 1) as usize; // +1 because scroll_y is 0-indexed but we want 1-indexed display\n                    let title = format!(\"{}/{}\", current_line, self.total_lines);\n\n                    block = block.title_top(Line::from(title).alignment(Alignment::Right).reversed());\n                }\n\n                paragraph = paragraph.block(block);\n                paragraph.render(area, buf);\n            }\n            PreviewContent::Terminal(parser) => {\n                // For terminal content, manipulate scrollback to implement scrolling\n                if let Ok(mut parser_guard) = parser.try_write() {\n                    let scrollback_len = parser_guard.screen().scrollback();\n                    // Reset scrollback to its full size first\n                    parser_guard.screen_mut().set_scrollback(VT_SCROLLBACK);\n                    // If the scrollback is not empty, we seem to be off by one\n                    self.total_lines = (scrollback_len.saturating_sub(1)\n                        + parser_guard.screen().contents().lines().count())\n                    .try_into()\n                    .unwrap();\n                    if self.scroll_y > 0 {\n                        trace!(\"scrolling in vt buffer: {}/{}\", self.scroll_y, self.total_lines);\n                        // Reduce scrollback by scroll_y to show earlier content\n                        parser_guard\n                            .screen_mut()\n                            .set_scrollback(scrollback_len.saturating_sub(self.scroll_y.into()));\n                    }\n                }\n\n                // Render using PseudoTerminal widget for proper terminal emulation\n                if let Ok(parser_guard) = parser.try_read() {\n                    let screen = parser_guard.screen();\n\n                    // Add scroll position indicator if scrolled\n                    if self.scroll_y > 0 && self.total_lines > 0 {\n                        let title = format!(\"{}/{}\", self.scroll_y + 1, self.total_lines);\n                        block = block.title_top(Line::from(title).alignment(Alignment::Right).reversed());\n                    }\n\n                    // Use PseudoTerminal widget to render the vt100 screen\n                    let pseudo_term = PseudoTerminal::new(screen)\n                        .cursor(tui_term::widget::Cursor::default().visibility(false))\n                        .block(block);\n                    pseudo_term.render(area, buf);\n                }\n\n                // Reset scrollback after rendering\n                if self.scroll_y > 0\n                    && let Ok(mut parser_guard) = parser.try_write()\n                {\n                    parser_guard.screen_mut().set_scrollback(VT_SCROLLBACK);\n                }\n            }\n        }\n\n        SkimRender::default()\n    }\n}\n"
  },
  {
    "path": "src/tui/statusline.rs",
    "content": "#[cfg(feature = \"cli\")]\nuse clap::ValueEnum;\n#[cfg(feature = \"cli\")]\nuse clap::builder::PossibleValue;\n\n/// Display mode for the info/status line\n#[derive(Debug, Clone, Default, Eq, PartialEq)]\npub enum InfoDisplay {\n    /// Display info in a separate line (default)\n    #[default]\n    Default,\n    /// Display info inline with the input\n    Inline,\n    /// Hide the info display\n    Hidden,\n}\n\n#[cfg(feature = \"cli\")]\nimpl ValueEnum for InfoDisplay {\n    fn value_variants<'a>() -> &'a [Self] {\n        use InfoDisplay::{Default, Hidden, Inline};\n        &[Default, Inline, Hidden]\n    }\n\n    fn to_possible_value(&self) -> Option<clap::builder::PossibleValue> {\n        use InfoDisplay::{Default, Hidden, Inline};\n        match self {\n            Default => Some(PossibleValue::new(\"default\")),\n            Inline => Some(PossibleValue::new(\"inline\")),\n            Hidden => Some(PossibleValue::new(\"hidden\")),\n        }\n    }\n}\n"
  },
  {
    "path": "src/tui/util.rs",
    "content": "use crossterm::terminal;\nuse ratatui::{\n    style::Style,\n    text::{Line, Span, Text},\n};\nuse std::fs::OpenOptions;\nuse std::io::{self, Read, Write};\nuse std::os::fd::{AsFd, AsRawFd};\nuse std::os::unix::fs::OpenOptionsExt;\nuse unicode_display_width::is_double_width;\n\n// Directly taken from https://docs.rs/unicode-display-width/0.3.0/src/unicode_display_width/lib.rs.html#77-81\n#[inline]\npub fn char_display_width(c: char) -> usize {\n    if c == '\\u{FE0F}' || is_double_width(c) {\n        return 2;\n    }\n    1\n}\n\npub fn wrap_text(input: Text, width: usize) -> Text {\n    if input.width() <= width {\n        return input;\n    }\n\n    let mut output = Text::default();\n\n    for input_line in input.iter() {\n        let mut current_line = Line::default();\n        let mut w = 0;\n        for span in &input_line.spans {\n            let mut curr = Span::default().style(span.style);\n            let mut curr_content = String::new();\n            for c in span.content.chars() {\n                if w + char_display_width(c) > width {\n                    // Push current span and line before wrapping\n                    if !curr_content.is_empty() {\n                        curr.content = curr_content.into();\n                        current_line.push_span(curr);\n                    }\n                    output.push_line(current_line);\n                    // Reset for new line\n                    current_line = Line::default();\n                    curr = Span::default().style(span.style);\n                    curr_content = String::new();\n                    w = 0;\n                }\n                curr_content.push(c);\n                w += char_display_width(c);\n            }\n            // Push remaining content in current span\n            if !curr_content.is_empty() {\n                curr.content = curr_content.into();\n                current_line.push_span(curr);\n            }\n        }\n        // Push remaining line\n        if !current_line.spans.is_empty() {\n            output.push_line(current_line);\n        }\n    }\n\n    output\n}\n\n/// Merges styles from right to left\n/// left has higher priority\n/// contrary to ratatui's `Style::patch`, this will override `Reset` with the new style if set\npub(crate) fn merge_styles(left: Style, right: Style) -> Style {\n    use ratatui::style::Color::Reset;\n    let mut res = Style::default();\n    macro_rules! set_field {\n        ($res:ident, $left:ident, $right:ident, $field:ident) => {\n            if left.$field == Some(Reset) {\n                $res.$field = $right.$field;\n            } else if $right.$field == Some(Reset) {\n                $res.$field = $left.$field;\n            } else {\n                $res.$field = $right.$field.or($left.$field);\n            }\n        };\n    }\n\n    set_field!(res, left, right, fg);\n    set_field!(res, left, right, bg);\n    set_field!(res, left, right, underline_color);\n    res.add_modifier = left.add_modifier | right.add_modifier;\n\n    res\n}\n\npub(crate) fn style_span(span: &mut Span, style: Style) {\n    span.style = merge_styles(style, span.style);\n}\npub(crate) fn style_line(line: &mut Line, style: Style) {\n    line.iter_mut().for_each(|span| style_span(span, style));\n}\npub(crate) fn style_text(text: &mut Text, style: Style) {\n    text.iter_mut().for_each(|line| style_line(line, style));\n}\n\n/// Find the end of an OSC sequence (terminated by ESC \\ or BEL)\npub(crate) fn find_osc_end(data: &[u8]) -> Option<usize> {\n    for i in 2..data.len() {\n        if data[i] == b'\\x07' {\n            // BEL terminator\n            return Some(i + 1);\n        }\n        if i + 1 < data.len() && data[i] == b'\\x1b' && data[i + 1] == b'\\\\' {\n            // ESC \\ terminator\n            return Some(i + 2);\n        }\n    }\n    None\n}\n\n/// Find the end of a CSI sequence\npub(crate) fn find_csi_end(data: &[u8]) -> Option<usize> {\n    for (i, c) in data.iter().enumerate().skip(2) {\n        // CSI sequences end with a byte in the range 0x40-0x7E\n        if (0x40..=0x7E).contains(c) {\n            return Some(i + 1);\n        }\n    }\n    None\n}\n\n/// Handle OSC query sequences and respond to them\npub(crate) fn handle_osc_query(seq: &[u8], writer: &mut Box<dyn std::io::Write + Send>) {\n    // Check if it's a query (contains '?')\n    if !seq.contains(&b'?') {\n        return;\n    }\n\n    // OSC 10 ; ? - Query foreground color\n    if seq.starts_with(b\"\\x1b]10;?\") {\n        let _ = writer.write_all(b\"\\x1b]10;rgb:ffff/ffff/ffff\\x1b\\\\\");\n        let _ = writer.flush();\n        trace!(\"responded to OSC 10 foreground color query\");\n    }\n    // OSC 11 ; ? - Query background color\n    else if seq.starts_with(b\"\\x1b]11;?\") {\n        let _ = writer.write_all(b\"\\x1b]11;rgb:0000/0000/0000\\x1b\\\\\");\n        let _ = writer.flush();\n        trace!(\"responded to OSC 11 background color query\");\n    }\n    // OSC 4 ; num ; ? - Query color palette\n    else if seq.starts_with(b\"\\x1b]4;\") {\n        // Extract the color number and respond with a default color\n        // Format: ESC ] 4 ; num ; rgb:rr/gg/bb ST\n        if let Some(idx) = seq.iter().position(|&b| b == b';')\n            && let Some(idx2) = seq[idx + 1..].iter().position(|&b| b == b';')\n        {\n            let color_num = &seq[idx + 1..idx + 1 + idx2];\n            let mut response = b\"\\x1b]4;\".to_vec();\n            response.extend_from_slice(color_num);\n            response.extend_from_slice(b\";rgb:8080/8080/8080\\x1b\\\\\");\n            let _ = writer.write_all(&response);\n            let _ = writer.flush();\n            trace!(\"responded to OSC 4 color palette query\");\n        }\n    }\n}\n\n/// Handle CSI query sequences and respond to them.\n/// Returns true if the sequence was a query (and should be filtered out).\npub(crate) fn handle_csi_query(seq: &[u8], writer: &mut Box<dyn std::io::Write + Send>) -> bool {\n    // CSI c or CSI 0 c - Primary Device Attributes (DA1)\n    if seq == b\"\\x1b[c\" || seq == b\"\\x1b[0c\" {\n        let _ = writer.write_all(b\"\\x1b[?1;2c\");\n        let _ = writer.flush();\n        trace!(\"responded to CSI c (DA1) query\");\n        return true;\n    }\n    // CSI > c or CSI > 0 c - Secondary Device Attributes (DA2)\n    else if seq == b\"\\x1b[>c\" || seq == b\"\\x1b[>0c\" {\n        let _ = writer.write_all(b\"\\x1b[>0;0;0c\");\n        let _ = writer.flush();\n        trace!(\"responded to CSI > c (DA2) query\");\n        return true;\n    }\n    // CSI 5 n - Device Status Report\n    else if seq == b\"\\x1b[5n\" {\n        let _ = writer.write_all(b\"\\x1b[0n\");\n        let _ = writer.flush();\n        trace!(\"responded to CSI 5 n (DSR) query\");\n        return true;\n    }\n    // CSI 6 n - Cursor Position Report\n    else if seq == b\"\\x1b[6n\" {\n        let _ = writer.write_all(b\"\\x1b[1;1R\");\n        let _ = writer.flush();\n        trace!(\"responded to CSI 6 n (CPR) query\");\n        return true;\n    }\n    // CSI ? 6 n - Extended Cursor Position Report\n    else if seq.starts_with(b\"\\x1b[?\") && seq.ends_with(b\"n\") {\n        let _ = writer.write_all(b\"\\x1b[?1;1;1R\");\n        let _ = writer.flush();\n        trace!(\"responded to CSI ? n (DECXCPR) query\");\n        return true;\n    }\n\n    false\n}\n\nstruct RawMode;\n\nimpl RawMode {\n    fn new() -> io::Result<Self> {\n        terminal::enable_raw_mode()?;\n        Ok(Self)\n    }\n}\n\nimpl Drop for RawMode {\n    fn drop(&mut self) {\n        let _ = terminal::disable_raw_mode();\n    }\n}\n\npub(crate) fn cursor_pos_from_tty() -> io::Result<(u16, u16)> {\n    let _guard = RawMode::new()?;\n    let mut tty = OpenOptions::new()\n        .read(true)\n        .write(true)\n        .custom_flags(nix::fcntl::OFlag::O_NONBLOCK.bits())\n        .open(\"/dev/tty\")?;\n    let delimiter = b'R';\n    // Where is the cursor?\n    // Use `ESC [ 6 n`.\n    write!(tty, \"\\x1B[6n\")?;\n    let mut buf: [u8; 32] = [0; 32];\n    let mut read_pos = 0;\n\n    let mut timeout = nix::sys::time::TimeVal::new(3, 0);\n    loop {\n        let mut rfds = nix::sys::select::FdSet::new();\n        rfds.insert(tty.as_fd());\n        match nix::sys::select::select(\n            rfds.highest().unwrap().as_raw_fd() + 1,\n            Some(&mut rfds),\n            None,\n            None,\n            Some(&mut timeout),\n        ) {\n            Ok(0) => {\n                return Err(io::Error::other(\"Cursor position detection timed out.\"));\n            }\n            Ok(1) => match tty.read(&mut buf[read_pos..]) {\n                Ok(n) => {\n                    read_pos += n;\n                    if buf[read_pos - 1] == delimiter {\n                        break;\n                    }\n                }\n                Err(e) if e.kind() == io::ErrorKind::WouldBlock => {}\n                Err(e) => return Err(e),\n            },\n            Err(nix::errno::Errno::EINTR) => {}\n            Err(errno) => {\n                return Err(io::Error::from_raw_os_error(errno as i32));\n            }\n            Ok(_) => unreachable!(),\n        }\n    }\n    // The answer will look like `ESC [ Cy ; Cx R`.\n    let read_str = String::from_utf8(buf[..read_pos - 1].to_owned()).unwrap();\n    let beg = read_str.rfind('[').unwrap();\n    let coords: String = read_str.chars().skip(beg + 1).collect();\n    let mut nums = coords.split(';');\n    let cy = nums.next().unwrap().parse::<u16>().unwrap();\n    let cx = nums.next().unwrap().parse::<u16>().unwrap();\n    Ok((cx, cy))\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use ansi_to_tui::IntoText as _;\n    use ratatui::style::{Color, Style};\n\n    #[test]\n    fn test_wrap_text_no_wrap_needed() {\n        // Text shorter than width should not be wrapped\n        let input = Text::from(\"short\");\n        let result = wrap_text(input.clone(), 10);\n        assert_eq!(result.lines.len(), 1);\n        assert_eq!(result.lines[0].spans[0].content, \"short\");\n    }\n\n    #[test]\n    fn test_wrap_text_exact_width() {\n        // Text exactly at width should not be wrapped\n        let input = Text::from(\"exact\");\n        let result = wrap_text(input, 5);\n        assert_eq!(result.lines.len(), 1);\n        assert_eq!(result.lines[0].spans[0].content, \"exact\");\n    }\n\n    #[test]\n    fn test_wrap_text_simple_wrap() {\n        // Text longer than width should wrap\n        let input = Text::from(\"hello world\");\n        let result = wrap_text(input, 5);\n        assert!(result.lines.len() > 1);\n        assert_eq!(result.lines[0].spans[0].content, \"hello\");\n        assert_eq!(result.lines[1].spans[0].content, \" worl\");\n        assert_eq!(result.lines[2].spans[0].content, \"d\");\n    }\n\n    #[test]\n    fn test_wrap_text_preserves_style() {\n        // Create styled text\n        let style = Style::default().fg(Color::Red);\n        let span = Span::styled(\"hello world\", style);\n        let input = Text::from(Line::from(vec![span]));\n\n        let result = wrap_text(input, 5);\n\n        // Verify style is preserved across all spans\n        for line in &result.lines {\n            for span in &line.spans {\n                assert_eq!(span.style.fg, Some(Color::Red));\n            }\n        }\n    }\n\n    #[test]\n    fn test_wrap_text_multiple_spans() {\n        // Create text with multiple spans\n        let span1 = Span::styled(\"hello\", Style::default().fg(Color::Red));\n        let span2 = Span::styled(\" world\", Style::default().fg(Color::Blue));\n        let input = Text::from(Line::from(vec![span1, span2]));\n\n        let result = wrap_text(input, 5);\n\n        // Should wrap into multiple lines\n        assert!(result.lines.len() > 1);\n\n        // Verify content is preserved\n        let reconstructed: String = result\n            .lines\n            .iter()\n            .flat_map(|line| line.spans.iter())\n            .map(|span| span.content.as_ref())\n            .collect();\n        assert_eq!(reconstructed, \"hello world\");\n    }\n\n    #[test]\n    fn test_wrap_text_multiple_lines() {\n        // Create text with multiple input lines\n        let input = Text::from(vec![Line::from(\"first line\"), Line::from(\"second line\")]);\n\n        let result = wrap_text(input, 5);\n\n        // Each input line should be processed\n        assert!(result.lines.len() >= 2);\n    }\n\n    #[test]\n    fn test_wrap_text_unicode_characters() {\n        // Test with wide Unicode characters\n        let input = Text::from(\"こんにちは\"); // Japanese characters (2 width each)\n        let result = wrap_text(input, 6);\n\n        // Should wrap correctly based on display width\n        assert!(result.lines.len() > 1);\n    }\n\n    #[test]\n    fn test_wrap_text_zero_width_characters() {\n        // Test with combining characters\n        let input = Text::from(\"a\\u{0301}b\"); // a with accent\n        let result = wrap_text(input, 10);\n\n        // Should handle zero-width characters\n        assert_eq!(result.lines.len(), 1);\n    }\n\n    #[test]\n    fn test_wrap_text_width_one() {\n        // Edge case: wrap at width 1\n        let input = Text::from(\"abc\");\n        let result = wrap_text(input, 1);\n\n        // Each character should be on its own line\n        assert_eq!(result.lines.len(), 3);\n        assert_eq!(result.lines[0].spans[0].content, \"a\");\n        assert_eq!(result.lines[1].spans[0].content, \"b\");\n        assert_eq!(result.lines[2].spans[0].content, \"c\");\n    }\n\n    #[test]\n    fn test_wrap_text_empty_input() {\n        // Test with empty text\n        let input = Text::default();\n        let result = wrap_text(input, 10);\n\n        // Should return empty text\n        assert_eq!(result.lines.len(), 0);\n    }\n\n    #[test]\n    fn test_wrap_text_preserves_multiple_styles() {\n        // Create complex multi-styled text\n        let red_style = Style::default().fg(Color::Red);\n        let blue_style = Style::default().fg(Color::Blue);\n        let green_style = Style::default().fg(Color::Green);\n\n        let span1 = Span::styled(\"hello\", red_style);\n        let span2 = Span::styled(\"world\", blue_style);\n        let span3 = Span::styled(\"test\", green_style);\n\n        let input = Text::from(Line::from(vec![span1, span2, span3]));\n        let result = wrap_text(input, 5);\n\n        // Collect all styles from result\n        let styles: Vec<_> = result\n            .lines\n            .iter()\n            .flat_map(|line| line.spans.iter())\n            .map(|span| span.style.fg)\n            .collect();\n\n        // Should contain all original colors\n        assert!(styles.contains(&Some(Color::Red)));\n        assert!(styles.contains(&Some(Color::Blue)));\n        assert!(styles.contains(&Some(Color::Green)));\n    }\n\n    #[test]\n    fn test_merge_styles() {\n        use ratatui::style::{Color::*, Modifier};\n        let input = \"before \\x1b[1;34mline1\\x1b[0m nocol\";\n        let styled = input.into_text().unwrap().lines[0].clone();\n        let red = Style::new().red();\n        let underline = Style::new().underlined();\n        assert_eq!(merge_styles(red, styled.spans[0].style).fg, Some(Red));\n        assert_eq!(merge_styles(red, styled.spans[1].style).fg, Some(Blue));\n        assert_eq!(merge_styles(red, styled.spans[1].style).add_modifier, Modifier::BOLD);\n        assert_eq!(merge_styles(red, styled.spans[2].style).fg, Some(Red));\n        assert_eq!(\n            merge_styles(underline, styled.spans[0].style).add_modifier & Modifier::UNDERLINED,\n            Modifier::UNDERLINED\n        );\n        assert_eq!(\n            merge_styles(underline, styled.spans[1].style).add_modifier & Modifier::UNDERLINED,\n            Modifier::UNDERLINED\n        );\n        assert_eq!(\n            merge_styles(underline, styled.spans[2].style).add_modifier & Modifier::UNDERLINED,\n            Modifier::UNDERLINED\n        );\n    }\n\n    #[test]\n    fn test_style_text() {\n        use ratatui::style::{Color::*, Modifier};\n        let input = \"before \\x1b[1;34mline1\\x1b[0m nocol\";\n        let mut styled = input.into_text().unwrap();\n        let red = Style::new().red();\n        style_text(&mut styled, red);\n        assert_eq!(styled.lines.len(), 1);\n        let line = styled.lines[0].clone();\n        assert_eq!(line.spans[0].style.fg, Some(Red));\n        assert_eq!(line.spans[1].style.fg, Some(Blue));\n        assert_eq!(line.spans[1].style.add_modifier, Modifier::BOLD);\n        assert_eq!(line.spans[2].style.fg, Some(Red));\n    }\n}\n"
  },
  {
    "path": "src/tui/widget.rs",
    "content": "use ratatui::buffer::Buffer;\nuse ratatui::layout::Rect;\nuse std::ops::BitOrAssign;\nuse std::sync::Arc;\n\nuse crate::options::SkimOptions;\nuse crate::theme::ColorTheme;\n\n/// Result of rendering a `SkimWidget`\n#[derive(Debug, Clone, Copy, Default)]\npub struct SkimRender {\n    /// Whether the items in the list have been updated\n    pub items_updated: bool,\n    /// Whether or not we need to reload the preview\n    pub run_preview: bool,\n}\n\nimpl BitOrAssign for SkimRender {\n    fn bitor_assign(&mut self, rhs: Self) {\n        self.items_updated |= rhs.items_updated;\n        self.run_preview |= rhs.run_preview;\n    }\n}\n\n/// Trait for Skim TUI widgets\npub trait SkimWidget: Sized {\n    /// Create a widget from options and theme\n    fn from_options(options: &SkimOptions, theme: Arc<ColorTheme>) -> Self;\n\n    /// Render the widget to the buffer\n    fn render(&mut self, area: Rect, buf: &mut Buffer) -> SkimRender;\n}\n"
  },
  {
    "path": "src/util.rs",
    "content": "use crate::field::FieldRange;\nuse crate::field::get_string_by_field;\nuse crate::helper::item::strip_ansi;\nuse crate::item::MatchedItem;\nuse regex::Regex;\nuse std::fmt::Write as _;\nuse std::fs::File;\nuse std::io::{BufRead, BufReader};\nuse std::prelude::v1::*;\n\n#[cfg(feature = \"cli\")]\n/// Unescape a delimiter string to handle escape sequences like \\x00, \\t, \\n, etc.\n///\n/// Supported escape sequences:\n/// - `\\x00` - `\\xff`: hexadecimal byte values\n/// - `\\t`: tab\n/// - `\\n`: newline\n/// - `\\r`: carriage return\n/// - `\\\\`: backslash\n///\n/// # Examples\n///\n/// ```ignore\n/// use skim::util::unescape_delimiter;\n///\n/// assert_eq!(unescape_delimiter(r\"\\x00\"), \"\\0\");\n/// assert_eq!(unescape_delimiter(r\"\\t\"), \"\\t\");\n/// assert_eq!(unescape_delimiter(r\"\\n\"), \"\\n\");\n/// assert_eq!(unescape_delimiter(r\"\\\\\"), \"\\\\\");\n/// ```\npub fn unescape_delimiter(s: &str) -> String {\n    let mut result = String::new();\n    let mut chars = s.chars();\n\n    while let Some(c) = chars.next() {\n        if c == '\\\\' {\n            match chars.next() {\n                Some('x') => {\n                    // Handle \\xNN hex escape\n                    let hex: String = chars.by_ref().take(2).collect();\n                    if hex.len() == 2 {\n                        if let Ok(byte) = u8::from_str_radix(&hex, 16) {\n                            // For null byte and other non-UTF8 safe bytes, we need to handle carefully\n                            // Regex works with strings, so we push the byte as a char\n                            result.push(byte as char);\n                        } else {\n                            // Invalid hex, keep as literal\n                            result.push('\\\\');\n                            result.push('x');\n                            result.push_str(&hex);\n                        }\n                    } else {\n                        // Not enough hex digits\n                        result.push('\\\\');\n                        result.push('x');\n                        result.push_str(&hex);\n                    }\n                }\n                Some('t') => result.push('\\t'),\n                Some('n') => result.push('\\n'),\n                Some('r') => result.push('\\r'),\n                Some('\\\\') | None => result.push('\\\\'),\n                Some(other) => {\n                    // Unknown escape, keep both backslash and character\n                    result.push('\\\\');\n                    result.push(other);\n                }\n            }\n        } else {\n            result.push(c);\n        }\n    }\n\n    result\n}\n\npub fn read_file_lines(filename: &str) -> std::result::Result<Vec<String>, std::io::Error> {\n    let file = File::open(filename)?;\n    BufReader::new(file).lines().collect()\n}\n\n/// Replace the fields in `pattern` with the items, expanding {...} patterns\n///\n/// Replaces:\n/// - `{}` -> currently selected item\n/// - `{1..}` etc -> fields of currently selected item, whose index is `selected`\n/// - `{+}` -> all selected items (multi-select)\n/// - `{q}` -> current query\n/// - `{cq}` -> current command query\n#[allow(clippy::too_many_arguments)]\n#[allow(clippy::too_many_lines)]\npub fn printf<'a>(\n    pattern: &str,\n    delimiter: &Regex,\n    replstr: &str,\n    selected: &(impl Iterator<Item = &'a MatchedItem> + std::clone::Clone),\n    current: &Option<MatchedItem>,\n    query: &str,\n    command_query: &str,\n    quote_args: bool,\n) -> String {\n    let escape_arg = |s: &str, quote: bool| {\n        let mut res = s.replace('\\0', \"\\\\0\").clone();\n        if quote && quote_args {\n            res = format!(\"'{}'\", res.replace('\\'', \"'\\\\''\"));\n        }\n        res\n    };\n\n    let item_text = current.as_ref().map(|s| strip_ansi(&s.output()).0).unwrap_or_default();\n    let escaped_item = escape_arg(&item_text, true);\n    let escaped_query = escape_arg(query, true);\n    let escaped_cmd_query = escape_arg(command_query, true);\n\n    // Split on replstr first\n    let replstr_parts = pattern.split(replstr);\n    let mut replaced_parts = Vec::new();\n\n    // Deal with every part to expand inside\n\n    for part in replstr_parts {\n        let mut sub = part.split('{');\n        let mut replaced = sub.next().unwrap_or_default().to_string();\n        for s in sub {\n            let mut inside = true;\n            let mut content = String::new();\n            for c in s.chars() {\n                if inside {\n                    if c == '}' {\n                        match content.as_str() {\n                            \"\" => replaced.push_str(\"{}\"),\n                            \"q\" => replaced.push_str(&escaped_query),\n                            \"cq\" => replaced.push_str(&escaped_cmd_query),\n                            \"n\" if current.as_ref().is_some() => {\n                                replaced.push_str(\n                                    current\n                                        .as_ref()\n                                        .map(|i| i.rank.index)\n                                        .unwrap_or_default()\n                                        .to_string()\n                                        .as_str(),\n                                );\n                            }\n                            s if s == \"+n\" || s.starts_with(\"+n:\") || s == \"+\" || s.starts_with(\"+:\") => {\n                                let is_n = s.starts_with(\"+n\");\n                                let accessor = if is_n {\n                                    |i: &MatchedItem| i.rank.index.to_string()\n                                } else {\n                                    |i: &MatchedItem| strip_ansi(&i.output()).0\n                                };\n                                let mut quote_individually = false;\n\n                                let delim = s.rsplit_once(':').map_or_else(\n                                    || {\n                                        quote_individually = quote_args;\n                                        \" \"\n                                    },\n                                    |x| x.1,\n                                );\n\n                                let mut expanded = selected\n                                    .clone()\n                                    .map(|i| escape_arg(&accessor(i), quote_individually))\n                                    .reduce(|a: String, b| a.clone() + delim + b.as_str())\n                                    .unwrap_or_default();\n                                if expanded.is_empty() {\n                                    expanded = current\n                                        .as_ref()\n                                        .map(|i| escape_arg(&accessor(i), quote_args))\n                                        .unwrap_or_default();\n                                }\n\n                                if quote_args && !quote_individually {\n                                    let _ = write!(replaced, \"'{expanded}'\");\n                                } else {\n                                    replaced.push_str(&expanded);\n                                }\n                            }\n                            s => {\n                                let (is_plus, stripped) = match s.strip_prefix('+') {\n                                    Some(x) => (true, x),\n                                    None => (false, s),\n                                };\n                                if is_plus {\n                                    let mut quote_individually = false;\n\n                                    let (stripped, delim) = stripped.rsplit_once(':').unwrap_or_else(|| {\n                                        quote_individually = quote_args;\n                                        (stripped, \" \")\n                                    });\n                                    if let Some(range) = FieldRange::from_str(stripped) {\n                                        let expanded = selected\n                                            .clone()\n                                            .map(|i| {\n                                                escape_arg(\n                                                    get_string_by_field(delimiter, &strip_ansi(&i.output()).0, &range)\n                                                        .unwrap_or_default(),\n                                                    quote_individually,\n                                                )\n                                            })\n                                            .reduce(|a: String, b| a.clone() + delim + b.as_str())\n                                            .unwrap_or_default();\n\n                                        if quote_args && !quote_individually {\n                                            let _ = write!(replaced, \"'{expanded}'\");\n                                        } else {\n                                            replaced.push_str(&expanded);\n                                        }\n                                    } else {\n                                        log::warn!(\"Failed to build multi-item field range from {content}\");\n                                        let _ = write!(replaced, \"{{{s}}}\");\n                                    }\n                                } else if let Some(range) = FieldRange::from_str(stripped) {\n                                    let replacement =\n                                        get_string_by_field(delimiter, &item_text, &range).unwrap_or_default();\n                                    replaced.push_str(&escape_arg(replacement, true));\n                                } else {\n                                    log::warn!(\"Failed to build field range from {content}\");\n                                    let _ = write!(replaced, \"{{{s}}}\");\n                                }\n                            }\n                        }\n\n                        content.clear();\n                        inside = false;\n                    } else {\n                        content.push(c);\n                    }\n                } else if c == '{' {\n                    inside = true;\n                } else {\n                    replaced.push(c);\n                }\n            }\n            // }\n        }\n        replaced_parts.push(replaced);\n    }\n\n    // Join back the replstr parts into the res\n    replaced_parts\n        .into_iter()\n        .reduce(|a: String, b| a + &escaped_item + &b)\n        .unwrap_or_default()\n}\n\n#[cfg(test)]\nmod test {\n    use super::*;\n    use crate::Rank;\n    use crate::item::{MatchedItem, RankBuilder};\n    use regex::Regex;\n    use std::sync::Arc;\n\n    fn make_item(s: &'static str) -> MatchedItem {\n        MatchedItem {\n            item: Arc::new(s),\n            rank: Rank::default(),\n            rank_builder: Arc::new(RankBuilder::default()),\n            matched_range: None,\n        }\n    }\n\n    #[test]\n    fn test_unescape_delimiter() {\n        assert_eq!(unescape_delimiter(r\"\\x00\"), \"\\0\");\n        assert_eq!(unescape_delimiter(r\"\\t\"), \"\\t\");\n        assert_eq!(unescape_delimiter(r\"\\n\"), \"\\n\");\n        assert_eq!(unescape_delimiter(r\"\\r\"), \"\\r\");\n        assert_eq!(unescape_delimiter(r\"\\\\\"), \"\\\\\");\n        assert_eq!(unescape_delimiter(r\"\\x09\"), \"\\t\");\n        assert_eq!(unescape_delimiter(r\"\\x0a\"), \"\\n\");\n        assert_eq!(unescape_delimiter(r\"foo\\x00bar\"), \"foo\\0bar\");\n        assert_eq!(unescape_delimiter(r\"[\\t\\n ]+\"), \"[\\t\\n ]+\");\n        // Invalid escape sequences should be kept as-is\n        assert_eq!(unescape_delimiter(r\"\\xGG\"), r\"\\xGG\");\n        assert_eq!(unescape_delimiter(r\"\\x0\"), r\"\\x0\");\n    }\n\n    #[test]\n    fn test_regex_null_byte_matching() {\n        use regex::Regex;\n\n        // Test that Regex can match null bytes\n        let delimiter = unescape_delimiter(r\"\\x00\");\n        let re = Regex::new(&delimiter).unwrap();\n        let text = \"a\\x00b\\x00c\";\n\n        let matches: Vec<_> = re.find_iter(text).collect();\n        assert_eq!(matches.len(), 2, \"Should find 2 null byte delimiters\");\n        assert_eq!(matches[0].start(), 1);\n        assert_eq!(matches[0].end(), 2);\n        assert_eq!(matches[1].start(), 3);\n        assert_eq!(matches[1].end(), 4);\n    }\n\n    #[test]\n    fn test_printf() {\n        let pattern = \"[1] {} [2] {..2} [3] {2..} [4] {+} [5] {q} [6] {cq} [7] {+:, } [8] {+n:','}\";\n        let items = [\n            make_item(\"item 1\"),\n            make_item(\"item 2\"),\n            make_item(\"item 3\"),\n            make_item(\"item 4\"),\n        ];\n        let delimiter = Regex::new(\" \").unwrap();\n        assert_eq!(\n            &printf(\n                pattern,\n                &delimiter,\n                \"{}\",\n                &items.iter(),\n                &Some(make_item(\"item 2\")),\n                \"query\",\n                \"cmd query\",\n                true\n            ),\n            \"[1] 'item 2' [2] 'item 2' [3] '2' [4] 'item 1' 'item 2' 'item 3' 'item 4' [5] 'query' [6] 'cmd query' [7] 'item 1, item 2, item 3, item 4' [8] '0','0','0','0'\"\n        );\n    }\n    #[test]\n    fn test_printf_plus() {\n        assert_eq!(\n            printf(\n                \"{+}\",\n                &Regex::new(\" \").unwrap(),\n                \"{}\",\n                &[make_item(\"1\"), make_item(\"2\")].iter(),\n                &Some(make_item(\"1\")),\n                \"q\",\n                \"cq\",\n                true\n            ),\n            \"'1' '2'\"\n        );\n        assert_eq!(\n            printf(\n                \"{+}\",\n                &Regex::new(\" \").unwrap(),\n                \"{}\",\n                &[].iter(),\n                &Some(make_item(\"1\")),\n                \"q\",\n                \"cq\",\n                true\n            ),\n            \"'1'\"\n        );\n    }\n    #[test]\n    fn test_printf_norec() {\n        assert_eq!(\n            printf(\n                \"{}\",\n                &Regex::new(\" \").unwrap(),\n                \"{}\",\n                &[].iter(),\n                &Some(make_item(\"{..2}\")),\n                \"q\",\n                \"cq\",\n                true\n            ),\n            \"'{..2}'\"\n        );\n    }\n    #[test]\n    fn test_printf_replstr() {\n        assert_eq!(\n            printf(\n                \"{} ##\",\n                &Regex::new(\" \").unwrap(),\n                \"##\",\n                &[make_item(\"1\"), make_item(\"2\")].iter(),\n                &Some(make_item(\"1\")),\n                \"q\",\n                \"cq\",\n                true\n            ),\n            \"{} '1'\"\n        );\n    }\n}\n"
  },
  {
    "path": "test.dockerfile",
    "content": "FROM rust:1-slim\n\nRUN apt-get update && apt-get install -y tmux bsdmainutils && apt-get clean\nCOPY rust-toolchain.toml .\n\nRUN cargo install cargo-nextest\n\nCOPY . .\n\nCMD [\"cargo\", \"nextest\", \"run\", \"--release\", \"--features\", \"test-utils\"]\n"
  },
  {
    "path": "tests/ansi.rs",
    "content": "#[allow(dead_code)]\n#[macro_use]\nmod common;\n\nuse common::tmux::Keys::*;\n\nsk_test!(test_ansi_flag_enabled, @cmd \"echo -e 'plain\\\\n\\\\x1b[31mred\\\\x1b[0m\\\\n\\\\x1b[32mgreen\\\\x1b[0m'\", &[\"--ansi\", \"--color\", \"current_match_bg:1,current_bg:2\"], {\n    @capture[0] starts_with(\">\");\n    @lines |l| (l.len() >= 3 && l.iter().any(|line| line.contains(\"plain\")));\n\n    @keys Key('d');\n    @capture[2] starts_with(\"> red\");\n\n    @capture_colored[*] contains(\"mre\\u{1b}\");\n    @keys Enter;\n    @output[*] trim().eq(\"red\");\n\n});\n\nsk_test!(test_ansi_flag_disabled, @cmd \"echo -e 'plain\\\\n\\\\x1b[31mred\\\\x1b[0m\\\\n\\\\x1b[32mgreen\\\\x1b[0m'\", &[], {\n    @capture[0] starts_with(\">\");\n    @capture[*] contains(\"plain\");\n\n    @keys Str(\"red\");\n\n    @capture[2] eq(\"> ?[31mred?[0m\");\n\n    @keys Enter;\n});\n\nsk_test!(test_ansi_matching_on_stripped_text, @cmd \"echo -e '\\\\x1b[32mgreen\\\\x1b[0m text\\\\n\\\\x1b[31mred\\\\x1b[0m text\\\\nplain text'\", &[\"--ansi\"], {\n    @capture[0] starts_with(\">\");\n    @lines |l| (l.len() >= 3 && l.iter().any(|line| line.contains(\"plain\")));\n    @keys Str(\"text\");\n    // Tiebreak will reorder items\n    @capture[2] contains(\"red text\");\n    @capture[3] contains(\"green text\");\n    @capture[4] contains(\"plain text\");\n\n\n    @keys Ctrl(&Key('u')), Str(\"green\");\n    @capture[2] contains(\"green\");\n\n    @lines |l| (l.len() == 3);\n});\n\nsk_test!(test_ansi_flag_no_strip, @cmd \"echo -e 'plain\\\\n\\\\x1b[31mred\\\\x1b[0m\\\\n\\\\x1b[32mgreen\\\\x1b[0m'\", &[\"--ansi\", \"--no-strip-ansi\", \"--color\", \"current_match_bg:1,current_bg:2\"], {\n    @capture[0] starts_with(\">\");\n    @lines |l| (l.len() >= 3 && l.iter().any(|line| line.contains(\"plain\")));\n\n    @keys Key('d');\n    @capture[2] starts_with(\"> red\");\n\n    @capture_colored[*] contains(\"mre\\u{1b}\");\n    @keys Enter;\n    @output[*] contains(\"mred\\u{1b}\");\n});\n\ninsta_test!(test_prompt_ansi, [\"a\"], &[\"--prompt\", \"\\x1b[1;34mprompt\\x1b[0m nocol\"], {\n    @snap;\n});\n"
  },
  {
    "path": "tests/binds.rs",
    "content": "#[allow(dead_code)]\n#[macro_use]\nmod common;\n\n// Test if-non-matched action: deletes character when no match\ninsta_test!(bind_if_non_matched, [\"a\", \"b\"], &[\"--bind\", \"enter:if-non-matched(backward-delete-char)\", \"-q\", \"ab\"], {\n    @snap;\n    @key Enter;\n    @snap;\n    @key Enter;\n    @char 'c';\n    @snap;\n});\n\n// Test append-and-select action: appends query to item and selects it\ninsta_test!(bind_append_and_select, [\"a\", \"\", \"b\", \"c\"], &[\"-m\", \"--bind\", \"ctrl-f:append-and-select\"], {\n    @snap;\n    @type \"xyz\";\n    @snap;\n    @ctrl 'f';\n    @snap;\n});\n\n// Test first/last actions: jump to first and last items\ninsta_test!(bind_first_last, [\"1\", \"2\", \"3\", \"4\", \"5\", \"6\", \"7\", \"8\", \"9\", \"10\"], &[\"--bind\", \"ctrl-f:first,ctrl-l:last\"], {\n    @snap;\n    @ctrl 'f';\n    @snap;\n    @ctrl 'l';\n    @snap;\n    @ctrl 'f';\n    @snap;\n});\n\n// Test top alias: top is an alias for first\ninsta_test!(bind_top_alias, [\"1\", \"2\", \"3\", \"4\", \"5\", \"6\", \"7\", \"8\", \"9\", \"10\"], &[\"--bind\", \"ctrl-t:top,ctrl-l:last\"], {\n    @snap;\n    @ctrl 'l';\n    @snap;\n    @ctrl 't';\n    @snap;\n});\n\n// Test change event: triggers on query change\ninsta_test!(bind_change, [\"1\", \"12\", \"13\", \"14\", \"15\", \"16\", \"17\", \"18\", \"19\", \"10\"], &[\"--bind\", \"change:first\"], {\n    @snap;\n    @key Up;\n    @key Up;\n    @snap;\n    @char '1';\n    @snap;\n});\n\ninsta_test!(bind_set_query_basic, [\"a\", \"b\", \"c\"], &[\"--bind\", \"ctrl-a:set-query(foo)\"], {\n    @snap;\n    @ctrl 'a';\n    @snap;\n});\n\ninsta_test!(bind_set_query_expand, [\"a\", \"b\", \"c\"], &[\"--bind\", \"ctrl-a:set-query({})\"], {\n    @snap;\n    @ctrl 'a';\n    @snap;\n});\n\ninsta_test!(bind_set_query_fields, [\"a.1\", \"b.2\", \"c.3\"], &[\"--bind\", \"ctrl-a:set-query({1})\", \"-d\", \"\\\\.\"], {\n    @snap;\n    @ctrl 'a';\n    @snap;\n});\n\ninsta_test!(bind_set_query_to_itself, [\"a\", \"b\", \"c\"], &[\"--bind\", \"ctrl-a:set-query({q})\"], {\n    @snap;\n    @ctrl 'a';\n    @snap;\n    @char 'a';\n    @snap;\n    @ctrl 'a';\n    @snap;\n});\n\ninsta_test!(bind_toggle_interactive, @interactive, &[\"--bind\", \"ctrl-a:toggle-interactive\", \"-i\", \"--cmd\", \"true\"], {\n    @snap;\n    @ctrl 'a';\n    @snap;\n});\n\ninsta_test!(bind_toggle_interactive_queries, @interactive, &[\"--bind\", \"ctrl-a:toggle-interactive\", \"-i\", \"--cmd\", \"true\", \"--query\", \"normal\", \"--cmd-query\", \"interactive\"], {\n    @snap;\n    @ctrl 'a';\n    @snap;\n    @key Left;\n    @char '|';\n    @snap;\n    @ctrl 'a';\n    @snap;\n    @ctrl 'a';\n    @snap;\n});\n\ninsta_test!(bind_set_preview_cmd, [\"a\", \"b\", \"c\"], &[\"--preview\", \"echo initial {}\", \"--bind\", \"ctrl-a:set-preview-cmd(echo new {})\"], {\n    @snap;\n    @ctrl 'a';\n    @snap;\n    @key Up;\n    @snap;\n});\n\ninsta_test!(bind_set_header_from_empty, [\"a\", \"b\", \"c\"], &[\"--bind\", \"ctrl-a:set-header(foo)\"], {\n    @snap;\n    @ctrl 'a';\n    @snap;\n});\n\ninsta_test!(bind_set_header_to_empty, [\"a\", \"b\", \"c\"], &[\"--bind\", \"ctrl-a:set-header\", \"--header\", \"foo\"], {\n    @snap;\n    @ctrl 'a';\n    @snap;\n});\n\ninsta_test!(bind_set_header_change, [\"a\", \"b\", \"c\"], &[\"--bind\", \"ctrl-a:set-header(bar)\", \"--header\", \"foo\"], {\n    @snap;\n    @ctrl 'a';\n    @snap;\n});\n"
  },
  {
    "path": "tests/case.rs",
    "content": "#[allow(dead_code)]\n#[macro_use]\nmod common;\n\n// Smart case: lowercase query matches case-insensitively\ninsta_test!(case_smart_lower, [\"aBcDeF\"], &[\"--case\", \"smart\"], {\n    @snap;\n    @type \"abc\";\n    @snap;\n});\n\n// Smart case: mixed-case query matches case-sensitively\ninsta_test!(case_smart_exact, [\"aBcDeF\"], &[\"--case\", \"smart\"], {\n    @snap;\n    @type \"aBc\";\n    @snap;\n});\n\n// Smart case: uppercase query doesn't match different case\ninsta_test!(case_smart_no_match, [\"aBcDeF\"], &[\"--case\", \"smart\"], {\n    @snap;\n    @type \"Abc\";\n    @snap;\n});\n\n// Ignore case: lowercase query matches\ninsta_test!(case_ignore_lower, [\"aBcDeF\"], &[\"--case\", \"ignore\"], {\n    @snap;\n    @type \"abc\";\n    @snap;\n});\n\n// Ignore case: exact case matches\ninsta_test!(case_ignore_exact, [\"aBcDeF\"], &[\"--case\", \"ignore\"], {\n    @snap;\n    @type \"aBc\";\n    @snap;\n});\n\n// Ignore case: different case matches\ninsta_test!(case_ignore_different, [\"aBcDeF\"], &[\"--case\", \"ignore\"], {\n    @snap;\n    @type \"Abc\";\n    @snap;\n});\n\n// Ignore case: non-matching character doesn't match\ninsta_test!(case_ignore_no_match, [\"aBcDeF\"], &[\"--case\", \"ignore\"], {\n    @snap;\n    @type \"z\";\n    @snap;\n});\n\n// Respect case: lowercase query doesn't match different case\ninsta_test!(case_respect_lower, [\"aBcDeF\"], &[\"--case\", \"respect\"], {\n    @snap;\n    @type \"abc\";\n    @snap;\n});\n\n// Respect case: exact case matches\ninsta_test!(case_respect_exact, [\"aBcDeF\"], &[\"--case\", \"respect\"], {\n    @snap;\n    @type \"aBc\";\n    @snap;\n});\n\n// Respect case: different case doesn't match\ninsta_test!(case_respect_no_match, [\"aBcDeF\"], &[\"--case\", \"respect\"], {\n    @snap;\n    @type \"Abc\";\n    @snap;\n});\n\n// Non-ascii input\n\ninsta_test!(case_non_ascii, [\"слово\", \"Слово\", \"СЛОВО\"], &[\"--case\", \"smart\"], {\n    @snap;\n    @type \"слово\";\n    @snap;\n    @ctrl 'w';\n    @type \"Слово\";\n    @snap;\n});\n"
  },
  {
    "path": "tests/common/insta.rs",
    "content": "use std::io::Cursor;\n\nuse clap::Parser;\nuse color_eyre::Result;\nuse crossterm::event::{KeyCode, KeyEvent, KeyModifiers};\nuse ratatui::backend::TestBackend;\nuse skim::{\n    Skim, SkimItemReceiver,\n    prelude::*,\n    tui::{Event, Tui, event::Action},\n};\n\n/// A test harness for running skim TUI tests with insta snapshots.\n///\n/// This struct wraps a [`Skim<TestBackend>`] instance, providing a synchronous,\n/// event-driven interface that mirrors how the real application works. Events are\n/// sent via the event channel and processed through the app's event loop.\n///\n/// The harness reuses as much of the production code path as possible:\n/// - Items are loaded through the same `Reader` + `SkimItemReader` pipeline as\n///   the real application\n/// - Reload events use `Skim::handle_reload()`, the same logic as the production\n///   event loop\n/// - Reader completion is checked via `Skim::check_reader()`, identical to\n///   production\npub struct TestHarness {\n    /// The Skim instance backed by a TestBackend for snapshot testing.\n    pub skim: Skim<TestBackend>,\n    /// Tokio runtime for async operations (preview commands, etc.)\n    pub runtime: tokio::runtime::Runtime,\n    /// The final event that caused the app to quit (for determining exit code)\n    pub final_event: Option<Event>,\n}\n\nimpl TestHarness {\n    /// Process all pending events from the event queue.\n    ///\n    /// This is the core method that processes events just like the real event loop\n    /// in `lib.rs`. It drains the event_rx channel and calls `app.handle_event()`\n    /// for each event, mimicking the actual application behavior.\n    ///\n    /// For `Event::Reload`, it delegates to `Skim::handle_reload()` — the same\n    /// method used by the production event loop.\n    pub fn tick(&mut self) -> Result<()> {\n        // Drain-and-process in a loop so events queued during processing\n        // are picked up on the next iteration.\n        loop {\n            let mut events = Vec::new();\n            while let Ok(event) = self.skim.tui_mut().event_rx.try_recv() {\n                events.push(event);\n            }\n            if events.is_empty() {\n                break;\n            }\n            for event in events {\n                self.process_event(event)?;\n            }\n        }\n        Ok(())\n    }\n\n    /// Process a single event through the same logic as the production event loop.\n    ///\n    /// `Event::Reload` is handled via `Skim::handle_reload()` — the exact same\n    /// method used in production. All other events are forwarded to\n    /// `app.handle_event()`.\n    fn process_event(&mut self, event: Event) -> Result<()> {\n        if let Event::Reload(ref new_cmd) = event {\n            let new_cmd = new_cmd.clone();\n            self.skim.handle_reload(&new_cmd);\n        } else {\n            // Let the app handle the event (this may queue more events)\n            // Enter the runtime context so that tokio::spawn() calls work\n            let _guard = self.runtime.enter();\n            let (app, tui) = self.skim.app_and_tui();\n            app.handle_event(tui, &event)?;\n        }\n\n        // Check reader status, just like the production event loop\n        self.skim.check_reader();\n\n        // Track if app should quit and what the final event was\n        if self.skim.app().should_quit && self.final_event.is_none() {\n            self.final_event = Some(event);\n        }\n\n        Ok(())\n    }\n\n    /// Send an event to the event queue.\n    ///\n    /// This queues an event for processing. Call `tick()` to process queued events.\n    pub fn send(&mut self, event: Event) -> Result<()> {\n        self.skim.tui_mut().event_tx.try_send(event)?;\n        Ok(())\n    }\n\n    /// Send a key event and process it immediately.\n    ///\n    /// This is the primary way to simulate user input. It:\n    /// 1. Sends the key event to the queue\n    /// 2. Processes all pending events (including any triggered by the key)\n    /// 3. Waits for reader (if running) and matcher to complete\n    pub fn key(&mut self, key: KeyEvent) -> Result<()> {\n        self.send(Event::Key(key))?;\n        self.tick()?;\n        self.wait_for_completion()?;\n        Ok(())\n    }\n\n    /// Send a character key event.\n    pub fn char(&mut self, c: char) -> Result<()> {\n        self.key(KeyEvent::new(KeyCode::Char(c), KeyModifiers::NONE))\n    }\n\n    /// Type a string, sending each character as a key event.\n    pub fn type_str(&mut self, s: &str) -> Result<()> {\n        for c in s.chars() {\n            self.char(c)?;\n        }\n        Ok(())\n    }\n\n    /// Send an action and process it immediately.\n    pub fn action(&mut self, action: Action) -> Result<()> {\n        self.send(Event::Action(action))?;\n        self.tick()?;\n        self.wait_for_completion()?;\n        Ok(())\n    }\n\n    /// Wait for any in-flight reader and matcher to complete.\n    ///\n    /// If the reader is still running (e.g., after a reload in interactive mode),\n    /// waits for it to finish first. Then waits for the matcher if it needs to run.\n    ///\n    /// If a debounced matcher restart is pending (e.g., because the query changed\n    /// within the 50ms debounce window), this forces the restart immediately so\n    /// tests don't see stale results.\n    fn wait_for_completion(&mut self) -> Result<()> {\n        // If a debounced matcher restart is pending, force it now.\n        // In production, the Heartbeat event would trigger this, but in tests\n        // we don't have a continuous heartbeat timer.\n        if self.skim.app().pending_matcher_restart {\n            self.skim.app_mut().restart_matcher(true);\n        }\n\n        // If the reader is running (not done), wait for it + matcher\n        if !self.skim.reader_done() {\n            self.wait_for_reader_and_matcher()?;\n        } else {\n            // Always wait for the matcher to ensure results are consumed.\n            // Even if stopped() is already true, wait_for_matcher will\n            // render and process heartbeat to consume processed_items.\n            self.wait_for_matcher()?;\n        }\n        Ok(())\n    }\n\n    /// Get a string representation of the current buffer for snapshot testing.\n    pub fn buffer_view(&self) -> String {\n        self.skim.tui_ref().backend().to_string()\n    }\n\n    /// Prepare for taking a snapshot by waiting for preview and processing heartbeat.\n    ///\n    /// This ensures the state is up-to-date before taking a snapshot.\n    /// Call `render()` and `buffer_view()` afterward to actually take the snapshot.\n    pub fn prepare_snap(&mut self) -> Result<()> {\n        // Send heartbeat first to trigger any debounced pending preview runs.\n        // The debounce logic in run_preview() sets pending_preview_run=true when\n        // a RunPreview is dropped. The Heartbeat handler checks this flag and\n        // spawns the preview. We need this to happen BEFORE wait_for_preview()\n        // so it can detect and wait for the spawned preview task.\n        self.send(Event::Heartbeat)?;\n        self.tick()?;\n\n        self.handle_remaining_events()?;\n\n        // Force a final render so that any state changes (e.g. PreviewReady) that\n        // were processed inside handle_remaining_events are reflected in the buffer\n        // before we take the snapshot.  We bypass the frame-rate throttle by sending\n        // Render directly instead of relying on the Heartbeat path.\n        self.send(Event::Render)?;\n        self.tick()?;\n        Ok(())\n    }\n\n    /// Take a snapshot of the current state.\n    ///\n    /// NOTE: This method should NOT be called from test code directly because\n    /// insta will use the wrong file path for the snapshot. Use the snap! macro instead.\n    #[doc(hidden)]\n    pub fn snap(&mut self) -> Result<()> {\n        self.prepare_snap()?;\n        let buf = self.buffer_view();\n        let cursor_pos = format!(\n            \"cursor: {}x{}\",\n            self.skim.app().cursor_pos.0,\n            self.skim.app().cursor_pos.1\n        );\n        insta::assert_snapshot!(buf + &cursor_pos);\n        Ok(())\n    }\n\n    /// Wait for the reader to finish producing items and the matcher to complete.\n    ///\n    /// This polls `Skim::reader_done()` and `Skim::check_reader()` — the same\n    /// methods used by the production event loop — until the reader has finished,\n    /// then waits for the matcher to process all items.\n    pub fn wait_for_reader_and_matcher(&mut self) -> Result<()> {\n        let timeout = std::time::Duration::from_secs(5);\n        let start = std::time::Instant::now();\n        let poll_interval = std::time::Duration::from_millis(10);\n\n        // Wait for reader to finish\n        while !self.skim.reader_done() {\n            if start.elapsed() > timeout {\n                return Err(color_eyre::eyre::eyre!(\"Timeout waiting for reader to finish\"));\n            }\n            // Check reader status (may restart matcher)\n            self.skim.check_reader();\n            std::thread::sleep(poll_interval);\n        }\n\n        // Final check to restart matcher with remaining items\n        self.skim.check_reader();\n\n        self.wait_for_matcher()?;\n        Ok(())\n    }\n\n    /// Wait for matcher to complete processing.\n    pub fn wait_for_matcher(&mut self) -> Result<()> {\n        let timeout = std::time::Duration::from_secs(5);\n        let start = std::time::Instant::now();\n        let poll_interval = std::time::Duration::from_millis(10);\n\n        // Wait for matcher to complete\n        while !self.skim.app().matcher_control.stopped() {\n            if start.elapsed() > timeout {\n                return Err(color_eyre::eyre::eyre!(\"Timeout waiting for matcher to stop\"));\n            }\n            std::thread::sleep(poll_interval);\n        }\n\n        // Give the background processing thread time to receive items\n        std::thread::sleep(std::time::Duration::from_millis(50));\n\n        // Process heartbeat to update status counters\n        self.send(Event::Heartbeat)?;\n        self.tick()?;\n\n        // Manually trigger preview if configured and an item is selected\n        // Note: on_item_changed won't trigger automatically because the item was\n        // already selected during render, so prev_item == new_item\n        let needs_preview = self.skim.app().options.preview.is_some() && self.skim.app().item_list.selected().is_some();\n        if needs_preview {\n            self.send(Event::RunPreview)?;\n            self.handle_remaining_events()?;\n        }\n\n        Ok(())\n    }\n\n    /// Wait for preview to be ready.\n    pub fn handle_remaining_events(&mut self) -> Result<()> {\n        // Process any queued events first (including RunPreview)\n        self.tick()?;\n\n        // If there's no preview task running, nothing to wait for\n        let has_pending = match self.skim.app().preview.thread_handle {\n            Some(ref handle) => !handle.is_finished(),\n            None => false,\n        };\n\n        if !has_pending {\n            // Thread is already done (or was never started). Drain any events it may\n            // have sent (e.g. PreviewReady) that arrived after our initial tick().\n            self.tick()?;\n            return Ok(());\n        }\n\n        // Wait for the preview thread to finish, then drain its events.\n        let timeout = std::time::Duration::from_secs(2);\n        let start = std::time::Instant::now();\n\n        loop {\n            // Sleep to give the background thread time to make progress\n            std::thread::sleep(std::time::Duration::from_millis(10));\n\n            // Drain and process any events (including PreviewReady)\n            self.tick()?;\n\n            // Exit as soon as the thread is done and we have processed its events\n            let finished = self\n                .skim\n                .app()\n                .preview\n                .thread_handle\n                .as_ref()\n                .map(|h| h.is_finished())\n                .unwrap_or(true);\n            if finished {\n                // One final drain to catch any events emitted right at thread exit\n                self.tick()?;\n                return Ok(());\n            }\n\n            if start.elapsed() > timeout {\n                return Ok(());\n            }\n        }\n    }\n\n    /// Process a heartbeat event to update status counters.\n    pub fn heartbeat(&mut self) -> Result<()> {\n        self.send(Event::Heartbeat)?;\n        self.tick()\n    }\n\n    /// Get the exit code that skim would return based on the final event.\n    ///\n    /// This mimics the logic in `bin/main.rs`:\n    /// - 130 if the app aborted (Ctrl+C, Ctrl+D, Esc, etc.)\n    /// - 0 if the app accepted (Enter)\n    /// - None if the app hasn't quit yet\n    pub fn app_exit_code(&self) -> Option<i32> {\n        if !self.skim.app().should_quit {\n            return None;\n        }\n\n        // Check if the final event was an Accept action\n        // This matches the logic in lib.rs:560\n        let is_abort = self\n            .final_event\n            .as_ref()\n            .map(|event| !matches!(event, Event::Action(Action::Accept(_))))\n            .unwrap_or(true);\n\n        Some(if is_abort { 130 } else { 0 })\n    }\n}\n\n// ============================================================================\n// Factory functions\n// ============================================================================\n\n/// Initialize a test harness with the given options, dimensions, and optional item source.\n///\n/// Uses [`Skim::init`] for the core initialization (App, Reader, theme, etc.)\n/// and [`Skim::init_tui_with`] to inject a [`TestBackend`].\n/// Then calls [`Skim::start`] to begin the reader and matcher — the same\n/// production code path used by the real application.\nfn enter_sized_with_source(\n    options: SkimOptions,\n    width: u16,\n    height: u16,\n    source: Option<SkimItemReceiver>,\n) -> Result<TestHarness> {\n    let backend = TestBackend::new(width, height);\n    let tui = Tui::new_for_test(backend)?;\n    let mut skim = Skim::<TestBackend>::init(options, source)?;\n    skim.init_tui_with(tui);\n\n    // Start the reader and matcher — the same call the production binary makes\n    skim.start();\n\n    // Create a multi-threaded tokio runtime for async operations (preview commands, etc.)\n    let runtime = tokio::runtime::Builder::new_multi_thread().enable_all().build()?;\n\n    let mut harness = TestHarness {\n        skim,\n        runtime,\n        final_event: None,\n    };\n\n    // Wait for the reader to finish and matcher to complete\n    harness.wait_for_reader_and_matcher()?;\n\n    Ok(harness)\n}\n\n/// Initialize a test harness with the given options and dimensions.\npub fn enter_sized(options: SkimOptions, width: u16, height: u16) -> Result<TestHarness> {\n    enter_sized_with_source(options, width, height, None)\n}\n\n/// Initialize a test harness with default dimensions (80x24).\npub fn enter(options: SkimOptions) -> Result<TestHarness> {\n    enter_sized(options, 80, 24)\n}\n\n/// Initialize a test harness with default options.\npub fn enter_default() -> Result<TestHarness> {\n    enter_sized(SkimOptions::default().build(), 80, 24)\n}\n\n/// Initialize a test harness with pre-loaded items.\n///\n/// Items are fed through the production `Reader` + `SkimItemReader` pipeline\n/// by converting them to a newline-separated byte stream and using\n/// `SkimItemReader::of_bufread()`.\npub fn enter_items<I, S>(items: I, options: SkimOptions) -> Result<TestHarness>\nwhere\n    I: IntoIterator<Item = S>,\n    S: AsRef<str>,\n{\n    // Build a newline-terminated string from items and feed through SkimItemReader.\n    // Each item must end with '\\n' so the reader produces one item per entry,\n    // including empty-string items like \"\".\n    let text: String = items\n        .into_iter()\n        .map(|s| {\n            let mut line = s.as_ref().to_owned();\n            line.push('\\n');\n            line\n        })\n        .collect();\n\n    let reader_opts = SkimItemReaderOption::from_options(&options);\n    let item_reader = SkimItemReader::new(reader_opts);\n    let rx = item_reader.of_bufread(Cursor::new(text));\n\n    enter_sized_with_source(options, 80, 24, Some(rx))\n}\n\n/// Initialize a test harness with command output as items.\n///\n/// The command is executed through the production `Reader` + `SkimItemReader`\n/// pipeline, identical to how the real application works.\npub fn enter_cmd(cmd: &str, options: SkimOptions) -> Result<TestHarness> {\n    let reader_opts = SkimItemReaderOption::from_options(&options);\n    let item_reader = SkimItemReader::new(reader_opts);\n    let rx = item_reader.of_bufread(std::io::BufReader::new(\n        std::process::Command::new(\"sh\")\n            .arg(\"-c\")\n            .arg(cmd)\n            .stdout(std::process::Stdio::piped())\n            .stderr(std::process::Stdio::null())\n            .spawn()?\n            .stdout\n            .ok_or_else(|| color_eyre::eyre::eyre!(\"Failed to capture stdout\"))?,\n    ));\n\n    enter_sized_with_source(options, 80, 24, Some(rx))\n}\n\n/// Initialize a test harness for interactive mode.\n///\n/// Uses [`Skim::start`] which already handles interactive mode correctly:\n/// it expands the command template with the initial query and starts the\n/// reader pipeline.\npub fn enter_interactive(options: SkimOptions) -> Result<TestHarness> {\n    // Skim::init() computes initial_cmd for interactive mode,\n    // and Skim::start() kicks off the reader with it.\n    // No special-casing needed here.\n    enter(options)\n}\n\n/// Parse SkimOptions from CLI-style arguments.\npub fn parse_options(args: &[&str]) -> SkimOptions {\n    let mut full_args = vec![\"sk\"];\n    full_args.extend(args);\n    SkimOptions::try_parse_from(full_args)\n        .expect(\"Failed to parse options\")\n        .build()\n}\n\n// ============================================================================\n// Macros\n// ============================================================================\n\n#[macro_export]\nmacro_rules! snap {\n    ($harness:ident) => {\n        $harness.prepare_snap()?;\n        let buf = $harness.buffer_view();\n        let cursor_pos = format!(\n            \"cursor: ({}, {})\",\n            $harness.skim.app().cursor_pos.1 + 1,\n            $harness.skim.app().cursor_pos.0 + 1\n        );\n        insta::assert_snapshot!(buf + &cursor_pos);\n    };\n}\n\n/// Macro for writing compact insta snapshot tests.\n///\n/// # Usage\n///\n/// ## Input syntax:\n/// - `[\"a\", \"b\", \"c\"]` - items array\n/// - `@cmd \"seq 1 100\"` - command output as items\n/// - `@interactive` - interactive mode with `--cmd`\n///\n/// ## Basic usage (just takes a snapshot):\n/// ```ignore\n/// insta_test!(test_name, [\"item1\", \"item2\"], &[\"--opts\"]);\n/// ```\n///\n/// ## DSL usage (with commands):\n/// ```ignore\n/// insta_test!(test_name, [\"a\", \"b\", \"c\"], &[\"--multi\"], {\n///     @snap;              // Take snapshot\n///     @char 'f';          // Send single character\n///     @type \"foo\";        // Type string\n///     @action Down(1);    // Send action\n///     @key Enter;         // Send special key\n///     @exited 0;          // Assert command exited with status code 0\n/// });\n/// ```\n#[macro_export]\nmacro_rules! insta_test {\n    // Simple variant with items array - just snapshot\n    ($name:ident, [$($item:expr),* $(,)?], $options:expr) => {\n        #[test]\n        fn $name() -> color_eyre::Result<()> {\n            let options = $crate::common::insta::parse_options($options);\n            let mut h = $crate::common::insta::enter_items([$($item),*], options)?;\n            $crate::snap!(h);\n            Ok(())\n        }\n    };\n\n    // Simple variant with items expression (identifier or expression) - just snapshot\n    ($name:ident, $items:expr, $options:expr) => {\n        #[test]\n        fn $name() -> color_eyre::Result<()> {\n            let options = $crate::common::insta::parse_options($options);\n            let mut h = $crate::common::insta::enter_items($items, options)?;\n            $crate::snap!(h);\n            Ok(())\n        }\n    };\n\n    // Simple variant with @cmd - just snapshot\n    ($name:ident, @cmd $cmd:expr, $options:expr) => {\n        #[test]\n        fn $name() -> color_eyre::Result<()> {\n            let options = $crate::common::insta::parse_options($options);\n            let mut h = $crate::common::insta::enter_cmd($cmd, options)?;\n            $crate::snap!(h);\n            Ok(())\n        }\n    };\n\n    // Simple variant with @interactive - just snapshot\n    ($name:ident, @interactive, $options:expr) => {\n        #[test]\n        fn $name() -> color_eyre::Result<()> {\n            let options = $crate::common::insta::parse_options($options);\n            let mut h = $crate::common::insta::enter_interactive(options)?;\n            $crate::snap!(h);\n            Ok(())\n        }\n    };\n\n    // DSL variant with items expression (identifier or expression)\n    ($name:ident, $items:expr, $options:expr, { $($content:tt)* }) => {\n        #[test]\n        fn $name() -> color_eyre::Result<()> {\n            let options = $crate::common::insta::parse_options($options);\n            let mut h = $crate::common::insta::enter_items($items, options)?;\n\n            insta_test!(@expand h; $($content)*);\n\n            Ok(())\n        }\n    };\n\n    // DSL variant with @cmd\n    ($name:ident, @cmd $cmd:expr, $options:expr, { $($content:tt)* }) => {\n        #[test]\n        fn $name() -> color_eyre::Result<()> {\n            let options = $crate::common::insta::parse_options($options);\n            let mut h = $crate::common::insta::enter_cmd($cmd, options)?;\n\n            insta_test!(@expand h; $($content)*);\n\n            Ok(())\n        }\n    };\n\n    // DSL variant with @interactive\n    ($name:ident, @interactive, $options:expr, { $($content:tt)* }) => {\n        #[test]\n        fn $name() -> color_eyre::Result<()> {\n            let options = $crate::common::insta::parse_options($options);\n            let mut h = $crate::common::insta::enter_interactive(options)?;\n\n            insta_test!(@expand h; $($content)*);\n\n            Ok(())\n        }\n    };\n\n    // Token processing rules\n    (@expand $h:ident; ) => {};\n\n    // @snap - take snapshot\n    (@expand $h:ident; @snap; $($rest:tt)*) => {\n        $crate::snap!($h);\n        insta_test!(@expand $h; $($rest)*);\n    };\n\n    // @char - send single character\n    (@expand $h:ident; @char $c:expr ; $($rest:tt)*) => {\n        $h.char($c)?;\n        insta_test!(@expand $h; $($rest)*);\n    };\n\n    // @type - type a string\n    (@expand $h:ident; @type $text:expr ; $($rest:tt)*) => {\n        $h.type_str($text)?;\n        insta_test!(@expand $h; $($rest)*);\n    };\n\n    // @action - send an action (e.g., @action Down(1); or @action BackwardChar;)\n    (@expand $h:ident; @action $action:ident ; $($rest:tt)*) => {\n        $h.action(skim::tui::event::Action::$action)?;\n        insta_test!(@expand $h; $($rest)*);\n    };\n\n    // @action with parenthesized args (e.g., @action Down(1);)\n    (@expand $h:ident; @action $action:ident ($($args:tt)*) ; $($rest:tt)*) => {\n        $h.action(skim::tui::event::Action::$action($($args)*))?;\n        insta_test!(@expand $h; $($rest)*);\n    };\n\n    // @key - send a special key (Enter, Escape, Tab, etc.)\n    (@expand $h:ident; @key $key:ident ; $($rest:tt)*) => {\n        $h.key(crossterm::event::KeyEvent::new(\n            crossterm::event::KeyCode::$key,\n            crossterm::event::KeyModifiers::NONE\n        ))?;\n        insta_test!(@expand $h; $($rest)*);\n    };\n\n    // @ctrl - send a key with Ctrl modifier\n    (@expand $h:ident; @ctrl $key:ident ; $($rest:tt)*) => {\n        $h.key(crossterm::event::KeyEvent::new(\n            crossterm::event::KeyCode::$key,\n            crossterm::event::KeyModifiers::CONTROL\n        ))?;\n        insta_test!(@expand $h; $($rest)*);\n    };\n\n    // @ctrl with char\n    (@expand $h:ident; @ctrl $key:literal ; $($rest:tt)*) => {\n        $h.key(crossterm::event::KeyEvent::new(\n            crossterm::event::KeyCode::Char($key),\n            crossterm::event::KeyModifiers::CONTROL\n        ))?;\n        insta_test!(@expand $h; $($rest)*);\n    };\n\n    // @alt - send a key with Alt modifier\n    (@expand $h:ident; @alt $key:ident ; $($rest:tt)*) => {\n        $h.key(crossterm::event::KeyEvent::new(\n            crossterm::event::KeyCode::$key,\n            crossterm::event::KeyModifiers::ALT\n        ))?;\n        insta_test!(@expand $h; $($rest)*);\n    };\n\n    // @alt with char\n    (@expand $h:ident; @alt $key:literal ; $($rest:tt)*) => {\n        $h.key(crossterm::event::KeyEvent::new(\n            crossterm::event::KeyCode::Char($key),\n            crossterm::event::KeyModifiers::ALT\n        ))?;\n        insta_test!(@expand $h; $($rest)*);\n    };\n\n    // @shift - send a key with Shift modifier\n    (@expand $h:ident; @shift $key:ident ; $($rest:tt)*) => {\n        $h.key(crossterm::event::KeyEvent::new(\n            crossterm::event::KeyCode::$key,\n            crossterm::event::KeyModifiers::SHIFT\n        ))?;\n        insta_test!(@expand $h; $($rest)*);\n    };\n\n    // @shift with char\n    (@expand $h:ident; @shift $key:literal ; $($rest:tt)*) => {\n        $h.key(crossterm::event::KeyEvent::new(\n            crossterm::event::KeyCode::Char($key),\n            crossterm::event::KeyModifiers::SHIFT\n        ))?;\n        insta_test!(@expand $h; $($rest)*);\n    };\n\n    // @dbg - debug print current buffer\n    (@expand $h:ident; @dbg; $($rest:tt)*) => {\n        $h.render()?;\n        println!(\"DBG buffer:\\n{}\", $h.buffer_view());\n        insta_test!(@expand $h; $($rest)*);\n    };\n\n    // @assert - run an assertion closure\n    // Pass a closure that takes the harness as parameter\n    // Usage: @assert(|h| h.skim.app().should_quit);\n    //        @assert(|h| h.skim.app().item_list.selected().unwrap().text() == \"1\");\n    (@expand $h:ident; @assert ( $assertion:expr ) ; $($rest:tt)*) => {\n        assert!(($assertion)(&$h));\n        insta_test!(@expand $h; $($rest)*);\n    };\n\n    // @exited - assert that the app would exit with a specific status code\n    // Usage: @exited 0;      // Assert successful exit (Accept)\n    //        @exited 130;    // Assert abort (Ctrl+C, Ctrl+D, Esc)\n    (@expand $h:ident; @exited $code:expr ; $($rest:tt)*) => {\n        assert_eq!(\n            $h.app_exit_code(),\n            Some($code),\n            \"Expected app to exit with status code {}, but got {:?}\",\n            $code,\n            $h.app_exit_code()\n        );\n        insta_test!(@expand $h; $($rest)*);\n    };\n}\n"
  },
  {
    "path": "tests/common/mod.rs",
    "content": "#[macro_use]\npub mod insta;\n#[macro_use]\npub mod tmux;\n\n#[cfg(all(debug_assertions, coverage))]\npub static SK: &str =\n    \"SKIM_DEFAULT_OPTIONS= SKIM_DEFAULT_COMMAND= SKIM_OPTIONS_FILE= ./target/llvm-cov-target/debug/sk\";\n#[cfg(all(debug_assertions, not(coverage)))]\npub static SK: &str = \"SKIM_DEFAULT_OPTIONS= SKIM_DEFAULT_COMMAND= SKIM_OPTIONS_FILE= ./target/debug/sk\";\n#[cfg(all(not(debug_assertions), coverage))]\npub static SK: &str =\n    \"SKIM_DEFAULT_OPTIONS= SKIM_DEFAULT_COMMAND= SKIM_OPTIONS_FILE= ./target/llvm-cov-target/release/sk\";\n#[cfg(all(not(debug_assertions), not(coverage)))]\npub static SK: &str = \"SKIM_DEFAULT_OPTIONS= SKIM_DEFAULT_COMMAND= SKIM_OPTIONS_FILE= ./target/release/sk\";\n"
  },
  {
    "path": "tests/common/tmux.rs",
    "content": "use std::{\n    fmt::{Display, Formatter},\n    fs::File,\n    io::{BufReader, ErrorKind, Read, Result},\n    path::Path,\n    process::Command,\n    thread::sleep,\n    time::Duration,\n};\n\nuse rand::RngExt as _;\nuse rand::distr::Alphanumeric;\nuse tempfile::{NamedTempFile, TempDir, tempdir};\nuse which::which;\n\nuse crate::common::SK;\n\npub fn sk(outfile: &str, opts: &[&str]) -> String {\n    format!(\n        \"{} {} > {}.part; mv {}.part {}\",\n        SK,\n        opts.join(\" \"),\n        outfile,\n        outfile,\n        outfile\n    )\n}\n\npub fn wait<F, T>(pred: F) -> Result<T>\nwhere\n    F: Fn() -> Result<T>,\n{\n    for _ in 1..500 {\n        if let Ok(t) = pred() {\n            return Ok(t);\n        }\n        sleep(Duration::from_millis(10));\n    }\n    Err(std::io::Error::new(std::io::ErrorKind::TimedOut, \"wait timed out\"))\n}\n\npub enum Keys<'a> {\n    Str(&'a str),\n    Key(char),\n    Ctrl(&'a Keys<'a>),\n    Alt(&'a Keys<'a>),\n    Enter,\n    Tab,\n    BTab,\n    Left,\n    Right,\n    BSpace,\n    Up,\n    Down,\n    Escape,\n}\n\nimpl Display for Keys<'_> {\n    fn fmt(&self, f: &mut Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {\n        use Keys::*;\n        match self {\n            Str(s) => write!(f, \"{}\", s),\n            Key(c) => write!(f, \"{}\", c),\n            Ctrl(k) => write!(f, \"C-{}\", k),\n            Alt(k) => write!(f, \"M-{}\", k),\n            Enter => write!(f, \"Enter\"),\n            Tab => write!(f, \"Tab\"),\n            BTab => write!(f, \"BTab\"),\n            Left => write!(f, \"Left\"),\n            Right => write!(f, \"Right\"),\n            BSpace => write!(f, \"BSpace\"),\n            Up => write!(f, \"Up\"),\n            Down => write!(f, \"Down\"),\n            Escape => write!(f, \"Escape\"),\n        }\n    }\n}\n\npub struct TmuxController {\n    pub window: String,\n    pub tempdir: TempDir,\n    pub outfile: Option<String>,\n}\n\nimpl Default for TmuxController {\n    fn default() -> Self {\n        Self {\n            window: String::new(),\n            tempdir: tempfile::tempdir().expect(\"Failed to create tempdir\"),\n            outfile: None,\n        }\n    }\n}\n\nimpl TmuxController {\n    pub fn run(args: &[&str]) -> Result<Vec<String>> {\n        let output = Command::new(which(\"tmux\").expect(\"Please install tmux to $PATH\"))\n            .args(args)\n            .output()?\n            .stdout\n            .split(|c| *c == b'\\n')\n            .map(|bytes| String::from_utf8(bytes.to_vec()).expect(\"Failed to parse bytes as UTF8 string\"))\n            .collect::<Vec<String>>();\n        Ok(output[0..output.len() - 1].to_vec())\n    }\n\n    pub fn new_named(name: &str) -> Result<Self> {\n        let unset_cmd = \"unset SKIM_DEFAULT_COMMAND SKIM_DEFAULT_OPTIONS PS1 PROMPT_COMMAND HISTFILE\";\n\n        let full_name = format!(\n            \"{name}-{}\",\n            rand::rng()\n                .sample_iter(&Alphanumeric)\n                .take(4)\n                .map(char::from)\n                .collect::<String>()\n        );\n        let shell_cmd = \"bash --rcfile None\";\n\n        Self::run(&[\n            \"new-window\",\n            \"-d\",\n            \"-P\",\n            \"-F\",\n            \"#I\",\n            \"-t\",\n            \"skim_e2e:\",\n            \"-n\",\n            &full_name,\n            &format!(\"{}; {}\", unset_cmd, shell_cmd),\n        ])?;\n\n        Self::run(&[\"set-window-option\", \"-t\", &full_name, \"pane-base-index\", \"0\"])?;\n\n        Ok(Self {\n            window: format!(\"skim_e2e:{full_name}\"),\n            tempdir: tempdir()?,\n            outfile: None,\n        })\n    }\n\n    pub fn new() -> Result<Self> {\n        let name: String = rand::rng()\n            .sample_iter(&Alphanumeric)\n            .take(16)\n            .map(char::from)\n            .collect();\n        Self::new_named(&name)\n    }\n\n    pub fn send_keys(&self, keys: &[Keys]) -> std::io::Result<()> {\n        print!(\"typing `\");\n        for key in keys {\n            Self::run(&[\"send-keys\", \"-t\", &self.window, &key.to_string()])?;\n            print!(\"{}\", key);\n        }\n        println!(\"`\");\n        Ok(())\n    }\n\n    pub fn tempfile(&self) -> Result<String> {\n        Ok(NamedTempFile::new_in(&self.tempdir)?\n            .path()\n            .to_str()\n            .unwrap()\n            .to_string())\n    }\n\n    // Returns the lines in reverted order\n    pub fn capture(&self) -> Result<Vec<String>> {\n        let tempfile = wait(|| {\n            let tempfile = self.tempfile()?;\n            Self::run(&[\n                \"capture-pane\",\n                \"-J\",\n                \"-b\",\n                &self.window,\n                \"-t\",\n                &format!(\"{}.0\", self.window),\n            ])?;\n            Self::run(&[\"save-buffer\", \"-b\", &self.window, &tempfile])?;\n            Ok(tempfile)\n        })?;\n\n        let mut string_lines = String::new();\n        BufReader::new(File::open(tempfile)?).read_to_string(&mut string_lines)?;\n\n        let str_lines = string_lines.trim();\n        Ok(str_lines\n            .split(\"\\n\")\n            .map(|s| s.to_string())\n            .collect::<Vec<String>>()\n            .into_iter()\n            .rev()\n            .collect())\n    }\n\n    // Capture with ANSI escape sequences preserved (using -e flag)\n    // Returns the lines in reverted order with ANSI codes\n    pub fn capture_colored(&self) -> Result<Vec<String>> {\n        let tempfile = wait(|| {\n            let tempfile = self.tempfile()?;\n            Self::run(&[\n                \"capture-pane\",\n                \"-e\",\n                \"-J\",\n                \"-b\",\n                &self.window,\n                \"-t\",\n                &format!(\"{}.0\", self.window),\n            ])?;\n            Self::run(&[\"save-buffer\", \"-b\", &self.window, &tempfile])?;\n            Ok(tempfile)\n        })?;\n\n        let mut string_lines = String::new();\n        BufReader::new(File::open(tempfile)?).read_to_string(&mut string_lines)?;\n\n        let str_lines = string_lines.trim();\n        Ok(str_lines\n            .split(\"\\n\")\n            .map(|s| s.to_string())\n            .collect::<Vec<String>>()\n            .into_iter()\n            .rev()\n            .collect())\n    }\n\n    pub fn until<F>(&self, pred: F) -> std::io::Result<()>\n    where\n        F: Fn(&[String]) -> bool,\n    {\n        match wait(|| {\n            let lines = self.capture()?;\n            if pred(&lines) {\n                return Ok(true);\n            }\n            Err(std::io::Error::other(\"pred not matched\"))\n        }) {\n            Ok(true) => Ok(()),\n            Ok(false) => Err(std::io::Error::other(self.capture()?.join(\"\\n\"))),\n            _ => Err(std::io::Error::new(\n                std::io::ErrorKind::TimedOut,\n                self.capture()?.join(\"\\n\"),\n            )),\n        }\n    }\n\n    /// Capture skim output without ANSI sequences\n    pub fn output(&self) -> Result<Vec<String>> {\n        if let Some(ref outfile) = self.outfile {\n            self.output_from(outfile)\n        } else {\n            Err(std::io::Error::new(\n                ErrorKind::NotFound,\n                \"You need to use start_sk to get an outfile\",\n            ))\n        }\n    }\n\n    /// Capture skim output from explicit outfile path\n    pub fn output_from(&self, outfile: &str) -> Result<Vec<String>> {\n        wait(|| {\n            if Path::new(&outfile).exists() {\n                Ok(())\n            } else {\n                Err(std::io::Error::new(ErrorKind::NotFound, \"outfile does not exist yet\"))\n            }\n        })?;\n        let mut string_lines = String::new();\n        BufReader::new(File::open(outfile)?).read_to_string(&mut string_lines)?;\n\n        let str_lines = string_lines.trim();\n        Ok(str_lines\n            .split(\"\\n\")\n            .map(|s| s.to_string())\n            .collect::<Vec<String>>()\n            .into_iter()\n            .collect())\n    }\n\n    pub fn start_sk(&mut self, stdin_cmd: Option<&str>, opts: &[&str]) -> Result<String> {\n        let outfile = self.tempfile()?;\n        let sk_cmd = sk(&outfile, opts);\n        let cmd = match stdin_cmd {\n            Some(s) => format!(\"{} | {}\", s, sk_cmd),\n            None => sk_cmd,\n        };\n        println!(\"--- starting up sk ---\");\n        self.send_keys(&[Keys::Str(&cmd), Keys::Enter])?;\n        println!(\"--- sk is running  ---\");\n        self.outfile = Some(outfile.clone());\n        Ok(outfile)\n    }\n}\n\nimpl Drop for TmuxController {\n    fn drop(&mut self) {\n        let _ = Self::run(&[\"kill-window\", \"-t\", &self.window]);\n    }\n}\n\n// ============================================================================\n// sk_test! - Macro for writing compact tmux-based integration tests\n// ============================================================================\n//\n// USAGE GUIDE\n// -----------\n//\n// 1. INPUT SYNTAX:\n//    - Echo string:  \"a\\\\nb\\\\nc\"      -> Runs: echo -n -e 'a\\nb\\nc'\n//    - Command:      @cmd \"seq 1 100\" -> Runs: seq 1 100 (pipe to sk)\n//\n// 2. DSL SYNTAX (Only syntax supported):\n//\n// sk_test!(test_name, \"input\", &[\"--opts\"], {\n//   @capture[0] eq(\">\");                     // Wait until capture[0] == \">\"\n//   @capture[1] trim().starts_with(\"3/3\");   // Wait until capture[1].trim().starts_with(\"3/3\")\n//   @capture[-1] eq(\"foo\");                  // Wait until last line == \"foo\"\n//   @capture[*] contains(\"bar\");             // Wait until any line contains \"bar\"\n//   @output[0] eq(\"result\");                 // Wait until output[0] == \"result\"\n//   @output[-1] eq(\"last\");                  // Wait until output[-1] (last line) == \"last\"\n//   @output[*] starts_with(\"prefix\");        // Wait until any output line starts with \"prefix\"\n//   @capture_colored[0] contains(\"\\x1b\");    // Wait until colored capture contains ANSI\n//   @lines |l| (l.len() > 5);                // Complex assertion with closure\n//   @keys Enter, Tab;                        // Send multiple keys\n//   @dbg;                                    // Debug print current capture\n// });\n//\n// NOTE: All methods use wait() for consistent retry behavior. Any TmuxController\n//       method that takes no args and returns Result<Vec<String>> can be used:\n//       capture, output, capture_colored, etc.\n//\n// EXAMPLES\n// --------\n//\n// Example 1: Simple test with echo input (all methods wait/retry)\n//   sk_test!(simple, \"a\\\\nb\\\\nc\", &[], {\n//     @capture[0] eq(\">\");        // Waits until condition is met\n//     @keys Enter;\n//     @output[0] eq(\"a\");          // Waits until output is available\n//   });\n//\n// Example 2: Using command input with @cmd\n//   sk_test!(with_seq, @cmd \"seq 1 10\", &[\"--bind\", \"'ctrl-t:toggle-all'\"], {\n//     @capture[0] eq(\">\");\n//     @keys Ctrl(&Key('t'));\n//     @capture[2] eq(\">>1\");\n//   });\n//\n// Example 3: Complex closures with @lines\n//   sk_test!(complex, \"apple\\\\nbanana\", &[], {\n//     @lines |l| (l.len() > 4);\n//     @keys Str(\"ana\");\n//     @lines |l| (l.iter().any(|x| x.contains(\"banana\")));\n//   });\n//\n// Example 4: Method chaining\n//   sk_test!(chaining, \"  foo  \\\\n  bar  \", &[], {\n//     @capture[2] trim().eq(\"foo\");\n//     @keys Enter;\n//     @output[0] trim().eq(\"foo\");\n//   });\n//\n// Example 5: Using wildcards and negative indices\n//   sk_test!(wildcards, \"apple\\\\nbanana\\\\ncherry\", &[], {\n//     @capture[*] contains(\"3/3\");           // Any line contains \"3/3\"\n//     @capture[-1] starts_with(\">\");         // Last line starts with \">\"\n//     @keys Str(\"ana\");\n//     @capture[*] contains(\"banana\");        // Any line contains \"banana\"\n//     @keys Enter;\n//     @output[0] eq(\"banana\");               // First output line\n//     @output[-1] eq(\"banana\");              // Last output line\n//     @output[*] starts_with(\"b\");           // Any output line starts with \"b\"\n//   });\n//\n// Example 6: New array syntax test\n//   sk_test!(new_syntax_test, \"foo\\\\nbar\\\\nbaz\", &[], {\n//     @capture[0] starts_with(\">\");\n//     @capture[1] contains(\"3/3\");\n//     @keys Enter;\n//     @output[0] eq(\"foo\");\n//     @output[- 1] eq(\"foo\");\n//   });\n//\n// Example 7: Wildcard syntax test\n//   sk_test!(wildcard_syntax_test, \"apple\\\\nbanana\\\\ncherry\", &[], {\n//     @capture[*] contains(\"3/3\");\n//     @keys Str(\"ana\");\n//     @capture[*] contains(\"banana\");\n//     @keys Enter;\n//     @output[*] eq(\"banana\");\n//   });\n//\n// Example 8: Comprehensive example showing all features\n//   sk_test!(comprehensive_example, \"foo\\\\nbar\\\\nbaz\\\\nqux\", &[], {\n//     // Positive index with simple method\n//     @capture[0] starts_with(\">\");\n//\n//     // Positive index with method chain\n//     @capture[1] trim().contains(\"4/4\");\n//\n//     // Wildcard - check if any line matches\n//     @capture[*] contains(\"foo\");\n//\n//     // Send keys\n//     @keys Str(\"ba\");\n//\n//     // Negative index - last line\n//     @capture[- 1] contains(\"bar\");\n//\n//     // Select first match\n//     @keys Enter;\n//\n//     // Output assertions\n//     @output[0] eq(\"bar\");           // First output line\n//     @output[- 1] eq(\"bar\");         // Last output line\n//     @output[*] starts_with(\"b\");    // Any output line starts with \"b\"\n//   });\n//\n// Example 9: Using capture_colored for ANSI escape sequences\n//   sk_test!(ansi_test, @cmd \"echo -e '\\\\x1b[31mred\\\\x1b[0m'\", &[\"--ansi\"], {\n//     @capture[*] contains(\"red\");\n//     @capture_colored[*] contains(\"\\x1b[31m\");  // Check for ANSI codes\n//     @keys Enter;\n//   });\n//\n// DSL COMMAND REFERENCE\n// ---------------------\n// @METHOD[N] method_chain     Wait until METHOD[N].method_chain is true (N = line number)\n// @METHOD[-N] method_chain    Wait until METHOD[-N].method_chain is true (negative index)\n// @METHOD[*] method_chain     Wait until any line matches (uses .iter().any())\n//   where METHOD is any TmuxController method returning Result<Vec<String>>:\n//     - capture: Wait until condition is true\n//     - output: Wait until condition is true\n//     - capture_colored: Wait until condition is true on colored capture\n//   All methods use wait() for consistent retry behavior\n// @lines |l| (expr)           Call tmux.until(|l| expr)? with closure\n// @keys key1, key2            Send keys (automatically adds ?)\n// @dbg                        Debug print current capture\n//\n// NOTES\n// -----\n// - The `tmux` variable is implicitly available in DSL blocks\n// - All variants automatically handle Result propagation and Ok(()) return\n// - DSL closures must be wrapped in parentheses: |l| (expr)\n// - Method chains support any String/&str method: eq(), starts_with(), contains(), trim(), etc.\n// - You can chain methods: trim().starts_with(\"foo\")\n// - Negative indices work like Python: -1 is last element, -2 is second-to-last, etc.\n// - ALL methods use wait() with retry logic - no immediate assertions\n// - wait() retries every 10ms for up to 10 seconds before timing out\n//\n#[allow(unused_macros)]\nmacro_rules! sk_test {\n    // Standard variant with echo input: explicit variable name with block\n    ($name:tt, $input:expr, $options:expr, $tmux:ident => $content:block) => {\n      #[test]\n      #[allow(unused_variables)]\n      fn $name() -> std::io::Result<()> {\n        let mut $tmux = crate::common::tmux::TmuxController::new()?;\n        $tmux.start_sk(Some(&format!(\"echo -n -e '{}'\", $input)), $options)?;\n\n        $content\n\n        Ok(())\n      }\n    };\n\n    // Standard variant with arbitrary command: use @cmd marker\n    ($name:tt, @cmd $cmd:expr, $options:expr, $tmux:ident => $content:block) => {\n      #[test]\n      #[allow(unused_variables)]\n      fn $name() -> std::io::Result<()> {\n        let mut $tmux = crate::common::tmux::TmuxController::new()?;\n        $tmux.start_sk(Some($cmd), $options)?;\n\n        $content\n\n        Ok(())\n      }\n    };\n\n    // DSL variant with echo input\n    ($name:tt, $input:expr, $options:expr, { $($content:tt)* }) => {\n      #[test]\n      #[allow(unused_variables)]\n      fn $name() -> std::io::Result<()> {\n        let mut tmux = crate::common::tmux::TmuxController::new_named(stringify!($name))?;\n        tmux.start_sk(Some(&format!(\"echo -n -e '{}'\", $input)), $options)?;\n\n        sk_test!(@expand tmux; $($content)*);\n\n        Ok(())\n      }\n    };\n\n    // DSL variant with arbitrary command: use @cmd marker\n    ($name:tt, @cmd $cmd:expr, $options:expr, { $($content:tt)* }) => {\n      #[test]\n      #[allow(unused_variables)]\n      fn $name() -> std::io::Result<()> {\n        let mut tmux = crate::common::tmux::TmuxController::new_named(stringify!($name))?;\n        tmux.start_sk(Some($cmd), $options)?;\n\n        sk_test!(@expand tmux; $($content)*);\n\n        Ok(())\n      }\n    };\n\n    // Token processing rules\n    (@expand $tmux:ident; ) => {};\n\n    // Generic method patterns - works with any TmuxController method\n    // @method[*] - check if any line matches (uses .iter().any())\n    (@expand $tmux:ident; @ $method:ident [ * ] $($rest:tt)*) => {\n        sk_test!(@method_any_collect $tmux, $method, [] ; $($rest)*);\n    };\n\n    // @method[-idx] for negative index - supports arbitrary method chains (must come before positive)\n    (@expand $tmux:ident; @ $method:ident [ - $idx:literal ] $($rest:tt)*) => {\n        sk_test!(@method_neg_collect $tmux, $method, $idx, [] ; $($rest)*);\n    };\n\n    // @method[idx] for positive index - supports arbitrary method chains\n    (@expand $tmux:ident; @ $method:ident [ $idx:literal ] $($rest:tt)*) => {\n        sk_test!(@method_pos_collect $tmux, $method, $idx, [] ; $($rest)*);\n    };\n\n    // Collect tokens until semicolon for positive index - dispatches to wait or assert\n    (@method_pos_collect $tmux:ident, $method:ident, $idx:expr, [$($methods:tt)*] ; ; $($rest:tt)*) => {\n        sk_test!(@method_pos_dispatch $tmux, $method, $idx, [$($methods)*]);\n        sk_test!(@expand $tmux; $($rest)*);\n    };\n    (@method_pos_collect $tmux:ident, $method:ident, $idx:expr, [$($methods:tt)*] ; $next:tt $($rest:tt)*) => {\n        sk_test!(@method_pos_collect $tmux, $method, $idx, [$($methods)* $next] ; $($rest)*);\n    };\n\n    // Dispatch for positive index - all methods use wait()\n    (@method_pos_dispatch $tmux:ident, $method:ident, $idx:expr, [$($methods:tt)*]) => {\n        {\n            if crate::common::tmux::wait(|| {\n                let lines = $tmux.$method()?;\n                if lines.len() > $idx && lines[$idx].$($methods)* {\n                    Ok(true)\n                } else {\n                    Err(std::io::Error::new(std::io::ErrorKind::Other, \"condition not met\"))\n                }\n            }).is_err() {\n                let lines = $tmux.$method().unwrap_or_default();\n                let actual = if lines.len() > $idx { &lines[$idx] } else { \"<no line>\" };\n                return Err(std::io::Error::new(\n                    std::io::ErrorKind::TimedOut,\n                    format!(\"Timed out waiting for {}[{}].{}, got: {}\", stringify!($method), $idx, stringify!($($methods)*), actual)\n                ));\n            }\n        }\n    };\n\n    // Collect tokens until semicolon for negative index - dispatches to wait or assert\n    (@method_neg_collect $tmux:ident, $method:ident, $idx:expr, [$($methods:tt)*] ; ; $($rest:tt)*) => {\n        sk_test!(@method_neg_dispatch $tmux, $method, $idx, [$($methods)*]);\n        sk_test!(@expand $tmux; $($rest)*);\n    };\n    (@method_neg_collect $tmux:ident, $method:ident, $idx:expr, [$($methods:tt)*] ; $next:tt $($rest:tt)*) => {\n        sk_test!(@method_neg_collect $tmux, $method, $idx, [$($methods)* $next] ; $($rest)*);\n    };\n\n    // Dispatch for negative index - all methods use wait()\n    (@method_neg_dispatch $tmux:ident, $method:ident, $idx:expr, [$($methods:tt)*]) => {\n        {\n            if crate::common::tmux::wait(|| {\n                let lines = $tmux.$method()?;\n                if lines.len() >= $idx {\n                    let actual_idx = lines.len() - $idx;\n                    if lines[actual_idx].$($methods)* {\n                        Ok(true)\n                    } else {\n                        Err(std::io::Error::new(std::io::ErrorKind::Other, \"condition not met\"))\n                    }\n                } else {\n                    Err(std::io::Error::new(std::io::ErrorKind::Other, \"not enough lines\"))\n                }\n            }).is_err() {\n                let lines = $tmux.$method().unwrap_or_default();\n                let actual_idx = lines.len().saturating_sub($idx);\n                let actual = if lines.len() >= $idx { &lines[actual_idx] } else { \"<no line>\" };\n                return Err(std::io::Error::new(\n                    std::io::ErrorKind::TimedOut,\n                    format!(\"Timed out waiting for {}[-{}].{}, got: {}\", stringify!($method), $idx, stringify!($($methods)*), actual)\n                ));\n            }\n        }\n    };\n\n    // Collect tokens until semicolon for wildcard [*] - dispatches to wait or assert\n    (@method_any_collect $tmux:ident, $method:ident, [$($methods:tt)*] ; ; $($rest:tt)*) => {\n        sk_test!(@method_any_dispatch $tmux, $method, [$($methods)*]);\n        sk_test!(@expand $tmux; $($rest)*);\n    };\n    (@method_any_collect $tmux:ident, $method:ident, [$($methods:tt)*] ; $next:tt $($rest:tt)*) => {\n        sk_test!(@method_any_collect $tmux, $method, [$($methods)* $next] ; $($rest)*);\n    };\n\n    // Dispatch for wildcard - all methods use wait()\n    (@method_any_dispatch $tmux:ident, $method:ident, [$($methods:tt)*]) => {\n        {\n            if crate::common::tmux::wait(|| {\n                let lines = $tmux.$method()?;\n                if lines.iter().any(|line| line.$($methods)*) {\n                    Ok(true)\n                } else {\n                    Err(std::io::Error::new(std::io::ErrorKind::Other, \"condition not met\"))\n                }\n            }).is_err() {\n                let lines = $tmux.$method().unwrap_or_default();\n                return Err(std::io::Error::new(\n                    std::io::ErrorKind::TimedOut,\n                    format!(\"Timed out waiting for {}[*] any line matching .{}, got: {:?}\", stringify!($method), stringify!($($methods)*), lines)\n                ));\n            }\n        }\n    };\n\n    // @lines command for tmux.until with closure\n    (@expand $tmux:ident; @ lines | $param:ident | ( $($body:tt)* ) ; $($rest:tt)*) => {\n        $tmux.until(|$param| $($body)*)?;\n        sk_test!(@expand $tmux; $($rest)*);\n    };\n\n    // @keys command for send_keys - supports any number of keys\n    (@expand $tmux:ident; @ keys $($key:expr),+ ; $($rest:tt)*) => {\n        send_keys!($tmux, $($key),+)?;\n        sk_test!(@expand $tmux; $($rest)*);\n    };\n\n    // @dbg command for debug printing\n    (@expand $tmux:ident; @ dbg ; $($rest:tt)*) => {\n        match $tmux.capture() {\n            Ok(lines) => println!(\"DBG: capture: {:?}\", lines),\n            Err(e) => println!(\"DBG: capture failed: {}\", e),\n        }\n        match $tmux.output() {\n            Ok(lines) => println!(\"DBG: output: {:?}\", lines),\n            Err(e) => println!(\"DBG: output failed: {}\", e),\n        }\n        sk_test!(@expand $tmux; $($rest)*);\n    };\n\n    // Pass through regular Rust statements that access tmux (catch-all, must be last)\n    (@expand $tmux:ident; $stmt:stmt ; $($rest:tt)*) => {\n        #[allow(redundant_semicolons)]\n        {\n            $stmt;\n            sk_test!(@expand $tmux; $($rest)*);\n        }\n    };\n\n}\n\n#[allow(unused_macros)]\nmacro_rules! assert_line {\n    ($tmux:ident, $line_nr:literal $($expression:tt)+) => {\n      {\n      if $tmux.until(|l| l.len() > $line_nr && l[$line_nr] $($expression)+).is_err() {\n          let lines = $tmux.capture().unwrap_or_default();\n          let actual = if lines.len() > $line_nr { &lines[$line_nr] } else { \"<no line>\" };\n          Err(std::io::std::io::Error::new(std::io::std::io::ErrorKind::TimedOut, format!(\"Timed out waiting for condition on line {}, got {} but expected it to {}\", $line_nr, actual, stringify!($($expression)+))))\n        } else {\n          Ok(())\n        }\n      }?\n    };\n}\n\n#[allow(unused_macros)]\nmacro_rules! send_keys {\n    ($tmux:ident, $($key:expr),+) => {\n      $tmux.send_keys(&[$($key),+])\n    };\n}\n\n#[allow(unused_macros)]\nmacro_rules! assert_output_line {\n    ($tmux:ident, $line_nr:literal $($expression:tt)+) => {\n        let output = $tmux.output()?;\n        println!(\"Output: {output:?}\");\n        assert!(output[$line_nr] $($expression)+, \"Timed out waiting for condition on output line {}, expected it to {}\", $line_nr, stringify!($($expression)+));\n    };\n}\n\n// Ultra-short aliases for compact test writing\n// Usage: line!(t, 0 == \">\") instead of assert_line!(t, 0 == \">\")\n#[allow(unused_macros)]\nmacro_rules! line {\n    ($tmux:ident, $line_nr:literal $($expression:tt)+) => {\n        assert_line!($tmux, $line_nr $($expression)+)\n    };\n}\n\n#[allow(unused_macros)]\nmacro_rules! keys {\n    ($tmux:ident, $($key:expr),+) => {\n        send_keys!($tmux, $($key),+)\n    };\n}\n\n#[allow(unused_macros)]\nmacro_rules! out {\n    ($tmux:ident, $line_nr:literal $($expression:tt)+) => {\n        assert_output_line!($tmux, $line_nr $($expression)+)\n    };\n}\n"
  },
  {
    "path": "tests/defaults.rs",
    "content": "#[allow(dead_code)]\n#[macro_use]\nmod common;\n\ninsta_test!(vanilla_basic, [\"1\", \"2\", \"3\"], &[]);\n\n// Using 100 items to represent a larger dataset\ninsta_test!(\n    vanilla,\n    [\n        \"1\", \"2\", \"3\", \"4\", \"5\", \"6\", \"7\", \"8\", \"9\", \"10\", \"11\", \"12\", \"13\", \"14\", \"15\", \"16\", \"17\", \"18\", \"19\", \"20\",\n        \"21\", \"22\", \"23\", \"24\", \"25\", \"26\", \"27\", \"28\", \"29\", \"30\", \"31\", \"32\", \"33\", \"34\", \"35\", \"36\", \"37\", \"38\",\n        \"39\", \"40\", \"41\", \"42\", \"43\", \"44\", \"45\", \"46\", \"47\", \"48\", \"49\", \"50\", \"51\", \"52\", \"53\", \"54\", \"55\", \"56\",\n        \"57\", \"58\", \"59\", \"60\", \"61\", \"62\", \"63\", \"64\", \"65\", \"66\", \"67\", \"68\", \"69\", \"70\", \"71\", \"72\", \"73\", \"74\",\n        \"75\", \"76\", \"77\", \"78\", \"79\", \"80\", \"81\", \"82\", \"83\", \"84\", \"85\", \"86\", \"87\", \"88\", \"89\", \"90\", \"91\", \"92\",\n        \"93\", \"94\", \"95\", \"96\", \"97\", \"98\", \"99\", \"100\"\n    ],\n    &[]\n);\n\ninsta_test!(interactive_mode_command_execution, @interactive, &[\"-i\", \"--cmd\", \"echo 'foo {q}'\"], {\n    @snap;\n    @type \"bar\";\n    @snap;\n    @type \"baz\";\n    @snap;\n});\n\ninsta_test!(unicode_input, [\"\"], &[\"-q\", \"󰬈󰬉󰬊\"], {\n    @snap;\n    @type \"|\";\n    @snap;\n    @key Left;\n    @key Left;\n    @type \"|\";\n    @snap;\n    @type \"󰬈\";\n    @snap;\n});\n"
  },
  {
    "path": "tests/highlighting.rs",
    "content": "#[allow(dead_code)]\n#[macro_use]\nmod common;\n\nuse common::tmux::Keys::*;\n\nsk_test!(highlight_match, @cmd \"echo -e 'apple\\\\nbanana\\\\ngrape'\", &[\"--color=matched:9,current_match:1\"], {\n    @capture[2] contains(\"apple\");\n    @keys Str(\"pp\");\n\n    // Wait for filtering to complete - should only show apple\n    @capture[1] contains(\"1/3\");\n    @capture[2] contains(\"apple\");\n\n    @capture_colored[2] contains(\"a\");\n    @capture_colored[2] contains(\"pp\");\n    @capture_colored[2] contains(\"le\");\n\n    // Check that the 'p' characters in \"apple\" have highlighting color codes\n    @capture_colored[2] contains(\"\\x1b[38;5;1m\");\n    @capture_colored[2] contains(\"pp\\x1b[\");\n\n    @keys Enter;\n\n    @output[0] eq(\"apple\");\n});\n\nsk_test!(highlight_split_match, @cmd \"echo -e 'apple\\\\nbanana\\\\ngrape'\", &[\"--color=matched:9,current_match:1,current_bg:236\"], {\n    @capture[2] contains(\"apple\");\n\n    @keys Str(\"aaa\");\n\n    // Wait for filtering to complete - should only show banana\n    @capture[1] contains(\"1/3\");\n    @capture[2] contains(\"banana\");\n\n\n    @capture_colored[2] contains(\"b\");\n    @capture_colored[2] contains(\"a\");\n    @capture_colored[2] contains(\"n\");\n\n    // Check that matched characters have the current_match foreground color (color 1)\n    @capture_colored[2] contains(\"\\x1b[38;5;1m\");\n    // Check that the current line has the current background color (color 236)\n    @capture_colored[2] contains(\"\\x1b[48;5;236m\");\n    // Check that there are 3 matched 'a' characters with foreground color 1\n    let match_fg_pattern = \"\\x1b[38;5;1ma\";\n    @capture_colored[2] matches(match_fg_pattern).count() == 3;\n\n    @keys Enter;\n\n    @output[0] eq(\"banana\");\n});\n"
  },
  {
    "path": "tests/history.rs",
    "content": "#[allow(dead_code)]\nmod common;\n\nuse common::tmux::Keys::*;\nuse common::tmux::TmuxController;\nuse std::fs::File;\nuse std::io::Read;\nuse std::io::Result;\nuse std::io::Write;\nuse std::path::Path;\n\n#[test]\nfn query_history() -> Result<()> {\n    let mut tmux = TmuxController::new()?;\n    let histfile = tmux.tempfile()?;\n\n    File::create(&histfile)?.write_all(b\"a\\nb\\nc\")?;\n\n    tmux.start_sk(Some(\"echo -e -n 'a\\\\nb\\\\nc'\"), &[\"--history\", &histfile])?;\n    tmux.until(|l| l[0].starts_with(\">\"))?;\n\n    tmux.send_keys(&[Ctrl(&Key('p'))])?;\n    tmux.until(|l| l[0].trim() == \"> c\")?;\n\n    tmux.send_keys(&[Ctrl(&Key('p'))])?;\n    tmux.until(|l| l[0].trim() == \"> b\")?;\n\n    tmux.send_keys(&[Ctrl(&Key('p'))])?;\n    tmux.until(|l| l[0].trim() == \"> a\")?;\n\n    tmux.send_keys(&[Ctrl(&Key('n'))])?;\n    tmux.until(|l| l[0].trim() == \"> b\")?;\n\n    tmux.send_keys(&[Key('n')])?;\n    tmux.until(|l| l[0].trim() == \"> bn\")?;\n\n    tmux.send_keys(&[Enter])?;\n\n    tmux.until(|_| {\n        let mut buf = String::new();\n        File::open(Path::new(&histfile))\n            .unwrap()\n            .read_to_string(&mut buf)\n            .unwrap();\n\n        println!(\"{}\", buf);\n        buf == \"a\\nb\\nc\\nbn\"\n    })?;\n\n    Ok(())\n}\n\n#[test]\nfn cmd_history() -> Result<()> {\n    let mut tmux = TmuxController::new()?;\n    let histfile = tmux.tempfile()?;\n\n    File::create(&histfile)?.write_all(b\"a\\nb\\nc\")?;\n\n    tmux.start_sk(\n        Some(\"echo -e -n 'a\\\\nb\\\\nc'\"),\n        &[\"-i\", \"-c\", \"'echo {}'\", \"--cmd-history\", &histfile],\n    )?;\n    tmux.until(|l| l[0].starts_with(\"c>\"))?;\n\n    tmux.send_keys(&[Ctrl(&Key('p'))])?;\n    tmux.until(|l| l[0].trim() == \"c> c\")?;\n\n    tmux.send_keys(&[Ctrl(&Key('p'))])?;\n    tmux.until(|l| l[0].trim() == \"c> b\")?;\n\n    tmux.send_keys(&[Ctrl(&Key('p'))])?;\n    tmux.until(|l| l[0].trim() == \"c> a\")?;\n\n    tmux.send_keys(&[Ctrl(&Key('n'))])?;\n    tmux.until(|l| l[0].trim() == \"c> b\")?;\n\n    tmux.send_keys(&[Key('n')])?;\n    tmux.until(|l| l[0].trim() == \"c> bn\")?;\n\n    tmux.send_keys(&[Enter])?;\n\n    tmux.until(|_| {\n        let mut buf = String::new();\n        File::open(Path::new(&histfile))\n            .unwrap()\n            .read_to_string(&mut buf)\n            .unwrap();\n\n        println!(\"{}\", buf);\n        buf == \"a\\nb\\nc\\nbn\"\n    })?;\n\n    Ok(())\n}\n"
  },
  {
    "path": "tests/issues.rs",
    "content": "#[allow(dead_code)]\n#[macro_use]\nmod common;\n\ninsta_test!(issue_359_multi_regex_unicode, [\"ああa\"], &[\"--regex\", \"-q\", \"a\"], {\n  @snap;\n});\n\ninsta_test!(issue_361_literal_space_control, [\"foo  bar\", \"foo bar\"], &[\"-q\", \"foo\\\\ bar\"], {\n    @snap;\n});\n\ninsta_test!(issue_361_literal_space_invert, [\"foo  bar\", \"foo bar\"], &[\"-q\", \"!foo\\\\ bar\"], {\n    @snap;\n});\n\ninsta_test!(issue_547_null_match, [\"\\0Test Test Test\"], &[], {\n    @snap;\n    @type \"Test\";\n    @snap;\n});\n\ninsta_test!(issue_929_double_width_chars, [\"\"], &[\"-q\", \"中文测试\"], {\n    @snap;\n    @char '|';\n    @snap;\n});\n"
  },
  {
    "path": "tests/keys.rs",
    "content": "#[allow(dead_code)]\n#[macro_use]\nmod common;\n\n// Using 100 items to test filtering and navigation (representative of larger datasets)\ninsta_test!(keys_basic, [\n    \"1\", \"2\", \"3\", \"4\", \"5\", \"6\", \"7\", \"8\", \"9\", \"10\",\n    \"11\", \"12\", \"13\", \"14\", \"15\", \"16\", \"17\", \"18\", \"19\", \"20\",\n    \"21\", \"22\", \"23\", \"24\", \"25\", \"26\", \"27\", \"28\", \"29\", \"30\",\n    \"31\", \"32\", \"33\", \"34\", \"35\", \"36\", \"37\", \"38\", \"39\", \"40\",\n    \"41\", \"42\", \"43\", \"44\", \"45\", \"46\", \"47\", \"48\", \"49\", \"50\",\n    \"51\", \"52\", \"53\", \"54\", \"55\", \"56\", \"57\", \"58\", \"59\", \"60\",\n    \"61\", \"62\", \"63\", \"64\", \"65\", \"66\", \"67\", \"68\", \"69\", \"70\",\n    \"71\", \"72\", \"73\", \"74\", \"75\", \"76\", \"77\", \"78\", \"79\", \"80\",\n    \"81\", \"82\", \"83\", \"84\", \"85\", \"86\", \"87\", \"88\", \"89\", \"90\",\n    \"91\", \"92\", \"93\", \"94\", \"95\", \"96\", \"97\", \"98\", \"99\", \"100\"\n], &[], {\n    @snap;\n    @type \"99\";\n    @snap;\n});\n\n// Input navigation keys\n\ninsta_test!(keys_arrows, [\"\"], &[\"-q\", \"foo bar foo-bar\"], {\n    @snap;\n    @key Left;\n    @char '|';\n    @snap;\n    @key Right;\n    @char '|';\n    @snap;\n});\n\ninsta_test!(keys_ctrl_arrows, [\"\"], &[\"-q\", \"foo bar foo-bar\"], {\n    @snap;\n    @ctrl Left;\n    @char '|';\n    @snap;\n    @ctrl Left;\n    @char '|';\n    @snap;\n    @ctrl Right;\n    @char '|';\n    @snap;\n});\n\ninsta_test!(keys_ctrl_a, [\"\"], &[\"-q\", \"foo bar foo-bar\"], {\n    @snap;\n    @ctrl 'a';\n    @char '|';\n    @snap;\n});\n\ninsta_test!(keys_ctrl_b, [\"\"], &[\"-q\", \"foo bar foo-bar\"], {\n    @snap;\n    @ctrl 'a';\n    @char '|';\n    @snap;\n    @ctrl 'f';\n    @char '|';\n    @snap;\n});\n\ninsta_test!(keys_ctrl_e, [\"\"], &[\"-q\", \"foo bar foo-bar\"], {\n    @snap;\n    @ctrl 'a';\n    @char '|';\n    @snap;\n    @ctrl 'e';\n    @char '|';\n    @snap;\n});\n\ninsta_test!(keys_ctrl_f, [\"\"], &[\"-q\", \"foo bar foo-bar\"], {\n    @snap;\n    @ctrl 'a';\n    @char '|';\n    @snap;\n    @ctrl 'f';\n    @char '|';\n    @snap;\n});\n\ninsta_test!(keys_ctrl_h, [\"\"], &[\"-q\", \"foo bar foo-bar\"], {\n    @snap;\n    @ctrl 'h';\n    @char '|';\n    @snap;\n});\n\ninsta_test!(keys_alt_b, [\"\"], &[\"-q\", \"foo bar foo-bar\"], {\n    @snap;\n    @alt 'b';\n    @char '|';\n    @snap;\n});\n\ninsta_test!(keys_alt_f, [\"\"], &[\"-q\", \"foo bar foo-bar\"], {\n    @snap;\n    @ctrl 'a';\n    @char '|';\n    @snap;\n    @alt 'f';\n    @char '|';\n    @snap;\n});\n\n// Input manipulation keys\n\ninsta_test!(keys_bspace, [\"\"], &[\"-q\", \"foo bar foo-bar\"], {\n    @snap;\n    @key Backspace;\n    @char '|';\n    @snap;\n});\n\ninsta_test!(keys_ctrl_c, [\"\"], &[\"-q\", \"foo bar foo-bar\"], {\n    @snap;\n    @ctrl 'c';\n    @exited 130;\n});\ninsta_test!(keys_ctrl_d, [\"\"], &[\"-q\", \"foo bar foo-bar\"], {\n    @snap;\n    @ctrl 'd';\n    @exited 130;\n});\n\ninsta_test!(keys_ctrl_u, [\"\"], &[\"-q\", \"foo bar foo-bar\"], {\n    @snap;\n    @ctrl 'u';\n    @char '|';\n    @snap;\n});\n\ninsta_test!(keys_ctrl_w, [\"\"], &[\"-q\", \"foo bar foo-bar\"], {\n    @snap;\n    @ctrl 'w';\n    @char '|';\n    @snap;\n});\n\ninsta_test!(keys_ctrl_y, [\"\"], &[\"-q\", \"foo bar foo-bar\"], {\n    @snap;\n    @alt Backspace;\n    @char '|';\n    @snap;\n    @ctrl 'y';\n    @char '|';\n    @snap;\n});\n\ninsta_test!(keys_alt_d, [\"\"], &[\"-q\", \"foo bar foo-bar\"], {\n    @snap;\n    @ctrl Left;\n    @char '|';\n    @snap;\n    @ctrl Left;\n    @char '|';\n    @snap;\n    @alt 'd';\n    @char '|';\n    @snap;\n});\n\ninsta_test!(keys_alt_bspace, [\"\"], &[\"-q\", \"foo bar foo-bar\"], {\n    @snap;\n    @alt Backspace;\n    @char '|';\n    @snap;\n});\n\n// Results navigation keys\n\ninsta_test!(keys_ctrl_k, [\n    \"1\", \"2\", \"3\", \"4\", \"5\", \"6\", \"7\", \"8\", \"9\", \"10\",\n    \"11\", \"12\", \"13\", \"14\", \"15\", \"16\", \"17\", \"18\", \"19\", \"20\"\n], &[], {\n    @snap;\n    @ctrl 'k';\n    @snap;\n});\n\ninsta_test!(keys_tab, [\n    \"1\", \"2\", \"3\", \"4\", \"5\", \"6\", \"7\", \"8\", \"9\", \"10\",\n    \"11\", \"12\", \"13\", \"14\", \"15\", \"16\", \"17\", \"18\", \"19\", \"20\"\n], &[], {\n    @snap;\n    @ctrl 'k';\n    @snap;\n    @key Tab;\n    @snap;\n});\n\ninsta_test!(keys_btab, [\n    \"1\", \"2\", \"3\", \"4\", \"5\", \"6\", \"7\", \"8\", \"9\", \"10\",\n    \"11\", \"12\", \"13\", \"14\", \"15\", \"16\", \"17\", \"18\", \"19\", \"20\"\n], &[], {\n    @snap;\n    @key BackTab;\n    @snap;\n});\n\ninsta_test!(keys_tab_empty, [\"\"], &[], {\n    @snap;\n    @key Tab;\n    @snap;\n    @char 'a';\n    @snap;\n});\n"
  },
  {
    "path": "tests/keys_interactive.rs",
    "content": "#[allow(dead_code)]\n#[macro_use]\nmod common;\n\n// Basic interactive mode test\ninsta_test!(keys_interactive_basic, [\"1\", \"2\", \"3\", \"4\"], &[\"-i\"], {\n    @snap;\n    @type \"99\";\n    @snap;\n});\n\n// Input navigation keys\n\ninsta_test!(keys_interactive_arrows, @interactive, &[\"-i\", \"--cmd\", \"true\", \"--cmd-query\", \"foo bar foo-bar\"], {\n    @snap;\n    @key Left;\n    @char '|';\n    @snap;\n    @key Right;\n    @char '|';\n    @snap;\n});\n\ninsta_test!(keys_interactive_ctrl_arrows, @interactive, &[\"-i\", \"--cmd\", \"true\", \"--cmd-query\", \"foo bar foo-bar\"], {\n    @snap;\n    @ctrl Left;\n    @char '|';\n    @snap;\n    @ctrl Left;\n    @char '|';\n    @snap;\n    @ctrl Right;\n    @char '|';\n    @snap;\n});\n\ninsta_test!(keys_interactive_ctrl_a, @interactive, &[\"-i\", \"--cmd\", \"true\", \"--cmd-query\", \"foo bar foo-bar\"], {\n    @snap;\n    @ctrl 'a';\n    @char '|';\n    @snap;\n});\n\ninsta_test!(keys_interactive_ctrl_b, @interactive, &[\"-i\", \"--cmd\", \"true\", \"--cmd-query\", \"foo bar foo-bar\"], {\n    @snap;\n    @ctrl 'a';\n    @char '|';\n    @snap;\n    @ctrl 'f';\n    @char '|';\n    @snap;\n});\n\ninsta_test!(keys_interactive_ctrl_e, @interactive, &[\"-i\", \"--cmd\", \"true\", \"--cmd-query\", \"foo bar foo-bar\"], {\n    @snap;\n    @ctrl 'a';\n    @char '|';\n    @snap;\n    @ctrl 'e';\n    @char '|';\n    @snap;\n});\n\ninsta_test!(keys_interactive_ctrl_f, @interactive, &[\"-i\", \"--cmd\", \"true\", \"--cmd-query\", \"foo bar foo-bar\"], {\n    @snap;\n    @ctrl 'a';\n    @char '|';\n    @snap;\n    @ctrl 'f';\n    @char '|';\n    @snap;\n});\n\ninsta_test!(keys_interactive_ctrl_h, @interactive, &[\"-i\", \"--cmd\", \"true\", \"--cmd-query\", \"foo bar foo-bar\"], {\n    @snap;\n    @ctrl 'h';\n    @char '|';\n    @snap;\n});\n\ninsta_test!(keys_interactive_alt_b, @interactive, &[\"-i\", \"--cmd\", \"true\", \"--cmd-query\", \"foo bar foo-bar\"], {\n    @snap;\n    @alt 'b';\n    @char '|';\n    @snap;\n});\n\ninsta_test!(keys_interactive_alt_f, @interactive, &[\"-i\", \"--cmd\", \"true\", \"--cmd-query\", \"foo bar foo-bar\"], {\n    @snap;\n    @ctrl 'a';\n    @char '|';\n    @snap;\n    @alt 'f';\n    @char '|';\n    @snap;\n});\n\n// Input manipulation keys\n\ninsta_test!(keys_interactive_bspace, @interactive, &[\"-i\", \"--cmd\", \"true\", \"--cmd-query\", \"foo bar foo-bar\"], {\n    @snap;\n    @key Backspace;\n    @char '|';\n    @snap;\n});\n\ninsta_test!(keys_interactive_ctrl_c, @interactive, &[\"-i\", \"--cmd\", \"true\", \"--cmd-query\", \"foo bar foo-bar\"], {\n    @snap;\n    @ctrl 'c';\n    @exited 130;\n});\ninsta_test!(keys_interactive_ctrl_d, @interactive, &[\"-i\", \"--cmd\", \"true\", \"--cmd-query\", \"foo bar foo-bar\"], {\n    @snap;\n    @ctrl 'd';\n    @exited 130;\n});\n\ninsta_test!(keys_interactive_ctrl_u, @interactive, &[\"-i\", \"--cmd\", \"true\", \"--cmd-query\", \"foo bar foo-bar\"], {\n    @snap;\n    @ctrl 'u';\n    @char '|';\n    @snap;\n});\n\ninsta_test!(keys_interactive_ctrl_w, @interactive, &[\"-i\", \"--cmd\", \"true\", \"--cmd-query\", \"foo bar foo-bar\"], {\n    @snap;\n    @ctrl 'w';\n    @char '|';\n    @snap;\n});\n\ninsta_test!(keys_interactive_ctrl_y, @interactive, &[\"-i\", \"--cmd\", \"true\", \"--cmd-query\", \"foo bar foo-bar\"], {\n    @snap;\n    @alt Backspace;\n    @char '|';\n    @snap;\n    @ctrl 'y';\n    @char '|';\n    @snap;\n});\n\ninsta_test!(keys_interactive_alt_d, @interactive, &[\"-i\", \"--cmd\", \"true\", \"--cmd-query\", \"foo bar foo-bar\"], {\n    @snap;\n    @ctrl Left;\n    @char '|';\n    @snap;\n    @ctrl Left;\n    @char '|';\n    @snap;\n    @alt 'd';\n    @char '|';\n    @snap;\n});\n\ninsta_test!(keys_interactive_alt_bspace, @interactive, &[\"-i\", \"--cmd\", \"true\", \"--cmd-query\", \"foo bar foo-bar\"], {\n    @snap;\n    @alt Backspace;\n    @char '|';\n    @snap;\n});\n\n// Results navigation keys\n\ninsta_test!(keys_interactive_ctrl_k, [\"1\", \"2\", \"3\", \"4\"], &[\"-i\"], {\n    @snap;\n    @ctrl 'k';\n    @snap;\n});\n\ninsta_test!(keys_interactive_tab, [\"1\", \"2\", \"3\", \"4\"], &[\"-i\"], {\n    @snap;\n    @ctrl 'k';\n    @snap;\n    @key Tab;\n    @snap;\n});\n\ninsta_test!(keys_interactive_btab, [\"1\", \"2\", \"3\", \"4\"], &[\"-i\"], {\n    @snap;\n    @key BackTab;\n    @snap;\n});\n\n// Tests Enter and Ctrl-M for accepting selection\n\ninsta_test!(keys_interactive_enter, [\"1\", \"2\", \"3\", \"4\"], &[\"-i\"], {\n    @snap;\n    @key Enter;\n    @assert(|h: &common::insta::TestHarness| h.skim.app().should_quit);\n    @assert(|h: &common::insta::TestHarness| h.skim.app().item_list.selected().unwrap().text() == \"1\");\n});\n"
  },
  {
    "path": "tests/layout.rs",
    "content": "#[allow(dead_code)]\n#[macro_use]\nmod common;\n\nfn args<'a>(extra: &[&'a str]) -> Vec<&'a str> {\n    let base_args = &[\n        \"-q\",\n        \"a\",\n        \"--header\",\n        \"header\",\n        \"--header-lines\",\n        \"2\",\n        \"--prompt\",\n        \"prompt \",\n        \"--selector\",\n        \"sel \",\n        \"--multi-selector\",\n        \"multi-sel \",\n        \"-m\",\n        \"--pre-select-n\",\n        \"2\",\n    ];\n    [base_args, extra].concat()\n}\n\ninsta_test!(layout_default, [\"header line 1\", \"header line 2\", \"a\", \"b\", \"c\", \"ab\", \"ac\"], &args(&[]), {\n    @snap;\n});\n\ninsta_test!(layout_border, [\"header line 1\", \"header line 2\", \"a\", \"b\", \"c\", \"ab\", \"ac\"], &args(&[\"--border\"]), {\n    @snap;\n});\n\ninsta_test!(layout_reverse, [\"header line 1\", \"header line 2\", \"a\", \"b\", \"c\", \"ab\", \"ac\"], &args(&[\"--layout\", \"reverse\"]), {\n    @snap;\n});\n\ninsta_test!(layout_reverse_border, [\"header line 1\", \"header line 2\", \"a\", \"b\", \"c\", \"ab\", \"ac\"], &args(&[\"--layout\", \"reverse\", \"--border\"]), {\n    @snap;\n});\n\ninsta_test!(layout_reverse_list, [\"header line 1\", \"header line 2\", \"a\", \"b\", \"c\", \"ab\", \"ac\"], &args(&[\"--layout\", \"reverse-list\"]), {\n    @snap;\n});\n\ninsta_test!(layout_reverse_list_border, [\"header line 1\", \"header line 2\", \"a\", \"b\", \"c\", \"ab\", \"ac\"], &args(&[\"--layout\", \"reverse-list\", \"--border\"]), {\n    @snap;\n});\n"
  },
  {
    "path": "tests/listen.rs",
    "content": "#[allow(dead_code)]\n#[macro_use]\nmod common;\n\nuse common::tmux::Keys::*;\nuse rand::{RngExt as _, distr::Alphabetic};\nuse std::{\n    io::{Result, Write as _},\n    process::{Child, Command, Stdio},\n};\n\nuse common::tmux::TmuxController;\n\nuse crate::common::SK;\n\nfn connect(name: &str) -> Result<Child> {\n    Command::new(\"/bin/sh\")\n        .arg(\"-c\")\n        .arg(format!(\"{SK} --remote {name}\"))\n        .stdin(Stdio::piped())\n        .spawn()\n}\nfn send(child: &mut Child, msg: &str) -> Result<()> {\n    let mut b = msg.bytes().collect::<Vec<_>>();\n    b.push(b'\\n');\n    child.stdin.as_mut().map(|s| s.write_all(&b));\n    Ok(())\n}\n\nfn setup(name: &str, extra_args: &[&str]) -> Result<(TmuxController, Child)> {\n    let mut tmux = TmuxController::new_named(name)?;\n    let socket_name = format!(\n        \"sk-test-{name}{}\",\n        rand::rng()\n            .sample_iter(&Alphabetic)\n            .take(4)\n            .map(char::from)\n            .collect::<String>()\n    );\n    tmux.start_sk(\n        Some(&format!(\"echo -n -e '{}'\", \"a\\\\nb\\\\nc\\\\nd\")),\n        &[&[\"--listen\", &socket_name], extra_args].concat(),\n    )?;\n    tmux.until(|l| !l.is_empty() && l[0].starts_with(\">\"))?;\n    let stream = connect(&socket_name)?;\n    Ok((tmux, stream))\n}\n\n#[test]\nfn listen_up() -> std::io::Result<()> {\n    let (tmux, mut stream) = setup(\"up\", &[])?;\n    sk_test!(@expand tmux;\n        @capture[2]starts_with(\"> a\");\n        send(&mut stream,\"up(2)\")? ;\n        @capture[2]trim().starts_with(\"a\");\n        @capture[4]starts_with(\"> c\");\n    );\n    Ok(())\n}\n\n#[test]\nfn listen_down() -> std::io::Result<()> {\n    let (tmux, mut stream) = setup(\"down\", &[])?;\n    sk_test!(@expand tmux;\n        @capture[2]starts_with(\"> a\");\n        @keys Up, Up;\n        @capture[2]trim().starts_with(\"a\");\n        @capture[4]starts_with(\"> c\");\n        send(&mut stream, \"down(2)\")?;\n        @capture[2]starts_with(\"> a\");\n    );\n    Ok(())\n}\n\n#[test]\nfn listen_abort() -> std::io::Result<()> {\n    let (tmux, mut stream) = setup(\"abort\", &[])?;\n    sk_test!(@expand tmux;\n        send(&mut stream, \"abort\")?;\n        @capture[0]trim().contains(\"$\");\n    );\n    Ok(())\n}\n\n// Test Accept action - adapted to use \"a\" instead of \"apple\"\n#[test]\nfn listen_accept() -> std::io::Result<()> {\n    let (tmux, mut stream) = setup(\"accept\", &[])?;\n    sk_test!(@expand tmux;\n        @capture[2]starts_with(\"> a\");\n        send(&mut stream, \"accept\")?;\n        @output[0]eq(\"a\");\n    );\n    Ok(())\n}\n\n// Test Accept with key - adapted to use \"a\" instead of \"apple\"\n#[test]\nfn listen_accept_key() -> std::io::Result<()> {\n    let (tmux, mut stream) = setup(\"accept_key\", &[])?;\n    sk_test!(@expand tmux;\n        send(&mut stream, \"accept(ctrl-a)\")?;\n        @output[0]eq(\"ctrl-a\");\n        @output[1]eq(\"a\");\n    );\n    Ok(())\n}\n\n#[test]\nfn listen_add_char() -> std::io::Result<()> {\n    let (tmux, mut stream) = setup(\"add_char\", &[])?;\n    sk_test!(@expand tmux;\n        send(&mut stream, \"add-char(a)\")?;\n        @capture[0]trim().eq(\"> a\");\n    );\n    Ok(())\n}\n\n#[test]\nfn listen_backward_char() -> std::io::Result<()> {\n    let (tmux, mut stream) = setup(\"backward_char\", &[])?;\n    sk_test!(@expand tmux;\n        @keys Str(\"hello\");\n        @capture[0]starts_with(\"> hello\");\n        send(&mut stream, \"backward-char\")?;\n        @keys Key('|');\n        @capture[0]trim().eq(\"> hell|o\");\n    );\n    Ok(())\n}\n\n#[test]\nfn listen_backward_delete_char() -> std::io::Result<()> {\n    let (tmux, mut stream) = setup(\"backward_delete_char\", &[])?;\n    sk_test!(@expand tmux;\n        @keys Str(\"test\");\n        @capture[0]trim().eq(\"> test\");\n        send(&mut stream, \"backward-delete-char\")?;\n        @capture[0]trim().eq(\"> tes\");\n    );\n    Ok(())\n}\n\n#[test]\nfn listen_backward_delete_char_eof() -> std::io::Result<()> {\n    let (tmux, mut stream) = setup(\"backward_delete_char_eof\", &[])?;\n    sk_test!(@expand tmux;\n        @keys Str(\"x\");\n        @capture[0]trim().eq(\"> x\");\n        send(&mut stream, \"backward-delete-char/eof\")?;\n        @capture[0]trim().eq(\">\");\n    );\n    Ok(())\n}\n\n#[test]\nfn listen_backward_kill_word() -> std::io::Result<()> {\n    let (tmux, mut stream) = setup(\"backward_kill_word\", &[])?;\n    sk_test!(@expand tmux;\n        @keys Str(\"hello world\");\n        @capture[0]trim().eq(\"> hello world\");\n        send(&mut stream, \"backward-kill-word\")?;\n        @capture[0]trim().eq(\"> hello\");\n    );\n    Ok(())\n}\n\n#[test]\nfn listen_backward_word() -> std::io::Result<()> {\n    let (tmux, mut stream) = setup(\"backward_word\", &[])?;\n    sk_test!(@expand tmux;\n        @keys Str(\"hello world\");\n        @capture[0]starts_with(\"> hello world\");\n        send(&mut stream, \"backward-word\")?;\n        @keys Key('|');\n        @capture[0]trim().eq(\"> hello |world\");\n    );\n    Ok(())\n}\n\n#[test]\nfn listen_end_of_line() -> std::io::Result<()> {\n    let (tmux, mut stream) = setup(\"end_of_line\", &[])?;\n    sk_test!(@expand tmux;\n        @keys Str(\"hello\");\n        @capture[0]trim().eq(\"> hello\");\n        send(&mut stream, \"beginning-of-line\")?;\n        send(&mut stream, \"end-of-line\")?;\n        @keys Key('X');\n        @capture[0]trim().eq(\"> helloX\");\n    );\n    Ok(())\n}\n\n#[test]\nfn listen_first() -> std::io::Result<()> {\n    let (tmux, mut stream) = setup(\"first\", &[])?;\n    sk_test!(@expand tmux;\n        @keys Up, Up;\n        @capture[4]starts_with(\"> c\");\n        send(&mut stream, \"first\")?;\n        @capture[2]starts_with(\"> a\");\n    );\n    Ok(())\n}\n\n#[test]\nfn listen_forward_char() -> std::io::Result<()> {\n    let (tmux, mut stream) = setup(\"forward_char\", &[])?;\n    sk_test!(@expand tmux;\n        @keys Str(\"hello\");\n        @capture[0]trim().eq(\"> hello\");\n        send(&mut stream, \"beginning-of-line\")?;\n        send(&mut stream, \"forward-char\")?;\n        @keys Key('X');\n        @capture[0]trim().eq(\"> hXello\");\n    );\n    Ok(())\n}\n\n#[test]\nfn listen_forward_word() -> std::io::Result<()> {\n    let (tmux, mut stream) = setup(\"forward_word\", &[])?;\n    sk_test!(@expand tmux;\n        @keys Str(\"hello world\");\n        @capture[0]trim().eq(\"> hello world\");\n        send(&mut stream, \"beginning-of-line\")?;\n        send(&mut stream, \"forward-word\")?;\n        @keys Key('X');\n        @capture[0]trim().eq(\"> helloX world\");\n    );\n    Ok(())\n}\n\n#[test]\nfn listen_kill_line() -> std::io::Result<()> {\n    let (tmux, mut stream) = setup(\"kill_line\", &[])?;\n    sk_test!(@expand tmux;\n        @keys Str(\"hello world\");\n        @capture[0]trim().eq(\"> hello world\");\n        send(&mut stream, \"backward-word\")?;\n        send(&mut stream, \"kill-line\")?;\n        @capture[0]trim().eq(\"> hello\");\n    );\n    Ok(())\n}\n\n#[test]\nfn listen_kill_word() -> std::io::Result<()> {\n    let (tmux, mut stream) = setup(\"kill_word\", &[])?;\n    sk_test!(@expand tmux;\n        @keys Str(\"hello world\");\n        @capture[0]trim().eq(\"> hello world\");\n        send(&mut stream, \"beginning-of-line\")?;\n        send(&mut stream, \"kill-word\")?;\n        @capture[0]trim().eq(\">  world\");\n    );\n    Ok(())\n}\n\n#[test]\nfn listen_last() -> std::io::Result<()> {\n    let (tmux, mut stream) = setup(\"last\", &[])?;\n    sk_test!(@expand tmux;\n        @capture[2]starts_with(\"> a\");\n        @capture[5]trim().eq(\"d\");\n        send(&mut stream, \"last\")?;\n        @capture[5]starts_with(\"> d\");\n    );\n    Ok(())\n}\n\n// Test Reload action with command\n#[test]\nfn listen_reload_cmd() -> std::io::Result<()> {\n    let (tmux, mut stream) = setup(\"reload_cmd\", &[])?;\n    sk_test!(@expand tmux;\n        @capture[1]trim().contains(\"4/4\");\n        send(&mut stream, \"reload(printf 'x\\\\ny\\\\nz'))\")?;\n        @capture[2]starts_with(\"> x\");\n    );\n    Ok(())\n}\n\n#[test]\nfn listen_select_all() -> std::io::Result<()> {\n    let (tmux, mut stream) = setup(\"select_all\", &[\"-m\"])?;\n    sk_test!(@expand tmux;\n        send(&mut stream, \"select-all\")?;\n        @capture[2]trim().eq(\">>a\");\n        @capture[3]trim().eq(\">b\");\n        @capture[4]trim().eq(\">c\");\n    );\n    Ok(())\n}\n\n#[test]\nfn listen_select_row() -> std::io::Result<()> {\n    let (tmux, mut stream) = setup(\"select_row\", &[\"-m\"])?;\n    sk_test!(@expand tmux;\n        @capture[2]starts_with(\"> a\");\n        send(&mut stream, \"select-row(2)\")?;\n        @capture[2]trim().eq(\"> a\");\n        @capture[4]trim().eq(\">c\");\n    );\n    Ok(())\n}\n\n#[test]\nfn listen_select() -> std::io::Result<()> {\n    let (tmux, mut stream) = setup(\"select\", &[\"-m\"])?;\n    sk_test!(@expand tmux;\n        send(&mut stream, \"select\")?;\n        @capture[2]starts_with(\">>\");\n    );\n    Ok(())\n}\n\n#[test]\nfn listen_toggle() -> std::io::Result<()> {\n    let (tmux, mut stream) = setup(\"toggle\", &[\"-m\"])?;\n    sk_test!(@expand tmux;\n        send(&mut stream, \"toggle\")?;\n        @capture[2]starts_with(\">>a\");\n        send(&mut stream, \"toggle\")?;\n        @capture[2]starts_with(\"> a\");\n    );\n    Ok(())\n}\n\n#[test]\nfn listen_toggle_all() -> std::io::Result<()> {\n    let (tmux, mut stream) = setup(\"toggle_all\", &[\"-m\"])?;\n    sk_test!(@expand tmux;\n        send(&mut stream, \"toggle-all\")?;\n        @capture[2]starts_with(\">>a\");\n        @capture[3]trim().eq(\">b\");\n        @capture[4]trim().eq(\">c\");\n    );\n    Ok(())\n}\n\n#[test]\nfn listen_toggle_in() -> std::io::Result<()> {\n    let (tmux, mut stream) = setup(\"toggle_in\", &[\"-m\"])?;\n    sk_test!(@expand tmux;\n        @keys Up;\n        @capture[2]trim().starts_with(\"a\");\n        @capture[3]starts_with(\"> b\");\n        send(&mut stream, \"toggle-in\")?;\n        @capture[2]starts_with(\"> a\");\n        @capture[3]trim().starts_with(\">b\");\n    );\n    Ok(())\n}\n\n#[test]\nfn listen_toggle_out() -> std::io::Result<()> {\n    let (tmux, mut stream) = setup(\"toggle_out\", &[\"-m\"])?;\n    sk_test!(@expand tmux;\n        @capture[2]starts_with(\"> a\");\n        @capture[3]trim().starts_with(\"b\");\n        send(&mut stream, \"toggle-out\")?;\n        @capture[2]trim().starts_with(\">a\");\n        @capture[3]starts_with(\"> b\");\n    );\n    Ok(())\n}\n\n// Test Top action (alias for First)\n#[test]\nfn listen_top() -> std::io::Result<()> {\n    let (tmux, mut stream) = setup(\"top\", &[])?;\n    sk_test!(@expand tmux;\n        @keys Up, Up;\n        @capture[4]starts_with(\"> c\");\n        send(&mut stream, \"top\")?;\n        @capture[2]starts_with(\"> a\");\n    );\n    Ok(())\n}\n\n#[test]\nfn listen_unix_line_discard() -> std::io::Result<()> {\n    let (tmux, mut stream) = setup(\"unix_line_discard\", &[])?;\n    sk_test!(@expand tmux;\n        @keys Str(\"hello world\");\n        @capture[0]trim().eq(\"> hello world\");\n        send(&mut stream, \"unix-line-discard\")?;\n        @capture[0]trim().eq(\">\");\n    );\n    Ok(())\n}\n\n#[test]\nfn listen_unix_word_rubout() -> std::io::Result<()> {\n    let (tmux, mut stream) = setup(\"unix_word_rubout\", &[])?;\n    sk_test!(@expand tmux;\n        @keys Str(\"hello world\");\n        @capture[0]trim().eq(\"> hello world\");\n        send(&mut stream, \"unix-word-rubout\")?;\n        @capture[0]trim().eq(\"> hello\");\n    );\n    Ok(())\n}\n\n#[test]\nfn listen_yank() -> std::io::Result<()> {\n    let (tmux, mut stream) = setup(\"yank\", &[])?;\n    sk_test!(@expand tmux;\n        @keys Str(\"hello\");\n        @capture[0]trim().eq(\"> hello\");\n        send(&mut stream, \"backward-kill-word\")?;\n        @capture[0]trim().eq(\">\");\n        send(&mut stream, \"yank\")?;\n        @capture[0]trim().eq(\"> hello\");\n    );\n    Ok(())\n}\n"
  },
  {
    "path": "tests/matcher.rs",
    "content": "#[allow(dead_code)]\n#[macro_use]\nmod common;\n\nconst INPUT_ITEMS: [&str; 49] = [\n    \"src/util.rs\",\n    \"src/completions.rs\",\n    \"src/manpage.rs\",\n    \"src/tmux.rs\",\n    \"src/helper/mod.rs\",\n    \"src/helper/selector.rs\",\n    \"src/helper/item_reader.rs\",\n    \"src/helper/item.rs\",\n    \"src/helper/macros.rs\",\n    \"src/options.rs\",\n    \"src/field.rs\",\n    \"src/matcher.rs\",\n    \"src/output.rs\",\n    \"src/item.rs\",\n    \"src/bin/main.rs\",\n    \"src/binds.rs\",\n    \"src/prelude.rs\",\n    \"src/theme.rs\",\n    \"src/engine/mod.rs\",\n    \"src/engine/util.rs\",\n    \"src/engine/fuzzy.rs\",\n    \"src/engine/all.rs\",\n    \"src/engine/split.rs\",\n    \"src/engine/regexp.rs\",\n    \"src/engine/factory.rs\",\n    \"src/engine/normalized.rs\",\n    \"src/engine/exact.rs\",\n    \"src/engine/andor.rs\",\n    \"src/skim_item.rs\",\n    \"src/fuzzy_matcher/mod.rs\",\n    \"src/fuzzy_matcher/util.rs\",\n    \"src/fuzzy_matcher/skim.rs\",\n    \"src/fuzzy_matcher/frizbee.rs\",\n    \"src/fuzzy_matcher/clangd.rs\",\n    \"src/tui/mod.rs\",\n    \"src/tui/util.rs\",\n    \"src/tui/options.rs\",\n    \"src/tui/widget.rs\",\n    \"src/tui/preview.rs\",\n    \"src/tui/item_list.rs\",\n    \"src/tui/event.rs\",\n    \"src/tui/backend.rs\",\n    \"src/tui/app.rs\",\n    \"src/tui/input.rs\",\n    \"src/tui/statusline.rs\",\n    \"src/tui/header.rs\",\n    \"src/lib.rs\",\n    \"src/spinlock.rs\",\n    \"src/reader.rs\",\n];\n\ninsta_test!(matcher_default, INPUT_ITEMS, &[\"-q\", \"stum\"], {\n    @snap;\n});\n\ninsta_test!(matcher_skim_v2, INPUT_ITEMS, &[\"-q\", \"stum\", \"--algo\", \"skim_v2\"], {\n    @snap;\n});\ninsta_test!(matcher_clangd, INPUT_ITEMS, &[\"-q\", \"stum\", \"--algo\", \"clangd\"], {\n    @snap;\n});\ninsta_test!(matcher_frizbee, INPUT_ITEMS, &[\"-q\", \"stum\", \"--algo\", \"frizbee\", \"--no-typos\"], {\n    @snap;\n});\ninsta_test!(matcher_frizbee_typos, INPUT_ITEMS, &[\"-q\", \"stum\", \"--algo\", \"frizbee\", \"--typos\"], {\n    @snap;\n});\ninsta_test!(matcher_fzy, INPUT_ITEMS, &[\"-q\", \"stum\", \"--algo\", \"fzy\", \"--no-typos\"], {\n    @snap;\n});\ninsta_test!(matcher_fzy_typos, INPUT_ITEMS, &[\"-q\", \"stum\", \"--algo\", \"fzy\", \"--typos\"], {\n    @snap;\n});\ninsta_test!(matcher_arinae, INPUT_ITEMS, &[\"-q\", \"stum\", \"--algo\", \"arinae\", \"--no-typos\"], {\n    @snap;\n});\ninsta_test!(matcher_arinae_typos, INPUT_ITEMS, &[\"-q\", \"stum\", \"--algo\", \"arinae\", \"--typos\"], {\n    @snap;\n});\n"
  },
  {
    "path": "tests/normalize.rs",
    "content": "#[allow(dead_code)]\n#[macro_use]\nmod common;\n\n// Test normalize: accented item matches unaccented query\ninsta_test!(insta_normalize_accented_item_unaccented_query, [\"café\", \"cafe\", \"tea\"], &[\"--normalize\"], {\n    @snap;\n    @type \"cafe\";\n    @snap;\n});\n\n// Test normalize: unaccented item matches accented query\ninsta_test!(insta_normalize_unaccented_item_accented_query, [\"café\", \"cafe\", \"tea\"], &[\"--normalize\"], {\n    @snap;\n    @type \"café\";\n    @snap;\n});\n\n// Test without normalize: accented item does NOT match unaccented query\ninsta_test!(insta_no_normalize_accented_item, [\"café\", \"cafe\", \"tea\"], &[], {\n    @snap;\n    @type \"cafe\";\n    @snap;\n});\n\n// Test normalize with multiple diacritics\ninsta_test!(insta_normalize_multiple_diacritics, [\"naïve\", \"naive\", \"résumé\", \"resume\"], &[\"--normalize\"], {\n    @snap;\n    @type \"naive\";\n    @snap;\n    @ctrl 'w';\n    @type \"resume\";\n    @snap;\n});\n\n// Test normalize with Cyrillic (should not affect non-Latin scripts)\ninsta_test!(insta_normalize_cyrillic, [\"слово\", \"Слово\"], &[\"--normalize\"], {\n    @snap;\n    @type \"слово\";\n    @snap;\n});\n\n// Test normalize with combined characters (e.g., ñ)\ninsta_test!(insta_normalize_combined_chars, [\"señor\", \"senor\", \"mañana\", \"manana\"], &[\"--normalize\"], {\n    @snap;\n    @type \"senor\";\n    @snap;\n    @ctrl 'w';\n    @type \"manana\";\n    @snap;\n});\n\n// Test normalize with exact match prefix (')\ninsta_test!(insta_normalize_exact_match, [\"café\", \"cafe\", \"cafeína\"], &[\"--normalize\"], {\n    @snap;\n    @type \"'cafe\";\n    @snap;\n});\n\n// Test normalize with negation (!)\ninsta_test!(insta_normalize_negation, [\"café\", \"cafe\", \"tea\"], &[\"--normalize\"], {\n    @snap;\n    @type \"!cafe\";\n    @snap;\n});\n\n// Test normalize with prefix match (^)\ninsta_test!(insta_normalize_prefix, [\"café con leche\", \"cafe solo\", \"té verde\"], &[\"--normalize\"], {\n    @snap;\n    @type \"^cafe\";\n    @snap;\n});\n\n// Test normalize with suffix match ($)\ninsta_test!(insta_normalize_suffix, [\"mi café\", \"the cafe\", \"green tea\"], &[\"--normalize\"], {\n    @snap;\n    @type \"cafe$\";\n    @snap;\n});\n\n// Test normalize combined with case insensitivity\ninsta_test!(insta_normalize_case_insensitive, [\"Café\", \"CAFE\", \"café\", \"cafe\"], &[\"--normalize\", \"--case\", \"ignore\"], {\n    @snap;\n    @type \"cafe\";\n    @snap;\n});\n\n// Test normalize with German umlauts\ninsta_test!(insta_normalize_umlauts, [\"über\", \"uber\", \"größe\", \"grosse\"], &[\"--normalize\"], {\n    @snap;\n    @type \"uber\";\n    @snap;\n    @ctrl 'w';\n    @type \"grosse\";\n    @snap;\n});\n"
  },
  {
    "path": "tests/options.rs",
    "content": "use crate::common::SK;\nuse std::process::Command;\n\n#[allow(dead_code)]\n#[macro_use]\nmod common;\n\ninsta_test!(opt_with_nth_preview, [\"f1,f2,f3,f4\"], &[\"--delimiter\", \",\", \"--with-nth\", \"2..\", \"--preview\", \"echo X{1}Y\"], {\n    @snap;\n});\n\n// Use info=hidden to hide the spinner\ninsta_test!(opt_min_query_length, [\"line1\", \"line2\", \"line3\"], &[\"--min-query-length\", \"3\", \"--info\", \"hidden\"], {\n    @snap;\n    @type \"li\";\n    @snap;\n    @char 'n';\n    @snap;\n});\n\n// Use info=hidden to hide the spinner\ninsta_test!(opt_min_query_length_interactive, @interactive, &[\"-i\", \"--min-query-length\", \"3\", \"--cmd\", \"printf 'line1\\\\nline2\\\\nline3'\", \"--info\", \"hidden\"], {\n    @snap;\n    @type \"li\";\n    @snap;\n    @char 'n';\n    @snap;\n});\n\ninsta_test!(opt_with_nth_1, [\"f1,f2,f3,f4\"], &[\"--delimiter\", \",\", \"--with-nth\", \"1\"], {\n    @snap;\n});\n\ninsta_test!(opt_with_nth_2, [\"f1,f2,f3,f4\"], &[\"--delimiter\", \",\", \"--with-nth\", \"2\"], {\n    @snap;\n});\n\ninsta_test!(opt_with_nth_4, [\"f1,f2,f3,f4\"], &[\"--delimiter\", \",\", \"--with-nth\", \"4\"], {\n    @snap;\n});\n\ninsta_test!(opt_with_nth_oob, [\"f1,f2,f3,f4\"], &[\"--delimiter\", \",\", \"--with-nth\", \"5\"], {\n    @snap;\n});\n\ninsta_test!(opt_with_nth_neg_1, [\"f1,f2,f3,f4\"], &[\"--delimiter\", \",\", \"--with-nth=-1\"], {\n    @snap;\n});\n\ninsta_test!(opt_with_nth_neg_2, [\"f1,f2,f3,f4\"], &[\"--delimiter\", \",\", \"--with-nth=-2\"], {\n    @snap;\n});\n\ninsta_test!(opt_with_nth_neg_4, [\"f1,f2,f3,f4\"], &[\"--delimiter\", \",\", \"--with-nth=-4\"], {\n    @snap;\n});\n\ninsta_test!(opt_with_nth_oob_4, [\"f1,f2,f3,f4\"], &[\"--delimiter\", \",\", \"--with-nth=-5\"], {\n    @snap;\n});\n\ninsta_test!(opt_with_nth_range_to_end, [\"f1,f2,f3,f4\"], &[\"--delimiter\", \",\", \"--with-nth\", \"2..\"], {\n    @snap;\n});\n\ninsta_test!(opt_with_nth_range_from_start, [\"f1,f2,f3,f4\"], &[\"--delimiter\", \",\", \"--with-nth\", \"..3\"], {\n    @snap;\n});\n\ninsta_test!(opt_with_nth_range_closed, [\"f1,f2,f3,f4\"], &[\"--delimiter\", \",\", \"--with-nth\", \"2..3\"], {\n    @snap;\n});\n\ninsta_test!(opt_with_nth_range_desc, [\"f1,f2,f3,f4\"], &[\"--delimiter\", \",\", \"--with-nth\", \"3..2\"], {\n    @snap;\n});\n\ninsta_test!(opt_nth_1, [\"f1,f2,f3,f4\"], &[\"--delimiter\", \",\", \"--nth\", \"1\"], {\n    @snap;\n    @char '1';\n    @snap;\n    @ctrl 'w';\n    @snap;\n    @char '2';\n    @snap;\n});\n\ninsta_test!(opt_nth_2, [\"f1,f2,f3,f4\"], &[\"--delimiter\", \",\", \"--nth\", \"2\"], {\n    @snap;\n    @char '2';\n    @snap;\n    @ctrl 'w';\n    @snap;\n    @char '1';\n    @snap;\n});\n\ninsta_test!(opt_nth_4, [\"f1,f2,f3,f4\"], &[\"--delimiter\", \",\", \"--nth\", \"4\"], {\n    @snap;\n    @char '4';\n    @snap;\n    @ctrl 'w';\n    @snap;\n    @char '1';\n    @snap;\n});\n\ninsta_test!(opt_nth_oob, [\"f1,f2,f3,f4\"], &[\"--delimiter\", \",\", \"--nth\", \"5\"], {\n    @snap;\n    @char '1';\n    @snap;\n});\n\ninsta_test!(opt_nth_neg_1, [\"f1,f2,f3,f4\"], &[\"--delimiter\", \",\", \"--nth=-1\"], {\n    @snap;\n    @char '4';\n    @snap;\n    @ctrl 'w';\n    @snap;\n    @char '1';\n    @snap;\n});\n\ninsta_test!(opt_nth_neg_2, [\"f1,f2,f3,f4\"], &[\"--delimiter\", \",\", \"--nth=-2\"], {\n    @snap;\n    @char '3';\n    @snap;\n    @ctrl 'w';\n    @snap;\n    @char '1';\n    @snap;\n});\n\ninsta_test!(opt_nth_neg_4, [\"f1,f2,f3,f4\"], &[\"--delimiter\", \",\", \"--nth=-4\"], {\n    @snap;\n    @char '1';\n    @snap;\n    @ctrl 'w';\n    @snap;\n    @char '2';\n    @snap;\n});\n\ninsta_test!(opt_nth_neg_oob, [\"f1,f2,f3,f4\"], &[\"--delimiter\", \",\", \"--nth=-5\"], {\n    @snap;\n    @char '1';\n    @snap;\n});\n\ninsta_test!(opt_nth_range_to_end, [\"f1,f2,f3,f4\"], &[\"--delimiter\", \",\", \"--nth\", \"2..\"], {\n    @snap;\n    @char '3';\n    @snap;\n    @ctrl 'w';\n    @snap;\n    @char '1';\n    @snap;\n});\n\ninsta_test!(opt_nth_range_from_start, [\"f1,f2,f3,f4\"], &[\"--delimiter\", \",\", \"--nth\", \"..3\"], {\n    @snap;\n    @char '1';\n    @snap;\n    @ctrl 'w';\n    @snap;\n    @char '4';\n    @snap;\n});\n\ninsta_test!(opt_nth_range_closed, [\"f1,f2,f3,f4\"], &[\"--delimiter\", \",\", \"--nth\", \"2..3\"], {\n    @snap;\n    @char '2';\n    @snap;\n    @ctrl 'w';\n    @snap;\n    @char '3';\n    @snap;\n    @ctrl 'w';\n    @snap;\n    @char '1';\n    @snap;\n    @ctrl 'w';\n    @snap;\n    @char '4';\n    @snap;\n});\n\ninsta_test!(opt_nth_range_dec, [\"f1,f2,f3,f4\"], &[\"--delimiter\", \",\", \"--nth\", \"3..2\"], {\n    @snap;\n    @char '1';\n    @snap;\n});\n\ninsta_test!(opt_hscroll_begin, [&format!(\"b{}\", &[\"a\"; 1000].join(\"\"))], &[\"-q\", \"b\"], {\n    @snap;\n});\n\ninsta_test!(opt_hscroll_middle, [&format!(\"{}b{}\", &[\"a\"; 1000].join(\"\"), &[\"a\"; 1000].join(\"\"))], &[\"-q\", \"b\"], {\n    @snap;\n});\n\ninsta_test!(opt_hscroll_end, [&format!(\"{}b\", &[\"a\"; 1000].join(\"\"))], &[\"-q\", \"b\"], {\n    @snap;\n});\n\ninsta_test!(opt_no_hscroll, [&format!(\"{}b\", &[\"a\"; 1000].join(\"\"))], &[\"-q\", \"b\", \"--no-hscroll\"], {\n    @snap;\n});\n\ninsta_test!(opt_tabstop_default, [\"a\\tb\"], &[], {\n    @snap;\n});\n\ninsta_test!(opt_tabstop_1, [\"a\\tb\"], &[\"--tabstop\", \"1\"], {\n    @snap;\n});\n\ninsta_test!(opt_tabstop_3, [\"aa\\tb\"], &[\"--tabstop\", \"3\"], {\n    @snap;\n});\n\ninsta_test!(opt_info_control, [\"a\", \"b\", \"c\"], &[], {\n    @snap;\n    @char 'a';\n    @snap;\n});\n\ninsta_test!(opt_info_default, [\"a\", \"b\", \"c\"], &[\"--info\", \"default\"], {\n    @snap;\n    @char 'a';\n    @snap;\n});\n\ninsta_test!(opt_no_info, [\"a\", \"b\", \"c\"], &[\"--no-info\"], {\n    @snap;\n});\n\ninsta_test!(opt_info_hidden, [\"a\", \"b\", \"c\"], &[\"--info\", \"hidden\"], {\n    @snap;\n});\n\ninsta_test!(opt_info_inline, [\"a\", \"b\", \"c\"], &[\"--info\", \"inline\"], {\n    @snap;\n    @char 'a';\n    @snap;\n});\n\ninsta_test!(opt_inline_info, [\"a\", \"b\", \"c\"], &[\"--inline-info\"], {\n    @snap;\n    @char 'a';\n    @snap;\n});\n\ninsta_test!(opt_header_only, [\"a\", \"b\", \"c\"], &[\"--header\", \"test_header\"], {\n    @snap;\n});\n\ninsta_test!(opt_header_multiline, [\"a\", \"b\", \"c\"], &[\"--header\", \"header 1\\nheader 2\"], {\n    @snap;\n});\n\ninsta_test!(opt_header_inline_info, [\"a\", \"b\", \"c\"], &[\"--header\", \"test_header\", \"--inline-info\"], {\n    @snap;\n});\n\ninsta_test!(opt_header_reverse, [\"a\", \"b\", \"c\"], &[\"--header\", \"test_header\", \"--reverse\"], {\n    @snap;\n});\n\ninsta_test!(opt_header_reverse_inline_info, [\"a\", \"b\", \"c\"], &[\"--header\", \"test_header\", \"--reverse\", \"--inline-info\"], {\n    @snap;\n});\n\ninsta_test!(opt_header_lines_1, [\"a\", \"b\", \"c\"], &[\"--header-lines\", \"1\"], {\n    @snap;\n});\n\ninsta_test!(opt_header_lines_all, [\"a\", \"b\", \"c\"], &[\"--header-lines\", \"4\"], {\n    @snap;\n});\n\ninsta_test!(opt_header_lines_inline_info, [\"a\", \"b\", \"c\"], &[\"--header-lines\", \"1\", \"--inline-info\"], {\n    @snap;\n});\n\ninsta_test!(opt_header_lines_reverse, [\"a\", \"b\", \"c\"], &[\"--header-lines\", \"1\", \"--reverse\"], {\n    @snap;\n});\n\ninsta_test!(opt_header_lines_reverse_inline_info, [\"a\", \"b\", \"c\"], &[\"--header-lines\", \"1\", \"--reverse\", \"--inline-info\"], {\n    @snap;\n});\n\ninsta_test!(opt_skip_to_pattern, [\"a/b/c\"], &[\"--skip-to-pattern\", \"[^/]*$\", \"--bind\", \"ctrl-a:scroll-left\", \"--bind\", \"ctrl-x:scroll-right\"], {\n    @snap;\n    @ctrl 'a';\n    @snap;\n    @ctrl 'x';\n    @snap;\n});\n\ninsta_test!(opt_multi, [\"a\", \"b\", \"c\"], &[\"--multi\"], {\n    @snap;\n    @shift Tab;\n    @snap;\n    @shift Tab;\n    @snap;\n});\n\ninsta_test!(opt_pre_select_n, [\"a\", \"b\", \"c\"], &[\"-m\", \"--pre-select-n\", \"2\"], {\n    @snap;\n});\n\ninsta_test!(opt_pre_select_items, [\"a\", \"b\", \"c\"], &[\"-m\", \"--pre-select-items\", \"$'b\\\\nc'\"], {\n    @snap;\n});\n\ninsta_test!(opt_pre_select_pat, [\"a\", \"b\", \"c\"], &[\"-m\", \"--pre-select-pat\", \"[b|c]\"], {\n    @snap;\n});\n\ninsta_test!(opt_no_clear_if_empty, @interactive, &[\"-i\", \"--no-clear-if-empty\", \"-c\", \"printf {q}\", \"--cmd-query\", \"xxxx\"], {\n    @snap;\n    @ctrl 'w';\n    @snap;\n});\n\ninsta_test!(opt_tac, [\"a\", \"b\"], &[\"--tac\"], {\n    @snap;\n});\n\ninsta_test!(opt_tac_with_header_lines, [\"a\", \"b\", \"c\", \"d\", \"e\"], &[\"--tac\", \"--header-lines\", \"2\"], {\n    @snap;\n});\n\ninsta_test!(opt_replstr, [\"a\", \"b\", \"c\"], &[\"-I\", \"..\", \"--preview\", \"echo foo {} ..\"], {\n    @snap;\n    @char 'a';\n    @snap;\n});\n\ninsta_test!(opt_selector, [\"a\", \"b\", \"c\"], &[\"--selector\", \"$\"], {\n    @snap;\n});\n\ninsta_test!(opt_no_sort, [\"ac\", \"bc\", \"cc\"], &[\"--no-sort\"], {\n    @snap;\n    @char 'c';\n    @snap;\n});\n\ninsta_test!(opt_multi_selector, [\"a\", \"b\", \"c\"], &[\"--multi-selector\", \"$\", \"-m\"], {\n    @snap;\n    @shift Tab;\n    @snap;\n});\n\ninsta_test!(opt_cycle, [\"a\", \"b\", \"c\"], &[\"--cycle\"], {\n    @snap;\n    @key Down;\n    @snap;\n    @key Up;\n    @snap;\n});\n\ninsta_test!(opt_cycle_header_lines, [\"a\", \"b\", \"c\", \"d\"], &[\"--cycle\", \"--header-lines\", \"1\"], {\n    @snap;\n    @key Down;\n    @snap;\n    @key Up;\n    @snap;\n});\n\ninsta_test!(opt_disabled, [\"a\", \"b\", \"c\", \"d\"], &[\"--disabled\"], {\n    @snap;\n    @char 'b';\n    @snap;\n});\n\nconst LONG_INPUT: &str = \"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\";\ninsta_test!(opt_wrap, [LONG_INPUT], &[\"--wrap\"], {\n    @snap;\n});\n\ninsta_test!(opt_multiple_flags_prompt, [\"\"], &[\"--prompt\", \"a\", \"--prompt\", \"b\", \"-p\", \"c\"], {\n    @snap;\n});\n\ninsta_test!(opt_multiple_flags_cmd_prompt, @interactive, &[\"-i\", \"--cmd-prompt\", \"a\", \"--cmd-prompt\", \"c\", \"--cmd\", \"echo\"], {\n    @snap;\n});\n\ninsta_test!(opt_multiple_flags_cmd_query, @interactive, &[\"-i\", \"--cmd-query\", \"a\", \"--cmd-query\", \"b\", \"--cmd\", \"echo\"], {\n    @snap;\n});\n\ninsta_test!(opt_multiple_flags_interactive, @interactive, &[\"-i\", \"--interactive\", \"--interactive\", \"--cmd\", \"echo\"], {\n    @snap;\n});\n\ninsta_test!(opt_multiple_flags_reverse, [\"\"], &[\"--reverse\", \"--reverse\"], {\n    @snap;\n});\n\ninsta_test!(opt_multiple_flags_combined_nth, [\"a b c\", \"d e f\"], &[\"--nth\", \"1,2\"], {\n    @snap;\n    @char 'c';\n    @snap;\n});\n\ninsta_test!(opt_multiple_flags_combined_with_nth, [\"a b c\", \"d e f\"], &[\"--with-nth\", \"1,2\"], {\n    @snap;\n});\n\ninsta_test!(opt_multiple_flags_reverse_and_layout, [\"a b c\", \"d e f\"], &[\"--reverse\", \"--layout\", \"default\"], {\n    @snap;\n});\n\ninsta_test!(opt_multiple_flags_layout_and_reverse, [\"a b c\", \"d e f\"], &[\"--layout\", \"default\", \"--reverse\"], {\n    @snap;\n});\n\ninsta_test!(opt_border_plain, [\"a\", \"b\", \"c\", \"ac\"], &[\"-q\", \"a\", \"--border\", \"plain\"], {\n    @snap;\n});\n\ninsta_test!(opt_border_rounded, [\"a\", \"b\", \"c\", \"ac\"], &[\"-q\", \"a\", \"--border\", \"rounded\"], {\n    @snap;\n});\n\ninsta_test!(opt_border_double, [\"a\", \"b\", \"c\", \"ac\"], &[\"-q\", \"a\", \"--border\", \"double\"], {\n    @snap;\n});\n\ninsta_test!(opt_border_thick, [\"a\", \"b\", \"c\", \"ac\"], &[\"-q\", \"a\", \"--border\", \"thick\"], {\n    @snap;\n});\n\ninsta_test!(opt_border_light_double_dashed, [\"a\", \"b\", \"c\", \"ac\"], &[\"-q\", \"a\", \"--border\", \"light-double-dashed\"], {\n    @snap;\n});\n\ninsta_test!(opt_border_heavy_double_dashed, [\"a\", \"b\", \"c\", \"ac\"], &[\"-q\", \"a\", \"--border\", \"heavy-double-dashed\"], {\n    @snap;\n});\n\ninsta_test!(opt_border_light_triple_dashed, [\"a\", \"b\", \"c\", \"ac\"], &[\"-q\", \"a\", \"--border\", \"light-triple-dashed\"], {\n    @snap;\n});\n\ninsta_test!(opt_border_heavy_triple_dashed, [\"a\", \"b\", \"c\", \"ac\"], &[\"-q\", \"a\", \"--border\", \"heavy-triple-dashed\"], {\n    @snap;\n});\n\ninsta_test!(opt_border_light_quadruple_dashed, [\"a\", \"b\", \"c\", \"ac\"], &[\"-q\", \"a\", \"--border\", \"light-quadruple-dashed\"], {\n    @snap;\n});\n\ninsta_test!(opt_border_heavy_quadruple_dashed, [\"a\", \"b\", \"c\", \"ac\"], &[\"-q\", \"a\", \"--border\", \"heavy-quadruple-dashed\"], {\n    @snap;\n});\n\ninsta_test!(opt_border_quadrant_inside, [\"a\", \"b\", \"c\", \"ac\"], &[\"-q\", \"a\", \"--border\", \"quadrant-inside\"], {\n    @snap;\n});\n\ninsta_test!(opt_border_quadrant_outside, [\"a\", \"b\", \"c\", \"ac\"], &[\"-q\", \"a\", \"--border\", \"quadrant-outside\"], {\n    @snap;\n});\n\n#[test]\nfn opt_select_1() -> std::io::Result<()> {\n    let res = Command::new(\"/bin/sh\")\n        .arg(\"-c\")\n        .env_clear()\n        .arg(format!(\"printf '1\\n2\\n3' | {SK} --select-1 -q 3\"))\n        .stdin(std::process::Stdio::null())\n        .output()?;\n    assert_eq!(res.status.code(), Some(0));\n    assert_eq!(res.stdout, b\"3\\n\");\n    Ok(())\n}\n\n#[test]\nfn opt_exit_0() -> std::io::Result<()> {\n    let res = Command::new(\"/bin/sh\")\n        .arg(\"-c\")\n        .env_clear()\n        .arg(format!(\"printf '1\\n2\\n3' | {SK} --exit-0 -q 4\"))\n        .stdin(std::process::Stdio::null())\n        .output()?;\n    assert_eq!(res.status.code(), Some(1));\n    assert_eq!(res.stdout, &[]);\n    Ok(())\n}\n\ninsta_test!(opt_select_1_enter, [\"1\", \"2\", \"3\", \"11\"], &[\"-q\", \"1\", \"--select-1\"], {\n    @snap;\n});\ninsta_test!(opt_exit_0_enter, [\"1\", \"2\", \"3\"], &[\"-q\", \"1\", \"--exit-0\"], {\n    @snap;\n});\n\ninsta_test!(opt_ellipsis, [\"aabbccddeeffggghiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz\"], &[\"--preview\", \"echo a\", \"--preview-window\", \"right:80%\", \"-q\", \"ij\", \"--ellipsis\", \"%%%\"], {\n    @snap;\n});\n"
  },
  {
    "path": "tests/preview.rs",
    "content": "#[allow(dead_code)]\n#[macro_use]\nmod common;\n\nconst PREVIEW: &str = \"printf \\\"=%.0s\\\\n\\\" $(seq 1 1000)\";\n\ninsta_test!(preview_preserve_quotes, [\"'\\\"ABC\\\"'\"], &[\"--preview\", \"echo X{}X\"], {\n    @snap;\n});\n\ninsta_test!(preview_nul_char, [\"a\\0b\"], &[\"--preview\", \"printf \\\"{}\\\" | hexdump -C\"], {\n    @snap;\n});\n\ninsta_test!(preview_window_left, [\"a\", \"b\"], &[\"--preview\", PREVIEW, \"--preview-window\", \"left\"], {\n    @snap;\n});\n\ninsta_test!(preview_window_down, [\"a\", \"b\"], &[\"--preview\", PREVIEW, \"--preview-window\", \"down\"], {\n    @snap;\n});\n\ninsta_test!(preview_window_up, [\"a\", \"b\"], &[\"--preview\", PREVIEW, \"--preview-window\", \"up\"], {\n    @snap;\n});\n\ninsta_test!(preview_offset_fixed, [\"a\", \"b\"], &[\"--preview\", PREVIEW, \"--preview-window\", \"left:+123\"], {\n    @snap;\n});\n\ninsta_test!(preview_offset_expr, [\"123 321\"], &[\"--preview\", PREVIEW, \"--preview-window\", \"left:+{2}\"], {\n    @snap;\n});\n\ninsta_test!(preview_offset_fixed_and_expr, [\"123 321\"], &[\"--preview\", PREVIEW, \"--preview-window\", \"left:+{2}-2\"], {\n    @snap;\n});\n\ninsta_test!(preview_nowrap, [\"x\"], &[\"--preview\", \"echo a bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\", \"--preview-window\", \"up\"], {\n    @snap;\n});\n\ninsta_test!(preview_wrap, [\"x\"], &[\"--preview\", \"echo a      bbbbbbbb\", \"--preview-window\", \"left:10:wrap\"], {\n    @snap;\n});\n\n// Test that preview updates when navigating between items\ninsta_test!(preview_navigation, [\"a\", \"b\", \"c\"], &[\"--preview\", \"echo {}\"], {\n    @snap;\n    @key Up;\n    @snap;\n});\n\ninsta_test!(preview_plus, [\"a\", \"b\", \"c\"], &[\"--preview\", \"echo {+}\", \"-m\"], {\n    @snap;\n    @key Up;\n    @snap;\n    @shift Tab;\n    @snap;\n    @shift Tab;\n    @snap;\n    @action DeselectAll;\n    @snap;\n});\n\n#[cfg(target_os = \"linux\")]\ninsta_test!(preview_no_pty_linux, [\"x\"], &[\"--preview\", \"tty -s && echo YES || echo NO\", \"--preview-window\", \"wrap\"], {\n    @snap;\n});\n\n#[cfg(target_os = \"linux\")]\nmod preview_pty {\n    use super::PREVIEW;\n\n    insta_test!(preview_pty_flag, [\"x\"], &[\"--preview\", \"tty -s && echo YES || echo NO\", \"--preview-window\", \":pty\"], {\n        @snap;\n    });\n    insta_test!(preview_pty_preserve_quotes, [\"'\\\"ABC\\\"'\"], &[\"--preview\", \"echo X{}X\", \"--preview-window\", \":pty\"], {\n        @snap;\n    });\n\n    insta_test!(preview_pty_nul_char, [\"a\\0b\"], &[\"--preview\", \"printf \\\"{}\\\" | hexdump -C\", \"--preview-window\", \":pty\"], {\n        @snap;\n    });\n\n    insta_test!(preview_pty_window_left, [\"a\", \"b\"], &[\"--preview\", PREVIEW, \"--preview-window\", \"left:pty\"], {\n        @snap;\n    });\n\n    insta_test!(preview_pty_window_down, [\"a\", \"b\"], &[\"--preview\", PREVIEW, \"--preview-window\", \"down:pty\"], {\n        @snap;\n    });\n\n    insta_test!(preview_pty_window_up, [\"a\", \"b\"], &[\"--preview\", PREVIEW, \"--preview-window\", \"up:pty\"], {\n        @snap;\n    });\n\n    insta_test!(preview_pty_offset_fixed, [\"a\", \"b\"], &[\"--preview\", PREVIEW, \"--preview-window\", \"left:+123:pty\"], {\n        @snap;\n    });\n\n    insta_test!(preview_pty_offset_expr, [\"123 321\"], &[\"--preview\", PREVIEW, \"--preview-window\", \"left:+{2}:pty\"], {\n        @snap;\n    });\n\n    insta_test!(preview_pty_offset_fixed_and_expr, [\"123 321\"], &[\"--preview\", PREVIEW, \"--preview-window\", \"left:+{2}-2:pty\"], {\n        @snap;\n    });\n\n    insta_test!(preview_pty_nowrap, [\"x\"], &[\"--preview\", \"echo a bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\", \"--preview-window\", \"up:pty\"], {\n        @snap;\n    });\n\n    insta_test!(preview_pty_wrap, [\"x\"], &[\"--preview\", \"echo a      bbbbbbbbbb\", \"--preview-window\", \"left:10:pty:wrap\"], {\n        @snap;\n    });\n\n    // Test that preview updates when navigating between items\n    insta_test!(preview_pty_navigation, [\"a\", \"b\", \"c\"], &[\"--preview\", \"echo {}\", \"--preview-window\", \":pty\"], {\n        @snap;\n        @key Up;\n        @snap;\n    });\n\n    insta_test!(preview_pty_plus, [\"a\", \"b\", \"c\"], &[\"--preview\", \"echo {+}\", \"-m\", \"--preview-window\", \":pty\"], {\n        @snap;\n        @key Up;\n        @snap;\n        @shift Tab;\n        @snap;\n        @shift Tab;\n        @snap;\n        @action DeselectAll;\n        @snap;\n    });\n}\n"
  },
  {
    "path": "tests/snapshots/ansi__prompt_ansi.snap",
    "content": "---\nsource: tests/ansi.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"> a                                                                             \"\n\"  1/1                                                                        0/0\"\n\"prompt nocol                                                                    \"\ncursor: (24, 13)\n"
  },
  {
    "path": "tests/snapshots/binds__bind_append_and_select-2.snap",
    "content": "---\nsource: tests/binds.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/4                                                                        0/0\"\n\"> xyz                                                                           \"\ncursor: (24, 6)\n"
  },
  {
    "path": "tests/snapshots/binds__bind_append_and_select-3.snap",
    "content": "---\nsource: tests/binds.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\">>xyz                                                                           \"\n\"  1/5 [1]                                                                    0/0\"\n\"> xyz                                                                           \"\ncursor: (24, 6)\n"
  },
  {
    "path": "tests/snapshots/binds__bind_append_and_select.snap",
    "content": "---\nsource: tests/binds.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  c                                                                             \"\n\"  b                                                                             \"\n\"                                                                                \"\n\"> a                                                                             \"\n\"  4/4                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/binds__bind_change-2.snap",
    "content": "---\nsource: tests/binds.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  10                                                                            \"\n\"  19                                                                            \"\n\"  18                                                                            \"\n\"  17                                                                            \"\n\"  16                                                                            \"\n\"  15                                                                            \"\n\"  14                                                                            \"\n\"> 13                                                                            \"\n\"  12                                                                            \"\n\"  1                                                                             \"\n\"  10/10                                                                      2/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/binds__bind_change-3.snap",
    "content": "---\nsource: tests/binds.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  10                                                                            \"\n\"  19                                                                            \"\n\"  18                                                                            \"\n\"  17                                                                            \"\n\"  16                                                                            \"\n\"  15                                                                            \"\n\"  14                                                                            \"\n\"  13                                                                            \"\n\"  12                                                                            \"\n\"> 1                                                                             \"\n\"  10/10                                                                      0/0\"\n\"> 1                                                                             \"\ncursor: (24, 4)\n"
  },
  {
    "path": "tests/snapshots/binds__bind_change.snap",
    "content": "---\nsource: tests/binds.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  10                                                                            \"\n\"  19                                                                            \"\n\"  18                                                                            \"\n\"  17                                                                            \"\n\"  16                                                                            \"\n\"  15                                                                            \"\n\"  14                                                                            \"\n\"  13                                                                            \"\n\"  12                                                                            \"\n\"> 1                                                                             \"\n\"  10/10                                                                      0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/binds__bind_first_last-2.snap",
    "content": "---\nsource: tests/binds.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  10                                                                            \"\n\"  9                                                                             \"\n\"  8                                                                             \"\n\"  7                                                                             \"\n\"  6                                                                             \"\n\"  5                                                                             \"\n\"  4                                                                             \"\n\"  3                                                                             \"\n\"  2                                                                             \"\n\"> 1                                                                             \"\n\"  10/10                                                                      0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/binds__bind_first_last-3.snap",
    "content": "---\nsource: tests/binds.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"> 10                                                                            \"\n\"  9                                                                             \"\n\"  8                                                                             \"\n\"  7                                                                             \"\n\"  6                                                                             \"\n\"  5                                                                             \"\n\"  4                                                                             \"\n\"  3                                                                             \"\n\"  2                                                                             \"\n\"  1                                                                             \"\n\"  10/10                                                                      9/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/binds__bind_first_last-4.snap",
    "content": "---\nsource: tests/binds.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  10                                                                            \"\n\"  9                                                                             \"\n\"  8                                                                             \"\n\"  7                                                                             \"\n\"  6                                                                             \"\n\"  5                                                                             \"\n\"  4                                                                             \"\n\"  3                                                                             \"\n\"  2                                                                             \"\n\"> 1                                                                             \"\n\"  10/10                                                                      0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/binds__bind_first_last.snap",
    "content": "---\nsource: tests/binds.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  10                                                                            \"\n\"  9                                                                             \"\n\"  8                                                                             \"\n\"  7                                                                             \"\n\"  6                                                                             \"\n\"  5                                                                             \"\n\"  4                                                                             \"\n\"  3                                                                             \"\n\"  2                                                                             \"\n\"> 1                                                                             \"\n\"  10/10                                                                      0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/binds__bind_if_non_matched-2.snap",
    "content": "---\nsource: tests/binds.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"> a                                                                             \"\n\"  1/2                                                                        0/0\"\n\"> a                                                                             \"\ncursor: (24, 4)\n"
  },
  {
    "path": "tests/snapshots/binds__bind_if_non_matched-3.snap",
    "content": "---\nsource: tests/binds.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/2                                                                        0/0\"\n\"> ac                                                                            \"\ncursor: (24, 5)\n"
  },
  {
    "path": "tests/snapshots/binds__bind_if_non_matched.snap",
    "content": "---\nsource: tests/binds.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/2                                                                        0/0\"\n\"> ab                                                                            \"\ncursor: (24, 5)\n"
  },
  {
    "path": "tests/snapshots/binds__bind_set_header_change-2.snap",
    "content": "---\nsource: tests/binds.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  c                                                                             \"\n\"  b                                                                             \"\n\"> a                                                                             \"\n\"  bar                                                                           \"\n\"  3/3                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/binds__bind_set_header_change.snap",
    "content": "---\nsource: tests/binds.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  c                                                                             \"\n\"  b                                                                             \"\n\"> a                                                                             \"\n\"  foo                                                                           \"\n\"  3/3                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/binds__bind_set_header_from_empty-2.snap",
    "content": "---\nsource: tests/binds.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  c                                                                             \"\n\"  b                                                                             \"\n\"> a                                                                             \"\n\"  foo                                                                           \"\n\"  3/3                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/binds__bind_set_header_from_empty.snap",
    "content": "---\nsource: tests/binds.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  c                                                                             \"\n\"  b                                                                             \"\n\"> a                                                                             \"\n\"  3/3                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/binds__bind_set_header_to_empty-2.snap",
    "content": "---\nsource: tests/binds.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  c                                                                             \"\n\"  b                                                                             \"\n\"> a                                                                             \"\n\"  3/3                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/binds__bind_set_header_to_empty.snap",
    "content": "---\nsource: tests/binds.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  c                                                                             \"\n\"  b                                                                             \"\n\"> a                                                                             \"\n\"  foo                                                                           \"\n\"  3/3                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/binds__bind_set_preview_cmd-2.snap",
    "content": "---\nsource: tests/binds.rs\nassertion_line: 101\nexpression: buf + & cursor_pos\n---\n\"                                        │new a                                  \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"  c                                     │                                       \"\n\"  b                                     │                                       \"\n\"> a                                     │                                       \"\n\"  3/3                                0/0│                                       \"\n\">                                       │                                       \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/binds__bind_set_preview_cmd-3.snap",
    "content": "---\nsource: tests/binds.rs\nassertion_line: 101\nexpression: buf + & cursor_pos\n---\n\"                                        │new b                                  \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"  c                                     │                                       \"\n\"> b                                     │                                       \"\n\"  a                                     │                                       \"\n\"  3/3                                1/0│                                       \"\n\">                                       │                                       \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/binds__bind_set_preview_cmd.snap",
    "content": "---\nsource: tests/binds.rs\nassertion_line: 101\nexpression: buf + & cursor_pos\n---\n\"                                        │initial a                              \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"  c                                     │                                       \"\n\"  b                                     │                                       \"\n\"> a                                     │                                       \"\n\"  3/3                                0/0│                                       \"\n\">                                       │                                       \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/binds__bind_set_query_basic-2.snap",
    "content": "---\nsource: tests/binds.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/3                                                                        0/0\"\n\"> foo                                                                           \"\ncursor: (24, 6)\n"
  },
  {
    "path": "tests/snapshots/binds__bind_set_query_basic.snap",
    "content": "---\nsource: tests/binds.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  c                                                                             \"\n\"  b                                                                             \"\n\"> a                                                                             \"\n\"  3/3                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/binds__bind_set_query_expand-2.snap",
    "content": "---\nsource: tests/binds.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"> a                                                                             \"\n\"  1/3                                                                        0/0\"\n\"> a                                                                             \"\ncursor: (24, 4)\n"
  },
  {
    "path": "tests/snapshots/binds__bind_set_query_expand.snap",
    "content": "---\nsource: tests/binds.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  c                                                                             \"\n\"  b                                                                             \"\n\"> a                                                                             \"\n\"  3/3                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/binds__bind_set_query_fields-2.snap",
    "content": "---\nsource: tests/binds.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"> a.1                                                                           \"\n\"  1/3                                                                        0/0\"\n\"> a                                                                             \"\ncursor: (24, 4)\n"
  },
  {
    "path": "tests/snapshots/binds__bind_set_query_fields.snap",
    "content": "---\nsource: tests/binds.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  c.3                                                                           \"\n\"  b.2                                                                           \"\n\"> a.1                                                                           \"\n\"  3/3                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/binds__bind_set_query_to_itself-2.snap",
    "content": "---\nsource: tests/binds.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  c                                                                             \"\n\"  b                                                                             \"\n\"> a                                                                             \"\n\"  3/3                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/binds__bind_set_query_to_itself-3.snap",
    "content": "---\nsource: tests/binds.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"> a                                                                             \"\n\"  1/3                                                                        0/0\"\n\"> a                                                                             \"\ncursor: (24, 4)\n"
  },
  {
    "path": "tests/snapshots/binds__bind_set_query_to_itself-4.snap",
    "content": "---\nsource: tests/binds.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"> a                                                                             \"\n\"  1/3                                                                        0/0\"\n\"> a                                                                             \"\ncursor: (24, 4)\n"
  },
  {
    "path": "tests/snapshots/binds__bind_set_query_to_itself.snap",
    "content": "---\nsource: tests/binds.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  c                                                                             \"\n\"  b                                                                             \"\n\"> a                                                                             \"\n\"  3/3                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/binds__bind_toggle_interactive-2.snap",
    "content": "---\nsource: tests/binds.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/0                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/binds__bind_toggle_interactive.snap",
    "content": "---\nsource: tests/binds.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/0                                                                        0/0\"\n\"c>                                                                              \"\ncursor: (24, 4)\n"
  },
  {
    "path": "tests/snapshots/binds__bind_toggle_interactive_queries-2.snap",
    "content": "---\nsource: tests/binds.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/0                                                                        0/0\"\n\"> normal                                                                        \"\ncursor: (24, 9)\n"
  },
  {
    "path": "tests/snapshots/binds__bind_toggle_interactive_queries-3.snap",
    "content": "---\nsource: tests/binds.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/0                                                                        0/0\"\n\"> norma|l                                                                       \"\ncursor: (24, 9)\n"
  },
  {
    "path": "tests/snapshots/binds__bind_toggle_interactive_queries-4.snap",
    "content": "---\nsource: tests/binds.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/0                                                                        0/0\"\n\"c> interactive                                                                  \"\ncursor: (24, 15)\n"
  },
  {
    "path": "tests/snapshots/binds__bind_toggle_interactive_queries-5.snap",
    "content": "---\nsource: tests/binds.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/0                                                                        0/0\"\n\"> norma|l                                                                       \"\ncursor: (24, 9)\n"
  },
  {
    "path": "tests/snapshots/binds__bind_toggle_interactive_queries.snap",
    "content": "---\nsource: tests/binds.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/0                                                                        0/0\"\n\"c> interactive                                                                  \"\ncursor: (24, 15)\n"
  },
  {
    "path": "tests/snapshots/binds__bind_top_alias-2.snap",
    "content": "---\nsource: tests/binds.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"> 10                                                                            \"\n\"  9                                                                             \"\n\"  8                                                                             \"\n\"  7                                                                             \"\n\"  6                                                                             \"\n\"  5                                                                             \"\n\"  4                                                                             \"\n\"  3                                                                             \"\n\"  2                                                                             \"\n\"  1                                                                             \"\n\"  10/10                                                                      9/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/binds__bind_top_alias-3.snap",
    "content": "---\nsource: tests/binds.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  10                                                                            \"\n\"  9                                                                             \"\n\"  8                                                                             \"\n\"  7                                                                             \"\n\"  6                                                                             \"\n\"  5                                                                             \"\n\"  4                                                                             \"\n\"  3                                                                             \"\n\"  2                                                                             \"\n\"> 1                                                                             \"\n\"  10/10                                                                      0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/binds__bind_top_alias.snap",
    "content": "---\nsource: tests/binds.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  10                                                                            \"\n\"  9                                                                             \"\n\"  8                                                                             \"\n\"  7                                                                             \"\n\"  6                                                                             \"\n\"  5                                                                             \"\n\"  4                                                                             \"\n\"  3                                                                             \"\n\"  2                                                                             \"\n\"> 1                                                                             \"\n\"  10/10                                                                      0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/case__case_ignore_different-2.snap",
    "content": "---\nsource: tests/case.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"> aBcDeF                                                                        \"\n\"  1/1                                                                        0/0\"\n\"> Abc                                                                           \"\ncursor: (24, 6)\n"
  },
  {
    "path": "tests/snapshots/case__case_ignore_different.snap",
    "content": "---\nsource: tests/case.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"> aBcDeF                                                                        \"\n\"  1/1                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/case__case_ignore_exact-2.snap",
    "content": "---\nsource: tests/case.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"> aBcDeF                                                                        \"\n\"  1/1                                                                        0/0\"\n\"> aBc                                                                           \"\ncursor: (24, 6)\n"
  },
  {
    "path": "tests/snapshots/case__case_ignore_exact.snap",
    "content": "---\nsource: tests/case.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"> aBcDeF                                                                        \"\n\"  1/1                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/case__case_ignore_lower-2.snap",
    "content": "---\nsource: tests/case.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"> aBcDeF                                                                        \"\n\"  1/1                                                                        0/0\"\n\"> abc                                                                           \"\ncursor: (24, 6)\n"
  },
  {
    "path": "tests/snapshots/case__case_ignore_lower.snap",
    "content": "---\nsource: tests/case.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"> aBcDeF                                                                        \"\n\"  1/1                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/case__case_ignore_no_match-2.snap",
    "content": "---\nsource: tests/case.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/1                                                                        0/0\"\n\"> z                                                                             \"\ncursor: (24, 4)\n"
  },
  {
    "path": "tests/snapshots/case__case_ignore_no_match.snap",
    "content": "---\nsource: tests/case.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"> aBcDeF                                                                        \"\n\"  1/1                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/case__case_non_ascii-2.snap",
    "content": "---\nsource: tests/case.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  СЛОВО                                                                         \"\n\"  Слово                                                                         \"\n\"> слово                                                                         \"\n\"  3/3                                                                        0/0\"\n\"> слово                                                                         \"\ncursor: (24, 8)\n"
  },
  {
    "path": "tests/snapshots/case__case_non_ascii-3.snap",
    "content": "---\nsource: tests/case.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"> Слово                                                                         \"\n\"  1/3                                                                        0/0\"\n\"> Слово                                                                         \"\ncursor: (24, 8)\n"
  },
  {
    "path": "tests/snapshots/case__case_non_ascii.snap",
    "content": "---\nsource: tests/case.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  СЛОВО                                                                         \"\n\"  Слово                                                                         \"\n\"> слово                                                                         \"\n\"  3/3                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/case__case_respect_exact-2.snap",
    "content": "---\nsource: tests/case.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"> aBcDeF                                                                        \"\n\"  1/1                                                                        0/0\"\n\"> aBc                                                                           \"\ncursor: (24, 6)\n"
  },
  {
    "path": "tests/snapshots/case__case_respect_exact.snap",
    "content": "---\nsource: tests/case.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"> aBcDeF                                                                        \"\n\"  1/1                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/case__case_respect_lower-2.snap",
    "content": "---\nsource: tests/case.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/1                                                                        0/0\"\n\"> abc                                                                           \"\ncursor: (24, 6)\n"
  },
  {
    "path": "tests/snapshots/case__case_respect_lower.snap",
    "content": "---\nsource: tests/case.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"> aBcDeF                                                                        \"\n\"  1/1                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/case__case_respect_no_match-2.snap",
    "content": "---\nsource: tests/case.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/1                                                                        0/0\"\n\"> Abc                                                                           \"\ncursor: (24, 6)\n"
  },
  {
    "path": "tests/snapshots/case__case_respect_no_match.snap",
    "content": "---\nsource: tests/case.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"> aBcDeF                                                                        \"\n\"  1/1                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/case__case_smart_exact-2.snap",
    "content": "---\nsource: tests/case.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"> aBcDeF                                                                        \"\n\"  1/1                                                                        0/0\"\n\"> aBc                                                                           \"\ncursor: (24, 6)\n"
  },
  {
    "path": "tests/snapshots/case__case_smart_exact.snap",
    "content": "---\nsource: tests/case.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"> aBcDeF                                                                        \"\n\"  1/1                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/case__case_smart_lower-2.snap",
    "content": "---\nsource: tests/case.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"> aBcDeF                                                                        \"\n\"  1/1                                                                        0/0\"\n\"> abc                                                                           \"\ncursor: (24, 6)\n"
  },
  {
    "path": "tests/snapshots/case__case_smart_lower.snap",
    "content": "---\nsource: tests/case.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"> aBcDeF                                                                        \"\n\"  1/1                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/case__case_smart_no_match-2.snap",
    "content": "---\nsource: tests/case.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/1                                                                        0/0\"\n\"> Abc                                                                           \"\ncursor: (24, 6)\n"
  },
  {
    "path": "tests/snapshots/case__case_smart_no_match.snap",
    "content": "---\nsource: tests/case.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"> aBcDeF                                                                        \"\n\"  1/1                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/defaults__interactive_mode_command_execution-2.snap",
    "content": "---\nsource: tests/defaults.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"> foo bar                                                                       \"\n\"  1/1                                                                        0/0\"\n\"c> bar                                                                          \"\ncursor: (24, 7)\n"
  },
  {
    "path": "tests/snapshots/defaults__interactive_mode_command_execution-3.snap",
    "content": "---\nsource: tests/defaults.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"> foo barbaz                                                                    \"\n\"  1/1                                                                        0/0\"\n\"c> barbaz                                                                       \"\ncursor: (24, 10)\n"
  },
  {
    "path": "tests/snapshots/defaults__interactive_mode_command_execution.snap",
    "content": "---\nsource: tests/defaults.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"> foo                                                                           \"\n\"  1/1                                                                        0/0\"\n\"c>                                                                              \"\ncursor: (24, 4)\n"
  },
  {
    "path": "tests/snapshots/defaults__unicode_input-2.snap",
    "content": "---\nsource: tests/defaults.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/1                                                                        0/0\"\n\"> 󰬈󰬉󰬊|                                                                          \"\ncursor: (24, 7)\n"
  },
  {
    "path": "tests/snapshots/defaults__unicode_input-3.snap",
    "content": "---\nsource: tests/defaults.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/1                                                                        0/0\"\n\"> 󰬈󰬉|󰬊|                                                                         \"\ncursor: (24, 6)\n"
  },
  {
    "path": "tests/snapshots/defaults__unicode_input-4.snap",
    "content": "---\nsource: tests/defaults.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/1                                                                        0/0\"\n\"> 󰬈󰬉|󰬈󰬊|                                                                        \"\ncursor: (24, 7)\n"
  },
  {
    "path": "tests/snapshots/defaults__unicode_input.snap",
    "content": "---\nsource: tests/defaults.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/1                                                                        0/0\"\n\"> 󰬈󰬉󰬊                                                                           \"\ncursor: (24, 6)\n"
  },
  {
    "path": "tests/snapshots/defaults__vanilla.snap",
    "content": "---\nsource: tests/defaults.rs\nexpression: buf + & cursor_pos\n---\n\"  22                                                                            \"\n\"  21                                                                            \"\n\"  20                                                                            \"\n\"  19                                                                            \"\n\"  18                                                                            \"\n\"  17                                                                            \"\n\"  16                                                                            \"\n\"  15                                                                            \"\n\"  14                                                                            \"\n\"  13                                                                            \"\n\"  12                                                                            \"\n\"  11                                                                            \"\n\"  10                                                                            \"\n\"  9                                                                             \"\n\"  8                                                                             \"\n\"  7                                                                             \"\n\"  6                                                                             \"\n\"  5                                                                             \"\n\"  4                                                                             \"\n\"  3                                                                             \"\n\"  2                                                                             \"\n\"> 1                                                                             \"\n\"  100/100                                                                    0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/defaults__vanilla_basic.snap",
    "content": "---\nsource: tests/defaults.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  3                                                                             \"\n\"  2                                                                             \"\n\"> 1                                                                             \"\n\"  3/3                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/issues__issue_359_multi_regex_unicode.snap",
    "content": "---\nsource: tests/issues.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"> ああa                                                                         \" Hidden by multi-width symbols: [(3, \" \"), (5, \" \")]\n\"  1/1/RE                                                                     0/0\"\n\"> a                                                                             \"\ncursor: (24, 4)\n"
  },
  {
    "path": "tests/snapshots/issues__issue_361_literal_space_control.snap",
    "content": "---\nsource: tests/issues.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  foo  bar                                                                      \"\n\"> foo bar                                                                       \"\n\"  2/2                                                                        0/0\"\n\"> foo\\ bar                                                                      \"\ncursor: (24, 11)\n"
  },
  {
    "path": "tests/snapshots/issues__issue_361_literal_space_invert.snap",
    "content": "---\nsource: tests/issues.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"> foo  bar                                                                      \"\n\"  1/2                                                                        0/0\"\n\"> !foo\\ bar                                                                     \"\ncursor: (24, 12)\n"
  },
  {
    "path": "tests/snapshots/issues__issue_547_null_match-2.snap",
    "content": "---\nsource: tests/issues.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"> Test Test Test                                                                \"\n\"  1/1                                                                        0/0\"\n\"> Test                                                                          \"\ncursor: (24, 7)\n"
  },
  {
    "path": "tests/snapshots/issues__issue_547_null_match.snap",
    "content": "---\nsource: tests/issues.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"> Test Test Test                                                                \"\n\"  1/1                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/issues__issue_929_double_width_chars-2.snap",
    "content": "---\nsource: tests/issues.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/1                                                                        0/0\"\n\"> 中文测试|                                                                     \" Hidden by multi-width symbols: [(3, \" \"), (5, \" \"), (7, \" \"), (9, \" \")]\ncursor: (24, 12)\n"
  },
  {
    "path": "tests/snapshots/issues__issue_929_double_width_chars.snap",
    "content": "---\nsource: tests/issues.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/1                                                                        0/0\"\n\"> 中文测试                                                                      \" Hidden by multi-width symbols: [(3, \" \"), (5, \" \"), (7, \" \"), (9, \" \")]\ncursor: (24, 11)\n"
  },
  {
    "path": "tests/snapshots/keys__keys_alt_b-2.snap",
    "content": "---\nsource: tests/keys.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/1                                                                        0/0\"\n\"> foo bar foo-|bar                                                              \"\ncursor: (24, 16)\n"
  },
  {
    "path": "tests/snapshots/keys__keys_alt_b.snap",
    "content": "---\nsource: tests/keys.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/1                                                                        0/0\"\n\"> foo bar foo-bar                                                               \"\ncursor: (24, 18)\n"
  },
  {
    "path": "tests/snapshots/keys__keys_alt_bspace-2.snap",
    "content": "---\nsource: tests/keys.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/1                                                                        0/0\"\n\"> foo bar foo-|                                                                 \"\ncursor: (24, 16)\n"
  },
  {
    "path": "tests/snapshots/keys__keys_alt_bspace.snap",
    "content": "---\nsource: tests/keys.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/1                                                                        0/0\"\n\"> foo bar foo-bar                                                               \"\ncursor: (24, 18)\n"
  },
  {
    "path": "tests/snapshots/keys__keys_alt_d-2.snap",
    "content": "---\nsource: tests/keys.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/1                                                                        0/0\"\n\"> foo bar foo-|bar                                                              \"\ncursor: (24, 16)\n"
  },
  {
    "path": "tests/snapshots/keys__keys_alt_d-3.snap",
    "content": "---\nsource: tests/keys.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/1                                                                        0/0\"\n\"> foo bar |foo-|bar                                                             \"\ncursor: (24, 12)\n"
  },
  {
    "path": "tests/snapshots/keys__keys_alt_d-4.snap",
    "content": "---\nsource: tests/keys.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/1                                                                        0/0\"\n\"> foo bar ||-|bar                                                               \"\ncursor: (24, 13)\n"
  },
  {
    "path": "tests/snapshots/keys__keys_alt_d.snap",
    "content": "---\nsource: tests/keys.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/1                                                                        0/0\"\n\"> foo bar foo-bar                                                               \"\ncursor: (24, 18)\n"
  },
  {
    "path": "tests/snapshots/keys__keys_alt_f-2.snap",
    "content": "---\nsource: tests/keys.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/1                                                                        0/0\"\n\"> |foo bar foo-bar                                                              \"\ncursor: (24, 4)\n"
  },
  {
    "path": "tests/snapshots/keys__keys_alt_f-3.snap",
    "content": "---\nsource: tests/keys.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/1                                                                        0/0\"\n\"> |foo| bar foo-bar                                                             \"\ncursor: (24, 8)\n"
  },
  {
    "path": "tests/snapshots/keys__keys_alt_f.snap",
    "content": "---\nsource: tests/keys.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/1                                                                        0/0\"\n\"> foo bar foo-bar                                                               \"\ncursor: (24, 18)\n"
  },
  {
    "path": "tests/snapshots/keys__keys_arrows-2.snap",
    "content": "---\nsource: tests/keys.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/1                                                                        0/0\"\n\"> foo bar foo-ba|r                                                              \"\ncursor: (24, 18)\n"
  },
  {
    "path": "tests/snapshots/keys__keys_arrows-3.snap",
    "content": "---\nsource: tests/keys.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/1                                                                        0/0\"\n\"> foo bar foo-ba|r|                                                             \"\ncursor: (24, 20)\n"
  },
  {
    "path": "tests/snapshots/keys__keys_arrows.snap",
    "content": "---\nsource: tests/keys.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/1                                                                        0/0\"\n\"> foo bar foo-bar                                                               \"\ncursor: (24, 18)\n"
  },
  {
    "path": "tests/snapshots/keys__keys_basic-2.snap",
    "content": "---\nsource: tests/keys.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"> 99                                                                            \"\n\"  1/100                                                                      0/0\"\n\"> 99                                                                            \"\ncursor: (24, 5)\n"
  },
  {
    "path": "tests/snapshots/keys__keys_basic.snap",
    "content": "---\nsource: tests/keys.rs\nexpression: buf + & cursor_pos\n---\n\"  22                                                                            \"\n\"  21                                                                            \"\n\"  20                                                                            \"\n\"  19                                                                            \"\n\"  18                                                                            \"\n\"  17                                                                            \"\n\"  16                                                                            \"\n\"  15                                                                            \"\n\"  14                                                                            \"\n\"  13                                                                            \"\n\"  12                                                                            \"\n\"  11                                                                            \"\n\"  10                                                                            \"\n\"  9                                                                             \"\n\"  8                                                                             \"\n\"  7                                                                             \"\n\"  6                                                                             \"\n\"  5                                                                             \"\n\"  4                                                                             \"\n\"  3                                                                             \"\n\"  2                                                                             \"\n\"> 1                                                                             \"\n\"  100/100                                                                    0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/keys__keys_bspace-2.snap",
    "content": "---\nsource: tests/keys.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/1                                                                        0/0\"\n\"> foo bar foo-ba|                                                               \"\ncursor: (24, 18)\n"
  },
  {
    "path": "tests/snapshots/keys__keys_bspace.snap",
    "content": "---\nsource: tests/keys.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/1                                                                        0/0\"\n\"> foo bar foo-bar                                                               \"\ncursor: (24, 18)\n"
  },
  {
    "path": "tests/snapshots/keys__keys_btab-2.snap",
    "content": "---\nsource: tests/keys.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"  20                                                                            \"\n\"  19                                                                            \"\n\"  18                                                                            \"\n\"  17                                                                            \"\n\"  16                                                                            \"\n\"  15                                                                            \"\n\"  14                                                                            \"\n\"  13                                                                            \"\n\"  12                                                                            \"\n\"  11                                                                            \"\n\"  10                                                                            \"\n\"  9                                                                             \"\n\"  8                                                                             \"\n\"  7                                                                             \"\n\"  6                                                                             \"\n\"  5                                                                             \"\n\"  4                                                                             \"\n\"  3                                                                             \"\n\"  2                                                                             \"\n\"> 1                                                                             \"\n\"  20/20                                                                      0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/keys__keys_btab.snap",
    "content": "---\nsource: tests/keys.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"  20                                                                            \"\n\"  19                                                                            \"\n\"  18                                                                            \"\n\"  17                                                                            \"\n\"  16                                                                            \"\n\"  15                                                                            \"\n\"  14                                                                            \"\n\"  13                                                                            \"\n\"  12                                                                            \"\n\"  11                                                                            \"\n\"  10                                                                            \"\n\"  9                                                                             \"\n\"  8                                                                             \"\n\"  7                                                                             \"\n\"  6                                                                             \"\n\"  5                                                                             \"\n\"  4                                                                             \"\n\"  3                                                                             \"\n\"  2                                                                             \"\n\"> 1                                                                             \"\n\"  20/20                                                                      0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/keys__keys_ctrl_a-2.snap",
    "content": "---\nsource: tests/keys.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/1                                                                        0/0\"\n\"> |foo bar foo-bar                                                              \"\ncursor: (24, 4)\n"
  },
  {
    "path": "tests/snapshots/keys__keys_ctrl_a.snap",
    "content": "---\nsource: tests/keys.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/1                                                                        0/0\"\n\"> foo bar foo-bar                                                               \"\ncursor: (24, 18)\n"
  },
  {
    "path": "tests/snapshots/keys__keys_ctrl_arrows-2.snap",
    "content": "---\nsource: tests/keys.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/1                                                                        0/0\"\n\"> foo bar foo-|bar                                                              \"\ncursor: (24, 16)\n"
  },
  {
    "path": "tests/snapshots/keys__keys_ctrl_arrows-3.snap",
    "content": "---\nsource: tests/keys.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/1                                                                        0/0\"\n\"> foo bar |foo-|bar                                                             \"\ncursor: (24, 12)\n"
  },
  {
    "path": "tests/snapshots/keys__keys_ctrl_arrows-4.snap",
    "content": "---\nsource: tests/keys.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/1                                                                        0/0\"\n\"> foo bar |foo-|bar|                                                            \"\ncursor: (24, 21)\n"
  },
  {
    "path": "tests/snapshots/keys__keys_ctrl_arrows.snap",
    "content": "---\nsource: tests/keys.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/1                                                                        0/0\"\n\"> foo bar foo-bar                                                               \"\ncursor: (24, 18)\n"
  },
  {
    "path": "tests/snapshots/keys__keys_ctrl_b-2.snap",
    "content": "---\nsource: tests/keys.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/1                                                                        0/0\"\n\"> |foo bar foo-bar                                                              \"\ncursor: (24, 4)\n"
  },
  {
    "path": "tests/snapshots/keys__keys_ctrl_b-3.snap",
    "content": "---\nsource: tests/keys.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/1                                                                        0/0\"\n\"> |f|oo bar foo-bar                                                             \"\ncursor: (24, 6)\n"
  },
  {
    "path": "tests/snapshots/keys__keys_ctrl_b.snap",
    "content": "---\nsource: tests/keys.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/1                                                                        0/0\"\n\"> foo bar foo-bar                                                               \"\ncursor: (24, 18)\n"
  },
  {
    "path": "tests/snapshots/keys__keys_ctrl_c.snap",
    "content": "---\nsource: tests/keys.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/1                                                                        0/0\"\n\"> foo bar foo-bar                                                               \"\ncursor: (24, 18)\n"
  },
  {
    "path": "tests/snapshots/keys__keys_ctrl_d-2.snap",
    "content": "---\nsource: tests/keys.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/1                                                                        0/0\"\n\"> |foo bar foo-bar                                                              \"\ncursor: (24, 4)\n"
  },
  {
    "path": "tests/snapshots/keys__keys_ctrl_d-3.snap",
    "content": "---\nsource: tests/keys.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/1                                                                        0/0\"\n\"> ||oo bar foo-bar                                                              \"\ncursor: (24, 5)\n"
  },
  {
    "path": "tests/snapshots/keys__keys_ctrl_d.snap",
    "content": "---\nsource: tests/keys.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/1                                                                        0/0\"\n\"> foo bar foo-bar                                                               \"\ncursor: (24, 18)\n"
  },
  {
    "path": "tests/snapshots/keys__keys_ctrl_e-2.snap",
    "content": "---\nsource: tests/keys.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/1                                                                        0/0\"\n\"> |foo bar foo-bar                                                              \"\ncursor: (24, 4)\n"
  },
  {
    "path": "tests/snapshots/keys__keys_ctrl_e-3.snap",
    "content": "---\nsource: tests/keys.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/1                                                                        0/0\"\n\"> |foo bar foo-bar|                                                             \"\ncursor: (24, 20)\n"
  },
  {
    "path": "tests/snapshots/keys__keys_ctrl_e.snap",
    "content": "---\nsource: tests/keys.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/1                                                                        0/0\"\n\"> foo bar foo-bar                                                               \"\ncursor: (24, 18)\n"
  },
  {
    "path": "tests/snapshots/keys__keys_ctrl_f-2.snap",
    "content": "---\nsource: tests/keys.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/1                                                                        0/0\"\n\"> |foo bar foo-bar                                                              \"\ncursor: (24, 4)\n"
  },
  {
    "path": "tests/snapshots/keys__keys_ctrl_f-3.snap",
    "content": "---\nsource: tests/keys.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/1                                                                        0/0\"\n\"> |f|oo bar foo-bar                                                             \"\ncursor: (24, 6)\n"
  },
  {
    "path": "tests/snapshots/keys__keys_ctrl_f.snap",
    "content": "---\nsource: tests/keys.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/1                                                                        0/0\"\n\"> foo bar foo-bar                                                               \"\ncursor: (24, 18)\n"
  },
  {
    "path": "tests/snapshots/keys__keys_ctrl_h-2.snap",
    "content": "---\nsource: tests/keys.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/1                                                                        0/0\"\n\"> foo bar foo-ba|                                                               \"\ncursor: (24, 18)\n"
  },
  {
    "path": "tests/snapshots/keys__keys_ctrl_h.snap",
    "content": "---\nsource: tests/keys.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/1                                                                        0/0\"\n\"> foo bar foo-bar                                                               \"\ncursor: (24, 18)\n"
  },
  {
    "path": "tests/snapshots/keys__keys_ctrl_k-2.snap",
    "content": "---\nsource: tests/keys.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"  20                                                                            \"\n\"  19                                                                            \"\n\"  18                                                                            \"\n\"  17                                                                            \"\n\"  16                                                                            \"\n\"  15                                                                            \"\n\"  14                                                                            \"\n\"  13                                                                            \"\n\"  12                                                                            \"\n\"  11                                                                            \"\n\"  10                                                                            \"\n\"  9                                                                             \"\n\"  8                                                                             \"\n\"  7                                                                             \"\n\"  6                                                                             \"\n\"  5                                                                             \"\n\"  4                                                                             \"\n\"  3                                                                             \"\n\"> 2                                                                             \"\n\"  1                                                                             \"\n\"  20/20                                                                      1/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/keys__keys_ctrl_k.snap",
    "content": "---\nsource: tests/keys.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"  20                                                                            \"\n\"  19                                                                            \"\n\"  18                                                                            \"\n\"  17                                                                            \"\n\"  16                                                                            \"\n\"  15                                                                            \"\n\"  14                                                                            \"\n\"  13                                                                            \"\n\"  12                                                                            \"\n\"  11                                                                            \"\n\"  10                                                                            \"\n\"  9                                                                             \"\n\"  8                                                                             \"\n\"  7                                                                             \"\n\"  6                                                                             \"\n\"  5                                                                             \"\n\"  4                                                                             \"\n\"  3                                                                             \"\n\"  2                                                                             \"\n\"> 1                                                                             \"\n\"  20/20                                                                      0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/keys__keys_ctrl_u-2.snap",
    "content": "---\nsource: tests/keys.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/1                                                                        0/0\"\n\"> |                                                                             \"\ncursor: (24, 4)\n"
  },
  {
    "path": "tests/snapshots/keys__keys_ctrl_u.snap",
    "content": "---\nsource: tests/keys.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/1                                                                        0/0\"\n\"> foo bar foo-bar                                                               \"\ncursor: (24, 18)\n"
  },
  {
    "path": "tests/snapshots/keys__keys_ctrl_w-2.snap",
    "content": "---\nsource: tests/keys.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/1                                                                        0/0\"\n\"> foo bar |                                                                     \"\ncursor: (24, 12)\n"
  },
  {
    "path": "tests/snapshots/keys__keys_ctrl_w.snap",
    "content": "---\nsource: tests/keys.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/1                                                                        0/0\"\n\"> foo bar foo-bar                                                               \"\ncursor: (24, 18)\n"
  },
  {
    "path": "tests/snapshots/keys__keys_ctrl_y-2.snap",
    "content": "---\nsource: tests/keys.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/1                                                                        0/0\"\n\"> foo bar foo-|                                                                 \"\ncursor: (24, 16)\n"
  },
  {
    "path": "tests/snapshots/keys__keys_ctrl_y-3.snap",
    "content": "---\nsource: tests/keys.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/1                                                                        0/0\"\n\"> foo bar foo-|bar|                                                             \"\ncursor: (24, 20)\n"
  },
  {
    "path": "tests/snapshots/keys__keys_ctrl_y.snap",
    "content": "---\nsource: tests/keys.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/1                                                                        0/0\"\n\"> foo bar foo-bar                                                               \"\ncursor: (24, 18)\n"
  },
  {
    "path": "tests/snapshots/keys__keys_tab-2.snap",
    "content": "---\nsource: tests/keys.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"  20                                                                            \"\n\"  19                                                                            \"\n\"  18                                                                            \"\n\"  17                                                                            \"\n\"  16                                                                            \"\n\"  15                                                                            \"\n\"  14                                                                            \"\n\"  13                                                                            \"\n\"  12                                                                            \"\n\"  11                                                                            \"\n\"  10                                                                            \"\n\"  9                                                                             \"\n\"  8                                                                             \"\n\"  7                                                                             \"\n\"  6                                                                             \"\n\"  5                                                                             \"\n\"  4                                                                             \"\n\"  3                                                                             \"\n\"> 2                                                                             \"\n\"  1                                                                             \"\n\"  20/20                                                                      1/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/keys__keys_tab-3.snap",
    "content": "---\nsource: tests/keys.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"  20                                                                            \"\n\"  19                                                                            \"\n\"  18                                                                            \"\n\"  17                                                                            \"\n\"  16                                                                            \"\n\"  15                                                                            \"\n\"  14                                                                            \"\n\"  13                                                                            \"\n\"  12                                                                            \"\n\"  11                                                                            \"\n\"  10                                                                            \"\n\"  9                                                                             \"\n\"  8                                                                             \"\n\"  7                                                                             \"\n\"  6                                                                             \"\n\"  5                                                                             \"\n\"  4                                                                             \"\n\"  3                                                                             \"\n\"  2                                                                             \"\n\"> 1                                                                             \"\n\"  20/20                                                                      0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/keys__keys_tab.snap",
    "content": "---\nsource: tests/keys.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"  20                                                                            \"\n\"  19                                                                            \"\n\"  18                                                                            \"\n\"  17                                                                            \"\n\"  16                                                                            \"\n\"  15                                                                            \"\n\"  14                                                                            \"\n\"  13                                                                            \"\n\"  12                                                                            \"\n\"  11                                                                            \"\n\"  10                                                                            \"\n\"  9                                                                             \"\n\"  8                                                                             \"\n\"  7                                                                             \"\n\"  6                                                                             \"\n\"  5                                                                             \"\n\"  4                                                                             \"\n\"  3                                                                             \"\n\"  2                                                                             \"\n\"> 1                                                                             \"\n\"  20/20                                                                      0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/keys__keys_tab_empty-2.snap",
    "content": "---\nsource: tests/keys.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\">                                                                               \"\n\"  1/1                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/keys__keys_tab_empty-3.snap",
    "content": "---\nsource: tests/keys.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/1                                                                        0/0\"\n\"> a                                                                             \"\ncursor: (24, 4)\n"
  },
  {
    "path": "tests/snapshots/keys__keys_tab_empty.snap",
    "content": "---\nsource: tests/keys.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\">                                                                               \"\n\"  1/1                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/keys_interactive__keys_interactive_alt_b-2.snap",
    "content": "---\nsource: tests/keys_interactive.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/0                                                                        0/0\"\n\"c> foo bar foo-|bar                                                             \"\ncursor: (24, 17)\n"
  },
  {
    "path": "tests/snapshots/keys_interactive__keys_interactive_alt_b.snap",
    "content": "---\nsource: tests/keys_interactive.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/0                                                                        0/0\"\n\"c> foo bar foo-bar                                                              \"\ncursor: (24, 19)\n"
  },
  {
    "path": "tests/snapshots/keys_interactive__keys_interactive_alt_bspace-2.snap",
    "content": "---\nsource: tests/keys_interactive.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/0                                                                        0/0\"\n\"c> foo bar foo-|                                                                \"\ncursor: (24, 17)\n"
  },
  {
    "path": "tests/snapshots/keys_interactive__keys_interactive_alt_bspace.snap",
    "content": "---\nsource: tests/keys_interactive.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/0                                                                        0/0\"\n\"c> foo bar foo-bar                                                              \"\ncursor: (24, 19)\n"
  },
  {
    "path": "tests/snapshots/keys_interactive__keys_interactive_alt_d-2.snap",
    "content": "---\nsource: tests/keys_interactive.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/0                                                                        0/0\"\n\"c> foo bar foo-|bar                                                             \"\ncursor: (24, 17)\n"
  },
  {
    "path": "tests/snapshots/keys_interactive__keys_interactive_alt_d-3.snap",
    "content": "---\nsource: tests/keys_interactive.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/0                                                                        0/0\"\n\"c> foo bar |foo-|bar                                                            \"\ncursor: (24, 13)\n"
  },
  {
    "path": "tests/snapshots/keys_interactive__keys_interactive_alt_d-4.snap",
    "content": "---\nsource: tests/keys_interactive.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/0                                                                        0/0\"\n\"c> foo bar ||-|bar                                                              \"\ncursor: (24, 14)\n"
  },
  {
    "path": "tests/snapshots/keys_interactive__keys_interactive_alt_d.snap",
    "content": "---\nsource: tests/keys_interactive.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/0                                                                        0/0\"\n\"c> foo bar foo-bar                                                              \"\ncursor: (24, 19)\n"
  },
  {
    "path": "tests/snapshots/keys_interactive__keys_interactive_alt_f-2.snap",
    "content": "---\nsource: tests/keys_interactive.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/0                                                                        0/0\"\n\"c> |foo bar foo-bar                                                             \"\ncursor: (24, 5)\n"
  },
  {
    "path": "tests/snapshots/keys_interactive__keys_interactive_alt_f-3.snap",
    "content": "---\nsource: tests/keys_interactive.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/0                                                                        0/0\"\n\"c> |foo| bar foo-bar                                                            \"\ncursor: (24, 9)\n"
  },
  {
    "path": "tests/snapshots/keys_interactive__keys_interactive_alt_f.snap",
    "content": "---\nsource: tests/keys_interactive.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/0                                                                        0/0\"\n\"c> foo bar foo-bar                                                              \"\ncursor: (24, 19)\n"
  },
  {
    "path": "tests/snapshots/keys_interactive__keys_interactive_arrows-2.snap",
    "content": "---\nsource: tests/keys_interactive.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/0                                                                        0/0\"\n\"c> foo bar foo-ba|r                                                             \"\ncursor: (24, 19)\n"
  },
  {
    "path": "tests/snapshots/keys_interactive__keys_interactive_arrows-3.snap",
    "content": "---\nsource: tests/keys_interactive.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/0                                                                        0/0\"\n\"c> foo bar foo-ba|r|                                                            \"\ncursor: (24, 21)\n"
  },
  {
    "path": "tests/snapshots/keys_interactive__keys_interactive_arrows.snap",
    "content": "---\nsource: tests/keys_interactive.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/0                                                                        0/0\"\n\"c> foo bar foo-bar                                                              \"\ncursor: (24, 19)\n"
  },
  {
    "path": "tests/snapshots/keys_interactive__keys_interactive_basic-2.snap",
    "content": "---\nsource: tests/keys_interactive.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  4                                                                             \"\n\"  3                                                                             \"\n\"  2                                                                             \"\n\"> 1                                                                             \"\n\"  4/4                                                                        0/0\"\n\"c> 99                                                                           \"\ncursor: (24, 6)\n"
  },
  {
    "path": "tests/snapshots/keys_interactive__keys_interactive_basic.snap",
    "content": "---\nsource: tests/keys_interactive.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  4                                                                             \"\n\"  3                                                                             \"\n\"  2                                                                             \"\n\"> 1                                                                             \"\n\"  4/4                                                                        0/0\"\n\"c>                                                                              \"\ncursor: (24, 4)\n"
  },
  {
    "path": "tests/snapshots/keys_interactive__keys_interactive_bspace-2.snap",
    "content": "---\nsource: tests/keys_interactive.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/0                                                                        0/0\"\n\"c> foo bar foo-ba|                                                              \"\ncursor: (24, 19)\n"
  },
  {
    "path": "tests/snapshots/keys_interactive__keys_interactive_bspace.snap",
    "content": "---\nsource: tests/keys_interactive.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/0                                                                        0/0\"\n\"c> foo bar foo-bar                                                              \"\ncursor: (24, 19)\n"
  },
  {
    "path": "tests/snapshots/keys_interactive__keys_interactive_btab-2.snap",
    "content": "---\nsource: tests/keys_interactive.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  4                                                                             \"\n\"  3                                                                             \"\n\"  2                                                                             \"\n\"> 1                                                                             \"\n\"  4/4                                                                        0/0\"\n\"c>                                                                              \"\ncursor: (24, 4)\n"
  },
  {
    "path": "tests/snapshots/keys_interactive__keys_interactive_btab.snap",
    "content": "---\nsource: tests/keys_interactive.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  4                                                                             \"\n\"  3                                                                             \"\n\"  2                                                                             \"\n\"> 1                                                                             \"\n\"  4/4                                                                        0/0\"\n\"c>                                                                              \"\ncursor: (24, 4)\n"
  },
  {
    "path": "tests/snapshots/keys_interactive__keys_interactive_ctrl_a-2.snap",
    "content": "---\nsource: tests/keys_interactive.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/0                                                                        0/0\"\n\"c> |foo bar foo-bar                                                             \"\ncursor: (24, 5)\n"
  },
  {
    "path": "tests/snapshots/keys_interactive__keys_interactive_ctrl_a.snap",
    "content": "---\nsource: tests/keys_interactive.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/0                                                                        0/0\"\n\"c> foo bar foo-bar                                                              \"\ncursor: (24, 19)\n"
  },
  {
    "path": "tests/snapshots/keys_interactive__keys_interactive_ctrl_arrows-2.snap",
    "content": "---\nsource: tests/keys_interactive.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/0                                                                        0/0\"\n\"c> foo bar foo-|bar                                                             \"\ncursor: (24, 17)\n"
  },
  {
    "path": "tests/snapshots/keys_interactive__keys_interactive_ctrl_arrows-3.snap",
    "content": "---\nsource: tests/keys_interactive.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/0                                                                        0/0\"\n\"c> foo bar |foo-|bar                                                            \"\ncursor: (24, 13)\n"
  },
  {
    "path": "tests/snapshots/keys_interactive__keys_interactive_ctrl_arrows-4.snap",
    "content": "---\nsource: tests/keys_interactive.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/0                                                                        0/0\"\n\"c> foo bar |foo-|bar|                                                           \"\ncursor: (24, 22)\n"
  },
  {
    "path": "tests/snapshots/keys_interactive__keys_interactive_ctrl_arrows.snap",
    "content": "---\nsource: tests/keys_interactive.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/0                                                                        0/0\"\n\"c> foo bar foo-bar                                                              \"\ncursor: (24, 19)\n"
  },
  {
    "path": "tests/snapshots/keys_interactive__keys_interactive_ctrl_b-2.snap",
    "content": "---\nsource: tests/keys_interactive.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/0                                                                        0/0\"\n\"c> |foo bar foo-bar                                                             \"\ncursor: (24, 5)\n"
  },
  {
    "path": "tests/snapshots/keys_interactive__keys_interactive_ctrl_b-3.snap",
    "content": "---\nsource: tests/keys_interactive.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/0                                                                        0/0\"\n\"c> |f|oo bar foo-bar                                                            \"\ncursor: (24, 7)\n"
  },
  {
    "path": "tests/snapshots/keys_interactive__keys_interactive_ctrl_b.snap",
    "content": "---\nsource: tests/keys_interactive.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/0                                                                        0/0\"\n\"c> foo bar foo-bar                                                              \"\ncursor: (24, 19)\n"
  },
  {
    "path": "tests/snapshots/keys_interactive__keys_interactive_ctrl_c.snap",
    "content": "---\nsource: tests/keys_interactive.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/0                                                                        0/0\"\n\"c> foo bar foo-bar                                                              \"\ncursor: (24, 19)\n"
  },
  {
    "path": "tests/snapshots/keys_interactive__keys_interactive_ctrl_d-2.snap",
    "content": "---\nsource: tests/keys_interactive.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/0                                                                        0/0\"\n\"c> |foo bar foo-bar                                                             \"\ncursor: (24, 5)\n"
  },
  {
    "path": "tests/snapshots/keys_interactive__keys_interactive_ctrl_d-3.snap",
    "content": "---\nsource: tests/keys_interactive.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/0                                                                        0/0\"\n\"c> ||oo bar foo-bar                                                             \"\ncursor: (24, 6)\n"
  },
  {
    "path": "tests/snapshots/keys_interactive__keys_interactive_ctrl_d.snap",
    "content": "---\nsource: tests/keys_interactive.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/0                                                                        0/0\"\n\"c> foo bar foo-bar                                                              \"\ncursor: (24, 19)\n"
  },
  {
    "path": "tests/snapshots/keys_interactive__keys_interactive_ctrl_e-2.snap",
    "content": "---\nsource: tests/keys_interactive.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/0                                                                        0/0\"\n\"c> |foo bar foo-bar                                                             \"\ncursor: (24, 5)\n"
  },
  {
    "path": "tests/snapshots/keys_interactive__keys_interactive_ctrl_e-3.snap",
    "content": "---\nsource: tests/keys_interactive.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/0                                                                        0/0\"\n\"c> |foo bar foo-bar|                                                            \"\ncursor: (24, 21)\n"
  },
  {
    "path": "tests/snapshots/keys_interactive__keys_interactive_ctrl_e.snap",
    "content": "---\nsource: tests/keys_interactive.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/0                                                                        0/0\"\n\"c> foo bar foo-bar                                                              \"\ncursor: (24, 19)\n"
  },
  {
    "path": "tests/snapshots/keys_interactive__keys_interactive_ctrl_f-2.snap",
    "content": "---\nsource: tests/keys_interactive.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/0                                                                        0/0\"\n\"c> |foo bar foo-bar                                                             \"\ncursor: (24, 5)\n"
  },
  {
    "path": "tests/snapshots/keys_interactive__keys_interactive_ctrl_f-3.snap",
    "content": "---\nsource: tests/keys_interactive.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/0                                                                        0/0\"\n\"c> |f|oo bar foo-bar                                                            \"\ncursor: (24, 7)\n"
  },
  {
    "path": "tests/snapshots/keys_interactive__keys_interactive_ctrl_f.snap",
    "content": "---\nsource: tests/keys_interactive.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/0                                                                        0/0\"\n\"c> foo bar foo-bar                                                              \"\ncursor: (24, 19)\n"
  },
  {
    "path": "tests/snapshots/keys_interactive__keys_interactive_ctrl_h-2.snap",
    "content": "---\nsource: tests/keys_interactive.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/0                                                                        0/0\"\n\"c> foo bar foo-ba|                                                              \"\ncursor: (24, 19)\n"
  },
  {
    "path": "tests/snapshots/keys_interactive__keys_interactive_ctrl_h.snap",
    "content": "---\nsource: tests/keys_interactive.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/0                                                                        0/0\"\n\"c> foo bar foo-bar                                                              \"\ncursor: (24, 19)\n"
  },
  {
    "path": "tests/snapshots/keys_interactive__keys_interactive_ctrl_k-2.snap",
    "content": "---\nsource: tests/keys_interactive.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  4                                                                             \"\n\"  3                                                                             \"\n\"> 2                                                                             \"\n\"  1                                                                             \"\n\"  4/4                                                                        1/0\"\n\"c>                                                                              \"\ncursor: (24, 4)\n"
  },
  {
    "path": "tests/snapshots/keys_interactive__keys_interactive_ctrl_k.snap",
    "content": "---\nsource: tests/keys_interactive.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  4                                                                             \"\n\"  3                                                                             \"\n\"  2                                                                             \"\n\"> 1                                                                             \"\n\"  4/4                                                                        0/0\"\n\"c>                                                                              \"\ncursor: (24, 4)\n"
  },
  {
    "path": "tests/snapshots/keys_interactive__keys_interactive_ctrl_m.snap",
    "content": "---\nsource: tests/keys_interactive.rs\nexpression: h.buffer_view()\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  4                                                                             \"\n\"  3                                                                             \"\n\"  2                                                                             \"\n\"> 1                                                                             \"\n\"  4/4                                                                        0/0\"\n\"c>                                                                              \"\n"
  },
  {
    "path": "tests/snapshots/keys_interactive__keys_interactive_ctrl_u-2.snap",
    "content": "---\nsource: tests/keys_interactive.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/0                                                                        0/0\"\n\"c> |                                                                            \"\ncursor: (24, 5)\n"
  },
  {
    "path": "tests/snapshots/keys_interactive__keys_interactive_ctrl_u.snap",
    "content": "---\nsource: tests/keys_interactive.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/0                                                                        0/0\"\n\"c> foo bar foo-bar                                                              \"\ncursor: (24, 19)\n"
  },
  {
    "path": "tests/snapshots/keys_interactive__keys_interactive_ctrl_w-2.snap",
    "content": "---\nsource: tests/keys_interactive.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/0                                                                        0/0\"\n\"c> foo bar |                                                                    \"\ncursor: (24, 13)\n"
  },
  {
    "path": "tests/snapshots/keys_interactive__keys_interactive_ctrl_w.snap",
    "content": "---\nsource: tests/keys_interactive.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/0                                                                        0/0\"\n\"c> foo bar foo-bar                                                              \"\ncursor: (24, 19)\n"
  },
  {
    "path": "tests/snapshots/keys_interactive__keys_interactive_ctrl_y-2.snap",
    "content": "---\nsource: tests/keys_interactive.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/0                                                                        0/0\"\n\"c> foo bar foo-|                                                                \"\ncursor: (24, 17)\n"
  },
  {
    "path": "tests/snapshots/keys_interactive__keys_interactive_ctrl_y-3.snap",
    "content": "---\nsource: tests/keys_interactive.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/0                                                                        0/0\"\n\"c> foo bar foo-|bar|                                                            \"\ncursor: (24, 21)\n"
  },
  {
    "path": "tests/snapshots/keys_interactive__keys_interactive_ctrl_y.snap",
    "content": "---\nsource: tests/keys_interactive.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/0                                                                        0/0\"\n\"c> foo bar foo-bar                                                              \"\ncursor: (24, 19)\n"
  },
  {
    "path": "tests/snapshots/keys_interactive__keys_interactive_enter.snap",
    "content": "---\nsource: tests/keys_interactive.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  4                                                                             \"\n\"  3                                                                             \"\n\"  2                                                                             \"\n\"> 1                                                                             \"\n\"  4/4                                                                        0/0\"\n\"c>                                                                              \"\ncursor: (24, 4)\n"
  },
  {
    "path": "tests/snapshots/keys_interactive__keys_interactive_tab-2.snap",
    "content": "---\nsource: tests/keys_interactive.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  4                                                                             \"\n\"  3                                                                             \"\n\"> 2                                                                             \"\n\"  1                                                                             \"\n\"  4/4                                                                        1/0\"\n\"c>                                                                              \"\ncursor: (24, 4)\n"
  },
  {
    "path": "tests/snapshots/keys_interactive__keys_interactive_tab-3.snap",
    "content": "---\nsource: tests/keys_interactive.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  4                                                                             \"\n\"  3                                                                             \"\n\"  2                                                                             \"\n\"> 1                                                                             \"\n\"  4/4                                                                        0/0\"\n\"c>                                                                              \"\ncursor: (24, 4)\n"
  },
  {
    "path": "tests/snapshots/keys_interactive__keys_interactive_tab.snap",
    "content": "---\nsource: tests/keys_interactive.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  4                                                                             \"\n\"  3                                                                             \"\n\"  2                                                                             \"\n\"> 1                                                                             \"\n\"  4/4                                                                        0/0\"\n\"c>                                                                              \"\ncursor: (24, 4)\n"
  },
  {
    "path": "tests/snapshots/layout__layout_border.snap",
    "content": "---\nsource: tests/layout.rs\nexpression: buf + & cursor_pos\n---\n\"┌──────────────────────────────────────────────────────────────────────────────┐\"\n\"│                                                                              │\"\n\"│                                                                              │\"\n\"│                                                                              │\"\n\"│                                                                              │\"\n\"│                                                                              │\"\n\"│                                                                              │\"\n\"│                                                                              │\"\n\"│                                                                              │\"\n\"│                                                                              │\"\n\"│                                                                              │\"\n\"│                                                                              │\"\n\"│              ac                                                              │\"\n\"│    multi-sel ab                                                              │\"\n\"│sel multi-sel a                                                               │\"\n\"└──────────────────────────────────────────────────────────────────────────────┘\"\n\"┌──────────────────────────────────────────────────────────────────────────────┐\"\n\"│              header line 2                                                   │\"\n\"│              header line 1                                                   │\"\n\"│              header                                                          │\"\n\"└──────────────────────────────────────────────────────────────────────────────┘\"\n\"┌  3/5 [2]──────────────────────────────────────────────────────────────────0/0┐\"\n\"│prompt a                                                                      │\"\n\"└──────────────────────────────────────────────────────────────────────────────┘\"\ncursor: (23, 10)\n"
  },
  {
    "path": "tests/snapshots/layout__layout_default.snap",
    "content": "---\nsource: tests/layout.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"              ac                                                                \"\n\"    multi-sel ab                                                                \"\n\"sel multi-sel a                                                                 \"\n\"              header line 2                                                     \"\n\"              header line 1                                                     \"\n\"              header                                                            \"\n\"  3/5 [2]                                                                    0/0\"\n\"prompt a                                                                        \"\ncursor: (24, 9)\n"
  },
  {
    "path": "tests/snapshots/layout__layout_reverse.snap",
    "content": "---\nsource: tests/layout.rs\nexpression: buf + & cursor_pos\n---\n\"prompt a                                                                        \"\n\"  3/5 [2]                                                                    0/0\"\n\"              header                                                            \"\n\"              header line 1                                                     \"\n\"              header line 2                                                     \"\n\"sel multi-sel a                                                                 \"\n\"    multi-sel ab                                                                \"\n\"              ac                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\ncursor: (1, 9)\n"
  },
  {
    "path": "tests/snapshots/layout__layout_reverse_border.snap",
    "content": "---\nsource: tests/layout.rs\nexpression: buf + & cursor_pos\n---\n\"┌──────────────────────────────────────────────────────────────────────────────┐\"\n\"│prompt a                                                                      │\"\n\"└  3/5 [2]──────────────────────────────────────────────────────────────────0/0┘\"\n\"┌──────────────────────────────────────────────────────────────────────────────┐\"\n\"│              header                                                          │\"\n\"│              header line 1                                                   │\"\n\"│              header line 2                                                   │\"\n\"└──────────────────────────────────────────────────────────────────────────────┘\"\n\"┌──────────────────────────────────────────────────────────────────────────────┐\"\n\"│sel multi-sel a                                                               │\"\n\"│    multi-sel ab                                                              │\"\n\"│              ac                                                              │\"\n\"│                                                                              │\"\n\"│                                                                              │\"\n\"│                                                                              │\"\n\"│                                                                              │\"\n\"│                                                                              │\"\n\"│                                                                              │\"\n\"│                                                                              │\"\n\"│                                                                              │\"\n\"│                                                                              │\"\n\"│                                                                              │\"\n\"│                                                                              │\"\n\"└──────────────────────────────────────────────────────────────────────────────┘\"\ncursor: (2, 10)\n"
  },
  {
    "path": "tests/snapshots/layout__layout_reverse_list.snap",
    "content": "---\nsource: tests/layout.rs\nexpression: buf + & cursor_pos\n---\n\"sel multi-sel a                                                                 \"\n\"    multi-sel ab                                                                \"\n\"              ac                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"              header line 1                                                     \"\n\"              header line 2                                                     \"\n\"              header                                                            \"\n\"  3/5 [2]                                                                    0/0\"\n\"prompt a                                                                        \"\ncursor: (24, 9)\n"
  },
  {
    "path": "tests/snapshots/layout__layout_reverse_list_border.snap",
    "content": "---\nsource: tests/layout.rs\nexpression: buf + & cursor_pos\n---\n\"┌──────────────────────────────────────────────────────────────────────────────┐\"\n\"│sel multi-sel a                                                               │\"\n\"│    multi-sel ab                                                              │\"\n\"│              ac                                                              │\"\n\"│                                                                              │\"\n\"│                                                                              │\"\n\"│                                                                              │\"\n\"│                                                                              │\"\n\"│                                                                              │\"\n\"│                                                                              │\"\n\"│                                                                              │\"\n\"│                                                                              │\"\n\"│                                                                              │\"\n\"│                                                                              │\"\n\"│                                                                              │\"\n\"└──────────────────────────────────────────────────────────────────────────────┘\"\n\"┌──────────────────────────────────────────────────────────────────────────────┐\"\n\"│              header line 1                                                   │\"\n\"│              header line 2                                                   │\"\n\"│              header                                                          │\"\n\"└──────────────────────────────────────────────────────────────────────────────┘\"\n\"┌  3/5 [2]──────────────────────────────────────────────────────────────────0/0┐\"\n\"│prompt a                                                                      │\"\n\"└──────────────────────────────────────────────────────────────────────────────┘\"\ncursor: (23, 10)\n"
  },
  {
    "path": "tests/snapshots/matcher__matcher_arinae.snap",
    "content": "---\nsource: tests/matcher.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  src/tui/item_list.rs                                                          \"\n\"> src/tui/mod.rs                                                                \"\n\"  2/49                                                                       0/0\"\n\"> stum                                                                          \"\ncursor: (24, 7)\n"
  },
  {
    "path": "tests/snapshots/matcher__matcher_arinae_typos.snap",
    "content": "---\nsource: tests/matcher.rs\nexpression: buf + & cursor_pos\n---\n\"  src/manpage.rs                                                                \"\n\"  src/util.rs                                                                   \"\n\"  src/fuzzy_matcher/clangd.rs                                                   \"\n\"  src/fuzzy_matcher/frizbee.rs                                                  \"\n\"  src/fuzzy_matcher/skim.rs                                                     \"\n\"  src/fuzzy_matcher/util.rs                                                     \"\n\"  src/fuzzy_matcher/mod.rs                                                      \"\n\"  src/item.rs                                                                   \"\n\"  src/theme.rs                                                                  \"\n\"  src/tmux.rs                                                                   \"\n\"  src/tui/header.rs                                                             \"\n\"  src/tui/statusline.rs                                                         \"\n\"  src/tui/input.rs                                                              \"\n\"  src/tui/app.rs                                                                \"\n\"  src/tui/backend.rs                                                            \"\n\"  src/tui/event.rs                                                              \"\n\"  src/tui/preview.rs                                                            \"\n\"  src/tui/widget.rs                                                             \"\n\"  src/tui/options.rs                                                            \"\n\"  src/tui/util.rs                                                               \"\n\"  src/tui/item_list.rs                                                          \"\n\"> src/tui/mod.rs                                                                \"\n\"  32/49                                                                      0/0\"\n\"> stum                                                                          \"\ncursor: (24, 7)\n"
  },
  {
    "path": "tests/snapshots/matcher__matcher_clangd.snap",
    "content": "---\nsource: tests/matcher.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  src/tui/item_list.rs                                                          \"\n\"> src/tui/mod.rs                                                                \"\n\"  2/49                                                                       0/0\"\n\"> stum                                                                          \"\ncursor: (24, 7)\n"
  },
  {
    "path": "tests/snapshots/matcher__matcher_default.snap",
    "content": "---\nsource: tests/matcher.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  src/tui/item_list.rs                                                          \"\n\"> src/tui/mod.rs                                                                \"\n\"  2/49                                                                       0/0\"\n\"> stum                                                                          \"\ncursor: (24, 7)\n"
  },
  {
    "path": "tests/snapshots/matcher__matcher_frizbee.snap",
    "content": "---\nsource: tests/matcher.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  src/tui/item_list.rs                                                          \"\n\"> src/tui/mod.rs                                                                \"\n\"  2/49                                                                       0/0\"\n\"> stum                                                                          \"\ncursor: (24, 7)\n"
  },
  {
    "path": "tests/snapshots/matcher__matcher_frizbee_typos.snap",
    "content": "---\nsource: tests/matcher.rs\nexpression: buf + & cursor_pos\n---\n\"  src/skim_item.rs                                                              \"\n\"  src/output.rs                                                                 \"\n\"  src/item.rs                                                                   \"\n\"  src/fuzzy_matcher/clangd.rs                                                   \"\n\"  src/fuzzy_matcher/frizbee.rs                                                  \"\n\"  src/fuzzy_matcher/skim.rs                                                     \"\n\"  src/fuzzy_matcher/util.rs                                                     \"\n\"  src/fuzzy_matcher/mod.rs                                                      \"\n\"  src/theme.rs                                                                  \"\n\"  src/tui/header.rs                                                             \"\n\"  src/tui/statusline.rs                                                         \"\n\"  src/tui/input.rs                                                              \"\n\"  src/tui/app.rs                                                                \"\n\"  src/tui/backend.rs                                                            \"\n\"  src/tui/event.rs                                                              \"\n\"  src/tui/preview.rs                                                            \"\n\"  src/tui/widget.rs                                                             \"\n\"  src/tui/options.rs                                                            \"\n\"  src/tui/util.rs                                                               \"\n\"  src/tmux.rs                                                                   \"\n\"  src/tui/item_list.rs                                                          \"\n\"> src/tui/mod.rs                                                                \"\n\"  24/49                                                                      0/0\"\n\"> stum                                                                          \"\ncursor: (24, 7)\n"
  },
  {
    "path": "tests/snapshots/matcher__matcher_fzy.snap",
    "content": "---\nsource: tests/matcher.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  src/tui/item_list.rs                                                          \"\n\"> src/tui/mod.rs                                                                \"\n\"  2/49                                                                       0/0\"\n\"> stum                                                                          \"\ncursor: (24, 7)\n"
  },
  {
    "path": "tests/snapshots/matcher__matcher_fzy_typos.snap",
    "content": "---\nsource: tests/matcher.rs\nexpression: buf + & cursor_pos\n---\n\"  src/output.rs                                                                 \"\n\"  src/helper/item_reader.rs                                                     \"\n\"  src/tmux.rs                                                                   \"\n\"  src/helper/item.rs                                                            \"\n\"  src/skim_item.rs                                                              \"\n\"  src/item.rs                                                                   \"\n\"  src/fuzzy_matcher/skim.rs                                                     \"\n\"  src/fuzzy_matcher/util.rs                                                     \"\n\"  src/fuzzy_matcher/mod.rs                                                      \"\n\"  src/tui/statusline.rs                                                         \"\n\"  src/tui/backend.rs                                                            \"\n\"  src/tui/preview.rs                                                            \"\n\"  src/tui/options.rs                                                            \"\n\"  src/tui/header.rs                                                             \"\n\"  src/tui/widget.rs                                                             \"\n\"  src/tui/input.rs                                                              \"\n\"  src/tui/event.rs                                                              \"\n\"  src/tui/util.rs                                                               \"\n\"  src/tui/app.rs                                                                \"\n\"  src/theme.rs                                                                  \"\n\"  src/tui/item_list.rs                                                          \"\n\"> src/tui/mod.rs                                                                \"\n\"  22/49                                                                      0/0\"\n\"> stum                                                                          \"\ncursor: (24, 7)\n"
  },
  {
    "path": "tests/snapshots/matcher__matcher_skim_v1.snap",
    "content": "---\nsource: tests/matcher.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  src/tui/item_list.rs                                                          \"\n\"> src/tui/mod.rs                                                                \"\n\"  2/49                                                                       0/0\"\n\"> stum                                                                          \"\ncursor: (24, 7)\n"
  },
  {
    "path": "tests/snapshots/matcher__matcher_skim_v2.snap",
    "content": "---\nsource: tests/matcher.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  src/tui/item_list.rs                                                          \"\n\"> src/tui/mod.rs                                                                \"\n\"  2/49                                                                       0/0\"\n\"> stum                                                                          \"\ncursor: (24, 7)\n"
  },
  {
    "path": "tests/snapshots/matcher__matcher_skim_v3.snap",
    "content": "---\nsource: tests/matcher.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  src/tui/item_list.rs                                                          \"\n\"> src/tui/mod.rs                                                                \"\n\"  2/49                                                                       0/0\"\n\"> stum                                                                          \"\ncursor: (24, 7)\n"
  },
  {
    "path": "tests/snapshots/matcher__matcher_skim_v3_typos.snap",
    "content": "---\nsource: tests/matcher.rs\nexpression: buf + & cursor_pos\n---\n\"  src/manpage.rs                                                                \"\n\"  src/util.rs                                                                   \"\n\"  src/item.rs                                                                   \"\n\"  src/fuzzy_matcher/clangd.rs                                                   \"\n\"  src/fuzzy_matcher/frizbee.rs                                                  \"\n\"  src/fuzzy_matcher/skim.rs                                                     \"\n\"  src/fuzzy_matcher/util.rs                                                     \"\n\"  src/fuzzy_matcher/mod.rs                                                      \"\n\"  src/theme.rs                                                                  \"\n\"  src/tmux.rs                                                                   \"\n\"  src/tui/header.rs                                                             \"\n\"  src/tui/statusline.rs                                                         \"\n\"  src/tui/input.rs                                                              \"\n\"  src/tui/app.rs                                                                \"\n\"  src/tui/backend.rs                                                            \"\n\"  src/tui/event.rs                                                              \"\n\"  src/tui/preview.rs                                                            \"\n\"  src/tui/widget.rs                                                             \"\n\"  src/tui/options.rs                                                            \"\n\"  src/tui/util.rs                                                               \"\n\"  src/tui/item_list.rs                                                          \"\n\"> src/tui/mod.rs                                                                \"\n\"  37/49                                                                      0/0\"\n\"> stum                                                                          \"\ncursor: (24, 7)\n"
  },
  {
    "path": "tests/snapshots/normalize__insta_no_normalize_accented_item-2.snap",
    "content": "---\nsource: tests/normalize.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"> cafe                                                                          \"\n\"  1/3                                                                        0/0\"\n\"> cafe                                                                          \"\ncursor: (24, 7)\n"
  },
  {
    "path": "tests/snapshots/normalize__insta_no_normalize_accented_item.snap",
    "content": "---\nsource: tests/normalize.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  tea                                                                           \"\n\"  cafe                                                                          \"\n\"> café                                                                          \"\n\"  3/3                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/normalize__insta_normalize_accented_item_unaccented_query-2.snap",
    "content": "---\nsource: tests/normalize.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  cafe                                                                          \"\n\"> café                                                                          \"\n\"  2/3                                                                        0/0\"\n\"> cafe                                                                          \"\ncursor: (24, 7)\n"
  },
  {
    "path": "tests/snapshots/normalize__insta_normalize_accented_item_unaccented_query.snap",
    "content": "---\nsource: tests/normalize.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  tea                                                                           \"\n\"  cafe                                                                          \"\n\"> café                                                                          \"\n\"  3/3                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/normalize__insta_normalize_case_insensitive-2.snap",
    "content": "---\nsource: tests/normalize.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  cafe                                                                          \"\n\"  café                                                                          \"\n\"  CAFE                                                                          \"\n\"> Café                                                                          \"\n\"  4/4                                                                        0/0\"\n\"> cafe                                                                          \"\ncursor: (24, 7)\n"
  },
  {
    "path": "tests/snapshots/normalize__insta_normalize_case_insensitive.snap",
    "content": "---\nsource: tests/normalize.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  cafe                                                                          \"\n\"  café                                                                          \"\n\"  CAFE                                                                          \"\n\"> Café                                                                          \"\n\"  4/4                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/normalize__insta_normalize_combined_chars-2.snap",
    "content": "---\nsource: tests/normalize.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  senor                                                                         \"\n\"> señor                                                                         \"\n\"  2/4                                                                        0/0\"\n\"> senor                                                                         \"\ncursor: (24, 8)\n"
  },
  {
    "path": "tests/snapshots/normalize__insta_normalize_combined_chars-3.snap",
    "content": "---\nsource: tests/normalize.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  manana                                                                        \"\n\"> mañana                                                                        \"\n\"  2/4                                                                        0/0\"\n\"> manana                                                                        \"\ncursor: (24, 9)\n"
  },
  {
    "path": "tests/snapshots/normalize__insta_normalize_combined_chars.snap",
    "content": "---\nsource: tests/normalize.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  manana                                                                        \"\n\"  mañana                                                                        \"\n\"  senor                                                                         \"\n\"> señor                                                                         \"\n\"  4/4                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/normalize__insta_normalize_cyrillic-2.snap",
    "content": "---\nsource: tests/normalize.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  Слово                                                                         \"\n\"> слово                                                                         \"\n\"  2/2                                                                        0/0\"\n\"> слово                                                                         \"\ncursor: (24, 8)\n"
  },
  {
    "path": "tests/snapshots/normalize__insta_normalize_cyrillic.snap",
    "content": "---\nsource: tests/normalize.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  Слово                                                                         \"\n\"> слово                                                                         \"\n\"  2/2                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/normalize__insta_normalize_exact_match-2.snap",
    "content": "---\nsource: tests/normalize.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  cafeína                                                                       \"\n\"  cafe                                                                          \"\n\"> café                                                                          \"\n\"  3/3                                                                        0/0\"\n\"> 'cafe                                                                         \"\ncursor: (24, 8)\n"
  },
  {
    "path": "tests/snapshots/normalize__insta_normalize_exact_match.snap",
    "content": "---\nsource: tests/normalize.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  cafeína                                                                       \"\n\"  cafe                                                                          \"\n\"> café                                                                          \"\n\"  3/3                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/normalize__insta_normalize_multiple_diacritics-2.snap",
    "content": "---\nsource: tests/normalize.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  naive                                                                         \"\n\"> naïve                                                                         \"\n\"  2/4                                                                        0/0\"\n\"> naive                                                                         \"\ncursor: (24, 8)\n"
  },
  {
    "path": "tests/snapshots/normalize__insta_normalize_multiple_diacritics-3.snap",
    "content": "---\nsource: tests/normalize.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  resume                                                                        \"\n\"> résumé                                                                        \"\n\"  2/4                                                                        0/0\"\n\"> resume                                                                        \"\ncursor: (24, 9)\n"
  },
  {
    "path": "tests/snapshots/normalize__insta_normalize_multiple_diacritics.snap",
    "content": "---\nsource: tests/normalize.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  resume                                                                        \"\n\"  résumé                                                                        \"\n\"  naive                                                                         \"\n\"> naïve                                                                         \"\n\"  4/4                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/normalize__insta_normalize_negation-2.snap",
    "content": "---\nsource: tests/normalize.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"> tea                                                                           \"\n\"  1/3                                                                        0/0\"\n\"> !cafe                                                                         \"\ncursor: (24, 8)\n"
  },
  {
    "path": "tests/snapshots/normalize__insta_normalize_negation.snap",
    "content": "---\nsource: tests/normalize.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  tea                                                                           \"\n\"  cafe                                                                          \"\n\"> café                                                                          \"\n\"  3/3                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/normalize__insta_normalize_prefix-2.snap",
    "content": "---\nsource: tests/normalize.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  cafe solo                                                                     \"\n\"> café con leche                                                                \"\n\"  2/3                                                                        0/0\"\n\"> ^cafe                                                                         \"\ncursor: (24, 8)\n"
  },
  {
    "path": "tests/snapshots/normalize__insta_normalize_prefix.snap",
    "content": "---\nsource: tests/normalize.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  té verde                                                                      \"\n\"  cafe solo                                                                     \"\n\"> café con leche                                                                \"\n\"  3/3                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/normalize__insta_normalize_suffix-2.snap",
    "content": "---\nsource: tests/normalize.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  the cafe                                                                      \"\n\"> mi café                                                                       \"\n\"  2/3                                                                        0/0\"\n\"> cafe$                                                                         \"\ncursor: (24, 8)\n"
  },
  {
    "path": "tests/snapshots/normalize__insta_normalize_suffix.snap",
    "content": "---\nsource: tests/normalize.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  green tea                                                                     \"\n\"  the cafe                                                                      \"\n\"> mi café                                                                       \"\n\"  3/3                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/normalize__insta_normalize_umlauts-2.snap",
    "content": "---\nsource: tests/normalize.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  uber                                                                          \"\n\"> über                                                                          \"\n\"  2/4                                                                        0/0\"\n\"> uber                                                                          \"\ncursor: (24, 7)\n"
  },
  {
    "path": "tests/snapshots/normalize__insta_normalize_umlauts-3.snap",
    "content": "---\nsource: tests/normalize.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"> grosse                                                                        \"\n\"  1/4                                                                        0/0\"\n\"> grosse                                                                        \"\ncursor: (24, 9)\n"
  },
  {
    "path": "tests/snapshots/normalize__insta_normalize_umlauts.snap",
    "content": "---\nsource: tests/normalize.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  grosse                                                                        \"\n\"  größe                                                                         \"\n\"  uber                                                                          \"\n\"> über                                                                          \"\n\"  4/4                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/normalize__insta_normalize_unaccented_item_accented_query-2.snap",
    "content": "---\nsource: tests/normalize.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  cafe                                                                          \"\n\"> café                                                                          \"\n\"  2/3                                                                        0/0\"\n\"> café                                                                          \"\ncursor: (24, 7)\n"
  },
  {
    "path": "tests/snapshots/normalize__insta_normalize_unaccented_item_accented_query.snap",
    "content": "---\nsource: tests/normalize.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  tea                                                                           \"\n\"  cafe                                                                          \"\n\"> café                                                                          \"\n\"  3/3                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/options__opt_border_double.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"╔══════════════════════════════════════════════════════════════════════════════╗\"\n\"║                                                                              ║\"\n\"║                                                                              ║\"\n\"║                                                                              ║\"\n\"║                                                                              ║\"\n\"║                                                                              ║\"\n\"║                                                                              ║\"\n\"║                                                                              ║\"\n\"║                                                                              ║\"\n\"║                                                                              ║\"\n\"║                                                                              ║\"\n\"║                                                                              ║\"\n\"║                                                                              ║\"\n\"║                                                                              ║\"\n\"║                                                                              ║\"\n\"║                                                                              ║\"\n\"║                                                                              ║\"\n\"║                                                                              ║\"\n\"║  ac                                                                          ║\"\n\"║> a                                                                           ║\"\n\"╚══════════════════════════════════════════════════════════════════════════════╝\"\n\"╔  2/4══════════════════════════════════════════════════════════════════════0/0╗\"\n\"║> a                                                                           ║\"\n\"╚══════════════════════════════════════════════════════════════════════════════╝\"\ncursor: (23, 5)\n"
  },
  {
    "path": "tests/snapshots/options__opt_border_heavy_double_dashed.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"┏╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍┓\"\n\"╏                                                                              ╏\"\n\"╏                                                                              ╏\"\n\"╏                                                                              ╏\"\n\"╏                                                                              ╏\"\n\"╏                                                                              ╏\"\n\"╏                                                                              ╏\"\n\"╏                                                                              ╏\"\n\"╏                                                                              ╏\"\n\"╏                                                                              ╏\"\n\"╏                                                                              ╏\"\n\"╏                                                                              ╏\"\n\"╏                                                                              ╏\"\n\"╏                                                                              ╏\"\n\"╏                                                                              ╏\"\n\"╏                                                                              ╏\"\n\"╏                                                                              ╏\"\n\"╏                                                                              ╏\"\n\"╏  ac                                                                          ╏\"\n\"╏> a                                                                           ╏\"\n\"┗╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍┛\"\n\"┏  2/4╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍0/0┓\"\n\"╏> a                                                                           ╏\"\n\"┗╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍┛\"\ncursor: (23, 5)\n"
  },
  {
    "path": "tests/snapshots/options__opt_border_heavy_quadruple_dashed.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"┏┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┓\"\n\"┋                                                                              ┋\"\n\"┋                                                                              ┋\"\n\"┋                                                                              ┋\"\n\"┋                                                                              ┋\"\n\"┋                                                                              ┋\"\n\"┋                                                                              ┋\"\n\"┋                                                                              ┋\"\n\"┋                                                                              ┋\"\n\"┋                                                                              ┋\"\n\"┋                                                                              ┋\"\n\"┋                                                                              ┋\"\n\"┋                                                                              ┋\"\n\"┋                                                                              ┋\"\n\"┋                                                                              ┋\"\n\"┋                                                                              ┋\"\n\"┋                                                                              ┋\"\n\"┋                                                                              ┋\"\n\"┋  ac                                                                          ┋\"\n\"┋> a                                                                           ┋\"\n\"┗┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┛\"\n\"┏  2/4┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉0/0┓\"\n\"┋> a                                                                           ┋\"\n\"┗┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┛\"\ncursor: (23, 5)\n"
  },
  {
    "path": "tests/snapshots/options__opt_border_heavy_triple_dashed.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"┏┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┓\"\n\"┇                                                                              ┇\"\n\"┇                                                                              ┇\"\n\"┇                                                                              ┇\"\n\"┇                                                                              ┇\"\n\"┇                                                                              ┇\"\n\"┇                                                                              ┇\"\n\"┇                                                                              ┇\"\n\"┇                                                                              ┇\"\n\"┇                                                                              ┇\"\n\"┇                                                                              ┇\"\n\"┇                                                                              ┇\"\n\"┇                                                                              ┇\"\n\"┇                                                                              ┇\"\n\"┇                                                                              ┇\"\n\"┇                                                                              ┇\"\n\"┇                                                                              ┇\"\n\"┇                                                                              ┇\"\n\"┇  ac                                                                          ┇\"\n\"┇> a                                                                           ┇\"\n\"┗┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┛\"\n\"┏  2/4┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅0/0┓\"\n\"┇> a                                                                           ┇\"\n\"┗┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┛\"\ncursor: (23, 5)\n"
  },
  {
    "path": "tests/snapshots/options__opt_border_light_double_dashed.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"┌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┐\"\n\"╎                                                                              ╎\"\n\"╎                                                                              ╎\"\n\"╎                                                                              ╎\"\n\"╎                                                                              ╎\"\n\"╎                                                                              ╎\"\n\"╎                                                                              ╎\"\n\"╎                                                                              ╎\"\n\"╎                                                                              ╎\"\n\"╎                                                                              ╎\"\n\"╎                                                                              ╎\"\n\"╎                                                                              ╎\"\n\"╎                                                                              ╎\"\n\"╎                                                                              ╎\"\n\"╎                                                                              ╎\"\n\"╎                                                                              ╎\"\n\"╎                                                                              ╎\"\n\"╎                                                                              ╎\"\n\"╎  ac                                                                          ╎\"\n\"╎> a                                                                           ╎\"\n\"└╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┘\"\n\"┌  2/4╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌0/0┐\"\n\"╎> a                                                                           ╎\"\n\"└╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┘\"\ncursor: (23, 5)\n"
  },
  {
    "path": "tests/snapshots/options__opt_border_light_quadruple_dashed.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"┌┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┐\"\n\"┊                                                                              ┊\"\n\"┊                                                                              ┊\"\n\"┊                                                                              ┊\"\n\"┊                                                                              ┊\"\n\"┊                                                                              ┊\"\n\"┊                                                                              ┊\"\n\"┊                                                                              ┊\"\n\"┊                                                                              ┊\"\n\"┊                                                                              ┊\"\n\"┊                                                                              ┊\"\n\"┊                                                                              ┊\"\n\"┊                                                                              ┊\"\n\"┊                                                                              ┊\"\n\"┊                                                                              ┊\"\n\"┊                                                                              ┊\"\n\"┊                                                                              ┊\"\n\"┊                                                                              ┊\"\n\"┊  ac                                                                          ┊\"\n\"┊> a                                                                           ┊\"\n\"└┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┘\"\n\"┌  2/4┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈0/0┐\"\n\"┊> a                                                                           ┊\"\n\"└┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┘\"\ncursor: (23, 5)\n"
  },
  {
    "path": "tests/snapshots/options__opt_border_light_triple_dashed.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"┌┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┐\"\n\"┆                                                                              ┆\"\n\"┆                                                                              ┆\"\n\"┆                                                                              ┆\"\n\"┆                                                                              ┆\"\n\"┆                                                                              ┆\"\n\"┆                                                                              ┆\"\n\"┆                                                                              ┆\"\n\"┆                                                                              ┆\"\n\"┆                                                                              ┆\"\n\"┆                                                                              ┆\"\n\"┆                                                                              ┆\"\n\"┆                                                                              ┆\"\n\"┆                                                                              ┆\"\n\"┆                                                                              ┆\"\n\"┆                                                                              ┆\"\n\"┆                                                                              ┆\"\n\"┆                                                                              ┆\"\n\"┆  ac                                                                          ┆\"\n\"┆> a                                                                           ┆\"\n\"└┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┘\"\n\"┌  2/4┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄0/0┐\"\n\"┆> a                                                                           ┆\"\n\"└┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┘\"\ncursor: (23, 5)\n"
  },
  {
    "path": "tests/snapshots/options__opt_border_plain.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"┌──────────────────────────────────────────────────────────────────────────────┐\"\n\"│                                                                              │\"\n\"│                                                                              │\"\n\"│                                                                              │\"\n\"│                                                                              │\"\n\"│                                                                              │\"\n\"│                                                                              │\"\n\"│                                                                              │\"\n\"│                                                                              │\"\n\"│                                                                              │\"\n\"│                                                                              │\"\n\"│                                                                              │\"\n\"│                                                                              │\"\n\"│                                                                              │\"\n\"│                                                                              │\"\n\"│                                                                              │\"\n\"│                                                                              │\"\n\"│                                                                              │\"\n\"│  ac                                                                          │\"\n\"│> a                                                                           │\"\n\"└──────────────────────────────────────────────────────────────────────────────┘\"\n\"┌  2/4──────────────────────────────────────────────────────────────────────0/0┐\"\n\"│> a                                                                           │\"\n\"└──────────────────────────────────────────────────────────────────────────────┘\"\ncursor: (23, 5)\n"
  },
  {
    "path": "tests/snapshots/options__opt_border_quadrant_inside.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"▗▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▖\"\n\"▐                                                                              ▌\"\n\"▐                                                                              ▌\"\n\"▐                                                                              ▌\"\n\"▐                                                                              ▌\"\n\"▐                                                                              ▌\"\n\"▐                                                                              ▌\"\n\"▐                                                                              ▌\"\n\"▐                                                                              ▌\"\n\"▐                                                                              ▌\"\n\"▐                                                                              ▌\"\n\"▐                                                                              ▌\"\n\"▐                                                                              ▌\"\n\"▐                                                                              ▌\"\n\"▐                                                                              ▌\"\n\"▐                                                                              ▌\"\n\"▐                                                                              ▌\"\n\"▐                                                                              ▌\"\n\"▐  ac                                                                          ▌\"\n\"▐> a                                                                           ▌\"\n\"▝▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▘\"\n\"▗  2/4▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄0/0▖\"\n\"▐> a                                                                           ▌\"\n\"▝▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▘\"\ncursor: (23, 5)\n"
  },
  {
    "path": "tests/snapshots/options__opt_border_quadrant_outside.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"▛▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▜\"\n\"▌                                                                              ▐\"\n\"▌                                                                              ▐\"\n\"▌                                                                              ▐\"\n\"▌                                                                              ▐\"\n\"▌                                                                              ▐\"\n\"▌                                                                              ▐\"\n\"▌                                                                              ▐\"\n\"▌                                                                              ▐\"\n\"▌                                                                              ▐\"\n\"▌                                                                              ▐\"\n\"▌                                                                              ▐\"\n\"▌                                                                              ▐\"\n\"▌                                                                              ▐\"\n\"▌                                                                              ▐\"\n\"▌                                                                              ▐\"\n\"▌                                                                              ▐\"\n\"▌                                                                              ▐\"\n\"▌  ac                                                                          ▐\"\n\"▌> a                                                                           ▐\"\n\"▙▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▟\"\n\"▛  2/4▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀0/0▜\"\n\"▌> a                                                                           ▐\"\n\"▙▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▟\"\ncursor: (23, 5)\n"
  },
  {
    "path": "tests/snapshots/options__opt_border_rounded.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"╭──────────────────────────────────────────────────────────────────────────────╮\"\n\"│                                                                              │\"\n\"│                                                                              │\"\n\"│                                                                              │\"\n\"│                                                                              │\"\n\"│                                                                              │\"\n\"│                                                                              │\"\n\"│                                                                              │\"\n\"│                                                                              │\"\n\"│                                                                              │\"\n\"│                                                                              │\"\n\"│                                                                              │\"\n\"│                                                                              │\"\n\"│                                                                              │\"\n\"│                                                                              │\"\n\"│                                                                              │\"\n\"│                                                                              │\"\n\"│                                                                              │\"\n\"│  ac                                                                          │\"\n\"│> a                                                                           │\"\n\"╰──────────────────────────────────────────────────────────────────────────────╯\"\n\"╭  2/4──────────────────────────────────────────────────────────────────────0/0╮\"\n\"│> a                                                                           │\"\n\"╰──────────────────────────────────────────────────────────────────────────────╯\"\ncursor: (23, 5)\n"
  },
  {
    "path": "tests/snapshots/options__opt_border_thick.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓\"\n\"┃                                                                              ┃\"\n\"┃                                                                              ┃\"\n\"┃                                                                              ┃\"\n\"┃                                                                              ┃\"\n\"┃                                                                              ┃\"\n\"┃                                                                              ┃\"\n\"┃                                                                              ┃\"\n\"┃                                                                              ┃\"\n\"┃                                                                              ┃\"\n\"┃                                                                              ┃\"\n\"┃                                                                              ┃\"\n\"┃                                                                              ┃\"\n\"┃                                                                              ┃\"\n\"┃                                                                              ┃\"\n\"┃                                                                              ┃\"\n\"┃                                                                              ┃\"\n\"┃                                                                              ┃\"\n\"┃  ac                                                                          ┃\"\n\"┃> a                                                                           ┃\"\n\"┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛\"\n\"┏  2/4━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━0/0┓\"\n\"┃> a                                                                           ┃\"\n\"┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛\"\ncursor: (23, 5)\n"
  },
  {
    "path": "tests/snapshots/options__opt_cycle-2.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"> c                                                                             \"\n\"  b                                                                             \"\n\"  a                                                                             \"\n\"  3/3                                                                        2/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/options__opt_cycle-3.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  c                                                                             \"\n\"  b                                                                             \"\n\"> a                                                                             \"\n\"  3/3                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/options__opt_cycle.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  c                                                                             \"\n\"  b                                                                             \"\n\"> a                                                                             \"\n\"  3/3                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/options__opt_cycle_header_lines-2.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"> d                                                                             \"\n\"  c                                                                             \"\n\"  b                                                                             \"\n\"  a                                                                             \"\n\"  3/3                                                                        2/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/options__opt_cycle_header_lines-3.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  d                                                                             \"\n\"  c                                                                             \"\n\"> b                                                                             \"\n\"  a                                                                             \"\n\"  3/3                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/options__opt_cycle_header_lines.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  d                                                                             \"\n\"  c                                                                             \"\n\"> b                                                                             \"\n\"  a                                                                             \"\n\"  3/3                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/options__opt_disabled-2.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  d                                                                             \"\n\"  c                                                                             \"\n\"  b                                                                             \"\n\"> a                                                                             \"\n\"  4/4                                                                        0/0\"\n\"> b                                                                             \"\ncursor: (24, 4)\n"
  },
  {
    "path": "tests/snapshots/options__opt_disabled.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  d                                                                             \"\n\"  c                                                                             \"\n\"  b                                                                             \"\n\"> a                                                                             \"\n\"  4/4                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/options__opt_ellipsis.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                │a                                                              \"\n\"                │                                                               \"\n\"                │                                                               \"\n\"                │                                                               \"\n\"                │                                                               \"\n\"                │                                                               \"\n\"                │                                                               \"\n\"                │                                                               \"\n\"                │                                                               \"\n\"                │                                                               \"\n\"                │                                                               \"\n\"                │                                                               \"\n\"                │                                                               \"\n\"                │                                                               \"\n\"                │                                                               \"\n\"                │                                                               \"\n\"                │                                                               \"\n\"                │                                                               \"\n\"                │                                                               \"\n\"                │                                                               \"\n\"                │                                                               \"\n\"> %%%fggghiij%%%│                                                               \"\n\"  1/1        0/0│                                                               \"\n\"> ij            │                                                               \"\ncursor: (24, 5)\n"
  },
  {
    "path": "tests/snapshots/options__opt_exit_0_enter.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"> 1                                                                             \"\n\"  1/3                                                                        0/0\"\n\"> 1                                                                             \"\ncursor: (24, 4)\n"
  },
  {
    "path": "tests/snapshots/options__opt_header_inline_info.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  c                                                                             \"\n\"  b                                                                             \"\n\"> a                                                                             \"\n\"  test_header                                                                   \"\n\">   < 3/3                                                                    0/0\"\ncursor: (25, 3)\n"
  },
  {
    "path": "tests/snapshots/options__opt_header_lines_1.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  c                                                                             \"\n\"> b                                                                             \"\n\"  a                                                                             \"\n\"  2/2                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/options__opt_header_lines_all.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  c                                                                             \"\n\"  b                                                                             \"\n\"  a                                                                             \"\n\"  0/0                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/options__opt_header_lines_inline_info.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  c                                                                             \"\n\"> b                                                                             \"\n\"  a                                                                             \"\n\">   < 2/2                                                                    0/0\"\ncursor: (25, 3)\n"
  },
  {
    "path": "tests/snapshots/options__opt_header_lines_reverse.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\">                                                                               \"\n\"  2/2                                                                        0/0\"\n\"  a                                                                             \"\n\"> b                                                                             \"\n\"  c                                                                             \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\ncursor: (1, 3)\n"
  },
  {
    "path": "tests/snapshots/options__opt_header_lines_reverse_inline_info.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\">   < 2/2                                                                    0/0\"\n\"  a                                                                             \"\n\"> b                                                                             \"\n\"  c                                                                             \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\ncursor: (1, 3)\n"
  },
  {
    "path": "tests/snapshots/options__opt_header_multiline.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  c                                                                             \"\n\"  b                                                                             \"\n\"> a                                                                             \"\n\"  header 1                                                                      \"\n\"  header 2                                                                      \"\n\"  3/3                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/options__opt_header_only.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  c                                                                             \"\n\"  b                                                                             \"\n\"> a                                                                             \"\n\"  test_header                                                                   \"\n\"  3/3                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/options__opt_header_reverse.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\">                                                                               \"\n\"  3/3                                                                        0/0\"\n\"  test_header                                                                   \"\n\"> a                                                                             \"\n\"  b                                                                             \"\n\"  c                                                                             \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\ncursor: (1, 3)\n"
  },
  {
    "path": "tests/snapshots/options__opt_header_reverse_inline_info.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\">   < 3/3                                                                    0/0\"\n\"  test_header                                                                   \"\n\"> a                                                                             \"\n\"  b                                                                             \"\n\"  c                                                                             \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\ncursor: (1, 3)\n"
  },
  {
    "path": "tests/snapshots/options__opt_hscroll_begin.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"> baaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa..\"\n\"  1/1                                                                        0/0\"\n\"> b                                                                             \"\ncursor: (24, 4)\n"
  },
  {
    "path": "tests/snapshots/options__opt_hscroll_end.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"> ..aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"\n\"  1/1                                                                        0/0\"\n\"> b                                                                             \"\ncursor: (24, 4)\n"
  },
  {
    "path": "tests/snapshots/options__opt_hscroll_middle.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"> ..aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa..\"\n\"  1/1                                                                        0/0\"\n\"> b                                                                             \"\ncursor: (24, 4)\n"
  },
  {
    "path": "tests/snapshots/options__opt_info_control-2.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"> a                                                                             \"\n\"  1/3                                                                        0/0\"\n\"> a                                                                             \"\ncursor: (24, 4)\n"
  },
  {
    "path": "tests/snapshots/options__opt_info_control.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  c                                                                             \"\n\"  b                                                                             \"\n\"> a                                                                             \"\n\"  3/3                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/options__opt_info_default-2.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"> a                                                                             \"\n\"  1/3                                                                        0/0\"\n\"> a                                                                             \"\ncursor: (24, 4)\n"
  },
  {
    "path": "tests/snapshots/options__opt_info_default.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  c                                                                             \"\n\"  b                                                                             \"\n\"> a                                                                             \"\n\"  3/3                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/options__opt_info_hidden.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  c                                                                             \"\n\"  b                                                                             \"\n\"> a                                                                             \"\n\">                                                                               \"\ncursor: (25, 3)\n"
  },
  {
    "path": "tests/snapshots/options__opt_info_inline-2.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"> a                                                                             \"\n\"> a  < 1/3                                                                   0/0\"\ncursor: (25, 4)\n"
  },
  {
    "path": "tests/snapshots/options__opt_info_inline.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  c                                                                             \"\n\"  b                                                                             \"\n\"> a                                                                             \"\n\">   < 3/3                                                                    0/0\"\ncursor: (25, 3)\n"
  },
  {
    "path": "tests/snapshots/options__opt_inline_info-2.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"> a                                                                             \"\n\"> a  < 1/3                                                                   0/0\"\ncursor: (25, 4)\n"
  },
  {
    "path": "tests/snapshots/options__opt_inline_info.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  c                                                                             \"\n\"  b                                                                             \"\n\"> a                                                                             \"\n\">   < 3/3                                                                    0/0\"\ncursor: (25, 3)\n"
  },
  {
    "path": "tests/snapshots/options__opt_min_query_length-2.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"> li                                                                            \"\ncursor: (25, 5)\n"
  },
  {
    "path": "tests/snapshots/options__opt_min_query_length-3.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  line3                                                                         \"\n\"  line2                                                                         \"\n\"> line1                                                                         \"\n\"> lin                                                                           \"\ncursor: (25, 6)\n"
  },
  {
    "path": "tests/snapshots/options__opt_min_query_length.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\">                                                                               \"\ncursor: (25, 3)\n"
  },
  {
    "path": "tests/snapshots/options__opt_min_query_length_interactive-2.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"c> li                                                                           \"\ncursor: (25, 6)\n"
  },
  {
    "path": "tests/snapshots/options__opt_min_query_length_interactive-3.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  line3                                                                         \"\n\"  line2                                                                         \"\n\"> line1                                                                         \"\n\"c> lin                                                                          \"\ncursor: (25, 7)\n"
  },
  {
    "path": "tests/snapshots/options__opt_min_query_length_interactive.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"c>                                                                              \"\ncursor: (25, 4)\n"
  },
  {
    "path": "tests/snapshots/options__opt_multi-2.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  c                                                                             \"\n\"> b                                                                             \"\n\" >a                                                                             \"\n\"  3/3 [1]                                                                    1/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/options__opt_multi-3.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"> c                                                                             \"\n\" >b                                                                             \"\n\" >a                                                                             \"\n\"  3/3 [2]                                                                    2/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/options__opt_multi.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  c                                                                             \"\n\"  b                                                                             \"\n\"> a                                                                             \"\n\"  3/3                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/options__opt_multi_selector-2.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  c                                                                             \"\n\"> b                                                                             \"\n\" $a                                                                             \"\n\"  3/3 [1]                                                                    1/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/options__opt_multi_selector.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  c                                                                             \"\n\"  b                                                                             \"\n\"> a                                                                             \"\n\"  3/3                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/options__opt_multiple_flags_cmd_prompt.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\">                                                                               \"\n\"  1/1                                                                        0/0\"\n\"c                                                                               \"\ncursor: (24, 2)\n"
  },
  {
    "path": "tests/snapshots/options__opt_multiple_flags_cmd_query.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\">                                                                               \"\n\"  1/1                                                                        0/0\"\n\"c> b                                                                            \"\ncursor: (24, 5)\n"
  },
  {
    "path": "tests/snapshots/options__opt_multiple_flags_combined_nth-2.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/2                                                                        0/0\"\n\"> c                                                                             \"\ncursor: (24, 4)\n"
  },
  {
    "path": "tests/snapshots/options__opt_multiple_flags_combined_nth.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  d e f                                                                         \"\n\"> a b c                                                                         \"\n\"  2/2                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/options__opt_multiple_flags_combined_with_nth.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  d e                                                                           \"\n\"> a b                                                                           \"\n\"  2/2                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/options__opt_multiple_flags_interactive.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\">                                                                               \"\n\"  1/1                                                                        0/0\"\n\"c>                                                                              \"\ncursor: (24, 4)\n"
  },
  {
    "path": "tests/snapshots/options__opt_multiple_flags_layout_and_reverse.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\">                                                                               \"\n\"  2/2                                                                        0/0\"\n\"> a b c                                                                         \"\n\"  d e f                                                                         \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\ncursor: (1, 3)\n"
  },
  {
    "path": "tests/snapshots/options__opt_multiple_flags_prompt.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\">                                                                               \"\n\"  1/1                                                                        0/0\"\n\"c                                                                               \"\ncursor: (24, 2)\n"
  },
  {
    "path": "tests/snapshots/options__opt_multiple_flags_reverse.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\">                                                                               \"\n\"  1/1                                                                        0/0\"\n\">                                                                               \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\ncursor: (1, 3)\n"
  },
  {
    "path": "tests/snapshots/options__opt_multiple_flags_reverse_and_layout.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  d e f                                                                         \"\n\"> a b c                                                                         \"\n\"  2/2                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/options__opt_no_clear_if_empty-2.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"> xxxx                                                                          \"\n\"  0/0                                                                        0/0\"\n\"c>                                                                              \"\ncursor: (24, 4)\n"
  },
  {
    "path": "tests/snapshots/options__opt_no_clear_if_empty.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"> xxxx                                                                          \"\n\"  1/1                                                                        0/0\"\n\"c> xxxx                                                                         \"\ncursor: (24, 8)\n"
  },
  {
    "path": "tests/snapshots/options__opt_no_hscroll.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"> aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa..\"\n\"  1/1                                                                        0/0\"\n\"> b                                                                             \"\ncursor: (24, 4)\n"
  },
  {
    "path": "tests/snapshots/options__opt_no_info.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  c                                                                             \"\n\"  b                                                                             \"\n\"> a                                                                             \"\n\">                                                                               \"\ncursor: (25, 3)\n"
  },
  {
    "path": "tests/snapshots/options__opt_no_sort-2.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  cc                                                                            \"\n\"  bc                                                                            \"\n\"> ac                                                                            \"\n\"  3/3                                                                        0/0\"\n\"> c                                                                             \"\ncursor: (24, 4)\n"
  },
  {
    "path": "tests/snapshots/options__opt_no_sort.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  cc                                                                            \"\n\"  bc                                                                            \"\n\"> ac                                                                            \"\n\"  3/3                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/options__opt_nth_1-2.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"> f1,f2,f3,f4                                                                   \"\n\"  1/1                                                                        0/0\"\n\"> 1                                                                             \"\ncursor: (24, 4)\n"
  },
  {
    "path": "tests/snapshots/options__opt_nth_1-3.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"> f1,f2,f3,f4                                                                   \"\n\"  1/1                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/options__opt_nth_1-4.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/1                                                                        0/0\"\n\"> 2                                                                             \"\ncursor: (24, 4)\n"
  },
  {
    "path": "tests/snapshots/options__opt_nth_1.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"> f1,f2,f3,f4                                                                   \"\n\"  1/1                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/options__opt_nth_2-2.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"> f1,f2,f3,f4                                                                   \"\n\"  1/1                                                                        0/0\"\n\"> 2                                                                             \"\ncursor: (24, 4)\n"
  },
  {
    "path": "tests/snapshots/options__opt_nth_2-3.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"> f1,f2,f3,f4                                                                   \"\n\"  1/1                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/options__opt_nth_2-4.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/1                                                                        0/0\"\n\"> 1                                                                             \"\ncursor: (24, 4)\n"
  },
  {
    "path": "tests/snapshots/options__opt_nth_2.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"> f1,f2,f3,f4                                                                   \"\n\"  1/1                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/options__opt_nth_4-2.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"> f1,f2,f3,f4                                                                   \"\n\"  1/1                                                                        0/0\"\n\"> 4                                                                             \"\ncursor: (24, 4)\n"
  },
  {
    "path": "tests/snapshots/options__opt_nth_4-3.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"> f1,f2,f3,f4                                                                   \"\n\"  1/1                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/options__opt_nth_4-4.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/1                                                                        0/0\"\n\"> 1                                                                             \"\ncursor: (24, 4)\n"
  },
  {
    "path": "tests/snapshots/options__opt_nth_4.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"> f1,f2,f3,f4                                                                   \"\n\"  1/1                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/options__opt_nth_neg_1-2.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"> f1,f2,f3,f4                                                                   \"\n\"  1/1                                                                        0/0\"\n\"> 4                                                                             \"\ncursor: (24, 4)\n"
  },
  {
    "path": "tests/snapshots/options__opt_nth_neg_1-3.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"> f1,f2,f3,f4                                                                   \"\n\"  1/1                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/options__opt_nth_neg_1-4.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/1                                                                        0/0\"\n\"> 1                                                                             \"\ncursor: (24, 4)\n"
  },
  {
    "path": "tests/snapshots/options__opt_nth_neg_1.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"> f1,f2,f3,f4                                                                   \"\n\"  1/1                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/options__opt_nth_neg_2-2.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"> f1,f2,f3,f4                                                                   \"\n\"  1/1                                                                        0/0\"\n\"> 3                                                                             \"\ncursor: (24, 4)\n"
  },
  {
    "path": "tests/snapshots/options__opt_nth_neg_2-3.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"> f1,f2,f3,f4                                                                   \"\n\"  1/1                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/options__opt_nth_neg_2-4.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/1                                                                        0/0\"\n\"> 1                                                                             \"\ncursor: (24, 4)\n"
  },
  {
    "path": "tests/snapshots/options__opt_nth_neg_2.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"> f1,f2,f3,f4                                                                   \"\n\"  1/1                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/options__opt_nth_neg_4-2.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"> f1,f2,f3,f4                                                                   \"\n\"  1/1                                                                        0/0\"\n\"> 1                                                                             \"\ncursor: (24, 4)\n"
  },
  {
    "path": "tests/snapshots/options__opt_nth_neg_4-3.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"> f1,f2,f3,f4                                                                   \"\n\"  1/1                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/options__opt_nth_neg_4-4.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/1                                                                        0/0\"\n\"> 2                                                                             \"\ncursor: (24, 4)\n"
  },
  {
    "path": "tests/snapshots/options__opt_nth_neg_4.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"> f1,f2,f3,f4                                                                   \"\n\"  1/1                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/options__opt_nth_neg_oob-2.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/1                                                                        0/0\"\n\"> 1                                                                             \"\ncursor: (24, 4)\n"
  },
  {
    "path": "tests/snapshots/options__opt_nth_neg_oob.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"> f1,f2,f3,f4                                                                   \"\n\"  1/1                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/options__opt_nth_oob-2.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/1                                                                        0/0\"\n\"> 1                                                                             \"\ncursor: (24, 4)\n"
  },
  {
    "path": "tests/snapshots/options__opt_nth_oob.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"> f1,f2,f3,f4                                                                   \"\n\"  1/1                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/options__opt_nth_range_closed-2.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"> f1,f2,f3,f4                                                                   \"\n\"  1/1                                                                        0/0\"\n\"> 2                                                                             \"\ncursor: (24, 4)\n"
  },
  {
    "path": "tests/snapshots/options__opt_nth_range_closed-3.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"> f1,f2,f3,f4                                                                   \"\n\"  1/1                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/options__opt_nth_range_closed-4.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"> f1,f2,f3,f4                                                                   \"\n\"  1/1                                                                        0/0\"\n\"> 3                                                                             \"\ncursor: (24, 4)\n"
  },
  {
    "path": "tests/snapshots/options__opt_nth_range_closed-5.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"> f1,f2,f3,f4                                                                   \"\n\"  1/1                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/options__opt_nth_range_closed-6.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/1                                                                        0/0\"\n\"> 1                                                                             \"\ncursor: (24, 4)\n"
  },
  {
    "path": "tests/snapshots/options__opt_nth_range_closed-7.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"> f1,f2,f3,f4                                                                   \"\n\"  1/1                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/options__opt_nth_range_closed-8.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/1                                                                        0/0\"\n\"> 4                                                                             \"\ncursor: (24, 4)\n"
  },
  {
    "path": "tests/snapshots/options__opt_nth_range_closed.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"> f1,f2,f3,f4                                                                   \"\n\"  1/1                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/options__opt_nth_range_dec-2.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/1                                                                        0/0\"\n\"> 1                                                                             \"\ncursor: (24, 4)\n"
  },
  {
    "path": "tests/snapshots/options__opt_nth_range_dec.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"> f1,f2,f3,f4                                                                   \"\n\"  1/1                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/options__opt_nth_range_from_start-2.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"> f1,f2,f3,f4                                                                   \"\n\"  1/1                                                                        0/0\"\n\"> 1                                                                             \"\ncursor: (24, 4)\n"
  },
  {
    "path": "tests/snapshots/options__opt_nth_range_from_start-3.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"> f1,f2,f3,f4                                                                   \"\n\"  1/1                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/options__opt_nth_range_from_start-4.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/1                                                                        0/0\"\n\"> 4                                                                             \"\ncursor: (24, 4)\n"
  },
  {
    "path": "tests/snapshots/options__opt_nth_range_from_start.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"> f1,f2,f3,f4                                                                   \"\n\"  1/1                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/options__opt_nth_range_to_end-2.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"> f1,f2,f3,f4                                                                   \"\n\"  1/1                                                                        0/0\"\n\"> 3                                                                             \"\ncursor: (24, 4)\n"
  },
  {
    "path": "tests/snapshots/options__opt_nth_range_to_end-3.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"> f1,f2,f3,f4                                                                   \"\n\"  1/1                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/options__opt_nth_range_to_end-4.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/1                                                                        0/0\"\n\"> 1                                                                             \"\ncursor: (24, 4)\n"
  },
  {
    "path": "tests/snapshots/options__opt_nth_range_to_end.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"> f1,f2,f3,f4                                                                   \"\n\"  1/1                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/options__opt_pre_select_items.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  c                                                                             \"\n\"  b                                                                             \"\n\"> a                                                                             \"\n\"  3/3                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/options__opt_pre_select_n.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  c                                                                             \"\n\" >b                                                                             \"\n\">>a                                                                             \"\n\"  3/3 [2]                                                                    0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/options__opt_pre_select_pat.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\" >c                                                                             \"\n\" >b                                                                             \"\n\"> a                                                                             \"\n\"  3/3 [2]                                                                    0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/options__opt_replstr-2.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                        │foo {} a                               \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"> a                                     │                                       \"\n\"  1/3                                0/0│                                       \"\n\"> a                                     │                                       \"\ncursor: (24, 4)\n"
  },
  {
    "path": "tests/snapshots/options__opt_replstr.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                        │foo {} a                               \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"  c                                     │                                       \"\n\"  b                                     │                                       \"\n\"> a                                     │                                       \"\n\"  3/3                                0/0│                                       \"\n\">                                       │                                       \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/options__opt_select_1_enter.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  11                                                                            \"\n\"> 1                                                                             \"\n\"  2/4                                                                        0/0\"\n\"> 1                                                                             \"\ncursor: (24, 4)\n"
  },
  {
    "path": "tests/snapshots/options__opt_selector.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  c                                                                             \"\n\"  b                                                                             \"\n\"$ a                                                                             \"\n\"  3/3                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/options__opt_skip_to_pattern-2.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"> ../c                                                                          \"\n\"  1/1                                                                       0/-1\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/options__opt_skip_to_pattern-3.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"> ..c                                                                           \"\n\"  1/1                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/options__opt_skip_to_pattern.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"> ..c                                                                           \"\n\"  1/1                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/options__opt_tabstop_1.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"> a b                                                                           \"\n\"  1/1                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/options__opt_tabstop_3.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"> aa b                                                                          \"\n\"  1/1                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/options__opt_tabstop_default.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"> a       b                                                                     \"\n\"  1/1                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/options__opt_tac.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  a                                                                             \"\n\"> b                                                                             \"\n\"  2/2                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/options__opt_tac_with_header_lines.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  c                                                                             \"\n\"  d                                                                             \"\n\"> e                                                                             \"\n\"  b                                                                             \"\n\"  a                                                                             \"\n\"  3/3                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/options__opt_with_nth_1.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"> f1,                                                                           \"\n\"  1/1                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/options__opt_with_nth_2.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"> f2,                                                                           \"\n\"  1/1                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/options__opt_with_nth_4.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"> f4                                                                            \"\n\"  1/1                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/options__opt_with_nth_neg_1.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"> f4                                                                            \"\n\"  1/1                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/options__opt_with_nth_neg_2.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"> f3,                                                                           \"\n\"  1/1                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/options__opt_with_nth_neg_4.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"> f1,                                                                           \"\n\"  1/1                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/options__opt_with_nth_oob.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\">                                                                               \"\n\"  1/1                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/options__opt_with_nth_oob_4.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\">                                                                               \"\n\"  1/1                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/options__opt_with_nth_preview.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                        │Xf1Y                                   \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"> f2,f3,f4                              │                                       \"\n\"  1/1                                0/0│                                       \"\n\">                                       │                                       \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/options__opt_with_nth_range_closed.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"> f2,f3,                                                                        \"\n\"  1/1                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/options__opt_with_nth_range_desc.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\">                                                                               \"\n\"  1/1                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/options__opt_with_nth_range_from_start.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"> f1,f2,f3,                                                                     \"\n\"  1/1                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/options__opt_with_nth_range_to_end.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"> f2,f3,f4                                                                      \"\n\"  1/1                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/options__opt_wrap.snap",
    "content": "---\nsource: tests/options.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"> aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"\n\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"\n\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa            \"\n\"  1/1                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/preview__preview_navigation-2.snap",
    "content": "---\nsource: tests/preview.rs\nexpression: buf + & cursor_pos\n---\n\"                                        │b                                      \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"  c                                     │                                       \"\n\"> b                                     │                                       \"\n\"  a                                     │                                       \"\n\"  3/3                                1/0│                                       \"\n\">                                       │                                       \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/preview__preview_navigation.snap",
    "content": "---\nsource: tests/preview.rs\nexpression: buf + & cursor_pos\n---\n\"                                        │a                                      \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"  c                                     │                                       \"\n\"  b                                     │                                       \"\n\"> a                                     │                                       \"\n\"  3/3                                0/0│                                       \"\n\">                                       │                                       \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/preview__preview_no_pty__preview_no_pty_flag.snap",
    "content": "---\nsource: tests/preview.rs\nexpression: buf + & cursor_pos\n---\n\"                                        │NO                                     \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"> x                                     │                                       \"\n\"  1/1                                0/0│                                       \"\n\">                                       │                                       \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/preview__preview_no_pty__preview_no_pty_navigation-2.snap",
    "content": "---\nsource: tests/preview.rs\nexpression: buf + & cursor_pos\n---\n\"                                        │b                                      \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"  c                                     │                                       \"\n\"> b                                     │                                       \"\n\"  a                                     │                                       \"\n\"  3/3                                1/0│                                       \"\n\">                                       │                                       \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/preview__preview_no_pty__preview_no_pty_navigation.snap",
    "content": "---\nsource: tests/preview.rs\nexpression: buf + & cursor_pos\n---\n\"                                        │a                                      \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"  c                                     │                                       \"\n\"  b                                     │                                       \"\n\"> a                                     │                                       \"\n\"  3/3                                0/0│                                       \"\n\">                                       │                                       \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/preview__preview_no_pty__preview_no_pty_nowrap.snap",
    "content": "---\nsource: tests/preview.rs\nexpression: buf + & cursor_pos\n---\n\"a bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"────────────────────────────────────────────────────────────────────────────────\"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"> x                                                                             \"\n\"  1/1                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/preview__preview_no_pty__preview_no_pty_nul_char.snap",
    "content": "---\nsource: tests/preview.rs\nexpression: buf + & cursor_pos\n---\n\"                                        │00000000  27 61 00 62 27               \"\n\"                                        │00000005                               \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"> ab                                    │                                       \"\n\"  1/1                                0/0│                                       \"\n\">                                       │                                       \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/preview__preview_no_pty__preview_no_pty_offset_expr.snap",
    "content": "---\nsource: tests/preview.rs\nexpression: buf + & cursor_pos\n---\n\"                               321/1000│                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │> 123 321                               \"\n\"=                                      │  1/1                                0/0\"\n\"=                                      │>                                       \"\ncursor: (24, 43)\n"
  },
  {
    "path": "tests/snapshots/preview__preview_no_pty__preview_no_pty_offset_fixed.snap",
    "content": "---\nsource: tests/preview.rs\nexpression: buf + & cursor_pos\n---\n\"                               123/1000│                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │  b                                     \"\n\"=                                      │> a                                     \"\n\"=                                      │  2/2                                0/0\"\n\"=                                      │>                                       \"\ncursor: (24, 43)\n"
  },
  {
    "path": "tests/snapshots/preview__preview_no_pty__preview_no_pty_offset_fixed_and_expr.snap",
    "content": "---\nsource: tests/preview.rs\nexpression: buf + & cursor_pos\n---\n\"                               319/1000│                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │> 123 321                               \"\n\"=                                      │  1/1                                0/0\"\n\"=                                      │>                                       \"\ncursor: (24, 43)\n"
  },
  {
    "path": "tests/snapshots/preview__preview_no_pty__preview_no_pty_plus-2.snap",
    "content": "---\nsource: tests/preview.rs\nexpression: buf + & cursor_pos\n---\n\"                                        │b                                      \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"  c                                     │                                       \"\n\"> b                                     │                                       \"\n\"  a                                     │                                       \"\n\"  3/3                                1/0│                                       \"\n\">                                       │                                       \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/preview__preview_no_pty__preview_no_pty_plus-3.snap",
    "content": "---\nsource: tests/preview.rs\nexpression: buf + & cursor_pos\n---\n\"                                        │b                                      \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"> c                                     │                                       \"\n\" >b                                     │                                       \"\n\"  a                                     │                                       \"\n\"  3/3 [1]                            2/0│                                       \"\n\">                                       │                                       \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/preview__preview_no_pty__preview_no_pty_plus-4.snap",
    "content": "---\nsource: tests/preview.rs\nexpression: buf + & cursor_pos\n---\n\"                                        │b c                                    \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\">>c                                     │                                       \"\n\" >b                                     │                                       \"\n\"  a                                     │                                       \"\n\"  3/3 [2]                            2/0│                                       \"\n\">                                       │                                       \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/preview__preview_no_pty__preview_no_pty_plus-5.snap",
    "content": "---\nsource: tests/preview.rs\nexpression: buf + & cursor_pos\n---\n\"                                        │c                                      \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"> c                                     │                                       \"\n\"  b                                     │                                       \"\n\"  a                                     │                                       \"\n\"  3/3                                2/0│                                       \"\n\">                                       │                                       \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/preview__preview_no_pty__preview_no_pty_plus.snap",
    "content": "---\nsource: tests/preview.rs\nexpression: buf + & cursor_pos\n---\n\"                                        │a                                      \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"  c                                     │                                       \"\n\"  b                                     │                                       \"\n\"> a                                     │                                       \"\n\"  3/3                                0/0│                                       \"\n\">                                       │                                       \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/preview__preview_no_pty__preview_no_pty_preserve_quotes.snap",
    "content": "---\nsource: tests/preview.rs\nexpression: buf + & cursor_pos\n---\n\"                                        │X'\"ABC\"'X                              \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"> '\"ABC\"'                               │                                       \"\n\"  1/1                                0/0│                                       \"\n\">                                       │                                       \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/preview__preview_no_pty__preview_no_pty_window_down.snap",
    "content": "---\nsource: tests/preview.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  b                                                                             \"\n\"> a                                                                             \"\n\"  2/2                                                                        0/0\"\n\">                                                                               \"\n\"────────────────────────────────────────────────────────────────────────────────\"\n\"=                                                                               \"\n\"=                                                                               \"\n\"=                                                                               \"\n\"=                                                                               \"\n\"=                                                                               \"\n\"=                                                                               \"\n\"=                                                                               \"\n\"=                                                                               \"\n\"=                                                                               \"\n\"=                                                                               \"\n\"=                                                                               \"\ncursor: (12, 3)\n"
  },
  {
    "path": "tests/snapshots/preview__preview_no_pty__preview_no_pty_window_left.snap",
    "content": "---\nsource: tests/preview.rs\nexpression: buf + & cursor_pos\n---\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │  b                                     \"\n\"=                                      │> a                                     \"\n\"=                                      │  2/2                                0/0\"\n\"=                                      │>                                       \"\ncursor: (24, 43)\n"
  },
  {
    "path": "tests/snapshots/preview__preview_no_pty__preview_no_pty_window_up.snap",
    "content": "---\nsource: tests/preview.rs\nexpression: buf + & cursor_pos\n---\n\"=                                                                               \"\n\"=                                                                               \"\n\"=                                                                               \"\n\"=                                                                               \"\n\"=                                                                               \"\n\"=                                                                               \"\n\"=                                                                               \"\n\"=                                                                               \"\n\"=                                                                               \"\n\"=                                                                               \"\n\"=                                                                               \"\n\"────────────────────────────────────────────────────────────────────────────────\"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  b                                                                             \"\n\"> a                                                                             \"\n\"  2/2                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/preview__preview_no_pty__preview_no_pty_wrap.snap",
    "content": "---\nsource: tests/preview.rs\nexpression: buf + & cursor_pos\n---\n\"a        │                                                                      \"\n\"bbbbbbbb │                                                                      \"\n\"         │                                                                      \"\n\"         │                                                                      \"\n\"         │                                                                      \"\n\"         │                                                                      \"\n\"         │                                                                      \"\n\"         │                                                                      \"\n\"         │                                                                      \"\n\"         │                                                                      \"\n\"         │                                                                      \"\n\"         │                                                                      \"\n\"         │                                                                      \"\n\"         │                                                                      \"\n\"         │                                                                      \"\n\"         │                                                                      \"\n\"         │                                                                      \"\n\"         │                                                                      \"\n\"         │                                                                      \"\n\"         │                                                                      \"\n\"         │                                                                      \"\n\"         │> x                                                                   \"\n\"         │  1/1                                                              0/0\"\n\"         │>                                                                     \"\ncursor: (24, 13)\n"
  },
  {
    "path": "tests/snapshots/preview__preview_no_pty_linux.snap",
    "content": "---\nsource: tests/preview.rs\nexpression: buf + & cursor_pos\n---\n\"                                        │NO                                     \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"> x                                     │                                       \"\n\"  1/1                                0/0│                                       \"\n\">                                       │                                       \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/preview__preview_nowrap.snap",
    "content": "---\nsource: tests/preview.rs\nexpression: buf + & cursor_pos\n---\n\"a bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"────────────────────────────────────────────────────────────────────────────────\"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"> x                                                                             \"\n\"  1/1                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/preview__preview_nul_char.snap",
    "content": "---\nsource: tests/preview.rs\nexpression: buf + & cursor_pos\n---\n\"                                        │00000000  27 61 00 62 27               \"\n\"                                        │00000005                               \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"> ab                                    │                                       \"\n\"  1/1                                0/0│                                       \"\n\">                                       │                                       \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/preview__preview_offset_expr.snap",
    "content": "---\nsource: tests/preview.rs\nexpression: buf + & cursor_pos\n---\n\"                               321/1000│                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │> 123 321                               \"\n\"=                                      │  1/1                                0/0\"\n\"=                                      │>                                       \"\ncursor: (24, 43)\n"
  },
  {
    "path": "tests/snapshots/preview__preview_offset_fixed.snap",
    "content": "---\nsource: tests/preview.rs\nexpression: buf + & cursor_pos\n---\n\"                               123/1000│                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │  b                                     \"\n\"=                                      │> a                                     \"\n\"=                                      │  2/2                                0/0\"\n\"=                                      │>                                       \"\ncursor: (24, 43)\n"
  },
  {
    "path": "tests/snapshots/preview__preview_offset_fixed_and_expr.snap",
    "content": "---\nsource: tests/preview.rs\nexpression: buf + & cursor_pos\n---\n\"                               319/1000│                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │> 123 321                               \"\n\"=                                      │  1/1                                0/0\"\n\"=                                      │>                                       \"\ncursor: (24, 43)\n"
  },
  {
    "path": "tests/snapshots/preview__preview_plus-2.snap",
    "content": "---\nsource: tests/preview.rs\nexpression: buf + & cursor_pos\n---\n\"                                        │b                                      \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"  c                                     │                                       \"\n\"> b                                     │                                       \"\n\"  a                                     │                                       \"\n\"  3/3                                1/0│                                       \"\n\">                                       │                                       \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/preview__preview_plus-3.snap",
    "content": "---\nsource: tests/preview.rs\nexpression: buf + & cursor_pos\n---\n\"                                        │b                                      \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"> c                                     │                                       \"\n\" >b                                     │                                       \"\n\"  a                                     │                                       \"\n\"  3/3 [1]                            2/0│                                       \"\n\">                                       │                                       \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/preview__preview_plus-4.snap",
    "content": "---\nsource: tests/preview.rs\nexpression: buf + & cursor_pos\n---\n\"                                        │b c                                    \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\">>c                                     │                                       \"\n\" >b                                     │                                       \"\n\"  a                                     │                                       \"\n\"  3/3 [2]                            2/0│                                       \"\n\">                                       │                                       \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/preview__preview_plus-5.snap",
    "content": "---\nsource: tests/preview.rs\nexpression: buf + & cursor_pos\n---\n\"                                        │c                                      \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"> c                                     │                                       \"\n\"  b                                     │                                       \"\n\"  a                                     │                                       \"\n\"  3/3                                2/0│                                       \"\n\">                                       │                                       \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/preview__preview_plus.snap",
    "content": "---\nsource: tests/preview.rs\nexpression: buf + & cursor_pos\n---\n\"                                        │a                                      \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"  c                                     │                                       \"\n\"  b                                     │                                       \"\n\"> a                                     │                                       \"\n\"  3/3                                0/0│                                       \"\n\">                                       │                                       \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/preview__preview_preserve_quotes.snap",
    "content": "---\nsource: tests/preview.rs\nexpression: buf + & cursor_pos\n---\n\"                                        │X'\"ABC\"'X                              \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"> '\"ABC\"'                               │                                       \"\n\"  1/1                                0/0│                                       \"\n\">                                       │                                       \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/preview__preview_pty__preview_pty_flag.snap",
    "content": "---\nsource: tests/preview.rs\nexpression: buf + & cursor_pos\n---\n\"                                        │YES                                    \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"> x                                     │                                       \"\n\"  1/1                                0/0│                                       \"\n\">                                       │                                       \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/preview__preview_pty__preview_pty_navigation-2.snap",
    "content": "---\nsource: tests/preview.rs\nexpression: buf + & cursor_pos\n---\n\"                                        │b                                      \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"  c                                     │                                       \"\n\"> b                                     │                                       \"\n\"  a                                     │                                       \"\n\"  3/3                                1/0│                                       \"\n\">                                       │                                       \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/preview__preview_pty__preview_pty_navigation.snap",
    "content": "---\nsource: tests/preview.rs\nexpression: buf + & cursor_pos\n---\n\"                                        │a                                      \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"  c                                     │                                       \"\n\"  b                                     │                                       \"\n\"> a                                     │                                       \"\n\"  3/3                                0/0│                                       \"\n\">                                       │                                       \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/preview__preview_pty__preview_pty_nowrap.snap",
    "content": "---\nsource: tests/preview.rs\nexpression: buf + & cursor_pos\n---\n\"a bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"────────────────────────────────────────────────────────────────────────────────\"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"> x                                                                             \"\n\"  1/1                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/preview__preview_pty__preview_pty_nul_char.snap",
    "content": "---\nsource: tests/preview.rs\nexpression: buf + & cursor_pos\n---\n\"                                        │00000000  27 61 00 62 27               \"\n\"                                        │00000005                               \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"> ab                                    │                                       \"\n\"  1/1                                0/0│                                       \"\n\">                                       │                                       \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/preview__preview_pty__preview_pty_offset_expr.snap",
    "content": "---\nsource: tests/preview.rs\nexpression: buf + & cursor_pos\n---\n\"                               321/1000│                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │> 123 321                               \"\n\"=                                      │  1/1                                0/0\"\n\"=                                      │>                                       \"\ncursor: (24, 43)\n"
  },
  {
    "path": "tests/snapshots/preview__preview_pty__preview_pty_offset_fixed.snap",
    "content": "---\nsource: tests/preview.rs\nexpression: buf + & cursor_pos\n---\n\"                               123/1000│                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │  b                                     \"\n\"=                                      │> a                                     \"\n\"=                                      │  2/2                                0/0\"\n\"=                                      │>                                       \"\ncursor: (24, 43)\n"
  },
  {
    "path": "tests/snapshots/preview__preview_pty__preview_pty_offset_fixed_and_expr.snap",
    "content": "---\nsource: tests/preview.rs\nexpression: buf + & cursor_pos\n---\n\"                               319/1000│                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │> 123 321                               \"\n\"=                                      │  1/1                                0/0\"\n\"=                                      │>                                       \"\ncursor: (24, 43)\n"
  },
  {
    "path": "tests/snapshots/preview__preview_pty__preview_pty_plus-2.snap",
    "content": "---\nsource: tests/preview.rs\nexpression: buf + & cursor_pos\n---\n\"                                        │b                                      \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"  c                                     │                                       \"\n\"> b                                     │                                       \"\n\"  a                                     │                                       \"\n\"  3/3                                1/0│                                       \"\n\">                                       │                                       \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/preview__preview_pty__preview_pty_plus-3.snap",
    "content": "---\nsource: tests/preview.rs\nexpression: buf + & cursor_pos\n---\n\"                                        │b                                      \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"> c                                     │                                       \"\n\" >b                                     │                                       \"\n\"  a                                     │                                       \"\n\"  3/3 [1]                            2/0│                                       \"\n\">                                       │                                       \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/preview__preview_pty__preview_pty_plus-4.snap",
    "content": "---\nsource: tests/preview.rs\nexpression: buf + & cursor_pos\n---\n\"                                        │b c                                    \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\">>c                                     │                                       \"\n\" >b                                     │                                       \"\n\"  a                                     │                                       \"\n\"  3/3 [2]                            2/0│                                       \"\n\">                                       │                                       \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/preview__preview_pty__preview_pty_plus-5.snap",
    "content": "---\nsource: tests/preview.rs\nexpression: buf + & cursor_pos\n---\n\"                                        │c                                      \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"> c                                     │                                       \"\n\"  b                                     │                                       \"\n\"  a                                     │                                       \"\n\"  3/3                                2/0│                                       \"\n\">                                       │                                       \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/preview__preview_pty__preview_pty_plus.snap",
    "content": "---\nsource: tests/preview.rs\nexpression: buf + & cursor_pos\n---\n\"                                        │a                                      \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"  c                                     │                                       \"\n\"  b                                     │                                       \"\n\"> a                                     │                                       \"\n\"  3/3                                0/0│                                       \"\n\">                                       │                                       \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/preview__preview_pty__preview_pty_preserve_quotes.snap",
    "content": "---\nsource: tests/preview.rs\nexpression: buf + & cursor_pos\n---\n\"                                        │X'\"ABC\"'X                              \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"> '\"ABC\"'                               │                                       \"\n\"  1/1                                0/0│                                       \"\n\">                                       │                                       \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/preview__preview_pty__preview_pty_window_down.snap",
    "content": "---\nsource: tests/preview.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  b                                                                             \"\n\"> a                                                                             \"\n\"  2/2                                                                        0/0\"\n\">                                                                               \"\n\"────────────────────────────────────────────────────────────────────────────────\"\n\"=                                                                               \"\n\"=                                                                               \"\n\"=                                                                               \"\n\"=                                                                               \"\n\"=                                                                               \"\n\"=                                                                               \"\n\"=                                                                               \"\n\"=                                                                               \"\n\"=                                                                               \"\n\"=                                                                               \"\n\"=                                                                               \"\ncursor: (12, 3)\n"
  },
  {
    "path": "tests/snapshots/preview__preview_pty__preview_pty_window_left.snap",
    "content": "---\nsource: tests/preview.rs\nexpression: buf + & cursor_pos\n---\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │  b                                     \"\n\"=                                      │> a                                     \"\n\"=                                      │  2/2                                0/0\"\n\"=                                      │>                                       \"\ncursor: (24, 43)\n"
  },
  {
    "path": "tests/snapshots/preview__preview_pty__preview_pty_window_up.snap",
    "content": "---\nsource: tests/preview.rs\nexpression: buf + & cursor_pos\n---\n\"=                                                                               \"\n\"=                                                                               \"\n\"=                                                                               \"\n\"=                                                                               \"\n\"=                                                                               \"\n\"=                                                                               \"\n\"=                                                                               \"\n\"=                                                                               \"\n\"=                                                                               \"\n\"=                                                                               \"\n\"=                                                                               \"\n\"────────────────────────────────────────────────────────────────────────────────\"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  b                                                                             \"\n\"> a                                                                             \"\n\"  2/2                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/preview__preview_pty__preview_pty_wrap.snap",
    "content": "---\nsource: tests/preview.rs\nexpression: buf + & cursor_pos\n---\n\"a bbbbbbb│                                                                      \"\n\"bb       │                                                                      \"\n\"         │                                                                      \"\n\"         │                                                                      \"\n\"         │                                                                      \"\n\"         │                                                                      \"\n\"         │                                                                      \"\n\"         │                                                                      \"\n\"         │                                                                      \"\n\"         │                                                                      \"\n\"         │                                                                      \"\n\"         │                                                                      \"\n\"         │                                                                      \"\n\"         │                                                                      \"\n\"         │                                                                      \"\n\"         │                                                                      \"\n\"         │                                                                      \"\n\"         │                                                                      \"\n\"         │                                                                      \"\n\"         │                                                                      \"\n\"         │                                                                      \"\n\"         │> x                                                                   \"\n\"         │  1/1                                                              0/0\"\n\"         │>                                                                     \"\ncursor: (24, 13)\n"
  },
  {
    "path": "tests/snapshots/preview__preview_pty_linux.snap",
    "content": "---\nsource: tests/preview.rs\nexpression: buf + & cursor_pos\n---\n\"                                        │NO                                     \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"                                        │                                       \"\n\"> x                                     │                                       \"\n\"  1/1                                0/0│                                       \"\n\">                                       │                                       \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/preview__preview_window_down.snap",
    "content": "---\nsource: tests/preview.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  b                                                                             \"\n\"> a                                                                             \"\n\"  2/2                                                                        0/0\"\n\">                                                                               \"\n\"────────────────────────────────────────────────────────────────────────────────\"\n\"=                                                                               \"\n\"=                                                                               \"\n\"=                                                                               \"\n\"=                                                                               \"\n\"=                                                                               \"\n\"=                                                                               \"\n\"=                                                                               \"\n\"=                                                                               \"\n\"=                                                                               \"\n\"=                                                                               \"\n\"=                                                                               \"\ncursor: (12, 3)\n"
  },
  {
    "path": "tests/snapshots/preview__preview_window_left.snap",
    "content": "---\nsource: tests/preview.rs\nexpression: buf + & cursor_pos\n---\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │                                        \"\n\"=                                      │  b                                     \"\n\"=                                      │> a                                     \"\n\"=                                      │  2/2                                0/0\"\n\"=                                      │>                                       \"\ncursor: (24, 43)\n"
  },
  {
    "path": "tests/snapshots/preview__preview_window_up.snap",
    "content": "---\nsource: tests/preview.rs\nexpression: buf + & cursor_pos\n---\n\"=                                                                               \"\n\"=                                                                               \"\n\"=                                                                               \"\n\"=                                                                               \"\n\"=                                                                               \"\n\"=                                                                               \"\n\"=                                                                               \"\n\"=                                                                               \"\n\"=                                                                               \"\n\"=                                                                               \"\n\"=                                                                               \"\n\"────────────────────────────────────────────────────────────────────────────────\"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  b                                                                             \"\n\"> a                                                                             \"\n\"  2/2                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/preview__preview_wrap.snap",
    "content": "---\nsource: tests/preview.rs\nexpression: buf + & cursor_pos\n---\n\"a        │                                                                      \"\n\"bbbbbbbb │                                                                      \"\n\"         │                                                                      \"\n\"         │                                                                      \"\n\"         │                                                                      \"\n\"         │                                                                      \"\n\"         │                                                                      \"\n\"         │                                                                      \"\n\"         │                                                                      \"\n\"         │                                                                      \"\n\"         │                                                                      \"\n\"         │                                                                      \"\n\"         │                                                                      \"\n\"         │                                                                      \"\n\"         │                                                                      \"\n\"         │                                                                      \"\n\"         │                                                                      \"\n\"         │                                                                      \"\n\"         │                                                                      \"\n\"         │                                                                      \"\n\"         │                                                                      \"\n\"         │> x                                                                   \"\n\"         │  1/1                                                              0/0\"\n\"         │>                                                                     \"\ncursor: (24, 13)\n"
  },
  {
    "path": "tests/snapshots/split_match__split_match_both_parts-2.snap",
    "content": "---\nsource: tests/split_match.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"> foo:bar                                                                       \"\n\"  1/3                                                                        0/0\"\n\"> foo:bar                                                                       \"\ncursor: (24, 10)\n"
  },
  {
    "path": "tests/snapshots/split_match__split_match_both_parts.snap",
    "content": "---\nsource: tests/split_match.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  foo:qux                                                                       \"\n\"  baz:qux                                                                       \"\n\"> foo:bar                                                                       \"\n\"  3/3                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/split_match__split_match_custom_delimiter-2.snap",
    "content": "---\nsource: tests/split_match.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"> foo/bar                                                                       \"\n\"  1/2                                                                        0/0\"\n\"> foo/bar                                                                       \"\ncursor: (24, 10)\n"
  },
  {
    "path": "tests/snapshots/split_match__split_match_custom_delimiter.snap",
    "content": "---\nsource: tests/split_match.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  baz/qux                                                                       \"\n\"> foo/bar                                                                       \"\n\"  2/2                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/split_match__split_match_delimiter_in_query_not_item-2.snap",
    "content": "---\nsource: tests/split_match.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  0/2                                                                        0/0\"\n\"> foo:bar                                                                       \"\ncursor: (24, 10)\n"
  },
  {
    "path": "tests/snapshots/split_match__split_match_delimiter_in_query_not_item.snap",
    "content": "---\nsource: tests/split_match.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  bazqux                                                                        \"\n\"> foobar                                                                        \"\n\"  2/2                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/split_match__split_match_empty_after-2.snap",
    "content": "---\nsource: tests/split_match.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  foo:qux                                                                       \"\n\"> foo:bar                                                                       \"\n\"  2/2                                                                        0/0\"\n\"> foo:                                                                          \"\ncursor: (24, 7)\n"
  },
  {
    "path": "tests/snapshots/split_match__split_match_empty_after.snap",
    "content": "---\nsource: tests/split_match.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  foo:qux                                                                       \"\n\"> foo:bar                                                                       \"\n\"  2/2                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/split_match__split_match_empty_before-2.snap",
    "content": "---\nsource: tests/split_match.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  baz:bar                                                                       \"\n\"> foo:bar                                                                       \"\n\"  2/2                                                                        0/0\"\n\"> :bar                                                                          \"\ncursor: (24, 7)\n"
  },
  {
    "path": "tests/snapshots/split_match__split_match_empty_before.snap",
    "content": "---\nsource: tests/split_match.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  baz:bar                                                                       \"\n\"> foo:bar                                                                       \"\n\"  2/2                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/split_match__split_match_multiple_delimiters_in_item-2.snap",
    "content": "---\nsource: tests/split_match.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  a:bc:cd                                                                       \"\n\"> a:b:c                                                                         \"\n\"  2/3                                                                        0/0\"\n\"> a:b:c                                                                         \"\ncursor: (24, 8)\n"
  },
  {
    "path": "tests/snapshots/split_match__split_match_multiple_delimiters_in_item.snap",
    "content": "---\nsource: tests/split_match.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  a:bc:cd                                                                       \"\n\"  x:y:z                                                                         \"\n\"> a:b:c                                                                         \"\n\"  3/3                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/split_match__split_match_no_delimiter_in_item-2.snap",
    "content": "---\nsource: tests/split_match.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"> foobar                                                                        \"\n\"  1/2                                                                        0/0\"\n\"> foo                                                                           \"\ncursor: (24, 6)\n"
  },
  {
    "path": "tests/snapshots/split_match__split_match_no_delimiter_in_item.snap",
    "content": "---\nsource: tests/split_match.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  bazqux                                                                        \"\n\"> foobar                                                                        \"\n\"  2/2                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/split_match__split_match_or-2.snap",
    "content": "---\nsource: tests/split_match.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  x:yz                                                                          \"\n\"> a:bc                                                                          \"\n\"  2/3                                                                        0/0\"\n\"> a:b | x:y                                                                     \"\ncursor: (24, 12)\n"
  },
  {
    "path": "tests/snapshots/split_match__split_match_or.snap",
    "content": "---\nsource: tests/split_match.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  z:ab                                                                          \"\n\"  x:yz                                                                          \"\n\"> a:bc                                                                          \"\n\"  3/3                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/split_match__split_match_query_before_delimiter-2.snap",
    "content": "---\nsource: tests/split_match.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  fbaz:boo                                                                      \"\n\"  baz:foo                                                                       \"\n\"  foo:qux                                                                       \"\n\"> foo:bar                                                                       \"\n\"  4/5                                                                        0/0\"\n\"> foo                                                                           \"\ncursor: (24, 6)\n"
  },
  {
    "path": "tests/snapshots/split_match__split_match_query_before_delimiter.snap",
    "content": "---\nsource: tests/split_match.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  fbaz:boo                                                                      \"\n\"  baz:foo                                                                       \"\n\"  foo:qux                                                                       \"\n\"  baz:qux                                                                       \"\n\"> foo:bar                                                                       \"\n\"  5/5                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/tiebreak__tiebreak_begin-2.snap",
    "content": "---\nsource: tests/tiebreak.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  aaba                                                                          \"\n\"> aba                                                                           \"\n\"  2/5                                                                        0/0\"\n\"> ba                                                                            \"\ncursor: (24, 5)\n"
  },
  {
    "path": "tests/snapshots/tiebreak__tiebreak_begin.snap",
    "content": "---\nsource: tests/tiebreak.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  ac                                                                            \"\n\"  aba                                                                           \"\n\"  c                                                                             \"\n\"  b                                                                             \"\n\"> aaba                                                                          \"\n\"  5/5                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/tiebreak__tiebreak_default-2.snap",
    "content": "---\nsource: tests/tiebreak.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  ab                                                                            \"\n\"> b                                                                             \"\n\"  2/5                                                                        0/0\"\n\"> b                                                                             \"\ncursor: (24, 4)\n"
  },
  {
    "path": "tests/snapshots/tiebreak__tiebreak_default.snap",
    "content": "---\nsource: tests/tiebreak.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  b                                                                             \"\n\"  ac                                                                            \"\n\"  ab                                                                            \"\n\"  c                                                                             \"\n\"> a                                                                             \"\n\"  5/5                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/tiebreak__tiebreak_end-2.snap",
    "content": "---\nsource: tests/tiebreak.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  aaba                                                                          \"\n\"> aba                                                                           \"\n\"  2/5                                                                        0/0\"\n\"> ba                                                                            \"\ncursor: (24, 5)\n"
  },
  {
    "path": "tests/snapshots/tiebreak__tiebreak_end.snap",
    "content": "---\nsource: tests/tiebreak.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  ac                                                                            \"\n\"  aba                                                                           \"\n\"  c                                                                             \"\n\"  b                                                                             \"\n\"> aaba                                                                          \"\n\"  5/5                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/tiebreak__tiebreak_index-2.snap",
    "content": "---\nsource: tests/tiebreak.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  b                                                                             \"\n\"> ab                                                                            \"\n\"  2/5                                                                        0/0\"\n\"> b                                                                             \"\ncursor: (24, 4)\n"
  },
  {
    "path": "tests/snapshots/tiebreak__tiebreak_index.snap",
    "content": "---\nsource: tests/tiebreak.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  b                                                                             \"\n\"  ac                                                                            \"\n\"  ab                                                                            \"\n\"  c                                                                             \"\n\"> a                                                                             \"\n\"  5/5                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/tiebreak__tiebreak_length-2.snap",
    "content": "---\nsource: tests/tiebreak.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  aaba                                                                          \"\n\"> aba                                                                           \"\n\"  2/5                                                                        0/0\"\n\"> ba                                                                            \"\ncursor: (24, 5)\n"
  },
  {
    "path": "tests/snapshots/tiebreak__tiebreak_length.snap",
    "content": "---\nsource: tests/tiebreak.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  aaba                                                                          \"\n\"  aba                                                                           \"\n\"  ac                                                                            \"\n\"  c                                                                             \"\n\"> b                                                                             \"\n\"  5/5                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/tiebreak__tiebreak_neg_begin-2.snap",
    "content": "---\nsource: tests/tiebreak.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  b                                                                             \"\n\"  aba                                                                           \"\n\"> aaba                                                                          \"\n\"  3/5                                                                        0/0\"\n\"> b                                                                             \"\ncursor: (24, 4)\n"
  },
  {
    "path": "tests/snapshots/tiebreak__tiebreak_neg_begin.snap",
    "content": "---\nsource: tests/tiebreak.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  ac                                                                            \"\n\"  aaba                                                                          \"\n\"  c                                                                             \"\n\"  b                                                                             \"\n\"> aba                                                                           \"\n\"  5/5                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/tiebreak__tiebreak_neg_end-2.snap",
    "content": "---\nsource: tests/tiebreak.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  aba                                                                           \"\n\"> aaba                                                                          \"\n\"  2/5                                                                        0/0\"\n\"> ba                                                                            \"\ncursor: (24, 5)\n"
  },
  {
    "path": "tests/snapshots/tiebreak__tiebreak_neg_end.snap",
    "content": "---\nsource: tests/tiebreak.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  ac                                                                            \"\n\"  aaba                                                                          \"\n\"  c                                                                             \"\n\"  b                                                                             \"\n\"> aba                                                                           \"\n\"  5/5                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/tiebreak__tiebreak_neg_index-2.snap",
    "content": "---\nsource: tests/tiebreak.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  b                                                                             \"\n\"> ab                                                                            \"\n\"  2/5                                                                        0/0\"\n\"> b                                                                             \"\ncursor: (24, 4)\n"
  },
  {
    "path": "tests/snapshots/tiebreak__tiebreak_neg_index.snap",
    "content": "---\nsource: tests/tiebreak.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  a                                                                             \"\n\"  b                                                                             \"\n\"  c                                                                             \"\n\"  ab                                                                            \"\n\"> ac                                                                            \"\n\"  5/5                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/tiebreak__tiebreak_neg_length-2.snap",
    "content": "---\nsource: tests/tiebreak.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  c                                                                             \"\n\"> ac                                                                            \"\n\"  2/5                                                                        0/0\"\n\"> c                                                                             \"\ncursor: (24, 4)\n"
  },
  {
    "path": "tests/snapshots/tiebreak__tiebreak_neg_length.snap",
    "content": "---\nsource: tests/tiebreak.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  c                                                                             \"\n\"  b                                                                             \"\n\"  ac                                                                            \"\n\"  aba                                                                           \"\n\"> aaba                                                                          \"\n\"  5/5                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/tiebreak__tiebreak_neg_pathname-2.snap",
    "content": "---\nsource: tests/tiebreak.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  foo                                                                           \"\n\"  baz/foo                                                                       \"\n\"> foo/bar                                                                       \"\n\"  3/3                                                                        0/0\"\n\"> foo                                                                           \"\ncursor: (24, 6)\n"
  },
  {
    "path": "tests/snapshots/tiebreak__tiebreak_neg_pathname.snap",
    "content": "---\nsource: tests/tiebreak.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  foo                                                                           \"\n\"  baz/foo                                                                       \"\n\"> foo/bar                                                                       \"\n\"  3/3                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/tiebreak__tiebreak_neg_score-2.snap",
    "content": "---\nsource: tests/tiebreak.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  b                                                                             \"\n\"> ab                                                                            \"\n\"  2/5                                                                        0/0\"\n\"> b                                                                             \"\ncursor: (24, 4)\n"
  },
  {
    "path": "tests/snapshots/tiebreak__tiebreak_neg_score.snap",
    "content": "---\nsource: tests/tiebreak.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  ac                                                                            \"\n\"  ab                                                                            \"\n\"  c                                                                             \"\n\"  b                                                                             \"\n\"> a                                                                             \"\n\"  5/5                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/snapshots/tiebreak__tiebreak_pathname-2.snap",
    "content": "---\nsource: tests/tiebreak.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  foo/bar                                                                       \"\n\"  foo                                                                           \"\n\"> baz/foo                                                                       \"\n\"  3/3                                                                        0/0\"\n\"> foo                                                                           \"\ncursor: (24, 6)\n"
  },
  {
    "path": "tests/snapshots/tiebreak__tiebreak_pathname.snap",
    "content": "---\nsource: tests/tiebreak.rs\nexpression: buf + & cursor_pos\n---\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"                                                                                \"\n\"  baz/foo                                                                       \"\n\"  foo/bar                                                                       \"\n\"> foo                                                                           \"\n\"  3/3                                                                        0/0\"\n\">                                                                               \"\ncursor: (24, 3)\n"
  },
  {
    "path": "tests/split_match.rs",
    "content": "#[allow(dead_code)]\n#[macro_use]\nmod common;\n\n// Test 1: Basic split match - query without delimiter matches before delimiter in item\ninsta_test!(split_match_query_before_delimiter, [\"foo:bar\", \"baz:qux\", \"foo:qux\", \"baz:foo\", \"fbaz:boo\"], &[\"--split-match\", \":\"], {\n    @snap;\n    @type \"foo\";\n    @snap;\n});\n\n// Test 2: Query with delimiter matches both parts\ninsta_test!(split_match_both_parts, [\"foo:bar\", \"baz:qux\", \"foo:qux\"], &[\"--split-match\", \":\"], {\n    @snap;\n    @type \"foo:bar\";\n    @snap;\n});\n\n// Test 3: Query with delimiter - empty before, match after\ninsta_test!(split_match_empty_before, [\"foo:bar\", \"baz:bar\"], &[\"--split-match\", \":\"], {\n    @snap;\n    @type \":bar\";\n    @snap;\n});\n\n// Test 4: Query with delimiter - match before, empty after\ninsta_test!(split_match_empty_after, [\"foo:bar\", \"foo:qux\"], &[\"--split-match\", \":\"], {\n    @snap;\n    @type \"foo:\";\n    @snap;\n});\n\n// Test 5: Item without delimiter - query without delimiter matches whole item\ninsta_test!(split_match_no_delimiter_in_item, [\"foobar\", \"bazqux\"], &[\"--split-match\", \":\"], {\n    @snap;\n    @type \"foo\";\n    @snap;\n});\n\n// Test 6: Item without delimiter - query with delimiter doesn't match\ninsta_test!(split_match_delimiter_in_query_not_item, [\"foobar\", \"bazqux\"], &[\"--split-match\", \":\"], {\n    @snap;\n    @type \"foo:bar\";\n    @snap;\n});\n\n// Test 7: Multiple delimiters in item - only first one is used for splitting\ninsta_test!(split_match_multiple_delimiters_in_item, [\"a:b:c\", \"x:y:z\", \"a:bc:cd\"], &[\"--split-match\", \":\"], {\n    @snap;\n    @type \"a:b:c\";\n    @snap;\n});\n\n// Test 8: Custom delimiter (/)\ninsta_test!(split_match_custom_delimiter, [\"foo/bar\", \"baz/qux\"], &[\"--split-match\", \"/\"], {\n    @snap;\n    @type \"foo/bar\";\n    @snap;\n});\n\ninsta_test!(split_match_or, [\"a:bc\", \"x:yz\", \"z:ab\"], &[\"--split-match\", \":\"], {\n    @snap;\n    @type \"a:b | x:y\";\n    @snap;\n});\n"
  },
  {
    "path": "tests/tiebreak.rs",
    "content": "#[allow(dead_code)]\n#[macro_use]\nmod common;\n\n// Default tiebreak: score,begin,end\n// With items \"a\", \"c\", \"ab\", \"ac\", \"b\", typing \"b\" should select \"b\" (exact match has best score)\ninsta_test!(tiebreak_default, [\"a\", \"c\", \"ab\", \"ac\", \"b\"], &[\"--tiebreak=score,begin,end\"], {\n    @snap;\n    @char 'b';\n    @snap;\n});\n\n// Negative score tiebreak: prefer lower scores\n// With items \"a\", \"b\", \"c\", \"ab\", \"ac\", typing \"b\" should select \"ab\" (prefers longer match)\ninsta_test!(tiebreak_neg_score, [\"a\", \"b\", \"c\", \"ab\", \"ac\"], &[\"--tiebreak=-score\"], {\n    @snap;\n    @char 'b';\n    @snap;\n});\n\n// Index tiebreak: prefer earlier items\n// With items \"a\", \"c\", \"ab\", \"ac\", \"b\", typing \"b\" should select \"ab\" (earlier index among matches)\ninsta_test!(tiebreak_index, [\"a\", \"c\", \"ab\", \"ac\", \"b\"], &[\"--tiebreak=index,score\"], {\n    @snap;\n    @char 'b';\n    @snap;\n});\n\n// Negative index tiebreak: prefer later items\n// With items \"a\", \"b\", \"c\", \"ab\", \"ac\", typing \"b\" should select \"ab\" (later index)\ninsta_test!(tiebreak_neg_index, [\"a\", \"b\", \"c\", \"ab\", \"ac\"], &[\"--tiebreak=-index,score\"], {\n    @snap;\n    @char 'b';\n    @snap;\n});\n\n// Begin tiebreak: prefer matches that begin earlier\n// With items \"aaba\", \"b\", \"c\", \"aba\", \"ac\", typing \"ba\" should select \"aba\" (match begins earlier)\ninsta_test!(tiebreak_begin, [\"aaba\", \"b\", \"c\", \"aba\", \"ac\"], &[\"--tiebreak=begin,score\"], {\n    @snap;\n    @type \"ba\";\n    @snap;\n});\n\n// Negative begin tiebreak: prefer matches that begin later\n// With items \"aba\", \"b\", \"c\", \"aaba\", \"ac\", typing \"b\" should select \"aaba\" (match begins later)\ninsta_test!(tiebreak_neg_begin, [\"aba\", \"b\", \"c\", \"aaba\", \"ac\"], &[\"--tiebreak=-begin,score\"], {\n    @snap;\n    @char 'b';\n    @snap;\n});\n\n// End tiebreak: prefer matches that end earlier\n// With items \"aaba\", \"b\", \"c\", \"aba\", \"ac\", typing \"ba\" should select \"aba\" (match ends earlier)\ninsta_test!(tiebreak_end, [\"aaba\", \"b\", \"c\", \"aba\", \"ac\"], &[\"--tiebreak=end,score\"], {\n    @snap;\n    @type \"ba\";\n    @snap;\n});\n\n// Negative end tiebreak: prefer matches that end later\n// With items \"aba\", \"b\", \"c\", \"aaba\", \"ac\", typing \"ba\" should select \"aaba\" (match ends later)\ninsta_test!(tiebreak_neg_end, [\"aba\", \"b\", \"c\", \"aaba\", \"ac\"], &[\"--tiebreak=-end,score\"], {\n    @snap;\n    @type \"ba\";\n    @snap;\n});\n\n// Length tiebreak: prefer shorter items\n// With items \"aaba\", \"b\", \"c\", \"aba\", \"ac\", typing \"ba\" should select \"aba\" (shorter)\ninsta_test!(tiebreak_length, [\"aaba\", \"b\", \"c\", \"aba\", \"ac\"], &[\"--tiebreak=length,score\"], {\n    @snap;\n    @type \"ba\";\n    @snap;\n});\n\n// Negative length tiebreak: prefer longer items\n// With items \"aaba\", \"b\", \"c\", \"aba\", \"ac\", typing \"c\" should select \"ac\" (longest match with 'c')\ninsta_test!(tiebreak_neg_length, [\"aaba\", \"b\", \"c\", \"aba\", \"ac\"], &[\"--tiebreak=-length,score\"], {\n    @snap;\n    @char 'c';\n    @snap;\n});\n\n// PathName tiebreak: prefer matches that fall within the filename portion (after the last `/` or `\\`)\n// With items \"foo/bar\", \"baz/foo\", \"foo\", typing \"foo\":\n//   \"foo\"     → match at begin=0, path_name_offset=0  → score = 0-0 = 0  (best)\n//   \"baz/foo\" → match at begin=4, path_name_offset=4  → score = 4-4 = 0  (best, tied)\n//   \"foo/bar\" → match at begin=0, path_name_offset=4  → score = 4-0 = 4  (penalised, match is in dir part)\n// \"foo\" wins (selected), \"baz/foo\" second, \"foo/bar\" last.\ninsta_test!(tiebreak_pathname, [\"foo/bar\", \"baz/foo\", \"foo\"], &[\"--tiebreak=pathname,score\"], {\n    @snap;\n    @type \"foo\";\n    @snap;\n});\n\n// Negative PathName tiebreak: prefer matches in directory components over the filename\n// Typing \"foo\" should prefer \"foo/bar\" (match starts in a dir component) when -pathname is used.\ninsta_test!(tiebreak_neg_pathname, [\"foo/bar\", \"baz/foo\", \"foo\"], &[\"--tiebreak=-pathname,score\"], {\n    @snap;\n    @type \"foo\";\n    @snap;\n});\n"
  },
  {
    "path": "tests/tmux.rs",
    "content": "#[allow(dead_code)]\nmod common;\n\nuse common::tmux::Keys::*;\nuse common::tmux::TmuxController;\nuse std::fs::File;\nuse std::fs::Permissions;\nuse std::io::Read;\nuse std::io::Result;\nuse std::io::Write;\nuse std::os::unix::fs::PermissionsExt;\nuse std::path::Path;\n\nfn setup_tmux_mock(tmux: &TmuxController) -> Result<String> {\n    let dir = &tmux.tempdir;\n    let path = dir.path().join(\"tmux\");\n    let mock_bin = Path::new(&path);\n    let mut writer = File::create(mock_bin)?;\n    let outfile = dir.path().join(\"tmux-mock-cmd\");\n    writer.write_fmt(format_args!(\n        \"#!/bin/sh\n\necho \\\"$@\\\" > {}\n\",\n        outfile.to_str().unwrap()\n    ))?;\n    std::fs::set_permissions(mock_bin, Permissions::from_mode(0o777))?;\n    tmux.send_keys(&[\n        Str(&format!(\"export PATH={}:$PATH\", dir.path().to_str().unwrap())),\n        Enter,\n    ])?;\n\n    tmux.until(|_| Path::new(&tmux.tempdir.path().join(\"tmux\")).exists())?;\n\n    Ok(outfile.to_str().unwrap().to_string())\n}\n\nfn get_tmux_cmd(outfile: &str) -> Result<String> {\n    let mut cmd = String::new();\n    File::open(outfile)?.read_to_string(&mut cmd)?;\n    Ok(cmd)\n}\n\n#[test]\nfn tmux_vanilla() -> Result<()> {\n    let mut tmux = TmuxController::new()?;\n    let outfile = setup_tmux_mock(&tmux)?;\n    tmux.start_sk(None, &[\"--tmux\"])?;\n    tmux.until(|_| Path::new(&outfile).exists())?;\n    let cmd = get_tmux_cmd(&outfile)?;\n    assert!(cmd.starts_with(\"display-popup\"));\n    assert!(cmd.contains(\"-E\"));\n    assert!(cmd.contains(\"--print-query\"));\n    assert!(cmd.contains(\"--print-cmd\"));\n    assert!(cmd.contains(\"--print-header\"));\n    assert!(cmd.contains(\"--print-current\"));\n    assert!(cmd.contains(\"--print-score\"));\n    assert!(!cmd.contains(\"<\"));\n\n    Ok(())\n}\n\n#[test]\nfn tmux_output_format() -> Result<()> {\n    let mut tmux = TmuxController::new()?;\n    let outfile = setup_tmux_mock(&tmux)?;\n    tmux.start_sk(\n        None,\n        &[\n            \"--tmux\",\n            \"--output-format\",\n            \"output-format\",\n            \"--output-format=output-format\",\n        ],\n    )?;\n    tmux.until(|_| Path::new(&outfile).exists())?;\n    let cmd = get_tmux_cmd(&outfile)?;\n    assert!(cmd.starts_with(\"display-popup\"));\n    assert!(cmd.contains(\"-E\"));\n    assert!(cmd.contains(\"--print-query\"));\n    assert!(cmd.contains(\"--print-cmd\"));\n    assert!(cmd.contains(\"--print-header\"));\n    assert!(cmd.contains(\"--print-current\"));\n    assert!(cmd.contains(\"--print-score\"));\n    assert!(cmd.contains(\"--print-score\"));\n    assert!(!cmd.contains(\"output-format\"));\n    assert!(!cmd.contains(\"<\"));\n\n    Ok(())\n}\n\n#[test]\nfn tmux_stdin() -> Result<()> {\n    let mut tmux = TmuxController::new()?;\n    let outfile = setup_tmux_mock(&tmux)?;\n    tmux.start_sk(Some(\"ls\"), &[\"--tmux\"])?;\n    tmux.until(|_| Path::new(&outfile).exists())?;\n    let cmd = get_tmux_cmd(&outfile)?;\n    println!(\"{}\", cmd);\n    assert!(cmd.contains(\"<\"));\n\n    Ok(())\n}\n\n#[test]\nfn tmux_quote_bash() -> Result<()> {\n    let mut tmux = TmuxController::new()?;\n    let outfile = setup_tmux_mock(&tmux)?;\n    tmux.send_keys(&[Str(\"export SHELL=/bin/bash\"), Enter])?;\n    tmux.send_keys(&[Str(\"export SKIM_ESCAPED_VAR=';;'\"), Enter])?;\n    tmux.start_sk(None, &[\"--tmux\", \"--bind 'ctrl-a:reload(ls /foo*)'\"])?;\n    tmux.until(|_| Path::new(&outfile).exists())?;\n    let cmd = get_tmux_cmd(&outfile)?;\n    assert!(cmd.starts_with(\"display-popup\"));\n    assert!(cmd.contains(\"-E\"));\n    assert!(cmd.contains(\"--bind $'ctrl-a:reload(ls /foo*)'\"));\n    assert!(cmd.contains(\"SKIM_ESCAPED_VAR=;\\\\;\"));\n\n    Ok(())\n}\n#[test]\nfn tmux_quote_zsh() -> Result<()> {\n    let mut tmux = TmuxController::new()?;\n    let outfile = setup_tmux_mock(&tmux)?;\n    tmux.send_keys(&[Str(\"export SHELL=/bin/zsh\"), Enter])?;\n    tmux.send_keys(&[Str(\"export SKIM_ESCAPED_VAR=';;'\"), Enter])?;\n    tmux.start_sk(None, &[\"--tmux\", \"--bind 'ctrl-a:reload(ls /foo*)'\"])?;\n    tmux.until(|_| Path::new(&outfile).exists())?;\n    let cmd = get_tmux_cmd(&outfile)?;\n    println!(\"{cmd}\");\n    assert!(cmd.starts_with(\"display-popup\"));\n    assert!(cmd.contains(\"-E\"));\n    assert!(cmd.contains(\n        \"sk --bind $'ctrl-a:reload(ls /foo*)' --print-query --print-cmd --print-header --print-current --print-score >\"\n    ));\n    assert!(cmd.contains(\"SKIM_ESCAPED_VAR=;\\\\;\"));\n\n    Ok(())\n}\n#[test]\nfn tmux_quote_sh() -> Result<()> {\n    let mut tmux = TmuxController::new()?;\n    let outfile = setup_tmux_mock(&tmux)?;\n    tmux.send_keys(&[Str(\"export SHELL=/bin/sh\"), Enter])?;\n    tmux.send_keys(&[Str(\"export SKIM_ESCAPED_VAR=';;'\"), Enter])?;\n    tmux.start_sk(None, &[\"--tmux\", \"--bind 'ctrl-a:reload(ls /foo*)'\"])?;\n    tmux.until(|_| Path::new(&outfile).exists())?;\n    let cmd = get_tmux_cmd(&outfile)?;\n    assert!(cmd.starts_with(\"display-popup\"));\n    assert!(cmd.contains(\"-E\"));\n    assert!(cmd.contains(\"--bind ctrl-a':reload(ls /foo*)'\"));\n    assert!(cmd.contains(\"SKIM_ESCAPED_VAR=;\\\\;\"));\n\n    Ok(())\n}\n#[test]\nfn tmux_quote_fish() -> Result<()> {\n    let mut tmux = TmuxController::new()?;\n    let outfile = setup_tmux_mock(&tmux)?;\n    tmux.send_keys(&[Str(\"export SHELL=/bin/fish\"), Enter])?;\n    tmux.send_keys(&[Str(\"export SKIM_ESCAPED_VAR=';;'\"), Enter])?;\n    tmux.start_sk(None, &[\"--tmux\", \"--bind 'ctrl-a:reload(ls /foo*)'\"])?;\n    tmux.until(|_| Path::new(&outfile).exists())?;\n    let cmd = get_tmux_cmd(&outfile)?;\n    assert!(cmd.starts_with(\"display-popup\"));\n    assert!(cmd.contains(\"-E\"));\n    assert!(cmd.contains(\"--bind ctrl-a':reload(ls /foo*)'\"));\n    assert!(cmd.contains(\"SKIM_ESCAPED_VAR=;\\\\;\"));\n\n    Ok(())\n}\n"
  },
  {
    "path": "tests/unix.rs",
    "content": "#![cfg(unix)]\n#[allow(dead_code)]\n#[macro_use]\nmod common;\nuse common::tmux::{Keys, TmuxController, sk};\nuse std::io::Result;\n\nsk_test!(vanilla_basic_tmux, \"1\\n2\\n3\", &[], {\n  @capture[0] eq(\">\");\n  @capture[1] trim().starts_with(\"3/3\");\n  @capture[1] ends_with(\"0/0\");\n  @capture[2] eq(\"> 1\");\n  @capture[3] eq(\"  2\");\n});\n\n#[test]\nfn default_command() -> Result<()> {\n    let tmux = TmuxController::new()?;\n\n    let outfile = tmux.tempfile()?;\n    let sk_cmd = sk(&outfile, &[]).replace(\"SKIM_DEFAULT_COMMAND=\", \"SKIM_DEFAULT_COMMAND='echo hello'\");\n    tmux.send_keys(&[Keys::Str(&sk_cmd), Keys::Enter])?;\n    tmux.until(|l| l[0].starts_with(\">\"))?;\n    tmux.until(|l| l.len() > 1 && l[1].starts_with(\"  1/1\"))?;\n    tmux.until(|l| l.len() > 2 && l[2] == \"> hello\")?;\n\n    tmux.send_keys(&[Keys::Enter])?;\n    tmux.until(|l| !l[0].starts_with(\">\"))?;\n\n    let output = tmux.output_from(&outfile)?;\n\n    assert_eq!(output[0], \"hello\");\n\n    Ok(())\n}\n\n#[test]\nfn default_options() -> Result<()> {\n    let tmux = TmuxController::new()?;\n\n    let outfile = tmux.tempfile()?;\n    let sk_cmd = sk(&outfile, &[]).replace(\"SKIM_DEFAULT_OPTIONS=\", \"SKIM_DEFAULT_OPTIONS='--prompt \\\"XXX \\\"'\");\n    tmux.send_keys(&[Keys::Str(&sk_cmd), Keys::Enter])?;\n    tmux.until(|l| l[0].starts_with(\"XXX\"))?;\n\n    Ok(())\n}\n\n#[test]\nfn options_file() -> Result<()> {\n    let tmux = TmuxController::new()?;\n\n    // Create options file with content from manpage example\n    let mut options_file = NamedTempFile::new()?;\n    options_file.write_all(\n        b\"# Preview\\n\\\n--preview 'echo {}'\\n\\\n--preview-window 'left:30%' # Preview window\\n\\\n--prompt '## '\\n\",\n    )?;\n\n    let outfile = tmux.tempfile()?;\n    let sk_cmd = sk(&outfile, &[]).replace(\n        \"SKIM_OPTIONS_FILE=\",\n        &format!(\"SKIM_OPTIONS_FILE='{}'\", options_file.path().to_str().unwrap()),\n    );\n    tmux.send_keys(&[Keys::Str(&format!(\"echo a | {sk_cmd}\")), Keys::Enter])?;\n    tmux.until(|l| l.len() > 2)?;\n    tmux.until(|l| l[0].ends_with(\"│#\"))?;\n    tmux.until(|l| l[2].trim().ends_with(\"│> a\"))?;\n    tmux.until(|l| l[l.len() - 1].starts_with('a'))?;\n\n    tmux.send_keys(&[Keys::Enter])?;\n\n    let output = tmux.output_from(&outfile)?;\n\n    assert_eq!(output[0], \"a\");\n\n    Ok(())\n}\n\nsk_test!(tmux_version_long, \"\", &[\"--version\"], {\n  @output[0] starts_with(\"sk \");\n});\nsk_test!(tmux_version_short, \"\", &[\"-V\"], {\n  @output[0] starts_with(\"sk \");\n});\n\nsk_test!(opt_read0, \"a\\\\0b\\\\0c\", &[\"--read0\"], {\n  @capture[1] starts_with(\"  3/3\");\n  @capture[2] starts_with(\"> a\");\n  @capture[3] ends_with(\"b\");\n  @capture[4] ends_with(\"c\");\n});\n\nsk_test!(opt_print0, \"a\\\\nb\\\\nc\", &[\"-m\", \"--print0\"], {\n  @lines |l| (l.len() > 4);\n  @keys BTab, BTab, Enter;\n  @lines |l| (!l.is_empty() && !l[0].starts_with(\">\"));\n  @output[0] trim().eq(\"a\\0b\\0\");\n});\n\nsk_test!(opt_print_query, \"10\\\\n20\\\\n30\", &[\"-q\", \"2\", \"--print-query\"], {\n  @capture[2] trim().eq(\"> 20\");\n  @keys Enter;\n  @capture[0] ne(\"> 2\");\n\n  @dbg;\n  @output[0] trim().eq(\"2\");\n  @output[1] trim().eq(\"20\");\n});\n\nsk_test!(opt_print_cmd, \"1\\\\n2\\\\n3\", &[\"--cmd-query\", \"cmd\", \"--print-cmd\"], {\n  @lines |l| (l.len() > 4);\n  @capture[0] starts_with(\">\");\n  @capture[2] trim().eq(\"> 1\");\n  @keys Enter;\n  @output[0] trim().eq(\"cmd\");\n  @output[1] trim().eq(\"1\");\n});\n\nsk_test!(opt_print_cmd_and_query, \"10\\\\n20\\\\n30\", &[\"--cmd-query\", \"cmd\", \"--print-cmd\", \"-q\", \"2\", \"--print-query\"], {\n  @capture[0] starts_with(\"> 2\");\n  @capture[2] trim().eq(\"> 20\");\n  @keys Enter;\n  @output[0] trim().eq(\"2\");\n  @output[1] trim().eq(\"cmd\");\n  @output[2] trim().eq(\"20\");\n});\n\nsk_test!(opt_print_header, \"x\", &[\"--header\", \"foo\", \"--print-header\"], {\n    @capture[0] starts_with(\">\");\n    @capture[2] starts_with(\"  foo\");\n    @capture[3] starts_with(\"> x\");\n    @keys Enter;\n    @output[0] trim().eq(\"foo\");\n    @output[1] trim().eq(\"x\");\n});\n\nsk_test!(opt_print_score, \"x\\\\nyx\\\\nyz\", &[\"--print-score\"], {\n    @capture[0] starts_with(\">\");\n    @keys Key('x');\n    @capture[0] starts_with(\"> x\");\n    @capture[1] trim().starts_with(\"2/3\");\n    @keys Enter;\n    @output[0] trim().eq(\"x\");\n    @output[1] trim().eq(\"50\");\n});\n\nsk_test!(opt_print_score_multi, \"x\\\\nyx\\\\nyz\", &[\"--print-score\", \"-m\"], {\n    @capture[0] starts_with(\">\");\n    @keys Key('x');\n    @capture[0] starts_with(\"> x\");\n    @capture[1] trim().starts_with(\"2/3\");\n    @keys BTab;\n    @capture[2] trim().eq(\">x\");\n    @capture[3] trim().eq(\"> yx\");\n    @keys BTab;\n    @capture[3] trim().eq(\">>yx\");\n    @keys Enter;\n    @output[0] trim().eq(\"x\");\n    @output[1] trim().eq(\"50\");\n    @output[2] trim().eq(\"yx\");\n    @output[3] trim().eq(\"18\");\n});\n\nsk_test!(opt_ansi_null, \"a\\\\0b\", &[\"--ansi\"], {\n  @capture[1] trim().starts_with(\"1/1\");\n  @keys Enter;\n  @output[0] contains(\"\\0\");\n});\n\nuse common::tmux::Keys::*;\n\nsk_test!(opt_reserved_options, \"a\\\\nb\", &[], tmux => {\n  let reserved_options = [\n      \"--extended\",\n      \"--literal\",\n      \"--no-mouse\",\n      \"--cycle\",\n      \"--hscroll-off=10\",\n      \"--filepath-word\",\n      \"--jump-labels=CHARS\",\n      \"--inline-info\",\n      \"--header=STR\",\n      \"--header-lines=1\",\n      \"--no-bold\",\n      \"--history-size=10\",\n      \"--sync\",\n      \"--no-sort\",\n      \"--select-1\",\n      \"-1\",\n      \"--exit-0\",\n      \"-0\",\n  ];\n\n  for option in reserved_options {\n      println!(\"Starting sk with opt {}\", option);\n      let mut tmux = TmuxController::new()?;\n      tmux.start_sk(Some(\"echo -n -e 'a\\\\nb'\"), &[option])?;\n      tmux.until(|l| !l.is_empty() && l[0].starts_with(\">\"))?;\n  }\n});\n\nsk_test!(opt_multiple_flags_basic, \"a\\\\nb\", &[], tmux => {\n  let basic_flags = [\n      \"--bind=ctrl-a:cancel --bind ctrl-b:cancel\",\n      \"--tiebreak=begin --tiebreak=score\",\n      \"--cmd asdf --cmd find\",\n      \"--query asdf -q xyz\",\n      \"--delimiter , --delimiter . -d ,\",\n      \"--nth 1,2 --nth=1,3 -n 1,3\",\n      \"--with-nth 1,2 --with-nth=1,3\",\n      \"-I {} -I XX\",\n      \"--color base --color light\",\n      \"--margin 30% --margin 0\",\n      \"--min-height 30% --min-height 10\",\n      \"--preview 'ls {}' --preview 'cat {}'\",\n      \"--preview-window up --preview-window down\",\n      \"--multi -m\",\n      \"--no-multi --no-multi\",\n      \"--tac --tac\",\n      \"--ansi --ansi\",\n      \"--exact -e\",\n      \"--regex --regex\",\n      \"--literal --literal\",\n      \"--no-mouse --no-mouse\",\n      \"--cycle --cycle\",\n      \"--no-hscroll --no-hscroll\",\n      \"--filepath-word --filepath-word\",\n      \"--inline-info --inline-info\",\n      \"--no-bold --no-bold\",\n      \"--print-query --print-query\",\n      \"--print-cmd --print-cmd\",\n      \"--print0 --print0\",\n      \"--sync --sync\",\n      \"--extended --extended\",\n      \"--no-sort --no-sort\",\n      \"--select-1 --select-1\",\n      \"--exit-0 --exit-0\",\n  ];\n\n  for cmd_flags in basic_flags {\n      let mut tmux = TmuxController::new()?;\n      tmux.start_sk(Some(\"echo -n -e 'a\\\\nb'\"), &[cmd_flags])?;\n      tmux.until(|l| !l.is_empty() && l[0].starts_with(\">\"))?;\n  }\n});\n\nuse std::io::Write;\nuse tempfile::NamedTempFile;\n\nsk_test!(opt_pre_select_file, \"a\\\\nb\\\\nc\", &[], tmux => {\n  let mut pre_select_file = NamedTempFile::new()?;\n  pre_select_file.write_all(b\"b\\nc\")?;\n  let mut tmux = TmuxController::new()?;\n  tmux.start_sk(\n      Some(\"echo -n -e 'a\\\\nb\\\\nc'\"),\n      &[\"-m\", \"--pre-select-file\", pre_select_file.path().to_str().unwrap()],\n  )?;\n  tmux.until(|l| l.len() > 4 && l[2] == \"> a\" && l[3].trim() == \">b\" && l[4].trim() == \">c\")?;\n});\n\nsk_test!(opt_accept_arg, \"a\\\\nb\", &[\"--bind\", \"ctrl-a:accept:hello\"], {\n  @capture[1] trim().starts_with(\"2/2\");\n  @keys Ctrl(&Key('a'));\n  @output[0] trim().eq(\"hello\");\n  @output[1] trim().eq(\"a\");\n});\n\n// Bind tests that require output capture\n\nsk_test!(bind_execute_0_results, \"\", &[\"--bind\", \"'ctrl-f:execute(echo foo{})'\"], {\n  @capture[0] eq(\">\");\n  @keys Ctrl(&Key('f')), Enter;\n  @capture[0] ne(\">\");\n\n  @output[0] eq(\"foo\");\n});\n\nsk_test!(bind_execute_0_results_noref, \"\", &[\"--bind\", \"'ctrl-f:execute(echo foo)'\"], {\n  @capture[0] eq(\">\");\n  @keys Ctrl(&Key('f')), Enter;\n  @capture[0] ne(\">\");\n\n  @output[0] eq(\"foo\");\n});\n\n#[test]\nfn bind_reload_no_arg() -> Result<()> {\n    let tmux = TmuxController::new()?;\n\n    let outfile = tmux.tempfile()?;\n    let sk_cmd = sk(&outfile, &[\"--bind\", \"'ctrl-a:reload'\"])\n        .replace(\"SKIM_DEFAULT_COMMAND=\", \"SKIM_DEFAULT_COMMAND='echo hello'\");\n    tmux.send_keys(&[Keys::Str(&sk_cmd), Keys::Enter])?;\n    tmux.until(|l| l[0].starts_with(\">\"))?;\n\n    tmux.send_keys(&[Keys::Ctrl(&Keys::Key('a'))])?;\n    tmux.until(|l| l.len() > 2 && l[2] == \"> hello\")?;\n\n    Ok(())\n}\n\nsk_test!(bind_reload_cmd, \"a\\\\n\\\\nb\\\\nc\", &[\"--bind\", \"'ctrl-a:reload(echo hello)'\"], {\n  @capture[2] eq(\"> a\");\n  @keys Ctrl(&Key('a'));\n  @capture[2] eq(\"> hello\");\n});\n\nsk_test!(inline_clear_on_exit, @cmd \"seq 1 10\", &[\"--height=50%\"], {\n    @capture[0] starts_with(\">\");\n    @keys Escape;\n    @lines |l| (!l.iter().any(|line| line.starts_with(\">\")));\n});\n\nsk_test!(issue_xxx_null_delimiter_with_nth, \"a\\\\0b\\\\0c\", &[\"--delimiter\", \"'\\\\x00'\", \"--with-nth\", \"2\"], {\n  @capture[0] starts_with(\">\");\n  @capture[2] starts_with(\"> b\");\n});\n\nsk_test!(issue_xxx_null_delimiter_nth, \"a\\\\0b\\\\0c\", &[\"--delimiter\", \"'\\\\x00'\", \"--nth\", \"2\"], {\n  @capture[0] starts_with(\">\");\n  @keys Key('c');\n  @capture[0] starts_with(\"> c\");\n  @capture[1] contains(\"0/1\");\n  @keys BSpace, Key('b');\n  @capture[0] starts_with(\"> b\");\n  @capture[2] starts_with(\"> abc\");\n});\n\nsk_test!(issue_1120_height_mode_clears_on_exit, @cmd \"seq 1 10\", &[\"--height=50%\"], {\n    @capture[0] starts_with(\">\");\n    @keys Key('\\x1b');\n    @lines |l| (!l.iter().any(|line| line.starts_with(\">\")));\n});\n"
  }
]