diff --git a/buildSrc/src/main/kotlin/Versions.kt b/buildSrc/src/main/kotlin/Versions.kt index 48eee32eb..e10ffc923 100644 --- a/buildSrc/src/main/kotlin/Versions.kt +++ b/buildSrc/src/main/kotlin/Versions.kt @@ -19,6 +19,7 @@ object Versions { val core = project val console = project + val consoleIntellij = "$project-160-213-1" // mirai-kotlin-idea-patch val consoleTerminal = project const val kotlinCompiler = "1.6.0" @@ -59,7 +60,7 @@ object Versions { const val intellijGradlePlugin = "1.3.0" // const val kotlinIntellijPlugin = "211-1.5.20-release-284-IJ7442.40" // keep to newest as kotlinCompiler - const val intellij = "2021.3" // don't update easily unless you want your disk space -= 500MB + const val intellij = "2021.3.2" // don't update easily unless you want your disk space -= 500MB } diff --git a/mirai-console/tools/intellij-plugin/build.gradle.kts b/mirai-console/tools/intellij-plugin/build.gradle.kts index f6dedd2b0..dffc563fd 100644 --- a/mirai-console/tools/intellij-plugin/build.gradle.kts +++ b/mirai-console/tools/intellij-plugin/build.gradle.kts @@ -1,10 +1,10 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ @file:Suppress("UnusedImport") @@ -18,7 +18,8 @@ plugins { } repositories { - maven("https://maven.aliyun.com/repository/public") + maven("https://maven.aliyun.com/repository/public") // IntelliJ dependencies are very large (>500MB) + mavenCentral() } version = Versions.console @@ -42,12 +43,11 @@ intellij { ) } -afterEvaluate { - java { - sourceCompatibility = JavaVersion.VERSION_11 - targetCompatibility = JavaVersion.VERSION_11 - } +java { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 } + tasks.getByName("publishPlugin", org.jetbrains.intellij.tasks.PublishPluginTask::class) { val pluginKey = project.findProperty("jetbrains.hub.key")?.toString() if (pluginKey != null) { @@ -65,12 +65,14 @@ fun File.resolveMkdir(relative: String): File { kotlin.target.compilations.all { kotlinOptions { jvmTarget = "11" + apiVersion = "1.5" // bundled Kotlin is 1.5.10 } } +// https://plugins.jetbrains.com/docs/intellij/kotlin.html#kotlin-standard-library tasks.withType { - sinceBuild.set("201.*") - untilBuild.set("215.*") + sinceBuild.set("212.*") + untilBuild.set("225.*") pluginDescription.set( """ Plugin development support for Mirai Console @@ -91,12 +93,12 @@ tasks.withType { } dependencies { - api(`jetbrains-annotations`) - api(`kotlinx-coroutines-jdk8`) - api(`kotlinx-coroutines-swing`) - - api(project(":mirai-console-compiler-common")) - - implementation(`kotlin-stdlib-jdk8`) - implementation(`kotlin-reflect`) + implementation(project(":mirai-console-compiler-common")) { + exclude("org.jetbrains.kotlin", "kotlin-stdlib") + exclude("org.jetbrains.kotlin", "kotlin-stdlib-jdk7") + exclude("org.jetbrains.kotlin", "kotlin-stdlib-jdk8") + } +// implementation(project(":mirai-console-compiler-common")) { +// isTransitive = false +// } } diff --git a/mirai-console/tools/intellij-plugin/gradle.properties b/mirai-console/tools/intellij-plugin/gradle.properties new file mode 100644 index 000000000..0e2bba2de --- /dev/null +++ b/mirai-console/tools/intellij-plugin/gradle.properties @@ -0,0 +1,9 @@ +# +# Copyright 2019-2022 Mamoe Technologies and contributors. +# +# ???????? GNU AFFERO GENERAL PUBLIC LICENSE version 3 ??????, ?????????????. +# Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. +# +# https://github.com/mamoe/mirai/blob/dev/LICENSE +# +kotlin.stdlib.default.dependency=false \ No newline at end of file diff --git a/mirai-console/tools/intellij-plugin/src/creator/MiraiModuleBuilder.kt b/mirai-console/tools/intellij-plugin/src/creator/MiraiModuleBuilder.kt index 95ae3be64..dba528523 100644 --- a/mirai-console/tools/intellij-plugin/src/creator/MiraiModuleBuilder.kt +++ b/mirai-console/tools/intellij-plugin/src/creator/MiraiModuleBuilder.kt @@ -1,10 +1,10 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ @@ -26,9 +26,6 @@ import com.intellij.openapi.startup.StartupManager import com.intellij.openapi.util.io.FileUtil import com.intellij.openapi.vfs.LocalFileSystem import com.intellij.openapi.vfs.VirtualFile -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.SupervisorJob -import kotlinx.coroutines.cancel import net.mamoe.mirai.console.intellij.assets.Icons import net.mamoe.mirai.console.intellij.creator.steps.BuildSystemStep import net.mamoe.mirai.console.intellij.creator.steps.OptionsStep @@ -37,6 +34,7 @@ import net.mamoe.mirai.console.intellij.creator.tasks.CreateProjectTask import java.nio.file.Files import java.nio.file.Path import java.nio.file.Paths +import java.util.concurrent.Executors class MiraiModuleBuilder : JavaModuleBuilder() { override fun getPresentableName() = MiraiModuleType.NAME @@ -90,15 +88,18 @@ class MiraiModuleBuilder : JavaModuleBuilder() { val vFile = LocalFileSystem.getInstance().refreshAndFindFileByPath(pathName) ?: throw IllegalStateException("Failed to refresh and file file: $path") + + return path to vFile } - private val scope = CoroutineScope(SupervisorJob()) + // private val scope = CoroutineScope(SupervisorJob()) + private val scope = Executors.newFixedThreadPool(2) private val model = MiraiProjectModel.create(scope) override fun cleanup() { super.cleanup() - scope.cancel() + scope.shutdownNow() } override fun createWizardSteps( @@ -107,7 +108,7 @@ class MiraiModuleBuilder : JavaModuleBuilder() { ): Array { return arrayOf( BuildSystemStep(model), - PluginCoordinatesStep(model), + PluginCoordinatesStep(model, scope), ) } diff --git a/mirai-console/tools/intellij-plugin/src/creator/MiraiProjectModel.kt b/mirai-console/tools/intellij-plugin/src/creator/MiraiProjectModel.kt index 2d7564f43..9dc78f761 100644 --- a/mirai-console/tools/intellij-plugin/src/creator/MiraiProjectModel.kt +++ b/mirai-console/tools/intellij-plugin/src/creator/MiraiProjectModel.kt @@ -1,22 +1,21 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ package net.mamoe.mirai.console.intellij.creator -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Deferred -import net.mamoe.mirai.console.intellij.creator.MiraiVersionKind.Companion.getMiraiVersionListAsync import net.mamoe.mirai.console.intellij.creator.steps.BuildSystemType import net.mamoe.mirai.console.intellij.creator.steps.LanguageType import net.mamoe.mirai.console.intellij.creator.tasks.adjustToClassName import net.mamoe.mirai.console.intellij.creator.tasks.lateinitReadWriteProperty +import java.util.concurrent.CompletableFuture +import java.util.concurrent.ExecutorService import kotlin.contracts.contract data class ProjectCoordinates( @@ -54,7 +53,7 @@ class MiraiProjectModel private constructor() { var packageName: String by lateinitReadWriteProperty { projectCoordinates.checkNotNull("projectCoordinates").groupId } - var availableMiraiVersions: Deferred>? = null + var availableMiraiVersions: CompletableFuture>? = null val availableMiraiVersionsOrFail get() = availableMiraiVersions.checkNotNull("availableMiraiVersions") fun checkValuesNotNull() { @@ -64,11 +63,23 @@ class MiraiProjectModel private constructor() { } companion object { - fun create(scope: CoroutineScope): MiraiProjectModel { + fun create(scope: ExecutorService): MiraiProjectModel { return MiraiProjectModel().apply { - availableMiraiVersions = scope.getMiraiVersionListAsync() + availableMiraiVersions = scope.async { MiraiVersionKind.getMiraiVersionList() } } } + + fun ExecutorService.async(block: () -> T): CompletableFuture { + val future = CompletableFuture() + submit { + try { + future.complete(block()) + } catch (e: Throwable) { + future.completeExceptionally(e) + } + } + return future + } } } diff --git a/mirai-console/tools/intellij-plugin/src/creator/MiraiVersion.kt b/mirai-console/tools/intellij-plugin/src/creator/MiraiVersion.kt index 5f3421753..580dccaa0 100644 --- a/mirai-console/tools/intellij-plugin/src/creator/MiraiVersion.kt +++ b/mirai-console/tools/intellij-plugin/src/creator/MiraiVersion.kt @@ -1,18 +1,20 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ package net.mamoe.mirai.console.intellij.creator -import kotlinx.coroutines.* +import com.intellij.openapi.diagnostic.Logger +import com.intellij.util.text.SemVer import org.jsoup.Jsoup import org.jsoup.nodes.Document +import java.io.IOException typealias MiraiVersion = String @@ -22,7 +24,7 @@ enum class MiraiVersionKind { }, Prerelease { override fun isThatKind(version: String): Boolean = - !version.contains("-dev") // && (version.contains("-M") || version.contains("-RC")) + version.contains("-M") || version.contains("-RC") }, Nightly { override fun isThatKind(version: String): Boolean = true // version.contains("-dev") @@ -34,8 +36,10 @@ enum class MiraiVersionKind { val DEFAULT = Stable private val REGEX_STABLE = Regex("""^\d+\.\d+(?:\.\d+)?$""") + private val LOG = Logger.getInstance(MiraiVersionKind::class.java) - private suspend fun getMiraiVersionList(): Set { + @Throws(IOException::class) + fun getMiraiVersionList(): Set { fun download(url: String): Document { return Jsoup.connect(url) .followRedirects(true) @@ -44,29 +48,87 @@ enum class MiraiVersionKind { .get() } - val document = runInterruptible { - // https://maven.aliyun.com/repository/central/net/mamoe/mirai-core/maven-metadata.xml - // https://repo.maven.apache.org/maven2/net/mamoe/mirai-core/maven-metadata.xml - kotlin.runCatching { - download("https://maven.aliyun.com/repository/central/net/mamoe/mirai-core/maven-metadata.xml") - }.recoverCatching { - download("https://repo.maven.apache.org/maven2/net/mamoe/mirai-core/maven-metadata.xml") - }.getOrThrow() - } + return kotlin.runCatching { + download("https://maven.aliyun.com/repository/central/net/mamoe/mirai-core/maven-metadata.xml") + }.recoverCatching { + download("https://repo.maven.apache.org/maven2/net/mamoe/mirai-core/maven-metadata.xml") + }.map { document -> + val xml = document.toString() - val xml = document.toString() - - return Regex("""\s*(.*?)\s*""").findAll(xml).mapNotNull { it.groupValues[1] }.toSet() + Regex("""\s*(.*?)\s*""") + .findAll(xml) + .mapNotNull { it.groupValues.getOrNull(1) } + .sortVersionsDescending() + .toSet() + }.getOrThrow() } - fun CoroutineScope.getMiraiVersionListAsync(): Deferred> { - return async(CoroutineName("getMiraiVersionListAsync")) { - getMiraiVersionList() - } - } + // Kotlin version: not working because + // Caused by: java.util.ServiceConfigurationError: kotlinx.coroutines.CoroutineExceptionHandler: com.intellij.openapi.application.impl.CoroutineExceptionHandlerImpl not a subtype +// +// private suspend fun getMiraiVersionList(): Set { +// suspend fun download(url: String): Document { +// return Jsoup.connect(url) +// .followRedirects(true) +// .ignoreContentType(true) +// .ignoreHttpErrors(true) +// .run { runInterruptible(Dispatchers.IO) { get() } } +// } +// +// val document = supervisorScope { +// val jobs = mutableListOf>() +// jobs += async { +// download("https://maven.aliyun.com/repository/central/net/mamoe/mirai-core/maven-metadata.xml") +// } +// jobs += async { +// download("https://repo.maven.apache.org/maven2/net/mamoe/mirai-core/maven-metadata.xml") +// } +// val timeout = launch { +// delay(10_000) +// } +// // select the faster one +// select { +// jobs.forEach { job -> job.onAwait { it } } +// timeout.onJoin { +// throw IllegalStateException("Timeout getMiraiVersionList").apply { +// jobs.forEach { +// if (it.isCompleted) { +// try { +// it.await() +// } catch (e: Throwable) { +// addSuppressed(e) +// } +// } +// } +// } +// } +// } +// jobs.forEach { it.cancel() } +// timeout.cancel() +// } +// +// val xml = document.toString() +// +// return Regex("""\s*(.*?)\s*""").findAll(xml).mapNotNull { it.groupValues.getOrNull(1) }.toSet() +// } + +// fun CoroutineScope.getMiraiVersionListAsync(): Deferred> { +// return async(CoroutineName("getMiraiVersionListAsync")) { +// getMiraiVersionList() +// } +// } } } +internal fun Sequence.sortVersionsDescending(): Sequence { + return this + .mapNotNull { SemVer.parseFromText(it) } + .sortedWith { o1, o2 -> + o2.compareTo(o1) + } + .map { it.toString() } +} + /* diff --git a/mirai-console/tools/intellij-plugin/src/creator/steps/PluginCoordinatesStep.kt b/mirai-console/tools/intellij-plugin/src/creator/steps/PluginCoordinatesStep.kt index 0462ef0bd..b7081f0e8 100644 --- a/mirai-console/tools/intellij-plugin/src/creator/steps/PluginCoordinatesStep.kt +++ b/mirai-console/tools/intellij-plugin/src/creator/steps/PluginCoordinatesStep.kt @@ -1,17 +1,18 @@ /* - * Copyright 2019-2021 Mamoe Technologies and contributors. + * Copyright 2019-2022 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/dev/LICENSE */ package net.mamoe.mirai.console.intellij.creator.steps import com.intellij.ide.util.projectWizard.ModuleWizardStep -import kotlinx.coroutines.* +import com.intellij.openapi.diagnostic.Logger +import com.intellij.vcs.log.submitSafe import net.mamoe.mirai.console.compiler.common.CheckerConstants.PLUGIN_ID_PATTERN import net.mamoe.mirai.console.intellij.creator.MiraiProjectModel import net.mamoe.mirai.console.intellij.creator.MiraiVersionKind @@ -24,10 +25,12 @@ import net.mamoe.mirai.console.intellij.creator.tasks.adjustToClassName import net.mamoe.mirai.console.intellij.diagnostics.ContextualParametersChecker import java.awt.event.ItemEvent import java.awt.event.ItemListener +import java.util.concurrent.ExecutorService import javax.swing.* class PluginCoordinatesStep( - private val model: MiraiProjectModel + private val model: MiraiProjectModel, + private val scope: ExecutorService, ) : ModuleWizardStep() { private lateinit var panel: JPanel @@ -54,7 +57,7 @@ class PluginCoordinatesStep( private val versionKindChangeListener: ItemListener = ItemListener { event -> if (event.stateChange != ItemEvent.SELECTED) return@ItemListener - updateVersionItems() + updateVersionItemsAsync() } override fun getPreferredFocusedComponent(): JComponent = idField @@ -70,8 +73,8 @@ class PluginCoordinatesStep( miraiVersionBox.addItem(VERSION_LOADING_PLACEHOLDER) miraiVersionBox.selectedItem = VERSION_LOADING_PLACEHOLDER - model.availableMiraiVersionsOrFail.invokeOnCompletion { - updateVersionItems() + model.availableMiraiVersionsOrFail.whenComplete { _, _ -> + updateVersionItemsAsync() } if (idField.text.isNullOrEmpty()) { @@ -87,16 +90,15 @@ class PluginCoordinatesStep( } } - @OptIn(DelicateCoroutinesApi::class) - private fun updateVersionItems() { - GlobalScope.launch(Dispatchers.Main + CoroutineName("updateVersionItems")) { - if (!model.availableMiraiVersionsOrFail.isCompleted) return@launch + private fun updateVersionItemsAsync() { + scope.submitSafe(LOG) { + if (!model.availableMiraiVersionsOrFail.isDone) return@submitSafe miraiVersionBox.removeAllItems() val expectingKind = miraiVersionKindBox.selectedItem as? MiraiVersionKind ?: MiraiVersionKind.DEFAULT - kotlin.runCatching { model.availableMiraiVersionsOrFail.await() } + kotlin.runCatching { model.availableMiraiVersionsOrFail.join() } .fold( onSuccess = { versions -> - versions.sortedDescending() + versions .filter { v -> expectingKind.isThatKind(v) } .forEach { v -> miraiVersionBox.addItem(v) } }, @@ -141,5 +143,6 @@ class PluginCoordinatesStep( companion object { const val VERSION_LOADING_PLACEHOLDER = "Loading..." + private val LOG = Logger.getInstance(PluginCoordinatesStep::class.java) } } diff --git a/mirai-console/tools/intellij-plugin/test/creator/MiraiVersionKindTest.kt b/mirai-console/tools/intellij-plugin/test/creator/MiraiVersionKindTest.kt new file mode 100644 index 000000000..540f1dc1d --- /dev/null +++ b/mirai-console/tools/intellij-plugin/test/creator/MiraiVersionKindTest.kt @@ -0,0 +1,36 @@ +/* + * Copyright 2019-2022 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/dev/LICENSE + */ +package creator + +import net.mamoe.mirai.console.intellij.creator.sortVersionsDescending +import org.junit.jupiter.api.Test +import kotlin.test.assertEquals + +class MiraiVersionKindTest { + + @Test + fun sortVersions() { + assertEquals( + "2.10.0, 2.10.0-RC, 2.10.0-M1, 2.9.0, 2.9.0-RC, 2.9.0-M2, 2.9.0-M1, 2.7.0, 2.7.0-RC" + .split(",") + .map { it.trim() }, + sequenceOf( + "2.9.0", + "2.9.0-M1", + "2.9.0-M2", + "2.9.0-RC", + "2.7.0", + "2.7.0-RC", + "2.10.0", + "2.10.0-RC", + "2.10.0-M1" + ).sortVersionsDescending().toList() + ) + } +} \ No newline at end of file