terminal: simplify scraping output (IJ-CR-121332)

GitOrigin-RevId: 927d3066c83eb475695e9614808dadb05988d134
This commit is contained in:
Sergey Simonchik
2023-12-04 16:03:34 +01:00
committed by intellij-monorepo-bot
parent 8017a66973
commit 9241925fa9
3 changed files with 80 additions and 23 deletions

View File

@@ -52,7 +52,7 @@ internal class ShellCommandOutputScraper(private val session: TerminalSession,
fun scrapeOutput(): StyledCommandOutput {
session.model.withContentLock {
val outputBuilder = OutputBuilder(textBuffer.width)
val outputBuilder = OutputBuilder()
if (!textBuffer.isUsingAlternateBuffer) {
outputBuilder.addLines(textBuffer.historyBuffer)
}
@@ -62,11 +62,10 @@ internal class ShellCommandOutputScraper(private val session: TerminalSession,
}
}
private class OutputBuilder(private val columns: Int) {
private class OutputBuilder {
private val output: StringBuilder = StringBuilder()
private val styles: MutableList<StyleRange> = mutableListOf()
private var pendingNewLines: Int = 0
private var pendingNuls: Int = 0
fun addLines(linesBuffer: LinesBuffer) {
for (i in 0 until linesBuffer.lineCount) {
@@ -75,26 +74,13 @@ private class OutputBuilder(private val columns: Int) {
}
private fun addLine(line: TerminalLine) {
var addedLength = 0
line.forEachEntry { entry ->
val charBuffer: CharBuffer = entry.text
val length = charBuffer.length
addedLength += length
if (length > 0) {
if (entry.isNul) {
pendingNuls += length
}
else {
addTextChunk(charBuffer.normalize(), entry.style)
}
if (entry.text.isNotEmpty() && !entry.isNul) {
addTextChunk(entry.text.normalize(), entry.style)
}
}
if (line.isWrapped) {
pendingNuls += (columns - addedLength).coerceAtLeast(0)
}
else {
if (!line.isWrapped) {
pendingNewLines++
pendingNuls = 0
}
}
@@ -109,10 +95,6 @@ private class OutputBuilder(private val columns: Int) {
output.append("\n")
}
pendingNewLines = 0
repeat(pendingNuls) {
output.append(' ')
}
pendingNuls = 0
val startOffset = output.length
output.append(text)
if (style != TextStyle.EMPTY) {

View File

@@ -11,6 +11,7 @@ import com.intellij.testFramework.ProjectRule
import com.intellij.testFramework.RuleChain
import com.jediterm.core.util.TermSize
import kotlinx.coroutines.*
import org.jetbrains.plugins.terminal.block.testApps.MoveCursorToLineEndAndPrint
import org.jetbrains.plugins.terminal.block.testApps.SimpleTextRepeater
import org.jetbrains.plugins.terminal.exp.*
import org.jetbrains.plugins.terminal.exp.completion.IJShellRuntimeDataProvider
@@ -118,6 +119,39 @@ class BlockTerminalTest(private val shellPath: String) {
assertCommandResult(0, SimpleTextRepeater.Helper.getExpectedOutput(items), outputFuture)
}
@Test
fun `mix of empty area and text #1`() {
val termSize = TermSize(10, 10)
val session = startBlockTerminalSession(termSize)
val outputFuture: CompletableFuture<CommandResult> = getCommandResultFuture(session)
val textsToPrint = listOf("foo")
session.sendCommandToExecuteWithoutAddingToHistory(MoveCursorToLineEndAndPrint.Helper.generateCommandLine(textsToPrint))
assertCommandResult(0, MoveCursorToLineEndAndPrint.Helper.getExpectedOutput(termSize, textsToPrint), outputFuture)
}
@Test
fun `mix of empty area and text #2`() {
val termSize = TermSize(10, 10)
val session = startBlockTerminalSession(termSize)
val outputFuture: CompletableFuture<CommandResult> = getCommandResultFuture(session)
val textsToPrint = listOf("foo", "a".repeat(termSize.columns + 1), "b")
session.sendCommandToExecuteWithoutAddingToHistory(MoveCursorToLineEndAndPrint.Helper.generateCommandLine(textsToPrint))
assertCommandResult(0, MoveCursorToLineEndAndPrint.Helper.getExpectedOutput(termSize, textsToPrint), outputFuture)
}
@Test
fun `mix of empty area and text #3`() {
val termSize = TermSize(15, 15)
val session = startBlockTerminalSession(termSize)
val outputFuture: CompletableFuture<CommandResult> = getCommandResultFuture(session)
val textsToPrint = ('a'..'z').map {
val cnt = it - 'a'
it.toString().repeat(cnt) + cnt.toString()
}
session.sendCommandToExecuteWithoutAddingToHistory(MoveCursorToLineEndAndPrint.Helper.generateCommandLine(textsToPrint))
assertCommandResult(0, MoveCursorToLineEndAndPrint.Helper.getExpectedOutput(termSize, textsToPrint), outputFuture)
}
private fun createCommandSentDeferred(session: TerminalSession): CompletableDeferred<Unit> {
val generatorCommandSent = CompletableDeferred<Unit>()
val generatorCommandSentDisposable = Disposer.newDisposable().also { disposable ->

View File

@@ -0,0 +1,41 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.jetbrains.plugins.terminal.block.testApps
import com.jediterm.core.util.TermSize
import org.jetbrains.plugins.terminal.exp.util.TerminalSessionTestUtil
object MoveCursorToLineEndAndPrint {
@JvmStatic
fun main(arg: Array<String>) {
check(arg.isNotEmpty()) { "One or more arguments are expected, got " + arg.size }
for (textToPrint in arg) {
check(textToPrint.isNotEmpty())
// [java.io.Console] doesn't provide terminal width =>
// To place the cursor at the line end, move the cursor far away to the right - it should stop as it reaches the edge.
val infiniteWidth = 10000
print("\u001b[${infiniteWidth}C")
print(textToPrint)
}
println() // new line to get rid of trailing '%' in zsh
}
object Helper {
fun generateCommandLine(textsToPrint: List<String>): String {
return TerminalSessionTestUtil.getJavaShellCommand(MoveCursorToLineEndAndPrint::class.java, *textsToPrint.toTypedArray())
}
fun getExpectedOutput(termSize: TermSize, textsToPrint: List<String>): String {
return " ".repeat(termSize.columns - 1) + textsToPrint.dropLast(1).joinToString("") {
val emptyColumns = termSize.columns - (it.length - 1) % termSize.columns
check(emptyColumns > 0)
if (emptyColumns == termSize.columns) {
it.dropLast(1) // string ends at the right edge
}
else {
it + " ".repeat(emptyColumns - 1)
}
} + textsToPrint.last() + System.lineSeparator()
}
}
}