Browse Source

Merge remote-tracking branch 'origin/master'

Him188 5 years ago
parent
commit
cd86878307
22 changed files with 380 additions and 159 deletions
  1. 2 2
      .github/workflows/bintray.yml
  2. 2 2
      .github/workflows/shadow.yml
  3. 3 4
      README.md
  4. 5 0
      backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/MiraiConsole.kt
  5. 3 1
      backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/plugin/BuiltInJvmPluginLoaderImpl.kt
  6. 1 0
      backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/plugin/JvmPluginInternal.kt
  7. 2 2
      backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/util/SemVersionInternal.kt
  8. 29 26
      backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/util/SemVersion.kt
  9. 93 17
      backend/mirai-console/src/test/kotlin/net/mamoe/mirai/console/util/TestSemVersion.kt
  10. 1 2
      buildSrc/src/main/kotlin/Versions.kt
  11. 8 8
      docs/Run.md
  12. 3 3
      frontend/mirai-console-terminal/build.gradle.kts
  13. 34 0
      frontend/mirai-console-terminal/src/main/kotlin/net/mamoe/mirai/console/pure/MiraiConsolePureLoader.kt
  14. 2 1
      frontend/mirai-console-terminal/src/main/kotlin/net/mamoe/mirai/console/terminal/BufferedOutputStream.kt
  15. 74 0
      frontend/mirai-console-terminal/src/main/kotlin/net/mamoe/mirai/console/terminal/ConsoleInputImpl.kt
  16. 5 4
      frontend/mirai-console-terminal/src/main/kotlin/net/mamoe/mirai/console/terminal/ConsoleTerminalSettings.kt
  17. 25 12
      frontend/mirai-console-terminal/src/main/kotlin/net/mamoe/mirai/console/terminal/ConsoleThread.kt
  18. 46 40
      frontend/mirai-console-terminal/src/main/kotlin/net/mamoe/mirai/console/terminal/MiraiConsoleImplementationTerminal.kt
  19. 28 21
      frontend/mirai-console-terminal/src/main/kotlin/net/mamoe/mirai/console/terminal/MiraiConsoleTerminalLoader.kt
  20. 11 11
      frontend/mirai-console-terminal/src/main/kotlin/net/mamoe/mirai/console/terminal/noconsole/NoConsole.kt
  21. 1 1
      settings.gradle.kts
  22. 2 2
      tools/intellij-plugin/run/projects/test-project/src/test/kotlin/RunConsole.kt

+ 2 - 2
.github/workflows/bintray.yml

@@ -32,6 +32,6 @@ jobs:
         run: ./gradlew :mirai-console:fillBuildConstants -Dbintray_user=${{ secrets.BINTRAY_USER }} -Pbintray_user=${{ secrets.BINTRAY_USER }} -Dbintray_key=${{ secrets.BINTRAY_KEY }} -Pbintray_key=${{ secrets.BINTRAY_KEY }}
       - name: Gradle :mirai-console:bintrayUpload
         run: ./gradlew :mirai-console:bintrayUpload -Dbintray_user=${{ secrets.BINTRAY_USER }} -Pbintray_user=${{ secrets.BINTRAY_USER }} -Dbintray_key=${{ secrets.BINTRAY_KEY }} -Pbintray_key=${{ secrets.BINTRAY_KEY }}
-      - name: Gradle :mirai-console-pure:bintrayUpload
-        run: ./gradlew :mirai-console-pure:bintrayUpload -Dbintray_user=${{ secrets.BINTRAY_USER }} -Pbintray_user=${{ secrets.BINTRAY_USER }} -Dbintray_key=${{ secrets.BINTRAY_KEY }} -Pbintray_key=${{ secrets.BINTRAY_KEY }}
+      - name: Gradle :mirai-console-terminal:bintrayUpload
+        run: ./gradlew :mirai-console-terminal:bintrayUpload -Dbintray_user=${{ secrets.BINTRAY_USER }} -Pbintray_user=${{ secrets.BINTRAY_USER }} -Dbintray_key=${{ secrets.BINTRAY_KEY }} -Pbintray_key=${{ secrets.BINTRAY_KEY }}
 

+ 2 - 2
.github/workflows/shadow.yml

@@ -28,8 +28,8 @@ jobs:
         run: ./gradlew build # if test's failed, don't publish
       - name: Gradle :mirai-console:githubUpload
         run: ./gradlew :mirai-console:githubUpload -Dgithub_token=${{ secrets.MAMOE_TOKEN }} -Pgithub_token=${{ secrets.MAMOE_TOKEN }}
-      - name: Gradle :mirai-console-pure:githubUpload
-        run: ./gradlew :mirai-console-pure:githubUpload -Dgithub_token=${{ secrets.MAMOE_TOKEN }} -Pgithub_token=${{ secrets.MAMOE_TOKEN }}
+      - name: Gradle :mirai-console-terminal:githubUpload
+        run: ./gradlew :mirai-console-terminal:githubUpload -Dgithub_token=${{ secrets.MAMOE_TOKEN }} -Pgithub_token=${{ secrets.MAMOE_TOKEN }}
 
 
 #      - name: Upload artifact

+ 3 - 4
README.md

@@ -28,12 +28,11 @@ console 由后端和前端一起工作. 使用时必须选择一个前端.
 
 前端:
 
-- `mirai-console-pure`: console 的轻量命令行前端.
+- `mirai-console-terminal`: console 的 Unix 终端界面前端.
 - `mirai-console-graphical`: console 的 JavaFX 图形化界面前端. (开发中)
-- `mirai-console-terminal`: console 的 Unix 终端界面前端. (开发中)
 
 
-**注意:`mirai-console` 后端和 pure 前端正在进行完全的重构, 所有 API 都不具有稳定性**
+**注意:`mirai-console` 后端和 terminal 前端正在进行完全的重构, 所有 API 都不具有稳定性**
 
 ### 使用
 
