diff --git a/src/main/kotlin/org/acejump/control/AceAction.kt b/src/main/kotlin/org/acejump/control/AceAction.kt index b3b796a..63bbcd8 100755 --- a/src/main/kotlin/org/acejump/control/AceAction.kt +++ b/src/main/kotlin/org/acejump/control/AceAction.kt @@ -1,90 +1,132 @@ package org.acejump.control +import com.intellij.codeInsight.editorActions.SelectWordUtil import com.intellij.openapi.actionSystem.AnActionEvent import com.intellij.openapi.actionSystem.CommonDataKeys.EDITOR import com.intellij.openapi.diagnostic.Logger +import com.intellij.openapi.diagnostic.debug +import com.intellij.openapi.editor.CaretState +import com.intellij.openapi.editor.ScrollType +import com.intellij.openapi.fileEditor.FileDocumentManager import com.intellij.openapi.project.DumbAwareAction +import com.intellij.openapi.util.TextRange import org.acejump.control.Handler.regexSearch import org.acejump.label.Pattern import org.acejump.label.Pattern.ALL_WORDS +import org.acejump.search.Finder import org.acejump.search.JumpMode import org.acejump.search.Jumper -import org.acejump.search.getNameOfFileInEditor import org.acejump.view.Boundary.* +import org.acejump.view.Model import org.acejump.view.Model.boundaries import org.acejump.view.Model.defaultBoundary import org.acejump.view.Model.editor +import org.acejump.view.Model.viewBounds /** * Entry point for all actions. The IntelliJ Platform calls AceJump here. */ -open class AceAction: DumbAwareAction() { - open val logger = Logger.getInstance(javaClass) - override fun update(action: AnActionEvent) { +sealed class AceAction: DumbAwareAction() { + val logger = Logger.getInstance(javaClass) + + final override fun update(action: AnActionEvent) { action.presentation.isEnabled = action.getData(EDITOR) != null } - override fun actionPerformed(e: AnActionEvent) { + final override fun actionPerformed(e: AnActionEvent) { editor = e.getData(EDITOR) ?: return boundaries = defaultBoundary - val textLength = editor.document.textLength - logger.info("Invoked on ${editor.getNameOfFileInEditor()} ($textLength)") + logger.debug { "Invoked on ${FileDocumentManager.getInstance().getFile(editor.document)?.presentableName} (${editor.document.textLength})" } Handler.activate() - customize() + invoke() } - open fun customize() = Jumper.cycleMode() + abstract fun invoke() + + object ActivateOrCycleMode : AceAction() { + override fun invoke() = Jumper.cycleMode() + } + + object ToggleJumpMode: AceAction() { + override fun invoke() = Jumper.toggleMode(JumpMode.JUMP) + } + + object ToggleJumpEndMode: AceAction() { + override fun invoke() = Jumper.toggleMode(JumpMode.JUMP_END) + } + + object ToggleSelectWordMode: AceAction() { + override fun invoke() = Jumper.toggleMode(JumpMode.TARGET) + } + + object ToggleDefinitionMode: AceAction() { + override fun invoke() = Jumper.toggleMode(JumpMode.DEFINE) + } + + object ToggleAllLinesMode: AceAction() { + override fun invoke() = regexSearch(Pattern.LINE_MARK) + } + + object ToggleAllWordsMode: AceAction() { + override fun invoke() = regexSearch(ALL_WORDS, SCREEN_BOUNDARY) + } + + object ToggleAllWordsForwardMode: AceAction() { + override fun invoke() = regexSearch(ALL_WORDS, AFTER_CARET_BOUNDARY) + } + + object ToggleAllWordsBackwardsMode: AceAction() { + override fun invoke() = regexSearch(ALL_WORDS, BEFORE_CARET_BOUNDARY) + } + + object ActOnHighlightedWords: AceAction() { + override fun invoke() = when(JumpMode.mode) { + JumpMode.DISABLED -> {} + JumpMode.JUMP -> if (editor.caretModel.supportsMultipleCarets()) jumpToAll() else Unit + JumpMode.JUMP_END -> if (editor.caretModel.supportsMultipleCarets()) jumpToWordEnds() else Unit + JumpMode.TARGET -> if (editor.caretModel.supportsMultipleCarets()) selectAllWords() else Unit + JumpMode.DEFINE -> {} + } + + private fun jumpToAll() { + val carets = Finder.allResults().map { + CaretState(editor.offsetToLogicalPosition(it), null, null) + } + if (carets.isEmpty()) return + Handler.reset() + editor.caretModel.caretsAndSelections = carets + editor.scrollingModel.scrollToCaret(ScrollType.MAKE_VISIBLE) + } + + private fun jumpToWordEnds() { + val ranges = ArrayList<TextRange>() + for (offset in Finder.allResults()) { + SelectWordUtil.addWordSelection(editor.settings.isCamelWords, Model.editorText, offset, ranges) + } + if (ranges.isEmpty()) return + + Handler.reset() + editor.caretModel.caretsAndSelections = ranges.map { + CaretState(editor.offsetToLogicalPosition(it.endOffset), null, null) + } + editor.scrollingModel.scrollToCaret(ScrollType.MAKE_VISIBLE) + } + + private fun selectAllWords() { + val ranges = ArrayList<TextRange>() + for (offset in Finder.allResults()) { + SelectWordUtil.addWordSelection(editor.settings.isCamelWords, Model.editorText, offset, ranges) + } + if (ranges.isEmpty()) return + + Handler.reset() + editor.caretModel.caretsAndSelections = ranges.map { + val start = editor.offsetToLogicalPosition(it.startOffset) + val end = editor.offsetToLogicalPosition(it.endOffset) + CaretState(end, start, end) + } + editor.scrollingModel.scrollToCaret(ScrollType.MAKE_VISIBLE) + } + } } - -/** - * When target mode is activated, selecting a tag will highlight an entire word. - */ - -class AceTargetAction: AceAction() { - override fun customize() = Jumper.toggleMode(JumpMode.TARGET) -} - -/* - * When line mode is activated, we will tag the beginning and end of each line. - * - * TODO: https://github.com/acejump/AceJump/issues/327 - * TODO: https://github.com/acejump/AceJump/issues/340 - */ - -class AceLineAction: AceAction() { - override fun customize() = regexSearch(Pattern.LINE_MARK) -} - -/** - * When declaration mode is activated, selecting a tag will take us to the - * definition (i.e. declaration) of the token in the editor, if it exists. - */ - -class AceDefinitionAction: AceAction() { - override fun customize() = Jumper.toggleMode(JumpMode.DEFINE) -} - -/** - * When word mode is activated, we will tag all words on the screen. - */ - -class AceWordAction: AceAction() { - override fun customize() = regexSearch(ALL_WORDS, SCREEN_BOUNDARY) -} - -/** - * Search for words from the start of the screen to the caret - */ - -class AceWordForwardAction: AceAction() { - override fun customize() = regexSearch(ALL_WORDS, AFTER_CARET_BOUNDARY) -} - -/** - * Search for words from the caret position to the start of the screen - */ - -class AceWordBackwardsAction: AceAction() { - override fun customize() = regexSearch(ALL_WORDS, BEFORE_CARET_BOUNDARY) -} \ No newline at end of file diff --git a/src/main/kotlin/org/acejump/search/Finder.kt b/src/main/kotlin/org/acejump/search/Finder.kt index b254f33..1f9776b 100644 --- a/src/main/kotlin/org/acejump/search/Finder.kt +++ b/src/main/kotlin/org/acejump/search/Finder.kt @@ -201,6 +201,8 @@ object Finder : Resettable { return kept } + fun allResults() = results + fun visibleResults() = results.filter { it in viewBounds } private fun String.isValidQuery() = diff --git a/src/main/kotlin/org/acejump/search/JumpMode.kt b/src/main/kotlin/org/acejump/search/JumpMode.kt index c423fd4..56bf50a 100644 --- a/src/main/kotlin/org/acejump/search/JumpMode.kt +++ b/src/main/kotlin/org/acejump/search/JumpMode.kt @@ -13,8 +13,9 @@ enum class JumpMode { companion object: Resettable { private var modeIndex = 0 - private var mode: JumpMode = DISABLED - set(value) { + + var mode: JumpMode = DISABLED + private set(value) { field = value setCaretColor(when (field) { JUMP -> AceConfig.jumpModeColor diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index 502a61e..7b68977 100755 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -22,44 +22,47 @@ <actions> <action id="AceAction" - class="org.acejump.control.AceAction" - text="Activate AceJump Mode" - description="Targets a character in AceJump"> + class="org.acejump.control.AceAction$ActivateOrCycleMode" + text="Activate / Cycle AceJump Mode"> <keyboard-shortcut keymap="Mac OS X" first-keystroke="ctrl SEMICOLON"/> <keyboard-shortcut keymap="Mac OS X 10.5+" first-keystroke="ctrl SEMICOLON"/> <keyboard-shortcut keymap="$default" first-keystroke="ctrl SEMICOLON"/> </action> + <action id="AceWordStartAction" + class="org.acejump.control.AceAction$ToggleJumpMode" + text="Start AceJump in Word Start Mode"/> + <action id="AceWordEndAction" + class="org.acejump.control.AceAction$ToggleJumpEndMode" + text="Start AceJump in Word End Mode"/> + <action id="AceTargetAction" + class="org.acejump.control.AceAction$ToggleSelectWordMode" + text="Start AceJump in Word Select Mode"> + <keyboard-shortcut keymap="Mac OS X" first-keystroke="ctrl alt SEMICOLON"/> + <keyboard-shortcut keymap="Mac OS X 10.5+" first-keystroke="ctrl alt SEMICOLON"/> + <keyboard-shortcut keymap="$default" first-keystroke="ctrl alt SEMICOLON"/> + </action> + <action id="AceDeclarationAction" + class="org.acejump.control.AceAction$ToggleDefinitionMode" + text="Start AceJump in Declaration Mode"/> <action id="AceLineAction" - class="org.acejump.control.AceLineAction" - text="Display Line Markers" + class="org.acejump.control.AceAction$ToggleAllLinesMode" + text="Start AceJump in All Lines Mode" description="Targets line markers in AceJump"> <keyboard-shortcut keymap="Mac OS X" first-keystroke="ctrl shift SEMICOLON"/> <keyboard-shortcut keymap="Mac OS X 10.5+" first-keystroke="ctrl shift SEMICOLON"/> <keyboard-shortcut keymap="$default" first-keystroke="ctrl shift SEMICOLON"/> </action> - <action id="AceTargetAction" - class="org.acejump.control.AceTargetAction" - text="Start in Target Mode" - description="Targets a whole word in AceJump"> - <keyboard-shortcut keymap="Mac OS X" first-keystroke="ctrl alt SEMICOLON"/> - <keyboard-shortcut keymap="Mac OS X 10.5+" first-keystroke="ctrl alt SEMICOLON"/> - <keyboard-shortcut keymap="$default" first-keystroke="ctrl alt SEMICOLON"/> - </action> <action id="AceWordAction" - class="org.acejump.control.AceWordAction" - text="Start in Word Mode" - description="Searches for all words on the screen in AceJump"/> + class="org.acejump.control.AceAction$ToggleAllWordsMode" + text="Start AceJump in All Words Mode"/> <action id="AceWordForwardAction" - class="org.acejump.control.AceWordForwardAction" - text="Start in Word Forward Mode" - description="Searches for all visible words after the caret in AceJump"/> + class="org.acejump.control.AceAction$ToggleAllWordsForwardMode" + text="Start in All Words After Caret Mode"/> <action id="AceWordBackwardsAction" - class="org.acejump.control.AceWordBackwardsAction" - text="Start in Word Backward Mode" - description="Searches for all visible words before the caret in AceJump"/> - <action id="AceDeclarationAction" - class="org.acejump.control.AceDefinitionAction" - text="Start in Declaration Mode" - description="AceJump will invoke the 'Navigate To' action after jumping"/> + class="org.acejump.control.AceAction$ToggleAllWordsBackwardsMode" + text="Start in All Words Before Caret Mode"/> + <action id="AceActOnHighlightedWords" + class="org.acejump.control.AceAction$ActOnHighlightedWords" + text="Act on Highlighted Words"/> </actions> </idea-plugin>