mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-01-05 01:50:56 +07:00
Module EEL: API for local and non-local processes.
See `com.intellij.platform.eel` package info GitOrigin-RevId: 11d0a8b0d9f75274bf604518dbe09e2ac85def20
This commit is contained in:
committed by
intellij-monorepo-bot
parent
71f50958a2
commit
bc2a7e6a45
@@ -9,5 +9,7 @@
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="library" name="kotlin-stdlib" level="project" />
|
||||
<orderEntry type="library" name="jetbrains-annotations" level="project" />
|
||||
<orderEntry type="library" name="kotlinx-coroutines-core" level="project" />
|
||||
<orderEntry type="module" module-name="intellij.platform.util.base" />
|
||||
</component>
|
||||
</module>
|
||||
@@ -1,57 +1,23 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.platform.ijent
|
||||
package com.intellij.platform.eel
|
||||
|
||||
import com.intellij.platform.ijent.fs.IjentFileSystemApi
|
||||
import com.intellij.platform.ijent.fs.IjentFileSystemPosixApi
|
||||
import com.intellij.platform.ijent.fs.IjentFileSystemWindowsApi
|
||||
|
||||
/**
|
||||
* Provides access to an IJent process running on some machine. An instance of this interface gives ability to run commands
|
||||
* on a local or a remote machine. Every instance corresponds to a single machine, i.e. unlike Run Targets, if IJent is launched
|
||||
* in a Docker container, every call to execute a process (see [IjentExecApi]) runs a command in the same Docker container.
|
||||
* Provides access to a local machine or an IJent process running on some machine. An instance of this interface gives ability to run commands
|
||||
* on a local or a remote machine.
|
||||
* in a Docker container, every call to execute a process (see [EelExecApi]) runs a command in the same Docker container.
|
||||
*
|
||||
* Usually, [com.intellij.platform.ijent.deploy] creates instances of [IjentApi].
|
||||
*/
|
||||
sealed interface IjentApi : AutoCloseable {
|
||||
val platform: IjentPlatform
|
||||
interface EelApi {
|
||||
val platform: EelPlatform
|
||||
|
||||
/**
|
||||
* Checks if the API is active and is safe to use. If it returns false, IJent on the other side is certainly unavailable.
|
||||
* If it returns true, it's likely available.
|
||||
*
|
||||
* The property must return true as soon as [close] is called.
|
||||
*
|
||||
* The property must not perform any blocking operation and must work fast.
|
||||
*/
|
||||
val isRunning: Boolean
|
||||
|
||||
/**
|
||||
* Returns basic info about the process that doesn't change during the lifetime of the process.
|
||||
*/
|
||||
val info: IjentInfo
|
||||
/** Docs: [EelExecApi] */
|
||||
val exec: EelExecApi
|
||||
|
||||
/**
|
||||
* Explicitly terminates the process on the remote machine.
|
||||
*
|
||||
* The method is not supposed to block the current thread.
|
||||
*
|
||||
* For awaiting, use [waitUntilExit].
|
||||
*/
|
||||
override fun close()
|
||||
|
||||
/**
|
||||
* Suspends until the IJent process on the remote side terminates.
|
||||
* This method doesn't throw exceptions.
|
||||
*/
|
||||
suspend fun waitUntilExit()
|
||||
|
||||
/** Docs: [IjentExecApi] */
|
||||
val exec: IjentExecApi
|
||||
|
||||
val fs: IjentFileSystemApi
|
||||
|
||||
/** Docs: [IjentTunnelsApi] */
|
||||
val tunnels: IjentTunnelsApi
|
||||
/** Docs: [EelTunnelsApi] */
|
||||
val tunnels: EelTunnelsApi
|
||||
|
||||
/**
|
||||
* On Unix-like OS, PID is int32. On Windows, PID is uint32. The type of Long covers both PID types, and a separate class doesn't allow
|
||||
@@ -62,14 +28,10 @@ sealed interface IjentApi : AutoCloseable {
|
||||
}
|
||||
}
|
||||
|
||||
interface IjentPosixApi : IjentApi {
|
||||
override val info: IjentPosixInfo
|
||||
override val fs: IjentFileSystemPosixApi
|
||||
override val tunnels: IjentTunnelsPosixApi
|
||||
interface EelPosixApi : EelApi {
|
||||
override val tunnels: EelTunnelsPosixApi
|
||||
}
|
||||
|
||||
interface IjentWindowsApi : IjentApi {
|
||||
override val info: IjentWindowsInfo
|
||||
override val fs: IjentFileSystemWindowsApi
|
||||
override val tunnels: IjentTunnelsWindowsApi
|
||||
interface EelWindowsApi : EelApi {
|
||||
override val tunnels: EelTunnelsWindowsApi
|
||||
}
|
||||
@@ -1,19 +1,19 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.platform.ijent
|
||||
package com.intellij.platform.eel
|
||||
|
||||
/**
|
||||
* Methods related to process execution: start a process, collect stdin/stdout/stderr of the process, etc.
|
||||
*/
|
||||
interface IjentExecApi {
|
||||
interface EelExecApi {
|
||||
// TODO Extract into a separate interface, like IjentFileSystemApi.Arguments
|
||||
/**
|
||||
* Starts a process on a remote machine. Right now, the child process may outlive the instance of IJent.
|
||||
* Starts a process on a local or remote machine.
|
||||
* stdin, stdout and stderr of the process are always forwarded, if there are.
|
||||
*
|
||||
* Beware that processes with [ExecuteProcessBuilder.pty] usually don't have stderr.
|
||||
* The [IjentChildProcess.stderr] must be an empty stream in such case.
|
||||
* The [EelProcess.stderr] must be an empty stream in such case.
|
||||
*
|
||||
* By default, environment is always inherited from the running IJent instance, which may be unwanted. [ExecuteProcessBuilder.env] allows
|
||||
* By default, environment is always inherited, which may be unwanted. [ExecuteProcessBuilder.env] allows
|
||||
* to alter some environment variables, it doesn't clear the variables from the parent. When the process should be started in an
|
||||
* environment like in a terminal, the response of [fetchLoginShellEnvVariables] should be put into [ExecuteProcessBuilder.env].
|
||||
*
|
||||
@@ -30,7 +30,7 @@ interface IjentExecApi {
|
||||
fun workingDirectory(workingDirectory: String?): ExecuteProcessBuilder
|
||||
|
||||
/**
|
||||
* Executes the process, returning either an [IjentChildProcess] or an error provided by the remote operating system.
|
||||
* Executes the process, returning either an [EelProcess] or an error provided by the remote operating system.
|
||||
*
|
||||
* The instance of the [ExecuteProcessBuilder] _may_ become invalid after this call.
|
||||
*
|
||||
@@ -42,11 +42,10 @@ interface IjentExecApi {
|
||||
/**
|
||||
* Gets the same environment variables on the remote machine as the user would get if they run the shell.
|
||||
*/
|
||||
@Throws(IjentUnavailableException::class)
|
||||
suspend fun fetchLoginShellEnvVariables(): Map<String, String>
|
||||
|
||||
sealed interface ExecuteProcessResult {
|
||||
class Success(val process: IjentChildProcess) : ExecuteProcessResult
|
||||
class Success(val process: EelProcess) : ExecuteProcessResult
|
||||
data class Failure(val errno: Int, val message: String) : ExecuteProcessResult
|
||||
}
|
||||
|
||||
@@ -54,14 +53,13 @@ interface IjentExecApi {
|
||||
data class Pty(val columns: Int, val rows: Int, val echo: Boolean)
|
||||
}
|
||||
|
||||
/** Docs: [IjentExecApi.executeProcessBuilder] */
|
||||
@Throws(IjentUnavailableException::class)
|
||||
suspend fun IjentExecApi.executeProcess(exe: String, vararg args: String): IjentExecApi.ExecuteProcessResult =
|
||||
/** Docs: [EelExecApi.executeProcessBuilder] */
|
||||
suspend fun EelExecApi.executeProcess(exe: String, vararg args: String): EelExecApi.ExecuteProcessResult =
|
||||
executeProcessBuilder(exe).args(listOf(*args)).execute()
|
||||
|
||||
/** Docs: [IjentExecApi.executeProcessBuilder] */
|
||||
fun IjentExecApi.executeProcessBuilder(exe: String, arg1: String, vararg args: String): IjentExecApi.ExecuteProcessBuilder =
|
||||
/** Docs: [EelExecApi.executeProcessBuilder] */
|
||||
fun EelExecApi.executeProcessBuilder(exe: String, arg1: String, vararg args: String): EelExecApi.ExecuteProcessBuilder =
|
||||
executeProcessBuilder(exe).args(listOf(arg1, *args))
|
||||
|
||||
fun IjentExecApi.ExecuteProcessBuilder.args(first: String, vararg other: String): IjentExecApi.ExecuteProcessBuilder =
|
||||
fun EelExecApi.ExecuteProcessBuilder.args(first: String, vararg other: String): EelExecApi.ExecuteProcessBuilder =
|
||||
args(listOf(first, *other))
|
||||
@@ -1,11 +1,11 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.platform.ijent
|
||||
package com.intellij.platform.eel
|
||||
|
||||
sealed interface IjentPlatform {
|
||||
sealed interface Posix : IjentPlatform
|
||||
sealed interface EelPlatform {
|
||||
sealed interface Posix : EelPlatform
|
||||
sealed interface Linux : Posix
|
||||
sealed interface Darwin : Posix
|
||||
sealed interface Windows : IjentPlatform
|
||||
sealed interface Windows : EelPlatform
|
||||
|
||||
data object Arm64Darwin : Darwin
|
||||
data object Aarch64Linux : Linux
|
||||
@@ -14,7 +14,7 @@ sealed interface IjentPlatform {
|
||||
data object X64Windows : Windows
|
||||
|
||||
companion object {
|
||||
fun getFor(os: String, arch: String): IjentPlatform? =
|
||||
fun getFor(os: String, arch: String): EelPlatform? =
|
||||
when (os.lowercase()) {
|
||||
"darwin" -> when (arch.lowercase()) {
|
||||
"arm64", "aarch64" -> Arm64Darwin
|
||||
@@ -34,12 +34,3 @@ sealed interface IjentPlatform {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val IjentPlatform.executableName: String
|
||||
get() = when (this) {
|
||||
IjentPlatform.Arm64Darwin -> "ijent-aarch64-apple-darwin-release"
|
||||
IjentPlatform.X8664Darwin -> "ijent-x86_64-apple-darwin-release"
|
||||
IjentPlatform.Aarch64Linux -> "ijent-aarch64-unknown-linux-musl-release"
|
||||
IjentPlatform.X8664Linux -> "ijent-x86_64-unknown-linux-musl-release"
|
||||
IjentPlatform.X64Windows -> "ijent-x86_64-pc-windows-gnu-release.exe"
|
||||
}
|
||||
@@ -1,18 +1,16 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.platform.ijent
|
||||
package com.intellij.platform.eel
|
||||
|
||||
import kotlinx.coroutines.Deferred
|
||||
import kotlinx.coroutines.channels.ReceiveChannel
|
||||
import kotlinx.coroutines.channels.SendChannel
|
||||
|
||||
/**
|
||||
* Represents some process which was launched by IJent via [IjentApi.executeProcess].
|
||||
* Represents some process which was launched via [EelExecApi.executeProcess].
|
||||
*
|
||||
* There are adapters for already written code: [com.intellij.execution.ijent.IjentChildProcessAdapter]
|
||||
* and [com.intellij.execution.ijent.IjentChildPtyProcessAdapter].
|
||||
*/
|
||||
interface IjentChildProcess {
|
||||
val pid: IjentApi.Pid
|
||||
interface EelProcess {
|
||||
val pid: EelApi.Pid
|
||||
|
||||
/**
|
||||
* Although data transmission via this channel could potentially stall due to overflow of [kotlinx.coroutines.channels.Channel],
|
||||
@@ -32,7 +30,7 @@ interface IjentChildProcess {
|
||||
*
|
||||
* Notice that every data chunk is flushed into the process separately. There's no buffering.
|
||||
*/
|
||||
@Throws(SendStdinError::class, IjentUnavailableException::class)
|
||||
@Throws(SendStdinError::class)
|
||||
suspend fun sendStdinWithConfirmation(data: ByteArray)
|
||||
|
||||
sealed class SendStdinError(msg: String) : Exception(msg) {
|
||||
@@ -51,7 +49,6 @@ interface IjentChildProcess {
|
||||
*
|
||||
* Does nothing yet on Windows.
|
||||
*/
|
||||
@Throws(IjentUnavailableException::class)
|
||||
suspend fun interrupt()
|
||||
|
||||
/**
|
||||
@@ -59,7 +56,6 @@ interface IjentChildProcess {
|
||||
*
|
||||
* Calls [`ExitProcess`](https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-exitprocess) on Windows.
|
||||
*/
|
||||
@Throws(IjentUnavailableException::class)
|
||||
suspend fun terminate()
|
||||
|
||||
/**
|
||||
@@ -68,10 +64,9 @@ interface IjentChildProcess {
|
||||
* Calls [`TerminateProcess`](https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-terminateprocess)
|
||||
* on Windows.
|
||||
*/
|
||||
@Throws(IjentUnavailableException::class)
|
||||
suspend fun kill()
|
||||
|
||||
@Throws(ResizePtyError::class, IjentUnavailableException::class) // Can't use @CheckReturnValue: KTIJ-7061
|
||||
@Throws(ResizePtyError::class) // Can't use @CheckReturnValue: KTIJ-7061
|
||||
suspend fun resizePty(columns: Int, rows: Int)
|
||||
|
||||
sealed class ResizePtyError(msg: String) : Exception(msg) {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.platform.ijent
|
||||
package com.intellij.platform.eel
|
||||
|
||||
import com.intellij.openapi.util.NlsSafe
|
||||
import com.intellij.platform.ijent.IjentNetworkResult.Ok
|
||||
import com.intellij.platform.ijent.IjentTunnelsApi.Connection
|
||||
import com.intellij.platform.eel.EelNetworkResult.Ok
|
||||
import com.intellij.platform.eel.EelTunnelsApi.Connection
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.channels.ReceiveChannel
|
||||
import kotlinx.coroutines.channels.SendChannel
|
||||
@@ -15,15 +15,15 @@ import kotlin.time.Duration
|
||||
/**
|
||||
* Methods for launching tunnels for TCP sockets, Unix sockets, etc.
|
||||
*/
|
||||
sealed interface IjentTunnelsApi {
|
||||
interface EelTunnelsApi {
|
||||
|
||||
/**
|
||||
* **This is a delicate API, for applied usages, please consider [withConnectionToRemotePort]**.
|
||||
*
|
||||
* Creates a connection to a TCP socket to a named host specified by [address].
|
||||
*
|
||||
* If the result is [IjentNetworkResult.Error], then there was an error during establishment of the connection.
|
||||
* Otherwise, the result is [IjentNetworkResult.Ok], which means that the connection is ready to use.
|
||||
* If the result is [EelNetworkResult.Error], then there was an error during establishment of the connection.
|
||||
* Otherwise, the result is [EelNetworkResult.Ok], which means that the connection is ready to use.
|
||||
*
|
||||
* The connection exists as a pair of channels [Connection.channelToServer] and [Connection.channelFromServer],
|
||||
* which allow communicating to a remote server from the IDE side.
|
||||
@@ -40,8 +40,7 @@ sealed interface IjentTunnelsApi {
|
||||
*
|
||||
* One should not forget to invoke [Connection.close] when the connection is not needed.
|
||||
*/
|
||||
@Throws(IjentUnavailableException::class)
|
||||
suspend fun getConnectionToRemotePort(address: HostAddress): IjentNetworkResult<Connection, IjentConnectionError>
|
||||
suspend fun getConnectionToRemotePort(address: HostAddress): EelNetworkResult<Connection, EelConnectionError>
|
||||
|
||||
/**
|
||||
* Creates a builder for address on the remote host.
|
||||
@@ -87,8 +86,8 @@ sealed interface IjentTunnelsApi {
|
||||
|
||||
/**
|
||||
* Sets timeout for connecting to remote host.
|
||||
* If the connection could not be established before [timeout], then [IjentConnectionError.ConnectionTimeout] would be returned
|
||||
* in [IjentTunnelsApi.getConnectionToRemotePort].
|
||||
* If the connection could not be established before [timeout], then [EelConnectionError.ConnectionTimeout] would be returned
|
||||
* in [EelTunnelsApi.getConnectionToRemotePort].
|
||||
*
|
||||
* Default value: 10 seconds.
|
||||
* The recognizable granularity is milliseconds.
|
||||
@@ -121,48 +120,41 @@ sealed interface IjentTunnelsApi {
|
||||
* Sets the size of send buffer of the socket
|
||||
* @see java.net.SocketOptions.SO_SNDBUF
|
||||
*/
|
||||
@Throws(IjentUnavailableException::class)
|
||||
suspend fun setSendBufferSize(size: UInt)
|
||||
|
||||
/**
|
||||
* Sets the receive buffer size of the socket
|
||||
* @see java.net.SocketOptions.SO_RCVBUF
|
||||
*/
|
||||
@Throws(IjentUnavailableException::class)
|
||||
suspend fun setReceiveBufferSize(size: UInt)
|
||||
|
||||
/**
|
||||
* Sets the keep alive option for the socket
|
||||
* @see java.net.SocketOptions.SO_KEEPALIVE
|
||||
*/
|
||||
@Throws(IjentUnavailableException::class)
|
||||
suspend fun setKeepAlive(keepAlive: Boolean)
|
||||
|
||||
/**
|
||||
* Sets the possibility to reuse address of the socket
|
||||
* @see java.net.SocketOptions.SO_REUSEADDR
|
||||
*/
|
||||
@Throws(IjentUnavailableException::class)
|
||||
suspend fun setReuseAddr(reuseAddr: Boolean)
|
||||
|
||||
/**
|
||||
* Sets linger timeout for the socket
|
||||
* @see java.net.SocketOptions.SO_LINGER
|
||||
*/
|
||||
@Throws(IjentUnavailableException::class)
|
||||
suspend fun setLinger(lingerInterval: Duration)
|
||||
|
||||
/**
|
||||
* Disables pending data until acknowledgement
|
||||
* @see java.net.SocketOptions.TCP_NODELAY
|
||||
*/
|
||||
@Throws(IjentUnavailableException::class)
|
||||
suspend fun setNoDelay(noDelay: Boolean)
|
||||
|
||||
/**
|
||||
* Closes the connection to the socket.
|
||||
*/
|
||||
@Throws(IjentUnavailableException::class)
|
||||
suspend fun close()
|
||||
}
|
||||
|
||||
@@ -187,7 +179,7 @@ operator fun Connection.component1(): SendChannel<ByteBuffer> = channelToServer
|
||||
*/
|
||||
operator fun Connection.component2(): ReceiveChannel<ByteBuffer> = channelFromServer
|
||||
|
||||
interface IjentTunnelsPosixApi : IjentTunnelsApi {
|
||||
interface EelTunnelsPosixApi : EelTunnelsApi {
|
||||
/**
|
||||
* Creates a remote UNIX socket forwarding. IJent listens for a connection on the remote machine, and when the connection
|
||||
* is accepted, the IDE communicates to the remote client via a pair of Kotlin channels.
|
||||
@@ -212,7 +204,6 @@ interface IjentTunnelsPosixApi : IjentTunnelsApi {
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
@Throws(IjentUnavailableException::class)
|
||||
suspend fun listenOnUnixSocket(path: CreateFilePath = CreateFilePath.MkTemp()): ListenOnUnixSocketResult
|
||||
|
||||
data class ListenOnUnixSocketResult(
|
||||
@@ -229,7 +220,7 @@ interface IjentTunnelsPosixApi : IjentTunnelsApi {
|
||||
}
|
||||
}
|
||||
|
||||
interface IjentTunnelsWindowsApi : IjentTunnelsApi
|
||||
interface EelTunnelsWindowsApi : EelTunnelsApi
|
||||
|
||||
/**
|
||||
* Convenience function for working with a connection to a remote server.
|
||||
@@ -238,7 +229,7 @@ interface IjentTunnelsWindowsApi : IjentTunnelsApi
|
||||
* ```kotlin
|
||||
*
|
||||
* suspend fun foo() {
|
||||
* ijentTunnelsApi.withConnectionToRemotePort("localhost", 8080, {
|
||||
* EelTunnelsApi.withConnectionToRemotePort("localhost", 8080, {
|
||||
* myErrorReporter.report(it)
|
||||
* }) { (channelTo, channelFrom) ->
|
||||
* handleConnection(channelTo, channelFrom)
|
||||
@@ -250,16 +241,15 @@ interface IjentTunnelsWindowsApi : IjentTunnelsApi
|
||||
* If the connection could not be established, then [errorHandler] is invoked.
|
||||
* Otherwise, [action] is invoked. The connection gets automatically closed when [action] finishes.
|
||||
*
|
||||
* @see com.intellij.platform.ijent.IjentTunnelsApi.getConnectionToRemotePort for more details on the behavior of [Connection]
|
||||
* @see EelTunnelsApi.getConnectionToRemotePort for more details on the behavior of [Connection]
|
||||
*/
|
||||
@Throws(IjentUnavailableException::class)
|
||||
suspend fun <T> IjentTunnelsApi.withConnectionToRemotePort(
|
||||
hostAddress: IjentTunnelsApi.HostAddress,
|
||||
errorHandler: suspend (IjentConnectionError) -> T,
|
||||
suspend fun <T> EelTunnelsApi.withConnectionToRemotePort(
|
||||
hostAddress: EelTunnelsApi.HostAddress,
|
||||
errorHandler: suspend (EelConnectionError) -> T,
|
||||
action: suspend CoroutineScope.(Connection) -> T,
|
||||
): T =
|
||||
when (val connectionResult = getConnectionToRemotePort(hostAddress)) {
|
||||
is IjentNetworkResult.Error -> errorHandler(connectionResult.error)
|
||||
is EelNetworkResult.Error -> errorHandler(connectionResult.error)
|
||||
is Ok -> try {
|
||||
coroutineScope { action(connectionResult.value) }
|
||||
}
|
||||
@@ -268,40 +258,38 @@ suspend fun <T> IjentTunnelsApi.withConnectionToRemotePort(
|
||||
}
|
||||
}
|
||||
|
||||
@Throws(IjentUnavailableException::class)
|
||||
suspend fun <T> IjentTunnelsApi.withConnectionToRemotePort(
|
||||
suspend fun <T> EelTunnelsApi.withConnectionToRemotePort(
|
||||
host: String, port: UShort,
|
||||
errorHandler: suspend (IjentConnectionError) -> T,
|
||||
errorHandler: suspend (EelConnectionError) -> T,
|
||||
action: suspend CoroutineScope.(Connection) -> T,
|
||||
): T = withConnectionToRemotePort(hostAddressBuilder(port).hostname(host).build(), errorHandler, action)
|
||||
|
||||
@Throws(IjentUnavailableException::class)
|
||||
suspend fun <T> IjentTunnelsApi.withConnectionToRemotePort(
|
||||
suspend fun <T> EelTunnelsApi.withConnectionToRemotePort(
|
||||
remotePort: UShort,
|
||||
errorHandler: suspend (IjentConnectionError) -> T,
|
||||
errorHandler: suspend (EelConnectionError) -> T,
|
||||
action: suspend CoroutineScope.(Connection) -> T,
|
||||
): T = withConnectionToRemotePort("localhost", remotePort, errorHandler, action)
|
||||
|
||||
/**
|
||||
* Represents a common class for all network-related errors appearing during the interaction with IJent
|
||||
* Represents a common class for all network-related errors appearing during the interaction with IJent or local process
|
||||
*/
|
||||
sealed interface IjentNetworkError
|
||||
sealed interface EelNetworkError
|
||||
|
||||
/**
|
||||
* Represents a result of a network operation
|
||||
*/
|
||||
sealed interface IjentNetworkResult<out T, out E : IjentNetworkError> {
|
||||
sealed interface EelNetworkResult<out T, out E : EelNetworkError> {
|
||||
/**
|
||||
* Used when a network operation completed successfully
|
||||
*/
|
||||
interface Ok<out T> : IjentNetworkResult<T, Nothing> {
|
||||
interface Ok<out T> : EelNetworkResult<T, Nothing> {
|
||||
val value: T
|
||||
}
|
||||
|
||||
/**
|
||||
* Used when a network operation completed with an error
|
||||
*/
|
||||
interface Error<out E : IjentNetworkError> : IjentNetworkResult<Nothing, E> {
|
||||
interface Error<out E : EelNetworkError> : EelNetworkResult<Nothing, E> {
|
||||
val error: E
|
||||
}
|
||||
}
|
||||
@@ -309,17 +297,17 @@ sealed interface IjentNetworkResult<out T, out E : IjentNetworkError> {
|
||||
/**
|
||||
* An error that can happen during the creation of a connection to a remote server
|
||||
*/
|
||||
interface IjentConnectionError : IjentNetworkError {
|
||||
interface EelConnectionError : EelNetworkError {
|
||||
val message: @NlsSafe String
|
||||
|
||||
data object ConnectionTimeout : IjentConnectionError {
|
||||
data object ConnectionTimeout : EelConnectionError {
|
||||
override val message: @NlsSafe String = "Connection could not be established because of timeout"
|
||||
}
|
||||
|
||||
/**
|
||||
* Returned when a hostname on the remote server was resolved to multiple different addresses.
|
||||
*/
|
||||
data object AmbiguousAddress : IjentConnectionError {
|
||||
data object AmbiguousAddress : EelConnectionError {
|
||||
override val message: String = "Hostname could not be resolved uniquely"
|
||||
}
|
||||
|
||||
@@ -327,19 +315,19 @@ interface IjentConnectionError : IjentNetworkError {
|
||||
* Returned when a socket could not be created because of an OS error.
|
||||
*/
|
||||
@JvmInline
|
||||
value class SocketCreationFailure(override val message: @NlsSafe String) : IjentConnectionError
|
||||
value class SocketCreationFailure(override val message: @NlsSafe String) : EelConnectionError
|
||||
|
||||
/**
|
||||
* Returned when resolve of remote address failed during the creation of a socket.
|
||||
*/
|
||||
object HostUnreachable : IjentConnectionError {
|
||||
object HostUnreachable : EelConnectionError {
|
||||
override val message: @NlsSafe String = "Remote host is unreachable"
|
||||
}
|
||||
|
||||
/**
|
||||
* Returned when the remote server does not accept connections.
|
||||
*/
|
||||
object ConnectionRefused : IjentConnectionError {
|
||||
object ConnectionRefused : EelConnectionError {
|
||||
override val message: @NlsSafe String = "Connection was refused by remote server"
|
||||
}
|
||||
|
||||
@@ -347,13 +335,13 @@ interface IjentConnectionError : IjentNetworkError {
|
||||
* Returned when hostname could not be resolved.
|
||||
*/
|
||||
@JvmInline
|
||||
value class ResolveFailure(override val message: @NlsSafe String) : IjentConnectionError
|
||||
value class ResolveFailure(override val message: @NlsSafe String) : EelConnectionError
|
||||
|
||||
/**
|
||||
* Unknown failure during a connection establishment
|
||||
*/
|
||||
@JvmInline
|
||||
value class UnknownFailure(override val message: @NlsSafe String) : IjentConnectionError
|
||||
value class UnknownFailure(override val message: @NlsSafe String) : EelConnectionError
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
/**
|
||||
* EEL API provides the same API for local and non-local (ssh/wsl/etc) system
|
||||
*/
|
||||
@ApiStatus.Internal
|
||||
package com.intellij.platform.eel;
|
||||
|
||||
|
||||
@@ -13,6 +13,8 @@ import com.intellij.openapi.diagnostic.logger
|
||||
import com.intellij.openapi.diagnostic.runAndLogException
|
||||
import com.intellij.openapi.project.DumbAwareAction
|
||||
import com.intellij.openapi.ui.Messages
|
||||
import com.intellij.platform.eel.EelExecApi
|
||||
import com.intellij.platform.eel.executeProcess
|
||||
import com.intellij.platform.ide.progress.ModalTaskOwner
|
||||
import com.intellij.platform.ide.progress.TaskCancellation
|
||||
import com.intellij.platform.ide.progress.withModalProgress
|
||||
@@ -20,7 +22,6 @@ import com.intellij.platform.ijent.IjentExecApi
|
||||
import com.intellij.platform.ijent.IjentMissingBinary
|
||||
import com.intellij.platform.ijent.community.impl.nio.IjentNioFileSystemProvider
|
||||
import com.intellij.platform.ijent.deploy
|
||||
import com.intellij.platform.ijent.executeProcess
|
||||
import com.intellij.platform.ijent.spi.IjentDeployingStrategy
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.channels.consumeEach
|
||||
@@ -76,8 +77,8 @@ abstract class AbstractIjentVerificationAction : DumbAwareAction() {
|
||||
|
||||
launch {
|
||||
val process = when (val p = ijent.exec.executeProcess("uname", "-a")) {
|
||||
is IjentExecApi.ExecuteProcessResult.Failure -> error(p)
|
||||
is IjentExecApi.ExecuteProcessResult.Success -> p.process
|
||||
is EelExecApi.ExecuteProcessResult.Failure -> error(p)
|
||||
is EelExecApi.ExecuteProcessResult.Success -> p.process
|
||||
}
|
||||
val stdout = ByteArrayOutputStream()
|
||||
process.stdout.consumeEach(stdout::write)
|
||||
|
||||
@@ -21,5 +21,6 @@
|
||||
<orderEntry type="module" module-name="intellij.platform.core.nio.fs" />
|
||||
<orderEntry type="module" module-name="intellij.platform.diagnostic.telemetry" />
|
||||
<orderEntry type="module" module-name="intellij.platform.ijent" />
|
||||
<orderEntry type="module" module-name="intellij.platform.eel" />
|
||||
</component>
|
||||
</module>
|
||||
@@ -1,20 +1,20 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.platform.ijent.community.impl
|
||||
|
||||
import com.intellij.platform.ijent.IjentApi
|
||||
import com.intellij.platform.eel.EelApi
|
||||
import com.intellij.platform.ijent.IjentPosixInfo
|
||||
import com.intellij.platform.ijent.IjentWindowsInfo
|
||||
|
||||
data class IjentPosixInfoImpl(
|
||||
override val architecture: String,
|
||||
override val remotePid: IjentApi.Pid,
|
||||
override val remotePid: EelApi.Pid,
|
||||
override val version: String,
|
||||
override val user: IjentPosixInfo.User,
|
||||
) : IjentPosixInfo
|
||||
|
||||
data class IjentWindowsInfoImpl(
|
||||
override val architecture: String,
|
||||
override val remotePid: IjentApi.Pid,
|
||||
override val remotePid: EelApi.Pid,
|
||||
override val version: String,
|
||||
override val user: IjentWindowsInfo.User,
|
||||
) : IjentWindowsInfo
|
||||
|
||||
@@ -14,5 +14,6 @@
|
||||
<orderEntry type="library" scope="TEST" name="kotlin-test-assertions-core-jvm" level="project" />
|
||||
<orderEntry type="module" module-name="intellij.platform.core" />
|
||||
<orderEntry type="module" module-name="intellij.platform.util.coroutines" />
|
||||
<orderEntry type="module" module-name="intellij.platform.eel" exported="" />
|
||||
</component>
|
||||
</module>
|
||||
@@ -1,45 +1,13 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.platform.ijent
|
||||
|
||||
sealed interface IjentPlatform {
|
||||
sealed interface Posix : IjentPlatform
|
||||
sealed interface Linux : Posix
|
||||
sealed interface Darwin : Posix
|
||||
sealed interface Windows : IjentPlatform
|
||||
import com.intellij.platform.eel.EelPlatform
|
||||
|
||||
data object Arm64Darwin : Darwin
|
||||
data object Aarch64Linux : Linux
|
||||
data object X8664Darwin : Darwin
|
||||
data object X8664Linux : Linux
|
||||
data object X64Windows : Windows
|
||||
|
||||
companion object {
|
||||
fun getFor(os: String, arch: String): IjentPlatform? =
|
||||
when (os.lowercase()) {
|
||||
"darwin" -> when (arch.lowercase()) {
|
||||
"arm64", "aarch64" -> Arm64Darwin
|
||||
"amd64", "x86_64", "x86-64" -> X8664Darwin
|
||||
else -> null
|
||||
}
|
||||
"linux" -> when (arch.lowercase()) {
|
||||
"arm64", "aarch64" -> Aarch64Linux
|
||||
"amd64", "x86_64", "x86-64" -> X8664Linux
|
||||
else -> null
|
||||
}
|
||||
"windows" -> when (arch.lowercase()) {
|
||||
"amd64", "x86_64", "x86-64" -> X64Windows
|
||||
else -> null
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val IjentPlatform.executableName: String
|
||||
val EelPlatform.executableName: String
|
||||
get() = when (this) {
|
||||
IjentPlatform.Arm64Darwin -> "ijent-aarch64-apple-darwin-release"
|
||||
IjentPlatform.X8664Darwin -> "ijent-x86_64-apple-darwin-release"
|
||||
IjentPlatform.Aarch64Linux -> "ijent-aarch64-unknown-linux-musl-release"
|
||||
IjentPlatform.X8664Linux -> "ijent-x86_64-unknown-linux-musl-release"
|
||||
IjentPlatform.X64Windows -> "ijent-x86_64-pc-windows-gnu-release.exe"
|
||||
EelPlatform.Arm64Darwin -> "ijent-aarch64-apple-darwin-release"
|
||||
EelPlatform.X8664Darwin -> "ijent-x86_64-apple-darwin-release"
|
||||
EelPlatform.Aarch64Linux -> "ijent-aarch64-unknown-linux-musl-release"
|
||||
EelPlatform.X8664Linux -> "ijent-x86_64-unknown-linux-musl-release"
|
||||
EelPlatform.X64Windows -> "ijent-x86_64-pc-windows-gnu-release.exe"
|
||||
}
|
||||
@@ -1,6 +1,10 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.platform.ijent
|
||||
|
||||
import com.intellij.platform.eel.EelPlatform
|
||||
import com.intellij.platform.eel.EelPosixApi
|
||||
import com.intellij.platform.eel.EelTunnelsApi
|
||||
import com.intellij.platform.eel.EelWindowsApi
|
||||
import com.intellij.platform.ijent.fs.IjentFileSystemApi
|
||||
import com.intellij.platform.ijent.fs.IjentFileSystemPosixApi
|
||||
import com.intellij.platform.ijent.fs.IjentFileSystemWindowsApi
|
||||
@@ -8,12 +12,11 @@ import com.intellij.platform.ijent.fs.IjentFileSystemWindowsApi
|
||||
/**
|
||||
* Provides access to an IJent process running on some machine. An instance of this interface gives ability to run commands
|
||||
* on a local or a remote machine. Every instance corresponds to a single machine, i.e. unlike Run Targets, if IJent is launched
|
||||
* in a Docker container, every call to execute a process (see [IjentExecApi]) runs a command in the same Docker container.
|
||||
* in a Docker container, every call to execute a process (see [com.intellij.platform.eel.EelExecApi]) runs a command in the same Docker container.
|
||||
*
|
||||
* Usually, [com.intellij.platform.ijent.deploy] creates instances of [IjentApi].
|
||||
* Usually, [com.intellij.platform.ijent.deploy] creates instances of [com.intellij.platform.eel.IjentApi].
|
||||
*/
|
||||
sealed interface IjentApi : AutoCloseable {
|
||||
val platform: IjentPlatform
|
||||
|
||||
/**
|
||||
* Checks if the API is active and is safe to use. If it returns false, IJent on the other side is certainly unavailable.
|
||||
@@ -45,30 +48,19 @@ sealed interface IjentApi : AutoCloseable {
|
||||
*/
|
||||
suspend fun waitUntilExit()
|
||||
|
||||
/** Docs: [IjentExecApi] */
|
||||
/** Docs: [com.intellij.platform.eel.EelExecApi] */
|
||||
val exec: IjentExecApi
|
||||
|
||||
val fs: IjentFileSystemApi
|
||||
|
||||
/** Docs: [IjentTunnelsApi] */
|
||||
val tunnels: IjentTunnelsApi
|
||||
|
||||
/**
|
||||
* On Unix-like OS, PID is int32. On Windows, PID is uint32. The type of Long covers both PID types, and a separate class doesn't allow
|
||||
* to forget that fact and misuse types in APIs.
|
||||
*/
|
||||
interface Pid {
|
||||
val value: Long
|
||||
}
|
||||
}
|
||||
|
||||
interface IjentPosixApi : IjentApi {
|
||||
interface IjentPosixApi : IjentApi, EelPosixApi {
|
||||
override val info: IjentPosixInfo
|
||||
override val fs: IjentFileSystemPosixApi
|
||||
override val tunnels: IjentTunnelsPosixApi
|
||||
}
|
||||
|
||||
interface IjentWindowsApi : IjentApi {
|
||||
interface IjentWindowsApi : IjentApi, EelWindowsApi {
|
||||
override val info: IjentWindowsInfo
|
||||
override val fs: IjentFileSystemWindowsApi
|
||||
override val tunnels: IjentTunnelsWindowsApi
|
||||
|
||||
@@ -1,82 +1,12 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.platform.ijent
|
||||
|
||||
import kotlinx.coroutines.Deferred
|
||||
import kotlinx.coroutines.channels.ReceiveChannel
|
||||
import kotlinx.coroutines.channels.SendChannel
|
||||
import com.intellij.platform.eel.EelProcess
|
||||
|
||||
/**
|
||||
* Represents some process which was launched by IJent via [IjentApi.executeProcess].
|
||||
* Represents some process which was launched by IJent via [com.intellij.platform.eel.EelApi.executeProcess].
|
||||
*
|
||||
* There are adapters for already written code: [com.intellij.execution.ijent.IjentChildProcessAdapter]
|
||||
* and [com.intellij.execution.ijent.IjentChildPtyProcessAdapter].
|
||||
*/
|
||||
interface IjentChildProcess {
|
||||
val pid: IjentApi.Pid
|
||||
|
||||
/**
|
||||
* Although data transmission via this channel could potentially stall due to overflow of [kotlinx.coroutines.channels.Channel],
|
||||
* this method does not allow to ensure that a data chunk was actually delivered to the remote process.
|
||||
* For synchronous delivery that reports about delivery result, please use [sendStdinWithConfirmation].
|
||||
*
|
||||
* Note that each chunk of data is individually and immediately flushed into the process without any intermediate buffer storage.
|
||||
*/
|
||||
val stdin: SendChannel<ByteArray>
|
||||
|
||||
val stdout: ReceiveChannel<ByteArray>
|
||||
val stderr: ReceiveChannel<ByteArray>
|
||||
val exitCode: Deferred<Int>
|
||||
|
||||
/**
|
||||
* Sends [data] into the process stdin and waits until the data is received by the process.
|
||||
*
|
||||
* Notice that every data chunk is flushed into the process separately. There's no buffering.
|
||||
*/
|
||||
@Throws(SendStdinError::class, IjentUnavailableException::class)
|
||||
suspend fun sendStdinWithConfirmation(data: ByteArray)
|
||||
|
||||
sealed class SendStdinError(msg: String) : Exception(msg) {
|
||||
class ProcessExited : SendStdinError("Process exited")
|
||||
|
||||
/**
|
||||
* This error doesn't imply that the process has already exited. It is possible to close the stdin of the process, and the process
|
||||
* may live quite a long time after that. However, usually processes exit immediately right after their stdin are closed.
|
||||
* Therefore, it may turn out that the process exits at the moment when this error is being observed by the API user.
|
||||
*/
|
||||
class StdinClosed : SendStdinError("Stdin closed")
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends `SIGINT` on Unix.
|
||||
*
|
||||
* Does nothing yet on Windows.
|
||||
*/
|
||||
@Throws(IjentUnavailableException::class)
|
||||
suspend fun interrupt()
|
||||
|
||||
/**
|
||||
* Sends `SIGTERM` on Unix.
|
||||
*
|
||||
* Calls [`ExitProcess`](https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-exitprocess) on Windows.
|
||||
*/
|
||||
@Throws(IjentUnavailableException::class)
|
||||
suspend fun terminate()
|
||||
|
||||
/**
|
||||
* Sends `SIGKILL` on Unix.
|
||||
*
|
||||
* Calls [`TerminateProcess`](https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-terminateprocess)
|
||||
* on Windows.
|
||||
*/
|
||||
@Throws(IjentUnavailableException::class)
|
||||
suspend fun kill()
|
||||
|
||||
@Throws(ResizePtyError::class, IjentUnavailableException::class) // Can't use @CheckReturnValue: KTIJ-7061
|
||||
suspend fun resizePty(columns: Int, rows: Int)
|
||||
|
||||
sealed class ResizePtyError(msg: String) : Exception(msg) {
|
||||
class ProcessExited : ResizePtyError("Process exited")
|
||||
class NoPty : ResizePtyError("Process has no PTY")
|
||||
data class Errno(val errno: Int, override val message: String) : ResizePtyError("[$errno] $message")
|
||||
}
|
||||
}
|
||||
interface IjentChildProcess : EelProcess
|
||||
|
||||
@@ -14,7 +14,7 @@ import kotlinx.coroutines.job
|
||||
* [ijentName] is a label used for logging, debugging, thread names, etc.
|
||||
*
|
||||
* By default, the IJent executable exits when the IDE exits.
|
||||
* [IjentApi.close] and [com.intellij.platform.ijent.bindToScope] may be used to terminate IJent earlier.
|
||||
* [com.intellij.platform.eel.IjentApi.close] and [com.intellij.platform.ijent.bindToScope] may be used to terminate IJent earlier.
|
||||
*
|
||||
* TODO Either define thrown exceptions or return something like Result.
|
||||
*/
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.platform.ijent
|
||||
|
||||
/**
|
||||
* Methods related to process execution: start a process, collect stdin/stdout/stderr of the process, etc.
|
||||
*/
|
||||
interface IjentExecApi {
|
||||
import com.intellij.platform.eel.EelExecApi
|
||||
|
||||
// TODO Extract into a separate interface, like IjentFileSystemApi.Arguments
|
||||
/**
|
||||
* Starts a process on a remote machine. Right now, the child process may outlive the instance of IJent.
|
||||
@@ -20,48 +18,4 @@ interface IjentExecApi {
|
||||
* All argument, all paths, should be valid for the remote machine. F.i., if the IDE runs on Windows, but IJent runs on Linux,
|
||||
* [ExecuteProcessBuilder.workingDirectory] is the path on the Linux host. There's no automatic path mapping in this interface.
|
||||
*/
|
||||
fun executeProcessBuilder(exe: String): ExecuteProcessBuilder
|
||||
|
||||
/** Docs: [executeProcessBuilder] */
|
||||
interface ExecuteProcessBuilder {
|
||||
fun args(args: List<String>): ExecuteProcessBuilder
|
||||
fun env(env: Map<String, String>): ExecuteProcessBuilder
|
||||
fun pty(pty: Pty?): ExecuteProcessBuilder
|
||||
fun workingDirectory(workingDirectory: String?): ExecuteProcessBuilder
|
||||
|
||||
/**
|
||||
* Executes the process, returning either an [IjentChildProcess] or an error provided by the remote operating system.
|
||||
*
|
||||
* The instance of the [ExecuteProcessBuilder] _may_ become invalid after this call.
|
||||
*
|
||||
* The method may throw a RuntimeException only in critical cases like connection loss or a bug.
|
||||
*/
|
||||
suspend fun execute(): ExecuteProcessResult
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the same environment variables on the remote machine as the user would get if they run the shell.
|
||||
*/
|
||||
@Throws(IjentUnavailableException::class)
|
||||
suspend fun fetchLoginShellEnvVariables(): Map<String, String>
|
||||
|
||||
sealed interface ExecuteProcessResult {
|
||||
class Success(val process: IjentChildProcess) : ExecuteProcessResult
|
||||
data class Failure(val errno: Int, val message: String) : ExecuteProcessResult
|
||||
}
|
||||
|
||||
/** [echo] must be true in general and must be false when the user is asked for a password. */
|
||||
data class Pty(val columns: Int, val rows: Int, val echo: Boolean)
|
||||
}
|
||||
|
||||
/** Docs: [IjentExecApi.executeProcessBuilder] */
|
||||
@Throws(IjentUnavailableException::class)
|
||||
suspend fun IjentExecApi.executeProcess(exe: String, vararg args: String): IjentExecApi.ExecuteProcessResult =
|
||||
executeProcessBuilder(exe).args(listOf(*args)).execute()
|
||||
|
||||
/** Docs: [IjentExecApi.executeProcessBuilder] */
|
||||
fun IjentExecApi.executeProcessBuilder(exe: String, arg1: String, vararg args: String): IjentExecApi.ExecuteProcessBuilder =
|
||||
executeProcessBuilder(exe).args(listOf(arg1, *args))
|
||||
|
||||
fun IjentExecApi.ExecuteProcessBuilder.args(first: String, vararg other: String): IjentExecApi.ExecuteProcessBuilder =
|
||||
args(listOf(first, *other))
|
||||
interface IjentExecApi: EelExecApi
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
package com.intellij.platform.ijent
|
||||
|
||||
import com.intellij.openapi.components.serviceAsync
|
||||
import com.intellij.platform.eel.EelPlatform
|
||||
import org.jetbrains.annotations.ApiStatus.Internal
|
||||
import java.nio.file.Path
|
||||
|
||||
@@ -18,14 +19,14 @@ interface IjentExecFileProvider {
|
||||
* Gets the path to the IJent binary. Suggests to install the plugin via dialog windows, so the method may work unpredictably long.
|
||||
*/
|
||||
@Throws(IjentMissingBinary::class)
|
||||
suspend fun getIjentBinary(targetPlatform: IjentPlatform): Path
|
||||
suspend fun getIjentBinary(targetPlatform: EelPlatform): Path
|
||||
}
|
||||
|
||||
class IjentMissingBinary(platform: IjentPlatform) : Exception("Failed to get an IJent binary for $platform") {
|
||||
class IjentMissingBinary(platform: EelPlatform) : Exception("Failed to get an IJent binary for $platform") {
|
||||
override fun getLocalizedMessage(): String = IjentBundle.message("failed.to.get.ijent.binary")
|
||||
}
|
||||
|
||||
internal class DefaultIjentExecFileProvider : IjentExecFileProvider {
|
||||
override suspend fun getIjentBinary(targetPlatform: IjentPlatform): Nothing =
|
||||
override suspend fun getIjentBinary(targetPlatform: EelPlatform): Nothing =
|
||||
throw IjentMissingBinary(targetPlatform)
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.platform.ijent
|
||||
|
||||
import com.intellij.platform.eel.EelApi
|
||||
|
||||
/**
|
||||
* [architecture] is the remote architecture of the built binary. Intended to be used for debugging purposes.
|
||||
* [remotePid] is a process ID of IJent running on the remote machine.
|
||||
@@ -8,7 +10,7 @@ package com.intellij.platform.ijent
|
||||
*/
|
||||
sealed interface IjentInfo {
|
||||
val architecture: String
|
||||
val remotePid: IjentApi.Pid
|
||||
val remotePid: EelApi.Pid
|
||||
val version: String
|
||||
val user: User
|
||||
|
||||
|
||||
@@ -1,359 +1,14 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.platform.ijent
|
||||
|
||||
import com.intellij.openapi.util.NlsSafe
|
||||
import com.intellij.platform.ijent.IjentNetworkResult.Ok
|
||||
import com.intellij.platform.ijent.IjentTunnelsApi.Connection
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.channels.ReceiveChannel
|
||||
import kotlinx.coroutines.channels.SendChannel
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
import java.io.IOException
|
||||
import java.nio.ByteBuffer
|
||||
import kotlin.time.Duration
|
||||
import com.intellij.platform.eel.EelTunnelsPosixApi
|
||||
import com.intellij.platform.eel.EelTunnelsWindowsApi
|
||||
|
||||
/**
|
||||
* Methods for launching tunnels for TCP sockets, Unix sockets, etc.
|
||||
*/
|
||||
sealed interface IjentTunnelsApi {
|
||||
|
||||
/**
|
||||
* **This is a delicate API, for applied usages, please consider [withConnectionToRemotePort]**.
|
||||
*
|
||||
* Creates a connection to a TCP socket to a named host specified by [address].
|
||||
*
|
||||
* If the result is [IjentNetworkResult.Error], then there was an error during establishment of the connection.
|
||||
* Otherwise, the result is [IjentNetworkResult.Ok], which means that the connection is ready to use.
|
||||
*
|
||||
* The connection exists as a pair of channels [Connection.channelToServer] and [Connection.channelFromServer],
|
||||
* which allow communicating to a remote server from the IDE side.
|
||||
*
|
||||
* Packets sent to the channel and received from the channel may be split and/or concatenated.
|
||||
* The packets may be split only if their size exceeds [com.intellij.platform.ijent.spi.RECOMMENDED_MAX_PACKET_SIZE].
|
||||
*
|
||||
* If the connection gets closed from the server, then the channels also get closed in the sense of [SendChannel.close].
|
||||
*
|
||||
* If an exception happens during sending, then [Connection.channelFromServer] gets closed exceptionally with [RemoteNetworkException].
|
||||
*
|
||||
* [Connection.channelToServer] can be closed separately with [SendChannel.close]. In this case, the EOF is sent to the server.
|
||||
* Note, that [Connection.channelFromServer] is __not__ closed in this case.
|
||||
*
|
||||
* One should not forget to invoke [Connection.close] when the connection is not needed.
|
||||
*/
|
||||
@Throws(IjentUnavailableException::class)
|
||||
suspend fun getConnectionToRemotePort(address: HostAddress): IjentNetworkResult<Connection, IjentConnectionError>
|
||||
|
||||
/**
|
||||
* Creates a builder for address on the remote host.
|
||||
*/
|
||||
fun hostAddressBuilder(port: UShort): HostAddress.Builder
|
||||
|
||||
/**
|
||||
* Represents an address to a remote host.
|
||||
*/
|
||||
interface HostAddress {
|
||||
/**
|
||||
* A builder class for remote host address.
|
||||
*/
|
||||
interface Builder {
|
||||
|
||||
/**
|
||||
* Sets the hostname for a remote host.
|
||||
* The hostname will be resolved remotely.
|
||||
*
|
||||
* By default, the hostname is `localhost`
|
||||
*/
|
||||
fun hostname(hostname: String): Builder
|
||||
|
||||
/**
|
||||
* If [hostname] is resolved to an IPv4 address, then it is used.
|
||||
*
|
||||
* Overrides [preferIPv6] and [preferOSDefault]
|
||||
*/
|
||||
fun preferIPv4(): Builder
|
||||
|
||||
/**
|
||||
* If [hostname] is resolved to an IPv6 address, then it is used.
|
||||
* Overrides [preferIPv4] and [preferOSDefault]
|
||||
*/
|
||||
fun preferIPv6(): Builder
|
||||
|
||||
/**
|
||||
* [hostname] is resolved according to the settings on the remote host.
|
||||
*
|
||||
* Overrides [preferIPv4] and [preferIPv6]. This is the default option.
|
||||
*/
|
||||
fun preferOSDefault(): Builder
|
||||
|
||||
/**
|
||||
* Sets timeout for connecting to remote host.
|
||||
* If the connection could not be established before [timeout], then [IjentConnectionError.ConnectionTimeout] would be returned
|
||||
* in [IjentTunnelsApi.getConnectionToRemotePort].
|
||||
*
|
||||
* Default value: 10 seconds.
|
||||
* The recognizable granularity is milliseconds.
|
||||
*/
|
||||
fun connectionTimeout(timeout: Duration): Builder
|
||||
|
||||
/**
|
||||
* Builds a remote host address object.
|
||||
*/
|
||||
fun build(): HostAddress
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Represents a controller for a remote connection
|
||||
*/
|
||||
interface Connection {
|
||||
/**
|
||||
* A channel to the remote server
|
||||
*/
|
||||
val channelToServer: SendChannel<ByteBuffer>
|
||||
|
||||
/**
|
||||
* A channel from the remote server
|
||||
*/
|
||||
val channelFromServer: ReceiveChannel<ByteBuffer>
|
||||
|
||||
/**
|
||||
* Sets the size of send buffer of the socket
|
||||
* @see java.net.SocketOptions.SO_SNDBUF
|
||||
*/
|
||||
@Throws(IjentUnavailableException::class)
|
||||
suspend fun setSendBufferSize(size: UInt)
|
||||
|
||||
/**
|
||||
* Sets the receive buffer size of the socket
|
||||
* @see java.net.SocketOptions.SO_RCVBUF
|
||||
*/
|
||||
@Throws(IjentUnavailableException::class)
|
||||
suspend fun setReceiveBufferSize(size: UInt)
|
||||
|
||||
/**
|
||||
* Sets the keep alive option for the socket
|
||||
* @see java.net.SocketOptions.SO_KEEPALIVE
|
||||
*/
|
||||
@Throws(IjentUnavailableException::class)
|
||||
suspend fun setKeepAlive(keepAlive: Boolean)
|
||||
|
||||
/**
|
||||
* Sets the possibility to reuse address of the socket
|
||||
* @see java.net.SocketOptions.SO_REUSEADDR
|
||||
*/
|
||||
@Throws(IjentUnavailableException::class)
|
||||
suspend fun setReuseAddr(reuseAddr: Boolean)
|
||||
|
||||
/**
|
||||
* Sets linger timeout for the socket
|
||||
* @see java.net.SocketOptions.SO_LINGER
|
||||
*/
|
||||
@Throws(IjentUnavailableException::class)
|
||||
suspend fun setLinger(lingerInterval: Duration)
|
||||
|
||||
/**
|
||||
* Disables pending data until acknowledgement
|
||||
* @see java.net.SocketOptions.TCP_NODELAY
|
||||
*/
|
||||
@Throws(IjentUnavailableException::class)
|
||||
suspend fun setNoDelay(noDelay: Boolean)
|
||||
|
||||
/**
|
||||
* Closes the connection to the socket.
|
||||
*/
|
||||
@Throws(IjentUnavailableException::class)
|
||||
suspend fun close()
|
||||
}
|
||||
|
||||
sealed class RemoteNetworkException(message: String) : IOException(message) {
|
||||
constructor() : this("")
|
||||
|
||||
class ConnectionReset : RemoteNetworkException()
|
||||
class ConnectionAborted : RemoteNetworkException()
|
||||
class UnknownFailure(error: String) : RemoteNetworkException(error)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience operator to decompose connection to a pair of channels when needed.
|
||||
* @return channel to server
|
||||
*/
|
||||
operator fun Connection.component1(): SendChannel<ByteBuffer> = channelToServer
|
||||
|
||||
/**
|
||||
* Convenience operator to decompose connection to a pair of channels when needed.
|
||||
* @return channel from server
|
||||
*/
|
||||
operator fun Connection.component2(): ReceiveChannel<ByteBuffer> = channelFromServer
|
||||
|
||||
interface IjentTunnelsPosixApi : IjentTunnelsApi {
|
||||
/**
|
||||
* Creates a remote UNIX socket forwarding. IJent listens for a connection on the remote machine, and when the connection
|
||||
* is accepted, the IDE communicates to the remote client via a pair of Kotlin channels.
|
||||
*
|
||||
* Packets sent to the channel and received from the channel may be split and/or concatenated.
|
||||
* The packets may be split only if their size exceeds [com.intellij.platform.ijent.spi.RECOMMENDED_MAX_PACKET_SIZE].
|
||||
*
|
||||
* The call accepts only one connection. If multiple connections should be accepted, the function is supposed to be called in a loop:
|
||||
* ```kotlin
|
||||
* val ijent: IjentApi = ijentApiFactory()
|
||||
*
|
||||
* val (socketPath, tx, rx) = listenOnUnixSocket(CreateFilePath.MkTemp(prefix = "ijent-", suffix = ".sock"))
|
||||
* println(socketPath) // /tmp/ijent-12345678.sock
|
||||
* launch {
|
||||
* handleConnection(tx, rx)
|
||||
* }
|
||||
* while (true) {
|
||||
* val (_, tx, rx) = listenOnUnixSocket(CreateFilePath.Fixed(socketPath))
|
||||
* launch {
|
||||
* handleConnection(tx, rx)
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
@Throws(IjentUnavailableException::class)
|
||||
suspend fun listenOnUnixSocket(path: CreateFilePath = CreateFilePath.MkTemp()): ListenOnUnixSocketResult
|
||||
|
||||
data class ListenOnUnixSocketResult(
|
||||
val unixSocketPath: String,
|
||||
val tx: SendChannel<ByteBuffer>,
|
||||
val rx: ReceiveChannel<ByteBuffer>,
|
||||
)
|
||||
|
||||
sealed interface CreateFilePath {
|
||||
data class Fixed(val path: String) : CreateFilePath
|
||||
|
||||
/** When [directory] is empty, the usual tmpdir is used. */
|
||||
data class MkTemp(val directory: String = "", val prefix: String = "", val suffix: String = "") : CreateFilePath
|
||||
}
|
||||
}
|
||||
|
||||
interface IjentTunnelsWindowsApi : IjentTunnelsApi
|
||||
|
||||
/**
|
||||
* Convenience function for working with a connection to a remote server.
|
||||
*
|
||||
* Example:
|
||||
* ```kotlin
|
||||
*
|
||||
* suspend fun foo() {
|
||||
* ijentTunnelsApi.withConnectionToRemotePort("localhost", 8080, {
|
||||
* myErrorReporter.report(it)
|
||||
* }) { (channelTo, channelFrom) ->
|
||||
* handleConnection(channelTo, channelFrom)
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* ```
|
||||
*
|
||||
* If the connection could not be established, then [errorHandler] is invoked.
|
||||
* Otherwise, [action] is invoked. The connection gets automatically closed when [action] finishes.
|
||||
*
|
||||
* @see com.intellij.platform.ijent.IjentTunnelsApi.getConnectionToRemotePort for more details on the behavior of [Connection]
|
||||
*/
|
||||
@Throws(IjentUnavailableException::class)
|
||||
suspend fun <T> IjentTunnelsApi.withConnectionToRemotePort(
|
||||
hostAddress: IjentTunnelsApi.HostAddress,
|
||||
errorHandler: suspend (IjentConnectionError) -> T,
|
||||
action: suspend CoroutineScope.(Connection) -> T,
|
||||
): T =
|
||||
when (val connectionResult = getConnectionToRemotePort(hostAddress)) {
|
||||
is IjentNetworkResult.Error -> errorHandler(connectionResult.error)
|
||||
is Ok -> try {
|
||||
coroutineScope { action(connectionResult.value) }
|
||||
}
|
||||
finally {
|
||||
connectionResult.value.close()
|
||||
}
|
||||
}
|
||||
|
||||
@Throws(IjentUnavailableException::class)
|
||||
suspend fun <T> IjentTunnelsApi.withConnectionToRemotePort(
|
||||
host: String, port: UShort,
|
||||
errorHandler: suspend (IjentConnectionError) -> T,
|
||||
action: suspend CoroutineScope.(Connection) -> T,
|
||||
): T = withConnectionToRemotePort(hostAddressBuilder(port).hostname(host).build(), errorHandler, action)
|
||||
|
||||
@Throws(IjentUnavailableException::class)
|
||||
suspend fun <T> IjentTunnelsApi.withConnectionToRemotePort(
|
||||
remotePort: UShort,
|
||||
errorHandler: suspend (IjentConnectionError) -> T,
|
||||
action: suspend CoroutineScope.(Connection) -> T,
|
||||
): T = withConnectionToRemotePort("localhost", remotePort, errorHandler, action)
|
||||
|
||||
/**
|
||||
* Represents a common class for all network-related errors appearing during the interaction with IJent
|
||||
*/
|
||||
sealed interface IjentNetworkError
|
||||
|
||||
/**
|
||||
* Represents a result of a network operation
|
||||
*/
|
||||
sealed interface IjentNetworkResult<out T, out E : IjentNetworkError> {
|
||||
/**
|
||||
* Used when a network operation completed successfully
|
||||
*/
|
||||
interface Ok<out T> : IjentNetworkResult<T, Nothing> {
|
||||
val value: T
|
||||
}
|
||||
|
||||
/**
|
||||
* Used when a network operation completed with an error
|
||||
*/
|
||||
interface Error<out E : IjentNetworkError> : IjentNetworkResult<Nothing, E> {
|
||||
val error: E
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An error that can happen during the creation of a connection to a remote server
|
||||
*/
|
||||
interface IjentConnectionError : IjentNetworkError {
|
||||
val message: @NlsSafe String
|
||||
|
||||
data object ConnectionTimeout : IjentConnectionError {
|
||||
override val message: @NlsSafe String = "Connection could not be established because of timeout"
|
||||
}
|
||||
|
||||
/**
|
||||
* Returned when a hostname on the remote server was resolved to multiple different addresses.
|
||||
*/
|
||||
data object AmbiguousAddress : IjentConnectionError {
|
||||
override val message: String = "Hostname could not be resolved uniquely"
|
||||
}
|
||||
|
||||
/**
|
||||
* Returned when a socket could not be created because of an OS error.
|
||||
*/
|
||||
@JvmInline
|
||||
value class SocketCreationFailure(override val message: @NlsSafe String) : IjentConnectionError
|
||||
|
||||
/**
|
||||
* Returned when resolve of remote address failed during the creation of a socket.
|
||||
*/
|
||||
object HostUnreachable : IjentConnectionError {
|
||||
override val message: @NlsSafe String = "Remote host is unreachable"
|
||||
}
|
||||
|
||||
/**
|
||||
* Returned when the remote server does not accept connections.
|
||||
*/
|
||||
object ConnectionRefused : IjentConnectionError {
|
||||
override val message: @NlsSafe String = "Connection was refused by remote server"
|
||||
}
|
||||
|
||||
/**
|
||||
* Returned when hostname could not be resolved.
|
||||
*/
|
||||
@JvmInline
|
||||
value class ResolveFailure(override val message: @NlsSafe String) : IjentConnectionError
|
||||
|
||||
/**
|
||||
* Unknown failure during a connection establishment
|
||||
*/
|
||||
@JvmInline
|
||||
value class UnknownFailure(override val message: @NlsSafe String) : IjentConnectionError
|
||||
}
|
||||
sealed interface IjentTunnelsApi
|
||||
|
||||
interface IjentTunnelsPosixApi : EelTunnelsPosixApi, IjentTunnelsApi
|
||||
|
||||
interface IjentTunnelsWindowsApi : EelTunnelsWindowsApi, IjentTunnelsApi
|
||||
|
||||
@@ -14,7 +14,7 @@ import java.nio.ByteBuffer
|
||||
sealed interface IjentFileSystemApi {
|
||||
|
||||
/**
|
||||
* The same as the user from [com.intellij.platform.ijent.IjentApi.info].
|
||||
* The same as the user from [com.intellij.platform.eel.IjentApi.info].
|
||||
*
|
||||
* There's a duplication of methods because [user] is required for checking file permissions correctly, but also it can be required
|
||||
* in other cases outside the filesystem.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.platform.ijent.fs
|
||||
|
||||
import com.intellij.platform.ijent.IjentPlatform
|
||||
import com.intellij.platform.eel.EelPlatform
|
||||
import com.intellij.platform.ijent.fs.IjentPath.Absolute.OS
|
||||
import java.nio.file.InvalidPathException
|
||||
|
||||
@@ -233,8 +233,8 @@ fun <P : IjentPath> IjentPathResult<P>.getOrThrow(): P =
|
||||
is IjentPathResult.Err -> throw InvalidPathException(raw, reason)
|
||||
}
|
||||
|
||||
val IjentPlatform.pathOs: OS
|
||||
val EelPlatform.pathOs: OS
|
||||
get() = when (this) {
|
||||
is IjentPlatform.Posix -> OS.UNIX
|
||||
is IjentPlatform.Windows -> OS.WINDOWS
|
||||
is EelPlatform.Posix -> OS.UNIX
|
||||
is EelPlatform.Windows -> OS.WINDOWS
|
||||
}
|
||||
@@ -4,7 +4,7 @@ package com.intellij.platform.ijent.spi
|
||||
import com.intellij.execution.CommandLineUtil.posixQuote
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.diagnostic.*
|
||||
import com.intellij.platform.ijent.IjentPlatform
|
||||
import com.intellij.platform.eel.EelPlatform
|
||||
import com.intellij.platform.ijent.getIjentGrpcArgv
|
||||
import com.intellij.util.io.computeDetached
|
||||
import com.intellij.util.io.copyToAsync
|
||||
@@ -48,7 +48,7 @@ abstract class IjentDeployingOverShellProcessStrategy(scope: CoroutineScope) : I
|
||||
context
|
||||
}
|
||||
|
||||
final override suspend fun getTargetPlatform(): IjentPlatform.Posix {
|
||||
final override suspend fun getTargetPlatform(): EelPlatform.Posix {
|
||||
return myContext.await().execCommand {
|
||||
getTargetPlatform()
|
||||
}
|
||||
@@ -265,7 +265,7 @@ internal suspend fun createDeployingContext(runWhichCmd: suspend (commands: Coll
|
||||
)
|
||||
}
|
||||
|
||||
private suspend fun DeployingContextAndShell.getTargetPlatform(): IjentPlatform.Posix = run {
|
||||
private suspend fun DeployingContextAndShell.getTargetPlatform(): EelPlatform.Posix = run {
|
||||
// There are two arguments in `uname` that can show the process architecture: `-m` and `-p`. According to `man uname`, `-p` is more
|
||||
// verbose, and that information may be sufficient for choosing the right binary.
|
||||
// https://man.freebsd.org/cgi/man.cgi?query=uname&sektion=1
|
||||
@@ -275,8 +275,8 @@ private suspend fun DeployingContextAndShell.getTargetPlatform(): IjentPlatform.
|
||||
|
||||
val targetPlatform = when {
|
||||
arch.isEmpty() -> throw IjentStartupError.IncompatibleTarget("Empty output of `uname`")
|
||||
"x86_64" in arch -> IjentPlatform.X8664Linux
|
||||
"aarch64" in arch -> IjentPlatform.Aarch64Linux
|
||||
"x86_64" in arch -> EelPlatform.X8664Linux
|
||||
"aarch64" in arch -> EelPlatform.Aarch64Linux
|
||||
else -> throw IjentStartupError.IncompatibleTarget("No binary for architecture $arch")
|
||||
}
|
||||
return targetPlatform
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.platform.ijent.spi
|
||||
|
||||
import com.intellij.platform.ijent.IjentPlatform
|
||||
import com.intellij.platform.eel.EelPlatform
|
||||
import com.intellij.platform.ijent.deploy
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
import java.nio.file.Path
|
||||
@@ -13,7 +13,7 @@ import java.nio.file.Path
|
||||
*
|
||||
* Every instance of [IjentDeployingStrategy] is used to start exactly one IJent.
|
||||
*
|
||||
* @see com.intellij.platform.ijent.IjentApi
|
||||
* @see com.intellij.platform.eel.IjentApi
|
||||
*/
|
||||
@ApiStatus.OverrideOnly
|
||||
interface IjentDeployingStrategy {
|
||||
@@ -25,7 +25,7 @@ interface IjentDeployingStrategy {
|
||||
*
|
||||
* @see com.intellij.platform.ijent.IjentExecFileProvider.getIjentBinary
|
||||
*/
|
||||
suspend fun getTargetPlatform(): IjentPlatform
|
||||
suspend fun getTargetPlatform(): EelPlatform
|
||||
|
||||
/**
|
||||
* Defines a set of options for connecting to a running IJent
|
||||
@@ -66,11 +66,11 @@ interface IjentDeployingStrategy {
|
||||
|
||||
interface Posix : IjentDeployingStrategy {
|
||||
/** @see [IjentDeployingStrategy.getTargetPlatform] */
|
||||
override suspend fun getTargetPlatform(): IjentPlatform.Posix
|
||||
override suspend fun getTargetPlatform(): EelPlatform.Posix
|
||||
}
|
||||
|
||||
interface Windows : IjentDeployingStrategy {
|
||||
/** @see [IjentDeployingStrategy.getTargetPlatform] */
|
||||
override suspend fun getTargetPlatform(): IjentPlatform.Windows
|
||||
override suspend fun getTargetPlatform(): EelPlatform.Windows
|
||||
}
|
||||
}
|
||||
@@ -2,11 +2,12 @@
|
||||
package com.intellij.platform.ijent.spi
|
||||
|
||||
import com.intellij.openapi.components.serviceAsync
|
||||
import com.intellij.platform.eel.EelPlatform
|
||||
import com.intellij.platform.ijent.*
|
||||
|
||||
/**
|
||||
* Given that there is some IJent process launched, this extension gets handles to stdin+stdout of the process and returns
|
||||
* an [IjentApi] instance for calling procedures on IJent side.
|
||||
* an [com.intellij.platform.eel.IjentApi] instance for calling procedures on IJent side.
|
||||
*/
|
||||
interface IjentSessionProvider {
|
||||
/**
|
||||
@@ -14,7 +15,7 @@ interface IjentSessionProvider {
|
||||
*/
|
||||
suspend fun connect(
|
||||
strategy: IjentConnectionStrategy,
|
||||
platform: IjentPlatform,
|
||||
platform: EelPlatform,
|
||||
mediator: IjentSessionMediator,
|
||||
): IjentApi
|
||||
|
||||
@@ -39,7 +40,7 @@ sealed class IjentStartupError : RuntimeException {
|
||||
}
|
||||
|
||||
internal class DefaultIjentSessionProvider : IjentSessionProvider {
|
||||
override suspend fun connect(strategy: IjentConnectionStrategy, platform: IjentPlatform, mediator: IjentSessionMediator): IjentApi {
|
||||
override suspend fun connect(strategy: IjentConnectionStrategy, platform: EelPlatform, mediator: IjentSessionMediator): IjentApi {
|
||||
throw IjentStartupError.MissingImplPlugin()
|
||||
}
|
||||
}
|
||||
@@ -51,7 +52,7 @@ internal class DefaultIjentSessionProvider : IjentSessionProvider {
|
||||
* The process terminates automatically only when the IDE exits, or if [IjentApi.close] is called explicitly.
|
||||
* [com.intellij.platform.ijent.bindToScope] may be useful for terminating the IJent process earlier.
|
||||
*/
|
||||
suspend fun connectToRunningIjent(ijentName: String, strategy: IjentConnectionStrategy, platform: IjentPlatform, process: Process): IjentApi {
|
||||
suspend fun connectToRunningIjent(ijentName: String, strategy: IjentConnectionStrategy, platform: EelPlatform, process: Process): IjentApi {
|
||||
val ijentSessionRegistry = IjentSessionRegistry.instanceAsync()
|
||||
val ijentId = ijentSessionRegistry.register(ijentName, oneOff = true) { ijentId ->
|
||||
val mediator = IjentSessionMediator.create(process, ijentId)
|
||||
@@ -62,9 +63,9 @@ suspend fun connectToRunningIjent(ijentName: String, strategy: IjentConnectionSt
|
||||
}
|
||||
|
||||
/** A specialized overload of [connectToRunningIjent] */
|
||||
suspend fun connectToRunningIjent(ijentName: String, strategy: IjentConnectionStrategy, platform: IjentPlatform.Posix, process: Process): IjentPosixApi =
|
||||
connectToRunningIjent(ijentName, strategy, platform as IjentPlatform, process) as IjentPosixApi
|
||||
suspend fun connectToRunningIjent(ijentName: String, strategy: IjentConnectionStrategy, platform: EelPlatform.Posix, process: Process): IjentPosixApi =
|
||||
connectToRunningIjent(ijentName, strategy, platform as EelPlatform, process) as IjentPosixApi
|
||||
|
||||
/** A specialized overload of [connectToRunningIjent] */
|
||||
suspend fun connectToRunningIjent(ijentName: String, strategy: IjentConnectionStrategy, platform: IjentPlatform.Windows, process: Process): IjentWindowsApi =
|
||||
connectToRunningIjent(ijentName, strategy, platform as IjentPlatform, process) as IjentWindowsApi
|
||||
suspend fun connectToRunningIjent(ijentName: String, strategy: IjentConnectionStrategy, platform: EelPlatform.Windows, process: Process): IjentWindowsApi =
|
||||
connectToRunningIjent(ijentName, strategy, platform as EelPlatform, process) as IjentWindowsApi
|
||||
|
||||
@@ -4,13 +4,14 @@
|
||||
package com.intellij.platform.ijent.tunnels
|
||||
|
||||
import com.intellij.openapi.diagnostic.Logger
|
||||
import com.intellij.platform.eel.EelTunnelsApi
|
||||
import com.intellij.platform.eel.withConnectionToRemotePort
|
||||
import com.intellij.platform.ijent.*
|
||||
import com.intellij.util.io.toByteArray
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.channels.ClosedSendChannelException
|
||||
import kotlinx.coroutines.channels.ReceiveChannel
|
||||
import kotlinx.coroutines.channels.SendChannel
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
import java.net.InetSocketAddress
|
||||
import java.net.ServerSocket
|
||||
import java.net.Socket
|
||||
@@ -20,9 +21,10 @@ import java.nio.ByteBuffer
|
||||
import java.util.concurrent.CancellationException
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
import kotlin.time.DurationUnit
|
||||
import com.intellij.platform.eel.component1
|
||||
import com.intellij.platform.eel.component2
|
||||
|
||||
|
||||
private val LOG: Logger = Logger.getInstance(IjentTunnelsApi::class.java)
|
||||
private val LOG: Logger = Logger.getInstance(EelTunnelsApi::class.java)
|
||||
|
||||
/**
|
||||
* A tunnel to a remote server (a.k.a. local port forwarding).
|
||||
@@ -36,7 +38,7 @@ private val LOG: Logger = Logger.getInstance(IjentTunnelsApi::class.java)
|
||||
*
|
||||
* This function returns when the server starts accepting connections.
|
||||
*/
|
||||
fun CoroutineScope.forwardLocalPort(tunnels: IjentTunnelsApi, localPort: Int, address: IjentTunnelsApi.HostAddress) {
|
||||
fun CoroutineScope.forwardLocalPort(tunnels: EelTunnelsApi, localPort: Int, address: EelTunnelsApi.HostAddress) {
|
||||
val serverSocket = ServerSocket()
|
||||
serverSocket.bind(InetSocketAddress("localhost", localPort))
|
||||
serverSocket.soTimeout = 2.seconds.toInt(DurationUnit.MILLISECONDS)
|
||||
@@ -120,7 +122,7 @@ private fun CoroutineScope.redirectIJentDataToClientConnection(connectionId: Int
|
||||
}
|
||||
outputStream.flush()
|
||||
}
|
||||
catch (e: IjentTunnelsApi.RemoteNetworkException) {
|
||||
catch (e: EelTunnelsApi.RemoteNetworkException) {
|
||||
LOG.warn("Connection $connectionId closed", e)
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
package com.intellij.execution.ijent
|
||||
|
||||
import com.intellij.execution.process.SelfKiller
|
||||
import com.intellij.platform.eel.EelProcess
|
||||
import com.intellij.platform.ijent.IjentChildProcess
|
||||
import com.intellij.util.concurrency.annotations.RequiresBackgroundThread
|
||||
import com.pty4j.PtyProcess
|
||||
@@ -53,7 +54,7 @@ class IjentChildPtyProcessAdapter(
|
||||
try {
|
||||
delegate.ijentChildProcess.resizePty(columns = winSize.columns, rows = winSize.rows)
|
||||
}
|
||||
catch (err: IjentChildProcess.ResizePtyError) {
|
||||
catch (err: EelProcess.ResizePtyError) {
|
||||
// The other implementation throw IllegalStateException in such cases.
|
||||
throw IllegalStateException(err.message, err)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.execution.ijent
|
||||
|
||||
import com.intellij.platform.eel.EelProcess
|
||||
import com.intellij.platform.ijent.IjentChildProcess
|
||||
import com.intellij.platform.util.coroutines.channel.ChannelOutputStream
|
||||
import kotlinx.coroutines.runBlocking
|
||||
@@ -32,7 +33,7 @@ internal class IjentStdinOutputStream(
|
||||
ijentChildProcess.sendStdinWithConfirmation(byteArrayOf())
|
||||
})
|
||||
}
|
||||
catch (err: IjentChildProcess.SendStdinError) {
|
||||
catch (err: EelProcess.SendStdinError) {
|
||||
throw IOException("Failed to flush the output stream: ${err.message}", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,8 +12,8 @@ import com.intellij.openapi.diagnostic.debug
|
||||
import com.intellij.openapi.progress.runBlockingCancellable
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.util.io.FileUtil
|
||||
import com.intellij.platform.eel.EelExecApi
|
||||
import com.intellij.platform.ijent.IjentChildProcess
|
||||
import com.intellij.platform.ijent.IjentExecApi
|
||||
import com.intellij.util.concurrency.annotations.RequiresBackgroundThread
|
||||
import com.intellij.util.concurrency.annotations.RequiresBlockingContext
|
||||
import com.intellij.util.suspendingLazy
|
||||
@@ -117,7 +117,7 @@ fun runProcessBlocking(
|
||||
val exePath = FileUtil.toSystemIndependentName(args.removeFirst())
|
||||
|
||||
val pty = ptyOptions?.run {
|
||||
IjentExecApi.Pty(initialColumns, initialRows, !consoleMode)
|
||||
EelExecApi.Pty(initialColumns, initialRows, !consoleMode)
|
||||
}
|
||||
|
||||
val workingDirectory = processBuilder.directory()?.toPath()?.let { windowsWorkingDirectory ->
|
||||
@@ -136,13 +136,13 @@ fun runProcessBlocking(
|
||||
.workingDirectory(workingDirectory)
|
||||
.execute()
|
||||
) {
|
||||
is IjentExecApi.ExecuteProcessResult.Success ->
|
||||
processResult.process.toProcess(
|
||||
is EelExecApi.ExecuteProcessResult.Success ->
|
||||
(processResult.process as IjentChildProcess).toProcess(
|
||||
coroutineScope = scope,
|
||||
isPty = pty != null,
|
||||
redirectStderr = processBuilder.redirectErrorStream(),
|
||||
)
|
||||
is IjentExecApi.ExecuteProcessResult.Failure -> throw IOException(processResult.message)
|
||||
is EelExecApi.ExecuteProcessResult.Failure -> throw IOException(processResult.message)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -99,5 +99,6 @@
|
||||
<orderEntry type="module" module-name="intellij.platform.util.io.storages" />
|
||||
<orderEntry type="library" name="kotlinx-collections-immutable" level="project" />
|
||||
<orderEntry type="library" scope="TEST" name="ktor-server-cio" level="project" />
|
||||
<orderEntry type="module" module-name="intellij.platform.eel" scope="TEST" />
|
||||
</component>
|
||||
</module>
|
||||
@@ -14,6 +14,8 @@ import com.intellij.openapi.util.Computable
|
||||
import com.intellij.openapi.util.Disposer
|
||||
import com.intellij.openapi.util.Key
|
||||
import com.intellij.openapi.util.use
|
||||
import com.intellij.platform.eel.EelExecApi
|
||||
import com.intellij.platform.eel.EelPlatform
|
||||
import com.intellij.platform.ijent.*
|
||||
import com.intellij.platform.ijent.fs.IjentFileSystemPosixApi
|
||||
import com.intellij.testFramework.junit5.TestApplication
|
||||
@@ -504,7 +506,7 @@ class WSLDistributionTest {
|
||||
enum class WslTestStrategy { Legacy, Ijent }
|
||||
|
||||
private class MockIjentApi(private val adapter: GeneralCommandLine, val rootUser: Boolean) : IjentPosixApi {
|
||||
override val platform: IjentPlatform get() = throw UnsupportedOperationException()
|
||||
override val platform: EelPlatform get() = throw UnsupportedOperationException()
|
||||
|
||||
override val isRunning: Boolean get() = true
|
||||
|
||||
@@ -522,7 +524,7 @@ private class MockIjentApi(private val adapter: GeneralCommandLine, val rootUser
|
||||
}
|
||||
|
||||
private class MockIjentExecApi(private val adapter: GeneralCommandLine, private val rootUser: Boolean) : IjentExecApi {
|
||||
override fun executeProcessBuilder(exe: String): IjentExecApi.ExecuteProcessBuilder =
|
||||
override fun executeProcessBuilder(exe: String): EelExecApi.ExecuteProcessBuilder =
|
||||
MockIjentApiExecuteProcessBuilder(adapter.apply { exePath = exe }, rootUser)
|
||||
|
||||
override suspend fun fetchLoginShellEnvVariables(): Map<String, String> = mapOf("SHELL" to TEST_SHELL)
|
||||
@@ -533,38 +535,38 @@ private val TEST_ROOT_USER_SET by lazy { Key.create<Boolean>("TEST_ROOT_USER_SET
|
||||
private class MockIjentApiExecuteProcessBuilder(
|
||||
private val adapter: GeneralCommandLine,
|
||||
rootUser: Boolean,
|
||||
) : IjentExecApi.ExecuteProcessBuilder {
|
||||
) : EelExecApi.ExecuteProcessBuilder {
|
||||
init {
|
||||
if (rootUser) {
|
||||
adapter.putUserData(TEST_ROOT_USER_SET, true)
|
||||
}
|
||||
}
|
||||
|
||||
override fun args(args: List<String>): IjentExecApi.ExecuteProcessBuilder = apply {
|
||||
override fun args(args: List<String>): EelExecApi.ExecuteProcessBuilder = apply {
|
||||
adapter.parametersList.run {
|
||||
clearAll()
|
||||
addAll(args)
|
||||
}
|
||||
}
|
||||
|
||||
override fun env(env: Map<String, String>): IjentExecApi.ExecuteProcessBuilder = apply {
|
||||
override fun env(env: Map<String, String>): EelExecApi.ExecuteProcessBuilder = apply {
|
||||
adapter.environment.run {
|
||||
clear()
|
||||
putAll(env)
|
||||
}
|
||||
}
|
||||
|
||||
override fun pty(pty: IjentExecApi.Pty?): IjentExecApi.ExecuteProcessBuilder = this
|
||||
override fun pty(pty: EelExecApi.Pty?): EelExecApi.ExecuteProcessBuilder = this
|
||||
|
||||
override fun workingDirectory(workingDirectory: String?): IjentExecApi.ExecuteProcessBuilder = apply {
|
||||
override fun workingDirectory(workingDirectory: String?): EelExecApi.ExecuteProcessBuilder = apply {
|
||||
adapter.setWorkDirectory(workingDirectory)
|
||||
}
|
||||
|
||||
override suspend fun execute(): IjentExecApi.ExecuteProcessResult = executeResultMock
|
||||
override suspend fun execute(): EelExecApi.ExecuteProcessResult = executeResultMock
|
||||
}
|
||||
|
||||
private val executeResultMock by lazy {
|
||||
IjentExecApi.ExecuteProcessResult.Failure(errno = 12345, message = "mock result ${Ksuid.generate()}")
|
||||
EelExecApi.ExecuteProcessResult.Failure(errno = 12345, message = "mock result ${Ksuid.generate()}")
|
||||
}
|
||||
|
||||
private class WslTestStrategyExtension
|
||||
|
||||
Reference in New Issue
Block a user