其他分享
首页 > 其他分享> > idea plugin插件开发2——预览代码(多窗口)

idea plugin插件开发2——预览代码(多窗口)

作者:互联网

目录

第一步:新建一个Executor

第二步:Runner

第三步:定义Action

第四步:plugin.xml配置


idea插件开发资料实在太少,官方demo的例子也很少。

 官方demo:GitHub - JetBrains/intellij-sdk-code-samples: Mirror of the IntelliJ SDK Docs Code Sampleshttps://github.com/JetBrains/intellij-sdk-code-samples

这里笔者介绍一下官方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