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