Merge 0.10->trunk
[prosody.git] / util / termcolours.lua
1 -- Prosody IM
2 -- Copyright (C) 2008-2010 Matthew Wild
3 -- Copyright (C) 2008-2010 Waqas Hussain
4 --
5 -- This project is MIT/X11 licensed. Please see the
6 -- COPYING file in the source package for more information.
7 --
8 --
9 -- luacheck: ignore 213/i
10
11
12 local t_concat, t_insert = table.concat, table.insert;
13 local char, format = string.char, string.format;
14 local tonumber = tonumber;
15 local ipairs = ipairs;
16 local io_write = io.write;
17 local m_floor = math.floor;
18 local type = type;
19 local setmetatable = setmetatable;
20 local pairs = pairs;
21
22 local windows;
23 if os.getenv("WINDIR") then
24         windows = require "util.windows";
25 end
26 local orig_color = windows and windows.get_consolecolor and windows.get_consolecolor();
27
28 local _ENV = nil;
29
30 local stylemap = {
31                         reset = 0; bright = 1, dim = 2, underscore = 4, blink = 5, reverse = 7, hidden = 8;
32                         black = 30; red = 31; green = 32; yellow = 33; blue = 34; magenta = 35; cyan = 36; white = 37;
33                         ["black background"] = 40; ["red background"] = 41; ["green background"] = 42; ["yellow background"] = 43; ["blue background"] = 44; ["magenta background"] = 45; ["cyan background"] = 46; ["white background"] = 47;
34                         bold = 1, dark = 2, underline = 4, underlined = 4, normal = 0;
35                 }
36
37 local winstylemap = {
38         ["0"] = orig_color, -- reset
39         ["1"] = 7+8, -- bold
40         ["1;33"] = 2+4+8, -- bold yellow
41         ["1;31"] = 4+8 -- bold red
42 }
43
44 local cssmap = {
45         [1] = "font-weight: bold", [2] = "opacity: 0.5", [4] = "text-decoration: underline", [8] = "visibility: hidden",
46         [30] = "color:black", [31] = "color:red", [32]="color:green", [33]="color:#FFD700",
47         [34] = "color:blue", [35] = "color: magenta", [36] = "color:cyan", [37] = "color: white",
48         [40] = "background-color:black", [41] = "background-color:red", [42]="background-color:green",
49         [43]="background-color:yellow", [44] = "background-color:blue", [45] = "background-color: magenta",
50         [46] = "background-color:cyan", [47] = "background-color: white";
51 };
52
53 local fmt_string = char(0x1B).."[%sm%s"..char(0x1B).."[0m";
54 local function getstring(style, text)
55         if style then
56                 return format(fmt_string, style, text);
57         else
58                 return text;
59         end
60 end
61
62 local function gray(n)
63         return m_floor(n*3/32)+0xe8;
64 end
65 local function color(r,g,b)
66         if r == g and g == b then
67                 return gray(r);
68         end
69         r = m_floor(r*3/128);
70         g = m_floor(g*3/128);
71         b = m_floor(b*3/128);
72         return 0x10 + ( r * 36 ) + ( g * 6 ) + ( b );
73 end
74 local function hex2rgb(hex)
75         local r = tonumber(hex:sub(1,2),16);
76         local g = tonumber(hex:sub(3,4),16);
77         local b = tonumber(hex:sub(5,6),16);
78         return r,g,b;
79 end
80
81 setmetatable(stylemap, { __index = function(_, style)
82         if type(style) == "string" and style:find("%x%x%x%x%x%x") == 1 then
83                 local g = style:sub(7) == " background" and "48;5;" or "38;5;";
84                 return g .. color(hex2rgb(style));
85         end
86 end } );
87
88 local csscolors = {
89         red = "ff0000"; fuchsia = "ff00ff"; green = "008000"; white = "ffffff";
90         lime = "00ff00"; yellow = "ffff00"; purple = "800080"; blue = "0000ff";
91         aqua = "00ffff"; olive  = "808000"; black  = "000000"; navy = "000080";
92         teal = "008080"; silver = "c0c0c0"; maroon = "800000"; gray = "808080";
93 }
94 for colorname, rgb in pairs(csscolors) do
95         stylemap[colorname] = stylemap[colorname] or stylemap[rgb];
96         colorname, rgb = colorname .. " background", rgb .. " background"
97         stylemap[colorname] = stylemap[colorname] or stylemap[rgb];
98 end
99
100 local function getstyle(...)
101         local styles, result = { ... }, {};
102         for i, style in ipairs(styles) do
103                 style = stylemap[style];
104                 if style then
105                         t_insert(result, style);
106                 end
107         end
108         return t_concat(result, ";");
109 end
110
111 local last = "0";
112 local function setstyle(style)
113         style = style or "0";
114         if style ~= last then
115                 io_write("\27["..style.."m");
116                 last = style;
117         end
118 end
119
120 if windows then
121         function setstyle(style)
122                 style = style or "0";
123                 if style ~= last then
124                         windows.set_consolecolor(winstylemap[style] or orig_color);
125                         last = style;
126                 end
127         end
128         if not orig_color then
129                 function setstyle() end
130         end
131 end
132
133 local function ansi2css(ansi_codes)
134         if ansi_codes == "0" then return "</span>"; end
135         local css = {};
136         for code in ansi_codes:gmatch("[^;]+") do
137                 t_insert(css, cssmap[tonumber(code)]);
138         end
139         return "</span><span style='"..t_concat(css, ";").."'>";
140 end
141
142 local function tohtml(input)
143         return input:gsub("\027%[(.-)m", ansi2css);
144 end
145
146 return {
147         getstring = getstring;
148         getstyle = getstyle;
149         setstyle = setstyle;
150         tohtml = tohtml;
151 };