Skip to content

Commit d9c0210

Browse files
committed
Implement robust parsing for XML-like file path and content structures within code blocks
1 parent 2432a49 commit d9c0210

8 files changed

Lines changed: 125 additions & 30 deletions

File tree

‎packages/vscode/package.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "gemini-coder",
33
"displayName": "Code Web Chat",
44
"description": "CWC initializes your favorite chatbot with code and instructions, hands-free!",
5-
"version": "1.409.0",
5+
"version": "1.410.0",
66
"scripts": {
77
"build": "npx vsce package --no-dependencies",
88
"vscode:prepublish": "rimraf out && npm run compile",

‎packages/vscode/src/commands/apply-chat-response-command/utils/clipboard-parser/clipboard-parser.spec.ts‎

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -721,7 +721,7 @@ describe('clipboard-parser', () => {
721721
expect(result.type).toBe('patches')
722722
expect(result.patches).toHaveLength(2)
723723

724-
expect(result.patches![0].file_path).toBe('src/lorem.ts')
724+
expect(result.patches![0].file_path).toBe('src/lorem.html')
725725
expect(result.patches![1].file_path).toBe('src/ipsum.ts')
726726

727727
expect(result.patches![0].content).toBe(
@@ -732,6 +732,27 @@ describe('clipboard-parser', () => {
732732
)
733733
})
734734

735+
it('should parse multiple diff files format in variant l', () => {
736+
const text = load_test_case_file(
737+
'diff-multiple-files-variant-l',
738+
'diff-multiple-files-variant-l.txt'
739+
)
740+
const result = parse_response(text, true)
741+
742+
expect(result.type).toBe('patches')
743+
expect(result.patches).toHaveLength(2)
744+
745+
expect(result.patches![0].file_path).toBe('src/lorem.html')
746+
expect(result.patches![1].file_path).toBe('src/ipsum.ts')
747+
748+
expect(result.patches![0].content).toBe(
749+
load_test_case_file('diff-multiple-files-variant-l', 'file-1.txt')
750+
)
751+
expect(result.patches![1].content).toBe(
752+
load_test_case_file('diff-multiple-files-variant-l', 'file-2.txt')
753+
)
754+
})
755+
735756
it('should parse multiple diff files format with inner triple backticks', () => {
736757
const text = load_test_case_file(
737758
'diff-inner-triple-backticks',

‎packages/vscode/src/commands/apply-chat-response-command/utils/clipboard-parser/extract-diff-patches/extract-diffs.ts‎

Lines changed: 74 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -113,45 +113,93 @@ const convert_code_block_to_new_file_diff = (lines: string[]): Diff | null => {
113113
// Regex to find file path. It can be commented or not.
114114
// It handles paths with slashes, backslashes, dots, alphanumerics, underscores, and hyphens.
115115
const path_regex = /(?:(?:\/\/|#|--|<!--)\s*)?([\w./\\-]+)/
116+
const xml_path_regex = /<file\s+path=["']([^"']+)["']/
116117

117118
let file_path: string | undefined
118119
let path_line_index = -1
120+
let content_lines: string[] = []
119121

120-
// Look for a file path in the first few lines of the code block
121-
for (let i = 0; i < Math.min(lines.length, 5); i++) {
122-
const line = lines[i].trim()
123-
const match = line.match(path_regex)
124-
125-
if (match && match[1]) {
126-
const potential_path = match[1]
127-
128-
if (
129-
(potential_path.includes('.') || potential_path.includes('/')) &&
130-
!potential_path.includes(' ')
131-
) {
132-
const rest_of_line = line
133-
.substring(line.indexOf(potential_path) + potential_path.length)
134-
.trim()
135-
// e.g. `25:5` for line and column, or `-->` for html comments
136-
const is_just_path_and_location = /^(?:\d+:\d+)?\s*(-->)?\s*$/.test(
137-
rest_of_line
138-
)
139-
140-
if (is_just_path_and_location) {
141-
file_path = potential_path.replace(/\\/g, '/') // normalize backslashes
142-
path_line_index = i
143-
break // Found it, stop searching.
122+
// Check for XML path format
123+
const first_line = lines.length > 0 ? lines[0].trim() : ''
124+
const xml_match = first_line.match(xml_path_regex)
125+
126+
if (xml_match && xml_match[1]) {
127+
file_path = xml_match[1].replace(/\\/g, '/')
128+
129+
const file_tag_start = lines.findIndex((l) => l.trim().startsWith('<file'))
130+
let file_tag_end = -1
131+
for (let i = lines.length - 1; i >= 0; i--) {
132+
if (lines[i].trim().startsWith('</file>')) {
133+
file_tag_end = i
134+
break
135+
}
136+
}
137+
138+
if (
139+
file_tag_start !== -1 &&
140+
file_tag_end !== -1 &&
141+
file_tag_end > file_tag_start
142+
) {
143+
const inner_lines = lines.slice(file_tag_start + 1, file_tag_end)
144+
145+
const cdata_start = inner_lines.findIndex((l) =>
146+
l.trim().startsWith('<![CDATA[')
147+
)
148+
let cdata_end = -1
149+
for (let i = inner_lines.length - 1; i >= 0; i--) {
150+
if (inner_lines[i].trim().endsWith(']]>')) {
151+
cdata_end = i
152+
break
144153
}
145154
}
155+
156+
if (cdata_start !== -1 && cdata_end !== -1 && cdata_end > cdata_start) {
157+
content_lines = inner_lines.slice(cdata_start + 1, cdata_end)
158+
} else {
159+
content_lines = inner_lines
160+
}
161+
} else {
162+
// malformed, maybe path is on first line, and content follows
163+
content_lines = lines.slice(1)
164+
}
165+
} else {
166+
// Look for a file path in the first few lines of the code block
167+
for (let i = 0; i < Math.min(lines.length, 5); i++) {
168+
const line = lines[i].trim()
169+
const match = line.match(path_regex)
170+
171+
if (match && match[1]) {
172+
const potential_path = match[1]
173+
174+
if (
175+
(potential_path.includes('.') || potential_path.includes('/')) &&
176+
!potential_path.includes(' ')
177+
) {
178+
const rest_of_line = line
179+
.substring(line.indexOf(potential_path) + potential_path.length)
180+
.trim()
181+
// e.g. `25:5` for line and column, or `-->` for html comments
182+
const is_just_path_and_location = /^(?:\d+:\d+)?\s*(-->)?\s*$/.test(
183+
rest_of_line
184+
)
185+
186+
if (is_just_path_and_location) {
187+
file_path = potential_path.replace(/\\/g, '/') // normalize backslashes
188+
path_line_index = i
189+
break // Found it, stop searching.
190+
}
191+
}
192+
}
193+
}
194+
if (path_line_index !== -1) {
195+
content_lines = lines.filter((_, index) => index !== path_line_index)
146196
}
147197
}
148198

149199
if (!file_path) {
150200
return null
151201
}
152202

153-
const content_lines = lines.filter((_, index) => index !== path_line_index)
154-
155203
const patch_lines = content_lines.map((line) => `+${line}`)
156204
const patch_content = [
157205
`--- /dev/null`,

‎packages/vscode/src/commands/apply-chat-response-command/utils/clipboard-parser/test-cases/diff-multiple-files-variant-k/diff-multiple-files-variant-k.txt‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
```html
2-
// src/lorem.ts
2+
// src/lorem.html
33
<div>lorem ipsum</div>
44
```
55

Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
--- /dev/null
2-
+++ b/src/lorem.ts
2+
+++ b/src/lorem.html
33
@@ -0,0 +1,1 @@
44
+<div>lorem ipsum</div>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
```html
2+
<file path="src/lorem.html">
3+
<![CDATA[
4+
<div>lorem ipsum</div>
5+
]]>
6+
</file>
7+
```
8+
9+
```diff
10+
--- src/ipsum.ts
11+
+++ src/ipsum.ts
12+
@@ -1,3 +1,3 @@
13+
console.log("hello")
14+
-console.log("old ipsum")
15+
+console.log("new ipsum")
16+
```
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
--- /dev/null
2+
+++ b/src/lorem.html
3+
@@ -0,0 +1,1 @@
4+
+<div>lorem ipsum</div>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
--- a/src/ipsum.ts
2+
+++ b/src/ipsum.ts
3+
@@ -1,3 +1,3 @@
4+
console.log("hello")
5+
-console.log("old ipsum")
6+
+console.log("new ipsum")

0 commit comments

Comments
 (0)