mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[terminal] IJPL-214284 store host working directory per terminal tab, not remote path
For example, if a terminal is running in WSL, store `\\wsl.localhost\Ubuntu\path\to\project` (host path) as a terminal working directory, not `/path/to/project` (remote path). Similarly, with DevContainers: store `/docker-87f454dd49a9/IdeaProjects/CursorVueDevContainer`, not `/IdeaProjects/CursorVueDevContainer` This will allow detecting the terminal environment correctly when restoring the terminal tab on project open. (cherry picked from commit a964ce9e22dc48085016a7ea371cf39aaf9637b5) IJ-CR-180815 GitOrigin-RevId: 051a56eab8cc90dc9e7ec251dc044aa145a8b624
This commit is contained in:
committed by
intellij-monorepo-bot
parent
6be296bc1c
commit
0381e3f989
@@ -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
|
||||
@@ -50,5 +50,7 @@
|
||||
<orderEntry type="module" module-name="intellij.platform.statistics" />
|
||||
<orderEntry type="module" module-name="intellij.platform.core.ui" />
|
||||
<orderEntry type="module" module-name="intellij.platform.lang" />
|
||||
<orderEntry type="module" module-name="intellij.platform.eel" />
|
||||
<orderEntry type="module" module-name="intellij.platform.eel.provider" />
|
||||
</component>
|
||||
</module>
|
||||
@@ -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<LocalTerminalTtyConnector>()?.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 <T> updateTabsAndStore(action: suspend (MutableMap<Int, TerminalSessionTab>) -> T): T {
|
||||
return tabsLock.withLock {
|
||||
try {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user