Skip to content

Support for Gateway 2023 #184

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Mar 2, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,14 @@

## Unreleased

### Added

- support for Gateway 2023

## 2.1.7 - 2023-02-28

### Fixed

- terminal link is now correct when host ends in `/`
- improved resiliency and error handling when trying to open the last successful connection

Expand Down
8 changes: 4 additions & 4 deletions gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,16 @@
pluginGroup=com.coder.gateway
pluginName=coder-gateway
# SemVer format -> https://semver.org
pluginVersion=2.1.7
pluginVersion=2.2.0
# See https://plugins.jetbrains.com/docs/intellij/build-number-ranges.html
# for insight into build numbers and IntelliJ Platform versions.
pluginSinceBuild=223.7571.70
pluginUntilBuild=223.*
pluginUntilBuild=231.*
# IntelliJ Platform Properties -> https://github.com/JetBrains/gradle-intellij-plugin#intellij-platform-properties
# Gateway available build versions https://www.jetbrains.com/intellij-repository/snapshots and https://www.jetbrains.com/intellij-repository/releases
platformType=GW
platformVersion=223.8617.56-CUSTOM-SNAPSHOT
instrumentationCompiler=223.8617.56-CUSTOM-SNAPSHOT
platformVersion=231.7665.28-CUSTOM-SNAPSHOT
instrumentationCompiler=231.7665.28-CUSTOM-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
Expand Down
16 changes: 7 additions & 9 deletions src/main/kotlin/com/coder/gateway/CoderGatewayMainView.kt
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
package com.coder.gateway

import com.coder.gateway.help.ABOUT_HELP_TOPIC
import com.coder.gateway.icons.CoderIcons
import com.coder.gateway.views.CoderGatewayConnectorWizardWrapperView
import com.coder.gateway.views.CoderGatewayRecentWorkspaceConnectionsView
import com.intellij.ui.components.ActionLink
import com.intellij.ui.components.BrowserLink
import com.intellij.openapi.help.HelpManager
import com.jetbrains.gateway.api.GatewayConnector
import com.jetbrains.gateway.api.GatewayConnectorDocumentation
import com.jetbrains.gateway.api.GatewayConnectorView
import com.jetbrains.gateway.api.GatewayRecentConnections
import com.jetbrains.rd.util.lifetime.Lifetime
import java.awt.Component
import javax.swing.Icon
import javax.swing.JComponent

