New Project Wizard for IDEA 221 (#1979)

* NewProjectWizard

* dependencies and validations

* Implement checkers

* Implement project generator

* Add new template RunTerminal.run.xml for Run Configuration

* Set supported IDEA version to 221

* Fix localization

* Fix since-until

* Rearrange files
This commit is contained in:
Him188 2022-04-20 17:03:10 +01:00 committed by GitHub
parent 15133c7902
commit 481973d5e6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
70 changed files with 1052 additions and 1446 deletions

View File

@ -19,14 +19,14 @@ object Versions {
val core = project
val console = project
val consoleIntellij = "213-$project-160-1" // idea-mirai-kotlin-patch
val consoleIntellij = "221-$project-162-1" // idea-mirai-kotlin-patch
val consoleTerminal = project
const val kotlinCompiler = "1.6.0"
const val kotlinStdlib = kotlinCompiler
const val dokka = "1.6.0"
const val kotlinCompilerForIdeaPlugin = "1.6.20-M1"
const val kotlinCompilerForIdeaPlugin = "1.6.20"
const val coroutines = "1.6.0"
const val atomicFU = "0.17.0"
@ -61,10 +61,10 @@ object Versions {
// If you the versions below, you need to sync changes to mirai-console/buildSrc/src/main/kotlin/Versions.kt
const val yamlkt = "0.10.2"
const val intellijGradlePlugin = "1.3.0"
const val intellijGradlePlugin = "1.5.3"
// const val kotlinIntellijPlugin = "211-1.5.20-release-284-IJ7442.40" // keep to newest as kotlinCompiler
const val intellij = "2021.3.2" // don't update easily unless you want your disk space -= 500MB
const val intellij = "2022.1" // don't update easily unless you want your disk space -= 500MB
}

View File

@ -71,8 +71,8 @@ kotlin.target.compilations.all {
// https://plugins.jetbrains.com/docs/intellij/kotlin.html#kotlin-standard-library
tasks.withType<org.jetbrains.intellij.tasks.PatchPluginXmlTask> {
sinceBuild.set("212.*")
untilBuild.set("225.*")
sinceBuild.set("221.0")
untilBuild.set("221.999999")
pluginDescription.set(
"""
Plugin development support for <a href='https://github.com/mamoe/mirai'>Mirai Console</a>

View File

@ -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
-->
<idea-plugin>
@ -25,9 +25,9 @@
<depends>com.intellij.gradle</depends>
<extensions defaultExtensionNs="com.intellij">
<moduleType id="MIRAI_MODULE_TYPE"
implementationClass="net.mamoe.mirai.console.intellij.creator.MiraiModuleType"/>
<moduleBuilder id="MIRAI_MODULE" builderClass="net.mamoe.mirai.console.intellij.creator.MiraiModuleBuilder"/>
<moduleType id="MIRAI_CONSOLE_PLUGIN_MODULE"
implementationClass="net.mamoe.mirai.console.intellij.wizard.MiraiModuleType"/>
<moduleBuilder id="MIRAI_MODULE" builderClass="net.mamoe.mirai.console.intellij.wizard.MiraiModuleBuilder"/>
<fileTemplateGroup implementation="net.mamoe.mirai.console.intellij.assets.FileTemplateRegistrar"/>
<codeInsight.lineMarkerProvider language="JAVA"

View File

@ -0,0 +1,14 @@
<!--
~ 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
-->
<html>
<body>
<p>This is a built-in file template used to create a new .gitignore for Gradle projects.</p>
</body>
</html>

View File

@ -0,0 +1,14 @@
<!--
~ 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
-->
<html>
<body>
<p>This is a built-in file template used to create a new gradle.properties for Mirai Console Plugin projects.</p>
</body>
</html>

View File

@ -0,0 +1,14 @@
<!--
~ 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
-->
<html>
<body>
<p>This is a built-in file template used to create a new build.gradle for Mirai Console Plugin projects.</p>
</body>
</html>

View File

@ -0,0 +1,14 @@
<!--
~ 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
-->
<html>
<body>
<p>This is a built-in file template used to create a new build.gradle.kts for Mirai Console Plugin projects.</p>
</body>
</html>

View File

@ -0,0 +1,14 @@
<!--
~ 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
-->
<html>
<body>
<p>This is a built-in file template used to create a new plugin main class for Mirai Console Plugin projects.</p>
</body>
</html>

View File

@ -0,0 +1,14 @@
<!--
~ 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
-->
<html>
<body>
<p>This is a built-in file template used to create a new plugin main class for Mirai Console Plugin projects.</p>
</body>
</html>

View File

@ -0,0 +1 @@
${PACKAGE_NAME}.${CLASS_NAME}

View File

@ -0,0 +1,14 @@
<!--
~ 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
-->
<html>
<body>
<p>This is a built-in file template used to create a new plugin main class for Mirai Console Plugin projects.</p>
</body>
</html>

View File

@ -0,0 +1,14 @@
<!--
~ 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
-->
<html>
<body>
<p>This is a built-in file template used to create a new settings.gradle.kts for Mirai Console Plugin projects.</p>
</body>
</html>

View File

@ -1,20 +1,14 @@
package $PACKAGE_NAME
import java.io.File
import net.mamoe.mirai.alsoLogin
import net.mamoe.mirai.console.MiraiConsole
import net.mamoe.mirai.console.plugin.PluginManager.INSTANCE.enable
import net.mamoe.mirai.console.plugin.PluginManager.INSTANCE.load
import net.mamoe.mirai.console.terminal.MiraiConsoleTerminalLoader
fun setupWorkingDir() {
// see: net.mamoe.mirai.console.terminal.MiraiConsoleImplementationTerminal
System.setProperty("user.dir", File("debug-sandbox").absolutePath)
}
import java.io.File
import java.util.Properties
suspend fun main() {
setupWorkingDir()
MiraiConsoleTerminalLoader.startAsDaemon()
val pluginInstance = ${CLASS_NAME}#if (${LANGUAGE_TYPE} != "Kotlin").INSTANCE #end
@ -23,7 +17,9 @@ suspend fun main() {
pluginInstance.load() // 主动加载插件, Console 会调用 ${CLASS_NAME}.onLoad
pluginInstance.enable() // 主动启用插件, Console 会调用 ${CLASS_NAME}.onEnable
val bot = MiraiConsole.addBot(123456, "").alsoLogin() // 登录一个测试环境的 Bot
val properties = Properties().apply { File("account.properties").inputStream().use { load(it) } }
val bot = MiraiConsole.addBot(properties.getProperty("id").toLong(), properties.getProperty("password")).alsoLogin() // 登录一个测试环境的 Bot
MiraiConsole.job.join()
}

View File

@ -0,0 +1,14 @@
<!--
~ 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
-->
<html>
<body>
<p>This is a built-in file template used to create a new settings.gradle.kts for Mirai Console Plugin projects.</p>
</body>
</html>

View File

@ -0,0 +1,11 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="RunTerminal" type="JetRunConfigurationType" nameIsGenerated="false">
<option name="MAIN_CLASS_NAME" value="${PACKAGE_NAME}.RunTerminalKt"/>
<module name="${MODULE_NAME}.test"/>
<shortenClasspath name="NONE"/>
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/debug-sandbox"/>
<method v="2">
<option name="Make" enabled="true"/>
</method>
</configuration>
</component>

View File

@ -0,0 +1,14 @@
<!--
~ 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
-->
<html>
<body>
<p>This is a built-in file template used to create a new settings.gradle.kts for Mirai Console Plugin projects.</p>
</body>
</html>

View File

@ -0,0 +1,2 @@
id=123456
password=pwd

View File

@ -0,0 +1,14 @@
<!--
~ 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
-->
<html>
<body>
<p>This is a built-in file template used to create a new gradle.properties for Mirai Console Plugin projects.</p>
</body>
</html>

View File

@ -1,14 +0,0 @@
<!--
~ Copyright 2019-2021 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/master/LICENSE
-->
<html>
<body>
<p>This is a built-in file template used to create a new .gitignore for Gradle projects.</p>
</body>
</html>

View File

@ -1,14 +0,0 @@
<!--
~ Copyright 2019-2021 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/master/LICENSE
-->
<html>
<body>
<p>This is a built-in file template used to create a new gradle.properties for Mirai Console Plugin projects.</p>
</body>
</html>

View File

@ -1,14 +0,0 @@
<!--
~ Copyright 2019-2021 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/master/LICENSE
-->
<html>
<body>
<p>This is a built-in file template used to create a new build.gradle for Mirai Console Plugin projects.</p>
</body>
</html>

View File

@ -1,14 +0,0 @@
<!--
~ Copyright 2019-2021 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/master/LICENSE
-->
<html>
<body>
<p>This is a built-in file template used to create a new build.gradle.kts for Mirai Console Plugin projects.</p>
</body>
</html>

View File

@ -1,14 +0,0 @@
<!--
~ Copyright 2019-2021 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/master/LICENSE
-->
<html>
<body>
<p>This is a built-in file template used to create a new plugin main class for Mirai Console Plugin projects.</p>
</body>
</html>

View File

@ -1,14 +0,0 @@
<!--
~ Copyright 2019-2021 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/master/LICENSE
-->
<html>
<body>
<p>This is a built-in file template used to create a new plugin main class for Mirai Console Plugin projects.</p>
</body>
</html>

View File

@ -1,14 +0,0 @@
<!--
~ Copyright 2019-2021 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/master/LICENSE
-->
<html>
<body>
<p>This is a built-in file template used to create a new settings.gradle.kts for Mirai Console Plugin projects.</p>
</body>
</html>

View File

@ -1,14 +0,0 @@
<!--
~ Copyright 2019-2021 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/master/LICENSE
-->
<html>
<body>
<p>This is a built-in file template used to create a new settings.gradle.kts for Mirai Console Plugin projects.</p>
</body>
</html>

View File

@ -0,0 +1,39 @@
#
# 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
#
module.presentation.name=Mirai Console Plugin
module.description="Template for building plugins for <b>Mirai Console</b>"
title.plugin.description=Plugin Description
label.plugin.version=Plugin Version:
label.version.stable=Stable
label.version.prerelease=Prerelease
label.version.nightly=Nightly
label.plugin.id=Plugin ID:
label.plugin.name=Plugin Name:
label.mirai.version.loading=Loading...
label.mirai.version=Mirai Version:
error.failed.to.download.mirai.version=Failed to download version list, please select a version manually
error.please.wait.for.mirai.version=Please wait for downloading version list
label.plugin.author=Plugin Author:
comment.plugin.id=Should consist of English characters and/or numbers, '.', ':', '-'. Example: "net.mamoe:chat-command".
comment.plugin.name=Example: "Chat Command".
comment.plugin.version=Should comply <a href="https://semver.org/">Semantic Versioning</a>, not including 'v'. Example: "0.1.0".
comment.mirai.version=Minimum version of Mirai Console this plugin requires on.
label.plugin.dependencies=Plugin Dependencies:
label.plugin.info=Plugin Info:
comment.plugin.dependencies=Each dependency is in format "id:VersionRequirement" (not including quotation marks). To declare optional dependency, add "?" at the end. \
Example: "net.mamoe.chat-command:[1.0.0, 2.0.0)?" declares and optional dependency on "net.mamoe.chat-command", requiring its version to be greater or equal to 1.0.0 and smaller than 2.0.0. \
Separate multiple dependencies into liens. \
Syntax for "VersionRequirement" can be found at <a href="https://ant.apache.org/ivy/history/latest-milestone/settings/version-matchers.html">Apache Ivy version-matchers</a>.
comment.plugin.info=Introductory information about this plugin.
text.hint.plugin.info=Optional
text.hint.plugin.dependencies=Optional
validation.plugin.name.forbidden.character="{0}" is forbidden in plugin name
validation.illegal.plugin.id=Invalid plugin id "{0}"
validation.illegal.version=Invalid version.\n{0}
no.error.message=No error message

View File

@ -0,0 +1,39 @@
#
# 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
#
module.presentation.name=Mirai Console 插件
module.description="构建 <b>Mirai Console</b> 插件的模板"
title.plugin.description=插件信息
label.plugin.version=插件版本号:
label.version.stable=稳定版
label.version.prerelease=预览版
label.version.nightly=开发版
label.plugin.id=插件 ID:
label.plugin.name=插件名称:
label.mirai.version.loading=加载中...
label.mirai.version=Mirai 版本号:
error.failed.to.download.mirai.version=下载版本列表失败, 请手动选择版本号
error.please.wait.for.mirai.version=请等待下载版本列表
label.plugin.author=插件作者:
comment.plugin.id=只能包含以下内容: 英文字母, 数字, '.', ':', '-'. 示例: "net.mamoe.chat-command".
comment.plugin.name=示例: "Chat Command".
comment.plugin.version=需遵循 <a href="https://semver.org/">语义化版本</a>, 不包含 'v'. 示例: "0.1.0".
comment.mirai.version=插件依赖的最低 Mirai Console 版本号
label.plugin.dependencies=插件依赖:
label.plugin.info=插件描述:
comment.plugin.dependencies=每个依赖格式为 "id:版本要求" (不包括引号). 若要定义可选依赖, 请在末尾添加 "?". \
示例: "net.mamoe.chat-command:[1.0.0, 2.0.0)?" 定义对 "net.mamoe.chat-command" 的可选依赖, 要求其版本大于或等于 1.0.0 且小于 2.0.0. \
多个依赖以换行符分割. \
"版本要求" 语法可在 <a href="https://ant.apache.org/ivy/history/latest-milestone/settings/version-matchers.html">Apache Ivy version-matchers</a> 查看.
comment.plugin.info=对该插件的介绍信息.
text.hint.plugin.info=可留空
text.hint.plugin.dependencies=可留空
validation.plugin.name.forbidden.character=插件名称中不允许存在 "{0}"
validation.illegal.plugin.id=插件 ID 无效: "{0}"
validation.illegal.version=插件版本无效\n{0}
no.error.message=无错误信息

View File

@ -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
*/
package net.mamoe.mirai.console.intellij.assets
@ -31,7 +31,11 @@ object FT { // file template
const val PluginMainKt = "Plugin main class Kotlin.kt"
const val PluginMainJava = "Plugin main class Java.java"
const val PluginMainService = "Plugin main service.txt"
const val Gitignore = ".gitignore"
const val RunTerminal = "RunTerminal.kt"
const val RunTerminalRun = "RunTerminal.run.xml"
const val AccountProperties = "account.properties"
}

View File

@ -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
*/
@ -21,6 +21,7 @@ class FileTemplateRegistrar : com.intellij.ide.fileTemplates.FileTemplateGroupDe
addTemplate(FileTemplateDescriptor(FT.PluginMainKt))
addTemplate(FileTemplateDescriptor(FT.PluginMainJava))
addTemplate(FileTemplateDescriptor(FT.PluginMainService))
addTemplate(FileTemplateDescriptor(FT.GradleProperties))
@ -30,6 +31,7 @@ class FileTemplateRegistrar : com.intellij.ide.fileTemplates.FileTemplateGroupDe
addTemplate(FileTemplateDescriptor(FT.Gitignore))
addTemplate(FileTemplateDescriptor(FT.RunTerminal))
addTemplate(FileTemplateDescriptor(FT.RunTerminalRun))
}
}

View File

@ -1,121 +0,0 @@
/*
* 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 net.mamoe.mirai.console.intellij.creator
import com.intellij.ide.util.projectWizard.JavaModuleBuilder
import com.intellij.ide.util.projectWizard.ModuleWizardStep
import com.intellij.ide.util.projectWizard.WizardContext
import com.intellij.openapi.Disposable
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.module.JavaModuleType
import com.intellij.openapi.module.ModuleType
import com.intellij.openapi.progress.ProgressManager
import com.intellij.openapi.project.DumbAwareRunnable
import com.intellij.openapi.project.DumbService
import com.intellij.openapi.roots.ModifiableRootModel
import com.intellij.openapi.roots.ui.configuration.ModulesProvider
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 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
import net.mamoe.mirai.console.intellij.creator.steps.PluginCoordinatesStep
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
override fun getNodeIcon() = Icons.MainIcon
override fun getGroupName() = MiraiModuleType.NAME
override fun getWeight() = BUILD_SYSTEM_WEIGHT - 1
override fun getBuilderId() = ID
override fun getModuleType(): ModuleType<*> = JavaModuleType.getModuleType()
override fun getParentGroup() = MiraiModuleType.NAME
override fun setupRootModel(rootModel: ModifiableRootModel) {
val project = rootModel.project
val (root, vFile) = createAndGetRoot()
rootModel.addContentEntry(vFile)
if (moduleJdk != null) {
rootModel.sdk = moduleJdk
} else {
rootModel.inheritSdk()
}
val r = DumbAwareRunnable {
ProgressManager.getInstance().run(CreateProjectTask(root, rootModel.module, model))
}
if (project.isDisposed) return
if (
ApplicationManager.getApplication().isUnitTestMode ||
ApplicationManager.getApplication().isHeadlessEnvironment
) {
r.run()
return
}
if (!project.isInitialized) {
StartupManager.getInstance(project).registerPostStartupActivity(r)
return
}
DumbService.getInstance(project).runWhenSmart(r)
}
private fun createAndGetRoot(): Pair<Path, VirtualFile> {
val temp = contentEntryPath ?: throw IllegalStateException("Failed to get content entry path")
val pathName = FileUtil.toSystemIndependentName(temp)
val path = Paths.get(pathName)
Files.createDirectories(path)
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 = Executors.newFixedThreadPool(2)
private val model = MiraiProjectModel.create(scope)
override fun cleanup() {
super.cleanup()
scope.shutdownNow()
}
override fun createWizardSteps(
wizardContext: WizardContext,
modulesProvider: ModulesProvider
): Array<ModuleWizardStep> {
return arrayOf(
BuildSystemStep(model),
PluginCoordinatesStep(model, scope),
)
}
override fun getCustomOptionsStep(context: WizardContext?, parentDisposable: Disposable?): ModuleWizardStep =
OptionsStep()
companion object {
const val ID = "MIRAI_MODULE"
}
}

View File

@ -1,121 +0,0 @@
/*
* 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 net.mamoe.mirai.console.intellij.creator
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(
val groupId: String, // already checked by pattern
val artifactId: String,
val version: String
) {
val packageName: String get() = groupId
}
data class PluginCoordinates(
val id: String?,
val name: String?,
val author: String?,
val info: String?,
val dependsOn: String?,
)
class MiraiProjectModel private constructor() {
// STEP: ProjectCreator
var projectCoordinates: ProjectCoordinates? = null
var buildSystemType: BuildSystemType = BuildSystemType.DEFAULT
var languageType: LanguageType = LanguageType.DEFAULT
var miraiVersion: String? = null
var pluginCoordinates: PluginCoordinates? = null
var mainClassQualifiedName: String by lateinitReadWriteProperty { "$packageName.$mainClassSimpleName" }
var mainClassSimpleName: String by lateinitReadWriteProperty {
pluginCoordinates?.run {
name?.adjustToClassName() ?: id?.substringAfterLast('.')?.adjustToClassName()
} ?: "PluginMain"
}
var packageName: String by lateinitReadWriteProperty { projectCoordinates.checkNotNull("projectCoordinates").groupId }
var availableMiraiVersions: CompletableFuture<Set<MiraiVersion>>? = null
val availableMiraiVersionsOrFail get() = availableMiraiVersions.checkNotNull("availableMiraiVersions")
fun checkValuesNotNull() {
checkNotNull(miraiVersion) { "miraiVersion" }
checkNotNull(pluginCoordinates) { "pluginCoordinates" }
checkNotNull(projectCoordinates) { "projectCoordinates" }
}
companion object {
fun create(scope: ExecutorService): MiraiProjectModel {
return MiraiProjectModel().apply {
availableMiraiVersions = scope.async { MiraiVersionKind.getMiraiVersionList() }
}
}
fun <T> ExecutorService.async(block: () -> T): CompletableFuture<T> {
val future = CompletableFuture<T>()
submit {
try {
future.complete(block())
} catch (e: Throwable) {
future.completeExceptionally(e)
}
}
return future
}
}
}
val MiraiProjectModel.templateProperties: Map<String, String?>
get() {
val projectCoordinates = projectCoordinates!!
val pluginCoordinates = pluginCoordinates!!
return mapOf(
"KOTLIN_VERSION" to KotlinVersion.CURRENT.toString(),
"MIRAI_VERSION" to miraiVersion!!,
"GROUP_ID" to projectCoordinates.groupId,
"VERSION" to projectCoordinates.version,
"USE_PROXY_REPO" to "true",
"ARTIFACT_ID" to projectCoordinates.artifactId,
"PLUGIN_ID" to pluginCoordinates.id,
"PLUGIN_NAME" to languageType.escapeString(pluginCoordinates.name),
"PLUGIN_AUTHOR" to languageType.escapeString(pluginCoordinates.author),
"PLUGIN_INFO" to languageType.escapeRawString(pluginCoordinates.info),
"PLUGIN_DEPENDS_ON" to pluginCoordinates.dependsOn,
"PLUGIN_VERSION" to projectCoordinates.version,
"PACKAGE_NAME" to packageName,
"CLASS_NAME" to mainClassSimpleName,
"LANGUAGE_TYPE" to languageType.toString(),
)
}
fun <T : Any> T?.checkNotNull(name: String): T {
contract {
returns() implies (this@checkNotNull != null)
}
checkNotNull(this) {
"$name is not yet initialized."
}
return this
}

View File

@ -1,113 +0,0 @@
/*
* Copyright 2019-2021 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/master/LICENSE
*/
package net.mamoe.mirai.console.intellij.creator.build
import com.intellij.codeInsight.actions.ReformatCodeProcessor
import com.intellij.openapi.module.Module
import com.intellij.openapi.progress.ProgressIndicator
import com.intellij.openapi.vfs.VfsUtil
import com.intellij.openapi.vfs.VirtualFile
import net.mamoe.mirai.console.intellij.assets.FT
import net.mamoe.mirai.console.intellij.creator.MiraiProjectModel
import net.mamoe.mirai.console.intellij.creator.tasks.getTemplate
import net.mamoe.mirai.console.intellij.creator.tasks.invokeAndWait
import net.mamoe.mirai.console.intellij.creator.tasks.runWriteActionAndWait
import net.mamoe.mirai.console.intellij.creator.tasks.writeChild
import net.mamoe.mirai.console.intellij.creator.templateProperties
import org.jetbrains.kotlin.idea.core.util.toPsiFile
import java.nio.file.Path
sealed class ProjectCreator(
val module: Module,
val root: VirtualFile,
val model: MiraiProjectModel,
) {
val project get() = module.project
init {
model.checkValuesNotNull()
}
protected val filesChanged = mutableListOf<Path>()
@Synchronized
protected fun addFileChanged(path: Path) {
filesChanged.add(path)
}
protected fun getTemplate(name: String) = project.getTemplate(name, model.templateProperties)
fun doFinish(indicator: ProgressIndicator) {
indicator.text2 = "Reformatting files"
invokeAndWait {
for (file in filesChanged) {
val psi = file.toFile().toPsiFile(project) ?: continue
ReformatCodeProcessor(psi, false).run()
}
}
}
abstract fun createProject(
module: Module,
root: VirtualFile,
model: MiraiProjectModel,
)
}
sealed class GradleProjectCreator(
module: Module, root: VirtualFile, model: MiraiProjectModel,
) : ProjectCreator(module, root, model) {
override fun createProject(module: Module, root: VirtualFile, model: MiraiProjectModel) {
runWriteActionAndWait {
VfsUtil.createDirectoryIfMissing(root, "src/main/${model.languageType.sourceSetDirName}")
VfsUtil.createDirectoryIfMissing(root, "src/main/resources")
addFileChanged(root.writeChild(model.languageType.pluginMainClassFile(this)))
addFileChanged(
root.writeChild(
"src/main/resources/META-INF/services/net.mamoe.mirai.console.plugin.jvm.JvmPlugin",
model.mainClassQualifiedName
)
)
addFileChanged(root.writeChild(".gitignore", getTemplate(FT.Gitignore)))
addFileChanged(root.writeChild("gradle.properties", getTemplate(FT.GradleProperties)))
addFileChanged(root.writeChild("src/test/kotlin/RunTerminal.kt", getTemplate(FT.RunTerminal)))
}
}
}
class GradleKotlinProjectCreator(
module: Module, root: VirtualFile, model: MiraiProjectModel,
) : GradleProjectCreator(
module, root, model,
) {
override fun createProject(module: Module, root: VirtualFile, model: MiraiProjectModel) {
super.createProject(module, root, model)
runWriteActionAndWait {
addFileChanged(root.writeChild("build.gradle.kts", getTemplate(FT.BuildGradleKts)))
addFileChanged(root.writeChild("settings.gradle.kts", getTemplate(FT.SettingsGradleKts)))
}
}
}
class GradleGroovyProjectCreator(
module: Module, root: VirtualFile, model: MiraiProjectModel,
) : GradleProjectCreator(
module, root, model,
) {
override fun createProject(module: Module, root: VirtualFile, model: MiraiProjectModel) {
super.createProject(module, root, model)
runWriteActionAndWait {
addFileChanged(root.writeChild("build.gradle", getTemplate(FT.BuildGradle)))
addFileChanged(root.writeChild("settings.gradle", getTemplate(FT.SettingsGradle)))
}
}
}

View File

@ -1,111 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="net.mamoe.mirai.console.intellij.creator.steps.BuildSystemStep">
<grid id="27dc6" binding="panel" layout-manager="GridLayoutManager" row-count="5" column-count="4" same-size-horizontally="false" same-size-vertically="false" hgap="10" vgap="10">
<margin top="10" left="10" bottom="10" right="10"/>
<constraints>
<xy x="20" y="20" width="589" height="400"/>
</constraints>
<properties/>
<border type="none" title-justification="1" title-position="3"/>
<children>
<component id="539d6" class="javax.swing.JLabel">
<constraints>
<grid row="1" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text value="ArtifactId:"/>
</properties>
</component>
<component id="f1b7a" class="javax.swing.JTextField" binding="artifactIdField">
<constraints>
<grid row="1" column="1" row-span="1" col-span="3" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false">
<preferred-size width="150" height="-1"/>
</grid>
</constraints>
<properties>
<text value="plugin"/>
</properties>
</component>
<component id="2c1ec" class="javax.swing.JTextField" binding="groupIdField">
<constraints>
<grid row="0" column="1" row-span="1" col-span="3" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false">
<preferred-size width="150" height="-1"/>
</grid>
</constraints>
<properties>
<text value="org.example"/>
</properties>
</component>
<component id="2e485" class="javax.swing.JTextField" binding="versionField">
<constraints>
<grid row="2" column="1" row-span="1" col-span="3" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false">
<preferred-size width="150" height="-1"/>
</grid>
</constraints>
<properties>
<text value="1.0-SNAPSHOT"/>
</properties>
</component>
<component id="6d341" class="javax.swing.JLabel">
<constraints>
<grid row="2" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text value="Version:"/>
</properties>
</component>
<vspacer id="3151a">
<constraints>
<grid row="4" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
</constraints>
</vspacer>
<hspacer id="8d42b">
<constraints>
<grid row="3" column="1" row-span="1" col-span="1" vsize-policy="1" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false">
<preferred-size width="229" height="11"/>
</grid>
</constraints>
</hspacer>
<component id="33f22" class="javax.swing.JLabel">
<constraints>
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text value="GroupId:"/>
</properties>
</component>
<component id="452df" class="javax.swing.JComboBox" binding="buildSystemBox">
<constraints>
<grid row="3" column="3" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="1" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<model/>
<toolTipText value="Build system"/>
</properties>
</component>
<component id="45fb1" class="javax.swing.JComboBox" binding="languageBox">
<constraints>
<grid row="3" column="2" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="1" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<model/>
<toolTipText value="Language"/>
</properties>
</component>
<component id="303a9" class="javax.swing.JLabel">
<constraints>
<grid row="4" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text value=""/>
</properties>
</component>
</children>
</grid>
<buttonGroups>
<group name="radioButtonGroup">
<member id="80d0b"/>
<member id="9d2d8"/>
</group>
</buttonGroups>
</form>

View File

@ -1,80 +0,0 @@
/*
* Copyright 2019-2021 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/master/LICENSE
*/
package net.mamoe.mirai.console.intellij.creator.steps
import com.intellij.ide.util.projectWizard.ModuleWizardStep
import net.mamoe.mirai.console.intellij.creator.MiraiProjectModel
import net.mamoe.mirai.console.intellij.creator.ProjectCoordinates
import net.mamoe.mirai.console.intellij.creator.tasks.PACKAGE_PATTERN
import net.mamoe.mirai.console.intellij.diagnostics.ContextualParametersChecker.Companion.SEMANTIC_VERSIONING_PATTERN
import javax.swing.JComboBox
import javax.swing.JPanel
import javax.swing.JTextField
/**
* @see MiraiProjectModel.projectCoordinates
* @see MiraiProjectModel.languageType
* @see MiraiProjectModel.buildSystemType
*/
class BuildSystemStep(
private val model: MiraiProjectModel
) : ModuleWizardStep() {
private lateinit var panel: JPanel
@field:Validation.NotBlank("Group ID")
@field:Validation.Pattern("Group ID", PACKAGE_PATTERN)
private lateinit var groupIdField: JTextField
@field:Validation.NotBlank("Artifact ID")
@field:Validation.Pattern("Artifact ID", PACKAGE_PATTERN)
private lateinit var artifactIdField: JTextField
@field:Validation.NotBlank("Version")
@field:Validation.Pattern("Version", SEMANTIC_VERSIONING_PATTERN)
private lateinit var versionField: JTextField
private lateinit var buildSystemBox: JComboBox<BuildSystemType>
private lateinit var languageBox: JComboBox<LanguageType>
override fun getComponent() = panel
override fun updateStep() {
buildSystemBox.removeAllItems()
buildSystemBox.isEnabled = true
BuildSystemType.values().forEach { buildSystemBox.addItem(it) }
buildSystemBox.selectedItem = BuildSystemType.DEFAULT
buildSystemBox.toolTipText = """
Gradle Kotlin DSL: build.gradle.kts <br/>
Gradle Groovy DSL: build.gradle
""".trimIndent()
languageBox.removeAllItems()
languageBox.isEnabled = true
LanguageType.values().forEach { languageBox.addItem(it) }
languageBox.selectedItem = LanguageType.DEFAULT
buildSystemBox.toolTipText = """
Language for main class.
""".trimIndent()
}
override fun updateDataModel() {
model.buildSystemType = this.buildSystemBox.selectedItem as BuildSystemType
model.languageType = this.languageBox.selectedItem as LanguageType
model.projectCoordinates = ProjectCoordinates(
groupId = groupIdField.text,
artifactId = artifactIdField.text,
version = versionField.text
)
}
override fun validate() = Validation.doValidation(this)
}

View File

@ -1,38 +0,0 @@
/*
* Copyright 2019-2021 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/master/LICENSE
*/
package net.mamoe.mirai.console.intellij.creator.steps
import com.intellij.openapi.module.Module
import com.intellij.openapi.vfs.VirtualFile
import net.mamoe.mirai.console.intellij.creator.MiraiProjectModel
import net.mamoe.mirai.console.intellij.creator.build.GradleGroovyProjectCreator
import net.mamoe.mirai.console.intellij.creator.build.GradleKotlinProjectCreator
import net.mamoe.mirai.console.intellij.creator.build.ProjectCreator
enum class BuildSystemType {
GradleKt {
override fun createBuildSystem(module: Module, root: VirtualFile, model: MiraiProjectModel): ProjectCreator =
GradleKotlinProjectCreator(module, root, model)
override fun toString(): String = "Gradle Kotlin DSL"
},
GradleGroovy {
override fun createBuildSystem(module: Module, root: VirtualFile, model: MiraiProjectModel): ProjectCreator =
GradleGroovyProjectCreator(module, root, model)
override fun toString(): String = "Gradle Groovy DSL"
}, ;
abstract fun createBuildSystem(module: Module, root: VirtualFile, model: MiraiProjectModel): ProjectCreator
companion object {
val DEFAULT = GradleKt
}
}

View File

@ -1,12 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="net.mamoe.mirai.console.intellij.creator.steps.OptionsStep">
<grid id="27dc6" binding="panel" layout-manager="GridLayoutManager" row-count="1" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<xy x="20" y="20" width="500" height="400"/>
</constraints>
<properties/>
<border type="none"/>
<children/>
</grid>
</form>

View File

@ -1,26 +0,0 @@
/*
* Copyright 2019-2021 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/master/LICENSE
*/
package net.mamoe.mirai.console.intellij.creator.steps
import com.intellij.ide.util.projectWizard.ModuleWizardStep
import javax.swing.JComponent
import javax.swing.JPanel
class OptionsStep : ModuleWizardStep() {
private lateinit var panel: JPanel
override fun getComponent(): JComponent {
return panel
}
override fun updateDataModel() {
}
}

View File

@ -1,215 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="net.mamoe.mirai.console.intellij.creator.steps.PluginCoordinatesStep">
<grid id="27dc6" binding="panel" layout-manager="GridLayoutManager" row-count="11" column-count="3" same-size-horizontally="false" same-size-vertically="false" hgap="10" vgap="10">
<margin top="10" left="10" bottom="10" right="10"/>
<constraints>
<xy x="20" y="20" width="531" height="541"/>
</constraints>
<properties/>
<border type="none" title-justification="1" title-position="3"/>
<children>
<vspacer id="3151a">
<constraints>
<grid row="10" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
</constraints>
</vspacer>
<component id="303a9" class="javax.swing.JLabel">
<constraints>
<grid row="10" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text value=""/>
<visible value="false"/>
</properties>
</component>
<component id="539d6" class="javax.swing.JLabel">
<constraints>
<grid row="10" column="2" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text value=""/>
<toolTipText value=""/>
<visible value="false"/>
</properties>
</component>
<component id="33f22" class="javax.swing.JLabel">
<constraints>
<grid row="8" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="9" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text value="Info:"/>
<toolTipText value="描述"/>
</properties>
</component>
<vspacer id="b4a73">
<constraints>
<grid row="9" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
</constraints>
</vspacer>
<component id="980c2" class="javax.swing.JTextArea" binding="infoArea">
<constraints>
<grid row="8" column="1" row-span="1" col-span="2" vsize-policy="6" hsize-policy="6" anchor="0" fill="3" indent="0" use-parent-layout="false">
<preferred-size width="150" height="119"/>
</grid>
</constraints>
<properties>
<dragEnabled value="true"/>
<text value=""/>
<toolTipText value="描述, 可选"/>
</properties>
</component>
<component id="3ca24" class="javax.swing.JLabel">
<constraints>
<grid row="6" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text value="Author:"/>
<toolTipText value="作者"/>
</properties>
</component>
<component id="45051" class="javax.swing.JTextField" binding="authorField">
<constraints>
<grid row="6" column="1" row-span="1" col-span="2" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false">
<preferred-size width="150" height="-1"/>
</grid>
</constraints>
<properties>
<text value=""/>
<toolTipText value="作者名称, 可选"/>
</properties>
</component>
<component id="c6d49" class="javax.swing.JLabel">
<constraints>
<grid row="5" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text value="Name:"/>
<toolTipText value="显示名称"/>
</properties>
</component>
<component id="27fb6" class="javax.swing.JTextField" binding="nameField">
<constraints>
<grid row="5" column="1" row-span="1" col-span="2" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false">
<preferred-size width="150" height="-1"/>
</grid>
</constraints>
<properties>
<text value=""/>
<toolTipText value="显示名称, 可选"/>
</properties>
</component>
<component id="28e8e" class="javax.swing.JLabel">
<constraints>
<grid row="7" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text value="Depends on:"/>
<toolTipText value="依赖的插件列表"/>
</properties>
</component>
<component id="44e5c" class="javax.swing.JTextField" binding="dependsOnField">
<constraints>
<grid row="7" column="1" row-span="1" col-span="2" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false">
<preferred-size width="150" height="-1"/>
</grid>
</constraints>
<properties>
<enabled value="false"/>
<text value=""/>
<toolTipText value="依赖的插件列表, 还不支持编辑, 请在创建项目后修改"/>
</properties>
</component>
<component id="53007" class="javax.swing.JSeparator">
<constraints>
<grid row="1" column="0" row-span="1" col-span="3" vsize-policy="6" hsize-policy="6" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
</component>
<component id="45fb1" class="javax.swing.JComboBox" binding="miraiVersionBox">
<constraints>
<grid row="0" column="2" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="1" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<editable value="true"/>
<enabled value="false"/>
<model>
<item value="Loading..."/>
</model>
</properties>
</component>
<component id="6d341" class="javax.swing.JLabel">
<constraints>
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<opaque value="true"/>
<text value="Mirai version:"/>
</properties>
</component>
<component id="452df" class="javax.swing.JComboBox" binding="miraiVersionKindBox">
<constraints>
<grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="1" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<enabled value="false"/>
<model>
<item value="Stable"/>
<item value="Prerelease"/>
</model>
<toolTipText value="Mirai 版本类型 &lt;br/&gt; Stable: 稳定 Prerelease: -M 和 -RC 测试版" noi18n="true"/>
</properties>
</component>
<component id="6ddd1" class="javax.swing.JLabel">
<constraints>
<grid row="2" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text value="*ID:"/>
<toolTipText value=""/>
</properties>
</component>
<component id="a76eb" class="javax.swing.JTextField" binding="idField">
<constraints>
<grid row="2" column="1" row-span="1" col-span="2" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false">
<preferred-size width="150" height="-1"/>
</grid>
</constraints>
<properties>
<text value=""/>
</properties>
</component>
<component id="9ee7e" class="javax.swing.JTextField" binding="mainClassField">
<constraints>
<grid row="3" column="1" row-span="1" col-span="2" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false">
<preferred-size width="150" height="-1"/>
</grid>
</constraints>
<properties>
<text value=""/>
<toolTipText value="依赖的插件列表, 可选"/>
</properties>
</component>
<component id="f4c2b" class="javax.swing.JLabel">
<constraints>
<grid row="3" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text value="*Main class:"/>
<toolTipText value="依赖的插件列表"/>
</properties>
</component>
<component id="f7d3f" class="javax.swing.JSeparator">
<constraints>
<grid row="4" column="0" row-span="1" col-span="3" vsize-policy="6" hsize-policy="6" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
</component>
</children>
</grid>
<buttonGroups>
<group name="radioButtonGroup">
<member id="80d0b"/>
<member id="9d2d8"/>
</group>
</buttonGroups>
</form>

View File

@ -1,148 +0,0 @@
/*
* 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 net.mamoe.mirai.console.intellij.creator.steps
import com.intellij.ide.util.projectWizard.ModuleWizardStep
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
import net.mamoe.mirai.console.intellij.creator.PluginCoordinates
import net.mamoe.mirai.console.intellij.creator.checkNotNull
import net.mamoe.mirai.console.intellij.creator.steps.Validation.NotBlank
import net.mamoe.mirai.console.intellij.creator.steps.Validation.Pattern
import net.mamoe.mirai.console.intellij.creator.tasks.QUALIFIED_CLASS_NAME_PATTERN
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 scope: ExecutorService,
) : ModuleWizardStep() {
private lateinit var panel: JPanel
@field:NotBlank("ID")
@field:Pattern("ID", PLUGIN_ID_PATTERN)
private lateinit var idField: JTextField
@field:NotBlank("Main class")
@field:Pattern("Main class", QUALIFIED_CLASS_NAME_PATTERN)
private lateinit var mainClassField: JTextField
private lateinit var nameField: JTextField
private lateinit var authorField: JTextField
private lateinit var dependsOnField: JTextField
private lateinit var infoArea: JTextArea
private lateinit var miraiVersionKindBox: JComboBox<MiraiVersionKind>
@field:NotBlank("Mirai version")
@field:Pattern("Mirai version", ContextualParametersChecker.SEMANTIC_VERSIONING_PATTERN)
private lateinit var miraiVersionBox: JComboBox<String>
override fun getComponent() = panel
private val versionKindChangeListener: ItemListener = ItemListener { event ->
if (event.stateChange != ItemEvent.SELECTED) return@ItemListener
updateVersionItemsAsync()
}
override fun getPreferredFocusedComponent(): JComponent = idField
override fun updateStep() {
miraiVersionKindBox.removeAllItems()
miraiVersionKindBox.isEnabled = true
MiraiVersionKind.values().forEach { miraiVersionKindBox.addItem(it) }
miraiVersionKindBox.selectedItem = MiraiVersionKind.DEFAULT
miraiVersionKindBox.addItemListener(versionKindChangeListener) // when selected, change versions
miraiVersionBox.removeAllItems()
miraiVersionBox.addItem(VERSION_LOADING_PLACEHOLDER)
miraiVersionBox.selectedItem = VERSION_LOADING_PLACEHOLDER
model.availableMiraiVersionsOrFail.whenComplete { _, _ ->
updateVersionItemsAsync()
}
if (idField.text.isNullOrEmpty()) {
model.projectCoordinates.checkNotNull("projectCoordinates").run {
idField.text = "$groupId.$artifactId"
}
}
if (mainClassField.text.isNullOrEmpty()) {
model.projectCoordinates.checkNotNull("projectCoordinates").run {
mainClassField.text = "$groupId.${artifactId.adjustToClassName()}"
}
}
}
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.join() }
.fold(
onSuccess = { versions ->
versions
.filter { v -> expectingKind.isThatKind(v) }
.forEach { v -> miraiVersionBox.addItem(v) }
},
onFailure = { e ->
Validation.popup(
"Failed to download version list, please select a version by yourself. \nCause: ${e.cause ?: e}",
miraiVersionBox
)
}
)
miraiVersionBox.isEnabled = true
}
}
override fun updateDataModel() {
model.pluginCoordinates = PluginCoordinates(
id = idField.text.trim(),
author = authorField.text,
name = nameField.text?.trim(),
info = infoArea.text?.trim(),
dependsOn = dependsOnField.text?.trim(),
)
model.miraiVersion = miraiVersionBox.selectedItem?.toString()?.trim() ?: "+"
model.packageName = mainClassField.text.substringBeforeLast('.')
model.mainClassSimpleName = mainClassField.text.substringAfterLast('.')
model.mainClassQualifiedName = mainClassField.text
}
override fun validate(): Boolean {
if (miraiVersionBox.selectedItem?.toString() == VERSION_LOADING_PLACEHOLDER) {
Validation.popup("请等待获取版本号", miraiVersionBox)
return false
}
if (!Validation.doValidation(this)) return false
if (!mainClassField.text.contains('.')) {
Validation.popup("Main class 需要包含包名", mainClassField)
return false
}
return true
}
companion object {
const val VERSION_LOADING_PLACEHOLDER = "Loading..."
private val LOG = Logger.getInstance(PluginCoordinatesStep::class.java)
}
}

View File

@ -1,129 +0,0 @@
/*
* Copyright 2019-2021 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/master/LICENSE
*/
package net.mamoe.mirai.console.intellij.creator.steps
import com.intellij.ide.util.projectWizard.ModuleWizardStep
import com.intellij.openapi.ui.MessageType
import com.intellij.openapi.ui.popup.Balloon
import com.intellij.openapi.ui.popup.JBPopupFactory
import com.intellij.ui.awt.RelativePoint
import net.mamoe.mirai.console.compiler.common.cast
import org.intellij.lang.annotations.Language
import java.lang.reflect.Field
import java.util.concurrent.ConcurrentLinkedQueue
import javax.swing.JComponent
import javax.swing.text.JTextComponent
import kotlin.reflect.KClass
import kotlin.reflect.full.createInstance
class Validation {
annotation class WithValidator(val clazz: KClass<out Validator<WithValidator>>) {
companion object {
init {
registerValidator<WithValidator> { annotation, component ->
val instance = annotation.clazz.objectInstance ?: annotation.clazz.createInstance()
instance.validate(annotation, component)
}
}
}
}
annotation class NotBlank(val tipName: String) {
companion object {
init {
registerValidator<NotBlank> { annotation, component ->
if (component.text.isNullOrBlank()) {
report("请填写 ${annotation.tipName}")
}
}
}
}
}
annotation class Pattern(val tipName: String, @Language("RegExp") val value: String) {
companion object {
init {
registerValidator<Pattern> { annotation, component ->
if (component.text?.matches(Regex(annotation.value)) != true) {
report("请正确填写 ${annotation.tipName}")
}
}
}
}
}
fun interface Validator<in A : Annotation> {
@Throws(ValidationException::class)
fun ValidationContext.validate(annotation: A, component: JTextComponent)
@Throws(ValidationException::class)
fun validate(annotation: A, component: JTextComponent) {
ValidationContext.run { validate(annotation, component) }
}
object ValidationContext {
fun report(message: String): Nothing = throw ValidationException(message)
}
}
class ValidationException(message: String) : Exception(message)
companion object {
private data class RegisteredValidator<A : Annotation>(val type: KClass<A>, val validator: Validator<A>)
private val validators: MutableCollection<RegisteredValidator<*>> = ConcurrentLinkedQueue()
private inline fun <reified A : Annotation> registerValidator(validator: Validator<A>) {
validators.add(RegisteredValidator(A::class, validator))
}
fun popup(message: String, component: JComponent) {
JBPopupFactory.getInstance()
.createHtmlTextBalloonBuilder(message, MessageType.ERROR, null)
.setFadeoutTime(2000)
.createBalloon()
.show(RelativePoint.getSouthWestOf(component), Balloon.Position.below)
}
/**
* @return `true` if no error
*/
fun doValidation(step: ModuleWizardStep): Boolean {
fun validateProperty(field: Field): Boolean {
field.isAccessible = true
val annotationsToValidate =
validators.associateBy { (type: KClass<out Annotation>) ->
field.annotations.find { it::class == type }
}
for ((annotation, validator) in annotationsToValidate) {
if (annotation == null) continue
val component = field.get(step) as JTextComponent
try {
validator.validator.cast<Validator<Annotation>>().validate(annotation, component)
} catch (e: ValidationException) {
popup(e.message ?: e.toString(), component)
return false // report one error only
}
}
return true
}
var result = true
for (prop in step::class.java.declaredFields) {
if (!validateProperty(prop)) result = false
}
return result
}
}
}

View File

@ -1,68 +0,0 @@
/*
* Copyright 2019-2021 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/master/LICENSE
*/
package net.mamoe.mirai.console.intellij.creator.tasks
import com.intellij.ide.ui.UISettings
import com.intellij.openapi.module.Module
import com.intellij.openapi.progress.ProgressIndicator
import com.intellij.openapi.progress.Task
import com.intellij.openapi.project.Project
import com.intellij.openapi.vfs.VfsUtil
import com.intellij.openapi.wm.WindowManager
import com.intellij.openapi.wm.ex.StatusBarEx
import net.mamoe.mirai.console.intellij.creator.MiraiProjectModel
import org.jetbrains.kotlin.idea.util.application.invokeLater
import org.jetbrains.plugins.gradle.service.project.open.linkAndRefreshGradleProject
import java.nio.file.Files
import java.nio.file.Path
class CreateProjectTask(
private val root: Path,
private val module: Module,
private val model: MiraiProjectModel,
) : Task.Backgroundable(module.project, "Creating project", false) {
override fun shouldStartInBackground() = false
override fun run(indicator: ProgressIndicator) {
if (module.isDisposed || project.isDisposed) return
Files.createDirectories(root)
invokeAndWait {
VfsUtil.markDirtyAndRefresh(false, true, true, root.vf)
}
val build = model.buildSystemType.createBuildSystem(module, root.vf, model)
build.createProject(module, root.vf, model)
invokeLater {
VfsUtil.markDirtyAndRefresh(false, true, true, root.vf)
}
invokeLater {
linkAndRefreshGradleProject(root.toAbsolutePath().toString(), project)
showProgress(project)
}
build.doFinish(indicator)
}
}
private fun showProgress(project: Project) {
if (!UISettings.instance.showStatusBar || UISettings.instance.presentationMode) {
return
}
val statusBar = WindowManager.getInstance().getStatusBar(project) as? StatusBarEx ?: return
statusBar.isProcessWindowOpen = true
}

View File

@ -1,5 +1,5 @@
/*
* 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.
@ -22,12 +22,12 @@ import net.mamoe.mirai.console.intellij.diagnostics.fix.ConfigurePluginMainServi
import net.mamoe.mirai.console.intellij.resolve.allSuperNames
import net.mamoe.mirai.console.intellij.resolve.hasAnnotation
import org.jetbrains.kotlin.idea.inspections.AbstractKotlinInspection
import org.jetbrains.kotlin.idea.debugger.readAction
import org.jetbrains.kotlin.idea.util.*
import org.jetbrains.kotlin.idea.util.application.runReadAction
import org.jetbrains.kotlin.idea.util.module
import org.jetbrains.kotlin.idea.util.rootManager
import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.psi.KtObjectDeclaration
import org.jetbrains.kotlin.psi.classOrObjectVisitor
import java.util.*
/*
private val bundle by lazy {
@ -107,8 +107,9 @@ class PluginMainServiceNotConfiguredInspection : AbstractKotlinInspection() {
}
}
return@runWithCancellationCheck services.any { serviceFile ->
serviceFile.readAction { f ->
f.inputStream.bufferedReader().use { reader -> reader.lineSequence().any { it == fqName } }
runReadAction {
serviceFile.inputStream.bufferedReader()
.use { reader -> reader.lineSequence().any { it == fqName } }
}
}
}

View File

@ -1,5 +1,5 @@
/*
* 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.
@ -16,11 +16,11 @@ import com.intellij.openapi.editor.Editor
import com.intellij.openapi.project.Project
import com.intellij.psi.*
import net.mamoe.mirai.console.intellij.resolve.*
import org.jetbrains.kotlin.idea.base.utils.fqname.getKotlinFqName
import org.jetbrains.kotlin.idea.caches.resolve.resolveToCall
import org.jetbrains.kotlin.idea.inspections.AbstractKotlinInspection
import org.jetbrains.kotlin.idea.inspections.KotlinUniversalQuickFix
import org.jetbrains.kotlin.idea.quickfix.KotlinCrossLanguageQuickFixAction
import org.jetbrains.kotlin.idea.refactoring.fqName.getKotlinFqName
import org.jetbrains.kotlin.idea.search.usagesSearch.descriptor
import org.jetbrains.kotlin.idea.util.ImportInsertHelper
import org.jetbrains.kotlin.name.FqName
@ -28,7 +28,7 @@ import org.jetbrains.kotlin.nj2k.postProcessing.resolve
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
import org.jetbrains.kotlin.psi.psiUtil.referenceExpression
import org.jetbrains.kotlin.resolve.calls.callUtil.getCalleeExpressionIfAny
import org.jetbrains.kotlin.resolve.calls.util.getCalleeExpressionIfAny
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import kotlin.contracts.contract

View File

@ -8,23 +8,14 @@
*/
package net.mamoe.mirai.console.intellij.creator.tasks
package net.mamoe.mirai.console.intellij.diagnostics
import com.intellij.ide.fileTemplates.FileTemplateManager
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.application.ModalityState
import com.intellij.openapi.application.runWriteAction
import com.intellij.openapi.project.Project
import com.intellij.openapi.vfs.LocalFileSystem
import com.intellij.openapi.vfs.VfsUtil
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.util.io.writeChild
import net.mamoe.mirai.console.intellij.creator.steps.NamedFile
import org.intellij.lang.annotations.Language
import java.nio.file.Path
import java.util.concurrent.atomic.AtomicReference
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
val Path.vfOrNull: VirtualFile?
get() = LocalFileSystem.getInstance().refreshAndFindFileByPath(this.toAbsolutePath().toString())
@ -42,20 +33,6 @@ fun VirtualFile.resolve(relative: String): VirtualFile? = VfsUtil.findRelativeFi
*relative.replace('\\', '/').split('/').toTypedArray()
)
fun <T> invokeAndWait(modalityState: ModalityState? = null, runnable: () -> T): T {
val app = ApplicationManager.getApplication()
if (app.isDispatchThread) return runnable()
return computeDelegated {
app.invokeAndWait({ it(runnable()) }, modalityState ?: ModalityState.defaultModalityState())
}
}
fun <T> runWriteActionAndWait(modalityState: ModalityState? = null, runnable: () -> T) {
invokeAndWait(modalityState) {
runWriteAction(runnable)
}
}
@PublishedApi
internal inline fun <T> computeDelegated(executor: (setter: (T) -> Unit) -> Unit): T {
var resultRef: T? = null
@ -64,29 +41,6 @@ internal inline fun <T> computeDelegated(executor: (setter: (T) -> Unit) -> Unit
return resultRef as T
}
fun Project.getTemplate(
templateName: String,
properties: Map<String, String?>? = null
): String {
val manager = FileTemplateManager.getInstance(this)
val template = manager.getJ2eeTemplate(templateName)
val allProperties = manager.defaultProperties
properties?.let { prop -> allProperties.putAll(prop.mapValues { it.value.orEmpty() }) }
return template.getText(allProperties)
}
fun Project.getTemplate(
templateName: String,
vararg properties: Pair<String, String?>
): String = getTemplate(templateName, properties.toMap())
internal fun VirtualFile.writeChild(pluginMainClassFile: NamedFile): Path {
return writeChild(pluginMainClassFile.path, pluginMainClassFile.content)
}
internal fun VirtualFile.writeChild(path: String, content: String): Path {
return toNioPath().writeChild(path, content)
}
@ -136,22 +90,4 @@ fun String.adjustToClassName(): String? {
if (result.isValidSimpleClassName()) return result
return null
}
@Suppress("RedundantNullableReturnType")
private val UNINITIALIZED: Any? = Any()
@Suppress("UNCHECKED_CAST")
fun <T, R> lateinitReadWriteProperty(initializer: () -> R) = object : ReadWriteProperty<T, R> {
private var field = AtomicReference(UNINITIALIZED)
override fun setValue(thisRef: T, property: KProperty<*>, value: R) {
field.set(value)
}
override tailrec fun getValue(thisRef: T, property: KProperty<*>): R {
val v = field.get()
if (v !== UNINITIALIZED) return v as R
field.compareAndSet(UNINITIALIZED, initializer())
return getValue(thisRef, property)
}
}

View File

@ -1,5 +1,5 @@
/*
* 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.
@ -27,7 +27,7 @@ import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
import org.jetbrains.kotlin.psi.psiUtil.getQualifiedExpressionForReceiver
import org.jetbrains.kotlin.psi.psiUtil.referenceExpression
import org.jetbrains.kotlin.resolve.calls.callUtil.getCalleeExpressionIfAny
import org.jetbrains.kotlin.resolve.calls.util.getCalleeExpressionIfAny
/*
private val bundle by lazy {

View File

@ -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
*/
package net.mamoe.mirai.console.intellij.diagnostics
@ -15,7 +15,7 @@ import net.mamoe.mirai.console.compiler.common.resolve.READ_ONLY_PLUGIN_DATA_FQ_
import net.mamoe.mirai.console.intellij.resolve.getResolvedCall
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.diagnostics.Diagnostic
import org.jetbrains.kotlin.idea.refactoring.fqName.getKotlinFqName
import org.jetbrains.kotlin.idea.base.utils.fqname.getKotlinFqName
import org.jetbrains.kotlin.idea.references.mainReference
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.KtElement

View File

@ -1,5 +1,5 @@
/*
* 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.
@ -16,8 +16,8 @@ import com.intellij.openapi.project.rootManager
import com.intellij.openapi.vfs.VfsUtil
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import net.mamoe.mirai.console.intellij.creator.tasks.readChildText
import net.mamoe.mirai.console.intellij.creator.tasks.writeChild
import net.mamoe.mirai.console.intellij.diagnostics.readChildText
import net.mamoe.mirai.console.intellij.diagnostics.writeChild
import org.jetbrains.kotlin.idea.core.isAndroidModule
import org.jetbrains.kotlin.idea.inspections.KotlinUniversalQuickFix
import org.jetbrains.kotlin.idea.quickfix.KotlinCrossLanguageQuickFixAction

View File

@ -14,10 +14,10 @@ import com.intellij.psi.PsiModifier
import net.mamoe.mirai.console.intellij.diagnostics.resolveReferencedType
import org.jetbrains.kotlin.asJava.elements.KtLightMethod
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.idea.base.utils.fqname.getKotlinFqName
import org.jetbrains.kotlin.idea.caches.resolve.resolveToCall
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.getReturnTypeReference
import org.jetbrains.kotlin.idea.refactoring.fqName.fqName
import org.jetbrains.kotlin.idea.refactoring.fqName.getKotlinFqName
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.nj2k.postProcessing.type
import org.jetbrains.kotlin.psi.KtExpression

View File

@ -1,22 +1,23 @@
/*
* 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.resolve
import com.intellij.openapi.project.Project
import com.intellij.psi.*
import net.mamoe.mirai.console.compiler.common.resolve.*
import net.mamoe.mirai.console.compiler.common.resolve.COMPOSITE_COMMAND_SUB_COMMAND_FQ_NAME
import net.mamoe.mirai.console.compiler.common.resolve.SIMPLE_COMMAND_HANDLER_COMMAND_FQ_NAME
import net.mamoe.mirai.console.compiler.common.resolve.findParent
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.base.utils.fqname.getKotlinFqName
import org.jetbrains.kotlin.idea.caches.resolve.resolveToCall
import org.jetbrains.kotlin.idea.refactoring.fqName.fqName
import org.jetbrains.kotlin.idea.refactoring.fqName.getKotlinFqName
import org.jetbrains.kotlin.idea.references.KtSimpleNameReference
import org.jetbrains.kotlin.idea.references.resolveToDescriptors
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
@ -26,10 +27,9 @@ import org.jetbrains.kotlin.nj2k.postProcessing.resolve
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.referenceExpression
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.calls.callUtil.getCall
import org.jetbrains.kotlin.resolve.calls.callUtil.getCalleeExpressionIfAny
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.resolve.calls.util.getCall
import org.jetbrains.kotlin.resolve.calls.util.getResolvedCall
import org.jetbrains.kotlin.resolve.constants.ArrayValue
import org.jetbrains.kotlin.resolve.constants.ConstantValue
import org.jetbrains.kotlin.resolve.constants.StringValue

View File

@ -0,0 +1,27 @@
/*
* 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 net.mamoe.mirai.console.intellij.wizard
enum class BuildSystemType {
GradleKt {
override fun createBuildSystem(model: MiraiProjectModel): ProjectCreator =
GradleKotlinProjectCreator(model)
override fun toString(): String = "Gradle Kotlin DSL"
},
GradleGroovy {
override fun createBuildSystem(model: MiraiProjectModel): ProjectCreator =
GradleGroovyProjectCreator(model)
override fun toString(): String = "Gradle Groovy DSL"
}, ;
abstract fun createBuildSystem(model: MiraiProjectModel): ProjectCreator
}

View File

@ -1,22 +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.steps
package net.mamoe.mirai.console.intellij.wizard
import com.intellij.ide.fileTemplates.FileTemplate
import net.mamoe.mirai.console.intellij.assets.FT
import net.mamoe.mirai.console.intellij.creator.build.ProjectCreator
import net.mamoe.mirai.console.intellij.creator.tasks.getTemplate
import net.mamoe.mirai.console.intellij.creator.templateProperties
data class NamedFile(
val path: String,
val content: String
val template: FileTemplate
)
interface ILanguageType {
@ -37,7 +35,6 @@ sealed class LanguageType : ILanguageType {
abstract fun <T : String?> escapeRawString(string: T): T
companion object {
val DEFAULT = Kotlin
fun values() = arrayOf(Kotlin, Java)
}
@ -47,7 +44,7 @@ sealed class LanguageType : ILanguageType {
override fun pluginMainClassFile(creator: ProjectCreator): NamedFile = creator.model.run {
return NamedFile(
path = "src/main/kotlin/$mainClassSimpleName.kt",
content = creator.project.getTemplate(FT.PluginMainKt, templateProperties)
template = creator.getTemplate(FT.PluginMainKt)
)
}
@ -64,7 +61,7 @@ sealed class LanguageType : ILanguageType {
override fun pluginMainClassFile(creator: ProjectCreator): NamedFile = creator.model.run {
return NamedFile(
path = "src/main/java/${packageName.replace('.', '/')}/$mainClassSimpleName.java",
content = creator.project.getTemplate(FT.PluginMainJava, templateProperties)
template = creator.getTemplate(FT.PluginMainJava)
)
}

View File

@ -0,0 +1,139 @@
/*
* 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 net.mamoe.mirai.console.intellij.wizard
import com.intellij.ide.fileTemplates.FileTemplateManager
import com.intellij.ide.starters.local.*
import com.intellij.ide.starters.local.wizard.StarterInitialStep
import com.intellij.ide.starters.shared.*
import com.intellij.ide.util.projectWizard.ModuleWizardStep
import com.intellij.ide.util.projectWizard.WizardContext
import com.intellij.openapi.module.Module
import com.intellij.openapi.project.ProjectManager
import com.intellij.openapi.roots.ui.configuration.ModulesProvider
import com.intellij.openapi.util.Key
import com.intellij.pom.java.LanguageLevel
import com.intellij.util.lang.JavaVersion
import net.mamoe.mirai.console.intellij.assets.FT
import net.mamoe.mirai.console.intellij.assets.Icons
class MiraiModuleBuilder : StarterModuleBuilder() {
companion object {
val MIRAI_PROJECT_MODEL_KEY = Key.create<MiraiProjectModel>("mirai.project.model")
val GRADLE_GROOVY_PROJECT: StarterProjectType = StarterProjectType("gradleGroovy", "Gradle Groovy DSL")
val GRADLE_KTS_PROJECT: StarterProjectType = StarterProjectType("gradleKts", "Gradle Kotlin DSL")
}
override fun getBuilderId() = "MiraiModuleBuilder"
override fun getPresentableName() = MiraiProjectWizardBundle.message("module.presentation.name")
override fun getWeight() = KOTLIN_WEIGHT - 2
override fun getNodeIcon() = Icons.MainIcon
override fun getDescription(): String = MiraiProjectWizardBundle.message("module.description")
override fun getProjectTypes(): List<StarterProjectType> = listOf(GRADLE_GROOVY_PROJECT, GRADLE_KTS_PROJECT)
override fun getTestFrameworks(): List<StarterTestRunner> = listOf(JUNIT_TEST_RUNNER)
override fun getMinJavaVersion(): JavaVersion = LanguageLevel.JDK_1_8.toJavaVersion()
override fun getStarterPack(): StarterPack {
return StarterPack(
"mirai", listOf(
Starter("mirai", "Mirai Console", getDependencyConfig("/starters/compose.pom"), emptyList())
)
)
}
override fun getLanguages(): List<StarterLanguage> = listOf(JAVA_STARTER_LANGUAGE, KOTLIN_STARTER_LANGUAGE)
override fun createWizardSteps(
wizardContext: WizardContext,
modulesProvider: ModulesProvider
): Array<ModuleWizardStep> = emptyArray()
override fun createOptionsStep(contextProvider: StarterContextProvider): StarterInitialStep {
return MiraiProjectWizardInitialStep(contextProvider)
}
override fun setupModule(module: Module) {
// manually set, we do not show the second page with libraries
starterContext.starter = starterContext.starterPack.starters.first()
starterContext.starterDependencyConfig = loadDependencyConfig()[starterContext.starter?.id]
super.setupModule(module)
}
override fun getTemplateProperties(): Map<String, Any> {
val model = starterContext.getUserData(MIRAI_PROJECT_MODEL_KEY)!!
model.run {
val projectCoordinates = projectCoordinates
val pluginCoordinates = pluginCoordinates
return mapOf<String, Any>(
"KOTLIN_VERSION" to KotlinVersion.CURRENT.toString(),
"MIRAI_VERSION" to miraiVersion,
"GROUP_ID" to projectCoordinates.groupId,
"VERSION" to projectCoordinates.version,
"PROJECT_NAME" to starterContext,
"USE_PROXY_REPO" to "true",
"ARTIFACT_ID" to projectCoordinates.artifactId,
"MODULE_NAME" to projectCoordinates.moduleName,
"PLUGIN_ID" to pluginCoordinates.id,
"PLUGIN_NAME" to languageType.escapeString(pluginCoordinates.name),
"PLUGIN_AUTHOR" to languageType.escapeString(pluginCoordinates.author),
"PLUGIN_INFO" to languageType.escapeRawString(pluginCoordinates.info),
"PLUGIN_DEPENDS_ON" to pluginCoordinates.dependsOn,
"PLUGIN_VERSION" to projectCoordinates.version,
"PACKAGE_NAME" to packageName,
"CLASS_NAME" to mainClassSimpleName,
"LANGUAGE_TYPE" to languageType.toString(),
)
}
}
override fun getAssets(starter: Starter): List<GeneratorAsset> {
val ftManager = FileTemplateManager.getInstance(ProjectManager.getInstance().defaultProject)
val assets = mutableListOf<GeneratorAsset>()
val model = starterContext.getUserData(MIRAI_PROJECT_MODEL_KEY)!!
val standardAssetsProvider = StandardAssetsProvider()
assets.addAll(standardAssetsProvider.getGradlewAssets())
model.buildSystemType.createBuildSystem(model)
.collectAssets { assets.add(it) }
assets.add(
GeneratorTemplateFile(
"src/main/resources/META-INF/services/net.mamoe.mirai.console.plugin.jvm.JvmPlugin",
ftManager.getCodeTemplate(FT.PluginMainService)
)
)
assets.add(GeneratorEmptyDirectory("debug-sandbox"))
assets.add(GeneratorEmptyDirectory("debug-sandbox/plugins"))
assets.add(GeneratorEmptyDirectory("debug-sandbox/data"))
assets.add(GeneratorEmptyDirectory("debug-sandbox/config"))
assets.add(
GeneratorTemplateFile(
"debug-sandbox/account.properties",
ftManager.getCodeTemplate(FT.AccountProperties)
)
)
assets.add(GeneratorTemplateFile(".run/RunTerminal.run.xml", ftManager.getCodeTemplate(FT.RunTerminalRun)))
return assets
}
}

View File

@ -1,20 +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
package net.mamoe.mirai.console.intellij.wizard
import com.intellij.openapi.module.JavaModuleType
import com.intellij.openapi.module.ModuleType
import com.intellij.openapi.module.ModuleTypeManager
import net.mamoe.mirai.console.intellij.assets.Icons
class MiraiModuleType : JavaModuleType() {
class MiraiModuleType : ModuleType<MiraiModuleBuilder>("MIRAI_CONSOLE_PLUGIN_MODULE") {
override fun createModuleBuilder() = MiraiModuleBuilder()
override fun getIcon() = Icons.MainIcon
override fun getNodeIcon(isOpened: Boolean) = Icons.MainIcon
@ -23,7 +23,7 @@ class MiraiModuleType : JavaModuleType() {
"Modules used for developing plugins for <b>Mirai Console</b>"
companion object {
private const val ID = "MIRAI_MODULE_TYPE"
private const val ID = "MIRAI_CONSOLE_PLUGIN_MODULE"
const val NAME = "Mirai"
val instance: MiraiModuleType

View File

@ -0,0 +1,56 @@
/*
* 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 net.mamoe.mirai.console.intellij.wizard
import net.mamoe.mirai.console.intellij.diagnostics.adjustToClassName
import kotlin.contracts.contract
data class ProjectCoordinates(
val groupId: String, // already checked by pattern
val artifactId: String,
val version: String,
val moduleName: String
) {
val packageName: String get() = groupId
}
data class PluginCoordinates(
val id: String,
val name: String,
val author: String,
val info: String,
val dependsOn: String,
)
class MiraiProjectModel(
val projectCoordinates: ProjectCoordinates,
val pluginCoordinates: PluginCoordinates,
val miraiVersion: String,
val buildSystemType: BuildSystemType,
val languageType: LanguageType,
val mainClassSimpleName: String = pluginCoordinates.run {
name.adjustToClassName() ?: id.substringAfterLast('.').adjustToClassName()
} ?: "PluginMain",
val packageName: String = projectCoordinates.checkNotNull("projectCoordinates").groupId,
)
fun <T : Any> T?.checkNotNull(name: String): T {
contract {
returns() implies (this@checkNotNull != null)
}
checkNotNull(this) {
"$name is not yet initialized."
}
return this
}

View File

@ -0,0 +1,264 @@
/*
* 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 net.mamoe.mirai.console.intellij.wizard
import com.intellij.ide.starters.local.StarterContextProvider
import com.intellij.ide.starters.local.wizard.StarterInitialStep
import com.intellij.ide.starters.shared.JAVA_STARTER_LANGUAGE
import com.intellij.ide.starters.shared.KOTLIN_STARTER_LANGUAGE
import com.intellij.ide.starters.shared.ValidationFunctions
import com.intellij.openapi.diagnostic.logger
import com.intellij.openapi.observable.util.trim
import com.intellij.openapi.ui.ComboBox
import com.intellij.openapi.ui.MessageType
import com.intellij.openapi.ui.popup.Balloon
import com.intellij.openapi.ui.popup.JBPopupFactory
import com.intellij.ui.SimpleTextAttributes.GRAYED_ITALIC_ATTRIBUTES
import com.intellij.ui.awt.RelativePoint
import com.intellij.ui.dsl.builder.*
import net.mamoe.mirai.console.intellij.wizard.MiraiProjectWizardBundle.message
import org.jetbrains.concurrency.Promise
import org.jetbrains.concurrency.runAsync
private val log = logger<MiraiProjectWizardInitialStep>()
class MiraiProjectWizardInitialStep(contextProvider: StarterContextProvider) : StarterInitialStep(contextProvider) {
private val pluginVersionProperty = propertyGraph.property("0.1.0")
private val pluginNameProperty = propertyGraph.lazyProperty { "" }
private val pluginIdProperty = propertyGraph.lazyProperty { "" }
private val pluginAuthorProperty = propertyGraph.lazyProperty { System.getProperty("user.name") }
private val pluginDependenciesProperty = propertyGraph.lazyProperty { "" }
private val pluginInfoProperty = propertyGraph.lazyProperty { "" }
private val miraiVersionKindProperty = propertyGraph.property(MiraiVersionKind.Stable)
private val miraiVersionProperty = propertyGraph.property("0.1.0")
var pluginVersion by pluginVersionProperty.trim()
var pluginName by pluginNameProperty.trim()
var pluginId by pluginIdProperty.trim()
var pluginAuthor by pluginAuthorProperty.trim()
var pluginDependencies by pluginDependenciesProperty.trim()
var pluginInfo by pluginInfoProperty.trim()
var miraiVersionKind by miraiVersionKindProperty
var miraiVersion by miraiVersionProperty
private lateinit var miraiVersionCell: Cell<ComboBox<String>>
override fun addFieldsAfter(layout: Panel) {
layout.group(message("title.plugin.description")) {
row(message("label.plugin.id")) {
textField()
.withSpecialValidation(
ValidationFunctions.CHECK_NOT_EMPTY,
)
.bindText(pluginIdProperty)
rowComment(message("comment.plugin.id"))
pluginIdProperty.dependsOn(groupIdProperty) { "$groupId.$artifactId" }
pluginIdProperty.dependsOn(artifactIdProperty) { "$groupId.$artifactId" }
}
row(message("label.plugin.name")) {
textField()
.withSpecialValidation(
ValidationFunctions.CHECK_NOT_EMPTY,
MiraiValidations.CHECK_FORBIDDEN_PLUGIN_NAME,
)
.bindText(pluginNameProperty)
pluginNameProperty.dependsOn(artifactIdProperty) {
artifactId.adjustToPresentationName()
}
rowComment(message("comment.plugin.name"))
}
row(message("label.plugin.version")) {
textField()
.withSpecialValidation(
ValidationFunctions.CHECK_NOT_EMPTY,
MiraiValidations.CHECK_ILLEGAL_VERSION_LINE
)
.bindText(pluginVersionProperty)
rowComment(message("comment.plugin.version"))
}
row(message("label.plugin.author")) {
textField().bindText(pluginAuthorProperty)
}
row(message("label.plugin.dependencies")) {
expandableTextField()
.withSpecialValidation(MiraiValidations.CHECK_PLUGIN_DEPENDENCIES_SEGMENT)
.bindText(pluginDependenciesProperty)
.component.emptyText.setText(message("text.hint.plugin.dependencies"), GRAYED_ITALIC_ATTRIBUTES)
rowComment(message("comment.plugin.dependencies"))
}
row(message("label.plugin.info")) {
expandableTextField().bindText(pluginInfoProperty)
.component.emptyText.setText(message("text.hint.plugin.info"), GRAYED_ITALIC_ATTRIBUTES)
rowComment(message("comment.plugin.info"))
}
row(message("label.mirai.version")) {
val miraiVersionKindCell = segmentedButton(MiraiVersionKind.values().toList()) { kind ->
when (kind) {
MiraiVersionKind.Stable -> message("label.version.stable")
MiraiVersionKind.Prerelease -> message("label.version.prerelease")
MiraiVersionKind.Nightly -> message("label.version.nightly")
}
}.bind(miraiVersionKindProperty)
miraiVersionCell = comboBox(listOf<String>())
.enabled(false)
.bindItem(miraiVersionProperty)
miraiVersionKindProperty.afterChange {
if (!miraiVersionCell.component.isEnabled) return@afterChange
updateVersionItems(miraiVersionKindCell, miraiVersionCell)
}
updateVersionItems(miraiVersionKindCell, miraiVersionCell)
rowComment(message("comment.mirai.version"))
}
}
// Update default values
languageProperty.set(KOTLIN_STARTER_LANGUAGE)
projectTypeProperty.set(MiraiModuleBuilder.GRADLE_KTS_PROJECT)
pluginIdProperty.set("$groupId.$artifactId")
pluginNameProperty.set(artifactId.adjustToPresentationName())
}
override fun updateDataModel() {
super.updateDataModel()
starterContext.putUserData(
/* key = */ MiraiModuleBuilder.MIRAI_PROJECT_MODEL_KEY,
/* value = */ MiraiProjectModel(
projectCoordinates = ProjectCoordinates(
groupId = groupId.trim(),
artifactId = artifactId.trim(),
version = pluginVersion.trim(),
moduleName = entityName
),
pluginCoordinates = PluginCoordinates(
id = pluginId.trim(),
name = pluginName.trim(),
author = pluginAuthor.trim(),
info = pluginInfo.trim(),
dependsOn = pluginDependencies.trim()
),
miraiVersion = miraiVersion,
buildSystemType = when (val projectType = projectTypeProperty.get()) {
MiraiModuleBuilder.GRADLE_KTS_PROJECT -> BuildSystemType.GradleKt
MiraiModuleBuilder.GRADLE_GROOVY_PROJECT -> BuildSystemType.GradleGroovy
else -> error("Unsupported project type: $projectType")
},
languageType = when (val language = languageProperty.get()) {
KOTLIN_STARTER_LANGUAGE -> LanguageType.Kotlin
JAVA_STARTER_LANGUAGE -> LanguageType.Java
else -> error("Unsupported language type: $language")
}
)
)
}
override fun validate(): Boolean {
if (miraiVersion == message("label.mirai.version.loading")) {
JBPopupFactory.getInstance()
.createHtmlTextBalloonBuilder(
message("error.please.wait.for.mirai.version"),
MessageType.WARNING, null
)
.setFadeoutTime(3000)
.createBalloon()
.show(
RelativePoint.getSouthWestOf(
miraiVersionCell.component
), Balloon.Position.atLeft
)
return false
}
return super.validate()
}
private fun updateVersionItems(
miraiVersionKindCell: SegmentedButton<MiraiVersionKind>,
miraiVersionCell: Cell<ComboBox<MiraiVersion>>
): Promise<Set<MiraiVersion>?> {
miraiVersionCell.component.isEditable = false
miraiVersionKindCell.enabled(false) // disable the kind selector until the async operation finishes
miraiVersionCell.enabled(false)
miraiVersionCell.component.removeAllItems()
miraiVersionCell.component.addItem(message("label.mirai.version.loading"))
return runAsync {
try {
val list = MiraiVersionKind.getMiraiVersionList()
miraiVersionCell.component.removeAllItems()
list.filter { miraiVersionKind.isThatKind(it) }
.forEach { v -> miraiVersionCell.component.addItem(v) }
list
} catch (e: Throwable) {
JBPopupFactory.getInstance()
.createHtmlTextBalloonBuilder(
message("error.failed.to.download.mirai.version"),
MessageType.ERROR, null
)
.setFadeoutTime(2000)
.createBalloon()
.show(
RelativePoint.getSouthOf(
miraiVersionCell.component
), Balloon.Position.below
)
null
}
}.onError { log.error(it) }
.onProcessed { versions ->
miraiVersionCell.component.isEditable = versions == null
miraiVersionKindCell.enabled(true)
miraiVersionCell.enabled(true)
}
}
}
private fun String.adjustToPresentationName(): String {
val result = buildString {
var doCapitalization = true
fun Char.isAllowed() = isLetterOrDigit() || this in "_- "
for (char in this@adjustToPresentationName) {
if (!char.isAllowed()) continue
if (doCapitalization) {
when {
char.isLetter() -> append(char.uppercase())
char == '_' -> {}
char == '-' -> {}
else -> append(char)
}
doCapitalization = false
} else {
if (char in "_- ") {
doCapitalization = true
append(' ')
} else {
append(char)
}
}
}
}.trim()
return result
}

View File

@ -0,0 +1,115 @@
/*
* 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 net.mamoe.mirai.console.intellij.wizard
import com.intellij.ide.starters.shared.TextValidationFunction
import net.mamoe.mirai.console.compiler.common.CheckerConstants
import net.mamoe.mirai.console.intellij.util.RequirementHelper
import net.mamoe.mirai.console.intellij.util.RequirementParser
import net.mamoe.mirai.console.intellij.wizard.MiraiProjectWizardBundle.message
object MiraiValidations {
val CHECK_FORBIDDEN_PLUGIN_NAME = TextValidationFunction { text ->
val lowercaseName = text.lowercase().trim()
val illegal =
CheckerConstants.PLUGIN_FORBIDDEN_NAMES.firstOrNull { it == lowercaseName }
if (illegal != null) {
message("validation.plugin.name.forbidden.character", illegal)
} else null
}
val CHECK_PLUGIN_ID = TextValidationFunction { text ->
if (!CheckerConstants.PLUGIN_ID_REGEX.matches(text.trim())) {
message("validation.illegal.plugin.id", text)
} else null
}
val CHECK_ILLEGAL_VERSION_LINE = TextValidationFunction { text ->
checkVersionLine(text)?.let {
message("validation.illegal.version", text, it)
}
}
val CHECK_PLUGIN_DEPENDENCIES_LINE = TextValidationFunction { text ->
try {
val trim = text.trim()
val dep = PluginDependency.parseFromString(trim)
if (!CheckerConstants.PLUGIN_ID_REGEX.matches(dep.id)) {
return@TextValidationFunction message("validation.illegal.plugin.id", dep.id)
}
dep.versionRequirement?.let { checkVersionRequirementLine(it) }?.let {
return@TextValidationFunction message("validation.illegal.version", it)
}
null // no error
} catch (e: IllegalArgumentException) {
message("validation.illegal.version", text, e.message ?: message("no.error.message"))
}
}
val CHECK_PLUGIN_DEPENDENCIES_SEGMENT = TextValidationFunction { text ->
text.lineSequence()
.map { it.trim() }
.filter { it.isNotEmpty() }
.map { CHECK_PLUGIN_DEPENDENCIES_LINE.checkText(it) }
.firstOrNull()
}
/**
* Check multiple lines and returns information about the first illegal one.
*/
val CHECK_ILLEGAL_VERSION_SEGMENT = TextValidationFunction { text ->
text.lineSequence()
.map { it.trim() }
.filter { it.isNotEmpty() }
.mapNotNull { CHECK_ILLEGAL_VERSION_LINE.checkText(text) }
.firstOrNull()
}
private class PluginDependency @JvmOverloads constructor(
val id: String,
val versionRequirement: String? = null,
) {
companion object {
/**
* Frozen version of [net.mamoe.mirai.console.plugin.description.PluginDescription.Companion.parseFromString] from `2.11.0-M2.2`.
*/
@JvmStatic
fun parseFromString(string: String): PluginDependency {
// val optional = string.endsWith('?')
val (id, version) = string.removeSuffix("?").let { rule ->
if (rule.contains(':')) {
rule.substringBeforeLast(':') to rule.substringAfterLast(':')
} else {
rule to null
}
}
return PluginDependency(id, version)
}
}
}
private fun checkVersionLine(version: String): String? {
return checkVersionRequirementLine(version) // for simplicity
}
private fun checkVersionRequirementLine(versionRequirement: String): String? {
kotlin.runCatching {
RequirementHelper.RequirementChecker.processLine(
RequirementParser.TokenReader(
versionRequirement
)
)
}.onFailure { return it.message ?: message("no.error.message") }
return null
}
}

View File

@ -8,7 +8,7 @@
*/
package net.mamoe.mirai.console.intellij.creator
package net.mamoe.mirai.console.intellij.wizard
import com.intellij.openapi.diagnostic.Logger
import com.intellij.util.text.SemVer
@ -24,7 +24,7 @@ enum class MiraiVersionKind {
},
Prerelease {
override fun isThatKind(version: String): Boolean =
version.contains("-M") || version.contains("-RC")
Stable.isThatKind(version) || version.contains("-M") || version.contains("-RC")
},
Nightly {
override fun isThatKind(version: String): Boolean = true // version.contains("-dev")

View File

@ -0,0 +1,31 @@
/*
* 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 net.mamoe.mirai.console.intellij.wizard
import com.intellij.AbstractBundle
import org.jetbrains.annotations.Nls
import org.jetbrains.annotations.NonNls
import org.jetbrains.annotations.PropertyKey
import java.util.function.Supplier
@NonNls
private const val BUNDLE = "messages.MiraiProjectWizardBundle"
object MiraiProjectWizardBundle : AbstractBundle(BUNDLE) {
@Nls
@JvmStatic
fun message(@NonNls @PropertyKey(resourceBundle = BUNDLE) key: String, vararg params: Any): String =
getMessage(key, *params)
@JvmStatic
fun messagePointer(@PropertyKey(resourceBundle = BUNDLE) key: String, vararg params: Any): Supplier<String> {
return getLazyMessage(key, *params)
}
}

View File

@ -0,0 +1,82 @@
/*
* 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 net.mamoe.mirai.console.intellij.wizard
import com.intellij.ide.fileTemplates.FileTemplate
import com.intellij.ide.fileTemplates.FileTemplateManager
import com.intellij.ide.starters.local.GeneratorAsset
import com.intellij.ide.starters.local.GeneratorEmptyDirectory
import com.intellij.ide.starters.local.GeneratorTemplateFile
import com.intellij.openapi.project.ProjectManager
import net.mamoe.mirai.console.intellij.assets.FT
sealed class ProjectCreator(
val model: MiraiProjectModel,
) {
private val manager get() = FileTemplateManager.getInstance(ProjectManager.getInstance().defaultProject)
fun getTemplate(name: String): FileTemplate = manager.getCodeTemplate(name)
abstract fun collectAssets(
collect: (GeneratorAsset) -> Unit,
)
}
sealed class GradleProjectCreator(
model: MiraiProjectModel,
) : ProjectCreator(model) {
override fun collectAssets(
collect: (GeneratorAsset) -> Unit
) {
collect(GeneratorEmptyDirectory("src/main/${model.languageType.sourceSetDirName}"))
collect(GeneratorEmptyDirectory("src/main/resources"))
collect(GeneratorTemplateFile(model.languageType.pluginMainClassFile(this)))
collect(GeneratorTemplateFile(".gitignore", getTemplate(FT.Gitignore)))
collect(GeneratorTemplateFile("gradle.properties", getTemplate(FT.GradleProperties)))
collect(GeneratorTemplateFile("src/test/kotlin/RunTerminal.kt", getTemplate(FT.RunTerminal)))
}
}
private fun GeneratorTemplateFile(targetFileName: NamedFile): GeneratorTemplateFile {
return GeneratorTemplateFile(targetFileName.path, targetFileName.template)
}
class GradleKotlinProjectCreator(
model: MiraiProjectModel,
) : GradleProjectCreator(
model,
) {
override fun collectAssets(
collect: (GeneratorAsset) -> Unit
) {
super.collectAssets(collect)
collect(GeneratorTemplateFile("build.gradle.kts", getTemplate(FT.BuildGradleKts)))
collect(GeneratorTemplateFile("settings.gradle.kts", getTemplate(FT.SettingsGradleKts)))
}
}
class GradleGroovyProjectCreator(
model: MiraiProjectModel,
) : GradleProjectCreator(
model,
) {
override fun collectAssets(
collect: (GeneratorAsset) -> Unit
) {
super.collectAssets(collect)
collect(GeneratorTemplateFile("build.gradle", getTemplate(FT.BuildGradle)))
collect(GeneratorTemplateFile("settings.gradle", getTemplate(FT.SettingsGradle)))
}
}

View File

@ -8,7 +8,7 @@
*/
package creator
import net.mamoe.mirai.console.intellij.creator.sortVersionsDescending
import net.mamoe.mirai.console.intellij.wizard.sortVersionsDescending
import org.junit.jupiter.api.Test
import kotlin.test.assertEquals

View File

@ -1,14 +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.tasks
import net.mamoe.mirai.console.intellij.diagnostics.adjustToClassName
import net.mamoe.mirai.console.intellij.diagnostics.isValidPackageName
import net.mamoe.mirai.console.intellij.diagnostics.isValidQualifiedClassName
import net.mamoe.mirai.console.intellij.diagnostics.isValidSimpleClassName
import org.junit.jupiter.api.Test
import kotlin.test.assertEquals
import kotlin.test.assertTrue