From a8663450dce4e02719e5f3f6b09a1bf03f6d229d Mon Sep 17 00:00:00 2001 From: Brice Dutheil Date: Wed, 1 Jul 2026 11:28:49 +0200 Subject: [PATCH] [terminal] IJPL-248564 Fix block selection copy when Copy on Selection is enabled Rely on getSelectedText that allowed to ask for all carets (which can be used of vertical selection). (cherry picked from commit 96edf48978d253d1bb5d1516ad8a2d1e2d62457b) IJ-CR-211956 GitOrigin-RevId: a8e77017920475082cd3e9ecae56b6eb351807e2 --- .../view/impl/CopyOnSelectionHandler.kt | 2 +- .../frontend/TerminalCopyOnSelectionTest.kt | 90 +++++++++++++++++++ 2 files changed, 91 insertions(+), 1 deletion(-) create mode 100644 plugins/terminal/tests/src/com/intellij/terminal/tests/reworked/frontend/TerminalCopyOnSelectionTest.kt diff --git a/plugins/terminal/frontend/src/com/intellij/terminal/frontend/view/impl/CopyOnSelectionHandler.kt b/plugins/terminal/frontend/src/com/intellij/terminal/frontend/view/impl/CopyOnSelectionHandler.kt index a4e09dc58f93..c90e73985dc4 100644 --- a/plugins/terminal/frontend/src/com/intellij/terminal/frontend/view/impl/CopyOnSelectionHandler.kt +++ b/plugins/terminal/frontend/src/com/intellij/terminal/frontend/view/impl/CopyOnSelectionHandler.kt @@ -43,7 +43,7 @@ internal class CopyOnSelectionHandler private constructor(private val settings: // Only perform copying if the editor is focused. // In most cases it is, but if the selection was updated through the API it may not be the case. if (!settings.copyOnSelect() || e.editor?.contentComponent?.isFocusOwner != true) return - val text = e.editor.selectionModel.selectedText ?: return + val text = e.editor.selectionModel.getSelectedText(true) ?: return copy(text) } } diff --git a/plugins/terminal/tests/src/com/intellij/terminal/tests/reworked/frontend/TerminalCopyOnSelectionTest.kt b/plugins/terminal/tests/src/com/intellij/terminal/tests/reworked/frontend/TerminalCopyOnSelectionTest.kt new file mode 100644 index 000000000000..a4da5120f9b1 --- /dev/null +++ b/plugins/terminal/tests/src/com/intellij/terminal/tests/reworked/frontend/TerminalCopyOnSelectionTest.kt @@ -0,0 +1,90 @@ +package com.intellij.terminal.tests.reworked.frontend + +import com.intellij.openapi.editor.Editor +import com.intellij.openapi.editor.LogicalPosition +import com.intellij.openapi.ide.CopyPasteManager +import com.intellij.openapi.util.Disposer +import com.intellij.platform.util.coroutines.childScope +import com.intellij.psi.PsiDocumentManager +import com.intellij.terminal.JBTerminalSystemSettingsProviderBase +import com.intellij.terminal.frontend.view.impl.TerminalEditorFactory +import com.intellij.testFramework.fixtures.BasePlatformTestCase +import kotlinx.coroutines.cancel +import org.jetbrains.plugins.terminal.block.reworked.lang.TerminalOutputPsiFile +import org.jetbrains.plugins.terminal.util.terminalProjectScope +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 +import java.awt.Component +import java.awt.KeyboardFocusManager +import java.awt.datatransfer.DataFlavor +import javax.swing.FocusManager + +@RunWith(JUnit4::class) +internal class TerminalCopyOnSelectionTest : BasePlatformTestCase() { + private var defaultFocusManager: KeyboardFocusManager? = null + + override fun setUp() { + super.setUp() + defaultFocusManager = KeyboardFocusManager.getCurrentKeyboardFocusManager() + } + + override fun tearDown() { + try { + KeyboardFocusManager.setCurrentKeyboardFocusManager(defaultFocusManager) + } + catch (e: Throwable) { + addSuppressedException(e) + } + finally { + super.tearDown() + } + } + + @Test + fun `copy on selection copies block selection from all selected lines`() { + val editor = createTerminalEditor() + editor.emitOnTerminal( + """ + first + second + third + """.trimIndent() + ) + + editor.selectionModel.setBlockSelection(LogicalPosition(0, 1), LogicalPosition(2, 4)) + + val transferable = copiedTransferable() + assertNotNull(transferable) + assertEquals("irs\neco\nhir", transferable!!.getTransferData(DataFlavor.stringFlavor)) + } + + private fun createTerminalEditor(): Editor { + val scope = terminalProjectScope(project).childScope("TerminalOutputEditor").also { + Disposer.register(testRootDisposable) { it.cancel() } + } + val editor = TerminalEditorFactory.createOutputEditor( + project, + object : JBTerminalSystemSettingsProviderBase() { + override fun copyOnSelect(): Boolean = true + }, + scope + ) + // Copy on selection only works if terminal is the focus owner. + KeyboardFocusManager.setCurrentKeyboardFocusManager( + object : FocusManager() { + override fun getFocusOwner(): Component = editor.contentComponent + } + ) + return editor + } + + private fun Editor.emitOnTerminal(text: String) { + document.setText(text) + val psiFile = PsiDocumentManager.getInstance(this@TerminalCopyOnSelectionTest.project).getPsiFile(this.document) as TerminalOutputPsiFile + psiFile.charsSequence = this.document.immutableCharSequence + } + + private fun copiedTransferable() = + CopyPasteManager.getInstance().let { it.systemSelectionContents ?: it.contents } +}