Skip to content

Commit 8e50316

Browse files
committed
Support parsing file content enclosed in non-standard XML tags e.g., addfile path="..." within chat responses
1 parent d476578 commit 8e50316

5 files changed

Lines changed: 53 additions & 18 deletions

File tree

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

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,21 @@ describe('clipboard-parser', () => {
8080
})
8181
})
8282

83+
it('parses file when using file-xml format with a non-standard tag', () => {
84+
const test_case = 'file-xml-non-standard-tag'
85+
const text = load_test_case_file(test_case, `${test_case}.txt`)
86+
const result = parse_multiple_files({
87+
response: text,
88+
is_single_root_folder_workspace: true
89+
})
90+
91+
expect(result).toHaveLength(1)
92+
expect(result[0]).toMatchObject({
93+
file_path: 'src/index.ts',
94+
content: load_test_case_file(test_case, '1-file.txt')
95+
})
96+
})
97+
8398
it('parses file when CDATA is present inside a markdown code block with a comment filename', () => {
8499
const test_case = 'cdata-inside-code-block'
85100
const text = load_test_case_file(test_case, `${test_case}.txt`)

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

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ const convert_code_block_to_new_file_diff = (params: {
167167
// Regex to find file path. It can be commented or not.
168168
// It handles paths with slashes, backslashes, dots, alphanumerics, underscores, and hyphens.
169169
const path_regex = /(?:(?:\/\/|#|--|<!--)\s*)?([\w./\\-]+)/
170-
const xml_path_regex = /<file\s+path=["']([^"']+)["']/
170+
const xml_path_regex = /<(\w+)\s+path=["']([^"']+)["']/
171171

172172
let file_path: string | undefined = params.file_path_hint
173173
let path_line_index = -1
@@ -177,20 +177,21 @@ const convert_code_block_to_new_file_diff = (params: {
177177
const first_line = params.lines.length > 0 ? params.lines[0].trim() : ''
178178
const xml_match = first_line.match(xml_path_regex)
179179

180-
if (xml_match && xml_match[1]) {
181-
const potential_path = xml_match[1].replace(/\\/g, '/')
180+
if (xml_match && xml_match[1] && xml_match[2]) {
181+
const xml_tag_name = xml_match[1]
182+
const potential_path = xml_match[2].replace(/\\/g, '/')
182183
if (potential_path.endsWith('/')) {
183184
// This is a directory, not a file.
184185
return null
185186
}
186187
file_path = potential_path
187188

188189
const file_tag_start = params.lines.findIndex((l) =>
189-
l.trim().startsWith('<file')
190+
l.trim().startsWith(`<${xml_tag_name}`)
190191
)
191192
let file_tag_end = -1
192193
for (let i = params.lines.length - 1; i >= 0; i--) {
193-
if (params.lines[i].trim().startsWith('</file>')) {
194+
if (params.lines[i].trim().startsWith(`</${xml_tag_name}>`)) {
194195
file_tag_end = i
195196
break
196197
}

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

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,14 @@ import {
77
} from '../clipboard-parser'
88
import { parse_file_content_only } from './file-content-only-parser'
99

10-
const extract_file_path_from_xml = (line: string): string | null => {
11-
const match = line.match(/<file\s+path=["']([^"']+)["']/)
12-
return match ? match[1] : null
10+
const extract_file_path_from_xml = (
11+
line: string
12+
): { tagName: string; path: string } | null => {
13+
const match = line.match(/<(\w+)\s+path=["']([^"']+)["']/)
14+
if (match && match[1] && match[2]) {
15+
return { tagName: match[1], path: match[2] }
16+
}
17+
return null
1318
}
1419

1520
export const parse_multiple_files = (params: {
@@ -37,6 +42,7 @@ export const parse_multiple_files = (params: {
3742
let last_seen_file_path_comment: string | null = null
3843
let is_markdown_container_block = false
3944
let current_language = ''
45+
let current_xml_tag: string | null = null
4046

4147
const lines = params.response.split('\n')
4248

@@ -136,18 +142,19 @@ export const parse_multiple_files = (params: {
136142
in_cdata = false
137143
is_markdown_container_block = false
138144
continue
139-
} else if (line.trim().startsWith('<file')) {
140-
const extracted_filename = extract_file_path_from_xml(line)
141-
if (extracted_filename) {
145+
} else if (line.trim().startsWith('<')) {
146+
const xml_info = extract_file_path_from_xml(line)
147+
if (xml_info) {
142148
if (current_text_block.trim()) {
143149
results.push({ type: 'text', content: current_text_block.trim() })
144150
}
145151
current_text_block = ''
146152

147153
state = 'CONTENT'
154+
current_xml_tag = xml_info.tagName
148155
top_level_xml_file_mode = true
149156
const { workspace_name, relative_path } = extract_workspace_and_path({
150-
raw_file_path: extracted_filename,
157+
raw_file_path: xml_info.path,
151158
is_single_root_folder_workspace:
152159
params.is_single_root_folder_workspace
153160
})
@@ -228,7 +235,7 @@ export const parse_multiple_files = (params: {
228235
}
229236
} else if (state == 'CONTENT') {
230237
if (top_level_xml_file_mode) {
231-
if (line.trim().startsWith('</file>')) {
238+
if (line.trim().startsWith(`</${current_xml_tag}>`)) {
232239
let final_content = current_content.trim()
233240
const content_lines = final_content.split('\n')
234241
if (
@@ -265,6 +272,7 @@ export const parse_multiple_files = (params: {
265272
current_workspace_name = undefined
266273
top_level_xml_file_mode = false
267274
in_cdata = false
275+
current_xml_tag = null
268276
} else if (line.trim().startsWith('<![CDATA[')) {
269277
in_cdata = true
270278
} else if (in_cdata && line.trim().includes(']]>')) {
@@ -386,12 +394,12 @@ export const parse_multiple_files = (params: {
386394
current_text_block = line.substring(last_backticks_index + 3)
387395
}
388396
} else {
389-
if (is_first_content_line && line.trim().startsWith('<file')) {
390-
const extracted_filename = extract_file_path_from_xml(line)
391-
if (extracted_filename) {
397+
if (is_first_content_line && line.trim().startsWith('<')) {
398+
const xml_info = extract_file_path_from_xml(line)
399+
if (xml_info) {
392400
const { workspace_name, relative_path } =
393401
extract_workspace_and_path({
394-
raw_file_path: extracted_filename,
402+
raw_file_path: xml_info.path,
395403
is_single_root_folder_workspace:
396404
params.is_single_root_folder_workspace
397405
})
@@ -400,6 +408,7 @@ export const parse_multiple_files = (params: {
400408
current_workspace_name = workspace_name
401409
}
402410
xml_file_mode = true
411+
current_xml_tag = xml_info.tagName
403412
is_first_content_line = false
404413
continue
405414
}
@@ -531,7 +540,11 @@ export const parse_multiple_files = (params: {
531540
continue
532541
}
533542

534-
if (xml_file_mode && !in_cdata && line.trim() == '</file>') {
543+
if (
544+
xml_file_mode &&
545+
!in_cdata &&
546+
line.trim() == `</${current_xml_tag}>`
547+
) {
535548
continue
536549
}
537550

Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
console.log("hello")
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
```typescript
2+
<addfile path="src/index.ts">
3+
console.log("hello")
4+
</addfile>
5+
```

0 commit comments

Comments
 (0)