-
-
Notifications
You must be signed in to change notification settings - Fork 34
/
slice.js
143 lines (114 loc) · 3.25 KB
/
slice.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
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
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
/* eslint max-lines: "off" */
"use strict";
var reAnsi = require("./regex-ansi")
, stringifiable = require("es5-ext/object/validate-stringifiable-value")
, length = require("./get-stripped-length")
, sgr = require("./lib/sgr")
, max = Math.max;
var Token = function (token) { this.token = token; };
var tokenize = function (str) {
var match = reAnsi().exec(str);
if (!match) {
return [str];
}
var index = match.index, head, prehead, tail;
if (index === 0) {
head = match[0];
tail = str.slice(head.length);
return [new Token(head)].concat(tokenize(tail));
}
prehead = str.slice(0, index);
head = match[0];
tail = str.slice(index + head.length);
return [prehead, new Token(head)].concat(tokenize(tail));
};
var isChunkInSlice = function (chunk, index, begin, end) {
var endIndex = chunk.length + index;
if (begin > endIndex) return false;
if (end < index) return false;
return true;
};
// eslint-disable-next-line max-lines-per-function
var sliceSeq = function (seq, begin, end) {
var sliced = seq.reduce(
function (state, chunk) {
var index = state.index;
if (chunk instanceof Token) {
var code = sgr.extractCode(chunk.token);
if (index <= begin) {
if (code in sgr.openers) {
sgr.openStyle(state.preOpeners, code);
}
if (code in sgr.closers) {
sgr.closeStyle(state.preOpeners, code);
}
} else if (index < end) {
if (code in sgr.openers) {
sgr.openStyle(state.inOpeners, code);
state.seq.push(chunk);
} else if (code in sgr.closers) {
state.inClosers.push(code);
state.seq.push(chunk);
}
}
} else {
var nextChunk = "";
if (isChunkInSlice(chunk, index, begin, end)) {
var relBegin = Math.max(begin - index, 0)
, relEnd = Math.min(end - index, chunk.length);
nextChunk = chunk.slice(relBegin, relEnd);
}
state.seq.push(nextChunk);
state.index = index + chunk.length;
}
return state;
},
{
index: 0,
seq: [],
// preOpeners -> [ mod ]
// preOpeners must be prepended to the slice if they wasn't closed til the end of it
// preOpeners must be closed if they wasn't closed til the end of the slice
preOpeners: [],
// inOpeners -> [ mod ]
// inOpeners already in the slice and must not be prepended to the slice
// inOpeners must be closed if they wasn't closed til the end of the slice
inOpeners: [], // opener CSI inside slice
// inClosers -> [ code ]
// closer CSIs for determining which pre/in-Openers must be closed
inClosers: []
}
);
sliced.seq = [].concat(
sgr.prepend(sliced.preOpeners), sliced.seq,
sgr.complete([].concat(sliced.preOpeners, sliced.inOpeners), sliced.inClosers)
);
return sliced.seq;
};
module.exports = function (str /*, begin, end*/) {
var seq, begin = Number(arguments[1]), end = Number(arguments[2]), len;
str = stringifiable(str);
len = length(str);
if (isNaN(begin)) {
begin = 0;
}
if (isNaN(end)) {
end = len;
}
if (begin < 0) {
begin = max(len + begin, 0);
}
if (end < 0) {
end = max(len + end, 0);
}
seq = tokenize(str);
seq = sliceSeq(seq, begin, end);
return seq
.map(function (chunk) {
if (chunk instanceof Token) {
return chunk.token;
}
return chunk;
})
.join("");
};