1
0
mirror of https://github.com/chylex/IntelliJ-AceJump.git synced 2025-04-09 17:15:43 +02:00

separation of concerns

This commit is contained in:
breandan.considine 2017-07-24 10:04:37 -04:00
parent a56b14c429
commit a2858d235e
8 changed files with 85 additions and 71 deletions
src/main/kotlin/com/johnlindquist/acejump

View File

@ -1,6 +1,5 @@
package com.johnlindquist.acejump.control
import com.intellij.find.FindModel
import com.intellij.openapi.actionSystem.AnAction
import com.intellij.openapi.actionSystem.CustomShortcutSet
import com.intellij.openapi.editor.Editor
@ -11,6 +10,7 @@ import com.intellij.util.SmartList
import com.johnlindquist.acejump.config.AceConfig.Companion.settings
import com.johnlindquist.acejump.search.*
import com.johnlindquist.acejump.search.Pattern.*
import com.johnlindquist.acejump.search.Searcher.search
import com.johnlindquist.acejump.search.Skipper.restoreScroll
import com.johnlindquist.acejump.search.Skipper.storeScroll
import com.johnlindquist.acejump.view.Canvas
@ -27,58 +27,38 @@ import javax.swing.JComponent
object Handler {
private var enabled = false
private var text = ""
private val editorTypeAction = EditorActionManager.getInstance().typedAction
private val handler = editorTypeAction.rawHandler
private var isShiftDown = false
private val keyMap = mapOf(
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_HOME to { search(START_OF_LINE) },
VK_LEFT to { search(START_OF_LINE) },
VK_RIGHT to { search(END_OF_LINE) },
VK_END to { search(END_OF_LINE) },
VK_UP to { search(CODE_INDENTS) },
VK_ESCAPE to { reset() },
VK_BACK_SPACE to { processBackspaceCommand() },
VK_ENTER to { Finder.maybeJumpIfJustOneTagRemains() },
VK_ENTER to { Tagger.maybeJumpIfJustOneTagRemains() },
VK_TAB to { Skipper.doesQueryExistIfSoSkipToIt(!isShiftDown) }
)
private fun findOrDropLast() =
if (!Finder.isQueryDeadEnd(text)) {
find(text)
} else {
text = text.dropLast(1)
}
private fun find(key: String, doSkim: Boolean = false) =
Highlighter.search(FindModel().apply { stringToFind = key; skim = doSkim })
fun findPattern(pattern: Pattern) =
Highlighter.search(FindModel().apply {
stringToFind = pattern.string
isRegularExpressions = true
skim = false
Finder.reset()
})
fun activate() = runNow { if (!enabled) start() else toggleTargetMode() }
fun processCommand(keyCode: Int) = keyMap[keyCode]?.invoke()
private fun processBackspaceCommand() {
text = ""
Finder.reset()
Highlighter.discard()
Tagger.reset()
Searcher.discard()
updateUIState()
}
private fun interceptPrintableKeystrokes() =
editorTypeAction.setupRawHandler { _, key, _ ->
text += key
if (text.length < 2) {
find(text, doSkim = true)
Trigger.restart(400L) { find(text, doSkim = false) }
} else findOrDropLast()
Searcher.query += key
if (Searcher.query.length < 2) {
Searcher.skim()
Trigger.restart(400L) { Searcher.search() }
} else Searcher.findOrDropLast(Searcher.query)
}
private fun configureEditor() =
@ -123,7 +103,7 @@ object Handler {
Jumper.hasJumped = false
reset()
} else {
Canvas.jumpLocations = Finder.markers
Canvas.jumpLocations = Tagger.markers
Canvas.repaint()
}
@ -133,9 +113,9 @@ object Handler {
Canvas.bindToEditor(editor)
}
if (text.isNotEmpty() || Finder.isRegex)
if (Searcher.query.isNotEmpty() || Tagger.isRegex)
runLater {
Finder.find()
Tagger.mark()
updateUIState()
}
}
@ -145,15 +125,14 @@ object Handler {
editor.component.uninstallCustomShortCutHandler()
editorTypeAction.setupRawHandler(handler)
enabled = false
text = ""
Finder.reset()
Highlighter.discard()
Tagger.reset()
Searcher.discard()
editor.restoreSettings()
}
fun toggleTargetMode(status: Boolean? = null) =
editor.colorsScheme.run {
if (Finder.toggleTargetMode(status))
if (Tagger.toggleTargetMode(status))
setColor(CARET_COLOR, settings.targetModeColor)
else
setColor(CARET_COLOR, settings.jumpModeColor)

View File

@ -8,7 +8,7 @@ import com.intellij.openapi.editor.event.VisibleAreaEvent
import com.intellij.openapi.editor.event.VisibleAreaListener
import com.johnlindquist.acejump.control.Handler.redoFind
import com.johnlindquist.acejump.control.Handler.reset
import com.johnlindquist.acejump.search.Finder
import com.johnlindquist.acejump.search.Tagger
import com.johnlindquist.acejump.search.getView
import com.johnlindquist.acejump.view.Model.editor
import com.johnlindquist.acejump.view.Model.viewBounds
@ -48,8 +48,8 @@ internal object Listener : CaretListener, FocusListener, AncestorListener,
private fun canTagsSurviveViewResize() =
editor.getView().run {
if (first in viewBounds && last in viewBounds) return true
else if (Finder.isRegex) return false
else !Finder.hasMatchBetweenOldAndNewView(viewBounds, this)
else if (Tagger.isRegex) return false
else !Tagger.hasMatchBetweenOldAndNewView(viewBounds, this)
}
override fun globalSchemeChange(scheme: EditorColorsScheme?) = redoFind()

View File

@ -15,9 +15,9 @@ import java.lang.Math.max
import java.lang.Math.min
var FindModel.skim: Boolean
get() = Finder.skim
get() = Tagger.skim
set(value) {
Finder.skim = value
Tagger.skim = value
}
operator fun Point.component1() = x

View File

@ -24,9 +24,9 @@ object Jumper {
var hasJumped = false
fun jump(marker: Marker) = editor.run {
if (Finder.findModel.stringToFind.last().isUpperCase())
if (Tagger.findModel.stringToFind.last().isUpperCase())
selectFromToOffset(caretModel.offset, marker.index)
else if (Finder.targetModeEnabled) {
else if (Tagger.targetModeEnabled) {
// Moving the caret will trigger a reset, flipping targetModeEnabled, so
// we need to move the caret and select the word in one single transaction
moveCaret(marker.index)

View File

@ -8,21 +8,29 @@ import com.intellij.openapi.Disposable
import com.intellij.openapi.editor.colors.EditorColors
import com.johnlindquist.acejump.control.Handler
import com.johnlindquist.acejump.view.Model.editor
import com.johnlindquist.acejump.view.Model.editorText
import com.johnlindquist.acejump.view.Model.project
import java.awt.Color
object Highlighter : Disposable, SearchResults.SearchResultsListener {
private var model: FindModel = FindModel()
object Searcher : Disposable, SearchResults.SearchResultsListener {
var query: String = ""
set(value) {
model = FindModel().apply { stringToFind = value }
field = value
}
var model: FindModel = FindModel()
override fun searchResultsUpdated(sr: SearchResults?) {
results?.occurrences
?.filter { it.startOffset in editor.getView() }
.let { resultsInView = it }
if (!model.skim) doFind()
doFind()
}
private fun SearchResults.filterOrNarrowInView() =
resultsInView?.partition { Finder.hasTagsAtIndex(it.startOffset) }?.run {
resultsInView?.partition { Tagger.hasTagsAtIndex(it.startOffset) }?.run {
second.forEach { exclude(it) }
resultsInView = first
}
@ -36,26 +44,43 @@ object Highlighter : Disposable, SearchResults.SearchResultsListener {
private lateinit var livePreviewController: LivePreviewController
fun skim() = search(model.apply { skim = true })
fun search(string: String = query) = search(FindModel().apply { stringToFind = string })
fun search(pattern: Pattern) =
Searcher.search(FindModel().apply {
stringToFind = pattern.string
isRegularExpressions = true
skim = false
Tagger.reset()
})
fun search(findModel: FindModel) {
model = findModel
if (results == null) init()
results?.filterOrNarrowInView()
val hasTags = Finder.hasTagsStartingWithChar(model.stringToFind.last())
if (!hasTags) livePreviewController.updateInBackground(model, false)
if (hasTags) doFind()
livePreviewController.updateInBackground(model, false)
}
private fun doFind() =
runLater {
Finder.findOrJump(model, results?.occurrences?.map { it.startOffset })
Tagger.markOrJump(model, results?.occurrences?.map { it.startOffset })
results?.filterOrNarrowInView()
Handler.updateUIState()
}
fun findOrDropLast(text: String = query) =
if (!isQueryDeadEnd(text)) {
search(text)
} else {
query = text.dropLast(1)
}
private fun init() {
results = SearchResults(editor, project).apply { addListener(Highlighter) }
results = SearchResults(editor, project).apply { addListener(Searcher) }
editor.colorsScheme.run {
setAttributes(EditorColors.TEXT_SEARCH_RESULT_ATTRIBUTES,
@ -67,9 +92,15 @@ object Highlighter : Disposable, SearchResults.SearchResultsListener {
livePreviewController.on()
}
fun isQueryDeadEnd(query: String) =
results?.occurrences?.any { editorText.regionMatches(it.startOffset, query, 0, query.length) } ?: true ||
!Tagger.hasTagSuffix(query)
fun getResultsInView() = resultsInView?.map { it.startOffset }
fun discard() {
query = ""
model = FindModel()
results?.removeListener(this)
results?.dispose()
livePreviewController.off()

View File

@ -3,7 +3,7 @@ package com.johnlindquist.acejump.search
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.editor.LogicalPosition
import com.intellij.openapi.editor.ScrollType.CENTER
import com.johnlindquist.acejump.search.Finder.textMatches
import com.johnlindquist.acejump.search.Tagger.textMatches
import com.johnlindquist.acejump.view.Model.editor
import com.johnlindquist.acejump.view.Model.viewBounds

View File

@ -7,9 +7,9 @@ import com.google.common.collect.Multimap
import com.intellij.find.FindModel
import com.intellij.openapi.diagnostic.Logger
import com.johnlindquist.acejump.config.AceConfig.Companion.settings
import com.johnlindquist.acejump.search.Finder.textMatches
import com.johnlindquist.acejump.search.Pattern.Companion.distance
import com.johnlindquist.acejump.search.Pattern.Companion.priotity
import com.johnlindquist.acejump.search.Tagger.textMatches
import com.johnlindquist.acejump.view.Marker
import com.johnlindquist.acejump.view.Model.editor
import com.johnlindquist.acejump.view.Model.editorText
@ -21,7 +21,7 @@ import java.util.*
* Singleton that searches for text in the editor and tags matching results.
*/
object Finder {
object Tagger {
var targetModeEnabled = false
var markers: Collection<Marker> = emptyList()
private set
@ -34,12 +34,12 @@ object Finder {
private var tagMap: BiMap<String, Int> = HashBiMap.create()
private var unseen2grams: LinkedHashSet<String> = linkedSetOf()
private var digraphs: Multimap<String, Int> = LinkedListMultimap.create()
private val logger = Logger.getInstance(Finder::class.java)
private val logger = Logger.getInstance(Tagger::class.java)
var findModel = FindModel()
var skim = false
fun findOrJump(findModel: FindModel, results: List<Int>?) {
if (results != null && results.isNotEmpty()) textMatches = results
fun markOrJump(findModel: FindModel, results: List<Int>?) {
if (results != null) textMatches = results
else return
if (!isRegex) isRegex = findModel.isRegularExpressions
@ -49,7 +49,7 @@ object Finder {
Regex.escape(findModel.stringToFind.toLowerCase())
query = (if (isRegex) " " else "") + findModel.stringToFind.toLowerCase()
find()
mark()
}
fun toggleTargetMode(status: Boolean? = null): Boolean {
@ -62,8 +62,8 @@ object Finder {
private fun jumpTo(marker: Marker) = Jumper.jump(marker)
fun find() {
computeMarkers()
fun mark() {
if (textMatches.isNotEmpty()) computeMarkers()
// TODO: Clean up this ugliness.
if (markers.size > 1 || query.length < 2) return
@ -282,7 +282,7 @@ object Finder {
}
/**
* Returns true if the Finder contains a match in the new view, that is not
* Returns true if the Tagger contains a match in the new view, that is not
* contained (visible) in the old view. This method assumes that textMatches
* are sorted from least to greatest.
*
@ -304,6 +304,10 @@ object Finder {
return textMatches.isEmpty() && markers.isEmpty()
}
fun hasTagsStartingWithChar(c: Char) = tagMap.any { it.key.startsWith(c.toLowerCase()) }
fun hasTagSuffix(query: String) = tagMap.any {
query.endsWith(it.key.first(), true) ||
query.endsWith(it.key, true)
}
fun hasTagsAtIndex(i: Int) = tagMap.containsValue(i)
}

View File

@ -2,7 +2,7 @@ package com.johnlindquist.acejump.view
import com.johnlindquist.acejump.config.AceConfig.Companion.settings
import com.johnlindquist.acejump.search.*
import com.johnlindquist.acejump.search.Finder.isRegex
import com.johnlindquist.acejump.search.Tagger.isRegex
import com.johnlindquist.acejump.view.Marker.Alignment.*
import com.johnlindquist.acejump.view.Model.arcD
import com.johnlindquist.acejump.view.Model.editor
@ -54,7 +54,7 @@ class Marker(val query: String, val tag: String?, val index: Int) {
?.apply { Canvas.registerTag(this, tag) }
?.let { highlightTag(it); drawTagForeground(it) }
if (Finder.isRegex)
if (Tagger.isRegex)
highlightText()
}
@ -150,6 +150,6 @@ class Marker(val query: String, val tag: String?, val index: Int) {
highlightAlreadyTyped()
highlightRemaining()
if (Finder.targetModeEnabled) surroundTargetWord()
if (Tagger.targetModeEnabled) surroundTargetWord()
}
}