From cb3cf41ff56bdb817c275998169e2b1fde322974 Mon Sep 17 00:00:00 2001 From: Victor Nogueira Date: Wed, 13 Apr 2022 09:27:06 +0000 Subject: [PATCH] [ide] Initialize a shared terminal on JetBrains IDEs when a workspace starts --- .../backend-plugin/gradle.properties | 2 +- .../jetbrains/remote/GitpodTerminalService.kt | 96 +++++++++++++++++++ .../src/main/resources/META-INF/plugin.xml | 3 + 3 files changed, 100 insertions(+), 1 deletion(-) create mode 100644 components/ide/jetbrains/backend-plugin/src/main/kotlin/io/gitpod/jetbrains/remote/GitpodTerminalService.kt diff --git a/components/ide/jetbrains/backend-plugin/gradle.properties b/components/ide/jetbrains/backend-plugin/gradle.properties index 9eddc33c33a1a4..42fbf8c61900a2 100644 --- a/components/ide/jetbrains/backend-plugin/gradle.properties +++ b/components/ide/jetbrains/backend-plugin/gradle.properties @@ -16,7 +16,7 @@ platformVersion=221.4994-EAP-CANDIDATE-SNAPSHOT platformDownloadSources=true # Plugin Dependencies -> https://plugins.jetbrains.com/docs/intellij/plugin-dependencies.html # Example: platformPlugins = com.intellij.java, com.jetbrains.php:203.4449.22 -platformPlugins=Git4Idea +platformPlugins=Git4Idea, org.jetbrains.plugins.terminal, com.jetbrains.codeWithMe # Opt-out flag for bundling Kotlin standard library. # See https://plugins.jetbrains.com/docs/intellij/kotlin.html#kotlin-standard-library for details. kotlin.stdlib.default.dependency=false diff --git a/components/ide/jetbrains/backend-plugin/src/main/kotlin/io/gitpod/jetbrains/remote/GitpodTerminalService.kt b/components/ide/jetbrains/backend-plugin/src/main/kotlin/io/gitpod/jetbrains/remote/GitpodTerminalService.kt new file mode 100644 index 00000000000000..c6330b99a80111 --- /dev/null +++ b/components/ide/jetbrains/backend-plugin/src/main/kotlin/io/gitpod/jetbrains/remote/GitpodTerminalService.kt @@ -0,0 +1,96 @@ +// Copyright (c) 2022 Gitpod GmbH. All rights reserved. +// Licensed under the GNU Affero General Public License (AGPL). +// See License-AGPL.txt in the project root for license information. + +package io.gitpod.jetbrains.remote + +import com.intellij.openapi.Disposable +import com.intellij.openapi.application.runInEdt +import com.intellij.openapi.project.Project +import com.intellij.openapi.wm.ToolWindowManager +import com.intellij.openapi.wm.ex.ToolWindowManagerListener +import com.intellij.remoteDev.util.onTerminationOrNow +import com.intellij.util.application +import com.jetbrains.rd.util.lifetime.Lifetime +import com.jetbrains.rdserver.terminal.BackendTerminalManager +import com.jetbrains.rdserver.unattendedHost.UnattendedHostManager +import kotlinx.coroutines.DelicateCoroutinesApi +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.delay +import kotlinx.coroutines.future.await +import kotlinx.coroutines.launch +import org.jetbrains.plugins.terminal.ShellTerminalWidget +import org.jetbrains.plugins.terminal.TerminalToolWindowFactory +import org.jetbrains.plugins.terminal.TerminalView +import java.util.concurrent.CompletableFuture + +@Suppress("UnstableApiUsage", "EXPERIMENTAL_IS_NOT_ENABLED", "OPT_IN_IS_NOT_ENABLED") +@OptIn(DelicateCoroutinesApi::class) +class GitpodTerminalService(private val project: Project) : Disposable { + private val lifetime = Lifetime.Eternal.createNested() + private val terminalView = TerminalView.getInstance(project) + private val backendTerminalManager = BackendTerminalManager.getInstance(project) + + override fun dispose() { + lifetime.terminate() + } + + init { + if (!application.isHeadlessEnvironment) { + val job = launch() + lifetime.onTerminationOrNow { job.cancel() } + } + } + + private fun launch() = GlobalScope.launch { + getTerminalToolWindowRegisteredEvent().await() + delayUntilControllerClientConnects() + val widget = createNewSharedTerminal().await() + printWelcomeMessage(widget) + } + + private fun printWelcomeMessage(widget: ShellTerminalWidget) { + widget.executeCommand( + "clear; echo '\uD83D\uDC4B Welcome to Gitpod!\n" + + "\t\t - Start by typing `gp --help` to see what you can do with Gitpod CLI.\n" + + "\t\t - Run `gp tasks --help` to learn how to attach and watch tasks defined in .gitpod.yml!\n'; gp tasks attach" + ) + } + + private fun getTerminalToolWindowRegisteredEvent(): CompletableFuture { + val completableFuture = CompletableFuture() + + val messageBusConnection = project.messageBus.connect() + + val toolWindowManagerListener = object : ToolWindowManagerListener { + override fun toolWindowsRegistered(ids: MutableList, toolWindowManager: ToolWindowManager) { + if (ids.contains(TerminalToolWindowFactory.TOOL_WINDOW_ID)) { + completableFuture.complete(null) + messageBusConnection.disconnect() + } + } + } + + messageBusConnection.subscribe(ToolWindowManagerListener.TOPIC, toolWindowManagerListener) + + return completableFuture + } + + private suspend fun delayUntilControllerClientConnects() { + while (UnattendedHostManager.getInstance().controllerClientId == null) { + delay(1000L) + } + } + + private fun createNewSharedTerminal(): CompletableFuture { + val completableFuture = CompletableFuture() + + runInEdt { + val shellTerminalWidget = terminalView.createLocalShellWidget(project.basePath, null) + backendTerminalManager.shareTerminal(shellTerminalWidget, "Gitpod") + completableFuture.complete(shellTerminalWidget) + } + + return completableFuture + } +} diff --git a/components/ide/jetbrains/backend-plugin/src/main/resources/META-INF/plugin.xml b/components/ide/jetbrains/backend-plugin/src/main/resources/META-INF/plugin.xml index 4f4a2bcca020f9..e30a75b80f350e 100644 --- a/components/ide/jetbrains/backend-plugin/src/main/resources/META-INF/plugin.xml +++ b/components/ide/jetbrains/backend-plugin/src/main/resources/META-INF/plugin.xml @@ -16,6 +16,8 @@ com.intellij.modules.platform + + @@ -24,6 +26,7 @@ +