[pycharm] PY-85090 Improve close detection for LoggingProcess

[pycharm] PY-85090 Address feedback

[pycharm] PY-85090 Add empty screen when contexts are selected

[pycharm] PY-85090 Change expand/collapse all logic

[pycharm] PY-85090 Improve collapse all/expand all logic

[pycharm] PY-85090 Add bulk read override


Merge-request: IJ-MR-179958
Merged-by: David Lysenko <david.lysenko@jetbrains.com>

GitOrigin-RevId: 00b7b0dd121eeebcb5920514ca8a535520f71160
This commit is contained in:
David Lysenko
2025-10-25 10:41:56 +00:00
committed by intellij-monorepo-bot
parent 6287d2f876
commit 72f575fa9b
4 changed files with 56 additions and 24 deletions

View File

@@ -7,6 +7,7 @@ import com.intellij.openapi.components.Service
import com.intellij.openapi.components.service
import com.intellij.util.io.awaitExit
import com.intellij.util.io.readLineAsync
import com.intellij.util.io.toByteArray
import com.jetbrains.python.TraceContext
import com.jetbrains.python.errorProcessing.Exe
import kotlinx.coroutines.CoroutineScope
@@ -24,7 +25,9 @@ import java.io.IOException
import java.io.InputStream
import java.io.InputStreamReader
import java.io.OutputStream
import java.nio.ByteBuffer
import java.util.concurrent.TimeUnit
import java.util.concurrent.atomic.AtomicBoolean
import java.util.concurrent.atomic.AtomicInteger
import kotlin.time.Clock
import kotlin.time.Instant
@@ -196,24 +199,17 @@ private class LoggingInputStream(
) : InputStream() {
private val bytes = ByteStreams.newDataOutput()
private var tail = 0
private var closed = AtomicBoolean(false)
val byteArray
get() = bytes.toByteArray()
override fun read(): Int {
val byte = try {
backingInputStream.read()
if (closed.get()) {
return -1
}
catch (e: IOException) {
// ugly hack; but the Process' `.destroy` methods abruptly close
// the stream, making all pending readers throw an exception.
// we can handle this case as legal here
if (e.message == "Stream closed") {
return -1
}
throw e
}
val byte = backingInputStream.read()
if (tail < LoggingLimits.MAX_OUTPUT_SIZE && byte != -1) {
bytes.write(byte)
@@ -222,6 +218,39 @@ private class LoggingInputStream(
return byte
}
/**
* Chunked read. The read bytes are also logged into the corresponding byte stream. If limit of [LoggingLimits.MAX_OUTPUT_SIZE] is
* reached, then the logged bytes are truncated.
*/
override fun read(b: ByteArray, off: Int, len: Int): Int {
if (closed.get()) {
return -1
}
val finalLen = backingInputStream.read(b, off, len)
if (finalLen != -1) {
val truncatedLen = if (tail + finalLen > LoggingLimits.MAX_OUTPUT_SIZE) {
LoggingLimits.MAX_OUTPUT_SIZE - tail
}
else {
finalLen
}
if (truncatedLen > 0) {
bytes.write(b, off, truncatedLen)
tail += truncatedLen
}
}
return finalLen
}
override fun close() {
closed.set(true)
super.close()
}
}
private suspend fun collectOutputLines(

View File

@@ -57,7 +57,7 @@ internal interface ProcessOutputController {
fun collapseAllContexts()
fun expandAllContexts()
fun selectProcess(process: LoggedProcess)
fun selectProcess(process: LoggedProcess?)
fun toggleTreeFilter(filter: TreeFilter)
fun toggleOutputFilter(filter: OutputFilter)
fun toggleProcessInfo()
@@ -185,26 +185,27 @@ class ProcessOutputControllerService(
}
override fun collapseAllContexts() {
processTreeUiState.treeState.openNodes.forEach {
processTreeUiState.treeState.toggleNode(it)
}
processTreeUiState.treeState.openNodes = setOf()
ProcessOutputUsageCollector.treeCollapseAllClicked()
}
override fun expandAllContexts() {
loggedProcesses.value
.mapNotNull { it.traceContext }
.toSet()
.subtract(processTreeUiState.treeState.openNodes)
.forEach {
processTreeUiState.treeState.toggleNode(it)
}
processTreeUiState.treeState.openNodes =
processTreeUiState.tree.value
.walkDepthFirst()
.mapNotNull {
when (val data = it.data) {
is TreeNode.Context -> data.traceContext
is TreeNode.Process -> null
}
}
.toSet()
ProcessOutputUsageCollector.treeExpandAllClicked()
}
override fun selectProcess(process: LoggedProcess) {
override fun selectProcess(process: LoggedProcess?) {
selectedProcess.value = process
ProcessOutputUsageCollector.treeProcessSelected()
}

View File

@@ -90,6 +90,8 @@ internal fun TreeSection(controller: ProcessOutputController) {
if (node is TreeNode.Process) {
controller.selectProcess(node.process)
} else {
controller.selectProcess(null)
}
},
style = LazyTreeStyle(

View File

@@ -78,7 +78,7 @@ internal abstract class ProcessOutputTest {
controllerSpy.expandAllContexts()
}
override fun selectProcess(process: LoggedProcess) {
override fun selectProcess(process: LoggedProcess?) {
controllerSpy.selectProcess(process)
}