add context implementation
This commit is contained in:
parent
4b842048a8
commit
0d59445cef
@ -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);
|
||||
|
||||
|
102
src/commonUtil.ts
Normal file
102
src/commonUtil.ts
Normal file
@ -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<CommandResult> {
|
||||
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<CommandResult> {
|
||||
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<string | undefined> {
|
||||
try {
|
||||
// 打开指定的文件
|
||||
const document = await vscode.workspace.openTextDocument(fileName);
|
||||
// 获取文件的语言标识符
|
||||
const languageId = document.languageId;
|
||||
return languageId;
|
||||
} catch (error) {
|
||||
// 如果无法打开文件或发生其他错误,返回undefined
|
||||
return undefined;
|
||||
}
|
||||
}
|
24
src/contextCodeSelected.ts
Normal file
24
src/contextCodeSelected.ts
Normal file
@ -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}]`;
|
||||
}
|
28
src/contextCustomCommand.ts
Normal file
28
src/contextCustomCommand.ts
Normal file
@ -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: '<custom command>',
|
||||
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 '';
|
||||
},
|
||||
};
|
27
src/contextFileSelected.ts
Normal file
27
src/contextFileSelected.ts
Normal file
@ -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}]`;
|
||||
}
|
17
src/contextGitDiff.ts
Normal file
17
src/contextGitDiff.ts
Normal file
@ -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}]`;
|
||||
},
|
||||
};
|
17
src/contextGitDiffCached.ts
Normal file
17
src/contextGitDiffCached.ts
Normal file
@ -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}]`;
|
||||
},
|
||||
};
|
@ -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);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -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);
|
||||
chatContextManager.registerContext(gitDiffCachedContext);
|
||||
chatContextManager.registerContext(gitDiffContext);
|
||||
chatContextManager.registerContext(customCommandContext);
|
||||
|
@ -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<void> {
|
||||
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<void> {
|
||||
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<void> {
|
||||
const codeContext = await handleCodeSelected(filePath, codeBlock);
|
||||
panel.webview.postMessage({ command: 'appendContext', context: codeContext });
|
||||
}
|
||||
|
||||
export function askAI(panel: vscode.WebviewPanel, codeBlock: string, question: string): void {
|
||||
|
Loading…
x
Reference in New Issue
Block a user