* Rename ConsolePure to ConsoleTerminal

* Fix the way to close console with Ctrl+C

* Fix windows pipeline error. Fix EndOfFileException

* Add ConsoleExperimentalApi

* Collect imports

* Review

- Change old CLI main deprecation level to ERROR
- Update documents
- Update tasks from pure to terminal

* Fix error in closing console.

* Fix terminal closing and Ctrl+C closing.

* Add console shut-downing status

* Don't invokeOnCompletion when console shut-downing

* Fix Input interrupt

* Ensure active unless console is shut downing

* Change MiraiConsole.isShutDowning to `!job.isActive`

* Code Review

- Update Message on MiraiConsolePureLoader.kt
- Change MiraiConsole.isShutDowning to MiraiConsole.isActive
- Change MiraiConsole.shutdown to ConsoleInternalApi

* run catching

* Fix console input

* Update shutdown

* Fix module

* Revert 5199395

* Typo
This commit is contained in:
Karlatemp 2020-09-18 12:43:57 +08:00 committed by GitHub
parent f990b7ce8c
commit 8fe2506e75
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 256 additions and 114 deletions

View File

@ -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 }}

View File

@ -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

View File

@ -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") // 前端, 用于启动测试
}
```

View File

@ -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
}
}

View File

@ -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()

View File

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

View File

@ -11,8 +11,7 @@ object Versions {
const val core = "1.3.0"
const val console = "1.0-RC-dev-2"
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

View File

@ -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

View File

@ -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

View File

@ -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)
}
}

View File

@ -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

View File

@ -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)) }
}
}
}
}

View File

@ -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 <karlatemp@vip.qq.com> <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()

View File

@ -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()
}
}

View File

@ -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
}

View File

@ -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)
}
}
}

View File

@ -10,12 +10,12 @@
/*
* @author Karlatemp <karlatemp@vip.qq.com> <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")

View File

@ -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")

View File

@ -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