Skip to content

Commit 04aa461

Browse files
committed
feat:支持发送文件到聊天窗口
1 parent c252530 commit 04aa461

File tree

6 files changed

+120
-1
lines changed

6 files changed

+120
-1
lines changed

package.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,13 @@
129129
"when": "false",
130130
"category": "CodeReDesign",
131131
"icon": "${add}"
132+
},
133+
{
134+
"command": "codeReDesign.sendToChat",
135+
"title": "CodeReDesign: Send to Chat",
136+
"when": "false",
137+
"category": "CodeReDesign",
138+
"icon": "${file}"
132139
}
133140
],
134141
"viewsContainers": {
@@ -206,6 +213,11 @@
206213
{
207214
"command": "codeReDesign.packupToCvb",
208215
"group": "cvb@1"
216+
},
217+
{
218+
"command": "codeReDesign.sendToChat",
219+
"group": "cvb@1",
220+
"when": "resourceScheme == file"
209221
}
210222
]
211223
},

src/chatPanel.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import path from 'path';
55
import * as fs from "fs";
66
import * as apiTools from './apiTools';
77
import { getOutputChannel } from './extension';
8+
import { processFilePlaceholder } from './metaCommand';
89

910
// Webview 输出通道实现
1011
class WebviewOutputChannel implements vscode.OutputChannel {
@@ -240,6 +241,8 @@ export class ChatPanel {
240241
}
241242

242243
private async handleSendOrEditMessage(message: any, webviewOutputChannel: WebviewOutputChannel): Promise<void> {
244+
message.text = await processFilePlaceholder(message.text);
245+
243246
if (message.command === 'editMessage' && message.index < this.conversation.length) {
244247
this.conversation.splice(message.index + 1);
245248
this.panel.webview.postMessage({ command: 'clearAfterIndex', index: message.index });
@@ -470,6 +473,18 @@ export class ChatPanel {
470473
return conversation;
471474
}
472475

476+
public static insertFilePathToInput(filePath: string): void {
477+
if (ChatPanel.currentPanel) {
478+
const formattedPath = `@file:${filePath}`;
479+
ChatPanel.currentPanel.panel.webview.postMessage({
480+
command: 'insertFilePath',
481+
content: formattedPath
482+
});
483+
} else {
484+
vscode.window.showInformationMessage('No active chat panel. Please open a chat panel first.');
485+
}
486+
}
487+
473488
public dispose(): void {
474489
ChatPanel.currentPanel = undefined;
475490
this.panel.dispose();

src/extension.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -398,7 +398,13 @@ export function activate(context: vscode.ExtensionContext) {
398398
}
399399
});
400400

401-
context.subscriptions.push(generateCvbCommand, redesignCvbCommand, applyCvbCommand, stopOperation, analyzeCodeCommand, startChatCommand, packupToCvbCommand);
401+
let sendFileToChatCommand = vscode.commands.registerCommand('codeReDesign.sendToChat', async (uri: vscode.Uri) => {
402+
if (uri && uri.scheme === 'file') {
403+
ChatPanel.insertFilePathToInput(uri.fsPath);
404+
}
405+
});
406+
407+
context.subscriptions.push(generateCvbCommand, redesignCvbCommand, applyCvbCommand, stopOperation, analyzeCodeCommand, startChatCommand, packupToCvbCommand, sendFileToChatCommand);
402408

403409
setupCvbAsMarkdown(context);
404410

src/metaCommand.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import * as fs from 'fs';
2+
import * as path from 'path';
3+
import {readFileAsUtf8} from './utiliti';
4+
5+
// Process @file:path placeholders in the message text
6+
export async function processFilePlaceholder(messageText: string): Promise<string> {
7+
const filePlaceholderRegex = /^@file:([^\s\n]+)(?:\s|\n|$)/m;
8+
const match = messageText.match(filePlaceholderRegex);
9+
10+
if (!match) {
11+
return messageText;
12+
}
13+
14+
const filePath = match[1];
15+
try {
16+
// Read file content
17+
const content = await readFileAsUtf8(filePath);
18+
const fileName = path.basename(filePath);
19+
20+
// Replace placeholder with formatted content
21+
return messageText.replace(
22+
filePlaceholderRegex,
23+
`@file:${filePath}\n---------------\n${content}\n---------------\n`
24+
);
25+
} catch (error) {
26+
return messageText.replace(
27+
filePlaceholderRegex,
28+
`@file:${filePath}\n---------------\n**Error reading file**: ${(error as Error).message}\n---------------\n`
29+
);
30+
}
31+
}

src/resources/chatPanelScript.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -349,6 +349,11 @@ function setupMessageHandlers() {
349349
}
350350
});
351351
break;
352+
case 'insertFilePath':
353+
// Insert file path at the beginning of the input
354+
input.value = data.content + ' ' + input.value;
355+
input.focus();
356+
break;
352357
}
353358
});
354359

src/utiliti.ts

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,56 @@
11
import { ExtensionContext, ExtensionMode } from 'vscode';
2+
import * as fs from 'fs/promises';
3+
import * as iconv from 'iconv-lite';
24

35
let isDebugMode: boolean;
46
export function activate(context: ExtensionContext) {
57
isDebugMode = context.extensionMode === ExtensionMode.Development;
8+
}
9+
10+
/**
11+
* 读取文件并根据其编码(GBK、UTF-8 或带 BOM 的 UTF-8)转换为 UTF-8 字符串
12+
* @param filePath 文件路径
13+
* @returns 转换后的 UTF-8 字符串
14+
* @throws 如果无法读取文件或解码失败,抛出错误
15+
*/
16+
export async function readFileAsUtf8(filePath: string): Promise<string> {
17+
try {
18+
// 读取文件的原始 Buffer
19+
const buffer = await fs.readFile(filePath);
20+
21+
// 检测是否为带 BOM 的 UTF-8
22+
const isUtf8WithBom =
23+
buffer.length >= 3 &&
24+
buffer[0] === 0xEF &&
25+
buffer[1] === 0xBB &&
26+
buffer[2] === 0xBF;
27+
28+
if (isUtf8WithBom) {
29+
// 移除 BOM 并作为 UTF-8 解码
30+
return buffer.slice(3).toString('utf8');
31+
}
32+
33+
// 尝试作为 UTF-8 解码
34+
try {
35+
// 先验证是否是有效的 UTF-8
36+
const utf8Text = buffer.toString('utf8');
37+
// 简单的 UTF-8 有效性检查:重新编码后比较
38+
if (Buffer.from(utf8Text, 'utf8').equals(buffer)) {
39+
return utf8Text;
40+
}
41+
} catch (utf8Error) {
42+
// 如果 UTF-8 解码失败,继续尝试 GBK
43+
}
44+
45+
// 尝试作为 GBK 解码
46+
try {
47+
const gbkText = iconv.decode(buffer, 'gbk');
48+
return gbkText;
49+
} catch (gbkError) {
50+
throw new Error(`Failed to decode file as GBK: ${(gbkError as Error).message}`);
51+
}
52+
53+
} catch (error) {
54+
throw new Error(`Failed to read file: ${(error as Error).message}`);
55+
}
656
}

0 commit comments

Comments
 (0)