diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 6d89b8a..358cbc6 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -63,13 +63,9 @@ jobs: - name: Build bare test run: | echo "Building bare test" - cd tests/bare/ - haxe test.hxml - cd ../../ + haxe tests/bare/test.hxml - name: Build unit tests run: | echo "Building unit tests" - cd tests/unit/ - haxe test.hxml - cd ../../ \ No newline at end of file + haxe tests/unit/test.hxml \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index 3118d95..938e059 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,15 +1,6 @@ { "haxe.configurations": - [ [ "-cp", "test/src" - , "-cp", "lib" - , "-D", "analyzer-optimize" - // , "-D", "log=verbose" - // , "-D", "special.log=[info,ERROR]" - // , "-D", "special.throw=[VERBOSE]" - // , "-D", "special.log=NONE" - // , "-D", "special.throw=NONE" - , "-main", "Main" - , "--interp" - ] + [ [ "tests/bare/test.hxml" ] + , [ "tests/unit/test.hxml", "-D logger.unit_test.show_styles" ] ] //, "haxe.displayServer": { // configuration for starting haxe completion server itself // "arguments": ["-v"], // arguments before --wait (-v is useful for debugging) diff --git a/lib/debug/Logger.hx b/lib/debug/Logger.hx index 67d3f4f..05f6b12 100644 --- a/lib/debug/Logger.hx +++ b/lib/debug/Logger.hx @@ -3,6 +3,7 @@ package debug; import debug.Assert; import haxe.PosInfos; +using debug.ansi.StyleTools; /** * Tool used to simplify the categorization of logs, and easily customize which type of logs * are displayed, and which throw exceptions. @@ -55,11 +56,9 @@ abstract Logger(LoggerRaw) from LoggerRaw } /** - * Controls how each every Logger will actually log the message, this can also be set for each - * individual logger with: - * `myLogger.log = (msg, ?pos)->haxe.Log.trace('[${getTimestamp()}] $msg', pos);` + * Controls how each every Logger will actually log the message */ - dynamic static public function globalLog(msg:String, ?pos:PosInfos) + dynamic static public function globalLog(msg:Any, ?pos:PosInfos) { haxe.Log.trace(msg, pos); } @@ -67,16 +66,41 @@ abstract Logger(LoggerRaw) from LoggerRaw /** * Set this to make a custom log formatter, and log will use this to format it's information */ - dynamic static public function globalFormatter(id:Null, priority:Priority, msg:Any, ?pos:PosInfos) + dynamic static public function globalFormatter(id:Null, priority:Priority, msg:Any, ?pos:PosInfos):Any { - return if (id != null && priority != NONE) - '$id[$priority]: $msg'; + final result = + if (id != null && priority != NONE) + '${id}[$priority]: $msg'; else if (priority != NONE) - '$priority: $msg'; + '$priority: $msg'; else if (id != null) - '$id: $msg'; + '$id: $msg'; else - '$msg'; + '$msg'; + + #if logger.no_color + return result; + #else + return colorizeMessage(priority, result); + #end + } + + static public function colorizeMessage(priority:Priority, msg:String):String + { + return switch priority + { + case ERROR: + // msg.style([COLOR_FG(RED), BOLD]); + msg.color(RED); + case WARN: + msg.color(YELLOW); + case INFO: + msg; + case NONE: + msg.color(WHITE); + case VERBOSE: + msg.style(DIM); + } } static final list = new Map(); @@ -96,15 +120,19 @@ abstract Logger(LoggerRaw) from LoggerRaw { this = (cast Logger.log: LoggerRaw); } - else if (list.exists(id)) - { - // Note: do not set priority again - this = list[id]; - } else { - this = LoggerRaw.fromLevels(id, priority, throwPriority); - list[id] = this; + final sanitizedID = LoggerTools.sanitizeID(id); + if (list.exists(sanitizedID)) + { + // Note: do not set priority again + this = list[sanitizedID]; + } + else + { + this = LoggerRaw.fromLevels(id, priority, throwPriority); + list[sanitizedID] = this; + } } } @@ -126,7 +154,7 @@ abstract Logger(LoggerRaw) from LoggerRaw if (this.id == null) return new Logger(subID); - final fullID = '${this.id}.$subID'; + final fullID = LoggerTools.sanitizeID('${this.id}.$subID'); if (list.exists(fullID)) return list[fullID]; @@ -177,14 +205,6 @@ private class LoggerRaw verbose = new LoggerPriority(this, VERBOSE); } - #if logger.unit_test - public function resetFromCompilerFlags() - { - logLevels.resetFromCompilerFlags(LOG, id); - throwLevels.resetFromCompilerFlags(THROW, id); - } - #end - public function destroy() { error.destroy(); @@ -193,8 +213,17 @@ private class LoggerRaw verbose.destroy(); } + #if logger.unit_test + public function resetFromCompilerFlags() + { + logLevels.resetFromCompilerFlags(LOG, id); + throwLevels.resetFromCompilerFlags(THROW, id); + } + #end + public function log(msg:Any, ?pos:PosInfos) { + final isEmpty = logLevels.isEmpty(); if (logLevels.isEmpty() == false) logFinal(NONE, msg, pos); } @@ -393,7 +422,7 @@ abstract PriorityList(Array) from Array if (id == null) return fromGlobalCompilerFlag(type, backup); - final id = contextFinder.replace(id.toLowerCase(), ""); + final id = LoggerTools.removeContext(LoggerTools.sanitizeID(id)); // check if flags are cached for this id final key = '$id.$type'; @@ -544,3 +573,33 @@ class LoggerDefines } #end } + + +private class LoggerTools +{ + static final contextFinder = ~/\[(.*?)\]/g; + + /** + * Removes style + */ + static public function sanitizeID(id:String) + { + return id.unstyle().toLowerCase(); + } + + /** + * Changes "Main[Foo].Sub[Context]" to "Main.Foo.Sub.Context" + */ + static public function subContext(id:String) + { + return contextFinder.replace(id, ".$1"); + } + + /** + * Removes context, ex: "Main[Foo].Sub[Context]" to "Main.Sub" + */ + static public function removeContext(id:String) + { + return contextFinder.replace(id, ""); + } +} \ No newline at end of file diff --git a/lib/debug/ansi/Color.hx b/lib/debug/ansi/Color.hx new file mode 100644 index 0000000..faa0794 --- /dev/null +++ b/lib/debug/ansi/Color.hx @@ -0,0 +1,90 @@ +package debug.ansi; + +import haxe.xml.Access; + +/** + * Class representing a color, based on Int + */ +enum abstract Color(Int) from Int from UInt to Int to UInt +{ + var BLACK = 0x000000; // 30 + var RED = 0xFF0000; // 31 + var GREEN = 0x00FF00; // 32 + var YELLOW = 0xFFFF00; // 33 + var BLUE = 0x0000FF; // 34 + var MAGENTA = 0xFF00FF; // 35 + var CYAN = 0x00FFFF; // 36 + var WHITE = 0xFFFFFF; // 37 + var GRAY = 0x808080; + var ORANGE = 0xFFA500; + var PURPLE = 0x800080; + var PINK = 0xFFC0CB; + var BROWN = 0x8B4513; + + public var red (get, never):UInt; + public var blue (get, never):UInt; + public var green(get, never):UInt; + + inline function get_red ():UInt return (this >> 16) & 0xFF; + inline function get_green():UInt return (this >> 8) & 0xFF; + inline function get_blue ():UInt return (this >> 0) & 0xFF; + + /** + * Set RGB values as integers (0 to 255) + * + * @param red The red value of the color from 0 to 255 + * @param green The green value of the color from 0 to 255 + * @param blue The green value of the color from 0 to 255 + * @return This color + */ + inline public function set(red:UInt, green:UInt, blue:UInt):Color + { + return this = concat(red, green, blue); + } + + /** + * Creates a color from an RGB int + * + * @param value Usually a hex integer, I.E.: 0xFF00FF + * @param clampBits If true, discards extra bits, I.E.: 0xFF00FF00 becomes 0x00FF00 + */ + overload public inline extern function new(value:UInt, clampBits = false) + { + this = clampBits ? value & 0xFFFFFF : value; + } + + overload public inline extern function new(red:UInt, green:UInt, blue:UInt) + { + this = concat(red, green, blue); + } + + /** + * Returns a new color with extra bits discarded + */ + inline public function to24Bit() + { + return new Color(this, true); + } + + /** + * Creates a hex string representing this color, in the format "0xRRGGBB" + */ + public function toString() + { + return '0x' + StringTools.lpad(StringTools.hex(this), "0", 6); + } + + /** + * Internal helper for concatenating three color bytes into 1 24bit integer + * + * @param red The red value of the color from 0 to 255 + * @param green The green value of the color from 0 to 255 + * @param blue The green value of the color from 0 to 255 + */ + static public inline function concat(red:UInt, green:UInt, blue:UInt) + { + return ((red & 0xFF) << 16) + | ((green & 0xFF) << 8) + | ((blue & 0xFF) << 0); + } +} \ No newline at end of file diff --git a/lib/debug/ansi/StyleTools.hx b/lib/debug/ansi/StyleTools.hx new file mode 100644 index 0000000..34ed9b6 --- /dev/null +++ b/lib/debug/ansi/StyleTools.hx @@ -0,0 +1,219 @@ +package debug.ansi; + +class StyleTools +{ + overload static public inline extern function unstyle(string:String):String + { + // #if interp + final split = string.split("\u001b["); + for (i in 1...split.length)// skip first + { + final str = split[i]; + split[i] = str.substr(str.indexOf("m") + 1); + } + + return split.join(""); + // #else // TODO: Test on various targets + // try + // { + // static final remover = ~/\u001b\[[0-9;]+m/g; + // return remover.split(string).join(""); + // } + // catch(e) + // { + // throw "Error creating EReg, create an issue, here: https://github.com/Geokureli/Logger/issues/new" + // } + // #end + } + + overload static public inline extern function style(string:String, style:Style):String + { + return apply(string, style); + } + + overload static public inline extern function style(string:String, styles:Array