1
0
mirror of https://github.com/chylex/IntelliJ-IdeaVim.git synced 2025-02-28 02:45:59 +01:00

Fix(VIM-2433): Do not clear clipboard after inserting with ideaput

This commit is contained in:
Alex Plate 2023-03-17 13:26:13 +02:00
parent db12fbe725
commit f0505b8919
No known key found for this signature in database
GPG Key ID: 0B97153C8FFEC09F
15 changed files with 104 additions and 30 deletions
src
main/java/com/maddyhome/idea/vim
extension
group/copy
newapi
test/java/org/jetbrains/plugins/ideavim/action/copy
vim-engine/src/main/kotlin/com/maddyhome/idea/vim

View File

@ -154,7 +154,7 @@ internal class ReplaceWithRegister : VimExtension {
usedType = SelectionType.CHARACTER_WISE
}
val textData = PutData.TextData(usedText, usedType, savedRegister.transferableData)
val textData = PutData.TextData(usedText, usedType, savedRegister.transferableData, savedRegister.name)
val putData = PutData(
textData,

View File

@ -184,7 +184,7 @@ internal class VimSurroundExtension : VimExtension {
.map { surrounding ->
val innerValue = injector.parser.toPrintableString(surrounding.innerText!!)
val text = newSurround?.let { it.first + innerValue + it.second } ?: innerValue
val textData = PutData.TextData(text, SelectionType.CHARACTER_WISE, emptyList())
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

@ -48,6 +48,7 @@ import com.maddyhome.idea.vim.put.ProcessedTextData
import com.maddyhome.idea.vim.put.PutData
import com.maddyhome.idea.vim.put.VimPasteProvider
import com.maddyhome.idea.vim.put.VimPutBase
import com.maddyhome.idea.vim.register.RegisterConstants
import java.awt.datatransfer.DataFlavor
internal class PutGroup : VimPutBase() {
@ -94,24 +95,13 @@ internal class PutGroup : VimPutBase() {
carets[caret] = pointMarker
}
val allContentsBefore = CopyPasteManager.getInstance().allContents
val sizeBeforeInsert = allContentsBefore.size
val firstItemBefore = allContentsBefore.firstOrNull()
logger.debug { "Transferable classes: ${text.transferableData.joinToString { it.javaClass.name }}" }
val origContent: TextBlockTransferable = injector.clipboardManager.setClipboardText(
text.text,
transferableData = text.transferableData,
) as TextBlockTransferable
val allContentsAfter = CopyPasteManager.getInstance().allContents
val sizeAfterInsert = allContentsAfter.size
try {
val registerChar = text.registerChar
if (registerChar != null && registerChar in RegisterConstants.CLIPBOARD_REGISTERS) {
(pasteProvider as IjPasteProvider).pasteProvider.performPaste(context)
} finally {
val textOnTop =
((firstItemBefore as? TextBlockTransferable)?.getTransferData(DataFlavor.stringFlavor) as? String) != text.text
if (sizeBeforeInsert != sizeAfterInsert || textOnTop) {
// Sometimes inserted text replaces existing one. E.g. on insert with + or * register
(CopyPasteManager.getInstance() as? CopyPasteManagerEx)?.run { removeContent(origContent) }
}
else {
pasteKeepingClipboard(text) {
(pasteProvider as IjPasteProvider).pasteProvider.performPaste(context)
}
}
@ -148,6 +138,51 @@ internal class PutGroup : VimPutBase() {
}
}
/**
* ideaput - option that enables "smartness" of the insert operation. For example, it automatically
* inserts import statements, or converts Java code to kotlin.
* Unfortunately, at the moment, this functionality of "additional text processing" is bound to
* paste operation. So here we do the trick, in order to insert text from the register with all the
* brains from IJ, we put this text into the clipboard and perform a regular IJ paste.
* In order to do this properly, after the paste, we should remove the clipboard text from
* the kill ring (stack of clipboard items)
* So, generally this function should look like this:
* ```
* setClipboardText(text)
* try {
* performPaste()
* } finally {
* removeTextFromClipboard()
* }
* ```
* And it was like this till some moment. However, if our text to paste matches the text that is already placed
* in the clipboard, instead of putting new text on top of stack, it merges the text into the last stack item.
* So, all the other code in this function is created to detect such case and do not remove last clipboard item.
*/
private fun pasteKeepingClipboard(text: ProcessedTextData, doPaste: () -> Unit) {
val allContentsBefore = CopyPasteManager.getInstance().allContents
val sizeBeforeInsert = allContentsBefore.size
val firstItemBefore = allContentsBefore.firstOrNull()
logger.debug { "Transferable classes: ${text.transferableData.joinToString { it.javaClass.name }}" }
val origContent: TextBlockTransferable = injector.clipboardManager.setClipboardText(
text.text,
transferableData = text.transferableData,
) as TextBlockTransferable
val allContentsAfter = CopyPasteManager.getInstance().allContents
val sizeAfterInsert = allContentsAfter.size
try {
doPaste()
} finally {
val textInClipboard = (firstItemBefore as? TextBlockTransferable)
?.getTransferData(DataFlavor.stringFlavor) as? String
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) }
}
}
}
override fun doIndent(
editor: VimEditor,
caret: VimCaret,

View File

@ -55,7 +55,7 @@ internal class IjClipboardManager : VimClipboardManager {
}
@Suppress("UNCHECKED_CAST")
override fun setClipboardText(text: String, rawText: String, transferableData: List<Any>): Any? {
override fun setClipboardText(text: String, rawText: String, transferableData: List<Any>): Transferable? {
val transferableData1 = (transferableData as List<TextBlockTransferableData>).toMutableList()
try {
val s = TextBlockTransferable.convertLineSeparators(text, "\n", transferableData1)
@ -75,9 +75,9 @@ internal class IjClipboardManager : VimClipboardManager {
override fun getTransferableData(vimEditor: VimEditor, textRange: TextRange, text: String): List<Any> {
val editor = (vimEditor as IjVimEditor).editor
val transferableData: MutableList<TextBlockTransferableData> = ArrayList()
val project = editor.project ?: return ArrayList()
val project = editor.project ?: return emptyList()
val file = PsiDocumentManager.getInstance(project).getPsiFile(editor.document) ?: return ArrayList()
val file = PsiDocumentManager.getInstance(project).getPsiFile(editor.document) ?: return emptyList()
DumbService.getInstance(project).withAlternativeResolveEnabled {
for (processor in CopyPastePostProcessor.EP_NAME.extensionList) {
try {

View File

@ -9,6 +9,7 @@
package org.jetbrains.plugins.ideavim.action.copy
import com.intellij.codeInsight.editorActions.TextBlockTransferable
import com.intellij.ide.CopyPasteManagerEx
import com.intellij.openapi.ide.CopyPasteManager
import com.maddyhome.idea.vim.VimPlugin
import com.maddyhome.idea.vim.api.injector
@ -22,6 +23,7 @@ import org.jetbrains.plugins.ideavim.impl.OptionTest
import org.jetbrains.plugins.ideavim.impl.TraceOptions
import org.jetbrains.plugins.ideavim.impl.VimOption
import org.jetbrains.plugins.ideavim.rangeOf
import java.awt.datatransfer.StringSelection
import java.util.*
/**
@ -114,4 +116,30 @@ class PutViaIdeaTest : VimTestCase() {
""".trimIndent()
assertState(after)
}
@TestWithoutNeovim(SkipNeovimReason.DIFFERENT)
@OptionTest(VimOption(OptionConstants.clipboard, limitedValues = [OptionConstants.clipboard_ideaput]))
fun `test insert block w1ith newline`() {
val before = """
A Discovery
$c
I found it in a legendary land
hard by the torrent of a mountain pass.
""".trimIndent()
configureByText(before)
// For this particular test, we want to set exact this type of transferable
CopyPasteManagerEx.getInstance().setContents(StringSelection("Hello"))
typeText("\"+p", "\"+p")
val after = """
A Discovery
HelloHello
I found it in a legendary land
hard by the torrent of a mountain pass.
""".trimIndent()
assertState(after)
}
}

View File

@ -82,7 +82,7 @@ private fun insertRegister(
val register: Register? = injector.registerGroup.getRegister(key)
if (register != null) {
val text = register.rawText ?: injector.parser.toPrintableString(register.keys)
val textData = PutData.TextData(text, SelectionType.CHARACTER_WISE, emptyList())
val textData = PutData.TextData(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, operatorArguments = operatorArguments)
return true

View File

@ -61,6 +61,7 @@ public sealed class PutTextBaseAction(
register.text ?: injector.parser.toPrintableString(register.keys),
register.type,
register.transferableData,
register.name,
)
}
return PutData(textData, null, count, insertTextBeforeCaret, indent, caretAfterInsertedText, -1)

View File

@ -63,6 +63,7 @@ public sealed class PutVisualTextBaseAction(
register.text ?: injector.parser.toPrintableString(register.keys),
register.type,
register.transferableData,
register.name,
)
}
val visualSelection = selection?.let { PutData.VisualSelection(mapOf(caret to it), it.type) }

View File

@ -9,6 +9,7 @@
package com.maddyhome.idea.vim.api
import com.maddyhome.idea.vim.common.TextRange
import java.awt.datatransfer.Transferable
public interface VimClipboardManager {
/**
@ -21,7 +22,7 @@ public interface VimClipboardManager {
/**
* Puts the supplied text into the system clipboard
*/
public fun setClipboardText(text: String, rawText: String = text, transferableData: List<Any>): Any?
public fun setClipboardText(text: String, rawText: String = text, transferableData: List<Any>): Transferable?
public fun getTransferableData(vimEditor: VimEditor, textRange: TextRange, text: String): List<Any>

View File

@ -14,4 +14,5 @@ public data class ProcessedTextData(
val text: String,
val typeInRegister: SelectionType,
val transferableData: List<Any>,
val registerChar: Char?,
)

View File

@ -36,5 +36,6 @@ public data class PutData(
val rawText: String?,
val typeInRegister: SelectionType,
val transferableData: List<Any>,
val registerChar: Char?,
)
}

View File

@ -58,7 +58,7 @@ public abstract class VimPutBase : VimPut {
): Boolean {
val additionalData = collectPreModificationData(editor, data)
deleteSelectedText(editor, data, operatorArguments, saveToRegister)
val processedText = processText(editor, null, data) ?: return false
val processedText = processText(null, data) ?: return false
putTextAndSetCaretPosition(editor, context, processedText, data, additionalData)
if (updateVisualMarks) {
@ -119,7 +119,7 @@ public abstract class VimPutBase : VimPut {
}
}
private fun processText(editor: VimEditor, caret: VimCaret?, data: PutData): ProcessedTextData? {
private fun processText(caret: VimCaret?, data: PutData): ProcessedTextData? {
var text = data.textData?.rawText ?: run {
if (caret == null) return null
if (data.visualSelection != null) {
@ -139,7 +139,12 @@ public abstract class VimPutBase : VimPut {
text.dropLast(1)
}
return ProcessedTextData(text, data.textData.typeInRegister, data.textData.transferableData)
return ProcessedTextData(
text,
data.textData.typeInRegister,
data.textData.transferableData,
data.textData.registerChar
)
}
protected fun moveCaretToEndPosition(
@ -514,7 +519,7 @@ public abstract class VimPutBase : VimPut {
modifyRegister,
)
}
val processedText = processText(editor, caret, data) ?: return false
val processedText = processText(caret, data) ?: return false
val updatedCaret = putForCaret(editor, caret, data, additionalData, context, processedText)
if (updateVisualMarks) {
wrapInsertedTextWithVisualMarks(updatedCaret, data, processedText)

View File

@ -35,7 +35,7 @@ public data class CopyTextCommand(val ranges: Ranges, val argument: String) : Co
val line = goToLineCommand.commandRanges.getFirstLine(editor, caret)
val transferableData = injector.clipboardManager.getTransferableData(editor, range, text)
val textData = PutData.TextData(text, SelectionType.LINE_WISE, transferableData)
val textData = PutData.TextData(text, SelectionType.LINE_WISE, transferableData, null)
val putData = PutData(
textData,
null,

View File

@ -77,7 +77,7 @@ public data class MoveTextCommand(val ranges: Ranges, val argument: String) : Co
val selectionEndOffset = lastSelectionInfo.end?.let { editor.bufferPositionToOffset(it) }
val text = editor.getText(range)
val textData = PutData.TextData(text, SelectionType.LINE_WISE, emptyList())
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

@ -42,6 +42,7 @@ public data class PutLinesCommand(val ranges: Ranges, val argument: String) : Co
it.text ?: injector.parser.toKeyNotation(it.keys),
SelectionType.LINE_WISE,
it.transferableData,
null,
)
}
val putData = PutData(