@@ -56,7 +55,7 @@ dependencies {
   implementation("net.mamoe:mirai-core:$CORE_VERSION") // mirai-core 的 API
   implementation("net.mamoe:mirai-console:$CONSOLE_VERSION") // 后端
   
-  testImplementation("net.mamoe:mirai-console-pure:$CONSOLE_VERSION") // 前端, 用于启动测试
+  testImplementation("net.mamoe:mirai-console-terminal:$CONSOLE_VERSION") // 前端, 用于启动测试
 }
 ```
 

+ 5 - 0
backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/MiraiConsole.kt

@@ -18,6 +18,7 @@ import kotlinx.coroutines.Job
 import net.mamoe.mirai.Bot
 import net.mamoe.mirai.console.MiraiConsole.INSTANCE
 import net.mamoe.mirai.console.MiraiConsoleImplementation.Companion.start
+import net.mamoe.mirai.console.command.BuiltInCommands
 import net.mamoe.mirai.console.extensions.BotConfigurationAlterer
 import net.mamoe.mirai.console.internal.MiraiConsoleImplementationBridge
 import net.mamoe.mirai.console.internal.extension.GlobalComponentStorage
@@ -146,6 +147,10 @@ public interface MiraiConsole : CoroutineScope {
                 else -> null!!
             }
         }
+
+        @ConsoleExperimentalApi("This is a low-level API and might be removed in the future.")
+        public val isActive: Boolean
+            get() = job.isActive
     }
 }
 

+ 3 - 1
backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/plugin/BuiltInJvmPluginLoaderImpl.kt

@@ -106,7 +106,9 @@ internal object BuiltInJvmPluginLoaderImpl :
 
     override fun disable(plugin: JvmPlugin) {
         if (!plugin.isEnabled) return
-        ensureActive()
+
+        if (MiraiConsole.isActive)
+            ensureActive()
 
         if (plugin is JvmPluginInternal) {
             plugin.internalOnDisable()

+ 1 - 0
backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/plugin/JvmPluginInternal.kt

@@ -151,6 +151,7 @@ internal abstract class JvmPluginInternal(
                 )
             )
             .also {
+                if (!MiraiConsole.isActive) return@also
                 BuiltInJvmPluginLoaderImpl.coroutineContext[Job]!!.invokeOnCompletion {
                     this.cancel()
                 }

+ 2 - 2
backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/util/SemVersionInternal.kt

@@ -184,8 +184,8 @@ internal object SemVersionInternal {
         identifier0 = identifier0.substring(ignoredSize)
         identifier1 = identifier1.substring(ignoredSize)
         // Multi-chunk comparing
-        val chunks0 = identifier0.split('-', '.', '_')
-        val chunks1 = identifier1.split('-', '.', '_')
+        val chunks0 = identifier0.split('-', '.')
+        val chunks1 = identifier1.split('-', '.')
         chunkLoop@ for (index in 0 until (max(chunks0.size, chunks1.size))) {
             val value0 = chunks0.getOrNull(index)
             val value1 = chunks1.getOrNull(index)

+ 29 - 26
backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/util/SemVersion.kt

@@ -14,8 +14,11 @@
 
 package net.mamoe.mirai.console.util
 
+import kotlinx.serialization.KSerializer
 import kotlinx.serialization.Serializable
 import kotlinx.serialization.Transient
+import kotlinx.serialization.builtins.serializer
+import net.mamoe.mirai.console.internal.data.map
 import net.mamoe.mirai.console.internal.util.SemVersionInternal
 import net.mamoe.mirai.console.util.SemVersion.Companion.equals
 
@@ -39,16 +42,14 @@ import net.mamoe.mirai.console.util.SemVersion.Companion.equals
  *   metadata    = "c25733b8"
  * )
  * ```
- * 其中 identifier 和 metadata 都是可选的, 该实现对于 mainVersion 的最大长度不作出限制,
- * 也建议 mainVersion 的长度不要过长或过短
- * 但是必须至少拥有两位及以上的版本描述符, (即必须拥有主版本号和次版本号).
- *
- * 比如 `1-M4` 是不合法的, 但是 `1.0-M4` 是合法的
+ * 其中 identifier 和 metadata 都是可选的.
+ * 对于核心版本号, 此实现稍微比 semver 宽松一些, 允许 x.y 的存在.
+ * 但是不允许 0.0.0.0 之类的存在
  *
  */
