美文网首页
(二十四)IntelliJ 插件开发——Idea下方工具窗口

(二十四)IntelliJ 插件开发——Idea下方工具窗口

作者: 秋水畏寒 | 来源:发表于2020-07-14 19:17 被阅读0次

    Github

    https://github.com/kungyutucheng/my_gradle_plugin

    运行环境

    macOS 10.14.5
    IntelliJ idea 2019.2.4

    参考

    sonarlint-intellij
    mybatis-log-plugin

    前言

    最近在编写自己的一个idea插件,其中有个功能需要在idea下方弹出一个tool window,效果和mybatis log的tool window窗口类似:


    效果图

    接下来,一步步实现以下功能:

    • 主体tool window
    • 左侧菜单栏Restart和Stop按钮
    • 左侧菜单栏idea其他类型按钮
    • 左侧菜单栏自定义按钮

    源码

    1. 主体tool window功能

    首先简单实现主体tool window的显示,先增加一个action入口,点击触发tool window弹出,注册action如下:

            <action id="com.kungyu.toolview.ConsoleViewAction" class="com.kungyu.toolview.ConsoleViewAction"
                    text="ConsoleViewAction" description="ConsoleViewAction">
                <add-to-group group-id="ToolsMenu" anchor="last"/>
            </action>
    

    接着实现ConsoleViewAction类:

    public class ConsoleViewAction extends AnAction {
        @Override
        public void actionPerformed(@NotNull AnActionEvent e) {
            CustomExecutor executor = new CustomExecutor(e.getProject());
            executor.run();
        }
    }
    

    可以看到,仅仅是在actionPerformed方法中创建了一个CustomExecutor对象,并调用了run方法。CustomExecutor是我们自定义的执行器,run方法被调用之后,会构建一个tool window并展示,具体代码如下:

    public class CustomExecutor implements Disposable {
    
        private ConsoleView consoleView = null;
    
        private Project project = null;
    
        public CustomExecutor(@NotNull Project project) {
            this.project = project;
            this.consoleView = createConsoleView(project);
        }
    
        private ConsoleView createConsoleView(Project project) {
            TextConsoleBuilder consoleBuilder = TextConsoleBuilderFactory.getInstance().createBuilder(project);
            ConsoleView console = consoleBuilder.getConsole();
            return console;
        }
    
        @Override
        public void dispose() {
            Disposer.dispose(this);
        }
    
        public void run() {
            if (project.isDisposed()) {
                return;
            }
    
            Executor executor = CustomRunExecutor.getRunExecutorInstance();
            if (executor == null) {
                return;
            }
    
            final RunnerLayoutUi.Factory factory = RunnerLayoutUi.Factory.getInstance(project);
            RunnerLayoutUi layoutUi = factory.create("runnerId", "runnerTitle", "sessionName", project);
            final JPanel consolePanel = createConsolePanel(consoleView);
    
            RunContentDescriptor descriptor = new RunContentDescriptor(new RunProfile() {
                @Nullable
                @Override
                public RunProfileState getState(@NotNull 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", consolePanel, "displayName", AllIcons.Debugger.Console, consolePanel);
            content.setCloseable(false);
            layoutUi.addContent(content);
    
            Disposer.register(descriptor,this);
    
            Disposer.register(content, consoleView);
    
            ExecutionManager.getInstance(project).getContentManager().showRunContent(executor, descriptor);
        }
    }
    

    CustomRunExecutor类继承了Executor,主要是定义了tool window的相关静态信息,代码如下:

    public class CustomRunExecutor extends Executor {
    
        public static final String TOOL_WINDOW_ID = "tool window plugin";
    
        @Override
        public String getToolWindowId() {
            return TOOL_WINDOW_ID;
        }
    
        @Override
        public Icon getToolWindowIcon() {
            return IconUtil.ICON;
        }
    
        @NotNull
        @Override
        public Icon getIcon() {
            return IconUtil.ICON;
        }
    
        @Override
        public Icon getDisabledIcon() {
            return IconUtil.ICON;
        }
    
        @Override
        public String getDescription() {
            return TOOL_WINDOW_ID;
        }
    
        @NotNull
        @Override
        public String getActionName() {
            return TOOL_WINDOW_ID;
        }
    
        @NotNull
        @Override
        public String getId() {
            return StringConst.PLUGIN_ID;
        }
    
        @NotNull
        @Override
        public String getStartActionText() {
            return TOOL_WINDOW_ID;
        }
    
        @Override
        public String getContextActionId() {
            return "custom context action id";
        }
    
        @Override
        public String getHelpId() {
            return TOOL_WINDOW_ID;
        }
    
        public static Executor getRunExecutorInstance() {
            return ExecutorRegistry.getInstance().getExecutorById(StringConst.PLUGIN_ID);
        }
    }
    

    之后注册executor扩展:

        <extensions defaultExtensionNs="com.intellij">
            <executor implementation="com.kungyu.toolview.CustomRunExecutor" id="CustomRunExecutor"/>
        </extensions>
    

    运行效果如下:


    入口 主体tool window

    2. 新增Restart和Stop按钮

    首先run方法中增加如下代码,代表添加左边工具条:

    layoutUi.getOptions().setLeftToolbar(createActionToolbar(consolePanel, consoleView, layoutUi, descriptor, executor), "RunnerToolbar");
    

    createActionToolbar方法定义如下:

        private ActionGroup createActionToolbar(JPanel consolePanel, ConsoleView consoleView, RunnerLayoutUi layoutUi, RunContentDescriptor descriptor, Executor executor) {
            final DefaultActionGroup actionGroup = new DefaultActionGroup();
            actionGroup.add(new RerunAction(consolePanel, consoleView));
            actionGroup.add(new StopAction());
            return actionGroup;
        }
    

    RetunrnAction定义如下:

        private class RerunAction extends AnAction implements DumbAware {
            private final ConsoleView consoleView;
    
            public RerunAction(JComponent consolePanel, ConsoleView consoleView) {
                super("Rerun", "Rerun", AllIcons.Actions.Restart);
                this.consoleView = consoleView;
                registerCustomShortcutSet(CommonShortcuts.getRerun(), consolePanel);
            }
    
            @Override
            public void actionPerformed(AnActionEvent e) {
                Disposer.dispose(consoleView);
                rerunAction.run();
            }
    
            @Override
            public void update(AnActionEvent e) {
                e.getPresentation().setVisible(rerunAction != null);
                e.getPresentation().setIcon(AllIcons.Actions.Restart);
            }
        }
    

    StopAction定义如下:

        private class StopAction extends AnAction implements DumbAware {
            public StopAction() {
                super("Stop", "Stop", AllIcons.Actions.Suspend);
            }
    
            @Override
            public void actionPerformed(AnActionEvent e) {
                stopAction.run();
            }
    
            @Override
            public void update(AnActionEvent e) {
                e.getPresentation().setVisible(stopAction != null);
                e.getPresentation().setEnabled(stopEnabled != null && stopEnabled.compute());
            }
        }
    

    可以看到,在update方法中,通过rerunAction != nullstopAction != null来控制其可见性,通过stopEnabled.compute()来声明stop按钮是否可用,故在CustomExecutor新增全局变量:

        private Runnable rerunAction;
        private Runnable stopAction;
    
        private Computable<Boolean> stopEnabled;
    

    同时,修改ConsoleViewAction,令其在初始化CustomExecutor的时候设置好returnActionstopActionstopEnabled

    public class ConsoleViewAction extends AnAction {
        @Override
        public void actionPerformed(@NotNull AnActionEvent e) {
            runExecutor(e.getProject());
    
        }
    
        public void runExecutor(Project project) {
            if (project == null) {
                return;
            }
            CustomExecutor executor = new CustomExecutor(project);
            // 设置restart和stop
            executor.withReturn(() -> runExecutor(project)).withStop(() -> ConfigUtil.setRunning(project,false), () ->
                ConfigUtil.getRunning(project));
            executor.run();
        }
    }
    

    其中,ConfigUtil定义如下,主要用来持久化数据:

    public class ConfigUtil {
    
        public static void setRunning(Project project, boolean value) {
            PropertiesComponent.getInstance(project).setValue(StringConst.RUNNING_KEY, value);
        }
    
        public static boolean getRunning(Project project){
            return PropertiesComponent.getInstance(project).getBoolean(StringConst.RUNNING_KEY);
        }
    }
    

    运行效果如下:


    return和stop

    3. idea其他类型按钮

    可以通过在第二点中的createActionToolbar中增加其他类型的idea按钮,代码如下:

        private ActionGroup createActionToolbar(JPanel consolePanel, ConsoleView consoleView, RunnerLayoutUi layoutUi, RunContentDescriptor descriptor, Executor executor) {
            final DefaultActionGroup actionGroup = new DefaultActionGroup();
            actionGroup.add(new RerunAction(consolePanel, consoleView));
            actionGroup.add(new StopAction());
            actionGroup.add(consoleView.createConsoleActions()[2]);
            actionGroup.add(consoleView.createConsoleActions()[3]);
            actionGroup.add(consoleView.createConsoleActions()[5]);
            return actionGroup;
        }
    

    效果如下:


    idea其他类型按钮

    通过debug可以发现,createConsoleActions返回下面这样一个数组:

    createConsoleActions
    用了idea这么久,具体是哪些按钮我想都很清楚了,其实就是文章一开始效果图里面左侧第二栏那6个按钮 效果图

    4. 自定义按钮

    同理,也是修改createActionToolbar

        private ActionGroup createActionToolbar(JPanel consolePanel, ConsoleView consoleView, RunnerLayoutUi layoutUi, RunContentDescriptor descriptor, Executor executor) {
            final DefaultActionGroup actionGroup = new DefaultActionGroup();
            actionGroup.add(new RerunAction(consolePanel, consoleView));
            actionGroup.add(new StopAction());
            actionGroup.add(consoleView.createConsoleActions()[2]);
            actionGroup.add(consoleView.createConsoleActions()[3]);
            actionGroup.add(consoleView.createConsoleActions()[5]);
            actionGroup.add(new CustomAction("custom action", "custom action", IconUtil.ICON));
            return actionGroup;
        }
    

    效果如下:


    自定义按钮

    总结

    总体而言,这节内容还是比较简单的,最麻烦的莫过于需要去阅读其他插件的代码,效率偏低且感觉不系统,很好奇官方文档写得这么差,为何idea生态貌似挺好的(一堆插件)?

    相关文章

      网友评论

          本文标题:(二十四)IntelliJ 插件开发——Idea下方工具窗口

          本文链接:https://www.haomeiwen.com/subject/ecpzcktx.html