Skip to content

Commit 55ef194

Browse files
committed
Enhance clipboard parsing to correctly extract file paths from XML tags, including unquoted paths and diff content embedded within XML structures
1 parent 6e31c39 commit 55ef194

14 files changed

Lines changed: 166 additions & 12 deletions

File tree

‎packages/vscode/package.json‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "gemini-coder",
33
"displayName": "Code Web Chat",
44
"description": "Superfast AI coding (CWC)",
5-
"version": "1.623.0",
5+
"version": "1.624.0",
66
"scripts": {
77
"build": "npx vsce package --no-dependencies",
88
"vscode:prepublish": "rimraf out && npm run compile",
@@ -578,9 +578,9 @@
578578
"scope": "resource",
579579
"description": "Style of generated code blocks in \"Edit context\" mode.",
580580
"default": {
581-
"whole": "Whenever showing a new or updated file: provide brief explanations, then print full path in a heading, and a markdown code block with its full contents—as I have a disability which means I can't type and need to be able to just copy and paste.",
582-
"truncated": "Whenever showing a new or updated file: provide brief explanations, then print full path in a heading, and a markdown code block with its truncated contents—as a space saving measure (unchanged parts should be hidden behind ellipsis comments, e.g. \"// ...\").",
583-
"diff": "Whenever showing a new or updated file: provide brief explanations, then print unified diff within markdown code block."
581+
"whole": "Whenever showing a new or updated file, provide brief explanation, then print path in a markdown heading (e.g. ### File: `src/index.ts`), followed by a markdown code block with full contents—as I have a disability which means I can't type and need to be able to just copy and paste.",
582+
"truncated": "Whenever showing a new or updated file, provide brief explanation, then print path in a markdown heading (e.g. ### File: `src/index.ts`), followed by a markdown code block with truncated contents—as a space saving measure (unchanged parts should be hidden behind ellipsis comments, e.g. \"// ...\").",
583+
"diff": "Whenever showing a new or updated file, provide brief explanation and print the Unified Diff within a markdown code block. Example: \n[brief explanation]\n```diff\n--- a/file.py\n+++ b/file.py\n@@ -2,1 +2,1 @@\n-old_value = 10\n+new_value = 20\n```"
584584
},
585585
"properties": {
586586
"whole": {

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

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -618,6 +618,26 @@ describe('clipboard-parser', () => {
618618
})
619619
})
620620

621+
it('parses file path from a code block preceding a content code block without blank line', () => {
622+
const test_case = 'path-above-code-block-enclosed-in-code-block-2'
623+
const text = load_test_case_file(test_case, `${test_case}.txt`)
624+
const result = parse_multiple_files({
625+
response: text,
626+
is_single_root_folder_workspace: true
627+
})
628+
629+
expect(result).toHaveLength(2)
630+
expect(result[0]).toMatchObject({
631+
type: 'text',
632+
content: load_test_case_file(test_case, '1-text.txt')
633+
})
634+
expect(result[1]).toMatchObject({
635+
type: 'file',
636+
file_path: 'src/hello-world.ts',
637+
content: load_test_case_file(test_case, '2-file.txt')
638+
})
639+
})
640+
621641
it('extracts file path from backticks when there are no intermediate lines before code block', () => {
622642
const test_case = 'path-in-backticks-no-intermediate-empty-lines'
623643
const text = load_test_case_file(test_case, `${test_case}.txt`)
@@ -1527,5 +1547,53 @@ describe('clipboard-parser', () => {
15271547
content: load_test_case_file(test_case, '1-file.txt')
15281548
})
15291549
})
1550+
1551+
it('parses diff within XML tags', () => {
1552+
const test_case = 'diff-within-xml-tags'
1553+
const text = load_test_case_file(test_case, `${test_case}.txt`)
1554+
const result = parse_response({
1555+
response: text,
1556+
is_single_root_folder_workspace: true
1557+
})
1558+
1559+
expect(result).toHaveLength(1)
1560+
expect(result[0]).toMatchObject({
1561+
type: 'diff',
1562+
file_path: 'src/index.ts',
1563+
content: load_test_case_file(test_case, '1-file.txt')
1564+
})
1565+
})
1566+
1567+
it('parses diff within XML tags with CDATA', () => {
1568+
const test_case = 'diff-within-xml-tags-with-cdata'
1569+
const text = load_test_case_file(test_case, `${test_case}.txt`)
1570+
const result = parse_response({
1571+
response: text,
1572+
is_single_root_folder_workspace: true
1573+
})
1574+
1575+
expect(result).toHaveLength(1)
1576+
expect(result[0]).toMatchObject({
1577+
type: 'diff',
1578+
file_path: 'src/index.ts',
1579+
content: load_test_case_file(test_case, '1-file.txt')
1580+
})
1581+
})
1582+
1583+
it('parses diff within XML tags with unquoted path', () => {
1584+
const test_case = 'diff-within-xml-tags-unquoted-path'
1585+
const text = load_test_case_file(test_case, `${test_case}.txt`)
1586+
const result = parse_response({
1587+
response: text,
1588+
is_single_root_folder_workspace: true
1589+
})
1590+
1591+
expect(result).toHaveLength(1)
1592+
expect(result[0]).toMatchObject({
1593+
type: 'diff',
1594+
file_path: 'src/index.ts',
1595+
content: load_test_case_file(test_case, '1-file.txt')
1596+
})
1597+
})
15301598
})
15311599
})

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

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

