1 /** ANSI escape codes and sequences. 2 * 3 * See_Also: https://en.wikipedia.org/wiki/ANSI_escape_code 4 */ 5 module nxt.ansi_escape; 6 7 public import nxt.color : ColorRGB8; 8 9 @safe: 10 11 /** Visual attributes. 12 */ 13 struct Attrs 14 { 15 @safe: 16 immutable(SGR)[] sgrs; ///< Ordered set of SGR, typically initialized from `static immutable(SGR)[]`. 17 ColorRGB8 foregroundColor; ///< Foreground color. 18 ColorRGB8 backgroundColor; ///< Background color. 19 bool useForegroundColor; ///< Indicate if 'foregroundColor is to be used. 20 bool useBackgroundColor; ///< Indicate if 'backgroundColor is to be used. 21 22 void set(scope void delegate(scope const(char)[]) @safe sink) 23 { 24 setSGRs(sink, sgrs); 25 if (useForegroundColor) 26 setForegroundColorRGB8(sink, foregroundColor); 27 if (useBackgroundColor) 28 setBackgroundColorRGB8(sink, backgroundColor); 29 } 30 31 void reset(scope void delegate(scope const(char)[]) @safe sink) 32 { 33 resetSGRs(sink); 34 } 35 } 36 37 /** SGR (Select Graphic Rendition) sets display attributes. 38 * 39 * See_Also: https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_parameters 40 */ 41 enum SGR : uint 42 { 43 init = 0, ///< Default. 44 bold = 1, ///< Bold or increased intensity. 45 faint = 2, ///< Faint (decreased intensity) 46 italic = 3, ///< Italic. Not widely supported. Sometimes treated as inverse. 47 underline = 4, ///< Underline. 48 slowBlink = 5, ///< Slow blink. 49 rapidBlink = 6, ///< Rapid blink. 50 reverseVideo = 7, ///< Reversed video (swap). Swap foreground with background color. 51 hide = 8, ///< Conceal (Hide). Not widely supported. 52 crossedOut = 9, ///< Crossed-out. Characters legible, but marked for deletion. 53 primaryDefaultFont = 10, ///< Primary (default) font. 54 fraktur = 20, ///< Fraktur. Rarely supported 55 56 blackForegroundColor = 30, ///< Black foreground color. 57 redForegroundColor = 31, ///< Red foreground color. 58 greenForegroundColor = 32, ///< Green foreground color. 59 yellowForegroundColor = 33, ///< Yellow foreground color. 60 blueForegroundColor = 34, ///< Blue foreground color. 61 magentaForegroundColor = 35, ///< Magenta foreground color. 62 cyanForegroundColor = 36, ///< Cyan foreground color. 63 whiteForegroundColor = 37, ///< White foreground color. 64 65 defaultForegroundColor = 39, ///< Default foreground color. 66 67 lightBlackForegroundColor = 90, ///< Light black foreground color. 68 lightRedForegroundColor = 91, ///< Light red foreground color. 69 lightGreenForegroundColor = 92, ///< Light green foreground color. 70 lightYellowForegroundColor = 93, ///< Light yellow foreground color. 71 lightBlueForegroundColor = 94, ///< Light blue foreground color. 72 lightMagentaForegroundColor = 95, ///< Light magenta foreground color. 73 lightCyanForegroundColor = 96, ///< Light cyan foreground color. 74 lightWhiteForegroundColor = 97, ///< Light white foreground color. 75 76 defaultBackgroundColor = 49, ///< Default background color. 77 78 framed = 51, ///< Framed. 79 encircled = 52, ///< Encircled. 80 overlined = 53, ///< Overlined. 81 notFramedOrEncircled = 54, ///< Not framed or encircled. 82 notOverlined = 55, ///< Not overlined. 83 IdeogramUnderlineOrRightSideLine = 60, ///< Ideogram underline or right side line. 84 } 85 86 private void setSGR(scope void delegate(scope const(char)[]) @safe sink, 87 const SGR sgr) 88 { 89 final switch (sgr) 90 { 91 static foreach (member; __traits(allMembers, SGR)) 92 { 93 case __traits(getMember, SGR, member): 94 enum _ = cast(int)__traits(getMember, SGR, member); // avoids `std.conv.to` 95 sink(_.stringof); 96 return; 97 } 98 } 99 } 100 101 void setSGRs(scope void delegate(scope const(char)[]) @safe sink, 102 scope const SGR[] sgrs...) @safe 103 { 104 sink("\033["); 105 const n = sgrs.length; 106 foreach (const index, const sgr; sgrs) 107 { 108 setSGR(sink, sgr); // needs to be first 109 if (index != n - 1) // if not last 110 sink(";"); // separator 111 } 112 sink("m"); 113 } 114 115 void resetSGRs(scope void delegate(scope const(char)[]) @safe sink) 116 { 117 sink("\033[0m"); 118 } 119 120 void putWithSGRs(scope void delegate(scope const(char)[]) @safe sink, 121 scope const(char)[] text, 122 scope const SGR[] sgrs...) @safe 123 { 124 setSGRs(sink, sgrs); // set 125 sink(text); 126 resetSGRs(sink); // reset 127 } 128 129 /** Set foreground color to `rgb`. 130 * 131 * See_Also: https://en.wikipedia.org/wiki/ANSI_escape_code#24-bit 132 */ 133 void setForegroundColorRGB8(scope void delegate(scope const(char)[]) @safe sink, 134 const ColorRGB8 rgb) @safe 135 { 136 sink("\033[38;2;"); 137 setColorRGB8Component(sink, rgb.redC); 138 sink(";"); 139 setColorRGB8Component(sink, rgb.greenC); 140 sink(";"); 141 setColorRGB8Component(sink, rgb.blueC); 142 sink("m"); 143 } 144 145 /** Set background color to `rgb`. 146 * 147 * See_Also: https://en.wikipedia.org/wiki/ANSI_escape_code#24-bit 148 */ 149 void setBackgroundColorRGB8(scope void delegate(scope const(char)[]) @safe sink, 150 const ColorRGB8 rgb) @safe 151 { 152 sink("\033[48;2;"); 153 setColorRGB8Component(sink, rgb.redC); 154 sink(";"); 155 setColorRGB8Component(sink, rgb.greenC); 156 sink(";"); 157 setColorRGB8Component(sink, rgb.blueC); 158 sink("m"); 159 } 160 161 /** Set RGB 24-bit color component `component`. 162 * 163 * See_Also: https://en.wikipedia.org/wiki/ANSI_escape_code#24-bit 164 */ 165 static private void setColorRGB8Component(scope void delegate(scope const(char)[]) @safe sink, 166 const ubyte component) @safe 167 { 168 final switch (component) 169 { 170 static foreach (value; 0 .. 256) 171 { 172 case value: 173 sink(value.stringof); // avoids `std.conv.to` 174 return; 175 } 176 } 177 } 178 179 version(none): 180 181 version(unittest) 182 class C 183 { 184 @safe: 185 @property void toString(scope void delegate(scope const(char)[]) @safe sink) const @trusted 186 { 187 putWithSGRs(sink, "XXX", SGR.yellowForegroundColor); 188 } 189 this() {} 190 } 191 192 @safe unittest 193 { 194 import std.stdio : writeln; 195 import std.conv : to; 196 auto c = new C(); 197 writeln(c.to!string); 198 }