(二十四)IntelliJ 插件開(kāi)發(fā)——Idea下方工具窗口

Github

https://github.com/kungyutucheng/my_gradle_plugin

運(yùn)行環(huán)境

macOS 10.14.5
IntelliJ idea 2019.2.4

參考

sonarlint-intellij
mybatis-log-plugin

前言

最近在編寫(xiě)自己的一個(gè)idea插件,其中有個(gè)功能需要在idea下方彈出一個(gè)tool window,效果和mybatis log的tool window窗口類(lèi)似:


效果圖

接下來(lái),一步步實(shí)現(xiàn)以下功能:

  • 主體tool window
  • 左側(cè)菜單欄Restart和Stop按鈕
  • 左側(cè)菜單欄idea其他類(lèi)型按鈕
  • 左側(cè)菜單欄自定義按鈕

源碼

1. 主體tool window功能

首先簡(jiǎn)單實(shí)現(xiàn)主體tool window的顯示,先增加一個(gè)action入口,點(diǎn)擊觸發(fā)tool window彈出,注冊(cè)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>

接著實(shí)現(xiàn)ConsoleViewAction類(lèi):

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

可以看到,僅僅是在actionPerformed方法中創(chuàng)建了一個(gè)CustomExecutor對(duì)象,并調(diào)用了run方法。CustomExecutor是我們自定義的執(zhí)行器,run方法被調(diào)用之后,會(huì)構(gòu)建一個(gè)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類(lèi)繼承了Executor,主要是定義了tool window的相關(guān)靜態(tài)信息,代碼如下:

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);
    }
}

之后注冊(cè)executor擴(kuò)展:

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

運(yùn)行效果如下:


入口
主體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方法中,通過(guò)rerunAction != nullstopAction != null來(lái)控制其可見(jiàn)性,通過(guò)stopEnabled.compute()來(lái)聲明stop按鈕是否可用,故在CustomExecutor新增全局變量:

    private Runnable rerunAction;
    private Runnable stopAction;

    private Computable<Boolean> stopEnabled;

同時(shí),修改ConsoleViewAction,令其在初始化CustomExecutor的時(shí)候設(shè)置好returnAction、stopActionstopEnabled

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);
        // 設(shè)置restart和stop
        executor.withReturn(() -> runExecutor(project)).withStop(() -> ConfigUtil.setRunning(project,false), () ->
            ConfigUtil.getRunning(project));
        executor.run();
    }
}

其中,ConfigUtil定義如下,主要用來(lái)持久化數(shù)據(jù):

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);
    }
}

運(yùn)行效果如下:


return和stop

3. idea其他類(lèi)型按鈕

可以通過(guò)在第二點(diǎn)中的createActionToolbar中增加其他類(lèi)型的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其他類(lèi)型按鈕

通過(guò)debug可以發(fā)現(xiàn),createConsoleActions返回下面這樣一個(gè)數(shù)組:

createConsoleActions

用了idea這么久,具體是哪些按鈕我想都很清楚了,其實(shí)就是文章一開(kāi)始效果圖里面左側(cè)第二欄那6個(gè)按鈕

效果圖

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;
    }

效果如下:


自定義按鈕

總結(jié)

總體而言,這節(jié)內(nèi)容還是比較簡(jiǎn)單的,最麻煩的莫過(guò)于需要去閱讀其他插件的代碼,效率偏低且感覺(jué)不系統(tǒng),很好奇官方文檔寫(xiě)得這么差,為何idea生態(tài)貌似挺好的(一堆插件)?

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容