├── .gitattributes ├── .github ├── dependabot.yml └── workflows │ └── CICD.yml ├── .gitignore ├── .pre-commit-config.yaml ├── .rustfmt.toml ├── Cargo.lock ├── Cargo.toml ├── FUNDING.yml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── build.rs ├── completions ├── bash │ └── code-minimap.bash ├── elvish │ └── code-minimap.elv ├── fish │ └── code-minimap.fish ├── powershell │ └── _code-minimap.ps1 └── zsh │ └── _code-minimap ├── examples ├── simple.rs └── write_to_string.rs └── src ├── bin └── code-minimap │ ├── cli.rs │ └── main.rs ├── core.rs ├── lib.rs └── lossy_reader.rs /.gitattributes: -------------------------------------------------------------------------------- 1 | /completions/**/* linguist-generated 2 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: cargo 4 | directory: "/" 5 | schedule: 6 | interval: weekly 7 | reviewers: 8 | - wfxr 9 | labels: 10 | - dependencies 11 | commit-message: 12 | prefix: "chore" 13 | prefix-development: "chore" 14 | include: "scope" 15 | -------------------------------------------------------------------------------- /.github/workflows/CICD.yml: -------------------------------------------------------------------------------- 1 | name: CICD 2 | 3 | env: 4 | CICD_INTERMEDIATES_DIR: "_cicd-intermediates" 5 | MSRV_FEATURES: "--all-features" 6 | 7 | on: 8 | workflow_dispatch: 9 | pull_request: 10 | push: 11 | branches: 12 | - master 13 | tags: 14 | - "*" 15 | 16 | jobs: 17 | crate_metadata: 18 | name: Extract crate metadata 19 | runs-on: ubuntu-latest 20 | steps: 21 | - uses: actions/checkout@v4 22 | - name: Extract crate information 23 | id: crate_metadata 24 | run: | 25 | cargo metadata --no-deps --format-version 1 | jq -r '"name=" + .packages[0].name' | tee -a $GITHUB_OUTPUT 26 | cargo metadata --no-deps --format-version 1 | jq -r '"version=" + .packages[0].version' | tee -a $GITHUB_OUTPUT 27 | cargo metadata --no-deps --format-version 1 | jq -r '"maintainer=" + .packages[0].authors[0]' | tee -a $GITHUB_OUTPUT 28 | cargo metadata --no-deps --format-version 1 | jq -r '"homepage=" + .packages[0].homepage' | tee -a $GITHUB_OUTPUT 29 | cargo metadata --no-deps --format-version 1 | jq -r '"description=" + .packages[0].description' | tee -a $GITHUB_OUTPUT 30 | cargo metadata --no-deps --format-version 1 | jq -r '"msrv=" + .packages[0].rust_version' | tee -a $GITHUB_OUTPUT 31 | outputs: 32 | name: ${{ steps.crate_metadata.outputs.name }} 33 | version: ${{ steps.crate_metadata.outputs.version }} 34 | maintainer: ${{ steps.crate_metadata.outputs.maintainer }} 35 | homepage: ${{ steps.crate_metadata.outputs.homepage }} 36 | description: ${{ steps.crate_metadata.outputs.description }} 37 | msrv: ${{ steps.crate_metadata.outputs.msrv }} 38 | 39 | ensure_cargo_fmt: 40 | name: Ensure 'cargo fmt' has been run 41 | runs-on: ubuntu-latest 42 | steps: 43 | - uses: dtolnay/rust-toolchain@stable 44 | with: 45 | components: rustfmt 46 | - uses: actions/checkout@v4 47 | - run: cargo fmt -- --check 48 | 49 | # min_version: 50 | # name: Minimum supported rust version 51 | # runs-on: ubuntu-latest 52 | # needs: crate_metadata 53 | # steps: 54 | # - name: Checkout source code 55 | # uses: actions/checkout@v4 56 | # 57 | # - name: Install rust toolchain (v${{ needs.crate_metadata.outputs.msrv }}) 58 | # uses: dtolnay/rust-toolchain@master 59 | # with: 60 | # toolchain: ${{ needs.crate_metadata.outputs.msrv }} 61 | # components: clippy 62 | # - name: Run clippy (on minimum supported rust version to prevent warnings we can't fix) 63 | # run: cargo clippy --locked --all-targets ${{ env.MSRV_FEATURES }} 64 | # - name: Run tests 65 | # run: cargo test --locked ${{ env.MSRV_FEATURES }} 66 | 67 | build: 68 | name: ${{ matrix.job.target }} (${{ matrix.job.os }}) 69 | runs-on: ${{ matrix.job.os }} 70 | needs: crate_metadata 71 | strategy: 72 | fail-fast: false 73 | matrix: 74 | job: 75 | - { target: aarch64-unknown-linux-gnu , os: ubuntu-20.04, use-cross: true } 76 | - { target: aarch64-unknown-linux-musl , os: ubuntu-20.04, use-cross: true } 77 | - { target: arm-unknown-linux-gnueabihf , os: ubuntu-20.04, use-cross: true } 78 | - { target: arm-unknown-linux-musleabihf, os: ubuntu-20.04, use-cross: true } 79 | - { target: i686-pc-windows-msvc , os: windows-2019 } 80 | - { target: i686-unknown-linux-gnu , os: ubuntu-20.04, use-cross: true } 81 | - { target: i686-unknown-linux-musl , os: ubuntu-20.04, use-cross: true } 82 | - { target: x86_64-apple-darwin , os: macos-13 } 83 | - { target: aarch64-apple-darwin , os: macos-latest } 84 | - { target: x86_64-pc-windows-gnu , os: windows-2019 } 85 | - { target: x86_64-pc-windows-msvc , os: windows-2019 } 86 | - { target: x86_64-unknown-linux-gnu , os: ubuntu-20.04, use-cross: true } 87 | - { target: x86_64-unknown-linux-musl , os: ubuntu-20.04, use-cross: true } 88 | env: 89 | BUILD_CMD: cargo 90 | steps: 91 | - name: Checkout source code 92 | uses: actions/checkout@v4 93 | 94 | - name: Install prerequisites 95 | shell: bash 96 | run: | 97 | case ${{ matrix.job.target }} in 98 | arm-unknown-linux-*) sudo apt-get -y update ; sudo apt-get -y install gcc-arm-linux-gnueabihf ;; 99 | aarch64-unknown-linux-gnu) sudo apt-get -y update ; sudo apt-get -y install gcc-aarch64-linux-gnu ;; 100 | esac 101 | 102 | - name: Install Rust toolchain 103 | uses: dtolnay/rust-toolchain@stable 104 | with: 105 | targets: ${{ matrix.job.target }} 106 | 107 | - name: Install cross 108 | if: matrix.job.use-cross 109 | uses: taiki-e/install-action@v2 110 | with: 111 | tool: cross 112 | 113 | - name: Overwrite build command env variable 114 | if: matrix.job.use-cross 115 | shell: bash 116 | run: echo "BUILD_CMD=cross" >> $GITHUB_ENV 117 | 118 | - name: Show version information (Rust, cargo, GCC) 119 | shell: bash 120 | run: | 121 | gcc --version || true 122 | rustup -V 123 | rustup toolchain list 124 | rustup default 125 | cargo -V 126 | rustc -V 127 | 128 | - name: Build 129 | shell: bash 130 | run: $BUILD_CMD build --locked --release --target=${{ matrix.job.target }} 131 | 132 | - name: Set binary name & path 133 | id: bin 134 | shell: bash 135 | run: | 136 | # Figure out suffix of binary 137 | EXE_suffix="" 138 | case ${{ matrix.job.target }} in 139 | *-pc-windows-*) EXE_suffix=".exe" ;; 140 | esac; 141 | 142 | # Setup paths 143 | BIN_NAME="${{ needs.crate_metadata.outputs.name }}${EXE_suffix}" 144 | BIN_PATH="target/${{ matrix.job.target }}/release/${BIN_NAME}" 145 | 146 | # Let subsequent steps know where to find the binary 147 | echo "BIN_PATH=${BIN_PATH}" >> $GITHUB_OUTPUT 148 | echo "BIN_NAME=${BIN_NAME}" >> $GITHUB_OUTPUT 149 | 150 | - name: Set testing options 151 | id: test-options 152 | shell: bash 153 | run: | 154 | # test only library unit tests and binary for arm-type targets 155 | unset CARGO_TEST_OPTIONS 156 | unset CARGO_TEST_OPTIONS ; case ${{ matrix.job.target }} in arm-* | aarch64-*) CARGO_TEST_OPTIONS="--bin ${{ needs.crate_metadata.outputs.name }}" ;; esac; 157 | echo "CARGO_TEST_OPTIONS=${CARGO_TEST_OPTIONS}" >> $GITHUB_OUTPUT 158 | 159 | - name: Run tests 160 | shell: bash 161 | run: $BUILD_CMD test --locked --target=${{ matrix.job.target }} ${{ steps.test-options.outputs.CARGO_TEST_OPTIONS}} 162 | 163 | - name: Test run 164 | shell: bash 165 | run: $BUILD_CMD run --locked --target=${{ matrix.job.target }} -- Cargo.toml 166 | 167 | - name: Strip binary 168 | shell: bash 169 | run: | 170 | if hash strip &>/dev/null; then 171 | # strip binary if possible 172 | strip "${{ steps.bin.outputs.BIN_PATH }}" || true 173 | fi 174 | 175 | - name: Create tarball 176 | id: package 177 | shell: bash 178 | run: | 179 | PKG_suffix=".tar.gz" ; case ${{ matrix.job.target }} in *-pc-windows-*) PKG_suffix=".zip" ;; esac; 180 | PKG_BASENAME=${{ needs.crate_metadata.outputs.name }}-v${{ needs.crate_metadata.outputs.version }}-${{ matrix.job.target }} 181 | PKG_NAME=${PKG_BASENAME}${PKG_suffix} 182 | echo "PKG_NAME=${PKG_NAME}" >> $GITHUB_OUTPUT 183 | 184 | PKG_STAGING="${{ env.CICD_INTERMEDIATES_DIR }}/package" 185 | ARCHIVE_DIR="${PKG_STAGING}/${PKG_BASENAME}/" 186 | mkdir -p "${ARCHIVE_DIR}" 187 | 188 | # Binary 189 | cp "${{ steps.bin.outputs.BIN_PATH }}" "$ARCHIVE_DIR" 190 | 191 | # README and LICENSE files 192 | cp "README.md" "LICENSE-MIT" "LICENSE-APACHE" "$ARCHIVE_DIR" 193 | 194 | # Autocompletion files 195 | cp -r completions "${ARCHIVE_DIR}" 196 | 197 | # base compressed package 198 | pushd "${PKG_STAGING}/" >/dev/null 199 | case ${{ matrix.job.target }} in 200 | *-pc-windows-*) 7z -y a "${PKG_NAME}" "${PKG_BASENAME}"/* | tail -2 ;; 201 | *) tar czf "${PKG_NAME}" "${PKG_BASENAME}"/* ;; 202 | esac; 203 | popd >/dev/null 204 | 205 | # Let subsequent steps know where to find the compressed package 206 | echo "PKG_PATH=${PKG_STAGING}/${PKG_NAME}" >> $GITHUB_OUTPUT 207 | 208 | - name: Create Debian package 209 | id: debian-package 210 | shell: bash 211 | if: startsWith(matrix.job.os, 'ubuntu') 212 | run: | 213 | COPYRIGHT_YEARS="2020 - "$(date "+%Y") 214 | DPKG_STAGING="${{ env.CICD_INTERMEDIATES_DIR }}/debian-package" 215 | DPKG_DIR="${DPKG_STAGING}/dpkg" 216 | mkdir -p "${DPKG_DIR}" 217 | 218 | DPKG_BASENAME=${{ needs.crate_metadata.outputs.name }} 219 | DPKG_CONFLICTS=${{ needs.crate_metadata.outputs.name }}-musl 220 | case ${{ matrix.job.target }} in *-musl*) DPKG_BASENAME=${{ needs.crate_metadata.outputs.name }}-musl ; DPKG_CONFLICTS=${{ needs.crate_metadata.outputs.name }} ;; esac; 221 | DPKG_VERSION=${{ needs.crate_metadata.outputs.version }} 222 | 223 | unset DPKG_ARCH 224 | case ${{ matrix.job.target }} in 225 | aarch64-*-linux-*) DPKG_ARCH=arm64 ;; 226 | arm-*-linux-*hf) DPKG_ARCH=armhf ;; 227 | i686-*-linux-*) DPKG_ARCH=i686 ;; 228 | x86_64-*-linux-*) DPKG_ARCH=amd64 ;; 229 | *) DPKG_ARCH=notset ;; 230 | esac; 231 | 232 | DPKG_NAME="${DPKG_BASENAME}_${DPKG_VERSION}_${DPKG_ARCH}.deb" 233 | echo "DPKG_NAME=${DPKG_NAME}" >> $GITHUB_OUTPUT 234 | 235 | # Binary 236 | install -Dm755 "${{ steps.bin.outputs.BIN_PATH }}" "${DPKG_DIR}/usr/bin/${{ steps.bin.outputs.BIN_NAME }}" 237 | 238 | # Man page 239 | # install -Dm644 'doc/${{ needs.crate_metadata.outputs.name }}.1' "${DPKG_DIR}/usr/share/man/man1/${{ needs.crate_metadata.outputs.name }}.1" 240 | # gzip -n --best "${DPKG_DIR}/usr/share/man/man1/${{ needs.crate_metadata.outputs.name }}.1" 241 | 242 | # Autocompletion files 243 | install -Dm644 'completions/bash/${{ needs.crate_metadata.outputs.name }}.bash' "${DPKG_DIR}/usr/share/bash-completion/completions/${{ needs.crate_metadata.outputs.name }}" 244 | install -Dm644 'completions/fish/${{ needs.crate_metadata.outputs.name }}.fish' "${DPKG_DIR}/usr/share/fish/vendor_completions.d/${{ needs.crate_metadata.outputs.name }}.fish" 245 | install -Dm644 'completions/zsh/_${{ needs.crate_metadata.outputs.name }}' "${DPKG_DIR}/usr/share/zsh/vendor-completions/_${{ needs.crate_metadata.outputs.name }}" 246 | 247 | # README and LICENSE 248 | install -Dm644 "README.md" "${DPKG_DIR}/usr/share/doc/${DPKG_BASENAME}/README.md" 249 | install -Dm644 "LICENSE-MIT" "${DPKG_DIR}/usr/share/doc/${DPKG_BASENAME}/LICENSE-MIT" 250 | install -Dm644 "LICENSE-APACHE" "${DPKG_DIR}/usr/share/doc/${DPKG_BASENAME}/LICENSE-APACHE" 251 | 252 | cat > "${DPKG_DIR}/usr/share/doc/${DPKG_BASENAME}/copyright" < "${DPKG_DIR}/DEBIAN/control" <> $GITHUB_OUTPUT 310 | 311 | # build dpkg 312 | fakeroot dpkg-deb --build "${DPKG_DIR}" "${DPKG_PATH}" 313 | 314 | - name: "Artifact upload: tarball" 315 | uses: actions/upload-artifact@master 316 | with: 317 | name: ${{ steps.package.outputs.PKG_NAME }} 318 | path: ${{ steps.package.outputs.PKG_PATH }} 319 | 320 | - name: "Artifact upload: Debian package" 321 | uses: actions/upload-artifact@master 322 | if: steps.debian-package.outputs.DPKG_NAME 323 | with: 324 | name: ${{ steps.debian-package.outputs.DPKG_NAME }} 325 | path: ${{ steps.debian-package.outputs.DPKG_PATH }} 326 | 327 | - name: Check for release 328 | id: is-release 329 | shell: bash 330 | run: | 331 | unset IS_RELEASE ; if [[ $GITHUB_REF =~ ^refs/tags/v[0-9].* ]]; then IS_RELEASE='true' ; fi 332 | echo "IS_RELEASE=${IS_RELEASE}" >> $GITHUB_OUTPUT 333 | 334 | - name: Publish archives and packages 335 | uses: softprops/action-gh-release@v1 336 | if: steps.is-release.outputs.IS_RELEASE 337 | with: 338 | files: | 339 | ${{ steps.package.outputs.PKG_PATH }} 340 | ${{ steps.debian-package.outputs.DPKG_PATH }} 341 | env: 342 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 343 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | # See https://pre-commit.com for more information 2 | # See https://pre-commit.com/hooks.html for more hooks 3 | repos: 4 | - repo: https://github.com/pre-commit/pre-commit-hooks 5 | rev: v5.0.0 6 | hooks: 7 | - id: trailing-whitespace 8 | - id: check-yaml 9 | - id: check-added-large-files 10 | - id: mixed-line-ending 11 | - id: check-toml 12 | - repo: local 13 | hooks: 14 | - id: cargo-fmt 15 | name: cargo fmt 16 | pass_filenames: false 17 | always_run: true 18 | language: system 19 | entry: cargo fmt 20 | - id: cargo-check 21 | name: cargo check 22 | pass_filenames: false 23 | always_run: true 24 | language: system 25 | entry: cargo check 26 | - id: cargo-clippy 27 | name: cargo clippy 28 | pass_filenames: false 29 | language: system 30 | always_run: true 31 | entry: cargo clippy 32 | args: ["--", "-D", "warnings"] 33 | - id: update-completions 34 | name: update shell completions 35 | pass_filenames: false 36 | language: system 37 | always_run: true 38 | entry: > 39 | sh -c ' 40 | touch build.rs && 41 | SHELL_COMPLETIONS_DIR=completions cargo build && 42 | git add completions 43 | ' 44 | -------------------------------------------------------------------------------- /.rustfmt.toml: -------------------------------------------------------------------------------- 1 | edition = "2021" 2 | struct_field_align_threshold = 40 3 | max_width = 120 4 | comment_width = 120 5 | reorder_imports = true 6 | fn_single_line = false 7 | imports_layout = "HorizontalVertical" 8 | match_arm_blocks = false 9 | overflow_delimited_expr = true 10 | imports_granularity = "Crate" 11 | struct_lit_width = 60 12 | struct_variant_width = 60 13 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 4 4 | 5 | [[package]] 6 | name = "aho-corasick" 7 | version = "1.1.3" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" 10 | dependencies = [ 11 | "memchr", 12 | ] 13 | 14 | [[package]] 15 | name = "anstream" 16 | version = "0.6.18" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" 19 | dependencies = [ 20 | "anstyle", 21 | "anstyle-parse", 22 | "anstyle-query", 23 | "anstyle-wincon", 24 | "colorchoice", 25 | "is_terminal_polyfill", 26 | "utf8parse", 27 | ] 28 | 29 | [[package]] 30 | name = "anstyle" 31 | version = "1.0.10" 32 | source = "registry+https://github.com/rust-lang/crates.io-index" 33 | checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" 34 | 35 | [[package]] 36 | name = "anstyle-parse" 37 | version = "0.2.6" 38 | source = "registry+https://github.com/rust-lang/crates.io-index" 39 | checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" 40 | dependencies = [ 41 | "utf8parse", 42 | ] 43 | 44 | [[package]] 45 | name = "anstyle-query" 46 | version = "1.1.2" 47 | source = "registry+https://github.com/rust-lang/crates.io-index" 48 | checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" 49 | dependencies = [ 50 | "windows-sys", 51 | ] 52 | 53 | [[package]] 54 | name = "anstyle-wincon" 55 | version = "3.0.6" 56 | source = "registry+https://github.com/rust-lang/crates.io-index" 57 | checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125" 58 | dependencies = [ 59 | "anstyle", 60 | "windows-sys", 61 | ] 62 | 63 | [[package]] 64 | name = "anyhow" 65 | version = "1.0.95" 66 | source = "registry+https://github.com/rust-lang/crates.io-index" 67 | checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04" 68 | 69 | [[package]] 70 | name = "atty" 71 | version = "0.2.14" 72 | source = "registry+https://github.com/rust-lang/crates.io-index" 73 | checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" 74 | dependencies = [ 75 | "hermit-abi", 76 | "libc", 77 | "winapi", 78 | ] 79 | 80 | [[package]] 81 | name = "autocfg" 82 | version = "1.4.0" 83 | source = "registry+https://github.com/rust-lang/crates.io-index" 84 | checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" 85 | 86 | [[package]] 87 | name = "bitflags" 88 | version = "2.6.0" 89 | source = "registry+https://github.com/rust-lang/crates.io-index" 90 | checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" 91 | 92 | [[package]] 93 | name = "cfg-if" 94 | version = "1.0.0" 95 | source = "registry+https://github.com/rust-lang/crates.io-index" 96 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 97 | 98 | [[package]] 99 | name = "clap" 100 | version = "4.5.23" 101 | source = "registry+https://github.com/rust-lang/crates.io-index" 102 | checksum = "3135e7ec2ef7b10c6ed8950f0f792ed96ee093fa088608f1c76e569722700c84" 103 | dependencies = [ 104 | "clap_builder", 105 | "clap_derive", 106 | ] 107 | 108 | [[package]] 109 | name = "clap_builder" 110 | version = "4.5.23" 111 | source = "registry+https://github.com/rust-lang/crates.io-index" 112 | checksum = "30582fc632330df2bd26877bde0c1f4470d57c582bbc070376afcd04d8cb4838" 113 | dependencies = [ 114 | "anstream", 115 | "anstyle", 116 | "clap_lex", 117 | "strsim", 118 | "terminal_size", 119 | ] 120 | 121 | [[package]] 122 | name = "clap_complete" 123 | version = "4.5.40" 124 | source = "registry+https://github.com/rust-lang/crates.io-index" 125 | checksum = "ac2e663e3e3bed2d32d065a8404024dad306e699a04263ec59919529f803aee9" 126 | dependencies = [ 127 | "clap", 128 | ] 129 | 130 | [[package]] 131 | name = "clap_derive" 132 | version = "4.5.18" 133 | source = "registry+https://github.com/rust-lang/crates.io-index" 134 | checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" 135 | dependencies = [ 136 | "heck", 137 | "proc-macro2", 138 | "quote", 139 | "syn", 140 | ] 141 | 142 | [[package]] 143 | name = "clap_lex" 144 | version = "0.7.4" 145 | source = "registry+https://github.com/rust-lang/crates.io-index" 146 | checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" 147 | 148 | [[package]] 149 | name = "code-minimap" 150 | version = "0.6.8" 151 | dependencies = [ 152 | "anyhow", 153 | "atty", 154 | "clap", 155 | "clap_complete", 156 | "itertools", 157 | "rstest", 158 | ] 159 | 160 | [[package]] 161 | name = "colorchoice" 162 | version = "1.0.3" 163 | source = "registry+https://github.com/rust-lang/crates.io-index" 164 | checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" 165 | 166 | [[package]] 167 | name = "either" 168 | version = "1.13.0" 169 | source = "registry+https://github.com/rust-lang/crates.io-index" 170 | checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" 171 | 172 | [[package]] 173 | name = "equivalent" 174 | version = "1.0.1" 175 | source = "registry+https://github.com/rust-lang/crates.io-index" 176 | checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" 177 | 178 | [[package]] 179 | name = "errno" 180 | version = "0.3.10" 181 | source = "registry+https://github.com/rust-lang/crates.io-index" 182 | checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" 183 | dependencies = [ 184 | "libc", 185 | "windows-sys", 186 | ] 187 | 188 | [[package]] 189 | name = "futures" 190 | version = "0.3.31" 191 | source = "registry+https://github.com/rust-lang/crates.io-index" 192 | checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" 193 | dependencies = [ 194 | "futures-channel", 195 | "futures-core", 196 | "futures-executor", 197 | "futures-io", 198 | "futures-sink", 199 | "futures-task", 200 | "futures-util", 201 | ] 202 | 203 | [[package]] 204 | name = "futures-channel" 205 | version = "0.3.31" 206 | source = "registry+https://github.com/rust-lang/crates.io-index" 207 | checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" 208 | dependencies = [ 209 | "futures-core", 210 | "futures-sink", 211 | ] 212 | 213 | [[package]] 214 | name = "futures-core" 215 | version = "0.3.31" 216 | source = "registry+https://github.com/rust-lang/crates.io-index" 217 | checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" 218 | 219 | [[package]] 220 | name = "futures-executor" 221 | version = "0.3.31" 222 | source = "registry+https://github.com/rust-lang/crates.io-index" 223 | checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" 224 | dependencies = [ 225 | "futures-core", 226 | "futures-task", 227 | "futures-util", 228 | ] 229 | 230 | [[package]] 231 | name = "futures-io" 232 | version = "0.3.31" 233 | source = "registry+https://github.com/rust-lang/crates.io-index" 234 | checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" 235 | 236 | [[package]] 237 | name = "futures-macro" 238 | version = "0.3.31" 239 | source = "registry+https://github.com/rust-lang/crates.io-index" 240 | checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" 241 | dependencies = [ 242 | "proc-macro2", 243 | "quote", 244 | "syn", 245 | ] 246 | 247 | [[package]] 248 | name = "futures-sink" 249 | version = "0.3.31" 250 | source = "registry+https://github.com/rust-lang/crates.io-index" 251 | checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" 252 | 253 | [[package]] 254 | name = "futures-task" 255 | version = "0.3.31" 256 | source = "registry+https://github.com/rust-lang/crates.io-index" 257 | checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" 258 | 259 | [[package]] 260 | name = "futures-timer" 261 | version = "3.0.3" 262 | source = "registry+https://github.com/rust-lang/crates.io-index" 263 | checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24" 264 | 265 | [[package]] 266 | name = "futures-util" 267 | version = "0.3.31" 268 | source = "registry+https://github.com/rust-lang/crates.io-index" 269 | checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" 270 | dependencies = [ 271 | "futures-channel", 272 | "futures-core", 273 | "futures-io", 274 | "futures-macro", 275 | "futures-sink", 276 | "futures-task", 277 | "memchr", 278 | "pin-project-lite", 279 | "pin-utils", 280 | "slab", 281 | ] 282 | 283 | [[package]] 284 | name = "glob" 285 | version = "0.3.1" 286 | source = "registry+https://github.com/rust-lang/crates.io-index" 287 | checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" 288 | 289 | [[package]] 290 | name = "hashbrown" 291 | version = "0.15.2" 292 | source = "registry+https://github.com/rust-lang/crates.io-index" 293 | checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" 294 | 295 | [[package]] 296 | name = "heck" 297 | version = "0.5.0" 298 | source = "registry+https://github.com/rust-lang/crates.io-index" 299 | checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" 300 | 301 | [[package]] 302 | name = "hermit-abi" 303 | version = "0.1.19" 304 | source = "registry+https://github.com/rust-lang/crates.io-index" 305 | checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" 306 | dependencies = [ 307 | "libc", 308 | ] 309 | 310 | [[package]] 311 | name = "indexmap" 312 | version = "2.7.0" 313 | source = "registry+https://github.com/rust-lang/crates.io-index" 314 | checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f" 315 | dependencies = [ 316 | "equivalent", 317 | "hashbrown", 318 | ] 319 | 320 | [[package]] 321 | name = "is_terminal_polyfill" 322 | version = "1.70.1" 323 | source = "registry+https://github.com/rust-lang/crates.io-index" 324 | checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" 325 | 326 | [[package]] 327 | name = "itertools" 328 | version = "0.13.0" 329 | source = "registry+https://github.com/rust-lang/crates.io-index" 330 | checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" 331 | dependencies = [ 332 | "either", 333 | ] 334 | 335 | [[package]] 336 | name = "libc" 337 | version = "0.2.169" 338 | source = "registry+https://github.com/rust-lang/crates.io-index" 339 | checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" 340 | 341 | [[package]] 342 | name = "linux-raw-sys" 343 | version = "0.4.14" 344 | source = "registry+https://github.com/rust-lang/crates.io-index" 345 | checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" 346 | 347 | [[package]] 348 | name = "memchr" 349 | version = "2.7.4" 350 | source = "registry+https://github.com/rust-lang/crates.io-index" 351 | checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" 352 | 353 | [[package]] 354 | name = "pin-project-lite" 355 | version = "0.2.15" 356 | source = "registry+https://github.com/rust-lang/crates.io-index" 357 | checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff" 358 | 359 | [[package]] 360 | name = "pin-utils" 361 | version = "0.1.0" 362 | source = "registry+https://github.com/rust-lang/crates.io-index" 363 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 364 | 365 | [[package]] 366 | name = "proc-macro-crate" 367 | version = "3.2.0" 368 | source = "registry+https://github.com/rust-lang/crates.io-index" 369 | checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b" 370 | dependencies = [ 371 | "toml_edit", 372 | ] 373 | 374 | [[package]] 375 | name = "proc-macro2" 376 | version = "1.0.92" 377 | source = "registry+https://github.com/rust-lang/crates.io-index" 378 | checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" 379 | dependencies = [ 380 | "unicode-ident", 381 | ] 382 | 383 | [[package]] 384 | name = "quote" 385 | version = "1.0.38" 386 | source = "registry+https://github.com/rust-lang/crates.io-index" 387 | checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" 388 | dependencies = [ 389 | "proc-macro2", 390 | ] 391 | 392 | [[package]] 393 | name = "regex" 394 | version = "1.11.1" 395 | source = "registry+https://github.com/rust-lang/crates.io-index" 396 | checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" 397 | dependencies = [ 398 | "aho-corasick", 399 | "memchr", 400 | "regex-automata", 401 | "regex-syntax", 402 | ] 403 | 404 | [[package]] 405 | name = "regex-automata" 406 | version = "0.4.9" 407 | source = "registry+https://github.com/rust-lang/crates.io-index" 408 | checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" 409 | dependencies = [ 410 | "aho-corasick", 411 | "memchr", 412 | "regex-syntax", 413 | ] 414 | 415 | [[package]] 416 | name = "regex-syntax" 417 | version = "0.8.5" 418 | source = "registry+https://github.com/rust-lang/crates.io-index" 419 | checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" 420 | 421 | [[package]] 422 | name = "relative-path" 423 | version = "1.9.3" 424 | source = "registry+https://github.com/rust-lang/crates.io-index" 425 | checksum = "ba39f3699c378cd8970968dcbff9c43159ea4cfbd88d43c00b22f2ef10a435d2" 426 | 427 | [[package]] 428 | name = "rstest" 429 | version = "0.23.0" 430 | source = "registry+https://github.com/rust-lang/crates.io-index" 431 | checksum = "0a2c585be59b6b5dd66a9d2084aa1d8bd52fbdb806eafdeffb52791147862035" 432 | dependencies = [ 433 | "futures", 434 | "futures-timer", 435 | "rstest_macros", 436 | "rustc_version", 437 | ] 438 | 439 | [[package]] 440 | name = "rstest_macros" 441 | version = "0.23.0" 442 | source = "registry+https://github.com/rust-lang/crates.io-index" 443 | checksum = "825ea780781b15345a146be27eaefb05085e337e869bff01b4306a4fd4a9ad5a" 444 | dependencies = [ 445 | "cfg-if", 446 | "glob", 447 | "proc-macro-crate", 448 | "proc-macro2", 449 | "quote", 450 | "regex", 451 | "relative-path", 452 | "rustc_version", 453 | "syn", 454 | "unicode-ident", 455 | ] 456 | 457 | [[package]] 458 | name = "rustc_version" 459 | version = "0.4.1" 460 | source = "registry+https://github.com/rust-lang/crates.io-index" 461 | checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" 462 | dependencies = [ 463 | "semver", 464 | ] 465 | 466 | [[package]] 467 | name = "rustix" 468 | version = "0.38.42" 469 | source = "registry+https://github.com/rust-lang/crates.io-index" 470 | checksum = "f93dc38ecbab2eb790ff964bb77fa94faf256fd3e73285fd7ba0903b76bedb85" 471 | dependencies = [ 472 | "bitflags", 473 | "errno", 474 | "libc", 475 | "linux-raw-sys", 476 | "windows-sys", 477 | ] 478 | 479 | [[package]] 480 | name = "semver" 481 | version = "1.0.24" 482 | source = "registry+https://github.com/rust-lang/crates.io-index" 483 | checksum = "3cb6eb87a131f756572d7fb904f6e7b68633f09cca868c5df1c4b8d1a694bbba" 484 | 485 | [[package]] 486 | name = "slab" 487 | version = "0.4.9" 488 | source = "registry+https://github.com/rust-lang/crates.io-index" 489 | checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" 490 | dependencies = [ 491 | "autocfg", 492 | ] 493 | 494 | [[package]] 495 | name = "strsim" 496 | version = "0.11.1" 497 | source = "registry+https://github.com/rust-lang/crates.io-index" 498 | checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" 499 | 500 | [[package]] 501 | name = "syn" 502 | version = "2.0.92" 503 | source = "registry+https://github.com/rust-lang/crates.io-index" 504 | checksum = "70ae51629bf965c5c098cc9e87908a3df5301051a9e087d6f9bef5c9771ed126" 505 | dependencies = [ 506 | "proc-macro2", 507 | "quote", 508 | "unicode-ident", 509 | ] 510 | 511 | [[package]] 512 | name = "terminal_size" 513 | version = "0.4.1" 514 | source = "registry+https://github.com/rust-lang/crates.io-index" 515 | checksum = "5352447f921fda68cf61b4101566c0bdb5104eff6804d0678e5227580ab6a4e9" 516 | dependencies = [ 517 | "rustix", 518 | "windows-sys", 519 | ] 520 | 521 | [[package]] 522 | name = "toml_datetime" 523 | version = "0.6.8" 524 | source = "registry+https://github.com/rust-lang/crates.io-index" 525 | checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" 526 | 527 | [[package]] 528 | name = "toml_edit" 529 | version = "0.22.22" 530 | source = "registry+https://github.com/rust-lang/crates.io-index" 531 | checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" 532 | dependencies = [ 533 | "indexmap", 534 | "toml_datetime", 535 | "winnow", 536 | ] 537 | 538 | [[package]] 539 | name = "unicode-ident" 540 | version = "1.0.14" 541 | source = "registry+https://github.com/rust-lang/crates.io-index" 542 | checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" 543 | 544 | [[package]] 545 | name = "utf8parse" 546 | version = "0.2.2" 547 | source = "registry+https://github.com/rust-lang/crates.io-index" 548 | checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" 549 | 550 | [[package]] 551 | name = "winapi" 552 | version = "0.3.9" 553 | source = "registry+https://github.com/rust-lang/crates.io-index" 554 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 555 | dependencies = [ 556 | "winapi-i686-pc-windows-gnu", 557 | "winapi-x86_64-pc-windows-gnu", 558 | ] 559 | 560 | [[package]] 561 | name = "winapi-i686-pc-windows-gnu" 562 | version = "0.4.0" 563 | source = "registry+https://github.com/rust-lang/crates.io-index" 564 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 565 | 566 | [[package]] 567 | name = "winapi-x86_64-pc-windows-gnu" 568 | version = "0.4.0" 569 | source = "registry+https://github.com/rust-lang/crates.io-index" 570 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 571 | 572 | [[package]] 573 | name = "windows-sys" 574 | version = "0.59.0" 575 | source = "registry+https://github.com/rust-lang/crates.io-index" 576 | checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" 577 | dependencies = [ 578 | "windows-targets", 579 | ] 580 | 581 | [[package]] 582 | name = "windows-targets" 583 | version = "0.52.6" 584 | source = "registry+https://github.com/rust-lang/crates.io-index" 585 | checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" 586 | dependencies = [ 587 | "windows_aarch64_gnullvm", 588 | "windows_aarch64_msvc", 589 | "windows_i686_gnu", 590 | "windows_i686_gnullvm", 591 | "windows_i686_msvc", 592 | "windows_x86_64_gnu", 593 | "windows_x86_64_gnullvm", 594 | "windows_x86_64_msvc", 595 | ] 596 | 597 | [[package]] 598 | name = "windows_aarch64_gnullvm" 599 | version = "0.52.6" 600 | source = "registry+https://github.com/rust-lang/crates.io-index" 601 | checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" 602 | 603 | [[package]] 604 | name = "windows_aarch64_msvc" 605 | version = "0.52.6" 606 | source = "registry+https://github.com/rust-lang/crates.io-index" 607 | checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" 608 | 609 | [[package]] 610 | name = "windows_i686_gnu" 611 | version = "0.52.6" 612 | source = "registry+https://github.com/rust-lang/crates.io-index" 613 | checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" 614 | 615 | [[package]] 616 | name = "windows_i686_gnullvm" 617 | version = "0.52.6" 618 | source = "registry+https://github.com/rust-lang/crates.io-index" 619 | checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" 620 | 621 | [[package]] 622 | name = "windows_i686_msvc" 623 | version = "0.52.6" 624 | source = "registry+https://github.com/rust-lang/crates.io-index" 625 | checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" 626 | 627 | [[package]] 628 | name = "windows_x86_64_gnu" 629 | version = "0.52.6" 630 | source = "registry+https://github.com/rust-lang/crates.io-index" 631 | checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" 632 | 633 | [[package]] 634 | name = "windows_x86_64_gnullvm" 635 | version = "0.52.6" 636 | source = "registry+https://github.com/rust-lang/crates.io-index" 637 | checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" 638 | 639 | [[package]] 640 | name = "windows_x86_64_msvc" 641 | version = "0.52.6" 642 | source = "registry+https://github.com/rust-lang/crates.io-index" 643 | checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" 644 | 645 | [[package]] 646 | name = "winnow" 647 | version = "0.6.20" 648 | source = "registry+https://github.com/rust-lang/crates.io-index" 649 | checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" 650 | dependencies = [ 651 | "memchr", 652 | ] 653 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "code-minimap" 3 | version = "0.6.8" 4 | authors = ["Wenxuan Zhang "] 5 | description = "A high performance code minimap generator" 6 | categories = ["command-line-utilities"] 7 | homepage = "https://github.com/wfxr/code-minimap" 8 | keywords = ["minimap", "tool"] 9 | license = "Apache-2.0 OR MIT" 10 | exclude = ["/completions"] 11 | repository = "https://github.com/wfxr/code-minimap" 12 | edition = "2021" 13 | build = "build.rs" 14 | 15 | [features] 16 | default = ["application"] 17 | # Feature required for build the `code-minimap` application. Should be disabled when 18 | # depending on `code-minimap` as a library. 19 | application = ["clap", "clap_complete"] 20 | 21 | [lib] 22 | name = "code_minimap" 23 | path = "src/lib.rs" 24 | 25 | [[bin]] 26 | name = "code-minimap" 27 | path = "src/bin/code-minimap/main.rs" 28 | 29 | [dependencies] 30 | anyhow = "1" 31 | itertools = "0" 32 | 33 | clap = { version = "4", features = ["wrap_help", "derive"], optional = true } 34 | clap_complete = { version = "4", optional = true } 35 | atty = "0.2.14" 36 | 37 | [build-dependencies] 38 | clap = { version = "4", features = ["wrap_help", "derive"], optional = true } 39 | clap_complete = { version = "4", optional = true } 40 | 41 | [dev-dependencies] 42 | rstest = "0" 43 | 44 | [profile.release] 45 | lto = true 46 | codegen-units = 1 47 | -------------------------------------------------------------------------------- /FUNDING.yml: -------------------------------------------------------------------------------- 1 | ko_fi: wfxr 2 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2020-2024 Wenxuan Zhang (https://wfxr.mit-license.org/2020). 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

