diff --git a/platform/execution-impl/src/com/intellij/terminal/TerminalExecutionConsole.java b/platform/execution-impl/src/com/intellij/terminal/TerminalExecutionConsole.java index 64293c67435b..e19a01f43d4f 100644 --- a/platform/execution-impl/src/com/intellij/terminal/TerminalExecutionConsole.java +++ b/platform/execution-impl/src/com/intellij/terminal/TerminalExecutionConsole.java @@ -268,12 +268,12 @@ public class TerminalExecutionConsole implements ConsoleView, ObservableConsoleV myTerminalWidget.createTerminalSession(ttyConnector); myTerminalWidget.start(); if (attachToProcessOutput) { - boolean convertLfToCrlf = shouldConvertLfToCrlf(processHandler); + boolean isProcessWithPty = isProcessWithPty(processHandler); if (processHandler instanceof ColoredProcessHandler coloredProcessHandler) { coloredProcessHandler.addRawTextListener(new ColoredProcessHandler.RawTextListener() { @Override public void onRawTextAvailable(@NotNull String text, @NotNull Key outputType) { - processProcessOutputText(text, outputType, convertLfToCrlf); + processProcessOutputText(text, outputType, isProcessWithPty); } }); } @@ -281,7 +281,7 @@ public class TerminalExecutionConsole implements ConsoleView, ObservableConsoleV processHandler.addProcessListener(new ProcessListener() { @Override public void onTextAvailable(@NotNull ProcessEvent event, @NotNull Key outputType) { - processProcessOutputText(event.getText(), outputType, convertLfToCrlf); + processProcessOutputText(event.getText(), outputType, isProcessWithPty); } }); } @@ -297,7 +297,7 @@ public class TerminalExecutionConsole implements ConsoleView, ObservableConsoleV }); } - private void processProcessOutputText(@NotNull String text, @NotNull Key outputType, boolean convertLfToCrlf) { + private void processProcessOutputText(@NotNull String text, @NotNull Key outputType, boolean isProcessWithPty) { try { ConsoleViewContentType contentType = null; if (outputType != ProcessOutputTypes.STDOUT) { @@ -306,7 +306,7 @@ public class TerminalExecutionConsole implements ConsoleView, ObservableConsoleV if (outputType == ProcessOutputTypes.SYSTEM) { text = StringUtil.convertLineSeparators(text, LineSeparator.CRLF.getSeparatorString()); } - else if (convertLfToCrlf) { + else if (!isProcessWithPty && myConvertLfToCrlfForNonPtyProcess) { text = convertTextToCRLF(text); } ConsoleViewContentType notNullContentType = ObjectUtils.notNull(contentType, ConsoleViewContentType.NORMAL_OUTPUT); @@ -327,16 +327,13 @@ public class TerminalExecutionConsole implements ConsoleView, ObservableConsoleV } } - private boolean shouldConvertLfToCrlf(@NotNull ProcessHandler processHandler) { - return myConvertLfToCrlfForNonPtyProcess && isNonPtyProcess(processHandler); - } - - private static boolean isNonPtyProcess(@NotNull ProcessHandler processHandler) { - if (processHandler instanceof BaseProcessHandler) { - Process process = ((BaseProcessHandler)processHandler).getProcess(); - return !(process instanceof PtyProcess); + private static boolean isProcessWithPty(@NotNull ProcessHandler processHandler) { + if (processHandler instanceof BaseProcessHandler baseProcessHandler) { + Process process = baseProcessHandler.getProcess(); + return process instanceof PtyProcess || + (process instanceof PtyBasedProcess ptyBasedProcess && ptyBasedProcess.hasPty()); } - return true; + return false; } @Override @@ -415,12 +412,7 @@ public class TerminalExecutionConsole implements ConsoleView, ObservableConsoleV } public static boolean isAcceptable(@NotNull ProcessHandler processHandler) { - if (processHandler instanceof BaseProcessHandler baseProcessHandler) { - Process process = baseProcessHandler.getProcess(); - return process instanceof PtyProcess || - (process instanceof PtyBasedProcess && ((PtyBasedProcess)process).hasPty()); - } - return false; + return isProcessWithPty(processHandler); } private final class ConsoleTerminalWidget extends JBTerminalWidget { diff --git a/platform/execution-impl/testSources/com/intellij/terminal/TerminalExecutionConsoleTest.kt b/platform/execution-impl/testSources/com/intellij/terminal/TerminalExecutionConsoleTest.kt index 50da6596088f..22e75353174d 100644 --- a/platform/execution-impl/testSources/com/intellij/terminal/TerminalExecutionConsoleTest.kt +++ b/platform/execution-impl/testSources/com/intellij/terminal/TerminalExecutionConsoleTest.kt @@ -54,14 +54,37 @@ class TerminalExecutionConsoleTest : BasePlatformTestCase() { return threadInfos.find { it.threadName.startsWith("TerminalEmulator-") } } + fun `test convert LF to CRLF for processes without PTY`(): Unit = timeoutRunBlocking(DEFAULT_TEST_TIMEOUT) { + val processHandler = OSProcessHandler(MockPtyBasedProcess(false), "my command", Charsets.UTF_8) + val console = withContext(Dispatchers.UI) { + TerminalExecutionConsole(project, processHandler) + } + console.withConvertLfToCrlfForNonPtyProcess(true) + TestProcessTerminationMessage.attach(processHandler) + processHandler.startNotify() + processHandler.notifyTextAvailable("Foo\nBar\nBaz", ProcessOutputTypes.STDOUT) + processHandler.destroyProcess() + console.awaitOutputContainsSubstring(substringToFind = TestProcessTerminationMessage.getMessage(MockPtyBasedProcess.EXIT_CODE)) + val output = TerminalOutput.collect(console.terminalWidget) + output.assertLinesAre(listOf( + "my command", + "Foo", + "Bar", + "Baz", + TestProcessTerminationMessage.getMessage(MockPtyBasedProcess.EXIT_CODE) + )) + withContext(Dispatchers.UI) { + Disposer.dispose(console) + } + } + fun `test support ColoredProcessHandler`(): Unit = timeoutRunBlockingWithConsole { console -> - val processHandler = ColoredProcessHandler(MockPtyBasedProcess(), "my command line", Charsets.UTF_8) + val processHandler = ColoredProcessHandler(MockPtyBasedProcess(true), "my command line", Charsets.UTF_8) assertTrue(TerminalExecutionConsole.isAcceptable(processHandler)) console.attachToProcess(processHandler) processHandler.startNotify() processHandler.notifyTextAvailable("\u001b[0m", ProcessOutputTypes.STDOUT) processHandler.notifyTextAvailable("\u001b[32mFoo\u001b[0m", ProcessOutputTypes.STDOUT) - processHandler.setShouldDestroyProcessRecursively(false) TestProcessTerminationMessage.attach(processHandler) processHandler.destroyProcess() console.awaitOutputContainsSubstring(substringToFind = TestProcessTerminationMessage.getMessage(MockPtyBasedProcess.EXIT_CODE)) @@ -75,7 +98,7 @@ class TerminalExecutionConsoleTest : BasePlatformTestCase() { } fun `test support OSProcessHandler`(): Unit = timeoutRunBlockingWithConsole { console -> - val processHandler = OSProcessHandler(MockPtyBasedProcess(), "command line", Charsets.UTF_8) + val processHandler = OSProcessHandler(MockPtyBasedProcess(true), "command line", Charsets.UTF_8) assertTrue(TerminalExecutionConsole.isAcceptable(processHandler)) console.attachToProcess(processHandler) processHandler.startNotify() diff --git a/platform/execution-impl/testSources/com/intellij/terminal/terminalTestExecution.kt b/platform/execution-impl/testSources/com/intellij/terminal/terminalTestExecution.kt index 9d10124ea6ed..c170f59572a5 100644 --- a/platform/execution-impl/testSources/com/intellij/terminal/terminalTestExecution.kt +++ b/platform/execution-impl/testSources/com/intellij/terminal/terminalTestExecution.kt @@ -5,6 +5,7 @@ import com.intellij.execution.process.KillableProcessHandler import com.intellij.execution.process.ProcessHandler import com.intellij.execution.process.ProcessTerminatedListener import com.intellij.execution.process.PtyBasedProcess +import com.intellij.execution.process.SelfKiller import com.intellij.platform.eel.EelExecApi.Pty import com.intellij.platform.eel.ExecuteProcessException import com.intellij.util.io.BaseDataReader @@ -43,7 +44,7 @@ internal fun ProcessHandler.writeToStdinAndHitEnter(input: String) { } } -internal class MockPtyBasedProcess : Process(), PtyBasedProcess { +internal class MockPtyBasedProcess(private val withPty: Boolean) : Process(), PtyBasedProcess, SelfKiller { private val exitCodeFuture: CompletableFuture = CompletableFuture() @@ -61,7 +62,7 @@ internal class MockPtyBasedProcess : Process(), PtyBasedProcess { override fun getErrorStream(): InputStream = InputStream.nullInputStream() override fun getInputStream(): InputStream = InputStream.nullInputStream() - override fun hasPty(): Boolean = true + override fun hasPty(): Boolean = withPty override fun setWindowSize(columns: Int, rows: Int) {}