class CoderGatewayMainView : GatewayConnector {
override fun getConnectorId() = CoderGatewayConstants.GATEWAY_CONNECTOR_ID
Expand All @@ -31,8 +31,10 @@ class CoderGatewayMainView : GatewayConnector {
return CoderGatewayBundle.message("gateway.connector.description")
}

override fun getDocumentationLink(): ActionLink {
return BrowserLink("Learn more", "https://coder.com/docs/coder-oss/latest")
override fun getDocumentationAction(): GatewayConnectorDocumentation {
return GatewayConnectorDocumentation(true) {
HelpManager.getInstance().invokeHelp(ABOUT_HELP_TOPIC)
}
}

override fun getRecentConnections(setContentCallback: (Component) -> Unit): GatewayRecentConnections {
Expand All @@ -43,10 +45,6 @@ class CoderGatewayMainView : GatewayConnector {
return CoderGatewayBundle.message("gateway.connector.title")
}

override fun getTitleAdornment(): JComponent? {
return null
}

override fun isAvailable(): Boolean {
return true
}
Expand Down
14 changes: 14 additions & 0 deletions src/main/kotlin/com/coder/gateway/help/CoderWebHelp.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.coder.gateway.help

import com.intellij.openapi.help.WebHelpProvider

const val ABOUT_HELP_TOPIC = "com.coder.gateway.about"

class CoderWebHelp : WebHelpProvider() {
override fun getHelpPageUrl(helpTopicId: String): String {
return when (helpTopicId) {
ABOUT_HELP_TOPIC -> "https://coder.com/docs/coder-oss/latest"
else -> "https://coder.com/docs/coder-oss/latest"
}
}
}
38 changes: 36 additions & 2 deletions src/main/kotlin/com/coder/gateway/sdk/TemplateIconDownloader.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,16 @@ package com.coder.gateway.sdk
import com.coder.gateway.icons.CoderIcons
import com.intellij.openapi.components.Service
import com.intellij.openapi.components.service
import com.intellij.util.IconUtil
import com.intellij.ui.JreHiDpiUtil
import com.intellij.ui.paint.alignToInt
import com.intellij.ui.scale.JBUIScale
import com.intellij.util.ImageLoader
import com.intellij.util.ui.ImageUtil
import org.imgscalr.Scalr
import java.awt.Component
import java.awt.Graphics
import java.awt.Graphics2D
import java.awt.image.BufferedImage
import java.net.URL
import javax.swing.Icon

Expand All @@ -30,7 +36,7 @@ class TemplateIconDownloader {
}
var img = ImageLoader.loadFromUrl(url)
if (img != null) {
val icon = IconUtil.toRetinaAwareIcon(Scalr.resize(ImageUtil.toBufferedImage(img), Scalr.Method.ULTRA_QUALITY, 32))
val icon = toRetinaAwareIcon(Scalr.resize(ImageUtil.toBufferedImage(img), Scalr.Method.ULTRA_QUALITY, 32))
cache[Pair(workspaceName, path)] = icon
return icon
}
Expand All @@ -39,6 +45,34 @@ class TemplateIconDownloader {
return iconForChar(workspaceName.lowercase().first())
}

private fun toRetinaAwareIcon(image: BufferedImage): Icon {
val sysScale = JBUIScale.sysScale()
return object : Icon {
override fun paintIcon(c: Component?, g: Graphics, x: Int, y: Int) {
if (isJreHiDPI) {
val newG = g.create(x, y, image.width, image.height) as Graphics2D
alignToInt(newG)
newG.scale(1.0 / sysScale, 1.0 / sysScale)
newG.drawImage(image, 0, 0, null)
newG.dispose()
} else {
g.drawImage(image, x, y, null)
}
}

override fun getIconWidth(): Int = if (isJreHiDPI) (image.width / sysScale).toInt() else image.width

override fun getIconHeight(): Int = if (isJreHiDPI) (image.height / sysScale).toInt() else image.height

private val isJreHiDPI: Boolean
get() = JreHiDpiUtil.isJreHiDPI(sysScale)

override fun toString(): String {
return "TemplateIconDownloader.toRetinaAwareIcon for $image"
}
}
}

private fun iconForChar(c: Char) = when (c) {
'0' -> CoderIcons.ZERO
'1' -> CoderIcons.ONE
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ import com.coder.gateway.views.steps.CoderWorkspacesStepView
import com.coder.gateway.views.steps.CoderWorkspacesWizardStep
import com.intellij.openapi.Disposable
import com.intellij.openapi.wm.impl.welcomeScreen.WelcomeScreenUIManager
import com.intellij.ui.dsl.builder.AlignX
import com.intellij.ui.dsl.builder.RightGap
import com.intellij.ui.dsl.builder.panel
import com.intellij.ui.dsl.gridLayout.HorizontalAlign
import com.intellij.util.ui.components.BorderLayoutPanel
import com.jetbrains.gateway.api.GatewayUI
import java.awt.Component
Expand Down Expand Up @@ -109,9 +109,10 @@ class CoderGatewayConnectorWizardView : BorderLayoutPanel(), Disposable {
indent {
row {

label("").resizableColumn().horizontalAlign(HorizontalAlign.FILL).gap(RightGap.SMALL)
previousButton = button("") { previous() }.horizontalAlign(HorizontalAlign.RIGHT).gap(RightGap.SMALL).applyToComponent { background = WelcomeScreenUIManager.getMainAssociatedComponentBackground() }.component
nextButton = button("") { next() }.horizontalAlign(HorizontalAlign.RIGHT).gap(RightGap.SMALL).applyToComponent { background = WelcomeScreenUIManager.getMainAssociatedComponentBackground() }.component
label("").resizableColumn().align(AlignX.FILL).gap(RightGap.SMALL)
previousButton = button("") { previous() }.align(AlignX.RIGHT).gap(RightGap.SMALL).applyToComponent { background = WelcomeScreenUIManager.getMainAssociatedComponentBackground() }.component
nextButton = button("") { next() }.align(AlignX.RIGHT).gap(RightGap.SMALL).applyToComponent { background = WelcomeScreenUIManager.getMainAssociatedComponentBackground() }.component
cell()
}
}.apply {
background = WelcomeScreenUIManager.getMainAssociatedComponentBackground()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,12 @@ import com.intellij.ui.DocumentAdapter
import com.intellij.ui.SearchTextField
import com.intellij.ui.components.ActionLink
import com.intellij.ui.components.JBScrollPane
import com.intellij.ui.dsl.builder.AlignX
import com.intellij.ui.dsl.builder.AlignY
import com.intellij.ui.dsl.builder.BottomGap
import com.intellij.ui.dsl.builder.RightGap
import com.intellij.ui.dsl.builder.TopGap
import com.intellij.ui.dsl.builder.panel
import com.intellij.ui.dsl.gridLayout.HorizontalAlign
import com.intellij.ui.dsl.gridLayout.VerticalAlign
import com.intellij.util.ui.JBFont
import com.intellij.util.ui.JBUI
import com.jetbrains.gateway.api.GatewayRecentConnections
Expand All @@ -38,7 +38,7 @@ import kotlinx.coroutines.cancel
import kotlinx.coroutines.launch
import java.awt.Component
import java.awt.Dimension
import java.util.Locale
import java.util.*
import javax.swing.JComponent
import javax.swing.JLabel
import javax.swing.event.DocumentEvent
Expand All @@ -65,14 +65,15 @@ class CoderGatewayRecentWorkspaceConnectionsView(private val setContentCallback:
panel {
indent {
row {
cell(JLabel()).resizableColumn().horizontalAlign(HorizontalAlign.FILL)
searchBar = cell(SearchTextField(false)).resizableColumn().horizontalAlign(HorizontalAlign.FILL).applyToComponent {
cell(JLabel()).resizableColumn().align(AlignX.FILL)
searchBar = cell(SearchTextField(false)).resizableColumn().align(AlignX.FILL).applyToComponent {
minimumSize = Dimension(350, -1)
textEditor.border = JBUI.Borders.empty(2, 5, 2, 0)
addDocumentListener(object : DocumentAdapter() {
override fun textChanged(e: DocumentEvent) {
val toSearchFor = [email protected]
val filteredConnections = recentConnectionsService.getAllRecentConnections().filter { it.coderWorkspaceHostname?.lowercase(Locale.getDefault())?.contains(toSearchFor) ?: false || it.projectPath?.lowercase(Locale.getDefault())?.contains(toSearchFor) ?: false }
val filteredConnections = recentConnectionsService.getAllRecentConnections()
.filter { it.coderWorkspaceHostname?.lowercase(Locale.getDefault())?.contains(toSearchFor) ?: false || it.projectPath?.lowercase(Locale.getDefault())?.contains(toSearchFor) ?: false }
updateContentView(filteredConnections.groupBy { it.coderWorkspaceHostname })
}
})
Expand All @@ -92,7 +93,7 @@ class CoderGatewayRecentWorkspaceConnectionsView(private val setContentCallback:
separator(background = WelcomeScreenUIManager.getSeparatorColor())
row {
resizableRow()
cell(recentWorkspacesContentPanel).resizableColumn().horizontalAlign(HorizontalAlign.FILL).verticalAlign(VerticalAlign.FILL).component
cell(recentWorkspacesContentPanel).resizableColumn().align(AlignX.FILL).align(AlignY.FILL).component
}
}
}.apply {
Expand All @@ -114,7 +115,7 @@ class CoderGatewayRecentWorkspaceConnectionsView(private val setContentCallback:
if (hostname != null) {
label(hostname).applyToComponent {
font = JBFont.h3().asBold()
}.horizontalAlign(HorizontalAlign.LEFT).gap(RightGap.SMALL)
}.align(AlignX.LEFT).gap(RightGap.SMALL)
actionButton(object : DumbAwareAction(CoderGatewayBundle.message("gateway.connector.recentconnections.terminal.button.tooltip"), "", CoderIcons.OPEN_TERMINAL) {
override fun actionPerformed(e: AnActionEvent) {
BrowserUtil.browse(recentConnections[0].webTerminalLink ?: "")
Expand All @@ -132,7 +133,7 @@ class CoderGatewayRecentWorkspaceConnectionsView(private val setContentCallback:
GatewayUI.getInstance().connect(connectionDetails.toWorkspaceParams())
}
})
label("").resizableColumn().horizontalAlign(HorizontalAlign.FILL)
label("").resizableColumn().align(AlignX.FILL)
label("Last opened: ${connectionDetails.lastOpened}").applyToComponent {
foreground = JBUI.CurrentTheme.ContextHelp.FOREGROUND
font = ComponentPanelBuilder.getCommentFont(font)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,11 @@ import com.intellij.ui.AnimatedIcon
import com.intellij.ui.ColoredListCellRenderer
import com.intellij.ui.DocumentAdapter
import com.intellij.ui.components.JBTextField
import com.intellij.ui.dsl.builder.AlignX
import com.intellij.ui.dsl.builder.BottomGap
import com.intellij.ui.dsl.builder.RowLayout
import com.intellij.ui.dsl.builder.TopGap
import com.intellij.ui.dsl.builder.panel
import com.intellij.ui.dsl.gridLayout.HorizontalAlign
import com.intellij.util.ui.JBFont
import com.intellij.util.ui.UIUtil
import com.intellij.util.ui.update.MergingUpdateQueue
Expand All @@ -47,6 +47,7 @@ import com.jetbrains.gateway.ssh.HighLevelHostAccessor
import com.jetbrains.gateway.ssh.IdeStatus
import com.jetbrains.gateway.ssh.IdeWithStatus
import com.jetbrains.gateway.ssh.IntelliJPlatformProduct
import com.jetbrains.gateway.ssh.util.validateRemotePath
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
Expand All @@ -62,7 +63,7 @@ import kotlinx.coroutines.withContext
import java.awt.Component
import java.awt.FlowLayout
import java.time.Duration
import java.util.Locale
import java.util.*
import javax.swing.ComboBoxModel
import javax.swing.DefaultComboBoxModel
import javax.swing.JLabel
Expand Down Expand Up @@ -99,13 +100,13 @@ class CoderLocateRemoteProjectStepView(private val disableNextAction: () -> Unit
label("IDE:")
cbIDE = cell(IDEComboBox(ideComboBoxModel).apply {
renderer = IDECellRenderer()
}).resizableColumn().horizontalAlign(HorizontalAlign.FILL).comment("The IDE will be downloaded from jetbrains.com").component
}).resizableColumn().align(AlignX.FILL).comment("The IDE will be downloaded from jetbrains.com").component
cell()
}.topGap(TopGap.NONE).layout(RowLayout.PARENT_GRID)

row {
label("Project directory:")
cell(tfProject).resizableColumn().horizontalAlign(HorizontalAlign.FILL).component
cell(tfProject).resizableColumn().align(AlignX.FILL).component
cell()
}.topGap(TopGap.NONE).bottomGap(BottomGap.NONE).layout(RowLayout.PARENT_GRID)
row {
Expand Down Expand Up @@ -190,8 +191,8 @@ class CoderLocateRemoteProjectStepView(private val disableNextAction: () -> Unit
pathValidationJobs.queue(Update.create("validate-remote-path") {
runBlocking {
try {
val isPathPresent = executor.isPathPresentOnRemote(tfProject.text)
if (!isPathPresent) {
val isPathPresent = validateRemotePath(tfProject.text, executor)
if (isPathPresent.pathOrNull == null) {
ComponentValidator.getInstance(tfProject).ifPresent {
it.updateInfo(ValidationInfo("Can't find directory: ${tfProject.text}", tfProject))
}
Expand Down
Loading