/
index.js
44 lines (31 loc) · 1.33 KB
/
index.js
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
// match[0] = full match
// match[1] = visible text
// match[2] = relevant escape code
// match[3] = skippable escape code
const ANSI_SEQUENCE = /^(.*?)(?:(\x1b\[[^m]+m|\x1b\]8;;.*?(?:\x1b\\|\u0007))|(\x1b\[\?[0-9]+[a-zA-Z]))/;
const segmenter = new Intl.Segmenter(`en`, {granularity: `grapheme`});
function sliceAnsi(orig, at = 0, until = orig.length) {
// Because to do this we'd need to know the printable length of the string,
// which would require to do two passes (or would complexify the main one)
if (at < 0 || until < 0)
throw new RangeError(`Negative indices aren't supported by this implementation`);
const length = until - at;
let slice = ``;
let skipped = 0;
let visible = 0;
while (orig.length > 0) {
const lookup = orig.match(ANSI_SEQUENCE) || [orig, orig, undefined];
let graphemes = Array.from(segmenter.segment(lookup[1]), entry => entry.segment);
const skipping = Math.min(at - skipped, graphemes.length);
graphemes = graphemes.slice(skipping);
const displaying = Math.min(length - visible, graphemes.length);
slice += graphemes.slice(0, displaying).join(``);
skipped += skipping;
visible += displaying;
if (typeof lookup[2] !== `undefined`)
slice += lookup[2];
orig = orig.slice(lookup[0].length);
}
return {slice, visible};
}
module.exports = sliceAnsi;