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; }