Repository navigation
feat(ripgrep): add Show Ripgrep Diagnostic command #281
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 4 commits
26ae683
3c10668
7d2fc7d
156c65c
4f2fab9
1e7a846
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,156 @@ | ||
| // npx vitest run src/services/ripgrep/__tests__/diagnostic.spec.ts | ||
|
|
||
| import * as path from "path" | ||
|
|
||
| import { vi, describe, it, expect, beforeEach } from "vitest" | ||
|
|
||
| import { getRipgrepDiagnostic } from "../diagnostic" | ||
|
|
||
| const ripgrepMock = vi.hoisted(() => ({ | ||
| value: undefined as { rgPath?: string; loadError?: string } | undefined, | ||
| })) | ||
|
|
||
| const fsMock = vi.hoisted(() => ({ | ||
| existing: new Set<string>(), | ||
| })) | ||
|
|
||
| vi.mock("../internal/loadRipgrep", () => ({ | ||
| loadRipgrep: () => ripgrepMock.value, | ||
| })) | ||
|
|
||
| vi.mock("../../../utils/fs", () => ({ | ||
| fileExistsAtPath: (p: string) => Promise.resolve(fsMock.existing.has(p)), | ||
| })) | ||
|
|
||
| const APP_ROOT = "/app" | ||
|
|
||
| const binName = process.platform.startsWith("win") ? "rg.exe" : "rg" | ||
| const universalRelBin = `bin/${process.platform}-${process.arch}/${binName}` | ||
|
|
||
| const expectedCandidates = [ | ||
| path.join(APP_ROOT, "node_modules", "@vscode", "ripgrep", "bin", binName), | ||
| path.join(APP_ROOT, "node_modules", "vscode-ripgrep", "bin", binName), | ||
| path.join(APP_ROOT, "node_modules.asar.unpacked", "vscode-ripgrep", "bin", binName), | ||
| path.join(APP_ROOT, "node_modules.asar.unpacked", "@vscode", "ripgrep", "bin", binName), | ||
| path.join(APP_ROOT, "node_modules", "@vscode", "ripgrep-universal", ...universalRelBin.split("/")), | ||
| path.join(APP_ROOT, "node_modules.asar.unpacked", "@vscode", "ripgrep-universal", ...universalRelBin.split("/")), | ||
| ] | ||
|
|
||
| describe("getRipgrepDiagnostic", () => { | ||
| beforeEach(() => { | ||
| ripgrepMock.value = undefined | ||
| fsMock.existing = new Set<string>() | ||
| }) | ||
|
|
||
| it("includes rgPath and fileExistsAtPath: true when loadRipgrep returns an existing path", async () => { | ||
| const rgPath = "/some/path" | ||
| ripgrepMock.value = { rgPath } | ||
| fsMock.existing = new Set([rgPath]) | ||
|
|
||
| const report = await getRipgrepDiagnostic(APP_ROOT) | ||
|
|
||
| expect(report).toContain("rgPath: /some/path") | ||
| expect(report).toContain("fileExistsAtPath: true") | ||
| expect(report).toContain("after .asar→.asar.unpacked: /some/path") | ||
| }) | ||
|
|
||
| it("rewrites node_modules.asar to node_modules.asar.unpacked in the report", async () => { | ||
| const rgPath = "/app/node_modules.asar/foo/rg" | ||
| const substituted = "/app/node_modules.asar.unpacked/foo/rg" | ||
| ripgrepMock.value = { rgPath } | ||
| fsMock.existing = new Set([substituted]) | ||
|
|
||
| const report = await getRipgrepDiagnostic(APP_ROOT) | ||
|
|
||
| expect(report).toContain(`after .asar→.asar.unpacked: ${substituted}`) | ||
| expect(report).toContain("fileExistsAtPath: true") | ||
| }) | ||
|
|
||
| it("does not double-substitute when rgPath already contains node_modules.asar.unpacked", async () => { | ||
| // A previous `\b` regex matched the `r`/`.` boundary inside | ||
| // `node_modules.asar.unpacked`, producing `.unpacked.unpacked`. | ||
| const rgPath = "/app/node_modules.asar.unpacked/foo/rg" | ||
| ripgrepMock.value = { rgPath } | ||
| fsMock.existing = new Set([rgPath]) | ||
|
|
||
| const report = await getRipgrepDiagnostic(APP_ROOT) | ||
|
|
||
| expect(report).toContain(`after .asar→.asar.unpacked: ${rgPath}`) | ||
| expect(report).not.toContain("node_modules.asar.unpacked.unpacked") | ||
| expect(report).toContain("fileExistsAtPath: true") | ||
| }) | ||
|
|
||
| it("reports require failure when loadRipgrep returns undefined", async () => { | ||
| ripgrepMock.value = undefined | ||
|
|
||
| const report = await getRipgrepDiagnostic(APP_ROOT) | ||
|
|
||
| expect(report).toContain("loadRipgrep() returned undefined (require threw)") | ||
| }) | ||
|
|
||
| it("reports the loadError when loadRipgrep returns a loadError field", async () => { | ||
| ripgrepMock.value = { loadError: "Cannot find module '@vscode/ripgrep'" } | ||
|
|
||
| const report = await getRipgrepDiagnostic(APP_ROOT) | ||
|
|
||
| expect(report).toContain("loadRipgrep() returned loadError: Cannot find module '@vscode/ripgrep'") | ||
| // Should not also push the success-branch lines. | ||
| expect(report).not.toContain("after .asar→.asar.unpacked:") | ||
| expect(report).not.toContain("rgPath:") | ||
| }) | ||
|
|
||
| it("reports rgPath: (undefined) when loadRipgrep returns an object without rgPath", async () => { | ||
| ripgrepMock.value = {} | ||
|
|
||
| const report = await getRipgrepDiagnostic(APP_ROOT) | ||
|
|
||
| expect(report).toContain("rgPath: (undefined)") | ||
| expect(report).not.toContain("after .asar→.asar.unpacked:") | ||
| }) | ||
|
|
||
| it("marks only the first probe candidate as found when only it exists", async () => { | ||
| fsMock.existing = new Set([expectedCandidates[0]]) | ||
|
|
||
| const report = await getRipgrepDiagnostic(APP_ROOT) | ||
|
|
||
| const found = expectedCandidates.filter((c) => report.includes(`✓ ${c}`)) | ||
| const missing = expectedCandidates.filter((c) => report.includes(`✗ ${c}`)) | ||
|
|
||
| expect(found).toEqual([expectedCandidates[0]]) | ||
| expect(missing).toEqual(expectedCandidates.slice(1)) | ||
| }) | ||
|
|
||
| it("marks all probe candidates as missing when none exist", async () => { | ||
| fsMock.existing = new Set<string>() | ||
|
|
||
| const report = await getRipgrepDiagnostic(APP_ROOT) | ||
|
|
||
| for (const candidate of expectedCandidates) { | ||
| expect(report).toContain(`✗ ${candidate}`) | ||
| } | ||
| expect(report).not.toContain("✓ ") | ||
| }) | ||
|
|
||
| it("rewrites node_modules.asar to node_modules.asar.unpacked on Windows paths (backslash separator)", async () => { | ||
| // Pure string-substitution test: the literal `win32-x64` segment is | ||
| // not derived from process.platform/arch, so this runs on any host. | ||
| const rgPath = "C:\\app\\node_modules.asar\\@vscode\\ripgrep-universal\\bin\\win32-x64\\rg.exe" | ||
| const substituted = "C:\\app\\node_modules.asar.unpacked\\@vscode\\ripgrep-universal\\bin\\win32-x64\\rg.exe" | ||
| ripgrepMock.value = { rgPath } | ||
| fsMock.existing = new Set([substituted]) | ||
|
|
||
| const report = await getRipgrepDiagnostic("C:\\app") | ||
|
|
||
| expect(report).toContain(`after .asar→.asar.unpacked: ${substituted}`) | ||
| expect(report).toContain("fileExistsAtPath: true") | ||
| }) | ||
|
|
||
| it("returns an explanatory report when vscode.env.appRoot is empty", async () => { | ||
| const report = await getRipgrepDiagnostic("") | ||
|
|
||
| expect(report).toContain("vscode.env.appRoot: (empty)") | ||
| expect(report).toContain("Cannot run diagnostic") | ||
| // path probe should NOT have run | ||
| expect(report).not.toContain("--- step 2: path probe under appRoot ---") | ||
| }) | ||
| }) |
Uh oh!
There was an error while loading. Please reload this page.