mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[terminal] Initial commit for creating test for popUp completion.
(cherry picked from commit 0134493ca5207aad0c4b586f88398507f9b30940) IJ-MR-170869 GitOrigin-RevId: 491b2ee14a59a784efb9bc56aa4e46f134135a04
This commit is contained in:
committed by
intellij-monorepo-bot
parent
c70eded9cb
commit
2c2d95bd92
+22
-10
@@ -31,6 +31,11 @@ import com.intellij.util.ui.components.BorderLayoutPanel
|
||||
import com.jediterm.core.util.TermSize
|
||||
import com.jediterm.terminal.TtyConnector
|
||||
import kotlinx.coroutines.*
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
import org.jetbrains.annotations.VisibleForTesting
|
||||
import org.jetbrains.plugins.terminal.TerminalFontSettingsListener
|
||||
import org.jetbrains.plugins.terminal.TerminalFontSettingsService
|
||||
import org.jetbrains.plugins.terminal.TerminalFontSizeProviderImpl
|
||||
import org.jetbrains.plugins.terminal.TerminalPanelMarker
|
||||
import org.jetbrains.plugins.terminal.block.TerminalContentView
|
||||
import org.jetbrains.plugins.terminal.block.output.TerminalOutputEditorInputMethodSupport
|
||||
@@ -54,7 +59,8 @@ import java.util.concurrent.CompletableFuture
|
||||
import javax.swing.JComponent
|
||||
import kotlin.math.min
|
||||
|
||||
internal class ReworkedTerminalView(
|
||||
@ApiStatus.Internal
|
||||
class ReworkedTerminalView(
|
||||
private val project: Project,
|
||||
settings: JBTerminalSystemSettingsProviderBase,
|
||||
private val sessionFuture: CompletableFuture<TerminalSession>,
|
||||
@@ -63,22 +69,29 @@ internal class ReworkedTerminalView(
|
||||
private val coroutineScope = terminalProjectScope(project).childScope("ReworkedTerminalView")
|
||||
|
||||
private val sessionModel: TerminalSessionModel
|
||||
private val blocksModel: TerminalBlocksModel
|
||||
|
||||
@VisibleForTesting
|
||||
val blocksModel: TerminalBlocksModel
|
||||
private val encodingManager: TerminalKeyEncodingManager
|
||||
private val controller: TerminalSessionController
|
||||
|
||||
private val terminalInput: TerminalInput
|
||||
private val terminalSearchController: TerminalSearchController
|
||||
|
||||
private val outputEditor: EditorEx
|
||||
@VisibleForTesting
|
||||
val outputEditor: EditorEx
|
||||
private val outputHyperlinkFacade: FrontendTerminalHyperlinkFacade?
|
||||
private val alternateBufferEditor: EditorEx
|
||||
|
||||
@VisibleForTesting
|
||||
val outputModel: TerminalOutputModelImpl
|
||||
private val alternateBufferHyperlinkFacade: FrontendTerminalHyperlinkFacade?
|
||||
private val outputModel: TerminalOutputModelImpl
|
||||
private val scrollingModel: TerminalOutputScrollingModel
|
||||
private var isAlternateScreenBuffer = false
|
||||
|
||||
private val terminalPanel: TerminalPanel
|
||||
@VisibleForTesting
|
||||
val outputEditorEventsHandler: TerminalEventsHandler
|
||||
|
||||
override val component: JComponent
|
||||
get() = terminalPanel
|
||||
@@ -105,18 +118,18 @@ internal class ReworkedTerminalView(
|
||||
|
||||
alternateBufferEditor = TerminalEditorFactory.createAlternateBufferEditor(project, settings, parentDisposable = this)
|
||||
val alternateBufferModel = TerminalOutputModelImpl(alternateBufferEditor.document, maxOutputLength = 0)
|
||||
val alternateBufferEventsHandler = TerminalEventsHandlerImpl(sessionModel, alternateBufferEditor, encodingManager, terminalInput, settings, null, alternateBufferModel)
|
||||
configureOutputEditor(
|
||||
project,
|
||||
editor = alternateBufferEditor,
|
||||
model = alternateBufferModel,
|
||||
settings,
|
||||
sessionModel,
|
||||
encodingManager,
|
||||
terminalInput,
|
||||
coroutineScope.childScope("TerminalAlternateBufferModel"),
|
||||
scrollingModel = null,
|
||||
fusCursorPaintingListener,
|
||||
fusFirstOutputListener,
|
||||
alternateBufferEventsHandler,
|
||||
)
|
||||
alternateBufferHyperlinkFacade = if (isSplitHyperlinksSupportEnabled()) {
|
||||
FrontendTerminalHyperlinkFacade(
|
||||
@@ -138,6 +151,7 @@ internal class ReworkedTerminalView(
|
||||
|
||||
scrollingModel = TerminalOutputScrollingModelImpl(outputEditor, outputModel, sessionModel, coroutineScope.childScope("TerminalOutputScrollingModel"))
|
||||
outputEditor.putUserData(TerminalOutputScrollingModel.KEY, scrollingModel)
|
||||
outputEditorEventsHandler = TerminalEventsHandlerImpl(sessionModel, outputEditor, encodingManager, terminalInput, settings, scrollingModel, outputModel)
|
||||
|
||||
configureOutputEditor(
|
||||
project,
|
||||
@@ -145,12 +159,11 @@ internal class ReworkedTerminalView(
|
||||
model = outputModel,
|
||||
settings,
|
||||
sessionModel,
|
||||
encodingManager,
|
||||
terminalInput,
|
||||
coroutineScope.childScope("TerminalOutputModel"),
|
||||
scrollingModel,
|
||||
fusCursorPaintingListener,
|
||||
fusFirstOutputListener,
|
||||
outputEditorEventsHandler,
|
||||
)
|
||||
|
||||
outputEditor.putUserData(TerminalSessionModel.KEY, sessionModel)
|
||||
@@ -318,12 +331,11 @@ internal class ReworkedTerminalView(
|
||||
model: TerminalOutputModel,
|
||||
settings: JBTerminalSystemSettingsProviderBase,
|
||||
sessionModel: TerminalSessionModel,
|
||||
encodingManager: TerminalKeyEncodingManager,
|
||||
terminalInput: TerminalInput,
|
||||
coroutineScope: CoroutineScope,
|
||||
scrollingModel: TerminalOutputScrollingModel?,
|
||||
fusCursorPainterListener: TerminalFusCursorPainterListener?,
|
||||
fusFirstOutputListener: TerminalFusFirstOutputListener?,
|
||||
eventsHandler: TerminalEventsHandlerImpl,
|
||||
) {
|
||||
val parentDisposable = coroutineScope.asDisposable() // same lifecycle as `this@ReworkedTerminalView`
|
||||
|
||||
|
||||
+3
-1
@@ -1,9 +1,11 @@
|
||||
package com.intellij.terminal.frontend
|
||||
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
import java.awt.event.MouseEvent
|
||||
import java.awt.event.MouseWheelEvent
|
||||
|
||||
internal interface TerminalEventsHandler {
|
||||
@ApiStatus.Internal
|
||||
interface TerminalEventsHandler {
|
||||
fun keyTyped(e: TimedKeyEvent) {}
|
||||
fun keyPressed(e: TimedKeyEvent) {}
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@ internal class TerminalTypeAhead(
|
||||
fun isDisabled() = PlatformUtils.isJetBrainsClient() || isDisabledInRegistry() || !isTypingCommand()
|
||||
|
||||
private fun isDisabledInRegistry(): Boolean = !Registry.`is`("terminal.type.ahead", false)
|
||||
|
||||
|
||||
private fun isTypingCommand(): Boolean = blocksModel.blocks.lastOrNull()?.let { lastBlock ->
|
||||
// The command start offset is where the prompt ends. If it's not there yet, it means the user can't type a command yet.
|
||||
// The output start offset is -1 until the command starts executing. Once that happens, it means the user can't type anymore.
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.intellij.terminal.frontend
|
||||
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
import java.awt.event.KeyEvent
|
||||
import kotlin.time.TimeMark
|
||||
import kotlin.time.TimeSource
|
||||
@@ -9,7 +10,8 @@ import kotlin.time.TimeSource
|
||||
* Original [java.awt.event.KeyEvent] already has the [KeyEvent.`when`] property, but this value is not monotonic
|
||||
* and can't be used for reliable measurements. So, we use [kotlin.time.TimeMark] instead.
|
||||
*/
|
||||
internal data class TimedKeyEvent(
|
||||
@ApiStatus.Internal
|
||||
data class TimedKeyEvent(
|
||||
val original: KeyEvent,
|
||||
val initTime: TimeMark = TimeSource.Monotonic.markNow(),
|
||||
)
|
||||
@@ -42,6 +42,7 @@ jvm_library(
|
||||
"//platform/projectModel-api:projectModel",
|
||||
"//platform/util/coroutines",
|
||||
"//platform/platform-impl/internal",
|
||||
"//plugins/sh",
|
||||
"//platform/code-style-api:codeStyle",
|
||||
"@lib//:assert_j",
|
||||
"@lib//:jediterm-core",
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
<orderEntry type="module" module-name="intellij.platform.projectModel" />
|
||||
<orderEntry type="module" module-name="intellij.platform.util.coroutines" />
|
||||
<orderEntry type="module" module-name="intellij.platform.ide.internal" />
|
||||
<orderEntry type="module" module-name="intellij.sh" />
|
||||
<orderEntry type="module" module-name="intellij.platform.codeStyle" />
|
||||
<orderEntry type="library" name="assertJ" level="project" />
|
||||
<orderEntry type="library" name="jediterm-core" level="project" />
|
||||
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
package com.intellij.terminal.tests.reworked.frontend
|
||||
|
||||
import com.intellij.codeInsight.lookup.LookupElement
|
||||
import com.intellij.codeInsight.lookup.LookupManager
|
||||
import com.intellij.openapi.Disposable
|
||||
import com.intellij.openapi.actionSystem.ActionManager
|
||||
import com.intellij.openapi.actionSystem.ActionUiKind
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent
|
||||
import com.intellij.openapi.actionSystem.CommonDataKeys
|
||||
import com.intellij.openapi.actionSystem.ex.ActionUtil
|
||||
import com.intellij.openapi.actionSystem.impl.SimpleDataContext
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.util.Disposer
|
||||
import com.intellij.openapi.util.registry.Registry
|
||||
import com.intellij.terminal.frontend.ReworkedTerminalView
|
||||
import com.intellij.terminal.frontend.TimedKeyEvent
|
||||
import com.intellij.terminal.session.TerminalBlocksModelState
|
||||
import com.intellij.terminal.session.TerminalOutputBlock
|
||||
import com.intellij.terminal.session.TerminalSession
|
||||
import org.jetbrains.plugins.terminal.JBTerminalSystemSettingsProvider
|
||||
import org.jetbrains.plugins.terminal.LocalBlockTerminalRunner.Companion.REWORKED_TERMINAL_COMPLETION_POPUP
|
||||
import org.jetbrains.plugins.terminal.block.reworked.TerminalOutputModel
|
||||
import java.awt.event.KeyEvent
|
||||
import java.awt.event.KeyEvent.VK_UNDEFINED
|
||||
import java.util.concurrent.CompletableFuture
|
||||
import kotlin.time.TimeSource
|
||||
|
||||
class TerminalCompletionFixture(val project: Project, testRootDisposable: Disposable) {
|
||||
|
||||
private val view: ReworkedTerminalView
|
||||
|
||||
init {
|
||||
val sessionFuture: CompletableFuture<TerminalSession> = CompletableFuture<TerminalSession>()
|
||||
view = ReworkedTerminalView(project, JBTerminalSystemSettingsProvider(), sessionFuture, null)
|
||||
Disposer.register(testRootDisposable, view)
|
||||
val terminalOutputBlock = TerminalOutputBlock(0, 0, 0, -1, 0, null)
|
||||
val blocksModelState = TerminalBlocksModelState(listOf(terminalOutputBlock), 0)
|
||||
view.blocksModel.restoreFromState(blocksModelState)
|
||||
Registry.get(REWORKED_TERMINAL_COMPLETION_POPUP).setValue(true, testRootDisposable)
|
||||
Registry.get("terminal.type.ahead").setValue(true, testRootDisposable)
|
||||
}
|
||||
|
||||
fun type(text: String) {
|
||||
for (c in text) {
|
||||
typeChar(c)
|
||||
}
|
||||
}
|
||||
|
||||
fun typeChar(keyChar: Char) {
|
||||
val keyEvent = KeyEvent(view.outputEditor.component, KeyEvent.KEY_TYPED, 1, 0,
|
||||
VK_UNDEFINED, keyChar, KeyEvent.KEY_LOCATION_UNKNOWN)
|
||||
val timedKeyEvent = TimedKeyEvent(keyEvent, TimeSource.Monotonic.markNow())
|
||||
view.outputEditorEventsHandler.keyTyped(timedKeyEvent)
|
||||
}
|
||||
|
||||
fun callCompletionPopup() {
|
||||
val actionId = "Terminal.CommandCompletion.Gen2"
|
||||
val action = ActionManager.getInstance().getAction(actionId)
|
||||
|
||||
val context = SimpleDataContext.builder()
|
||||
.add(CommonDataKeys.PROJECT, project)
|
||||
.add(CommonDataKeys.EDITOR, view.outputEditor)
|
||||
.add(TerminalOutputModel.KEY, view.outputModel)
|
||||
.build()
|
||||
|
||||
val event = AnActionEvent.createEvent(action, context, null,
|
||||
"", ActionUiKind.NONE, null)
|
||||
|
||||
ActionUtil.updateAction(action, event)
|
||||
if (event.presentation.isEnabledAndVisible) {
|
||||
ActionUtil.performAction(action, event)
|
||||
}
|
||||
}
|
||||
|
||||
fun isLookupActive(): Boolean {
|
||||
val lookupManager = LookupManager.getInstance(project)
|
||||
return lookupManager.activeLookup != null
|
||||
}
|
||||
|
||||
fun getLookupElements(): List<LookupElement> {
|
||||
val lookupManager = LookupManager.getInstance(project)
|
||||
val activeLookup = lookupManager.activeLookup
|
||||
return activeLookup?.items ?: emptyList()
|
||||
}
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
package com.intellij.terminal.tests.reworked.frontend
|
||||
|
||||
import com.intellij.testFramework.fixtures.BasePlatformTestCase
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.junit.runners.JUnit4
|
||||
|
||||
@RunWith(JUnit4::class)
|
||||
class TerminalCompletionTest : BasePlatformTestCase() {
|
||||
|
||||
@Test
|
||||
fun `test completions list filtered on typing`() {
|
||||
val terminalCompletionFixture = TerminalCompletionFixture(project, testRootDisposable)
|
||||
terminalCompletionFixture.type("git s")
|
||||
terminalCompletionFixture.callCompletionPopup()
|
||||
assertEquals(true, terminalCompletionFixture.isLookupActive())
|
||||
val beforeResult = terminalCompletionFixture.getLookupElements()
|
||||
val beforeResultStrings = beforeResult.map { it.lookupString }
|
||||
assertEquals(listOf("show", "stage", "stash", "status", "submodule", "switch"), beforeResultStrings)
|
||||
|
||||
terminalCompletionFixture.type("t")
|
||||
val result = terminalCompletionFixture.getLookupElements()
|
||||
val resultStrings = result.map { it.lookupString }
|
||||
assertEquals(listOf("stage", "stash", "status"), resultStrings)
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user