Add ansi subsystem (only formatter for now)
All checks were successful
PRs & Pushes / build (push) Successful in 4m47s
PRs & Pushes / test (push) Successful in 4m41s
PRs & Pushes / build-apidoc (push) Successful in 2m52s

This commit is contained in:
JeremyStar™ 2024-12-20 21:49:18 +01:00
parent 76b2105e53
commit 0ffd137891
Signed by: JeremyStarTM
GPG key ID: E366BAEF67E4704D
8 changed files with 247 additions and 1 deletions

2
ansi/README.md Normal file
View 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
View 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"))
}

View file

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

View file

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

@ -0,0 +1,2 @@
sosengine-*-git.properties
sosengine-*-gradle.properties

View file

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

View file

@ -27,6 +27,9 @@ include("")
// -> Base
include("base")
// -> Subsystems
include("ansi")
// -> Testing
include("testapp")
include("testing")

View file

@ -26,6 +26,7 @@ plugins {
dependencies {
// sos!engine
implementation(project(":base"))
implementation(project(":ansi"))
}
// Configure JAR