mirror of
https://github.com/chylex/IntelliJ-AceJump.git
synced 2025-09-16 07:24:50 +02:00
Compare commits
1 Commits
43dfec940e
...
broken-ide
Author | SHA1 | Date | |
---|---|---|---|
20ab421389
|
@@ -8,7 +8,7 @@ plugins {
|
|||||||
}
|
}
|
||||||
|
|
||||||
group = "org.acejump"
|
group = "org.acejump"
|
||||||
version = "chylex-23"
|
version = "chylex-21"
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
@@ -18,12 +18,12 @@ intellij {
|
|||||||
version.set("2024.2")
|
version.set("2024.2")
|
||||||
updateSinceUntilBuild.set(false)
|
updateSinceUntilBuild.set(false)
|
||||||
|
|
||||||
plugins.add("IdeaVIM:chylex-40")
|
// plugins.add("IdeaVIM:chylex-40")
|
||||||
plugins.add("com.intellij.classic.ui:242.20224.159")
|
// plugins.add("com.intellij.classic.ui:242.20224.159")
|
||||||
|
|
||||||
pluginsRepositories {
|
pluginsRepositories {
|
||||||
custom("https://intellij.chylex.com")
|
// custom("https://intellij.chylex.com")
|
||||||
marketplace()
|
// marketplace()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,6 +1,5 @@
|
|||||||
package org.acejump.action
|
package org.acejump.action
|
||||||
|
|
||||||
import com.intellij.openapi.actionSystem.ActionUpdateThread
|
|
||||||
import com.intellij.openapi.actionSystem.AnActionEvent
|
import com.intellij.openapi.actionSystem.AnActionEvent
|
||||||
import com.intellij.openapi.actionSystem.CommonDataKeys
|
import com.intellij.openapi.actionSystem.CommonDataKeys
|
||||||
import com.intellij.openapi.application.ApplicationManager
|
import com.intellij.openapi.application.ApplicationManager
|
||||||
@@ -168,10 +167,6 @@ sealed class AceVimAction : DumbAwareAction() {
|
|||||||
action.presentation.isEnabled = action.getData(CommonDataKeys.EDITOR) != null
|
action.presentation.isEnabled = action.getData(CommonDataKeys.EDITOR) != null
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getActionUpdateThread(): ActionUpdateThread {
|
|
||||||
return ActionUpdateThread.BGT
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun actionPerformed(e: AnActionEvent) {
|
override fun actionPerformed(e: AnActionEvent) {
|
||||||
val editor = e.getData(CommonDataKeys.EDITOR) ?: return
|
val editor = e.getData(CommonDataKeys.EDITOR) ?: return
|
||||||
val session = SessionManager.start(editor, AceVimMode.JumpAllEditors.getJumpEditors(editor))
|
val session = SessionManager.start(editor, AceVimMode.JumpAllEditors.getJumpEditors(editor))
|
||||||
|
@@ -13,6 +13,7 @@ class AceConfigurable : Configurable {
|
|||||||
|
|
||||||
override fun isModified() =
|
override fun isModified() =
|
||||||
panel.allowedChars != settings.allowedChars ||
|
panel.allowedChars != settings.allowedChars ||
|
||||||
|
panel.prefixChars != settings.prefixChars ||
|
||||||
panel.keyboardLayout != settings.layout ||
|
panel.keyboardLayout != settings.layout ||
|
||||||
panel.minQueryLengthInt != settings.minQueryLength ||
|
panel.minQueryLengthInt != settings.minQueryLength ||
|
||||||
panel.editorFadeOpacityPercent != settings.editorFadeOpacity ||
|
panel.editorFadeOpacityPercent != settings.editorFadeOpacity ||
|
||||||
@@ -23,6 +24,7 @@ class AceConfigurable : Configurable {
|
|||||||
|
|
||||||
override fun apply() {
|
override fun apply() {
|
||||||
settings.allowedChars = panel.allowedChars
|
settings.allowedChars = panel.allowedChars
|
||||||
|
settings.prefixChars = panel.prefixChars
|
||||||
settings.layout = panel.keyboardLayout
|
settings.layout = panel.keyboardLayout
|
||||||
settings.minQueryLength = panel.minQueryLengthInt ?: settings.minQueryLength
|
settings.minQueryLength = panel.minQueryLengthInt ?: settings.minQueryLength
|
||||||
settings.editorFadeOpacity = panel.editorFadeOpacityPercent
|
settings.editorFadeOpacity = panel.editorFadeOpacityPercent
|
||||||
|
@@ -8,6 +8,7 @@ import java.awt.Color
|
|||||||
data class AceSettings(
|
data class AceSettings(
|
||||||
var layout: KeyLayout = QWERTY,
|
var layout: KeyLayout = QWERTY,
|
||||||
var allowedChars: String = layout.allChars,
|
var allowedChars: String = layout.allChars,
|
||||||
|
var prefixChars: String = ";",
|
||||||
var minQueryLength: Int = 1,
|
var minQueryLength: Int = 1,
|
||||||
var editorFadeOpacity: Int = 70,
|
var editorFadeOpacity: Int = 70,
|
||||||
|
|
||||||
|
@@ -5,16 +5,16 @@ import com.intellij.ui.ColorPanel
|
|||||||
import com.intellij.ui.components.JBSlider
|
import com.intellij.ui.components.JBSlider
|
||||||
import com.intellij.ui.components.JBTextArea
|
import com.intellij.ui.components.JBTextArea
|
||||||
import com.intellij.ui.components.JBTextField
|
import com.intellij.ui.components.JBTextField
|
||||||
import com.intellij.ui.dsl.builder.COLUMNS_LARGE
|
import com.intellij.ui.layout.Cell
|
||||||
import com.intellij.ui.dsl.builder.COLUMNS_SHORT
|
import com.intellij.ui.layout.GrowPolicy.MEDIUM_TEXT
|
||||||
import com.intellij.ui.dsl.builder.columns
|
import com.intellij.ui.layout.GrowPolicy.SHORT_TEXT
|
||||||
import com.intellij.ui.dsl.builder.panel
|
import com.intellij.ui.layout.panel
|
||||||
import org.acejump.input.KeyLayout
|
import org.acejump.input.KeyLayout
|
||||||
import java.awt.Color
|
import java.awt.Color
|
||||||
import java.awt.Dimension
|
|
||||||
import java.awt.Font
|
import java.awt.Font
|
||||||
import java.util.Hashtable
|
import java.util.Hashtable
|
||||||
import javax.swing.JCheckBox
|
import javax.swing.JCheckBox
|
||||||
|
import javax.swing.JComponent
|
||||||
import javax.swing.JLabel
|
import javax.swing.JLabel
|
||||||
import javax.swing.JPanel
|
import javax.swing.JPanel
|
||||||
import javax.swing.JSlider
|
import javax.swing.JSlider
|
||||||
@@ -27,6 +27,7 @@ import kotlin.reflect.KProperty
|
|||||||
@Suppress("UsePropertyAccessSyntax")
|
@Suppress("UsePropertyAccessSyntax")
|
||||||
internal class AceSettingsPanel {
|
internal class AceSettingsPanel {
|
||||||
private val tagAllowedCharsField = JBTextField()
|
private val tagAllowedCharsField = JBTextField()
|
||||||
|
private val tagPrefixCharsField = JBTextField()
|
||||||
private val keyboardLayoutCombo = ComboBox<KeyLayout>()
|
private val keyboardLayoutCombo = ComboBox<KeyLayout>()
|
||||||
private val keyboardLayoutArea = JBTextArea().apply { isEditable = false }
|
private val keyboardLayoutArea = JBTextArea().apply { isEditable = false }
|
||||||
private val minQueryLengthField = JBTextField()
|
private val minQueryLengthField = JBTextField()
|
||||||
@@ -37,7 +38,6 @@ internal class AceSettingsPanel {
|
|||||||
paintTicks = true
|
paintTicks = true
|
||||||
minorTickSpacing = 1
|
minorTickSpacing = 1
|
||||||
majorTickSpacing = 1
|
majorTickSpacing = 1
|
||||||
minimumSize = Dimension(275, minimumSize.height)
|
|
||||||
}
|
}
|
||||||
private val jumpModeColorWheel = ColorPanel()
|
private val jumpModeColorWheel = ColorPanel()
|
||||||
private val tagForeground1ColorWheel = ColorPanel()
|
private val tagForeground1ColorWheel = ColorPanel()
|
||||||
@@ -46,40 +46,52 @@ internal class AceSettingsPanel {
|
|||||||
|
|
||||||
init {
|
init {
|
||||||
tagAllowedCharsField.apply { font = Font("monospaced", font.style, font.size) }
|
tagAllowedCharsField.apply { font = Font("monospaced", font.style, font.size) }
|
||||||
|
tagPrefixCharsField.apply { font = Font("monospaced", font.style, font.size) }
|
||||||
keyboardLayoutArea.apply { font = Font("monospaced", font.style, font.size) }
|
keyboardLayoutArea.apply { font = Font("monospaced", font.style, font.size) }
|
||||||
keyboardLayoutCombo.setupEnumItems { keyChars = it.rows.joinToString("\n") }
|
keyboardLayoutCombo.setupEnumItems { keyChars = it.rows.joinToString("\n") }
|
||||||
}
|
}
|
||||||
|
|
||||||
internal val rootPanel: JPanel = panel {
|
internal val rootPanel: JPanel = panel {
|
||||||
group("Characters and Layout") {
|
fun Cell.short(component: JComponent) = component(growPolicy = SHORT_TEXT)
|
||||||
row("Allowed characters in tags:") { cell(tagAllowedCharsField).columns(COLUMNS_LARGE) }
|
fun Cell.medium(component: JComponent) = component(growPolicy = MEDIUM_TEXT)
|
||||||
row("Keyboard layout:") { cell(keyboardLayoutCombo).columns(COLUMNS_SHORT) }
|
|
||||||
row("Keyboard design:") { cell(keyboardLayoutArea).columns(COLUMNS_SHORT) }
|
titledRow("Characters and Layout") {
|
||||||
|
row("Allowed characters in tags:") { medium(tagAllowedCharsField) }
|
||||||
|
row("Allowed prefix characters in tags:") { medium(tagPrefixCharsField) }
|
||||||
|
row("Keyboard layout:") { short(keyboardLayoutCombo) }
|
||||||
|
row("Keyboard design:") { short(keyboardLayoutArea) }
|
||||||
}
|
}
|
||||||
|
|
||||||
group("Behavior") {
|
titledRow("Behavior") {
|
||||||
row("Minimum typed characters (1-10):") { cell(minQueryLengthField).columns(COLUMNS_SHORT) }
|
row("Minimum typed characters (1-10):") { short(minQueryLengthField) }
|
||||||
}
|
}
|
||||||
|
|
||||||
group("Colors") {
|
titledRow("Colors") {
|
||||||
row("Caret background:") {
|
row("Caret background:") {
|
||||||
cell(jumpModeColorWheel)
|
cell {
|
||||||
|
component(jumpModeColorWheel)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
row("Tag foreground:") {
|
row("Tag foreground:") {
|
||||||
cell(tagForeground1ColorWheel)
|
cell {
|
||||||
cell(tagForeground2ColorWheel)
|
component(tagForeground1ColorWheel)
|
||||||
|
component(tagForeground2ColorWheel)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
row("Search highlight:") {
|
row("Search highlight:") {
|
||||||
cell(searchHighlightColorWheel)
|
cell {
|
||||||
|
component(searchHighlightColorWheel)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
row("Editor fade opacity (%):") {
|
row("Editor fade opacity (%):") {
|
||||||
cell(editorFadeOpacitySlider)
|
medium(editorFadeOpacitySlider)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Property-to-property delegation: https://stackoverflow.com/q/45074596/1772342
|
// Property-to-property delegation: https://stackoverflow.com/q/45074596/1772342
|
||||||
internal var allowedChars by tagAllowedCharsField
|
internal var allowedChars by tagAllowedCharsField
|
||||||
|
internal var prefixChars by tagPrefixCharsField
|
||||||
internal var keyboardLayout by keyboardLayoutCombo
|
internal var keyboardLayout by keyboardLayoutCombo
|
||||||
internal var keyChars by keyboardLayoutArea
|
internal var keyChars by keyboardLayoutArea
|
||||||
internal var minQueryLength by minQueryLengthField
|
internal var minQueryLength by minQueryLengthField
|
||||||
@@ -99,6 +111,7 @@ internal class AceSettingsPanel {
|
|||||||
|
|
||||||
fun reset(settings: AceSettings) {
|
fun reset(settings: AceSettings) {
|
||||||
allowedChars = settings.allowedChars
|
allowedChars = settings.allowedChars
|
||||||
|
prefixChars = settings.prefixChars
|
||||||
keyboardLayout = settings.layout
|
keyboardLayout = settings.layout
|
||||||
minQueryLength = settings.minQueryLength.toString()
|
minQueryLength = settings.minQueryLength.toString()
|
||||||
editorFadeOpacityPercent = settings.editorFadeOpacity
|
editorFadeOpacityPercent = settings.editorFadeOpacity
|
||||||
@@ -110,7 +123,7 @@ internal class AceSettingsPanel {
|
|||||||
|
|
||||||
// Removal pending support for https://youtrack.jetbrains.com/issue/KT-8575
|
// Removal pending support for https://youtrack.jetbrains.com/issue/KT-8575
|
||||||
|
|
||||||
private operator fun JTextComponent.getValue(a: AceSettingsPanel, p: KProperty<*>) = text.lowercase()
|
private operator fun JTextComponent.getValue(a: AceSettingsPanel, p: KProperty<*>) = text.toLowerCase()
|
||||||
private operator fun JTextComponent.setValue(a: AceSettingsPanel, p: KProperty<*>, s: String) = setText(s)
|
private operator fun JTextComponent.setValue(a: AceSettingsPanel, p: KProperty<*>, s: String) = setText(s)
|
||||||
|
|
||||||
private operator fun ColorPanel.getValue(a: AceSettingsPanel, p: KProperty<*>) = selectedColor
|
private operator fun ColorPanel.getValue(a: AceSettingsPanel, p: KProperty<*>) = selectedColor
|
||||||
|
@@ -5,18 +5,13 @@ package org.acejump.input
|
|||||||
* ergonomically difficult they are to press.
|
* ergonomically difficult they are to press.
|
||||||
*/
|
*/
|
||||||
@Suppress("unused", "SpellCheckingInspection")
|
@Suppress("unused", "SpellCheckingInspection")
|
||||||
enum class KeyLayout(
|
enum class KeyLayout(internal val rows: Array<String>, priority: String, internal val characterRemapping: Map<Char, Char> = emptyMap()) {
|
||||||
internal val rows: Array<String>,
|
|
||||||
priority: String,
|
|
||||||
private val characterSides: Pair<Set<Char>, Set<Char>> = Pair(emptySet(), emptySet()),
|
|
||||||
internal val characterRemapping: Map<Char, Char> = emptyMap(),
|
|
||||||
) {
|
|
||||||
COLEMK(arrayOf("1234567890", "qwfpgjluy", "arstdhneio", "zxcvbkm"), priority = "tndhseriaovkcmbxzgjplfuwyq5849673210"),
|
COLEMK(arrayOf("1234567890", "qwfpgjluy", "arstdhneio", "zxcvbkm"), priority = "tndhseriaovkcmbxzgjplfuwyq5849673210"),
|
||||||
WORKMN(arrayOf("1234567890", "qdrwbjfup", "ashtgyneoi", "zxmcvkl"), priority = "tnhegysoaiclvkmxzwfrubjdpq5849673210"),
|
WORKMN(arrayOf("1234567890", "qdrwbjfup", "ashtgyneoi", "zxmcvkl"), priority = "tnhegysoaiclvkmxzwfrubjdpq5849673210"),
|
||||||
DVORAK(arrayOf("1234567890", "pyfgcrl", "aoeuidhtns", "qjkxbmwvz"), priority = "uhetidonasxkbjmqwvzgfycprl5849673210"),
|
DVORAK(arrayOf("1234567890", "pyfgcrl", "aoeuidhtns", "qjkxbmwvz"), priority = "uhetidonasxkbjmqwvzgfycprl5849673210"),
|
||||||
QWERTY(arrayOf("1234567890", "qwertyuiop", "asdfghjkl", "zxcvbnm"), priority = "fjghdkslavncmbxzrutyeiwoqp5849673210", characterSides = sides(listOf("123456", "qwert", "asdfg", "zxcvb"), listOf("7890", "yuiop", "hjkl", "nm"))),
|
QWERTY(arrayOf("1234567890", "qwertyuiop", "asdfghjkl", "zxcvbnm"), priority = "fjghdkslavncmbxzrutyeiwoqp5849673210"),
|
||||||
QWERTZ(arrayOf("1234567890", "qwertzuiop", "asdfghjkl", "yxcvbnm"), priority = "fjghdkslavncmbxyrutzeiwoqp5849673210", characterSides = sides(listOf("123456", "qwert", "asdfg", "yxcvb"), listOf("7890", "zuiop", "hjkl", "nm"))),
|
QWERTZ(arrayOf("1234567890", "qwertzuiop", "asdfghjkl", "yxcvbnm"), priority = "fjghdkslavncmbxyrutzeiwoqp5849673210"),
|
||||||
QWERTZ_CZ(arrayOf("1234567890", "qwertzuiop", "asdfghjkl", "yxcvbnm"), priority = "fjghdkslavncmbxyrutzeiwoqp5849673210", characterSides = sides(listOf("123456", "qwert", "asdfg", "yxcvb"), listOf("7890", "zuiop", "hjkl", "nm")), characterRemapping = mapOf(
|
QWERTZ_CZ(arrayOf("1234567890", "qwertzuiop", "asdfghjkl", "yxcvbnm"), priority = "fjghdkslavncmbxyrutzeiwoqp5849673210", characterRemapping = mapOf(
|
||||||
'+' to '1',
|
'+' to '1',
|
||||||
'ě' to '2',
|
'ě' to '2',
|
||||||
'š' to '3',
|
'š' to '3',
|
||||||
@@ -33,20 +28,9 @@ enum class KeyLayout(
|
|||||||
NORMAN(arrayOf("1234567890", "qwdfkjurl", "asetgynioh", "zxcvbpm"), priority = "tneigysoahbvpcmxzjkufrdlwq5849673210");
|
NORMAN(arrayOf("1234567890", "qwdfkjurl", "asetgynioh", "zxcvbpm"), priority = "tneigysoahbvpcmxzjkufrdlwq5849673210");
|
||||||
|
|
||||||
internal val allChars = rows.joinToString("").toCharArray().apply(CharArray::sort).joinToString("")
|
internal val allChars = rows.joinToString("").toCharArray().apply(CharArray::sort).joinToString("")
|
||||||
private val allPriorities = priority.mapIndexed { index, char -> char to index }.toMap()
|
internal val allPriorities = priority.mapIndexed { index, char -> char to index }.toMap()
|
||||||
|
|
||||||
fun priority(char: Char): Int {
|
internal inline fun priority(crossinline tagToChar: (String) -> Char): (String) -> Int {
|
||||||
return allPriorities[char] ?: allChars.length
|
return { allPriorities.getOrDefault(tagToChar(it), Int.MAX_VALUE) }
|
||||||
}
|
|
||||||
|
|
||||||
fun areOnSameSide(c1: Char, c2: Char): Boolean {
|
|
||||||
return (c1 in characterSides.first && c2 in characterSides.first) || (c1 in characterSides.second && c2 in characterSides.second)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun sides(left: List<String>, right: List<String>): Pair<Set<Char>, Set<Char>> {
|
|
||||||
return Pair(
|
|
||||||
left.flatMapTo(mutableSetOf()) { it.toCharArray().toSet() },
|
|
||||||
right.flatMapTo(mutableSetOf()) { it.toCharArray().toSet() }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
@@ -1,22 +1,23 @@
|
|||||||
package org.acejump.input
|
package org.acejump.input
|
||||||
|
|
||||||
import org.acejump.config.AceSettings
|
import org.acejump.config.AceSettings
|
||||||
import kotlin.math.pow
|
|
||||||
import kotlin.math.roundToInt
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stores data specific to the selected keyboard layout. We want to assign tags with easily reachable keys first, and ideally have tags
|
* Stores data specific to the selected keyboard layout. We want to assign tags with easily reachable keys first, and ideally have tags
|
||||||
* with repeated keys (ex. FF, JJ) or adjacent keys (ex. GH, UJ).
|
* with repeated keys (ex. FF, JJ) or adjacent keys (ex. GH, UJ).
|
||||||
*/
|
*/
|
||||||
internal object KeyLayoutCache {
|
internal object KeyLayoutCache {
|
||||||
lateinit var allowedTagsSorted: List<String>
|
/**
|
||||||
|
* Returns all possible two key tags, pre-sorted according to [tagOrder].
|
||||||
|
*/
|
||||||
|
lateinit var allPossibleTagsLowercase: List<String>
|
||||||
private set
|
private set
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called before any lazily initialized properties are used, to ensure that they are initialized even if the settings are missing.
|
* Called before any lazily initialized properties are used, to ensure that they are initialized even if the settings are missing.
|
||||||
*/
|
*/
|
||||||
fun ensureInitialized(settings: AceSettings) {
|
fun ensureInitialized(settings: AceSettings) {
|
||||||
if (!::allowedTagsSorted.isInitialized) {
|
if (!::allPossibleTagsLowercase.isInitialized) {
|
||||||
reset(settings)
|
reset(settings)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -25,38 +26,22 @@ internal object KeyLayoutCache {
|
|||||||
* Re-initializes cached data according to updated settings.
|
* Re-initializes cached data according to updated settings.
|
||||||
*/
|
*/
|
||||||
fun reset(settings: AceSettings) {
|
fun reset(settings: AceSettings) {
|
||||||
val allowedChars = processCharList(settings.allowedChars).ifEmpty { processCharList(settings.layout.allChars) }
|
@Suppress("ConvertLambdaToReference")
|
||||||
val allowedTags = mutableSetOf<String>()
|
val allSuffixChars = processCharList(settings.allowedChars).ifEmpty { processCharList(settings.layout.allChars).toList() }
|
||||||
|
val allPrefixChars = processCharList(settings.prefixChars).filterNot(allSuffixChars::contains).plus("")
|
||||||
|
|
||||||
for (c1 in allowedChars) {
|
val tagOrder = compareBy(
|
||||||
allowedTags.add("$c1")
|
String::length,
|
||||||
|
{ if (it.length == 1) Int.MIN_VALUE else allPrefixChars.indexOf(it.first().toString()) },
|
||||||
for (c2 in allowedChars) {
|
settings.layout.priority(String::last)
|
||||||
if (c1 != c2) {
|
)
|
||||||
allowedTags.add("$c1$c2")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
allowedTagsSorted = allowedTags.sortedBy { rankPriority(settings.layout, it) }
|
allPossibleTagsLowercase = allSuffixChars
|
||||||
|
.flatMap { suffix -> allPrefixChars.map { prefix -> "$prefix$suffix" } }
|
||||||
|
.sortedWith(tagOrder)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun processCharList(charList: String): List<Char> {
|
private fun processCharList(charList: String): Set<String> {
|
||||||
return charList.toCharArray().map(Char::lowercaseChar).distinct()
|
return charList.toCharArray().map(Char::lowercase).toSet()
|
||||||
}
|
|
||||||
|
|
||||||
private fun rankPriority(layout: KeyLayout, tag: String): Int {
|
|
||||||
val c1 = tag.first()
|
|
||||||
val p1 = (1.0 + layout.priority(c1)).pow(3)
|
|
||||||
|
|
||||||
if (tag.length == 1) {
|
|
||||||
return p1.roundToInt()
|
|
||||||
}
|
|
||||||
|
|
||||||
val c2 = tag.last()
|
|
||||||
val p2 = (1.0 + layout.priority(c2)).pow(3)
|
|
||||||
|
|
||||||
val multiplier = if (layout.areOnSameSide(c1, c2)) 2 else 1
|
|
||||||
return (((p1 * 50) + p2 + 1000) * multiplier).roundToInt()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -28,13 +28,13 @@ internal sealed class SearchQuery {
|
|||||||
* If the first character of the query is lowercase, then the entire query will be case-insensitive,
|
* If the first character of the query is lowercase, then the entire query will be case-insensitive,
|
||||||
* and only beginnings of words and camel humps will be matched.
|
* and only beginnings of words and camel humps will be matched.
|
||||||
*/
|
*/
|
||||||
class Literal(override val rawText: String) : SearchQuery() {
|
class Literal(override val rawText: String, val excludeMiddlesOfWords: Boolean) : SearchQuery() {
|
||||||
init {
|
init {
|
||||||
require(rawText.isNotEmpty())
|
require(rawText.isNotEmpty())
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun refine(char: Char): SearchQuery {
|
override fun refine(char: Char): SearchQuery {
|
||||||
return Literal(rawText + char)
|
return Literal(rawText + char, excludeMiddlesOfWords)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getHighlightLength(text: CharSequence, offset: Int): Int {
|
override fun getHighlightLength(text: CharSequence, offset: Int): Int {
|
||||||
@@ -44,14 +44,19 @@ internal sealed class SearchQuery {
|
|||||||
override fun toRegex(): Regex {
|
override fun toRegex(): Regex {
|
||||||
val firstChar = rawText.first()
|
val firstChar = rawText.first()
|
||||||
val pattern = if (firstChar.isLowerCase()) {
|
val pattern = if (firstChar.isLowerCase()) {
|
||||||
val fullPattern = Regex.escape(rawText)
|
if (excludeMiddlesOfWords) {
|
||||||
"(?i)$fullPattern"
|
val firstCharUppercasePattern = Regex.escape(firstChar.uppercaseChar().toString())
|
||||||
|
val firstCharPattern = Regex.escape(firstChar.toString())
|
||||||
|
val remainingPattern = if (rawText.length > 1) Regex.escape(rawText.drop(1)) else ""
|
||||||
|
"(?:$firstCharUppercasePattern|(?<![a-zA-Z])$firstCharPattern)$remainingPattern"
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
val fullPattern = Regex.escape(rawText)
|
||||||
|
"(?i)$fullPattern"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
val firstCharUppercasePattern = Regex.escape(firstChar.toString())
|
Regex.escape(rawText)
|
||||||
val firstCharLowercasePattern = Regex.escape(firstChar.lowercase())
|
|
||||||
val remainingPattern = if (rawText.length > 1) Regex.escape(rawText.drop(1)) else ""
|
|
||||||
"(?:$firstCharUppercasePattern|(?<![a-zA-Z])$firstCharLowercasePattern)$remainingPattern"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return Regex(pattern, setOf(RegexOption.MULTILINE))
|
return Regex(pattern, setOf(RegexOption.MULTILINE))
|
||||||
@@ -65,7 +70,7 @@ internal sealed class SearchQuery {
|
|||||||
override val rawText = ""
|
override val rawText = ""
|
||||||
|
|
||||||
override fun refine(char: Char): SearchQuery {
|
override fun refine(char: Char): SearchQuery {
|
||||||
return Literal(char.toString())
|
return Literal(char.toString(), excludeMiddlesOfWords = false)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getHighlightLength(text: CharSequence, offset: Int): Int {
|
override fun getHighlightLength(text: CharSequence, offset: Int): Int {
|
||||||
|
@@ -40,7 +40,7 @@ class Tagger(private val editors: List<Editor>, results: Map<Editor, IntList>) {
|
|||||||
.flatMap { (editor, sites) -> sites.map { site -> Tag(editor, site) } }
|
.flatMap { (editor, sites) -> sites.map { site -> Tag(editor, site) } }
|
||||||
.sortedWith(siteOrder(editors, caches))
|
.sortedWith(siteOrder(editors, caches))
|
||||||
|
|
||||||
tagMap = generateTags(tagSites).zip(tagSites).toMap()
|
tagMap = KeyLayoutCache.allPossibleTagsLowercase.zip(tagSites).toMap()
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun type(char: Char): TaggingResult {
|
internal fun type(char: Char): TaggingResult {
|
||||||
@@ -61,40 +61,6 @@ class Tagger(private val editors: List<Editor>, results: Map<Editor, IntList>) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private companion object {
|
private companion object {
|
||||||
private fun generateTags(tagSites: List<Tag>): List<String> {
|
|
||||||
val tags = mutableListOf<String>()
|
|
||||||
|
|
||||||
val containedSingleCharTags = mutableSetOf<Char>()
|
|
||||||
val blockedSingleCharTags = mutableSetOf<Char>()
|
|
||||||
|
|
||||||
for (tag in KeyLayoutCache.allowedTagsSorted) {
|
|
||||||
val firstChar = tag.first()
|
|
||||||
|
|
||||||
if (tag.length == 1) {
|
|
||||||
if (firstChar in blockedSingleCharTags) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
containedSingleCharTags.add(firstChar)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (containedSingleCharTags.remove(firstChar)) {
|
|
||||||
tags.remove(firstChar.toString())
|
|
||||||
}
|
|
||||||
|
|
||||||
blockedSingleCharTags.add(firstChar)
|
|
||||||
}
|
|
||||||
|
|
||||||
tags.add(tag)
|
|
||||||
|
|
||||||
if (tags.size >= tagSites.size) {
|
|
||||||
return tags
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return tags
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun sortResults(results: Map<Editor, IntList>, caches: Map<Editor, EditorOffsetCache>) {
|
private fun sortResults(results: Map<Editor, IntList>, caches: Map<Editor, EditorOffsetCache>) {
|
||||||
for ((editor, offsets) in results) {
|
for ((editor, offsets) in results) {
|
||||||
val cache = caches.getValue(editor)
|
val cache = caches.getValue(editor)
|
||||||
@@ -133,11 +99,12 @@ class Tagger(private val editors: List<Editor>, results: Map<Editor, IntList>) {
|
|||||||
return@Comparator if (aIsVisible) -1 else 1
|
return@Comparator if (aIsVisible) -1 else 1
|
||||||
}
|
}
|
||||||
|
|
||||||
val firstEditor = editorPriority[0]
|
val aPosition = aCaches.offsetToXY(aEditor, a.offset)
|
||||||
val caretPosition = caches.getValue(firstEditor).offsetToXY(firstEditor, firstEditor.caretModel.offset)
|
val bPosition = bCaches.offsetToXY(bEditor, b.offset)
|
||||||
|
|
||||||
val aDistance = aCaches.offsetToXY(aEditor, a.offset).distanceSq(caretPosition)
|
val caretPosition = editorPriority[0].offsetToXY(editorPriority[0].caretModel.offset)
|
||||||
val bDistance = bCaches.offsetToXY(bEditor, b.offset).distanceSq(caretPosition)
|
val aDistance = aPosition.distanceSq(caretPosition)
|
||||||
|
val bDistance = bPosition.distanceSq(caretPosition)
|
||||||
|
|
||||||
return@Comparator aDistance.compareTo(bDistance)
|
return@Comparator aDistance.compareTo(bDistance)
|
||||||
}
|
}
|
||||||
|
@@ -2,7 +2,6 @@ package org.acejump.session
|
|||||||
|
|
||||||
import com.intellij.openapi.editor.Editor
|
import com.intellij.openapi.editor.Editor
|
||||||
import org.acejump.boundaries.Boundaries
|
import org.acejump.boundaries.Boundaries
|
||||||
import org.acejump.boundaries.StandardBoundaries
|
|
||||||
import org.acejump.config.AceConfig
|
import org.acejump.config.AceConfig
|
||||||
import org.acejump.search.SearchProcessor
|
import org.acejump.search.SearchProcessor
|
||||||
import org.acejump.search.SearchQuery
|
import org.acejump.search.SearchQuery
|
||||||
@@ -18,7 +17,7 @@ sealed interface SessionState {
|
|||||||
private val defaultBoundary: Boundaries,
|
private val defaultBoundary: Boundaries,
|
||||||
) : SessionState {
|
) : SessionState {
|
||||||
override fun type(char: Char): TypeResult {
|
override fun type(char: Char): TypeResult {
|
||||||
val searchProcessor = SearchProcessor(jumpEditors, SearchQuery.Literal(char.toString()), defaultBoundary)
|
val searchProcessor = SearchProcessor(jumpEditors, SearchQuery.Literal(char.toString(), excludeMiddlesOfWords = true), defaultBoundary)
|
||||||
|
|
||||||
return if (searchProcessor.isQueryFinished) {
|
return if (searchProcessor.isQueryFinished) {
|
||||||
TypeResult.ChangeState(SelectTag(actions, jumpEditors, searchProcessor))
|
TypeResult.ChangeState(SelectTag(actions, jumpEditors, searchProcessor))
|
||||||
@@ -64,15 +63,9 @@ sealed interface SessionState {
|
|||||||
override fun type(char: Char): TypeResult {
|
override fun type(char: Char): TypeResult {
|
||||||
if (char == ' ') {
|
if (char == ' ') {
|
||||||
val query = searchProcessor.query
|
val query = searchProcessor.query
|
||||||
if (query is SearchQuery.Literal) {
|
if (query is SearchQuery.Literal && query.excludeMiddlesOfWords) {
|
||||||
val newBoundaries = when (searchProcessor.boundaries) {
|
val newQuery = SearchQuery.Literal(query.rawText, excludeMiddlesOfWords = false)
|
||||||
StandardBoundaries.VISIBLE_ON_SCREEN -> StandardBoundaries.AFTER_CARET
|
val newSearchProcessor = SearchProcessor(jumpEditors, newQuery, searchProcessor.boundaries)
|
||||||
StandardBoundaries.AFTER_CARET -> StandardBoundaries.BEFORE_CARET
|
|
||||||
StandardBoundaries.BEFORE_CARET -> StandardBoundaries.VISIBLE_ON_SCREEN
|
|
||||||
else -> searchProcessor.boundaries
|
|
||||||
}
|
|
||||||
|
|
||||||
val newSearchProcessor = SearchProcessor(jumpEditors, query, newBoundaries)
|
|
||||||
return TypeResult.ChangeState(SelectTag(actions, jumpEditors, newSearchProcessor))
|
return TypeResult.ChangeState(SelectTag(actions, jumpEditors, newSearchProcessor))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -12,13 +12,10 @@ import java.awt.Rectangle
|
|||||||
* Describes a 1 or 2 character shortcut that points to a specific character in the editor.
|
* Describes a 1 or 2 character shortcut that points to a specific character in the editor.
|
||||||
*/
|
*/
|
||||||
internal class TagMarker(
|
internal class TagMarker(
|
||||||
private val firstChar: String,
|
private val tag: CharArray,
|
||||||
private val secondChar: String,
|
|
||||||
val offset: Int
|
val offset: Int
|
||||||
) {
|
) {
|
||||||
private constructor(tag: String, offset: Int) : this(tag.first().toString(), tag.drop(1), offset)
|
private val length = tag.size
|
||||||
|
|
||||||
private val length = firstChar.length + secondChar.length
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
/**
|
/**
|
||||||
@@ -31,7 +28,7 @@ internal class TagMarker(
|
|||||||
* character ([typedTag]) matches the first [tag] character, only the second [tag] character is displayed.
|
* character ([typedTag]) matches the first [tag] character, only the second [tag] character is displayed.
|
||||||
*/
|
*/
|
||||||
fun create(tag: String, offset: Int, typedTag: String): TagMarker {
|
fun create(tag: String, offset: Int, typedTag: String): TagMarker {
|
||||||
return TagMarker(tag.drop(typedTag.length), offset)
|
return TagMarker(tag.drop(typedTag.length).toCharArray(), offset)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,11 +65,11 @@ internal class TagMarker(
|
|||||||
|
|
||||||
g.font = font.tagFont
|
g.font = font.tagFont
|
||||||
g.color = font.foregroundColor1
|
g.color = font.foregroundColor1
|
||||||
g.drawString(firstChar, x, y)
|
g.drawChars(tag, 0, 1, x, y)
|
||||||
|
|
||||||
if (secondChar.isNotEmpty()) {
|
if (tag.size > 1) {
|
||||||
g.color = font.foregroundColor2
|
g.color = font.foregroundColor2
|
||||||
g.drawString(secondChar, x + font.tagCharWidth, y)
|
g.drawChars(tag, 1, length - 1, x + font.tagCharWidth, y)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user