1
0
mirror of https://github.com/chylex/IntelliJ-IdeaVim.git synced 2025-04-23 04:15:46 +02:00

Revert per-caret registers

This commit is contained in:
chylex 2024-07-08 02:53:01 +02:00
parent ce6a296233
commit fb270cdbc5
Signed by: chylex
SSH Key Fingerprint: SHA256:WqM8X/1DDn11LbYM0H5wsqZUjbcKxVsic37L+ERcF4o
33 changed files with 382 additions and 587 deletions

View File

@ -282,4 +282,4 @@ fun VimExtensionFacade.exportOperatorFunction(name: String, function: OperatorFu
fun interface ScriptFunction {
fun execute(editor: VimEditor, context: ExecutionContext, args: Map<String, VimDataType>): ExecutionResult
}
}

View File

@ -21,9 +21,7 @@ import com.intellij.openapi.editor.markup.TextAttributes
import com.intellij.openapi.util.Disposer
import com.intellij.util.Alarm
import com.intellij.util.Alarm.ThreadToUse
import com.jetbrains.rd.util.first
import com.maddyhome.idea.vim.VimPlugin
import com.maddyhome.idea.vim.api.ImmutableVimCaret
import com.maddyhome.idea.vim.api.VimEditor
import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.common.ModeChangeListener
@ -123,9 +121,9 @@ internal class VimHighlightedYank : VimExtension, VimYankListener, ModeChangeLis
initialised = false
}
override fun yankPerformed(caretToRange: Map<ImmutableVimCaret, TextRange>) {
override fun yankPerformed(editor: VimEditor, range: TextRange) {
ensureInitialised()
highlightHandler.highlightYankRange(caretToRange)
highlightHandler.highlightYankRange(editor.ij, range)
}
override fun modeChanged(editor: VimEditor, oldMode: Mode) {
@ -146,25 +144,22 @@ internal class VimHighlightedYank : VimExtension, VimYankListener, ModeChangeLis
private var lastEditor: Editor? = null
private val highlighters = mutableSetOf<RangeHighlighter>()
fun highlightYankRange(caretToRange: Map<ImmutableVimCaret, TextRange>) {
fun highlightYankRange(editor: Editor, range: TextRange) {
// from vim-highlightedyank docs: When a new text is yanked or user starts editing, the old highlighting would be deleted
clearYankHighlighters()
val editor = caretToRange.first().key.editor.ij
lastEditor = editor
val attributes = getHighlightTextAttributes(editor)
for (range in caretToRange.values) {
for (i in 0 until range.size()) {
val highlighter = editor.markupModel.addRangeHighlighter(
range.startOffsets[i],
range.endOffsets[i],
HighlighterLayer.SELECTION,
attributes,
HighlighterTargetArea.EXACT_RANGE,
)
highlighters.add(highlighter)
}
for (i in 0 until range.size()) {
val highlighter = editor.markupModel.addRangeHighlighter(
range.startOffsets[i],
range.endOffsets[i],
HighlighterLayer.SELECTION,
attributes,
HighlighterTargetArea.EXACT_RANGE,
)
highlighters.add(highlighter)
}
// from vim-highlightedyank docs: A negative number makes the highlight persistent.
@ -282,4 +277,4 @@ internal class VimHighlightedYank : VimExtension, VimYankListener, ModeChangeLis
return default
}
}
}
}

View File

@ -29,7 +29,6 @@ import com.maddyhome.idea.vim.extension.exportOperatorFunction
import com.maddyhome.idea.vim.group.visual.VimSelection
import com.maddyhome.idea.vim.helper.exitVisualMode
import com.maddyhome.idea.vim.key.OperatorFunction
import com.maddyhome.idea.vim.newapi.IjVimCopiedText
import com.maddyhome.idea.vim.newapi.IjVimEditor
import com.maddyhome.idea.vim.newapi.ij
import com.maddyhome.idea.vim.newapi.vim
@ -154,8 +153,7 @@ private fun doReplace(editor: Editor, context: DataContext, caret: ImmutableVimC
usedType = SelectionType.CHARACTER_WISE
}
val copiedText = IjVimCopiedText(usedText, (savedRegister.copiedText as IjVimCopiedText).transferableData)
val textData = PutData.TextData(savedRegister.name, copiedText, usedType)
val textData = PutData.TextData(usedText, usedType, savedRegister.transferableData, savedRegister.name)
val putData = PutData(
textData,

View File

@ -207,7 +207,7 @@ internal class VimSurroundExtension : VimExtension {
val trimmedValue = if (newSurround.shouldTrim) innerValue.trim() else innerValue
it.first + trimmedValue + it.second
} ?: innerValue
val textData = PutData.TextData(null, injector.clipboardManager.dumbCopiedText(text), SelectionType.CHARACTER_WISE)
val textData = PutData.TextData(text, SelectionType.CHARACTER_WISE, emptyList(), null)
val putData = PutData(textData, null, 1, insertTextBeforeCaret = true, rawIndent = true, caretAfterInsertedText = false)
surrounding.caret to putData

View File

@ -25,10 +25,9 @@ import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import static com.maddyhome.idea.vim.api.VimInjectorKt.injector;
/**
* This group works with command associated with copying and pasting text
*/
@ -128,7 +127,7 @@ public class RegisterGroup extends VimRegisterGroupBase implements PersistentSta
final String text = VimPlugin.getXML().getSafeXmlText(textElement);
if (text != null) {
logger.trace("Register data parsed");
register = new Register(key, injector.getClipboardManager().dumbCopiedText(text), type);
register = new Register(key, type, text, Collections.emptyList());
}
else {
logger.trace("Cannot parse register data");

View File

@ -36,7 +36,6 @@ import com.maddyhome.idea.vim.ide.isClionNova
import com.maddyhome.idea.vim.ide.isRider
import com.maddyhome.idea.vim.mark.VimMarkConstants.MARK_CHANGE_POS
import com.maddyhome.idea.vim.newapi.IjVimCaret
import com.maddyhome.idea.vim.newapi.IjVimCopiedText
import com.maddyhome.idea.vim.newapi.IjVimEditor
import com.maddyhome.idea.vim.newapi.ij
import com.maddyhome.idea.vim.newapi.vim
@ -127,7 +126,7 @@ internal class PutGroup : VimPutBase() {
point.dispose()
if (!caret.isValid) return@forEach
val caretPossibleEndOffset = lastPastedRegion?.endOffset ?: (startOffset + text.copiedText.text.length)
val caretPossibleEndOffset = lastPastedRegion?.endOffset ?: (startOffset + text.text.length)
val endOffset = if (data.indent) {
doIndent(
vimEditor,
@ -179,10 +178,12 @@ internal class PutGroup : VimPutBase() {
val allContentsBefore = CopyPasteManager.getInstance().allContents
val sizeBeforeInsert = allContentsBefore.size
val firstItemBefore = allContentsBefore.firstOrNull()
logger.debug { "Copied text: ${text.copiedText}" }
val (textContent, transferableData) = text.copiedText as IjVimCopiedText
logger.debug { "Transferable classes: ${text.transferableData.joinToString { it.javaClass.name }}" }
val origContent: TextBlockTransferable =
injector.clipboardManager.setClipboardText(textContent, textContent, transferableData) as TextBlockTransferable
injector.clipboardManager.setClipboardText(
text.text,
transferableData = text.transferableData,
) as TextBlockTransferable
val allContentsAfter = CopyPasteManager.getInstance().allContents
val sizeAfterInsert = allContentsAfter.size
try {
@ -190,7 +191,7 @@ internal class PutGroup : VimPutBase() {
} finally {
val textInClipboard = (firstItemBefore as? TextBlockTransferable)
?.getTransferData(DataFlavor.stringFlavor) as? String
val textOnTop = textInClipboard != null && textInClipboard != text.copiedText.text
val textOnTop = textInClipboard != null && textInClipboard != text.text
if (sizeBeforeInsert != sizeAfterInsert || textOnTop) {
// Sometimes an inserted text replaces an existing one. E.g. on insert with + or * register
(CopyPasteManager.getInstance() as? CopyPasteManagerEx)?.run { removeContent(origContent) }

View File

@ -18,7 +18,6 @@ import com.intellij.openapi.editor.VisualPosition
import com.intellij.openapi.editor.markup.RangeHighlighter
import com.intellij.openapi.util.Key
import com.intellij.openapi.util.UserDataHolder
import com.maddyhome.idea.vim.api.CaretRegisterStorageBase
import com.maddyhome.idea.vim.api.LocalMarkStorage
import com.maddyhome.idea.vim.api.SelectionInfo
import com.maddyhome.idea.vim.common.InsertSequence
@ -98,7 +97,6 @@ internal var Caret.vimInsertStart: RangeMarker by userDataOr {
}
// TODO: Data could be lost during visual block motion
internal var Caret.registerStorage: CaretRegisterStorageBase? by userDataCaretToEditor()
internal var Caret.markStorage: LocalMarkStorage? by userDataCaretToEditor()
internal var Caret.lastSelectionInfo: SelectionInfo? by userDataCaretToEditor()

View File

@ -12,8 +12,6 @@ import com.intellij.openapi.editor.Caret
import com.intellij.openapi.editor.LogicalPosition
import com.intellij.openapi.editor.VisualPosition
import com.maddyhome.idea.vim.api.BufferPosition
import com.maddyhome.idea.vim.api.CaretRegisterStorage
import com.maddyhome.idea.vim.api.CaretRegisterStorageBase
import com.maddyhome.idea.vim.api.ImmutableVimCaret
import com.maddyhome.idea.vim.api.LocalMarkStorage
import com.maddyhome.idea.vim.api.SelectionInfo
@ -21,6 +19,7 @@ import com.maddyhome.idea.vim.api.VimCaret
import com.maddyhome.idea.vim.api.VimCaretBase
import com.maddyhome.idea.vim.api.VimEditor
import com.maddyhome.idea.vim.api.VimVisualPosition
import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.common.InsertSequence
import com.maddyhome.idea.vim.common.LiveRange
import com.maddyhome.idea.vim.group.visual.VisualChange
@ -29,7 +28,6 @@ import com.maddyhome.idea.vim.helper.insertHistory
import com.maddyhome.idea.vim.helper.lastSelectionInfo
import com.maddyhome.idea.vim.helper.markStorage
import com.maddyhome.idea.vim.helper.moveToInlayAwareOffset
import com.maddyhome.idea.vim.helper.registerStorage
import com.maddyhome.idea.vim.helper.resetVimLastColumn
import com.maddyhome.idea.vim.helper.vimInsertStart
import com.maddyhome.idea.vim.helper.vimLastColumn
@ -37,22 +35,14 @@ import com.maddyhome.idea.vim.helper.vimLastVisualOperatorRange
import com.maddyhome.idea.vim.helper.vimLine
import com.maddyhome.idea.vim.helper.vimSelectionStart
import com.maddyhome.idea.vim.helper.vimSelectionStartClear
import com.maddyhome.idea.vim.register.VimRegisterGroup
import com.maddyhome.idea.vim.state.mode.SelectionType
internal class IjVimCaret(val caret: Caret) : VimCaretBase() {
override val registerStorage: CaretRegisterStorage
get() {
var storage = this.caret.registerStorage
if (storage == null) {
initInjector() // To initialize injector used in CaretRegisterStorageBase
storage = CaretRegisterStorageBase(this)
this.caret.registerStorage = storage
} else if (storage.caret != this) {
storage.caret = this
}
return storage
}
override val registerStorage: VimRegisterGroup
get() = injector.registerGroup
override val markStorage: LocalMarkStorage
get() {
var storage = this.caret.markStorage

View File

@ -179,21 +179,38 @@ internal class IjVimEditor(editor: Editor) : MutableLinearEditor, VimEditorBase(
return editor.caretModel.allCarets.map { IjVimCaret(it) }
}
override var isFirstCaret = true
override var isReversingCarets = false
@Suppress("ideavimRunForEachCaret")
override fun forEachCaret(action: (VimCaret) -> Unit) {
if (editor.vim.inBlockSelection) {
action(IjVimCaret(editor.caretModel.primaryCaret))
} else {
editor.caretModel.runForEachCaret({
if (it.isValid) {
action(IjVimCaret(it))
}
}, false)
try {
editor.caretModel.runForEachCaret({
if (it.isValid) {
action(IjVimCaret(it))
isFirstCaret = false
}
}, false)
} finally {
isFirstCaret = true
}
}
}
override fun forEachNativeCaret(action: (VimCaret) -> Unit, reverse: Boolean) {
editor.caretModel.runForEachCaret({ action(IjVimCaret(it)) }, reverse)
isReversingCarets = reverse
try {
editor.caretModel.runForEachCaret({
action(IjVimCaret(it))
isFirstCaret = false
}, reverse)
} finally {
isFirstCaret = true
isReversingCarets = false
}
}
override fun isInForEachCaretScope(): Boolean {

View File

@ -9,6 +9,7 @@
package org.jetbrains.plugins.ideavim.ex.implementation.variables
import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.newapi.vim
import org.jetbrains.plugins.ideavim.VimTestCase
import org.junit.jupiter.api.Test
import kotlin.test.assertEquals
@ -43,10 +44,12 @@ class RegisterVariableTest : VimTestCase() {
configureByText("abcd")
enterCommand("""vnoremap <expr> y '"' . v:register . 'y'""")
typeText("vl\"zy")
val register = injector.registerGroup.getRegisters()
val vimEditor = fixture.editor.vim
val context = injector.executionContextManager.getEditorExecutionContext(vimEditor)
val register = injector.registerGroup.getRegisters(vimEditor, context)
.filter { reg -> reg.name == 'z' }
.first()
assertEquals("ab", register.text)
}
}
}

View File

@ -69,15 +69,10 @@ class InsertRegisterAction : VimActionHandler.SingleExecution() {
*/
@RWLockLabel.SelfSynchronized
private fun insertRegister(editor: VimEditor, context: ExecutionContext, key: Char): Boolean {
val register: Register? = injector.registerGroup.getRegister(editor, context, key)
val register: Register? = injector.registerGroup.getRegister(key)
if (register != null) {
val textData = PutData.TextData(
register.name,
injector.clipboardManager.dumbCopiedText(register.text),
SelectionType.CHARACTER_WISE
)
val putData =
PutData(textData, null, 1, insertTextBeforeCaret = true, rawIndent = true, caretAfterInsertedText = true)
val textData = PutData.TextData(register.text, SelectionType.CHARACTER_WISE, emptyList(), register.name)
val putData = PutData(textData, null, 1, insertTextBeforeCaret = true, rawIndent = true, caretAfterInsertedText = true)
injector.put.putText(editor, context, putData)
return true
}

View File

@ -10,7 +10,6 @@ package com.maddyhome.idea.vim.action.copy
import com.intellij.vim.annotations.CommandOrMotion
import com.intellij.vim.annotations.Mode
import com.maddyhome.idea.vim.api.ExecutionContext
import com.maddyhome.idea.vim.api.ImmutableVimCaret
import com.maddyhome.idea.vim.api.VimEditor
import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.command.Argument
@ -36,33 +35,40 @@ sealed class PutTextBaseAction(
val count = operatorArguments.count1
val sortedCarets = editor.sortedCarets()
return if (sortedCarets.size > 1) {
val caretToPutData = sortedCarets.associateWith { getPutDataForCaret(editor, context, it, count) }
val putData = getPutData(count)
val splitText = putData.textData?.rawText?.split('\n')?.dropLastWhile(String::isEmpty)
val caretToPutData = if (splitText != null && splitText.size == sortedCarets.size) {
sortedCarets.mapIndexed { index, caret -> caret to putData.copy(textData = putData.textData.copy(rawText = splitText[splitText.lastIndex - index])) }.toMap()
} else {
sortedCarets.associateWith { putData }
}
var result = true
caretToPutData.forEach {
result = injector.put.putTextForCaret(editor, it.key, context, it.value) && result
}
result
} else {
val putData = getPutDataForCaret(editor, context, sortedCarets.single(), count)
injector.put.putText(editor, context, putData)
injector.put.putText(editor, context, getPutData(count))
}
}
private fun getPutDataForCaret(
editor: VimEditor,
context: ExecutionContext,
caret: ImmutableVimCaret,
count: Int,
private fun getPutData(count: Int,
): PutData {
val registerService = injector.registerGroup
val registerChar = if (caret.editor.carets().size == 1) {
registerService.currentRegister
} else {
registerService.getCurrentRegisterForMulticaret()
}
val register = caret.registerStorage.getRegister(editor, context, registerChar)
val textData = register?.let { TextData(register) }
return PutData(textData, null, count, insertTextBeforeCaret, indent, caretAfterInsertedText, -1)
return PutData(getRegisterTextData(), null, count, insertTextBeforeCaret, indent, caretAfterInsertedText, -1)
}
}
fun getRegisterTextData(): TextData? {
val register = injector.registerGroup.getRegister(injector.registerGroup.currentRegister)
return register?.let {
TextData(
register.text ?: injector.parser.toPrintableString(register.keys),
register.type,
register.transferableData,
register.name,
)
}
}

View File

@ -41,8 +41,22 @@ sealed class PutVisualTextBaseAction(
): Boolean {
if (caretsAndSelections.isEmpty()) return false
val count = cmd.count
val caretToPutData =
editor.sortedCarets().associateWith { getPutDataForCaret(editor, context, it, caretsAndSelections[it], count) }
val sortedCarets =
editor.sortedCarets()
val textData = getRegisterTextData()
val splitText = textData?.rawText?.split('\n')?.dropLastWhile(String::isEmpty)
val caretToTextData = if (splitText != null && splitText.size == sortedCarets.size) {
sortedCarets.mapIndexed { index, caret -> caret to textData.copy(rawText = splitText[splitText.lastIndex - index]) }.toMap()
} else {
sortedCarets.associateWith { textData }
}
val caretToPutData = caretToTextData.mapValues { (caret, textData) ->
getPutDataForCaret(textData, caret, caretsAndSelections[caret], count)
}
injector.registerGroup.resetRegister()
var result = true
caretToPutData.forEach {
@ -50,17 +64,11 @@ sealed class PutVisualTextBaseAction(
}
return result
}
private fun getPutDataForCaret(
editor: VimEditor,
context: ExecutionContext,
private fun getPutDataForCaret(textData: PutData.TextData?,
caret: VimCaret,
selection: VimSelection?,
count: Int,
): PutData {
val lastRegisterChar = injector.registerGroup.lastRegisterChar
val register = caret.registerStorage.getRegister(editor, context, lastRegisterChar)
val textData = register?.let { PutData.TextData(register) }
count: Int,): PutData {
val visualSelection = selection?.let { PutData.VisualSelection(mapOf(caret to it), it.type) }
return PutData(textData, visualSelection, count, insertTextBeforeCaret, indent, caretAfterInsertedText)
}

View File

@ -9,7 +9,6 @@
package com.maddyhome.idea.vim.api
import com.maddyhome.idea.vim.common.LiveRange
import com.maddyhome.idea.vim.common.TextRange
import com.maddyhome.idea.vim.group.visual.VisualChange
import com.maddyhome.idea.vim.group.visual.vimMoveBlockSelectionToOffset
import com.maddyhome.idea.vim.group.visual.vimMoveSelectionToCaret
@ -17,13 +16,11 @@ import com.maddyhome.idea.vim.handler.Motion
import com.maddyhome.idea.vim.helper.RWLockLabel
import com.maddyhome.idea.vim.helper.StrictMode
import com.maddyhome.idea.vim.helper.exitVisualMode
import com.maddyhome.idea.vim.register.Register
import com.maddyhome.idea.vim.state.mode.SelectionType
import com.maddyhome.idea.vim.register.VimRegisterGroup
import com.maddyhome.idea.vim.state.mode.inBlockSelection
import com.maddyhome.idea.vim.state.mode.inCommandLineModeWithVisual
import com.maddyhome.idea.vim.state.mode.inSelectMode
import com.maddyhome.idea.vim.state.mode.inVisualMode
import javax.swing.KeyStroke
/**
* Immutable interface of the caret. Immutable caret is an important concept of Fleet.
@ -65,7 +62,7 @@ interface ImmutableVimCaret {
fun hasSelection(): Boolean
var lastSelectionInfo: SelectionInfo
val registerStorage: CaretRegisterStorage
val registerStorage: VimRegisterGroup
val markStorage: LocalMarkStorage
}
@ -149,36 +146,3 @@ fun VimCaret.moveToMotion(motion: Motion): VimCaret {
this
}
}
interface CaretRegisterStorage {
val caret: ImmutableVimCaret
/**
* Stores text to caret's recordable (named/numbered/unnamed) register
*/
@Deprecated("Please use the same method, but with ExecutionContext")
fun storeText(editor: VimEditor, range: TextRange, type: SelectionType, isDelete: Boolean): Boolean
fun storeText(
editor: VimEditor,
context: ExecutionContext,
range: TextRange,
type: SelectionType,
isDelete: Boolean,
): Boolean
/**
* Gets text from caret's recordable register
* If the register is not recordable - global text state will be returned
*/
@Deprecated("Please use com.maddyhome.idea.vim.api.CaretRegisterStorage#getRegister(com.maddyhome.idea.vim.api.VimEditor, com.maddyhome.idea.vim.api.ExecutionContext, char)")
fun getRegister(r: Char): Register?
fun getRegister(editor: VimEditor, context: ExecutionContext, r: Char): Register?
@Deprecated("Please use com.maddyhome.idea.vim.api.CaretRegisterStorage#setKeys(com.maddyhome.idea.vim.api.VimEditor, com.maddyhome.idea.vim.api.ExecutionContext, char, java.util.List<? extends javax.swing.KeyStroke>)")
fun setKeys(register: Char, keys: List<KeyStroke>)
fun setKeys(editor: VimEditor, context: ExecutionContext, register: Char, keys: List<KeyStroke>)
@Deprecated("Please use com.maddyhome.idea.vim.api.CaretRegisterStorage#saveRegister(com.maddyhome.idea.vim.api.VimEditor, com.maddyhome.idea.vim.api.ExecutionContext, char, com.maddyhome.idea.vim.register.Register)")
fun saveRegister(r: Char, register: Register)
fun saveRegister(editor: VimEditor, context: ExecutionContext, r: Char, register: Register)
}

View File

@ -8,106 +8,4 @@
package com.maddyhome.idea.vim.api
import com.maddyhome.idea.vim.common.TextRange
import com.maddyhome.idea.vim.register.Register
import com.maddyhome.idea.vim.register.RegisterConstants
import com.maddyhome.idea.vim.register.VimRegisterGroupBase
import com.maddyhome.idea.vim.state.mode.SelectionType
import javax.swing.KeyStroke
abstract class VimCaretBase : VimCaret
open class CaretRegisterStorageBase(override var caret: ImmutableVimCaret) : CaretRegisterStorage,
VimRegisterGroupBase() {
companion object {
private const val ALLOWED_TO_STORE_REGISTERS = RegisterConstants.RECORDABLE_REGISTERS +
RegisterConstants.SMALL_DELETION_REGISTER +
RegisterConstants.BLACK_HOLE_REGISTER +
RegisterConstants.LAST_INSERTED_TEXT_REGISTER +
RegisterConstants.LAST_SEARCH_REGISTER
}
override var lastRegisterChar: Char
get() {
return injector.registerGroup.lastRegisterChar
}
set(_) {}
override var isRegisterSpecifiedExplicitly: Boolean
get() {
return injector.registerGroup.isRegisterSpecifiedExplicitly
}
set(_) {}
@Deprecated("Please use the same method, but with ExecutionContext")
override fun storeText(editor: VimEditor, range: TextRange, type: SelectionType, isDelete: Boolean): Boolean {
val context = injector.executionContextManager.getEditorExecutionContext(editor)
return storeText(editor, context, caret, range, type, isDelete)
}
override fun storeText(
editor: VimEditor,
context: ExecutionContext,
range: TextRange,
type: SelectionType,
isDelete: Boolean,
): Boolean {
val registerChar = if (caret.editor.carets().size == 1) currentRegister else getCurrentRegisterForMulticaret()
if (caret.isPrimary) {
val registerService = injector.registerGroup
registerService.lastRegisterChar = registerChar
return registerService.storeText(editor, context, caret, range, type, isDelete)
} else {
if (!ALLOWED_TO_STORE_REGISTERS.contains(registerChar)) {
return false
}
val text = preprocessTextBeforeStoring(editor.getText(range), type)
return storeTextInternal(editor, context, range, text, type, registerChar, isDelete)
}
}
override fun getRegister(r: Char): Register? {
val editorStub = injector.fallbackWindow
val contextStub = injector.executionContextManager.getEditorExecutionContext(editorStub)
return getRegister(editorStub, contextStub, r)
}
override fun getRegister(editor: VimEditor, context: ExecutionContext, r: Char): Register? {
if (caret.isPrimary || !RegisterConstants.RECORDABLE_REGISTERS.contains(r)) {
return injector.registerGroup.getRegister(editor, context, r)
}
return super.getRegister(editor, context, r) ?: injector.registerGroup.getRegister(editor, context, r)
}
override fun setKeys(register: Char, keys: List<KeyStroke>) {
val editorStub = injector.fallbackWindow
val contextStub = injector.executionContextManager.getEditorExecutionContext(editorStub)
setKeys(editorStub, contextStub, register, keys)
}
override fun setKeys(editor: VimEditor, context: ExecutionContext, register: Char, keys: List<KeyStroke>) {
if (caret.isPrimary) {
injector.registerGroup.setKeys(register, keys)
}
if (!RegisterConstants.RECORDABLE_REGISTERS.contains(register)) {
return
}
return super.setKeys(register, keys)
}
override fun saveRegister(r: Char, register: Register) {
val editorStub = injector.fallbackWindow
val contextStub = injector.executionContextManager.getEditorExecutionContext(editorStub)
saveRegister(editorStub, contextStub, r, register)
}
override fun saveRegister(editor: VimEditor, context: ExecutionContext, r: Char, register: Register) {
if (caret.isPrimary) {
injector.registerGroup.saveRegister(editor, context, r, register)
}
if (!RegisterConstants.RECORDABLE_REGISTERS.contains(r)) {
return
}
return super.saveRegister(editor, context, r, register)
}
}

View File

@ -182,31 +182,41 @@ abstract class VimChangeGroupBase : VimChangeGroup {
return false
}
}
val isInsertMode = editor.mode == Mode.INSERT || editor.mode == Mode.REPLACE
val shouldYank = type != null && !isInsertMode && saveToRegister
if (shouldYank && !caret.registerStorage.storeText(editor, context, updatedRange, type, isDelete = true)) {
return false
}
val startOffsets = updatedRange.startOffsets
val endOffsets = updatedRange.endOffsets
for (i in updatedRange.size() - 1 downTo 0) {
val (newRange, _) = editor.search(
startOffsets[i] to endOffsets[i],
val mode = editor.mode
if (type == null ||
(mode == Mode.INSERT || mode == Mode.REPLACE) ||
!saveToRegister ||
injector.registerGroup.storeText(
editor,
LineDeleteShift.NL_ON_END
) ?: continue
injector.application.runWriteAction {
context,
caret,
updatedRange,
type,
true,
!editor.isFirstCaret,
editor.isReversingCarets
)
) {
val startOffsets = updatedRange.startOffsets
val endOffsets = updatedRange.endOffsets
for (i in updatedRange.size() - 1 downTo 0) {
val (newRange, _) = editor.search(
startOffsets[i] to endOffsets[i],
editor,
LineDeleteShift.NL_ON_END
) ?: continue
injector.application.runWriteAction {
editor.deleteString(TextRange(newRange.first, newRange.second))
}
}
if (type != null) {
val start = updatedRange.startOffset
injector.markService.setMark(caret, MARK_CHANGE_POS, start)
injector.markService.setChangeMarks(caret, TextRange(start, start + 1))
}
return true
}
if (type != null) {
val start = updatedRange.startOffset
injector.markService.setMark(caret, MARK_CHANGE_POS, start)
injector.markService.setChangeMarks(caret, TextRange(start, start + 1))
}
return true
return false
}
/**

View File

@ -111,7 +111,8 @@ interface VimEditor {
* This method should perform caret merging after the operations. This is similar to IJ runForEachCaret
* TODO review
*/
val isFirstCaret: Boolean
val isReversingCarets: Boolean
fun forEachCaret(action: (VimCaret) -> Unit)
fun forEachNativeCaret(action: (VimCaret) -> Unit, reverse: Boolean = false)
fun isInForEachCaretScope(): Boolean

View File

@ -17,8 +17,6 @@ import com.maddyhome.idea.vim.common.VimListenersNotifier
import com.maddyhome.idea.vim.diagnostic.VimLogger
import com.maddyhome.idea.vim.diagnostic.vimLogger
import com.maddyhome.idea.vim.impl.state.VimStateMachineImpl
import com.maddyhome.idea.vim.register.VimRegisterGroup
import com.maddyhome.idea.vim.register.VimRegisterGroupBase
import com.maddyhome.idea.vim.state.VimStateMachine
import com.maddyhome.idea.vim.vimscript.services.VariableService
import com.maddyhome.idea.vim.vimscript.services.VimVariableServiceBase
@ -28,7 +26,6 @@ import com.maddyhome.idea.vim.yank.YankGroupBase
abstract class VimInjectorBase : VimInjector {
companion object {
val logger: VimLogger by lazy { vimLogger<VimInjectorBase>() }
val registerGroupStub: VimRegisterGroupBase by lazy { object : VimRegisterGroupBase() {} }
}
override val vimState: VimStateMachine = VimStateMachineImpl()
@ -38,8 +35,6 @@ abstract class VimInjectorBase : VimInjector {
override val variableService: VariableService by lazy { object : VimVariableServiceBase() {} }
override val registerGroup: VimRegisterGroup by lazy { registerGroupStub }
override val registerGroupIfCreated: VimRegisterGroup? by lazy { registerGroupStub }
override val messages: VimMessages by lazy { VimMessagesStub() }
override val processGroup: VimProcessGroup by lazy { VimProcessGroupStub() }
override val application: VimApplication by lazy { VimApplicationStub() }

View File

@ -8,7 +8,6 @@
package com.maddyhome.idea.vim.common
import com.maddyhome.idea.vim.api.ImmutableVimCaret
import com.maddyhome.idea.vim.api.VimEditor
import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.state.mode.Mode
@ -72,9 +71,9 @@ class VimListenersNotifier {
isReplaceCharListeners.forEach { it.isReplaceCharChanged(editor) }
}
fun notifyYankPerformed(caretToRange: Map<ImmutableVimCaret, TextRange>) {
fun notifyYankPerformed(editor: VimEditor, range: TextRange) {
if (!injector.enabler.isEnabled()) return // we remove all the listeners when turning the plugin off, but let's do it just in case
yankListeners.forEach { it.yankPerformed(caretToRange) }
yankListeners.forEach { it.yankPerformed(editor, range) }
}
fun reset() {

View File

@ -8,8 +8,8 @@
package com.maddyhome.idea.vim.common
import com.maddyhome.idea.vim.api.ImmutableVimCaret
import com.maddyhome.idea.vim.api.VimEditor
interface VimYankListener {
fun yankPerformed(caretToRange: Map<ImmutableVimCaret, TextRange>)
}
fun yankPerformed(editor: VimEditor, range: TextRange)
}

View File

@ -8,11 +8,11 @@
package com.maddyhome.idea.vim.put
import com.maddyhome.idea.vim.common.VimCopiedText
import com.maddyhome.idea.vim.state.mode.SelectionType
data class ProcessedTextData(
val registerChar: Char?,
val copiedText: VimCopiedText,
val text: String,
val typeInRegister: SelectionType,
val transferableData: List<Any>,
val registerChar: Char?,
)

View File

@ -9,9 +9,7 @@
package com.maddyhome.idea.vim.put
import com.maddyhome.idea.vim.api.VimCaret
import com.maddyhome.idea.vim.common.VimCopiedText
import com.maddyhome.idea.vim.group.visual.VimSelection
import com.maddyhome.idea.vim.register.Register
import com.maddyhome.idea.vim.state.mode.SelectionType
/**
@ -35,12 +33,9 @@ data class PutData(
)
data class TextData(
val registerChar: Char?,
val copiedText: VimCopiedText,
val rawText: String?,
val typeInRegister: SelectionType,
) {
constructor(register: Register) : this(register.name, register.copiedText, register.type)
val rawText = copiedText.text // TODO do not call it raw text...
}
val transferableData: List<Any>,
val registerChar: Char?,
)
}

View File

@ -141,7 +141,6 @@ abstract class VimPutBase : VimPut {
if (data.visualSelection?.typeInEditor?.isLine == true && data.textData.typeInRegister.isChar) text += "\n"
// TODO: shouldn't it be adjusted when we are storing the text?
if (data.textData.typeInRegister.isLine && text.isNotEmpty() && text.last() != '\n') text += '\n'
if (data.textData.typeInRegister.isChar && text.lastOrNull() == '\n' && data.visualSelection?.typeInEditor?.isLine == false) {
@ -150,9 +149,10 @@ abstract class VimPutBase : VimPut {
}
return ProcessedTextData(
data.textData.registerChar,
data.textData.copiedText.updateText(text),
text,
data.textData.typeInRegister,
data.textData.transferableData,
data.textData.registerChar,
)
}
@ -512,7 +512,7 @@ abstract class VimPutBase : VimPut {
startOffsets.forEach { startOffset ->
val selectionType = data.visualSelection?.typeInEditor ?: SelectionType.CHARACTER_WISE
val (endOffset, updatedCaret) = putTextInternal(
editor, updated, context, text.copiedText.text, text.typeInRegister, selectionType,
editor, updated, context, text.text, text.typeInRegister, selectionType,
startOffset, data.count, data.indent, data.caretAfterInsertedText,
)
updated = updatedCaret

View File

@ -633,7 +633,13 @@ class VimRegex(pattern: String) {
override fun carets(): List<VimCaret> = emptyList()
override fun nativeCarets(): List<VimCaret> = emptyList()
override val isFirstCaret: Boolean
get() = false
override val isReversingCarets: Boolean
get() = false
override fun forEachCaret(action: (VimCaret) -> Unit) {}
override fun forEachNativeCaret(action: (VimCaret) -> Unit, reverse: Boolean) {}

View File

@ -8,32 +8,86 @@
package com.maddyhome.idea.vim.register
import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.common.VimCopiedText
import com.maddyhome.idea.vim.helper.EngineStringHelper
import com.maddyhome.idea.vim.state.mode.SelectionType
import org.jetbrains.annotations.NonNls
import java.awt.event.KeyEvent
import javax.swing.KeyStroke
// TODO should we prefer keys over text, as they are more informative?
// TODO e.g.  could be both <Esc> and <C-[> after trying to restore original keys
data class Register(
val name: Char,
val copiedText: VimCopiedText,
val type: SelectionType,
) {
val text = copiedText.text
val keys get() = injector.parser.stringToKeys(copiedText.text)
val printableString: String =
EngineStringHelper.toPrintableCharacters(keys) // should be the same as [text], but we can't render control notation properly
class Register {
var name: Char
val type: SelectionType
val keys: MutableList<KeyStroke>
val transferableData: MutableList<out Any>
val rawText: String?
constructor(name: Char, type: SelectionType, keys: MutableList<KeyStroke>) : this(
name,
injector.clipboardManager.dumbCopiedText(injector.parser.toPrintableString(keys)),
type
)
// constructor(name: Char, type: SelectionType, text: String, transferableData: MutableList<out Any>) : this(name, text, type, transferableData)
constructor(name: Char, type: SelectionType, keys: MutableList<KeyStroke>) {
this.name = name
this.type = type
this.keys = keys
this.transferableData = mutableListOf()
this.rawText = text
}
override fun toString(): String = "@$name = $printableString"
constructor(
name: Char,
type: SelectionType,
text: String,
transferableData: MutableList<out Any>,
) {
this.name = name
this.type = type
this.keys = injector.parser.stringToKeys(text).toMutableList()
this.transferableData = transferableData
this.rawText = text
}
constructor(
name: Char,
type: SelectionType,
text: String,
transferableData: MutableList<out Any>,
rawText: String,
) {
this.name = name
this.type = type
this.keys = injector.parser.stringToKeys(text).toMutableList()
this.transferableData = transferableData
this.rawText = rawText
}
val text: String?
get() {
val builder = StringBuilder()
for (key in keys) {
val c = key.keyChar
if (c == KeyEvent.CHAR_UNDEFINED) {
return null
}
builder.append(c)
}
return builder.toString()
}
val printableString: String
get() = EngineStringHelper.toPrintableCharacters(keys) // should be the same as [text], but we can't render control notation properly
/**
* Append the supplied text to any existing text.
*/
fun addTextAndResetTransferableData(text: String) {
addKeys(injector.parser.stringToKeys(text))
transferableData.clear()
}
fun prependTextAndResetTransferableData(text: String) {
this.keys.addAll(0, injector.parser.stringToKeys(text))
transferableData.clear()
}
fun addKeys(keys: List<KeyStroke>) {
this.keys.addAll(keys)
}
object KeySorter : Comparator<Register> {
@NonNls
@ -44,27 +98,3 @@ data class Register(
}
}
}
/**
* Imagine you yanked two lines and have the following content in your register a - foo\nbar\n (register type is line-wise)
* Now, there are three different ways to append content, each with a different outcome:
* - If you append a macro qAbazq, you'll get foo\nbarbaz\n in register `a` and it stays line-wise
* - If you use Vim script and execute let @A = "baz", the result will be foo\nbar\nbaz and the register becomes character-wise
* - If you copy "baz" to register A, it becomes foo\nbar\nbaz\n and stays line-wise
*
* At the moment, we will stick to the third option to not overcomplicate the plugin
* (until there is a user who notices the difference)
*/
fun Register.addText(text: String): Register {
return when (this.type) {
SelectionType.CHARACTER_WISE -> {
Register(this.name, injector.clipboardManager.dumbCopiedText(this.text + text), SelectionType.CHARACTER_WISE) // todo it's empty for historical reasons, but should we really clear transferable data?
}
SelectionType.LINE_WISE -> {
Register(this.name, injector.clipboardManager.dumbCopiedText(this.text + text + (if (text.endsWith('\n')) "" else "\n")), SelectionType.LINE_WISE) // todo it's empty for historical reasons, but should we really clear transferable data?
}
SelectionType.BLOCK_WISE -> {
Register(this.name, injector.clipboardManager.dumbCopiedText(this.text + "\n" + text), SelectionType.BLOCK_WISE) // todo it's empty for historical reasons, but should we really clear transferable data?
}
}
}

View File

@ -17,6 +17,13 @@ import javax.swing.KeyStroke
interface VimRegisterGroup {
/**
* Get the last register selected by the user
*
* @return The register, null if no such register
*/
@Deprecated("Please use com.maddyhome.idea.vim.register.VimRegisterGroup#getLastRegister(com.maddyhome.idea.vim.api.VimEditor, com.maddyhome.idea.vim.api.ExecutionContext)")
val lastRegister: Register?
var lastRegisterChar: Char
val currentRegister: Char
@ -32,7 +39,6 @@ interface VimRegisterGroup {
val isRegisterSpecifiedExplicitly: Boolean
val defaultRegister: Char
fun getLastRegister(editor: VimEditor, context: ExecutionContext): Register?
fun isValid(reg: Char): Boolean
fun selectRegister(reg: Char): Boolean
fun resetRegister()
@ -42,15 +48,6 @@ interface VimRegisterGroup {
fun isRegisterWritable(reg: Char): Boolean
/** Store text into the last register. */
@Deprecated("Please use com.maddyhome.idea.vim.register.VimRegisterGroup#storeText(com.maddyhome.idea.vim.api.VimEditor, com.maddyhome.idea.vim.api.ExecutionContext, com.maddyhome.idea.vim.api.ImmutableVimCaret, com.maddyhome.idea.vim.common.TextRange, com.maddyhome.idea.vim.state.mode.SelectionType, boolean)")
fun storeText(
editor: VimEditor,
caret: ImmutableVimCaret,
range: TextRange,
type: SelectionType,
isDelete: Boolean,
): Boolean
fun storeText(
editor: VimEditor,
context: ExecutionContext,
@ -58,31 +55,20 @@ interface VimRegisterGroup {
range: TextRange,
type: SelectionType,
isDelete: Boolean,
forceAppend: Boolean = false,
prependInsteadOfAppend: Boolean = false
): Boolean
/**
* Stores text to any writable register (used for the let command)
*/
@Deprecated("Please use com.maddyhome.idea.vim.register.VimRegisterGroup#storeText(com.maddyhome.idea.vim.api.VimEditor, com.maddyhome.idea.vim.api.ExecutionContext, char, java.lang.String)")
fun storeText(register: Char, text: String): Boolean
fun storeText(editor: VimEditor, context: ExecutionContext, register: Char, text: String): Boolean
/**
* Stores text to any writable register (used for multicaret tests)
*/
@TestOnly
// todo better tests
@Deprecated("Please use com.maddyhome.idea.vim.register.VimRegisterGroup#storeText(com.maddyhome.idea.vim.api.VimEditor, com.maddyhome.idea.vim.api.ExecutionContext, char, java.lang.String, com.maddyhome.idea.vim.state.mode.SelectionType)")
fun storeText(register: Char, text: String, selectionType: SelectionType): Boolean
@TestOnly
fun storeText(
editor: VimEditor,
context: ExecutionContext,
register: Char,
text: String,
selectionType: SelectionType,
): Boolean
fun storeText(editor: VimEditor, context: ExecutionContext, register: Char, text: String, selectionType: SelectionType): Boolean
/**
* Stores text, character wise, in the given special register
@ -98,29 +84,17 @@ interface VimRegisterGroup {
* preferable to yank from the fixture editor.
*/
fun storeTextSpecial(register: Char, text: String): Boolean
@Deprecated("Please use com.maddyhome.idea.vim.register.VimRegisterGroup#getRegister(com.maddyhome.idea.vim.api.VimEditor, com.maddyhome.idea.vim.api.ExecutionContext, char)")
fun getRegister(r: Char): Register?
fun getRegister(editor: VimEditor, context: ExecutionContext, r: Char): Register?
@Deprecated("Please use com.maddyhome.idea.vim.register.VimRegisterGroup#getRegisters(com.maddyhome.idea.vim.api.VimEditor, com.maddyhome.idea.vim.api.ExecutionContext)")
fun getRegisters(): List<Register>
fun getRegisters(editor: VimEditor, context: ExecutionContext): List<Register>
@Deprecated("Please use com.maddyhome.idea.vim.register.VimRegisterGroup#saveRegister(com.maddyhome.idea.vim.api.VimEditor, com.maddyhome.idea.vim.api.ExecutionContext, char, com.maddyhome.idea.vim.register.Register)")
fun saveRegister(r: Char, register: Register)
fun saveRegister(editor: VimEditor, context: ExecutionContext, r: Char, register: Register)
fun startRecording(register: Char): Boolean
@Deprecated("Please use com.maddyhome.idea.vim.register.VimRegisterGroup#getPlaybackRegister(com.maddyhome.idea.vim.api.VimEditor, com.maddyhome.idea.vim.api.ExecutionContext, char)")
fun getPlaybackRegister(r: Char): Register?
fun getPlaybackRegister(editor: VimEditor, context: ExecutionContext, r: Char): Register?
fun recordText(text: String)
fun setKeys(register: Char, keys: List<KeyStroke>)
fun setKeys(register: Char, keys: List<KeyStroke>, type: SelectionType)
@Deprecated("Please use com.maddyhome.idea.vim.register.VimRegisterGroup#finishRecording(com.maddyhome.idea.vim.api.VimEditor, com.maddyhome.idea.vim.api.ExecutionContext)")
fun finishRecording()
fun finishRecording(editor: VimEditor, context: ExecutionContext)
fun getCurrentRegisterForMulticaret(): Char // `set clipbaard+=unnamedplus` should not make system register the default one when working with multiple carets VIM-2804
fun isSystemClipboard(register: Char): Boolean

View File

@ -17,7 +17,6 @@ import com.maddyhome.idea.vim.api.globalOptions
import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.command.Argument
import com.maddyhome.idea.vim.common.TextRange
import com.maddyhome.idea.vim.common.VimCopiedText
import com.maddyhome.idea.vim.diagnostic.VimLogger
import com.maddyhome.idea.vim.diagnostic.debug
import com.maddyhome.idea.vim.diagnostic.vimLogger
@ -75,9 +74,13 @@ abstract class VimRegisterGroupBase : VimRegisterGroup {
override val defaultRegister: Char
get() = defaultRegisterChar
override fun getLastRegister(editor: VimEditor, context: ExecutionContext): Register? {
return getRegister(editor, context, lastRegisterChar)
}
/**
* Get the last register selected by the user
*
* @return The register, null if no such register
*/
override val lastRegister: Register?
get() = getRegister(lastRegisterChar)
private val onClipboardChanged: () -> Unit = {
val clipboardOptionValue = injector.globalOptions().clipboard
@ -113,13 +116,20 @@ abstract class VimRegisterGroupBase : VimRegisterGroup {
return if (isValid(reg)) {
isRegisterSpecifiedExplicitly = true
lastRegisterChar = reg
logger.debug { "register selected: $lastRegisterChar" }
logger.debug { "register selected: $lastRegister" }
true
} else {
false
}
}
override fun getRegister(r: Char): Register? {
val dummyEditor = injector.fallbackWindow
val dummyContext = injector.executionContextManager.getEditorExecutionContext(dummyEditor)
return getRegister(dummyEditor, dummyContext, r)
}
/**
* Reset the selected register back to the default register.
*/
@ -175,6 +185,8 @@ abstract class VimRegisterGroupBase : VimRegisterGroup {
type: SelectionType,
register: Char,
isDelete: Boolean,
forceAppend: Boolean,
prependInsteadOfAppend: Boolean,
): Boolean {
// Null register doesn't get saved, but acts like it was
if (lastRegisterChar == BLACK_HOLE_REGISTER) return true
@ -193,50 +205,62 @@ abstract class VimRegisterGroupBase : VimRegisterGroup {
end = t
}
val copiedText =
if (start != -1) { // FIXME: so, we had invalid ranges all the time?.. I've never handled such cases
injector.clipboardManager.collectCopiedText(editor, context, range, text)
} else {
injector.clipboardManager.dumbCopiedText(text)
}
logger.debug { "Copy to '$lastRegisterChar' with copied text: $copiedText" }
// If this is an uppercase register, we need to append the text to the corresponding lowercase register
if (Character.isUpperCase(register)) {
val transferableData: List<Any> =
if (start != -1) injector.clipboardManager.getTransferableData(editor, range) else ArrayList()
var processedText =
if (start != -1) injector.clipboardManager.preprocessText(editor, range, text, transferableData) else text
logger.debug {
val transferableClasses = transferableData.joinToString(",") { it.javaClass.name }
"Copy to '$lastRegister' with transferable data: $transferableClasses"
}
if (Character.isUpperCase(register) || forceAppend) {
if (forceAppend && type == SelectionType.CHARACTER_WISE) {
processedText = if (prependInsteadOfAppend)
processedText + '\n'
else
'\n' + processedText
}
val lreg = Character.toLowerCase(register)
val r = myRegisters[lreg]
// Append the text if the lowercase register existed
if (r != null) {
myRegisters[lreg] = r.addText(copiedText.text)
if (prependInsteadOfAppend) {
r.prependTextAndResetTransferableData(processedText)
}
else {
r.addTextAndResetTransferableData(processedText)
}
} else {
myRegisters[lreg] = Register(lreg, copiedText, type)
logger.debug { "register '$register' contains: \"$copiedText\"" }
myRegisters[lreg] = Register(lreg, type, processedText, ArrayList(transferableData))
logger.debug { "register '$register' contains: \"$processedText\"" }
} // Set the text if the lowercase register didn't exist yet
} else {
myRegisters[register] = Register(register, copiedText, type)
logger.debug { "register '$register' contains: \"$copiedText\"" }
myRegisters[register] = Register(register, type, processedText, ArrayList(transferableData))
logger.debug { "register '$register' contains: \"$processedText\"" }
} // Put the text in the specified register
if (register == CLIPBOARD_REGISTER) {
injector.clipboardManager.setClipboardContent(editor, context, copiedText)
injector.clipboardManager.setClipboardText(processedText, text, ArrayList(transferableData))
if (!isRegisterSpecifiedExplicitly && !isDelete && isPrimaryRegisterSupported() && OptionConstants.clipboard_unnamedplus in injector.globalOptions().clipboard) {
injector.clipboardManager.setPrimaryContent(editor, context, copiedText)
injector.clipboardManager.setPrimaryText(processedText, text, ArrayList(transferableData))
}
}
if (register == PRIMARY_REGISTER) {
if (isPrimaryRegisterSupported()) {
injector.clipboardManager.setPrimaryContent(editor, context, copiedText)
injector.clipboardManager.setPrimaryText(processedText, text, ArrayList(transferableData))
if (!isRegisterSpecifiedExplicitly && !isDelete && OptionConstants.clipboard_unnamed in injector.globalOptions().clipboard) {
injector.clipboardManager.setClipboardContent(editor, context, copiedText)
injector.clipboardManager.setClipboardText(processedText, text, ArrayList(transferableData))
}
} else {
injector.clipboardManager.setClipboardContent(editor, context, copiedText)
injector.clipboardManager.setClipboardText(processedText, text, ArrayList(transferableData))
}
}
// Also add it to the unnamed register if the default wasn't specified
if (register != UNNAMED_REGISTER && ".:/".indexOf(register) == -1) {
myRegisters[UNNAMED_REGISTER] = Register(UNNAMED_REGISTER, copiedText, type)
logger.debug { "register '$UNNAMED_REGISTER' contains: \"$copiedText\"" }
myRegisters[UNNAMED_REGISTER] = Register(UNNAMED_REGISTER, type, processedText, ArrayList(transferableData))
logger.debug { "register '$UNNAMED_REGISTER' contains: \"$processedText\"" }
}
if (isDelete) {
@ -256,44 +280,26 @@ abstract class VimRegisterGroupBase : VimRegisterGroup {
while (d >= '1') {
val t = myRegisters[d]
if (t != null) {
val incName = (d.code + 1).toChar()
myRegisters[incName] = Register(incName, t.copiedText, t.type)
t.name = (d.code + 1).toChar()
myRegisters[(d.code + 1).toChar()] = t
}
d--
}
myRegisters['1'] = Register('1', copiedText, type)
myRegisters['1'] = Register('1', type, processedText, ArrayList(transferableData))
}
// Deletes smaller than one line and without specified register go the the "-" register
if (smallInlineDeletion && register == defaultRegister) {
myRegisters[SMALL_DELETION_REGISTER] =
Register(SMALL_DELETION_REGISTER, copiedText, type)
Register(SMALL_DELETION_REGISTER, type, processedText, ArrayList(transferableData))
}
} else if (register == defaultRegister) {
myRegisters['0'] = Register('0', copiedText, type)
logger.debug { "register '0' contains: \"$copiedText\"" }
myRegisters['0'] = Register('0', type, processedText, ArrayList(transferableData))
logger.debug { "register '0' contains: \"$processedText\"" }
} // Yanks also go to register 0 if the default register was used
return true
}
@Deprecated("Please use com.maddyhome.idea.vim.register.VimRegisterGroup#storeText(com.maddyhome.idea.vim.api.VimEditor, com.maddyhome.idea.vim.api.ExecutionContext, com.maddyhome.idea.vim.api.ImmutableVimCaret, com.maddyhome.idea.vim.common.TextRange, com.maddyhome.idea.vim.state.mode.SelectionType, boolean)")
override fun storeText(
editor: VimEditor,
caret: ImmutableVimCaret,
range: TextRange,
type: SelectionType,
isDelete: Boolean,
): Boolean {
return storeText(
editor,
injector.executionContextManager.getEditorExecutionContext(editor),
caret,
range,
type,
isDelete
)
}
/**
* Store text into the last register.
*
@ -310,10 +316,12 @@ abstract class VimRegisterGroupBase : VimRegisterGroup {
range: TextRange,
type: SelectionType,
isDelete: Boolean,
forceAppend: Boolean,
prependInsteadOfAppend: Boolean
): Boolean {
if (isRegisterWritable()) {
val text = preprocessTextBeforeStoring(editor.getText(range), type)
return storeTextInternal(editor, context, range, text, type, lastRegisterChar, isDelete)
return storeTextInternal(editor, context, range, text, type, lastRegisterChar, isDelete, forceAppend, prependInsteadOfAppend)
}
return false
@ -348,68 +356,36 @@ abstract class VimRegisterGroupBase : VimRegisterGroup {
if (READONLY_REGISTERS.indexOf(register) == -1 && register != LAST_SEARCH_REGISTER && register != UNNAMED_REGISTER) {
return false
}
myRegisters[register] = Register(
register,
injector.clipboardManager.dumbCopiedText(text),
myRegisters[register] = Register(register,
SelectionType.CHARACTER_WISE
) // TODO why transferable data is not collected?
, text, ArrayList())
logger.debug { "register '$register' contains: \"$text\"" }
return true
}
@Deprecated("Please use com.maddyhome.idea.vim.register.VimRegisterGroup#getRegister(com.maddyhome.idea.vim.api.VimEditor, com.maddyhome.idea.vim.api.ExecutionContext, char)")
override fun getRegister(r: Char): Register? {
val dummyEditor = injector.fallbackWindow
val dummyContext = injector.executionContextManager.getEditorExecutionContext(dummyEditor)
return getRegister(dummyEditor, dummyContext, r)
override fun storeText(editor: VimEditor, context: ExecutionContext, register: Char, text: String): Boolean {
return storeText(editor, context, register, text, SelectionType.CHARACTER_WISE)
}
@Deprecated("Please use com.maddyhome.idea.vim.register.VimRegisterGroup#storeText(com.maddyhome.idea.vim.api.VimEditor, com.maddyhome.idea.vim.api.ExecutionContext, char, java.lang.String, com.maddyhome.idea.vim.state.mode.SelectionType)")
override fun storeText(register: Char, text: String, selectionType: SelectionType): Boolean {
val dummyEditor = injector.fallbackWindow
val dummyContext = injector.executionContextManager.getEditorExecutionContext(dummyEditor)
return storeText(dummyEditor, dummyContext, register, text, selectionType)
}
override fun storeText(
editor: VimEditor,
context: ExecutionContext,
register: Char,
text: String,
selectionType: SelectionType,
override fun storeText(editor: VimEditor, context: ExecutionContext, register: Char, text: String, selectionType: SelectionType,
): Boolean {
if (!WRITABLE_REGISTERS.contains(register)) {
return false
}
logger.debug { "register '$register' contains: \"$text\"" }
val oldRegister = getRegister(editor, context, register.lowercaseChar())
val newRegister = if (register.isUpperCase() && oldRegister != null) {
oldRegister.addText(text)
val textToStore = if (register.isUpperCase()) {
(getRegister(register.lowercaseChar())?.rawText ?: "") + text
} else {
Register(
register,
injector.clipboardManager.dumbCopiedText(text),
selectionType
) // FIXME why don't we collect transferable data?
text
}
saveRegister(editor, context, register, newRegister)
val reg = Register(register, selectionType, textToStore, ArrayList())
saveRegister(editor, context, register, reg)
if (register == '/') {
injector.searchGroup.lastSearchPattern = text // todo we should not have this field if we have the "/" register
}
return true
}
@Deprecated("Please use com.maddyhome.idea.vim.register.VimRegisterGroup#storeText(com.maddyhome.idea.vim.api.VimEditor, com.maddyhome.idea.vim.api.ExecutionContext, char, java.lang.String)")
override fun storeText(register: Char, text: String): Boolean {
val dummyEditor = injector.fallbackWindow
val dummyContext = injector.executionContextManager.getEditorExecutionContext(dummyEditor)
return storeText(dummyEditor, dummyContext, register, text)
}
override fun storeText(editor: VimEditor, context: ExecutionContext, register: Char, text: String): Boolean {
return storeText(editor, context, register, text, SelectionType.CHARACTER_WISE)
}
private fun guessSelectionType(text: String): SelectionType {
return if (text.endsWith("\n")) SelectionType.LINE_WISE else SelectionType.CHARACTER_WISE
}
@ -421,10 +397,10 @@ abstract class VimRegisterGroupBase : VimRegisterGroup {
* @param r - the register character corresponding to either the primary selection (*) or clipboard selection (+)
* @return the content of the selection, if available, otherwise null
*/
private fun refreshClipboardRegister(editor: VimEditor, context: ExecutionContext, r: Char): Register? {
private fun refreshClipboardRegister(r: Char): Register? {
return when (r) {
PRIMARY_REGISTER -> refreshPrimaryRegister(editor, context)
CLIPBOARD_REGISTER -> refreshClipboardRegister(editor, context)
PRIMARY_REGISTER -> refreshPrimaryRegister()
CLIPBOARD_REGISTER -> refreshClipboardRegister()
else -> throw RuntimeException("Clipboard register expected, got $r")
}
}
@ -433,56 +409,60 @@ abstract class VimRegisterGroupBase : VimRegisterGroup {
return System.getenv("DISPLAY") != null && injector.systemInfoService.isXWindow
}
private fun setSystemPrimaryRegisterText(editor: VimEditor, context: ExecutionContext, copiedText: VimCopiedText) {
logger.trace("Setting text: $copiedText to primary selection...")
private fun setSystemPrimaryRegisterText(text: String, rawText: String, transferableData: List<Any>) {
logger.trace("Setting text: $text to primary selection...")
if (isPrimaryRegisterSupported()) {
try {
injector.clipboardManager.setPrimaryContent(editor, context, copiedText)
injector.clipboardManager.setPrimaryText(text, rawText, transferableData)
} catch (e: Exception) {
logger.warn("False positive X11 primary selection support")
logger.trace("Setting text to primary selection failed. Setting it to clipboard selection instead")
setSystemClipboardRegisterText(editor, context, copiedText)
setSystemClipboardRegisterText(text, rawText, transferableData)
}
} else {
logger.trace("X11 primary selection is not supporting. Setting clipboard selection instead")
setSystemClipboardRegisterText(editor, context, copiedText)
setSystemClipboardRegisterText(text, rawText, transferableData)
}
}
private fun setSystemClipboardRegisterText(editor: VimEditor, context: ExecutionContext, copiedText: VimCopiedText) {
injector.clipboardManager.setClipboardContent(editor, context, copiedText)
private fun setSystemClipboardRegisterText(text: String, rawText: String, transferableData: List<Any>) {
injector.clipboardManager.setClipboardText(text, rawText, transferableData)
}
private fun refreshPrimaryRegister(editor: VimEditor, context: ExecutionContext): Register? {
private fun refreshPrimaryRegister(): Register? {
logger.trace("Syncing cached primary selection value..")
if (!isPrimaryRegisterSupported()) {
logger.trace("X11 primary selection is not supported. Syncing clipboard selection..")
return refreshClipboardRegister(editor, context)
return refreshClipboardRegister()
}
try {
val clipboardData = injector.clipboardManager.getPrimaryContent(editor, context) ?: return null
val clipboardData = injector.clipboardManager.getPrimaryTextAndTransferableData() ?: return null
val currentRegister = myRegisters[PRIMARY_REGISTER]
if (currentRegister != null && clipboardData.text == currentRegister.text) {
val text = clipboardData.first
val transferableData = clipboardData.second?.toMutableList()
if (currentRegister != null && text == currentRegister.text) {
return currentRegister
}
return Register(PRIMARY_REGISTER, clipboardData, guessSelectionType(clipboardData.text))
return transferableData?.let { Register(PRIMARY_REGISTER, guessSelectionType(text), text, it) }
} catch (e: Exception) {
logger.warn("False positive X11 primary selection support")
logger.trace("Syncing primary selection failed. Syncing clipboard selection instead")
return refreshClipboardRegister(editor, context)
return refreshClipboardRegister()
}
}
private fun refreshClipboardRegister(editor: VimEditor, context: ExecutionContext): Register? {
private fun refreshClipboardRegister(): Register? {
// for some reason non-X systems use PRIMARY_REGISTER as a clipboard storage
val systemAwareClipboardRegister = if (isPrimaryRegisterSupported()) CLIPBOARD_REGISTER else PRIMARY_REGISTER
val clipboardData = injector.clipboardManager.getClipboardContent(editor, context) ?: return null
val clipboardData = injector.clipboardManager.getClipboardTextAndTransferableData() ?: return null
val currentRegister = myRegisters[systemAwareClipboardRegister]
if (currentRegister != null && clipboardData.text == currentRegister.text) {
val text = clipboardData.first
val transferableData = clipboardData.second?.toMutableList()
if (currentRegister != null && text == currentRegister.text) {
return currentRegister
}
return Register(systemAwareClipboardRegister, clipboardData, guessSelectionType(clipboardData.text))
return transferableData?.let { Register(systemAwareClipboardRegister, guessSelectionType(text), text, it) }
}
override fun getRegister(editor: VimEditor, context: ExecutionContext, r: Char): Register? {
@ -492,50 +472,35 @@ abstract class VimRegisterGroupBase : VimRegisterGroup {
myR = Character.toLowerCase(myR)
}
return if (CLIPBOARD_REGISTERS.indexOf(myR) >= 0) refreshClipboardRegister(
editor,
context,
myR
) else myRegisters[myR]
}
@Deprecated("Please use com.maddyhome.idea.vim.register.VimRegisterGroup#getRegisters(com.maddyhome.idea.vim.api.VimEditor, com.maddyhome.idea.vim.api.ExecutionContext)")
override fun getRegisters(): List<Register> {
val dummyEditor = injector.fallbackWindow
val dummyContext = injector.executionContextManager.getEditorExecutionContext(dummyEditor)
return getRegisters(dummyEditor, dummyContext)
myR) else myRegisters[myR]
}
override fun getRegisters(editor: VimEditor, context: ExecutionContext): List<Register> {
val filteredRegisters = myRegisters.values.filterNot { CLIPBOARD_REGISTERS.contains(it.name) }.toMutableList()
val clipboardRegisters = CLIPBOARD_REGISTERS
.filterNot { it == CLIPBOARD_REGISTER && !isPrimaryRegisterSupported() } // for some reason non-X systems use PRIMARY_REGISTER as a clipboard storage
.mapNotNull { refreshClipboardRegister(editor, context, it) }
.mapNotNull { refreshClipboardRegister(it) }
return (filteredRegisters + clipboardRegisters).sortedWith(Register.KeySorter)
}
@Deprecated("Please use com.maddyhome.idea.vim.register.VimRegisterGroup#saveRegister(com.maddyhome.idea.vim.api.VimEditor, com.maddyhome.idea.vim.api.ExecutionContext, char, com.maddyhome.idea.vim.register.Register)")
override fun saveRegister(r: Char, register: Register) {
val dummyEditor = injector.fallbackWindow
val dummyContext = injector.executionContextManager.getEditorExecutionContext(dummyEditor)
saveRegister(dummyEditor, dummyContext, r, register)
}
override fun saveRegister(editor: VimEditor, context: ExecutionContext, r: Char, register: Register) {
var myR = if (Character.isUpperCase(r)) Character.toLowerCase(r) else r
val text = register.text
val rawText = register.rawText
if (CLIPBOARD_REGISTERS.indexOf(myR) >= 0) {
if (CLIPBOARD_REGISTERS.indexOf(myR) >= 0 && text != null && rawText != null) {
when (myR) {
CLIPBOARD_REGISTER -> {
if (!isPrimaryRegisterSupported()) {
// it looks wrong, but for some reason non-X systems use the * register to store the clipboard content
myR = PRIMARY_REGISTER
}
setSystemClipboardRegisterText(editor, context, register.copiedText)
setSystemClipboardRegisterText(text, rawText, ArrayList(register.transferableData))
}
PRIMARY_REGISTER -> {
setSystemPrimaryRegisterText(editor, context, register.copiedText)
setSystemPrimaryRegisterText(text, rawText, ArrayList(register.transferableData))
}
}
}
@ -552,15 +517,8 @@ abstract class VimRegisterGroupBase : VimRegisterGroup {
}
}
@Deprecated("Please use com.maddyhome.idea.vim.register.VimRegisterGroup#getPlaybackRegister(com.maddyhome.idea.vim.api.VimEditor, com.maddyhome.idea.vim.api.ExecutionContext, char)")
override fun getPlaybackRegister(r: Char): Register? {
val dummyEditor = injector.fallbackWindow
val dummyContext = injector.executionContextManager.getEditorExecutionContext(dummyEditor)
return getPlaybackRegister(dummyEditor, dummyContext, r)
}
override fun getPlaybackRegister(editor: VimEditor, context: ExecutionContext, r: Char): Register? {
return if (PLAYBACK_REGISTERS.indexOf(r) != 0) getRegister(editor, context, r) else null
return if (PLAYBACK_REGISTERS.indexOf(r) != 0) getRegister(r) else null
}
override fun recordText(text: String) {
@ -578,19 +536,12 @@ abstract class VimRegisterGroupBase : VimRegisterGroup {
myRegisters[register] = Register(register, type, keys.toMutableList())
}
@Deprecated("Please use com.maddyhome.idea.vim.register.VimRegisterGroup#finishRecording(com.maddyhome.idea.vim.api.VimEditor, com.maddyhome.idea.vim.api.ExecutionContext)")
override fun finishRecording() {
val dummyEditor = injector.fallbackWindow
val dummyContext = injector.executionContextManager.getEditorExecutionContext(dummyEditor)
finishRecording(dummyEditor, dummyContext)
}
override fun finishRecording(editor: VimEditor, context: ExecutionContext) {
val register = recordRegister
if (register != null) {
var reg: Register? = null
if (Character.isUpperCase(register)) {
reg = getRegister(editor, context, register)
reg = getRegister(register)
}
val myRecordList = recordList
@ -599,7 +550,7 @@ abstract class VimRegisterGroupBase : VimRegisterGroup {
reg = Register(Character.toLowerCase(register), SelectionType.CHARACTER_WISE, myRecordList)
myRegisters[Character.toLowerCase(register)] = reg
} else {
myRegisters[reg.name.lowercaseChar()] = reg.addText(injector.parser.toPrintableString(myRecordList))
reg.addKeys(myRecordList)
}
}
}

View File

@ -11,6 +11,7 @@ package com.maddyhome.idea.vim.vimscript.model.commands
import com.intellij.vim.annotations.ExCommand
import com.maddyhome.idea.vim.api.ExecutionContext
import com.maddyhome.idea.vim.api.VimEditor
import com.maddyhome.idea.vim.api.getText
import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.command.OperatorArguments
import com.maddyhome.idea.vim.ex.ranges.Range
@ -37,7 +38,7 @@ data class CopyTextCommand(val range: Range, val modifier: CommandModifier, val
val carets = editor.sortedCarets()
for (caret in carets) {
val range = getLineRange(editor, caret).toTextRange(editor)
val copiedText = injector.clipboardManager.collectCopiedText(editor, context, range)
val text = editor.getText(range)
// Copy is defined as:
// :[range]co[py] {address}
@ -46,7 +47,8 @@ data class CopyTextCommand(val range: Range, val modifier: CommandModifier, val
// the line _before_ the first line (i.e., copy to above the first line).
val address1 = getAddressFromArgument(editor)
val textData = PutData.TextData(null, copiedText, SelectionType.LINE_WISE)
val transferableData = injector.clipboardManager.getTransferableData(editor, range)
val textData = PutData.TextData(text, SelectionType.LINE_WISE, transferableData, null)
var mutableCaret = caret
val putData = if (address1 == 0) {
// TODO: This should maintain current column location

View File

@ -88,7 +88,7 @@ data class MoveTextCommand(val range: Range, val modifier: CommandModifier, val
val selectionEndOffset = lastSelectionInfo.end?.let { editor.bufferPositionToOffset(it) }
val text = editor.getText(range)
val textData = PutData.TextData(null, injector.clipboardManager.dumbCopiedText(text), SelectionType.LINE_WISE)
val textData = PutData.TextData(text, SelectionType.LINE_WISE, emptyList(), null)
val dropNewLineInEnd = (line + linesMoved == editor.lineCount() - 1 && text.last() == '\n') ||
(lineRange.endLine == editor.lineCount() - 1)

View File

@ -47,7 +47,12 @@ data class PutLinesCommand(val range: Range, val modifier: CommandModifier, val
val line = if (range.size() == 0) -1 else getLine(editor)
val textData = registerGroup.getRegister(editor, context, registerGroup.lastRegisterChar)?.let {
PutData.TextData(null, it.copiedText, SelectionType.LINE_WISE)
PutData.TextData(
it.text ?: injector.parser.toKeyNotation(it.keys),
SelectionType.LINE_WISE,
it.transferableData,
null,
)
}
val putData = PutData(
textData,

View File

@ -14,6 +14,7 @@ import com.maddyhome.idea.vim.api.VimEditor
import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.command.OperatorArguments
import com.maddyhome.idea.vim.ex.ranges.Range
import com.maddyhome.idea.vim.helper.EngineStringHelper
import com.maddyhome.idea.vim.state.mode.SelectionType
import com.maddyhome.idea.vim.vimscript.model.ExecutionResult
@ -41,7 +42,8 @@ data class RegistersCommand(val range: Range, val modifier: CommandModifier, val
SelectionType.CHARACTER_WISE -> "c"
SelectionType.BLOCK_WISE -> "b"
}
" $type \"${reg.name} ${reg.printableString.take(200)}"
val text = reg.rawText?.let { injector.parser.parseKeys(it) } ?: reg.keys
" $type \"${reg.name} ${EngineStringHelper.toPrintableCharacters(text).take(200)}"
}
injector.outputPanel.output(editor, context, regs)

View File

@ -38,8 +38,6 @@ interface VimYankGroup {
* @param count The number of lines to yank
* @return true if able to yank the lines, false if not
*/
@Deprecated("Please use the same method, but with ExecutionContext")
fun yankLine(editor: VimEditor, count: Int): Boolean
fun yankLine(editor: VimEditor, context: ExecutionContext, count: Int): Boolean
/**
@ -50,8 +48,6 @@ interface VimYankGroup {
* @param type The type of yank
* @return true if able to yank the range, false if not
*/
@Deprecated("Please use the same method, but with ExecutionContext")
fun yankRange(editor: VimEditor, range: TextRange?, type: SelectionType, moveCursor: Boolean): Boolean
fun yankRange(
editor: VimEditor,
context: ExecutionContext,

View File

@ -10,18 +10,14 @@ package com.maddyhome.idea.vim.yank
import com.maddyhome.idea.vim.action.motion.updown.MotionDownLess1FirstNonSpaceAction
import com.maddyhome.idea.vim.api.ExecutionContext
import com.maddyhome.idea.vim.api.ImmutableVimCaret
import com.maddyhome.idea.vim.api.VimCaret
import com.maddyhome.idea.vim.api.VimEditor
import com.maddyhome.idea.vim.api.anyNonWhitespace
import com.maddyhome.idea.vim.api.getLineEndForOffset
import com.maddyhome.idea.vim.api.getLineStartForOffset
import com.maddyhome.idea.vim.api.injector
import com.maddyhome.idea.vim.command.Argument
import com.maddyhome.idea.vim.command.MotionType
import com.maddyhome.idea.vim.command.OperatorArguments
import com.maddyhome.idea.vim.common.TextRange
import com.maddyhome.idea.vim.handler.MotionActionHandler
import com.maddyhome.idea.vim.state.mode.SelectionType
import org.jetbrains.annotations.Contract
import kotlin.math.min
@ -30,20 +26,16 @@ open class YankGroupBase : VimYankGroup {
private fun yankRange(
editor: VimEditor,
context: ExecutionContext,
caretToRange: Map<ImmutableVimCaret, Pair<TextRange, SelectionType>>,
range: TextRange,
type: SelectionType,
startOffsets: Map<VimCaret, Int>?,
): Boolean {
startOffsets?.forEach { (caret, offset) ->
caret.moveToOffset(offset)
}
injector.listenersNotifier.notifyYankPerformed(caretToRange.mapValues { it.value.first })
var result = true
for ((caret, myRange) in caretToRange) {
result = caret.registerStorage.storeText(editor, context, myRange.first, myRange.second, false) && result
}
return result
injector.listenersNotifier.notifyYankPerformed(editor, range)
return injector.registerGroup.storeText(editor, context, editor.primaryCaret(), range, type, false)
}
@Contract("_, _ -> new")
@ -83,11 +75,12 @@ open class YankGroupBase : VimYankGroup {
operatorArguments: OperatorArguments,
): Boolean {
val motion = argument as? Argument.Motion ?: return false
val motionType = motion.getMotionType()
val nativeCaretCount = editor.nativeCarets().size
if (nativeCaretCount <= 0) return false
val caretToRange = HashMap<ImmutableVimCaret, Pair<TextRange, SelectionType>>(nativeCaretCount)
val ranges = ArrayList<Pair<Int, Int>>(nativeCaretCount)
// This logic is from original vim
val startOffsets =
@ -97,49 +90,28 @@ open class YankGroupBase : VimYankGroup {
HashMap<VimCaret, Int>(nativeCaretCount)
}
for (caret in editor.nativeCarets()) {
var motionType = motion.getMotionType()
val motionRange = injector.motion.getMotionRange(editor, caret, context, argument, operatorArguments)
?: continue
assert(motionRange.size() == 1)
ranges.add(motionRange.startOffset to motionRange.endOffset)
startOffsets?.put(caret, motionRange.normalize().startOffset)
// Yank motion commands that are not linewise become linewise if all the following are true:
// 1) The range is across multiple lines
// 2) There is only whitespace before the start of the range
// 3) There is only whitespace after the end of the range
if (argument.motion is MotionActionHandler && argument.motion.motionType == MotionType.EXCLUSIVE) {
val start = editor.offsetToBufferPosition(motionRange.startOffset)
val end = editor.offsetToBufferPosition(motionRange.endOffset)
if (start.line != end.line
&& !editor.anyNonWhitespace(motionRange.startOffset, -1)
&& !editor.anyNonWhitespace(motionRange.endOffset, 1)
) {
motionType = SelectionType.LINE_WISE
}
}
caretToRange[caret] = TextRange(motionRange.startOffset, motionRange.endOffset) to motionType
}
if (caretToRange.isEmpty()) return false
val range = getTextRange(ranges, motionType) ?: return false
if (range.size() == 0) return false
return yankRange(
editor,
context,
caretToRange,
range,
motionType,
startOffsets,
)
}
@Deprecated("Please use the same method, but with ExecutionContext")
override fun yankLine(editor: VimEditor, count: Int): Boolean {
val context = injector.executionContextManager.getEditorExecutionContext(editor)
return yankLine(editor, context, count)
}
/**
* This yanks count lines of text
*
@ -149,24 +121,18 @@ open class YankGroupBase : VimYankGroup {
*/
override fun yankLine(editor: VimEditor, context: ExecutionContext, count: Int): Boolean {
val caretCount = editor.nativeCarets().size
val caretToRange = HashMap<ImmutableVimCaret, Pair<TextRange, SelectionType>>(caretCount)
val ranges = ArrayList<Pair<Int, Int>>(caretCount)
for (caret in editor.nativeCarets()) {
val start = injector.motion.moveCaretToCurrentLineStart(editor, caret)
val end =
min(injector.motion.moveCaretToRelativeLineEnd(editor, caret, count - 1, true) + 1, editor.fileSize().toInt())
val end = min(injector.motion.moveCaretToRelativeLineEnd(editor, caret, count - 1, true) + 1, editor.fileSize().toInt())
if (end == -1) continue
caretToRange[caret] = TextRange(start, end) to SelectionType.LINE_WISE
ranges.add(start to end)
}
return yankRange(editor, context, caretToRange, null)
}
@Deprecated("Please use the same method, but with ExecutionContext")
override fun yankRange(editor: VimEditor, range: TextRange?, type: SelectionType, moveCursor: Boolean): Boolean {
val context = injector.executionContextManager.getEditorExecutionContext(editor)
return yankRange(editor, context, range, type, moveCursor)
val range = getTextRange(ranges, SelectionType.LINE_WISE) ?: return false
return yankRange(editor, context, range, SelectionType.LINE_WISE, null)
}
/**
@ -177,15 +143,8 @@ open class YankGroupBase : VimYankGroup {
* @param type The type of yank
* @return true if able to yank the range, false if not
*/
override fun yankRange(
editor: VimEditor,
context: ExecutionContext,
range: TextRange?,
type: SelectionType,
moveCursor: Boolean,
): Boolean {
override fun yankRange(editor: VimEditor, context: ExecutionContext, range: TextRange?, type: SelectionType, moveCursor: Boolean): Boolean {
range ?: return false
val caretToRange = HashMap<ImmutableVimCaret, Pair<TextRange, SelectionType>>()
if (type == SelectionType.LINE_WISE) {
for (i in 0 until range.size()) {
@ -205,19 +164,17 @@ open class YankGroupBase : VimYankGroup {
val startOffsets = HashMap<VimCaret, Int>(editor.nativeCarets().size)
if (type == SelectionType.BLOCK_WISE) {
startOffsets[editor.primaryCaret()] = range.normalize().startOffset
caretToRange[editor.primaryCaret()] = range to type
} else {
for ((i, caret) in editor.nativeCarets().withIndex()) {
val textRange = TextRange(rangeStartOffsets[i], rangeEndOffsets[i])
startOffsets[caret] = textRange.normalize().startOffset
caretToRange[caret] = textRange to type
}
}
return if (moveCursor) {
yankRange(editor, context, caretToRange, startOffsets)
yankRange(editor, context, range, type, startOffsets)
} else {
yankRange(editor, context, caretToRange, null)
yankRange(editor, context, range, type, null)
}
}
}