1
0
mirror of https://github.com/chylex/IntelliJ-AceJump.git synced 2025-01-06 04:42:46 +01:00

Support real-time font size/style changes and reduce some technical debt

This commit is contained in:
breandan 2017-04-24 07:24:26 -04:00
parent c3846838f2
commit 461169ecd1
10 changed files with 118 additions and 111 deletions

View File

@ -62,6 +62,7 @@ To start an instance of IntelliJ IDEA with AceJump installed, run ` ./gradlew ru
## History
- 3.2.1 AceJump now synchronizes font style changes in real-time.
- 3.2.0 Support Back/Forward navigation in the IntelliJ Platform.
- 3.1.8 Fixes some errors that occur when the user closes an editor prematurely.
- 3.1.6 Fixes a rare tag collision scenario and UninitializedPropertyAccess exception

View File

@ -4,7 +4,7 @@ import org.jetbrains.intellij.IntelliJPluginExtension
buildscript {
repositories {
gradleScriptKotlin()
maven { setUrl("http://dl.bintray.com/kotlin/kotlin-eap-1.1") }
maven { setUrl("https://dl.bintray.com/jetbrains/intellij-plugin-service") }
}
dependencies {
@ -13,7 +13,7 @@ buildscript {
}
plugins {
id("org.jetbrains.intellij") version "0.2.5"
id("org.jetbrains.intellij") version "0.2.7"
}
apply {
@ -27,4 +27,4 @@ configure<IntelliJPluginExtension> {
}
group = "com.johnlindquist"
version = "3.2.0"
version = "3.2.1"

View File

@ -34,7 +34,7 @@ object AceLineAction : DumbAwareAction() {
override fun actionPerformed(e: AnActionEvent) {
AceAction.actionPerformed(e)
KeyboardHandler.find(LINE_MARK)
KeyboardHandler.findPattern(LINE_MARK)
}
}

View File

@ -16,6 +16,7 @@ import com.johnlindquist.acejump.search.Finder
import com.johnlindquist.acejump.search.Jumper
import com.johnlindquist.acejump.search.Pattern
import com.johnlindquist.acejump.search.Pattern.*
import com.johnlindquist.acejump.search.Pattern.Companion.REGEX_PREFIX
import com.johnlindquist.acejump.search.getDefaultEditor
import com.johnlindquist.acejump.ui.AceUI
import com.johnlindquist.acejump.ui.AceUI.editor
@ -33,31 +34,31 @@ import javax.swing.event.AncestorEvent
import javax.swing.event.AncestorListener
object KeyboardHandler {
@Volatile
var isEnabled = false
private var text = ""
private val editorActionManager = EditorActionManager.getInstance()
private val handler = editorActionManager.typedAction.rawHandler
private val keyMap = mutableMapOf(
VK_HOME to { find(START_OF_LINE) },
VK_LEFT to { find(START_OF_LINE) },
VK_RIGHT to { find(END_OF_LINE) },
VK_END to { find(END_OF_LINE) },
VK_UP to { find(CODE_INDENTS) },
VK_ESCAPE to { resetUIState() },
VK_HOME to { findPattern(START_OF_LINE) },
VK_LEFT to { findPattern(START_OF_LINE) },
VK_RIGHT to { findPattern(END_OF_LINE) },
VK_END to { findPattern(END_OF_LINE) },
VK_UP to { findPattern(CODE_INDENTS) },
VK_ESCAPE to { reset() },
VK_BACK_SPACE to { processBackspaceCommand() }
)
private fun findAndUpdateUI(query: String, key: Char) {
private fun findString(query: String, key: Char) {
getApplication().runReadAction { Finder.findOrJump(query, key) }
getApplication().invokeLater { updateUIState() }
}
fun find(pattern: Pattern) {
fun findPattern(pattern: Pattern) {
Finder.reset()
findModel.isRegularExpressions = true
findAndUpdateUI(pattern.pattern, Pattern.REGEX_PREFIX)
// TODO: Fix this really bad hack.
findString(pattern.pattern, REGEX_PREFIX)
}
fun activate() = if (!isEnabled) startListening() else toggleTargetMode()
@ -72,7 +73,7 @@ object KeyboardHandler {
private val resetListener = object : CaretListener, VisibleAreaListener,
FocusListener, AncestorListener, EditorColorsListener {
override fun globalSchemeChange(scheme: EditorColorsScheme?) = redo()
override fun globalSchemeChange(scheme: EditorColorsScheme?) = redoQuery()
override fun ancestorAdded(event: AncestorEvent?) = reset()
@ -80,7 +81,7 @@ object KeyboardHandler {
override fun ancestorRemoved(event: AncestorEvent?) = reset()
override fun visibleAreaChanged(e: VisibleAreaEvent?) = redo()
override fun visibleAreaChanged(e: VisibleAreaEvent?) = redoQuery()
override fun focusLost(e: FocusEvent?) = reset()
@ -93,22 +94,21 @@ object KeyboardHandler {
override fun caretRemoved(e: CaretEvent?) = reset()
}
private fun configureEditor() {
AceUI.editor = getDefaultEditor()
fun interceptPrintableKeystrokes() {
editorActionManager.typedAction.setupRawHandler { _, key, _ ->
text += key
findString(text, key)
}
}
editor = getDefaultEditor()
setupCursor()
setupCanvas()
interceptPrintableKeystrokes()
addListeners()
}
private fun interceptPrintableKeystrokes() {
editorActionManager.typedAction.setupRawHandler { _, key, _ ->
text += key
findAndUpdateUI(text, key)
}
}
private var backup: List<*>? = null
// Bulenkov: BuildInfo, Graphics2DLog, DrawString/DrawChars, IDEEventQueue.dispatcher
@ -126,15 +126,15 @@ object KeyboardHandler {
}
private fun startListening() {
fun startup() {
configureEditor()
installCustomShortcutHandler()
}
isEnabled = true
startup()
}
private fun startup() {
configureEditor()
installCustomShortcutHandler()
}
private fun updateUIState() {
if (Jumper.hasJumped) {
Jumper.hasJumped = false
@ -145,17 +145,27 @@ object KeyboardHandler {
}
}
fun redo() {
fun redoQuery() {
val tmpText = text
reset()
startup()
activate()
text = tmpText
if (text.isNotEmpty()) {
findAndUpdateUI(text, text.last())
findString(text, text.last())
}
}
fun reset() {
fun resetUIState() {
text = ""
isEnabled = false
editor.component.putClientProperty(ACTIONS_KEY, backup)
AceKeyAction.unregisterCustomShortcutSet(editor.component)
editorActionManager.typedAction.setupRawHandler(handler)
Finder.reset()
restoreEditorSettings()
}
removeListeners()
resetUIState()
}
@ -180,17 +190,6 @@ object KeyboardHandler {
}
}
private fun resetUIState() {
text = ""
isEnabled = false
editor.component.putClientProperty(ACTIONS_KEY, backup)
AceKeyAction.unregisterCustomShortcutSet(editor.component)
editorActionManager.typedAction.setupRawHandler(handler)
Finder.reset()
Canvas.reset()
restoreEditorSettings()
}
fun toggleTargetMode(status: Boolean? = null) {
if (Finder.toggleTargetMode(status))
editor.colorsScheme.setColor(CARET_COLOR, RED)

View File

@ -160,8 +160,7 @@ fun Editor.getLineEndOffset(line: Int, allowEnd: Boolean) =
* @return The normalized logical line number
*/
fun Editor.normalizeLine(line: Int) =
max(0, min(line, getLineCount() - 1))
fun Editor.normalizeLine(line: Int) = max(0, min(line, getLineCount() - 1))
/**
* Ensures that the supplied offset for the given logical line is within the

View File

@ -5,67 +5,65 @@ import com.intellij.find.FindModel
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.editor.colors.EditorColors.CARET_COLOR
import com.intellij.openapi.editor.colors.EditorColorsManager.getInstance
import com.intellij.openapi.editor.colors.EditorColorsScheme
import com.intellij.openapi.editor.ex.EditorSettingsExternalizable
import com.intellij.openapi.project.Project
import com.johnlindquist.acejump.KeyboardHandler
import com.johnlindquist.acejump.search.getDefaultEditor
import java.awt.Color.*
import java.awt.Font
import java.awt.Font.BOLD
import javax.swing.SwingUtilities.convertPoint
object AceUI {
lateinit var project: Project
lateinit var document: String
lateinit var findModel: FindModel
lateinit var findManager: FindManager
var editor: Editor = getDefaultEditor()
set(value) {
if (value != field) {
try {
KeyboardHandler.reset()
} catch (e: UninitializedPropertyAccessException) { }
field = value
if (value == field) {
return
}
project = editor.project!!
document = editor.document.charsSequence.toString().toLowerCase()
findModel = FindManager.getInstance(project).findInFileModel.clone()
findManager = FindManager.getInstance(project)
//TODO: add listener to update these when settings change
scheme = getInstance().globalScheme
fontWidth = editor.component.getFontMetrics(editor.component.font).stringWidth("w")
fontHeight = editor.component.font.size
lineHeight = editor.lineHeight
lineSpacing = scheme.lineSpacing
rectHOffset = fontHeight - lineHeight + 4
findModel.isFindAll = true
findModel.isFromCursor = true
findModel.isForward = true
findModel.isRegularExpressions = false
findModel.isWholeWordsOnly = false
findModel.isCaseSensitive = false
findModel.isPreserveCase = false
findModel.setSearchHighlighters(true)
if (!KeyboardHandler.isEnabled) {
naturalBlock = EditorSettingsExternalizable.getInstance().isBlockCursor
naturalColor = getInstance().globalScheme.getColor(CARET_COLOR)!!
naturalBlink = EditorSettingsExternalizable.getInstance().isBlinkCaret
try {
KeyboardHandler.reset()
} catch (e: UninitializedPropertyAccessException) {
println(e)
}
field = value
naturalBlock = EditorSettingsExternalizable.getInstance().isBlockCursor
naturalColor = getInstance().globalScheme.getColor(CARET_COLOR)!!
naturalBlink = EditorSettingsExternalizable.getInstance().isBlinkCaret
}
val project: Project
get() = editor.project!!
var document: String = editor.document.charsSequence.toString().toLowerCase()
val findModel: FindModel by lazy {
val findModel = FindModel()
findModel.isFindAll = true
findModel.setSearchHighlighters(true)
findModel
}
val findManager: FindManager = FindManager.getInstance(project)
var naturalBlock = EditorSettingsExternalizable.getInstance().isBlockCursor
var naturalColor = getInstance().globalScheme.getColor(CARET_COLOR)!!
var naturalBlink = EditorSettingsExternalizable.getInstance().isBlinkCaret
var scheme = getInstance().globalScheme
var fontWidth = editor.component.getFontMetrics(editor.component.font).stringWidth("w")
var fontHeight = editor.colorsScheme.editorFontSize
var lineHeight = editor.lineHeight
var lineSpacing = scheme.lineSpacing
var rectHOffset = fontHeight - lineHeight + 4
val scheme: EditorColorsScheme
get() = editor.colorsScheme
val font: Font
get() = Font(scheme.editorFontName, BOLD, scheme.editorFontSize)
val fontWidth
get() = editor.component.getFontMetrics(font).stringWidth("w")
val fontHeight: Int
get() = editor.colorsScheme.editorFontSize
val lineHeight: Int
get() = editor.lineHeight
val lineSpacing: Float
get() = scheme.lineSpacing
val rectHOffset: Int
get() = fontHeight - lineHeight + 4
val boxColor = red
val editorHighlightColor = yellow
@ -91,8 +89,17 @@ object AceUI {
}
fun restoreEditorSettings() {
restoreCanvas()
restoreCursor()
}
private fun restoreCanvas() {
Canvas.reset()
editor.contentComponent.remove(Canvas)
editor.contentComponent.repaint()
}
private fun restoreCursor() {
editor.settings.isBlinkCaret = naturalBlink
editor.settings.isBlockCursor = naturalBlock
editor.colorsScheme.setColor(CARET_COLOR, naturalColor)

View File

@ -1,5 +1,6 @@
package com.johnlindquist.acejump.ui
import com.johnlindquist.acejump.ui.AceUI.fontWidth
import com.johnlindquist.acejump.ui.AceUI.scheme
import java.awt.Font
import java.awt.Font.BOLD
@ -11,10 +12,6 @@ object Canvas : JComponent() {
var existingTags = hashSetOf<Pair<Int, Int>>()
var jumpLocations: Collection<JumpInfo> = arrayListOf()
init {
font = Font(scheme.editorFontName, BOLD, scheme.editorFontSize)
}
override fun paint(graphics: Graphics) {
if (jumpLocations.isEmpty())
return
@ -27,7 +24,7 @@ object Canvas : JComponent() {
fun registerTag(point: Pair<Int, Int>, tag: String) =
(-1..(tag.length)).forEach {
existingTags.add(Pair(point.first + it * AceUI.fontWidth, point.second))
existingTags.add(Pair(point.first + it * fontWidth, point.second))
}
fun isFree(point: Pair<Int, Int>) = !existingTags.contains(point)

View File

@ -3,6 +3,7 @@ package com.johnlindquist.acejump.ui
import com.johnlindquist.acejump.search.Finder
import com.johnlindquist.acejump.search.Finder.query
import com.johnlindquist.acejump.search.Pattern
import com.johnlindquist.acejump.search.Pattern.Companion.REGEX_PREFIX
import com.johnlindquist.acejump.search.getLineStartOffset
import com.johnlindquist.acejump.search.getPointFromVisualPosition
import com.johnlindquist.acejump.ui.AceUI.acejumpHighlightColor
@ -10,7 +11,10 @@ import com.johnlindquist.acejump.ui.AceUI.boxColor
import com.johnlindquist.acejump.ui.AceUI.document
import com.johnlindquist.acejump.ui.AceUI.editor
import com.johnlindquist.acejump.ui.AceUI.editorHighlightColor
import com.johnlindquist.acejump.ui.AceUI.font
import com.johnlindquist.acejump.ui.AceUI.fontHeight
import com.johnlindquist.acejump.ui.AceUI.fontWidth
import com.johnlindquist.acejump.ui.AceUI.lineHeight
import com.johnlindquist.acejump.ui.AceUI.rectHOffset
import com.johnlindquist.acejump.ui.JumpInfo.Alignment.*
import java.awt.AlphaComposite.SRC_OVER
@ -21,7 +25,7 @@ import java.awt.RenderingHints.KEY_ANTIALIASING
import java.awt.RenderingHints.VALUE_ANTIALIAS_ON
class JumpInfo(val tag: String, val index: Int) {
val isRegex = query.first() == Pattern.REGEX_PREFIX
val isRegex = query.first() == REGEX_PREFIX
val line = editor.offsetToVisualPosition(index).line
var originOffset = editor.offsetToVisualPosition(index)
var queryLength = query.length
@ -60,7 +64,7 @@ class JumpInfo(val tag: String, val index: Int) {
g2d.composite = getInstance(SRC_OVER, 1.toFloat())
//the foreground text
g2d.font = Canvas.font
g2d.font = font
g2d.color = BLACK
g2d.drawString(text.toUpperCase(), tagX, tagPoint.y - rectHOffset + fontHeight)
}
@ -68,11 +72,11 @@ class JumpInfo(val tag: String, val index: Int) {
val startOfThisLine = editor.getLineStartOffset(line)
private fun alignTag(ac: Canvas): Pair<Int, Int> {
val y = tagPoint.y - AceUI.rectHOffset
val x = tagPoint.x + AceUI.fontWidth
val top = Pair(x - AceUI.fontWidth, y - AceUI.lineHeight)
val bottom = Pair(x - AceUI.fontWidth, y + AceUI.lineHeight)
val left = Pair(srcPoint.x - AceUI.fontWidth * (text.length), y)
val y = tagPoint.y - rectHOffset
val x = tagPoint.x + fontWidth
val top = Pair(x - fontWidth, y - lineHeight)
val bottom = Pair(x - fontWidth, y + lineHeight)
val left = Pair(srcPoint.x - fontWidth * (text.length), y)
val right = Pair(x, y)
val nextCharIsWhiteSpace = document.length <= index + 1 ||
@ -103,8 +107,8 @@ class JumpInfo(val tag: String, val index: Int) {
var tagX = x
val lastQueryChar = query.last()
var tagWidth = text.length * AceUI.fontWidth
val searchWidth = (trueOffset + 1) * AceUI.fontWidth
var tagWidth = text.length * fontWidth
val searchWidth = (trueOffset + 1) * fontWidth
val indexOfEditorChar = index + query.length - 1
val editorChar =
@ -113,16 +117,17 @@ class JumpInfo(val tag: String, val index: Int) {
else
0.toChar()
// TODO: Use the built-in find-highlighter
fun highlightAlreadyTyped() {
g2d.composite = getInstance(SRC_OVER, 0.40.toFloat())
g2d.color = acejumpHighlightColor
if (lastQueryChar == tag.first() && lastQueryChar != editorChar) {
g2d.fillRect(tagX, tagPoint.y - rectHOffset, AceUI.fontWidth, AceUI.fontHeight + 3)
tagX += AceUI.fontWidth
tagWidth -= AceUI.fontWidth
g2d.fillRect(tagX, tagPoint.y - rectHOffset, fontWidth, fontHeight + 3)
tagX += fontWidth
tagWidth -= fontWidth
}
g2d.fillRect(srcPoint.x - 1, tagPoint.y - rectHOffset, searchWidth, AceUI.fontHeight + 3)
g2d.fillRect(srcPoint.x - 1, tagPoint.y - rectHOffset, searchWidth, fontHeight + 3)
}
fun highlightRemaining() {
@ -133,7 +138,7 @@ class JumpInfo(val tag: String, val index: Int) {
if (alignment != RIGHT || hasSpaceToTheRight || isRegex)
g2d.composite = getInstance(SRC_OVER, 1.toFloat())
g2d.fillRect(tagX, tagPoint.y - rectHOffset, tagWidth, AceUI.fontHeight + 3)
g2d.fillRect(tagX, tagPoint.y - rectHOffset, tagWidth, fontHeight + 3)
}
fun surroundTargetWord() {
@ -144,10 +149,10 @@ class JumpInfo(val tag: String, val index: Int) {
val startPoint = editor.offsetToVisualPosition(wordStart)
val startPointO = editor.getPointFromVisualPosition(startPoint)
val xPosition = startPointO.originalPoint.x
val width = (wordEnd - wordStart) * AceUI.fontWidth
val width = (wordEnd - wordStart) * fontWidth
if (document[index].isLetterOrDigit())
g2d.drawRect(xPosition, tagPoint.y - rectHOffset, width, AceUI.fontHeight + 3)
g2d.drawRect(xPosition, tagPoint.y - rectHOffset, width, fontHeight + 3)
}
highlightAlreadyTyped()

View File

@ -8,7 +8,7 @@
Simply hit "ctrl+;", type a character, then type the matching character to Ace Jump. ]]>
</description>
<change-notes>Support Back/Forward navigation in the IntelliJ Platform.</change-notes>
<change-notes>AceJump now tracks and adjusts to font changes in the editor much more rapidly.</change-notes>
<depends>com.intellij.modules.platform</depends>

View File

@ -9,5 +9,4 @@ import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase
*/
class AceTest: EditorActionTestCase() {
override fun getActionId() = "AceAction"
}