Skip to content

Commit

Permalink
Support registering commands from json
Browse files Browse the repository at this point in the history
  • Loading branch information
lippfi committed Nov 10, 2023
1 parent 37067d5 commit 876e16f
Show file tree
Hide file tree
Showing 11 changed files with 119 additions and 22 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/*
* Copyright 2003-2023 The IdeaVim authors
*
* Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE.txt file or at
* https://opensource.org/licenses/MIT.
*/

package com.intellij.vim.processors

import kotlinx.serialization.Serializable

@Serializable
data class CommandBean(val keys: String, val `class`: String, val modes: String)
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import com.google.devtools.ksp.symbol.KSClassDeclaration
import com.google.devtools.ksp.symbol.KSFile
import com.google.devtools.ksp.symbol.KSVisitorVoid
import com.intellij.vim.annotations.CommandOrMotion
import kotlinx.serialization.Serializable
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import kotlin.io.path.Path
Expand Down Expand Up @@ -55,6 +54,4 @@ class CommandOrMotionProcessor(private val environment: SymbolProcessorEnvironme
}
}

@Serializable
data class CommandBean(val keys: String, val `class`: String, val modes: String)
}
2 changes: 1 addition & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ dependencies {
api(project(":vim-engine"))

ksp(project(":annotation-processors"))
compileOnly(project(":annotation-processors"))
implementation(project(":annotation-processors"))

testApi("com.squareup.okhttp3:okhttp:4.11.0")

Expand Down
46 changes: 32 additions & 14 deletions src/main/java/com/maddyhome/idea/vim/RegisterActions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,14 @@ package com.maddyhome.idea.vim

import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.extensions.ExtensionPointName
import com.maddyhome.idea.vim.action.EngineCommandProvider
import com.maddyhome.idea.vim.action.IntellijCommandProvider
import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.handler.ActionBeanClass
import com.maddyhome.idea.vim.handler.EditorActionHandlerBase
import com.maddyhome.idea.vim.key.MappingOwner
import com.maddyhome.idea.vim.newapi.IjVimActionsInitiator
import com.maddyhome.idea.vim.newapi.globalIjOptions
import java.awt.event.KeyEvent
import javax.swing.KeyStroke

Expand All @@ -26,8 +30,10 @@ public object RegisterActions {
@JvmStatic
public fun registerActions() {
registerVimCommandActions()
registerEmptyShortcuts()
registerEpListener()
if (!injector.globalIjOptions().commandOrMotionAnnotation) {
registerEmptyShortcuts()
registerEpListener()
}
}

@Deprecated("Moving to annotations approach instead of xml")
Expand All @@ -41,10 +47,16 @@ public object RegisterActions {
}

public fun findAction(id: String): EditorActionHandlerBase? {
return VIM_ACTIONS_EP.getExtensionList(ApplicationManager.getApplication()).stream()
.filter { vimActionBean: ActionBeanClass -> vimActionBean.actionId == id }
.findFirst().map { obj: ActionBeanClass -> obj.instance }
.orElse(null)
if (injector.globalIjOptions().commandOrMotionAnnotation) {
val commandBean = EngineCommandProvider.getCommands().firstOrNull { it.actionId == id }
?: IntellijCommandProvider.getCommands().firstOrNull { it.actionId == id } ?: return null
return commandBean.instance
} else {
return VIM_ACTIONS_EP.getExtensionList(ApplicationManager.getApplication()).stream()
.filter { vimActionBean: ActionBeanClass -> vimActionBean.actionId == id }
.findFirst().map { obj: ActionBeanClass -> obj.instance }
.orElse(null)
}
}

public fun findActionOrDie(id: String): EditorActionHandlerBase {
Expand All @@ -59,18 +71,24 @@ public object RegisterActions {

private fun registerVimCommandActions() {
val parser = VimPlugin.getKey()
VIM_ACTIONS_EP.getExtensionList(ApplicationManager.getApplication()).stream().map { bean: ActionBeanClass? ->
IjVimActionsInitiator(
bean!!
)
}
.forEach { actionHolder: IjVimActionsInitiator? ->
parser.registerCommandAction(
actionHolder!!
if (injector.globalIjOptions().commandOrMotionAnnotation) {
EngineCommandProvider.getCommands().forEach { parser.registerCommandAction(it) }
IntellijCommandProvider.getCommands().forEach { parser.registerCommandAction(it) }
} else {
VIM_ACTIONS_EP.getExtensionList(ApplicationManager.getApplication()).stream().map { bean: ActionBeanClass? ->
IjVimActionsInitiator(
bean!!
)
}
.forEach { actionHolder: IjVimActionsInitiator? ->
parser.registerCommandAction(
actionHolder!!
)
}
}
}

// todo do we really need this?
private fun registerEmptyShortcuts() {
val parser = VimPlugin.getKey()

Expand Down
20 changes: 20 additions & 0 deletions src/main/java/com/maddyhome/idea/vim/group/KeyGroup.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import com.maddyhome.idea.vim.VimPlugin;
import com.maddyhome.idea.vim.action.ComplicatedKeysAction;
import com.maddyhome.idea.vim.action.VimShortcutKeyAction;
import com.maddyhome.idea.vim.action.change.LazyVimCommand;
import com.maddyhome.idea.vim.api.*;
import com.maddyhome.idea.vim.command.MappingMode;
import com.maddyhome.idea.vim.ex.ExOutputModel;
Expand Down Expand Up @@ -208,6 +209,25 @@ public void registerShortcutWithoutAction(KeyStroke keyStroke, MappingOwner owne
registerRequiredShortcut(Collections.singletonList(keyStroke), owner);
}

public void registerCommandAction(@NotNull LazyVimCommand command) {
if (ApplicationManager.getApplication().isUnitTestMode()) {
initIdentityChecker();
for (List<KeyStroke> keys : command.getKeys()) {
checkCommand(command.getModes(), command, keys);
}
}

for (List<KeyStroke> keyStrokes : command.getKeys()) {
registerRequiredShortcut(keyStrokes, MappingOwner.IdeaVim.System.INSTANCE);

for (MappingMode mappingMode : command.getModes()) {
Node<VimActionsInitiator> node = getKeyRoot(mappingMode);
NodesKt.addLeafs(node, keyStrokes, command);
}
}
}

@Deprecated
public void registerCommandAction(@NotNull VimActionsInitiator actionHolder) {
IjVimActionsInitiator holder = (IjVimActionsInitiator)actionHolder;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,13 @@ package org.jetbrains.plugins.ideavim

import com.maddyhome.idea.vim.RegisterActions.VIM_ACTIONS_EP
import com.maddyhome.idea.vim.VimPlugin
import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.command.MappingMode
import com.maddyhome.idea.vim.state.mode.Mode
import com.maddyhome.idea.vim.handler.ActionBeanClass
import com.maddyhome.idea.vim.key.CommandNode
import com.maddyhome.idea.vim.key.CommandPartNode
import com.maddyhome.idea.vim.newapi.globalIjOptions
import org.jetbrains.plugins.ideavim.impl.OptionTest
import org.jetbrains.plugins.ideavim.impl.VimOption
import org.junit.jupiter.api.Test
Expand Down Expand Up @@ -84,6 +86,7 @@ class RegisterActionsTest : VimTestCase() {
VimOption(TestOptionConstants.whichwrap, doesntAffectTest = true),
)
fun `test unregister extension`() {
if (injector.globalIjOptions().commandOrMotionAnnotation) return
val before = "I ${c}found it in a legendary land"
val after = "I f${c}ound it in a legendary land"
var motionRightAction: ActionBeanClass? = null
Expand Down
2 changes: 1 addition & 1 deletion vim-engine/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ dependencies {
compileOnly("org.jetbrains:annotations:24.0.1")

ksp(project(":annotation-processors"))
compileOnly(project(":annotation-processors"))
implementation(project(":annotation-processors"))
compileOnly("org.jetbrains.kotlinx:kotlinx-serialization-json-jvm:1.6.0")
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

package com.maddyhome.idea.vim.action

import com.intellij.vim.processors.CommandOrMotionProcessor
import com.intellij.vim.processors.CommandBean
import com.maddyhome.idea.vim.action.change.LazyVimCommand
import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.command.MappingMode
Expand All @@ -23,7 +23,7 @@ public interface CommandProvider {
@OptIn(ExperimentalSerializationApi::class)
public fun getCommands(): Collection<LazyVimCommand> {
val classLoader = this.javaClass.classLoader
val commands: List<CommandOrMotionProcessor.CommandBean> = Json.decodeFromStream(getFile())
val commands: List<CommandBean> = Json.decodeFromStream(getFile())
return commands
.groupBy { it.`class` }
.map {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

package com.maddyhome.idea.vim.action.change

import com.maddyhome.idea.vim.api.VimActionsInitiator
import com.maddyhome.idea.vim.command.MappingMode
import com.maddyhome.idea.vim.handler.EditorActionHandlerBase
import com.maddyhome.idea.vim.vimscript.model.LazyInstance
Expand All @@ -18,6 +19,10 @@ public class LazyVimCommand(
public val modes: Set<MappingMode>,
className: String,
classLoader: ClassLoader,
) : LazyInstance<EditorActionHandlerBase>(className, classLoader) {
) : LazyInstance<EditorActionHandlerBase>(className, classLoader), VimActionsInitiator {
public val actionId: String = EditorActionHandlerBase.getActionId(className)

override fun getInstance(): EditorActionHandlerBase {
return instance
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@
package com.maddyhome.idea.vim.api

import com.maddyhome.idea.vim.handler.EditorActionHandlerBase
import org.jetbrains.annotations.ApiStatus

@Deprecated(message = "Replace it with LazyVimCommand")
@ApiStatus.ScheduledForRemoval(inVersion = "2.9.0")
public interface VimActionsInitiator {
public fun getInstance(): EditorActionHandlerBase
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

package com.maddyhome.idea.vim.api

import com.maddyhome.idea.vim.action.change.LazyVimCommand
import com.maddyhome.idea.vim.command.MappingMode
import com.maddyhome.idea.vim.extension.ExtensionHandler
import com.maddyhome.idea.vim.handler.EditorActionHandlerBase
Expand Down Expand Up @@ -65,6 +66,7 @@ public abstract class VimKeyGroupBase : VimKeyGroup {

override fun getKeyMappingLayer(mode: MappingMode): KeyMappingLayer = getKeyMapping(mode)

@Deprecated("Initialization EditorActionHandlerBase for this method breaks the point of lazy initialization")
protected fun checkCommand(
mappingModes: Set<MappingMode>,
action: EditorActionHandlerBase,
Expand All @@ -76,6 +78,13 @@ public abstract class VimKeyGroupBase : VimKeyGroup {
checkCorrectCombination(action, keys)
}

protected fun checkCommand(mappingModes: Set<MappingMode>, command: LazyVimCommand, keys: List<KeyStroke>) {
for (mappingMode in mappingModes) {
checkIdentity(mappingMode, command.actionId, keys)
}
checkCorrectCombination(command, keys)
}

private fun checkIdentity(mappingMode: MappingMode, actName: String, keys: List<KeyStroke>) {
val keySets = identityChecker!!.getOrPut(mappingMode) { HashSet() }
if (keys in keySets) {
Expand All @@ -84,6 +93,7 @@ public abstract class VimKeyGroupBase : VimKeyGroup {
keySets.add(keys.toMutableList())
}

@Deprecated("Initialization EditorActionHandlerBase for this method breaks the point of lazy initialization")
private fun checkCorrectCombination(action: EditorActionHandlerBase, keys: List<KeyStroke>) {
for (entry in prefixes!!.entries) {
val prefix = entry.key
Expand Down Expand Up @@ -111,6 +121,33 @@ public abstract class VimKeyGroupBase : VimKeyGroup {
prefixes!![keys.toMutableList()] = action.id
}

private fun checkCorrectCombination(command: LazyVimCommand, keys: List<KeyStroke>) {
for (entry in prefixes!!.entries) {
val prefix = entry.key
if (prefix.size == keys.size) continue
val shortOne = min(prefix.size, keys.size)
var i = 0
while (i < shortOne) {
if (prefix[i] != keys[i]) break
i++
}

val actionExceptions = listOf(
"VimInsertDeletePreviousWordAction",
"VimInsertAfterCursorAction",
"VimInsertBeforeCursorAction",
"VimFilterVisualLinesAction",
"VimAutoIndentMotionAction",
)
if (i == shortOne && command.actionId !in actionExceptions && entry.value !in actionExceptions) {
throw RuntimeException(
"Prefix found! $keys in command ${command.actionId} is the same as ${prefix.joinToString(", ") { it.toString() }} in ${entry.value}",
)
}
}
prefixes!![keys.toMutableList()] = command.actionId
}

override val savedShortcutConflicts: MutableMap<KeyStroke, ShortcutOwnerInfo>
get() = myShortcutConflicts

Expand Down

0 comments on commit 876e16f

Please sign in to comment.