[terminal] IJPL-188055 fix detecting default shell using EelApi

GitOrigin-RevId: b1fba6f92158e14a6eea100d9407c873d2689b82
This commit is contained in:
Sergey Simonchik
2025-05-27 23:40:12 +02:00
committed by intellij-monorepo-bot
parent 5544fd6ebd
commit 2e5cefd8e0
3 changed files with 44 additions and 22 deletions

View File

@@ -10,7 +10,14 @@ import com.intellij.openapi.project.Project
import com.intellij.openapi.project.guessProjectDir
import com.intellij.openapi.util.SystemInfo
import com.intellij.openapi.util.io.FileUtil
import com.intellij.openapi.util.io.NioFiles
import com.intellij.openapi.util.text.Strings
import com.intellij.platform.eel.EelDescriptor
import com.intellij.platform.eel.isWindows
import com.intellij.platform.eel.provider.LocalEelDescriptor
import com.intellij.platform.eel.provider.getEelDescriptor
import com.intellij.platform.eel.provider.toEelApiBlocking
import com.intellij.platform.eel.provider.utils.fetchLoginShellEnvVariablesBlocking
import com.intellij.util.xmlb.annotations.Property
import org.jetbrains.plugins.terminal.settings.TerminalLocalOptions
import java.io.File
@@ -98,12 +105,23 @@ class TerminalProjectOptionsProvider(val project: Project) : PersistentStateComp
}
private fun isProjectLevelShellPath(workingDirectory: () -> String?): Boolean {
return SystemInfo.isWindows && findWslDistributionName(workingDirectory()) != null
val eelDescriptor = toEelDescriptor(workingDirectory)
return eelDescriptor !== LocalEelDescriptor
}
private fun toEelDescriptor(workingDirectory: () -> String?): EelDescriptor {
val path = workingDirectory()?.let {
NioFiles.toPath(it)
}
return path?.getEelDescriptor() ?: LocalEelDescriptor
}
fun defaultShellPath(): String = findDefaultShellPath { startingDirectory }
private fun findDefaultShellPath(workingDirectory: () -> String?): String {
if (shouldUseEelApi()) {
return findDefaultShellPath(toEelDescriptor(workingDirectory))
}
if (SystemInfo.isWindows) {
val wslDistributionName = findWslDistributionName(workingDirectory())
if (wslDistributionName != null) {
@@ -128,6 +146,14 @@ class TerminalProjectOptionsProvider(val project: Project) : PersistentStateComp
return if (directory == null) null else WslPath.parseWindowsUncPath(directory)?.distributionId
}
private fun findDefaultShellPath(eelDescriptor: EelDescriptor): String {
if (eelDescriptor.platform.isWindows) {
return "powershell.exe"
}
val eelApi = eelDescriptor.toEelApiBlocking()
return eelApi.exec.fetchLoginShellEnvVariablesBlocking()["SHELL"] ?: "/bin/sh"
}
companion object {
private val LOG = Logger.getInstance(TerminalProjectOptionsProvider::class.java)

View File

@@ -16,7 +16,6 @@ import com.intellij.openapi.vfs.impl.wsl.WslConstants;
import com.intellij.platform.eel.EelDescriptor;
import com.intellij.platform.eel.EelPlatform;
import com.intellij.platform.eel.provider.EelProviderUtil;
import com.intellij.platform.eel.provider.LocalEelDescriptor;
import com.intellij.platform.eel.provider.utils.EelUtilsKt;
import com.intellij.terminal.ui.TerminalWidget;
import com.intellij.util.EnvironmentRestorer;
@@ -34,7 +33,10 @@ import org.jetbrains.plugins.terminal.util.TerminalEnvironment;
import java.nio.file.Files;
import java.nio.file.InvalidPathException;
import java.nio.file.Path;
import java.util.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.stream.Collectors;
import static org.jetbrains.plugins.terminal.LocalTerminalDirectRunner.isDirectory;
@@ -50,7 +52,7 @@ public final class LocalOptionsConfigurer {
String workingDir = getWorkingDirectory(baseOptions.getWorkingDirectory(), project);
Map<String, String> envs = getTerminalEnvironment(baseOptions.getEnvVariables(), workingDir, project, eelDescriptor);
List<String> initialCommand = getInitialCommand(baseOptions, project, eelDescriptor);
List<String> initialCommand = getInitialCommand(baseOptions, project);
TerminalWidget widget = baseOptions.getWidget();
if (widget != null) {
widget.setShellCommand(initialCommand);
@@ -153,11 +155,7 @@ public final class LocalOptionsConfigurer {
return envs;
}
private static @NotNull List<String> getInitialCommand(@NotNull ShellStartupOptions options, @NotNull Project project, @Nullable EelDescriptor eelDescriptor) {
if (eelDescriptor != null && eelDescriptor != LocalEelDescriptor.INSTANCE) {
return LocalTerminalStartCommandBuilder.convertShellPathToCommand(Optional.of(fetchLoginShellEnvVariables(eelDescriptor)).map(e -> e.get("SHELL")).orElse("/bin/sh"));
}
private static @NotNull List<String> getInitialCommand(@NotNull ShellStartupOptions options, @NotNull Project project) {
List<String> shellCommand = options.getShellCommand();
return shellCommand != null ? shellCommand : LocalTerminalStartCommandBuilder.convertShellPathToCommand(getShellPath(project));
}

View File

@@ -11,6 +11,7 @@ import com.intellij.openapi.util.registry.Registry.Companion.`is`
import com.intellij.platform.eel.EelApi
import com.intellij.platform.eel.EelExecApi
import com.intellij.platform.eel.ExecuteProcessException
import com.intellij.platform.eel.provider.LocalEelDescriptor
import com.intellij.platform.eel.provider.asEelPath
import com.intellij.platform.eel.provider.getEelDescriptor
import com.intellij.platform.eel.spawnProcess
@@ -48,7 +49,7 @@ internal fun logCommonStartupInfo(
", time to process created: ${durationBetweenStartupAndConnectorCreated.toMillis()} ms")
}
@Throws(ErrnoException::class)
@Throws(ExecuteProcessException::class)
internal fun startProcess(
command: List<String>,
envs: Map<String, String>,
@@ -63,7 +64,7 @@ internal fun startProcess(
}
private suspend fun convertCommandToRemote(eelApi: EelApi, command: List<String>): List<String> {
if (isWslCommand(command)) {
if (eelApi.descriptor != LocalEelDescriptor && isWslCommand(command)) {
val shell = eelApi.exec.fetchLoginShellEnvVariables()["SHELL"] ?: "/bin/sh"
return listOf(shell, LocalTerminalDirectRunner.LOGIN_CLI_OPTION, LocalTerminalStartCommandBuilder.INTERACTIVE_CLI_OPTION)
}
@@ -88,9 +89,12 @@ private suspend fun getEelApi(
val wslDistribNameFromWorkingDirectory = WslPath.parseWindowsUncPath(workingDirectory.toString())?.distributionId
if (wslDistribNameFromCommandline != wslDistribNameFromWorkingDirectory) {
val wslRootPath = WSLDistribution(wslDistribNameFromCommandline).getUNCRootPath()
val eelApi = wslRootPath.getEelDescriptor().toEelApi()
val userHome = runCatching { eelApi.exec.fetchLoginShellEnvVariables()["HOME"] }.getOrNull()
return eelApi to wslRootPath.resolve(userHome ?: ".")
val eelDescriptor = wslRootPath.getEelDescriptor()
if (eelDescriptor != LocalEelDescriptor) {
val eelApi = eelDescriptor.toEelApi()
val userHome = runCatching { eelApi.exec.fetchLoginShellEnvVariables()["HOME"] }.getOrNull()
return eelApi to wslRootPath.resolve(userHome ?: ".")
}
}
}
return workingDirectory.getEelDescriptor().toEelApi() to workingDirectory
@@ -106,7 +110,7 @@ private fun getWslDistributionNameFromCommand(command: List<String>): String? {
return null
}
@Throws(ErrnoException::class)
@Throws(ExecuteProcessException::class)
private suspend fun doStartProcess(
eelApi: EelApi,
command: List<String>,
@@ -119,17 +123,11 @@ private suspend fun doStartProcess(
.env(envs)
.workingDirectory(workingDirectory.asEelPath())
.interactionOptions(EelExecApi.Pty(initialTermSize.columns, initialTermSize.rows, true))
return try {
execOptions.eelIt().convertToJavaProcess() as PtyProcess
} catch (e : ExecuteProcessException) {
throw ErrnoException(e)
}
return execOptions.eelIt().convertToJavaProcess() as PtyProcess
}
internal fun shouldUseEelApi(): Boolean {
return `is`("terminal.use.EelApi", false)
}
internal class ErrnoException(val error: ExecuteProcessException): Exception(error.message)
private val log: Logger = logger<AbstractTerminalRunner<*>>()