mirror of
https://github.com/chylex/IntelliJ-AceJump.git
synced 2024-11-24 15:42:44 +01:00
Compare commits
6 Commits
43dfec940e
...
cb814e0999
Author | SHA1 | Date | |
---|---|---|---|
cb814e0999 | |||
1fc06e8120 | |||
841f2fd125 | |||
6c8f19e311 | |||
ce4f3b5f03 | |||
46f42c88eb |
@ -8,7 +8,7 @@ plugins {
|
|||||||
}
|
}
|
||||||
|
|
||||||
group = "org.acejump"
|
group = "org.acejump"
|
||||||
version = "chylex-23"
|
version = "chylex-25"
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
@ -18,7 +18,7 @@ intellij {
|
|||||||
version.set("2024.2")
|
version.set("2024.2")
|
||||||
updateSinceUntilBuild.set(false)
|
updateSinceUntilBuild.set(false)
|
||||||
|
|
||||||
plugins.add("IdeaVIM:chylex-40")
|
plugins.add("IdeaVIM:chylex-41")
|
||||||
plugins.add("com.intellij.classic.ui:242.20224.159")
|
plugins.add("com.intellij.classic.ui:242.20224.159")
|
||||||
|
|
||||||
pluginsRepositories {
|
pluginsRepositories {
|
||||||
|
@ -16,7 +16,7 @@ import com.maddyhome.idea.vim.group.visual.vimSetSelection
|
|||||||
import com.maddyhome.idea.vim.helper.inVisualMode
|
import com.maddyhome.idea.vim.helper.inVisualMode
|
||||||
import com.maddyhome.idea.vim.helper.vimSelectionStart
|
import com.maddyhome.idea.vim.helper.vimSelectionStart
|
||||||
import com.maddyhome.idea.vim.newapi.vim
|
import com.maddyhome.idea.vim.newapi.vim
|
||||||
import com.maddyhome.idea.vim.state.mode.Mode
|
import com.maddyhome.idea.vim.state.mode.Mode.OP_PENDING
|
||||||
import com.maddyhome.idea.vim.state.mode.SelectionType
|
import com.maddyhome.idea.vim.state.mode.SelectionType
|
||||||
import org.acejump.boundaries.StandardBoundaries.AFTER_CARET
|
import org.acejump.boundaries.StandardBoundaries.AFTER_CARET
|
||||||
import org.acejump.boundaries.StandardBoundaries.BEFORE_CARET
|
import org.acejump.boundaries.StandardBoundaries.BEFORE_CARET
|
||||||
@ -52,8 +52,8 @@ sealed class AceVimAction : DumbAwareAction() {
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
val vim = editor.vim
|
val vim = editor.vim
|
||||||
val keyHandler = KeyHandler.getInstance()
|
if (vim.mode is OP_PENDING) {
|
||||||
if (keyHandler.isOperatorPending(vim.mode, keyHandler.keyHandlerState)) {
|
val keyHandler = KeyHandler.getInstance()
|
||||||
val key = keyHandler.keyHandlerState.commandBuilder.keys.singleOrNull()?.keyChar
|
val key = keyHandler.keyHandlerState.commandBuilder.keys.singleOrNull()?.keyChar
|
||||||
|
|
||||||
keyHandler.fullReset(vim)
|
keyHandler.fullReset(vim)
|
||||||
@ -71,10 +71,10 @@ sealed class AceVimAction : DumbAwareAction() {
|
|||||||
if (action != null) {
|
if (action != null) {
|
||||||
ApplicationManager.getApplication().invokeLater {
|
ApplicationManager.getApplication().invokeLater {
|
||||||
WriteAction.run<Nothing> {
|
WriteAction.run<Nothing> {
|
||||||
keyHandler.keyHandlerState.commandBuilder.pushCommandPart(action)
|
keyHandler.keyHandlerState.commandBuilder.addAction(action)
|
||||||
|
|
||||||
val cmd = keyHandler.keyHandlerState.commandBuilder.buildCommand()
|
val cmd = keyHandler.keyHandlerState.commandBuilder.buildCommand()
|
||||||
val operatorArguments = OperatorArguments(vim.mode is Mode.OP_PENDING, cmd.rawCount, injector.vimState.mode)
|
val operatorArguments = OperatorArguments(vim.mode is OP_PENDING, cmd.rawCount, injector.vimState.mode)
|
||||||
|
|
||||||
injector.vimState.executingCommand = cmd
|
injector.vimState.executingCommand = cmd
|
||||||
injector.actionExecutor.executeVimAction(vim, action, context, operatorArguments)
|
injector.actionExecutor.executeVimAction(vim, action, context, operatorArguments)
|
||||||
|
@ -18,6 +18,11 @@ sealed class EditorOffsetCache {
|
|||||||
*/
|
*/
|
||||||
abstract fun visibleArea(editor: Editor): Pair<Point, Point>
|
abstract fun visibleArea(editor: Editor): Pair<Point, Point>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether the offset is in the visible area rectangle.
|
||||||
|
*/
|
||||||
|
abstract fun isVisible(editor: Editor, offset: Int): Boolean
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the editor offset at the provided pixel coordinate.
|
* Returns the editor offset at the provided pixel coordinate.
|
||||||
*/
|
*/
|
||||||
@ -36,6 +41,7 @@ sealed class EditorOffsetCache {
|
|||||||
|
|
||||||
private class Cache : EditorOffsetCache() {
|
private class Cache : EditorOffsetCache() {
|
||||||
private var visibleArea: Pair<Point, Point>? = null
|
private var visibleArea: Pair<Point, Point>? = null
|
||||||
|
private val lineToVisibleOffsetRange = Int2ObjectOpenHashMap<IntRange>()
|
||||||
private val pointToOffset = Object2IntOpenHashMap<Point>().apply { defaultReturnValue(-1) }
|
private val pointToOffset = Object2IntOpenHashMap<Point>().apply { defaultReturnValue(-1) }
|
||||||
private val offsetToPoint = Int2ObjectOpenHashMap<Point>()
|
private val offsetToPoint = Int2ObjectOpenHashMap<Point>()
|
||||||
|
|
||||||
@ -43,6 +49,24 @@ sealed class EditorOffsetCache {
|
|||||||
return visibleArea ?: Uncached.visibleArea(editor).also { visibleArea = it }
|
return visibleArea ?: Uncached.visibleArea(editor).also { visibleArea = it }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun isVisible(editor: Editor, offset: Int): Boolean {
|
||||||
|
val visualLine = editor.offsetToVisualLine(offset, false)
|
||||||
|
|
||||||
|
var visibleRange = lineToVisibleOffsetRange.get(visualLine)
|
||||||
|
if (visibleRange == null) {
|
||||||
|
val (topLeft, bottomRight) = visibleArea(editor)
|
||||||
|
val lineY = editor.visualLineToY(visualLine)
|
||||||
|
|
||||||
|
val firstVisibleOffset = xyToOffset(editor, Point(topLeft.x, lineY))
|
||||||
|
val lastVisibleOffset = xyToOffset(editor, Point(bottomRight.x, lineY))
|
||||||
|
|
||||||
|
visibleRange = firstVisibleOffset..lastVisibleOffset
|
||||||
|
lineToVisibleOffsetRange.put(visualLine, visibleRange)
|
||||||
|
}
|
||||||
|
|
||||||
|
return offset in visibleRange
|
||||||
|
}
|
||||||
|
|
||||||
override fun xyToOffset(editor: Editor, pos: Point): Int {
|
override fun xyToOffset(editor: Editor, pos: Point): Int {
|
||||||
val offset = pointToOffset.getInt(pos)
|
val offset = pointToOffset.getInt(pos)
|
||||||
|
|
||||||
@ -51,7 +75,6 @@ sealed class EditorOffsetCache {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return Uncached.xyToOffset(editor, pos).also {
|
return Uncached.xyToOffset(editor, pos).also {
|
||||||
@Suppress("ReplacePutWithAssignment")
|
|
||||||
pointToOffset.put(pos, it)
|
pointToOffset.put(pos, it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -64,7 +87,6 @@ sealed class EditorOffsetCache {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return Uncached.offsetToXY(editor, offset).also {
|
return Uncached.offsetToXY(editor, offset).also {
|
||||||
@Suppress("ReplacePutWithAssignment")
|
|
||||||
offsetToPoint.put(offset, it)
|
offsetToPoint.put(offset, it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -80,6 +102,15 @@ sealed class EditorOffsetCache {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun isVisible(editor: Editor, offset: Int): Boolean {
|
||||||
|
val (topLeft, bottomRight) = visibleArea(editor)
|
||||||
|
val pos = offsetToXY(editor, offset)
|
||||||
|
val x = pos.x
|
||||||
|
val y = pos.y
|
||||||
|
|
||||||
|
return x >= topLeft.x && y >= topLeft.y && x <= bottomRight.x && y <= bottomRight.y
|
||||||
|
}
|
||||||
|
|
||||||
override fun xyToOffset(editor: Editor, pos: Point): Int {
|
override fun xyToOffset(editor: Editor, pos: Point): Int {
|
||||||
return editor.logicalPositionToOffset(editor.xyToLogicalPosition(pos))
|
return editor.logicalPositionToOffset(editor.xyToLogicalPosition(pos))
|
||||||
}
|
}
|
||||||
|
@ -13,23 +13,7 @@ enum class StandardBoundaries : Boundaries {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun isOffsetInside(editor: Editor, offset: Int, cache: EditorOffsetCache): Boolean {
|
override fun isOffsetInside(editor: Editor, offset: Int, cache: EditorOffsetCache): Boolean {
|
||||||
|
return cache.isVisible(editor, offset)
|
||||||
// If we are not using a cache, calling getOffsetRange will cause additional 1-2 pixel coordinate -> offset lookups, which is a lot
|
|
||||||
// more expensive than one lookup compared against the visible area.
|
|
||||||
|
|
||||||
// However, if we are using a cache, it's likely that the topmost and bottommost positions are already cached whereas the provided
|
|
||||||
// offset isn't, so we save a lookup for every offset outside the range.
|
|
||||||
|
|
||||||
if (cache !== EditorOffsetCache.Uncached && offset !in getOffsetRange(editor, cache)) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
val (topLeft, bottomRight) = cache.visibleArea(editor)
|
|
||||||
val pos = cache.offsetToXY(editor, offset)
|
|
||||||
val x = pos.x
|
|
||||||
val y = pos.y
|
|
||||||
|
|
||||||
return x >= topLeft.x && y >= topLeft.y && x <= bottomRight.x && y <= bottomRight.y
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -10,7 +10,6 @@ import com.intellij.openapi.editor.actionSystem.TypedActionHandler
|
|||||||
* sessions' own handlers.
|
* sessions' own handlers.
|
||||||
*/
|
*/
|
||||||
internal object EditorKeyListener : TypedActionHandler {
|
internal object EditorKeyListener : TypedActionHandler {
|
||||||
private val action = TypedAction.getInstance()
|
|
||||||
private val attached = mutableMapOf<Editor, TypedActionHandler>()
|
private val attached = mutableMapOf<Editor, TypedActionHandler>()
|
||||||
private var originalHandler: TypedActionHandler? = null
|
private var originalHandler: TypedActionHandler? = null
|
||||||
|
|
||||||
@ -20,8 +19,9 @@ internal object EditorKeyListener : TypedActionHandler {
|
|||||||
|
|
||||||
fun attach(editor: Editor, callback: TypedActionHandler) {
|
fun attach(editor: Editor, callback: TypedActionHandler) {
|
||||||
if (attached.isEmpty()) {
|
if (attached.isEmpty()) {
|
||||||
originalHandler = action.rawHandler
|
val typedAction = TypedAction.getInstance()
|
||||||
action.setupRawHandler(this)
|
originalHandler = typedAction.rawHandler
|
||||||
|
typedAction.setupRawHandler(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
attached[editor] = callback
|
attached[editor] = callback
|
||||||
@ -31,7 +31,7 @@ internal object EditorKeyListener : TypedActionHandler {
|
|||||||
attached.remove(editor)
|
attached.remove(editor)
|
||||||
|
|
||||||
if (attached.isEmpty()) {
|
if (attached.isEmpty()) {
|
||||||
originalHandler?.let(action::setupRawHandler)
|
originalHandler?.let(TypedAction.getInstance()::setupRawHandler)
|
||||||
originalHandler = null
|
originalHandler = null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ package org.acejump.search
|
|||||||
import com.intellij.openapi.editor.Editor
|
import com.intellij.openapi.editor.Editor
|
||||||
import it.unimi.dsi.fastutil.ints.IntArrayList
|
import it.unimi.dsi.fastutil.ints.IntArrayList
|
||||||
import org.acejump.boundaries.Boundaries
|
import org.acejump.boundaries.Boundaries
|
||||||
|
import org.acejump.boundaries.EditorOffsetCache
|
||||||
import org.acejump.clone
|
import org.acejump.clone
|
||||||
import org.acejump.config.AceConfig
|
import org.acejump.config.AceConfig
|
||||||
import org.acejump.immutableText
|
import org.acejump.immutableText
|
||||||
@ -17,9 +18,10 @@ class SearchProcessor private constructor(query: SearchQuery, val boundaries: Bo
|
|||||||
|
|
||||||
if (regex != null) {
|
if (regex != null) {
|
||||||
for (editor in editors) {
|
for (editor in editors) {
|
||||||
|
val cache = EditorOffsetCache.new()
|
||||||
val offsets = IntArrayList()
|
val offsets = IntArrayList()
|
||||||
|
|
||||||
val offsetRange = boundaries.getOffsetRange(editor)
|
val offsetRange = boundaries.getOffsetRange(editor, cache)
|
||||||
var result = regex.find(editor.immutableText, offsetRange.first)
|
var result = regex.find(editor.immutableText, offsetRange.first)
|
||||||
|
|
||||||
while (result != null) {
|
while (result != null) {
|
||||||
@ -29,7 +31,7 @@ class SearchProcessor private constructor(query: SearchQuery, val boundaries: Bo
|
|||||||
if (highlightEnd > offsetRange.last) {
|
if (highlightEnd > offsetRange.last) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
else if (boundaries.isOffsetInside(editor, index) && !editor.foldingModel.isOffsetCollapsed(index)) {
|
else if (boundaries.isOffsetInside(editor, index, cache) && !editor.foldingModel.isOffsetCollapsed(index)) {
|
||||||
offsets.add(index)
|
offsets.add(index)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@ package org.acejump.search
|
|||||||
import com.google.common.collect.ArrayListMultimap
|
import com.google.common.collect.ArrayListMultimap
|
||||||
import com.intellij.openapi.editor.Editor
|
import com.intellij.openapi.editor.Editor
|
||||||
import it.unimi.dsi.fastutil.ints.IntList
|
import it.unimi.dsi.fastutil.ints.IntList
|
||||||
|
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap
|
||||||
import org.acejump.boundaries.EditorOffsetCache
|
import org.acejump.boundaries.EditorOffsetCache
|
||||||
import org.acejump.boundaries.StandardBoundaries.VISIBLE_ON_SCREEN
|
import org.acejump.boundaries.StandardBoundaries.VISIBLE_ON_SCREEN
|
||||||
import org.acejump.input.KeyLayoutCache
|
import org.acejump.input.KeyLayoutCache
|
||||||
@ -40,7 +41,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 = generateTags(tagSites.size).zip(tagSites).toMap()
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun type(char: Char): TaggingResult {
|
internal fun type(char: Char): TaggingResult {
|
||||||
@ -61,13 +62,15 @@ 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> {
|
private fun generateTags(tagCount: Int): List<String> {
|
||||||
|
val allowedTagsSorted = KeyLayoutCache.allowedTagsSorted
|
||||||
val tags = mutableListOf<String>()
|
val tags = mutableListOf<String>()
|
||||||
|
|
||||||
val containedSingleCharTags = mutableSetOf<Char>()
|
val containedSingleCharTags = mutableSetOf<Char>()
|
||||||
val blockedSingleCharTags = mutableSetOf<Char>()
|
val blockedSingleCharTags = mutableSetOf<Char>()
|
||||||
|
val doubleCharTagCountsByFirstChar = Object2IntOpenHashMap<Char>()
|
||||||
|
|
||||||
for (tag in KeyLayoutCache.allowedTagsSorted) {
|
for (tag in allowedTagsSorted) {
|
||||||
val firstChar = tag.first()
|
val firstChar = tag.first()
|
||||||
|
|
||||||
if (tag.length == 1) {
|
if (tag.length == 1) {
|
||||||
@ -83,15 +86,41 @@ class Tagger(private val editors: List<Editor>, results: Map<Editor, IntList>) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
blockedSingleCharTags.add(firstChar)
|
blockedSingleCharTags.add(firstChar)
|
||||||
|
doubleCharTagCountsByFirstChar.addTo(firstChar, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
tags.add(tag)
|
tags.add(tag)
|
||||||
|
|
||||||
if (tags.size >= tagSites.size) {
|
if (tags.size >= tagCount) {
|
||||||
return tags
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// In rare cases, the final tag list may contain a double character tag that is the only tag starting with its first character,
|
||||||
|
// so we replace it with the single character tag.
|
||||||
|
for (entry in doubleCharTagCountsByFirstChar.object2IntEntrySet()) {
|
||||||
|
if (entry.intValue != 1) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
tags.removeAt(tags.indexOfFirst { it.first() == entry.key })
|
||||||
|
|
||||||
|
val tag = entry.key.toString()
|
||||||
|
var previousTagIndex = -1
|
||||||
|
|
||||||
|
// The implementation of searching where to place the single character tag is theoretically slow,
|
||||||
|
// but getting here is so rare it doesn't matter.
|
||||||
|
for (i in allowedTagsSorted.indexOf(tag) - 1 downTo 0) {
|
||||||
|
previousTagIndex = tags.indexOf(allowedTagsSorted[i])
|
||||||
|
|
||||||
|
if (previousTagIndex != -1) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tags.add(previousTagIndex + 1, tag)
|
||||||
|
}
|
||||||
|
|
||||||
return tags
|
return tags
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user