1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121 | 1
1
1
1
1
1
1
1
1
1
133
116
116
116
99
99
99
1
1
1
68
31
31
31
31
14
1
65
51
51
51
51
31
14
14
1
99
1
17
17
17
1
1
| var blessed = require('blessed');
var _ = require('lazy.js');
var hljs = require('highlight.js'); hljs.configure({classPrefix: ''});
var cheerio = require('cheerio');
var entities = require('entities');
var Lexer = require('lex');
var util = require('./util');
var textUtil = require('./textUtil');
var logger = require('./logger');
function markup (text, style, start, end) {
if (!text) return text;
start = typeof start !== 'undefined' ? markup.index(text, start) : 0;
end = typeof end !== 'undefined' ? markup.index(text, end) : Infinity;
if (start === end) return text;
style = style || '';
var middle = style + text.slice(start, end) + markup.closeTags(style);
return text.slice(0, start) + middle + text.slice(end);
}
markup._tagRegExp = /\{(\/?)([\w\-,;!#]*)\}/g;
markup._addTag = function (openTags, tag, close, name) {
if (!close) openTags.push(tag);
else if (!name) openTags.splice(0, Infinity);
else {
var lastTagIndex = openTags.lastIndexOf('{'+name+'}');
if (lastTagIndex !== -1) openTags.splice(lastTagIndex, 1);
}
};
markup.index = function (markedUp, index) {
if (index <= 0) return 0;
var textLength = 0;
var i = 0;
var done = false;
var appendTags = [];
function retVal () { return i - (textLength - index) - appendTags.join('').length; }
function addText (text, real) {
if (done) return retVal();
i += (real || text).length;
textLength += text.length;
if (textLength == index) done = true;
Iif (textLength > index) return retVal();
}
return new Lexer(addText)
.addRule(/\{open\}/, addText.bind(null, '{'))
.addRule(/\{close\}/, addText.bind(null, '}'))
.addRule(markup._tagRegExp, function (tag, close, name) {
i += tag.length;
Iif (done) {
markup._addTag.apply(null, [appendTags].concat(util.toArray(arguments)));
}
})
.setInput(markedUp)
.lex() || markedUp.length;
};
markup.closeTags = function (markedUp) {
return (markedUp
.replace(markup._tagRegExp, '{/$2}', 'g') // 'g' flag ignored :(
.match(markup._tagRegExp) || [])
.reverse()
.join('');
};
markup.getOpenTags = function (text) {
var openTags = [];
new Lexer(function () {})
.addRule(/\{(open|close)\}/, function () {})
.addRule(markup._tagRegExp, markup._addTag.bind(null, openTags))
.setInput(text)
.lex();
return openTags;
};
markup.highlight = function (text, language) {
if (language === false) return [];
var highlighted;
if (language) {
try { highlighted = hljs.highlight(language, text, true); } catch (e) {}
}
if (!highlighted) highlighted = hljs.highlightAuto(text);
var $ = cheerio.load(highlighted.value); // Assumes hljs output doesn't use curlies
var ranges = [];
do {
var lastElCount = elCount;
var elCount = $('*:not(:has(*))').replaceWith(function () {
var $el = $(this);
var text = '';
[this].concat($el.parents().get(), [$.root()]).reverse().reduce(function (parent, el) {
$(parent).contents().each(function () {
var $sibling = $(this);
if ($sibling.is(el)) return false;
text += $sibling.text();
});
return el;
});
var lines = textUtil.splitLines(text);
var linesPlusEl = textUtil.splitLines(text + $el.text());
ranges.push({
range: [
[lines .length - 1, _(lines) .last().length],
[linesPlusEl.length - 1, _(linesPlusEl).last().length]
],
properties: {
type: 'syntax',
syntax: ($el.attr('class') || '').match(/\S+/g) || []
}
});
return $el.text();
}).length;
} while (lastElCount !== elCount);
return ranges;
};
module.exports = markup;
|