Add ansi subsystem (only formatter for now)
This commit is contained in:
parent
76b2105e53
commit
0ffd137891
8 changed files with 247 additions and 1 deletions
2
ansi/README.md
Normal file
2
ansi/README.md
Normal file
|
@ -0,0 +1,2 @@
|
|||
# `/ansi`
|
||||
The ANSI subsystem, which hides all the ANSI escape sequences stuff behind a nice to use API.
|
24
ansi/build.gradle.kts
Normal file
24
ansi/build.gradle.kts
Normal file
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* STAROPENSOURCE ENGINE SOURCE FILE
|
||||
* Copyright (c) 2024 The StarOpenSource Engine Authors
|
||||
* Licensed under the GNU General Public License v3.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// Dependencies
|
||||
dependencies {
|
||||
// sos!engine
|
||||
implementation(project(":base"))
|
||||
}
|
|
@ -0,0 +1,144 @@
|
|||
/*
|
||||
* STAROPENSOURCE ENGINE SOURCE FILE
|
||||
* Copyright (c) 2024 The StarOpenSource Engine Authors
|
||||
* Licensed under the GNU General Public License v3.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package de.staropensource.engine.ansi
|
||||
|
||||
import de.staropensource.engine.ansi.AnsiSubsystem.Companion.logger
|
||||
import de.staropensource.engine.base.implementable.formatter.TwoCycleFormatterImpl
|
||||
|
||||
/**
|
||||
* Formats a string using ANSI
|
||||
* escape sequences.
|
||||
*
|
||||
* @since v1-alpha10
|
||||
*/
|
||||
class AnsiFormatter : TwoCycleFormatterImpl() {
|
||||
/**
|
||||
* Companion object of [AnsiFormatter].
|
||||
*
|
||||
* @since v1-alpha10
|
||||
*/
|
||||
companion object {
|
||||
/**
|
||||
* The `ESC` character.
|
||||
*
|
||||
* @since v1-alpha10
|
||||
*/
|
||||
const val ESCAPE: Char = '\u001b'
|
||||
|
||||
/**
|
||||
* Global instance of [AnsiFormatter].
|
||||
*
|
||||
* @since v1-alpha10
|
||||
*/
|
||||
@JvmStatic
|
||||
val instance: AnsiFormatter = AnsiFormatter()
|
||||
}
|
||||
|
||||
@Suppress("CyclomaticComplexMethod")
|
||||
override fun format(messageComponents: Array<String>): String = buildString {
|
||||
val enabledTags: MutableSet<String> = mutableSetOf()
|
||||
var lastColor: String? = null
|
||||
var modifiers: String = "0;"
|
||||
|
||||
for (component: String in messageComponents)
|
||||
// Reset
|
||||
if (component == "RESET") {
|
||||
append("${ESCAPE}[0m")
|
||||
modifiers = "0;"
|
||||
enabledTags.clear()
|
||||
|
||||
// Text
|
||||
} else if (component.startsWith("TEXT:")) {
|
||||
append(component.substring(5))
|
||||
|
||||
// Attributes
|
||||
} else if (component.startsWith("ATTRIBUTE:")) {
|
||||
try {
|
||||
if (!enabledTags.contains(component)) {
|
||||
modifiers = attribute(component.substring(10), modifiers)!!
|
||||
enabledTags.add(component)
|
||||
if (lastColor != null)
|
||||
append(color(lastColor, modifiers))
|
||||
}
|
||||
} catch (_: NullPointerException) {
|
||||
logger.crash("Internal inconsistency: Unknown attribute '${component.substring(10)}' (component '${component}')")
|
||||
}
|
||||
|
||||
// Colors
|
||||
} else if (component.startsWith("COLOR:")) {
|
||||
try {
|
||||
lastColor = component.substring(6)
|
||||
append(color(lastColor, modifiers)!!)
|
||||
} catch (_: NullPointerException) {
|
||||
logger.crash("Internal inconsistency: Unknown color '${component.substring(6)}' (component '${component}')")
|
||||
}
|
||||
} else
|
||||
logger.crash("Internal inconsistency: Unknown component '${component}'")
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the matching ANSI color code.
|
||||
*
|
||||
* @param color color to return
|
||||
* @return ANSI color code
|
||||
* @since v1-alpha10
|
||||
*/
|
||||
@Suppress("CyclomaticComplexMethod")
|
||||
private fun color(color: String, modifiers: String): String? {
|
||||
var colorCode: String = "${ESCAPE}[${modifiers}"
|
||||
|
||||
when (color) {
|
||||
"WHITE" -> colorCode += "37"
|
||||
"BLACK" -> colorCode += "30"
|
||||
"GRAY" -> colorCode += "39;5;244"
|
||||
"YELLOW" -> colorCode += "33"
|
||||
"GREEN" -> colorCode += "38;5;157"
|
||||
"CYAN" -> colorCode += "36"
|
||||
"LIGHT_BLUE" -> colorCode += "38;5;39"
|
||||
"BLUE" -> colorCode += "34"
|
||||
"PURPLE" -> colorCode += "38;5;129"
|
||||
"MAGENTA" -> colorCode += "35"
|
||||
"PINK" -> colorCode += "38;5;199"
|
||||
"RED" -> colorCode += "31"
|
||||
"ORANGE" -> colorCode += "38;5;208"
|
||||
else -> return null
|
||||
}
|
||||
|
||||
colorCode += "m"
|
||||
|
||||
return colorCode
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the attribute and
|
||||
* returns the matching modifiers.
|
||||
*
|
||||
* @param attribute attribute to resolve
|
||||
* @return modifiers
|
||||
* @since v1-alpha10
|
||||
*/
|
||||
private fun attribute(attribute: String, modifiers: String): String? = when (attribute) {
|
||||
"BOLD" -> modifiers.plus("0;")
|
||||
"ITALIC" -> modifiers.plus("3;")
|
||||
"UNDERLINE" -> modifiers.plus("4;")
|
||||
"STRIKETHROUGH" -> modifiers.plus("9;")
|
||||
else -> null
|
||||
}
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* STAROPENSOURCE ENGINE SOURCE FILE
|
||||
* Copyright (c) 2024 The StarOpenSource Engine Authors
|
||||
* Licensed under the GNU General Public License v3.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package de.staropensource.engine.ansi
|
||||
|
||||
import de.staropensource.engine.base.implementable.Subsystem
|
||||
import de.staropensource.engine.base.logging.Logger
|
||||
import de.staropensource.engine.base.utility.dnihbd.BuildInformation
|
||||
|
||||
/**
|
||||
* The ANSI subsystem.
|
||||
*
|
||||
* @since v1-alpha10
|
||||
*/
|
||||
class AnsiSubsystem : Subsystem() {
|
||||
/**
|
||||
* Companion object of [AnsiSubsystem].
|
||||
*
|
||||
* @since v1-alpha10
|
||||
*/
|
||||
companion object {
|
||||
/**
|
||||
* Contains the [Logger] instance of the ANSI subsystem.
|
||||
*
|
||||
* @since v1-alpha10
|
||||
*/
|
||||
internal val logger: Logger = Logger(channel = "engine-ansi")
|
||||
|
||||
/**
|
||||
* Contains a [BuildInformation] instance
|
||||
* providing information about the running
|
||||
* ANSI subsystem build.
|
||||
*
|
||||
* @see BuildInformation
|
||||
* @since v1-alpha10
|
||||
*/
|
||||
var info: BuildInformation? = null
|
||||
}
|
||||
|
||||
|
||||
// -----> Metadata
|
||||
override fun getName(): String = "ANSI"
|
||||
override fun getVersion(): String = info?.versionString(semver = false) ?: "unknown"
|
||||
|
||||
|
||||
// -----> Lifecycle
|
||||
override fun initialize() {
|
||||
info = BuildInformation("sosengine-ansi")
|
||||
}
|
||||
|
||||
override fun shutdown(final: Boolean, fatalCrash: Boolean) {
|
||||
info = null
|
||||
}
|
||||
}
|
2
ansi/src/main/resources/.gitignore
vendored
Normal file
2
ansi/src/main/resources/.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
sosengine-*-git.properties
|
||||
sosengine-*-gradle.properties
|
|
@ -18,7 +18,7 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package de.staropensource.engine.newsubprj;
|
||||
package de.staropensource.engine.newsubprj
|
||||
|
||||
/**
|
||||
* An example class.
|
||||
|
|
|
@ -27,6 +27,9 @@ include("")
|
|||
// -> Base
|
||||
include("base")
|
||||
|
||||
// -> Subsystems
|
||||
include("ansi")
|
||||
|
||||
// -> Testing
|
||||
include("testapp")
|
||||
include("testing")
|
||||
|
|
|
@ -26,6 +26,7 @@ plugins {
|
|||
dependencies {
|
||||
// sos!engine
|
||||
implementation(project(":base"))
|
||||
implementation(project(":ansi"))
|
||||
}
|
||||
|
||||
// Configure JAR
|
||||
|
|
Loading…
Reference in a new issue