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:
parent
db12fbe725
commit
f0505b8919
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
action
api
put
vimscript/model/commands
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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 {
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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) }
|
||||
|
@ -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>
|
||||
|
||||
|
@ -14,4 +14,5 @@ public data class ProcessedTextData(
|
||||
val text: String,
|
||||
val typeInRegister: SelectionType,
|
||||
val transferableData: List<Any>,
|
||||
val registerChar: Char?,
|
||||
)
|
||||
|
@ -36,5 +36,6 @@ public data class PutData(
|
||||
val rawText: String?,
|
||||
val typeInRegister: SelectionType,
|
||||
val transferableData: List<Any>,
|
||||
val registerChar: Char?,
|
||||
)
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
|
@ -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(
|
||||
|
Loading…
Reference in New Issue
Block a user