Compare commits
7 Commits
c9fd077253
...
ab314d2071
Author | SHA1 | Date |
---|---|---|
chylex | ab314d2071 | |
chylex | e0bd7778df | |
chylex | f95fd49da1 | |
chylex | f6f65557e2 | |
chylex | a496b253ba | |
chylex | 16df8a3150 | |
chylex | a2b79d4f52 |
|
@ -2,6 +2,7 @@
|
|||
/.idea/inspectionProfiles
|
||||
/.idea/jarRepositories.xml
|
||||
/.idea/misc.xml
|
||||
/.idea/*.iml
|
||||
|
||||
/.gradle/
|
||||
/build/
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="GradleMigrationSettings" migrationVersion="1" />
|
||||
<component name="GradleSettings">
|
||||
<option name="linkedExternalProjectsSettings">
|
||||
<GradleProjectSettings>
|
||||
<option name="delegatedBuild" value="true" />
|
||||
<option name="testRunner" value="GRADLE" />
|
||||
<option name="distributionType" value="DEFAULT_WRAPPED" />
|
||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||
<option name="modules">
|
||||
|
@ -13,4 +16,4 @@
|
|||
</GradleProjectSettings>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
||||
</project>
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Plugin" type="GradleRunConfiguration" factoryName="Gradle">
|
||||
<ExternalSystemSettings>
|
||||
<option name="executionName" />
|
||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||
<option name="externalSystemIdString" value="GRADLE" />
|
||||
<option name="scriptParameters" value=":runIde" />
|
||||
<option name="taskDescriptions">
|
||||
<list />
|
||||
</option>
|
||||
<option name="taskNames">
|
||||
<list />
|
||||
</option>
|
||||
<option name="vmOptions" />
|
||||
</ExternalSystemSettings>
|
||||
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
|
||||
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
|
||||
<DebugAllEnabled>false</DebugAllEnabled>
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
</component>
|
|
@ -0,0 +1,26 @@
|
|||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Plugin + IdeaVIM" type="GradleRunConfiguration" factoryName="Gradle">
|
||||
<ExternalSystemSettings>
|
||||
<option name="env">
|
||||
<map>
|
||||
<entry key="IDEAVIM" value="1" />
|
||||
</map>
|
||||
</option>
|
||||
<option name="executionName" />
|
||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||
<option name="externalSystemIdString" value="GRADLE" />
|
||||
<option name="scriptParameters" value=":runIde" />
|
||||
<option name="taskDescriptions">
|
||||
<list />
|
||||
</option>
|
||||
<option name="taskNames">
|
||||
<list />
|
||||
</option>
|
||||
<option name="vmOptions" />
|
||||
</ExternalSystemSettings>
|
||||
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
|
||||
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
|
||||
<DebugAllEnabled>false</DebugAllEnabled>
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
</component>
|
|
@ -1,11 +1,11 @@
|
|||
plugins {
|
||||
kotlin("jvm") version "1.5.10"
|
||||
id("org.jetbrains.intellij") version "1.1.4"
|
||||
id("org.jetbrains.intellij") version "1.2.0"
|
||||
java
|
||||
}
|
||||
|
||||
group = "com.chylex.intellij.keyboardmaster"
|
||||
version = "0.1.3"
|
||||
version = "0.1.4"
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
|
@ -18,5 +18,9 @@ dependencies {
|
|||
}
|
||||
|
||||
intellij {
|
||||
version.set("2021.2")
|
||||
version.set("2021.2.2")
|
||||
|
||||
if (System.getenv("IDEAVIM") == "1") {
|
||||
plugins.add("IdeaVIM:0.66")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
package com.chylex.intellij.keyboardmaster
|
||||
|
||||
import com.chylex.intellij.keyboardmaster.feature.codeCompletion.CodeCompletionPopupKeyHandler
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.startup.StartupActivity
|
||||
|
||||
class PluginStartup : StartupActivity.DumbAware {
|
||||
private var isInitialized = false
|
||||
|
||||
override fun runActivity(project: Project) {
|
||||
if (!isInitialized) {
|
||||
isInitialized = true
|
||||
|
||||
val application = ApplicationManager.getApplication()
|
||||
if (application.isUnitTestMode) {
|
||||
CodeCompletionPopupKeyHandler.registerRawHandler()
|
||||
}
|
||||
else {
|
||||
application.invokeLater(CodeCompletionPopupKeyHandler.Companion::registerRawHandler)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -10,6 +10,7 @@ class PluginConfigurable : Configurable {
|
|||
|
||||
private val codeCompletionItemShortcuts = JBTextField(20)
|
||||
private val codeCompletionNextPageShortcut = JBTextField(2)
|
||||
private val codeCompletionPrevPageShortcut = JBTextField(2)
|
||||
|
||||
override fun getDisplayName(): String {
|
||||
return "Keyboard Master"
|
||||
|
@ -20,6 +21,7 @@ class PluginConfigurable : Configurable {
|
|||
titledRow("Code Completion") {
|
||||
row("Item shortcuts:") { component(codeCompletionItemShortcuts) }
|
||||
row("Next page shortcut:") { component(codeCompletionNextPageShortcut) }
|
||||
row("Prev page shortcut:") { component(codeCompletionPrevPageShortcut) }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -34,6 +36,7 @@ class PluginConfigurable : Configurable {
|
|||
PluginConfiguration.modify {
|
||||
it.codeCompletionItemShortcuts = codeCompletionItemShortcuts.text
|
||||
it.codeCompletionNextPageShortcut = codeCompletionNextPageShortcut.text.firstOrNull()?.code ?: 0
|
||||
it.codeCompletionPrevPageShortcut = codeCompletionPrevPageShortcut.text.firstOrNull()?.code ?: 0
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -41,6 +44,7 @@ class PluginConfigurable : Configurable {
|
|||
PluginConfiguration.read {
|
||||
codeCompletionItemShortcuts.text = it.codeCompletionItemShortcuts
|
||||
codeCompletionNextPageShortcut.text = it.codeCompletionNextPageShortcut.let { code -> if (code == 0) "" else code.toChar().toString() }
|
||||
codeCompletionPrevPageShortcut.text = it.codeCompletionPrevPageShortcut.let { code -> if (code == 0) "" else code.toChar().toString() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
package com.chylex.intellij.keyboardmaster.configuration
|
||||
|
||||
import com.chylex.intellij.keyboardmaster.lookup.ProjectLookupListener
|
||||
import com.chylex.intellij.keyboardmaster.feature.codeCompletion.CodeCompletionPopupConfiguration
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.components.PersistentStateComponent
|
||||
import com.intellij.openapi.components.State
|
||||
|
@ -14,6 +14,7 @@ import com.intellij.util.xmlb.XmlSerializerUtil
|
|||
class PluginConfiguration : PersistentStateComponent<PluginConfiguration> {
|
||||
var codeCompletionItemShortcuts = "123456789"
|
||||
var codeCompletionNextPageShortcut: Int = '0'.code
|
||||
var codeCompletionPrevPageShortcut: Int = 0
|
||||
|
||||
companion object {
|
||||
private val instance: PluginConfiguration
|
||||
|
@ -31,8 +32,8 @@ class PluginConfiguration : PersistentStateComponent<PluginConfiguration> {
|
|||
instance.apply(callback).apply(this::update)
|
||||
}
|
||||
|
||||
private fun update(instance: PluginConfiguration) {
|
||||
ProjectLookupListener.updateShortcuts(instance)
|
||||
private fun update(instance: PluginConfiguration) = with(instance) {
|
||||
CodeCompletionPopupConfiguration.updateShortcuts(codeCompletionItemShortcuts, codeCompletionNextPageShortcut, codeCompletionPrevPageShortcut)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
package com.chylex.intellij.keyboardmaster.feature.codeCompletion
|
||||
|
||||
import com.chylex.intellij.keyboardmaster.configuration.PluginConfiguration
|
||||
import com.intellij.util.containers.IntIntHashMap
|
||||
|
||||
object CodeCompletionPopupConfiguration {
|
||||
const val SHORTCUT_NONE = -1
|
||||
const val SHORTCUT_NEXT_PAGE = -2
|
||||
const val SHORTCUT_PREV_PAGE = -3
|
||||
|
||||
private val charToShortcutMap = IntIntHashMap(16, SHORTCUT_NONE)
|
||||
private var hintTexts = mutableListOf<String>()
|
||||
|
||||
val itemShortcutCount
|
||||
get() = hintTexts.size
|
||||
|
||||
init {
|
||||
PluginConfiguration.load()
|
||||
}
|
||||
|
||||
fun updateShortcuts(itemShortcutChars: String, nextPageShortcutCode: Int, previousPageShortcutCode: Int) {
|
||||
charToShortcutMap.clear()
|
||||
|
||||
if (nextPageShortcutCode != 0) {
|
||||
charToShortcutMap[nextPageShortcutCode] = SHORTCUT_NEXT_PAGE
|
||||
}
|
||||
|
||||
if (previousPageShortcutCode != 0) {
|
||||
charToShortcutMap[previousPageShortcutCode] = SHORTCUT_PREV_PAGE
|
||||
}
|
||||
|
||||
for ((index, char) in itemShortcutChars.withIndex()) {
|
||||
charToShortcutMap[char.code] = index
|
||||
}
|
||||
|
||||
hintTexts.clear()
|
||||
for (char in itemShortcutChars) {
|
||||
hintTexts.add(" [$char]")
|
||||
}
|
||||
}
|
||||
|
||||
fun getShortcut(char: Char): Int {
|
||||
return charToShortcutMap[char.code]
|
||||
}
|
||||
|
||||
fun getHintText(index: Int): String {
|
||||
return hintTexts[index]
|
||||
}
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
package com.chylex.intellij.keyboardmaster.feature.codeCompletion
|
||||
|
||||
import com.intellij.codeInsight.lookup.LookupFocusDegree
|
||||
import com.intellij.codeInsight.lookup.LookupManager
|
||||
import com.intellij.codeInsight.lookup.impl.LookupImpl
|
||||
import com.intellij.codeInsight.template.impl.editorActions.TypedActionHandlerBase
|
||||
import com.intellij.openapi.actionSystem.DataContext
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.editor.actionSystem.TypedAction
|
||||
import com.intellij.openapi.editor.actionSystem.TypedActionHandler
|
||||
import com.intellij.ui.ScrollingUtil
|
||||
import javax.swing.ListModel
|
||||
|
||||
/**
|
||||
* Handles configured key bindings inside a code completion popup menu.
|
||||
*/
|
||||
class CodeCompletionPopupKeyHandler private constructor(originalHandler: TypedActionHandler?) : TypedActionHandlerBase(originalHandler) {
|
||||
companion object {
|
||||
/**
|
||||
* Registers the key handler as a raw handler, because IdeaVIM steals keys from Keyboard Master when renaming an element in normal mode.
|
||||
*/
|
||||
fun registerRawHandler() {
|
||||
TypedAction.getInstance().let { it.setupRawHandler(CodeCompletionPopupKeyHandler(it.rawHandler)) }
|
||||
}
|
||||
}
|
||||
|
||||
override fun execute(editor: Editor, charTyped: Char, dataContext: DataContext) {
|
||||
if (!executeImpl(editor, charTyped)) {
|
||||
myOriginalHandler?.execute(editor, charTyped, dataContext)
|
||||
}
|
||||
}
|
||||
|
||||
private fun executeImpl(editor: Editor, charTyped: Char): Boolean {
|
||||
val shortcutItem = CodeCompletionPopupConfiguration.getShortcut(charTyped)
|
||||
if (shortcutItem == CodeCompletionPopupConfiguration.SHORTCUT_NONE) {
|
||||
return false
|
||||
}
|
||||
|
||||
val lookup = LookupManager.getActiveLookup(editor)
|
||||
if (lookup !is LookupImpl) {
|
||||
return false
|
||||
}
|
||||
|
||||
val offset = CodeCompletionPopupListener.getPageOffset(lookup)
|
||||
|
||||
if (shortcutItem == CodeCompletionPopupConfiguration.SHORTCUT_NEXT_PAGE) {
|
||||
setPageOffset(lookup) {
|
||||
val newTopIndex = offset + CodeCompletionPopupConfiguration.itemShortcutCount
|
||||
if (newTopIndex >= it.size) offset else newTopIndex
|
||||
}
|
||||
}
|
||||
else if (shortcutItem == CodeCompletionPopupConfiguration.SHORTCUT_PREV_PAGE) {
|
||||
setPageOffset(lookup) {
|
||||
val newTopIndex = offset - CodeCompletionPopupConfiguration.itemShortcutCount
|
||||
if (newTopIndex < 0) 0 else newTopIndex
|
||||
}
|
||||
}
|
||||
else {
|
||||
selectItem(lookup, offset + shortcutItem)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
private inline fun setPageOffset(lookup: LookupImpl, getNewTopIndex: (ListModel<*>) -> Int) {
|
||||
val list = lookup.list
|
||||
val newTopIndex = getNewTopIndex(list.model)
|
||||
|
||||
CodeCompletionPopupListener.setPageOffset(lookup, newTopIndex)
|
||||
lookup.selectedIndex = newTopIndex
|
||||
ScrollingUtil.ensureRangeIsVisible(list, newTopIndex, newTopIndex + CodeCompletionPopupConfiguration.itemShortcutCount - 1)
|
||||
lookup.markSelectionTouched()
|
||||
lookup.refreshUi(false, true)
|
||||
}
|
||||
|
||||
private fun selectItem(lookup: LookupImpl, index: Int) {
|
||||
if (!lookup.isFocused) {
|
||||
lookup.lookupFocusDegree = LookupFocusDegree.FOCUSED
|
||||
lookup.refreshUi(false, true)
|
||||
}
|
||||
|
||||
lookup.selectedIndex = index
|
||||
}
|
||||
}
|
|
@ -1,49 +1,20 @@
|
|||
package com.chylex.intellij.keyboardmaster.lookup
|
||||
package com.chylex.intellij.keyboardmaster.feature.codeCompletion
|
||||
|
||||
import com.chylex.intellij.keyboardmaster.configuration.PluginConfiguration
|
||||
import com.intellij.codeInsight.lookup.Lookup
|
||||
import com.intellij.codeInsight.lookup.LookupElementPresentation
|
||||
import com.intellij.codeInsight.lookup.LookupManagerListener
|
||||
import com.intellij.codeInsight.lookup.impl.LookupImpl
|
||||
import com.intellij.openapi.util.Key
|
||||
import com.intellij.util.containers.IntIntHashMap
|
||||
|
||||
/**
|
||||
* Adds hints to code completion items with the digit that selects it.
|
||||
* Adds hints to code completion popup items with the character that selects the item.
|
||||
*/
|
||||
class ProjectLookupListener : LookupManagerListener {
|
||||
class CodeCompletionPopupListener : LookupManagerListener {
|
||||
companion object {
|
||||
private val OFFSET_KEY = Key.create<Int>("chylexKeyboardMasterOffset")
|
||||
private val IS_MODIFIED_KEY = Key.create<Boolean>("chylexKeyboardMasterModified")
|
||||
|
||||
private var hintTexts = mutableListOf<String>()
|
||||
private val charToShortcutMap = IntIntHashMap(16, -1)
|
||||
|
||||
val itemShortcutCount
|
||||
get() = hintTexts.size
|
||||
|
||||
init {
|
||||
PluginConfiguration.load()
|
||||
}
|
||||
|
||||
fun updateShortcuts(configuration: PluginConfiguration) {
|
||||
hintTexts.clear()
|
||||
for (char in configuration.codeCompletionItemShortcuts) {
|
||||
hintTexts.add(" [$char]")
|
||||
}
|
||||
|
||||
charToShortcutMap.clear()
|
||||
configuration.codeCompletionNextPageShortcut.takeUnless { it == 0 }?.let { charToShortcutMap[it] = 0 }
|
||||
for ((index, char) in configuration.codeCompletionItemShortcuts.withIndex()) {
|
||||
charToShortcutMap[char.code] = index + 1
|
||||
}
|
||||
}
|
||||
|
||||
fun getShortcut(char: Char): Int {
|
||||
return charToShortcutMap[char.code]
|
||||
}
|
||||
|
||||
fun getLookupOffset(lookup: LookupImpl): Int {
|
||||
fun getPageOffset(lookup: LookupImpl): Int {
|
||||
val offset = lookup.getUserData(OFFSET_KEY)
|
||||
if (offset == null || offset >= lookup.list.model.size) {
|
||||
return 0
|
||||
|
@ -53,13 +24,13 @@ class ProjectLookupListener : LookupManagerListener {
|
|||
}
|
||||
}
|
||||
|
||||
fun setLookupOffset(lookup: LookupImpl, newOffset: Int) {
|
||||
fun setPageOffset(lookup: LookupImpl, newOffset: Int) {
|
||||
lookup.putUserData(OFFSET_KEY, newOffset)
|
||||
}
|
||||
}
|
||||
|
||||
override fun activeLookupChanged(oldLookup: Lookup?, newLookup: Lookup?) {
|
||||
if (newLookup !is LookupImpl || newLookup.getUserData(IS_MODIFIED_KEY) == true || itemShortcutCount == 0) {
|
||||
if (newLookup !is LookupImpl || newLookup.getUserData(IS_MODIFIED_KEY) == true || CodeCompletionPopupConfiguration.itemShortcutCount == 0) {
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -69,9 +40,9 @@ class ProjectLookupListener : LookupManagerListener {
|
|||
newLookup.addPresentationCustomizer { item, presentation ->
|
||||
val itemList = newLookup.list.model
|
||||
val itemCount = itemList.size
|
||||
val offset = getLookupOffset(newLookup)
|
||||
val offset = getPageOffset(newLookup)
|
||||
|
||||
for (index in hintTexts.indices) {
|
||||
for (index in 0 until CodeCompletionPopupConfiguration.itemShortcutCount) {
|
||||
val itemIndex = offset + index
|
||||
if (itemIndex >= itemCount) {
|
||||
break
|
||||
|
@ -80,7 +51,7 @@ class ProjectLookupListener : LookupManagerListener {
|
|||
if (item === itemList.getElementAt(itemIndex)) {
|
||||
val customized = LookupElementPresentation()
|
||||
customized.copyFrom(presentation)
|
||||
customized.appendTailTextItalic(hintTexts[index], true)
|
||||
customized.appendTailTextItalic(CodeCompletionPopupConfiguration.getHintText(index), true)
|
||||
return@addPresentationCustomizer customized
|
||||
}
|
||||
}
|
|
@ -1,60 +0,0 @@
|
|||
package com.chylex.intellij.keyboardmaster.lookup
|
||||
|
||||
import com.intellij.codeInsight.lookup.LookupFocusDegree
|
||||
import com.intellij.codeInsight.lookup.LookupManager
|
||||
import com.intellij.codeInsight.lookup.impl.LookupImpl
|
||||
import com.intellij.codeInsight.template.impl.editorActions.TypedActionHandlerBase
|
||||
import com.intellij.openapi.actionSystem.DataContext
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.editor.actionSystem.TypedActionHandler
|
||||
import com.intellij.ui.ScrollingUtil
|
||||
|
||||
/**
|
||||
* When typing digits 1-9 inside a code completion popup menu, selects the n-th item in the list.
|
||||
* When typing the digit 0, moves down the list by 9 items, wrapping around if needed.
|
||||
*/
|
||||
class LookupTypedActionHandler(originalHandler: TypedActionHandler?) : TypedActionHandlerBase(originalHandler) {
|
||||
override fun execute(editor: Editor, charTyped: Char, dataContext: DataContext) {
|
||||
if (!executeImpl(editor, charTyped)) {
|
||||
myOriginalHandler?.execute(editor, charTyped, dataContext)
|
||||
}
|
||||
}
|
||||
|
||||
private fun executeImpl(editor: Editor, charTyped: Char): Boolean {
|
||||
val shortcutItem = ProjectLookupListener.getShortcut(charTyped)
|
||||
if (shortcutItem == -1) {
|
||||
return false
|
||||
}
|
||||
|
||||
val lookup = LookupManager.getActiveLookup(editor)
|
||||
if (lookup !is LookupImpl) {
|
||||
return false
|
||||
}
|
||||
|
||||
val offset = ProjectLookupListener.getLookupOffset(lookup)
|
||||
|
||||
if (shortcutItem == 0) {
|
||||
val list = lookup.list
|
||||
val itemCount = list.model.size
|
||||
|
||||
val shortcutCount = ProjectLookupListener.itemShortcutCount
|
||||
val topIndex = (offset + shortcutCount).let { if (it >= itemCount) 0 else it }
|
||||
|
||||
ProjectLookupListener.setLookupOffset(lookup, topIndex)
|
||||
lookup.selectedIndex = topIndex
|
||||
ScrollingUtil.ensureRangeIsVisible(list, topIndex, topIndex + shortcutCount - 1)
|
||||
lookup.markSelectionTouched()
|
||||
lookup.refreshUi(false, true)
|
||||
}
|
||||
else {
|
||||
if (!lookup.isFocused) {
|
||||
lookup.lookupFocusDegree = LookupFocusDegree.FOCUSED
|
||||
lookup.refreshUi(false, true)
|
||||
}
|
||||
|
||||
lookup.selectedIndex = offset + shortcutItem - 1
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
|
@ -13,13 +13,12 @@
|
|||
<depends>com.intellij.modules.platform</depends>
|
||||
|
||||
<projectListeners>
|
||||
<listener class="com.chylex.intellij.keyboardmaster.lookup.ProjectLookupListener" topic="com.intellij.codeInsight.lookup.LookupManagerListener" />
|
||||
<listener class="com.chylex.intellij.keyboardmaster.feature.codeCompletion.CodeCompletionPopupListener" topic="com.intellij.codeInsight.lookup.LookupManagerListener" />
|
||||
</projectListeners>
|
||||
|
||||
<extensions defaultExtensionNs="com.intellij">
|
||||
<applicationService serviceImplementation="com.chylex.intellij.keyboardmaster.configuration.PluginConfiguration" />
|
||||
<applicationConfigurable parentId="tools" instance="com.chylex.intellij.keyboardmaster.configuration.PluginConfigurable" id="com.chylex.keyboardmaster" />
|
||||
<!--suppress PluginXmlValidity, PluginXmlDynamicPlugin -->
|
||||
<editorTypedHandler implementationClass="com.chylex.intellij.keyboardmaster.lookup.LookupTypedActionHandler" />
|
||||
<postStartupActivity implementation="com.chylex.intellij.keyboardmaster.PluginStartup" order="last" />
|
||||
</extensions>
|
||||
</idea-plugin>
|
||||
|
|
Loading…
Reference in New Issue