UNPKG

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