in src/main/java/com/maddyhome/idea/vim/listener/IdeaSpecifics.kt [75:151]
override fun beforeActionPerformed(action: AnAction, event: AnActionEvent) {
if (VimPlugin.isNotEnabled()) return
val hostEditor = event.dataContext.getData(CommonDataKeys.HOST_EDITOR)
if (hostEditor != null) {
editor = hostEditor
}
val isVimAction = (action as? AnActionWrapper)?.delegate is VimShortcutKeyAction
if (!isVimAction && injector.vimState.mode == Mode.INSERT && action !is EnterAction) {
val undoService = injector.undo as VimTimestampBasedUndoService
val nanoTime = System.nanoTime()
editor?.vim?.nativeCarets()?.forEach { undoService.endInsertSequence(it, it.offset, nanoTime) }
}
if (!isVimAction && injector.globalIjOptions().trackactionids) {
if (action !is NotificationService.ActionIdNotifier.CopyActionId && action !is NotificationService.ActionIdNotifier.StopTracking) {
val id: String? =
ActionManager.getInstance().getId(action) ?: (action.shortcutSet as? ProxyShortcutSet)?.actionId
val candidates = if (id == null) {
// Some actions are specific to the component they're registered for, and are copies of a global action,
// reusing the action ID and shortcuts (e.g. `NextTab` is different for editor tabs and tool window tabs).
// Unfortunately, ActionManager doesn't know about these "local" actions, so can't return the action ID.
// However, the new "local" action does copy the shortcuts of the global template action, so we can look up
// all actions with matching shortcuts. We might return more action IDs than expected, so this is a list of
// candidates, not a definite match of the action being executed, but the list should include our target
// action. Note that we might return duplicate IDs because the keymap might have multiple shortcuts mapped
// to the same action. The notifier will handle de-duplication and sorting as a presentation detail.
action.shortcutSet.shortcuts.flatMap { KeymapManager.getInstance().activeKeymap.getActionIdList(it) }
} else {
emptyList()
}
val intentionName = if (action is ApplyIntentionAction) {
action.name
}
else null
// We can still get empty ID and empty candidates. Notably, for the tool window toggle buttons on the new UI.
// We could filter out action events with `place == ActionPlaces.TOOLWINDOW_TOOLBAR_BAR`
VimPlugin.getNotifications(event.dataContext.getData(CommonDataKeys.PROJECT)).notifyActionId(id, candidates, intentionName)
}
}
if (hostEditor != null && action is ChooseItemAction && injector.registerGroup.isRecording) {
val lookup = LookupManager.getActiveLookup(hostEditor)
val lookupItem = lookup?.currentItem
if (lookup is LookupImpl && lookupItem != null) {
val caretOffset = hostEditor.caretModel.primaryCaret.offset
val completionPrefixLength = lookup.itemMatcher(lookupItem).prefix.length + lookup.additionalPrefix.length
val completionStartOffset = caretOffset - completionPrefixLength
val documentLength = hostEditor.document.textLength
val charsToRemove = caretOffset - completionStartOffset
val register = VimPlugin.getRegister()
if (charsToRemove > 0) {
val backSpaceKey = KeyStroke.getKeyStroke(KeyEvent.VK_BACK_SPACE, 0)
repeat(charsToRemove) {
register.recordKeyStroke(backSpaceKey)
}
}
val completionStartMarker = hostEditor.document.createRangeMarker(
completionStartOffset,
completionStartOffset
).apply {
isGreedyToLeft = true
}
completionData = CompletionData(
completionStartMarker,
completionStartOffset,
caretOffset,
documentLength - charsToRemove
)
}
}
}