diff --git a/plugins/terminal/src/org/jetbrains/plugins/terminal/LocalTerminalTtyConnector.java b/plugins/terminal/src/org/jetbrains/plugins/terminal/LocalTerminalTtyConnector.java index 3e74cd449221..a950e4153e0a 100644 --- a/plugins/terminal/src/org/jetbrains/plugins/terminal/LocalTerminalTtyConnector.java +++ b/plugins/terminal/src/org/jetbrains/plugins/terminal/LocalTerminalTtyConnector.java @@ -1,15 +1,19 @@ // Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package org.jetbrains.plugins.terminal; +import com.google.common.base.Ascii; import com.intellij.openapi.diagnostic.Logger; import com.intellij.terminal.pty.PtyProcessTtyConnector; import com.intellij.util.concurrency.AppExecutorUtil; import com.jediterm.core.util.TermSize; import com.pty4j.PtyProcess; import com.pty4j.unix.UnixPtyProcess; +import com.pty4j.windows.conpty.WinConPtyProcess; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; +import java.io.IOException; +import java.io.OutputStream; import java.nio.charset.Charset; import java.util.concurrent.TimeUnit; @@ -35,10 +39,35 @@ public class LocalTerminalTtyConnector extends PtyProcessTtyConnector { }, 1000, TimeUnit.MILLISECONDS); } else { + if (myProcess instanceof WinConPtyProcess winConPtyProcess && !winConPtyProcess.isBundledConPtyLibrary()) { + sendInterruptToWinConPtyProcess(); + } myProcess.destroy(); } } + /** + * Workaround for the ConPTY issue #15373 + * which was fixed in Windows Terminal 1.22. + * Even though this version is bundled with IDE, ConPTY can be loaded from the OS. + * @see workaround discussion + */ + private void sendInterruptToWinConPtyProcess() { + OutputStream outputStream = myProcess.getOutputStream(); + if (outputStream != null && myProcess.isAlive()) { + try { + // ConPTY will process `0x03` and raise `CTRL_C_EVENT`. + // This will hopefully terminate the whole process hierarchy of running user command, + // which is a must for commands like `npm run dev`. + outputStream.write(Ascii.ETX); + outputStream.flush(); + } + catch (IOException e) { + LOG.info("Failed to send Ctrl+C to " + myProcess.getClass().getSimpleName() + ", alive:" + myProcess.isAlive(), e); + } + } + } + @Override public void resize(@NotNull TermSize termSize) { if (LOG.isDebugEnabled()) {