UNPKG

5.17 kBJavaScriptView Raw
1var hljs = require('highlight.js');
2var colorize;
3
4/***
5 * hicat() : hicat(str, options)
6 * Highlights a given `str` string.
7 *
8 * var hicat = require('hicat');
9 * hicat("echo 'hi'", { filename: 'script.sh' })
10 * => "echo \033[32m'hi'\033[0m"
11 *
12 * Options available:
13 *
14 * ~ filename (String): filename
15 * ~ lang (String): language to use
16 * ~ debug (Boolean): shows extra info if `true`
17 * ~ numbers (Boolean): shows line numbers if `true`
18 */
19
20function hicat (str, options) {
21 if (!options) options = {};
22
23 var lang = options.lang || (options.filename && extname(options.filename));
24 var out;
25
26 if (lang) {
27 if (lang === 'json')
28 str = formatJson(str);
29 try {
30 out = hljs.highlight(lang, str);
31 } catch (e) {
32 out = hljs.highlightAuto(str);
33 }
34 } else {
35 out = hljs.highlightAuto(str);
36 }
37
38 if (!out || !out.value) throw new Error("failed to highlight");
39
40 // convert <span> tags to ANSI colors
41 out.ansi = html2ansi(out.value, {
42 lang: out.language,
43 debug: options.debug
44 });
45
46 // Add line numbers
47 if (options.numbers) {
48 var i = 0;
49 var lines = out.value.split("\n").length;
50 var digits = (""+lines).length;
51 if (digits < 3) digits = 3;
52 out.ansi = out.ansi.replace(/^/gm, function (s) {
53 i++;
54 if (i >= lines) return s;
55
56 var pad = digits - (""+i).length;
57 var prefix = Array(pad+1).join(" ") + i + ' ';
58 prefix = colorize(prefix, color('line_number'));
59 return prefix + s;
60 });
61 }
62
63 // Add language comment
64 if (options.debug) {
65 var info = "/* hicat language: " + out.language + " */";
66 info = colorize(info, color('tag', 'debug'));
67 out.ansi += "\n\n" + info + "\n";
68 }
69
70 return {
71 ansi: out.ansi,
72 language: out.language,
73 html: out.value,
74 raw: str
75 };
76}
77
78/**
79 * colors : hicat.colors
80 * The color scheme. This is a key-value object that `hicat.color()` refers to.
81 */
82
83hicat.colors = require('./lib/colors');
84
85/**
86 * listLanguages() : hicat.listLanguages()
87 * Returns a list of supported languages.
88 *
89 * listLanguages()
90 * => ['javascript', 'python', 'c', ...]
91 */
92
93hicat.listLanguages = hljs.listLanguages;
94
95/**
96 * extname() : extname(filename)
97 * (private) Extracts the extension from a given `filename`.
98 *
99 * extname('hi.json')
100 * => 'json'
101 */
102
103function extname (fname) {
104 var m = fname.match(/\.([^\.]+)$/);
105 if (m) return m[1];
106}
107
108/**
109 * color() : hicat.color(token)
110 * Returns the color for a given token.
111 *
112 * color('string')
113 * => '32'
114 * color('attribute', 'json')
115 * => '32'
116 */
117
118var color = hicat.color = function (token, lang) {
119 if (lang)
120 return getColor(lang + ':' + token) || getColor(token);
121 else
122 return getColor(token);
123
124 function getColor (token) {
125 var code = token, newcode;
126 while (true) {
127 newcode = hicat.colors[code];
128 if (newcode) code = newcode;
129 else if (token !== code) return code;
130 else return;
131 }
132 }
133};
134
135/**
136 * html2ansi() : html2ansi(str, options)
137 * (private) Converts hljs-style spans from a given HTML `str` into ANSI
138 * color codes. Available options:
139 *
140 * ~ lang (String): language
141 * ~ debug (Boolean): true or false
142 *
143 * html2ansi('<span class="hljs-string">"hi"</span>")
144 * => "\033[31m"hi"\033[0m"
145 */
146
147function html2ansi (str, options) {
148 // do multiple passes as spans can be multiple
149 while (~str.indexOf('<span class="hljs-')) {
150 str = replaceSpan(str, options);
151 }
152
153 return str
154 .replace(/<span class="([^"]*)">/g, '')
155 .replace(/<\/span>/g, '')
156 .replace(/&lt;/g, '<')
157 .replace(/&gt;/g, '>')
158 .replace(/&amp;/g, '&');
159}
160
161/**
162 * replaceSpan() : replaceSpan(html, options)
163 * (private) Replaces span tags with ANSI codes in the given `html` string.
164 * A delegate of `html2ansi`.
165 *
166 * html = '<span class="hljs-tag">hi</span>'
167 * replaceSpan(html, 'java')
168 * => "\033[0;32mhi\033[0m"
169 */
170
171function replaceSpan (str, options) {
172 return str
173 .replace(/<span class="hljs-([^"]*)">([^<]*)<\/span>/g, function (_, token, s) {
174 var code = color(token, options && options.lang);
175 if (options.debug) {
176 return "" +
177 colorize("[" + token + "]", color('tag', 'debug')) +
178 colorize(s, code) +
179 colorize("[/" + token + "]", color('tag', 'debug'));
180 }
181 return colorize(s, code);
182 });
183}
184
185/**
186 * formatJson() : formatJson(str)
187 * Prettifies a JSON string
188 */
189
190function formatJson (str) {
191 try {
192 // return require('util').inspect(JSON.parse(str));
193 return JSON.stringify(JSON.parse(str), null, 2) + "\n";
194 } catch (e) {
195 return str;
196 }
197}
198
199/**
200 * colorize() : hicat.colorize(str, color)
201 * Applies the color `color` to the string `str`.
202 *
203 * colorize("hello", 32)
204 * => "\033[32mhello\033[0m"
205 */
206
207colorize = hicat.colorize = function (s, color) {
208 if (!color) return s;
209
210 var reset = "\033[0m",
211 code = "\033[" + color + "m";
212
213 // improved support for less -R, since it automatically resets the colors per line
214 if (~s.indexOf("\n")) s = s.replace(/\n/g, "\n"+code);
215
216 // nesting
217 if (~s.indexOf(reset)) {
218 s = s.replace(/\033\[0m/g, code);
219 }
220
221 return code + s + reset;
222};
223
224module.exports = hicat;