Conversation
…output output The gap test used Math.min(...addedLines), which is the start of the first block ever emitted rather than the end of the previous one, so `--` appeared between context blocks that touch or overlap. A match line first printed as another match's context kept the `-` prefix and stopped being identifiable as a match. The same spread is re-evaluated per match and exhausts the call stack past a few hundred thousand accumulated context lines, which surfaced to MCP clients as SEARCH_ERROR on repository-sized outputs. Track the previous block's end as a running maximum and decide the prefix from the set of matching line numbers instead of the current loop iteration. Measured on yamadashy#1868's fixture: 3 spurious `--` inside a contiguous 8-line run and one match downgraded to context; 30k disjoint matches took 79s and 60k threw. After: zero separators, all four matches marked, 27ms and 91ms. Context: yamadashy#1868 Constraint: Existing formatSearchResults expectations encoded the old output and were corrected in the same commit; keep `--` semantics identical to grep -C rather than inventing a denser format. Validation: npm run test (1822 passed, 20 skipped); npx tsc --noEmit; npm run lint exit 0; npm run build; with only the source fix reverted, the two new tests plus five corrected ones fail.
|
Important Review skippedAuto incremental reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Repository: yamadashy/repomix/.coderabbit.yaml Review profile: CHILL Plan: Advanced Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository: yamadashy/repomix/.coderabbit.yaml Review profile: CHILL Plan: Advanced Run ID: 📒 Files selected for processing (2)
Included review availability: Your plan provides up to 10 included reviews per hour; 8 remain after this review. 📝 WalkthroughWalkthrough
ChangesGrep output formatting
Priority: ➖ Normal Estimated code review effort: 3 (Moderate) | ~20 minutes Change: Bug fix · Severity of issue fixed: Medium 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Summary
grep_repomix_output'sformattedOutputis documented as grep-style output, but two of its formatting rules are notgrep -Crules, and the code implementing them fails outright on repository-sized outputs. Fixes #1868.The gap check compared the wrong pair of blocks.
addedLinesaccumulates every line emitted so far, soMath.min(...addedLines)is the start of the first block, not the end of the previous one. The condition reads "does this block start after the first line?" instead of "are lines missing between these two blocks?", so--was pushed between blocks that touch or overlap. TrackinglastBlockEndas a running maximum asks the intended question.A match could be labelled as context. The prefix came from
i === match.lineNumber - 1, evaluated only for the match currently being rendered, and lines already inaddedLineswere skipped. When an earlier match's context emitted a line that is itself a match, it kept the-prefix. Deciding from the set of matching line numbers makes the mark independent of emission order.The same expression was also the failure mode on large outputs. It re-spreads every accumulated line per match: quadratic, and past a few hundred thousand entries the argument list exhausts the stack.
lastBlockEndis a scalar comparison, so the loop is linear and has no argument limit to hit.Measured before / after
Fixture from #1868 (six lines, four matches two apart), called through the stdio MCP server with
contextLines: 1. Lines 50–57 are consecutive, so the correct output has no separators;totalMatchesreported 4 while only 3 lines were marked.Before:
After:
formatSearchResultsdirectly, disjoint matches 8 lines apart, context 1/1, one core:mainRangeErrorRangeErrorThe
RangeErroris caught by the tool and returned asreason: "SEARCH_ERROR"with the messageError: Maximum call stack size exceeded, so the client gets no result rather than a partial one.Tests
formatSearchResultscases: a real gap still separates, and overlapping blocks do not.--between overlapping blocks,'3-line 3'for a matching line, and the multilingual integration case where lines 2 and 3 are matches rendered as context). They are corrected here rather than relaxed — each now asserts thegrep -Cresult for the same input.Checklist
npm run testnpm run lintAlso
npx tsc --noEmitclean,npm run buildclean, and the change was reverted source-side only to confirm 7 tests fail without it.