mirror of
https://github.com/BurntSushi/ripgrep.git
synced 2025-08-14 19:55:47 -07:00
Compare commits
143 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
2d763a9a1b | ||
|
afc820c9e9 | ||
|
b9f2da2782 | ||
|
823255b3fd | ||
|
fcfe98fe58 | ||
|
0434b5034d | ||
|
b004eda8c8 | ||
|
f1b4b182f2 | ||
|
e169881a36 | ||
|
83a4af7cb8 | ||
|
cf91d6e67a | ||
|
0904f55d3e | ||
|
8ead46a3e5 | ||
|
d9744f3b03 | ||
|
a6275648b3 | ||
|
7fc48961ed | ||
|
bd8a7ae793 | ||
|
ff8afcf8aa | ||
|
aebab44e3e | ||
|
ca88b2fd95 | ||
|
57e90533a0 | ||
|
fe07bd7669 | ||
|
95979048c9 | ||
|
5548e538b1 | ||
|
c2f1653ddd | ||
|
78803979c5 | ||
|
85a86eba2b | ||
|
119a58a400 | ||
|
3b7fd442a6 | ||
|
cbc598f245 | ||
|
6dfaec03e8 | ||
|
5fbc4fee64 | ||
|
004370bd16 | ||
|
de4baa1002 | ||
|
163ac157d3 | ||
|
e2362d4d51 | ||
|
d6b59feff8 | ||
|
94305125ef | ||
|
79cbe89deb | ||
|
bf63fe8f25 | ||
|
8bd5950296 | ||
|
6e0539ab91 | ||
|
4649aa9700 | ||
|
c009652e77 | ||
|
b9f7a9ba2b | ||
|
a1960877cf | ||
|
bb0925af91 | ||
|
be117dbafa | ||
|
06dc13ad2d | ||
|
c6c2e69b8f | ||
|
e67c868ddd | ||
|
d33f2e2f70 | ||
|
082edafffa | ||
|
7c8dc332b3 | ||
|
ea961915b5 | ||
|
7943bdfe82 | ||
|
312a7884fc | ||
|
ac02f54c89 | ||
|
24b337b940 | ||
|
a5083f99ce | ||
|
f89cdba5df | ||
|
f7b677d136 | ||
|
3f68a8f3d7 | ||
|
9d738ad0c0 | ||
|
6c5108ed17 | ||
|
e0f1000df6 | ||
|
ea99421ec8 | ||
|
af8c386d5e | ||
|
71d71d2d98 | ||
|
c9ebcbd8ab | ||
|
dec0dc3196 | ||
|
2f0a269f07 | ||
|
0a0893a765 | ||
|
35160a1cdb | ||
|
f1d23c06e3 | ||
|
22b677900f | ||
|
bb6f0f5519 | ||
|
b6ef99ee55 | ||
|
bb8601b2ba | ||
|
02b47b7469 | ||
|
d922b7ac11 | ||
|
2acf25c689 | ||
|
80007698d3 | ||
|
3ad0e83471 | ||
|
eca13f08a2 | ||
|
4f99f82b19 | ||
|
327d74f161 | ||
|
9da0995df4 | ||
|
e9abbc1a02 | ||
|
9bd30e8e48 | ||
|
59212d08d3 | ||
|
6ebebb2aaa | ||
|
e92e2ef813 | ||
|
4a30819302 | ||
|
9b42af96f0 | ||
|
648a65f197 | ||
|
bdf01f46a6 | ||
|
1c775f3a82 | ||
|
e50df40a19 | ||
|
1fa76d2a42 | ||
|
44aa5a417d | ||
|
2c3897585d | ||
|
6e9141a9ca | ||
|
c8e4a84519 | ||
|
f02a50a69d | ||
|
b9c774937f | ||
|
67dd809a80 | ||
|
e0a85678e1 | ||
|
23af5fb043 | ||
|
5dec4b8e37 | ||
|
827082a33a | ||
|
6c2a550e1e | ||
|
8e8fc9c503 | ||
|
2057023dc5 | ||
|
3f2fe0afee | ||
|
56c7ad175a | ||
|
5b7a30846f | ||
|
2a4dba3fbf | ||
|
84d65865e6 | ||
|
d9aaa11873 | ||
|
67ad9917ad | ||
|
daa157b5f9 | ||
|
ca5e294ad6 | ||
|
6c7947b819 | ||
|
9acb4a5405 | ||
|
0096c74c11 | ||
|
8c48355b03 | ||
|
f9b86de963 | ||
|
d23b74975a | ||
|
a5cbdb3dfe | ||
|
b6bac8484e | ||
|
805fa32d18 | ||
|
2d518dd1f9 | ||
|
8575d26179 | ||
|
2e81a7adfe | ||
|
cd5440fb62 | ||
|
2ee690e87a | ||
|
59f86a45d3 | ||
|
2d31af38a2 | ||
|
0da1176e7d | ||
|
eeffcd50b7 | ||
|
625743d7c8 | ||
|
3d0171040a |
1
.github/FUNDING.yml
vendored
Normal file
1
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
github: [BurntSushi]
|
2
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
2
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@@ -24,7 +24,7 @@ body:
|
|||||||
attributes:
|
attributes:
|
||||||
label: What version of ripgrep are you using?
|
label: What version of ripgrep are you using?
|
||||||
description: Enter the output of `rg --version`.
|
description: Enter the output of `rg --version`.
|
||||||
placeholder: ex. ripgrep 13.0.0
|
placeholder: ex. ripgrep 0.2.1
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
|
|
||||||
|
63
.github/workflows/ci.yml
vendored
63
.github/workflows/ci.yml
vendored
@@ -53,7 +53,7 @@ jobs:
|
|||||||
include:
|
include:
|
||||||
- build: pinned
|
- build: pinned
|
||||||
os: ubuntu-latest
|
os: ubuntu-latest
|
||||||
rust: 1.74.0
|
rust: 1.88.0
|
||||||
- build: stable
|
- build: stable
|
||||||
os: ubuntu-latest
|
os: ubuntu-latest
|
||||||
rust: stable
|
rust: stable
|
||||||
@@ -75,6 +75,18 @@ jobs:
|
|||||||
os: ubuntu-latest
|
os: ubuntu-latest
|
||||||
rust: stable
|
rust: stable
|
||||||
target: aarch64-unknown-linux-gnu
|
target: aarch64-unknown-linux-gnu
|
||||||
|
- build: stable-arm-gnueabihf
|
||||||
|
os: ubuntu-latest
|
||||||
|
rust: stable
|
||||||
|
target: armv7-unknown-linux-gnueabihf
|
||||||
|
- build: stable-arm-musleabihf
|
||||||
|
os: ubuntu-latest
|
||||||
|
rust: stable
|
||||||
|
target: armv7-unknown-linux-musleabihf
|
||||||
|
- build: stable-arm-musleabi
|
||||||
|
os: ubuntu-latest
|
||||||
|
rust: stable
|
||||||
|
target: armv7-unknown-linux-musleabi
|
||||||
- build: stable-powerpc64
|
- build: stable-powerpc64
|
||||||
os: ubuntu-latest
|
os: ubuntu-latest
|
||||||
rust: stable
|
rust: stable
|
||||||
@@ -87,11 +99,14 @@ jobs:
|
|||||||
os: macos-latest
|
os: macos-latest
|
||||||
rust: nightly
|
rust: nightly
|
||||||
- build: win-msvc
|
- build: win-msvc
|
||||||
os: windows-2022
|
os: windows-latest
|
||||||
rust: nightly
|
rust: nightly
|
||||||
- build: win-gnu
|
- build: win-gnu
|
||||||
os: windows-2022
|
os: windows-latest
|
||||||
rust: nightly-x86_64-gnu
|
rust: nightly-x86_64-gnu
|
||||||
|
- build: winaarch64-msvc
|
||||||
|
os: windows-11-arm
|
||||||
|
rust: nightly
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
@@ -165,7 +180,7 @@ jobs:
|
|||||||
# 'rg' binary (done in test-complete) with qemu, which is a pain and
|
# 'rg' binary (done in test-complete) with qemu, which is a pain and
|
||||||
# doesn't really gain us much. If shell completion works in one place,
|
# doesn't really gain us much. If shell completion works in one place,
|
||||||
# it probably works everywhere.
|
# it probably works everywhere.
|
||||||
if: matrix.target == '' && matrix.os != 'windows-2022'
|
if: matrix.target == '' && !startsWith(matrix.os, 'windows')
|
||||||
shell: bash
|
shell: bash
|
||||||
run: ci/test-complete
|
run: ci/test-complete
|
||||||
|
|
||||||
@@ -177,6 +192,21 @@ jobs:
|
|||||||
shell: bash
|
shell: bash
|
||||||
run: ${{ env.CARGO }} test --bin rg ${{ env.TARGET_FLAGS }} flags::defs::tests::available_shorts -- --nocapture
|
run: ${{ env.CARGO }} test --bin rg ${{ env.TARGET_FLAGS }} flags::defs::tests::available_shorts -- --nocapture
|
||||||
|
|
||||||
|
# Setup and compile on the wasm32-wasip1 target
|
||||||
|
wasm:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
- name: Install Rust
|
||||||
|
uses: dtolnay/rust-toolchain@master
|
||||||
|
with:
|
||||||
|
toolchain: stable
|
||||||
|
- name: Add wasm32-wasip1 target
|
||||||
|
run: rustup target add wasm32-wasip1
|
||||||
|
- name: Basic build
|
||||||
|
run: cargo build --verbose
|
||||||
|
|
||||||
rustfmt:
|
rustfmt:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
@@ -203,3 +233,28 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
RUSTDOCFLAGS: -D warnings
|
RUSTDOCFLAGS: -D warnings
|
||||||
run: cargo doc --no-deps --document-private-items --workspace
|
run: cargo doc --no-deps --document-private-items --workspace
|
||||||
|
|
||||||
|
fuzz_testing:
|
||||||
|
name: Compile Fuzz Test Targets
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Install required packages (Ubuntu)
|
||||||
|
run: |
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install g++ --yes
|
||||||
|
|
||||||
|
- name: Install Rust
|
||||||
|
uses: dtolnay/rust-toolchain@master
|
||||||
|
with:
|
||||||
|
toolchain: stable
|
||||||
|
|
||||||
|
- name: Install Fuzzer
|
||||||
|
run: cargo install cargo-fuzz
|
||||||
|
working-directory: fuzz
|
||||||
|
|
||||||
|
- name: Verify fuzz targets build
|
||||||
|
run: cargo check
|
||||||
|
working-directory: fuzz
|
||||||
|
56
.github/workflows/release.yml
vendored
56
.github/workflows/release.yml
vendored
@@ -80,6 +80,24 @@ jobs:
|
|||||||
target: aarch64-unknown-linux-gnu
|
target: aarch64-unknown-linux-gnu
|
||||||
strip: aarch64-linux-gnu-strip
|
strip: aarch64-linux-gnu-strip
|
||||||
qemu: qemu-aarch64
|
qemu: qemu-aarch64
|
||||||
|
- build: stable-arm-gnueabihf
|
||||||
|
os: ubuntu-latest
|
||||||
|
rust: stable
|
||||||
|
target: armv7-unknown-linux-gnueabihf
|
||||||
|
strip: arm-linux-gnueabihf-strip
|
||||||
|
qemu: qemu-arm
|
||||||
|
- build: stable-arm-musleabihf
|
||||||
|
os: ubuntu-latest
|
||||||
|
rust: stable
|
||||||
|
target: armv7-unknown-linux-musleabihf
|
||||||
|
strip: arm-linux-musleabihf-strip
|
||||||
|
qemu: qemu-arm
|
||||||
|
- build: stable-arm-musleabi
|
||||||
|
os: ubuntu-latest
|
||||||
|
rust: stable
|
||||||
|
target: armv7-unknown-linux-musleabi
|
||||||
|
strip: arm-linux-musleabi-strip
|
||||||
|
qemu: qemu-arm
|
||||||
- build: stable-powerpc64
|
- build: stable-powerpc64
|
||||||
os: ubuntu-latest
|
os: ubuntu-latest
|
||||||
rust: stable
|
rust: stable
|
||||||
@@ -104,6 +122,10 @@ jobs:
|
|||||||
os: windows-latest
|
os: windows-latest
|
||||||
rust: nightly-x86_64-gnu
|
rust: nightly-x86_64-gnu
|
||||||
target: x86_64-pc-windows-gnu
|
target: x86_64-pc-windows-gnu
|
||||||
|
- build: winaarch64-msvc
|
||||||
|
os: windows-11-arm
|
||||||
|
rust: nightly
|
||||||
|
target: aarch64-pc-windows-msvc
|
||||||
- build: win32-msvc
|
- build: win32-msvc
|
||||||
os: windows-latest
|
os: windows-latest
|
||||||
rust: nightly
|
rust: nightly
|
||||||
@@ -157,7 +179,7 @@ jobs:
|
|||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
${{ env.CARGO }} build --verbose --release --features pcre2 ${{ env.TARGET_FLAGS }}
|
${{ env.CARGO }} build --verbose --release --features pcre2 ${{ env.TARGET_FLAGS }}
|
||||||
if [ "${{ matrix.os }}" = "windows-latest" ]; then
|
if [[ "${{ matrix.os }}" == windows-* ]]; then
|
||||||
bin="target/${{ matrix.target }}/release/rg.exe"
|
bin="target/${{ matrix.target }}/release/rg.exe"
|
||||||
else
|
else
|
||||||
bin="target/${{ matrix.target }}/release/rg"
|
bin="target/${{ matrix.target }}/release/rg"
|
||||||
@@ -175,9 +197,9 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
docker run --rm -v \
|
docker run --rm -v \
|
||||||
"$PWD/target:/target:Z" \
|
"$PWD/target:/target:Z" \
|
||||||
"rustembedded/cross:${{ matrix.target }}" \
|
"ghcr.io/cross-rs/${{ matrix.target }}:main" \
|
||||||
"${{ matrix.strip }}" \
|
"${{ matrix.strip }}" \
|
||||||
"/target/${{ matrix.target }}/release/rg"
|
"/$BIN"
|
||||||
|
|
||||||
- name: Determine archive name
|
- name: Determine archive name
|
||||||
shell: bash
|
shell: bash
|
||||||
@@ -210,37 +232,37 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
docker run --rm -v \
|
docker run --rm -v \
|
||||||
"$PWD/target:/target:Z" \
|
"$PWD/target:/target:Z" \
|
||||||
"rustembedded/cross:${{ matrix.target }}" \
|
"ghcr.io/cross-rs/${{ matrix.target }}:main" \
|
||||||
"${{ matrix.qemu }}" "/$BIN" --version
|
"${{ matrix.qemu }}" "/$BIN" --version
|
||||||
docker run --rm -v \
|
docker run --rm -v \
|
||||||
"$PWD/target:/target:Z" \
|
"$PWD/target:/target:Z" \
|
||||||
"rustembedded/cross:${{ matrix.target }}" \
|
"ghcr.io/cross-rs/${{ matrix.target }}:main" \
|
||||||
"${{ matrix.qemu }}" "/$BIN" \
|
"${{ matrix.qemu }}" "/$BIN" \
|
||||||
--generate complete-bash > "$ARCHIVE/complete/rg.bash"
|
--generate complete-bash > "$ARCHIVE/complete/rg.bash"
|
||||||
docker run --rm -v \
|
docker run --rm -v \
|
||||||
"$PWD/target:/target:Z" \
|
"$PWD/target:/target:Z" \
|
||||||
"rustembedded/cross:${{ matrix.target }}" \
|
"ghcr.io/cross-rs/${{ matrix.target }}:main" \
|
||||||
"${{ matrix.qemu }}" "/$BIN" \
|
"${{ matrix.qemu }}" "/$BIN" \
|
||||||
--generate complete-fish > "$ARCHIVE/complete/rg.fish"
|
--generate complete-fish > "$ARCHIVE/complete/rg.fish"
|
||||||
docker run --rm -v \
|
docker run --rm -v \
|
||||||
"$PWD/target:/target:Z" \
|
"$PWD/target:/target:Z" \
|
||||||
"rustembedded/cross:${{ matrix.target }}" \
|
"ghcr.io/cross-rs/${{ matrix.target }}:main" \
|
||||||
"${{ matrix.qemu }}" "/$BIN" \
|
"${{ matrix.qemu }}" "/$BIN" \
|
||||||
--generate complete-powershell > "$ARCHIVE/complete/_rg.ps1"
|
--generate complete-powershell > "$ARCHIVE/complete/_rg.ps1"
|
||||||
docker run --rm -v \
|
docker run --rm -v \
|
||||||
"$PWD/target:/target:Z" \
|
"$PWD/target:/target:Z" \
|
||||||
"rustembedded/cross:${{ matrix.target }}" \
|
"ghcr.io/cross-rs/${{ matrix.target }}:main" \
|
||||||
"${{ matrix.qemu }}" "/$BIN" \
|
"${{ matrix.qemu }}" "/$BIN" \
|
||||||
--generate complete-zsh > "$ARCHIVE/complete/_rg"
|
--generate complete-zsh > "$ARCHIVE/complete/_rg"
|
||||||
docker run --rm -v \
|
docker run --rm -v \
|
||||||
"$PWD/target:/target:Z" \
|
"$PWD/target:/target:Z" \
|
||||||
"rustembedded/cross:${{ matrix.target }}" \
|
"ghcr.io/cross-rs/${{ matrix.target }}:main" \
|
||||||
"${{ matrix.qemu }}" "/$BIN" \
|
"${{ matrix.qemu }}" "/$BIN" \
|
||||||
--generate man > "$ARCHIVE/doc/rg.1"
|
--generate man > "$ARCHIVE/doc/rg.1"
|
||||||
|
|
||||||
- name: Build archive (Windows)
|
- name: Build archive (Windows)
|
||||||
shell: bash
|
shell: bash
|
||||||
if: matrix.os == 'windows-latest'
|
if: startsWith(matrix.os, 'windows')
|
||||||
run: |
|
run: |
|
||||||
7z a "$ARCHIVE.zip" "$ARCHIVE"
|
7z a "$ARCHIVE.zip" "$ARCHIVE"
|
||||||
certutil -hashfile "$ARCHIVE.zip" SHA256 > "$ARCHIVE.zip.sha256"
|
certutil -hashfile "$ARCHIVE.zip" SHA256 > "$ARCHIVE.zip.sha256"
|
||||||
@@ -249,7 +271,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Build archive (Unix)
|
- name: Build archive (Unix)
|
||||||
shell: bash
|
shell: bash
|
||||||
if: matrix.os != 'windows-latest'
|
if: ${{ !startsWith(matrix.os, 'windows') }}
|
||||||
run: |
|
run: |
|
||||||
tar czf "$ARCHIVE.tar.gz" "$ARCHIVE"
|
tar czf "$ARCHIVE.tar.gz" "$ARCHIVE"
|
||||||
shasum -a 256 "$ARCHIVE.tar.gz" > "$ARCHIVE.tar.gz.sha256"
|
shasum -a 256 "$ARCHIVE.tar.gz" > "$ARCHIVE.tar.gz.sha256"
|
||||||
@@ -332,14 +354,15 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
cargo deb --profile deb --target ${{ env.TARGET }}
|
cargo deb --profile deb --target ${{ env.TARGET }}
|
||||||
version="${{ needs.create-release.outputs.version }}"
|
version="${{ needs.create-release.outputs.version }}"
|
||||||
deb="target/${{ env.TARGET }}/debian/ripgrep_$version-1_amd64.deb"
|
echo "DEB_DIR=target/${{ env.TARGET }}/debian" >> $GITHUB_ENV
|
||||||
echo "DEB=$deb" >> $GITHUB_ENV
|
echo "DEB_NAME=ripgrep_$version-1_amd64.deb" >> $GITHUB_ENV
|
||||||
|
|
||||||
- name: Create sha256 sum of deb file
|
- name: Create sha256 sum of deb file
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
sum="$DEB.sha256"
|
cd "$DEB_DIR"
|
||||||
shasum -a 256 "$DEB" > "$sum"
|
sum="$DEB_NAME.sha256"
|
||||||
|
shasum -a 256 "$DEB_NAME" > "$sum"
|
||||||
echo "SUM=$sum" >> $GITHUB_ENV
|
echo "SUM=$sum" >> $GITHUB_ENV
|
||||||
|
|
||||||
- name: Upload release archive
|
- name: Upload release archive
|
||||||
@@ -347,5 +370,6 @@ jobs:
|
|||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
|
cd "$DEB_DIR"
|
||||||
version="${{ needs.create-release.outputs.version }}"
|
version="${{ needs.create-release.outputs.version }}"
|
||||||
gh release upload "$version" ${{ env.DEB }} ${{ env.SUM }}
|
gh release upload "$version" "$DEB_NAME" "$SUM"
|
||||||
|
131
CHANGELOG.md
131
CHANGELOG.md
@@ -1,9 +1,130 @@
|
|||||||
|
TBD
|
||||||
|
===
|
||||||
|
Unreleased changes. Release notes have not yet been written.
|
||||||
|
|
||||||
|
Performance improvements:
|
||||||
|
|
||||||
|
* [PERF #2865](https://github.com/BurntSushi/ripgrep/pull/2865):
|
||||||
|
Avoid using path canonicalization on Windows when emitting hyperlinks.
|
||||||
|
|
||||||
|
Bug fixes:
|
||||||
|
|
||||||
|
* [BUG #829](https://github.com/BurntSushi/ripgrep/issues/829),
|
||||||
|
[BUG #2731](https://github.com/BurntSushi/ripgrep/issues/2731),
|
||||||
|
[BUG #2747](https://github.com/BurntSushi/ripgrep/issues/2747),
|
||||||
|
[BUG #2778](https://github.com/BurntSushi/ripgrep/issues/2778),
|
||||||
|
[BUG #2836](https://github.com/BurntSushi/ripgrep/issues/2836),
|
||||||
|
[BUG #2933](https://github.com/BurntSushi/ripgrep/pull/2933):
|
||||||
|
Fix bug related to gitignores from parent directories.
|
||||||
|
* [BUG #1332](https://github.com/BurntSushi/ripgrep/issues/1332),
|
||||||
|
[BUG #3001](https://github.com/BurntSushi/ripgrep/issues/3001):
|
||||||
|
Make `rg -vf file` where `file` is empty match everything.
|
||||||
|
* [BUG #2177](https://github.com/BurntSushi/ripgrep/issues/2177):
|
||||||
|
Ignore a UTF-8 BOM marker at the start of `.gitignore` (and similar files).
|
||||||
|
|
||||||
|
Feature enhancements:
|
||||||
|
|
||||||
|
* Many enhancements to the default set of file types available for filtering.
|
||||||
|
* [FEATURE #1872](https://github.com/BurntSushi/ripgrep/issues/1872):
|
||||||
|
Make `-r/--replace` work with `--json`.
|
||||||
|
* [FEATURE #2708](https://github.com/BurntSushi/ripgrep/pull/2708):
|
||||||
|
Completions for the fish shell take ripgrep's config file into account.
|
||||||
|
* [FEATURE #2841](https://github.com/BurntSushi/ripgrep/pull/2841):
|
||||||
|
Add `italic` to the list of available style attributes in `--color`.
|
||||||
|
* [FEATURE #2842](https://github.com/BurntSushi/ripgrep/pull/2842):
|
||||||
|
Directories containing `.jj` are now treated as git repositories.
|
||||||
|
* [FEATURE #2849](https://github.com/BurntSushi/ripgrep/pull/2849):
|
||||||
|
When using multithreading, schedule files to search in order given on CLI.
|
||||||
|
* [FEATURE #2943](https://github.com/BurntSushi/ripgrep/issues/2943):
|
||||||
|
Add `aarch64` release artifacts for Windows.
|
||||||
|
|
||||||
|
|
||||||
|
14.1.1 (2024-09-08)
|
||||||
|
===================
|
||||||
|
This is a minor release with a bug fix for a matching bug. In particular, a bug
|
||||||
|
was found that could cause ripgrep to ignore lines that should match. That is,
|
||||||
|
false negatives. It is difficult to characterize the specific set of regexes
|
||||||
|
in which this occurs as it requires multiple different optimization strategies
|
||||||
|
to collide and produce an incorrect result. But as one reported example, in
|
||||||
|
ripgrep, the regex `(?i:e.x|ex)` does not match `e-x` when it should. (This
|
||||||
|
bug is a result of an inner literal optimization performed in the `grep-regex`
|
||||||
|
crate and not in the `regex` crate.)
|
||||||
|
|
||||||
|
Bug fixes:
|
||||||
|
|
||||||
|
* [BUG #2884](https://github.com/BurntSushi/ripgrep/issues/2884):
|
||||||
|
Fix bug where ripgrep could miss some matches that it should report.
|
||||||
|
|
||||||
|
Miscellaneous:
|
||||||
|
|
||||||
|
* [MISC #2748](https://github.com/BurntSushi/ripgrep/issues/2748):
|
||||||
|
Remove ripgrep's `simd-accel` feature because it was frequently broken.
|
||||||
|
|
||||||
|
|
||||||
|
14.1.0 (2024-01-06)
|
||||||
|
===================
|
||||||
|
This is a minor release with a few small new features and bug fixes. This
|
||||||
|
release contains a bug fix for unbounded memory growth while walking a
|
||||||
|
directory tree. This release also includes improvements to the completions for
|
||||||
|
the `fish` shell, and release binaries for several additional ARM targets.
|
||||||
|
|
||||||
|
Bug fixes:
|
||||||
|
|
||||||
|
* [BUG #2664](https://github.com/BurntSushi/ripgrep/issues/2690):
|
||||||
|
Fix unbounded memory growth in the `ignore` crate.
|
||||||
|
|
||||||
|
Feature enhancements:
|
||||||
|
|
||||||
|
* Added or improved file type filtering for Lean and Meson.
|
||||||
|
* [FEATURE #2684](https://github.com/BurntSushi/ripgrep/issues/2684):
|
||||||
|
Improve completions for the `fish` shell.
|
||||||
|
* [FEATURE #2702](https://github.com/BurntSushi/ripgrep/pull/2702):
|
||||||
|
Add release binaries for `armv7-unknown-linux-gnueabihf`,
|
||||||
|
`armv7-unknown-linux-musleabihf` and `armv7-unknown-linux-musleabi`.
|
||||||
|
|
||||||
|
|
||||||
|
14.0.3 (2023-11-28)
|
||||||
|
===================
|
||||||
|
This is a patch release with a bug fix for the `--sortr` flag.
|
||||||
|
|
||||||
|
Bug fixes:
|
||||||
|
|
||||||
|
* [BUG #2664](https://github.com/BurntSushi/ripgrep/issues/2664):
|
||||||
|
Fix `--sortr=path`. I left a `todo!()` in the source. Oof.
|
||||||
|
|
||||||
|
|
||||||
|
14.0.2 (2023-11-27)
|
||||||
|
===================
|
||||||
|
This is a patch release with a few small bug fixes.
|
||||||
|
|
||||||
|
Bug fixes:
|
||||||
|
|
||||||
|
* [BUG #2654](https://github.com/BurntSushi/ripgrep/issues/2654):
|
||||||
|
Fix `deb` release sha256 sum file.
|
||||||
|
* [BUG #2658](https://github.com/BurntSushi/ripgrep/issues/2658):
|
||||||
|
Fix partial regression in the behavior of `--null-data --line-regexp`.
|
||||||
|
* [BUG #2659](https://github.com/BurntSushi/ripgrep/issues/2659):
|
||||||
|
Fix Fish shell completions.
|
||||||
|
* [BUG #2662](https://github.com/BurntSushi/ripgrep/issues/2662):
|
||||||
|
Fix typo in documentation for `-i/--ignore-case`.
|
||||||
|
|
||||||
|
|
||||||
|
14.0.1 (2023-11-26)
|
||||||
|
===================
|
||||||
|
This a patch release meant to fix `cargo install ripgrep` on Windows.
|
||||||
|
|
||||||
|
Bug fixes:
|
||||||
|
|
||||||
|
* [BUG #2653](https://github.com/BurntSushi/ripgrep/issues/2653):
|
||||||
|
Include `pkg/windows/Manifest.xml` in crate package.
|
||||||
|
|
||||||
|
|
||||||
14.0.0 (2023-11-26)
|
14.0.0 (2023-11-26)
|
||||||
===================
|
===================
|
||||||
ripgrep 14 is a new major version release of ripgrep that has some new
|
ripgrep 14 is a new major version release of ripgrep that has some new
|
||||||
features, performance improvements and a lot of bug fixes.
|
features, performance improvements and a lot of bug fixes.
|
||||||
|
|
||||||
The headling feature in this release is hyperlink support. In this release,
|
The headlining feature in this release is hyperlink support. In this release,
|
||||||
they are an opt-in feature but may change to an opt-out feature in the future.
|
they are an opt-in feature but may change to an opt-out feature in the future.
|
||||||
To enable them, try passing `--hyperlink-format default`. If you use [VS Code],
|
To enable them, try passing `--hyperlink-format default`. If you use [VS Code],
|
||||||
then try passing `--hyperlink-format vscode`. Please [report your experience
|
then try passing `--hyperlink-format vscode`. Please [report your experience
|
||||||
@@ -12,10 +133,10 @@ with hyperlinks][report-hyperlinks], positive or negative.
|
|||||||
[VS Code]: https://code.visualstudio.com/
|
[VS Code]: https://code.visualstudio.com/
|
||||||
[report-hyperlinks]: https://github.com/BurntSushi/ripgrep/discussions/2611
|
[report-hyperlinks]: https://github.com/BurntSushi/ripgrep/discussions/2611
|
||||||
|
|
||||||
Another headling development in this release is that it contains a rewrite of
|
Another headlining development in this release is that it contains a rewrite
|
||||||
its regex engine. You generally shouldn't notice any changes, except for some
|
of its regex engine. You generally shouldn't notice any changes, except for
|
||||||
searches may get faster. You can read more about the [regex engine rewrite on
|
some searches may get faster. You can read more about the [regex engine rewrite
|
||||||
my blog][regex-internals]. Please [report your performance improvements or
|
on my blog][regex-internals]. Please [report your performance improvements or
|
||||||
regressions that you notice][report-perf].
|
regressions that you notice][report-perf].
|
||||||
|
|
||||||
[report-perf]: https://github.com/BurntSushi/ripgrep/discussions/2652
|
[report-perf]: https://github.com/BurntSushi/ripgrep/discussions/2652
|
||||||
|
410
Cargo.lock
generated
410
Cargo.lock
generated
@@ -4,30 +4,39 @@ version = 3
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "aho-corasick"
|
name = "aho-corasick"
|
||||||
version = "1.1.2"
|
version = "1.1.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0"
|
checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anyhow"
|
name = "anyhow"
|
||||||
version = "1.0.75"
|
version = "1.0.98"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6"
|
checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "autocfg"
|
name = "arbitrary"
|
||||||
version = "1.1.0"
|
version = "1.4.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223"
|
||||||
|
dependencies = [
|
||||||
|
"derive_arbitrary",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitflags"
|
||||||
|
version = "2.9.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bstr"
|
name = "bstr"
|
||||||
version = "1.8.0"
|
version = "1.12.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "542f33a8835a0884b006a0c3df3dadd99c0c3f296ed26c2fdc8028e01ad6230c"
|
checksum = "234113d19d0d7d613b40e86fb654acf958910802bcceab913a4f9e7cda03b1a4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"memchr",
|
"memchr",
|
||||||
"regex-automata",
|
"regex-automata",
|
||||||
@@ -36,71 +45,73 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
version = "1.0.83"
|
version = "1.2.28"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0"
|
checksum = "4ad45f4f74e4e20eaa392913b7b33a7091c87e59628f4dd27888205ad888843c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"jobserver",
|
"jobserver",
|
||||||
"libc",
|
"libc",
|
||||||
|
"shlex",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cfg-if"
|
name = "cfg-if"
|
||||||
version = "1.0.0"
|
version = "1.0.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crossbeam-channel"
|
name = "crossbeam-channel"
|
||||||
version = "0.5.8"
|
version = "0.5.15"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200"
|
checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
|
||||||
"crossbeam-utils",
|
"crossbeam-utils",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crossbeam-deque"
|
name = "crossbeam-deque"
|
||||||
version = "0.8.3"
|
version = "0.8.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef"
|
checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
|
||||||
"crossbeam-epoch",
|
"crossbeam-epoch",
|
||||||
"crossbeam-utils",
|
"crossbeam-utils",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crossbeam-epoch"
|
name = "crossbeam-epoch"
|
||||||
version = "0.9.15"
|
version = "0.9.18"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7"
|
checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg",
|
|
||||||
"cfg-if",
|
|
||||||
"crossbeam-utils",
|
"crossbeam-utils",
|
||||||
"memoffset",
|
|
||||||
"scopeguard",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crossbeam-utils"
|
name = "crossbeam-utils"
|
||||||
version = "0.8.16"
|
version = "0.8.21"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294"
|
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "derive_arbitrary"
|
||||||
|
version = "1.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "30542c1ad912e0e3d22a1935c290e12e8a29d704a420177a31faad4a601a0800"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "encoding_rs"
|
name = "encoding_rs"
|
||||||
version = "0.8.33"
|
version = "0.8.35"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1"
|
checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"packed_simd",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -113,16 +124,29 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "glob"
|
name = "getrandom"
|
||||||
version = "0.3.1"
|
version = "0.3.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
|
checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
"r-efi",
|
||||||
|
"wasi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "glob"
|
||||||
|
version = "0.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "globset"
|
name = "globset"
|
||||||
version = "0.4.14"
|
version = "0.4.16"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aho-corasick",
|
"aho-corasick",
|
||||||
|
"arbitrary",
|
||||||
"bstr",
|
"bstr",
|
||||||
"glob",
|
"glob",
|
||||||
"log",
|
"log",
|
||||||
@@ -134,7 +158,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "grep"
|
name = "grep"
|
||||||
version = "0.2.13"
|
version = "0.3.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"grep-cli",
|
"grep-cli",
|
||||||
"grep-matcher",
|
"grep-matcher",
|
||||||
@@ -148,7 +172,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "grep-cli"
|
name = "grep-cli"
|
||||||
version = "0.1.10"
|
version = "0.1.11"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bstr",
|
"bstr",
|
||||||
"globset",
|
"globset",
|
||||||
@@ -168,7 +192,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "grep-pcre2"
|
name = "grep-pcre2"
|
||||||
version = "0.1.7"
|
version = "0.1.8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"grep-matcher",
|
"grep-matcher",
|
||||||
"log",
|
"log",
|
||||||
@@ -177,7 +201,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "grep-printer"
|
name = "grep-printer"
|
||||||
version = "0.1.7"
|
version = "0.2.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bstr",
|
"bstr",
|
||||||
"grep-matcher",
|
"grep-matcher",
|
||||||
@@ -191,7 +215,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "grep-regex"
|
name = "grep-regex"
|
||||||
version = "0.1.12"
|
version = "0.1.13"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bstr",
|
"bstr",
|
||||||
"grep-matcher",
|
"grep-matcher",
|
||||||
@@ -202,7 +226,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "grep-searcher"
|
name = "grep-searcher"
|
||||||
version = "0.1.12"
|
version = "0.1.14"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bstr",
|
"bstr",
|
||||||
"encoding_rs",
|
"encoding_rs",
|
||||||
@@ -217,7 +241,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ignore"
|
name = "ignore"
|
||||||
version = "0.4.21"
|
version = "0.4.23"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bstr",
|
"bstr",
|
||||||
"crossbeam-channel",
|
"crossbeam-channel",
|
||||||
@@ -233,112 +257,58 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itoa"
|
name = "itoa"
|
||||||
version = "1.0.9"
|
version = "1.0.15"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38"
|
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "jemalloc-sys"
|
|
||||||
version = "0.5.4+5.3.0-patched"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ac6c1946e1cea1788cbfde01c993b52a10e2da07f4bac608228d1bed20bfebf2"
|
|
||||||
dependencies = [
|
|
||||||
"cc",
|
|
||||||
"libc",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "jemallocator"
|
|
||||||
version = "0.5.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a0de374a9f8e63150e6f5e8a60cc14c668226d7a347d8aee1a45766e3c4dd3bc"
|
|
||||||
dependencies = [
|
|
||||||
"jemalloc-sys",
|
|
||||||
"libc",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "jobserver"
|
name = "jobserver"
|
||||||
version = "0.1.27"
|
version = "0.1.33"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8c37f63953c4c63420ed5fd3d6d398c719489b9f872b9fa683262f8edd363c7d"
|
checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"getrandom",
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lexopt"
|
name = "lexopt"
|
||||||
version = "0.3.0"
|
version = "0.3.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "baff4b617f7df3d896f97fe922b64817f6cd9a756bb81d40f8883f2f66dcb401"
|
checksum = "9fa0e2a1fcbe2f6be6c42e342259976206b383122fc152e872795338b5a3f3a7"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.150"
|
version = "0.2.174"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c"
|
checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "libm"
|
|
||||||
version = "0.2.8"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "log"
|
name = "log"
|
||||||
version = "0.4.20"
|
version = "0.4.27"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
|
checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "memchr"
|
name = "memchr"
|
||||||
version = "2.6.4"
|
version = "2.7.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167"
|
checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "memmap2"
|
name = "memmap2"
|
||||||
version = "0.9.0"
|
version = "0.9.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "deaba38d7abf1d4cca21cc89e932e542ba2b9258664d2a9ef0e61512039c9375"
|
checksum = "fd3f7eed9d3848f8b98834af67102b720745c4ec028fcd0aa0239277e7de374f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "memoffset"
|
|
||||||
version = "0.9.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c"
|
|
||||||
dependencies = [
|
|
||||||
"autocfg",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "num-traits"
|
|
||||||
version = "0.2.17"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c"
|
|
||||||
dependencies = [
|
|
||||||
"autocfg",
|
|
||||||
"libm",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "packed_simd"
|
|
||||||
version = "0.3.9"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "1f9f08af0c877571712e2e3e686ad79efad9657dbf0f7c3c8ba943ff6c38932d"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
"num-traits",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pcre2"
|
name = "pcre2"
|
||||||
version = "0.2.6"
|
version = "0.2.9"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4c9d53a8ea5fc3d3568d3de4bebc12606fd0eb8234c602576f1f1ee4880488a7"
|
checksum = "3be55c43ac18044541d58d897e8f4c55157218428953ebd39d86df3ba0286b2b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"log",
|
"log",
|
||||||
@@ -347,9 +317,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pcre2-sys"
|
name = "pcre2-sys"
|
||||||
version = "0.2.7"
|
version = "0.2.9"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8f8f5556f23cf2c0b481949fdfc19a7cd9b27ddcb00ef3477b0f4935cbdaedf2"
|
checksum = "550f5d18fb1b90c20b87e161852c10cde77858c3900c5059b5ad2a1449f11d8a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cc",
|
"cc",
|
||||||
"libc",
|
"libc",
|
||||||
@@ -358,33 +328,39 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pkg-config"
|
name = "pkg-config"
|
||||||
version = "0.3.27"
|
version = "0.3.32"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964"
|
checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.70"
|
version = "1.0.95"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b"
|
checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quote"
|
name = "quote"
|
||||||
version = "1.0.33"
|
version = "1.0.40"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
|
checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex"
|
name = "r-efi"
|
||||||
version = "1.10.2"
|
version = "5.3.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343"
|
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex"
|
||||||
|
version = "1.11.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aho-corasick",
|
"aho-corasick",
|
||||||
"memchr",
|
"memchr",
|
||||||
@@ -394,9 +370,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex-automata"
|
name = "regex-automata"
|
||||||
version = "0.4.3"
|
version = "0.4.9"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f"
|
checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aho-corasick",
|
"aho-corasick",
|
||||||
"memchr",
|
"memchr",
|
||||||
@@ -405,19 +381,18 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex-syntax"
|
name = "regex-syntax"
|
||||||
version = "0.8.2"
|
version = "0.8.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
|
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ripgrep"
|
name = "ripgrep"
|
||||||
version = "14.0.0"
|
version = "14.1.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bstr",
|
"bstr",
|
||||||
"grep",
|
"grep",
|
||||||
"ignore",
|
"ignore",
|
||||||
"jemallocator",
|
|
||||||
"lexopt",
|
"lexopt",
|
||||||
"log",
|
"log",
|
||||||
"serde",
|
"serde",
|
||||||
@@ -425,14 +400,15 @@ dependencies = [
|
|||||||
"serde_json",
|
"serde_json",
|
||||||
"termcolor",
|
"termcolor",
|
||||||
"textwrap",
|
"textwrap",
|
||||||
|
"tikv-jemallocator",
|
||||||
"walkdir",
|
"walkdir",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ryu"
|
name = "ryu"
|
||||||
version = "1.0.15"
|
version = "1.0.20"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741"
|
checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "same-file"
|
name = "same-file"
|
||||||
@@ -443,26 +419,20 @@ dependencies = [
|
|||||||
"winapi-util",
|
"winapi-util",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "scopeguard"
|
|
||||||
version = "1.2.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.193"
|
version = "1.0.219"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89"
|
checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_derive"
|
name = "serde_derive"
|
||||||
version = "1.0.193"
|
version = "1.0.219"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3"
|
checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -471,20 +441,27 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_json"
|
name = "serde_json"
|
||||||
version = "1.0.108"
|
version = "1.0.140"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b"
|
checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"itoa",
|
"itoa",
|
||||||
|
"memchr",
|
||||||
"ryu",
|
"ryu",
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "shlex"
|
||||||
version = "2.0.39"
|
version = "1.3.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a"
|
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "2.0.104"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -493,62 +470,151 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "termcolor"
|
name = "termcolor"
|
||||||
version = "1.4.0"
|
version = "1.4.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ff1bc3d3f05aff0403e8ac0d92ced918ec05b666a43f83297ccef5bea8a3d449"
|
checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"winapi-util",
|
"winapi-util",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "textwrap"
|
name = "textwrap"
|
||||||
version = "0.16.0"
|
version = "0.16.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d"
|
checksum = "c13547615a44dc9c452a8a534638acdf07120d4b6847c8178705da06306a3057"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tikv-jemalloc-sys"
|
||||||
|
version = "0.6.0+5.3.0-1-ge13ca993e8ccb9ba9847cc330696e02839f328f7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cd3c60906412afa9c2b5b5a48ca6a5abe5736aec9eb48ad05037a677e52e4e2d"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tikv-jemallocator"
|
||||||
|
version = "0.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4cec5ff18518d81584f477e9bfdf957f5bb0979b0bac3af4ca30b5b3ae2d2865"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"tikv-jemalloc-sys",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-ident"
|
name = "unicode-ident"
|
||||||
version = "1.0.12"
|
version = "1.0.18"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "walkdir"
|
name = "walkdir"
|
||||||
version = "2.4.0"
|
version = "2.5.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee"
|
checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"same-file",
|
"same-file",
|
||||||
"winapi-util",
|
"winapi-util",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winapi"
|
name = "wasi"
|
||||||
version = "0.3.9"
|
version = "0.14.2+wasi-0.2.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"winapi-i686-pc-windows-gnu",
|
"wit-bindgen-rt",
|
||||||
"winapi-x86_64-pc-windows-gnu",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "winapi-i686-pc-windows-gnu"
|
|
||||||
version = "0.4.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winapi-util"
|
name = "winapi-util"
|
||||||
version = "0.1.6"
|
version = "0.1.9"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596"
|
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"winapi",
|
"windows-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winapi-x86_64-pc-windows-gnu"
|
name = "windows-sys"
|
||||||
version = "0.4.0"
|
version = "0.59.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
|
||||||
|
dependencies = [
|
||||||
|
"windows-targets",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-targets"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
|
||||||
|
dependencies = [
|
||||||
|
"windows_aarch64_gnullvm",
|
||||||
|
"windows_aarch64_msvc",
|
||||||
|
"windows_i686_gnu",
|
||||||
|
"windows_i686_gnullvm",
|
||||||
|
"windows_i686_msvc",
|
||||||
|
"windows_x86_64_gnu",
|
||||||
|
"windows_x86_64_gnullvm",
|
||||||
|
"windows_x86_64_msvc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_gnullvm"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_msvc"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_gnu"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_gnullvm"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_msvc"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnu"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnullvm"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_msvc"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wit-bindgen-rt"
|
||||||
|
version = "0.39.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
]
|
||||||
|
28
Cargo.toml
28
Cargo.toml
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "ripgrep"
|
name = "ripgrep"
|
||||||
version = "14.0.0" #:version
|
version = "14.1.1" #:version
|
||||||
authors = ["Andrew Gallant <jamslam@gmail.com>"]
|
authors = ["Andrew Gallant <jamslam@gmail.com>"]
|
||||||
description = """
|
description = """
|
||||||
ripgrep is a line-oriented search tool that recursively searches the current
|
ripgrep is a line-oriented search tool that recursively searches the current
|
||||||
@@ -17,14 +17,15 @@ exclude = [
|
|||||||
"HomebrewFormula",
|
"HomebrewFormula",
|
||||||
"/.github/",
|
"/.github/",
|
||||||
"/ci/",
|
"/ci/",
|
||||||
"/pkg/",
|
"/pkg/brew",
|
||||||
"/benchsuite/",
|
"/benchsuite/",
|
||||||
"/scripts/",
|
"/scripts/",
|
||||||
|
"/crates/fuzz",
|
||||||
]
|
]
|
||||||
build = "build.rs"
|
build = "build.rs"
|
||||||
autotests = false
|
autotests = false
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.72"
|
rust-version = "1.88"
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
bench = false
|
bench = false
|
||||||
@@ -51,16 +52,16 @@ members = [
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1.0.75"
|
anyhow = "1.0.75"
|
||||||
bstr = "1.7.0"
|
bstr = "1.7.0"
|
||||||
grep = { version = "0.2.13", path = "crates/grep" }
|
grep = { version = "0.3.2", path = "crates/grep" }
|
||||||
ignore = { version = "0.4.21", path = "crates/ignore" }
|
ignore = { version = "0.4.23", path = "crates/ignore" }
|
||||||
lexopt = "0.3.0"
|
lexopt = "0.3.0"
|
||||||
log = "0.4.5"
|
log = "0.4.5"
|
||||||
serde_json = "1.0.23"
|
serde_json = "1.0.23"
|
||||||
termcolor = "1.1.0"
|
termcolor = "1.1.0"
|
||||||
textwrap = { version = "0.16.0", default-features = false }
|
textwrap = { version = "0.16.0", default-features = false }
|
||||||
|
|
||||||
[target.'cfg(all(target_env = "musl", target_pointer_width = "64"))'.dependencies.jemallocator]
|
[target.'cfg(all(target_env = "musl", target_pointer_width = "64"))'.dependencies.tikv-jemallocator]
|
||||||
version = "0.5.0"
|
version = "0.6.0"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
serde = "1.0.77"
|
serde = "1.0.77"
|
||||||
@@ -68,12 +69,23 @@ serde_derive = "1.0.77"
|
|||||||
walkdir = "2"
|
walkdir = "2"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
simd-accel = ["grep/simd-accel"]
|
|
||||||
pcre2 = ["grep/pcre2"]
|
pcre2 = ["grep/pcre2"]
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
debug = 1
|
debug = 1
|
||||||
|
|
||||||
|
[profile.release-lto]
|
||||||
|
inherits = "release"
|
||||||
|
opt-level = 3
|
||||||
|
debug = "none"
|
||||||
|
strip = "symbols"
|
||||||
|
debug-assertions = false
|
||||||
|
overflow-checks = false
|
||||||
|
lto = "fat"
|
||||||
|
panic = "abort"
|
||||||
|
incremental = false
|
||||||
|
codegen-units = 1
|
||||||
|
|
||||||
# This is the main way to strip binaries in the deb package created by
|
# This is the main way to strip binaries in the deb package created by
|
||||||
# 'cargo deb'. For other release binaries, we (currently) call 'strip'
|
# 'cargo deb'. For other release binaries, we (currently) call 'strip'
|
||||||
# explicitly in the release process.
|
# explicitly in the release process.
|
||||||
|
14
Cross.toml
14
Cross.toml
@@ -1,14 +0,0 @@
|
|||||||
[target.x86_64-unknown-linux-musl]
|
|
||||||
image = "burntsushi/cross:x86_64-unknown-linux-musl"
|
|
||||||
|
|
||||||
[target.i686-unknown-linux-gnu]
|
|
||||||
image = "burntsushi/cross:i686-unknown-linux-gnu"
|
|
||||||
|
|
||||||
[target.aarch64-unknown-linux-gnu]
|
|
||||||
image = "burntsushi/cross:aarch64-unknown-linux-gnu"
|
|
||||||
|
|
||||||
[target.powerpc64-unknown-linux-gnu]
|
|
||||||
image = "burntsushi/cross:powerpc64-unknown-linux-gnu"
|
|
||||||
|
|
||||||
[target.s390x-unknown-linux-gnu]
|
|
||||||
image = "burntsushi/cross:s390x-unknown-linux-gnu"
|
|
29
FAQ.md
29
FAQ.md
@@ -94,7 +94,7 @@ Does ripgrep have support for shell auto-completion?
|
|||||||
|
|
||||||
Yes! If you installed ripgrep through a package manager on a Unix system, then
|
Yes! If you installed ripgrep through a package manager on a Unix system, then
|
||||||
the shell completion files included in the release archive should have been
|
the shell completion files included in the release archive should have been
|
||||||
installed for you automatically. If not, you can generate completes using
|
installed for you automatically. If not, you can generate completions using
|
||||||
ripgrep's command line interface.
|
ripgrep's command line interface.
|
||||||
|
|
||||||
For **bash**:
|
For **bash**:
|
||||||
@@ -113,14 +113,31 @@ $ mkdir -p "$dir"
|
|||||||
$ rg --generate complete-fish > "$dir/rg.fish"
|
$ rg --generate complete-fish > "$dir/rg.fish"
|
||||||
```
|
```
|
||||||
|
|
||||||
For **zsh**:
|
For **zsh**, the recommended approach is:
|
||||||
|
|
||||||
```
|
```zsh
|
||||||
$ dir="$HOME/.zsh-complete"
|
$ dir="$HOME/.zsh-complete"
|
||||||
$ mkdir -p "$dir"
|
$ mkdir -p "$dir"
|
||||||
$ rg --generate complete-zsh > "$dir/_rg"
|
$ rg --generate complete-zsh > "$dir/_rg"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
And then add `$HOME/.zsh-complete` to your `fpath` in, e.g., your
|
||||||
|
`$HOME/.zshrc` file:
|
||||||
|
|
||||||
|
```zsh
|
||||||
|
fpath=($HOME/.zsh-complete $fpath)
|
||||||
|
```
|
||||||
|
|
||||||
|
Or if you'd prefer to load and generate completions at the same time, you can
|
||||||
|
add the following to your `$HOME/.zshrc` file:
|
||||||
|
|
||||||
|
```zsh
|
||||||
|
$ source <(rg --generate complete-zsh)
|
||||||
|
```
|
||||||
|
|
||||||
|
Note though that while this approach is easier to setup, is generally slower
|
||||||
|
than the previous method, and will add more time to loading your shell prompt.
|
||||||
|
|
||||||
For **PowerShell**, create the completions:
|
For **PowerShell**, create the completions:
|
||||||
|
|
||||||
```
|
```
|
||||||
@@ -248,8 +265,8 @@ The `--colors` flag is a bit more complicated. The general format is:
|
|||||||
to bold the output or not).
|
to bold the output or not).
|
||||||
* `{value}` is determined by the value of `{attribute}`. If
|
* `{value}` is determined by the value of `{attribute}`. If
|
||||||
`{attribute}` is `style`, then `{value}` should be one of `nobold`,
|
`{attribute}` is `style`, then `{value}` should be one of `nobold`,
|
||||||
`bold`, `nointense`, `intense`, `nounderline` or `underline`. If
|
`bold`, `nointense`, `intense`, `nounderline`, `underline`, `noitalic` or
|
||||||
`{attribute}` is `fg` or `bg`, then `{value}` should be a color.
|
`italic`. If `{attribute}` is `fg` or `bg`, then `{value}` should be a color.
|
||||||
|
|
||||||
A color is specified by either one of eight of English names, a single 256-bit
|
A color is specified by either one of eight of English names, a single 256-bit
|
||||||
number or an RGB triple (with over 16 million possible values, or "true
|
number or an RGB triple (with over 16 million possible values, or "true
|
||||||
@@ -1038,7 +1055,7 @@ How can I donate to ripgrep or its maintainers?
|
|||||||
|
|
||||||
I welcome [sponsorship](https://github.com/sponsors/BurntSushi/).
|
I welcome [sponsorship](https://github.com/sponsors/BurntSushi/).
|
||||||
|
|
||||||
Or if you'd prefer, donating to a charitably organization that you like would
|
Or if you'd prefer, donating to a charitable organization that you like would
|
||||||
also be most welcome. My favorites are:
|
also be most welcome. My favorites are:
|
||||||
|
|
||||||
* [The Internet Archive](https://archive.org/donate/)
|
* [The Internet Archive](https://archive.org/donate/)
|
||||||
|
122
README.md
122
README.md
@@ -42,7 +42,7 @@ This example searches the entire
|
|||||||
[Linux kernel source tree](https://github.com/BurntSushi/linux)
|
[Linux kernel source tree](https://github.com/BurntSushi/linux)
|
||||||
(after running `make defconfig && make -j8`) for `[A-Z]+_SUSPEND`, where
|
(after running `make defconfig && make -j8`) for `[A-Z]+_SUSPEND`, where
|
||||||
all matches must be words. Timings were collected on a system with an Intel
|
all matches must be words. Timings were collected on a system with an Intel
|
||||||
i7-6900K 3.2 GHz.
|
i9-12900K 5.2 GHz.
|
||||||
|
|
||||||
Please remember that a single benchmark is never enough! See my
|
Please remember that a single benchmark is never enough! See my
|
||||||
[blog post on ripgrep](https://blog.burntsushi.net/ripgrep/)
|
[blog post on ripgrep](https://blog.burntsushi.net/ripgrep/)
|
||||||
@@ -50,13 +50,14 @@ for a very detailed comparison with more benchmarks and analysis.
|
|||||||
|
|
||||||
| Tool | Command | Line count | Time |
|
| Tool | Command | Line count | Time |
|
||||||
| ---- | ------- | ---------- | ---- |
|
| ---- | ------- | ---------- | ---- |
|
||||||
| ripgrep (Unicode) | `rg -n -w '[A-Z]+_SUSPEND'` | 452 | **0.136s** |
|
| ripgrep (Unicode) | `rg -n -w '[A-Z]+_SUSPEND'` | 536 | **0.082s** (1.00x) |
|
||||||
| [git grep](https://www.kernel.org/pub/software/scm/git/docs/git-grep.html) | `git grep -P -n -w '[A-Z]+_SUSPEND'` | 452 | 0.348s |
|
| [hypergrep](https://github.com/p-ranav/hypergrep) | `hgrep -n -w '[A-Z]+_SUSPEND'` | 536 | 0.167s (2.04x) |
|
||||||
| [ugrep (Unicode)](https://github.com/Genivia/ugrep) | `ugrep -r --ignore-files --no-hidden -I -w '[A-Z]+_SUSPEND'` | 452 | 0.506s |
|
| [git grep](https://www.kernel.org/pub/software/scm/git/docs/git-grep.html) | `git grep -P -n -w '[A-Z]+_SUSPEND'` | 536 | 0.273s (3.34x) |
|
||||||
| [The Silver Searcher](https://github.com/ggreer/the_silver_searcher) | `ag -w '[A-Z]+_SUSPEND'` | 452 | 0.654s |
|
| [The Silver Searcher](https://github.com/ggreer/the_silver_searcher) | `ag -w '[A-Z]+_SUSPEND'` | 534 | 0.443s (5.43x) |
|
||||||
| [git grep](https://www.kernel.org/pub/software/scm/git/docs/git-grep.html) | `LC_ALL=C git grep -E -n -w '[A-Z]+_SUSPEND'` | 452 | 1.150s |
|
| [ugrep](https://github.com/Genivia/ugrep) | `ugrep -r --ignore-files --no-hidden -I -w '[A-Z]+_SUSPEND'` | 536 | 0.639s (7.82x) |
|
||||||
| [ack](https://github.com/beyondgrep/ack3) | `ack -w '[A-Z]+_SUSPEND'` | 452 | 4.054s |
|
| [git grep](https://www.kernel.org/pub/software/scm/git/docs/git-grep.html) | `LC_ALL=C git grep -E -n -w '[A-Z]+_SUSPEND'` | 536 | 0.727s (8.91x) |
|
||||||
| [git grep (Unicode)](https://www.kernel.org/pub/software/scm/git/docs/git-grep.html) | `LC_ALL=en_US.UTF-8 git grep -E -n -w '[A-Z]+_SUSPEND'` | 452 | 4.205s |
|
| [git grep (Unicode)](https://www.kernel.org/pub/software/scm/git/docs/git-grep.html) | `LC_ALL=en_US.UTF-8 git grep -E -n -w '[A-Z]+_SUSPEND'` | 536 | 2.670s (32.70x) |
|
||||||
|
| [ack](https://github.com/beyondgrep/ack3) | `ack -w '[A-Z]+_SUSPEND'` | 2677 | 2.935s (35.94x) |
|
||||||
|
|
||||||
Here's another benchmark on the same corpus as above that disregards gitignore
|
Here's another benchmark on the same corpus as above that disregards gitignore
|
||||||
files and searches with a whitelist instead. The corpus is the same as in the
|
files and searches with a whitelist instead. The corpus is the same as in the
|
||||||
@@ -65,24 +66,52 @@ doing equivalent work:
|
|||||||
|
|
||||||
| Tool | Command | Line count | Time |
|
| Tool | Command | Line count | Time |
|
||||||
| ---- | ------- | ---------- | ---- |
|
| ---- | ------- | ---------- | ---- |
|
||||||
| ripgrep | `rg -uuu -tc -n -w '[A-Z]+_SUSPEND'` | 388 | **0.096s** |
|
| ripgrep | `rg -uuu -tc -n -w '[A-Z]+_SUSPEND'` | 447 | **0.063s** (1.00x) |
|
||||||
| [ugrep](https://github.com/Genivia/ugrep) | `ugrep -r -n --include='*.c' --include='*.h' -w '[A-Z]+_SUSPEND'` | 388 | 0.493s |
|
| [ugrep](https://github.com/Genivia/ugrep) | `ugrep -r -n --include='*.c' --include='*.h' -w '[A-Z]+_SUSPEND'` | 447 | 0.607s (9.62x) |
|
||||||
| [GNU grep](https://www.gnu.org/software/grep/) | `egrep -r -n --include='*.c' --include='*.h' -w '[A-Z]+_SUSPEND'` | 388 | 0.806s |
|
| [GNU grep](https://www.gnu.org/software/grep/) | `grep -E -r -n --include='*.c' --include='*.h' -w '[A-Z]+_SUSPEND'` | 447 | 0.674s (10.69x) |
|
||||||
|
|
||||||
And finally, a straight-up comparison between ripgrep, ugrep and GNU grep on a
|
Now we'll move to searching on single large file. Here is a straight-up
|
||||||
single large file cached in memory
|
comparison between ripgrep, ugrep and GNU grep on a file cached in memory
|
||||||
(~13GB, [`OpenSubtitles.raw.en.gz`](http://opus.nlpl.eu/download.php?f=OpenSubtitles/v2018/mono/OpenSubtitles.raw.en.gz)):
|
(~13GB, [`OpenSubtitles.raw.en.gz`](http://opus.nlpl.eu/download.php?f=OpenSubtitles/v2018/mono/OpenSubtitles.raw.en.gz), decompressed):
|
||||||
|
|
||||||
| Tool | Command | Line count | Time |
|
| Tool | Command | Line count | Time |
|
||||||
| ---- | ------- | ---------- | ---- |
|
| ---- | ------- | ---------- | ---- |
|
||||||
| ripgrep | `rg -w 'Sherlock [A-Z]\w+'` | 7882 | **2.769s** |
|
| ripgrep (Unicode) | `rg -w 'Sherlock [A-Z]\w+'` | 7882 | **1.042s** (1.00x) |
|
||||||
| [ugrep](https://github.com/Genivia/ugrep) | `ugrep -w 'Sherlock [A-Z]\w+'` | 7882 | 6.802s |
|
| [ugrep](https://github.com/Genivia/ugrep) | `ugrep -w 'Sherlock [A-Z]\w+'` | 7882 | 1.339s (1.28x) |
|
||||||
| [GNU grep](https://www.gnu.org/software/grep/) | `LC_ALL=en_US.UTF-8 egrep -w 'Sherlock [A-Z]\w+'` | 7882 | 9.027s |
|
| [GNU grep (Unicode)](https://www.gnu.org/software/grep/) | `LC_ALL=en_US.UTF-8 egrep -w 'Sherlock [A-Z]\w+'` | 7882 | 6.577s (6.31x) |
|
||||||
|
|
||||||
In the above benchmark, passing the `-n` flag (for showing line numbers)
|
In the above benchmark, passing the `-n` flag (for showing line numbers)
|
||||||
increases the times to `3.423s` for ripgrep and `13.031s` for GNU grep. ugrep
|
increases the times to `1.664s` for ripgrep and `9.484s` for GNU grep. ugrep
|
||||||
times are unaffected by the presence or absence of `-n`.
|
times are unaffected by the presence or absence of `-n`.
|
||||||
|
|
||||||
|
Beware of performance cliffs though:
|
||||||
|
|
||||||
|
| Tool | Command | Line count | Time |
|
||||||
|
| ---- | ------- | ---------- | ---- |
|
||||||
|
| ripgrep (Unicode) | `rg -w '[A-Z]\w+ Sherlock [A-Z]\w+'` | 485 | **1.053s** (1.00x) |
|
||||||
|
| [GNU grep (Unicode)](https://www.gnu.org/software/grep/) | `LC_ALL=en_US.UTF-8 grep -E -w '[A-Z]\w+ Sherlock [A-Z]\w+'` | 485 | 6.234s (5.92x) |
|
||||||
|
| [ugrep](https://github.com/Genivia/ugrep) | `ugrep -w '[A-Z]\w+ Sherlock [A-Z]\w+'` | 485 | 28.973s (27.51x) |
|
||||||
|
|
||||||
|
And performance can drop precipitously across the board when searching big
|
||||||
|
files for patterns without any opportunities for literal optimizations:
|
||||||
|
|
||||||
|
| Tool | Command | Line count | Time |
|
||||||
|
| ---- | ------- | ---------- | ---- |
|
||||||
|
| ripgrep | `rg '[A-Za-z]{30}'` | 6749 | **15.569s** (1.00x) |
|
||||||
|
| [ugrep](https://github.com/Genivia/ugrep) | `ugrep -E '[A-Za-z]{30}'` | 6749 | 21.857s (1.40x) |
|
||||||
|
| [GNU grep](https://www.gnu.org/software/grep/) | `LC_ALL=C grep -E '[A-Za-z]{30}'` | 6749 | 32.409s (2.08x) |
|
||||||
|
| [GNU grep (Unicode)](https://www.gnu.org/software/grep/) | `LC_ALL=en_US.UTF-8 grep -E '[A-Za-z]{30}'` | 6795 | 8m30s (32.74x) |
|
||||||
|
|
||||||
|
Finally, high match counts also tend to both tank performance and smooth
|
||||||
|
out the differences between tools (because performance is dominated by how
|
||||||
|
quickly one can handle a match and not the algorithm used to detect the match,
|
||||||
|
generally speaking):
|
||||||
|
|
||||||
|
| Tool | Command | Line count | Time |
|
||||||
|
| ---- | ------- | ---------- | ---- |
|
||||||
|
| ripgrep | `rg the` | 83499915 | **6.948s** (1.00x) |
|
||||||
|
| [ugrep](https://github.com/Genivia/ugrep) | `ugrep the` | 83499915 | 11.721s (1.69x) |
|
||||||
|
| [GNU grep](https://www.gnu.org/software/grep/) | `LC_ALL=C grep the` | 83499915 | 15.217s (2.19x) |
|
||||||
|
|
||||||
### Why should I use ripgrep?
|
### Why should I use ripgrep?
|
||||||
|
|
||||||
@@ -109,7 +138,7 @@ times are unaffected by the presence or absence of `-n`.
|
|||||||
backreferences in your patterns, which are not supported in ripgrep's default
|
backreferences in your patterns, which are not supported in ripgrep's default
|
||||||
regex engine. PCRE2 support can be enabled with `-P/--pcre2` (use PCRE2
|
regex engine. PCRE2 support can be enabled with `-P/--pcre2` (use PCRE2
|
||||||
always) or `--auto-hybrid-regex` (use PCRE2 only if needed). An alternative
|
always) or `--auto-hybrid-regex` (use PCRE2 only if needed). An alternative
|
||||||
syntax is provided via the `--engine (default|pcre2|auto-hybrid)` option.
|
syntax is provided via the `--engine (default|pcre2|auto)` option.
|
||||||
* ripgrep has [rudimentary support for replacements](GUIDE.md#replacements),
|
* ripgrep has [rudimentary support for replacements](GUIDE.md#replacements),
|
||||||
which permit rewriting output based on what was matched.
|
which permit rewriting output based on what was matched.
|
||||||
* ripgrep supports [searching files in text encodings](GUIDE.md#file-encoding)
|
* ripgrep supports [searching files in text encodings](GUIDE.md#file-encoding)
|
||||||
@@ -191,6 +220,16 @@ configuration files, passthru, support for searching compressed files,
|
|||||||
multiline search and opt-in fancy regex support via PCRE2.
|
multiline search and opt-in fancy regex support via PCRE2.
|
||||||
|
|
||||||
|
|
||||||
|
### Playground
|
||||||
|
|
||||||
|
If you'd like to try ripgrep before installing, there's an unofficial
|
||||||
|
[playground](https://codapi.org/ripgrep/) and an [interactive
|
||||||
|
tutorial](https://codapi.org/try/ripgrep/).
|
||||||
|
|
||||||
|
If you have any questions about these, please open an issue in the [tutorial
|
||||||
|
repo](https://github.com/nalgeon/tryxinyminutes).
|
||||||
|
|
||||||
|
|
||||||
### Installation
|
### Installation
|
||||||
|
|
||||||
The binary name for ripgrep is `rg`.
|
The binary name for ripgrep is `rg`.
|
||||||
@@ -279,11 +318,17 @@ If you're a **Nix** user, you can install ripgrep from
|
|||||||
$ nix-env --install ripgrep
|
$ nix-env --install ripgrep
|
||||||
```
|
```
|
||||||
|
|
||||||
|
If you're a **Flox** user, you can install ripgrep as follows:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ flox install ripgrep
|
||||||
|
```
|
||||||
|
|
||||||
If you're a **Guix** user, you can install ripgrep from the official
|
If you're a **Guix** user, you can install ripgrep from the official
|
||||||
package collection:
|
package collection:
|
||||||
|
|
||||||
```
|
```
|
||||||
$ sudo guix install ripgrep
|
$ guix install ripgrep
|
||||||
```
|
```
|
||||||
|
|
||||||
If you're a **Debian** user (or a user of a Debian derivative like **Ubuntu**),
|
If you're a **Debian** user (or a user of a Debian derivative like **Ubuntu**),
|
||||||
@@ -291,8 +336,8 @@ then ripgrep can be installed using a binary `.deb` file provided in each
|
|||||||
[ripgrep release](https://github.com/BurntSushi/ripgrep/releases).
|
[ripgrep release](https://github.com/BurntSushi/ripgrep/releases).
|
||||||
|
|
||||||
```
|
```
|
||||||
$ curl -LO https://github.com/BurntSushi/ripgrep/releases/download/13.0.0/ripgrep_13.0.0_amd64.deb
|
$ curl -LO https://github.com/BurntSushi/ripgrep/releases/download/14.1.0/ripgrep_14.1.1-1_amd64.deb
|
||||||
$ sudo dpkg -i ripgrep_13.0.0_amd64.deb
|
$ sudo dpkg -i ripgrep_14.1.1-1_amd64.deb
|
||||||
```
|
```
|
||||||
|
|
||||||
If you run Debian stable, ripgrep is [officially maintained by
|
If you run Debian stable, ripgrep is [officially maintained by
|
||||||
@@ -358,9 +403,16 @@ same port as Haiku x86_64 using the x86 secondary architecture build:
|
|||||||
$ sudo pkgman install ripgrep_x86
|
$ sudo pkgman install ripgrep_x86
|
||||||
```
|
```
|
||||||
|
|
||||||
|
If you're a **Void Linux** user, then you can install ripgrep from the
|
||||||
|
[official repository](https://voidlinux.org/packages/?arch=x86_64&q=ripgrep):
|
||||||
|
|
||||||
|
```
|
||||||
|
$ sudo xbps-install -Syv ripgrep
|
||||||
|
```
|
||||||
|
|
||||||
If you're a **Rust programmer**, ripgrep can be installed with `cargo`.
|
If you're a **Rust programmer**, ripgrep can be installed with `cargo`.
|
||||||
|
|
||||||
* Note that the minimum supported version of Rust for ripgrep is **1.70.0**,
|
* Note that the minimum supported version of Rust for ripgrep is **1.88.0**,
|
||||||
although ripgrep may work with older versions.
|
although ripgrep may work with older versions.
|
||||||
* Note that the binary may be bigger than expected because it contains debug
|
* Note that the binary may be bigger than expected because it contains debug
|
||||||
symbols. This is intentional. To remove debug symbols and therefore reduce
|
symbols. This is intentional. To remove debug symbols and therefore reduce
|
||||||
@@ -383,7 +435,7 @@ $ cargo binstall ripgrep
|
|||||||
|
|
||||||
ripgrep is written in Rust, so you'll need to grab a
|
ripgrep is written in Rust, so you'll need to grab a
|
||||||
[Rust installation](https://www.rust-lang.org/) in order to compile it.
|
[Rust installation](https://www.rust-lang.org/) in order to compile it.
|
||||||
ripgrep compiles with Rust 1.70.0 (stable) or newer. In general, ripgrep tracks
|
ripgrep compiles with Rust 1.88.0 (stable) or newer. In general, ripgrep tracks
|
||||||
the latest stable release of the Rust compiler.
|
the latest stable release of the Rust compiler.
|
||||||
|
|
||||||
To build ripgrep:
|
To build ripgrep:
|
||||||
@@ -396,18 +448,13 @@ $ ./target/release/rg --version
|
|||||||
0.1.3
|
0.1.3
|
||||||
```
|
```
|
||||||
|
|
||||||
If you have a Rust nightly compiler and a recent Intel CPU, then you can enable
|
**NOTE:** In the past, ripgrep supported a `simd-accel` Cargo feature when
|
||||||
additional optional SIMD acceleration like so:
|
using a Rust nightly compiler. This only benefited UTF-16 transcoding.
|
||||||
|
Since it required unstable features, this build mode was prone to breakage.
|
||||||
```
|
Because of that, support for it has been removed. If you want SIMD
|
||||||
RUSTFLAGS="-C target-cpu=native" cargo build --release --features 'simd-accel'
|
optimizations for UTF-16 transcoding, then you'll have to petition the
|
||||||
```
|
[`encoding_rs`](https://github.com/hsivonen/encoding_rs) project to use stable
|
||||||
|
APIs.
|
||||||
The `simd-accel` feature enables SIMD support in certain ripgrep dependencies
|
|
||||||
(responsible for transcoding). They are not necessary to get SIMD optimizations
|
|
||||||
for search; those are enabled automatically. Hopefully, some day, the
|
|
||||||
`simd-accel` feature will similarly become unnecessary. **WARNING:** Currently,
|
|
||||||
enabling this option can increase compilation times dramatically.
|
|
||||||
|
|
||||||
Finally, optional PCRE2 support can be built with ripgrep by enabling the
|
Finally, optional PCRE2 support can be built with ripgrep by enabling the
|
||||||
`pcre2` feature:
|
`pcre2` feature:
|
||||||
@@ -416,9 +463,6 @@ Finally, optional PCRE2 support can be built with ripgrep by enabling the
|
|||||||
$ cargo build --release --features 'pcre2'
|
$ cargo build --release --features 'pcre2'
|
||||||
```
|
```
|
||||||
|
|
||||||
(Tip: use `--features 'pcre2 simd-accel'` to also include compile time SIMD
|
|
||||||
optimizations, which will only work with a nightly compiler.)
|
|
||||||
|
|
||||||
Enabling the PCRE2 feature works with a stable Rust compiler and will
|
Enabling the PCRE2 feature works with a stable Rust compiler and will
|
||||||
attempt to automatically find and link with your system's PCRE2 library via
|
attempt to automatically find and link with your system's PCRE2 library via
|
||||||
`pkg-config`. If one doesn't exist, then ripgrep will build PCRE2 from source
|
`pkg-config`. If one doesn't exist, then ripgrep will build PCRE2 from source
|
||||||
|
@@ -27,6 +27,7 @@
|
|||||||
`cargo update -p ripgrep` so that the `Cargo.lock` is updated. Commit the
|
`cargo update -p ripgrep` so that the `Cargo.lock` is updated. Commit the
|
||||||
changes and create a new signed tag. Alternatively, use
|
changes and create a new signed tag. Alternatively, use
|
||||||
`cargo-up --no-push --no-release Cargo.toml {VERSION}` to automate this.
|
`cargo-up --no-push --no-release Cargo.toml {VERSION}` to automate this.
|
||||||
|
* Run `cargo package` and ensure it succeeds.
|
||||||
* Push changes to GitHub, NOT including the tag. (But do not publish a new
|
* Push changes to GitHub, NOT including the tag. (But do not publish a new
|
||||||
version of ripgrep to crates.io yet.)
|
version of ripgrep to crates.io yet.)
|
||||||
* Once CI for `master` finishes successfully, push the version tag. (Trying to
|
* Once CI for `master` finishes successfully, push the version tag. (Trying to
|
||||||
|
4
build.rs
4
build.rs
@@ -22,7 +22,7 @@ fn set_windows_exe_options() {
|
|||||||
manifest.push(MANIFEST);
|
manifest.push(MANIFEST);
|
||||||
let Some(manifest) = manifest.to_str() else { return };
|
let Some(manifest) = manifest.to_str() else { return };
|
||||||
|
|
||||||
println!("cargo:rerun-if-changed={}", MANIFEST);
|
println!("cargo:rerun-if-changed={MANIFEST}");
|
||||||
// Embed the Windows application manifest file.
|
// Embed the Windows application manifest file.
|
||||||
println!("cargo:rustc-link-arg-bin=rg=/MANIFEST:EMBED");
|
println!("cargo:rustc-link-arg-bin=rg=/MANIFEST:EMBED");
|
||||||
println!("cargo:rustc-link-arg-bin=rg=/MANIFESTINPUT:{manifest}");
|
println!("cargo:rustc-link-arg-bin=rg=/MANIFESTINPUT:{manifest}");
|
||||||
@@ -42,5 +42,5 @@ fn set_git_revision_hash() {
|
|||||||
if rev.is_empty() {
|
if rev.is_empty() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
println!("cargo:rustc-env=RIPGREP_BUILD_GIT_HASH={}", rev);
|
println!("cargo:rustc-env=RIPGREP_BUILD_GIT_HASH={rev}");
|
||||||
}
|
}
|
||||||
|
@@ -1,23 +0,0 @@
|
|||||||
These are Docker images used for cross compilation in CI builds (or locally)
|
|
||||||
via the [Cross](https://github.com/rust-embedded/cross) tool.
|
|
||||||
|
|
||||||
The Cross tool actually provides its own Docker images, and all Docker images
|
|
||||||
in this directory are derived from one of them. We provide our own in order to
|
|
||||||
customize the environment. For example, we need to install compression tools
|
|
||||||
like `xz` so that tests for the `-z/--search-zip` flag are run.
|
|
||||||
|
|
||||||
If you make a change to a Docker image, then you can re-build it. `cd` into the
|
|
||||||
directory containing the `Dockerfile` and run:
|
|
||||||
|
|
||||||
$ cd x86_64-unknown-linux-musl
|
|
||||||
$ ./build
|
|
||||||
|
|
||||||
At this point, subsequent uses of `cross` will now use your built image since
|
|
||||||
Docker prefers local images over remote images. In order to make these changes
|
|
||||||
stick, they need to be pushed to Docker Hub:
|
|
||||||
|
|
||||||
$ docker push burntsushi/cross:x86_64-unknown-linux-musl
|
|
||||||
|
|
||||||
Of course, only I (BurntSushi) can push to that location. To make `cross` use
|
|
||||||
a different location, then edit `Cross.toml` in the root of this repo to use
|
|
||||||
a different image name for the desired target.
|
|
@@ -1,4 +0,0 @@
|
|||||||
FROM rustembedded/cross:aarch64-unknown-linux-gnu
|
|
||||||
|
|
||||||
COPY stage/ubuntu-install-packages /
|
|
||||||
RUN /ubuntu-install-packages
|
|
@@ -1,5 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
|
|
||||||
mkdir -p stage
|
|
||||||
cp ../../ubuntu-install-packages ./stage/
|
|
||||||
docker build -t burntsushi/cross:aarch64-unknown-linux-gnu .
|
|
@@ -1,4 +0,0 @@
|
|||||||
FROM rustembedded/cross:i686-unknown-linux-gnu
|
|
||||||
|
|
||||||
COPY stage/ubuntu-install-packages /
|
|
||||||
RUN /ubuntu-install-packages
|
|
@@ -1,5 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
|
|
||||||
mkdir -p stage
|
|
||||||
cp ../../ubuntu-install-packages ./stage/
|
|
||||||
docker build -t burntsushi/cross:i686-unknown-linux-gnu .
|
|
@@ -1,4 +0,0 @@
|
|||||||
FROM rustembedded/cross:powerpc64-unknown-linux-gnu
|
|
||||||
|
|
||||||
COPY stage/ubuntu-install-packages /
|
|
||||||
RUN /ubuntu-install-packages
|
|
@@ -1,5 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
|
|
||||||
mkdir -p stage
|
|
||||||
cp ../../ubuntu-install-packages ./stage/
|
|
||||||
docker build -t burntsushi/cross:powerpc64-unknown-linux-gnu .
|
|
@@ -1,4 +0,0 @@
|
|||||||
FROM rustembedded/cross:s390x-unknown-linux-gnu
|
|
||||||
|
|
||||||
COPY stage/ubuntu-install-packages /
|
|
||||||
RUN /ubuntu-install-packages
|
|
@@ -1,5 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
|
|
||||||
mkdir -p stage
|
|
||||||
cp ../../ubuntu-install-packages ./stage/
|
|
||||||
docker build -t burntsushi/cross:s390x-unknown-linux-gnu .
|
|
@@ -1,4 +0,0 @@
|
|||||||
FROM rustembedded/cross:x86_64-unknown-linux-musl
|
|
||||||
|
|
||||||
COPY stage/ubuntu-install-packages /
|
|
||||||
RUN /ubuntu-install-packages
|
|
@@ -1,5 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
|
|
||||||
mkdir -p stage
|
|
||||||
cp ../../ubuntu-install-packages ./stage/
|
|
||||||
docker build -t burntsushi/cross:x86_64-unknown-linux-musl .
|
|
@@ -11,4 +11,4 @@ if ! command -V sudo; then
|
|||||||
fi
|
fi
|
||||||
sudo apt-get update
|
sudo apt-get update
|
||||||
sudo apt-get install -y --no-install-recommends \
|
sudo apt-get install -y --no-install-recommends \
|
||||||
zsh xz-utils liblz4-tool musl-tools brotli zstd
|
zsh xz-utils liblz4-tool musl-tools brotli zstd g++
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "grep-cli"
|
name = "grep-cli"
|
||||||
version = "0.1.10" #:version
|
version = "0.1.11" #:version
|
||||||
authors = ["Andrew Gallant <jamslam@gmail.com>"]
|
authors = ["Andrew Gallant <jamslam@gmail.com>"]
|
||||||
description = """
|
description = """
|
||||||
Utilities for search oriented command line applications.
|
Utilities for search oriented command line applications.
|
||||||
@@ -15,7 +15,7 @@ edition = "2021"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bstr = { version = "1.6.2", features = ["std"] }
|
bstr = { version = "1.6.2", features = ["std"] }
|
||||||
globset = { version = "0.4.14", path = "../globset" }
|
globset = { version = "0.4.15", path = "../globset" }
|
||||||
log = "0.4.20"
|
log = "0.4.20"
|
||||||
termcolor = "1.3.0"
|
termcolor = "1.3.0"
|
||||||
|
|
||||||
|
@@ -25,10 +25,10 @@ pub fn hostname() -> io::Result<OsString> {
|
|||||||
}
|
}
|
||||||
#[cfg(not(any(windows, unix)))]
|
#[cfg(not(any(windows, unix)))]
|
||||||
{
|
{
|
||||||
io::Error::new(
|
Err(io::Error::new(
|
||||||
io::ErrorKind::Other,
|
io::ErrorKind::Other,
|
||||||
"hostname could not be found on unsupported platform",
|
"hostname could not be found on unsupported platform",
|
||||||
)
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -178,22 +178,71 @@ pub fn is_readable_stdin() -> bool {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let stdin = std::io::stdin();
|
let stdin = std::io::stdin();
|
||||||
let Ok(fd) = stdin.as_fd().try_clone_to_owned() else { return false };
|
let fd = match stdin.as_fd().try_clone_to_owned() {
|
||||||
|
Ok(fd) => fd,
|
||||||
|
Err(err) => {
|
||||||
|
log::debug!(
|
||||||
|
"for heuristic stdin detection on Unix, \
|
||||||
|
could not clone stdin file descriptor \
|
||||||
|
(thus assuming stdin is not readable): {err}",
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
let file = File::from(fd);
|
let file = File::from(fd);
|
||||||
let Ok(md) = file.metadata() else { return false };
|
let md = match file.metadata() {
|
||||||
|
Ok(md) => md,
|
||||||
|
Err(err) => {
|
||||||
|
log::debug!(
|
||||||
|
"for heuristic stdin detection on Unix, \
|
||||||
|
could not get file metadata for stdin \
|
||||||
|
(thus assuming stdin is not readable): {err}",
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
let ft = md.file_type();
|
let ft = md.file_type();
|
||||||
ft.is_file() || ft.is_fifo() || ft.is_socket()
|
let is_file = ft.is_file();
|
||||||
|
let is_fifo = ft.is_fifo();
|
||||||
|
let is_socket = ft.is_socket();
|
||||||
|
let is_readable = is_file || is_fifo || is_socket;
|
||||||
|
log::debug!(
|
||||||
|
"for heuristic stdin detection on Unix, \
|
||||||
|
found that \
|
||||||
|
is_file={is_file}, is_fifo={is_fifo} and is_socket={is_socket}, \
|
||||||
|
and thus concluded that is_stdin_readable={is_readable}",
|
||||||
|
);
|
||||||
|
is_readable
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
fn imp() -> bool {
|
fn imp() -> bool {
|
||||||
winapi_util::file::typ(winapi_util::HandleRef::stdin())
|
let stdin = winapi_util::HandleRef::stdin();
|
||||||
.map(|t| t.is_disk() || t.is_pipe())
|
let typ = match winapi_util::file::typ(stdin) {
|
||||||
.unwrap_or(false)
|
Ok(typ) => typ,
|
||||||
|
Err(err) => {
|
||||||
|
log::debug!(
|
||||||
|
"for heuristic stdin detection on Windows, \
|
||||||
|
could not get file type of stdin \
|
||||||
|
(thus assuming stdin is not readable): {err}",
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let is_disk = typ.is_disk();
|
||||||
|
let is_pipe = typ.is_pipe();
|
||||||
|
let is_readable = is_disk || is_pipe;
|
||||||
|
log::debug!(
|
||||||
|
"for heuristic stdin detection on Windows, \
|
||||||
|
found that is_disk={is_disk} and is_pipe={is_pipe}, \
|
||||||
|
and thus concluded that is_stdin_readable={is_readable}",
|
||||||
|
);
|
||||||
|
is_readable
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(any(unix, windows)))]
|
#[cfg(not(any(unix, windows)))]
|
||||||
fn imp() -> bool {
|
fn imp() -> bool {
|
||||||
|
log::debug!("on non-{{Unix,Windows}}, assuming stdin is not readable");
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
use std::io::{self, IsTerminal};
|
use std::io::{self, IsTerminal};
|
||||||
|
|
||||||
use termcolor::{self, HyperlinkSpec};
|
use termcolor::HyperlinkSpec;
|
||||||
|
|
||||||
/// A writer that supports coloring with either line or block buffering.
|
/// A writer that supports coloring with either line or block buffering.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
29
crates/core/flags/complete/encodings.sh
Normal file
29
crates/core/flags/complete/encodings.sh
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
# This is impossible to read, but these encodings rarely if ever change, so
|
||||||
|
# it probably does not matter. They are derived from the list given here:
|
||||||
|
# https://encoding.spec.whatwg.org/#concept-encoding-get
|
||||||
|
#
|
||||||
|
# The globbing here works in both fish and zsh (though they expand it in
|
||||||
|
# different orders). It may work in other shells too.
|
||||||
|
|
||||||
|
{{,us-}ascii,arabic,chinese,cyrillic,greek{,8},hebrew,korean}
|
||||||
|
logical visual mac {,cs}macintosh x-mac-{cyrillic,roman,ukrainian}
|
||||||
|
866 ibm{819,866} csibm866
|
||||||
|
big5{,-hkscs} {cn-,cs}big5 x-x-big5
|
||||||
|
cp{819,866,125{0,1,2,3,4,5,6,7,8}} x-cp125{0,1,2,3,4,5,6,7,8}
|
||||||
|
csiso2022{jp,kr} csiso8859{6,8}{e,i}
|
||||||
|
csisolatin{1,2,3,4,5,6,9} csisolatin{arabic,cyrillic,greek,hebrew}
|
||||||
|
ecma-{114,118} asmo-708 elot_928 sun_eu_greek
|
||||||
|
euc-{jp,kr} x-euc-jp cseuckr cseucpkdfmtjapanese
|
||||||
|
{,x-}gbk csiso58gb231280 gb18030 {,cs}gb2312 gb_2312{,-80} hz-gb-2312
|
||||||
|
iso-2022-{cn,cn-ext,jp,kr}
|
||||||
|
iso8859{,-}{1,2,3,4,5,6,7,8,9,10,11,13,14,15}
|
||||||
|
iso-8859-{1,2,3,4,5,6,7,8,9,10,11,{6,8}-{e,i},13,14,15,16} iso_8859-{1,2,3,4,5,6,7,8,9,15}
|
||||||
|
iso_8859-{1,2,6,7}:1987 iso_8859-{3,4,5,8}:1988 iso_8859-9:1989
|
||||||
|
iso-ir-{58,100,101,109,110,126,127,138,144,148,149,157}
|
||||||
|
koi{,8,8-r,8-ru,8-u,8_r} cskoi8r
|
||||||
|
ks_c_5601-{1987,1989} ksc{,_}5691 csksc56011987
|
||||||
|
latin{1,2,3,4,5,6} l{1,2,3,4,5,6,9}
|
||||||
|
shift{-,_}jis csshiftjis {,x-}sjis ms_kanji ms932
|
||||||
|
utf{,-}8 utf-16{,be,le} unicode-1-1-utf-8
|
||||||
|
windows-{31j,874,949,125{0,1,2,3,4,5,6,7,8}} dos-874 tis-620 ansi_x3.4-1968
|
||||||
|
x-user-defined auto none
|
@@ -2,43 +2,72 @@
|
|||||||
Provides completions for ripgrep's CLI for the fish shell.
|
Provides completions for ripgrep's CLI for the fish shell.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use crate::flags::defs::FLAGS;
|
use crate::flags::{defs::FLAGS, CompletionType};
|
||||||
|
|
||||||
const TEMPLATE: &'static str =
|
const TEMPLATE: &'static str = "complete -c rg !SHORT! -l !LONG! -d '!DOC!'";
|
||||||
"complete -c rg -n '__fish_use_subcommand' !SHORT! !LONG! !DOC!\n";
|
const TEMPLATE_NEGATED: &'static str =
|
||||||
const TEMPLATE_CHOICES: &'static str =
|
"complete -c rg -l !NEGATED! -n '__rg_contains_opt !LONG! !SHORT!' -d '!DOC!'\n";
|
||||||
"complete -c rg -n '__fish_use_subcommand' !SHORT! !LONG! !DOC! -r -f -a '!CHOICES!'\n";
|
|
||||||
|
|
||||||
/// Generate completions for Fish.
|
/// Generate completions for Fish.
|
||||||
///
|
///
|
||||||
/// Note that these completions are based on what was produced for ripgrep <=13
|
/// Reference: <https://fishshell.com/docs/current/completions.html>
|
||||||
/// using Clap 2.x. Improvements on this are welcome.
|
|
||||||
pub(crate) fn generate() -> String {
|
pub(crate) fn generate() -> String {
|
||||||
let mut out = String::new();
|
let mut out = String::new();
|
||||||
|
out.push_str(include_str!("prelude.fish"));
|
||||||
|
out.push('\n');
|
||||||
for flag in FLAGS.iter() {
|
for flag in FLAGS.iter() {
|
||||||
let short = match flag.name_short() {
|
let short = match flag.name_short() {
|
||||||
None => "".to_string(),
|
None => "".to_string(),
|
||||||
Some(byte) => format!("-s {}", char::from(byte)),
|
Some(byte) => format!("-s {}", char::from(byte)),
|
||||||
};
|
};
|
||||||
let long = format!("-l '{}'", flag.name_long().replace("'", "\\'"));
|
let long = flag.name_long();
|
||||||
let doc = format!("-d '{}'", flag.doc_short().replace("'", "\\'"));
|
let doc = flag.doc_short().replace("'", "\\'");
|
||||||
let template = if flag.doc_choices().is_empty() {
|
let mut completion = TEMPLATE
|
||||||
TEMPLATE.to_string()
|
.replace("!SHORT!", &short)
|
||||||
} else {
|
.replace("!LONG!", &long)
|
||||||
TEMPLATE_CHOICES
|
.replace("!DOC!", &doc);
|
||||||
.replace("!CHOICES!", &flag.doc_choices().join(" "))
|
|
||||||
};
|
match flag.completion_type() {
|
||||||
out.push_str(
|
CompletionType::Filename => {
|
||||||
&template
|
completion.push_str(" -r -F");
|
||||||
.replace("!SHORT!", &short)
|
}
|
||||||
.replace("!LONG!", &long)
|
CompletionType::Executable => {
|
||||||
.replace("!DOC!", &doc),
|
completion.push_str(" -r -f -a '(__fish_complete_command)'");
|
||||||
);
|
}
|
||||||
|
CompletionType::Filetype => {
|
||||||
|
completion.push_str(
|
||||||
|
" -r -f -a '(rg --type-list | string replace : \\t)'",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
CompletionType::Encoding => {
|
||||||
|
completion.push_str(" -r -f -a '");
|
||||||
|
completion.push_str(super::ENCODINGS);
|
||||||
|
completion.push_str("'");
|
||||||
|
}
|
||||||
|
CompletionType::Other if !flag.doc_choices().is_empty() => {
|
||||||
|
completion.push_str(" -r -f -a '");
|
||||||
|
completion.push_str(&flag.doc_choices().join(" "));
|
||||||
|
completion.push_str("'");
|
||||||
|
}
|
||||||
|
CompletionType::Other if !flag.is_switch() => {
|
||||||
|
completion.push_str(" -r -f");
|
||||||
|
}
|
||||||
|
CompletionType::Other => (),
|
||||||
|
}
|
||||||
|
|
||||||
|
completion.push('\n');
|
||||||
|
out.push_str(&completion);
|
||||||
|
|
||||||
if let Some(negated) = flag.name_negated() {
|
if let Some(negated) = flag.name_negated() {
|
||||||
|
let short = match flag.name_short() {
|
||||||
|
None => "".to_string(),
|
||||||
|
Some(byte) => char::from(byte).to_string(),
|
||||||
|
};
|
||||||
out.push_str(
|
out.push_str(
|
||||||
&template
|
&TEMPLATE_NEGATED
|
||||||
.replace("!SHORT!", "")
|
.replace("!NEGATED!", &negated)
|
||||||
.replace("!LONG!", &negated)
|
.replace("!SHORT!", &short)
|
||||||
|
.replace("!LONG!", &long)
|
||||||
.replace("!DOC!", &doc),
|
.replace("!DOC!", &doc),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@@ -2,6 +2,8 @@
|
|||||||
Modules for generating completions for various shells.
|
Modules for generating completions for various shells.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
static ENCODINGS: &'static str = include_str!("encodings.sh");
|
||||||
|
|
||||||
pub(super) mod bash;
|
pub(super) mod bash;
|
||||||
pub(super) mod fish;
|
pub(super) mod fish;
|
||||||
pub(super) mod powershell;
|
pub(super) mod powershell;
|
||||||
|
@@ -72,7 +72,7 @@ pub(crate) fn generate() -> String {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let Some(negated) = flag.name_negated() {
|
if let Some(negated) = flag.name_negated() {
|
||||||
let dash_name = format!("--{}", negated);
|
let dash_name = format!("--{negated}");
|
||||||
flags.push_str("\n ");
|
flags.push_str("\n ");
|
||||||
flags.push_str(
|
flags.push_str(
|
||||||
&TEMPLATE_FLAG
|
&TEMPLATE_FLAG
|
||||||
|
31
crates/core/flags/complete/prelude.fish
Normal file
31
crates/core/flags/complete/prelude.fish
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
# Usage: __rg_contains_opt LONG [SHORT]
|
||||||
|
function __rg_contains_opt --description 'Specialized __fish_contains_opt'
|
||||||
|
# Cache the config file because this function is called many times per
|
||||||
|
# completion attempt.
|
||||||
|
# The cache will persist for the entire shell session (even if the
|
||||||
|
# variable or the file contents change).
|
||||||
|
if not set -q __rg_config
|
||||||
|
set -g __rg_config
|
||||||
|
if set -qx RIPGREP_CONFIG_PATH
|
||||||
|
set __rg_config (
|
||||||
|
cat -- $RIPGREP_CONFIG_PATH 2>/dev/null \
|
||||||
|
| string trim \
|
||||||
|
| string match -rv '^$|^#'
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
set -l commandline (commandline -cpo) (commandline -ct) $__rg_config
|
||||||
|
|
||||||
|
if contains -- "--$argv[1]" $commandline
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
|
||||||
|
if set -q argv[2]
|
||||||
|
if string match -qr -- "^-[^-]*$argv[2]" $commandline
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return 1
|
||||||
|
end
|
@@ -413,32 +413,8 @@ _rg_encodings() {
|
|||||||
local -a expl
|
local -a expl
|
||||||
local -aU _encodings
|
local -aU _encodings
|
||||||
|
|
||||||
# This is impossible to read, but these encodings rarely if ever change, so it
|
|
||||||
# probably doesn't matter. They are derived from the list given here:
|
|
||||||
# https://encoding.spec.whatwg.org/#concept-encoding-get
|
|
||||||
_encodings=(
|
_encodings=(
|
||||||
{{,us-}ascii,arabic,chinese,cyrillic,greek{,8},hebrew,korean}
|
!ENCODINGS!
|
||||||
logical visual mac {,cs}macintosh x-mac-{cyrillic,roman,ukrainian}
|
|
||||||
866 ibm{819,866} csibm866
|
|
||||||
big5{,-hkscs} {cn-,cs}big5 x-x-big5
|
|
||||||
cp{819,866,125{0..8}} x-cp125{0..8}
|
|
||||||
csiso2022{jp,kr} csiso8859{6,8}{e,i}
|
|
||||||
csisolatin{{1..6},9} csisolatin{arabic,cyrillic,greek,hebrew}
|
|
||||||
ecma-{114,118} asmo-708 elot_928 sun_eu_greek
|
|
||||||
euc-{jp,kr} x-euc-jp cseuckr cseucpkdfmtjapanese
|
|
||||||
{,x-}gbk csiso58gb231280 gb18030 {,cs}gb2312 gb_2312{,-80} hz-gb-2312
|
|
||||||
iso-2022-{cn,cn-ext,jp,kr}
|
|
||||||
iso8859{,-}{{1..11},13,14,15}
|
|
||||||
iso-8859-{{1..11},{6,8}-{e,i},13,14,15,16} iso_8859-{{1..9},15}
|
|
||||||
iso_8859-{1,2,6,7}:1987 iso_8859-{3,4,5,8}:1988 iso_8859-9:1989
|
|
||||||
iso-ir-{58,100,101,109,110,126,127,138,144,148,149,157}
|
|
||||||
koi{,8,8-r,8-ru,8-u,8_r} cskoi8r
|
|
||||||
ks_c_5601-{1987,1989} ksc{,_}5691 csksc56011987
|
|
||||||
latin{1..6} l{{1..6},9}
|
|
||||||
shift{-,_}jis csshiftjis {,x-}sjis ms_kanji ms932
|
|
||||||
utf{,-}8 utf-16{,be,le} unicode-1-1-utf-8
|
|
||||||
windows-{31j,874,949,125{0..8}} dos-874 tis-620 ansi_x3.4-1968
|
|
||||||
x-user-defined auto none
|
|
||||||
)
|
)
|
||||||
|
|
||||||
_wanted encodings expl encoding compadd -a "$@" - _encodings
|
_wanted encodings expl encoding compadd -a "$@" - _encodings
|
||||||
@@ -458,7 +434,15 @@ _rg_types() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
_rg "$@"
|
# Don't run the completion function when being sourced by itself.
|
||||||
|
#
|
||||||
|
# See https://github.com/BurntSushi/ripgrep/issues/2956
|
||||||
|
# See https://github.com/BurntSushi/ripgrep/pull/2957
|
||||||
|
if [[ $funcstack[1] == _rg ]] || (( ! $+functions[compdef] )); then
|
||||||
|
_rg "$@"
|
||||||
|
else
|
||||||
|
compdef _rg rg
|
||||||
|
fi
|
||||||
|
|
||||||
################################################################################
|
################################################################################
|
||||||
# ZSH COMPLETION REFERENCE
|
# ZSH COMPLETION REFERENCE
|
||||||
|
@@ -19,5 +19,5 @@ long as it meets criteria 3 and 4 above.
|
|||||||
|
|
||||||
/// Generate completions for zsh.
|
/// Generate completions for zsh.
|
||||||
pub(crate) fn generate() -> String {
|
pub(crate) fn generate() -> String {
|
||||||
include_str!("rg.zsh").to_string()
|
include_str!("rg.zsh").replace("!ENCODINGS!", super::ENCODINGS.trim_end())
|
||||||
}
|
}
|
||||||
|
@@ -34,6 +34,8 @@ use crate::flags::{
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
use crate::flags::parse::parse_low_raw;
|
use crate::flags::parse::parse_low_raw;
|
||||||
|
|
||||||
|
use super::CompletionType;
|
||||||
|
|
||||||
/// A list of all flags in ripgrep via implementations of `Flag`.
|
/// A list of all flags in ripgrep via implementations of `Flag`.
|
||||||
///
|
///
|
||||||
/// The order of these flags matter. It determines the order of the flags in
|
/// The order of these flags matter. It determines the order of the flags in
|
||||||
@@ -833,7 +835,7 @@ provided multiple times. Settings are applied iteratively. Pre-existing color
|
|||||||
labels are limited to one of eight choices: \fBred\fP, \fBblue\fP, \fBgreen\fP,
|
labels are limited to one of eight choices: \fBred\fP, \fBblue\fP, \fBgreen\fP,
|
||||||
\fBcyan\fP, \fBmagenta\fP, \fByellow\fP, \fBwhite\fP and \fBblack\fP. Styles
|
\fBcyan\fP, \fBmagenta\fP, \fByellow\fP, \fBwhite\fP and \fBblack\fP. Styles
|
||||||
are limited to \fBnobold\fP, \fBbold\fP, \fBnointense\fP, \fBintense\fP,
|
are limited to \fBnobold\fP, \fBbold\fP, \fBnointense\fP, \fBintense\fP,
|
||||||
\fBnounderline\fP or \fBunderline\fP.
|
\fBnounderline\fP, \fBunderline\fP, \fBnoitalic\fP or \fBitalic\fP.
|
||||||
.sp
|
.sp
|
||||||
The format of the flag is
|
The format of the flag is
|
||||||
\fB{\fP\fItype\fP\fB}:{\fP\fIattribute\fP\fB}:{\fP\fIvalue\fP\fB}\fP.
|
\fB{\fP\fItype\fP\fB}:{\fP\fIattribute\fP\fB}:{\fP\fIvalue\fP\fB}\fP.
|
||||||
@@ -1582,6 +1584,9 @@ The encoding detection that ripgrep uses can be reverted to its automatic mode
|
|||||||
via the \flag-negate{encoding} flag.
|
via the \flag-negate{encoding} flag.
|
||||||
"
|
"
|
||||||
}
|
}
|
||||||
|
fn completion_type(&self) -> CompletionType {
|
||||||
|
CompletionType::Encoding
|
||||||
|
}
|
||||||
|
|
||||||
fn update(&self, v: FlagValue, args: &mut LowArgs) -> anyhow::Result<()> {
|
fn update(&self, v: FlagValue, args: &mut LowArgs) -> anyhow::Result<()> {
|
||||||
let value = match v {
|
let value = match v {
|
||||||
@@ -1977,6 +1982,9 @@ When \flag{file} or \flag{regexp} is used, then ripgrep treats all positional
|
|||||||
arguments as files or directories to search.
|
arguments as files or directories to search.
|
||||||
"
|
"
|
||||||
}
|
}
|
||||||
|
fn completion_type(&self) -> CompletionType {
|
||||||
|
CompletionType::Filename
|
||||||
|
}
|
||||||
|
|
||||||
fn update(&self, v: FlagValue, args: &mut LowArgs) -> anyhow::Result<()> {
|
fn update(&self, v: FlagValue, args: &mut LowArgs) -> anyhow::Result<()> {
|
||||||
let path = PathBuf::from(v.unwrap_value());
|
let path = PathBuf::from(v.unwrap_value());
|
||||||
@@ -2619,7 +2627,7 @@ of printing the file path as a prefix for each matched line.
|
|||||||
This is the default mode when printing to a tty.
|
This is the default mode when printing to a tty.
|
||||||
.sp
|
.sp
|
||||||
When \fBstdout\fP is not a tty, then ripgrep will default to the standard
|
When \fBstdout\fP is not a tty, then ripgrep will default to the standard
|
||||||
grep-like format. Once can force this format in Unix-like environments by
|
grep-like format. One can force this format in Unix-like environments by
|
||||||
piping the output of ripgrep to \fBcat\fP. For example, \fBrg\fP \fIfoo\fP \fB|
|
piping the output of ripgrep to \fBcat\fP. For example, \fBrg\fP \fIfoo\fP \fB|
|
||||||
cat\fP.
|
cat\fP.
|
||||||
"
|
"
|
||||||
@@ -2738,7 +2746,7 @@ impl Flag for Hidden {
|
|||||||
Search hidden files and directories. By default, hidden files and directories
|
Search hidden files and directories. By default, hidden files and directories
|
||||||
are skipped. Note that if a hidden file or a directory is whitelisted in
|
are skipped. Note that if a hidden file or a directory is whitelisted in
|
||||||
an ignore file, then it will be searched even if this flag isn't provided.
|
an ignore file, then it will be searched even if this flag isn't provided.
|
||||||
Similarly if a hidden file or directory is given explicitly as an argumnet to
|
Similarly if a hidden file or directory is given explicitly as an argument to
|
||||||
ripgrep.
|
ripgrep.
|
||||||
.sp
|
.sp
|
||||||
A file or directory is considered hidden if its base name starts with a dot
|
A file or directory is considered hidden if its base name starts with a dot
|
||||||
@@ -2808,6 +2816,9 @@ to calling \fBgethostname\fP. On Windows, this corresponds to calling
|
|||||||
ripgrep uses your system's hostname for producing hyperlinks.
|
ripgrep uses your system's hostname for producing hyperlinks.
|
||||||
"#
|
"#
|
||||||
}
|
}
|
||||||
|
fn completion_type(&self) -> CompletionType {
|
||||||
|
CompletionType::Executable
|
||||||
|
}
|
||||||
|
|
||||||
fn update(&self, v: FlagValue, args: &mut LowArgs) -> anyhow::Result<()> {
|
fn update(&self, v: FlagValue, args: &mut LowArgs) -> anyhow::Result<()> {
|
||||||
let path = PathBuf::from(v.unwrap_value());
|
let path = PathBuf::from(v.unwrap_value());
|
||||||
@@ -3078,7 +3089,7 @@ Individual patterns can still be matched case sensitively by using
|
|||||||
inline regex flags. For example, \fB(?\-i)abc\fP will match \fBabc\fP
|
inline regex flags. For example, \fB(?\-i)abc\fP will match \fBabc\fP
|
||||||
case sensitively even when this flag is used.
|
case sensitively even when this flag is used.
|
||||||
.sp
|
.sp
|
||||||
This flag overrides \flag{case-sensitive} and flag{smart-case}.
|
This flag overrides \flag{case-sensitive} and \flag{smart-case}.
|
||||||
"#
|
"#
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3141,6 +3152,9 @@ If you are looking for a way to include or exclude files and directories
|
|||||||
directly on the command line, then use \flag{glob} instead.
|
directly on the command line, then use \flag{glob} instead.
|
||||||
"
|
"
|
||||||
}
|
}
|
||||||
|
fn completion_type(&self) -> CompletionType {
|
||||||
|
CompletionType::Filename
|
||||||
|
}
|
||||||
|
|
||||||
fn update(&self, v: FlagValue, args: &mut LowArgs) -> anyhow::Result<()> {
|
fn update(&self, v: FlagValue, args: &mut LowArgs) -> anyhow::Result<()> {
|
||||||
let path = PathBuf::from(v.unwrap_value());
|
let path = PathBuf::from(v.unwrap_value());
|
||||||
@@ -5410,6 +5424,9 @@ format, then \fBpzstd\fP is used to decompress the contents to stdout.
|
|||||||
This overrides the \flag{search-zip} flag.
|
This overrides the \flag{search-zip} flag.
|
||||||
"#
|
"#
|
||||||
}
|
}
|
||||||
|
fn completion_type(&self) -> CompletionType {
|
||||||
|
CompletionType::Executable
|
||||||
|
}
|
||||||
|
|
||||||
fn update(&self, v: FlagValue, args: &mut LowArgs) -> anyhow::Result<()> {
|
fn update(&self, v: FlagValue, args: &mut LowArgs) -> anyhow::Result<()> {
|
||||||
let path = match v {
|
let path = match v {
|
||||||
@@ -6781,6 +6798,9 @@ any rules found in ignore files.
|
|||||||
To see the list of available file types, use the \flag{type-list} flag.
|
To see the list of available file types, use the \flag{type-list} flag.
|
||||||
"#
|
"#
|
||||||
}
|
}
|
||||||
|
fn completion_type(&self) -> CompletionType {
|
||||||
|
CompletionType::Filetype
|
||||||
|
}
|
||||||
|
|
||||||
fn update(&self, v: FlagValue, args: &mut LowArgs) -> anyhow::Result<()> {
|
fn update(&self, v: FlagValue, args: &mut LowArgs) -> anyhow::Result<()> {
|
||||||
args.type_changes.push(TypeChange::Select {
|
args.type_changes.push(TypeChange::Select {
|
||||||
@@ -7000,6 +7020,9 @@ will only search files that are unrecognized by its type definitions.
|
|||||||
To see the list of available file types, use the \flag{type-list} flag.
|
To see the list of available file types, use the \flag{type-list} flag.
|
||||||
"#
|
"#
|
||||||
}
|
}
|
||||||
|
fn completion_type(&self) -> CompletionType {
|
||||||
|
CompletionType::Filetype
|
||||||
|
}
|
||||||
|
|
||||||
fn update(&self, v: FlagValue, args: &mut LowArgs) -> anyhow::Result<()> {
|
fn update(&self, v: FlagValue, args: &mut LowArgs) -> anyhow::Result<()> {
|
||||||
args.type_changes.push(TypeChange::Negate {
|
args.type_changes.push(TypeChange::Negate {
|
||||||
@@ -7238,7 +7261,7 @@ impl Flag for Vimgrep {
|
|||||||
Category::Output
|
Category::Output
|
||||||
}
|
}
|
||||||
fn doc_short(&self) -> &'static str {
|
fn doc_short(&self) -> &'static str {
|
||||||
r"Print results im a vim compatible format."
|
r"Print results in a vim compatible format."
|
||||||
}
|
}
|
||||||
fn doc_long(&self) -> &'static str {
|
fn doc_long(&self) -> &'static str {
|
||||||
r"
|
r"
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
.TH RG 1 2023-11-26 "!!VERSION!!" "User Commands"
|
.TH RG 1 2024-09-08 "!!VERSION!!" "User Commands"
|
||||||
.
|
.
|
||||||
.
|
.
|
||||||
.SH NAME
|
.SH NAME
|
||||||
@@ -43,10 +43,10 @@ configuration file. The file can specify one shell argument per line. Lines
|
|||||||
starting with \fB#\fP are ignored. For more details, see \fBCONFIGURATION
|
starting with \fB#\fP are ignored. For more details, see \fBCONFIGURATION
|
||||||
FILES\fP below.
|
FILES\fP below.
|
||||||
.sp
|
.sp
|
||||||
ripgrep will automatically detect if stdin exists and search stdin for a regex
|
ripgrep will automatically detect if stdin is a readable file and search stdin
|
||||||
pattern, e.g. \fBls | rg foo\fP. In some environments, stdin may exist when
|
for a regex pattern, e.g. \fBls | rg foo\fP. In some environments, stdin may
|
||||||
it shouldn't. To turn off stdin detection, one can explicitly specify the
|
exist when it shouldn't. To turn off stdin detection, one can explicitly
|
||||||
directory to search, e.g. \fBrg foo ./\fP.
|
specify the directory to search, e.g. \fBrg foo ./\fP.
|
||||||
.sp
|
.sp
|
||||||
Like other tools such as \fBls\fP, ripgrep will alter its output depending on
|
Like other tools such as \fBls\fP, ripgrep will alter its output depending on
|
||||||
whether stdout is connected to a tty. By default, when printing a tty, ripgrep
|
whether stdout is connected to a tty. By default, when printing a tty, ripgrep
|
||||||
|
@@ -161,9 +161,6 @@ fn compile_cpu_features() -> Vec<String> {
|
|||||||
fn features() -> Vec<String> {
|
fn features() -> Vec<String> {
|
||||||
let mut features = vec![];
|
let mut features = vec![];
|
||||||
|
|
||||||
let simd_accel = cfg!(feature = "simd-accel");
|
|
||||||
features.push(format!("{sign}simd-accel", sign = sign(simd_accel)));
|
|
||||||
|
|
||||||
let pcre2 = cfg!(feature = "pcre2");
|
let pcre2 = cfg!(feature = "pcre2");
|
||||||
features.push(format!("{sign}pcre2", sign = sign(pcre2)));
|
features.push(format!("{sign}pcre2", sign = sign(pcre2)));
|
||||||
|
|
||||||
|
@@ -484,9 +484,9 @@ impl HiArgs {
|
|||||||
if self.crlf {
|
if self.crlf {
|
||||||
builder.crlf(true);
|
builder.crlf(true);
|
||||||
}
|
}
|
||||||
// We don't need to set this in multiline mode since mulitline
|
// We don't need to set this in multiline mode since multiline
|
||||||
// matchers don't use optimizations related to line terminators.
|
// matchers don't use optimizations related to line terminators.
|
||||||
// Moreover, a mulitline regex used with --null-data should
|
// Moreover, a multiline regex used with --null-data should
|
||||||
// be allowed to match NUL bytes explicitly, which this would
|
// be allowed to match NUL bytes explicitly, which this would
|
||||||
// otherwise forbid.
|
// otherwise forbid.
|
||||||
if self.null_data {
|
if self.null_data {
|
||||||
@@ -517,7 +517,7 @@ impl HiArgs {
|
|||||||
/// When this returns false, it is impossible for ripgrep to ever report
|
/// When this returns false, it is impossible for ripgrep to ever report
|
||||||
/// a match.
|
/// a match.
|
||||||
pub(crate) fn matches_possible(&self) -> bool {
|
pub(crate) fn matches_possible(&self) -> bool {
|
||||||
if self.patterns.patterns.is_empty() {
|
if self.patterns.patterns.is_empty() && !self.invert_match {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if self.max_count == Some(0) {
|
if self.max_count == Some(0) {
|
||||||
@@ -589,6 +589,7 @@ impl HiArgs {
|
|||||||
.pretty(false)
|
.pretty(false)
|
||||||
.max_matches(self.max_count)
|
.max_matches(self.max_count)
|
||||||
.always_begin_end(false)
|
.always_begin_end(false)
|
||||||
|
.replacement(self.replace.clone().map(|r| r.into()))
|
||||||
.build(wtr)
|
.build(wtr)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -771,7 +772,13 @@ impl HiArgs {
|
|||||||
let Some(ref sort) = self.sort else { return Box::new(haystacks) };
|
let Some(ref sort) = self.sort else { return Box::new(haystacks) };
|
||||||
let mut with_timestamps: Vec<_> = match sort.kind {
|
let mut with_timestamps: Vec<_> = match sort.kind {
|
||||||
SortModeKind::Path if !sort.reverse => return Box::new(haystacks),
|
SortModeKind::Path if !sort.reverse => return Box::new(haystacks),
|
||||||
SortModeKind::Path => todo!(),
|
SortModeKind::Path => {
|
||||||
|
let mut haystacks = haystacks.collect::<Vec<Haystack>>();
|
||||||
|
haystacks.sort_by(|ref h1, ref h2| {
|
||||||
|
h1.path().cmp(h2.path()).reverse()
|
||||||
|
});
|
||||||
|
return Box::new(haystacks.into_iter());
|
||||||
|
}
|
||||||
SortModeKind::LastModified => {
|
SortModeKind::LastModified => {
|
||||||
attach_timestamps(haystacks, |md| md.modified()).collect()
|
attach_timestamps(haystacks, |md| md.modified()).collect()
|
||||||
}
|
}
|
||||||
@@ -1074,9 +1081,18 @@ impl Paths {
|
|||||||
}
|
}
|
||||||
paths.push(path);
|
paths.push(path);
|
||||||
}
|
}
|
||||||
|
log::debug!("number of paths given to search: {}", paths.len());
|
||||||
if !paths.is_empty() {
|
if !paths.is_empty() {
|
||||||
let is_one_file = paths.len() == 1
|
let is_one_file = paths.len() == 1
|
||||||
&& (paths[0] == Path::new("-") || paths[0].is_file());
|
// Note that we specifically use `!paths[0].is_dir()` here
|
||||||
|
// instead of `paths[0].is_file()`. Namely, the latter can
|
||||||
|
// return `false` even when the path is something resembling
|
||||||
|
// a file. So instead, we just consider the path a file as
|
||||||
|
// long as we know it isn't a directory.
|
||||||
|
//
|
||||||
|
// See: https://github.com/BurntSushi/ripgrep/issues/2736
|
||||||
|
&& (paths[0] == Path::new("-") || !paths[0].is_dir());
|
||||||
|
log::debug!("is_one_file? {is_one_file:?}");
|
||||||
return Ok(Paths { paths, has_implicit_path: false, is_one_file });
|
return Ok(Paths { paths, has_implicit_path: false, is_one_file });
|
||||||
}
|
}
|
||||||
// N.B. is_readable_stdin is a heuristic! Part of the issue is that a
|
// N.B. is_readable_stdin is a heuristic! Part of the issue is that a
|
||||||
|
@@ -70,7 +70,7 @@ mod parse;
|
|||||||
/// value. Flags that accept multiple values are an unsupported abberation.
|
/// value. Flags that accept multiple values are an unsupported abberation.
|
||||||
trait Flag: Debug + Send + Sync + UnwindSafe + RefUnwindSafe + 'static {
|
trait Flag: Debug + Send + Sync + UnwindSafe + RefUnwindSafe + 'static {
|
||||||
/// Returns true if this flag is a switch. When a flag is a switch, the
|
/// Returns true if this flag is a switch. When a flag is a switch, the
|
||||||
/// CLI parser will look for a value after the flag is seen.
|
/// CLI parser will not look for a value after the flag is seen.
|
||||||
fn is_switch(&self) -> bool;
|
fn is_switch(&self) -> bool;
|
||||||
|
|
||||||
/// A short single byte name for this flag. This returns `None` by default,
|
/// A short single byte name for this flag. This returns `None` by default,
|
||||||
@@ -150,6 +150,10 @@ trait Flag: Debug + Send + Sync + UnwindSafe + RefUnwindSafe + 'static {
|
|||||||
&[]
|
&[]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn completion_type(&self) -> CompletionType {
|
||||||
|
CompletionType::Other
|
||||||
|
}
|
||||||
|
|
||||||
/// Given the parsed value (which might just be a switch), this should
|
/// Given the parsed value (which might just be a switch), this should
|
||||||
/// update the state in `args` based on the value given for this flag.
|
/// update the state in `args` based on the value given for this flag.
|
||||||
///
|
///
|
||||||
@@ -228,6 +232,21 @@ impl Category {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The kind of argument a flag accepts, to be used for shell completions.
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
enum CompletionType {
|
||||||
|
/// No special category. is_switch() and doc_choices() may apply.
|
||||||
|
Other,
|
||||||
|
/// A path to a file.
|
||||||
|
Filename,
|
||||||
|
/// A command in $PATH.
|
||||||
|
Executable,
|
||||||
|
/// The name of a file type, as used by e.g. --type.
|
||||||
|
Filetype,
|
||||||
|
/// The name of an encoding_rs encoding, as used by --encoding.
|
||||||
|
Encoding,
|
||||||
|
}
|
||||||
|
|
||||||
/// Represents a value parsed from the command line.
|
/// Represents a value parsed from the command line.
|
||||||
///
|
///
|
||||||
/// This doesn't include the corresponding flag, but values come in one of
|
/// This doesn't include the corresponding flag, but values come in one of
|
||||||
|
@@ -323,7 +323,7 @@ enum FlagLookup<'a> {
|
|||||||
UnrecognizedLong(String),
|
UnrecognizedLong(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The info about a flag associated with a flag's ID in the the flag map.
|
/// The info about a flag associated with a flag's ID in the flag map.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct FlagInfo {
|
struct FlagInfo {
|
||||||
/// The flag object and its associated metadata.
|
/// The flag object and its associated metadata.
|
||||||
|
@@ -6,7 +6,7 @@ print to stderr. We therefore avoid bringing in extra dependencies just for
|
|||||||
this functionality.
|
this functionality.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use log::{self, Log};
|
use log::Log;
|
||||||
|
|
||||||
/// The simplest possible logger that logs to stderr.
|
/// The simplest possible logger that logs to stderr.
|
||||||
///
|
///
|
||||||
|
@@ -37,7 +37,7 @@ mod search;
|
|||||||
// i686.
|
// i686.
|
||||||
#[cfg(all(target_env = "musl", target_pointer_width = "64"))]
|
#[cfg(all(target_env = "musl", target_pointer_width = "64"))]
|
||||||
#[global_allocator]
|
#[global_allocator]
|
||||||
static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
|
static ALLOC: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc;
|
||||||
|
|
||||||
/// Then, as it was, then again it will be.
|
/// Then, as it was, then again it will be.
|
||||||
fn main() -> ExitCode {
|
fn main() -> ExitCode {
|
||||||
@@ -468,7 +468,7 @@ fn print_stats<W: Write>(
|
|||||||
{bytes_printed} bytes printed
|
{bytes_printed} bytes printed
|
||||||
{bytes_searched} bytes searched
|
{bytes_searched} bytes searched
|
||||||
{search_time:0.6} seconds spent searching
|
{search_time:0.6} seconds spent searching
|
||||||
{process_time:0.6} seconds
|
{process_time:0.6} seconds total
|
||||||
",
|
",
|
||||||
matches = stats.matches(),
|
matches = stats.matches(),
|
||||||
lines = stats.matched_lines(),
|
lines = stats.matched_lines(),
|
||||||
|
@@ -28,7 +28,7 @@ static ERRORED: AtomicBool = AtomicBool::new(false);
|
|||||||
///
|
///
|
||||||
/// This locks stdout, not stderr, even though this prints to stderr. This
|
/// This locks stdout, not stderr, even though this prints to stderr. This
|
||||||
/// avoids the appearance of interleaving output when stdout and stderr both
|
/// avoids the appearance of interleaving output when stdout and stderr both
|
||||||
/// correspond to a tty.)
|
/// correspond to a tty.
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! eprintln_locked {
|
macro_rules! eprintln_locked {
|
||||||
($($tt:tt)*) => {{
|
($($tt:tt)*) => {{
|
||||||
@@ -39,21 +39,29 @@ macro_rules! eprintln_locked {
|
|||||||
// lock stdout before printing to stderr. This avoids interleaving
|
// lock stdout before printing to stderr. This avoids interleaving
|
||||||
// lines within ripgrep because `search_parallel` uses `termcolor`,
|
// lines within ripgrep because `search_parallel` uses `termcolor`,
|
||||||
// which accesses the same stdout lock when writing lines.
|
// which accesses the same stdout lock when writing lines.
|
||||||
let stdout = std::io::stdout();
|
let stdout = std::io::stdout().lock();
|
||||||
let _handle = stdout.lock();
|
let mut stderr = std::io::stderr().lock();
|
||||||
// We specifically ignore any errors here. One plausible error we
|
// We specifically ignore any errors here. One plausible error we
|
||||||
// can get in some cases is a broken pipe error. And when that
|
// can get in some cases is a broken pipe error. And when that
|
||||||
// occurs, we should exit gracefully. Otherwise, just abort with
|
// occurs, we should exit gracefully. Otherwise, just abort with
|
||||||
// an error code because there isn't much else we can do.
|
// an error code because there isn't much else we can do.
|
||||||
//
|
//
|
||||||
// See: https://github.com/BurntSushi/ripgrep/issues/1966
|
// See: https://github.com/BurntSushi/ripgrep/issues/1966
|
||||||
if let Err(err) = writeln!(std::io::stderr(), $($tt)*) {
|
if let Err(err) = write!(stderr, "rg: ") {
|
||||||
if err.kind() == std::io::ErrorKind::BrokenPipe {
|
if err.kind() == std::io::ErrorKind::BrokenPipe {
|
||||||
std::process::exit(0);
|
std::process::exit(0);
|
||||||
} else {
|
} else {
|
||||||
std::process::exit(2);
|
std::process::exit(2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if let Err(err) = writeln!(stderr, $($tt)*) {
|
||||||
|
if err.kind() == std::io::ErrorKind::BrokenPipe {
|
||||||
|
std::process::exit(0);
|
||||||
|
} else {
|
||||||
|
std::process::exit(2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
drop(stdout);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
@@ -91,19 +99,19 @@ macro_rules! ignore_message {
|
|||||||
|
|
||||||
/// Returns true if and only if messages should be shown.
|
/// Returns true if and only if messages should be shown.
|
||||||
pub(crate) fn messages() -> bool {
|
pub(crate) fn messages() -> bool {
|
||||||
MESSAGES.load(Ordering::SeqCst)
|
MESSAGES.load(Ordering::Relaxed)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set whether messages should be shown or not.
|
/// Set whether messages should be shown or not.
|
||||||
///
|
///
|
||||||
/// By default, they are not shown.
|
/// By default, they are not shown.
|
||||||
pub(crate) fn set_messages(yes: bool) {
|
pub(crate) fn set_messages(yes: bool) {
|
||||||
MESSAGES.store(yes, Ordering::SeqCst)
|
MESSAGES.store(yes, Ordering::Relaxed)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns true if and only if "ignore" related messages should be shown.
|
/// Returns true if and only if "ignore" related messages should be shown.
|
||||||
pub(crate) fn ignore_messages() -> bool {
|
pub(crate) fn ignore_messages() -> bool {
|
||||||
IGNORE_MESSAGES.load(Ordering::SeqCst)
|
IGNORE_MESSAGES.load(Ordering::Relaxed)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set whether "ignore" related messages should be shown or not.
|
/// Set whether "ignore" related messages should be shown or not.
|
||||||
@@ -114,12 +122,12 @@ pub(crate) fn ignore_messages() -> bool {
|
|||||||
/// `messages` is disabled, then "ignore" messages are never shown, regardless
|
/// `messages` is disabled, then "ignore" messages are never shown, regardless
|
||||||
/// of this setting.
|
/// of this setting.
|
||||||
pub(crate) fn set_ignore_messages(yes: bool) {
|
pub(crate) fn set_ignore_messages(yes: bool) {
|
||||||
IGNORE_MESSAGES.store(yes, Ordering::SeqCst)
|
IGNORE_MESSAGES.store(yes, Ordering::Relaxed)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns true if and only if ripgrep came across a non-fatal error.
|
/// Returns true if and only if ripgrep came across a non-fatal error.
|
||||||
pub(crate) fn errored() -> bool {
|
pub(crate) fn errored() -> bool {
|
||||||
ERRORED.load(Ordering::SeqCst)
|
ERRORED.load(Ordering::Relaxed)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Indicate that ripgrep has come across a non-fatal error.
|
/// Indicate that ripgrep has come across a non-fatal error.
|
||||||
@@ -127,5 +135,5 @@ pub(crate) fn errored() -> bool {
|
|||||||
/// Callers should not use this directly. Instead, it is called automatically
|
/// Callers should not use this directly. Instead, it is called automatically
|
||||||
/// via the `err_message` macro.
|
/// via the `err_message` macro.
|
||||||
pub(crate) fn set_errored() {
|
pub(crate) fn set_errored() {
|
||||||
ERRORED.store(true, Ordering::SeqCst);
|
ERRORED.store(true, Ordering::Relaxed);
|
||||||
}
|
}
|
||||||
|
@@ -307,15 +307,14 @@ impl<W: WriteColor> SearchWorker<W> {
|
|||||||
io::Error::new(
|
io::Error::new(
|
||||||
io::ErrorKind::Other,
|
io::ErrorKind::Other,
|
||||||
format!(
|
format!(
|
||||||
"preprocessor command could not start: '{:?}': {}",
|
"preprocessor command could not start: '{cmd:?}': {err}",
|
||||||
cmd, err,
|
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
let result = self.search_reader(path, &mut rdr).map_err(|err| {
|
let result = self.search_reader(path, &mut rdr).map_err(|err| {
|
||||||
io::Error::new(
|
io::Error::new(
|
||||||
io::ErrorKind::Other,
|
io::ErrorKind::Other,
|
||||||
format!("preprocessor command failed: '{:?}': {}", cmd, err),
|
format!("preprocessor command failed: '{cmd:?}': {err}"),
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
let close_result = rdr.close();
|
let close_result = rdr.close();
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "globset"
|
name = "globset"
|
||||||
version = "0.4.14" #:version
|
version = "0.4.16" #:version
|
||||||
authors = ["Andrew Gallant <jamslam@gmail.com>"]
|
authors = ["Andrew Gallant <jamslam@gmail.com>"]
|
||||||
description = """
|
description = """
|
||||||
Cross platform single glob and glob set matching. Glob set matching is the
|
Cross platform single glob and glob set matching. Glob set matching is the
|
||||||
@@ -21,6 +21,7 @@ bench = false
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
aho-corasick = "1.1.1"
|
aho-corasick = "1.1.1"
|
||||||
|
arbitrary = { version = "1.3.2", optional = true, features = ["derive"] }
|
||||||
bstr = { version = "1.6.2", default-features = false, features = ["std"] }
|
bstr = { version = "1.6.2", default-features = false, features = ["std"] }
|
||||||
log = { version = "0.4.20", optional = true }
|
log = { version = "0.4.20", optional = true }
|
||||||
serde = { version = "1.0.188", optional = true }
|
serde = { version = "1.0.188", optional = true }
|
||||||
@@ -41,6 +42,7 @@ serde_json = "1.0.107"
|
|||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["log"]
|
default = ["log"]
|
||||||
|
arbitrary = ["dep:arbitrary"]
|
||||||
# DEPRECATED. It is a no-op. SIMD is done automatically through runtime
|
# DEPRECATED. It is a no-op. SIMD is done automatically through runtime
|
||||||
# dispatch.
|
# dispatch.
|
||||||
simd-accel = []
|
simd-accel = []
|
||||||
|
@@ -1,3 +1,4 @@
|
|||||||
|
use std::fmt::Write;
|
||||||
use std::path::{is_separator, Path};
|
use std::path::{is_separator, Path};
|
||||||
|
|
||||||
use regex_automata::meta::Regex;
|
use regex_automata::meta::Regex;
|
||||||
@@ -70,7 +71,8 @@ impl MatchStrategy {
|
|||||||
///
|
///
|
||||||
/// It cannot be used directly to match file paths, but it can be converted
|
/// It cannot be used directly to match file paths, but it can be converted
|
||||||
/// to a regular expression string or a matcher.
|
/// to a regular expression string or a matcher.
|
||||||
#[derive(Clone, Debug, Eq)]
|
#[derive(Clone, Eq)]
|
||||||
|
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
|
||||||
pub struct Glob {
|
pub struct Glob {
|
||||||
glob: String,
|
glob: String,
|
||||||
re: String,
|
re: String,
|
||||||
@@ -91,6 +93,21 @@ impl std::hash::Hash for Glob {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Debug for Glob {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
if f.alternate() {
|
||||||
|
f.debug_struct("Glob")
|
||||||
|
.field("glob", &self.glob)
|
||||||
|
.field("re", &self.re)
|
||||||
|
.field("opts", &self.opts)
|
||||||
|
.field("tokens", &self.tokens)
|
||||||
|
.finish()
|
||||||
|
} else {
|
||||||
|
f.debug_tuple("Glob").field(&self.glob).finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl std::fmt::Display for Glob {
|
impl std::fmt::Display for Glob {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
self.glob.fmt(f)
|
self.glob.fmt(f)
|
||||||
@@ -193,6 +210,7 @@ pub struct GlobBuilder<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
||||||
|
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
|
||||||
struct GlobOptions {
|
struct GlobOptions {
|
||||||
/// Whether to match case insensitively.
|
/// Whether to match case insensitively.
|
||||||
case_insensitive: bool,
|
case_insensitive: bool,
|
||||||
@@ -219,6 +237,7 @@ impl GlobOptions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default, Eq, PartialEq)]
|
#[derive(Clone, Debug, Default, Eq, PartialEq)]
|
||||||
|
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
|
||||||
struct Tokens(Vec<Token>);
|
struct Tokens(Vec<Token>);
|
||||||
|
|
||||||
impl std::ops::Deref for Tokens {
|
impl std::ops::Deref for Tokens {
|
||||||
@@ -235,6 +254,7 @@ impl std::ops::DerefMut for Tokens {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
|
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
|
||||||
enum Token {
|
enum Token {
|
||||||
Literal(char),
|
Literal(char),
|
||||||
Any,
|
Any,
|
||||||
@@ -732,7 +752,9 @@ impl Tokens {
|
|||||||
/// Convert a Unicode scalar value to an escaped string suitable for use as
|
/// Convert a Unicode scalar value to an escaped string suitable for use as
|
||||||
/// a literal in a non-Unicode regex.
|
/// a literal in a non-Unicode regex.
|
||||||
fn char_to_escaped_literal(c: char) -> String {
|
fn char_to_escaped_literal(c: char) -> String {
|
||||||
bytes_to_escaped_literal(&c.to_string().into_bytes())
|
let mut buf = [0; 4];
|
||||||
|
let bytes = c.encode_utf8(&mut buf).as_bytes();
|
||||||
|
bytes_to_escaped_literal(bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Converts an arbitrary sequence of bytes to a UTF-8 string. All non-ASCII
|
/// Converts an arbitrary sequence of bytes to a UTF-8 string. All non-ASCII
|
||||||
@@ -741,11 +763,12 @@ fn bytes_to_escaped_literal(bs: &[u8]) -> String {
|
|||||||
let mut s = String::with_capacity(bs.len());
|
let mut s = String::with_capacity(bs.len());
|
||||||
for &b in bs {
|
for &b in bs {
|
||||||
if b <= 0x7F {
|
if b <= 0x7F {
|
||||||
s.push_str(®ex_syntax::escape(
|
regex_syntax::escape_into(
|
||||||
char::from(b).encode_utf8(&mut [0; 4]),
|
char::from(b).encode_utf8(&mut [0; 4]),
|
||||||
));
|
&mut s,
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
s.push_str(&format!("\\x{:02x}", b));
|
write!(&mut s, "\\x{:02x}", b).unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
s
|
s
|
||||||
|
@@ -94,6 +94,19 @@ Standard Unix-style glob syntax is supported:
|
|||||||
|
|
||||||
A `GlobBuilder` can be used to prevent wildcards from matching path separators,
|
A `GlobBuilder` can be used to prevent wildcards from matching path separators,
|
||||||
or to enable case insensitive matching.
|
or to enable case insensitive matching.
|
||||||
|
|
||||||
|
# Crate Features
|
||||||
|
|
||||||
|
This crate includes optional features that can be enabled if necessary.
|
||||||
|
These features are not required but may be useful depending on the use case.
|
||||||
|
|
||||||
|
The following features are available:
|
||||||
|
|
||||||
|
* **arbitrary** -
|
||||||
|
Enabling this feature introduces a public dependency on the
|
||||||
|
[`arbitrary`](https://crates.io/crates/arbitrary)
|
||||||
|
crate. Namely, it implements the `Arbitrary` trait from that crate for the
|
||||||
|
[`Glob`] type. This feature is disabled by default.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#![deny(missing_docs)]
|
#![deny(missing_docs)]
|
||||||
@@ -351,6 +364,43 @@ impl GlobSet {
|
|||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns true if all globs in this set match the path given.
|
||||||
|
///
|
||||||
|
/// This will return true if the set of globs is empty, as in that case all
|
||||||
|
/// `0` of the globs will match.
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use globset::{Glob, GlobSetBuilder};
|
||||||
|
///
|
||||||
|
/// let mut builder = GlobSetBuilder::new();
|
||||||
|
/// builder.add(Glob::new("src/*").unwrap());
|
||||||
|
/// builder.add(Glob::new("**/*.rs").unwrap());
|
||||||
|
/// let set = builder.build().unwrap();
|
||||||
|
///
|
||||||
|
/// assert!(set.matches_all("src/foo.rs"));
|
||||||
|
/// assert!(!set.matches_all("src/bar.c"));
|
||||||
|
/// assert!(!set.matches_all("test.rs"));
|
||||||
|
/// ```
|
||||||
|
pub fn matches_all<P: AsRef<Path>>(&self, path: P) -> bool {
|
||||||
|
self.matches_all_candidate(&Candidate::new(path.as_ref()))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns ture if all globs in this set match the path given.
|
||||||
|
///
|
||||||
|
/// This takes a Candidate as input, which can be used to amortize the cost
|
||||||
|
/// of peparing a path for matching.
|
||||||
|
///
|
||||||
|
/// This will return true if the set of globs is empty, as in that case all
|
||||||
|
/// `0` of the globs will match.
|
||||||
|
pub fn matches_all_candidate(&self, path: &Candidate<'_>) -> bool {
|
||||||
|
for strat in &self.strats {
|
||||||
|
if !strat.is_match(path) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the sequence number of every glob pattern that matches the
|
/// Returns the sequence number of every glob pattern that matches the
|
||||||
/// given path.
|
/// given path.
|
||||||
pub fn matches<P: AsRef<Path>>(&self, path: P) -> Vec<usize> {
|
pub fn matches<P: AsRef<Path>>(&self, path: P) -> Vec<usize> {
|
||||||
@@ -461,20 +511,33 @@ impl GlobSet {
|
|||||||
required_exts.0.len(),
|
required_exts.0.len(),
|
||||||
regexes.literals.len()
|
regexes.literals.len()
|
||||||
);
|
);
|
||||||
Ok(GlobSet {
|
let mut strats = Vec::with_capacity(7);
|
||||||
len: pats.len(),
|
// Only add strategies that are populated
|
||||||
strats: vec![
|
if !exts.0.is_empty() {
|
||||||
GlobSetMatchStrategy::Extension(exts),
|
strats.push(GlobSetMatchStrategy::Extension(exts));
|
||||||
GlobSetMatchStrategy::BasenameLiteral(base_lits),
|
}
|
||||||
GlobSetMatchStrategy::Literal(lits),
|
if !base_lits.0.is_empty() {
|
||||||
GlobSetMatchStrategy::Suffix(suffixes.suffix()),
|
strats.push(GlobSetMatchStrategy::BasenameLiteral(base_lits));
|
||||||
GlobSetMatchStrategy::Prefix(prefixes.prefix()),
|
}
|
||||||
GlobSetMatchStrategy::RequiredExtension(
|
if !lits.0.is_empty() {
|
||||||
required_exts.build()?,
|
strats.push(GlobSetMatchStrategy::Literal(lits));
|
||||||
),
|
}
|
||||||
GlobSetMatchStrategy::Regex(regexes.regex_set()?),
|
if !suffixes.is_empty() {
|
||||||
],
|
strats.push(GlobSetMatchStrategy::Suffix(suffixes.suffix()));
|
||||||
})
|
}
|
||||||
|
if !prefixes.is_empty() {
|
||||||
|
strats.push(GlobSetMatchStrategy::Prefix(prefixes.prefix()));
|
||||||
|
}
|
||||||
|
if !required_exts.0.is_empty() {
|
||||||
|
strats.push(GlobSetMatchStrategy::RequiredExtension(
|
||||||
|
required_exts.build()?,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
if !regexes.is_empty() {
|
||||||
|
strats.push(GlobSetMatchStrategy::Regex(regexes.regex_set()?));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(GlobSet { len: pats.len(), strats })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -892,6 +955,10 @@ impl MultiStrategyBuilder {
|
|||||||
patset: Arc::new(Pool::new(create)),
|
patset: Arc::new(Pool::new(create)),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_empty(&self) -> bool {
|
||||||
|
self.literals.is_empty()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
@@ -928,13 +995,26 @@ impl RequiredExtensionStrategyBuilder {
|
|||||||
///
|
///
|
||||||
/// The escaping works by surrounding meta-characters with brackets. For
|
/// The escaping works by surrounding meta-characters with brackets. For
|
||||||
/// example, `*` becomes `[*]`.
|
/// example, `*` becomes `[*]`.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use globset::escape;
|
||||||
|
///
|
||||||
|
/// assert_eq!(escape("foo*bar"), "foo[*]bar");
|
||||||
|
/// assert_eq!(escape("foo?bar"), "foo[?]bar");
|
||||||
|
/// assert_eq!(escape("foo[bar"), "foo[[]bar");
|
||||||
|
/// assert_eq!(escape("foo]bar"), "foo[]]bar");
|
||||||
|
/// assert_eq!(escape("foo{bar"), "foo[{]bar");
|
||||||
|
/// assert_eq!(escape("foo}bar"), "foo[}]bar");
|
||||||
|
/// ```
|
||||||
pub fn escape(s: &str) -> String {
|
pub fn escape(s: &str) -> String {
|
||||||
let mut escaped = String::with_capacity(s.len());
|
let mut escaped = String::with_capacity(s.len());
|
||||||
for c in s.chars() {
|
for c in s.chars() {
|
||||||
match c {
|
match c {
|
||||||
// note that ! does not need escaping because it is only special
|
// note that ! does not need escaping because it is only special
|
||||||
// inside brackets
|
// inside brackets
|
||||||
'?' | '*' | '[' | ']' => {
|
'?' | '*' | '[' | ']' | '{' | '}' => {
|
||||||
escaped.push('[');
|
escaped.push('[');
|
||||||
escaped.push(c);
|
escaped.push(c);
|
||||||
escaped.push(']');
|
escaped.push(']');
|
||||||
@@ -979,6 +1059,7 @@ mod tests {
|
|||||||
let set = GlobSetBuilder::new().build().unwrap();
|
let set = GlobSetBuilder::new().build().unwrap();
|
||||||
assert!(!set.is_match(""));
|
assert!(!set.is_match(""));
|
||||||
assert!(!set.is_match("a"));
|
assert!(!set.is_match("a"));
|
||||||
|
assert!(set.matches_all("a"));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -1019,4 +1100,16 @@ mod tests {
|
|||||||
let matches = set.matches("nada");
|
let matches = set.matches("nada");
|
||||||
assert_eq!(0, matches.len());
|
assert_eq!(0, matches.len());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn debug() {
|
||||||
|
let mut builder = GlobSetBuilder::new();
|
||||||
|
builder.add(Glob::new("*foo*").unwrap());
|
||||||
|
builder.add(Glob::new("*bar*").unwrap());
|
||||||
|
builder.add(Glob::new("*quux*").unwrap());
|
||||||
|
assert_eq!(
|
||||||
|
format!("{builder:?}"),
|
||||||
|
"GlobSetBuilder { pats: [Glob(\"*foo*\"), Glob(\"*bar*\"), Glob(\"*quux*\")] }",
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "grep"
|
name = "grep"
|
||||||
version = "0.2.13" #:version
|
version = "0.3.2" #:version
|
||||||
authors = ["Andrew Gallant <jamslam@gmail.com>"]
|
authors = ["Andrew Gallant <jamslam@gmail.com>"]
|
||||||
description = """
|
description = """
|
||||||
Fast line oriented regex searching as a library.
|
Fast line oriented regex searching as a library.
|
||||||
@@ -14,20 +14,20 @@ license = "Unlicense OR MIT"
|
|||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
grep-cli = { version = "0.1.10", path = "../cli" }
|
grep-cli = { version = "0.1.11", path = "../cli" }
|
||||||
grep-matcher = { version = "0.1.7", path = "../matcher" }
|
grep-matcher = { version = "0.1.7", path = "../matcher" }
|
||||||
grep-pcre2 = { version = "0.1.7", path = "../pcre2", optional = true }
|
grep-pcre2 = { version = "0.1.8", path = "../pcre2", optional = true }
|
||||||
grep-printer = { version = "0.1.7", path = "../printer" }
|
grep-printer = { version = "0.2.2", path = "../printer" }
|
||||||
grep-regex = { version = "0.1.12", path = "../regex" }
|
grep-regex = { version = "0.1.13", path = "../regex" }
|
||||||
grep-searcher = { version = "0.1.12", path = "../searcher" }
|
grep-searcher = { version = "0.1.14", path = "../searcher" }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
termcolor = "1.0.4"
|
termcolor = "1.0.4"
|
||||||
walkdir = "2.2.7"
|
walkdir = "2.2.7"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
simd-accel = ["grep-searcher/simd-accel"]
|
|
||||||
pcre2 = ["grep-pcre2"]
|
pcre2 = ["grep-pcre2"]
|
||||||
|
|
||||||
# This feature is DEPRECATED. Runtime dispatch is used for SIMD now.
|
# These features are DEPRECATED. Runtime dispatch is used for SIMD now.
|
||||||
|
simd-accel = []
|
||||||
avx-accel = []
|
avx-accel = []
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "ignore"
|
name = "ignore"
|
||||||
version = "0.4.21" #:version
|
version = "0.4.23" #:version
|
||||||
authors = ["Andrew Gallant <jamslam@gmail.com>"]
|
authors = ["Andrew Gallant <jamslam@gmail.com>"]
|
||||||
description = """
|
description = """
|
||||||
A fast library for efficiently matching ignore files such as `.gitignore`
|
A fast library for efficiently matching ignore files such as `.gitignore`
|
||||||
@@ -20,7 +20,7 @@ bench = false
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
crossbeam-deque = "0.8.3"
|
crossbeam-deque = "0.8.3"
|
||||||
globset = { version = "0.4.14", path = "../globset" }
|
globset = { version = "0.4.15", path = "../globset" }
|
||||||
log = "0.4.20"
|
log = "0.4.20"
|
||||||
memchr = "2.6.3"
|
memchr = "2.6.3"
|
||||||
same-file = "1.0.6"
|
same-file = "1.0.6"
|
||||||
@@ -36,7 +36,7 @@ version = "0.1.2"
|
|||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
bstr = { version = "1.6.2", default-features = false, features = ["std"] }
|
bstr = { version = "1.6.2", default-features = false, features = ["std"] }
|
||||||
crossbeam-channel = "0.5.8"
|
crossbeam-channel = "0.5.15"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
# DEPRECATED. It is a no-op. SIMD is done automatically through runtime
|
# DEPRECATED. It is a no-op. SIMD is done automatically through runtime
|
||||||
|
@@ -27,7 +27,7 @@ pub(crate) const DEFAULT_TYPES: &[(&[&str], &[&str])] = &[
|
|||||||
(&["bat", "batch"], &["*.bat"]),
|
(&["bat", "batch"], &["*.bat"]),
|
||||||
(&["bazel"], &[
|
(&["bazel"], &[
|
||||||
"*.bazel", "*.bzl", "*.BUILD", "*.bazelrc", "BUILD", "MODULE.bazel",
|
"*.bazel", "*.bzl", "*.BUILD", "*.bazelrc", "BUILD", "MODULE.bazel",
|
||||||
"WORKSPACE", "WORKSPACE.bazel",
|
"WORKSPACE", "WORKSPACE.bazel", "WORKSPACE.bzlmod",
|
||||||
]),
|
]),
|
||||||
(&["bitbake"], &["*.bb", "*.bbappend", "*.bbclass", "*.conf", "*.inc"]),
|
(&["bitbake"], &["*.bb", "*.bbappend", "*.bbclass", "*.conf", "*.inc"]),
|
||||||
(&["brotli"], &["*.br"]),
|
(&["brotli"], &["*.br"]),
|
||||||
@@ -118,6 +118,7 @@ pub(crate) const DEFAULT_TYPES: &[(&[&str], &[&str])] = &[
|
|||||||
(&["jupyter"], &["*.ipynb", "*.jpynb"]),
|
(&["jupyter"], &["*.ipynb", "*.jpynb"]),
|
||||||
(&["k"], &["*.k"]),
|
(&["k"], &["*.k"]),
|
||||||
(&["kotlin"], &["*.kt", "*.kts"]),
|
(&["kotlin"], &["*.kt", "*.kts"]),
|
||||||
|
(&["lean"], &["*.lean"]),
|
||||||
(&["less"], &["*.less"]),
|
(&["less"], &["*.less"]),
|
||||||
(&["license"], &[
|
(&["license"], &[
|
||||||
// General
|
// General
|
||||||
@@ -158,6 +159,7 @@ pub(crate) const DEFAULT_TYPES: &[(&[&str], &[&str])] = &[
|
|||||||
"[Gg][Nn][Uu]makefile", "[Mm]akefile",
|
"[Gg][Nn][Uu]makefile", "[Mm]akefile",
|
||||||
"[Gg][Nn][Uu]makefile.am", "[Mm]akefile.am",
|
"[Gg][Nn][Uu]makefile.am", "[Mm]akefile.am",
|
||||||
"[Gg][Nn][Uu]makefile.in", "[Mm]akefile.in",
|
"[Gg][Nn][Uu]makefile.in", "[Mm]akefile.in",
|
||||||
|
"Makefile.*",
|
||||||
"*.mk", "*.mak"
|
"*.mk", "*.mak"
|
||||||
]),
|
]),
|
||||||
(&["mako"], &["*.mako", "*.mao"]),
|
(&["mako"], &["*.mako", "*.mao"]),
|
||||||
@@ -172,7 +174,7 @@ pub(crate) const DEFAULT_TYPES: &[(&[&str], &[&str])] = &[
|
|||||||
"*.mdx",
|
"*.mdx",
|
||||||
]),
|
]),
|
||||||
(&["matlab"], &["*.m"]),
|
(&["matlab"], &["*.m"]),
|
||||||
(&["meson"], &["meson.build", "meson_options.txt"]),
|
(&["meson"], &["meson.build", "meson_options.txt", "meson.options"]),
|
||||||
(&["minified"], &["*.min.html", "*.min.css", "*.min.js"]),
|
(&["minified"], &["*.min.html", "*.min.css", "*.min.js"]),
|
||||||
(&["mint"], &["*.mint"]),
|
(&["mint"], &["*.mint"]),
|
||||||
(&["mk"], &["mkfile"]),
|
(&["mk"], &["mkfile"]),
|
||||||
@@ -180,7 +182,7 @@ pub(crate) const DEFAULT_TYPES: &[(&[&str], &[&str])] = &[
|
|||||||
(&["motoko"], &["*.mo"]),
|
(&["motoko"], &["*.mo"]),
|
||||||
(&["msbuild"], &[
|
(&["msbuild"], &[
|
||||||
"*.csproj", "*.fsproj", "*.vcxproj", "*.proj", "*.props", "*.targets",
|
"*.csproj", "*.fsproj", "*.vcxproj", "*.proj", "*.props", "*.targets",
|
||||||
"*.sln",
|
"*.sln", "*.slnf"
|
||||||
]),
|
]),
|
||||||
(&["nim"], &["*.nim", "*.nimf", "*.nimble", "*.nims"]),
|
(&["nim"], &["*.nim", "*.nimf", "*.nimble", "*.nims"]),
|
||||||
(&["nix"], &["*.nix"]),
|
(&["nix"], &["*.nix"]),
|
||||||
@@ -226,11 +228,12 @@ pub(crate) const DEFAULT_TYPES: &[(&[&str], &[&str])] = &[
|
|||||||
// Idiomatic files
|
// Idiomatic files
|
||||||
"config.ru", "Gemfile", ".irbrc", "Rakefile",
|
"config.ru", "Gemfile", ".irbrc", "Rakefile",
|
||||||
// Extensions
|
// Extensions
|
||||||
"*.gemspec", "*.rb", "*.rbw"
|
"*.gemspec", "*.rb", "*.rbw", "*.rake"
|
||||||
]),
|
]),
|
||||||
(&["rust"], &["*.rs"]),
|
(&["rust"], &["*.rs"]),
|
||||||
(&["sass"], &["*.sass", "*.scss"]),
|
(&["sass"], &["*.sass", "*.scss"]),
|
||||||
(&["scala"], &["*.scala", "*.sbt"]),
|
(&["scala"], &["*.scala", "*.sbt"]),
|
||||||
|
(&["seed7"], &["*.sd7", "*.s7i"]),
|
||||||
(&["sh"], &[
|
(&["sh"], &[
|
||||||
// Portable/misc. init files
|
// Portable/misc. init files
|
||||||
".login", ".logout", ".profile", "profile",
|
".login", ".logout", ".profile", "profile",
|
||||||
@@ -264,6 +267,7 @@ pub(crate) const DEFAULT_TYPES: &[(&[&str], &[&str])] = &[
|
|||||||
(&["sql"], &["*.sql", "*.psql"]),
|
(&["sql"], &["*.sql", "*.psql"]),
|
||||||
(&["stylus"], &["*.styl"]),
|
(&["stylus"], &["*.styl"]),
|
||||||
(&["sv"], &["*.v", "*.vg", "*.sv", "*.svh", "*.h"]),
|
(&["sv"], &["*.v", "*.vg", "*.sv", "*.svh", "*.h"]),
|
||||||
|
(&["svelte"], &["*.svelte", "*.svelte.ts"]),
|
||||||
(&["svg"], &["*.svg"]),
|
(&["svg"], &["*.svg"]),
|
||||||
(&["swift"], &["*.swift"]),
|
(&["swift"], &["*.swift"]),
|
||||||
(&["swig"], &["*.def", "*.i"]),
|
(&["swig"], &["*.def", "*.i"]),
|
||||||
@@ -288,6 +292,7 @@ pub(crate) const DEFAULT_TYPES: &[(&[&str], &[&str])] = &[
|
|||||||
(&["twig"], &["*.twig"]),
|
(&["twig"], &["*.twig"]),
|
||||||
(&["txt"], &["*.txt"]),
|
(&["txt"], &["*.txt"]),
|
||||||
(&["typoscript"], &["*.typoscript", "*.ts"]),
|
(&["typoscript"], &["*.typoscript", "*.ts"]),
|
||||||
|
(&["typst"], &["*.typ"]),
|
||||||
(&["usd"], &["*.usd", "*.usda", "*.usdc"]),
|
(&["usd"], &["*.usd", "*.usda", "*.usdc"]),
|
||||||
(&["v"], &["*.v", "*.vsh"]),
|
(&["v"], &["*.v", "*.vsh"]),
|
||||||
(&["vala"], &["*.vala"]),
|
(&["vala"], &["*.vala"]),
|
||||||
@@ -301,7 +306,9 @@ pub(crate) const DEFAULT_TYPES: &[(&[&str], &[&str])] = &[
|
|||||||
(&["vimscript"], &[
|
(&["vimscript"], &[
|
||||||
"*.vim", ".vimrc", ".gvimrc", "vimrc", "gvimrc", "_vimrc", "_gvimrc",
|
"*.vim", ".vimrc", ".gvimrc", "vimrc", "gvimrc", "_vimrc", "_gvimrc",
|
||||||
]),
|
]),
|
||||||
|
(&["vue"], &["*.vue"]),
|
||||||
(&["webidl"], &["*.idl", "*.webidl", "*.widl"]),
|
(&["webidl"], &["*.idl", "*.webidl", "*.widl"]),
|
||||||
|
(&["wgsl"], &["*.wgsl"]),
|
||||||
(&["wiki"], &["*.mediawiki", "*.wiki"]),
|
(&["wiki"], &["*.mediawiki", "*.wiki"]),
|
||||||
(&["xml"], &[
|
(&["xml"], &[
|
||||||
"*.xml", "*.xml.dist", "*.dtd", "*.xsl", "*.xslt", "*.xsd", "*.xjb",
|
"*.xml", "*.xml.dist", "*.dtd", "*.xsl", "*.xslt", "*.xsd", "*.xjb",
|
||||||
|
@@ -19,7 +19,7 @@ use std::{
|
|||||||
fs::{File, FileType},
|
fs::{File, FileType},
|
||||||
io::{self, BufRead},
|
io::{self, BufRead},
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
sync::{Arc, RwLock},
|
sync::{Arc, RwLock, Weak},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
@@ -34,11 +34,13 @@ use crate::{
|
|||||||
/// IgnoreMatch represents information about where a match came from when using
|
/// IgnoreMatch represents information about where a match came from when using
|
||||||
/// the `Ignore` matcher.
|
/// the `Ignore` matcher.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
|
#[allow(dead_code)]
|
||||||
pub(crate) struct IgnoreMatch<'a>(IgnoreMatchInner<'a>);
|
pub(crate) struct IgnoreMatch<'a>(IgnoreMatchInner<'a>);
|
||||||
|
|
||||||
/// IgnoreMatchInner describes precisely where the match information came from.
|
/// IgnoreMatchInner describes precisely where the match information came from.
|
||||||
/// This is private to allow expansion to more matchers in the future.
|
/// This is private to allow expansion to more matchers in the future.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
|
#[allow(dead_code)]
|
||||||
enum IgnoreMatchInner<'a> {
|
enum IgnoreMatchInner<'a> {
|
||||||
Override(overrides::Glob<'a>),
|
Override(overrides::Glob<'a>),
|
||||||
Gitignore(&'a gitignore::Glob),
|
Gitignore(&'a gitignore::Glob),
|
||||||
@@ -99,7 +101,7 @@ struct IgnoreInner {
|
|||||||
/// Note that this is never used during matching, only when adding new
|
/// Note that this is never used during matching, only when adding new
|
||||||
/// parent directory matchers. This avoids needing to rebuild glob sets for
|
/// parent directory matchers. This avoids needing to rebuild glob sets for
|
||||||
/// parent directories if many paths are being searched.
|
/// parent directories if many paths are being searched.
|
||||||
compiled: Arc<RwLock<HashMap<OsString, Ignore>>>,
|
compiled: Arc<RwLock<HashMap<OsString, Weak<IgnoreInner>>>>,
|
||||||
/// The path to the directory that this matcher was built from.
|
/// The path to the directory that this matcher was built from.
|
||||||
dir: PathBuf,
|
dir: PathBuf,
|
||||||
/// An override matcher (default is empty).
|
/// An override matcher (default is empty).
|
||||||
@@ -198,9 +200,11 @@ impl Ignore {
|
|||||||
let mut ig = self.clone();
|
let mut ig = self.clone();
|
||||||
for parent in parents.into_iter().rev() {
|
for parent in parents.into_iter().rev() {
|
||||||
let mut compiled = self.0.compiled.write().unwrap();
|
let mut compiled = self.0.compiled.write().unwrap();
|
||||||
if let Some(prebuilt) = compiled.get(parent.as_os_str()) {
|
if let Some(weak) = compiled.get(parent.as_os_str()) {
|
||||||
ig = prebuilt.clone();
|
if let Some(prebuilt) = weak.upgrade() {
|
||||||
continue;
|
ig = Ignore(prebuilt);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
let (mut igtmp, err) = ig.add_child_path(parent);
|
let (mut igtmp, err) = ig.add_child_path(parent);
|
||||||
errs.maybe_push(err);
|
errs.maybe_push(err);
|
||||||
@@ -208,12 +212,16 @@ impl Ignore {
|
|||||||
igtmp.absolute_base = Some(absolute_base.clone());
|
igtmp.absolute_base = Some(absolute_base.clone());
|
||||||
igtmp.has_git =
|
igtmp.has_git =
|
||||||
if self.0.opts.require_git && self.0.opts.git_ignore {
|
if self.0.opts.require_git && self.0.opts.git_ignore {
|
||||||
parent.join(".git").exists()
|
parent.join(".git").exists() || parent.join(".jj").exists()
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
};
|
};
|
||||||
ig = Ignore(Arc::new(igtmp));
|
let ig_arc = Arc::new(igtmp);
|
||||||
compiled.insert(parent.as_os_str().to_os_string(), ig.clone());
|
ig = Ignore(ig_arc.clone());
|
||||||
|
compiled.insert(
|
||||||
|
parent.as_os_str().to_os_string(),
|
||||||
|
Arc::downgrade(&ig_arc),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
(ig, errs.into_error_option())
|
(ig, errs.into_error_option())
|
||||||
}
|
}
|
||||||
@@ -243,7 +251,7 @@ impl Ignore {
|
|||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
let has_git = git_type.map(|_| true).unwrap_or(false);
|
let has_git = git_type.is_some() || dir.join(".jj").exists();
|
||||||
|
|
||||||
let mut errs = PartialErrorBuilder::default();
|
let mut errs = PartialErrorBuilder::default();
|
||||||
let custom_ig_matcher = if self.0.custom_ignore_filenames.is_empty() {
|
let custom_ig_matcher = if self.0.custom_ignore_filenames.is_empty() {
|
||||||
@@ -282,6 +290,7 @@ impl Ignore {
|
|||||||
errs.maybe_push(err);
|
errs.maybe_push(err);
|
||||||
m
|
m
|
||||||
};
|
};
|
||||||
|
|
||||||
let gi_exclude_matcher = if !self.0.opts.git_exclude {
|
let gi_exclude_matcher = if !self.0.opts.git_exclude {
|
||||||
Gitignore::empty()
|
Gitignore::empty()
|
||||||
} else {
|
} else {
|
||||||
@@ -453,21 +462,23 @@ impl Ignore {
|
|||||||
// off of `path`. Overall, this seems a little ham-fisted, but
|
// off of `path`. Overall, this seems a little ham-fisted, but
|
||||||
// it does fix a nasty bug. It should do fine until we overhaul
|
// it does fix a nasty bug. It should do fine until we overhaul
|
||||||
// this crate.
|
// this crate.
|
||||||
let dirpath = self.0.dir.as_path();
|
let path = abs_parent_path.join(
|
||||||
let path_prefix = match strip_prefix("./", dirpath) {
|
self.parents()
|
||||||
None => dirpath,
|
.take_while(|ig| !ig.0.is_absolute_parent)
|
||||||
Some(stripped_dot_slash) => stripped_dot_slash,
|
.last()
|
||||||
};
|
.map_or(path, |ig| {
|
||||||
let path = match strip_prefix(path_prefix, path) {
|
strip_if_is_prefix(
|
||||||
None => abs_parent_path.join(path),
|
"/",
|
||||||
Some(p) => {
|
strip_if_is_prefix(
|
||||||
let p = match strip_prefix("/", p) {
|
strip_if_is_prefix(
|
||||||
None => p,
|
"./",
|
||||||
Some(p) => p,
|
ig.0.dir.as_path(),
|
||||||
};
|
),
|
||||||
abs_parent_path.join(p)
|
path,
|
||||||
}
|
),
|
||||||
};
|
)
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
for ig in
|
for ig in
|
||||||
self.parents().skip_while(|ig| !ig.0.is_absolute_parent)
|
self.parents().skip_while(|ig| !ig.0.is_absolute_parent)
|
||||||
@@ -866,6 +877,15 @@ fn resolve_git_commondir(
|
|||||||
Ok(commondir_abs)
|
Ok(commondir_abs)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Strips `prefix` from `path` if it's a prefix, otherwise returns `path`
|
||||||
|
/// unchanged.
|
||||||
|
fn strip_if_is_prefix<'a, P: AsRef<Path> + ?Sized>(
|
||||||
|
prefix: &'a P,
|
||||||
|
path: &'a Path,
|
||||||
|
) -> &'a Path {
|
||||||
|
strip_prefix(prefix, path).map_or(path, |p| p)
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::{io::Write, path::Path};
|
use std::{io::Write, path::Path};
|
||||||
@@ -935,6 +955,19 @@ mod tests {
|
|||||||
assert!(ig.matched("baz", false).is_none());
|
assert!(ig.matched("baz", false).is_none());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn gitignore_with_jj() {
|
||||||
|
let td = tmpdir();
|
||||||
|
mkdirp(td.path().join(".jj"));
|
||||||
|
wfile(td.path().join(".gitignore"), "foo\n!bar");
|
||||||
|
|
||||||
|
let (ig, err) = IgnoreBuilder::new().build().add_child(td.path());
|
||||||
|
assert!(err.is_none());
|
||||||
|
assert!(ig.matched("foo", false).is_ignore());
|
||||||
|
assert!(ig.matched("bar", false).is_whitelist());
|
||||||
|
assert!(ig.matched("baz", false).is_none());
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn gitignore_no_git() {
|
fn gitignore_no_git() {
|
||||||
let td = tmpdir();
|
let td = tmpdir();
|
||||||
|
@@ -390,6 +390,7 @@ impl GitignoreBuilder {
|
|||||||
Err(err) => return Some(Error::Io(err).with_path(path)),
|
Err(err) => return Some(Error::Io(err).with_path(path)),
|
||||||
Ok(file) => file,
|
Ok(file) => file,
|
||||||
};
|
};
|
||||||
|
log::debug!("opened gitignore file: {}", path.display());
|
||||||
let rdr = BufReader::new(file);
|
let rdr = BufReader::new(file);
|
||||||
let mut errs = PartialErrorBuilder::default();
|
let mut errs = PartialErrorBuilder::default();
|
||||||
for (i, line) in rdr.lines().enumerate() {
|
for (i, line) in rdr.lines().enumerate() {
|
||||||
@@ -401,6 +402,12 @@ impl GitignoreBuilder {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Match Git's handling of .gitignore files that begin with the Unicode BOM
|
||||||
|
const UTF8_BOM: &str = "\u{feff}";
|
||||||
|
let line =
|
||||||
|
if i == 0 { line.trim_start_matches(UTF8_BOM) } else { &line };
|
||||||
|
|
||||||
if let Err(err) = self.add_line(Some(path.to_path_buf()), &line) {
|
if let Err(err) = self.add_line(Some(path.to_path_buf()), &line) {
|
||||||
errs.push(err.tagged(path, lineno));
|
errs.push(err.tagged(path, lineno));
|
||||||
}
|
}
|
||||||
|
@@ -527,7 +527,7 @@ mod tests {
|
|||||||
|
|
||||||
let tmpdir = env::temp_dir();
|
let tmpdir = env::temp_dir();
|
||||||
for _ in 0..TRIES {
|
for _ in 0..TRIES {
|
||||||
let count = COUNTER.fetch_add(1, Ordering::SeqCst);
|
let count = COUNTER.fetch_add(1, Ordering::Relaxed);
|
||||||
let path = tmpdir.join("rust-ignore").join(count.to_string());
|
let path = tmpdir.join("rust-ignore").join(count.to_string());
|
||||||
if path.is_dir() {
|
if path.is_dir() {
|
||||||
continue;
|
continue;
|
||||||
|
@@ -23,9 +23,11 @@ use crate::{
|
|||||||
/// The lifetime `'a` refers to the lifetime of the matcher that produced
|
/// The lifetime `'a` refers to the lifetime of the matcher that produced
|
||||||
/// this glob.
|
/// this glob.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
|
#[allow(dead_code)]
|
||||||
pub struct Glob<'a>(GlobInner<'a>);
|
pub struct Glob<'a>(GlobInner<'a>);
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
|
#[allow(dead_code)]
|
||||||
enum GlobInner<'a> {
|
enum GlobInner<'a> {
|
||||||
/// No glob matched, but the file path should still be ignored.
|
/// No glob matched, but the file path should still be ignored.
|
||||||
UnmatchedIgnore,
|
UnmatchedIgnore,
|
||||||
|
@@ -11,7 +11,7 @@ use std::{
|
|||||||
use {
|
use {
|
||||||
crossbeam_deque::{Stealer, Worker as Deque},
|
crossbeam_deque::{Stealer, Worker as Deque},
|
||||||
same_file::Handle,
|
same_file::Handle,
|
||||||
walkdir::{self, WalkDir},
|
walkdir::WalkDir,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
@@ -591,7 +591,7 @@ impl WalkBuilder {
|
|||||||
///
|
///
|
||||||
/// Note that this *doesn't* return something that implements `Iterator`.
|
/// Note that this *doesn't* return something that implements `Iterator`.
|
||||||
/// Instead, the returned value must be run with a closure. e.g.,
|
/// Instead, the returned value must be run with a closure. e.g.,
|
||||||
/// `builder.build_parallel().run(|| |path| println!("{:?}", path))`.
|
/// `builder.build_parallel().run(|| |path| { println!("{path:?}"); WalkState::Continue })`.
|
||||||
pub fn build_parallel(&self) -> WalkParallel {
|
pub fn build_parallel(&self) -> WalkParallel {
|
||||||
WalkParallel {
|
WalkParallel {
|
||||||
paths: self.paths.clone().into_iter(),
|
paths: self.paths.clone().into_iter(),
|
||||||
@@ -1305,7 +1305,7 @@ impl WalkParallel {
|
|||||||
|
|
||||||
fn threads(&self) -> usize {
|
fn threads(&self) -> usize {
|
||||||
if self.threads == 0 {
|
if self.threads == 0 {
|
||||||
2
|
std::thread::available_parallelism().map_or(1, |n| n.get()).min(12)
|
||||||
} else {
|
} else {
|
||||||
self.threads
|
self.threads
|
||||||
}
|
}
|
||||||
@@ -1420,8 +1420,11 @@ impl Stack {
|
|||||||
stealers: stealers.clone(),
|
stealers: stealers.clone(),
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
// Distribute the initial messages.
|
// Distribute the initial messages, reverse the order to cancel out
|
||||||
|
// the other reversal caused by the inherent LIFO processing of the
|
||||||
|
// per-thread stacks which are filled here.
|
||||||
init.into_iter()
|
init.into_iter()
|
||||||
|
.rev()
|
||||||
.zip(stacks.iter().cycle())
|
.zip(stacks.iter().cycle())
|
||||||
.for_each(|(m, s)| s.push(m));
|
.for_each(|(m, s)| s.push(m));
|
||||||
stacks
|
stacks
|
||||||
|
2
crates/ignore/tests/gitignore_skip_bom.gitignore
Normal file
2
crates/ignore/tests/gitignore_skip_bom.gitignore
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
ignore/this/path
|
||||||
|
# This file begins with a BOM (U+FEFF)
|
17
crates/ignore/tests/gitignore_skip_bom.rs
Normal file
17
crates/ignore/tests/gitignore_skip_bom.rs
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
use ignore::gitignore::GitignoreBuilder;
|
||||||
|
|
||||||
|
const IGNORE_FILE: &'static str = "tests/gitignore_skip_bom.gitignore";
|
||||||
|
|
||||||
|
/// Skip a Byte-Order Mark (BOM) at the beginning of the file, matching Git's
|
||||||
|
/// behavior.
|
||||||
|
///
|
||||||
|
/// Ref: <https://github.com/BurntSushi/ripgrep/issues/2177>
|
||||||
|
#[test]
|
||||||
|
fn gitignore_skip_bom() {
|
||||||
|
let mut builder = GitignoreBuilder::new("ROOT");
|
||||||
|
let error = builder.add(IGNORE_FILE);
|
||||||
|
assert!(error.is_none(), "failed to open gitignore file");
|
||||||
|
let g = builder.build().unwrap();
|
||||||
|
|
||||||
|
assert!(g.matched("ignore/this/path", false).is_ignore());
|
||||||
|
}
|
@@ -389,6 +389,15 @@ pub trait Captures {
|
|||||||
/// for the overall match.
|
/// for the overall match.
|
||||||
fn get(&self, i: usize) -> Option<Match>;
|
fn get(&self, i: usize) -> Option<Match>;
|
||||||
|
|
||||||
|
/// Return the overall match for the capture.
|
||||||
|
///
|
||||||
|
/// This returns the match for index `0`. That is it is equivalent to
|
||||||
|
/// `get(0).unwrap()`
|
||||||
|
#[inline]
|
||||||
|
fn as_match(&self) -> Match {
|
||||||
|
self.get(0).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns true if and only if these captures are empty. This occurs
|
/// Returns true if and only if these captures are empty. This occurs
|
||||||
/// when `len` is `0`.
|
/// when `len` is `0`.
|
||||||
///
|
///
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "grep-pcre2"
|
name = "grep-pcre2"
|
||||||
version = "0.1.7" #:version
|
version = "0.1.8" #:version
|
||||||
authors = ["Andrew Gallant <jamslam@gmail.com>"]
|
authors = ["Andrew Gallant <jamslam@gmail.com>"]
|
||||||
description = """
|
description = """
|
||||||
Use PCRE2 with the 'grep' crate.
|
Use PCRE2 with the 'grep' crate.
|
||||||
|
@@ -55,7 +55,12 @@ impl RegexMatcherBuilder {
|
|||||||
format!("(?:{})", p.as_ref())
|
format!("(?:{})", p.as_ref())
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
let mut singlepat = pats.join("|");
|
let mut singlepat = if patterns.is_empty() {
|
||||||
|
// A way to spell a pattern that can never match anything.
|
||||||
|
r"[^\S\s]".to_string()
|
||||||
|
} else {
|
||||||
|
pats.join("|")
|
||||||
|
};
|
||||||
if self.case_smart && !has_uppercase_literal(&singlepat) {
|
if self.case_smart && !has_uppercase_literal(&singlepat) {
|
||||||
builder.caseless(true);
|
builder.caseless(true);
|
||||||
}
|
}
|
||||||
@@ -428,7 +433,7 @@ fn has_uppercase_literal(pattern: &str) -> bool {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use grep_matcher::{LineMatchKind, Matcher};
|
use grep_matcher::LineMatchKind;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "grep-printer"
|
name = "grep-printer"
|
||||||
version = "0.1.7" #:version
|
version = "0.2.2" #:version
|
||||||
authors = ["Andrew Gallant <jamslam@gmail.com>"]
|
authors = ["Andrew Gallant <jamslam@gmail.com>"]
|
||||||
description = """
|
description = """
|
||||||
An implementation of the grep crate's Sink trait that provides standard
|
An implementation of the grep crate's Sink trait that provides standard
|
||||||
@@ -21,14 +21,14 @@ serde = ["dep:serde", "dep:serde_json"]
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
bstr = "1.6.2"
|
bstr = "1.6.2"
|
||||||
grep-matcher = { version = "0.1.7", path = "../matcher" }
|
grep-matcher = { version = "0.1.7", path = "../matcher" }
|
||||||
grep-searcher = { version = "0.1.12", path = "../searcher" }
|
grep-searcher = { version = "0.1.14", path = "../searcher" }
|
||||||
log = "0.4.5"
|
log = "0.4.5"
|
||||||
termcolor = "1.3.0"
|
termcolor = "1.3.0"
|
||||||
serde = { version = "1.0.193", optional = true }
|
serde = { version = "1.0.193", optional = true }
|
||||||
serde_json = { version = "1.0.107", optional = true }
|
serde_json = { version = "1.0.107", optional = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
grep-regex = { version = "0.1.12", path = "../regex" }
|
grep-regex = { version = "0.1.13", path = "../regex" }
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
# We want to document all features.
|
# We want to document all features.
|
||||||
|
@@ -65,7 +65,7 @@ impl std::fmt::Display for ColorError {
|
|||||||
f,
|
f,
|
||||||
"unrecognized style attribute '{}'. Choose from: \
|
"unrecognized style attribute '{}'. Choose from: \
|
||||||
nobold, bold, nointense, intense, nounderline, \
|
nobold, bold, nointense, intense, nounderline, \
|
||||||
underline.",
|
underline, noitalic, italic.",
|
||||||
name,
|
name,
|
||||||
),
|
),
|
||||||
ColorError::InvalidFormat(ref original) => write!(
|
ColorError::InvalidFormat(ref original) => write!(
|
||||||
@@ -121,7 +121,7 @@ pub struct ColorSpecs {
|
|||||||
/// `0x`.
|
/// `0x`.
|
||||||
///
|
///
|
||||||
/// Valid style instructions are `nobold`, `bold`, `intense`, `nointense`,
|
/// Valid style instructions are `nobold`, `bold`, `intense`, `nointense`,
|
||||||
/// `underline`, `nounderline`.
|
/// `underline`, `nounderline`, `italic`, `noitalic`.
|
||||||
///
|
///
|
||||||
/// ## Example
|
/// ## Example
|
||||||
///
|
///
|
||||||
@@ -201,6 +201,8 @@ enum Style {
|
|||||||
NoIntense,
|
NoIntense,
|
||||||
Underline,
|
Underline,
|
||||||
NoUnderline,
|
NoUnderline,
|
||||||
|
Italic,
|
||||||
|
NoItalic,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ColorSpecs {
|
impl ColorSpecs {
|
||||||
@@ -286,6 +288,12 @@ impl SpecValue {
|
|||||||
Style::NoUnderline => {
|
Style::NoUnderline => {
|
||||||
cspec.set_underline(false);
|
cspec.set_underline(false);
|
||||||
}
|
}
|
||||||
|
Style::Italic => {
|
||||||
|
cspec.set_italic(true);
|
||||||
|
}
|
||||||
|
Style::NoItalic => {
|
||||||
|
cspec.set_italic(false);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -370,6 +378,8 @@ impl std::str::FromStr for Style {
|
|||||||
"nointense" => Ok(Style::NoIntense),
|
"nointense" => Ok(Style::NoIntense),
|
||||||
"underline" => Ok(Style::Underline),
|
"underline" => Ok(Style::Underline),
|
||||||
"nounderline" => Ok(Style::NoUnderline),
|
"nounderline" => Ok(Style::NoUnderline),
|
||||||
|
"italic" => Ok(Style::Italic),
|
||||||
|
"noitalic" => Ok(Style::NoItalic),
|
||||||
_ => Err(ColorError::UnrecognizedStyle(s.to_string())),
|
_ => Err(ColorError::UnrecognizedStyle(s.to_string())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -702,16 +702,20 @@ impl HyperlinkPath {
|
|||||||
/// Returns a hyperlink path from an OS path.
|
/// Returns a hyperlink path from an OS path.
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
pub(crate) fn from_path(original_path: &Path) -> Option<HyperlinkPath> {
|
pub(crate) fn from_path(original_path: &Path) -> Option<HyperlinkPath> {
|
||||||
// On Windows, Path::canonicalize returns the result of
|
// On Windows, we use `std::path::absolute` instead of `Path::canonicalize`
|
||||||
// GetFinalPathNameByHandleW with VOLUME_NAME_DOS,
|
// as it can be much faster since it does not touch the file system.
|
||||||
// which produces paths such as the following:
|
// It wraps the [`GetFullPathNameW`][1] API, except for verbatim paths
|
||||||
|
// (those which start with `\\?\`, see [the documentation][2] for details).
|
||||||
|
//
|
||||||
|
// Here, we strip any verbatim path prefixes since we cannot use them
|
||||||
|
// in hyperlinks anyway. This can only happen if the user explicitly
|
||||||
|
// supplies a verbatim path as input, which already needs to be absolute:
|
||||||
//
|
//
|
||||||
// \\?\C:\dir\file.txt (local path)
|
// \\?\C:\dir\file.txt (local path)
|
||||||
// \\?\UNC\server\dir\file.txt (network share)
|
// \\?\UNC\server\dir\file.txt (network share)
|
||||||
//
|
//
|
||||||
// The \\?\ prefix comes from VOLUME_NAME_DOS and is constant.
|
// The `\\?\` prefix is constant for verbatim paths, and can be followed
|
||||||
// It is followed either by the drive letter, or by UNC\
|
// by `UNC\` (universal naming convention), which denotes a network share.
|
||||||
// (universal naming convention), which denotes a network share.
|
|
||||||
//
|
//
|
||||||
// Given that the default URL format on Windows is file://{path}
|
// Given that the default URL format on Windows is file://{path}
|
||||||
// we need to return the following from this function:
|
// we need to return the following from this function:
|
||||||
@@ -750,18 +754,19 @@ impl HyperlinkPath {
|
|||||||
//
|
//
|
||||||
// It doesn't parse any other number of slashes in "file//server" as a
|
// It doesn't parse any other number of slashes in "file//server" as a
|
||||||
// network path.
|
// network path.
|
||||||
|
//
|
||||||
|
// [1]: https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getfullpathnamew
|
||||||
|
// [2]: https://learn.microsoft.com/en-us/windows/win32/fileio/naming-a-file
|
||||||
|
|
||||||
const WIN32_NAMESPACE_PREFIX: &str = r"\\?\";
|
const WIN32_NAMESPACE_PREFIX: &str = r"\\?\";
|
||||||
const UNC_PREFIX: &str = r"UNC\";
|
const UNC_PREFIX: &str = r"UNC\";
|
||||||
|
|
||||||
// As for Unix, we canonicalize the path to make sure we have an
|
let path = match std::path::absolute(original_path) {
|
||||||
// absolute path.
|
|
||||||
let path = match original_path.canonicalize() {
|
|
||||||
Ok(path) => path,
|
Ok(path) => path,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
log::debug!(
|
log::debug!(
|
||||||
"hyperlink creation for {:?} failed, error occurred \
|
"hyperlink creation for {:?} failed, error occurred \
|
||||||
during path canonicalization: {}",
|
during conversion to absolute path: {}",
|
||||||
original_path,
|
original_path,
|
||||||
err,
|
err,
|
||||||
);
|
);
|
||||||
@@ -784,24 +789,20 @@ impl HyperlinkPath {
|
|||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
// As the comment above says, we expect all canonicalized paths to
|
|
||||||
// begin with a \\?\. If it doesn't, then something weird is happening
|
|
||||||
// and we should just give up.
|
|
||||||
if !string.starts_with(WIN32_NAMESPACE_PREFIX) {
|
|
||||||
log::debug!(
|
|
||||||
"hyperlink creation for {:?} failed, canonicalization \
|
|
||||||
returned {:?}, which does not start with \\\\?\\",
|
|
||||||
original_path,
|
|
||||||
path,
|
|
||||||
);
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
string = &string[WIN32_NAMESPACE_PREFIX.len()..];
|
|
||||||
|
|
||||||
// And as above, drop the UNC prefix too, but keep the leading slash.
|
// Strip verbatim path prefixes (see the comment above for details).
|
||||||
if string.starts_with(UNC_PREFIX) {
|
if string.starts_with(WIN32_NAMESPACE_PREFIX) {
|
||||||
string = &string[(UNC_PREFIX.len() - 1)..];
|
string = &string[WIN32_NAMESPACE_PREFIX.len()..];
|
||||||
|
|
||||||
|
// Drop the UNC prefix if there is one, but keep the leading slash.
|
||||||
|
if string.starts_with(UNC_PREFIX) {
|
||||||
|
string = &string[(UNC_PREFIX.len() - 1)..];
|
||||||
|
}
|
||||||
|
} else if string.starts_with(r"\\") || string.starts_with(r"//") {
|
||||||
|
// Drop one of the two leading slashes of network paths, it will be added back.
|
||||||
|
string = &string[1..];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Finally, add a leading slash. In the local file case, this turns
|
// Finally, add a leading slash. In the local file case, this turns
|
||||||
// C:\foo\bar into /C:\foo\bar (and then percent encoding turns it into
|
// C:\foo\bar into /C:\foo\bar (and then percent encoding turns it into
|
||||||
// /C:/foo/bar). In the network share case, this turns \share\foo\bar
|
// /C:/foo/bar). In the network share case, this turns \share\foo\bar
|
||||||
@@ -811,6 +812,13 @@ impl HyperlinkPath {
|
|||||||
Some(HyperlinkPath::encode(with_slash.as_bytes()))
|
Some(HyperlinkPath::encode(with_slash.as_bytes()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// For other platforms (not windows, not unix), return None and log a debug message.
|
||||||
|
#[cfg(not(any(windows, unix)))]
|
||||||
|
pub(crate) fn from_path(original_path: &Path) -> Option<HyperlinkPath> {
|
||||||
|
log::debug!("hyperlinks are not supported on this platform");
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
/// Percent-encodes a path.
|
/// Percent-encodes a path.
|
||||||
///
|
///
|
||||||
/// The alphanumeric ASCII characters and "-", ".", "_", "~" are unreserved
|
/// The alphanumeric ASCII characters and "-", ".", "_", "~" are unreserved
|
||||||
@@ -999,4 +1007,33 @@ mod tests {
|
|||||||
err(InvalidVariable("bar{{".to_string())),
|
err(InvalidVariable("bar{{".to_string())),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(windows)]
|
||||||
|
fn convert_to_hyperlink_path() {
|
||||||
|
let convert = |path| {
|
||||||
|
String::from_utf8(
|
||||||
|
HyperlinkPath::from_path(Path::new(path)).unwrap().0,
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(convert(r"C:\dir\file.txt"), "/C:/dir/file.txt");
|
||||||
|
assert_eq!(
|
||||||
|
convert(r"C:\foo\bar\..\other\baz.txt"),
|
||||||
|
"/C:/foo/other/baz.txt"
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(convert(r"\\server\dir\file.txt"), "//server/dir/file.txt");
|
||||||
|
assert_eq!(
|
||||||
|
convert(r"\\server\dir\foo\..\other\file.txt"),
|
||||||
|
"//server/dir/other/file.txt"
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(convert(r"\\?\C:\dir\file.txt"), "/C:/dir/file.txt");
|
||||||
|
assert_eq!(
|
||||||
|
convert(r"\\?\UNC\server\dir\file.txt"),
|
||||||
|
"//server/dir/file.txt"
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
use std::{
|
use std::{
|
||||||
io::{self, Write},
|
io::{self, Write},
|
||||||
path::Path,
|
path::Path,
|
||||||
|
sync::Arc,
|
||||||
time::Instant,
|
time::Instant,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -13,7 +14,8 @@ use {
|
|||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
counter::CounterWriter, jsont, stats::Stats, util::find_iter_at_in_context,
|
counter::CounterWriter, jsont, stats::Stats,
|
||||||
|
util::find_iter_at_in_context, util::Replacer,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// The configuration for the JSON printer.
|
/// The configuration for the JSON printer.
|
||||||
@@ -26,11 +28,17 @@ struct Config {
|
|||||||
pretty: bool,
|
pretty: bool,
|
||||||
max_matches: Option<u64>,
|
max_matches: Option<u64>,
|
||||||
always_begin_end: bool,
|
always_begin_end: bool,
|
||||||
|
replacement: Arc<Option<Vec<u8>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Config {
|
impl Default for Config {
|
||||||
fn default() -> Config {
|
fn default() -> Config {
|
||||||
Config { pretty: false, max_matches: None, always_begin_end: false }
|
Config {
|
||||||
|
pretty: false,
|
||||||
|
max_matches: None,
|
||||||
|
always_begin_end: false,
|
||||||
|
replacement: Arc::new(None),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -98,6 +106,24 @@ impl JSONBuilder {
|
|||||||
self.config.always_begin_end = yes;
|
self.config.always_begin_end = yes;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set the bytes that will be used to replace each occurrence of a match
|
||||||
|
/// found.
|
||||||
|
///
|
||||||
|
/// The replacement bytes given may include references to capturing groups,
|
||||||
|
/// which may either be in index form (e.g., `$2`) or can reference named
|
||||||
|
/// capturing groups if present in the original pattern (e.g., `$foo`).
|
||||||
|
///
|
||||||
|
/// For documentation on the full format, please see the `Capture` trait's
|
||||||
|
/// `interpolate` method in the
|
||||||
|
/// [grep-printer](https://docs.rs/grep-printer) crate.
|
||||||
|
pub fn replacement(
|
||||||
|
&mut self,
|
||||||
|
replacement: Option<Vec<u8>>,
|
||||||
|
) -> &mut JSONBuilder {
|
||||||
|
self.config.replacement = Arc::new(replacement);
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The JSON printer, which emits results in a JSON lines format.
|
/// The JSON printer, which emits results in a JSON lines format.
|
||||||
@@ -256,7 +282,8 @@ impl JSONBuilder {
|
|||||||
/// encoded, then the byte offsets correspond to the data after base64
|
/// encoded, then the byte offsets correspond to the data after base64
|
||||||
/// decoding.) The `submatch` objects are guaranteed to be sorted by their
|
/// decoding.) The `submatch` objects are guaranteed to be sorted by their
|
||||||
/// starting offsets. Note that it is possible for this array to be empty,
|
/// starting offsets. Note that it is possible for this array to be empty,
|
||||||
/// for example, when searching reports inverted matches.
|
/// for example, when searching reports inverted matches. If the configuration
|
||||||
|
/// specifies a replacement, the resulting replacement text is also present.
|
||||||
///
|
///
|
||||||
/// #### Message: **context**
|
/// #### Message: **context**
|
||||||
///
|
///
|
||||||
@@ -286,7 +313,9 @@ impl JSONBuilder {
|
|||||||
/// decoding.) The `submatch` objects are guaranteed to be sorted by
|
/// decoding.) The `submatch` objects are guaranteed to be sorted by
|
||||||
/// their starting offsets. Note that it is possible for this array to be
|
/// their starting offsets. Note that it is possible for this array to be
|
||||||
/// non-empty, for example, when searching reports inverted matches such that
|
/// non-empty, for example, when searching reports inverted matches such that
|
||||||
/// the original matcher could match things in the contextual lines.
|
/// the original matcher could match things in the contextual lines. If the
|
||||||
|
/// configuration specifies a replacemement, the resulting replacement text
|
||||||
|
/// is also present.
|
||||||
///
|
///
|
||||||
/// #### Object: **submatch**
|
/// #### Object: **submatch**
|
||||||
///
|
///
|
||||||
@@ -308,6 +337,10 @@ impl JSONBuilder {
|
|||||||
/// the `lines` field in the
|
/// the `lines` field in the
|
||||||
/// [`match`](#message-match) or [`context`](#message-context)
|
/// [`match`](#message-match) or [`context`](#message-context)
|
||||||
/// messages.
|
/// messages.
|
||||||
|
/// * **replacement** (optional) - An
|
||||||
|
/// [arbitrary data object](#object-arbitrary-data) corresponding to the
|
||||||
|
/// replacement text for this submatch, if the configuration specifies
|
||||||
|
/// a replacement.
|
||||||
///
|
///
|
||||||
/// #### Object: **stats**
|
/// #### Object: **stats**
|
||||||
///
|
///
|
||||||
@@ -447,6 +480,23 @@ impl JSONBuilder {
|
|||||||
/// }
|
/// }
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
|
/// and here's what a match type item would looks like if a replacement text
|
||||||
|
/// of 'Moriarity' was given as a parameter:
|
||||||
|
/// ```json
|
||||||
|
/// {
|
||||||
|
/// "type": "match",
|
||||||
|
/// "data": {
|
||||||
|
/// "path": {"text": "/home/andrew/sherlock"},
|
||||||
|
/// "lines": {"text": "For the Doctor Watsons of this world, as opposed to the Sherlock\n"},
|
||||||
|
/// "line_number": 1,
|
||||||
|
/// "absolute_offset": 0,
|
||||||
|
/// "submatches": [
|
||||||
|
/// {"match": {"text": "Watson"}, "replacement": {"text": "Moriarity"}, "start": 15, "end": 21}
|
||||||
|
/// ]
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct JSON<W> {
|
pub struct JSON<W> {
|
||||||
config: Config,
|
config: Config,
|
||||||
@@ -471,6 +521,7 @@ impl<W: io::Write> JSON<W> {
|
|||||||
) -> JSONSink<'static, 's, M, W> {
|
) -> JSONSink<'static, 's, M, W> {
|
||||||
JSONSink {
|
JSONSink {
|
||||||
matcher,
|
matcher,
|
||||||
|
replacer: Replacer::new(),
|
||||||
json: self,
|
json: self,
|
||||||
path: None,
|
path: None,
|
||||||
start_time: Instant::now(),
|
start_time: Instant::now(),
|
||||||
@@ -497,6 +548,7 @@ impl<W: io::Write> JSON<W> {
|
|||||||
{
|
{
|
||||||
JSONSink {
|
JSONSink {
|
||||||
matcher,
|
matcher,
|
||||||
|
replacer: Replacer::new(),
|
||||||
json: self,
|
json: self,
|
||||||
path: Some(path.as_ref()),
|
path: Some(path.as_ref()),
|
||||||
start_time: Instant::now(),
|
start_time: Instant::now(),
|
||||||
@@ -559,6 +611,7 @@ impl<W> JSON<W> {
|
|||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct JSONSink<'p, 's, M: Matcher, W> {
|
pub struct JSONSink<'p, 's, M: Matcher, W> {
|
||||||
matcher: M,
|
matcher: M,
|
||||||
|
replacer: Replacer<M>,
|
||||||
json: &'s mut JSON<W>,
|
json: &'s mut JSON<W>,
|
||||||
path: Option<&'p Path>,
|
path: Option<&'p Path>,
|
||||||
start_time: Instant,
|
start_time: Instant,
|
||||||
@@ -643,6 +696,31 @@ impl<'p, 's, M: Matcher, W: io::Write> JSONSink<'p, 's, M, W> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// If the configuration specifies a replacement, then this executes the
|
||||||
|
/// replacement, lazily allocating memory if necessary.
|
||||||
|
///
|
||||||
|
/// To access the result of a replacement, use `replacer.replacement()`.
|
||||||
|
fn replace(
|
||||||
|
&mut self,
|
||||||
|
searcher: &Searcher,
|
||||||
|
bytes: &[u8],
|
||||||
|
range: std::ops::Range<usize>,
|
||||||
|
) -> io::Result<()> {
|
||||||
|
self.replacer.clear();
|
||||||
|
if self.json.config.replacement.is_some() {
|
||||||
|
let replacement =
|
||||||
|
(*self.json.config.replacement).as_ref().map(|r| &*r).unwrap();
|
||||||
|
self.replacer.replace_all(
|
||||||
|
searcher,
|
||||||
|
&self.matcher,
|
||||||
|
bytes,
|
||||||
|
range,
|
||||||
|
replacement,
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns true if this printer should quit.
|
/// Returns true if this printer should quit.
|
||||||
///
|
///
|
||||||
/// This implements the logic for handling quitting after seeing a certain
|
/// This implements the logic for handling quitting after seeing a certain
|
||||||
@@ -711,10 +789,15 @@ impl<'p, 's, M: Matcher, W: io::Write> Sink for JSONSink<'p, 's, M, W> {
|
|||||||
mat.buffer(),
|
mat.buffer(),
|
||||||
mat.bytes_range_in_buffer(),
|
mat.bytes_range_in_buffer(),
|
||||||
)?;
|
)?;
|
||||||
|
self.replace(searcher, mat.buffer(), mat.bytes_range_in_buffer())?;
|
||||||
self.stats.add_matches(self.json.matches.len() as u64);
|
self.stats.add_matches(self.json.matches.len() as u64);
|
||||||
self.stats.add_matched_lines(mat.lines().count() as u64);
|
self.stats.add_matched_lines(mat.lines().count() as u64);
|
||||||
|
|
||||||
let submatches = SubMatches::new(mat.bytes(), &self.json.matches);
|
let submatches = SubMatches::new(
|
||||||
|
mat.bytes(),
|
||||||
|
&self.json.matches,
|
||||||
|
self.replacer.replacement(),
|
||||||
|
);
|
||||||
let msg = jsont::Message::Match(jsont::Match {
|
let msg = jsont::Message::Match(jsont::Match {
|
||||||
path: self.path,
|
path: self.path,
|
||||||
lines: mat.bytes(),
|
lines: mat.bytes(),
|
||||||
@@ -740,7 +823,12 @@ impl<'p, 's, M: Matcher, W: io::Write> Sink for JSONSink<'p, 's, M, W> {
|
|||||||
}
|
}
|
||||||
let submatches = if searcher.invert_match() {
|
let submatches = if searcher.invert_match() {
|
||||||
self.record_matches(searcher, ctx.bytes(), 0..ctx.bytes().len())?;
|
self.record_matches(searcher, ctx.bytes(), 0..ctx.bytes().len())?;
|
||||||
SubMatches::new(ctx.bytes(), &self.json.matches)
|
self.replace(searcher, ctx.bytes(), 0..ctx.bytes().len())?;
|
||||||
|
SubMatches::new(
|
||||||
|
ctx.bytes(),
|
||||||
|
&self.json.matches,
|
||||||
|
self.replacer.replacement(),
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
SubMatches::empty()
|
SubMatches::empty()
|
||||||
};
|
};
|
||||||
@@ -831,19 +919,27 @@ enum SubMatches<'a> {
|
|||||||
impl<'a> SubMatches<'a> {
|
impl<'a> SubMatches<'a> {
|
||||||
/// Create a new set of match ranges from a set of matches and the
|
/// Create a new set of match ranges from a set of matches and the
|
||||||
/// corresponding bytes that those matches apply to.
|
/// corresponding bytes that those matches apply to.
|
||||||
fn new(bytes: &'a [u8], matches: &[Match]) -> SubMatches<'a> {
|
fn new(
|
||||||
|
bytes: &'a [u8],
|
||||||
|
matches: &[Match],
|
||||||
|
replacement: Option<(&'a [u8], &'a [Match])>,
|
||||||
|
) -> SubMatches<'a> {
|
||||||
if matches.len() == 1 {
|
if matches.len() == 1 {
|
||||||
let mat = matches[0];
|
let mat = matches[0];
|
||||||
SubMatches::Small([jsont::SubMatch {
|
SubMatches::Small([jsont::SubMatch {
|
||||||
m: &bytes[mat],
|
m: &bytes[mat],
|
||||||
|
replacement: replacement
|
||||||
|
.map(|(rbuf, rmatches)| &rbuf[rmatches[0]]),
|
||||||
start: mat.start(),
|
start: mat.start(),
|
||||||
end: mat.end(),
|
end: mat.end(),
|
||||||
}])
|
}])
|
||||||
} else {
|
} else {
|
||||||
let mut match_ranges = vec![];
|
let mut match_ranges = vec![];
|
||||||
for &mat in matches {
|
for (i, &mat) in matches.iter().enumerate() {
|
||||||
match_ranges.push(jsont::SubMatch {
|
match_ranges.push(jsont::SubMatch {
|
||||||
m: &bytes[mat],
|
m: &bytes[mat],
|
||||||
|
replacement: replacement
|
||||||
|
.map(|(rbuf, rmatches)| &rbuf[rmatches[i]]),
|
||||||
start: mat.start(),
|
start: mat.start(),
|
||||||
end: mat.end(),
|
end: mat.end(),
|
||||||
});
|
});
|
||||||
|
@@ -135,6 +135,7 @@ impl<'a> serde::Serialize for Context<'a> {
|
|||||||
|
|
||||||
pub(crate) struct SubMatch<'a> {
|
pub(crate) struct SubMatch<'a> {
|
||||||
pub(crate) m: &'a [u8],
|
pub(crate) m: &'a [u8],
|
||||||
|
pub(crate) replacement: Option<&'a [u8]>,
|
||||||
pub(crate) start: usize,
|
pub(crate) start: usize,
|
||||||
pub(crate) end: usize,
|
pub(crate) end: usize,
|
||||||
}
|
}
|
||||||
@@ -148,6 +149,9 @@ impl<'a> serde::Serialize for SubMatch<'a> {
|
|||||||
|
|
||||||
let mut state = s.serialize_struct("SubMatch", 3)?;
|
let mut state = s.serialize_struct("SubMatch", 3)?;
|
||||||
state.serialize_field("match", &Data::from_bytes(self.m))?;
|
state.serialize_field("match", &Data::from_bytes(self.m))?;
|
||||||
|
if let Some(r) = self.replacement {
|
||||||
|
state.serialize_field("replacement", &Data::from_bytes(r))?;
|
||||||
|
}
|
||||||
state.serialize_field("start", &self.start)?;
|
state.serialize_field("start", &self.start)?;
|
||||||
state.serialize_field("end", &self.end)?;
|
state.serialize_field("end", &self.end)?;
|
||||||
state.end()
|
state.end()
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "grep-regex"
|
name = "grep-regex"
|
||||||
version = "0.1.12" #:version
|
version = "0.1.13" #:version
|
||||||
authors = ["Andrew Gallant <jamslam@gmail.com>"]
|
authors = ["Andrew Gallant <jamslam@gmail.com>"]
|
||||||
description = """
|
description = """
|
||||||
Use Rust's regex library with the 'grep' crate.
|
Use Rust's regex library with the 'grep' crate.
|
||||||
|
@@ -233,7 +233,7 @@ impl ConfiguredHIR {
|
|||||||
&self.config
|
&self.config
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return a reference to the underyling HIR.
|
/// Return a reference to the underlying HIR.
|
||||||
pub(crate) fn hir(&self) -> &Hir {
|
pub(crate) fn hir(&self) -> &Hir {
|
||||||
&self.hir
|
&self.hir
|
||||||
}
|
}
|
||||||
|
@@ -15,7 +15,7 @@ use crate::{config::ConfiguredHIR, error::Error};
|
|||||||
/// that are in turn used to build a simpler regex that is more amenable to
|
/// that are in turn used to build a simpler regex that is more amenable to
|
||||||
/// optimization.
|
/// optimization.
|
||||||
///
|
///
|
||||||
/// The main idea underyling the validity of this technique is the fact
|
/// The main idea underlying the validity of this technique is the fact
|
||||||
/// that ripgrep searches individuals lines and not across lines. (Unless
|
/// that ripgrep searches individuals lines and not across lines. (Unless
|
||||||
/// -U/--multiline is enabled.) Namely, we can pluck literals out of the regex,
|
/// -U/--multiline is enabled.) Namely, we can pluck literals out of the regex,
|
||||||
/// search for them, find the bounds of the line in which that literal occurs
|
/// search for them, find the bounds of the line in which that literal occurs
|
||||||
@@ -430,6 +430,7 @@ impl Extractor {
|
|||||||
}
|
}
|
||||||
seq1.union(seq2);
|
seq1.union(seq2);
|
||||||
assert!(seq1.len().map_or(true, |x| x <= self.limit_total));
|
assert!(seq1.len().map_or(true, |x| x <= self.limit_total));
|
||||||
|
seq1.prefix = seq1.prefix && seq2.prefix;
|
||||||
seq1
|
seq1
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -586,10 +587,15 @@ impl TSeq {
|
|||||||
lits.iter().any(is_poisonous)
|
lits.iter().any(is_poisonous)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Compare the two sequences and return the one that is believed to be best
|
/// Compare the two sequences and return the one that is believed to be
|
||||||
/// according to a hodge podge of heuristics.
|
/// best according to a hodge podge of heuristics.
|
||||||
fn choose(self, other: TSeq) -> TSeq {
|
fn choose(self, other: TSeq) -> TSeq {
|
||||||
let (seq1, seq2) = (self, other);
|
let (mut seq1, mut seq2) = (self, other);
|
||||||
|
// Whichever one we pick, by virtue of picking one, we choose
|
||||||
|
// to not take the other. So we must consider the result inexact.
|
||||||
|
seq1.make_inexact();
|
||||||
|
seq2.make_inexact();
|
||||||
|
|
||||||
if !seq1.is_finite() {
|
if !seq1.is_finite() {
|
||||||
return seq2;
|
return seq2;
|
||||||
} else if !seq2.is_finite() {
|
} else if !seq2.is_finite() {
|
||||||
@@ -681,7 +687,7 @@ mod tests {
|
|||||||
assert_eq!(e(r"foo"), seq([E("foo")]));
|
assert_eq!(e(r"foo"), seq([E("foo")]));
|
||||||
assert_eq!(e(r"[a-z]foo[a-z]"), seq([I("foo")]));
|
assert_eq!(e(r"[a-z]foo[a-z]"), seq([I("foo")]));
|
||||||
assert_eq!(e(r"[a-z](foo)(bar)[a-z]"), seq([I("foobar")]));
|
assert_eq!(e(r"[a-z](foo)(bar)[a-z]"), seq([I("foobar")]));
|
||||||
assert_eq!(e(r"[a-z]([a-z]foo)(bar[a-z])[a-z]"), seq([I("foobar")]));
|
assert_eq!(e(r"[a-z]([a-z]foo)(bar[a-z])[a-z]"), seq([I("foo")]));
|
||||||
assert_eq!(e(r"[a-z]([a-z]foo)([a-z]foo)[a-z]"), seq([I("foo")]));
|
assert_eq!(e(r"[a-z]([a-z]foo)([a-z]foo)[a-z]"), seq([I("foo")]));
|
||||||
assert_eq!(e(r"(\d{1,3}\.){3}\d{1,3}"), seq([I(".")]));
|
assert_eq!(e(r"(\d{1,3}\.){3}\d{1,3}"), seq([I(".")]));
|
||||||
assert_eq!(e(r"[a-z]([a-z]foo){3}[a-z]"), seq([I("foo")]));
|
assert_eq!(e(r"[a-z]([a-z]foo){3}[a-z]"), seq([I("foo")]));
|
||||||
@@ -689,7 +695,7 @@ mod tests {
|
|||||||
assert_eq!(e(r"[a-z]([a-z]foo[a-z]){3}[a-z]"), seq([I("foo")]));
|
assert_eq!(e(r"[a-z]([a-z]foo[a-z]){3}[a-z]"), seq([I("foo")]));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
e(r"[a-z]([a-z]foo){3}(bar[a-z]){3}[a-z]"),
|
e(r"[a-z]([a-z]foo){3}(bar[a-z]){3}[a-z]"),
|
||||||
seq([I("foobar")])
|
seq([I("foo")])
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -935,14 +941,14 @@ mod tests {
|
|||||||
assert_eq!(Seq::infinite(), e(r"[A-Z]+"));
|
assert_eq!(Seq::infinite(), e(r"[A-Z]+"));
|
||||||
assert_eq!(seq([I("1")]), e(r"1[A-Z]"));
|
assert_eq!(seq([I("1")]), e(r"1[A-Z]"));
|
||||||
assert_eq!(seq([I("1")]), e(r"1[A-Z]2"));
|
assert_eq!(seq([I("1")]), e(r"1[A-Z]2"));
|
||||||
assert_eq!(seq([E("123")]), e(r"[A-Z]+123"));
|
assert_eq!(seq([I("123")]), e(r"[A-Z]+123"));
|
||||||
assert_eq!(seq([I("123")]), e(r"[A-Z]+123[A-Z]+"));
|
assert_eq!(seq([I("123")]), e(r"[A-Z]+123[A-Z]+"));
|
||||||
assert_eq!(Seq::infinite(), e(r"1|[A-Z]|3"));
|
assert_eq!(Seq::infinite(), e(r"1|[A-Z]|3"));
|
||||||
assert_eq!(seq([E("1"), I("2"), E("3")]), e(r"1|2[A-Z]|3"),);
|
assert_eq!(seq([E("1"), I("2"), E("3")]), e(r"1|2[A-Z]|3"),);
|
||||||
assert_eq!(seq([E("1"), I("2"), E("3")]), e(r"1|[A-Z]2[A-Z]|3"),);
|
assert_eq!(seq([E("1"), I("2"), E("3")]), e(r"1|[A-Z]2[A-Z]|3"),);
|
||||||
assert_eq!(seq([E("1"), E("2"), E("3")]), e(r"1|[A-Z]2|3"),);
|
assert_eq!(seq([E("1"), I("2"), E("3")]), e(r"1|[A-Z]2|3"),);
|
||||||
assert_eq!(seq([E("1"), I("2"), E("4")]), e(r"1|2[A-Z]3|4"),);
|
assert_eq!(seq([E("1"), I("2"), E("4")]), e(r"1|2[A-Z]3|4"),);
|
||||||
assert_eq!(seq([E("2")]), e(r"(?:|1)[A-Z]2"));
|
assert_eq!(seq([I("2")]), e(r"(?:|1)[A-Z]2"));
|
||||||
assert_eq!(inexact([I("a")]), e(r"a.z"));
|
assert_eq!(inexact([I("a")]), e(r"a.z"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1005,4 +1011,11 @@ mod tests {
|
|||||||
let s = e(r"foobarfoo|foo| |foofoo");
|
let s = e(r"foobarfoo|foo| |foofoo");
|
||||||
assert_eq!(Seq::infinite(), s);
|
assert_eq!(Seq::infinite(), s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Regression test for: https://github.com/BurntSushi/ripgrep/issues/2884
|
||||||
|
#[test]
|
||||||
|
fn case_insensitive_alternation() {
|
||||||
|
let s = e(r"(?i:e.x|ex)");
|
||||||
|
assert_eq!(s, seq([I("X"), I("x")]));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -552,8 +552,6 @@ impl RegexCaptures {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use grep_matcher::{LineMatchKind, Matcher};
|
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
// Test that enabling word matches does the right thing and demonstrate
|
// Test that enabling word matches does the right thing and demonstrate
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "grep-searcher"
|
name = "grep-searcher"
|
||||||
version = "0.1.12" #:version
|
version = "0.1.14" #:version
|
||||||
authors = ["Andrew Gallant <jamslam@gmail.com>"]
|
authors = ["Andrew Gallant <jamslam@gmail.com>"]
|
||||||
description = """
|
description = """
|
||||||
Fast line oriented regex searching as a library.
|
Fast line oriented regex searching as a library.
|
||||||
@@ -23,11 +23,10 @@ memchr = "2.6.3"
|
|||||||
memmap = { package = "memmap2", version = "0.9.0" }
|
memmap = { package = "memmap2", version = "0.9.0" }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
grep-regex = { version = "0.1.12", path = "../regex" }
|
grep-regex = { version = "0.1.13", path = "../regex" }
|
||||||
regex = "1.9.5"
|
regex = "1.9.5"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
simd-accel = ["encoding_rs/simd-accel"]
|
# These features are DEPRECATED. Runtime dispatch is used for SIMD now.
|
||||||
|
simd-accel = []
|
||||||
# This feature is DEPRECATED. Runtime dispatch is used for SIMD now.
|
|
||||||
avx-accel = []
|
avx-accel = []
|
||||||
|
@@ -538,6 +538,11 @@ fn replace_bytes(
|
|||||||
while let Some(i) = bytes.find_byte(src) {
|
while let Some(i) = bytes.find_byte(src) {
|
||||||
bytes[i] = replacement;
|
bytes[i] = replacement;
|
||||||
bytes = &mut bytes[i + 1..];
|
bytes = &mut bytes[i + 1..];
|
||||||
|
|
||||||
|
// To search for adjacent `src` bytes we use a different strategy.
|
||||||
|
// Since binary data tends to have long runs of NUL terminators,
|
||||||
|
// it is faster to compare one-byte-at-a-time than to stop and start
|
||||||
|
// memchr (through `find_byte`) for every byte in a sequence.
|
||||||
while bytes.get(0) == Some(&src) {
|
while bytes.get(0) == Some(&src) {
|
||||||
bytes[0] = replacement;
|
bytes[0] = replacement;
|
||||||
bytes = &mut bytes[1..];
|
bytes = &mut bytes[1..];
|
||||||
@@ -548,7 +553,7 @@ fn replace_bytes(
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use bstr::{ByteSlice, ByteVec};
|
use bstr::ByteVec;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
@@ -577,6 +582,9 @@ and exhibited clearly, with a label attached.\
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn replace() {
|
fn replace() {
|
||||||
|
assert_eq!(replace_str("", b'b', b'z'), (s(""), None));
|
||||||
|
assert_eq!(replace_str("a", b'a', b'a'), (s("a"), None));
|
||||||
|
assert_eq!(replace_str("a", b'b', b'z'), (s("a"), None));
|
||||||
assert_eq!(replace_str("abc", b'b', b'z'), (s("azc"), Some(1)));
|
assert_eq!(replace_str("abc", b'b', b'z'), (s("azc"), Some(1)));
|
||||||
assert_eq!(replace_str("abb", b'b', b'z'), (s("azz"), Some(1)));
|
assert_eq!(replace_str("abb", b'b', b'z'), (s("azz"), Some(1)));
|
||||||
assert_eq!(replace_str("aba", b'a', b'z'), (s("zbz"), Some(0)));
|
assert_eq!(replace_str("aba", b'a', b'z'), (s("zbz"), Some(0)));
|
||||||
|
@@ -198,8 +198,6 @@ fn preceding_by_pos(
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use grep_matcher::Match;
|
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
const SHERLOCK: &'static str = "\
|
const SHERLOCK: &'static str = "\
|
||||||
|
@@ -612,6 +612,17 @@ impl<'s, M: Matcher, S: Sink> Core<'s, M, S> {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if let Some(line_term) = self.matcher.line_terminator() {
|
if let Some(line_term) = self.matcher.line_terminator() {
|
||||||
|
// FIXME: This works around a bug in grep-regex where it does
|
||||||
|
// not set the line terminator of the regex itself, and thus
|
||||||
|
// line anchors like `(?m:^)` and `(?m:$)` will not match
|
||||||
|
// anything except for `\n`. So for now, we just disable the fast
|
||||||
|
// line-by-line searcher which requires the regex to be able to
|
||||||
|
// deal with line terminators correctly. The slow line-by-line
|
||||||
|
// searcher strips line terminators and thus absolves the regex
|
||||||
|
// engine from needing to care about whether they are `\n` or NUL.
|
||||||
|
if line_term.as_byte() == b'\x00' {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
if line_term == self.config.line_term {
|
if line_term == self.config.line_term {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@@ -7,7 +7,6 @@ use std::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use {
|
use {
|
||||||
encoding_rs,
|
|
||||||
encoding_rs_io::DecodeReaderBytesBuilder,
|
encoding_rs_io::DecodeReaderBytesBuilder,
|
||||||
grep_matcher::{LineTerminator, Match, Matcher},
|
grep_matcher::{LineTerminator, Match, Matcher},
|
||||||
};
|
};
|
||||||
@@ -1005,6 +1004,7 @@ fn slice_has_bom(slice: &[u8]) -> bool {
|
|||||||
None => return false,
|
None => return false,
|
||||||
Some((enc, _)) => enc,
|
Some((enc, _)) => enc,
|
||||||
};
|
};
|
||||||
|
log::trace!("found byte-order mark (BOM) for encoding {enc:?}");
|
||||||
[encoding_rs::UTF_16LE, encoding_rs::UTF_16BE, encoding_rs::UTF_8]
|
[encoding_rs::UTF_16LE, encoding_rs::UTF_16BE, encoding_rs::UTF_8]
|
||||||
.contains(&enc)
|
.contains(&enc)
|
||||||
}
|
}
|
||||||
|
@@ -725,8 +725,6 @@ impl TesterConfig {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use grep_matcher::{Match, Matcher};
|
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
fn m(start: usize, end: usize) -> Match {
|
fn m(start: usize, end: usize) -> Match {
|
||||||
|
317
doc/rg.1.txt.tpl
317
doc/rg.1.txt.tpl
@@ -1,317 +0,0 @@
|
|||||||
rg(1)
|
|
||||||
=====
|
|
||||||
|
|
||||||
Name
|
|
||||||
----
|
|
||||||
rg - recursively search the current directory for lines matching a pattern
|
|
||||||
|
|
||||||
|
|
||||||
Synopsis
|
|
||||||
--------
|
|
||||||
*rg* [_OPTIONS_] _PATTERN_ [_PATH_...]
|
|
||||||
|
|
||||||
*rg* [_OPTIONS_] *-e* _PATTERN_... [_PATH_...]
|
|
||||||
|
|
||||||
*rg* [_OPTIONS_] *-f* _PATTERNFILE_... [_PATH_...]
|
|
||||||
|
|
||||||
*rg* [_OPTIONS_] *--files* [_PATH_...]
|
|
||||||
|
|
||||||
*rg* [_OPTIONS_] *--type-list*
|
|
||||||
|
|
||||||
*command* | *rg* [_OPTIONS_] _PATTERN_
|
|
||||||
|
|
||||||
*rg* [_OPTIONS_] *--help*
|
|
||||||
|
|
||||||
*rg* [_OPTIONS_] *--version*
|
|
||||||
|
|
||||||
|
|
||||||
DESCRIPTION
|
|
||||||
-----------
|
|
||||||
ripgrep (rg) recursively searches the current directory for a regex pattern.
|
|
||||||
By default, ripgrep will respect your .gitignore and automatically skip hidden
|
|
||||||
files/directories and binary files.
|
|
||||||
|
|
||||||
ripgrep's default regex engine uses finite automata and guarantees linear
|
|
||||||
time searching. Because of this, features like backreferences and arbitrary
|
|
||||||
look-around are not supported. However, if ripgrep is built with PCRE2, then
|
|
||||||
the *--pcre2* flag can be used to enable backreferences and look-around.
|
|
||||||
|
|
||||||
ripgrep supports configuration files. Set *RIPGREP_CONFIG_PATH* to a
|
|
||||||
configuration file. The file can specify one shell argument per line. Lines
|
|
||||||
starting with *#* are ignored. For more details, see the man page or the
|
|
||||||
*README*.
|
|
||||||
|
|
||||||
ripgrep will automatically detect if stdin exists and search stdin for a regex
|
|
||||||
pattern, e.g. *ls | rg foo*. In some environments, stdin may exist when it
|
|
||||||
shouldn't. To turn off stdin detection explicitly specify the directory to
|
|
||||||
search, e.g. *rg foo ./*.
|
|
||||||
|
|
||||||
Tip: to disable all smart filtering and make ripgrep behave a bit more like
|
|
||||||
classical grep, use *rg -uuu*.
|
|
||||||
|
|
||||||
|
|
||||||
REGEX SYNTAX
|
|
||||||
------------
|
|
||||||
ripgrep uses Rust's regex engine by default, which documents its syntax:
|
|
||||||
https://docs.rs/regex/*/regex/#syntax
|
|
||||||
|
|
||||||
ripgrep uses byte-oriented regexes, which has some additional documentation:
|
|
||||||
https://docs.rs/regex/*/regex/bytes/index.html#syntax
|
|
||||||
|
|
||||||
To a first approximation, ripgrep uses Perl-like regexes without look-around or
|
|
||||||
backreferences. This makes them very similar to the "extended" (ERE) regular
|
|
||||||
expressions supported by *egrep*, but with a few additional features like
|
|
||||||
Unicode character classes.
|
|
||||||
|
|
||||||
If you're using ripgrep with the *--pcre2* flag, then please consult
|
|
||||||
https://www.pcre.org or the PCRE2 man pages for documentation on the supported
|
|
||||||
syntax.
|
|
||||||
|
|
||||||
|
|
||||||
POSITIONAL ARGUMENTS
|
|
||||||
--------------------
|
|
||||||
_PATTERN_::
|
|
||||||
A regular expression used for searching. To match a pattern beginning with a
|
|
||||||
dash, use the -e/--regexp option.
|
|
||||||
|
|
||||||
_PATH_::
|
|
||||||
A file or directory to search. Directories are searched recursively. File
|
|
||||||
paths specified explicitly on the command line override glob and ignore
|
|
||||||
rules.
|
|
||||||
|
|
||||||
|
|
||||||
OPTIONS
|
|
||||||
-------
|
|
||||||
Note that many options can be disabled via flags. In some cases, those flags
|
|
||||||
are not listed in a first class way below. For example, the *--column*
|
|
||||||
flag (listed below) enables column numbers in ripgrep's output, but the
|
|
||||||
*--no-column* flag (not listed below) disables them. The reverse can also
|
|
||||||
exist. For example, the *--no-ignore* flag (listed below) disables ripgrep's
|
|
||||||
*gitignore* logic, but the *--ignore* flag (not listed below) enables it. These
|
|
||||||
flags are useful for overriding a ripgrep configuration file on the command
|
|
||||||
line. Each flag's documentation notes whether an inverted flag exists. In all
|
|
||||||
cases, the flag specified last takes precedence.
|
|
||||||
|
|
||||||
{OPTIONS}
|
|
||||||
|
|
||||||
|
|
||||||
EXIT STATUS
|
|
||||||
-----------
|
|
||||||
If ripgrep finds a match, then the exit status of the program is 0. If no match
|
|
||||||
could be found, then the exit status is 1. If an error occurred, then the exit
|
|
||||||
status is always 2 unless ripgrep was run with the *--quiet* flag and a match
|
|
||||||
was found. In summary:
|
|
||||||
|
|
||||||
* `0` exit status occurs only when at least one match was found, and if
|
|
||||||
no error occurred, unless *--quiet* was given.
|
|
||||||
* `1` exit status occurs only when no match was found and no error occurred.
|
|
||||||
* `2` exit status occurs when an error occurred. This is true for both
|
|
||||||
catastrophic errors (e.g., a regex syntax error) and for soft errors (e.g.,
|
|
||||||
unable to read a file).
|
|
||||||
|
|
||||||
|
|
||||||
AUTOMATIC FILTERING
|
|
||||||
-------------------
|
|
||||||
TL;DR - To disable automatic filtering, use 'rg -uuu'.
|
|
||||||
|
|
||||||
One of ripgrep's most important features is its automatic smart filtering.
|
|
||||||
It is the most apparent differentiating feature between ripgrep and other tools
|
|
||||||
like 'grep'. As such, its behavior may be surprising to users that aren't
|
|
||||||
expecting it.
|
|
||||||
|
|
||||||
ripgrep does four types of filtering automatically:
|
|
||||||
|
|
||||||
1. Files and directories that match ignore rules are not searched.
|
|
||||||
2. Hidden files and directories are not searched.
|
|
||||||
3. Binary files (files with a 'NUL' byte) are not searched.
|
|
||||||
4. Symbolic links are not followed.
|
|
||||||
|
|
||||||
The first type of filtering is the most sophisticated. ripgrep will attempt to
|
|
||||||
respect your gitignore rules as faithfully as possible. In particular, this
|
|
||||||
includes the following:
|
|
||||||
|
|
||||||
* Any global rules, e.g., in '$HOME/.config/git/ignore'.
|
|
||||||
* Any rules in '.gitignore'.
|
|
||||||
* Any local rules, e.g., in '.git/info/exclude'.
|
|
||||||
|
|
||||||
In some cases, ripgrep and git will not always be in sync in terms of which
|
|
||||||
files are ignored. For example, a file that is ignored via '.gitignore' but is
|
|
||||||
tracked by git would not be searched by ripgrep even though git tracks it. This
|
|
||||||
is unlikely to ever be fixed. Instead, you should either make sure your exclude
|
|
||||||
rules match the files you track precisely, or otherwise use 'git grep' for
|
|
||||||
search.
|
|
||||||
|
|
||||||
Additional ignore rules can be provided outside of a git context:
|
|
||||||
|
|
||||||
* Any rules in '.ignore'.
|
|
||||||
* Any rules in '.rgignore'.
|
|
||||||
* Any rules in files specified with the '--ignore-file' flag.
|
|
||||||
|
|
||||||
The precedence of ignore rules is as follows, with later items overriding
|
|
||||||
earlier items:
|
|
||||||
|
|
||||||
* Files given by '--ignore-file'.
|
|
||||||
* Global gitignore rules, e.g., from '$HOME/.config/git/ignore'.
|
|
||||||
* Local rules from '.git/info/exclude'.
|
|
||||||
* Rules from '.gitignore'.
|
|
||||||
* Rules from '.ignore'.
|
|
||||||
* Rules from '.rgignore'.
|
|
||||||
|
|
||||||
So for example, if 'foo' were in a '.gitignore' and '!foo' were in an
|
|
||||||
'.rgignore', then 'foo' would not be ignored since '.rgignore' takes precedence
|
|
||||||
over '.gitignore'.
|
|
||||||
|
|
||||||
Each of the types of filtering can be configured via command line flags:
|
|
||||||
|
|
||||||
* There are several flags starting with '--no-ignore' that toggle which,
|
|
||||||
if any, ignore rules are respected. '--no-ignore' by itself will disable
|
|
||||||
all of them.
|
|
||||||
* '-./--hidden' will force ripgrep to search hidden files and directories.
|
|
||||||
* '--binary' will force ripgrep to search binary files.
|
|
||||||
* '-L/--follow' will force ripgrep to follow symlinks.
|
|
||||||
|
|
||||||
As a special short hand, the `-u` flag can be specified up to three times. Each
|
|
||||||
additional time incrementally decreases filtering:
|
|
||||||
|
|
||||||
* '-u' is equivalent to '--no-ignore'.
|
|
||||||
* '-uu' is equivalent to '--no-ignore --hidden'.
|
|
||||||
* '-uuu' is equivalent to '--no-ignore --hidden --binary'.
|
|
||||||
|
|
||||||
In particular, 'rg -uuu' should search the same exact content as 'grep -r'.
|
|
||||||
|
|
||||||
|
|
||||||
CONFIGURATION FILES
|
|
||||||
-------------------
|
|
||||||
ripgrep supports reading configuration files that change ripgrep's default
|
|
||||||
behavior. The format of the configuration file is an "rc" style and is very
|
|
||||||
simple. It is defined by two rules:
|
|
||||||
|
|
||||||
1. Every line is a shell argument, after trimming whitespace.
|
|
||||||
2. Lines starting with *#* (optionally preceded by any amount of
|
|
||||||
whitespace) are ignored.
|
|
||||||
|
|
||||||
ripgrep will look for a single configuration file if and only if the
|
|
||||||
*RIPGREP_CONFIG_PATH* environment variable is set and is non-empty. ripgrep
|
|
||||||
will parse shell arguments from this file on startup and will behave as if
|
|
||||||
the arguments in this file were prepended to any explicit arguments given to
|
|
||||||
ripgrep on the command line. Note though that the 'rg' command you run must
|
|
||||||
still be valid. That is, it must always contain at least one pattern at the
|
|
||||||
command line, even if the configuration file uses the '-e/--regexp' flag.
|
|
||||||
|
|
||||||
For example, if your ripgreprc file contained a single line:
|
|
||||||
|
|
||||||
--smart-case
|
|
||||||
|
|
||||||
then the following command
|
|
||||||
|
|
||||||
RIPGREP_CONFIG_PATH=wherever/.ripgreprc rg foo
|
|
||||||
|
|
||||||
would behave identically to the following command
|
|
||||||
|
|
||||||
rg --smart-case foo
|
|
||||||
|
|
||||||
another example is adding types
|
|
||||||
|
|
||||||
--type-add
|
|
||||||
web:*.{html,css,js}*
|
|
||||||
|
|
||||||
would behave identically to the following command
|
|
||||||
|
|
||||||
rg --type-add 'web:*.{html,css,js}*' foo
|
|
||||||
|
|
||||||
same with using globs
|
|
||||||
|
|
||||||
--glob=!.git
|
|
||||||
|
|
||||||
or
|
|
||||||
|
|
||||||
--glob
|
|
||||||
!.git
|
|
||||||
|
|
||||||
would behave identically to the following command
|
|
||||||
|
|
||||||
rg --glob '!.git' foo
|
|
||||||
|
|
||||||
The bottom line is that every shell argument needs to be on its own line. So
|
|
||||||
for example, a config file containing
|
|
||||||
|
|
||||||
-j 4
|
|
||||||
|
|
||||||
is probably not doing what you intend. Instead, you want
|
|
||||||
|
|
||||||
-j
|
|
||||||
4
|
|
||||||
|
|
||||||
ripgrep also provides a flag, *--no-config*, that when present will suppress
|
|
||||||
any and all support for configuration. This includes any future support
|
|
||||||
for auto-loading configuration files from pre-determined paths.
|
|
||||||
|
|
||||||
Conflicts between configuration files and explicit arguments are handled
|
|
||||||
exactly like conflicts in the same command line invocation. That is,
|
|
||||||
this command:
|
|
||||||
|
|
||||||
RIPGREP_CONFIG_PATH=wherever/.ripgreprc rg foo --case-sensitive
|
|
||||||
|
|
||||||
is exactly equivalent to
|
|
||||||
|
|
||||||
rg --smart-case foo --case-sensitive
|
|
||||||
|
|
||||||
in which case, the *--case-sensitive* flag would override the *--smart-case*
|
|
||||||
flag.
|
|
||||||
|
|
||||||
|
|
||||||
SHELL COMPLETION
|
|
||||||
----------------
|
|
||||||
Shell completion files are included in the release tarball for Bash, Fish, Zsh
|
|
||||||
and PowerShell.
|
|
||||||
|
|
||||||
For *bash*, move *rg.bash* to *$XDG_CONFIG_HOME/bash_completion*
|
|
||||||
or */etc/bash_completion.d/*.
|
|
||||||
|
|
||||||
For *fish*, move *rg.fish* to *$HOME/.config/fish/completions*.
|
|
||||||
|
|
||||||
For *zsh*, move *_rg* to one of your *$fpath* directories.
|
|
||||||
|
|
||||||
|
|
||||||
CAVEATS
|
|
||||||
-------
|
|
||||||
ripgrep may abort unexpectedly when using default settings if it searches a
|
|
||||||
file that is simultaneously truncated. This behavior can be avoided by passing
|
|
||||||
the *--no-mmap* flag which will forcefully disable the use of memory maps in
|
|
||||||
all cases.
|
|
||||||
|
|
||||||
ripgrep may use a large amount of memory depending on a few factors. Firstly,
|
|
||||||
if ripgrep uses parallelism for search (the default), then the entire output
|
|
||||||
for each individual file is buffered into memory in order to prevent
|
|
||||||
interleaving matches in the output. To avoid this, you can disable parallelism
|
|
||||||
with the *-j1* flag. Secondly, ripgrep always needs to have at least a single
|
|
||||||
line in memory in order to execute a search. A file with a very long line can
|
|
||||||
thus cause ripgrep to use a lot of memory. Generally, this only occurs when
|
|
||||||
searching binary data with the *-a* flag enabled. (When the *-a* flag isn't
|
|
||||||
enabled, ripgrep will replace all NUL bytes with line terminators, which
|
|
||||||
typically prevents exorbitant memory usage.) Thirdly, when ripgrep searches
|
|
||||||
a large file using a memory map, the process will report its resident memory
|
|
||||||
usage as the size of the file. However, this does not mean ripgrep actually
|
|
||||||
needed to use that much memory; the operating system will generally handle this
|
|
||||||
for you.
|
|
||||||
|
|
||||||
|
|
||||||
VERSION
|
|
||||||
-------
|
|
||||||
{VERSION}
|
|
||||||
|
|
||||||
|
|
||||||
HOMEPAGE
|
|
||||||
--------
|
|
||||||
https://github.com/BurntSushi/ripgrep
|
|
||||||
|
|
||||||
Please report bugs and feature requests in the issue tracker. Please do your
|
|
||||||
best to provide a reproducible test case for bugs. This should include the
|
|
||||||
corpus being searched, the *rg* command, the actual output and the expected
|
|
||||||
output. Please also include the output of running the same *rg* command but
|
|
||||||
with the *--debug* flag.
|
|
||||||
|
|
||||||
|
|
||||||
AUTHORS
|
|
||||||
-------
|
|
||||||
Andrew Gallant <jamslam@gmail.com>
|
|
4
fuzz/.gitignore
vendored
Normal file
4
fuzz/.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
target
|
||||||
|
corpus
|
||||||
|
artifacts
|
||||||
|
coverage
|
188
fuzz/Cargo.lock
generated
Normal file
188
fuzz/Cargo.lock
generated
Normal file
@@ -0,0 +1,188 @@
|
|||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 4
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "aho-corasick"
|
||||||
|
version = "1.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "arbitrary"
|
||||||
|
version = "1.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110"
|
||||||
|
dependencies = [
|
||||||
|
"derive_arbitrary",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bstr"
|
||||||
|
version = "1.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c48f0051a4b4c5e0b6d365cd04af53aeaa209e3cc15ec2cdb69e73cc87fbd0dc"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cc"
|
||||||
|
version = "1.0.83"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0"
|
||||||
|
dependencies = [
|
||||||
|
"jobserver",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "derive_arbitrary"
|
||||||
|
version = "1.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fuzz"
|
||||||
|
version = "0.0.1"
|
||||||
|
dependencies = [
|
||||||
|
"globset",
|
||||||
|
"libfuzzer-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "globset"
|
||||||
|
version = "0.4.16"
|
||||||
|
dependencies = [
|
||||||
|
"aho-corasick",
|
||||||
|
"arbitrary",
|
||||||
|
"bstr",
|
||||||
|
"log",
|
||||||
|
"regex-automata",
|
||||||
|
"regex-syntax",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jobserver"
|
||||||
|
version = "0.1.27"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8c37f63953c4c63420ed5fd3d6d398c719489b9f872b9fa683262f8edd363c7d"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libc"
|
||||||
|
version = "0.2.152"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libfuzzer-sys"
|
||||||
|
version = "0.4.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a96cfd5557eb82f2b83fed4955246c988d331975a002961b07c81584d107e7f7"
|
||||||
|
dependencies = [
|
||||||
|
"arbitrary",
|
||||||
|
"cc",
|
||||||
|
"once_cell",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "log"
|
||||||
|
version = "0.4.20"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "memchr"
|
||||||
|
version = "2.7.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "once_cell"
|
||||||
|
version = "1.19.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro2"
|
||||||
|
version = "1.0.78"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quote"
|
||||||
|
version = "1.0.35"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-automata"
|
||||||
|
version = "0.4.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3b7fa1134405e2ec9353fd416b17f8dacd46c473d7d3fd1cf202706a14eb792a"
|
||||||
|
dependencies = [
|
||||||
|
"aho-corasick",
|
||||||
|
"memchr",
|
||||||
|
"regex-syntax",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-syntax"
|
||||||
|
version = "0.8.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde"
|
||||||
|
version = "1.0.195"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "63261df402c67811e9ac6def069e4786148c4563f4b50fd4bf30aa370d626b02"
|
||||||
|
dependencies = [
|
||||||
|
"serde_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_derive"
|
||||||
|
version = "1.0.195"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "46fe8f8603d81ba86327b23a2e9cdf49e1255fb94a4c5f297f6ee0547178ea2c"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "2.0.48"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-ident"
|
||||||
|
version = "1.0.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
25
fuzz/Cargo.toml
Normal file
25
fuzz/Cargo.toml
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
[package]
|
||||||
|
name = "fuzz"
|
||||||
|
version = "0.0.1"
|
||||||
|
publish = false
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[package.metadata]
|
||||||
|
cargo-fuzz = true
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
libfuzzer-sys = "0.4"
|
||||||
|
globset = { path = "../crates/globset", features = ["arbitrary"] }
|
||||||
|
|
||||||
|
# Prevent this from interfering with workspaces
|
||||||
|
[workspace]
|
||||||
|
members = ["."]
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
debug = 1
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "fuzz_glob"
|
||||||
|
path = "fuzz_targets/fuzz_glob.rs"
|
||||||
|
test = false
|
||||||
|
doc = false
|
52
fuzz/README.md
Normal file
52
fuzz/README.md
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
# Fuzz Testing
|
||||||
|
|
||||||
|
## Introduction
|
||||||
|
|
||||||
|
Fuzz testing produces pseudo-random / arbitrary data that is used to find
|
||||||
|
stability issues within a code base. While Rust provides a strong type system,
|
||||||
|
this does not guarantee that an object will convert properly from one struct
|
||||||
|
to another. It is the responsibility of the developer to ensure that a struct
|
||||||
|
is converted properly. Fuzz testing will generate input within the domain of
|
||||||
|
each property. This arbitrary data can then be used to convert from ObjectA
|
||||||
|
to ObjectB and then back. This type of testing will help catch bugs that the
|
||||||
|
type system is not able to see.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
This crate relies on the `cargo-fuzz` component. To install this component,
|
||||||
|
run the following from the `fuzz` directory:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cargo install cargo-fuzz
|
||||||
|
```
|
||||||
|
|
||||||
|
## Listing Targets
|
||||||
|
|
||||||
|
Once installed, fuzz targets can be listed by running the following command:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cargo fuzz list
|
||||||
|
```
|
||||||
|
|
||||||
|
This command will print out a list of all targets that can be tested.
|
||||||
|
|
||||||
|
## Running Fuzz Tests
|
||||||
|
|
||||||
|
To run a fuzz test, the target must be specified:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cargo fuzz run <target>
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that the above will run the fuzz test indefinitely. Use the
|
||||||
|
`-max_total_time=<num seconds>` flag to specify how many seconds the test
|
||||||
|
should run for:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cargo fuzz run <target> -- -max_total_time=5
|
||||||
|
```
|
||||||
|
|
||||||
|
The above command will run the fuzz test for five seconds. If the test
|
||||||
|
completes without error it will show how many tests were run successfully.
|
||||||
|
The test will abort and return a non-zero error code if it is able to produce
|
||||||
|
an error. The arbitrary input will be displayed in the event of a failure.
|
22
fuzz/fuzz_targets/fuzz_glob.rs
Normal file
22
fuzz/fuzz_targets/fuzz_glob.rs
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
#![no_main]
|
||||||
|
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
use globset::Glob;
|
||||||
|
|
||||||
|
libfuzzer_sys::fuzz_target!(|glob_str: &str| {
|
||||||
|
let Ok(glob) = Glob::new(glob_str) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let Ok(glob2) = Glob::from_str(glob_str) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Verify that a `Glob` constructed with `new` is the same as a `Glob`` constructed
|
||||||
|
// with `from_str`.
|
||||||
|
assert_eq!(glob, glob2);
|
||||||
|
|
||||||
|
// Verify that `Glob::glob` produces the same string as the original.
|
||||||
|
assert_eq!(glob.glob(), glob_str);
|
||||||
|
});
|
@@ -1,14 +1,14 @@
|
|||||||
class RipgrepBin < Formula
|
class RipgrepBin < Formula
|
||||||
version '13.0.0'
|
version '14.1.1'
|
||||||
desc "Recursively search directories for a regex pattern."
|
desc "Recursively search directories for a regex pattern."
|
||||||
homepage "https://github.com/BurntSushi/ripgrep"
|
homepage "https://github.com/BurntSushi/ripgrep"
|
||||||
|
|
||||||
if OS.mac?
|
if OS.mac?
|
||||||
url "https://github.com/BurntSushi/ripgrep/releases/download/#{version}/ripgrep-#{version}-x86_64-apple-darwin.tar.gz"
|
url "https://github.com/BurntSushi/ripgrep/releases/download/#{version}/ripgrep-#{version}-x86_64-apple-darwin.tar.gz"
|
||||||
sha256 "585c18350cb8d4392461edd6c921e6edd5a97cbfc03b567d7bd440423e118082"
|
sha256 "fc87e78f7cb3fea12d69072e7ef3b21509754717b746368fd40d88963630e2b3"
|
||||||
elsif OS.linux?
|
elsif OS.linux?
|
||||||
url "https://github.com/BurntSushi/ripgrep/releases/download/#{version}/ripgrep-#{version}-x86_64-unknown-linux-musl.tar.gz"
|
url "https://github.com/BurntSushi/ripgrep/releases/download/#{version}/ripgrep-#{version}-x86_64-unknown-linux-musl.tar.gz"
|
||||||
sha256 "ee4e0751ab108b6da4f47c52da187d5177dc371f0f512a7caaec5434e711c091"
|
sha256 "4cf9f2741e6c465ffdb7c26f38056a59e2a2544b51f7cc128ef28337eeae4d8e"
|
||||||
end
|
end
|
||||||
|
|
||||||
conflicts_with "ripgrep"
|
conflicts_with "ripgrep"
|
||||||
|
@@ -356,6 +356,17 @@ rgtest!(f263_sort_files, |dir: Dir, mut cmd: TestCommand| {
|
|||||||
eqnice!(expected, cmd.arg("--sort-files").arg("test").stdout());
|
eqnice!(expected, cmd.arg("--sort-files").arg("test").stdout());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// See: https://github.com/BurntSushi/ripgrep/issues/263
|
||||||
|
rgtest!(f263_sort_files_reverse, |dir: Dir, mut cmd: TestCommand| {
|
||||||
|
dir.create("foo", "test");
|
||||||
|
dir.create("abc", "test");
|
||||||
|
dir.create("zoo", "test");
|
||||||
|
dir.create("bar", "test");
|
||||||
|
|
||||||
|
let expected = "zoo:test\nfoo:test\nbar:test\nabc:test\n";
|
||||||
|
eqnice!(expected, cmd.arg("--sortr=path").arg("test").stdout());
|
||||||
|
});
|
||||||
|
|
||||||
// See: https://github.com/BurntSushi/ripgrep/issues/275
|
// See: https://github.com/BurntSushi/ripgrep/issues/275
|
||||||
rgtest!(f275_pathsep, |dir: Dir, mut cmd: TestCommand| {
|
rgtest!(f275_pathsep, |dir: Dir, mut cmd: TestCommand| {
|
||||||
dir.create_dir("foo");
|
dir.create_dir("foo");
|
||||||
@@ -961,10 +972,10 @@ rgtest!(f1404_nothing_searched_warning, |dir: Dir, mut cmd: TestCommand| {
|
|||||||
cmd.assert_err();
|
cmd.assert_err();
|
||||||
|
|
||||||
// Test that we actually get an error message that we expect.
|
// Test that we actually get an error message that we expect.
|
||||||
let output = cmd.cmd().output().unwrap();
|
let output = cmd.raw_output();
|
||||||
let stderr = String::from_utf8_lossy(&output.stderr);
|
let stderr = String::from_utf8_lossy(&output.stderr);
|
||||||
let expected = "\
|
let expected = "\
|
||||||
No files were searched, which means ripgrep probably applied \
|
rg: No files were searched, which means ripgrep probably applied \
|
||||||
a filter you didn't expect.\n\
|
a filter you didn't expect.\n\
|
||||||
Running with --debug will show why files are being skipped.\n\
|
Running with --debug will show why files are being skipped.\n\
|
||||||
";
|
";
|
||||||
@@ -984,7 +995,7 @@ rgtest!(f1404_nothing_searched_ignored, |dir: Dir, mut cmd: TestCommand| {
|
|||||||
|
|
||||||
// But since --no-messages is given, there should not be any error message
|
// But since --no-messages is given, there should not be any error message
|
||||||
// printed.
|
// printed.
|
||||||
let output = cmd.cmd().output().unwrap();
|
let output = cmd.raw_output();
|
||||||
let stderr = String::from_utf8_lossy(&output.stderr);
|
let stderr = String::from_utf8_lossy(&output.stderr);
|
||||||
let expected = "";
|
let expected = "";
|
||||||
eqnice!(expected, stderr);
|
eqnice!(expected, stderr);
|
||||||
|
@@ -21,45 +21,47 @@ impl Message {
|
|||||||
fn unwrap_begin(&self) -> Begin {
|
fn unwrap_begin(&self) -> Begin {
|
||||||
match *self {
|
match *self {
|
||||||
Message::Begin(ref x) => x.clone(),
|
Message::Begin(ref x) => x.clone(),
|
||||||
ref x => panic!("expected Message::Begin but got {:?}", x),
|
ref x => panic!("expected Message::Begin but got {x:?}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unwrap_end(&self) -> End {
|
fn unwrap_end(&self) -> End {
|
||||||
match *self {
|
match *self {
|
||||||
Message::End(ref x) => x.clone(),
|
Message::End(ref x) => x.clone(),
|
||||||
ref x => panic!("expected Message::End but got {:?}", x),
|
ref x => panic!("expected Message::End but got {x:?}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unwrap_match(&self) -> Match {
|
fn unwrap_match(&self) -> Match {
|
||||||
match *self {
|
match *self {
|
||||||
Message::Match(ref x) => x.clone(),
|
Message::Match(ref x) => x.clone(),
|
||||||
ref x => panic!("expected Message::Match but got {:?}", x),
|
ref x => panic!("expected Message::Match but got {x:?}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unwrap_context(&self) -> Context {
|
fn unwrap_context(&self) -> Context {
|
||||||
match *self {
|
match *self {
|
||||||
Message::Context(ref x) => x.clone(),
|
Message::Context(ref x) => x.clone(),
|
||||||
ref x => panic!("expected Message::Context but got {:?}", x),
|
ref x => panic!("expected Message::Context but got {x:?}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unwrap_summary(&self) -> Summary {
|
fn unwrap_summary(&self) -> Summary {
|
||||||
match *self {
|
match *self {
|
||||||
Message::Summary(ref x) => x.clone(),
|
Message::Summary(ref x) => x.clone(),
|
||||||
ref x => panic!("expected Message::Summary but got {:?}", x),
|
ref x => panic!("expected Message::Summary but got {x:?}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, PartialEq, Eq)]
|
#[derive(Clone, Debug, Deserialize, PartialEq, Eq)]
|
||||||
|
#[serde(deny_unknown_fields)]
|
||||||
struct Begin {
|
struct Begin {
|
||||||
path: Option<Data>,
|
path: Option<Data>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, PartialEq, Eq)]
|
#[derive(Clone, Debug, Deserialize, PartialEq, Eq)]
|
||||||
|
#[serde(deny_unknown_fields)]
|
||||||
struct End {
|
struct End {
|
||||||
path: Option<Data>,
|
path: Option<Data>,
|
||||||
binary_offset: Option<u64>,
|
binary_offset: Option<u64>,
|
||||||
@@ -67,12 +69,14 @@ struct End {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, PartialEq, Eq)]
|
#[derive(Clone, Debug, Deserialize, PartialEq, Eq)]
|
||||||
|
#[serde(deny_unknown_fields)]
|
||||||
struct Summary {
|
struct Summary {
|
||||||
elapsed_total: Duration,
|
elapsed_total: Duration,
|
||||||
stats: Stats,
|
stats: Stats,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, PartialEq, Eq)]
|
#[derive(Clone, Debug, Deserialize, PartialEq, Eq)]
|
||||||
|
#[serde(deny_unknown_fields)]
|
||||||
struct Match {
|
struct Match {
|
||||||
path: Option<Data>,
|
path: Option<Data>,
|
||||||
lines: Data,
|
lines: Data,
|
||||||
@@ -82,6 +86,7 @@ struct Match {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, PartialEq, Eq)]
|
#[derive(Clone, Debug, Deserialize, PartialEq, Eq)]
|
||||||
|
#[serde(deny_unknown_fields)]
|
||||||
struct Context {
|
struct Context {
|
||||||
path: Option<Data>,
|
path: Option<Data>,
|
||||||
lines: Data,
|
lines: Data,
|
||||||
@@ -91,9 +96,11 @@ struct Context {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, PartialEq, Eq)]
|
#[derive(Clone, Debug, Deserialize, PartialEq, Eq)]
|
||||||
|
#[serde(deny_unknown_fields)]
|
||||||
struct SubMatch {
|
struct SubMatch {
|
||||||
#[serde(rename = "match")]
|
#[serde(rename = "match")]
|
||||||
m: Data,
|
m: Data,
|
||||||
|
replacement: Option<Data>,
|
||||||
start: usize,
|
start: usize,
|
||||||
end: usize,
|
end: usize,
|
||||||
}
|
}
|
||||||
@@ -117,6 +124,7 @@ impl Data {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, PartialEq, Eq)]
|
#[derive(Clone, Debug, Deserialize, PartialEq, Eq)]
|
||||||
|
#[serde(deny_unknown_fields)]
|
||||||
struct Stats {
|
struct Stats {
|
||||||
elapsed: Duration,
|
elapsed: Duration,
|
||||||
searches: u64,
|
searches: u64,
|
||||||
@@ -128,6 +136,7 @@ struct Stats {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, PartialEq, Eq)]
|
#[derive(Clone, Debug, Deserialize, PartialEq, Eq)]
|
||||||
|
#[serde(deny_unknown_fields)]
|
||||||
struct Duration {
|
struct Duration {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
duration: time::Duration,
|
duration: time::Duration,
|
||||||
@@ -178,6 +187,7 @@ rgtest!(basic, |dir: Dir, mut cmd: TestCommand| {
|
|||||||
absolute_offset: 129,
|
absolute_offset: 129,
|
||||||
submatches: vec![SubMatch {
|
submatches: vec![SubMatch {
|
||||||
m: Data::text("Sherlock Holmes"),
|
m: Data::text("Sherlock Holmes"),
|
||||||
|
replacement: None,
|
||||||
start: 48,
|
start: 48,
|
||||||
end: 63,
|
end: 63,
|
||||||
},],
|
},],
|
||||||
@@ -189,6 +199,57 @@ rgtest!(basic, |dir: Dir, mut cmd: TestCommand| {
|
|||||||
assert_eq!(msgs[4].unwrap_summary().stats.bytes_printed, 494);
|
assert_eq!(msgs[4].unwrap_summary().stats.bytes_printed, 494);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
rgtest!(replacement, |dir: Dir, mut cmd: TestCommand| {
|
||||||
|
dir.create("sherlock", SHERLOCK);
|
||||||
|
cmd.arg("--json")
|
||||||
|
.arg("-B1")
|
||||||
|
.arg("Sherlock Holmes")
|
||||||
|
.args(["-r", "John Watson"])
|
||||||
|
.arg("sherlock");
|
||||||
|
|
||||||
|
let msgs = json_decode(&cmd.stdout());
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
msgs[0].unwrap_begin(),
|
||||||
|
Begin { path: Some(Data::text("sherlock")) }
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
msgs[1].unwrap_context(),
|
||||||
|
Context {
|
||||||
|
path: Some(Data::text("sherlock")),
|
||||||
|
lines: Data::text(
|
||||||
|
"Holmeses, success in the province of \
|
||||||
|
detective work must always\n",
|
||||||
|
),
|
||||||
|
line_number: Some(2),
|
||||||
|
absolute_offset: 65,
|
||||||
|
submatches: vec![],
|
||||||
|
}
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
msgs[2].unwrap_match(),
|
||||||
|
Match {
|
||||||
|
path: Some(Data::text("sherlock")),
|
||||||
|
lines: Data::text(
|
||||||
|
"be, to a very large extent, the result of luck. \
|
||||||
|
Sherlock Holmes\n",
|
||||||
|
),
|
||||||
|
line_number: Some(3),
|
||||||
|
absolute_offset: 129,
|
||||||
|
submatches: vec![SubMatch {
|
||||||
|
m: Data::text("Sherlock Holmes"),
|
||||||
|
replacement: Some(Data::text("John Watson")),
|
||||||
|
start: 48,
|
||||||
|
end: 63,
|
||||||
|
},],
|
||||||
|
}
|
||||||
|
);
|
||||||
|
assert_eq!(msgs[3].unwrap_end().path, Some(Data::text("sherlock")));
|
||||||
|
assert_eq!(msgs[3].unwrap_end().binary_offset, None);
|
||||||
|
assert_eq!(msgs[4].unwrap_summary().stats.searches_with_match, 1);
|
||||||
|
assert_eq!(msgs[4].unwrap_summary().stats.bytes_printed, 531);
|
||||||
|
});
|
||||||
|
|
||||||
rgtest!(quiet_stats, |dir: Dir, mut cmd: TestCommand| {
|
rgtest!(quiet_stats, |dir: Dir, mut cmd: TestCommand| {
|
||||||
dir.create("sherlock", SHERLOCK);
|
dir.create("sherlock", SHERLOCK);
|
||||||
cmd.arg("--json")
|
cmd.arg("--json")
|
||||||
@@ -244,6 +305,7 @@ rgtest!(notutf8, |dir: Dir, mut cmd: TestCommand| {
|
|||||||
absolute_offset: 0,
|
absolute_offset: 0,
|
||||||
submatches: vec![SubMatch {
|
submatches: vec![SubMatch {
|
||||||
m: Data::bytes("/w=="),
|
m: Data::bytes("/w=="),
|
||||||
|
replacement: None,
|
||||||
start: 4,
|
start: 4,
|
||||||
end: 5,
|
end: 5,
|
||||||
},],
|
},],
|
||||||
@@ -285,6 +347,7 @@ rgtest!(notutf8_file, |dir: Dir, mut cmd: TestCommand| {
|
|||||||
absolute_offset: 0,
|
absolute_offset: 0,
|
||||||
submatches: vec![SubMatch {
|
submatches: vec![SubMatch {
|
||||||
m: Data::bytes("/w=="),
|
m: Data::bytes("/w=="),
|
||||||
|
replacement: None,
|
||||||
start: 4,
|
start: 4,
|
||||||
end: 5,
|
end: 5,
|
||||||
},],
|
},],
|
||||||
@@ -305,7 +368,12 @@ rgtest!(crlf, |dir: Dir, mut cmd: TestCommand| {
|
|||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
msgs[1].unwrap_match().submatches[0].clone(),
|
msgs[1].unwrap_match().submatches[0].clone(),
|
||||||
SubMatch { m: Data::text("Sherlock"), start: 56, end: 64 },
|
SubMatch {
|
||||||
|
m: Data::text("Sherlock"),
|
||||||
|
replacement: None,
|
||||||
|
start: 56,
|
||||||
|
end: 64
|
||||||
|
},
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@@ -411,7 +411,7 @@ rgtest!(include_zero, |dir: Dir, mut cmd: TestCommand| {
|
|||||||
cmd.args(&["--count", "--include-zero", "nada"]);
|
cmd.args(&["--count", "--include-zero", "nada"]);
|
||||||
cmd.assert_err();
|
cmd.assert_err();
|
||||||
|
|
||||||
let output = cmd.cmd().output().unwrap();
|
let output = cmd.raw_output();
|
||||||
let stdout = String::from_utf8_lossy(&output.stdout);
|
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||||
let expected = "sherlock:0\n";
|
let expected = "sherlock:0\n";
|
||||||
|
|
||||||
@@ -423,7 +423,7 @@ rgtest!(include_zero_override, |dir: Dir, mut cmd: TestCommand| {
|
|||||||
cmd.args(&["--count", "--include-zero", "--no-include-zero", "nada"]);
|
cmd.args(&["--count", "--include-zero", "--no-include-zero", "nada"]);
|
||||||
cmd.assert_err();
|
cmd.assert_err();
|
||||||
|
|
||||||
let output = cmd.cmd().output().unwrap();
|
let output = cmd.raw_output();
|
||||||
let stdout = String::from_utf8_lossy(&output.stdout);
|
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||||
assert!(stdout.is_empty());
|
assert!(stdout.is_empty());
|
||||||
});
|
});
|
||||||
|
@@ -399,12 +399,12 @@ rgtest!(r428_unrecognized_style, |dir: Dir, mut cmd: TestCommand| {
|
|||||||
cmd.arg("--colors=match:style:").arg("Sherlock");
|
cmd.arg("--colors=match:style:").arg("Sherlock");
|
||||||
cmd.assert_err();
|
cmd.assert_err();
|
||||||
|
|
||||||
let output = cmd.cmd().output().unwrap();
|
let output = cmd.raw_output();
|
||||||
let stderr = String::from_utf8_lossy(&output.stderr);
|
let stderr = String::from_utf8_lossy(&output.stderr);
|
||||||
let expected = "\
|
let expected = "\
|
||||||
error parsing flag --colors: \
|
rg: error parsing flag --colors: \
|
||||||
unrecognized style attribute ''. Choose from: nobold, bold, nointense, \
|
unrecognized style attribute ''. Choose from: nobold, bold, nointense, \
|
||||||
intense, nounderline, underline.
|
intense, nounderline, underline, noitalic, italic.
|
||||||
";
|
";
|
||||||
eqnice!(expected, stderr);
|
eqnice!(expected, stderr);
|
||||||
});
|
});
|
||||||
@@ -569,6 +569,197 @@ rgtest!(r807, |dir: Dir, mut cmd: TestCommand| {
|
|||||||
eqnice!(".a/c/file:test\n", cmd.arg("--hidden").arg("test").stdout());
|
eqnice!(".a/c/file:test\n", cmd.arg("--hidden").arg("test").stdout());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// See: https://github.com/BurntSushi/ripgrep/pull/2711
|
||||||
|
//
|
||||||
|
// Note that this isn't a regression test. In particular, this didn't fail
|
||||||
|
// with ripgrep 14.1.1. I couldn't figure out how to turn what the OP gave me
|
||||||
|
// into a failing test.
|
||||||
|
rgtest!(r2711, |dir: Dir, _cmd: TestCommand| {
|
||||||
|
dir.create_dir("a/b");
|
||||||
|
dir.create("a/.ignore", ".foo");
|
||||||
|
dir.create("a/b/.foo", "");
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut cmd = dir.command();
|
||||||
|
eqnice!("a/.ignore\n", cmd.arg("--hidden").arg("--files").stdout());
|
||||||
|
}
|
||||||
|
{
|
||||||
|
let mut cmd = dir.command();
|
||||||
|
eqnice!(
|
||||||
|
"./a/.ignore\n",
|
||||||
|
cmd.arg("--hidden").arg("--files").arg("./").stdout()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut cmd = dir.command();
|
||||||
|
eqnice!(
|
||||||
|
"a/.ignore\n",
|
||||||
|
cmd.arg("--hidden").arg("--files").arg("a").stdout()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
let mut cmd = dir.command();
|
||||||
|
cmd.arg("--hidden").arg("--files").arg("a/b").assert_err();
|
||||||
|
}
|
||||||
|
{
|
||||||
|
let mut cmd = dir.command();
|
||||||
|
eqnice!(
|
||||||
|
"./a/.ignore\n",
|
||||||
|
cmd.arg("--hidden").arg("--files").arg("./a").stdout()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut cmd = dir.command();
|
||||||
|
cmd.current_dir(dir.path().join("a"));
|
||||||
|
eqnice!(".ignore\n", cmd.arg("--hidden").arg("--files").stdout());
|
||||||
|
}
|
||||||
|
{
|
||||||
|
let mut cmd = dir.command();
|
||||||
|
cmd.current_dir(dir.path().join("a").join("b"));
|
||||||
|
cmd.arg("--hidden").arg("--files").assert_err();
|
||||||
|
}
|
||||||
|
{
|
||||||
|
let mut cmd = dir.command();
|
||||||
|
cmd.current_dir(dir.path().join("./a"));
|
||||||
|
eqnice!(".ignore\n", cmd.arg("--hidden").arg("--files").stdout());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// See: https://github.com/BurntSushi/ripgrep/issues/829
|
||||||
|
rgtest!(r829_original, |dir: Dir, _cmd: TestCommand| {
|
||||||
|
dir.create_dir("a/b");
|
||||||
|
dir.create(".ignore", "/a/b");
|
||||||
|
dir.create("a/b/test.txt", "Sample text");
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut cmd = dir.command();
|
||||||
|
cmd.args(&["Sample"]).assert_err();
|
||||||
|
}
|
||||||
|
{
|
||||||
|
let mut cmd = dir.command();
|
||||||
|
cmd.args(&["Sample", "a"]).assert_err();
|
||||||
|
}
|
||||||
|
{
|
||||||
|
let mut cmd = dir.command();
|
||||||
|
cmd.current_dir(dir.path().join("a"));
|
||||||
|
cmd.args(&["Sample"]).assert_err();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// See: https://github.com/BurntSushi/ripgrep/issues/2731
|
||||||
|
rgtest!(r829_2731, |dir: Dir, _cmd: TestCommand| {
|
||||||
|
dir.create_dir("some_dir/build");
|
||||||
|
dir.create("some_dir/build/foo", "string");
|
||||||
|
dir.create(".ignore", "build/\n!/some_dir/build/");
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut cmd = dir.command();
|
||||||
|
eqnice!("some_dir/build/foo\n", cmd.arg("-l").arg("string").stdout());
|
||||||
|
}
|
||||||
|
{
|
||||||
|
let mut cmd = dir.command();
|
||||||
|
eqnice!(
|
||||||
|
"some_dir/build/foo\n",
|
||||||
|
cmd.arg("-l").arg("string").arg("some_dir").stdout()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
let mut cmd = dir.command();
|
||||||
|
eqnice!(
|
||||||
|
"./some_dir/build/foo\n",
|
||||||
|
cmd.arg("-l").arg("string").arg("./some_dir").stdout()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
let mut cmd = dir.command();
|
||||||
|
eqnice!(
|
||||||
|
"some_dir/build/foo\n",
|
||||||
|
cmd.arg("-l").arg("string").arg("some_dir/build").stdout()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
let mut cmd = dir.command();
|
||||||
|
eqnice!(
|
||||||
|
"./some_dir/build/foo\n",
|
||||||
|
cmd.arg("-l").arg("string").arg("./some_dir/build").stdout()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// See: https://github.com/BurntSushi/ripgrep/issues/2747
|
||||||
|
rgtest!(r829_2747, |dir: Dir, _cmd: TestCommand| {
|
||||||
|
dir.create_dir("a/c/b");
|
||||||
|
dir.create_dir("a/src/f/b");
|
||||||
|
dir.create("a/c/b/foo", "");
|
||||||
|
dir.create("a/src/f/b/foo", "");
|
||||||
|
dir.create(".ignore", "/a/*/b");
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut cmd = dir.command();
|
||||||
|
eqnice!("a/src/f/b/foo\n", cmd.arg("--files").stdout());
|
||||||
|
}
|
||||||
|
{
|
||||||
|
let mut cmd = dir.command();
|
||||||
|
eqnice!("a/src/f/b/foo\n", cmd.arg("--files").arg("a/src").stdout());
|
||||||
|
}
|
||||||
|
{
|
||||||
|
let mut cmd = dir.command();
|
||||||
|
cmd.current_dir(dir.path().join("a/src"));
|
||||||
|
eqnice!("f/b/foo\n", cmd.arg("--files").stdout());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// See: https://github.com/BurntSushi/ripgrep/issues/2778
|
||||||
|
rgtest!(r829_2778, |dir: Dir, _cmd: TestCommand| {
|
||||||
|
dir.create_dir("parent/subdir");
|
||||||
|
dir.create(".ignore", "/parent/*.txt");
|
||||||
|
dir.create("parent/ignore-me.txt", "");
|
||||||
|
dir.create("parent/subdir/dont-ignore-me.txt", "");
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut cmd = dir.command();
|
||||||
|
eqnice!(
|
||||||
|
"parent/subdir/dont-ignore-me.txt\n",
|
||||||
|
cmd.arg("--files").stdout()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
let mut cmd = dir.command();
|
||||||
|
cmd.current_dir(dir.path().join("parent"));
|
||||||
|
eqnice!("subdir/dont-ignore-me.txt\n", cmd.arg("--files").stdout());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// See: https://github.com/BurntSushi/ripgrep/issues/2836
|
||||||
|
rgtest!(r829_2836, |dir: Dir, _cmd: TestCommand| {
|
||||||
|
dir.create_dir("testdir/sub/sub2");
|
||||||
|
dir.create(".ignore", "/testdir/sub/sub2/\n");
|
||||||
|
dir.create("testdir/sub/sub2/foo", "");
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut cmd = dir.command();
|
||||||
|
cmd.arg("--files").assert_err();
|
||||||
|
}
|
||||||
|
{
|
||||||
|
let mut cmd = dir.command();
|
||||||
|
cmd.current_dir(dir.path().join("testdir"));
|
||||||
|
cmd.arg("--files").assert_err();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// See: https://github.com/BurntSushi/ripgrep/pull/2933
|
||||||
|
rgtest!(r829_2933, |dir: Dir, mut cmd: TestCommand| {
|
||||||
|
dir.create_dir("testdir/sub/sub2");
|
||||||
|
dir.create(".ignore", "/testdir/sub/sub2/");
|
||||||
|
dir.create("testdir/sub/sub2/testfile", "needle");
|
||||||
|
|
||||||
|
let args = &["--files-with-matches", "needle"];
|
||||||
|
cmd.current_dir(dir.path().join("testdir"));
|
||||||
|
cmd.args(args).assert_err();
|
||||||
|
});
|
||||||
|
|
||||||
// See: https://github.com/BurntSushi/ripgrep/issues/900
|
// See: https://github.com/BurntSushi/ripgrep/issues/900
|
||||||
rgtest!(r900, |dir: Dir, mut cmd: TestCommand| {
|
rgtest!(r900, |dir: Dir, mut cmd: TestCommand| {
|
||||||
dir.create("sherlock", SHERLOCK);
|
dir.create("sherlock", SHERLOCK);
|
||||||
@@ -764,6 +955,43 @@ rgtest!(r1319, |dir: Dir, mut cmd: TestCommand| {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// See: https://github.com/BurntSushi/ripgrep/issues/1332
|
||||||
|
rgtest!(r1334_invert_empty_patterns, |dir: Dir, _cmd: TestCommand| {
|
||||||
|
dir.create("zero-patterns", "");
|
||||||
|
dir.create("one-pattern", "\n");
|
||||||
|
dir.create("haystack", "one\ntwo\nthree\n");
|
||||||
|
|
||||||
|
// zero patterns matches nothing
|
||||||
|
{
|
||||||
|
let mut cmd = dir.command();
|
||||||
|
cmd.arg("-f").arg("zero-patterns").arg("haystack").assert_err();
|
||||||
|
}
|
||||||
|
// one pattern that matches empty string matches everything
|
||||||
|
{
|
||||||
|
let mut cmd = dir.command();
|
||||||
|
eqnice!(
|
||||||
|
"one\ntwo\nthree\n",
|
||||||
|
cmd.arg("-f").arg("one-pattern").arg("haystack").stdout()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// inverting zero patterns matches everything
|
||||||
|
// (This is the regression. ripgrep used to match nothing because of an
|
||||||
|
// incorrect optimization.)
|
||||||
|
{
|
||||||
|
let mut cmd = dir.command();
|
||||||
|
eqnice!(
|
||||||
|
"one\ntwo\nthree\n",
|
||||||
|
cmd.arg("-vf").arg("zero-patterns").arg("haystack").stdout()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// inverting one pattern that matches empty string matches nothing
|
||||||
|
{
|
||||||
|
let mut cmd = dir.command();
|
||||||
|
cmd.arg("-vf").arg("one-pattern").arg("haystack").assert_err();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// See: https://github.com/BurntSushi/ripgrep/issues/1334
|
// See: https://github.com/BurntSushi/ripgrep/issues/1334
|
||||||
rgtest!(r1334_crazy_literals, |dir: Dir, mut cmd: TestCommand| {
|
rgtest!(r1334_crazy_literals, |dir: Dir, mut cmd: TestCommand| {
|
||||||
dir.create("patterns", &"1.208.0.0/12\n".repeat(40));
|
dir.create("patterns", &"1.208.0.0/12\n".repeat(40));
|
||||||
@@ -965,6 +1193,15 @@ rgtest!(f1757, |dir: Dir, _: TestCommand| {
|
|||||||
eqnice!("rust/source.rs\n", dir.command().args(args).stdout());
|
eqnice!("rust/source.rs\n", dir.command().args(args).stdout());
|
||||||
let args = &["--files-with-matches", "needle", "./rust"];
|
let args = &["--files-with-matches", "needle", "./rust"];
|
||||||
eqnice!("./rust/source.rs\n", dir.command().args(args).stdout());
|
eqnice!("./rust/source.rs\n", dir.command().args(args).stdout());
|
||||||
|
|
||||||
|
dir.create_dir("rust1/target/onemore");
|
||||||
|
dir.create(".ignore", "rust1/target/onemore");
|
||||||
|
dir.create("rust1/source.rs", "needle");
|
||||||
|
dir.create("rust1/target/onemore/rustdoc-output.html", "needle");
|
||||||
|
let args = &["--files-with-matches", "needle", "rust1"];
|
||||||
|
eqnice!("rust1/source.rs\n", dir.command().args(args).stdout());
|
||||||
|
let args = &["--files-with-matches", "needle", "./rust1"];
|
||||||
|
eqnice!("./rust1/source.rs\n", dir.command().args(args).stdout());
|
||||||
});
|
});
|
||||||
|
|
||||||
// See: https://github.com/BurntSushi/ripgrep/issues/1765
|
// See: https://github.com/BurntSushi/ripgrep/issues/1765
|
||||||
@@ -1210,3 +1447,10 @@ rgtest!(r2574, |dir: Dir, mut cmd: TestCommand| {
|
|||||||
.stdout();
|
.stdout();
|
||||||
eqnice!("some.domain.com\nsome.domain.com\n", got);
|
eqnice!("some.domain.com\nsome.domain.com\n", got);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// See: https://github.com/BurntSushi/ripgrep/issues/2658
|
||||||
|
rgtest!(r2658_null_data_line_regexp, |dir: Dir, mut cmd: TestCommand| {
|
||||||
|
dir.create("haystack", "foo\0bar\0quux\0");
|
||||||
|
let got = cmd.args(&["--null-data", "--line-regexp", r"bar"]).stdout();
|
||||||
|
eqnice!("haystack:bar\0", got);
|
||||||
|
});
|
||||||
|
@@ -9,6 +9,8 @@ use std::sync::atomic::{AtomicUsize, Ordering};
|
|||||||
use std::thread;
|
use std::thread;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
|
use bstr::ByteSlice;
|
||||||
|
|
||||||
static TEST_DIR: &'static str = "ripgrep-tests";
|
static TEST_DIR: &'static str = "ripgrep-tests";
|
||||||
static NEXT_ID: AtomicUsize = AtomicUsize::new(0);
|
static NEXT_ID: AtomicUsize = AtomicUsize::new(0);
|
||||||
|
|
||||||
@@ -66,14 +68,14 @@ impl Dir {
|
|||||||
/// does not need to be distinct for each invocation, but should correspond
|
/// does not need to be distinct for each invocation, but should correspond
|
||||||
/// to a logical grouping of tests.
|
/// to a logical grouping of tests.
|
||||||
pub fn new(name: &str) -> Dir {
|
pub fn new(name: &str) -> Dir {
|
||||||
let id = NEXT_ID.fetch_add(1, Ordering::SeqCst);
|
let id = NEXT_ID.fetch_add(1, Ordering::Relaxed);
|
||||||
let root = env::current_exe()
|
let root = env::current_exe()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.parent()
|
.parent()
|
||||||
.expect("executable's directory")
|
.expect("executable's directory")
|
||||||
.to_path_buf();
|
.to_path_buf();
|
||||||
let dir =
|
let dir =
|
||||||
env::temp_dir().join(TEST_DIR).join(name).join(&format!("{}", id));
|
env::temp_dir().join(TEST_DIR).join(name).join(&format!("{id}"));
|
||||||
if dir.exists() {
|
if dir.exists() {
|
||||||
nice_err(&dir, fs::remove_dir_all(&dir));
|
nice_err(&dir, fs::remove_dir_all(&dir));
|
||||||
}
|
}
|
||||||
@@ -282,16 +284,7 @@ impl TestCommand {
|
|||||||
/// Runs and captures the stdout of the given command.
|
/// Runs and captures the stdout of the given command.
|
||||||
pub fn stdout(&mut self) -> String {
|
pub fn stdout(&mut self) -> String {
|
||||||
let o = self.output();
|
let o = self.output();
|
||||||
let stdout = String::from_utf8_lossy(&o.stdout);
|
String::from_utf8_lossy(&o.stdout).into_owned()
|
||||||
match stdout.parse() {
|
|
||||||
Ok(t) => t,
|
|
||||||
Err(err) => {
|
|
||||||
panic!(
|
|
||||||
"could not convert from string: {:?}\n\n{}",
|
|
||||||
err, stdout
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Pipe `input` to a command, and collect the output.
|
/// Pipe `input` to a command, and collect the output.
|
||||||
@@ -311,27 +304,26 @@ impl TestCommand {
|
|||||||
let output = self.expect_success(child.wait_with_output().unwrap());
|
let output = self.expect_success(child.wait_with_output().unwrap());
|
||||||
worker.join().unwrap().unwrap();
|
worker.join().unwrap().unwrap();
|
||||||
|
|
||||||
let stdout = String::from_utf8_lossy(&output.stdout);
|
String::from_utf8_lossy(&output.stdout).into_owned()
|
||||||
match stdout.parse() {
|
|
||||||
Ok(t) => t,
|
|
||||||
Err(err) => {
|
|
||||||
panic!(
|
|
||||||
"could not convert from string: {:?}\n\n{}",
|
|
||||||
err, stdout
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the output of a command. If the command failed, then this panics.
|
/// Gets the output of a command. If the command failed, then this panics.
|
||||||
pub fn output(&mut self) -> process::Output {
|
pub fn output(&mut self) -> process::Output {
|
||||||
let output = self.cmd.output().unwrap();
|
let output = self.raw_output();
|
||||||
self.expect_success(output)
|
self.expect_success(output)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Gets the raw output of a command after filtering nonsense like jemalloc
|
||||||
|
/// error messages from stderr.
|
||||||
|
pub fn raw_output(&mut self) -> process::Output {
|
||||||
|
let mut output = self.cmd.output().unwrap();
|
||||||
|
output.stderr = strip_jemalloc_nonsense(&output.stderr);
|
||||||
|
output
|
||||||
|
}
|
||||||
|
|
||||||
/// Runs the command and asserts that it resulted in an error exit code.
|
/// Runs the command and asserts that it resulted in an error exit code.
|
||||||
pub fn assert_err(&mut self) {
|
pub fn assert_err(&mut self) {
|
||||||
let o = self.cmd.output().unwrap();
|
let o = self.raw_output();
|
||||||
if o.status.success() {
|
if o.status.success() {
|
||||||
panic!(
|
panic!(
|
||||||
"\n\n===== {:?} =====\n\
|
"\n\n===== {:?} =====\n\
|
||||||
@@ -479,7 +471,7 @@ fn dir_list<P: AsRef<Path>>(dir: P) -> Vec<String> {
|
|||||||
/// So... we just manually handle these cases. So fucking fun.
|
/// So... we just manually handle these cases. So fucking fun.
|
||||||
fn cross_runner() -> Option<String> {
|
fn cross_runner() -> Option<String> {
|
||||||
let runner = std::env::var("CROSS_RUNNER").ok()?;
|
let runner = std::env::var("CROSS_RUNNER").ok()?;
|
||||||
if runner.is_empty() {
|
if runner.is_empty() || runner == "empty" {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
if cfg!(target_arch = "powerpc64") {
|
if cfg!(target_arch = "powerpc64") {
|
||||||
@@ -500,3 +492,17 @@ fn cross_runner() -> Option<String> {
|
|||||||
pub fn is_cross() -> bool {
|
pub fn is_cross() -> bool {
|
||||||
std::env::var("CROSS_RUNNER").ok().map_or(false, |v| !v.is_empty())
|
std::env::var("CROSS_RUNNER").ok().map_or(false, |v| !v.is_empty())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Strips absolutely fucked `<jemalloc>:` lines from the output.
|
||||||
|
///
|
||||||
|
/// In theory this only happens under qemu, which is where our tests run under
|
||||||
|
/// `cross`. But is messes with our tests, because... they don't expect the
|
||||||
|
/// allocator to fucking write to stderr. I mean, what the fuck? Who prints a
|
||||||
|
/// warning message with absolutely no instruction for what to do with it or
|
||||||
|
/// how to disable it. Absolutely fucking bonkers.
|
||||||
|
fn strip_jemalloc_nonsense(data: &[u8]) -> Vec<u8> {
|
||||||
|
let lines = data
|
||||||
|
.lines_with_terminator()
|
||||||
|
.filter(|line| !line.starts_with_str("<jemalloc>:"));
|
||||||
|
bstr::concat(lines)
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user