Skip to content

Commit 73e565c

Browse files
committed
dofs: Mark matching grep context lines
1 parent 7443e11 commit 73e565c

2 files changed

Lines changed: 43 additions & 9 deletions

File tree

packages/dofs/src/fs/grep.test.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,41 @@ describe("grep", () => {
9090
});
9191
});
9292

93+
it("marks adjacent matches as matching context", async () => {
94+
await withDB(async (db) => {
95+
await writeFile(db, "/a.txt", "TODO one\nTODO two\nplain\n", {}, () => 0);
96+
97+
const matches = await grep(db, "TODO", "/a.txt", { contextLines: 1 });
98+
expect(matches).toEqual([
99+
{
100+
path: "/a.txt",
101+
line: 1,
102+
text: "TODO one",
103+
context: [
104+
{ line: 1, text: "TODO one", isMatch: true },
105+
{ line: 2, text: "TODO two", isMatch: true },
106+
],
107+
},
108+
{
109+
path: "/a.txt",
110+
line: 2,
111+
text: "TODO two",
112+
context: [
113+
{ line: 1, text: "TODO one", isMatch: true },
114+
{ line: 2, text: "TODO two", isMatch: true },
115+
{ line: 3, text: "plain", isMatch: false },
116+
],
117+
},
118+
]);
119+
120+
if (matches[0].context === undefined || matches[1].context === undefined) {
121+
throw new Error("expected grep context");
122+
}
123+
matches[0].context[0].text = "changed";
124+
expect(matches[1].context[0].text).toBe("TODO one");
125+
});
126+
});
127+
93128
it("applies offset and limit across files in path and line order", async () => {
94129
await withDB(async (db) => {
95130
await writeFile(db, "/a.txt", "TODO a1\nTODO a2\n", {}, () => 0);

packages/dofs/src/fs/grep.ts

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -145,27 +145,26 @@ async function scanFile(
145145
state: ScanState,
146146
out: WorkspaceGrepMatch[],
147147
): Promise<boolean> {
148-
const before: NumberedLine[] = [];
148+
const before: WorkspaceGrepContextLine[] = [];
149149
const pending: PendingMatch[] = [];
150150

151151
for await (const current of readLines(db, path)) {
152+
const isMatch = matcher.test(current.text);
153+
const contextLine = { ...current, isMatch };
152154
for (const item of pending) {
153-
item.match.context?.push({ ...current, isMatch: false });
155+
item.match.context?.push({ ...contextLine });
154156
item.remaining -= 1;
155157
}
156158
flushReady(pending, out);
157159
if (state.accepted >= limit && pending.length === 0) return true;
158160

159-
if (matcher.test(current.text)) {
161+
if (isMatch) {
160162
const matchIndex = state.seen;
161163
state.seen += 1;
162164
if (matchIndex >= offset && state.accepted < limit) {
163-
const match: WorkspaceGrepMatch = { path, ...current };
165+
const match: WorkspaceGrepMatch = { path, line: current.line, text: current.text };
164166
if (contextLines > 0) {
165-
match.context = [
166-
...before.map((line) => ({ ...line, isMatch: false })),
167-
{ ...current, isMatch: true },
168-
];
167+
match.context = [...before.map((line) => ({ ...line })), { ...contextLine }];
169168
pending.push({ match, remaining: contextLines });
170169
} else {
171170
out.push(match);
@@ -174,7 +173,7 @@ async function scanFile(
174173
}
175174
}
176175

177-
before.push(current);
176+
before.push(contextLine);
178177
if (before.length > contextLines) before.shift();
179178
if (state.accepted >= limit && pending.length === 0) return true;
180179
}

0 commit comments

Comments
 (0)