1 /**
2  * Authors: Pedro Tacla Yamada
3  * Date: June 9, 2014
4  * License: Licensed under the MIT license. See LICENSE for more information
5  * Version: 1.0.2
6  */
7 module colorize.colors;
8 
9 import std..string : format;
10 
11 private template color_type(int offset)
12 {
13   static enum type : int
14   {
15     init = 39 + offset,
16 
17     black   = 30 + offset,
18     red     = 31 + offset,
19     green   = 32 + offset,
20     yellow  = 33 + offset,
21     blue    = 34 + offset,
22     magenta = 35 + offset,
23     cyan    = 36 + offset,
24     white   = 37 + offset,
25 
26     light_black   = 90 + offset,
27     light_red     = 91 + offset,
28     light_green   = 92 + offset,
29     light_yellow  = 93 + offset,
30     light_blue    = 94 + offset,
31     light_magenta = 95 + offset,
32     light_cyan    = 96 + offset,
33     light_white   = 97 + offset
34   }
35 }
36 
37 alias color_type!0 .type fg;
38 alias color_type!10 .type bg;
39 
40 // Text modes
41 static enum mode : int
42 {
43   init      = 0,
44   bold      = 1,
45   underline = 4,
46   blink     = 5,
47   swap      = 7,
48   hide      = 8
49 }
50 
51 /**
52  * Wraps a string around color escape sequences.
53  *
54  * Params:
55  *   str = The string to wrap with colors and modes
56  *   c   = The foreground color (see the fg enum type)
57  *   b   = The background color (see the bg enum type)
58  *   m   = The text mode        (see the mode enum type)
59  * Example:
60  * ---
61  * writeln("This is blue".color(fg.blue));
62  * writeln(
63  *   color("This is red over green blinking", fg.blue, bg.green, mode.blink)
64  * );
65  * ---
66  */
67 
68 string color(
69   const string str,
70   const fg c=fg.init,
71   const bg b=bg.init,
72   const mode m=mode.init
73 ) pure
74 {
75   return format("\033[%d;%d;%dm%s\033[0m", m, c, b, str);
76 }
77 
78 unittest
79 {
80   import colorize.cwrite;
81   string ret;
82 
83   ret = "This is yellow".color(fg.yellow);
84   cwriteln(ret);
85   assert(ret == "\033[33mThis is yellow\033[0m");
86 
87   ret = "This is light green".color(fg.light_green);
88   cwriteln(ret);
89   assert(ret == "\033[92mThis is light green\033[0m");
90 
91   ret = "This is light blue with red background".color(fg.light_blue, bg.red);
92   cwriteln(ret);
93   assert(ret == "\033[0;94;41mThis is light blue with red background\033[0m");
94 
95   ret = "This is red on blue blinking".color(fg.red, bg.blue, mode.blink);
96   cwriteln(ret);
97   assert(ret == "\033[5;31;44mThis is red on blue blinking\033[0m");
98 
99   ret = color("This is magenta", "magenta");
100   cwriteln(ret);
101   assert(ret == "\033[35mThis is magenta\033[0m");
102 }
103 
104 string colorHelper(const string str, const string name) pure
105 {
106   int code;
107 
108   switch(name)
109   {
110     case "init": code = 39; break;
111 
112     case "black"  : code = 30; break;
113     case "red"    : code = 31; break;
114     case "green"  : code = 32; break;
115     case "yellow" : code = 33; break;
116     case "blue"   : code = 34; break;
117     case "magenta": code = 35; break;
118     case "cyan"   : code = 36; break;
119     case "white"  : code = 37; break;
120 
121     case "light_black"  : code = 90; break;
122     case "light_red"    : code = 91; break;
123     case "light_green"  : code = 92; break;
124     case "light_yellow" : code = 93; break;
125     case "light_blue"   : code = 94; break;
126     case "light_magenta": code = 95; break;
127     case "light_cyan"   : code = 96; break;
128     case "light_white"  : code = 97; break;
129 
130     case "bg_init": code = 49; break;
131 
132     case "bg_black"  : code = 40; break;
133     case "bg_red"    : code = 41; break;
134     case "bg_green"  : code = 42; break;
135     case "bg_yellow" : code = 43; break;
136     case "bg_blue"   : code = 44; break;
137     case "bg_magenta": code = 45; break;
138     case "bg_cyan"   : code = 46; break;
139     case "bg_white"  : code = 47; break;
140 
141     case "bg_light_black"  : code = 100; break;
142     case "bg_light_red"    : code = 101; break;
143     case "bg_light_green"  : code = 102; break;
144     case "bg_light_yellow" : code = 103; break;
145     case "bg_light_blue"   : code = 104; break;
146     case "bg_light_magenta": code = 105; break;
147     case "bg_light_cyan"   : code = 106; break;
148     case "bg_light_white"  : code = 107; break;
149 
150     case "mode_init": code = 0; break;
151     case "mode_bold"     : code = 1; break;
152     case "mode_underline": code = 4; break;
153     case "mode_blink"    : code = 5; break;
154     case "mode_swap"     : code = 7; break;
155     case "mode_hide"     : code = 8; break;
156 
157     default:
158       throw new Exception(
159         "Unknown fg color, bg color or mode \"" ~ name ~ "\""
160       );
161   }
162 
163   return format("\033[%dm%s\033[0m", code, str);
164 }
165 
166 string colorHelper(T)(const string str, const T t=T.init) pure
167   if(is(T : fg) || is(T : bg) || is(T : mode))
168 {
169   return format("\033[%dm%s\033[0m", t, str);
170 }
171 
172 // Custom colors
173 string customForeground(const string str, float r, float g, float b)
174 {
175   uint red   = cast(uint)(r * 5 + 0.5);
176   uint green = cast(uint)(g * 5 + 0.5);
177   uint blue  = cast(uint)(b * 5 + 0.5);
178 
179   return format("\033[38;5;%dm%s\033[0m",
180                 16 + (red * 36 + green * 6 + blue),
181                 str);
182 }
183 
184 alias colorHelper!bg background;
185 alias colorHelper!fg foreground;
186 alias colorHelper!mode style;
187 
188 alias background color;
189 alias foreground color;
190 alias style color;
191 alias colorHelper color;
192 
193 unittest
194 {
195   import colorize.cwrite;
196   string ret;
197 
198   ret = "This is red on blue blinking"
199     .foreground(fg.red)
200     .background(bg.blue)
201     .style(mode.blink);
202 
203   cwriteln(ret);
204   assert(ret == "\033[5m\033[44m\033[31mThis is red on blue blinking\033[0m\033[0m\033[0m");
205 
206   ret = "This is a custom color";
207   cwriteln(ret.customForeground(1, 0.1, 0.3));
208 }