CoroutineScope issue

示例:

ShireCoroutineScope.scope(context.project).launch {
    val suggestion = StringBuilder()

    flow?.cancelWithConsole(context.console)?.cancellable()?.collect { char ->
        suggestion.append(char)

        invokeLater {
            context.console?.print(char, ConsoleViewContentType.NORMAL_OUTPUT)
        }
    }

    postExecute.invoke(suggestion.toString(), null)
}

Data Context

1. 从 AnAction 中获取 DataContext

如果你正在实现一个继承自 AnAction 的动作类,可以通过 AnActionEvent 直接获取 DataContext。如下所示:

public class MyAction extends AnAction {
    @Override
    public void actionPerformed(AnActionEvent e) {
        DataContext dataContext = e.getDataContext();
        // 你可以从 DataContext 获取更多数据
    }
}

2. 从一个组件中获取 DataContext

如果你有一个 UI 组件,比如一个按钮,你可以使用 DataManager 来获取该组件的 DataContext。例如:

JComponent component = ...; // 你的组件
DataContext dataContext = DataManager.getInstance().getDataContext(component);

3. 从当前焦点的组件获取 DataContext

如果你需要从当前焦点的组件获取 DataContext,可以这样做:

DataContext dataContext = DataManager.getInstance().getDataContextFromFocus().getResult();

4. 使用 PlatformDataKeys

获取 DataContext 后,你可以使用 PlatformDataKeys 来提取特定的数据。例如,获取当前的 Editor

Editor editor = CommonDataKeys.EDITOR.getData(dataContext);
Project project = CommonDataKeys.PROJECT.getData(dataContext);
VirtualFile file = CommonDataKeys.VIRTUAL_FILE.getData(dataContext);

5. 存储 Event 的 DataContext

在某些情况下,你可能需要在动作执行之前存储 DataContext。你可以使用 VariableActionEventDataHolder 来存储数据。例如:

class ShireSonarLintAction : AnAction() {
    // ...
    override fun actionPerformed(e: AnActionEvent) {
        val project = e.project ?: return

        VariableActionEventDataHolder.putData(VariableActionEventDataHolder(e.dataContext))

        val config = shireActionConfigs(project).firstOrNull() ?: return
        ShireRunFileAction.executeShireFile(project, config, null)
    }
}

使用 VariableActionEventDataHolder 存储 DataContext 后,你可以在动作执行时获取数据。例如:

fun getCommitWorkflowUi(): CommitWorkflowUi? {
    VariableActionEventDataHolder.getData()?.vcsDataContext?.let {
        val commitWorkflowUi = it.getData(VcsDataKeys.COMMIT_WORKFLOW_UI)
        return commitWorkflowUi as CommitWorkflowUi?
    }

    val dataContext = DataManager.getInstance().dataContextFromFocus.result
    val commitWorkflowUi = dataContext?.getData(VcsDataKeys.COMMIT_WORKFLOW_UI)
    return commitWorkflowUi
}

自定义 ContextMenu 位置

普通方式

参考:ShireActionStartupActivity 中的实现,如:attachTerminalAction 方法

private fun attachTerminalAction() {
    val actionManager = ActionManager.getInstance()
    val toolsMenu = actionManager.getAction("TerminalToolwindowActionGroup") as? DefaultActionGroup ?: return

    val action = actionManager.getAction("ShireTerminalAction")
    if (!toolsMenu.containsAction(action)) {
        toolsMenu.add(action)
    }
}
  1. 从 ActionManager 中获取目标 ActionGroup
  2. 将自定义 Action 添加到目标 ActionGroup 中

动态方式

对于不对外提供的插件,可以尝试监听是否有对应的事件,诸如 Sonarlint 使用的是 Panel,因此可以监听 Panel 的事件,然后动态添加 Action。

private fun attachSonarLintAction(project: Project) {
    project.messageBus.connect().subscribe(ToolWindowManagerListener.TOPIC, SonarLintToolWindowListener(project));
}

对应的 SonarLintToolWindowListener 实现如下:

class SonarLintToolWindowListener(private val project: Project) : ToolWindowManagerListener {
    override fun toolWindowShown(toolWindow: ToolWindow) {
        if (toolWindow.id != "SonarLint") return

        val action = ActionManager.getInstance().getAction("ShireSonarLintAction")

        val contentManager = toolWindow.contentManager
        val content = contentManager.getContent(0) ?: return

        val simpleToolWindowPanel = content.component as? SimpleToolWindowPanel
        val actionToolbar = simpleToolWindowPanel?.toolbar?.components?.get(0) as? ActionToolbar ?: return
        val actionGroup = actionToolbar.actionGroup as? DefaultActionGroup

        if (actionGroup?.containsAction(action) == false) {
            actionGroup.add(action)
        }
    }
}