ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

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

2022-02-23 19:33:41  阅读:347  来源: 互联网

标签:插件 return intellij plugin 多窗口 project import com public


目录

第一步:新建一个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

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有