diff --git a/plugins/terminal/backend/BUILD.bazel b/plugins/terminal/backend/BUILD.bazel
index 7283952d4710..2c7e9a0a475b 100644
--- a/plugins/terminal/backend/BUILD.bazel
+++ b/plugins/terminal/backend/BUILD.bazel
@@ -31,6 +31,8 @@ jvm_library(
"//platform/statistics",
"//platform/core-ui",
"//platform/lang-api:lang",
+ "//platform/eel",
+ "//platform/eel-provider",
]
)
### auto-generated section `build intellij.terminal.backend` end
\ No newline at end of file
diff --git a/plugins/terminal/backend/intellij.terminal.backend.iml b/plugins/terminal/backend/intellij.terminal.backend.iml
index ed21d9fbb38f..c724f58ad37c 100644
--- a/plugins/terminal/backend/intellij.terminal.backend.iml
+++ b/plugins/terminal/backend/intellij.terminal.backend.iml
@@ -50,5 +50,7 @@
+
+
\ No newline at end of file
diff --git a/plugins/terminal/backend/src/com/intellij/terminal/backend/TerminalTabsManager.kt b/plugins/terminal/backend/src/com/intellij/terminal/backend/TerminalTabsManager.kt
index 85b5831f52b3..4e665bb56210 100644
--- a/plugins/terminal/backend/src/com/intellij/terminal/backend/TerminalTabsManager.kt
+++ b/plugins/terminal/backend/src/com/intellij/terminal/backend/TerminalTabsManager.kt
@@ -5,21 +5,29 @@ import com.intellij.codeWithMe.ClientIdContextElement
import com.intellij.openapi.components.Service
import com.intellij.openapi.components.service
import com.intellij.openapi.diagnostic.Logger
+import com.intellij.openapi.diagnostic.debug
import com.intellij.openapi.diagnostic.logger
import com.intellij.openapi.project.Project
+import com.intellij.platform.eel.EelDescriptor
+import com.intellij.platform.eel.path.EelPath
+import com.intellij.platform.eel.path.EelPathException
+import com.intellij.platform.eel.provider.asNioPath
import com.intellij.platform.util.coroutines.childScope
import com.intellij.util.AwaitCancellationAndInvoke
+import com.intellij.util.asSafely
import com.intellij.util.awaitCancellationAndInvoke
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.withContext
+import org.jetbrains.plugins.terminal.LocalTerminalTtyConnector
import org.jetbrains.plugins.terminal.ShellStartupOptions
import org.jetbrains.plugins.terminal.block.reworked.session.TerminalSessionTab
import org.jetbrains.plugins.terminal.session.impl.TerminalCloseEvent
import org.jetbrains.plugins.terminal.session.impl.TerminalStateChangedEvent
import java.util.concurrent.atomic.AtomicInteger
+import kotlin.io.path.pathString
@OptIn(AwaitCancellationAndInvoke::class)
@Service(Service.Level.PROJECT)
@@ -89,7 +97,9 @@ internal class TerminalTabsManager(private val project: Project, private val cor
)
tabs[tabId] = updatedTab
- trackWorkingDirectory(updatedTab, scope.childScope("Working directory tracking"))
+ // to be replaced with another way of getting EelDescriptor
+ val eelDescriptor = result.ttyConnector.connector.asSafely()?.eelDescriptor
+ trackWorkingDirectory(updatedTab, eelDescriptor, scope.childScope("Working directory tracking"))
scope.awaitCancellationAndInvoke {
updateTabsAndStore { tabs ->
@@ -143,7 +153,7 @@ internal class TerminalTabsManager(private val project: Project, private val cor
* So, the working directory is persisted in the [TerminalTabsStorage]
* and can be used to start the new session on the next IDE launch.
*/
- private fun trackWorkingDirectory(tab: TerminalSessionTab, coroutineScope: CoroutineScope) {
+ private fun trackWorkingDirectory(tab: TerminalSessionTab, eelDescriptor: EelDescriptor?, coroutineScope: CoroutineScope) {
val sessionId = tab.sessionId ?: error("This method should be called only for tabs with started sessions: $tab")
val session = TerminalSessionsManager.getInstance().getSession(sessionId) ?: error("No session for tab $tab")
@@ -153,11 +163,14 @@ internal class TerminalTabsManager(private val project: Project, private val cor
var currentDirectory: String? = tab.workingDirectory
outputFlow.collect { events ->
for (event in events) {
- if (event is TerminalStateChangedEvent && event.state.currentDirectory != currentDirectory) {
- currentDirectory = event.state.currentDirectory
- updateTabsAndStore { tabs ->
- val updatedTab = tabs[tab.id]?.copy(workingDirectory = currentDirectory) ?: return@updateTabsAndStore
- tabs[tab.id] = updatedTab
+ if (event is TerminalStateChangedEvent) {
+ val newCurrentDirectory = convertRemoteDirectoryToHost(event.state.currentDirectory, eelDescriptor)
+ if (newCurrentDirectory != currentDirectory) {
+ currentDirectory = newCurrentDirectory
+ updateTabsAndStore { tabs ->
+ val updatedTab = tabs[tab.id]?.copy(workingDirectory = newCurrentDirectory) ?: return@updateTabsAndStore
+ tabs[tab.id] = updatedTab
+ }
}
}
}
@@ -165,6 +178,27 @@ internal class TerminalTabsManager(private val project: Project, private val cor
}
}
+ private fun convertRemoteDirectoryToHost(remotePath: String, descriptor: EelDescriptor?): String {
+ if (descriptor == null) return remotePath
+ val eelPath: EelPath = try {
+ EelPath.parse(remotePath, descriptor)
+ }
+ catch (e: EelPathException) {
+ LOG.debug(e) {
+ "Failed to convert remote path ($remotePath) to host: descriptor=$descriptor"
+ }
+ return remotePath
+ }
+ return try {
+ eelPath.asNioPath().pathString
+ }
+ catch (e: IllegalArgumentException) {
+ LOG.info("Failed to convert remote path ($remotePath) to host: " +
+ "EelPath($eelPath) -> Nio conversion failed, descriptor=$descriptor", e)
+ remotePath
+ }
+ }
+
private suspend fun updateTabsAndStore(action: suspend (MutableMap) -> T): T {
return tabsLock.withLock {
try {
diff --git a/plugins/terminal/src/org/jetbrains/plugins/terminal/LocalTerminalTtyConnector.java b/plugins/terminal/src/org/jetbrains/plugins/terminal/LocalTerminalTtyConnector.java
index d2832f46ef8d..13ad11ca1e58 100644
--- a/plugins/terminal/src/org/jetbrains/plugins/terminal/LocalTerminalTtyConnector.java
+++ b/plugins/terminal/src/org/jetbrains/plugins/terminal/LocalTerminalTtyConnector.java
@@ -4,6 +4,7 @@ package org.jetbrains.plugins.terminal;
import com.google.common.base.Ascii;
import com.intellij.execution.ijent.IjentChildPtyProcessAdapter;
import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.platform.eel.EelDescriptor;
import com.intellij.terminal.pty.PtyProcessTtyConnector;
import com.intellij.util.concurrency.AppExecutorUtil;
import com.jediterm.core.util.TermSize;
@@ -30,6 +31,11 @@ public class LocalTerminalTtyConnector extends PtyProcessTtyConnector {
myShellProcessHolder = shellProcessHolder;
}
+ @ApiStatus.Internal
+ public @NotNull EelDescriptor getEelDescriptor() {
+ return myShellProcessHolder.getDescriptor();
+ }
+
@Override
public void close() {
if (myProcess instanceof UnixPtyProcess) {
diff --git a/plugins/terminal/src/org/jetbrains/plugins/terminal/terminalStartup.kt b/plugins/terminal/src/org/jetbrains/plugins/terminal/terminalStartup.kt
index a6fc63ea197a..044e64a86d93 100644
--- a/plugins/terminal/src/org/jetbrains/plugins/terminal/terminalStartup.kt
+++ b/plugins/terminal/src/org/jetbrains/plugins/terminal/terminalStartup.kt
@@ -236,6 +236,8 @@ internal class ShellProcessHolder(
val ptyProcess: PtyProcess = eelProcess.convertToJavaProcess() as PtyProcess
private val shellPid: EelApi.Pid = eelProcess.pid
+ val descriptor: EelDescriptor get() = eelApi.descriptor
+
fun terminatePosixShell() {
terminalApplicationScope().launch(Dispatchers.IO) {
if (!ptyProcess.isAlive) {