diff --git a/src/utils/search.ts b/src/utils/search.ts index 3b10bb6..ec0d682 100644 --- a/src/utils/search.ts +++ b/src/utils/search.ts @@ -182,6 +182,29 @@ export function findExample(name: string): FileInfo | null { return match || null; } +/** + * Get numeric priority for a search result (lower = higher priority). + * SDK sources rank above example apps. + */ +export function getResultPriority(result: SearchResult): number { + const { repo, file } = result; + + if (repo === "aztec-packages") { + if (/\baztec-nr\b/.test(file) || /\byarn-project\b/.test(file)) return 1; + if (/\bnoir-contracts\b/.test(file)) return 2; + // Other aztec-packages paths (boxes, playground, etc.) + return 4; + } + + if (repo === "noir") { + if (/\bnoir_stdlib\b/.test(file)) return 3; + return 4; + } + + // Example apps / community repos + return 5; +} + // --- Helper functions --- /** @@ -197,8 +220,6 @@ function parseRgOutput(output: string, maxResults: number): SearchResult[] { const lines = output.split("\n").filter(Boolean); for (const line of lines) { - if (results.length >= maxResults) break; - // Format: /path/to/file:linenum:content const match = line.match(/^(.+?):(\d+):(.*)$/); if (match) { @@ -215,7 +236,8 @@ function parseRgOutput(output: string, maxResults: number): SearchResult[] { } } - return results; + results.sort((a, b) => getResultPriority(a) - getResultPriority(b)); + return results.slice(0, maxResults); } function manualSearch( @@ -243,15 +265,11 @@ function manualSearch( } for (const file of files) { - if (results.length >= maxResults) break; - try { const content = readFileSync(file, "utf-8"); const lines = content.split("\n"); for (let i = 0; i < lines.length; i++) { - if (results.length >= maxResults) break; - if (searchRegex.test(lines[i])) { const relativePath = relative(REPOS_DIR, file); const repoPart = relativePath.split("/")[0]; @@ -275,7 +293,8 @@ function manualSearch( // Globby error, return empty } - return results; + results.sort((a, b) => getResultPriority(a) - getResultPriority(b)); + return results.slice(0, maxResults); } function findContracts(basePath: string, repoName: string): FileInfo[] { diff --git a/tests/utils/search.test.ts b/tests/utils/search.test.ts index e87905e..1b557e5 100644 --- a/tests/utils/search.test.ts +++ b/tests/utils/search.test.ts @@ -30,6 +30,7 @@ import { readFile, findExample, getFileType, + getResultPriority, } from "../../src/utils/search.js"; const mockExecSync = vi.mocked(execSync); @@ -365,3 +366,83 @@ describe("findExample", () => { expect(result).toBeNull(); }); }); + +describe("getResultPriority", () => { + it("ranks aztec-nr / yarn-project as highest priority (1)", () => { + expect(getResultPriority({ repo: "aztec-packages", file: "aztec-packages/yarn-project/aztec.js/src/main.ts", content: "", line: 1 })).toBe(1); + expect(getResultPriority({ repo: "aztec-packages", file: "aztec-packages/aztec-nr/aztec/src/lib.nr", content: "", line: 1 })).toBe(1); + }); + + it("ranks noir-contracts as priority 2", () => { + expect(getResultPriority({ repo: "aztec-packages", file: "aztec-packages/noir-projects/noir-contracts/token/src/main.nr", content: "", line: 1 })).toBe(2); + }); + + it("ranks noir_stdlib as priority 3", () => { + expect(getResultPriority({ repo: "noir", file: "noir/noir_stdlib/src/hash.nr", content: "", line: 1 })).toBe(3); + }); + + it("ranks other aztec-packages and noir paths as priority 4", () => { + expect(getResultPriority({ repo: "aztec-packages", file: "aztec-packages/boxes/token/src/main.nr", content: "", line: 1 })).toBe(4); + expect(getResultPriority({ repo: "noir", file: "noir/tooling/nargo/src/lib.rs", content: "", line: 1 })).toBe(4); + }); + + it("ranks example repos as lowest priority (5)", () => { + expect(getResultPriority({ repo: "aztec-examples", file: "aztec-examples/token/src/main.nr", content: "", line: 1 })).toBe(5); + expect(getResultPriority({ repo: "aztec-starter", file: "aztec-starter/src/main.nr", content: "", line: 1 })).toBe(5); + expect(getResultPriority({ repo: "gregoswap", file: "gregoswap/src/main.nr", content: "", line: 1 })).toBe(5); + }); +}); + +describe("search result sorting", () => { + it("sorts ripgrep results by source priority", () => { + mockExistsSync.mockReturnValue(true); + mockExecSync.mockReturnValue( + "/fake/repos/gregoswap/src/main.nr:1:fn transfer() {\n" + + "/fake/repos/aztec-packages/yarn-project/aztec.js/src/main.ts:5:fn transfer() {\n" + + "/fake/repos/aztec-examples/token/src/main.nr:3:fn transfer() {\n" + + "/fake/repos/aztec-packages/noir-projects/noir-contracts/token/src/main.nr:10:fn transfer() {\n" + + "/fake/repos/noir/noir_stdlib/src/hash.nr:7:fn transfer() {\n" + ); + + const results = searchCode("transfer"); + expect(results[0].repo).toBe("aztec-packages"); + expect(results[0].file).toContain("yarn-project"); + expect(results[1].repo).toBe("aztec-packages"); + expect(results[1].file).toContain("noir-contracts"); + expect(results[2].repo).toBe("noir"); + expect(results[2].file).toContain("noir_stdlib"); + expect(results[3].repo).toBe("gregoswap"); + expect(results[4].repo).toBe("aztec-examples"); + }); + + it("sorts manual search results by source priority", () => { + mockExistsSync.mockReturnValue(true); + mockExecSync.mockImplementation(() => { throw new Error("rg not found"); }); + mockGlobbySync.mockReturnValue([ + "/fake/repos/aztec-starter/src/main.nr", + "/fake/repos/aztec-packages/yarn-project/aztec.js/src/lib.nr", + ]); + mockReadFileSync + .mockReturnValueOnce("fn transfer() {" as any) + .mockReturnValueOnce("fn transfer() {" as any); + + const results = searchCode("transfer"); + expect(results).toHaveLength(2); + expect(results[0].repo).toBe("aztec-packages"); + expect(results[1].repo).toBe("aztec-starter"); + }); + + it("applies sorting before maxResults limit", () => { + mockExistsSync.mockReturnValue(true); + mockExecSync.mockReturnValue( + "/fake/repos/gregoswap/src/a.nr:1:match\n" + + "/fake/repos/aztec-examples/src/b.nr:2:match\n" + + "/fake/repos/aztec-packages/yarn-project/c.nr:3:match\n" + ); + + const results = searchCode("match", { maxResults: 2 }); + expect(results).toHaveLength(2); + // SDK result should be kept even though it appeared last in raw output + expect(results[0].repo).toBe("aztec-packages"); + }); +});