多语言编码Agent解决方案(3)-VSCode扩展实现
本部分包含VSCode扩展的完整实现,包括多语言支持、命令注册、API调用和自动补全功能。
·
VSCode扩展实现:支持多语言的编码Agent集成
本部分包含VSCode扩展的完整实现,包括多语言支持、命令注册、API调用和自动补全功能。
1. VSCode扩展目录结构
vscode-extension/
├── extension.js # 扩展主文件
├── i18n.js # 国际化模块
├── package.json # 扩展配置
└── locales/ # 语言文件
├── en.json
├── zh-CN.json
└── ja.json
2. 国际化模块 (i18n.js)
// vscode-extension/i18n.js
const vscode = require('vscode');
const fs = require('fs');
const path = require('path');
class I18nManager {
constructor() {
this.translations = {};
this.currentLang = 'en';
this.loadTranslations();
}
loadTranslations() {
// 获取VSCode的语言设置
const vscodeLang = vscode.env.language;
// 映射VSCode语言代码到我们的语言文件
const langMap = {
'zh-cn': 'zh-CN',
'zh-tw': 'zh-CN', // 简化为zh-CN
'ja': 'ja',
'en': 'en',
'en-us': 'en'
};
this.currentLang = langMap[vscodeLang.toLowerCase()] || 'en';
// 加载语言文件
const localesDir = path.join(__dirname, 'locales');
const supportedLangs = ['en', 'zh-CN', 'ja'];
supportedLangs.forEach(lang => {
try {
const filePath = path.join(localesDir, `${lang}.json`);
const content = fs.readFileSync(filePath, 'utf8');
this.translations[lang] = JSON.parse(content);
} catch (error) {
console.error(`Failed to load language file for ${lang}:`, error);
}
});
// 如果当前语言没有翻译,使用英文
if (!this.translations[this.currentLang]) {
this.currentLang = 'en';
}
}
get(key, params = {}) {
const keys = key.split('.');
let translation = this.translations[this.currentLang];
// 逐级获取嵌套的值
for (const k of keys) {
if (translation && typeof translation === 'object') {
translation = translation[k];
} else {
break;
}
}
// 如果找不到,尝试英文
if (!translation) {
translation = this.translations['en'];
for (const k of keys) {
if (translation && typeof translation === 'object') {
translation = translation[k];
} else {
break;
}
}
}
// 如果还是找不到,返回key
if (!translation) {
return key;
}
// 处理参数替换
return translation.replace(/\{(\w+)\}/g, (match, key) => {
return params.hasOwnProperty(key) ? params[key] : match;
});
}
getCurrentLanguage() {
return this.currentLang;
}
}
// 创建全局实例
const i18n = new I18nManager();
// 导出便捷函数
function localize(key, params) {
return i18n.get(key, params);
}
module.exports = {
i18n,
localize
};
3. 语言文件 (locales)
en.json
{
"extension": {
"activated": "Coding Agent extension activated",
"ready": "Agent Ready",
"completing": "Completing...",
"generating": "Generating...",
"explaining": "Explaining...",
"refactoring": "Refactoring...",
"debugging": "Debugging...",
"generatingTests": "Generating tests...",
"error": "Error",
"disconnected": "Disconnected",
"connected": "Coding Agent backend connected",
"cannotConnect": "Cannot connect to Coding Agent backend, please ensure the service is started"
},
"commands": {
"complete": "Agent: Complete Code",
"generate": "Agent: Generate Code",
"explain": "Agent: Explain Code",
"refactor": "Agent: Refactor Code",
"debug": "Agent: Debug Code",
"test": "Agent: Generate Tests"
},
"prompts": {
"generateDescription": "Describe the code you want to generate",
"generatePlaceholder": "e.g., Create a REST API endpoint",
"selectCode": "Please select code first",
"completeFailed": "Completion failed: {error}",
"generateFailed": "Generation failed: {error}",
"explainFailed": "Explanation failed: {error}",
"refactorFailed": "Refactoring failed: {error}",
"debugFailed": "Debug failed: {error}",
"testFailed": "Test generation failed: {error}",
"refactorComplete": "Refactoring complete: {suggestions}"
},
"output": {
"explanation": "Code Explanation",
"debugAnalysis": "Debug Analysis",
"aiGenerated": "AI Generated",
"agentSuggestion": "Coding Agent Suggestion"
}
}
zh-CN.json
{
"extension": {
"activated": "编码助手扩展已激活",
"ready": "助手就绪",
"completing": "正在补全...",
"generating": "正在生成...",
"explaining": "正在解释...",
"refactoring": "正在重构...",
"debugging": "正在调试...",
"generatingTests": "正在生成测试...",
"error": "错误",
"disconnected": "未连接",
"connected": "编码助手后端已连接",
"cannotConnect": "无法连接到编码助手后端,请确保服务已启动"
},
"commands": {
"complete": "助手: 补全代码",
"generate": "助手: 生成代码",
"explain": "助手: 解释代码",
"refactor": "助手: 重构代码",
"debug": "助手: 调试代码",
"test": "助手: 生成测试"
},
"prompts": {
"generateDescription": "描述你想生成的代码",
"generatePlaceholder": "例如:创建一个REST API端点",
"selectCode": "请先选择代码",
"completeFailed": "补全失败: {error}",
"generateFailed": "生成失败: {error}",
"explainFailed": "解释失败: {error}",
"refactorFailed": "重构失败: {error}",
"debugFailed": "调试失败: {error}",
"testFailed": "测试生成失败: {error}",
"refactorComplete": "重构完成: {suggestions}"
},
"output": {
"explanation": "代码解释",
"debugAnalysis": "调试分析",
"aiGenerated": "AI生成",
"agentSuggestion": "编码助手建议"
}
}
ja.json
{
"extension": {
"activated": "コーディングエージェント拡張が有効になりました",
"ready": "エージェント準備完了",
"completing": "補完中...",
"generating": "生成中...",
"explaining": "説明中...",
"refactoring": "リファクタリング中...",
"debugging": "デバッグ中...",
"generatingTests": "テスト生成中...",
"error": "エラー",
"disconnected": "切断",
"connected": "コーディングエージェントバックエンドに接続しました",
"cannotConnect": "コーディングエージェントバックエンドに接続できません。サービスが起動していることを確認してください"
},
"commands": {
"complete": "エージェント: コード補完",
"generate": "エージェント: コード生成",
"explain": "エージェント: コード説明",
"refactor": "エージェント: コードリファクタリング",
"debug": "エージェント: コードデバッグ",
"test": "エージェント: テスト生成"
},
"prompts": {
"generateDescription": "生成したいコードを説明してください",
"generatePlaceholder": "例:REST APIエンドポイントを作成",
"selectCode": "最初にコードを選択してください",
"completeFailed": "補完失敗: {error}",
"generateFailed": "生成失敗: {error}",
"explainFailed": "説明失敗: {error}",
"refactorFailed": "リファクタリング失敗: {error}",
"debugFailed": "デバッグ失敗: {error}",
"testFailed": "テスト生成失敗: {error}",
"refactorComplete": "リファクタリング完了: {suggestions}"
},
"output": {
"explanation": "コード説明",
"debugAnalysis": "デバッグ分析",
"aiGenerated": "AI生成",
"agentSuggestion": "コーディングエージェントの提案"
}
}
4. 扩展主文件 (extension.js)
// vscode-extension/extension.js
const vscode = require('vscode');
const axios = require('axios');
const { localize, i18n } = require('./i18n');
// 配置
const API_BASE_URL = 'http://localhost:8000/api';
let statusBarItem;
function activate(context) {
console.log(localize('extension.activated'));
// 创建状态栏项
statusBarItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Right, 100);
statusBarItem.text = `$(sparkle) ${localize('extension.ready')}`;
statusBarItem.show();
// 获取当前语言设置
const currentLang = i18n.getCurrentLanguage();
// 创建axios实例,自动添加语言头
const apiClient = axios.create({
baseURL: API_BASE_URL,
headers: {
'Accept-Language': currentLang
}
});
// 检查后端连接
checkBackendConnection(apiClient);
// 注册命令:代码补全
context.subscriptions.push(vscode.commands.registerCommand('codingAgent.complete', async () => {
const editor = vscode.window.activeTextEditor;
if (!editor) return;
const document = editor.document;
const position = editor.selection.active;
const code = document.getText();
const cursorOffset = document.offsetAt(position);
try {
statusBarItem.text = `$(sync~spin) ${localize('extension.completing')}`;
const response = await apiClient.post('/code', {
action: 'complete',
code,
cursor_position: cursorOffset,
language: document.languageId,
max_tokens: 256
});
// 插入补全的代码
await editor.edit(editBuilder => {
editBuilder.insert(position, response.data.result);
});
statusBarItem.text = `$(sparkle) ${localize('extension.ready')}`;
} catch (error) {
vscode.window.showErrorMessage(localize('prompts.completeFailed', { error: error.message }));
statusBarItem.text = `$(error) ${localize('extension.error')}`;
}
}));
// 注册命令:生成代码
context.subscriptions.push(vscode.commands.registerCommand('codingAgent.generate', async () => {
const instruction = await vscode.window.showInputBox({
prompt: localize('prompts.generateDescription'),
placeHolder: localize('prompts.generatePlaceholder')
});
if (!instruction) return;
const editor = vscode.window.activeTextEditor;
const language = editor ? editor.document.languageId : 'python';
try {
statusBarItem.text = `$(sync~spin) ${localize('extension.generating')}`;
const response = await apiClient.post('/code', {
action: 'generate',
instruction,
language,
max_tokens: 512
});
if (editor) {
await editor.edit(editBuilder => {
editBuilder.insert(editor.selection.active, response.data.result);
});
} else {
const doc = await vscode.workspace.openTextDocument({ content: response.data.result, language });
await vscode.window.showTextDocument(doc);
}
statusBarItem.text = `$(sparkle) ${localize('extension.ready')}`;
} catch (error) {
vscode.window.showErrorMessage(localize('prompts.generateFailed', { error: error.message }));
statusBarItem.text = `$(error) ${localize('extension.error')}`;
}
}));
// 注册命令:解释代码
context.subscriptions.push(vscode.commands.registerCommand('codingAgent.explain', async () => {
const editor = vscode.window.activeTextEditor;
if (!editor) return;
const selection = editor.selection;
const code = editor.document.getText(selection);
if (!code) {
vscode.window.showWarningMessage(localize('prompts.selectCode'));
return;
}
try {
statusBarItem.text = `$(sync~spin) ${localize('extension.explaining')}`;
const response = await apiClient.post('/code', {
action: 'explain',
code,
language: editor.document.languageId
});
const outputChannel = vscode.window.createOutputChannel(localize('output.explanation'));
outputChannel.appendLine(`=== ${localize('output.explanation')} ===\n`);
outputChannel.appendLine(response.data.result);
outputChannel.show();
statusBarItem.text = `$(sparkle) ${localize('extension.ready')}`;
} catch (error) {
vscode.window.showErrorMessage(localize('prompts.explainFailed', { error: error.message }));
statusBarItem.text = `$(error) ${localize('extension.error')}`;
}
}));
// 注册命令:重构代码
context.subscriptions.push(vscode.commands.registerCommand('codingAgent.refactor', async () => {
const editor = vscode.window.activeTextEditor;
if (!editor) return;
const selection = editor.selection;
const code = editor.document.getText(selection);
if (!code) {
vscode.window.showWarningMessage(localize('prompts.selectCode'));
return;
}
try {
statusBarItem.text = `$(sync~spin) ${localize('extension.refactoring')}`;
const response = await apiClient.post('/code', {
action: 'refactor',
code,
language: editor.document.languageId
});
await editor.edit(editBuilder => {
editBuilder.replace(selection, response.data.result);
});
if (response.data.suggestions && response.data.suggestions.length > 0) {
vscode.window.showInformationMessage(localize('prompts.refactorComplete', { suggestions: response.data.suggestions.join(', ') }));
}
statusBarItem.text = `$(sparkle) ${localize('extension.ready')}`;
} catch (error) {
vscode.window.showErrorMessage(localize('prompts.refactorFailed', { error: error.message }));
statusBarItem.text = `$(error) ${localize('extension.error')}`;
}
}));
// 注册命令:调试代码
context.subscriptions.push(vscode.commands.registerCommand('codingAgent.debug', async () => {
const editor = vscode.window.activeTextEditor;
if (!editor) return;
const code = editor.document.getText();
try {
statusBarItem.text = `$(sync~spin) ${localize('extension.debugging')}`;
const response = await apiClient.post('/code', {
action: 'debug',
code,
language: editor.document.languageId
});
const outputChannel = vscode.window.createOutputChannel(localize('output.debugAnalysis'));
outputChannel.appendLine(`=== ${localize('output.debugAnalysis')} ===\n`);
outputChannel.appendLine(response.data.result);
outputChannel.show();
statusBarItem.text = `$(sparkle) ${localize('extension.ready')}`;
} catch (error) {
vscode.window.showErrorMessage(localize('prompts.debugFailed', { error: error.message }));
statusBarItem.text = `$(error) ${localize('extension.error')}`;
}
}));
// 注册命令:生成测试
context.subscriptions.push(vscode.commands.registerCommand('codingAgent.test', async () => {
const editor = vscode.window.activeTextEditor;
if (!editor) return;
const selection = editor.selection;
const code = selection.isEmpty ? editor.document.getText() : editor.document.getText(selection);
try {
statusBarItem.text = `$(sync~spin) ${localize('extension.generatingTests')}`;
const response = await apiClient.post('/code', {
action: 'test',
code,
language: editor.document.languageId
});
const testDoc = await vscode.workspace.openTextDocument({
content: response.data.result,
language: editor.document.languageId
});
await vscode.window.showTextDocument(testDoc, vscode.ViewColumn.Beside);
statusBarItem.text = `$(sparkle) ${localize('extension.ready')}`;
} catch (error) {
vscode.window.showErrorMessage(localize('prompts.testFailed', { error: error.message }));
statusBarItem.text = `$(error) ${localize('extension.error')}`;
}
}));
// 自动补全提供者(可选:注册为Inline Completion Provider)
context.subscriptions.push(vscode.languages.registerInlineCompletionItemProvider(
{ scheme: 'file', language: '*' },
{
async provideInlineCompletionItems(document, position, context, token) {
const codeBefore = document.getText(new vscode.Range(new vscode.Position(0, 0), position));
const codeAfter = document.getText(new vscode.Range(position, document.lineAt(document.lineCount - 1).range.end));
try {
const response = await apiClient.post('/code', {
action: 'complete',
code: codeBefore + codeAfter,
cursor_position: document.offsetAt(position),
language: document.languageId,
max_tokens: 100
});
return [new vscode.InlineCompletionItem(response.data.result)];
} catch (error) {
console.error('Inline completion failed:', error);
return [];
}
}
}
));
// 监听语言变化(如果VSCode语言改变,重新加载)
context.subscriptions.push(vscode.workspace.onDidChangeConfiguration(event => {
if (event.affectsConfiguration('general.language')) {
i18n.loadTranslations();
}
}));
}
// 检查后端连接函数
async function checkBackendConnection(apiClient) {
try {
const response = await apiClient.get('/health');
if (response.data.status === 'healthy') {
statusBarItem.text = `$(sparkle) ${localize('extension.connected')}`;
} else {
statusBarItem.text = `$(alert) ${localize('extension.cannotConnect')}`;
}
} catch (error) {
statusBarItem.text = `$(alert) ${localize('extension.cannotConnect')}`;
}
}
function deactivate() {
if (statusBarItem) {
statusBarItem.dispose();
}
}
module.exports = {
activate,
deactivate
};
5. 扩展配置 (package.json)
{
"name": "coding-agent",
"displayName": "Coding Agent",
"description": "Local AI-powered coding assistant with multi-language support for VS Code",
"version": "1.0.0",
"engines": {
"vscode": "^1.80.0"
},
"categories": [
"Programming Languages",
"Machine Learning"
],
"activationEvents": [
"onLanguage:python",
"onLanguage:javascript",
"onLanguage:java",
"onCommand:codingAgent.complete",
"onCommand:codingAgent.generate",
"onCommand:codingAgent.explain",
"onCommand:codingAgent.refactor",
"onCommand:codingAgent.debug",
"onCommand:codingAgent.test"
],
"main": "./extension.js",
"contributes": {
"commands": [
{
"command": "codingAgent.complete",
"title": "%commands.complete%"
},
{
"command": "codingAgent.generate",
"title": "%commands.generate%"
},
{
"command": "codingAgent.explain",
"title": "%commands.explain%"
},
{
"command": "codingAgent.refactor",
"title": "%commands.refactor%"
},
{
"command": "codingAgent.debug",
"title": "%commands.debug%"
},
{
"command": "codingAgent.test",
"title": "%commands.test%"
}
],
"keybindings": [
{
"command": "codingAgent.complete",
"key": "ctrl+shift+space",
"mac": "cmd+shift+space"
},
{
"command": "codingAgent.generate",
"key": "ctrl+shift+g",
"mac": "cmd+shift+g"
},
{
"command": "codingAgent.explain",
"key": "ctrl+shift+e",
"mac": "cmd+shift+e"
},
{
"command": "codingAgent.refactor",
"key": "ctrl+shift+r",
"mac": "cmd+shift+r"
},
{
"command": "codingAgent.debug",
"key": "ctrl+shift+d",
"mac": "cmd+shift+d"
},
{
"command": "codingAgent.test",
"key": "ctrl+shift+t",
"mac": "cmd+shift+t"
}
],
"menus": {
"editor/context": [
{
"command": "codingAgent.complete",
"group": "codingAgent@1"
},
{
"command": "codingAgent.explain",
"group": "codingAgent@2"
},
{
"command": "codingAgent.refactor",
"group": "codingAgent@3"
},
{
"command": "codingAgent.test",
"group": "codingAgent@4"
}
]
}
},
"scripts": {
"lint": "eslint .",
"pretest": "npm run lint",
"test": "node ./test/runTest.js"
},
"dependencies": {
"axios": "^1.6.0"
},
"devDependencies": {
"@types/vscode": "^1.80.0",
"@types/mocha": "^10.0.0",
"@types/node": "16.x",
"eslint": "^8.0.0",
"@vscode/test-electron": "^2.0.0"
}
}
更多推荐
所有评论(0)