add commit message command

This commit is contained in:
bobo.yang 2023-05-03 23:21:46 +08:00 committed by Rankin Zheng
parent 7f6a423045
commit 05bee3880b
8 changed files with 140 additions and 24 deletions

View File

@ -2,7 +2,7 @@ export interface Command {
name: string; name: string;
pattern: string; pattern: string;
description: string; description: string;
handler: (userInput: string) => string; handler: (userInput: string) => Promise<string>;
} }
class CommandManager { class CommandManager {
@ -27,21 +27,34 @@ export interface Command {
return this.commands; return this.commands;
} }
processText(text: string): string { async processText(text: string): Promise<string> {
let result = text; // 定义一个异步函数来处理单个命令
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;
};
this.commands.forEach((commandObj) => { // 处理所有命令
const commandPattern = new RegExp( let result = text;
`\\/(${commandObj.pattern.replace("{{prompt}}", "(.+?)")})`, for (const commandObj of this.commands) {
"g" result = await processCommand(commandObj, result);
); }
result = result.replace(commandPattern, (_, matchedUserInput) => { return result;
return commandObj.handler(matchedUserInput);
});
});
return result;
} }
} }

View File

@ -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<void> {
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`;
},
};

View File

@ -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 <context>, 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.

View File

@ -0,0 +1 @@
You are a software developer assistant

View File

@ -4,7 +4,7 @@ export const exampleCommand1: Command = {
name: 'exampleCommand1', name: 'exampleCommand1',
pattern: 'example: command1 {{prompt}}', pattern: 'example: command1 {{prompt}}',
description: '这是一个示例命令1', description: '这是一个示例命令1',
handler: (userInput: string) => { handler: async (userInput: string) => {
return `示例命令1处理了以下文本${userInput}`; return `示例命令1处理了以下文本${userInput}`;
}, },
}; };

View File

@ -4,7 +4,7 @@ export const exampleCommand2: Command = {
name: 'exampleCommand2', name: 'exampleCommand2',
pattern: 'example: command2 {{prompt}}', pattern: 'example: command2 {{prompt}}',
description: '这是一个示例命令2', description: '这是一个示例命令2',
handler: (userInput: string) => { handler: async (userInput: string) => {
return `示例命令2处理了以下文本${userInput}`; return `示例命令2处理了以下文本${userInput}`;
}, },
}; };

View File

@ -1,9 +1,11 @@
import CommandManager from './commandManager'; import CommandManager from './commandManager';
import { exampleCommand1 } from './exampleCommand1'; import { exampleCommand1 } from './exampleCommand1';
import { exampleCommand2 } from './exampleCommand2'; import { exampleCommand2 } from './exampleCommand2';
import { commitMessageCommand } from './commitMessageCommand';
const commandManager = CommandManager.getInstance(); const commandManager = CommandManager.getInstance();
// 注册命令 // 注册命令
commandManager.registerCommand(exampleCommand1); commandManager.registerCommand(exampleCommand1);
commandManager.registerCommand(exampleCommand2); commandManager.registerCommand(exampleCommand2);
commandManager.registerCommand(commitMessageCommand);

View File

@ -41,19 +41,43 @@ export function askAI(panel: vscode.WebviewPanel, codeBlock: string, question: s
} }
// Add this function to messageHandler.ts // 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 contextRegex = /\[context\|(.*?)\]/g;
const instructionRegex = /\[instruction\|(.*?)\]/g;
const referenceRegex = /\[reference\|(.*?)\]/g;
const contextPaths = []; const contextPaths = [];
const instructionPaths = [];
const referencePaths = [];
let match; let match;
// 提取 context
while ((match = contextRegex.exec(message)) !== null) { while ((match = contextRegex.exec(message)) !== null) {
contextPaths.push(match[1]); contextPaths.push(match[1]);
} }
const text = message.replace(contextRegex, '').trim(); // 提取 instruction
return { context: contextPaths, text }; 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( async function handleMessage(
message: any, message: any,
panel: vscode.WebviewPanel panel: vscode.WebviewPanel
@ -63,12 +87,21 @@ async function handleMessage(
switch (message.command) { switch (message.command) {
case 'sendMessage': 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 } : {}; const chatOptions: any = lastPromptHash ? { parent: lastPromptHash } : {};
if (parsedMessage.context.length > 0) { if (parsedMessage.context.length > 0) {
chatOptions.context = parsedMessage.context; chatOptions.context = parsedMessage.context;
} }
if (parsedMessage.instruction.length > 0) {
chatOptions.instruction = parsedMessage.instruction;
}
if (parsedMessage.reference.length > 0) {
chatOptions.reference = parsedMessage.reference;
}
let partialData = ""; let partialData = "";
const onData = (partialResponse: string) => { const onData = (partialResponse: string) => {
@ -112,7 +145,7 @@ async function handleMessage(
panel.webview.postMessage({ command: 'regCommandList', result: commandList }); panel.webview.postMessage({ command: 'regCommandList', result: commandList });
return; return;
case 'convertCommand': case 'convertCommand':
const newText = CommandManager.getInstance().processText(message.text); const newText = await CommandManager.getInstance().processText(message.text);
panel.webview.postMessage({ command: 'convertCommand', result: newText }); panel.webview.postMessage({ command: 'convertCommand', result: newText });
return; return;
} }