mirror of
https://github.com/mamoe/mirai.git
synced 2025-03-13 14:50:43 +08:00
Ansi support
This commit is contained in:
parent
a6ce62f1c3
commit
01418845f3
@ -24,6 +24,7 @@ import net.mamoe.mirai.console.plugin.PluginManager
|
|||||||
import net.mamoe.mirai.console.plugin.center.PluginCenter
|
import net.mamoe.mirai.console.plugin.center.PluginCenter
|
||||||
import net.mamoe.mirai.console.plugin.jvm.JvmPluginLoader
|
import net.mamoe.mirai.console.plugin.jvm.JvmPluginLoader
|
||||||
import net.mamoe.mirai.console.plugin.loader.PluginLoader
|
import net.mamoe.mirai.console.plugin.loader.PluginLoader
|
||||||
|
import net.mamoe.mirai.console.util.AnsiMessageBuilder
|
||||||
import net.mamoe.mirai.console.util.ConsoleExperimentalApi
|
import net.mamoe.mirai.console.util.ConsoleExperimentalApi
|
||||||
import net.mamoe.mirai.console.util.ConsoleInternalApi
|
import net.mamoe.mirai.console.util.ConsoleInternalApi
|
||||||
import net.mamoe.mirai.console.util.CoroutineScopeUtils.childScopeContext
|
import net.mamoe.mirai.console.util.CoroutineScopeUtils.childScopeContext
|
||||||
@ -151,6 +152,18 @@ public interface MiraiConsole : CoroutineScope {
|
|||||||
public val isActive: Boolean
|
public val isActive: Boolean
|
||||||
get() = job.isActive
|
get() = job.isActive
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 该前端是否支持使用 Ansi 输出彩色信息
|
||||||
|
*
|
||||||
|
* 注: 不是每个前端都可能提供 `org.fusesource.jansi:jansi` 库支持,
|
||||||
|
* 请不要直接使用 `org.fusesource.jansi:jansi`
|
||||||
|
*
|
||||||
|
* @see [AnsiMessageBuilder]
|
||||||
|
*/
|
||||||
|
@ConsoleExperimentalApi
|
||||||
|
public val isAnsiSupport: Boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -170,6 +170,13 @@ public interface MiraiConsoleImplementation : CoroutineScope {
|
|||||||
*/
|
*/
|
||||||
public fun createLogger(identity: String?): MiraiLogger
|
public fun createLogger(identity: String?): MiraiLogger
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 该前端是否支持使用 Ansi 输出彩色信息
|
||||||
|
*
|
||||||
|
* 注: 若为 `true`, 建议携带 `org.fusesource.jansi:jansi`
|
||||||
|
*/
|
||||||
|
public val isAnsiSupport: Boolean get() = false
|
||||||
|
|
||||||
public companion object {
|
public companion object {
|
||||||
internal lateinit var instance: MiraiConsoleImplementation
|
internal lateinit var instance: MiraiConsoleImplementation
|
||||||
private val initLock = ReentrantLock()
|
private val initLock = ReentrantLock()
|
||||||
|
@ -82,6 +82,7 @@ internal object MiraiConsoleImplementationBridge : CoroutineScope, MiraiConsoleI
|
|||||||
override val dataStorageForBuiltIns: PluginDataStorage by instance::dataStorageForBuiltIns
|
override val dataStorageForBuiltIns: PluginDataStorage by instance::dataStorageForBuiltIns
|
||||||
override val configStorageForBuiltIns: PluginDataStorage by instance::configStorageForBuiltIns
|
override val configStorageForBuiltIns: PluginDataStorage by instance::configStorageForBuiltIns
|
||||||
override val consoleInput: ConsoleInput by instance::consoleInput
|
override val consoleInput: ConsoleInput by instance::consoleInput
|
||||||
|
override val isAnsiSupport: Boolean by instance::isAnsiSupport
|
||||||
|
|
||||||
override fun createLoginSolver(requesterBot: Long, configuration: BotConfiguration): LoginSolver =
|
override fun createLoginSolver(requesterBot: Long, configuration: BotConfiguration): LoginSolver =
|
||||||
instance.createLoginSolver(requesterBot, configuration)
|
instance.createLoginSolver(requesterBot, configuration)
|
||||||
|
165
backend/mirai-console/src/util/AnsiMessageBuilder.kt
Normal file
165
backend/mirai-console/src/util/AnsiMessageBuilder.kt
Normal file
@ -0,0 +1,165 @@
|
|||||||
|
/*
|
||||||
|
* 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 through the following link.
|
||||||
|
*
|
||||||
|
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||||
|
*/
|
||||||
|
@file:Suppress("unused", "MemberVisibilityCanBePrivate")
|
||||||
|
|
||||||
|
package net.mamoe.mirai.console.util
|
||||||
|
|
||||||
|
import net.mamoe.mirai.console.command.CommandSender
|
||||||
|
import net.mamoe.mirai.console.command.ConsoleCommandSender
|
||||||
|
import net.mamoe.mirai.console.internal.MiraiConsoleImplementationBridge
|
||||||
|
|
||||||
|
public open class AnsiMessageBuilder internal constructor(
|
||||||
|
public val builder: StringBuilder
|
||||||
|
) : Appendable {
|
||||||
|
public inline fun <R> builder(action: StringBuilder.() -> R): R = builder.action()
|
||||||
|
override fun toString(): String = builder.toString()
|
||||||
|
|
||||||
|
public open fun ansi(code: String): AnsiMessageBuilder = append(code)
|
||||||
|
public open fun reset(): AnsiMessageBuilder = append(Color.RESET)
|
||||||
|
public open fun white(): AnsiMessageBuilder = append(Color.WHITE)
|
||||||
|
public open fun red(): AnsiMessageBuilder = append(Color.RED)
|
||||||
|
public open fun emeraldGreen(): AnsiMessageBuilder = append(Color.EMERALD_GREEN)
|
||||||
|
public open fun gold(): AnsiMessageBuilder = append(Color.GOLD)
|
||||||
|
public open fun blue(): AnsiMessageBuilder = append(Color.BLUE)
|
||||||
|
public open fun purple(): AnsiMessageBuilder = append(Color.PURPLE)
|
||||||
|
public open fun green(): AnsiMessageBuilder = append(Color.GREEN)
|
||||||
|
public open fun gray(): AnsiMessageBuilder = append(Color.GRAY)
|
||||||
|
public open fun lightRed(): AnsiMessageBuilder = append(Color.LIGHT_RED)
|
||||||
|
public open fun lightGreen(): AnsiMessageBuilder = append(Color.LIGHT_GREEN)
|
||||||
|
public open fun lightYellow(): AnsiMessageBuilder = append(Color.LIGHT_YELLOW)
|
||||||
|
public open fun lightBlue(): AnsiMessageBuilder = append(Color.LIGHT_BLUE)
|
||||||
|
public open fun lightPurple(): AnsiMessageBuilder = append(Color.LIGHT_PURPLE)
|
||||||
|
public open fun lightCyan(): AnsiMessageBuilder = append(Color.LIGHT_CYAN)
|
||||||
|
|
||||||
|
internal object Color {
|
||||||
|
const val RESET = "\u001b[0m"
|
||||||
|
const val WHITE = "\u001b[30m"
|
||||||
|
const val RED = "\u001b[31m"
|
||||||
|
const val EMERALD_GREEN = "\u001b[32m"
|
||||||
|
const val GOLD = "\u001b[33m"
|
||||||
|
const val BLUE = "\u001b[34m"
|
||||||
|
const val PURPLE = "\u001b[35m"
|
||||||
|
const val GREEN = "\u001b[36m"
|
||||||
|
const val GRAY = "\u001b[90m"
|
||||||
|
const val LIGHT_RED = "\u001b[91m"
|
||||||
|
const val LIGHT_GREEN = "\u001b[92m"
|
||||||
|
const val LIGHT_YELLOW = "\u001b[93m"
|
||||||
|
const val LIGHT_BLUE = "\u001b[94m"
|
||||||
|
const val LIGHT_PURPLE = "\u001b[95m"
|
||||||
|
const val LIGHT_CYAN = "\u001b[96m"
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class NoAnsiMessageBuilder(builder: StringBuilder) : AnsiMessageBuilder(builder) {
|
||||||
|
override fun reset(): AnsiMessageBuilder = this
|
||||||
|
override fun white(): AnsiMessageBuilder = this
|
||||||
|
override fun red(): AnsiMessageBuilder = this
|
||||||
|
override fun emeraldGreen(): AnsiMessageBuilder = this
|
||||||
|
override fun gold(): AnsiMessageBuilder = this
|
||||||
|
override fun blue(): AnsiMessageBuilder = this
|
||||||
|
override fun purple(): AnsiMessageBuilder = this
|
||||||
|
override fun green(): AnsiMessageBuilder = this
|
||||||
|
override fun gray(): AnsiMessageBuilder = this
|
||||||
|
override fun lightRed(): AnsiMessageBuilder = this
|
||||||
|
override fun lightGreen(): AnsiMessageBuilder = this
|
||||||
|
override fun lightYellow(): AnsiMessageBuilder = this
|
||||||
|
override fun lightBlue(): AnsiMessageBuilder = this
|
||||||
|
override fun lightPurple(): AnsiMessageBuilder = this
|
||||||
|
override fun lightCyan(): AnsiMessageBuilder = this
|
||||||
|
override fun ansi(code: String): AnsiMessageBuilder = this
|
||||||
|
}
|
||||||
|
|
||||||
|
public companion object {
|
||||||
|
public fun ansiMessageBuilder(
|
||||||
|
builder: StringBuilder,
|
||||||
|
noAnsi: Boolean = false
|
||||||
|
): AnsiMessageBuilder = if (noAnsi) {
|
||||||
|
NoAnsiMessageBuilder(builder)
|
||||||
|
} else AnsiMessageBuilder(builder)
|
||||||
|
|
||||||
|
public fun ansiMessageBuilder(
|
||||||
|
size: Int = 16,
|
||||||
|
noAnsi: Boolean = false
|
||||||
|
): AnsiMessageBuilder = ansiMessageBuilder(StringBuilder(size), noAnsi)
|
||||||
|
|
||||||
|
public inline fun buildAnsiMessage(
|
||||||
|
builder: StringBuilder,
|
||||||
|
noAnsi: Boolean = false,
|
||||||
|
action: AnsiMessageBuilder.() -> Unit
|
||||||
|
): String = ansiMessageBuilder(builder, noAnsi).apply(action).toString()
|
||||||
|
|
||||||
|
public inline fun buildAnsiMessage(
|
||||||
|
size: Int = 16,
|
||||||
|
noAnsi: Boolean = false,
|
||||||
|
action: AnsiMessageBuilder.() -> Unit
|
||||||
|
): String = ansiMessageBuilder(size, noAnsi).apply(action).toString()
|
||||||
|
|
||||||
|
@ConsoleExperimentalApi
|
||||||
|
public fun isAnsiSupport(sender: CommandSender): Boolean =
|
||||||
|
if (sender is ConsoleCommandSender) {
|
||||||
|
MiraiConsoleImplementationBridge.isAnsiSupport
|
||||||
|
} else false
|
||||||
|
|
||||||
|
public suspend fun CommandSender.sendAnsiMessage(
|
||||||
|
size: Int = 16,
|
||||||
|
builder: AnsiMessageBuilder.() -> Unit
|
||||||
|
) {
|
||||||
|
sendMessage(buildAnsiMessage(size, noAnsi = !isAnsiSupport(this), builder))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////////////
|
||||||
|
override fun append(c: Char): AnsiMessageBuilder {
|
||||||
|
builder.append(c); return this
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun append(csq: CharSequence?): AnsiMessageBuilder {
|
||||||
|
builder.append(csq); return this
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun append(csq: CharSequence?, start: Int, end: Int): AnsiMessageBuilder {
|
||||||
|
builder.append(csq, start, end); return this
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun append(any: Any?): AnsiMessageBuilder {
|
||||||
|
builder.append(any); return this
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun append(value: String): AnsiMessageBuilder {
|
||||||
|
builder.append(value); return this
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun append(value: String, start: Int, end: Int): AnsiMessageBuilder {
|
||||||
|
builder.append(value, start, end); return this
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun append(value: Boolean): AnsiMessageBuilder {
|
||||||
|
builder.append(value); return this
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun append(value: Float): AnsiMessageBuilder {
|
||||||
|
builder.append(value); return this
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun append(value: Double): AnsiMessageBuilder {
|
||||||
|
builder.append(value); return this
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun append(value: Int): AnsiMessageBuilder {
|
||||||
|
builder.append(value); return this
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun append(value: Long): AnsiMessageBuilder {
|
||||||
|
builder.append(value); return this
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun append(value: Short): AnsiMessageBuilder {
|
||||||
|
builder.append(value); return this
|
||||||
|
}
|
||||||
|
/////////////////////////////////////////////////////////////////////////////////
|
||||||
|
}
|
@ -75,6 +75,7 @@ class MiraiConsoleImplementationTerminal
|
|||||||
MiraiConsole.mainLogger.error("Exception in coroutine $coroutineName", throwable)
|
MiraiConsole.mainLogger.error("Exception in coroutine $coroutineName", throwable)
|
||||||
}) {
|
}) {
|
||||||
override val consoleInput: ConsoleInput get() = ConsoleInputImpl
|
override val consoleInput: ConsoleInput get() = ConsoleInputImpl
|
||||||
|
override val isAnsiSupport: Boolean get() = true
|
||||||
|
|
||||||
override fun createLoginSolver(requesterBot: Long, configuration: BotConfiguration): LoginSolver {
|
override fun createLoginSolver(requesterBot: Long, configuration: BotConfiguration): LoginSolver {
|
||||||
return DefaultLoginSolver(input = { requestInput("LOGIN> ") })
|
return DefaultLoginSolver(input = { requestInput("LOGIN> ") })
|
||||||
@ -155,7 +156,7 @@ private object ConsoleFrontEndDescImpl : MiraiConsoleFrontEndDescription {
|
|||||||
override val version: SemVersion = SemVersion(net.mamoe.mirai.console.internal.MiraiConsoleBuildConstants.versionConst)
|
override val version: SemVersion = SemVersion(net.mamoe.mirai.console.internal.MiraiConsoleBuildConstants.versionConst)
|
||||||
}
|
}
|
||||||
|
|
||||||
private val ANSI_RESET = Ansi().reset().toString()
|
internal val ANSI_RESET = Ansi().reset().toString()
|
||||||
|
|
||||||
internal val LoggerCreator: (identity: String?) -> MiraiLogger = {
|
internal val LoggerCreator: (identity: String?) -> MiraiLogger = {
|
||||||
PlatformLogger(identity = it, output = { line ->
|
PlatformLogger(identity = it, output = { line ->
|
||||||
|
@ -191,7 +191,7 @@ internal fun overrideSTD() {
|
|||||||
internal object ConsoleCommandSenderImplTerminal : MiraiConsoleImplementation.ConsoleCommandSenderImpl {
|
internal object ConsoleCommandSenderImplTerminal : MiraiConsoleImplementation.ConsoleCommandSenderImpl {
|
||||||
override suspend fun sendMessage(message: String) {
|
override suspend fun sendMessage(message: String) {
|
||||||
kotlin.runCatching {
|
kotlin.runCatching {
|
||||||
lineReader.printAbove(message)
|
lineReader.printAbove(message + ANSI_RESET)
|
||||||
}.onFailure { exception ->
|
}.onFailure { exception ->
|
||||||
// If failed. It means JLine Terminal not working...
|
// If failed. It means JLine Terminal not working...
|
||||||
PrintStream(FileOutputStream(FileDescriptor.err)).use {
|
PrintStream(FileOutputStream(FileDescriptor.err)).use {
|
||||||
|
Loading…
Reference in New Issue
Block a user