1
0
mirror of https://github.com/chylex/IntelliJ-IdeaVim.git synced 2025-03-06 00:32:52 +01:00

Fixed review issues

This commit is contained in:
Vitalii Karavaev 2018-08-09 11:18:39 +03:00
parent 77bd800d95
commit d46cab6fc8
2 changed files with 172 additions and 98 deletions
src/com/maddyhome/idea/vim/extension/multiplecursors
test/org/jetbrains/plugins/ideavim/extension/multiplecursors

View File

@ -6,7 +6,8 @@ import com.intellij.openapi.editor.Editor
import com.intellij.openapi.editor.ScrollType
import com.maddyhome.idea.vim.VimPlugin
import com.maddyhome.idea.vim.command.CommandState
import com.maddyhome.idea.vim.command.CommandState.*
import com.maddyhome.idea.vim.command.CommandState.Mode
import com.maddyhome.idea.vim.command.CommandState.SubMode
import com.maddyhome.idea.vim.command.MappingMode
import com.maddyhome.idea.vim.common.TextRange
import com.maddyhome.idea.vim.extension.VimExtensionFacade.putExtensionHandlerMapping
@ -17,143 +18,121 @@ import com.maddyhome.idea.vim.group.MotionGroup
import com.maddyhome.idea.vim.helper.CaretData
import com.maddyhome.idea.vim.helper.StringHelper.parseKeys
private const val NEXT_WHOLE_OCCURRENCE = "<Plug>NextWholeOccurrence"
private const val NEXT_OCCURRENCE = "<Plug>NextOccurrence"
private const val NOT_WHOLE_OCCURRENCE = "<Plug>NotWholeOccurrence"
private const val SKIP_OCCURRENCE = "<Plug>SkipOccurrence"
private const val REMOVE_OCCURRENCE = "<Plug>RemoveOccurrence"
private const val ALL_WHOLE_OCCURRENCES = "<Plug>AllWholeOccurrences"
private const val ALL_OCCURRENCES = "<Plug>AllOccurrences"
private const val NOT_WHOLE_ALL_OCCURRENCES = "<Plug>NotWholeAllOccurrences"
private class State private constructor() {
var nextOffset = -1
lateinit var firstRange: TextRange
private object Holder {
val INSTANCE = State()
}
companion object {
val instance: State by lazy { Holder.INSTANCE }
}
}
private fun selectRange(editor: Editor, caret: Caret, startOffset: Int, endOffset: Int) {
CaretData.setVisualStart(caret, startOffset)
VimPlugin.getMotion().updateSelection(editor, caret, endOffset)
editor.scrollingModel.scrollToCaret(ScrollType.CENTER)
}
private fun handleFirstSelection(editor: Editor, whole: Boolean) {
val commandState = CommandState.getInstance(editor)
val state = State.instance
val caret = editor.caretModel.primaryCaret
state.firstRange = VimPlugin.getMotion().getWordRange(editor, caret, 1, false, false)
val firstRange = state.firstRange
val startOffset = firstRange.startOffset
val endOffset = firstRange.endOffset
state.nextOffset = VimPlugin.getSearch().searchWord(editor, caret, 1, whole, 1)
commandState.pushState(Mode.VISUAL, SubMode.VISUAL_CHARACTER, MappingMode.VISUAL)
selectRange(editor, caret, startOffset, endOffset)
MotionGroup.moveCaret(editor, caret, endOffset, true)
}
private fun handleNextSelection(editor: Editor): Boolean {
val state = State.instance
val caret = editor.caretModel.addCaret(editor.offsetToVisualPosition(state.nextOffset), true)
?: throw IllegalStateException("Multiple carets are not supported")
val range = VimPlugin.getMotion().getWordRange(editor, caret, 1, false, false)
val firstRange = state.firstRange
val startOffset = range.startOffset
val endOffset = range.startOffset + firstRange.endOffset - firstRange.startOffset
if (startOffset == firstRange.startOffset && endOffset == firstRange.endOffset) {
editor.caretModel.removeCaret(caret)
VimPlugin.showMessage("No more matches")
return false
}
state.nextOffset = VimPlugin.getSearch().searchNext(editor, caret, 1)
selectRange(editor, caret, startOffset, endOffset)
MotionGroup.moveCaret(editor, caret, endOffset, true)
return true
}
class VimMultipleCursorsExtension : VimNonDisposableExtension() {
private var nextOffset = -1
private lateinit var firstRange: TextRange
private val hasNext: Boolean get() = nextOffset != firstRange.startOffset
override fun getName() = "multiple-cursors"
override fun initOnce() {
putExtensionHandlerMapping(MappingMode.NVO, parseKeys(NEXT_OCCURRENCE), NextOccurrenceHandler(), false)
putExtensionHandlerMapping(MappingMode.NVO, parseKeys(NOT_WHOLE_OCCURRENCE), NextOccurrenceHandler(false), false)
putExtensionHandlerMapping(MappingMode.NO, parseKeys(ALL_OCCURRENCES), AllOccurrencesHandler(), false)
putExtensionHandlerMapping(MappingMode.NO, parseKeys(NOT_WHOLE_ALL_OCCURRENCES), AllOccurrencesHandler(false),
false)
putExtensionHandlerMapping(MappingMode.NVO, parseKeys(NEXT_WHOLE_OCCURRENCE), NextOccurrenceHandler(), false)
putExtensionHandlerMapping(MappingMode.NVO, parseKeys(NEXT_OCCURRENCE), NextOccurrenceHandler(whole = false), false)
putExtensionHandlerMapping(MappingMode.NO, parseKeys(ALL_WHOLE_OCCURRENCES), AllOccurrencesHandler(), false)
putExtensionHandlerMapping(MappingMode.NO, parseKeys(ALL_OCCURRENCES), AllOccurrencesHandler(whole = false), false)
putExtensionHandlerMapping(MappingMode.V, parseKeys(SKIP_OCCURRENCE), SkipOccurrenceHandler(), false)
putExtensionHandlerMapping(MappingMode.V, parseKeys(REMOVE_OCCURRENCE), RemoveOccurrenceHandler(), false)
putKeyMapping(MappingMode.NVO, parseKeys("<A-n>"), parseKeys(NEXT_OCCURRENCE), true)
putKeyMapping(MappingMode.NVO, parseKeys("g<A-n>"), parseKeys(NOT_WHOLE_OCCURRENCE), true)
putKeyMapping(MappingMode.NVO, parseKeys("<A-n>"), parseKeys(NEXT_WHOLE_OCCURRENCE), true)
putKeyMapping(MappingMode.NVO, parseKeys("g<A-n>"), parseKeys(NEXT_OCCURRENCE), true)
putKeyMapping(MappingMode.V, parseKeys("<A-x>"), parseKeys(SKIP_OCCURRENCE), true)
putKeyMapping(MappingMode.V, parseKeys("<A-p>"), parseKeys(REMOVE_OCCURRENCE), true)
}
private class NextOccurrenceHandler(val whole: Boolean = true) : VimExtensionHandler {
override fun execute(editor: Editor, context: DataContext) {
inner class NextOccurrenceHandler(val whole: Boolean = true) : VimExtensionHandler {
override fun execute(editor: Editor, context: DataContext) =
if (editor.caretModel.caretCount == 1 && CommandState.getInstance(editor).mode != Mode.VISUAL) {
handleFirstSelection(editor, whole)
}
else {
handleNextSelection(editor)
}
}
}
private class AllOccurrencesHandler(val whole: Boolean = true) : VimExtensionHandler {
inner class AllOccurrencesHandler(val whole: Boolean = true) : VimExtensionHandler {
override fun execute(editor: Editor, context: DataContext) {
handleFirstSelection(editor, whole)
while (handleNextSelection(editor)) {}
while (hasNext) handleNextSelection(editor)
}
}
private class SkipOccurrenceHandler : VimExtensionHandler {
inner class SkipOccurrenceHandler : VimExtensionHandler {
override fun execute(editor: Editor, context: DataContext) {
val caret = editor.caretModel.primaryCaret
val offset = VimPlugin.getSearch().searchWord(editor, caret, 1, true, 1)
if (offset == -1) return
caret.moveToOffset(offset)
val state = State.instance
val range = VimPlugin.getMotion().getWordRange(editor, caret, 1, false, false)
editor.selectionModel.removeSelection()
MotionGroup.moveCaret(editor, caret, nextOffset)
if (editor.caretModel.caretCount == 1) {
state.firstRange = range
val firstLength = firstRange.length
firstRange = VimPlugin.getMotion().getWordRange(editor, caret, 1, false, false)
if (firstRange.length != firstLength) {
firstRange = TextRange(firstRange.startOffset, firstRange.startOffset + firstLength)
}
}
val startOffset = range.startOffset
val endOffset = range.endOffset
state.nextOffset = VimPlugin.getSearch().searchWord(editor, caret, 1, true, 1)
selectRange(editor, caret, startOffset, endOffset)
val endOffset = nextOffset + firstRange.length
selectRange(editor, caret, nextOffset, endOffset)
MotionGroup.moveCaret(editor, caret, endOffset, true)
nextOffset = VimPlugin.getSearch().searchNext(editor, caret, 1)
}
}
private class RemoveOccurrenceHandler : VimExtensionHandler {
inner class RemoveOccurrenceHandler : VimExtensionHandler {
override fun execute(editor: Editor, context: DataContext) {
State.instance.nextOffset = CaretData.getVisualStart(editor.caretModel.primaryCaret)
nextOffset = CaretData.getVisualStart(editor.caretModel.primaryCaret)
editor.selectionModel.removeSelection()
if (!editor.caretModel.removeCaret(editor.caretModel.primaryCaret)) {
getInstance(editor).popState()
if (CommandState.getInstance(editor).mode == Mode.VISUAL) {
CommandState.getInstance(editor).popState()
}
}
}
}
}
private fun selectRange(editor: Editor, caret: Caret, startOffset: Int, endOffset: Int) {
CaretData.setVisualStart(caret, startOffset)
VimPlugin.getMotion().updateSelection(editor, caret, endOffset)
editor.scrollingModel.scrollToCaret(ScrollType.CENTER)
}
private fun handleFirstSelection(editor: Editor, whole: Boolean) {
val caret = editor.caretModel.primaryCaret
firstRange = VimPlugin.getMotion().getWordRange(editor, caret, 1, false, false)
nextOffset = VimPlugin.getSearch().searchWord(editor, caret, 1, whole, 1)
CommandState.getInstance(editor).pushState(Mode.VISUAL, SubMode.VISUAL_CHARACTER, MappingMode.VISUAL)
selectRange(editor, caret, firstRange.startOffset, firstRange.endOffset)
MotionGroup.moveCaret(editor, caret, firstRange.endOffset, true)
}
private fun handleNextSelection(editor: Editor) {
if (!hasNext) {
VimPlugin.showMessage("No more matches")
return
}
val caret = editor.caretModel.addCaret(editor.offsetToVisualPosition(nextOffset), true)
?: throw IllegalStateException("Multiple carets are not supported")
val endOffset = nextOffset + firstRange.length
selectRange(editor, caret, nextOffset, endOffset)
MotionGroup.moveCaret(editor, caret, endOffset, true)
nextOffset = VimPlugin.getSearch().searchNext(editor, caret, 1)
}
}
private val TextRange.length: Int
get() {
if (isMultiple) throw IllegalStateException("Multiple range found")
return maxLength
}

View File

@ -270,7 +270,7 @@ class VimMultipleCursorsExtensionTest : VimTestCase() {
""".trimMargin()
configureByText(before)
typeText(parseKeys("<Plug>AllOccurrences"))
typeText(parseKeys("<Plug>AllWholeOccurrences"))
val after = """<selection>qwe</selection>
|asd
@ -292,7 +292,7 @@ class VimMultipleCursorsExtensionTest : VimTestCase() {
""".trimMargin()
configureByText(before)
typeText(parseKeys("<Plug>NotWholeAllOccurrences"))
typeText(parseKeys("<Plug>AllOccurrences"))
val after = """<selection>Int</selection>
|<selection>Int</selection>eger
|<selection>Int</selection>
@ -303,4 +303,99 @@ class VimMultipleCursorsExtensionTest : VimTestCase() {
""".trimMargin()
myFixture.checkResult(after)
}
fun testSelectSubstring() {
val before = """q<caret>we
|asdqweasd
|qwe
|asd
""".trimMargin()
configureByText(before)
typeText(parseKeys("g<A-n>".repeat(3)))
val after = """<selection>qwe</selection>
|asd<selection>qwe</selection>asd
|<selection>qwe</selection>
|asd
""".trimMargin()
myFixture.checkResult(after)
}
fun testSkipSelectionSubstring() {
val before = """qw<caret>e
|asdqweasd
|ads
|asdqweasd
|qwe
""".trimMargin()
configureByText(before)
typeText(parseKeys("g<A-n>", "<A-x>", "<A-n>".repeat(2)))
val after = """qwe
|asd<selection>qwe</selection>asd
|ads
|asd<selection>qwe</selection>asd
|<selection>qwe</selection>
""".trimMargin()
myFixture.checkResult(after)
}
fun testSelectSingleOccurrence() {
val before = """q<caret>we
|asd
|zxc
|cvb
|dfg
|rty
""".trimMargin()
configureByText(before)
typeText(parseKeys("<A-n>".repeat(4)))
val after = """<selection>qwe</selection>
|asd
|zxc
|cvb
|dfg
|rty
""".trimMargin()
myFixture.checkResult(after)
}
fun testSelectAllSingleOccurrence() {
val before = """qwe
|asd
|z<caret>xc
|adgf
|dfgh
|awe
|td
|gfhsd
|fg
""".trimMargin()
configureByText(before)
typeText(parseKeys("<Plug>AllOccurrences"))
val after = before.replace("z<caret>xc", "<selection>zxc</selection>")
myFixture.checkResult(after)
}
fun testRemoveSubSelection() {
val before = """Int
|kekInteger
|lolInteger
""".trimMargin()
configureByText(before)
typeText(parseKeys("g<A-n>", "<A-n>".repeat(2), "<A-p>"))
val after = """<selection>Int</selection>
|kek<selection>Int</selection>eger
|lolInteger
""".trimMargin()
myFixture.checkResult(after)
}
}