🛰 code-minimap

2 |

3 | A high performance code minimap render. 4 |

5 | 6 |

7 | 8 | CICD 9 | 10 | License 11 | 12 | Version 13 | 14 | 15 | Platform 16 | 17 |

18 | 19 | This tool is for generating text minimaps at 🚀 speed. 20 | You can use it to implement IDE-like minimap plugin for a terminal text editor, 21 | [minimap.vim](https://github.com/wfxr/minimap.vim) for example. 22 | 23 | ### Features 24 | 25 | * Small and *fast* (see [benchmarks](#benchmark) below). 26 | * Memory efficient. 27 | * Freely zoom. 28 | * [Multi platforms](https://github.com/wfxr/code-minimap/releases) support. 29 | 30 | ### Usage 31 | 32 | ``` 33 | $ code-minimap src/core.rs -H 0.6 -V 0.5 34 | ⣿⣿⣿⣿⣿⠿⠛⠓⠒⠒⠂ 35 | ⣉⣿⣿⣿⣟⣛⣛⣛⠒⠒⠂ 36 | ⠀⠉⣿⣿⣿⣿⠭⠭⠭⠭⠤⠤⠤⠤⠤ 37 | ⠀⠉⠛⠻⢿⣿⣿⣿⣿⣶⣶⣶⣒⣒⣒⣒⣒⣒⣀⣀⣀⣀⣀⣀⣀⣀⣀⡀ 38 | ⠀⣀⣶⣾⣿⣿⣿⣿⣭⣭⣭⣤⣤⣤⣤⣤⠤⠤⠤⠤⠤ 39 | ⣿⣿⣿⣶⡒⠒⠒⠒ 40 | ⣿⣿⣶⣶⣶⣶⣶⣶⣤⣤⣤⣤⣤⣤⣤⣤⣄ 41 | ⣭⣭⣭⣭⠭⠭⠭⠭⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉ 42 | ⣿⣿⣿⣿⣧⣤⣤⣤⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⡀ 43 | ⣛⣿⣿⣿⣟⣛⣒⣒⠂ 44 | ⣀⣛⣛⣛⣛⣛⣛⣛⣛⣛⣛⣛⣛⣛⣛⣛⣋⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⡀ 45 | ⠀⣤⣭⣽⣿⣷⣶⣶⣶⠶⠶⠶⠶⠶⠶⠶⠶⠶⠶⠶⠶⠒⠒⠒⠒⠒ 46 | ⠀⠶⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠿⠛⠓⠒⠒⠒⠒⠒ 47 | ⣉⣛⣛⣛⣛⣛⣛⣛⣛⣛⣛⣛⡛⠛⠛⠛⠛ 48 | ⠒⣶⣶⣶⣶⣶⣶⣶⣶⣶⣶⣤⣤⣤⣤⣤⣤⣤⣤⣤⣤⣤⣤⣤⣤⣤⣤⣤⣤⣤⣤⣤⣤⣤⡄ 49 | ⠀⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡇ 50 | ⠄⠛⠛⠛⠛⠛⠛⠛⠛⠛⠛⠛⠛⠛⠛⠛⠛⠛⠛⠛⠛⠛⠛⠛⠛⠛⠛⠛⠛⠛⠛⠛⠛⠛⠃ 51 | ``` 52 | 53 | Run `code-minimap --help` to view detailed usage. 54 | 55 | ### Installation 56 | 57 | #### On Linux 58 | 59 | ##### Arch Linux 60 | `code-minimap` is available in the Arch User Repository. To install it from [AUR](https://aur.archlinux.org/packages/code-minimap): 61 | 62 | ``` 63 | yay -S code-minimap 64 | ``` 65 | 66 | ##### Alpine Linux 67 | `code-minimap` is available in the [testing](https://pkgs.alpinelinux.org/packages?name=code-minimap&branch=edge&repo=testing) repository: 68 | 69 | ``` 70 | apk add code-minimap 71 | ``` 72 | 73 | ##### Gentoo 74 | `code-minimap` is available in the [GURU](https://github.com/gentoo/guru/) project repository: 75 | ``` 76 | emerge --ask code-minimap 77 | ``` 78 | 79 | 80 | #### On macOS 81 | 82 | You can install `code-minimap` with Homebrew: 83 | 84 | ``` 85 | brew install code-minimap 86 | ``` 87 | 88 | or with MacPorts: 89 | 90 | ``` 91 | sudo port install code-minimap 92 | ``` 93 | 94 | #### On Windows 95 | 96 | You can install `code-minimap` with [scoop](https://github.com/lukesampson/scoop): 97 | 98 | ``` 99 | scoop bucket add extras 100 | scoop install code-minimap 101 | ``` 102 | 103 | #### From binaries 104 | 105 | Prebuilt versions of `code-minimap` for various architectures are available at [Github release page](https://github.com/wfxr/code-minimap/releases). 106 | 107 | *Note that you can try the `musl` version (which is statically-linked) if runs into dependency related errors.* 108 | 109 | #### From source 110 | 111 | `code-minimap` is also published on [crates.io](https://crates.io). If you have the latest Rust toolchains installed you can use `cargo` to install it from source: 112 | 113 | ``` 114 | cargo install --locked code-minimap 115 | ``` 116 | 117 | If you want the latest version, clone this repository and run `cargo build --release`. 118 | 119 | ### Benchmark 120 | 121 | - [src/core.rs](https://github.com/wfxr/code-minimap/blob/v0.3.0/src/core.rs): 122 | 123 | ``` 124 | $ hyperfine -w 10 'code-minimap src/core.rs' 125 | Benchmark #1: code-minimap src/core.rs 126 | Time (mean ± σ): 0.2 ms ± 0.1 ms [User: 0.4 ms, System: 0.3 ms] 127 | Range (min … max): 0.2 ms … 1.1 ms 1560 runs 128 | ``` 129 | **79** lines, **4K** size, **0.2ms**. 130 | 131 | --------------------------------- 132 | 133 | - [all rust code from rust-1.46.0](https://github.com/rust-lang/rust/archive/1.46.0.tar.gz): 134 | 135 | ``` 136 | $ fd -t f -e rs -x cat "{}" >> /tmp/all-in-one.rs 137 | $ hyperfine -w 10 'code-minimap /tmp/all-in-one.rs' 138 | Benchmark #1: code-minimap /tmp/all-in-one.rs 139 | Time (mean ± σ): 322.7 ms ± 4.5 ms [User: 298.7 ms, System: 23.8 ms] 140 | Range (min … max): 318.5 ms … 334.1 ms 10 runs 141 | ``` 142 | **1,153,225** lines, **37M** size, **323ms**. 143 | 144 | --------------------------------- 145 | 146 | - [huge random file](): 147 | ``` 148 | $ base64 /dev/urandom | head -10000000 > huge.txt 149 | $ hyperfine -w 1 'code-minimap huge.txt' 150 | Benchmark #1: code-minimap huge.txt 151 | Time (mean ± σ): 2.941 s ± 0.034 s [User: 2.618 s, System: 0.321 s] 152 | Range (min … max): 2.919 s … 3.028 s 10 runs 153 | 154 | ``` 155 | **10,000,000** lines, **735M** size, **2.9s**. 156 | 157 | --------------------------------- 158 | 159 | *Test environment:* 160 | 161 | ``` 162 | Binary version: 0.3.0 163 | OS: Arch Linux x86_64 164 | Kernel: 5.8.10-arch1-1 165 | CPU: Intel i9-9900K (16) @ 5.000GHz 166 | ``` 167 | 168 | ### Related Project 169 | 170 | [minimap.vim](https://github.com/wfxr/minimap.vim): Blazing fast minimap for vim. 171 | 172 | ### License 173 | 174 | `code-minimap` is distributed under the terms of both the MIT License and the Apache License 2.0. 175 | 176 | See the [LICENSE-APACHE](LICENSE-APACHE) and [LICENSE-MIT](LICENSE-MIT) files for license details. 177 | -------------------------------------------------------------------------------- /build.rs: -------------------------------------------------------------------------------- 1 | use clap::CommandFactory; 2 | use std::{fs, path::Path}; 3 | 4 | include!("src/bin/code-minimap/cli.rs"); 5 | 6 | fn main() -> Result<(), Box> { 7 | let outdir = std::env::var_os("SHELL_COMPLETIONS_DIR") 8 | .or_else(|| std::env::var_os("OUT_DIR")) 9 | .expect("OUT_DIR not found"); 10 | let outdir_path = Path::new(&outdir); 11 | let cmd = &mut App::command(); 12 | 13 | for shell in Shell::value_variants() { 14 | let dir = outdir_path.join(shell.to_string()); 15 | fs::create_dir_all(&dir)?; 16 | clap_complete::generate_to(*shell, cmd, cmd.get_name().to_string(), &dir)?; 17 | } 18 | Ok(()) 19 | } 20 | -------------------------------------------------------------------------------- /completions/bash/code-minimap.bash: -------------------------------------------------------------------------------- 1 | _code-minimap() { 2 | local i cur prev opts cmd 3 | COMPREPLY=() 4 | cur="${COMP_WORDS[COMP_CWORD]}" 5 | prev="${COMP_WORDS[COMP_CWORD-1]}" 6 | cmd="" 7 | opts="" 8 | 9 | for i in ${COMP_WORDS[@]} 10 | do 11 | case "${cmd},${i}" in 12 | ",$1") 13 | cmd="code__minimap" 14 | ;; 15 | code__minimap,completion) 16 | cmd="code__minimap__completion" 17 | ;; 18 | code__minimap,help) 19 | cmd="code__minimap__help" 20 | ;; 21 | code__minimap__help,completion) 22 | cmd="code__minimap__help__completion" 23 | ;; 24 | code__minimap__help,help) 25 | cmd="code__minimap__help__help" 26 | ;; 27 | *) 28 | ;; 29 | esac 30 | done 31 | 32 | case "${cmd}" in 33 | code__minimap) 34 | opts="-H -V -h --horizontal-scale --vertical-scale --padding --encoding --version --help [FILE] completion help" 35 | if [[ ${cur} == -* || ${COMP_CWORD} -eq 1 ]] ; then 36 | COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) 37 | return 0 38 | fi 39 | case "${prev}" in 40 | --horizontal-scale) 41 | COMPREPLY=($(compgen -f "${cur}")) 42 | return 0 43 | ;; 44 | -H) 45 | COMPREPLY=($(compgen -f "${cur}")) 46 | return 0 47 | ;; 48 | --vertical-scale) 49 | COMPREPLY=($(compgen -f "${cur}")) 50 | return 0 51 | ;; 52 | -V) 53 | COMPREPLY=($(compgen -f "${cur}")) 54 | return 0 55 | ;; 56 | --padding) 57 | COMPREPLY=($(compgen -f "${cur}")) 58 | return 0 59 | ;; 60 | --encoding) 61 | COMPREPLY=($(compgen -W "utf8-lossy utf8" -- "${cur}")) 62 | return 0 63 | ;; 64 | *) 65 | COMPREPLY=() 66 | ;; 67 | esac 68 | COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) 69 | return 0 70 | ;; 71 | code__minimap__completion) 72 | opts="-h --help bash elvish fish powershell zsh" 73 | if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then 74 | COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) 75 | return 0 76 | fi 77 | case "${prev}" in 78 | *) 79 | COMPREPLY=() 80 | ;; 81 | esac 82 | COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) 83 | return 0 84 | ;; 85 | code__minimap__help) 86 | opts="completion help" 87 | if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then 88 | COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) 89 | return 0 90 | fi 91 | case "${prev}" in 92 | *) 93 | COMPREPLY=() 94 | ;; 95 | esac 96 | COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) 97 | return 0 98 | ;; 99 | code__minimap__help__completion) 100 | opts="" 101 | if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then 102 | COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) 103 | return 0 104 | fi 105 | case "${prev}" in 106 | *) 107 | COMPREPLY=() 108 | ;; 109 | esac 110 | COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) 111 | return 0 112 | ;; 113 | code__minimap__help__help) 114 | opts="" 115 | if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then 116 | COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) 117 | return 0 118 | fi 119 | case "${prev}" in 120 | *) 121 | COMPREPLY=() 122 | ;; 123 | esac 124 | COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) 125 | return 0 126 | ;; 127 | esac 128 | } 129 | 130 | if [[ "${BASH_VERSINFO[0]}" -eq 4 && "${BASH_VERSINFO[1]}" -ge 4 || "${BASH_VERSINFO[0]}" -gt 4 ]]; then 131 | complete -F _code-minimap -o nosort -o bashdefault -o default code-minimap 132 | else 133 | complete -F _code-minimap -o bashdefault -o default code-minimap 134 | fi 135 | -------------------------------------------------------------------------------- /completions/elvish/code-minimap.elv: -------------------------------------------------------------------------------- 1 | 2 | use builtin; 3 | use str; 4 | 5 | set edit:completion:arg-completer[code-minimap] = {|@words| 6 | fn spaces {|n| 7 | builtin:repeat $n ' ' | str:join '' 8 | } 9 | fn cand {|text desc| 10 | edit:complex-candidate $text &display=$text' '(spaces (- 14 (wcswidth $text)))$desc 11 | } 12 | var command = 'code-minimap' 13 | for word $words[1..-1] { 14 | if (str:has-prefix $word '-') { 15 | break 16 | } 17 | set command = $command';'$word 18 | } 19 | var completions = [ 20 | &'code-minimap'= { 21 | cand -H 'Specify horizontal scale factor' 22 | cand --horizontal-scale 'Specify horizontal scale factor' 23 | cand -V 'Specify vertical scale factor' 24 | cand --vertical-scale 'Specify vertical scale factor' 25 | cand --padding 'Specify padding width' 26 | cand --encoding 'Specify input encoding' 27 | cand --version 'Print version' 28 | cand -h 'Print help' 29 | cand --help 'Print help' 30 | cand completion 'Generate shell completion file' 31 | cand help 'Print this message or the help of the given subcommand(s)' 32 | } 33 | &'code-minimap;completion'= { 34 | cand -h 'Print help' 35 | cand --help 'Print help' 36 | } 37 | &'code-minimap;help'= { 38 | cand completion 'Generate shell completion file' 39 | cand help 'Print this message or the help of the given subcommand(s)' 40 | } 41 | &'code-minimap;help;completion'= { 42 | } 43 | &'code-minimap;help;help'= { 44 | } 45 | ] 46 | $completions[$command] 47 | } 48 | -------------------------------------------------------------------------------- /completions/fish/code-minimap.fish: -------------------------------------------------------------------------------- 1 | # Print an optspec for argparse to handle cmd's options that are independent of any subcommand. 2 | function __fish_code_minimap_global_optspecs 3 | string join \n H/horizontal-scale= V/vertical-scale= padding= encoding= version h/help 4 | end 5 | 6 | function __fish_code_minimap_needs_command 7 | # Figure out if the current invocation already has a command. 8 | set -l cmd (commandline -opc) 9 | set -e cmd[1] 10 | argparse -s (__fish_code_minimap_global_optspecs) -- $cmd 2>/dev/null 11 | or return 12 | if set -q argv[1] 13 | # Also print the command, so this can be used to figure out what it is. 14 | echo $argv[1] 15 | return 1 16 | end 17 | return 0 18 | end 19 | 20 | function __fish_code_minimap_using_subcommand 21 | set -l cmd (__fish_code_minimap_needs_command) 22 | test -z "$cmd" 23 | and return 1 24 | contains -- $cmd[1] $argv 25 | end 26 | 27 | complete -c code-minimap -n "__fish_code_minimap_needs_command" -s H -l horizontal-scale -d 'Specify horizontal scale factor' -r 28 | complete -c code-minimap -n "__fish_code_minimap_needs_command" -s V -l vertical-scale -d 'Specify vertical scale factor' -r 29 | complete -c code-minimap -n "__fish_code_minimap_needs_command" -l padding -d 'Specify padding width' -r 30 | complete -c code-minimap -n "__fish_code_minimap_needs_command" -l encoding -d 'Specify input encoding' -r -f -a "{utf8-lossy\t'',utf8\t''}" 31 | complete -c code-minimap -n "__fish_code_minimap_needs_command" -l version -d 'Print version' 32 | complete -c code-minimap -n "__fish_code_minimap_needs_command" -s h -l help -d 'Print help' 33 | complete -c code-minimap -n "__fish_code_minimap_needs_command" -a "completion" -d 'Generate shell completion file' 34 | complete -c code-minimap -n "__fish_code_minimap_needs_command" -a "help" -d 'Print this message or the help of the given subcommand(s)' 35 | complete -c code-minimap -n "__fish_code_minimap_using_subcommand completion" -s h -l help -d 'Print help' 36 | complete -c code-minimap -n "__fish_code_minimap_using_subcommand help; and not __fish_seen_subcommand_from completion help" -f -a "completion" -d 'Generate shell completion file' 37 | complete -c code-minimap -n "__fish_code_minimap_using_subcommand help; and not __fish_seen_subcommand_from completion help" -f -a "help" -d 'Print this message or the help of the given subcommand(s)' 38 | -------------------------------------------------------------------------------- /completions/powershell/_code-minimap.ps1: -------------------------------------------------------------------------------- 1 | 2 | using namespace System.Management.Automation 3 | using namespace System.Management.Automation.Language 4 | 5 | Register-ArgumentCompleter -Native -CommandName 'code-minimap' -ScriptBlock { 6 | param($wordToComplete, $commandAst, $cursorPosition) 7 | 8 | $commandElements = $commandAst.CommandElements 9 | $command = @( 10 | 'code-minimap' 11 | for ($i = 1; $i -lt $commandElements.Count; $i++) { 12 | $element = $commandElements[$i] 13 | if ($element -isnot [StringConstantExpressionAst] -or 14 | $element.StringConstantType -ne [StringConstantType]::BareWord -or 15 | $element.Value.StartsWith('-') -or 16 | $element.Value -eq $wordToComplete) { 17 | break 18 | } 19 | $element.Value 20 | }) -join ';' 21 | 22 | $completions = @(switch ($command) { 23 | 'code-minimap' { 24 | [CompletionResult]::new('-H', '-H ', [CompletionResultType]::ParameterName, 'Specify horizontal scale factor') 25 | [CompletionResult]::new('--horizontal-scale', '--horizontal-scale', [CompletionResultType]::ParameterName, 'Specify horizontal scale factor') 26 | [CompletionResult]::new('-V', '-V ', [CompletionResultType]::ParameterName, 'Specify vertical scale factor') 27 | [CompletionResult]::new('--vertical-scale', '--vertical-scale', [CompletionResultType]::ParameterName, 'Specify vertical scale factor') 28 | [CompletionResult]::new('--padding', '--padding', [CompletionResultType]::ParameterName, 'Specify padding width') 29 | [CompletionResult]::new('--encoding', '--encoding', [CompletionResultType]::ParameterName, 'Specify input encoding') 30 | [CompletionResult]::new('--version', '--version', [CompletionResultType]::ParameterName, 'Print version') 31 | [CompletionResult]::new('-h', '-h', [CompletionResultType]::ParameterName, 'Print help') 32 | [CompletionResult]::new('--help', '--help', [CompletionResultType]::ParameterName, 'Print help') 33 | [CompletionResult]::new('completion', 'completion', [CompletionResultType]::ParameterValue, 'Generate shell completion file') 34 | [CompletionResult]::new('help', 'help', [CompletionResultType]::ParameterValue, 'Print this message or the help of the given subcommand(s)') 35 | break 36 | } 37 | 'code-minimap;completion' { 38 | [CompletionResult]::new('-h', '-h', [CompletionResultType]::ParameterName, 'Print help') 39 | [CompletionResult]::new('--help', '--help', [CompletionResultType]::ParameterName, 'Print help') 40 | break 41 | } 42 | 'code-minimap;help' { 43 | [CompletionResult]::new('completion', 'completion', [CompletionResultType]::ParameterValue, 'Generate shell completion file') 44 | [CompletionResult]::new('help', 'help', [CompletionResultType]::ParameterValue, 'Print this message or the help of the given subcommand(s)') 45 | break 46 | } 47 | 'code-minimap;help;completion' { 48 | break 49 | } 50 | 'code-minimap;help;help' { 51 | break 52 | } 53 | }) 54 | 55 | $completions.Where{ $_.CompletionText -like "$wordToComplete*" } | 56 | Sort-Object -Property ListItemText 57 | } 58 | -------------------------------------------------------------------------------- /completions/zsh/_code-minimap: -------------------------------------------------------------------------------- 1 | #compdef code-minimap 2 | 3 | autoload -U is-at-least 4 | 5 | _code-minimap() { 6 | typeset -A opt_args 7 | typeset -a _arguments_options 8 | local ret=1 9 | 10 | if is-at-least 5.2; then 11 | _arguments_options=(-s -S -C) 12 | else 13 | _arguments_options=(-s -C) 14 | fi 15 | 16 | local context curcontext="$curcontext" state line 17 | _arguments "${_arguments_options[@]}" : \ 18 | '-H+[Specify horizontal scale factor]:HSCALE:_default' \ 19 | '--horizontal-scale=[Specify horizontal scale factor]:HSCALE:_default' \ 20 | '-V+[Specify vertical scale factor]:VSCALE:_default' \ 21 | '--vertical-scale=[Specify vertical scale factor]:VSCALE:_default' \ 22 | '--padding=[Specify padding width]:PADDING:_default' \ 23 | '--encoding=[Specify input encoding]:ENCODING:(utf8-lossy utf8)' \ 24 | '--version[Print version]' \ 25 | '-h[Print help]' \ 26 | '--help[Print help]' \ 27 | '::file -- File to read:_files' \ 28 | ":: :_code-minimap_commands" \ 29 | "*::: :->code-minimap" \ 30 | && ret=0 31 | case $state in 32 | (code-minimap) 33 | words=($line[2] "${words[@]}") 34 | (( CURRENT += 1 )) 35 | curcontext="${curcontext%:*:*}:code-minimap-command-$line[2]:" 36 | case $line[2] in 37 | (completion) 38 | _arguments "${_arguments_options[@]}" : \ 39 | '-h[Print help]' \ 40 | '--help[Print help]' \ 41 | ':shell -- Target shell name:(bash elvish fish powershell zsh)' \ 42 | && ret=0 43 | ;; 44 | (help) 45 | _arguments "${_arguments_options[@]}" : \ 46 | ":: :_code-minimap__help_commands" \ 47 | "*::: :->help" \ 48 | && ret=0 49 | 50 | case $state in 51 | (help) 52 | words=($line[1] "${words[@]}") 53 | (( CURRENT += 1 )) 54 | curcontext="${curcontext%:*:*}:code-minimap-help-command-$line[1]:" 55 | case $line[1] in 56 | (completion) 57 | _arguments "${_arguments_options[@]}" : \ 58 | && ret=0 59 | ;; 60 | (help) 61 | _arguments "${_arguments_options[@]}" : \ 62 | && ret=0 63 | ;; 64 | esac 65 | ;; 66 | esac 67 | ;; 68 | esac 69 | ;; 70 | esac 71 | } 72 | 73 | (( $+functions[_code-minimap_commands] )) || 74 | _code-minimap_commands() { 75 | local commands; commands=( 76 | 'completion:Generate shell completion file' \ 77 | 'help:Print this message or the help of the given subcommand(s)' \ 78 | ) 79 | _describe -t commands 'code-minimap commands' commands "$@" 80 | } 81 | (( $+functions[_code-minimap__completion_commands] )) || 82 | _code-minimap__completion_commands() { 83 | local commands; commands=() 84 | _describe -t commands 'code-minimap completion commands' commands "$@" 85 | } 86 | (( $+functions[_code-minimap__help_commands] )) || 87 | _code-minimap__help_commands() { 88 | local commands; commands=( 89 | 'completion:Generate shell completion file' \ 90 | 'help:Print this message or the help of the given subcommand(s)' \ 91 | ) 92 | _describe -t commands 'code-minimap help commands' commands "$@" 93 | } 94 | (( $+functions[_code-minimap__help__completion_commands] )) || 95 | _code-minimap__help__completion_commands() { 96 | local commands; commands=() 97 | _describe -t commands 'code-minimap help completion commands' commands "$@" 98 | } 99 | (( $+functions[_code-minimap__help__help_commands] )) || 100 | _code-minimap__help__help_commands() { 101 | local commands; commands=() 102 | _describe -t commands 'code-minimap help help commands' commands "$@" 103 | } 104 | 105 | if [ "$funcstack[1]" = "_code-minimap" ]; then 106 | _code-minimap "$@" 107 | else 108 | compdef _code-minimap code-minimap 109 | fi 110 | -------------------------------------------------------------------------------- /examples/simple.rs: -------------------------------------------------------------------------------- 1 | use std::io; 2 | 3 | fn main() { 4 | let stdin = io::stdin(); 5 | code_minimap::print(stdin.lock(), 1.0, 1.0, None).unwrap(); 6 | } 7 | -------------------------------------------------------------------------------- /examples/write_to_string.rs: -------------------------------------------------------------------------------- 1 | use std::io; 2 | 3 | fn main() { 4 | let stdin = io::stdin(); 5 | let minimap = code_minimap::write_to_string(stdin.lock(), 1.0, 1.0, None).unwrap(); 6 | print!("{}", minimap); 7 | } 8 | -------------------------------------------------------------------------------- /src/bin/code-minimap/cli.rs: -------------------------------------------------------------------------------- 1 | use std::path::PathBuf; 2 | 3 | use clap::{ 4 | builder::{ 5 | styling::{AnsiColor, Effects}, 6 | Styles, 7 | }, 8 | ArgAction, 9 | Parser, 10 | ValueEnum, 11 | }; 12 | 13 | use clap_complete::Shell; 14 | 15 | #[derive(Parser)] 16 | #[clap(about, version)] 17 | #[clap(next_line_help = true)] 18 | #[clap(disable_version_flag = true)] 19 | #[clap( 20 | styles(Styles::styled() 21 | .header(AnsiColor::Yellow.on_default() | Effects::BOLD) 22 | .usage(AnsiColor::Yellow.on_default() | Effects::BOLD) 23 | .literal(AnsiColor::Green.on_default() | Effects::BOLD) 24 | .placeholder(AnsiColor::Cyan.on_default()) 25 | ) 26 | )] 27 | pub struct App { 28 | /// File to read. 29 | pub file: Option, 30 | 31 | /// Specify horizontal scale factor. 32 | #[arg(short = 'H', long = "horizontal-scale", default_value = "1.0")] 33 | pub hscale: f64, 34 | 35 | /// Specify vertical scale factor. 36 | #[arg(short = 'V', long = "vertical-scale", default_value = "1.0")] 37 | pub vscale: f64, 38 | 39 | #[arg(long)] 40 | /// Specify padding width. 41 | pub padding: Option, 42 | 43 | /// Specify input encoding. 44 | #[arg(long, value_enum, default_value_t = Encoding::UTF8Lossy, ignore_case = true)] 45 | pub encoding: Encoding, 46 | 47 | /// Subcommand. 48 | #[command(subcommand)] 49 | pub subcommand: Option, 50 | 51 | /// Print version. 52 | #[arg(long, action = ArgAction::Version)] 53 | version: Option, 54 | } 55 | 56 | #[derive(Parser)] 57 | pub enum Subcommand { 58 | /// Generate shell completion file 59 | Completion { 60 | /// Target shell name. 61 | shell: Shell, 62 | }, 63 | } 64 | 65 | #[derive(Parser)] 66 | pub struct CompletionOpt { 67 | /// Target shell name. 68 | pub shell: Shell, 69 | } 70 | 71 | #[derive(Copy, Clone, ValueEnum)] 72 | pub enum Encoding { 73 | UTF8Lossy, 74 | UTF8, 75 | } 76 | -------------------------------------------------------------------------------- /src/bin/code-minimap/main.rs: -------------------------------------------------------------------------------- 1 | mod cli; 2 | 3 | use anyhow::bail; 4 | use clap::{CommandFactory, Parser}; 5 | use cli::{App, Encoding, Subcommand}; 6 | use code_minimap::lossy_reader::LossyReader; 7 | use std::{ 8 | fs::File, 9 | io::{self, BufRead, BufReader, Read}, 10 | process, 11 | }; 12 | 13 | fn main() { 14 | if let Err(e) = try_main() { 15 | if let Some(ioerr) = e.root_cause().downcast_ref::() { 16 | if ioerr.kind() == io::ErrorKind::BrokenPipe { 17 | std::process::exit(0); 18 | } 19 | } 20 | eprintln!("{}: {}", env!("CARGO_PKG_NAME"), e); 21 | process::exit(1) 22 | } 23 | } 24 | 25 | fn try_main() -> anyhow::Result<()> { 26 | let opt: App = App::parse(); 27 | match opt.subcommand { 28 | Some(Subcommand::Completion { shell }) => { 29 | let cmd = &mut App::command(); 30 | clap_complete::generate(shell, cmd, cmd.get_name().to_string(), &mut io::stdout()) 31 | } 32 | None => { 33 | let stdin = io::stdin(); 34 | let reader = match &opt.file { 35 | Some(path) => buf_reader(&opt.encoding, File::open(path)?), 36 | None if atty::is(atty::Stream::Stdin) => bail!("no input file specified (use -h for help)"), 37 | None => buf_reader(&opt.encoding, stdin), 38 | }; 39 | code_minimap::print(reader, opt.hscale, opt.vscale, opt.padding)?; 40 | } 41 | } 42 | Ok(()) 43 | } 44 | 45 | fn buf_reader(encoding: &Encoding, reader: R) -> Box { 46 | match encoding { 47 | Encoding::UTF8 => Box::new(BufReader::new(reader)), 48 | Encoding::UTF8Lossy => Box::new(LossyReader::new(reader)), 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/core.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | io::{self, BufRead, Write}, 3 | ops::Range, 4 | }; 5 | 6 | use itertools::Itertools; 7 | 8 | /// Write minimap to the writer. 9 | pub fn write( 10 | mut writer: impl Write, 11 | reader: impl BufRead, 12 | hscale: f64, 13 | vscale: f64, 14 | padding: Option, 15 | ) -> io::Result<()> { 16 | let mut frame = [0..0, 0..0, 0..0, 0..0]; 17 | reader 18 | .lines() 19 | .map(|line| { 20 | line.map(|line| { 21 | let beg = line.find(|c: char| !c.is_whitespace()).unwrap_or(usize::MAX); 22 | let end = line.rfind(|c: char| !c.is_whitespace()).unwrap_or(0); 23 | (beg, end) 24 | }) 25 | }) 26 | .enumerate() 27 | .map(|(i, line)| (scale(i, vscale), line)) 28 | .chunk_by(|(i, _)| *i) 29 | .into_iter() 30 | .chunks(4) 31 | .into_iter() 32 | .try_for_each(|chunk| { 33 | let mut chunk_size = 0; 34 | for (i, (_, group)) in chunk.enumerate() { 35 | let (beg, end) = group.into_iter().try_fold((usize::MAX, 0), |(beg, end), (_, line)| { 36 | line.map(|(b, e)| (beg.min(b), end.max(e))) 37 | })?; 38 | frame[i] = beg..(end + 1); 39 | chunk_size += 1; 40 | } 41 | frame.iter_mut().skip(chunk_size).for_each(|row| *row = 0..0); 42 | scale_frame(&mut frame, hscale); 43 | write_frame(&mut writer, &frame, padding) 44 | }) 45 | } 46 | 47 | /// Print minimap to the stdout. 48 | /// 49 | /// # Examples 50 | /// 51 | /// Basic usage: 52 | /// 53 | /// ``` 54 | /// use std::{io, io::BufReader}; 55 | /// 56 | /// let stdin = io::stdin(); 57 | /// code_minimap::print(stdin.lock(), 1.0, 1.0, None).unwrap(); 58 | /// ``` 59 | pub fn print(reader: impl BufRead, hscale: f64, vscale: f64, padding: Option) -> io::Result<()> { 60 | write(io::stdout(), reader, hscale, vscale, padding) 61 | } 62 | 63 | /// Write minimap to a string. 64 | /// 65 | /// # Examples 66 | /// 67 | /// Basic usage: 68 | /// 69 | /// ``` 70 | /// use std::{io, io::BufReader}; 71 | /// 72 | /// let stdin = io::stdin(); 73 | /// let s = 74 | /// code_minimap::write_to_string(stdin.lock(), 1.0, 1.0, None).unwrap(); 75 | /// print!("{}", s); 76 | /// ``` 77 | pub fn write_to_string(reader: impl BufRead, hscale: f64, vscale: f64, padding: Option) -> io::Result { 78 | let mut buf = Vec::new(); 79 | write(&mut buf, reader, hscale, vscale, padding)?; 80 | Ok(String::from_utf8(buf).unwrap()) 81 | } 82 | 83 | fn write_frame(mut writer: impl Write, frame: &[Range], padding: Option) -> std::io::Result<()> { 84 | let idx = |pos| { 85 | frame 86 | .iter() 87 | .enumerate() 88 | .fold(0, |acc, (i, x)| if x.contains(&pos) { acc + (1 << i) } else { acc }) 89 | }; 90 | let end = frame.iter().max_by_key(|range| range.end).unwrap().end; 91 | let line: String = (0..end) 92 | .step_by(2) 93 | .map(|i| BRAILLE_MATRIX[(idx(i)) + (idx(i + 1) << 4)]) 94 | .collect(); 95 | match padding { 96 | Some(padding) => writeln!(writer, "{0:<1$}", line, padding), 97 | None => writeln!(writer, "{}", line), 98 | } 99 | } 100 | 101 | fn scale_frame(frame: &mut [Range], factor: f64) { 102 | for x in frame { 103 | *x = scale(x.start, factor)..scale(x.end, factor); 104 | } 105 | } 106 | 107 | fn scale(x: usize, factor: f64) -> usize { 108 | (x as f64 * factor) as usize 109 | } 110 | 111 | #[rustfmt::skip] 112 | const BRAILLE_MATRIX : [char; 256] = [ 113 | '⠀', '⠁', '⠂', '⠃', '⠄', '⠅', '⠆', '⠇', '⡀', '⡁', '⡂', '⡃', '⡄', '⡅', '⡆', '⡇', 114 | '⠈', '⠉', '⠊', '⠋', '⠌', '⠍', '⠎', '⠏', '⡈', '⡉', '⡊', '⡋', '⡌', '⡍', '⡎', '⡏', 115 | '⠐', '⠑', '⠒', '⠓', '⠔', '⠕', '⠖', '⠗', '⡐', '⡑', '⡒', '⡓', '⡔', '⡕', '⡖', '⡗', 116 | '⠘', '⠙', '⠚', '⠛', '⠜', '⠝', '⠞', '⠟', '⡘', '⡙', '⡚', '⡛', '⡜', '⡝', '⡞', '⡟', 117 | '⠠', '⠡', '⠢', '⠣', '⠤', '⠥', '⠦', '⠧', '⡠', '⡡', '⡢', '⡣', '⡤', '⡥', '⡦', '⡧', 118 | '⠨', '⠩', '⠪', '⠫', '⠬', '⠭', '⠮', '⠯', '⡨', '⡩', '⡪', '⡫', '⡬', '⡭', '⡮', '⡯', 119 | '⠰', '⠱', '⠲', '⠳', '⠴', '⠵', '⠶', '⠷', '⡰', '⡱', '⡲', '⡳', '⡴', '⡵', '⡶', '⡷', 120 | '⠸', '⠹', '⠺', '⠻', '⠼', '⠽', '⠾', '⠿', '⡸', '⡹', '⡺', '⡻', '⡼', '⡽', '⡾', '⡿', 121 | '⢀', '⢁', '⢂', '⢃', '⢄', '⢅', '⢆', '⢇', '⣀', '⣁', '⣂', '⣃', '⣄', '⣅', '⣆', '⣇', 122 | '⢈', '⢉', '⢊', '⢋', '⢌', '⢍', '⢎', '⢏', '⣈', '⣉', '⣊', '⣋', '⣌', '⣍', '⣎', '⣏', 123 | '⢐', '⢑', '⢒', '⢓', '⢔', '⢕', '⢖', '⢗', '⣐', '⣑', '⣒', '⣓', '⣔', '⣕', '⣖', '⣗', 124 | '⢘', '⢙', '⢚', '⢛', '⢜', '⢝', '⢞', '⢟', '⣘', '⣙', '⣚', '⣛', '⣜', '⣝', '⣞', '⣟', 125 | '⢠', '⢡', '⢢', '⢣', '⢤', '⢥', '⢦', '⢧', '⣠', '⣡', '⣢', '⣣', '⣤', '⣥', '⣦', '⣧', 126 | '⢨', '⢩', '⢪', '⢫', '⢬', '⢭', '⢮', '⢯', '⣨', '⣩', '⣪', '⣫', '⣬', '⣭', '⣮', '⣯', 127 | '⢰', '⢱', '⢲', '⢳', '⢴', '⢵', '⢶', '⢷', '⣰', '⣱', '⣲', '⣳', '⣴', '⣵', '⣶', '⣷', 128 | '⢸', '⢹', '⢺', '⢻', '⢼', '⢽', '⢾', '⢿', '⣸', '⣹', '⣺', '⣻', '⣼', '⣽', '⣾', '⣿', 129 | ]; 130 | 131 | #[cfg(test)] 132 | mod test { 133 | use rstest::*; 134 | 135 | use super::*; 136 | 137 | #[rstest( 138 | input, 139 | expected, 140 | case("", ""), 141 | case("a", "⠁"), 142 | case("aaaa\nbbbb\ncccc\ndddd", "⣿⣿"), 143 | case("aaa\n aa\n a\n a", "⠙⢇"), 144 | case(" a b c\n d efg \n h i\n jk", "⢐⡛⠿⠭") 145 | )] 146 | fn test_write_to_string(input: &'static str, expected: &str) { 147 | let actual = write_to_string(input.as_bytes(), 1.0, 1.0, None).unwrap(); 148 | assert_eq!(expected, actual.trim()); 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | pub(crate) mod core; 2 | pub mod lossy_reader; 3 | pub use crate::core::*; 4 | -------------------------------------------------------------------------------- /src/lossy_reader.rs: -------------------------------------------------------------------------------- 1 | use std::io::{self, BufRead, BufReader, Read}; 2 | 3 | pub struct LossyReader { 4 | inner: BufReader, 5 | } 6 | 7 | impl LossyReader { 8 | pub fn new(inner: R) -> Self { 9 | Self { inner: BufReader::new(inner) } 10 | } 11 | } 12 | 13 | impl Read for LossyReader { 14 | fn read(&mut self, buf: &mut [u8]) -> io::Result { 15 | self.inner.read(buf) 16 | } 17 | } 18 | 19 | impl BufRead for LossyReader { 20 | fn fill_buf(&mut self) -> io::Result<&[u8]> { 21 | self.inner.fill_buf() 22 | } 23 | 24 | fn consume(&mut self, amt: usize) { 25 | self.inner.consume(amt) 26 | } 27 | 28 | fn read_line(&mut self, buf: &mut String) -> std::io::Result { 29 | let mut bytes = Vec::new(); 30 | let len = self.read_until(b'\n', &mut bytes)?; 31 | buf.push_str(&String::from_utf8_lossy(&bytes)); 32 | Ok(len) 33 | } 34 | } 35 | --------------------------------------------------------------------------------