1
0
mirror of https://github.com/chylex/IntelliJ-AceJump.git synced 2024-10-17 06:42:47 +02:00

Compare commits

..

No commits in common. "cb814e099917fab86ad5d182a34fb47a997cbda3" and "43dfec940e0658195c549130012b4413b6a6c465" have entirely different histories.

7 changed files with 37 additions and 83 deletions

View File

@ -8,7 +8,7 @@ plugins {
} }
group = "org.acejump" group = "org.acejump"
version = "chylex-25" version = "chylex-23"
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-41") 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 {

View File

@ -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.OP_PENDING import com.maddyhome.idea.vim.state.mode.Mode
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
if (vim.mode is OP_PENDING) {
val keyHandler = KeyHandler.getInstance() val keyHandler = KeyHandler.getInstance()
if (keyHandler.isOperatorPending(vim.mode, keyHandler.keyHandlerState)) {
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.addAction(action) keyHandler.keyHandlerState.commandBuilder.pushCommandPart(action)
val cmd = keyHandler.keyHandlerState.commandBuilder.buildCommand() val cmd = keyHandler.keyHandlerState.commandBuilder.buildCommand()
val operatorArguments = OperatorArguments(vim.mode is OP_PENDING, cmd.rawCount, injector.vimState.mode) val operatorArguments = OperatorArguments(vim.mode is Mode.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)

View File

@ -18,11 +18,6 @@ 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.
*/ */
@ -41,7 +36,6 @@ 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>()
@ -49,24 +43,6 @@ 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)
@ -75,6 +51,7 @@ 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)
} }
} }
@ -87,6 +64,7 @@ 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)
} }
} }
@ -102,15 +80,6 @@ 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))
} }

View File

@ -13,7 +13,23 @@ 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
} }
}, },

View File

@ -10,6 +10,7 @@ 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
@ -19,9 +20,8 @@ internal object EditorKeyListener : TypedActionHandler {
fun attach(editor: Editor, callback: TypedActionHandler) { fun attach(editor: Editor, callback: TypedActionHandler) {
if (attached.isEmpty()) { if (attached.isEmpty()) {
val typedAction = TypedAction.getInstance() originalHandler = action.rawHandler
originalHandler = typedAction.rawHandler action.setupRawHandler(this)
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(TypedAction.getInstance()::setupRawHandler) originalHandler?.let(action::setupRawHandler)
originalHandler = null originalHandler = null
} }
} }

View File

@ -3,7 +3,6 @@ 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
@ -18,10 +17,9 @@ 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, cache) val offsetRange = boundaries.getOffsetRange(editor)
var result = regex.find(editor.immutableText, offsetRange.first) var result = regex.find(editor.immutableText, offsetRange.first)
while (result != null) { while (result != null) {
@ -31,7 +29,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, cache) && !editor.foldingModel.isOffsetCollapsed(index)) { else if (boundaries.isOffsetInside(editor, index) && !editor.foldingModel.isOffsetCollapsed(index)) {
offsets.add(index) offsets.add(index)
} }

View File

@ -3,7 +3,6 @@ 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
@ -41,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.size).zip(tagSites).toMap() tagMap = generateTags(tagSites).zip(tagSites).toMap()
} }
internal fun type(char: Char): TaggingResult { internal fun type(char: Char): TaggingResult {
@ -62,15 +61,13 @@ class Tagger(private val editors: List<Editor>, results: Map<Editor, IntList>) {
} }
private companion object { private companion object {
private fun generateTags(tagCount: Int): List<String> { private fun generateTags(tagSites: List<Tag>): 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 allowedTagsSorted) { for (tag in KeyLayoutCache.allowedTagsSorted) {
val firstChar = tag.first() val firstChar = tag.first()
if (tag.length == 1) { if (tag.length == 1) {
@ -86,41 +83,15 @@ 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 >= tagCount) { if (tags.size >= tagSites.size) {
break return tags
} }
} }
// 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
} }