Detecting source of AnAction declare in plugin.xml

I have this in my plugin.xml:

    <action id="foo.invoke"
            class="foo.bar.MyInvokeAction"
            icon="/icons/pluginIcon.svg">
      <keyboard-shortcut first-keystroke="ctrl I" keymap="$default" replace-all="true"/>
      <add-to-group group-id="Floating.CodeToolbar" anchor="first"/>
    </action>

And this as my action:

object MyInvokeAction : EditorAction(InvokeHandler()) {
  const val ACTION_ID: String = "foo.invoke"

  class InvokeHandler : EditorWriteActionHandler() {
    override fun executeWriteAction(editor: Editor, caret: Caret?, dataContext: DataContext?) {
      val project = editor.project ?: return
      MyManager.invoke("source") // TODO: how to adapt the 'source' ?
    }
  }
}

I would like to be able to know what caused the executeWriteAction to be called.
How do I programmatically figure out if it was the button clicked from the Floating.CodeToolbar, or the Ctrl+I shortcut?

Never use object for anything registered in the platform, but always class. See Configuring Kotlin Support | IntelliJ Platform Plugin SDK

1 Like

You can query context specific data from dataContext, see com.intellij.openapi.actionSystem.CommonDataKeys and others.

Not sure it’s possible to capture these two specific contexts, why do you need to distinguish them?

I’ll fix my object usage, thanks for pointing that out.
But just being curious here: what is the repercussion of using object there? The documentation mentions there are exceptions where this is ok, so I’m slightly confused. My action has been working fine, so it’s unclear why this is a necessary change.

And regarding the DataContext, I see nothing in CommonDataKeys that would be relevant to derive the value I’m looking for.

why do you need to distinguish them?

It’s for the purpose of telemetry. I want to be determine how often people use the CodeToolbar as an entry-point, versus how often people use the shortcut. Eventually, we will have even more entry-points, and it’s important for us to know which ones are actually being used.

About object: all entities registered in plugin.xml are fully managed by the platform. Using object effectively prevents that, also Actions must be state-less.

For telemetry, you might want to try com.intellij.openapi.actionSystem.AnActionEvent#getPlace and the various AnActionEvent#isFrom....() methods.

1 Like

By the way, regarding the object, it was setup this way because of this button we create which allows users to open the KeyMap for the Action so that they can quickly edit it.

  private fun createShortcutBtn(): JButton {
    val invokeActionId = MyInvokeAction.ACTION_ID
    val btnTxt = getShortcutBtnTxt(invokeActionId)
    val btn = JButton(btnTxt, PluginIcons.SETTINGS)

    // opening the Keymap preferences with the shortcut for the invoke action selected
    btn.addActionListener {
      val editKeymapsDialog = EditKeymapsDialog(project, invokeActionId)
      if (editKeymapsDialog.configurable == null) {
        return@addActionListener
      }
      AppUIUtil.invokeOnEdt {
        val modified = editKeymapsDialog.showAndGet()
        if (modified) {
          btn.text = getShortcutBtnTxt(invokeActionId)
        }
      }
    }

    return btn
  }

It seems like having the ACTION_ID available as a “static” variable makes the most sense semantically speaking since it is indeed a property associated with the action, rather than something specific to each separate instance.

Yes, the ACTION_ID field can be exposed as static constant.

The best I could figure out is this, which seems to be working fine:

class MyInvokeAction : EditorAction(InvokeHandler()) {

  override fun update(e: AnActionEvent) {
    lastPlace = e.place
    super.update(e)
  }

  class InvokeHandler : EditorWriteActionHandler() {
    override fun executeWriteAction(editor: Editor, caret: Caret?, dataContext: DataContext?) {
      val project = editor.project ?: return
      MyManager.invoke(lastPlace)
    }
  }

  companion object {
    const val ACTION_ID: String = "foo.invoke"
    /** Used in logging to know where the action was invoked from */
    private var lastPlace: String = "unknown"
  }
}

Thanks!