-@Serializable
+@Serializable(with = SemVersion.SemVersionAsStringSerializer::class)
 public data class SemVersion internal constructor(
-    /** 核心版本号, 至少包含一个主版本号和一个次版本号 */
+    /** 核心版本号, 由主版本号, 次版本号和修订号组成, 其中修订号不一定存在 */
     public val mainVersion: IntArray,
     /** 先行版本号识别符 */
     public val identifier: String? = null,
@@ -64,7 +65,15 @@ public data class SemVersion internal constructor(
         public fun check(version: SemVersion): Boolean
     }
 
+    public object SemVersionAsStringSerializer : KSerializer<SemVersion> by String.serializer().map(
+        serializer = { it.toString() },
+        deserializer = { parse(it) }
+    )
+
     public companion object {
+        private val SEM_VERSION_REGEX =
+            """^(0|[1-9]\d*)\.(0|[1-9]\d*)(?:\.(0|[1-9]\d*))?(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$""".toRegex()
+
         /** 解析核心版本号, eg: `1.0.0` -> IntArray[1, 0, 0] */
         @JvmStatic
         private fun String.parseMainVersion(): IntArray =
@@ -78,14 +87,23 @@ public data class SemVersion internal constructor(
          * - 必须包含主版本号和次版本号
          * - 存在 先行版本号 的时候 先行版本号 不能为空
          * - 存在 元数据 的时候 元数据 不能为空
+         * - 核心版本号只允许 `x.y` 和 `x.y.z` 的存在
+         *     - `1.0-RC` 是合法的
+         *     - `1.0.0-RC` 也是合法的, 与 `1.0-RC` 一样
+         *     - `1.0.0.0-RC` 是不合法的, 将会抛出一个 [IllegalArgumentException]
          *
          * 注意情况:
          * - 第一个 `+` 之后的所有内容全部识别为元数据
          *     - `1.0+METADATA-M4`, metadata="METADATA-M4"
+         * - 如果不确定版本号是否合法, 可以使用 [regex101.com](https://regex101.com/r/vkijKf/1/) 进行检查
+         *     - 此实现使用的正则表达式为 `^(0|[1-9]\d*)\.(0|[1-9]\d*)(?:\.(0|[1-9]\d*))?(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$`
          */
         @Throws(IllegalArgumentException::class, NumberFormatException::class)
         @JvmStatic
         public fun parse(version: String): SemVersion {
+            if (!SEM_VERSION_REGEX.matches(version)) {
+                throw IllegalArgumentException("`$version` not a valid version")
+            }
             var mainVersionEnd: Int = 0
             kotlin.run {
                 val iterator = version.iterator()
@@ -116,24 +134,9 @@ public data class SemVersion internal constructor(
                 }
             }
             return SemVersion(
-                mainVersion = version.substring(0, mainVersionEnd).also { mainVersion ->
-                    if (mainVersion.indexOf('.') == -1) {
-                        throw IllegalArgumentException("$mainVersion must has more than one label")
-                    }
-                    if (mainVersion.last() == '.') {
-                        throw IllegalArgumentException("Version string cannot end-with `.`")
-                    }
-                }.parseMainVersion(),
-                identifier = identifier?.also {
-                    if (it.isBlank()) {
-                        throw IllegalArgumentException("The identifier cannot be blank.")
-                    }
-                },
-                metadata = metadata?.also {
-                    if (it.isBlank()) {
-                        throw IllegalArgumentException("The metadata cannot be blank.")
-                    }
-                }
+                mainVersion = version.substring(0, mainVersionEnd).parseMainVersion(),
+                identifier = identifier,
+                metadata = metadata
             )
         }
 
@@ -153,7 +156,7 @@ public data class SemVersion internal constructor(
          *
          * 对于多个规则, 也允许使用 `||` 拼接在一起.
          * 例如:
-         * - `1.x || 2.x || 3.0`
+         * - `1.x || 2.x || 3.0.0`
          * - `<= 0.5.3 || >= 1.0.0`
          *
          * 特别注意:

+ 93 - 17
backend/mirai-console/src/test/kotlin/net/mamoe/mirai/console/util/TestSemVersion.kt

@@ -23,7 +23,7 @@ internal class TestSemVersion {
         fun String.sem(): SemVersion = SemVersion.parse(this)
         assert("1.0".sem() < "1.0.1".sem())
         assert("1.0.0".sem() == "1.0".sem())
-        assert("1.1".sem() > "1.0.0.1".sem())
+        assert("1.1".sem() > "1.0.0".sem())
         assert("1.0-M4".sem() < "1.0-M5".sem())
         assert("1.0-M5-dev-7".sem() < "1.0-M5-dev-15".sem())
         assert("1.0-M5-dev-79".sem() < "1.0-M5-dev-7001".sem())
@@ -54,7 +54,6 @@ internal class TestSemVersion {
         }
         SemVersion.parseRangeRequirement("1.0")
             .assert("1.0").assert("1.0.0")
-            .assert("1.0.0.0")
             .assertFalse("1.1.0").assertFalse("2.0.0")
         SemVersion.parseRangeRequirement("1.x")
             .assert("1.0").assert("1.1")
@@ -62,12 +61,12 @@ internal class TestSemVersion {
             .assertFalse("2.33")
         SemVersion.parseRangeRequirement("2.0 || 1.2.x")
             .assert("2.0").assert("2.0.0")
-            .assertFalse("2.1").assertFalse("2.0.0.1")
+            .assertFalse("2.1")
             .assert("1.2.5").assert("1.2.0").assertFalse("1.2")
             .assertFalse("1.0.0")
-        SemVersion.parseRangeRequirement("1.0.0 - 114.514.1919.810")
+        SemVersion.parseRangeRequirement("1.0.0 - 114.514.1919")
             .assert("1.0.0")
-            .assert("114.514").assert("114.514.1919.810")
+            .assert("114.514").assert("114.514.1919")
             .assertFalse("0.0.1")
             .assertFalse("4444.4444")
         SemVersion.parseRangeRequirement("[1.0.0, 19190.0]")
@@ -75,7 +74,7 @@ internal class TestSemVersion {
             .assert("19190.0").assertFalse("19198.10")
         SemVersion.parseRangeRequirement(" >= 1.0.0")
             .assert("1.0.0")
-            .assert("114.514.1919.810")
+            .assert("114.514.1919")
             .assertFalse("0.0.0")
             .assertFalse("0.98774587")
         SemVersion.parseRangeRequirement("> 1.0.0")
@@ -85,21 +84,21 @@ internal class TestSemVersion {
 
     }
 
+    private fun String.check() {
+        val sem = SemVersion.parse(this)
+        assert(this == sem.toString()) { "$this != $sem" }
+    }
+
+    private fun String.checkInvalid() {
+        kotlin.runCatching { SemVersion.parse(this) }
+            .onSuccess { assert(false) { "$this not a invalid sem-version" } }
+    }
+
     @Test
     internal fun testSemVersionParsing() {
-        fun String.check() {
-            val sem = SemVersion.parse(this)
-            assert(this == sem.toString()) { "$this != $sem" }
-        }
-
-        fun String.checkInvalid() {
-            kotlin.runCatching { SemVersion.parse(this) }
-                .onSuccess { assert(false) { "$this not a invalid sem-version" } }
-                .onFailure { println("$this - $it") }
-        }
         "0.0".check()
         "1.0.0".check()
-        "1.2.3.4.5.6.7.8".check()
+        "1.2.3.4.5.6.7.8".checkInvalid()
         "5555.0-A".check()
         "5555.0-A+METADATA".check()
         "5555.0+METADATA".check()
@@ -113,4 +112,81 @@ internal class TestSemVersion {
         "5.1+68-7".check()
         "5.1+68-".check()
     }
+    @Test
+    internal fun testSemVersionOfficial(){
+        """
+            1.0-RC
+            0.0.4
+            1.2.3
+            10.20.30
+            1.1.2-prerelease+meta
+            1.1.2+meta
+            1.1.2+meta-valid
+            1.0.0-alpha
+            1.0.0-beta
+            1.0.0-alpha.beta
+            1.0.0-alpha.beta.1
+            1.0.0-alpha.1
+            1.0.0-alpha0.valid
+            1.0.0-alpha.0valid
+            1.0.0-alpha-a.b-c-somethinglong+build.1-aef.1-its-okay
+            1.0.0-rc.1+build.1
+            2.0.0-rc.1+build.123
+            1.2.3-beta
+            10.2.3-DEV-SNAPSHOT
+            1.2.3-SNAPSHOT-123
+            1.0.0
+            2.0.0
+            1.1.7
+            2.0.0+build.1848
+            2.0.1-alpha.1227
+            1.0.0-alpha+beta
+            1.2.3----RC-SNAPSHOT.12.9.1--.12+788
+            1.2.3----R-S.12.9.1--.12+meta
+            1.2.3----RC-SNAPSHOT.12.9.1--.12
+            1.0.0+0.build.1-rc.10000aaa-kk-0.1
+            1.0.0-0A.is.legal
+        """.trimIndent().split('\n').asSequence()
+            .filter { it.isNotBlank() }.map { it.trim() }.forEach { it.check() }
+        """
+            1
+            1.2.3-0123
+            1.2.3-0123.0123
+            1.1.2+.123
+            +invalid
+            -invalid
+            -invalid+invalid
+            -invalid.01
+            alpha
+            alpha.beta
+            alpha.beta.1
+            alpha.1
+            alpha+beta
+            alpha_beta
+            alpha.
+            alpha..
+            beta
+            1.0.0-alpha_beta
+            -alpha.
+            1.0.0-alpha..
+            1.0.0-alpha..1
+            1.0.0-alpha...1
+            1.0.0-alpha....1
+            1.0.0-alpha.....1
+            1.0.0-alpha......1
+            1.0.0-alpha.......1
+            01.1.1
+            1.01.1
+            1.1.01
+            1.2.3.DEV
+            1.2.31.2.3----RC-SNAPSHOT.12.09.1--..12+788
+            1.2.31.2.3-RC
+            -1.0.3-gamma+b7718
+            +justmeta
+            9.8.7+meta+meta
+            9.8.7-whatever+meta+meta
+            99999999999999999999999.999999999999999999.99999999999999999----RC-SNAPSHOT.12.09.1--------------------------------..12
+        """.trimIndent().split('\n').asSequence()
+            .filter { it.isNotBlank() }.map { it.trim() }.forEach { it.checkInvalid() }
+    }
 }

+ 1 - 2
buildSrc/src/main/kotlin/Versions.kt

@@ -11,8 +11,7 @@ object Versions {
     const val core = "1.3.0"
     const val console = "1.0-RC-dev-3"
     const val consoleGraphical = "0.0.7"
-    const val consoleTerminal = "0.1.0"
-    const val consolePure = console
+    const val consoleTerminal = console
 
     const val kotlinCompiler = "1.4.10"
     const val kotlinStdlib = kotlinCompiler

+ 8 - 8
docs/Run.md

@@ -19,16 +19,16 @@ https://github.com/LXY1226/MiraiOK
 - mirai-console 任一前端
 - 相关依赖
 
-只有 mirai-console 前端才有入口点 `main` 方法。目前只有一个 pure 前端可用。
+只有 mirai-console 前端才有入口点 `main` 方法。目前只有一个 terminal 前端可用。
 
-### 启动 mirai-console-pure 前端
+### 启动 mirai-console-terminal 前端
 
 mirai 在版本发布时会同时发布打包依赖的 Shadow JAR,存放在 [mirai-repo]。
 
 1. 在 [mirai-repo] 下载如下三个模块的最新版本文件并放到一个文件夹内 (如 `libs`):
    - mirai-core-qqandroid
    - mirai-console
-   - mirai-console-pure
+   - mirai-console-terminal
 
 2. 创建一个新的文件, 名为 `start-mirai-console.bat`/`start-mirai-console.ps1`/`start-mirai-console.sh`
 
@@ -36,26 +36,26 @@ Windows CMD:
 ```shell script
 @echo off
 title Mirai Console
-java -cp "./libs/*" net.mamoe.mirai.console.pure.MiraiConsolePureLoader %*
+java -cp "./libs/*" net.mamoe.mirai.console.terminal.MiraiConsoleTerminalLoader %*
 pause
 ```
 
 Windows PowerShell:
 ```shell script
 $Host.UI.RawUI.WindowTitle = "Mirai Console"
-java -cp "./libs/*" net.mamoe.mirai.console.pure.MiraiConsolePureLoader $args
+java -cp "./libs/*" net.mamoe.mirai.console.terminal.MiraiConsoleTerminalLoader $args
 pause
 ```
 
 Linux:
 ```shell script
 #!/usr/bin/env bash
-java -cp "./libs/*" net.mamoe.mirai.console.pure.MiraiConsolePureLoader $*
+java -cp "./libs/*" net.mamoe.mirai.console.terminal.MiraiConsoleTerminalLoader $*
 ```
 
 然后就可以开始使用 mirai-console 了
 
-### mirai-console-pure 前端参数
-使用 `./start-mirai-console --help` 查看 mirai-console-pure 支持的启动参数
+### mirai-console-terminal 前端参数
+使用 `./start-mirai-console --help` 查看 mirai-console-terminal 支持的启动参数
 
 [mirai-repo]: https://github.com/project-mirai/mirai-repo/tree/master/shadow

+ 3 - 3
frontend/mirai-console-pure/build.gradle.kts → frontend/mirai-console-terminal/build.gradle.kts

@@ -66,10 +66,10 @@ ext.apply {
     this.set("shadowJar", x)
 }
 
-version = Versions.consolePure
+version = Versions.consoleTerminal
 
-description = "Console Pure CLI frontend for mirai"
+description = "Console Terminal CLI frontend for mirai"
 
-setupPublishing("mirai-console-pure", bintrayPkgName = "mirai-console-pure")
+setupPublishing("mirai-console-terminal", bintrayPkgName = "mirai-console-terminal")
 
 // endregion

+ 34 - 0
frontend/mirai-console-terminal/src/main/kotlin/net/mamoe/mirai/console/pure/MiraiConsolePureLoader.kt

@@ -0,0 +1,34 @@
+/*
+ * Copyright 2019-2020 Mamoe Technologies and contributors.
+ *
+ * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
+ * Use of this source code is governed by the GNU AFFERO GENERAL PUBLIC LICENSE version 3 license that can be found via the following link.
+ *
+ * https://github.com/mamoe/mirai/blob/master/LICENSE
+ *
+ */
+
+package net.mamoe.mirai.console.pure
+
+import net.mamoe.mirai.console.terminal.MiraiConsoleTerminalLoader
+
+@Deprecated(
+    message = "Please use MiraiConsoleTerminalLoader",
+    level = DeprecationLevel.ERROR,
+    replaceWith = ReplaceWith(
+        "MiraiConsoleTerminalLoader",
+        "net.mamoe.mirai.console.terminal.MiraiConsoleTerminalLoader"
+    )
+)
+object MiraiConsolePureLoader {
+    @Deprecated(
+        message = "for binary compatibility",
+        level = DeprecationLevel.ERROR
+    )
+    @JvmStatic
+    fun main(args: Array<String>) {
+        System.err.println("WARNING: Mirai Console Pure已经更名为 Mirai Console Terminal")
+        System.err.println("请使用新的入口点 net.mamoe.mirai.console.terminal.MiraiConsoleTerminalLoader")
+        MiraiConsoleTerminalLoader.main(args)
+    }
+}

+ 2 - 1
frontend/mirai-console-pure/src/main/kotlin/net/mamoe/mirai/console/pure/BufferedOutputStream.kt → frontend/mirai-console-terminal/src/main/kotlin/net/mamoe/mirai/console/terminal/BufferedOutputStream.kt

@@ -5,9 +5,10 @@
  * Use of this source code is governed by the GNU AFFERO GENERAL PUBLIC LICENSE version 3 license that can be found via the following link.
  *
  * https://github.com/mamoe/mirai/blob/master/LICENSE
+ *
  */
 
-package net.mamoe.mirai.console.pure
+package net.mamoe.mirai.console.terminal
 
 import java.io.ByteArrayOutputStream
 import java.io.OutputStream

+ 74 - 0
frontend/mirai-console-terminal/src/main/kotlin/net/mamoe/mirai/console/terminal/ConsoleInputImpl.kt

@@ -0,0 +1,74 @@
+/*
+ * Copyright 2019-2020 Mamoe Technologies and contributors.
+ *
+ * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
+ * Use of this source code is governed by the GNU AFFERO GENERAL PUBLIC LICENSE version 3 license that can be found via the following link.
+ *
+ * https://github.com/mamoe/mirai/blob/master/LICENSE
+ *
+ */
+
+package net.mamoe.mirai.console.terminal
+
+import kotlinx.coroutines.CancellableContinuation
+import kotlinx.coroutines.suspendCancellableCoroutine
+import net.mamoe.mirai.console.util.ConsoleInput
+import org.fusesource.jansi.Ansi
+import org.jline.reader.EndOfFileException
+import java.time.Instant
+import java.time.LocalDateTime
+import java.time.ZoneId
+import java.time.format.DateTimeFormatter
+import java.util.concurrent.Executors
+import kotlin.coroutines.resumeWithException
+
+
+internal object ConsoleInputImpl : ConsoleInput {
+    private val format = DateTimeFormatter.ofPattern("HH:mm:ss")
+    internal val thread = Executors.newSingleThreadExecutor { task ->
+        Thread(task, "Mirai Console Input Thread").also {
+            it.isDaemon = false
+        }
+    }
+    internal var executingCoroutine: CancellableContinuation<String>? = null
+
+
+    override suspend fun requestInput(hint: String): String {
+        return suspendCancellableCoroutine { coroutine ->
+            if (thread.isShutdown || thread.isTerminated) {
+                coroutine.resumeWithException(EndOfFileException())
+                return@suspendCancellableCoroutine
+            }
+            executingCoroutine = coroutine
+            kotlin.runCatching {
+                thread.submit {
+                    kotlin.runCatching {
+                        lineReader.readLine(
+                            if (hint.isNotEmpty()) {
+                                lineReader.printAbove(
+                                    Ansi.ansi()
+                                        .fgCyan()
+                                        .a(
+                                            LocalDateTime.ofInstant(Instant.now(), ZoneId.systemDefault())
+                                                .format(format)
+                                        )
+                                        .a(" ")
+                                        .fgMagenta().a(hint)
+                                        .reset()
+                                        .toString()
+                                )
+                                "$hint > "
+                            } else "> "
+                        )
+                    }.let { result ->
+                        executingCoroutine = null
+                        coroutine.resumeWith(result)
+                    }
+                }
+            }.onFailure { error ->
+                executingCoroutine = null
+                kotlin.runCatching { coroutine.resumeWithException(EndOfFileException(error)) }
+            }
+        }
+    }
+}

+ 5 - 4
frontend/mirai-console-pure/src/main/kotlin/net/mamoe/mirai/console/pure/ConsolePureSettings.kt → frontend/mirai-console-terminal/src/main/kotlin/net/mamoe/mirai/console/terminal/ConsoleTerminalSettings.kt

@@ -5,12 +5,13 @@
  * Use of this source code is governed by the GNU AFFERO GENERAL PUBLIC LICENSE version 3 license that can be found via the following link.
  *
  * https://github.com/mamoe/mirai/blob/master/LICENSE
+ *
  */
 /*
  * @author Karlatemp <[email protected]> <https://github.com/Karlatemp>
  */
 
-package net.mamoe.mirai.console.pure
+package net.mamoe.mirai.console.terminal
 
 @Retention(AnnotationRetention.BINARY)
 @RequiresOptIn(level = RequiresOptIn.Level.WARNING)
@@ -23,10 +24,10 @@ package net.mamoe.mirai.console.pure
     AnnotationTarget.CONSTRUCTOR
 )
 @MustBeDocumented
-annotation class ConsolePureExperimentalApi
+annotation class ConsoleTerminalExperimentalApi
 
-@ConsolePureExperimentalApi
-public object ConsolePureSettings {
+@ConsoleTerminalExperimentalApi
+public object ConsoleTerminalSettings {
     @JvmField
     var setupAnsi: Boolean = System.getProperty("os.name")
         .toLowerCase()

+ 25 - 12
frontend/mirai-console-pure/src/main/kotlin/net/mamoe/mirai/console/pure/ConsoleThread.kt → frontend/mirai-console-terminal/src/main/kotlin/net/mamoe/mirai/console/terminal/ConsoleThread.kt

@@ -5,13 +5,14 @@
  * Use of this source code is governed by the GNU AFFERO GENERAL PUBLIC LICENSE version 3 license that can be found via the following link.
  *
  * https://github.com/mamoe/mirai/blob/master/LICENSE
+ *
  */
 
-package net.mamoe.mirai.console.pure
+package net.mamoe.mirai.console.terminal
 
 import kotlinx.coroutines.CancellationException
 import kotlinx.coroutines.CoroutineName
-import kotlinx.coroutines.cancel
+import kotlinx.coroutines.delay
 import kotlinx.coroutines.launch
 import net.mamoe.mirai.console.MiraiConsole
 import net.mamoe.mirai.console.command.BuiltInCommands
@@ -20,18 +21,33 @@ import net.mamoe.mirai.console.command.CommandExecuteStatus
 import net.mamoe.mirai.console.command.CommandManager
 import net.mamoe.mirai.console.command.CommandManager.INSTANCE.executeCommand
 import net.mamoe.mirai.console.command.ConsoleCommandSender
+import net.mamoe.mirai.console.terminal.noconsole.NoConsole
 import net.mamoe.mirai.console.util.ConsoleInternalApi
 import net.mamoe.mirai.console.util.requestInput
 import net.mamoe.mirai.utils.DefaultLogger
+import org.jline.reader.EndOfFileException
 import org.jline.reader.UserInterruptException
 
 val consoleLogger by lazy { DefaultLogger("console") }
 
-@OptIn(ConsoleInternalApi::class, ConsolePureExperimentalApi::class)
+@OptIn(ConsoleInternalApi::class, ConsoleTerminalExperimentalApi::class)
 internal fun startupConsoleThread() {
-    if (ConsolePureSettings.noConsole) return
+    if (terminal is NoConsole) return
 
-    MiraiConsole.launch(CoroutineName("Input")) {
+    MiraiConsole.launch(CoroutineName("Input Cancelling Daemon")) {
+        while (true) {
+            delay(2000)
+        }
+    }.invokeOnCompletion {
+        runCatching<Unit> {
+            terminal.close()
+            ConsoleInputImpl.thread.shutdownNow()
+            runCatching {
+                ConsoleInputImpl.executingCoroutine?.cancel(EndOfFileException())
+            }
+        }.exceptionOrNull()?.printStackTrace()
+    }
+    MiraiConsole.launch(CoroutineName("Console Command")) {
         while (true) {
             try {
                 val next = MiraiConsole.requestInput("").let {
@@ -65,17 +81,14 @@ internal fun startupConsoleThread() {
             } catch (e: CancellationException) {
                 return@launch
             } catch (e: UserInterruptException) {
-                MiraiConsole.cancel()
+                BuiltInCommands.StopCommand.run { ConsoleCommandSender.handle() }
+                return@launch
+            } catch (eof: EndOfFileException) {
+                consoleLogger.warning("Closing input service...")
                 return@launch
             } catch (e: Throwable) {
                 consoleLogger.error("Unhandled exception", e)
             }
         }
     }
-
-    MiraiConsole.job.invokeOnCompletion {
-        runCatching {
-            terminal.close()
-        }.exceptionOrNull()?.printStackTrace()
-    }
 }

+ 46 - 40
frontend/mirai-console-pure/src/main/kotlin/net/mamoe/mirai/console/pure/MiraiConsoleImplementationPure.kt → frontend/mirai-console-terminal/src/main/kotlin/net/mamoe/mirai/console/terminal/MiraiConsoleImplementationTerminal.kt

@@ -5,6 +5,7 @@
  * Use of this source code is governed by the GNU AFFERO GENERAL PUBLIC LICENSE version 3 license that can be found via the following link.
  *
  * https://github.com/mamoe/mirai/blob/master/LICENSE
+ *
  */
 
 @file:Suppress(
@@ -17,13 +18,16 @@
     "INVISIBLE_ABSTRACT_MEMBER_FROM_SUPER_WARNING",
     "EXPOSED_SUPER_CLASS"
 )
-@file:OptIn(ConsoleInternalApi::class, ConsoleFrontEndImplementation::class, ConsolePureExperimentalApi::class)
+@file:OptIn(ConsoleInternalApi::class, ConsoleFrontEndImplementation::class, ConsoleTerminalExperimentalApi::class)
 
-package net.mamoe.mirai.console.pure
+package net.mamoe.mirai.console.terminal
 
 
 import com.vdurmont.semver4j.Semver
-import kotlinx.coroutines.*
+import kotlinx.coroutines.CancellationException
+import kotlinx.coroutines.CoroutineExceptionHandler
+import kotlinx.coroutines.CoroutineName
+import kotlinx.coroutines.CoroutineScope
 import net.mamoe.mirai.console.ConsoleFrontEndImplementation
 import net.mamoe.mirai.console.MiraiConsole
 import net.mamoe.mirai.console.MiraiConsoleFrontEndDescription
@@ -32,10 +36,10 @@ import net.mamoe.mirai.console.data.MultiFilePluginDataStorage
 import net.mamoe.mirai.console.data.PluginDataStorage
 import net.mamoe.mirai.console.plugin.jvm.JvmPluginLoader
 import net.mamoe.mirai.console.plugin.loader.PluginLoader
-import net.mamoe.mirai.console.pure.ConsoleInputImpl.requestInput
+import net.mamoe.mirai.console.terminal.ConsoleInputImpl.requestInput
+import net.mamoe.mirai.console.terminal.noconsole.AllEmptyLineReader
+import net.mamoe.mirai.console.terminal.noconsole.NoConsole
 import net.mamoe.mirai.console.util.ConsoleExperimentalApi
-import net.mamoe.mirai.console.pure.noconsole.AllEmptyLineReader
-import net.mamoe.mirai.console.pure.noconsole.NoConsole
 import net.mamoe.mirai.console.util.ConsoleInput
 import net.mamoe.mirai.console.util.ConsoleInternalApi
 import net.mamoe.mirai.console.util.NamedSupervisorJob
@@ -46,31 +50,28 @@ import org.jline.reader.LineReaderBuilder
 import org.jline.reader.impl.completer.NullCompleter
 import org.jline.terminal.Terminal
 import org.jline.terminal.TerminalBuilder
+import org.jline.terminal.impl.AbstractWindowsTerminal
 import java.nio.file.Path
 import java.nio.file.Paths
-import java.time.Instant
-import java.time.LocalDateTime
-import java.time.ZoneId
-import java.time.format.DateTimeFormatter
 
 /**
- * mirai-console-pure 后端实现
+ * mirai-console-terminal 后端实现
  *
- * @see MiraiConsolePureLoader CLI 入口点
+ * @see MiraiConsoleTerminalLoader CLI 入口点
  */
 @ConsoleExperimentalApi
-class MiraiConsoleImplementationPure
+class MiraiConsoleImplementationTerminal
 @JvmOverloads constructor(
     override val rootPath: Path = Paths.get(".").toAbsolutePath(),
     override val builtInPluginLoaders: List<Lazy<PluginLoader<*, *>>> = listOf(lazy { JvmPluginLoader }),
     override val frontEndDescription: MiraiConsoleFrontEndDescription = ConsoleFrontEndDescImpl,
-    override val consoleCommandSender: MiraiConsoleImplementation.ConsoleCommandSenderImpl = ConsoleCommandSenderImplPure,
+    override val consoleCommandSender: MiraiConsoleImplementation.ConsoleCommandSenderImpl = ConsoleCommandSenderImplTerminal,
     override val dataStorageForJvmPluginLoader: PluginDataStorage = MultiFilePluginDataStorage(rootPath.resolve("data")),
     override val dataStorageForBuiltIns: PluginDataStorage = MultiFilePluginDataStorage(rootPath.resolve("data")),
     override val configStorageForJvmPluginLoader: PluginDataStorage = MultiFilePluginDataStorage(rootPath.resolve("config")),
     override val configStorageForBuiltIns: PluginDataStorage = MultiFilePluginDataStorage(rootPath.resolve("config")),
 ) : MiraiConsoleImplementation, CoroutineScope by CoroutineScope(
-    NamedSupervisorJob("MiraiConsoleImplementationPure") +
+    NamedSupervisorJob("MiraiConsoleImplementationTerminal") +
             CoroutineExceptionHandler { coroutineContext, throwable ->
                 if (throwable is CancellationException) {
                     return@CoroutineExceptionHandler
@@ -94,30 +95,9 @@ class MiraiConsoleImplementationPure
     }
 }
 
-private object ConsoleInputImpl : ConsoleInput {
-    private val format = DateTimeFormatter.ofPattern("HH:mm:ss")
-
-    override suspend fun requestInput(hint: String): String {
-        return withContext(Dispatchers.IO) {
-            lineReader.readLine(
-                if (hint.isNotEmpty()) {
-                    lineReader.printAbove(
-                        Ansi.ansi()
-                            .fgCyan().a(LocalDateTime.ofInstant(Instant.now(), ZoneId.systemDefault()).format(format))
-                            .a(" ")
-                            .fgMagenta().a(hint)
-                            .reset()
-                            .toString()
-                    )
-                    "$hint > "
-                } else "> "
-            )
-        }
-    }
-}
-
 val lineReader: LineReader by lazy {
-    if (ConsolePureSettings.noConsole) return@lazy AllEmptyLineReader
+    val terminal = terminal
+    if (terminal is NoConsole) return@lazy AllEmptyLineReader
 
     LineReaderBuilder.builder()
         .terminal(terminal)
@@ -126,7 +106,7 @@ val lineReader: LineReader by lazy {
 }
 
 val terminal: Terminal = run {
-    if (ConsolePureSettings.noConsole) return@run NoConsole
+    if (ConsoleTerminalSettings.noConsole) return@run NoConsole
 
     val dumb = System.getProperty("java.class.path")
         .contains("idea_rt.jar") || System.getProperty("mirai.idea") !== null || System.getenv("mirai.idea") !== null
@@ -134,7 +114,33 @@ val terminal: Terminal = run {
     runCatching {
         TerminalBuilder.builder()
             .dumb(dumb)
+            .paused(true)
             .build()
+            .let { terminal ->
+                if (terminal is AbstractWindowsTerminal) {
+                    val pumpField = runCatching {
+                        AbstractWindowsTerminal::class.java.getDeclaredField("pump").also {
+                            it.isAccessible = true
+                        }
+                    }.onFailure { err ->
+                        err.printStackTrace()
+                        return@let terminal.also { it.resume() }
+                    }.getOrThrow()
+                    var response = terminal
+                    terminal.setOnClose {
+                        response = NoConsole
+                    }
+                    terminal.resume()
+                    val pumpThread = pumpField[terminal] as? Thread ?: return@let NoConsole
+                    @Suppress("ControlFlowWithEmptyBody")
+                    while (pumpThread.state == Thread.State.NEW);
+                    Thread.sleep(1000)
+                    terminal.setOnClose(null)
+                    return@let response
+                }
+                terminal.resume()
+                terminal
+            }
     }.recoverCatching {
         TerminalBuilder.builder()
             .jansi(true)
@@ -147,7 +153,7 @@ val terminal: Terminal = run {
 }
 
 private object ConsoleFrontEndDescImpl : MiraiConsoleFrontEndDescription {
-    override val name: String get() = "Pure"
+    override val name: String get() = "Terminal"
     override val vendor: String get() = "Mamoe Technologies"
     override val version: Semver = net.mamoe.mirai.console.internal.MiraiConsoleBuildConstants.version
 }

+ 28 - 21
frontend/mirai-console-pure/src/main/kotlin/net/mamoe/mirai/console/pure/MiraiConsolePureLoader.kt → frontend/mirai-console-terminal/src/main/kotlin/net/mamoe/mirai/console/terminal/MiraiConsoleTerminalLoader.kt

@@ -5,6 +5,7 @@
  * Use of this source code is governed by the GNU AFFERO GENERAL PUBLIC LICENSE version 3 license that can be found via the following link.
  *
  * https://github.com/mamoe/mirai/blob/master/LICENSE
+ *
  */
 
 @file:Suppress(
@@ -15,9 +16,9 @@
     "INVISIBLE_GETTER",
     "INVISIBLE_ABSTRACT_MEMBER_FROM_SUPER",
 )
-@file:OptIn(ConsoleInternalApi::class, ConsolePureExperimentalApi::class)
+@file:OptIn(ConsoleInternalApi::class, ConsoleTerminalExperimentalApi::class)
 
-package net.mamoe.mirai.console.pure
+package net.mamoe.mirai.console.terminal
 
 import kotlinx.coroutines.CancellationException
 import kotlinx.coroutines.CoroutineScope
@@ -26,20 +27,22 @@ import net.mamoe.mirai.console.MiraiConsole
 import net.mamoe.mirai.console.MiraiConsoleImplementation
 import net.mamoe.mirai.console.MiraiConsoleImplementation.Companion.start
 import net.mamoe.mirai.console.data.AutoSavePluginDataHolder
-import net.mamoe.mirai.console.pure.noconsole.SystemOutputPrintStream
+import net.mamoe.mirai.console.terminal.noconsole.SystemOutputPrintStream
 import net.mamoe.mirai.console.util.ConsoleExperimentalApi
 import net.mamoe.mirai.console.util.ConsoleInternalApi
 import net.mamoe.mirai.console.util.CoroutineScopeUtils.childScope
 import net.mamoe.mirai.message.data.Message
 import net.mamoe.mirai.utils.DefaultLogger
 import net.mamoe.mirai.utils.minutesToMillis
+import java.io.FileDescriptor
+import java.io.FileOutputStream
 import java.io.PrintStream
 import kotlin.system.exitProcess
 
 /**
- * mirai-console-pure CLI 入口点
+ * mirai-console-terminal CLI 入口点
  */
-object MiraiConsolePureLoader {
+object MiraiConsoleTerminalLoader {
     @JvmStatic
     fun main(args: Array<String>) {
         parse(args, exitProcess = true)
@@ -53,10 +56,10 @@ object MiraiConsolePureLoader {
         }
     }
 
-    @ConsolePureExperimentalApi
+    @ConsoleTerminalExperimentalApi
     fun printHelpMessage() {
         val help = listOf(
-            "" to "Mirai-Console[Pure FrontEnd] v" + kotlin.runCatching {
+            "" to "Mirai-Console[Terminal FrontEnd] v" + kotlin.runCatching {
                 net.mamoe.mirai.console.internal.MiraiConsoleBuildConstants.version
             }.getOrElse { "<unknown>" },
             "" to "",
@@ -96,7 +99,7 @@ object MiraiConsolePureLoader {
         }
     }
 
-    @ConsolePureExperimentalApi
+    @ConsoleTerminalExperimentalApi
     fun parse(args: Array<String>, exitProcess: Boolean = false) {
         val iterator = args.iterator()
         while (iterator.hasNext()) {
@@ -107,19 +110,19 @@ object MiraiConsolePureLoader {
                     return
                 }
                 "--no-console" -> {
-                    ConsolePureSettings.noConsole = true
+                    ConsoleTerminalSettings.noConsole = true
                 }
                 "--dont-setup-terminal-ansi" -> {
-                    ConsolePureSettings.setupAnsi = false
+                    ConsoleTerminalSettings.setupAnsi = false
                 }
                 "--no-ansi" -> {
-                    ConsolePureSettings.noAnsi = true
-                    ConsolePureSettings.setupAnsi = false
+                    ConsoleTerminalSettings.noAnsi = true
+                    ConsoleTerminalSettings.setupAnsi = false
                 }
                 "--reading-replacement" -> {
-                    ConsolePureSettings.noConsoleSafeReading = true
+                    ConsoleTerminalSettings.noConsoleSafeReading = true
                     if (iterator.hasNext()) {
-                        ConsolePureSettings.noConsoleReadingReplacement = iterator.next()
+                        ConsoleTerminalSettings.noConsoleReadingReplacement = iterator.next()
                     } else {
                         println("Bad option `--reading-replacement`")
                         println("Usage: --reading-replacement <string>")
@@ -129,7 +132,7 @@ object MiraiConsolePureLoader {
                     }
                 }
                 "--safe-reading" -> {
-                    ConsolePureSettings.noConsoleSafeReading = true
+                    ConsoleTerminalSettings.noConsoleSafeReading = true
                 }
                 else -> {
                     println("Unknown option `$option`")
@@ -140,13 +143,13 @@ object MiraiConsolePureLoader {
                 }
             }
         }
-        if (ConsolePureSettings.noConsole)
+        if (ConsoleTerminalSettings.noConsole)
             SystemOutputPrintStream // Setup Output Channel
     }
 
     @Suppress("MemberVisibilityCanBePrivate")
     @ConsoleExperimentalApi
-    fun startAsDaemon(instance: MiraiConsoleImplementationPure = MiraiConsoleImplementationPure()) {
+    fun startAsDaemon(instance: MiraiConsoleImplementationTerminal = MiraiConsoleImplementationTerminal()) {
         instance.start()
         overrideSTD()
         startupConsoleThread()
@@ -160,7 +163,7 @@ internal object ConsoleDataHolder : AutoSavePluginDataHolder,
 
     @ConsoleExperimentalApi
     override val dataHolderName: String
-        get() = "Pure"
+        get() = "Terminal"
 }
 
 internal fun overrideSTD() {
@@ -181,12 +184,16 @@ internal fun overrideSTD() {
 }
 
 
-internal object ConsoleCommandSenderImplPure : MiraiConsoleImplementation.ConsoleCommandSenderImpl {
+internal object ConsoleCommandSenderImplTerminal : MiraiConsoleImplementation.ConsoleCommandSenderImpl {
     override suspend fun sendMessage(message: String) {
         kotlin.runCatching {
             lineReader.printAbove(message)
-        }.onFailure {
-            consoleLogger.error("Exception while ConsoleCommandSenderImplPure.sendMessage", it)
+        }.onFailure { exception ->
+            // If failed. It means JLine Terminal not working...
+            PrintStream(FileOutputStream(FileDescriptor.err)).use {
+                it.println("Exception while ConsoleCommandSenderImplTerminal.sendMessage")
+                exception.printStackTrace(it)
+            }
         }
     }
 

+ 11 - 11
frontend/mirai-console-pure/src/main/kotlin/net/mamoe/mirai/console/pure/noconsole/NoConsole.kt → frontend/mirai-console-terminal/src/main/kotlin/net/mamoe/mirai/console/terminal/noconsole/NoConsole.kt

@@ -10,12 +10,12 @@
 /*
  * @author Karlatemp <[email protected]> <https://github.com/Karlatemp>
  */
-@file:OptIn(ConsolePureExperimentalApi::class)
+@file:OptIn(ConsoleTerminalExperimentalApi::class)
 
-package net.mamoe.mirai.console.pure.noconsole
+package net.mamoe.mirai.console.terminal.noconsole
 
-import net.mamoe.mirai.console.pure.ConsolePureExperimentalApi
-import net.mamoe.mirai.console.pure.ConsolePureSettings
+import net.mamoe.mirai.console.terminal.ConsoleTerminalExperimentalApi
+import net.mamoe.mirai.console.terminal.ConsoleTerminalSettings
 import org.jline.keymap.KeyMap
 import org.jline.reader.*
 import org.jline.terminal.Attributes
@@ -69,8 +69,8 @@ internal object AllIgnoredOutputStream : OutputStream() {
 }
 
 internal val SystemOutputPrintStream by lazy {
-    @OptIn(ConsolePureExperimentalApi::class)
-    if (ConsolePureSettings.setupAnsi) {
+    @OptIn(ConsoleTerminalExperimentalApi::class)
+    if (ConsoleTerminalSettings.setupAnsi) {
         org.fusesource.jansi.AnsiConsole.systemInstall()
     }
     System.out
@@ -81,16 +81,16 @@ internal object AllEmptyLineReader : LineReader {
 
     override fun printAbove(str: String?) {
         if (str == null) return
-        @OptIn(ConsolePureExperimentalApi::class)
-        if (ConsolePureSettings.noAnsi) {
+        @OptIn(ConsoleTerminalExperimentalApi::class)
+        if (ConsoleTerminalSettings.noAnsi) {
             SystemOutputPrintStream.println(ANSI_REGEX.replace(str, ""))
         } else SystemOutputPrintStream.println(str)
     }
 
-    @OptIn(ConsolePureExperimentalApi::class)
+    @OptIn(ConsoleTerminalExperimentalApi::class)
     override fun readLine(): String =
-        if (ConsolePureSettings.noConsoleSafeReading) ConsolePureSettings.noConsoleReadingReplacement
-        else error("Unsupported Reading line when console front-end closed.")
+        if (ConsoleTerminalSettings.noConsoleSafeReading) ConsoleTerminalSettings.noConsoleReadingReplacement
+        else throw EndOfFileException("Unsupported Reading line when console front-end closed.")
 
     // region
     private fun <T> ignored(): T = error("Ignored")

+ 1 - 1
settings.gradle.kts

@@ -19,7 +19,7 @@ fun includeProject(projectPath: String, path: String? = null) {
 
 includeProject(":mirai-console", "backend/mirai-console")
 includeProject(":mirai-console.codegen", "backend/codegen")
-includeProject(":mirai-console-pure", "frontend/mirai-console-pure")
+includeProject(":mirai-console-terminal", "frontend/mirai-console-terminal")
 includeProject(":mirai-console-compiler-common", "tools/compiler-common")
 includeProject(":mirai-console-intellij", "tools/intellij-plugin")
 includeProject(":mirai-console-gradle", "tools/gradle-plugin")

+ 2 - 2
tools/intellij-plugin/run/projects/test-project/src/test/kotlin/RunConsole.kt

@@ -2,11 +2,11 @@ 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.pure.MiraiConsolePureLoader
+import net.mamoe.mirai.console.terminal.MiraiConsoleTerminalLoader
 import org.example.myplugin.MyPluginMain
 
 suspend fun main() {
-    MiraiConsolePureLoader.startAsDaemon()
+    MiraiConsoleTerminalLoader.startAsDaemon()
 
     MyPluginMain.load() // 主动加载插件, Console 会调用 MyPluginMain.onLoad
     MyPluginMain.enable() // 主动启用插件, Console 会调用 MyPluginMain.onEnable