Jelajahi Sumber

Merge pull request #506 from danmackinlay/fix-505-json-line-output

feat: Include line in --json search output

# Conflicts:
#	CHANGELOG.md
Tobias Lütke 1 bulan lalu
induk
melakukan
76a2f0fb31
3 mengubah file dengan 21 tambahan dan 1 penghapusan
  1. 2 0
      CHANGELOG.md
  2. 5 1
      src/cli/formatter.ts
  3. 14 0
      test/formatter.test.ts

+ 2 - 0
CHANGELOG.md

@@ -25,6 +25,8 @@
   (thanks @Mic92)
 - Sync duplicated `handelize()` test expectations with the restored lowercase
   behavior.
+- Include `line` in `--json` search output so editor integrations can jump
+  directly to `file:line`. Closes #505 (thanks @danmackinlay)
 
 ## [2.0.1] - 2026-03-10
 

+ 5 - 1
src/cli/formatter.ts

@@ -101,8 +101,11 @@ export function searchResultsToJson(
   const query = opts.query || "";
   const output = results.map(row => {
     const bodyStr = row.body || "";
+    const snippetInfo = bodyStr
+      ? extractSnippet(bodyStr, query, 300, row.chunkPos, undefined, opts.intent)
+      : undefined;
     let body = opts.full ? bodyStr : undefined;
-    let snippet = !opts.full ? extractSnippet(bodyStr, query, 300, row.chunkPos, undefined, opts.intent).snippet : undefined;
+    let snippet = !opts.full ? snippetInfo?.snippet : undefined;
 
     if (opts.lineNumbers) {
       if (body) body = addLineNumbers(body);
@@ -113,6 +116,7 @@ export function searchResultsToJson(
       docid: `#${row.docid}`,
       score: Math.round(row.score * 100) / 100,
       file: row.displayPath,
+      ...(snippetInfo && { line: snippetInfo.line }),
       title: row.title,
       ...(row.context && { context: row.context }),
       ...(body && { body }),

+ 14 - 0
test/formatter.test.ts

@@ -95,6 +95,20 @@ describe("search results include context in all formats", () => {
     expect(parsed[0].context).toBe(TEST_CONTEXT);
   });
 
+  test("JSON format includes line", () => {
+    const output = searchResultsToJson(results, { query: "keynote" });
+    const parsed = JSON.parse(output);
+    expect(parsed[0].line).toBeTypeOf("number");
+    expect(parsed[0].line).toBeGreaterThan(0);
+  });
+
+  test("JSON format includes line with --full", () => {
+    const output = searchResultsToJson(results, { query: "keynote", full: true });
+    const parsed = JSON.parse(output);
+    expect(parsed[0].line).toBeTypeOf("number");
+    expect(parsed[0].line).toBeGreaterThan(0);
+  });
+
   test("CSV format includes context", () => {
     const output = searchResultsToCsv(results, { query: "keynote" });
     // Header should have context column