From b4d528a3b09232ec95d7cf76ec2d5bc3e081741e Mon Sep 17 00:00:00 2001 From: Konstantin Hudyakov Date: Fri, 11 Apr 2025 13:47:25 +0300 Subject: [PATCH] [terminal] IJPL-182482 Refactor: use TtyListener instead of additional tty connector wrapping GitOrigin-RevId: 90e3eed07269cb40ee99c2b1f704f16b44b4043a --- .../backend/ObservableTtyConnector.kt | 15 +++-- .../terminal/backend/TerminalSessionStart.kt | 8 ++- .../terminal/backend/TtyConnectorListener.kt | 4 +- .../backend/fus/BackendLatencyServiceImpl.kt | 67 +++++++------------ 4 files changed, 41 insertions(+), 53 deletions(-) diff --git a/plugins/terminal/backend/src/com/intellij/terminal/backend/ObservableTtyConnector.kt b/plugins/terminal/backend/src/com/intellij/terminal/backend/ObservableTtyConnector.kt index 781a7d140d8e..9baf1462862e 100644 --- a/plugins/terminal/backend/src/com/intellij/terminal/backend/ObservableTtyConnector.kt +++ b/plugins/terminal/backend/src/com/intellij/terminal/backend/ObservableTtyConnector.kt @@ -25,15 +25,20 @@ class ObservableTtyConnector(delegate: TtyConnector) : ProxyTtyConnector { override fun read(buf: CharArray, offset: Int, length: Int): Int { val charsReadCount = connector.read(buf, offset, length) if (charsReadCount > 0) { - fireCharsRead(buf, offset, charsReadCount) + fireListeners { it.charsRead(buf, offset, length) } } return charsReadCount } - private fun fireCharsRead(buf: CharArray, offset: Int, length: Int) { + override fun write(bytes: ByteArray) { + connector.write(bytes) + fireListeners { it.bytesWritten(bytes) } + } + + private inline fun fireListeners(action: (TtyConnectorListener) -> Unit) { for (listener in listeners) { try { - listener.charsRead(buf, offset, length) + action(listener) } catch (t: Throwable) { thisLogger().error(t) @@ -41,10 +46,6 @@ class ObservableTtyConnector(delegate: TtyConnector) : ProxyTtyConnector { } } - override fun write(bytes: ByteArray?) { - connector.write(bytes) - } - override fun write(string: String?) { connector.write(string) } diff --git a/plugins/terminal/backend/src/com/intellij/terminal/backend/TerminalSessionStart.kt b/plugins/terminal/backend/src/com/intellij/terminal/backend/TerminalSessionStart.kt index 71e00ef9163f..bc5be076d9ec 100644 --- a/plugins/terminal/backend/src/com/intellij/terminal/backend/TerminalSessionStart.kt +++ b/plugins/terminal/backend/src/com/intellij/terminal/backend/TerminalSessionStart.kt @@ -7,9 +7,11 @@ import com.intellij.platform.util.coroutines.childScope import com.intellij.terminal.JBTerminalSystemSettingsProviderBase import com.intellij.terminal.TerminalExecutorServiceManagerImpl import com.intellij.terminal.backend.fus.enableFus +import com.intellij.terminal.backend.fus.installFusListener import com.intellij.terminal.session.TerminalSession import com.intellij.terminal.session.TerminalSessionTerminatedEvent import com.intellij.util.AwaitCancellationAndInvoke +import com.intellij.util.asDisposable import com.intellij.util.awaitCancellationAndInvoke import com.jediterm.core.typeahead.TerminalTypeAheadManager import com.jediterm.core.util.TermSize @@ -55,9 +57,11 @@ internal fun createTerminalSession( coroutineScope: CoroutineScope, fusActivity: BackendOutputActivity, ): TerminalSession { - val connector = enableFus(ttyConnector, fusActivity) + val observableTtyConnector = ttyConnector as? ObservableTtyConnector ?: ObservableTtyConnector(ttyConnector) + installFusListener(observableTtyConnector, fusActivity, parentDisposable = coroutineScope.asDisposable()) + val maxHistoryLinesCount = AdvancedSettings.getInt("terminal.buffer.max.lines.count") - val services: JediTermServices = createJediTermServices(connector, fusActivity, initialSize, maxHistoryLinesCount, settings) + val services: JediTermServices = createJediTermServices(observableTtyConnector, fusActivity, initialSize, maxHistoryLinesCount, settings) val outputScope = coroutineScope.childScope("Terminal output forwarding") val shellIntegrationController = TerminalShellIntegrationController(services.controller) diff --git a/plugins/terminal/backend/src/com/intellij/terminal/backend/TtyConnectorListener.kt b/plugins/terminal/backend/src/com/intellij/terminal/backend/TtyConnectorListener.kt index 8f42f2610255..a614088614b8 100644 --- a/plugins/terminal/backend/src/com/intellij/terminal/backend/TtyConnectorListener.kt +++ b/plugins/terminal/backend/src/com/intellij/terminal/backend/TtyConnectorListener.kt @@ -4,5 +4,7 @@ import org.jetbrains.annotations.ApiStatus @ApiStatus.Internal interface TtyConnectorListener { - fun charsRead(buf: CharArray, offset: Int, length: Int) + fun charsRead(buf: CharArray, offset: Int, length: Int) {} + + fun bytesWritten(bytes: ByteArray) {} } \ No newline at end of file diff --git a/plugins/terminal/backend/src/com/intellij/terminal/backend/fus/BackendLatencyServiceImpl.kt b/plugins/terminal/backend/src/com/intellij/terminal/backend/fus/BackendLatencyServiceImpl.kt index f9053c72261f..caa80156be4c 100644 --- a/plugins/terminal/backend/src/com/intellij/terminal/backend/fus/BackendLatencyServiceImpl.kt +++ b/plugins/terminal/backend/src/com/intellij/terminal/backend/fus/BackendLatencyServiceImpl.kt @@ -1,16 +1,15 @@ package com.intellij.terminal.backend.fus +import com.intellij.openapi.Disposable import com.intellij.openapi.diagnostic.logger import com.intellij.platform.rpc.UID +import com.intellij.terminal.backend.ObservableTtyConnector +import com.intellij.terminal.backend.TtyConnectorListener import com.intellij.terminal.session.TerminalContentUpdatedEvent import com.intellij.terminal.session.TerminalWriteBytesEvent -import com.jediterm.core.util.TermSize import com.jediterm.terminal.TerminalDataStream -import com.jediterm.terminal.TtyBasedArrayDataStream -import com.jediterm.terminal.TtyConnector import fleet.multiplatform.shims.ConcurrentHashMap import org.jetbrains.plugins.terminal.fus.BackendLatencyService -import org.jetbrains.plugins.terminal.fus.BackendLatencyService.Companion.getInstance import org.jetbrains.plugins.terminal.fus.BackendOutputActivity import org.jetbrains.plugins.terminal.fus.BackendTypingActivity import org.jetbrains.plugins.terminal.fus.ReworkedTerminalUsageCollector @@ -37,8 +36,27 @@ internal class BackendLatencyServiceImpl : BackendLatencyService { } } -internal fun enableFus(ttyConnector: TtyConnector, fusActivity: BackendOutputActivity): TtyConnector = - FusAwareTtyConnector(ttyConnector, fusActivity) +internal fun installFusListener( + ttyConnector: ObservableTtyConnector, + fusActivity: BackendOutputActivity, + parentDisposable: Disposable, +) { + ttyConnector.addListener(parentDisposable, object : TtyConnectorListener { + override fun charsRead(buf: CharArray, offset: Int, length: Int) { + fusActivity.charsRead(length) + } + + override fun bytesWritten(bytes: ByteArray) { + val typingActivity = BackendLatencyService.getInstance().getBackendTypingActivityOrNull(bytes) ?: return + try { + typingActivity.reportDuration() + } + finally { + typingActivity.finishBytesProcessing() + } + } + }) +} internal fun enableFus(stream: TerminalDataStream, fusActivity: BackendOutputActivity): TerminalDataStream = FusAwareTtyBasedDataStream(stream, fusActivity) @@ -296,43 +314,6 @@ private class BackendOutputActivityImpl : BackendOutputActivity { } } -private class FusAwareTtyConnector(private val original: TtyConnector, private val outputActivity: BackendOutputActivity) : TtyConnector { - override fun read(buf: CharArray, offset: Int, length: Int): Int = original.read(buf, offset, length).also { charsRead -> - outputActivity.charsRead(charsRead) - } - - override fun write(bytes: ByteArray) { - val typingActivity = getInstance().getBackendTypingActivityOrNull(bytes) - try { - original.write(bytes) - typingActivity?.reportDuration() - } - finally { - typingActivity?.finishBytesProcessing() - } - } - - override fun write(string: String) { - original.write(string) - } - - override fun isConnected(): Boolean = original.isConnected - - override fun waitFor(): Int = original.waitFor() - - override fun ready(): Boolean = original.ready() - - override fun getName(): String? = original.name - - override fun close() { - original.close() - } - - override fun resize(termSize: TermSize) { - original.resize(termSize) - } -} - // used to track individual instances of data classes private class IdentityWrapper(private val instance: T) { override fun equals(other: Any?): Boolean = instance === (other as? IdentityWrapper)?.instance