Skip to content

feat: hint ref'd by matchLabels/-Expressions & list them in editor (#642) #858

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

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
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
6 changes: 5 additions & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,13 @@ java {

repositories {
mavenLocal()
maven { url = uri("https://repository.jboss.org") }
/*
* github repo with intellij-common needs to be listed before jboss repository. Both have 1.9.9-SNAPSHOT
* First hit wins regardless of timestamp
*/
maven { url = uri("https://raw.githubusercontent.com/redhat-developer/intellij-common/repository/snapshots") }
maven { url = uri("https://raw.githubusercontent.com/redhat-developer/intellij-common/repository/releases") }
maven { url = uri("https://repository.jboss.org") }
mavenCentral()
intellijPlatform {
defaultRepositories()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import io.fabric8.kubernetes.api.model.HasMetadata
import io.fabric8.kubernetes.client.KubernetesClient
import io.fabric8.kubernetes.client.utils.KubernetesResourceUtil
import io.fabric8.kubernetes.client.utils.Serialization
import java.util.concurrent.atomic.AtomicBoolean
import java.util.concurrent.locks.ReentrantLock
import kotlin.concurrent.withLock

Expand All @@ -39,7 +40,7 @@ open class EditorResource(
) -> ClusterResource? =
ClusterResource.Factory::create
) {
var disposed: Boolean = false
var disposed: AtomicBoolean = AtomicBoolean(false)
/** for testing purposes */
private var state: EditorResourceState? = null
/** for testing purposes */
Expand Down Expand Up @@ -94,6 +95,9 @@ open class EditorResource(

/** for testing purposes */
open fun setState(state: EditorResourceState?) {
if (disposed.get()) {
return
}
resourceChangeMutex.withLock {
this.state = state
}
Expand All @@ -108,14 +112,18 @@ open class EditorResource(
* @see [createState]
*/
fun getState(): EditorResourceState {
return resourceChangeMutex.withLock {
val existingState = this.state
if (existingState == null) {
val newState = createState(resource, null)
setState(newState)
newState
} else {
existingState
return if (disposed.get()) {
Disposed()
} else {
resourceChangeMutex.withLock {
val existingState = this.state
if (existingState == null) {
val newState = createState(resource, null)
setState(newState)
newState
} else {
existingState
}
}
}
}
Expand Down Expand Up @@ -262,7 +270,7 @@ open class EditorResource(
/** for testing purposes */
protected open fun setLastPushedPulled(resource: HasMetadata?) {
resourceChangeMutex.withLock {
lastPushedPulled = resource
this.lastPushedPulled = resource
}
}

Expand Down Expand Up @@ -332,13 +340,13 @@ open class EditorResource(
}

fun dispose() {
if (disposed) {
if (disposed.get()) {
return
}
this.disposed = true
clusterResource?.close()
this.disposed.set(true)
setLastPushedPulled(null)
setResourceVersion(null)
clusterResource?.close()
}

private fun createClusterResource(resource: HasMetadata): ClusterResource? {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,20 @@ class Error(val title: String, val message: String? = null): EditorResourceState
}
}

class Disposed: EditorResourceState() {

override fun equals(other: Any?): Boolean {
if (this === other) {
return true
}
return other is Disposed
}

override fun hashCode(): Int {
return Objects.hash()
}
}

open class Identical: EditorResourceState()

abstract class Different(val exists: Boolean, val isOutdatedVersion: Boolean): EditorResourceState() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ open class EditorResources(
// remove editor resource for old resource that doesn't exist anymore
!new.contains(identifier)
// or editor resource that was disposed (ex. change in namespace, context)
|| editorResource.disposed
|| editorResource.disposed.get()
}
resources.keys.removeAll(toRemove.keys)
toRemove.values.forEach { editorResource ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,6 @@ open class ResourceEditorFactory protected constructor(

/* for testing purposes */
protected open fun getTelemetryMessageBuilder(): TelemetryMessageBuilder {
return TelemetryService.instance;
return TelemetryService.instance
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,17 @@ import com.intellij.codeInsight.hints.InlayHintsProvider
import com.intellij.codeInsight.hints.InlayHintsSink
import com.intellij.codeInsight.hints.NoSettings
import com.intellij.codeInsight.hints.SettingsKey
import com.intellij.codeInsight.hints.presentation.PresentationFactory
import com.intellij.json.psi.JsonFile
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.application.ReadAction
import com.intellij.openapi.editor.Editor
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import com.intellij.ui.dsl.builder.panel
import com.redhat.devtools.intellij.common.validation.KubernetesTypeInfo
import com.redhat.devtools.intellij.kubernetes.editor.inlay.base64.Base64Presentations
import com.redhat.devtools.intellij.kubernetes.editor.inlay.selector.SelectorPresentations
import com.redhat.devtools.intellij.kubernetes.editor.util.PsiElements
import org.jetbrains.yaml.psi.YAMLFile
import javax.swing.JComponent

Expand Down Expand Up @@ -65,34 +67,45 @@ internal class ResourceEditorInlayHintsProvider : InlayHintsProvider<NoSettings>
}
return when(element) {
is YAMLFile -> {
create(element, sink, editor)
create(element, sink, editor, factory)
false
}
is JsonFile -> {
create(element, sink, editor)
create(element, sink, editor, factory)
false
}
else -> true
}
}

private fun create(file: YAMLFile, sink: InlayHintsSink, editor: Editor) {
private fun create(file: YAMLFile, sink: InlayHintsSink, editor: Editor, factory: PresentationFactory) {
return ReadAction.run<Exception> {
file.documents.forEach { document ->
val info = KubernetesTypeInfo.create(document) ?: return@forEach
val element = document.topLevelValue ?: return@forEach
Base64Presentations.create(element, info, sink, editor)?.create()
}
file.documents
.mapNotNull { document -> document.topLevelValue }
.forEach { element ->
createPresentations(element, sink, editor, factory)
}
}
}

private fun create(file: JsonFile, sink: InlayHintsSink, editor: Editor) {
private fun create(file: JsonFile, sink: InlayHintsSink, editor: Editor, factory: PresentationFactory) {
return ReadAction.run<Exception> {
val info = KubernetesTypeInfo.create(file) ?: return@run
val element = file.topLevelValue ?: return@run
Base64Presentations.create(element, info, sink, editor)?.create()
file.allTopLevelValues.forEach { element ->
createPresentations(element, sink, editor, factory)
}
}
}

private fun createPresentations(element: PsiElement, sink: InlayHintsSink, editor: Editor, factory: PresentationFactory) {
val info = KubernetesTypeInfo.create(element) ?: return
Base64Presentations.create(element, info, sink, editor, factory)

val fileType = editor.virtualFile?.fileType ?: return
val project = editor.project ?: return
//PsiElements.getAll(fileType, project)
val allElements = PsiElements.getAllNoExclusions(fileType, project)
SelectorPresentations.createForSelector(element, allElements, sink, editor, factory)
SelectorPresentations.createForAllLabels(element, allElements, sink, editor, factory)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,26 +40,41 @@ object Base64Presentations {
private const val SECRET_RESOURCE_KIND = "Secret"
private const val CONFIGMAP_RESOURCE_KIND = "ConfigMap"

fun create(element: PsiElement, info: KubernetesTypeInfo, sink: InlayHintsSink, editor: Editor): InlayPresentationsFactory? {
return when {
fun create(
element: PsiElement,
info: KubernetesTypeInfo,
sink: InlayHintsSink,
editor: Editor,
factory: PresentationFactory,
/* for testing purposes */
stringPresentationFactory: (element: PsiElement, sink: InlayHintsSink, editor: Editor, factory: PresentationFactory) -> Unit
= { element, sink, editor, factory ->
StringPresentationsFactory(element, sink, editor, factory).create()
},
/* for testing purposes */
binaryPresentationFactory: (element: PsiElement, sink: InlayHintsSink, editor: Editor, factory: PresentationFactory) -> Unit
= { element, sink, editor, factory ->
BinaryPresentationsFactory(element, sink, editor, factory).create()
},
) {
when {
isKubernetesResource(SECRET_RESOURCE_KIND, info) -> {
val data = getDataValue(element) ?: return null
StringPresentationsFactory(data, sink, editor)
val data = element.getDataValue() ?: return
stringPresentationFactory.invoke(data, sink, editor, factory)
}

isKubernetesResource(CONFIGMAP_RESOURCE_KIND, info) -> {
val binaryData = getBinaryData(element) ?: return null
BinaryPresentationsFactory(binaryData, sink, editor)
val binaryData = element.getBinaryData() ?: return
binaryPresentationFactory.invoke(binaryData, sink, editor, factory)
}

else -> null
}
}

abstract class InlayPresentationsFactory(
private val element: PsiElement,
protected val sink: InlayHintsSink,
protected val editor: Editor
protected val editor: Editor,
protected val factory: PresentationFactory
) {

protected companion object {
Expand All @@ -69,17 +84,15 @@ object Base64Presentations {

fun create(): Collection<InlayPresentation> {
return element.children.mapNotNull { child ->
val adapter = Base64ValueAdapter(child)
create(adapter)
create(Base64ValueAdapter(child))
}
}

protected abstract fun create(adapter: Base64ValueAdapter): InlayPresentation?

}

class StringPresentationsFactory(element: PsiElement, sink: InlayHintsSink, editor: Editor)
: InlayPresentationsFactory(element, sink, editor) {
class StringPresentationsFactory(element: PsiElement, sink: InlayHintsSink, editor: Editor, factory: PresentationFactory)
: InlayPresentationsFactory(element, sink, editor, factory) {

override fun create(adapter: Base64ValueAdapter): InlayPresentation? {
val decoded = adapter.getDecoded() ?: return null
Expand All @@ -89,20 +102,22 @@ object Base64Presentations {
onValidValue(adapter::set, editor.project),
editor
)::show
val presentation = create(decoded, onClick, editor) ?: return null
val presentation = create(decoded, onClick, factory) ?: return null
sink.addInlineElement(offset, false, presentation, false)
return presentation
}

private fun create(text: String, onClick: (event: MouseEvent) -> Unit, editor: Editor): InlayPresentation? {
val factory = PresentationFactory(editor)
private fun create(text: String, onClick: (event: MouseEvent) -> Unit, factory: PresentationFactory): InlayPresentation? {
val trimmed = trimWithEllipsis(text, INLAY_HINT_MAX_WIDTH) ?: return null
val textPresentation = factory.smallText(trimmed)
val hoverPresentation = factory.referenceOnHover(textPresentation) { event, _ ->
onClick.invoke(event)
}
val tooltipPresentation = factory.withTooltip("Click to change value", hoverPresentation)
return factory.roundWithBackground(tooltipPresentation)
return factory.roundWithBackground(
factory.withTooltip(
"Click to change value",
factory.referenceOnHover(
factory.smallText(trimmed)) { event, _ ->
onClick.invoke(event)
}
)
)
}

private fun onValidValue(setter: (value: String, wrapAt: Int) -> Unit, project: Project?)
Expand All @@ -118,8 +133,8 @@ object Base64Presentations {

}

class BinaryPresentationsFactory(element: PsiElement, sink: InlayHintsSink, editor: Editor)
: InlayPresentationsFactory(element, sink, editor) {
class BinaryPresentationsFactory(element: PsiElement, sink: InlayHintsSink, editor: Editor, factory: PresentationFactory)
: InlayPresentationsFactory(element, sink, editor, factory) {

override fun create(adapter: Base64ValueAdapter): InlayPresentation? {
val decoded = adapter.getDecodedBytes() ?: return null
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,14 @@
******************************************************************************/
package com.redhat.devtools.intellij.kubernetes.editor.inlay.base64

import com.intellij.json.psi.JsonProperty
import com.intellij.psi.PsiElement
import com.redhat.devtools.intellij.kubernetes.editor.util.decodeBase64
import com.redhat.devtools.intellij.kubernetes.editor.util.decodeBase64ToBytes
import com.redhat.devtools.intellij.kubernetes.editor.util.encodeBase64
import com.redhat.devtools.intellij.kubernetes.editor.util.getValue
import com.redhat.devtools.intellij.kubernetes.editor.util.setValue
import org.jetbrains.yaml.psi.YAMLKeyValue

class Base64ValueAdapter(private val element: PsiElement) {

Expand Down Expand Up @@ -80,6 +82,10 @@ class Base64ValueAdapter(private val element: PsiElement) {
}

fun getStartOffset(): Int? {
return com.redhat.devtools.intellij.kubernetes.editor.util.getStartOffset(element)
return when(element) {
is YAMLKeyValue -> element.value?.textRange?.startOffset
is JsonProperty -> element.value?.textRange?.startOffset
else -> null
}
}
}
Loading
Loading