Skip to content

Commit

Permalink
Merge pull request #778 from doki-theme/2024.2
Browse files Browse the repository at this point in the history
Added 2024.2 Build & JetBrains Marketplace Support
  • Loading branch information
Unthrottled authored Jul 9, 2024
2 parents b954dab + 1e7984d commit ab9fa45
Show file tree
Hide file tree
Showing 13 changed files with 150 additions and 66 deletions.
5 changes: 5 additions & 0 deletions changelog/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
Changelog
---
# 88.5-1.14.0 [Marketplace Support]

- Added initial 2024.2 build support
- Should be able to be published to marketplace now.

# 88.5-1.13.0 [2024.1 Build Support]

- Added initial 2024.1 build support
Expand Down
3 changes: 2 additions & 1 deletion changelog/RELEASE-NOTES.md
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
- Added initial 2024.1 build support
- Added initial 2024.2 build support
- Should be able to be published to marketplace now.
8 changes: 4 additions & 4 deletions gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@
# -> https://www.jetbrains.org/intellij/sdk/docs/reference_guide/intellij_artifacts.html

pluginGroup=io.unthrottled
pluginVersion=88.5-1.13.0
pluginSinceBuild=233.8264.8
pluginUntilBuild=241.*
pluginVersion=88.5-1.14.0
pluginSinceBuild=241
pluginUntilBuild=242.*

# Plugin Verifier integration -> https://github.com/JetBrains/gradle-intellij-plugin#plugin-verifier-dsl
# See https://jb.gg/intellij-platform-builds-list for available build versions.
pluginVerifierIdeVersions = 2023.3.2
pluginVerifierIdeVersions = 2024.1, 2024.2

