From 50c375bd909ed9e85645b295fd6bc181b7cd0c59 Mon Sep 17 00:00:00 2001 From: "bobo.yang" Date: Wed, 3 May 2023 21:19:02 +0800 Subject: [PATCH 1/8] update devchat options --- src/devchat.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/devchat.ts b/src/devchat.ts index e739b41..a249a19 100644 --- a/src/devchat.ts +++ b/src/devchat.ts @@ -74,10 +74,10 @@ class DevChat { args.push("-r", options.reference.join(",")); } if (options.header) { - args.push("--header", options.header.join(",")); + args.push("-i", options.header.join(",")); } if (options.context) { - args.push("--context", options.context.join(",")); + args.push("-c", options.context.join(",")); } args.push(content) From 6ba0e00294e1afa4cb64a427cf6a4d9e3a10dd11 Mon Sep 17 00:00:00 2001 From: "bobo.yang" Date: Wed, 3 May 2023 23:21:46 +0800 Subject: [PATCH 2/8] add commit message command --- src/commandManager.ts | 47 ++++++++++++------- src/commitMessageCommand.ts | 58 ++++++++++++++++++++++++ src/commitMessageCommandInstructions.txt | 9 ++++ src/commonInstructions.txt | 1 + src/exampleCommand1.ts | 2 +- src/exampleCommand2.ts | 2 +- src/loadCommands.ts | 2 + src/messageHandler.ts | 43 ++++++++++++++++-- 8 files changed, 140 insertions(+), 24 deletions(-) create mode 100644 src/commitMessageCommand.ts create mode 100644 src/commitMessageCommandInstructions.txt create mode 100644 src/commonInstructions.txt diff --git a/src/commandManager.ts b/src/commandManager.ts index 66e66a5..5fbeb6c 100644 --- a/src/commandManager.ts +++ b/src/commandManager.ts @@ -2,7 +2,7 @@ export interface Command { name: string; pattern: string; description: string; - handler: (userInput: string) => string; + handler: (userInput: string) => Promise; } class CommandManager { @@ -27,22 +27,35 @@ export interface Command { return this.commands; } - processText(text: string): string { - let result = text; - - this.commands.forEach((commandObj) => { - const commandPattern = new RegExp( - `\\/(${commandObj.pattern.replace("{{prompt}}", "(.+?)")})`, - "g" - ); - - result = result.replace(commandPattern, (_, matchedUserInput) => { - return commandObj.handler(matchedUserInput); - }); - }); - - return result; - } + async processText(text: string): Promise { + // 定义一个异步函数来处理单个命令 + const processCommand = async (commandObj: Command, userInput: string) => { + const commandPattern = new RegExp( + `\\/(${commandObj.pattern.replace('{{prompt}}', '(.+?)')})`, + 'g' + ); + const matches = Array.from(text.matchAll(commandPattern)); + const replacements = await Promise.all( + matches.map(async (match) => { + const matchedUserInput = match[1]; + return await commandObj.handler(matchedUserInput); + }) + ); + let result = text; + for (let i = 0; i < matches.length; i++) { + result = result.replace(matches[i][0], replacements[i]); + } + return result; + }; + + // 处理所有命令 + let result = text; + for (const commandObj of this.commands) { + result = await processCommand(commandObj, result); + } + + return result; + } } export default CommandManager; diff --git a/src/commitMessageCommand.ts b/src/commitMessageCommand.ts new file mode 100644 index 0000000..6e92d70 --- /dev/null +++ b/src/commitMessageCommand.ts @@ -0,0 +1,58 @@ +import {Command} from './commandManager'; + +import { exec } from 'child_process'; +import * as vscode from 'vscode'; + +import * as fs from 'fs'; +import * as os from 'os'; +import * as path from 'path'; +import { promisify } from 'util'; + +const mkdirAsync = promisify(fs.mkdir); +const execAsync = promisify(exec); +const writeFileAsync = promisify(fs.writeFile); + + +async function createTempDirectory(tempDir: string): Promise { + try { + await mkdirAsync(tempDir, {recursive: true}); + } catch (err) { + console.error(`Error creating temporary directory: ${err}`); + } +} + +async function writeDiffFile(diff_file: string) { + try { + const workspaceDir = vscode.workspace.workspaceFolders?.[0].uri.fsPath; + + const { stdout, stderr } = await execAsync('git diff --cached', { + cwd: workspaceDir + }); + if (stderr) { + console.error(`Error output from git diff --cached: ${stderr}`); + return; + } + // 将结果写入到临时文件中 + const tempFilePath = diff_file; + await writeFileAsync(tempFilePath, stdout); + } catch (err) { + console.error(`Error executing git diff --cached: ${err}`); + } +} + +export const commitMessageCommand: Command = { + name: 'commitMessageCommand', + pattern: 'git: commit message', + description: 'commit message for changes', + handler: async (userInput: string) => { + const systemTempDir = os.tmpdir(); + const tempDir = path.join(systemTempDir, 'devchat/context'); + + // 创建临时目录 + await createTempDirectory(tempDir); + const diff_file = path.join(tempDir, 'diff_output.txt'); + await writeDiffFile(diff_file); + + return `[instruction|./commonInstructions.txt] [instruction|./commitMessageCommandInstructions.txt] [context|${diff_file}] Write a commit message`; + }, +}; diff --git a/src/commitMessageCommandInstructions.txt b/src/commitMessageCommandInstructions.txt new file mode 100644 index 0000000..13b7626 --- /dev/null +++ b/src/commitMessageCommandInstructions.txt @@ -0,0 +1,9 @@ +As a software developer assistant, your task is to provide clear and concise responses and write commit messages based on given code, requirements, or conversations. Follow these guidelines: + +1. A commit message should include a title and multiple body lines. +2. Adhere to best practices, such as keeping titles under 50 characters and limiting body lines to under 72 characters. +3. Enclose messages in code blocks using triple backticks (```). +4. Utilize the , if provided, to create the summary. +5. Utilize the previous messages, if provided in the end of this prompt, to create the summary. Note that not all previous messages are necessarily relevant. + +If you need more information, feel free to ask. \ No newline at end of file diff --git a/src/commonInstructions.txt b/src/commonInstructions.txt new file mode 100644 index 0000000..ce7e220 --- /dev/null +++ b/src/commonInstructions.txt @@ -0,0 +1 @@ +You are a software developer assistant \ No newline at end of file diff --git a/src/exampleCommand1.ts b/src/exampleCommand1.ts index eff4a12..df00100 100644 --- a/src/exampleCommand1.ts +++ b/src/exampleCommand1.ts @@ -4,7 +4,7 @@ export const exampleCommand1: Command = { name: 'exampleCommand1', pattern: 'example: command1 {{prompt}}', description: '这是一个示例命令1', - handler: (userInput: string) => { + handler: async (userInput: string) => { return `示例命令1处理了以下文本:${userInput}`; }, }; diff --git a/src/exampleCommand2.ts b/src/exampleCommand2.ts index 79ceebd..2da3546 100644 --- a/src/exampleCommand2.ts +++ b/src/exampleCommand2.ts @@ -4,7 +4,7 @@ export const exampleCommand2: Command = { name: 'exampleCommand2', pattern: 'example: command2 {{prompt}}', description: '这是一个示例命令2', - handler: (userInput: string) => { + handler: async (userInput: string) => { return `示例命令2处理了以下文本:${userInput}`; }, }; diff --git a/src/loadCommands.ts b/src/loadCommands.ts index f01df39..c5724dd 100644 --- a/src/loadCommands.ts +++ b/src/loadCommands.ts @@ -1,9 +1,11 @@ import CommandManager from './commandManager'; import { exampleCommand1 } from './exampleCommand1'; import { exampleCommand2 } from './exampleCommand2'; +import { commitMessageCommand } from './commitMessageCommand'; const commandManager = CommandManager.getInstance(); // 注册命令 commandManager.registerCommand(exampleCommand1); commandManager.registerCommand(exampleCommand2); +commandManager.registerCommand(commitMessageCommand); \ No newline at end of file diff --git a/src/messageHandler.ts b/src/messageHandler.ts index b16ad70..d54445c 100644 --- a/src/messageHandler.ts +++ b/src/messageHandler.ts @@ -41,19 +41,43 @@ export function askAI(panel: vscode.WebviewPanel, codeBlock: string, question: s } // Add this function to messageHandler.ts -function parseMessageForContext(message: string): { context: string[]; text: string } { +function parseMessage(message: string): { context: string[]; instruction: string[]; reference: string[]; text: string } { const contextRegex = /\[context\|(.*?)\]/g; + const instructionRegex = /\[instruction\|(.*?)\]/g; + const referenceRegex = /\[reference\|(.*?)\]/g; + const contextPaths = []; + const instructionPaths = []; + const referencePaths = []; + let match; + // 提取 context while ((match = contextRegex.exec(message)) !== null) { contextPaths.push(match[1]); } - const text = message.replace(contextRegex, '').trim(); - return { context: contextPaths, text }; + // 提取 instruction + while ((match = instructionRegex.exec(message)) !== null) { + instructionPaths.push(match[1]); + } + + // 提取 reference + while ((match = referenceRegex.exec(message)) !== null) { + referencePaths.push(match[1]); + } + + // 移除标签,保留纯文本 + const text = message + .replace(contextRegex, '') + .replace(instructionRegex, '') + .replace(referenceRegex, '') + .trim(); + + return { context: contextPaths, instruction: instructionPaths, reference: referencePaths, text }; } + async function handleMessage( message: any, panel: vscode.WebviewPanel @@ -63,12 +87,21 @@ async function handleMessage( switch (message.command) { case 'sendMessage': - const parsedMessage = parseMessageForContext(message.text); + const newText2 = await CommandManager.getInstance().processText(message.text); + panel.webview.postMessage({ command: 'convertCommand', result: newText2 }); + + const parsedMessage = parseMessage(newText2); const chatOptions: any = lastPromptHash ? { parent: lastPromptHash } : {}; if (parsedMessage.context.length > 0) { chatOptions.context = parsedMessage.context; } + if (parsedMessage.instruction.length > 0) { + chatOptions.instruction = parsedMessage.instruction; + } + if (parsedMessage.reference.length > 0) { + chatOptions.reference = parsedMessage.reference; + } let partialData = ""; const onData = (partialResponse: string) => { @@ -112,7 +145,7 @@ async function handleMessage( panel.webview.postMessage({ command: 'regCommandList', result: commandList }); return; case 'convertCommand': - const newText = CommandManager.getInstance().processText(message.text); + const newText = await CommandManager.getInstance().processText(message.text); panel.webview.postMessage({ command: 'convertCommand', result: newText }); return; } From ef8f2a461ed825d2c38453930f5d810228255195 Mon Sep 17 00:00:00 2001 From: "bobo.yang" Date: Thu, 4 May 2023 07:27:26 +0800 Subject: [PATCH 3/8] support commitmsg code block --- .../commitMessageCommandInstructions.txt | 1 + {src => instructions}/commonInstructions.txt | 0 src/commitMessageCommand.ts | 6 +++++- src/extension.ts | 7 ++++--- src/extensionContext.ts | 15 +++++++++++++++ src/messageHandler.ts | 2 +- 6 files changed, 26 insertions(+), 5 deletions(-) rename {src => instructions}/commitMessageCommandInstructions.txt (89%) rename {src => instructions}/commonInstructions.txt (100%) create mode 100644 src/extensionContext.ts diff --git a/src/commitMessageCommandInstructions.txt b/instructions/commitMessageCommandInstructions.txt similarity index 89% rename from src/commitMessageCommandInstructions.txt rename to instructions/commitMessageCommandInstructions.txt index 13b7626..a933188 100644 --- a/src/commitMessageCommandInstructions.txt +++ b/instructions/commitMessageCommandInstructions.txt @@ -5,5 +5,6 @@ As a software developer assistant, your task is to provide clear and concise res 3. Enclose messages in code blocks using triple backticks (```). 4. Utilize the , if provided, to create the summary. 5. Utilize the previous messages, if provided in the end of this prompt, to create the summary. Note that not all previous messages are necessarily relevant. +6. Please output commit message in a markdown code block, flag as commitmsg type. If you need more information, feel free to ask. \ No newline at end of file diff --git a/src/commonInstructions.txt b/instructions/commonInstructions.txt similarity index 100% rename from src/commonInstructions.txt rename to instructions/commonInstructions.txt diff --git a/src/commitMessageCommand.ts b/src/commitMessageCommand.ts index 6e92d70..92942d2 100644 --- a/src/commitMessageCommand.ts +++ b/src/commitMessageCommand.ts @@ -7,6 +7,7 @@ import * as fs from 'fs'; import * as os from 'os'; import * as path from 'path'; import { promisify } from 'util'; +import ExtensionContextHolder from './extensionContext'; const mkdirAsync = promisify(fs.mkdir); const execAsync = promisify(exec); @@ -53,6 +54,9 @@ export const commitMessageCommand: Command = { const diff_file = path.join(tempDir, 'diff_output.txt'); await writeDiffFile(diff_file); - return `[instruction|./commonInstructions.txt] [instruction|./commitMessageCommandInstructions.txt] [context|${diff_file}] Write a commit message`; + const commonInstructions = vscode.Uri.joinPath(ExtensionContextHolder.context?.extensionUri as vscode.Uri, 'instructions/commonInstructions.txt'); + const commitMsgInstructions = vscode.Uri.joinPath(ExtensionContextHolder.context?.extensionUri as vscode.Uri, 'instructions/commitMessageCommandInstructions.txt'); + + return `[instruction|${commonInstructions.path}] [instruction|${commitMsgInstructions.path}] [context|${diff_file}] Write a commit message`; }, }; diff --git a/src/extension.ts b/src/extension.ts index 4a2732b..9c7b205 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -1,11 +1,12 @@ -const vscode = require('vscode'); +import * as vscode from 'vscode'; const ChatPanel = require('./chatPanel').default; const sendFileSelectMessage = require('./messageHandler').sendFileSelectMessage; const sendCodeSelectMessage = require('./messageHandler').sendCodeSelectMessage; const askAI = require('./messageHandler').askAI; +import ExtensionContextHolder from './extensionContext'; - -function activate(context: { extensionUri: any; subscriptions: any[]; }) { +function activate(context: vscode.ExtensionContext) { + ExtensionContextHolder.context = context; let disposable = vscode.commands.registerCommand('devchat.openChatPanel', () => { if (vscode.workspace.workspaceFolders) { ChatPanel.createOrShow(context.extensionUri); diff --git a/src/extensionContext.ts b/src/extensionContext.ts new file mode 100644 index 0000000..d10fb0d --- /dev/null +++ b/src/extensionContext.ts @@ -0,0 +1,15 @@ +import * as vscode from 'vscode'; + +class ExtensionContextHolder { + private static _context: vscode.ExtensionContext | undefined; + + static set context(context: vscode.ExtensionContext | undefined) { + this._context = context; + } + + static get context(): vscode.ExtensionContext | undefined { + return this._context; + } +} + +export default ExtensionContextHolder; diff --git a/src/messageHandler.ts b/src/messageHandler.ts index d54445c..32100da 100644 --- a/src/messageHandler.ts +++ b/src/messageHandler.ts @@ -97,7 +97,7 @@ async function handleMessage( chatOptions.context = parsedMessage.context; } if (parsedMessage.instruction.length > 0) { - chatOptions.instruction = parsedMessage.instruction; + chatOptions.header = parsedMessage.instruction; } if (parsedMessage.reference.length > 0) { chatOptions.reference = parsedMessage.reference; From 8366a8eea65b665ba7afb772b5ece7458d452c28 Mon Sep 17 00:00:00 2001 From: "bobo.yang" Date: Thu, 4 May 2023 08:20:19 +0800 Subject: [PATCH 4/8] add doCommit message --- src/dtm.ts | 51 +++++++++++++++++++++++++++++-------------- src/extension.ts | 1 - src/messageHandler.ts | 8 +++++++ 3 files changed, 43 insertions(+), 17 deletions(-) diff --git a/src/dtm.ts b/src/dtm.ts index d4ff0af..f0a3ef8 100644 --- a/src/dtm.ts +++ b/src/dtm.ts @@ -1,11 +1,6 @@ -// dtm.ts - -import { exec } from "child_process"; -import { promisify } from "util"; +import { spawn } from "child_process"; import * as vscode from 'vscode'; -const execAsync = promisify(exec); - interface DtmResponse { status: number; message: string; @@ -19,21 +14,45 @@ class DtmWrapper { this.workspaceDir = vscode.workspace.workspaceFolders?.[0].uri.fsPath || '.'; } - async scaffold(directoryTree: string): Promise { - const { stdout } = await execAsync(`dtm scaffold "${directoryTree}" -o json`, { - cwd: this.workspaceDir, + private async runCommand(command: string, args: string[]): Promise { + return new Promise((resolve, reject) => { + const child = spawn(command, args, { cwd: this.workspaceDir }); + let stdout = ''; + let stderr = ''; + + child.stdout.on('data', (data) => { + stdout += data; + }); + + child.stderr.on('data', (data) => { + stderr += data; + }); + + child.on('close', (code) => { + if (code === 0) { + resolve(JSON.parse(stdout.trim())); + } else { + reject(JSON.parse(stdout.trim())); + } + }); }); - return JSON.parse(stdout.trim()); + } + + async scaffold(directoryTree: string): Promise { + return await this.runCommand('dtm', ['scaffold', directoryTree, '-o', 'json']); } async patch(patchFilePath: string): Promise { + return await this.runCommand('dtm', ['patch', patchFilePath, '-o', 'json']); + } + + async commit(commitMsg: string): Promise { try { - const { stdout } = await execAsync(`dtm patch ${patchFilePath} -o json`, { - cwd: this.workspaceDir, - }); - return JSON.parse(stdout.trim()); - } catch (e) { - return JSON.parse((e as Error & { stdout: string }).stdout.trim()); + return await this.runCommand('dtm', ['commit', '-m', commitMsg, '-o', 'json']); + } catch (error) { + // 处理异常 + console.error('Error in commit:', error); + return {'status': -1, 'message': 'exception error', 'log': 'exception error'}; } } } diff --git a/src/extension.ts b/src/extension.ts index 9c7b205..a7cacb5 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -2,7 +2,6 @@ import * as vscode from 'vscode'; const ChatPanel = require('./chatPanel').default; const sendFileSelectMessage = require('./messageHandler').sendFileSelectMessage; const sendCodeSelectMessage = require('./messageHandler').sendCodeSelectMessage; -const askAI = require('./messageHandler').askAI; import ExtensionContextHolder from './extensionContext'; function activate(context: vscode.ExtensionContext) { diff --git a/src/messageHandler.ts b/src/messageHandler.ts index 32100da..4854ed7 100644 --- a/src/messageHandler.ts +++ b/src/messageHandler.ts @@ -148,6 +148,14 @@ async function handleMessage( const newText = await CommandManager.getInstance().processText(message.text); panel.webview.postMessage({ command: 'convertCommand', result: newText }); return; + case 'doCommit': + const commitResult = await dtmWrapper.commit(message.content); + if (commitResult.status === 0) { + vscode.window.showInformationMessage('Commit successfully.'); + } else { + vscode.window.showErrorMessage(`Error commit fail: ${commitResult.message} ${commitResult.log}`); + } + return; } } From f553719ba254659803ffe15258b99bbeca83cfe7 Mon Sep 17 00:00:00 2001 From: "bobo.yang" Date: Thu, 4 May 2023 11:50:45 +0800 Subject: [PATCH 5/8] support user definded instructions --- instructions/commonInstructions.txt | 1 - instructions/instCode.txt | 22 ++++++++++++ ...Instructions.txt => instCommitMessage.txt} | 0 instructions/instCommon.txt | 1 + instructions/instLangPython.txt | 1 + package-lock.json | 14 ++++++++ package.json | 1 + src/commitMessageCommand.ts | 5 +-- src/extension.ts | 35 +++++++++++++++++++ src/messageHandler.ts | 28 +++++++++++++-- 10 files changed, 100 insertions(+), 8 deletions(-) delete mode 100644 instructions/commonInstructions.txt create mode 100644 instructions/instCode.txt rename instructions/{commitMessageCommandInstructions.txt => instCommitMessage.txt} (100%) create mode 100644 instructions/instCommon.txt create mode 100644 instructions/instLangPython.txt diff --git a/instructions/commonInstructions.txt b/instructions/commonInstructions.txt deleted file mode 100644 index ce7e220..0000000 --- a/instructions/commonInstructions.txt +++ /dev/null @@ -1 +0,0 @@ -You are a software developer assistant \ No newline at end of file diff --git a/instructions/instCode.txt b/instructions/instCode.txt new file mode 100644 index 0000000..b861cf3 --- /dev/null +++ b/instructions/instCode.txt @@ -0,0 +1,22 @@ +As a software developer assistant, your tasks are to: + +- Provide a clear and concise response to address the user's . +- Write code and give advice based on given code or information in the if provided. +- Follow language-specific best practices and coding standards. + +When responding: + +1. Summarize and describe the requirements or provided information in your own words. +2. The summary and description should better be written in bullet points (excluding code). +3. If modifying given code, output the changes and avoid unnecessary unchanged code. +4. Enclose code or changes within blocks using triple backticks (```), and include the programming language and the file path, if available. For example: +``` +```python path=./path/to/file.py +print("Hello, World!") +``` +``` +If no file paths or folder structure are provided and you are unsure about the file path of the code, you may omit the file path. +5. Use separate code blocks for different files. +6. Utilize the previous messages, if provided in the end of this prompt, to create your response. Note that not all previous messages are necessarily relevant. +7. When providing a suggestion or instruction, begin by explaining the reason behind it. +8. If you need more information, ask for it. \ No newline at end of file diff --git a/instructions/commitMessageCommandInstructions.txt b/instructions/instCommitMessage.txt similarity index 100% rename from instructions/commitMessageCommandInstructions.txt rename to instructions/instCommitMessage.txt diff --git a/instructions/instCommon.txt b/instructions/instCommon.txt new file mode 100644 index 0000000..1a1b2df --- /dev/null +++ b/instructions/instCommon.txt @@ -0,0 +1 @@ +You are a software developer assistant. \ No newline at end of file diff --git a/instructions/instLangPython.txt b/instructions/instLangPython.txt new file mode 100644 index 0000000..df9d0af --- /dev/null +++ b/instructions/instLangPython.txt @@ -0,0 +1 @@ +When writing Python code, include type hints where appropriate and maintain compliance with PEP-8 guidelines, such as providing docstrings for modules, classes, and functions. \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index d607a9a..a0cbbc6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "dependencies": { "axios": "^1.3.6", "dotenv": "^16.0.3", + "ncp": "^2.0.0", "node-fetch": "^3.3.1", "nonce": "^1.0.4", "openai": "^3.2.1", @@ -4898,6 +4899,14 @@ "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", "dev": true }, + "node_modules/ncp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz", + "integrity": "sha512-zIdGUrPRFTUELUvr3Gmc7KZ2Sw/h1PiVM0Af/oHB6zgnV1ikqSfRk+TOufi79aHYCW3NiOXmr1BP5nWbzojLaA==", + "bin": { + "ncp": "bin/ncp" + } + }, "node_modules/neo-async": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", @@ -10133,6 +10142,11 @@ "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", "dev": true }, + "ncp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz", + "integrity": "sha512-zIdGUrPRFTUELUvr3Gmc7KZ2Sw/h1PiVM0Af/oHB6zgnV1ikqSfRk+TOufi79aHYCW3NiOXmr1BP5nWbzojLaA==" + }, "neo-async": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", diff --git a/package.json b/package.json index 661b071..1ba0f12 100644 --- a/package.json +++ b/package.json @@ -96,6 +96,7 @@ "dependencies": { "axios": "^1.3.6", "dotenv": "^16.0.3", + "ncp": "^2.0.0", "node-fetch": "^3.3.1", "nonce": "^1.0.4", "openai": "^3.2.1", diff --git a/src/commitMessageCommand.ts b/src/commitMessageCommand.ts index 92942d2..d169131 100644 --- a/src/commitMessageCommand.ts +++ b/src/commitMessageCommand.ts @@ -54,9 +54,6 @@ export const commitMessageCommand: Command = { const diff_file = path.join(tempDir, 'diff_output.txt'); await writeDiffFile(diff_file); - const commonInstructions = vscode.Uri.joinPath(ExtensionContextHolder.context?.extensionUri as vscode.Uri, 'instructions/commonInstructions.txt'); - const commitMsgInstructions = vscode.Uri.joinPath(ExtensionContextHolder.context?.extensionUri as vscode.Uri, 'instructions/commitMessageCommandInstructions.txt'); - - return `[instruction|${commonInstructions.path}] [instruction|${commitMsgInstructions.path}] [context|${diff_file}] Write a commit message`; + return `[context|${diff_file}] Write a commit message`; }, }; diff --git a/src/extension.ts b/src/extension.ts index a7cacb5..0034aac 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -1,11 +1,46 @@ import * as vscode from 'vscode'; +import * as fs from 'fs'; +import * as path from 'path'; +import * as ncp from 'ncp'; // 需要安装 ncp 模块,用于复制目录 + const ChatPanel = require('./chatPanel').default; const sendFileSelectMessage = require('./messageHandler').sendFileSelectMessage; const sendCodeSelectMessage = require('./messageHandler').sendCodeSelectMessage; import ExtensionContextHolder from './extensionContext'; +function createChatDirectoryAndCopyInstructionsSync(extensionUri: vscode.Uri) { + const workspaceFolders = vscode.workspace.workspaceFolders; + if (!workspaceFolders) { + return; + } + + const workspaceRoot = workspaceFolders[0].uri.fsPath; + const chatDirPath = path.join(workspaceRoot, '.chat'); + const instructionsSrcPath = path.join(extensionUri.fsPath, 'instructions'); + + try { + // 检查 .chat 目录是否存在,如果不存在,则创建它 + if (!fs.existsSync(chatDirPath)) { + fs.mkdirSync(chatDirPath); + } + + // 将 instructions 目录复制到 .chat 目录中 + ncp.ncp(instructionsSrcPath, path.join(chatDirPath, 'instructions'), (err) => { + if (err) { + console.error('Error copying instructions:', err); + } + }); + } catch (error) { + console.error('Error creating .chat directory and copying instructions:', error); + } +} + function activate(context: vscode.ExtensionContext) { ExtensionContextHolder.context = context; + + // 创建 .chat 目录并复制 instructions + createChatDirectoryAndCopyInstructionsSync(context.extensionUri); + let disposable = vscode.commands.registerCommand('devchat.openChatPanel', () => { if (vscode.workspace.workspaceFolders) { ChatPanel.createOrShow(context.extensionUri); diff --git a/src/messageHandler.ts b/src/messageHandler.ts index 4854ed7..6b0a371 100644 --- a/src/messageHandler.ts +++ b/src/messageHandler.ts @@ -77,6 +77,28 @@ function parseMessage(message: string): { context: string[]; instruction: string return { context: contextPaths, instruction: instructionPaths, reference: referencePaths, text }; } +function getInstructionFiles(): string[] { + const instructionFiles: string[] = []; + const workspaceDir = vscode.workspace.workspaceFolders?.[0].uri.fsPath; + if (workspaceDir) { + const chatInstructionsPath = path.join(workspaceDir, '.chat', 'instructions'); + try { + // 读取 chatInstructionsPath 目录下的所有文件和目录 + const files = fs.readdirSync(chatInstructionsPath); + // 过滤出文件,忽略目录 + for (const file of files) { + const filePath = path.join(chatInstructionsPath, file); + const fileStats = fs.statSync(filePath); + if (fileStats.isFile()) { + instructionFiles.push(filePath); + } + } + } catch (error) { + console.error('Error reading instruction files:', error); + } + } + return instructionFiles; +} async function handleMessage( message: any, @@ -96,9 +118,9 @@ async function handleMessage( if (parsedMessage.context.length > 0) { chatOptions.context = parsedMessage.context; } - if (parsedMessage.instruction.length > 0) { - chatOptions.header = parsedMessage.instruction; - } + + chatOptions.header = getInstructionFiles(); + if (parsedMessage.reference.length > 0) { chatOptions.reference = parsedMessage.reference; } From b193dda852a5c73afe59c1382ba9d1eda658ff03 Mon Sep 17 00:00:00 2001 From: "bobo.yang" Date: Thu, 4 May 2023 12:00:41 +0800 Subject: [PATCH 6/8] don't copy instructions again --- src/extension.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/extension.ts b/src/extension.ts index 0034aac..d3c5e4f 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -22,6 +22,8 @@ function createChatDirectoryAndCopyInstructionsSync(extensionUri: vscode.Uri) { // 检查 .chat 目录是否存在,如果不存在,则创建它 if (!fs.existsSync(chatDirPath)) { fs.mkdirSync(chatDirPath); + } else { + return; } // 将 instructions 目录复制到 .chat 目录中 From ff840476cc890420a6de2675eef0f440ac384337 Mon Sep 17 00:00:00 2001 From: "bobo.yang" Date: Thu, 4 May 2023 13:50:06 +0800 Subject: [PATCH 7/8] add context by menu --- media/chatPanel.css | 34 ++++++++++++++++ media/chatPanel.html | 2 +- media/inputContainer.js | 88 +++++++++++++++++++++++++++++++++++++++++ src/contextManager.ts | 41 +++++++++++++++++++ src/exampleContext.ts | 9 +++++ src/loadContexts.ts | 7 ++++ src/messageHandler.ts | 10 +++++ 7 files changed, 190 insertions(+), 1 deletion(-) create mode 100644 src/contextManager.ts create mode 100644 src/exampleContext.ts create mode 100644 src/loadContexts.ts diff --git a/media/chatPanel.css b/media/chatPanel.css index c9c4ea1..dbf92fd 100644 --- a/media/chatPanel.css +++ b/media/chatPanel.css @@ -179,3 +179,37 @@ pre { #send-button:hover { background-color: var(--vscode-button-hover-bg); } + +.popup-context-menu { + display: none; + position: absolute; + background-color: #f9f9f9; + box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2); + padding: 12px 16px; + z-index: 1; +} +.popup-context-menu a { + color: black; + text-decoration: none; + display: block; +} +.popup-context-menu a:hover { + background-color: #f1f1f1; +} + +.popup-command-menu { + display: none; + position: absolute; + background-color: #f9f9f9; + box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2); + padding: 12px 16px; + z-index: 1; +} +.popup-command-menu a { + color: black; + text-decoration: none; + display: block; +} +.popup-command-menu a:hover { + background-color: #f1f1f1; +} \ No newline at end of file diff --git a/media/chatPanel.html b/media/chatPanel.html index 99658e2..1fbc4f2 100644 --- a/media/chatPanel.html +++ b/media/chatPanel.html @@ -22,7 +22,7 @@
- +
diff --git a/media/inputContainer.js b/media/inputContainer.js index 905f4fd..16c8b28 100644 --- a/media/inputContainer.js +++ b/media/inputContainer.js @@ -3,6 +3,13 @@ function initInputContainer() { const messageInput = document.getElementById('message-input'); const sendButton = document.getElementById('send-button'); + const addContextButton = document.getElementById('add-context-button'); + const addCommandButton = document.getElementById('add-command-button'); + const popupContextMenu = document.getElementById('popupContextMenu'); + const popupCommandMenu = document.getElementById('popupCommandMenu'); + + let contextList = []; + let commandList = []; messageInput.addEventListener('keypress', function (e) { if (e.key === 'Enter' && !e.shiftKey) { @@ -31,6 +38,20 @@ function initInputContainer() { } }); + addContextButton.addEventListener('click', (event) => { + popupContextMenu.style.display = popupContextMenu.style.display === 'block' ? 'none' : 'block'; + // 设置弹出菜单的位置 + popupContextMenu.style.left = event.pageX + 'px'; + popupContextMenu.style.top = event.pageY + 'px'; + }); + + addCommandButton.addEventListener('click', (event) => { + popupCommandMenu.style.display = popupCommandMenu.style.display === 'block' ? 'none' : 'block'; + // 设置弹出菜单的位置 + popupCommandMenu.style.left = event.pageX + 'px'; + popupCommandMenu.style.top = event.pageY + 'px'; + }); + messageUtil.registerHandler('file_select', (message) => { addFileToMessageInput(message.filePath); }); @@ -38,6 +59,73 @@ function initInputContainer() { messageUtil.registerHandler('code_select', (message) => { addCodeToMessageInput(message.codeBlock); }); + + messageUtil.registerHandler('appendContext', (message) => { + addCodeToMessageInput(message.context); + }); + + messageUtil.registerHandler('regContextList', (message) => { + contextList = message.result; + + const menuItems = []; + for (let i = 0; i < contextList.length; i++) { + menuItems.push({ + text: contextList[i].name, + href: contextList[i].name + }); + } + + menuItems.forEach(item => { + const menuItem = document.createElement('a'); + menuItem.textContent = 'add ' + item.text; + menuItem.href = item.text; + + popupContextMenu.appendChild(menuItem); + + // 为每个菜单项添加点击事件监听器 + menuItem.addEventListener('click', (event) => { + // 阻止标签的默认行为(例如导航到链接) + event.preventDefault(); + // 在此处定义点击处理逻辑 + messageUtil.sendMessage({command: 'addContext', selected: item.href}) + // 隐藏弹出菜单 + popupContextMenu.style.display = 'none'; + }); + }); + }); + + messageUtil.registerHandler('regCommandList', (message) => { + commandList = message.result; + + const menuItems = []; + for (let i = 0; i < commandList.length; i++) { + menuItems.push({ + text: commandList[i].pattern, + href: commandList[i].pattern + }); + } + + menuItems.forEach(item => { + const menuItem = document.createElement('a'); + menuItem.textContent = item.text; + menuItem.href = item.href; + + popupCommandMenu.appendChild(menuItem); + + // 为每个菜单项添加点击事件监听器 + menuItem.addEventListener('click', (event) => { + // 阻止标签的默认行为(例如导航到链接) + event.preventDefault(); + // 在此处定义点击处理逻辑 + addCodeToMessageInput("/" + item.href); + // 隐藏弹出菜单 + popupCommandMenu.style.display = 'none'; + }); + }); + }); + + messageUtil.sendMessage({command: 'regContextList'}); + messageUtil.sendMessage({command: 'regCommandList'}); } function addFileToMessageInput(filePath) { diff --git a/src/contextManager.ts b/src/contextManager.ts new file mode 100644 index 0000000..2421396 --- /dev/null +++ b/src/contextManager.ts @@ -0,0 +1,41 @@ +export interface ChatContext { + name: string; + description: string; + handler: () => Promise; + } + + class ChatContextManager { + private static instance: ChatContextManager; + private contexts: ChatContext[] = []; + + private constructor() {} + + public static getInstance(): ChatContextManager { + if (!ChatContextManager.instance) { + ChatContextManager.instance = new ChatContextManager(); + } + + return ChatContextManager.instance; + } + + registerContext(context: ChatContext): void { + this.contexts.push(context); + } + + getContextList(): ChatContext[] { + return this.contexts; + } + + async processText(command: string): Promise { + // 处理所有命令 + for (const contextObj of this.contexts) { + if (contextObj.name == command) { + return await contextObj.handler(); + } + } + + return ''; + } + } + + export default ChatContextManager; diff --git a/src/exampleContext.ts b/src/exampleContext.ts new file mode 100644 index 0000000..5c36284 --- /dev/null +++ b/src/exampleContext.ts @@ -0,0 +1,9 @@ +import { ChatContext } from './contextManager'; + +export const exampleContext: ChatContext = { + name: 'exampleContext', + description: '这是一个示例上下文', + handler: async () => { + return `[context|example file name]`; + }, +}; diff --git a/src/loadContexts.ts b/src/loadContexts.ts new file mode 100644 index 0000000..4f91cbd --- /dev/null +++ b/src/loadContexts.ts @@ -0,0 +1,7 @@ +import ChatContextManager from './contextManager'; +import { exampleContext } from './exampleContext'; + +const chatContextManager = ChatContextManager.getInstance(); + +// 注册命令 +chatContextManager.registerContext(exampleContext); \ No newline at end of file diff --git a/src/messageHandler.ts b/src/messageHandler.ts index 6b0a371..b30e822 100644 --- a/src/messageHandler.ts +++ b/src/messageHandler.ts @@ -9,7 +9,9 @@ import DtmWrapper from './dtm'; import applyCode, {applyCodeFile} from './applyCode'; import './loadCommands'; +import './loadContexts' import CommandManager, { Command } from './commandManager'; +import ChatContextManager from './contextManager'; import * as vscode3 from 'vscode'; @@ -178,6 +180,14 @@ async function handleMessage( vscode.window.showErrorMessage(`Error commit fail: ${commitResult.message} ${commitResult.log}`); } return; + case 'regContextList': + const contextList = ChatContextManager.getInstance().getContextList(); + panel.webview.postMessage({ command: 'regContextList', result: contextList }); + return; + case 'addContext': + const contextStr = await ChatContextManager.getInstance().processText(message.selected); + panel.webview.postMessage({ command: 'appendContext', context: contextStr }); + return; } } From e08350d5462b5d5d2ee94e6757990dc4c0810230 Mon Sep 17 00:00:00 2001 From: "bobo.yang" Date: Thu, 4 May 2023 16:55:40 +0800 Subject: [PATCH 8/8] add context implementation --- src/commitMessageCommand.ts | 5 +- src/commonUtil.ts | 102 ++++++++++++++++++++++++++++++++++++ src/contextCodeSelected.ts | 24 +++++++++ src/contextCustomCommand.ts | 28 ++++++++++ src/contextFileSelected.ts | 27 ++++++++++ src/contextGitDiff.ts | 17 ++++++ src/contextGitDiffCached.ts | 17 ++++++ src/extension.ts | 7 +-- src/loadContexts.ts | 8 ++- src/messageHandler.ts | 12 +++-- 10 files changed, 236 insertions(+), 11 deletions(-) create mode 100644 src/commonUtil.ts create mode 100644 src/contextCodeSelected.ts create mode 100644 src/contextCustomCommand.ts create mode 100644 src/contextFileSelected.ts create mode 100644 src/contextGitDiff.ts create mode 100644 src/contextGitDiffCached.ts diff --git a/src/commitMessageCommand.ts b/src/commitMessageCommand.ts index d169131..3796e83 100644 --- a/src/commitMessageCommand.ts +++ b/src/commitMessageCommand.ts @@ -7,6 +7,7 @@ import * as fs from 'fs'; import * as os from 'os'; import * as path from 'path'; import { promisify } from 'util'; +import { createTempSubdirectory } from './commonUtil'; import ExtensionContextHolder from './extensionContext'; const mkdirAsync = promisify(fs.mkdir); @@ -46,11 +47,9 @@ export const commitMessageCommand: Command = { pattern: 'git: commit message', description: 'commit message for changes', handler: async (userInput: string) => { - const systemTempDir = os.tmpdir(); - const tempDir = path.join(systemTempDir, 'devchat/context'); + const tempDir = createTempSubdirectory('devchat/context'); // 创建临时目录 - await createTempDirectory(tempDir); const diff_file = path.join(tempDir, 'diff_output.txt'); await writeDiffFile(diff_file); diff --git a/src/commonUtil.ts b/src/commonUtil.ts new file mode 100644 index 0000000..bb82a14 --- /dev/null +++ b/src/commonUtil.ts @@ -0,0 +1,102 @@ +import * as fs from 'fs'; +import * as os from 'os'; +import * as path from 'path'; +import * as vscode from 'vscode'; + +import { spawn, exec } from 'child_process'; + +export function createTempSubdirectory(subdir: string): string { + // 获取系统临时目录 + const tempDir = os.tmpdir(); + // 构建完整的目录路径 + let targetDir = path.join(tempDir, subdir, Date.now().toString()); + // 检查目录是否存在,如果存在则重新生成目录名称 + while (fs.existsSync(targetDir)) { + targetDir = path.join(tempDir, subdir, Date.now().toString()); + } + // 递归创建目录 + fs.mkdirSync(targetDir, { recursive: true }); + // 返回创建的目录的绝对路径 + return targetDir; +} + +interface CommandResult { + exitCode: number | null; + stdout: string; + stderr: string; + } + + export async function runCommandAndWriteOutput( + command: string, + args: string[], + outputFile: string + ): Promise { + return new Promise((resolve) => { + // 获取当前工作区目录 + const workspaceDir = vscode.workspace.workspaceFolders?.[0].uri.fsPath || '.'; + + // 使用spawn执行命令 + const childProcess = spawn(command, args, { cwd: workspaceDir }); + + let stdout = ''; + let stderr = ''; + + // 监听stdout数据 + childProcess.stdout.on('data', (data) => { + stdout += data.toString(); + }); + + // 监听stderr数据 + childProcess.stderr.on('data', (data) => { + stderr += data.toString(); + }); + + // 监听进程退出事件 + childProcess.on('exit', (exitCode) => { + // 将命令输出结果写入到文件 + fs.writeFileSync(outputFile, stdout); + + // 返回结果 + resolve({ + exitCode, + stdout, + stderr, + }); + }); + }); + } + + export async function runCommandStringAndWriteOutput( + commandString: string, + outputFile: string + ): Promise { + return new Promise((resolve) => { + const workspaceDir = vscode.workspace.workspaceFolders?.[0].uri.fsPath || '.'; + + // 使用exec执行命令行字符串 + const childProcess = exec(commandString, { cwd: workspaceDir }, (error, stdout, stderr) => { + // 将命令输出结果写入到文件 + fs.writeFileSync(outputFile, stdout); + + // 返回结果 + resolve({ + exitCode: error && error.code? error.code : 0, + stdout, + stderr, + }); + }); + }); + } + + export async function getLanguageIdByFileName(fileName: string): Promise { + try { + // 打开指定的文件 + const document = await vscode.workspace.openTextDocument(fileName); + // 获取文件的语言标识符 + const languageId = document.languageId; + return languageId; + } catch (error) { + // 如果无法打开文件或发生其他错误,返回undefined + return undefined; + } + } \ No newline at end of file diff --git a/src/contextCodeSelected.ts b/src/contextCodeSelected.ts new file mode 100644 index 0000000..e86eda2 --- /dev/null +++ b/src/contextCodeSelected.ts @@ -0,0 +1,24 @@ + +import * as vscode from 'vscode'; +import * as path from 'path'; +import { createTempSubdirectory, getLanguageIdByFileName } from './commonUtil'; + +export async function handleCodeSelected(fileSelected: string, codeSelected: string) { + // get file name from fileSelected + const fileName = path.basename(fileSelected); + + // create temp directory and file + const tempDir = await createTempSubdirectory('devchat/context'); + const tempFile = path.join(tempDir, fileName); + + // get the language from fileSelected + const languageId = await getLanguageIdByFileName(fileSelected); + + // convert fileContent to markdown code block with languageId and file path + const markdownCodeBlock = `\`\`\`${languageId} path=${fileSelected}\n${codeSelected}\n\`\`\``; + + // save markdownCodeBlock to temp file + await vscode.workspace.fs.writeFile(vscode.Uri.file(tempFile), Buffer.from(markdownCodeBlock)); + + return `[context|${tempFile}]`; +} \ No newline at end of file diff --git a/src/contextCustomCommand.ts b/src/contextCustomCommand.ts new file mode 100644 index 0000000..3ec5914 --- /dev/null +++ b/src/contextCustomCommand.ts @@ -0,0 +1,28 @@ +import * as path from 'path'; +import * as vscode from 'vscode'; +import { ChatContext } from './contextManager'; +import { createTempSubdirectory, runCommandAndWriteOutput, runCommandStringAndWriteOutput } from './commonUtil'; + +export const customCommandContext: ChatContext = { + name: '', + description: 'custorm command', + handler: async () => { + // popup a dialog to ask for the command line to run + const customCommand = await vscode.window.showInputBox({ + prompt: 'Input your custom command', + placeHolder: 'for example: ls -l' + }); + + // 检查用户是否输入了命令 + if (customCommand) { + const tempDir = await createTempSubdirectory('devchat/context'); + const diff_file = path.join(tempDir, 'custom.txt'); + const result = await runCommandStringAndWriteOutput(customCommand, diff_file); + console.log(result.exitCode); + console.log(result.stdout); + console.log(result.stderr); + return `[context|${diff_file}]`; + } + return ''; + }, +}; diff --git a/src/contextFileSelected.ts b/src/contextFileSelected.ts new file mode 100644 index 0000000..faa212e --- /dev/null +++ b/src/contextFileSelected.ts @@ -0,0 +1,27 @@ + +import * as vscode from 'vscode'; +import * as path from 'path'; +import { createTempSubdirectory, getLanguageIdByFileName } from './commonUtil'; + +export async function handleFileSelected(fileSelected: string) { + // get file name from fileSelected + const fileName = path.basename(fileSelected); + + // create temp directory and file + const tempDir = await createTempSubdirectory('devchat/context'); + const tempFile = path.join(tempDir, fileName); + + // load content in fileSelected + const fileContent = await vscode.workspace.fs.readFile(vscode.Uri.file(fileSelected)); + + // get the language from fileSelected + const languageId = await getLanguageIdByFileName(fileSelected); + + // convert fileContent to markdown code block with languageId and file path + const markdownCodeBlock = `\`\`\`${languageId} path=${fileSelected}\n${fileContent}\n\`\`\``; + + // save markdownCodeBlock to temp file + await vscode.workspace.fs.writeFile(vscode.Uri.file(tempFile), Buffer.from(markdownCodeBlock)); + + return `[context|${tempFile}]`; +} \ No newline at end of file diff --git a/src/contextGitDiff.ts b/src/contextGitDiff.ts new file mode 100644 index 0000000..a902e21 --- /dev/null +++ b/src/contextGitDiff.ts @@ -0,0 +1,17 @@ +import * as path from 'path'; +import { ChatContext } from './contextManager'; +import { createTempSubdirectory, runCommandAndWriteOutput } from './commonUtil'; + +export const gitDiffContext: ChatContext = { + name: 'git diff', + description: 'diff for all changes', + handler: async () => { + const tempDir = await createTempSubdirectory('devchat/context'); + const diff_file = path.join(tempDir, 'diff_all.txt'); + const result = await runCommandAndWriteOutput('git', ['diff'], diff_file); + console.log(result.exitCode); + console.log(result.stdout); + console.log(result.stderr); + return `[context|${diff_file}]`; + }, +}; diff --git a/src/contextGitDiffCached.ts b/src/contextGitDiffCached.ts new file mode 100644 index 0000000..f029d0b --- /dev/null +++ b/src/contextGitDiffCached.ts @@ -0,0 +1,17 @@ +import * as path from 'path'; +import { ChatContext } from './contextManager'; +import { createTempSubdirectory, runCommandAndWriteOutput } from './commonUtil'; + +export const gitDiffCachedContext: ChatContext = { + name: 'git diff cached', + description: 'diff for cached changes', + handler: async () => { + const tempDir = await createTempSubdirectory('devchat/context'); + const diff_file = path.join(tempDir, 'diff_cached.txt'); + const result = await runCommandAndWriteOutput('git', ['diff', '--cached'], diff_file); + console.log(result.exitCode); + console.log(result.stdout); + console.log(result.stderr); + return `[context|${diff_file}]`; + }, +}; diff --git a/src/extension.ts b/src/extension.ts index d3c5e4f..4cfeca0 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -9,6 +9,7 @@ const sendCodeSelectMessage = require('./messageHandler').sendCodeSelectMessage; import ExtensionContextHolder from './extensionContext'; function createChatDirectoryAndCopyInstructionsSync(extensionUri: vscode.Uri) { + const workspaceFolders = vscode.workspace.workspaceFolders; if (!workspaceFolders) { return; @@ -51,7 +52,7 @@ function activate(context: vscode.ExtensionContext) { } }); - const disposable_add_context = vscode.commands.registerCommand('devchat.addConext', (uri: { path: any; }) => { + const disposable_add_context = vscode.commands.registerCommand('devchat.addConext', async (uri: { path: any; }) => { if (!ChatPanel.currentPanel()) { if (vscode.workspace.workspaceFolders) { ChatPanel.createOrShow(context.extensionUri); @@ -61,7 +62,7 @@ function activate(context: vscode.ExtensionContext) { } } - sendFileSelectMessage(ChatPanel.currentPanel().panel(), uri.path); + await sendFileSelectMessage(ChatPanel.currentPanel().panel(), uri.path); }); const disposableCodeContext = vscode.commands.registerCommand('devchat.askForCode', async () => { @@ -77,7 +78,7 @@ function activate(context: vscode.ExtensionContext) { } const selectedText = editor.document.getText(editor.selection); - sendCodeSelectMessage(ChatPanel.currentPanel().panel(), selectedText); + await sendCodeSelectMessage(ChatPanel.currentPanel().panel(), editor.document.fileName, selectedText); } }); diff --git a/src/loadContexts.ts b/src/loadContexts.ts index 4f91cbd..28c6796 100644 --- a/src/loadContexts.ts +++ b/src/loadContexts.ts @@ -1,7 +1,13 @@ import ChatContextManager from './contextManager'; import { exampleContext } from './exampleContext'; +import { gitDiffCachedContext } from './contextGitDiffCached'; +import { gitDiffContext } from './contextGitDiff'; +import { customCommandContext } from './contextCustomCommand'; const chatContextManager = ChatContextManager.getInstance(); // 注册命令 -chatContextManager.registerContext(exampleContext); \ No newline at end of file +chatContextManager.registerContext(exampleContext); +chatContextManager.registerContext(gitDiffCachedContext); +chatContextManager.registerContext(gitDiffContext); +chatContextManager.registerContext(customCommandContext); diff --git a/src/messageHandler.ts b/src/messageHandler.ts index b30e822..ed66670 100644 --- a/src/messageHandler.ts +++ b/src/messageHandler.ts @@ -12,6 +12,8 @@ import './loadCommands'; import './loadContexts' import CommandManager, { Command } from './commandManager'; import ChatContextManager from './contextManager'; +import { handleCodeSelected } from './contextCodeSelected'; +import { handleFileSelected } from './contextFileSelected'; import * as vscode3 from 'vscode'; @@ -30,12 +32,14 @@ async function deleteTempPatchFile(filePath: string): Promise { await unlinkAsync(filePath); } -export function sendFileSelectMessage(panel: vscode.WebviewPanel, filePath: string): void { - panel.webview.postMessage({ command: 'file_select', filePath }); +export async function sendFileSelectMessage(panel: vscode.WebviewPanel, filePath: string): Promise { + const codeContext = await handleFileSelected(filePath); + panel.webview.postMessage({ command: 'appendContext', context: codeContext }); } -export function sendCodeSelectMessage(panel: vscode.WebviewPanel, codeBlock: string): void { - panel.webview.postMessage({ command: 'code_select', codeBlock }); +export async function sendCodeSelectMessage(panel: vscode.WebviewPanel, filePath: string, codeBlock: string): Promise { + const codeContext = await handleCodeSelected(filePath, codeBlock); + panel.webview.postMessage({ command: 'appendContext', context: codeContext }); } export function askAI(panel: vscode.WebviewPanel, codeBlock: string, question: string): void {