diff --git a/dub.json b/dub.json index 2d72e33..547b549 100644 --- a/dub.json +++ b/dub.json @@ -3,7 +3,10 @@ "description": "A port of Ruby's colorize library to D.", "license": "MIT", "copyright": "Copyright © 2014, Pedro Tacla Yamada", + "importPaths": ["source"], + "sourcePaths": ["source"], "authors": [ - "Pedro Tacla Yamada" + "Pedro Tacla Yamada", + "ponce" ] } diff --git a/source/colorize.d b/source/colorize/colorize.d similarity index 99% rename from source/colorize.d rename to source/colorize/colorize.d index ae707c6..49628d8 100644 --- a/source/colorize.d +++ b/source/colorize/colorize.d @@ -4,6 +4,7 @@ * License: Licensed under the MIT license. See LICENSE for more information * Version: 0.1.0 */ +module colorize.colorize; import std.string : format; diff --git a/source/colorize/cwrite.d b/source/colorize/cwrite.d new file mode 100644 index 0000000..f5e5857 --- /dev/null +++ b/source/colorize/cwrite.d @@ -0,0 +1,74 @@ +/** + * Authors: ponce + * Date: July 28, 2014 + * License: Licensed under the MIT license. See LICENSE for more information + * Version: 0.1.0 + */ +module colorize.cwrite; + +import std.stdio : File, stdout; + +import colorize.winterm; + +/// Coloured write. +void cwrite(T...)(T args) if (!is(T[0] : File)) +{ + stdout.cwrite(args); +} + +/// Coloured writef. +void cwritef(T...)(T args) +{ + stdout.cwritef(args); +} + +/// Coloured writefln. +void cwritefln(T...)(T args) +{ + stdout.cwritef(args, "\n"); +} + +/// Coloured writeln. +void cwriteln(T...)(T args) +{ + // Most general instance + stdout.cwrite(args, '\n'); +} + +/// Coloured writef to a File. +void cwritef(Char, A...)(File f, in Char[] fmt, A args) +{ + auto s = format(fmt, args); + f.cwrite(s); +} + +/// Coloured writef to a File. +void cwrite(S...)(File f, S args) +{ + import std.conv : to; + + string s = ""; + foreach(arg; args) + s ~= to!string(arg); + + version(Windows) + { + WinTermEmulation winterm; + winterm.initialize(); + foreach(dchar c ; s) + { + auto charAction = winterm.feed(c); + final switch(charAction) with (WinTermEmulation.CharAction) + { + case drop: break; + case write: f.write(c); break; + case flush: f.flush(); break; + } + } + } + else + { + f.write(s); + } +} + diff --git a/source/colorize/package.d b/source/colorize/package.d new file mode 100644 index 0000000..c4c07f5 --- /dev/null +++ b/source/colorize/package.d @@ -0,0 +1,10 @@ +/** + * Authors: ponce + * Date: July 28, 2014 + * License: Licensed under the MIT license. See LICENSE for more information + * Version: 0.1.0 + */ +module colorize; + +public import colorize.colorize; +public import colorize.cwrite; \ No newline at end of file diff --git a/source/colorize/winterm.d b/source/colorize/winterm.d new file mode 100644 index 0000000..a10e731 --- /dev/null +++ b/source/colorize/winterm.d @@ -0,0 +1,162 @@ +/** + * Authors: ponce + * Date: July 28, 2014 + * License: Licensed under the MIT license. See LICENSE for more information + * Version: 0.1.0 + */ +module colorize.winterm; + + +version(Windows) +{ + import core.sys.windows.windows; + + // This is a state machine to enable terminal colors on Windows. + // Parses and interpret ANSI/VT100 Terminal Control Escape Sequences. + // Only supports colour sequences, will output char incorrectly on invalid input. + struct WinTermEmulation + { + public: + @nogc void initialize() nothrow + { + // saves console attributes + _console = GetStdHandle(STD_OUTPUT_HANDLE); + _savedInitialColor = (0 != GetConsoleScreenBufferInfo(_console, &consoleInfo)); + _state = State.initial; + } + + @nogc ~this() nothrow + { + // Restore initial text attributes on release + if (_savedInitialColor) + { + SetConsoleTextAttribute(_console, consoleInfo.wAttributes); + _savedInitialColor = false; + } + } + + enum CharAction + { + write, + drop, + flush + } + + // Eat one character and update color state accordingly. + // Returns what to do with the fed character. + @nogc CharAction feed(dchar d) nothrow + { + final switch(_state) with (State) + { + case initial: + if (d == '\x1B') + { + _state = escaped; + return CharAction.flush; + } + break; + + case escaped: + if (d == '[') + { + _state = readingAttribute; + _parsedAttr = 0; + return CharAction.drop; + } + break; + + + case readingAttribute: + if (d >= '0' && d <= '9') + { + _parsedAttr = _parsedAttr * 10 + (d - '0'); + return CharAction.drop; + } + else if (d == ';') + { + executeAttribute(_parsedAttr); + _parsedAttr = 0; + return CharAction.drop; + } + else if (d == 'm') + { + executeAttribute(_parsedAttr); + _state = State.initial; + return CharAction.drop; + } + break; + } + return CharAction.write; + } + + private: + HANDLE _console; + bool _savedInitialColor; + CONSOLE_SCREEN_BUFFER_INFO consoleInfo; + State _state; + WORD _currentAttr; + int _parsedAttr; + + enum State + { + initial, + escaped, + readingAttribute + } + + @nogc void setForegroundColor(WORD fgFlags) nothrow + { + _currentAttr = _currentAttr & ~(FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_INTENSITY); + _currentAttr = _currentAttr | fgFlags; + SetConsoleTextAttribute(_console, _currentAttr); + } + + @nogc void setBackgroundColor(WORD bgFlags) nothrow + { + _currentAttr = _currentAttr & ~(BACKGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_RED | BACKGROUND_INTENSITY); + _currentAttr = _currentAttr | bgFlags; + SetConsoleTextAttribute(_console, _currentAttr); + } + + @nogc void executeAttribute(int attr) nothrow + { + switch (attr) + { + case 0: + // reset all attributes + SetConsoleTextAttribute(_console, consoleInfo.wAttributes); + break; + + default: + if ( (30 <= attr && attr <= 37) || (90 <= attr && attr <= 97) ) + { + WORD color = 0; + if (90 <= attr && attr <= 97) + { + color = FOREGROUND_INTENSITY; + attr -= 60; + } + attr -= 30; + color |= (attr & 1 ? FOREGROUND_RED : 0) | (attr & 2 ? FOREGROUND_GREEN : 0) | (attr & 4 ? FOREGROUND_BLUE : 0); + setForegroundColor(color); + } + + if ( (40 <= attr && attr <= 47) || (100 <= attr && attr <= 107) ) + { + WORD color = 0; + if (100 <= attr && attr <= 107) + { + color = BACKGROUND_INTENSITY; + attr -= 60; + } + attr -= 40; + color |= (attr & 1 ? BACKGROUND_RED : 0) | (attr & 2 ? BACKGROUND_GREEN : 0) | (attr & 4 ? BACKGROUND_BLUE : 0); + setBackgroundColor(color); + } + } + } + } +} + + +