IntelliJ插件实现:支持多语言的编码Agent集成
本部分包含IntelliJ插件的完整实现,包括多语言支持、动作注册、API调用和UI集成。插件使用Java开发,基于IntelliJ Platform Plugin SDK。
1. IntelliJ插件目录结构
intellij-plugin/
├── src/ # 源代码
│ └── main
│ ├── java
│ │ └── com
│ │ └── codingagent
│ │ ├── AgentActions.java # 动作处理类
│ │ ├── ApiClient.java # API调用工具
│ │ └── i18n
│ │ └── Messages.java # 国际化消息类
│ └── resources/ # 资源文件
│ └── locales/ # 语言文件
│ ├── messages.properties # 英文(默认)
│ ├── messages_zh_CN.properties # 中文
│ └── messages_ja.properties # 日文
├── META-INF/ # 元数据
│ └── plugin.xml # 插件配置
└── build.gradle # 构建脚本(可选,假设使用Gradle)
2. 国际化支持 (Messages.java)
java
// src/main/java/com/codingagent/i18n/Messages.java
package com.codingagent.i18n;
import com.intellij.BundleBase;
import com.intellij.openapi.util.NotNullLazyValue;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.PropertyKey;
import java.util.ResourceBundle;
public class Messages {
@NonNls
private static final String BUNDLE = "locales.messages";
private static final NotNullLazyValue<ResourceBundle> LAZY_BUNDLE = NotNullLazyValue.createValue(() -> {
String lang = System.getProperty("user.language");
if ("zh".equals(lang)) {
return ResourceBundle.getBundle(BUNDLE, new java.util.Locale("zh", "CN"));
} else if ("ja".equals(lang)) {
return ResourceBundle.getBundle(BUNDLE, java.util.Locale.JAPANESE);
} else {
return ResourceBundle.getBundle(BUNDLE, java.util.Locale.ENGLISH);
}
});
private Messages() {}
public static String message(@PropertyKey(resourceBundle = BUNDLE) String key, Object... params) {
return BundleBase.message(LAZY_BUNDLE.getValue(), key, params);
}
}
3. 语言文件 (locales)
messages.properties (英文,默认)
extension.activated=Coding Agent plugin activated
extension.ready=Agent Ready
extension.completing=Completing...
extension.generating=Generating...
extension.explaining=Explaining...
extension.refactoring=Refactoring...
extension.debugging=Debugging...
extension.generatingTests=Generating tests...
extension.error=Error
extension.disconnected=Disconnected
extension.connected=Coding Agent backend connected
extension.cannotConnect=Cannot connect to Coding Agent backend, please ensure the service is started
commands.complete=Agent: Complete Code
commands.generate=Agent: Generate Code
commands.explain=Agent: Explain Code
commands.refactor=Agent: Refactor Code
commands.debug=Agent: Debug Code
commands.test=Agent: Generate Tests
prompts.generateDescription=Describe the code you want to generate
prompts.generatePlaceholder=e.g., Create a REST API endpoint
prompts.selectCode=Please select code first
prompts.completeFailed=Completion failed: {0}
prompts.generateFailed=Generation failed: {0}
prompts.explainFailed=Explanation failed: {0}
prompts.refactorFailed=Refactoring failed: {0}
prompts.debugFailed=Debug failed: {0}
prompts.testFailed=Test generation failed: {0}
prompts.refactorComplete=Refactoring complete: {0}
output.explanation=Code Explanation
output.debugAnalysis=Debug Analysis
output.aiGenerated=AI Generated
output.agentSuggestion=Coding Agent Suggestion
messages_zh_CN.properties (中文)
extension.activated=编码助手插件已激活
extension.ready=助手就绪
extension.completing=正在补全...
extension.generating=正在生成...
extension.explaining=正在解释...
extension.refactoring=正在重构...
extension.debugging=正在调试...
extension.generatingTests=正在生成测试...
extension.error=错误
extension.disconnected=未连接
extension.connected=编码助手后端已连接
extension.cannotConnect=无法连接到编码助手后端,请确保服务已启动
commands.complete=助手: 补全代码
commands.generate=助手: 生成代码
commands.explain=助手: 解释代码
commands.refactor=助手: 重构代码
commands.debug=助手: 调试代码
commands.test=助手: 生成测试
prompts.generateDescription=描述你想生成的代码
prompts.generatePlaceholder=例如:创建一个REST API端点
prompts.selectCode=请先选择代码
prompts.completeFailed=补全失败: {0}
prompts.generateFailed=生成失败: {0}
prompts.explainFailed=解释失败: {0}
prompts.refactorFailed=重构失败: {0}
prompts.debugFailed=调试失败: {0}
prompts.testFailed=测试生成失败: {0}
prompts.refactorComplete=重构完成: {0}
output.explanation=代码解释
output.debugAnalysis=调试分析
output.aiGenerated=AI生成
output.agentSuggestion=编码助手建议
messages_ja.properties (日文)
extension.activated=コーディングエージェントプラグインが有効になりました
extension.ready=エージェント準備完了
extension.completing=補完中...
extension.generating=生成中...
extension.explaining=説明中...
extension.refactoring=リファクタリング中...
extension.debugging=デバッグ中...
extension.generatingTests=テスト生成中...
extension.error=エラー
extension.disconnected=切断
extension.connected=コーディングエージェントバックエンドに接続しました
extension.cannotConnect=コーディングエージェントバックエンドに接続できません。サービスが起動していることを確認してください
commands.complete=エージェント: コード補完
commands.generate=エージェント: コード生成
commands.explain=エージェント: コード説明
commands.refactor=エージェント: コードリファクタリング
commands.debug=エージェント: コードデバッグ
commands.test=エージェント: テスト生成
prompts.generateDescription=生成したいコードを説明してください
prompts.generatePlaceholder=例:REST APIエンドポイントを作成
prompts.selectCode=最初にコードを選択してください
prompts.completeFailed=補完失敗: {0}
prompts.generateFailed=生成失敗: {0}
prompts.explainFailed=説明失敗: {0}
prompts.refactorFailed=リファクタリング失敗: {0}
prompts.debugFailed=デバッグ失敗: {0}
prompts.testFailed=テスト生成失敗: {0}
prompts.refactorComplete=リファクタリング完了: {0}
output.explanation=コード説明
output.debugAnalysis=デバッグ分析
output.aiGenerated=AI生成
output.agentSuggestion=コーディングエージェントの提案
4. API调用工具 (ApiClient.java)
java
// src/main/java/com/codingagent/ApiClient.java
package com.codingagent;
import com.intellij.openapi.util.text.StringUtil;
import okhttp3.*;
import org.jetbrains.annotations.NotNull;
import org.json.JSONObject;
import java.io.IOException;
public class ApiClient {
private static final String BASE_URL = "http://localhost:8000/api";
private final OkHttpClient client = new OkHttpClient();
private final String acceptLanguage;
public ApiClient(String acceptLanguage) {
this.acceptLanguage = acceptLanguage;
}
public JSONObject post(String endpoint, JSONObject requestBody) throws IOException {
RequestBody body = RequestBody.create(
requestBody.toString(),
MediaType.get("application/json; charset=utf-8")
);
Request request = new Request.Builder()
.url(BASE_URL + endpoint)
.post(body)
.addHeader("Accept-Language", acceptLanguage)
.build();
try (Response response = client.newCall(request).execute()) {
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
return new JSONObject(StringUtil.notNullize(response.body().string()));
}
}
public JSONObject get(String endpoint) throws IOException {
Request request = new Request.Builder()
.url(BASE_URL + endpoint)
.addHeader("Accept-Language", acceptLanguage)
.build();
try (Response response = client.newCall(request).execute()) {
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
return new JSONObject(StringUtil.notNullize(response.body().string()));
}
}
}
5. 动作处理类 (AgentActions.java)
java
// src/main/java/com/codingagent/AgentActions.java
package com.codingagent;
import com.codingagent.i18n.Messages;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.CommonDataKeys;
import com.intellij.openapi.editor.Caret;
import com.intellij.openapi.editor.CaretModel;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.SelectionModel;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.ui.popup.JBPopupFactory;
import com.intellij.openapi.vfs.VirtualFile;
import org.json.JSONArray;
import org.json.JSONObject;
public class AgentActions {
private static final ApiClient apiClient = new ApiClient(System.getProperty("user.language"));
public static class CompleteAction extends AnAction {
@Override
public void actionPerformed(@NotNull AnActionEvent e) {
Editor editor = e.getData(CommonDataKeys.EDITOR);
if (editor == null) return;
Document document = editor.getDocument();
CaretModel caretModel = editor.getCaretModel();
int offset = caretModel.getOffset();
try {
JSONObject request = new JSONObject();
request.put("action", "complete");
request.put("code", document.getText());
request.put("cursor_position", offset);
request.put("language", getLanguage(e.getData(CommonDataKeys.VIRTUAL_FILE)));
JSONObject response = apiClient.post("/code", request);
String result = response.getString("result");
com.intellij.openapi.command.WriteCommandAction.runWriteCommandAction(e.getProject(), () -> {
document.insertString(offset, result);
});
} catch (Exception ex) {
Messages.showErrorDialog(Messages.message("prompts.completeFailed", ex.getMessage()), Messages.message("extension.error"));
}
}
}
public static class GenerateAction extends AnAction {
@Override
public void actionPerformed(@NotNull AnActionEvent e) {
Project project = e.getProject();
String instruction = Messages.showInputDialog(project,
Messages.message("prompts.generateDescription"),
Messages.message("commands.generate"),
null,
Messages.message("prompts.generatePlaceholder"), null);
if (instruction == null) return;
Editor editor = e.getData(CommonDataKeys.EDITOR);
if (editor == null) return;
Document document = editor.getDocument();
int offset = editor.getCaretModel().getOffset();
try {
JSONObject request = new JSONObject();
request.put("action", "generate");
request.put("instruction", instruction);
request.put("language", getLanguage(e.getData(CommonDataKeys.VIRTUAL_FILE)));
JSONObject response = apiClient.post("/code", request);
String result = response.getString("result");
com.intellij.openapi.command.WriteCommandAction.runWriteCommandAction(project, () -> {
document.insertString(offset, result);
});
} catch (Exception ex) {
Messages.showErrorDialog(Messages.message("prompts.generateFailed", ex.getMessage()), Messages.message("extension.error"));
}
}
}
public static class ExplainAction extends AnAction {
@Override
public void actionPerformed(@NotNull AnActionEvent e) {
Editor editor = e.getData(CommonDataKeys.EDITOR);
if (editor == null) return;
SelectionModel selectionModel = editor.getSelectionModel();
String code = selectionModel.getSelectedText();
if (code == null || code.isEmpty()) {
Messages.showWarningDialog(Messages.message("prompts.selectCode"), "Warning");
return;
}
try {
JSONObject request = new JSONObject();
request.put("action", "explain");
request.put("code", code);
JSONObject response = apiClient.post("/code", request);
String result = response.getString("result");
JBPopupFactory.getInstance().createMessage(result).showInFocusCenter();
} catch (Exception ex) {
Messages.showErrorDialog(Messages.message("prompts.explainFailed", ex.getMessage()), Messages.message("extension.error"));
}
}
}
public static class RefactorAction extends AnAction {
@Override
public void actionPerformed(@NotNull AnActionEvent e) {
Editor editor = e.getData(CommonDataKeys.EDITOR);
if (editor == null) return;
SelectionModel selectionModel = editor.getSelectionModel();
String code = selectionModel.getSelectedText();
if (code == null || code.isEmpty()) {
Messages.showWarningDialog(Messages.message("prompts.selectCode"), "Warning");
return;
}
int start = selectionModel.getSelectionStart();
int end = selectionModel.getSelectionEnd();
try {
JSONObject request = new JSONObject();
request.put("action", "refactor");
request.put("code", code);
JSONObject response = apiClient.post("/code", request);
String result = response.getString("result");
Document document = editor.getDocument();
com.intellij.openapi.command.WriteCommandAction.runWriteCommandAction(e.getProject(), () -> {
document.replaceString(start, end, result);
});
JSONArray suggestions = response.optJSONArray("suggestions");
if (suggestions != null) {
Messages.showInfoDialog(Messages.message("prompts.refactorComplete", suggestions.toString()), "Info");
}
} catch (Exception ex) {
Messages.showErrorDialog(Messages.message("prompts.refactorFailed", ex.getMessage()), Messages.message("extension.error"));
}
}
}
public static class DebugAction extends AnAction {
@Override
public void actionPerformed(@NotNull AnActionEvent e) {
Editor editor = e.getData(CommonDataKeys.EDITOR);
if (editor == null) return;
String code = editor.getDocument().getText();
try {
JSONObject request = new JSONObject();
request.put("action", "debug");
request.put("code", code);
JSONObject response = apiClient.post("/code", request);
String result = response.getString("result");
JBPopupFactory.getInstance().createMessage(result).showInFocusCenter();
} catch (Exception ex) {
Messages.showErrorDialog(Messages.message("prompts.debugFailed", ex.getMessage()), Messages.message("extension.error"));
}
}
}
public static class TestAction extends AnAction {
@Override
public void actionPerformed(@NotNull AnActionEvent e) {
Editor editor = e.getData(CommonDataKeys.EDITOR);
if (editor == null) return;
SelectionModel selectionModel = editor.getSelectionModel();
String code = selectionModel.hasSelection() ? selectionModel.getSelectedText() : editor.getDocument().getText();
try {
JSONObject request = new JSONObject();
request.put("action", "test");
request.put("code", code);
JSONObject response = apiClient.post("/code", request);
String result = response.getString("result");
// 创建新文件或显示在弹出窗口中
JBPopupFactory.getInstance().createMessage(result).showInFocusCenter();
} catch (Exception ex) {
Messages.showErrorDialog(Messages.message("prompts.testFailed", ex.getMessage()), Messages.message("extension.error"));
}
}
}
private static String getLanguage(VirtualFile file) {
if (file == null) return "java";
String extension = file.getExtension();
if (extension == null) return "java";
switch (extension) {
case "py": return "python";
case "js": return "javascript";
// 添加更多
default: return "java";
}
}
}
6. 插件配置 (META-INF/plugin.xml)
xml
<idea-plugin>
<id>com.codingagent.intellij</id>
<name>Coding Agent</name>
<vendor>Your Company</vendor>
<version>1.0.0</version>
<description>Local AI-powered coding assistant with multi-language support for IntelliJ</description>
<depends>com.intellij.modules.platform</depends>
<actions>
<group id="CodingAgent.Menu" text="Coding Agent" popup="true">
<add-to-group group-id="EditorPopupMenu" anchor="last"/>
<action id="CodingAgent.Complete" class="com.codingagent.AgentActions$CompleteAction" text="%commands.complete%"/>
<action id="CodingAgent.Generate" class="com.codingagent.AgentActions$GenerateAction" text="%commands.generate%"/>
<action id="CodingAgent.Explain" class="com.codingagent.AgentActions$ExplainAction" text="%commands.explain%"/>
<action id="CodingAgent.Refactor" class="com.codingagent.AgentActions$RefactorAction" text="%commands.refactor%"/>
<action id="CodingAgent.Debug" class="com.codingagent.AgentActions$DebugAction" text="%commands.debug%"/>
<action id="CodingAgent.Test" class="com.codingagent.AgentActions$TestAction" text="%commands.test%"/>
</group>
</actions>
<extensions defaultExtensionNs="com.intellij">
<!-- 如有需要,添加更多扩展 -->
</extensions>
</idea-plugin>
这个IntelliJ插件实现提供了完整的编码辅助功能,支持多语言界面,并与本地后端服务集成。注意:实际开发中需要添加OkHttp依赖到build.gradle,并处理更多边缘情况。