User: rednesto Date: 28 Apr 23 19:58 Revision: 1a088b94c292968b1b2e0f4324352013372be3a8 Summary: Add support for newer Adventure text color APIs TeamCity URL: http://ci.mcdev.io:80/viewModification.html?tab=vcsModificationFiles&modId=8462&personal=false Index: src/main/kotlin/insight/ColorLineMarkerProvider.kt =================================================================== --- src/main/kotlin/insight/ColorLineMarkerProvider.kt (revision 44eca6da07dc05d86ef7b91a05ba1885c8415a4c) +++ src/main/kotlin/insight/ColorLineMarkerProvider.kt (revision 1a088b94c292968b1b2e0f4324352013372be3a8) @@ -137,9 +137,32 @@ val c = ColorChooser.chooseColor(psiElement.project, editor.component, "Choose Color", color, false) ?: return@handler when (workElement) { - is ULiteralExpression -> workElement.setColor(c.rgb and 0xFFFFFF) - is UCallExpression -> workElement.setColor(c.red, c.green, c.blue) + is ULiteralExpression -> { + val currentValue = workElement.evaluate() + if (currentValue is Int) { + workElement.setColor(c.rgb and 0xFFFFFF) + } else if (currentValue is String) { + if (currentValue.length == 4) { + val hexString = "#" + + Integer.toUnsignedString(c.red, 16).first() + + Integer.toUnsignedString(c.green, 16).first() + + Integer.toUnsignedString(c.blue, 16).first() + workElement.setColor(hexString, true) + } else { + val hexString = "#" + Integer.toUnsignedString(c.rgb, 16).substring(2) + workElement.setColor(hexString, true) - } + } + } + } + is UCallExpression -> { + if (workElement.methodName == "hsvLike") { + val (h, s, v) = Color.RGBtoHSB(c.red, c.green, c.blue, null) + workElement.setColorHSV(h, s, v) + } + + workElement.setColor(c.red, c.green, c.blue) + } + } }, ) Index: src/main/kotlin/insight/ColorUtil.kt =================================================================== --- src/main/kotlin/insight/ColorUtil.kt (revision 44eca6da07dc05d86ef7b91a05ba1885c8415a4c) +++ src/main/kotlin/insight/ColorUtil.kt (revision 1a088b94c292968b1b2e0f4324352013372be3a8) @@ -8,6 +8,7 @@ * MIT License */ +@file:Suppress("UseJBColor") package com.demonwav.mcdev.insight import com.demonwav.mcdev.facet.MinecraftFacet @@ -143,7 +144,7 @@ return when { // Single Integer Argument - types.size == 1 && types[0] == PsiType.INT -> + types.size == 1 -> colorFromSingleArgument(arguments[0])?.let { it to arguments[0] } // Triple Integer Argument types.size == 3 && types.all { it == PsiType.INT } -> @@ -163,8 +164,30 @@ } private fun colorFromSingleArgument(expression: UExpression): Color? { - return Color(expression.evaluate() as? Int ?: return null) + return when (val paramVal = expression.evaluate()) { + is Int -> Color(paramVal as? Int ?: return null) + is String -> { + if (paramVal.startsWith("#")) { + val hexString = paramVal.substring(1) + when (hexString.length) { + 6 -> hexString.toIntOrNull(16)?.let(::Color) + 3 -> { + val hexInt = hexString.toIntOrNull(16) + ?: return null + val r = (hexInt and 0xf00) shr 8 or (hexInt and 0xf00) shr 4 + val g = (hexInt and 0x0f0) shr 4 or (hexInt and 0x0f0) + val b = (hexInt and 0x00f) shl 4 or (hexInt and 0x00f) + Color(r, g, b) -} + } + else -> null + } + } else { + null + } + } + else -> null + } +} private fun colorFromThreeArguments(expressions: List): Color? { fun normalize(value: Any?): Int? = when (value) { @@ -189,10 +212,17 @@ return colorFromThreeArguments(newExpression.valueArguments) } -fun UElement.setColor(color: String) { +fun UElement.setColor(color: String, isStringLiteral: Boolean = false) { val sourcePsi = this.sourcePsi ?: return sourcePsi.containingFile.runWriteAction { val project = sourcePsi.project + if (isStringLiteral) { + val literal = generationPlugin?.getElementFactory(project)?.createStringLiteralExpression(color, sourcePsi) + ?: return@runWriteAction + this.replace(literal) + return@runWriteAction + } + val parent = this.uastParent val newColorRef = generationPlugin?.getElementFactory(project)?.createQualifiedReference(color, sourcePsi) ?: return@runWriteAction @@ -241,3 +271,22 @@ b.sourcePsi?.replace(literalExpressionThree) } } + +fun UCallExpression.setColorHSV(h: Float, s: Float, v: Float) { + val sourcePsi = this.sourcePsi ?: return + sourcePsi.containingFile.runWriteAction { + val hExpr = this.valueArguments[0] + val sExpr = this.valueArguments[1] + val vExpr = this.valueArguments[2] + + val factory = JVMElementFactories.requireFactory(sourcePsi.language, sourcePsi.project) + + val literalExpressionOne = factory.createExpressionFromText(h.toString() + "f", null) + val literalExpressionTwo = factory.createExpressionFromText(s.toString() + "f", null) + val literalExpressionThree = factory.createExpressionFromText(v.toString() + "f", null) + + hExpr.sourcePsi?.replace(literalExpressionOne) + sExpr.sourcePsi?.replace(literalExpressionTwo) + vExpr.sourcePsi?.replace(literalExpressionThree) + } +} Index: src/main/kotlin/platform/adventure/color/AdventureColorUtil.kt =================================================================== --- src/main/kotlin/platform/adventure/color/AdventureColorUtil.kt (revision 44eca6da07dc05d86ef7b91a05ba1885c8415a4c) +++ src/main/kotlin/platform/adventure/color/AdventureColorUtil.kt (revision 1a088b94c292968b1b2e0f4324352013372be3a8) @@ -14,10 +14,78 @@ import com.demonwav.mcdev.platform.adventure.AdventureConstants import com.demonwav.mcdev.platform.adventure.AdventureModuleType import com.intellij.psi.PsiElement +import com.intellij.psi.PsiType import java.awt.Color +import kotlin.math.roundToInt +import org.jetbrains.uast.UCallExpression import org.jetbrains.uast.UElement +import org.jetbrains.uast.UExpression import org.jetbrains.uast.UIdentifier +import org.jetbrains.uast.UQualifiedReferenceExpression +import org.jetbrains.uast.getParentOfType import org.jetbrains.uast.toUElementOfType -fun PsiElement.findAdventureColor(): Pair? = - this.toUElementOfType()?.findColor(AdventureModuleType, AdventureConstants.TEXT_COLOR_CLASS, null) +fun PsiElement.findAdventureColor(): Pair? { + val identifier = this.toUElementOfType() + ?: return null + + if (identifier.name == "hsvLike") { + val call = identifier.getParentOfType() + ?: return null + + if (call.resolve()?.containingClass?.qualifiedName != "net.kyori.adventure.util.HSVLike") { + return null + } + + val params = call.valueArguments + val h = params.getOrNull(0)?.evaluate() as? Float ?: return null + val s = params.getOrNull(1)?.evaluate() as? Float ?: return null + val v = params.getOrNull(2)?.evaluate() as? Float ?: return null + return Color.getHSBColor(h, s, v) to call + } + + if (identifier.name == "lerp") { + val call = identifier.getParentOfType() + ?: return null + + if (call.resolve()?.containingClass?.qualifiedName != "net.kyori.adventure.text.format.TextColor") { + return null + } + + val params = call.valueArguments + val t = params.getOrNull(0)?.evaluate() as? Float ?: return null + val (a, _) = params.getOrNull(1)?.findAdventureColor() ?: return null + val (b, _) = params.getOrNull(2)?.findAdventureColor() ?: return null + val clampedT = t.coerceIn(0f, 1f) + @Suppress("UseJBColor") + val color = Color( + (a.red + clampedT * (b.red - a.red)).roundToInt(), + (a.green + clampedT * (b.green - a.green)).roundToInt(), + (a.blue + clampedT * (b.blue - a.blue)).roundToInt() + ) + return color to call + } + + if (identifier.name == "color") { + val call = identifier.getParentOfType() + ?: return null + + if (call.resolve()?.containingClass?.qualifiedName != "net.kyori.adventure.text.format.TextColor") { + return null + } + + val params = call.valueArguments + if (params.size == 1 && params[0].getExpressionType() != PsiType.INT) { + return params[0].findAdventureColor() + } + } + + return identifier.findColor(AdventureModuleType, AdventureConstants.TEXT_COLOR_CLASS, null) + ?: identifier.findColor(AdventureModuleType, "net.kyori.adventure.util.HSVLike", null) +} + +fun UExpression.findAdventureColor(): Pair? = when (this) { + is UCallExpression -> this.methodIdentifier?.sourcePsi?.findAdventureColor() + is UQualifiedReferenceExpression -> this.referenceNameElement?.sourcePsi?.findAdventureColor() + else -> null +}