idea plugin插件开发2——预览代码(多窗口)
作者:互联网
目录
idea插件开发资料实在太少,官方demo的例子也很少。
这里笔者介绍一下官方demo中没有的例子——代码预览窗口。
这只是一个demo,用于实际开发,还要结合官方手册来。
插件效果图:
代码目录结构:
第一步:新建一个Executor
假设我们预览的代码是Controller.java,插件为了演示,代码写死了,必须有这个类。
@RestController
public class Controller {
@GetMapping(value = "/controller/insert")
public String insert(String id) {
return "ok";
}
}
底部Executor代码,可以自行更改图标icon。
package com.example.action.t3_tool_windows.code_preview;
import com.intellij.execution.Executor;
import com.intellij.icons.AllIcons;
import org.jetbrains.annotations.NotNull;
import javax.swing.Icon;
public class CodePreview_Executor extends Executor {
public static final String PLUGIN_ID = "CodePreviewExecutor";
public static final String TOOL_WINDOW_ID = "CodePreviewExecutor";
public static final String CONTEXT_ACTION_ID = "11111";
@NotNull
@Override
public String getId() {
return PLUGIN_ID;
}
@Override
public String getToolWindowId() {
return TOOL_WINDOW_ID;
}
@Override
public Icon getToolWindowIcon() {
return AllIcons.Actions.Redo;
}
@NotNull
@Override
public Icon getIcon() {
return AllIcons.Actions.Redo;
}
@Override
public Icon getDisabledIcon() {
return AllIcons.Actions.Redo;
}
@Override
public String getDescription() {
return TOOL_WINDOW_ID;
}
@NotNull
@Override
public String getActionName() {
return TOOL_WINDOW_ID;
}
@NotNull
@Override
public String getStartActionText() {
return TOOL_WINDOW_ID;
}
@Override
public String getContextActionId() {
return CONTEXT_ACTION_ID;
}
@Override
public String getHelpId() {
return TOOL_WINDOW_ID;
}
}
第二步:Runner
这里需要用工具类:
public enum MyExecutorUtil {
;
/**
* 返回正在运行的 Executor
* @param id Executor id
*/
public static Executor getRunExecutorInstance(String id) {
return ExecutorRegistry.getInstance().getExecutorById(id);
}
}
package com.example.action.t3_tool_windows.code_preview;
import com.example.utils.MyExecutorUtil;
import com.intellij.execution.DefaultExecutionResult;
import com.intellij.execution.ExecutionException;
import com.intellij.execution.ExecutionManager;
import com.intellij.execution.Executor;
import com.intellij.execution.configurations.RunProfile;
import com.intellij.execution.configurations.RunProfileState;
import com.intellij.execution.runners.ExecutionEnvironment;
import com.intellij.execution.ui.RunContentDescriptor;
import com.intellij.execution.ui.RunnerLayoutUi;
import com.intellij.find.FindModel;
import com.intellij.find.impl.FindInProjectUtil;
import com.intellij.icons.AllIcons;
import com.intellij.openapi.actionSystem.ActionGroup;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.DefaultActionGroup;
import com.intellij.openapi.actionSystem.ToggleAction;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.Splitter;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.search.PsiShortNamesCache;
import com.intellij.ui.OnePixelSplitter;
import com.intellij.ui.content.Content;
import com.intellij.usageView.UsageInfo;
import com.intellij.usages.UsageViewPresentation;
import com.intellij.usages.impl.UsagePreviewPanel;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.Icon;
import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.JTextPane;
import java.awt.BorderLayout;
import java.util.ArrayList;
import java.util.List;
public class CodePreview_Runner {
/**
* Project
*/
private Project project = null;
public CodePreview_Runner(@NotNull Project project) {
this.project = project;
}
public void run() {
// 返回定义的 Executor
Executor executor = MyExecutorUtil.getRunExecutorInstance(CodePreview_Executor.PLUGIN_ID);
if (executor == null) {
return;
}
// 读取 psi 文件
PsiFile psiFile = getPsiFileByName(project, "Controller.java");
// 可以通过 document 创建 psifile
// Document document1 = psiFile.getViewProvider().getDocument();
// PsiFile myFile = MyPsiUtil.createPsiFileByDocument(project, document1);
// 初始化 Code Previewer Presentation
UsageViewPresentation viewPresentation1 = FindInProjectUtil.setupViewPresentation(false, new FindModel());
// 初始化 Code Previewer 展示面板
UsagePreviewPanel usagePreviewPanel = new UsagePreviewPanel(project, viewPresentation1);
usagePreviewPanel.setVisible(true);
// code preview 窗口, 默认跳转到类中的第一个方法(如果类和方法都存在)
updatePreviewByPsiFileFirstMethod(usagePreviewPanel, psiFile);
// 创建一个 JComponent
JComponent component = usagePreviewPanel.createComponent();
JPanel rightPanel = new JPanel(new BorderLayout());
rightPanel.add(component, BorderLayout.CENTER);
// 创建 RunnerLayoutUi
final RunnerLayoutUi.Factory factory = RunnerLayoutUi.Factory.getInstance(project);
RunnerLayoutUi layoutUi = factory.create("id", "title", "session name", project);
JTextPane leftPanel = new JTextPane();
leftPanel.setText("左边的窗口");
// 创建 ToolWindow 左右分屏窗口
Splitter splitterPanel = new OnePixelSplitter(false);
splitterPanel.setFirstComponent(leftPanel);
splitterPanel.setSecondComponent(rightPanel);
// 创建描述信息
RunContentDescriptor descriptor = new RunContentDescriptor(new RunProfile() {
@Nullable
@Override
public RunProfileState getState(@NotNull com.intellij.execution.Executor executor, @NotNull ExecutionEnvironment environment) throws ExecutionException {
return null;
}
@NotNull
@Override
public String getName() {
return "name";
}
@Nullable
@Override
public Icon getIcon() {
return null;
}
}, new DefaultExecutionResult(), layoutUi);
descriptor.setExecutionId(System.nanoTime());
final Content content = layoutUi.createContent("contentId", splitterPanel, "displayName", AllIcons.Debugger.Console, splitterPanel);
content.setCloseable(true);
layoutUi.addContent(content);
// 新增左边工具条
layoutUi.getOptions().setLeftToolbar(createActionToolbar(leftPanel), "RunnerToolbar1");
ExecutionManager.getInstance(project).getContentManager().showRunContent(executor, descriptor);
}
private ActionGroup createActionToolbar(JTextPane textPane) {
final DefaultActionGroup actionGroup = new DefaultActionGroup();
actionGroup.add(new MyPreviewAction());
return actionGroup;
}
public PsiFile getPsiFileByName(Project project, String fileName) {
if (null == fileName || fileName.isEmpty()) {
throw new RuntimeException("文件名不能为空");
}
// 查找名称 fileNmae 的文件
PsiFile[] files = PsiShortNamesCache.getInstance(project).getFilesByName(fileName);
return files[0];
}
/**
* code preview 窗口, 默认跳转到类中的第一个方法(如果类和方法都存在)
* @param usagePreviewPanel
* @param psiFile
*/
private void updatePreviewByPsiFileFirstMethod(UsagePreviewPanel usagePreviewPanel, PsiFile psiFile) {
int startOffset = 0;
int endOffset = 0;
List<UsageInfo> infos = new ArrayList<>();
// code preview 窗口, 默认跳转到类中的第一个方法(如果类和方法都存在)
PsiElement[] subElement = psiFile.getChildren();
for (PsiElement entity : subElement) {
if (entity instanceof PsiClass) {
PsiClass psiClass = (PsiClass) entity;
PsiMethod[] methods = psiClass.getMethods();
if (methods.length >= 1) {
PsiMethod firstMethod = methods[0];
startOffset = firstMethod.getTextOffset();
endOffset = startOffset + firstMethod.getName().length();
}
}
}
// startOffset:需要跳转的位置 (startOffset,endOffset) 表示一个高亮显示区间
infos.add(new UsageInfo(psiFile, startOffset, endOffset));
usagePreviewPanel.updateLayout(infos.isEmpty() ? null : infos);
}
private final class MyPreviewAction extends ToggleAction {
MyPreviewAction() {
super("Preview Source", null, AllIcons.Actions.PreviewDetails);
}
@Override
public boolean isSelected(@NotNull AnActionEvent e) {
return true;
}
@Override
public void setSelected(@NotNull AnActionEvent e, boolean state) {
if (state) {
// TODO
}
}
}
}
第三步:定义Action
这里需要用到工具类:
import com.intellij.ide.util.PropertiesComponent;
import com.intellij.openapi.project.Project;
public enum MyProjectUtil {
;
public static void setRunning(Project project, String key, boolean value) {
PropertiesComponent.getInstance(project).setValue(key, value);
}
public static boolean getRunning(Project project, String key) {
return PropertiesComponent.getInstance(project).getBoolean(key);
}
}
Action代码:
package com.example.action.t3_tool_windows.code_preview.action;
import com.example.action.t3_tool_windows.code_preview.CodePreview_Runner;
import com.example.utils.MyProjectUtil;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.project.Project;
import org.jetbrains.annotations.NotNull;
public class CodePreviewAction extends AnAction {
@Override
public void actionPerformed(@NotNull AnActionEvent e) {
runExecutor(e.getProject());
}
public void runExecutor(Project project) {
if (project == null) {
return;
}
MyProjectUtil.setRunning(project, "running", true);
CodePreview_Runner executor = new CodePreview_Runner(project);
executor.run();
}
}
第四步:plugin.xml配置
plugin.xml中配置代码如下,请根据自身情况调整。
<!-- 插件扩展 -->
<extensions defaultExtensionNs="com.intellij">
<!-- 自定义 底部view -->
<executor id="MyExecutor" order="last"
implementation="com.example.action.t3_tool_windows.code_preview.CodePreview_Executor"/>
</extensions>
<actions>
<!-- 自定义分组 -->
<group id="zhang.group1.id" text="Zhang-菜单汇总" description="Group1 description">
<add-to-group group-id="MainMenu" anchor="last"/>
<action id="zhang.bottom_tool.id2"
class="com.example.action.t3_tool_windows.code_preview.action.CodePreviewAction"
text="底部-ToolWindow-代码预览" description="Description">
</action>
</group>
</actions>
笔者项目用的 build.gradle 配置如下:
plugins {
id 'org.jetbrains.intellij' version '0.4.10'
id "org.jetbrains.kotlin.jvm" version "1.3.41"
id 'java'
}
repositories {
mavenCentral()
}
dependencies {
testCompile group: 'junit', name: 'junit', version: '4.12'
}
apply {
"java"
"terminal"
"ant"
}
group 'org.example'
version '1.0-SNAPSHOT'
// See https://github.com/JetBrains/gradle-intellij-plugin/
intellij {
version '2019.3.3'
// 必须,否则无法找到 PsiClass 等
plugins = ['java', 'gradle']
intellij.updateSinceUntilBuild false
}
sourceCompatibility = 1.8
targetCompatibility = 1.8
apply {
"java"
"terminal"
"ant"
}
test {
useJUnitPlatform()
}
tasks.withType(JavaCompile) {
options.encoding = "UTF-8"
}
buildPlugin {
buildSearchableOptions.enabled = false
}
文章推荐:
笔者的开源插件:Java Mybatis SQL Scannerhttps://zhangxiaofan.blog.csdn.net/article/details/123094358
标签:插件,return,intellij,plugin,多窗口,project,import,com,public 来源: https://blog.csdn.net/q258523454/article/details/123097015