Skip to content

Commit 6be5714

Browse files
committed
refactor(diffUtils): remove SEARCH/REPLACE handling and normalize diff content processing
1 parent 223e04d commit 6be5714

File tree

2 files changed

+5
-85
lines changed

2 files changed

+5
-85
lines changed

webview-ui/src/components/chat/ChatRow.tsx

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -399,15 +399,11 @@ export const ChatRowContent = ({
399399
// Inline diff stats for edit/apply_diff/insert/search-replace/newFile asks
400400
const diffTextForStats = useMemo(() => {
401401
if (!tool) return ""
402-
// For appliedDiff, backend provides unified diff; do not fallback/normalize
403-
if ((tool as any).tool === "appliedDiff") {
404-
return ((tool as any).content as string) || ""
405-
}
406402
return (
407403
extractUnifiedDiff({
408404
toolName: tool.tool as string,
409405
path: tool.path,
410-
diff: (tool as any).content,
406+
diff: (tool as any).content ?? (tool as any).diff,
411407
content: (tool as any).diff,
412408
}) || ""
413409
)
@@ -420,14 +416,10 @@ export const ChatRowContent = ({
420416
// Clean diff content for display (normalize to unified diff)
421417
const cleanDiffContent = useMemo(() => {
422418
if (!tool) return undefined
423-
// For appliedDiff, show backend's unified diff directly
424-
if ((tool as any).tool === "appliedDiff") {
425-
return ((tool as any).content as string) || undefined
426-
}
427419
const unified = extractUnifiedDiff({
428420
toolName: tool.tool as string,
429421
path: tool.path,
430-
diff: (tool as any).content,
422+
diff: (tool as any).content ?? (tool as any).diff,
431423
content: (tool as any).diff,
432424
})
433425
return unified || undefined

webview-ui/src/utils/diffUtils.ts

Lines changed: 3 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
/**
22
* Frontend-only normalization helper.
33
* - If a unified diff already exists, return it.
4-
* - If a Roo SEARCH/REPLACE block is provided, convert to unified diff.
54
* - If it's a new file with raw content, synthesize a unified diff with all lines as additions.
65
* - Otherwise, pass through raw content (DiffView will no-op if not unified).
76
*/
@@ -17,17 +16,14 @@ export function extractUnifiedDiff(params: {
1716
if (!raw) return ""
1817

1918
raw = stripCData(raw)
19+
// Remove diff noise lines like "\ No newline at end of file"
20+
raw = raw.replace(/(^|\n)[ \t]*(?:\\ )?No newline at end of file[ \t]*(?=\n|$)/gi, "$1")
2021

2122
// Explicit new file: build a unified diff from raw content
2223
if ((params.toolName || "").toLowerCase() === "newfilecreated") {
2324
return convertNewFileToUnifiedDiff(raw, filePath)
2425
}
2526

26-
// SEARCH/REPLACE blocks → unified
27-
if (isSearchReplace(raw)) {
28-
return convertSearchReplaceToUnifiedDiff(raw, filePath)
29-
}
30-
3127
// Already unified?
3228
if (isUnifiedDiff(raw)) {
3329
return raw
@@ -44,13 +40,6 @@ function isUnifiedDiff(s: string): boolean {
4440
return hasHunk || hasHeaders
4541
}
4642

47-
/** Detects Roo SEARCH/REPLACE multi-block format */
48-
function isSearchReplace(s: string): boolean {
49-
return (
50-
/(^|\n)<<<<<<< ?SEARCH|(^|\n)<<<<<<<? ?SEARCH|(^|\n)<<<<<<< SEARCH>/.test(s) || /(^|\n)<<<<<<< ?SEARCH/.test(s)
51-
)
52-
}
53-
5443
/** Remove CDATA markers and any HTML-encoded variants */
5544
function stripCData(s: string): string {
5645
return (
@@ -61,71 +50,10 @@ function stripCData(s: string): string {
6150
)
6251
}
6352

64-
/**
65-
* Convert Roo SEARCH/REPLACE blocks into unified diff using the diff library.
66-
* Matches optional metadata lines and optional '-------' separator.
67-
*/
68-
export function convertSearchReplaceToUnifiedDiff(content: string, filePath?: string): string {
69-
// Backend-compatible regex: captures :start_line: and :end_line:, optional '-------', and SEARCH/REPLACE bodies
70-
const blockRegex =
71-
/(?:^|\n)(?<!\\)<<<<<<< SEARCH>?\s*\n((?::start_line:\s*(\d+)\s*\n))?((?::end_line:\s*(\d+)\s*\n))?((?<!\\)-------\s*\n)?([\s\S]*?)(?:\n)?(?:(?<=\n)(?<!\\)=======\s*\n)([\s\S]*?)(?:\n)?(?:(?<=\n)(?<!\\)>>>>>>> REPLACE)(?=\n|$)/g
72-
73-
const fileName = filePath || "file"
74-
let hasBlocks = false
75-
let headerEmitted = false
76-
let unified = ""
77-
78-
// Helper to normalize EOLs and get stable line arrays without trailing empty caused by final newline
79-
const toStableLines = (s: string): string[] => {
80-
const norm = s.replace(/\r\n/g, "\n")
81-
if (norm === "") return []
82-
const parts = norm.split("\n")
83-
return parts[parts.length - 1] === "" ? parts.slice(0, -1) : parts
84-
}
85-
86-
let match: RegExpExecArray | null
87-
while ((match = blockRegex.exec(content)) !== null) {
88-
hasBlocks = true
89-
90-
// 1: full start_line line, 2: start_line number, 3: full end_line line, 4: end_line number
91-
const startLine = match[2] ? parseInt(match[2], 10) : 1
92-
const endLine = match[4] ? parseInt(match[4], 10) : undefined
93-
94-
// 6: SEARCH body, 7: REPLACE body
95-
const searchBody = match[6] ?? ""
96-
const replaceBody = match[7] ?? ""
97-
98-
const searchLines = toStableLines(searchBody)
99-
const replaceLines = toStableLines(replaceBody)
100-
101-
// Old/new hunk metadata. If end_line is present, prefer it for oldLines; otherwise count SEARCH lines.
102-
const oldStart = startLine
103-
const oldLines = endLine !== undefined ? Math.max(0, endLine - startLine + 1) : searchLines.length
104-
const newStart = startLine
105-
const newLines = replaceLines.length
106-
107-
// Emit file headers once so parsePatch can recognize a complete unified diff
108-
if (!headerEmitted) {
109-
unified += `--- a/${fileName}\n`
110-
unified += `+++ b/${fileName}\n`
111-
headerEmitted = true
112-
}
113-
114-
// Hunk header
115-
unified += `@@ -${oldStart},${oldLines} +${newStart},${newLines} @@\n`
116-
117-
// We don't have surrounding context here; emit deletions then additions to visualize the change
118-
for (const line of searchLines) unified += `-${line}\n`
119-
for (const line of replaceLines) unified += `+${line}\n`
120-
}
121-
122-
return hasBlocks ? unified : content
123-
}
124-
12553
/** Build a unified diff for a brand new file (all content lines are additions).
12654
* Trailing newline is ignored for line counting and emission.
12755
*/
128-
export function convertNewFileToUnifiedDiff(content: string, filePath?: string): string {
56+
function convertNewFileToUnifiedDiff(content: string, filePath?: string): string {
12957
const fileName = filePath || "file"
13058
// Normalize EOLs to keep counts consistent
13159
const normalized = content.replace(/\r\n/g, "\n")

0 commit comments

Comments
 (0)