Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 49 additions & 1 deletion src/services/ripgrep/__tests__/index.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@
// npx vitest run src/services/ripgrep/__tests__/index.spec.ts

import { truncateLine } from "../index"
import path from "path"
import { vi, describe, it, expect, beforeEach } from "vitest"

import { truncateLine, getBinPath } from "../index"
import { fileExistsAtPath } from "../../../utils/fs"

vi.mock("../../../utils/fs", () => ({
fileExistsAtPath: vi.fn(),
}))

const mockFileExists = vi.mocked(fileExistsAtPath)

describe("Ripgrep line truncation", () => {
// The default MAX_LINE_LENGTH is 500 in the implementation
Expand Down Expand Up @@ -48,3 +58,41 @@ describe("Ripgrep line truncation", () => {
expect(truncated).toContain("[truncated...]")
})
})

describe("getBinPath", () => {
const appRoot = "/fake/vscode/appRoot"
const binName = process.platform.startsWith("win") ? "rg.exe" : "rg"
const platformDir = `${process.platform}-${process.arch}`

beforeEach(() => {
mockFileExists.mockReset()
mockFileExists.mockResolvedValue(false)
})

it("resolves ripgrep from the classic @vscode/ripgrep layout", async () => {
const rg = path.join(appRoot, "node_modules/@vscode/ripgrep/bin", binName)
mockFileExists.mockImplementation(async (p: string) => p === rg)

expect(await getBinPath(appRoot)).toBe(rg)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a test that verifies probe priority — e.g. that when both classic and universal paths exist, the classic one wins? The waterfall ordering in getBinPath is load-bearing, but nothing currently asserts which path is returned when more than one fileExistsAtPath call would return true.

})

it("resolves ripgrep from the @vscode/ripgrep-universal layout (VS Code Insiders)", async () => {
const rg = path.join(appRoot, "node_modules/@vscode/ripgrep-universal/bin", platformDir, binName)
mockFileExists.mockImplementation(async (p: string) => p === rg)

expect(await getBinPath(appRoot)).toBe(rg)
})

it("resolves ripgrep from the unpacked `@vscode/ripgrep-universal` layout", async () => {
const rg = path.join(appRoot, "node_modules.asar.unpacked/@vscode/ripgrep-universal/bin", platformDir, binName)
mockFileExists.mockImplementation(async (p: string) => p === rg)

expect(await getBinPath(appRoot)).toBe(rg)
})

it("returns undefined when ripgrep cannot be found", async () => {
mockFileExists.mockResolvedValue(false)

expect(await getBinPath(appRoot)).toBeUndefined()
})
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The tests cover the classic layout, universal, and unpacked-universal probes — but getBinPath has three more candidates (index.ts lines 104–106): node_modules/vscode-ripgrep/bin, node_modules.asar.unpacked/vscode-ripgrep/bin/, and node_modules.asar.unpacked/@vscode/ripgrep/bin/. Worth adding cases for those too, otherwise a refactor that accidentally removes or reorders them would pass the suite undetected.

})
19 changes: 16 additions & 3 deletions src/services/ripgrep/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ This file provides functionality to perform regex searches on files using ripgre
Inspired by: https://github.com/DiscreteTom/vscode-ripgrep-utils

Key components:
1. getBinPath: Locates the ripgrep binary within the VSCode installation.
1. getBinPath: Locates the ripgrep binary inside the VS Code installation.
2. execRipgrep: Executes the ripgrep command and returns the output.
3. regexSearchFiles: The main function that performs regex searches on files.
- Parameters:
Expand Down Expand Up @@ -51,6 +51,11 @@ rel/path/to/helper.ts
const isWindows = process.platform.startsWith("win")
const binName = isWindows ? "rg.exe" : "rg"

// VS Code's @vscode/ripgrep-universal package (used by recent VS Code builds,
// including the Insiders staged-install layout) nests the binary under
// bin/<platform>-<arch>/ rather than directly in bin/.
const ripgrepUniversalBinDir = `bin/${process.platform}-${process.arch}`
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The name ripgrepUniversalBinDir reads as the platform-arch directory (e.g. linux-x64), but the value already includes the bin/ prefix: `bin/${process.platform}-${process.arch}`. A reader skimming lines 107–108 has to mentally expand the constant to see where bin/ lives. Something like ripgrepUniversalBinSubpath might make it clearer that this is the full sub-path from the package root, not just the leaf directory.


interface SearchFileResult {
file: string
searchResults: SearchResult[]
Expand Down Expand Up @@ -80,7 +85,13 @@ export function truncateLine(line: string, maxLength: number = MAX_LINE_LENGTH):
return line.length > maxLength ? line.substring(0, maxLength) + " [truncated...]" : line
}
/**
* Get the path to the ripgrep binary within the VSCode installation
* Get the path to the ripgrep binary shipped inside the VS Code installation.
*
* Both the long-standing `@vscode/ripgrep` layout and the newer
* `@vscode/ripgrep-universal` layout are checked — the latter is what VS Code
* Insiders' staged-install builds use (see microsoft/vscode#252063).
*
* Returns `undefined` when ripgrep cannot be located.
*/
export async function getBinPath(vscodeAppRoot: string): Promise<string | undefined> {
const checkPath = async (pkgFolder: string) => {
Expand All @@ -92,7 +103,9 @@ export async function getBinPath(vscodeAppRoot: string): Promise<string | undefi
(await checkPath("node_modules/@vscode/ripgrep/bin/")) ||
(await checkPath("node_modules/vscode-ripgrep/bin")) ||
(await checkPath("node_modules.asar.unpacked/vscode-ripgrep/bin/")) ||
(await checkPath("node_modules.asar.unpacked/@vscode/ripgrep/bin/"))
(await checkPath("node_modules.asar.unpacked/@vscode/ripgrep/bin/")) ||
(await checkPath(`node_modules/@vscode/ripgrep-universal/${ripgrepUniversalBinDir}`)) ||
(await checkPath(`node_modules.asar.unpacked/@vscode/ripgrep-universal/${ripgrepUniversalBinDir}`))
)
}

Expand Down
Loading