diff --git a/plugins/terminal/intellij.terminal.iml b/plugins/terminal/intellij.terminal.iml index 64469073b938..fbd2d2ca13e6 100644 --- a/plugins/terminal/intellij.terminal.iml +++ b/plugins/terminal/intellij.terminal.iml @@ -111,5 +111,7 @@ + + \ No newline at end of file diff --git a/plugins/terminal/resources/META-INF/terminal.xml b/plugins/terminal/resources/META-INF/terminal.xml index fed2cd572883..d27436d501f4 100644 --- a/plugins/terminal/resources/META-INF/terminal.xml +++ b/plugins/terminal/resources/META-INF/terminal.xml @@ -63,6 +63,9 @@ + diff --git a/plugins/terminal/src/org/jetbrains/plugins/terminal/LocalTerminalDirectRunner.java b/plugins/terminal/src/org/jetbrains/plugins/terminal/LocalTerminalDirectRunner.java index 3db188e763dc..3d6bf4d9e8e5 100644 --- a/plugins/terminal/src/org/jetbrains/plugins/terminal/LocalTerminalDirectRunner.java +++ b/plugins/terminal/src/org/jetbrains/plugins/terminal/LocalTerminalDirectRunner.java @@ -36,6 +36,7 @@ import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import static org.jetbrains.plugins.terminal.LocalBlockTerminalRunner.*; +import static org.jetbrains.plugins.terminal.TerminalStartupKt.startProcess; import static org.jetbrains.plugins.terminal.util.ShellNameUtil.*; public class LocalTerminalDirectRunner extends AbstractTerminalRunner { @@ -110,21 +111,33 @@ public class LocalTerminalDirectRunner extends AbstractTerminalRunner, + envs: Map, + initialWorkingDirectory: Path, + initialTermSize: TermSize, +): PtyProcess { + return runBlockingMaybeCancellable { + val (eelApi, workingDirectory) = getEelApi(initialWorkingDirectory, command) + val remoteCommand = convertCommandToRemote(eelApi, command) + doStartProcess(eelApi, remoteCommand, envs, workingDirectory, initialTermSize) + } +} + +private suspend fun convertCommandToRemote(eelApi: EelApi, command: List): List { + if (isWslCommand(command)) { + val shell = eelApi.exec.fetchLoginShellEnvVariables()["SHELL"] ?: "/bin/sh" + return listOf(shell, LocalTerminalDirectRunner.LOGIN_CLI_OPTION, LocalTerminalStartCommandBuilder.INTERACTIVE_CLI_OPTION) + } + return command +} + +private fun isWslCommand(command: List): Boolean { + if (SystemInfo.isWindows) { + val exePath = command.getOrNull(0) ?: return false + val exeFileName = PathUtil.getFileName(exePath) + return exeFileName.equals("wsl.exe", true) || exeFileName.equals("wsl", true) + } + return false +} + +private suspend fun getEelApi( + workingDirectory: Path, + command: List, +): Pair { + val wslDistribNameFromCommandline = getWslDistributionNameFromCommand(command) + if (wslDistribNameFromCommandline != null) { + val wslDistribNameFromWorkingDirectory = WslPath.parseWindowsUncPath(workingDirectory.toString())?.distributionId + if (wslDistribNameFromCommandline != wslDistribNameFromWorkingDirectory) { + val wslRootPath = WSLDistribution(wslDistribNameFromCommandline).getUNCRootPath() + val eelApi = wslRootPath.getEelDescriptor().upgrade() + val userHome = runCatching { eelApi.exec.fetchLoginShellEnvVariables()["HOME"] }.getOrNull() + return eelApi to wslRootPath.resolve(userHome ?: ".") + } + } + return workingDirectory.getEelDescriptor().upgrade() to workingDirectory +} + +private fun getWslDistributionNameFromCommand(command: List): String? { + if (isWslCommand(command)) { + val distributionOptionName = command.getOrNull(1) + if (distributionOptionName == "-d" || distributionOptionName == "--distribution") { + return command.getOrNull(2) + } + } + return null +} + +@Throws(ErrnoException::class) +private suspend fun doStartProcess( + eelApi: EelApi, + command: List, + envs: Map, + workingDirectory: Path, + initialTermSize: TermSize, +): PtyProcess { + // TODO migrate to generated builders in 252 (not available in 251) + @Suppress("DEPRECATION") + val execOptions = EelExecApi.ExecuteProcessOptions.Builder(command.first()) + .args(command.takeLast(command.size - 1)) + .env(envs) + .workingDirectory(workingDirectory.asEelPath()) + .ptyOrStdErrSettings(EelExecApi.Pty(initialTermSize.columns, initialTermSize.rows, true)) + .build() + val processResult = eelApi.exec.execute(execOptions) + return when (processResult) { + is EelResult.Ok -> processResult.value.convertToJavaProcess() as PtyProcess + is EelResult.Error -> throw ErrnoException(processResult.error) + } +} + +internal class ErrnoException(val error: ExecuteProcessError): Exception(error.message) + private val log: Logger = logger>()