1
0
mirror of https://github.com/chylex/IntelliJ-AceJump.git synced 2025-09-16 16:24:48 +02:00

1 Commits

Author SHA1 Message Date
20ab421389 Broken 2024-09-03 00:49:17 +02:00
11 changed files with 95 additions and 153 deletions

View File

@@ -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()
} }
} }

View File

@@ -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))

View File

@@ -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

View File

@@ -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,

View File

@@ -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

View File

@@ -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() }
)
}

View File

@@ -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()) },
settings.layout.priority(String::last)
)
for (c2 in allowedChars) { allPossibleTagsLowercase = allSuffixChars
if (c1 != c2) { .flatMap { suffix -> allPrefixChars.map { prefix -> "$prefix$suffix" } }
allowedTags.add("$c1$c2") .sortedWith(tagOrder)
}
}
}
allowedTagsSorted = allowedTags.sortedBy { rankPriority(settings.layout, it) }
} }
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()
} }
} }

View File

@@ -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 {

View File

@@ -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)
} }

View File

@@ -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))
} }
} }

View File

@@ -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)
} }
} }