1 /++
2  + Simple coloring module for strings
3  +
4  + Copyright: Copyright © 2017, Christian Köstlin
5  + Authors: Christian Koestlin
6  + License: MIT
7  +/
8 module colored;
9 
10 public import colored.packageversion;
11 
12 import std..string;
13 
14 /// Available Colors
15 enum AnsiColor
16 {
17     black = 30,
18     red = 31,
19     green = 32,
20     yellow = 33,
21     blue = 34,
22     magenta = 35,
23     cyan = 36,
24     lightGray = 37,
25     defaultColor = 39,
26     darkGray = 90,
27     lightRed = 91,
28     lightGreen = 92,
29     lightYellow = 93,
30     lightBlue = 94,
31     lightMagenta = 95,
32     lightCyan = 96,
33     white = 97
34 }
35 
36 /// Available Styles
37 enum Style
38 {
39     bold = 1,
40     dim = 2,
41     underlined = 4,
42     blink = 5,
43     reverse = 7,
44     hidden = 8
45 }
46 
47 /// Internal structure to style a string
48 struct StyledString
49 {
50     private string s;
51     private int[] befores;
52     private int[] afters;
53     this(string s)
54     {
55         this.s = s;
56     }
57 
58     private StyledString addPair(int before, int after)
59     {
60         befores ~= before;
61         afters ~= after;
62         return this;
63     }
64 
65     StyledString setForeground(int color)
66     {
67         return addPair(color, 0);
68     }
69 
70     StyledString setBackground(int color)
71     {
72         return addPair(color + 10, 0);
73     }
74 
75     StyledString addStyle(int style)
76     {
77         return addPair(style, style + 20);
78     }
79 
80     string toString() @safe
81     {
82         import std.algorithm;
83 
84         auto prefix = befores.map!(a => "\033[%dm".format(a)).join("");
85         auto suffix = afters.map!(a => "\033[%dm".format(a)).join("");
86         return "%s%s%s".format(prefix, s, suffix);
87     }
88 }
89 
90 @("styledstring") unittest
91 {
92     import unit_threaded;
93     import std.stdio;
94     import std.traits;
95 
96     foreach (immutable color; [EnumMembers!AnsiColor])
97     {
98         auto colorName = "%s".format(color);
99         writeln(StyledString(colorName).setForeground(color));
100     }
101     foreach (immutable color; [EnumMembers!AnsiColor])
102     {
103         auto colorName = "bg%s".format(color);
104         writeln(StyledString(colorName).setBackground(color));
105     }
106     foreach (immutable style; [EnumMembers!Style])
107     {
108         auto styleName = "%s".format(style);
109         writeln(StyledString(styleName).addStyle(style));
110     }
111 }
112 
113 auto colorMixin(T)()
114 {
115     import std.traits;
116 
117     string res = "";
118     foreach (immutable color; [EnumMembers!T])
119     {
120         auto t = typeof(T.init).stringof;
121         auto c = "%s".format(color);
122         res ~= "auto %1$s(string s) { return StyledString(s).setForeground(%2$s.%1$s); }\n".format(c,
123                 t);
124         res ~= "auto %1$s(StyledString s) { return s.setForeground(%2$s.%1$s); }\n".format(c, t);
125         string name = c[0 .. 1].toUpper ~ c[1 .. $];
126         res ~= "auto on%3$s(string s) { return StyledString(s).setBackground(%2$s.%1$s); }\n".format(c,
127                 t, name);
128         res ~= "auto on%3$s(StyledString s) { return s.setBackground(%2$s.%1$s); }\n".format(c,
129                 t, name);
130     }
131     return res;
132 }
133 
134 auto styleMixin(T)()
135 {
136     import std.traits;
137 
138     string res = "";
139     foreach (immutable style; [EnumMembers!T])
140     {
141         auto t = typeof(T.init).stringof;
142         auto s = "%s".format(style);
143         res ~= "auto %1$s(string s) { return StyledString(s).addStyle(%2$s.%1$s); }\n".format(s, t);
144         res ~= "auto %1$s(StyledString s) { return s.addStyle(%2$s.%1$s); }\n".format(s, t);
145     }
146     return res;
147 }
148 
149 mixin(colorMixin!AnsiColor);
150 mixin(styleMixin!Style);
151 
152 @("api") unittest
153 {
154     import std.stdio;
155 
156     "redOnGreen".red.onGreen.writeln;
157     "redOnYellowBoldUnderlined".red.onYellow.bold.underlined.writeln;
158 }