前段时间搞AI代码提示的Idea插件。
我整理了一下idea插件部分的代码,包括监听用户触发调用代码提示、代码提示回显、使用tab采纳,使用esc取消提示等功能。
第一部分 插件监听器
首先,要创建一个监听器,用来监听用户行为。创建一个Listener。(还有很多idea内置的监听器,但我用了ProjectManagerListener )
public class EditorEventListener implements ProjectManagerListener {
public void projectOpened(@NotNull Project project) {
//在该位置写入监听的事件
}
}
创建完成Listener后,还是需要在插件配置文件plugin.xml中配置监听。
<listener class="你创建监听器包的位置.EditorEventListener "
topic="com.intellij.openapi.project.ProjectManagerListener">
</listener>
在监听的事件中,我使用了监听光标位置变化、监听文档内容变化、监听用户是否输入结束,这三个监听方式。
下面是监听光标位置和位置变化:
EditorFactory.getInstance().getEventMulticaster().addSelectionListener(new SelectionListener() {
@Override
public void selectionChanged(@NotNull SelectionEvent event) {
// 获取光标所在行
CursorEditorEvent.onCursorEditorEvent(event);
}
}, project);
// 监听光标位置变化
EditorFactory.getInstance().getEventMulticaster().addCaretListener(new CaretListener() {
@Override
public void caretPositionChanged(@NotNull CaretEvent event) {
// 光标位置发生变化
int line = event.getNewPosition().line;
int column = event.getNewPosition().column;
//LOG.info("Caret position changed: line " + line + ", column " + column);
}
}, project);
下面是监听文档内容变化:
EditorFactory.getInstance().getEventMulticaster().addDocumentListener(new DocumentListener() {
@Override
public void beforeDocumentChange(@NotNull DocumentEvent event) {
// 文档修改即将发生(暂时没用)
}
// 文档修改已经发生
@Override
public void documentChanged(@NotNull DocumentEvent event) {
// 文档修改已经发生
//LOG.info("Document changed: " + event.getNewFragment());
}
}, project);
理论上使用监听用户文档变化就可以作为触发,调用AI续写模型。但只要写一个字或者一个字母他就会调用一次,这个对AI模型的压力过大,也有太多浪费和没必要的请求。所有就有了接下来监听用户是否输入结束(也不是完全能精准监控到用户是否完成,而是在一定时间内,来判读用户是否结束输入了)
//监听用户是否输入结束
EditorFactory.getInstance().addEditorFactoryListener(new EditorFactoryListener() {
@Override
public void editorCreated(@NotNull EditorFactoryEvent event) {
Editor editor = event.getEditor();
Caret primaryCaret = editor.getCaretModel().getPrimaryCaret();
//添加文档监听器
editor.getDocument().addDocumentListener(new DocumentListener() {
private TimerTask currentTask;
@Override
public void documentChanged(@NotNull DocumentEvent event) {
if (currentTask != null) {
currentTask.cancel();
}
currentTask = new TimerTask() {
@Override
public void run() {
// 输入结束后的逻辑
//LOG.info("Input ended!");
}
};
timer.schedule(currentTask, INPUT_IDLE_DELAY_MS);
}
});
}
@Override
public void editorReleased(@NotNull EditorFactoryEvent event) {
}
}, project);
完整部分:
public class EditorEventListener implements ProjectManagerListener {
private static final Logger LOG = Logger.getInstance(EditorEventListener.class);
private final Timer timer = new Timer();
//对输入的延迟处理的时间
private static final long INPUT_IDLE_DELAY_MS = 500;
@Override
public void projectOpened(@NotNull Project project) {
/**
* 监听光标位置变化
*/
EditorFactory.getInstance().getEventMulticaster().addSelectionListener(new SelectionListener() {
@Override
public void selectionChanged(@NotNull SelectionEvent event) {
// 获取光标所在行
CursorEditorEvent.onCursorEditorEvent(event);
}
}, project);
// 监听光标位置变化
EditorFactory.getInstance().getEventMulticaster().addCaretListener(new CaretListener() {
@Override
public void caretPositionChanged(@NotNull CaretEvent event) {
// 光标位置发生变化
int line = event.getNewPosition().line;
int column = event.getNewPosition().column;
LOG.info("Caret position changed: line " + line + ", column " + column);
}
}, project);
// 监听文档内容变化
EditorFactory.getInstance().getEventMulticaster().addDocumentListener(new DocumentListener() {
@Override
public void beforeDocumentChange(@NotNull DocumentEvent event) {
// 文档修改即将发生(暂时没用)
}
// 文档修改已经发生
@Override
public void documentChanged(@NotNull DocumentEvent event) {
// 文档修改已经发生
LOG.info("Document changed: " + event.getNewFragment());
}
}, project);
//监听用户是否输入结束
EditorFactory.getInstance().addEditorFactoryListener(new EditorFactoryListener() {
@Override
public void editorCreated(@NotNull EditorFactoryEvent event) {
Editor editor = event.getEditor();
Caret primaryCaret = editor.getCaretModel().getPrimaryCaret();
//添加文档监听器
editor.getDocument().addDocumentListener(new DocumentListener() {
private TimerTask currentTask;
@Override
public void documentChanged(@NotNull DocumentEvent event) {
if (currentTask != null) {
currentTask.cancel();
}
currentTask = new TimerTask() {
@Override
public void run() {
// 输入结束后的逻辑
LOG.info("Input ended!");
//监听用户短时间不在操作后的逻辑
}
};
timer.schedule(currentTask, INPUT_IDLE_DELAY_MS);
}
});
}
@Override
public void editorReleased(@NotNull EditorFactoryEvent event) {
}
}, project);
// 监听文件编辑器选择变化(暂时没用)
project.getMessageBus().connect().subscribe(FileEditorManagerListener.FILE_EDITOR_MANAGER, new FileEditorManagerListener() {
@Override
public void selectionChanged(@NotNull FileEditorManagerEvent event) {
VirtualFile newFile = event.getNewFile();
if (newFile != null) {
LOG.info("File selection changed: " + newFile.getName());
}
}
});
}
}
其中使用TimerTask,来创建在主流程之外新的线程来执行,也用来监控用户在INPUT_IDLE_DELAY_MS时间内没有任何操作后,再请求续写模型。用新的线程来执行的时候,不会形象用户正常的输入操作,否则在idea插件的主线程上会形象到用户输入,如果你的后续操作时间很少或者忽略不计,可以在主线程上,但我使用的AI模型的效率比较差,所以为了用户使用插件的感受,我使用了这个。如果有大佬还有其他想法,咱们可以交流一下。
以上就是插件监听器部分的代码,我直接copy的自己工程的代码,可能有遗留的参数,可以留言,我到时候再去整理一下。