platformType = IU
platformVersion = 2023.3.2
Expand Down
5 changes: 2 additions & 3 deletions src/main/kotlin/io/unthrottled/doki/TheDokiTheme.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import com.intellij.openapi.application.ApplicationActivationListener
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.extensions.PluginId
import com.intellij.openapi.project.Project
import com.intellij.openapi.startup.StartupManager
import com.intellij.openapi.wm.IdeFrame
import io.unthrottled.doki.config.ThemeConfig
import io.unthrottled.doki.hax.HackComponent.hackLAF
Expand Down Expand Up @@ -114,7 +113,7 @@ class TheDokiTheme : Disposable {
LegacyMigration.newVersionMigration(project)
ThemeConfig.instance.version = version
ThemeManager.instance.currentTheme.ifPresent {
StartupManager.getInstance(project).runAfterOpened {
ApplicationManager.getApplication().invokeLater {
UpdateNotification.display(
project,
version,
Expand All @@ -124,7 +123,7 @@ class TheDokiTheme : Disposable {
}
}

StartupManager.getInstance(project).runAfterOpened {
ApplicationManager.getApplication().invokeLater {
PromotionManager.registerPromotion(version)
}
}
Expand Down
78 changes: 51 additions & 27 deletions src/main/kotlin/io/unthrottled/doki/hax/SvgLoaderHacker.kt
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
package io.unthrottled.doki.hax

import com.intellij.ui.svg.setSelectionColorPatcherProvider
import com.intellij.util.SVGLoader
import io.unthrottled.doki.icon.ColorPatcher
import io.unthrottled.doki.service.PluginService
import io.unthrottled.doki.themes.DokiTheme
import java.util.Optional
import io.unthrottled.doki.util.Logging
import io.unthrottled.doki.util.logger
import io.unthrottled.doki.util.runSafely
import io.unthrottled.doki.util.runSafelyWithResult
import java.lang.reflect.InvocationHandler
import java.lang.reflect.Method
import java.lang.reflect.Proxy

typealias PatcherProvider = SVGLoader.SvgElementColorPatcherProvider

object SvgLoaderHacker {
object SvgLoaderHacker : Logging {
/**
* Enables the ability to have more than one color patcher.
*/
Expand All @@ -18,30 +20,52 @@ object SvgLoaderHacker {
return
}

collectOtherPatcher()
.ifPresent { otherPatcher ->
ColorPatcher.setOtherPatcher(otherPatcher)
}

ColorPatcher.setDokiTheme(dokiTheme)

SVGLoader.colorPatcherProvider = ColorPatcher
setSelectionColorPatcherProvider(ColorPatcher)
runSafely({
setHackedPatcher()
}) {
logger().warn("Unable to set hacked patcher", it)
}
}

private fun collectOtherPatcher(): Optional<PatcherProvider> =
Optional.ofNullable(
SVGLoader::class.java.declaredFields
.firstOrNull { it.name == "colorPatcherProvider" },
)
.map { ourColorPatcherField ->
ourColorPatcherField.isAccessible = true
ourColorPatcherField.get(null)
}
.filter { it is PatcherProvider }
.filter { it !is ColorPatcher }
.map {
val otherPatcher = it as PatcherProvider
otherPatcher
private fun setHackedPatcher() {
val patcherProxyHandler =
object : InvocationHandler, Logging {
val associatedMethods = ColorPatcher.javaClass.methods.associateBy { it.name }

override fun invoke(
proxy: Any?,
method: Method?,
arguments: Array<out Any>?,
): Any? {
if (method == null) return null
return runSafelyWithResult({
val methodToInvoke = associatedMethods[method.name]
val usableArguments = arguments ?: emptyArray()
methodToInvoke?.invoke(
ColorPatcher,
*usableArguments,
)
}) {
logger().warn("unable to invoke proxy handler method", it)
null
}
}
}
val patcherProviderClass = Class.forName("com.intellij.util.SVGLoader\$SvgElementColorPatcherProvider")
val proxiedSVGElementColorProvider =
Proxy.newProxyInstance(
patcherProviderClass.classLoader,
arrayOf(patcherProviderClass),
patcherProxyHandler,
)
val svgLoaderClass = Class.forName("com.intellij.util.SVGLoader")
val setPatcher = svgLoaderClass.declaredMethods.firstOrNull { it.name == "setColorPatcherProvider" }
setPatcher?.invoke(null, proxiedSVGElementColorProvider)

val clazz = Class.forName("com.intellij.ui.svg.SvgKt")
val setPatcherProvider = clazz.declaredMethods.firstOrNull { it.name == "setSelectionColorPatcherProvider" }
setPatcherProvider?.invoke(null, proxiedSVGElementColorProvider)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package io.unthrottled.doki.hax.svg

import com.intellij.ui.svg.SvgAttributePatcher

interface SvgElementColorPatcherProvider {
fun attributeForPath(path: String): SvgAttributePatcher? = null

/**
* Returns a digest of the current SVG color patcher.
*
* Consider using a two-element array, where the first element is a hash of the input data for the patcher,
* and the second is an ID of the patcher (see [com.intellij.ui.icons.ColorPatcherIdGenerator]).
*/
fun digest(): LongArray
}
64 changes: 50 additions & 14 deletions src/main/kotlin/io/unthrottled/doki/icon/ColorPatcher.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,17 @@ package io.unthrottled.doki.icon
import com.google.common.cache.Cache
import com.google.common.cache.CacheBuilder
import com.intellij.ide.ui.LafManager
import com.intellij.ide.ui.laf.UIThemeLookAndFeelInfoImpl
import com.intellij.ide.ui.UITheme
import com.intellij.ui.ColorUtil
import com.intellij.ui.JBColor
import com.intellij.ui.JBColor.namedColor
import com.intellij.ui.svg.SvgAttributePatcher
import com.intellij.util.io.DigestUtil
import io.unthrottled.doki.hax.PatcherProvider
import io.unthrottled.doki.hax.svg.SvgElementColorPatcherProvider
import io.unthrottled.doki.themes.DokiTheme
import io.unthrottled.doki.themes.ThemeManager
import io.unthrottled.doki.util.Logging
import io.unthrottled.doki.util.logger
import io.unthrottled.doki.util.runSafely
import io.unthrottled.doki.util.runSafelyWithResult
import io.unthrottled.doki.util.toHexString
Expand All @@ -25,7 +27,7 @@ object NoOptPatcher : SvgAttributePatcher {
}

val noOptPatcherProvider =
object : PatcherProvider {
object : SvgElementColorPatcherProvider {
val longArray = longArrayOf(0)

override fun digest(): LongArray {
Expand All @@ -34,9 +36,9 @@ val noOptPatcherProvider =
}

@Suppress("TooManyFunctions")
object ColorPatcher : PatcherProvider {
private var otherColorPatcherProvider: PatcherProvider = noOptPatcherProvider
private var uiColorPatcherProvider: PatcherProvider = noOptPatcherProvider
object ColorPatcher : SvgElementColorPatcherProvider {
private var otherColorPatcherProvider: SvgElementColorPatcherProvider = noOptPatcherProvider
private var uiColorPatcherProvider: SvgElementColorPatcherProvider = noOptPatcherProvider
private lateinit var dokiTheme: DokiTheme
private val patcherProviderCache = HashSet<String>()

Expand All @@ -45,16 +47,50 @@ object ColorPatcher : PatcherProvider {
calculateAndSetNewDigest()
LafManager.getInstance()
?.currentUIThemeLookAndFeel.toOptional()
.filter { it is UIThemeLookAndFeelInfoImpl }
.map { it as UIThemeLookAndFeelInfoImpl }
.ifPresent {
this.uiColorPatcherProvider = it.theme.colorPatcher ?: noOptPatcherProvider
.map {
it to it.javaClass.methods.firstOrNull { method -> method.name == "getTheme" }
}
clearCaches()
}
.filter { it.second != null }
.ifPresent {
val theme = ((it.second?.invoke(it.first)) as UITheme)
val themeClass = UITheme::class.java
val themeClassMethods = themeClass.methods
val attr = themeClassMethods.firstOrNull { method -> method.name == "attributeForPath" }
val digest = themeClassMethods.firstOrNull { method -> method.name == "digest" }
this.uiColorPatcherProvider =
object : SvgElementColorPatcherProvider, Logging {
override fun attributeForPath(path: String): SvgAttributePatcher? {
return runSafelyWithResult({
val patcherForPath = attr?.invoke(digest, path)
val patchColorsMethod =
patcherForPath?.javaClass
?.methods?.firstOrNull { method -> method.name == "patchColors" }
object : SvgAttributePatcher {
override fun patchColors(attributes: MutableMap<String, String>) {
runSafelyWithResult({
patchColorsMethod
?.invoke(patcherForPath, attributes)
}) { patchingError ->
logger().warn("unable to patch colors", patchingError)
}
}
}
}) {
logger().warn("Unable to patch path for raisins", it)
null
}
}

fun setOtherPatcher(otherPatcher: PatcherProvider) {
this.otherColorPatcherProvider = otherPatcher
override fun digest(): LongArray {
return runSafelyWithResult({
digest?.invoke(theme) as LongArray
}) { digestError ->
logger().warn("Unable to get digest", digestError)
longArrayOf()
}
}
}
}
clearCaches()
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ package io.unthrottled.doki.integrations
import com.google.gson.Gson
import com.intellij.ide.IdeBundle
import com.intellij.ide.ui.LafManager
import com.intellij.openapi.application.ApplicationInfo
import com.intellij.openapi.application.ApplicationNamesInfo
import com.intellij.openapi.application.ex.ApplicationInfoEx
import com.intellij.openapi.application.impl.ApplicationInfoImpl
import com.intellij.openapi.diagnostic.ErrorReportSubmitter
import com.intellij.openapi.diagnostic.IdeaLoggingEvent
import com.intellij.openapi.diagnostic.SubmittedReportInfo
Expand Down Expand Up @@ -120,7 +120,7 @@ class ErrorReporter : ErrorReportSubmitter() {
ManagementFactory.getGarbageCollectorMXBeans().stream()
.map { it.name }.collect(Collectors.joining(","))

private fun getBuildInfo(appInfo: ApplicationInfoImpl): String {
private fun getBuildInfo(appInfo: ApplicationInfo): String {
var buildInfo = IdeBundle.message("about.box.build.number", appInfo.build.asString())
val cal = appInfo.buildDate
var buildDate = ""
Expand All @@ -132,8 +132,8 @@ class ErrorReporter : ErrorReportSubmitter() {
return buildInfo
}

private fun getAppName(): Pair<ApplicationInfoImpl, String> {
val appInfo = ApplicationInfoEx.getInstanceEx() as ApplicationInfoImpl
private fun getAppName(): Pair<ApplicationInfo, String> {
val appInfo = ApplicationInfoEx.getInstanceEx() as ApplicationInfo
var appName = appInfo.fullApplicationName
val edition = ApplicationNamesInfo.getInstance().editionName
if (edition != null) appName += " ($edition)"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ object RestTools {

fun <T> performRequest(
url: String,
bodyExtractor: (InputStream) -> T,
): Optional<T> {
bodyExtractor: (InputStream) -> T & Any,
): Optional<T & Any> {
log.info("Attempting to download remote asset: $url")
return runSafelyWithResult({
HttpRequests.request(url)
Expand All @@ -46,7 +46,7 @@ object RestTools {
body.toOptional()
}) {
log.warn("Unable to get remote asset: $url for raisins", it)
Optional.empty<T>()
Optional.empty<T & Any>()
}
}
}) {
Expand Down
5 changes: 2 additions & 3 deletions src/main/kotlin/io/unthrottled/doki/legacy/EXPUIFixer.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import com.intellij.ide.ui.LafManager
import com.intellij.ide.ui.LafManagerListener
import com.intellij.openapi.Disposable
import com.intellij.openapi.application.ApplicationManager
import com.intellij.ui.ExperimentalUI
import com.intellij.ui.NewUI
import com.intellij.util.ui.JBUI
import io.unthrottled.doki.themes.ThemeManager
import javax.swing.UIDefaults
Expand All @@ -17,9 +17,8 @@ object EXPUIFixer : LafManagerListener, Disposable {
connection.subscribe(LafManagerListener.TOPIC, this)
}

@Suppress("UnstableApiUsage")
fun fixExperimentalUI() {
if (!ExperimentalUI.isNewUI()) return
if (!NewUI.isEnabled()) return

overrideSetProperties(0)
}
Expand Down
14 changes: 10 additions & 4 deletions src/main/kotlin/io/unthrottled/doki/service/PluginService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,16 @@ import com.fasterxml.jackson.annotation.JsonProperty
import com.fasterxml.jackson.core.type.TypeReference
import com.fasterxml.jackson.databind.ObjectMapper
import com.intellij.ide.plugins.PluginManagerCore
import com.intellij.openapi.application.ApplicationInfo
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.application.impl.ApplicationInfoImpl
import com.intellij.openapi.application.ex.ApplicationInfoEx
import com.intellij.openapi.extensions.PluginId
import com.intellij.util.Urls
import com.intellij.util.io.HttpRequests
import io.unthrottled.doki.util.Logging
import io.unthrottled.doki.util.logger
import io.unthrottled.doki.util.runSafelyWithResult
import io.unthrottled.doki.util.toOptional
import java.util.Collections
import java.util.concurrent.Callable

Expand Down Expand Up @@ -45,8 +47,12 @@ object PluginService : Logging {
)

private val PLUGIN_MANAGER_URL by lazy {
ApplicationInfoImpl.getInstanceEx()
.pluginManagerUrl
ApplicationInfo.getInstance()
.toOptional()
.filter { it is ApplicationInfoEx }
.map { it as ApplicationInfoEx }
.map { it.pluginManagerUrl }
.orElseGet { "https://plugins.jetbrains.com" }
.trimEnd('/')
}
private val COMPATIBLE_UPDATE_URL by lazy { "$PLUGIN_MANAGER_URL/api/search/compatibleUpdates" }
Expand Down Expand Up @@ -81,7 +87,7 @@ object PluginService : Logging {
val data =
objectMapper.writeValueAsString(
CompatibleUpdateRequest(
ApplicationInfoImpl.getInstanceEx()
ApplicationInfo.getInstance()
.build.asString(),
ids.map { it.idString },
),
Expand Down
4 changes: 2 additions & 2 deletions src/main/kotlin/io/unthrottled/doki/stickers/MarginService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ class MarginService : Logging {
private const val STICKER_Y_OFFSET = 0.05
private const val STICKER_X_OFFSET = 0.03

val instance: MarginService =
ApplicationManager.getApplication().getService(MarginService::class.java)
val instance: MarginService
get() = ApplicationManager.getApplication().getService(MarginService::class.java)
}

private val gson =
Expand Down
Loading

0 comments on commit ab9fa45

Please sign in to comment.