Skip to content

Commit f6d6c4f

Browse files
committed
deduplicate code
1 parent 938b750 commit f6d6c4f

File tree

7 files changed

+441
-370
lines changed

7 files changed

+441
-370
lines changed

core/autocomplete/filtering/streamTransforms/lineStream.ts

Lines changed: 23 additions & 133 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,17 @@ import { distance } from "fastest-levenshtein";
33
import { DiffLine } from "../../..";
44
import { LineStream } from "../../../diff/util";
55

6+
import {
7+
headerIsMarkdown,
8+
isMarkdownFile,
9+
MarkdownBlockStateTracker,
10+
collectAllLines,
11+
} from "../../../utils/markdownUtils";
12+
import {
13+
shouldStopAtMarkdownBlock,
14+
processBlockNesting as processBlockNestingUtil,
15+
} from "../../../utils/streamMarkdownUtils";
16+
617
export { filterCodeBlockLines } from "./filterCodeBlock";
718

819
export type LineFilter = (args: {
@@ -18,23 +29,6 @@ export type CharacterFilter = (args: {
1829
multiline: boolean;
1930
}) => AsyncGenerator<string>;
2031

21-
// Duplicated from gui/src/components/StyledMarkdownPreview/utils/headerIsMarkdown.ts
22-
function headerIsMarkdown(header: string): boolean {
23-
return (
24-
header === "md" ||
25-
header === "markdown" ||
26-
header === "gfm" ||
27-
header === "github-markdown" ||
28-
header.includes(" md") ||
29-
header.includes(" markdown") ||
30-
header.includes(" gfm") ||
31-
header.includes(" github-markdown") ||
32-
header.split(" ")[0]?.split(".").pop() === "md" ||
33-
header.split(" ")[0]?.split(".").pop() === "markdown" ||
34-
header.split(" ")[0]?.split(".").pop() === "gfm"
35-
);
36-
}
37-
3832
function isBracketEnding(line: string): boolean {
3933
return line
4034
.trim()
@@ -124,19 +118,6 @@ function isUselessLine(line: string): boolean {
124118
return hasUselessLine || trimmed.startsWith("// end");
125119
}
126120

127-
/**
128-
* Determines if a file is a markdown file based on its filepath.
129-
*/
130-
export function isMarkdownFile(filepath?: string): boolean {
131-
if (!filepath) {
132-
return false;
133-
}
134-
135-
return ["md", "markdown", "gfm"].includes(
136-
filepath.split(".").pop()?.toLowerCase() || "",
137-
);
138-
}
139-
140121
/**
141122
* Determines if the code block has nested markdown blocks.
142123
*/
@@ -147,116 +128,25 @@ export function hasNestedMarkdownBlocks(
147128
return (
148129
(firstLine.startsWith("```") &&
149130
headerIsMarkdown(firstLine.replace(/`/g, ""))) ||
150-
Boolean(filepath && headerIsMarkdown(filepath))
131+
Boolean(filepath && isMarkdownFile(filepath))
151132
);
152133
}
153134

154-
/**
155-
* Collects all lines from a LineStream into an array for analysis.
156-
*/
157-
export async function collectAllLines(rawLines: LineStream): Promise<string[]> {
158-
const allLines: string[] = [];
159-
for await (const line of rawLines) {
160-
allLines.push(line);
161-
}
162-
return allLines;
163-
}
164-
165-
/**
166-
* State tracker for markdown block analysis to avoid recomputing on each call.
167-
*/
168-
export class MarkdownBlockState {
169-
private trimmedLines: string[];
170-
private bareBacktickPositions: number[];
171-
private markdownNestCount: number = 0;
172-
private lastProcessedIndex: number = -1;
173-
174-
constructor(allLines: string[]) {
175-
this.trimmedLines = allLines.map((l) => l.trim());
176-
// Pre-compute positions of all bare backtick lines for faster lookup
177-
this.bareBacktickPositions = [];
178-
for (let i = 0; i < this.trimmedLines.length; i++) {
179-
if (this.trimmedLines[i].match(/^`+$/)) {
180-
this.bareBacktickPositions.push(i);
181-
}
182-
}
183-
}
184-
185-
/**
186-
* Determines if we should stop at the given markdown block position.
187-
* Maintains state across calls to avoid redundant computation.
188-
*/
189-
shouldStopAtPosition(currentIndex: number): boolean {
190-
if (this.trimmedLines[currentIndex] !== "```") {
191-
return false;
192-
}
193-
194-
// Process any lines we haven't seen yet up to currentIndex
195-
for (let j = this.lastProcessedIndex + 1; j <= currentIndex; j++) {
196-
const currentLine = this.trimmedLines[j];
197-
198-
if (this.markdownNestCount > 0) {
199-
// Inside a markdown block
200-
if (currentLine.match(/^`+$/)) {
201-
// Found bare backticks - check if this is the last one
202-
if (j === currentIndex) {
203-
const remainingBareBackticks = this.bareBacktickPositions.filter(
204-
(pos) => pos > j,
205-
).length;
206-
if (remainingBareBackticks === 0) {
207-
this.markdownNestCount = 0;
208-
this.lastProcessedIndex = j;
209-
return true;
210-
}
211-
}
212-
} else if (currentLine.startsWith("```")) {
213-
// Going into a nested codeblock
214-
this.markdownNestCount++;
215-
}
216-
} else {
217-
// Not inside a markdown block yet
218-
if (currentLine.startsWith("```")) {
219-
const header = currentLine.replaceAll("`", "");
220-
if (headerIsMarkdown(header)) {
221-
this.markdownNestCount = 1;
222-
}
223-
}
224-
}
225-
}
226-
227-
this.lastProcessedIndex = currentIndex;
228-
return false;
229-
}
230-
}
231-
232-
/**
233-
* Determines if we should stop at a markdown block based on nested markdown logic.
234-
* This handles the complex case where markdown blocks contain other markdown blocks.
235-
* Uses optimized state tracking to avoid redundant computation.
236-
*/
237-
export function shouldStopAtMarkdownBlock(
238-
stateTracker: MarkdownBlockState,
239-
currentIndex: number,
240-
): boolean {
241-
return stateTracker.shouldStopAtPosition(currentIndex);
242-
}
135+
// Re-export shared utilities
136+
export { collectAllLines, isMarkdownFile };
137+
export { MarkdownBlockStateTracker as MarkdownBlockState };
138+
export { shouldStopAtMarkdownBlock };
243139

244-
/**
245-
* Processes block nesting logic and returns updated state.
246-
*/
140+
// Wrapper for processBlockNesting with local shouldRemoveLineBeforeStart function
247141
export function processBlockNesting(
248142
line: string,
249143
seenFirstFence: boolean,
250144
): { newSeenFirstFence: boolean; shouldSkip: boolean } {
251-
if (!seenFirstFence && shouldRemoveLineBeforeStart(line)) {
252-
return { newSeenFirstFence: false, shouldSkip: true };
253-
}
254-
255-
if (!seenFirstFence) {
256-
return { newSeenFirstFence: true, shouldSkip: false };
257-
}
258-
259-
return { newSeenFirstFence: seenFirstFence, shouldSkip: false };
145+
return processBlockNestingUtil(
146+
line,
147+
seenFirstFence,
148+
shouldRemoveLineBeforeStart,
149+
);
260150
}
261151

262152
export const USELESS_LINES = [""];
@@ -326,7 +216,7 @@ export async function* avoidPathLine(
326216
// Sometimes the model with copy this pattern, which is unwanted
327217
for await (const line of stream) {
328218
if (line.startsWith(`${comment} Path: `)) {
329-
continue;
219+
continue; // continue in the Continue codebase! How meta!
330220
}
331221
yield line;
332222
}

core/autocomplete/filtering/streamTransforms/lineStream.vitest.ts

Lines changed: 0 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -846,32 +846,6 @@ describe("lineStream", () => {
846846
});
847847

848848
// Tests for new helper functions
849-
describe("isMarkdownFile", () => {
850-
it("should return true for markdown file extensions", () => {
851-
expect(lineStream.isMarkdownFile("README.md")).toBe(true);
852-
expect(lineStream.isMarkdownFile("docs.markdown")).toBe(true);
853-
expect(lineStream.isMarkdownFile("notes.gfm")).toBe(true);
854-
expect(lineStream.isMarkdownFile("path/to/file.md")).toBe(true);
855-
});
856-
857-
it("should return false for non-markdown file extensions", () => {
858-
expect(lineStream.isMarkdownFile("script.js")).toBe(false);
859-
expect(lineStream.isMarkdownFile("styles.css")).toBe(false);
860-
expect(lineStream.isMarkdownFile("document.txt")).toBe(false);
861-
expect(lineStream.isMarkdownFile("config.json")).toBe(false);
862-
});
863-
864-
it("should return false for undefined or empty filepath", () => {
865-
expect(lineStream.isMarkdownFile(undefined)).toBe(false);
866-
expect(lineStream.isMarkdownFile("")).toBe(false);
867-
});
868-
869-
it("should handle case insensitive extensions", () => {
870-
expect(lineStream.isMarkdownFile("README.MD")).toBe(true);
871-
expect(lineStream.isMarkdownFile("docs.MARKDOWN")).toBe(true);
872-
expect(lineStream.isMarkdownFile("notes.GFM")).toBe(true);
873-
});
874-
});
875849

876850
describe("hasNestedMarkdownBlocks", () => {
877851
it("should detect nested markdown blocks from first line", () => {
@@ -1037,59 +1011,6 @@ describe("lineStream", () => {
10371011
});
10381012
});
10391013

1040-
describe("processBlockNesting", () => {
1041-
it("should skip lines that should be removed before start", () => {
1042-
const result = lineStream.processBlockNesting("```typescript", false);
1043-
expect(result).toEqual({
1044-
newSeenFirstFence: false,
1045-
shouldSkip: true,
1046-
});
1047-
});
1048-
1049-
it("should skip [CODE] blocks", () => {
1050-
const result = lineStream.processBlockNesting("[CODE]", false);
1051-
expect(result).toEqual({
1052-
newSeenFirstFence: false,
1053-
shouldSkip: true,
1054-
});
1055-
});
1056-
1057-
it("should set first fence when not seen yet", () => {
1058-
const result = lineStream.processBlockNesting("const x = 5;", false);
1059-
expect(result).toEqual({
1060-
newSeenFirstFence: true,
1061-
shouldSkip: false,
1062-
});
1063-
});
1064-
1065-
it("should not change state when first fence already seen", () => {
1066-
const result = lineStream.processBlockNesting("const x = 5;", true);
1067-
expect(result).toEqual({
1068-
newSeenFirstFence: true,
1069-
shouldSkip: false,
1070-
});
1071-
});
1072-
1073-
it("should handle <COMPLETION> prefix", () => {
1074-
const result = lineStream.processBlockNesting("<COMPLETION>", false);
1075-
expect(result).toEqual({
1076-
newSeenFirstFence: false,
1077-
shouldSkip: true,
1078-
});
1079-
});
1080-
1081-
it("should handle START EDITING HERE blocks", () => {
1082-
const result = lineStream.processBlockNesting(
1083-
"<START EDITING HERE>",
1084-
false,
1085-
);
1086-
expect(result).toEqual({
1087-
newSeenFirstFence: false,
1088-
shouldSkip: true,
1089-
});
1090-
});
1091-
});
1092-
10931014
describe("MarkdownBlockState", () => {
10941015
it("should initialize with correct state", () => {
10951016
const allLines = ["```markdown", "content", "```", "more content", "```"];

0 commit comments

Comments
 (0)