341341
let file_path: string | undefined = params.file_path_hint
342342
let path_line_index = -1
@@ -346,9 +346,9 @@ const convert_code_block_to_new_file_diff = (params: {
346346
const first_line = params.lines.length > 0 ? params.lines[0].trim() : ''
347347
const xml_match = first_line.match(xml_path_regex)
348348

349-
if (xml_match && xml_match[1] && xml_match[2]) {
349+
if (xml_match && xml_match[1] && (xml_match[2] || xml_match[3])) {
350350
const xml_tag_name = xml_match[1]
351-
const potential_path = normalize_path(xml_match[2])
351+
const potential_path = normalize_path(xml_match[2] || xml_match[3])
352352
if (potential_path.endsWith('/')) {
353353
// This is a directory, not a file.
354354
return null
@@ -446,8 +446,11 @@ const convert_code_block_to_new_file_diff = (params: {
446446
raw_file_path: file_path,
447447
is_single_root_folder_workspace: params.is_single_root
448448
})
449+
450+
const is_new_file = first_real_content_line.trim().startsWith('@@ -0,0')
451+
449452
const patch_content = [
450-
`--- a/${relative_path}`,
453+
is_new_file ? '--- /dev/null' : `--- a/${relative_path}`,
451454
`+++ b/${relative_path}`,
452455
...content_lines
453456
].join('\n')
@@ -557,7 +560,7 @@ const extract_all_code_block_patches = (params: {
557560
code_blocks[i - 1].start + 1,
558561
code_blocks[i - 1].end
559562
)
560-
const xml_path_regex = /<file\s+path=["']([^"']+)["']([^>]*)>/
563+
const xml_path_regex = /<[\w-]+\s+path=["']([^"']+)["']([^>]*)>/
561564
const non_empty_lines = prev_block_lines.filter((l) => l.trim() != '')
562565
if (non_empty_lines.length === 1) {
563566
const match = non_empty_lines[0].match(xml_path_regex)
@@ -655,7 +658,7 @@ const extract_all_code_block_patches = (params: {
655658
if (i < code_blocks.length - 1) {
656659
const next_block = code_blocks[i + 1]
657660
if (next_block.type === 'diff' || next_block.type === 'patch') {
658-
const xml_path_regex = /<file\s+path=["']([^"']+)["']/
661+
const xml_path_regex = /<[\w-]+\s+path=["']([^"']+)["']/
659662
const non_empty_lines = block_lines.filter((l) => l.trim() !== '')
660663
if (non_empty_lines.length === 1) {
661664
const match = non_empty_lines[0].match(xml_path_regex)
@@ -826,6 +829,28 @@ export const extract_diffs = (params: {
826829
is_single_root: params.is_single_root
827830
})
828831
} else {
832+
const xml_file_tag_start_regex = /^\s*<([\w-]+)\s+path=(?:["']([^"']+)["']|([^>\s]+))/
833+
const start_match =
834+
lines.length > 0 ? lines[0].trim().match(xml_file_tag_start_regex) : null
835+
836+
if (start_match) {
837+
const tag_name = start_match[1]
838+
const xml_file_tag_end_regex = new RegExp(`^\\s*<\\/${tag_name}>`)
839+
if (
840+
lines.length >= 2 &&
841+
xml_file_tag_end_regex.test(lines[lines.length - 1].trim()) &&
842+
lines.some((l) => l.trim().startsWith('@@'))
843+
) {
844+
const result = convert_code_block_to_new_file_diff({
845+
lines: lines,
846+
is_single_root: params.is_single_root
847+
})
848+
if (result) {
849+
return [result]
850+
}
851+
}
852+
}
853+
829854
return parse_multiple_raw_patches({
830855
all_lines: lines,
831856
is_single_root: params.is_single_root

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

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ export const parse_multiple_files = (params: {
115115
let is_markdown_container_block = false
116116
let current_language = ''
117117
let current_xml_tag: string | null = null
118+
let current_file_name_was_comment = false
118119

119120
const lines = params.response.split('\n')
120121

@@ -199,6 +200,7 @@ export const parse_multiple_files = (params: {
199200
if (workspace_name) {
200201
current_workspace_name = workspace_name
201202
}
203+
current_file_name_was_comment = false
202204
}
203205
last_seen_file_path_comment = null
204206
current_content = ''
@@ -417,6 +419,25 @@ export const parse_multiple_files = (params: {
417419
const trimmed_line = line.trim()
418420

419421
if (trimmed_line.startsWith('```') && trimmed_line !== '```') {
422+
if (
423+
backtick_nesting_level === 1 &&
424+
current_file_name &&
425+
current_content.trim() === '' &&
426+
!current_file_name_was_comment &&
427+
!xml_file_mode
428+
) {
429+
state = 'TEXT'
430+
last_seen_file_path_comment = current_file_name
431+
current_file_name = ''
432+
current_content = ''
433+
is_first_content_line = false
434+
current_workspace_name = undefined
435+
xml_file_mode = false
436+
in_cdata = false
437+
i--
438+
continue
439+
}
440+
420441
if (
421442
backtick_nesting_level == 1 &&
422443
(current_language == 'markdown' || current_language == 'md')
@@ -474,7 +495,8 @@ export const parse_multiple_files = (params: {
474495
backtick_nesting_level === 1 &&
475496
current_file_name &&
476497
i + 1 < lines.length &&
477-
lines[i + 1].trim() !== ''
498+
lines[i + 1].trim() !== '' &&
499+
!lines[i + 1].trim().startsWith('```')
478500
) {
479501
should_close = false
480502
}
@@ -557,6 +579,7 @@ export const parse_multiple_files = (params: {
557579
xml_file_mode = true
558580
current_xml_tag = xml_info.tagName
559581
is_first_content_line = false
582+
current_file_name_was_comment = true
560583
continue
561584
}
562585
}
@@ -594,6 +617,7 @@ export const parse_multiple_files = (params: {
594617
}
595618

596619
is_first_content_line = false
620+
current_file_name_was_comment = true
597621
continue
598622
}
599623

@@ -650,6 +674,7 @@ export const parse_multiple_files = (params: {
650674
params.is_single_root_folder_workspace
651675
})
652676
current_file_name = relative_path
677+
current_file_name_was_comment = is_comment
653678
if (
654679
backtick_nesting_level > 1 &&
655680
(current_language == 'markdown' || current_language == 'md')
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
--- /dev/null
2+
+++ b/src/index.ts
3+
@@ -0,0 +1,1 @@
4+
+console.log('hello')
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<file path=src/index.ts>
2+
@@ -0,0 +1,1 @@
3+
+console.log('hello')
4+
</file>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
--- /dev/null
2+
+++ b/src/index.ts
3+
@@ -0,0 +1,1 @@
4+
+console.log('hello')
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<file path="src/index.ts">
2+
<![CDATA[
3+
@@ -0,0 +1,1 @@
4+
+console.log('hello')
5+
]]>
6+
</file>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
--- /dev/null
2+
+++ b/src/index.ts
3+
@@ -0,0 +1,1 @@
4+
+console.log('hello')
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<file path="src/index.ts">
2+
@@ -0,0 +1,1 @@
3+
+console.log('hello')
4+
</file>

0 commit comments

Comments
 (0)