@alchemy.run/sigil 0.0.0-alpha.1 → 0.0.0-alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +299 -299
- package/dist/ansi.d.ts +223 -0
- package/dist/ansi.js +2 -0
- package/dist/{devtools-QpCMm9JH.mjs → devtools-BhYGjb7h.js} +1 -1
- package/dist/index-DDVME65c.d.ts +919 -0
- package/dist/index.d.ts +1657 -0
- package/dist/index.js +4643 -0
- package/dist/sgr-CMfEpjSk.d.ts +91 -0
- package/dist/truncate-CBiyyZzw.js +2156 -0
- package/dist/yoga-5jKhYCJC.js +3465 -0
- package/dist/yoga.d.ts +2 -0
- package/dist/yoga.js +2 -0
- package/package.json +37 -17
- package/src/ansi/chalk.ts +179 -0
- package/src/ansi/cursor.ts +48 -0
- package/src/ansi/east-asian-width.ts +215 -0
- package/src/ansi/escapes.ts +128 -0
- package/src/ansi/index.ts +27 -0
- package/src/ansi/sgr.ts +237 -0
- package/src/ansi/slice.ts +43 -0
- package/src/ansi/string-width.ts +236 -0
- package/src/ansi/strip.ts +33 -0
- package/src/ansi/supports-color.ts +213 -0
- package/src/ansi/tokenize.ts +453 -0
- package/src/ansi/truncate.ts +194 -0
- package/src/ansi/widest-line.ts +12 -0
- package/src/ansi/wrap.ts +766 -0
- package/src/ansi-tokenizer.ts +510 -0
- package/src/auto-bind.ts +41 -0
- package/src/boxes.ts +100 -0
- package/src/code-excerpt.ts +39 -0
- package/src/colorize.ts +60 -0
- package/src/components/AccessibilityContext.ts +5 -0
- package/src/components/AnimationContext.ts +24 -0
- package/src/components/App.tsx +781 -0
- package/src/components/AppContext.ts +111 -0
- package/src/components/BackgroundContext.ts +8 -0
- package/src/components/Box.tsx +116 -0
- package/src/components/CursorContext.ts +19 -0
- package/src/components/ErrorBoundary.tsx +38 -0
- package/src/components/ErrorOverview.tsx +133 -0
- package/src/components/FocusContext.ts +30 -0
- package/src/components/Newline.tsx +15 -0
- package/src/components/Spacer.tsx +10 -0
- package/src/components/Static.tsx +59 -0
- package/src/components/StderrContext.ts +26 -0
- package/src/components/StdinContext.ts +49 -0
- package/src/components/StdoutContext.ts +28 -0
- package/src/components/Text.tsx +144 -0
- package/src/components/Transform.tsx +37 -0
- package/src/cursor-position.ts +103 -0
- package/src/devtools-window-polyfill.ts +73 -0
- package/src/devtools.ts +43 -0
- package/src/dom.ts +292 -0
- package/src/get-max-width.ts +11 -0
- package/src/global.d.ts +36 -0
- package/src/hooks/use-animation.ts +142 -0
- package/src/hooks/use-app.ts +8 -0
- package/src/hooks/use-box-metrics.ts +134 -0
- package/src/hooks/use-cursor.ts +33 -0
- package/src/hooks/use-focus-manager.ts +62 -0
- package/src/hooks/use-focus.ts +83 -0
- package/src/hooks/use-input.ts +267 -0
- package/src/hooks/use-is-screen-reader-enabled.ts +12 -0
- package/src/hooks/use-paste.ts +78 -0
- package/src/hooks/use-stderr.ts +8 -0
- package/src/hooks/use-stdin.ts +10 -0
- package/src/hooks/use-stdout.ts +8 -0
- package/src/hooks/use-window-size.ts +41 -0
- package/src/indent-string.ts +16 -0
- package/src/index.ts +44 -0
- package/src/ink.tsx +1506 -0
- package/src/input-parser.ts +283 -0
- package/src/instances.ts +9 -0
- package/src/is-in-ci.ts +7 -0
- package/src/kitty-keyboard.ts +57 -0
- package/src/log-update.ts +370 -0
- package/src/measure-element.ts +62 -0
- package/src/measure-text.ts +31 -0
- package/src/output.ts +308 -0
- package/src/parse-keypress.ts +516 -0
- package/src/parse-stack-line.ts +139 -0
- package/src/patch-console.ts +62 -0
- package/src/quick-lru.ts +85 -0
- package/src/reconciler.ts +451 -0
- package/src/render-background.ts +38 -0
- package/src/render-border.ts +134 -0
- package/src/render-node-to-output.ts +191 -0
- package/src/render-to-string.ts +131 -0
- package/src/render.ts +276 -0
- package/src/renderer.ts +73 -0
- package/src/sanitize-ansi.ts +33 -0
- package/src/signal-exit.ts +107 -0
- package/src/squash-text-nodes.ts +40 -0
- package/src/stream.ts +30 -0
- package/src/styles.ts +748 -0
- package/src/terminal-size.ts +57 -0
- package/src/throttle.ts +73 -0
- package/src/types.ts +15 -0
- package/src/utils.ts +40 -0
- package/src/wrap-text.ts +50 -0
- package/src/write-synchronized.ts +9 -0
- package/src/yoga/config.ts +57 -0
- package/src/yoga/core/absoluteLayout.ts +626 -0
- package/src/yoga/core/baseline.ts +66 -0
- package/src/yoga/core/cache.ts +136 -0
- package/src/yoga/core/calculateLayout.ts +2920 -0
- package/src/yoga/core/config.ts +104 -0
- package/src/yoga/core/flexLine.ts +177 -0
- package/src/yoga/core/helpers.ts +293 -0
- package/src/yoga/core/layoutResults.ts +167 -0
- package/src/yoga/core/node.ts +611 -0
- package/src/yoga/core/numeric.ts +44 -0
- package/src/yoga/core/pixelGrid.ts +151 -0
- package/src/yoga/core/style.ts +887 -0
- package/src/yoga/core/types.ts +224 -0
- package/src/yoga/generated/YGEnums.ts +263 -0
- package/src/yoga/index.ts +19 -0
- package/src/yoga/node.ts +1140 -0
- package/dist/index.d.mts +0 -2379
- package/dist/index.mjs +0 -10072
|
@@ -0,0 +1,510 @@
|
|
|
1
|
+
import { BEL, C1_CSI, C1_ST, ESC } from "./ansi/escapes.ts";
|
|
2
|
+
|
|
3
|
+
const bellCharacter = BEL;
|
|
4
|
+
const escapeCharacter = ESC;
|
|
5
|
+
const stringTerminatorCharacter = C1_ST;
|
|
6
|
+
const csiCharacter = C1_CSI;
|
|
7
|
+
const oscCharacter = "\u009D";
|
|
8
|
+
const dcsCharacter = "\u0090";
|
|
9
|
+
const pmCharacter = "\u009E";
|
|
10
|
+
const apcCharacter = "\u009F";
|
|
11
|
+
const sosCharacter = "\u0098";
|
|
12
|
+
|
|
13
|
+
type ControlStringType = "osc" | "dcs" | "pm" | "apc" | "sos";
|
|
14
|
+
|
|
15
|
+
type CsiToken = {
|
|
16
|
+
readonly type: "csi";
|
|
17
|
+
readonly value: string;
|
|
18
|
+
readonly parameterString: string;
|
|
19
|
+
readonly intermediateString: string;
|
|
20
|
+
readonly finalCharacter: string;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
type EscToken = {
|
|
24
|
+
readonly type: "esc";
|
|
25
|
+
readonly value: string;
|
|
26
|
+
readonly intermediateString: string;
|
|
27
|
+
readonly finalCharacter: string;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
type ControlStringToken = {
|
|
31
|
+
readonly type: ControlStringType;
|
|
32
|
+
readonly value: string;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
type TextToken = {
|
|
36
|
+
readonly type: "text";
|
|
37
|
+
readonly value: string;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
type StToken = {
|
|
41
|
+
readonly type: "st";
|
|
42
|
+
readonly value: string;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
type C1Token = {
|
|
46
|
+
readonly type: "c1";
|
|
47
|
+
readonly value: string;
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
type InvalidToken = {
|
|
51
|
+
readonly type: "invalid";
|
|
52
|
+
readonly value: string;
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
export type AnsiToken =
|
|
56
|
+
| TextToken
|
|
57
|
+
| CsiToken
|
|
58
|
+
| EscToken
|
|
59
|
+
| ControlStringToken
|
|
60
|
+
| StToken
|
|
61
|
+
| C1Token
|
|
62
|
+
| InvalidToken;
|
|
63
|
+
|
|
64
|
+
const isCsiParameterCharacter = (character: string): boolean => {
|
|
65
|
+
const codePoint = character.codePointAt(0);
|
|
66
|
+
|
|
67
|
+
return codePoint !== undefined && codePoint >= 0x30 && codePoint <= 0x3f;
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const isCsiIntermediateCharacter = (character: string): boolean => {
|
|
71
|
+
const codePoint = character.codePointAt(0);
|
|
72
|
+
|
|
73
|
+
return codePoint !== undefined && codePoint >= 0x20 && codePoint <= 0x2f;
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
const isCsiFinalCharacter = (character: string): boolean => {
|
|
77
|
+
const codePoint = character.codePointAt(0);
|
|
78
|
+
|
|
79
|
+
return codePoint !== undefined && codePoint >= 0x40 && codePoint <= 0x7e;
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
const isEscapeIntermediateCharacter = (character: string): boolean => {
|
|
83
|
+
const codePoint = character.codePointAt(0);
|
|
84
|
+
|
|
85
|
+
return codePoint !== undefined && codePoint >= 0x20 && codePoint <= 0x2f;
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
const isEscapeFinalCharacter = (character: string): boolean => {
|
|
89
|
+
const codePoint = character.codePointAt(0);
|
|
90
|
+
|
|
91
|
+
return codePoint !== undefined && codePoint >= 0x30 && codePoint <= 0x7e;
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
const isC1ControlCharacter = (character: string): boolean => {
|
|
95
|
+
const codePoint = character.codePointAt(0);
|
|
96
|
+
|
|
97
|
+
return codePoint !== undefined && codePoint >= 0x80 && codePoint <= 0x9f;
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
// Standards references:
|
|
101
|
+
// ECMA-48 control functions and CSI byte classes: https://ecma-international.org/publications-and-standards/standards/ecma-48/
|
|
102
|
+
// xterm CSI parameter/intermediate/final format notes: https://invisible-island.net/xterm/ecma-48-parameter-format.html
|
|
103
|
+
// xterm/OSC BEL termination behavior: https://davidrg.github.io/ckwin/dev/ctlseqs.html
|
|
104
|
+
const readCsiSequence = (
|
|
105
|
+
text: string,
|
|
106
|
+
fromIndex: number,
|
|
107
|
+
):
|
|
108
|
+
| {
|
|
109
|
+
readonly endIndex: number;
|
|
110
|
+
readonly parameterString: string;
|
|
111
|
+
readonly intermediateString: string;
|
|
112
|
+
readonly finalCharacter: string;
|
|
113
|
+
}
|
|
114
|
+
| undefined => {
|
|
115
|
+
let index = fromIndex;
|
|
116
|
+
|
|
117
|
+
while (index < text.length) {
|
|
118
|
+
const character = text[index]!;
|
|
119
|
+
|
|
120
|
+
if (!isCsiParameterCharacter(character)) {
|
|
121
|
+
break;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
index++;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const parameterString = text.slice(fromIndex, index);
|
|
128
|
+
const intermediateStartIndex = index;
|
|
129
|
+
|
|
130
|
+
while (index < text.length) {
|
|
131
|
+
const character = text[index]!;
|
|
132
|
+
|
|
133
|
+
if (!isCsiIntermediateCharacter(character)) {
|
|
134
|
+
break;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
index++;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const intermediateString = text.slice(intermediateStartIndex, index);
|
|
141
|
+
const finalCharacter = text[index];
|
|
142
|
+
|
|
143
|
+
if (finalCharacter === undefined || !isCsiFinalCharacter(finalCharacter)) {
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return {
|
|
148
|
+
endIndex: index + 1,
|
|
149
|
+
parameterString,
|
|
150
|
+
intermediateString,
|
|
151
|
+
finalCharacter,
|
|
152
|
+
};
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
const findControlStringTerminatorIndex = (
|
|
156
|
+
text: string,
|
|
157
|
+
fromIndex: number,
|
|
158
|
+
allowBellTerminator: boolean,
|
|
159
|
+
): number | undefined => {
|
|
160
|
+
for (let index = fromIndex; index < text.length; index++) {
|
|
161
|
+
const character = text[index];
|
|
162
|
+
|
|
163
|
+
if (allowBellTerminator && character === bellCharacter) {
|
|
164
|
+
return index + 1;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
if (character === stringTerminatorCharacter) {
|
|
168
|
+
return index + 1;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if (character === escapeCharacter) {
|
|
172
|
+
const followingCharacter = text[index + 1];
|
|
173
|
+
|
|
174
|
+
// Tmux escapes ESC bytes in payload as ESC ESC.
|
|
175
|
+
if (followingCharacter === escapeCharacter) {
|
|
176
|
+
index++;
|
|
177
|
+
continue;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
if (followingCharacter === "\\") {
|
|
181
|
+
return index + 2;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
return;
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
const readEscapeSequence = (
|
|
190
|
+
text: string,
|
|
191
|
+
fromIndex: number,
|
|
192
|
+
):
|
|
193
|
+
| {
|
|
194
|
+
readonly endIndex: number;
|
|
195
|
+
readonly intermediateString: string;
|
|
196
|
+
readonly finalCharacter: string;
|
|
197
|
+
}
|
|
198
|
+
| undefined => {
|
|
199
|
+
let index = fromIndex;
|
|
200
|
+
|
|
201
|
+
while (index < text.length) {
|
|
202
|
+
const character = text[index]!;
|
|
203
|
+
|
|
204
|
+
if (!isEscapeIntermediateCharacter(character)) {
|
|
205
|
+
break;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
index++;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
const intermediateString = text.slice(fromIndex, index);
|
|
212
|
+
const finalCharacter = text[index];
|
|
213
|
+
|
|
214
|
+
if (finalCharacter === undefined || !isEscapeFinalCharacter(finalCharacter)) {
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
return {
|
|
219
|
+
endIndex: index + 1,
|
|
220
|
+
intermediateString,
|
|
221
|
+
finalCharacter,
|
|
222
|
+
};
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
// Centralize control-string rules so ESC and C1 paths do not diverge.
|
|
226
|
+
const getControlStringFromEscapeIntroducer = (
|
|
227
|
+
character: string,
|
|
228
|
+
):
|
|
229
|
+
| {
|
|
230
|
+
readonly type: ControlStringType;
|
|
231
|
+
readonly allowBellTerminator: boolean;
|
|
232
|
+
}
|
|
233
|
+
| undefined => {
|
|
234
|
+
switch (character) {
|
|
235
|
+
case "]": {
|
|
236
|
+
return { type: "osc", allowBellTerminator: true };
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
case "P": {
|
|
240
|
+
return { type: "dcs", allowBellTerminator: false };
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
case "^": {
|
|
244
|
+
return { type: "pm", allowBellTerminator: false };
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
case "_": {
|
|
248
|
+
return { type: "apc", allowBellTerminator: false };
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
case "X": {
|
|
252
|
+
return { type: "sos", allowBellTerminator: false };
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
default: {
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
const getControlStringFromC1Introducer = (
|
|
262
|
+
character: string,
|
|
263
|
+
):
|
|
264
|
+
| {
|
|
265
|
+
readonly type: ControlStringType;
|
|
266
|
+
readonly allowBellTerminator: boolean;
|
|
267
|
+
}
|
|
268
|
+
| undefined => {
|
|
269
|
+
switch (character) {
|
|
270
|
+
case oscCharacter: {
|
|
271
|
+
return { type: "osc", allowBellTerminator: true };
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
case dcsCharacter: {
|
|
275
|
+
return { type: "dcs", allowBellTerminator: false };
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
case pmCharacter: {
|
|
279
|
+
return { type: "pm", allowBellTerminator: false };
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
case apcCharacter: {
|
|
283
|
+
return { type: "apc", allowBellTerminator: false };
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
case sosCharacter: {
|
|
287
|
+
return { type: "sos", allowBellTerminator: false };
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
default: {
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
};
|
|
295
|
+
|
|
296
|
+
export const hasAnsiControlCharacters = (text: string): boolean => {
|
|
297
|
+
if (text.includes(escapeCharacter)) {
|
|
298
|
+
return true;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
for (const character of text) {
|
|
302
|
+
if (isC1ControlCharacter(character)) {
|
|
303
|
+
return true;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
return false;
|
|
308
|
+
};
|
|
309
|
+
|
|
310
|
+
const malformedFromIndex = (
|
|
311
|
+
tokens: AnsiToken[],
|
|
312
|
+
text: string,
|
|
313
|
+
textStartIndex: number,
|
|
314
|
+
fromIndex: number,
|
|
315
|
+
): AnsiToken[] => {
|
|
316
|
+
if (fromIndex > textStartIndex) {
|
|
317
|
+
tokens.push({ type: "text", value: text.slice(textStartIndex, fromIndex) });
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// Treat the remainder as invalid so callers can drop it as one unsafe unit.
|
|
321
|
+
tokens.push({ type: "invalid", value: text.slice(fromIndex) });
|
|
322
|
+
|
|
323
|
+
return tokens;
|
|
324
|
+
};
|
|
325
|
+
|
|
326
|
+
export const tokenizeAnsi = (text: string): AnsiToken[] => {
|
|
327
|
+
if (!hasAnsiControlCharacters(text)) {
|
|
328
|
+
return [{ type: "text", value: text }];
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
const tokens: AnsiToken[] = [];
|
|
332
|
+
let textStartIndex = 0;
|
|
333
|
+
|
|
334
|
+
for (let index = 0; index < text.length;) {
|
|
335
|
+
const character = text[index];
|
|
336
|
+
|
|
337
|
+
if (character === undefined) {
|
|
338
|
+
break;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
if (character === escapeCharacter) {
|
|
342
|
+
const followingCharacter = text[index + 1];
|
|
343
|
+
|
|
344
|
+
if (followingCharacter === undefined) {
|
|
345
|
+
return malformedFromIndex(tokens, text, textStartIndex, index);
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
if (followingCharacter === "[") {
|
|
349
|
+
const csiSequence = readCsiSequence(text, index + 2);
|
|
350
|
+
|
|
351
|
+
if (csiSequence === undefined) {
|
|
352
|
+
return malformedFromIndex(tokens, text, textStartIndex, index);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
if (index > textStartIndex) {
|
|
356
|
+
tokens.push({ type: "text", value: text.slice(textStartIndex, index) });
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
tokens.push({
|
|
360
|
+
type: "csi",
|
|
361
|
+
value: text.slice(index, csiSequence.endIndex),
|
|
362
|
+
parameterString: csiSequence.parameterString,
|
|
363
|
+
intermediateString: csiSequence.intermediateString,
|
|
364
|
+
finalCharacter: csiSequence.finalCharacter,
|
|
365
|
+
});
|
|
366
|
+
index = csiSequence.endIndex;
|
|
367
|
+
textStartIndex = index;
|
|
368
|
+
continue;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
const escapeControlString = getControlStringFromEscapeIntroducer(followingCharacter);
|
|
372
|
+
|
|
373
|
+
if (escapeControlString !== undefined) {
|
|
374
|
+
const controlStringTerminatorIndex = findControlStringTerminatorIndex(
|
|
375
|
+
text,
|
|
376
|
+
index + 2,
|
|
377
|
+
escapeControlString.allowBellTerminator,
|
|
378
|
+
);
|
|
379
|
+
|
|
380
|
+
if (controlStringTerminatorIndex === undefined) {
|
|
381
|
+
return malformedFromIndex(tokens, text, textStartIndex, index);
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
if (index > textStartIndex) {
|
|
385
|
+
tokens.push({ type: "text", value: text.slice(textStartIndex, index) });
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
tokens.push({
|
|
389
|
+
type: escapeControlString.type,
|
|
390
|
+
value: text.slice(index, controlStringTerminatorIndex),
|
|
391
|
+
});
|
|
392
|
+
index = controlStringTerminatorIndex;
|
|
393
|
+
textStartIndex = index;
|
|
394
|
+
continue;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
const escapeSequence = readEscapeSequence(text, index + 1);
|
|
398
|
+
|
|
399
|
+
if (escapeSequence === undefined) {
|
|
400
|
+
// Incomplete escape sequences with intermediates are malformed control strings.
|
|
401
|
+
if (isEscapeIntermediateCharacter(followingCharacter)) {
|
|
402
|
+
return malformedFromIndex(tokens, text, textStartIndex, index);
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
if (index > textStartIndex) {
|
|
406
|
+
tokens.push({ type: "text", value: text.slice(textStartIndex, index) });
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
// Ignore lone ESC and continue tokenizing the rest.
|
|
410
|
+
index++;
|
|
411
|
+
textStartIndex = index;
|
|
412
|
+
continue;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
if (index > textStartIndex) {
|
|
416
|
+
tokens.push({ type: "text", value: text.slice(textStartIndex, index) });
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
tokens.push({
|
|
420
|
+
type: "esc",
|
|
421
|
+
value: text.slice(index, escapeSequence.endIndex),
|
|
422
|
+
intermediateString: escapeSequence.intermediateString,
|
|
423
|
+
finalCharacter: escapeSequence.finalCharacter,
|
|
424
|
+
});
|
|
425
|
+
index = escapeSequence.endIndex;
|
|
426
|
+
textStartIndex = index;
|
|
427
|
+
continue;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
if (character === csiCharacter) {
|
|
431
|
+
const csiSequence = readCsiSequence(text, index + 1);
|
|
432
|
+
|
|
433
|
+
if (csiSequence === undefined) {
|
|
434
|
+
return malformedFromIndex(tokens, text, textStartIndex, index);
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
if (index > textStartIndex) {
|
|
438
|
+
tokens.push({ type: "text", value: text.slice(textStartIndex, index) });
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
tokens.push({
|
|
442
|
+
type: "csi",
|
|
443
|
+
value: text.slice(index, csiSequence.endIndex),
|
|
444
|
+
parameterString: csiSequence.parameterString,
|
|
445
|
+
intermediateString: csiSequence.intermediateString,
|
|
446
|
+
finalCharacter: csiSequence.finalCharacter,
|
|
447
|
+
});
|
|
448
|
+
index = csiSequence.endIndex;
|
|
449
|
+
textStartIndex = index;
|
|
450
|
+
continue;
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
const c1ControlString = getControlStringFromC1Introducer(character);
|
|
454
|
+
|
|
455
|
+
if (c1ControlString !== undefined) {
|
|
456
|
+
const controlStringTerminatorIndex = findControlStringTerminatorIndex(
|
|
457
|
+
text,
|
|
458
|
+
index + 1,
|
|
459
|
+
c1ControlString.allowBellTerminator,
|
|
460
|
+
);
|
|
461
|
+
|
|
462
|
+
if (controlStringTerminatorIndex === undefined) {
|
|
463
|
+
return malformedFromIndex(tokens, text, textStartIndex, index);
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
if (index > textStartIndex) {
|
|
467
|
+
tokens.push({ type: "text", value: text.slice(textStartIndex, index) });
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
tokens.push({
|
|
471
|
+
type: c1ControlString.type,
|
|
472
|
+
value: text.slice(index, controlStringTerminatorIndex),
|
|
473
|
+
});
|
|
474
|
+
index = controlStringTerminatorIndex;
|
|
475
|
+
textStartIndex = index;
|
|
476
|
+
continue;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
if (character === stringTerminatorCharacter) {
|
|
480
|
+
if (index > textStartIndex) {
|
|
481
|
+
tokens.push({ type: "text", value: text.slice(textStartIndex, index) });
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
tokens.push({ type: "st", value: character });
|
|
485
|
+
index++;
|
|
486
|
+
textStartIndex = index;
|
|
487
|
+
continue;
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
// Strip remaining C1 controls as standalone functions.
|
|
491
|
+
if (isC1ControlCharacter(character)) {
|
|
492
|
+
if (index > textStartIndex) {
|
|
493
|
+
tokens.push({ type: "text", value: text.slice(textStartIndex, index) });
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
tokens.push({ type: "c1", value: character });
|
|
497
|
+
index++;
|
|
498
|
+
textStartIndex = index;
|
|
499
|
+
continue;
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
index++;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
if (textStartIndex < text.length) {
|
|
506
|
+
tokens.push({ type: "text", value: text.slice(textStartIndex) });
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
return tokens;
|
|
510
|
+
};
|
package/src/auto-bind.ts
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
// Derived from `auto-bind` (MIT, Sindre Sorhus), reduced to the surface Ink uses.
|
|
2
|
+
// Reflective prototype-walking code: the broad `object` type is the right one here.
|
|
3
|
+
/* eslint-disable @typescript-eslint/no-restricted-types */
|
|
4
|
+
|
|
5
|
+
// Gets all non-builtin properties up the prototype chain.
|
|
6
|
+
const getAllProperties = (object: object): Array<[object, string | symbol]> => {
|
|
7
|
+
const properties: Array<[object, string | symbol]> = [];
|
|
8
|
+
let current: object | null = object;
|
|
9
|
+
|
|
10
|
+
do {
|
|
11
|
+
for (const key of Reflect.ownKeys(current)) {
|
|
12
|
+
properties.push([current, key]);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
current = Reflect.getPrototypeOf(current);
|
|
16
|
+
} while (current && current !== Object.prototype);
|
|
17
|
+
|
|
18
|
+
return properties;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export const autoBind = <T extends object>(self: T): T => {
|
|
22
|
+
for (const [object, key] of getAllProperties(
|
|
23
|
+
(self.constructor as { prototype: object }).prototype,
|
|
24
|
+
)) {
|
|
25
|
+
if (key === "constructor") {
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const descriptor = Reflect.getOwnPropertyDescriptor(object, key);
|
|
30
|
+
|
|
31
|
+
if (descriptor && typeof descriptor.value === "function") {
|
|
32
|
+
const value = (self as Record<string | symbol, unknown>)[key];
|
|
33
|
+
|
|
34
|
+
if (typeof value === "function") {
|
|
35
|
+
(self as Record<string | symbol, unknown>)[key] = value.bind(self);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return self;
|
|
41
|
+
};
|
package/src/boxes.ts
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
// Derived from `cli-boxes` (MIT, Sindre Sorhus).
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
Style of the box border.
|
|
5
|
+
*/
|
|
6
|
+
export type BoxStyle = {
|
|
7
|
+
readonly topLeft: string;
|
|
8
|
+
readonly top: string;
|
|
9
|
+
readonly topRight: string;
|
|
10
|
+
readonly right: string;
|
|
11
|
+
readonly bottomRight: string;
|
|
12
|
+
readonly bottom: string;
|
|
13
|
+
readonly bottomLeft: string;
|
|
14
|
+
readonly left: string;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export const boxes = {
|
|
18
|
+
single: {
|
|
19
|
+
topLeft: "┌",
|
|
20
|
+
top: "─",
|
|
21
|
+
topRight: "┐",
|
|
22
|
+
right: "│",
|
|
23
|
+
bottomRight: "┘",
|
|
24
|
+
bottom: "─",
|
|
25
|
+
bottomLeft: "└",
|
|
26
|
+
left: "│",
|
|
27
|
+
},
|
|
28
|
+
double: {
|
|
29
|
+
topLeft: "╔",
|
|
30
|
+
top: "═",
|
|
31
|
+
topRight: "╗",
|
|
32
|
+
right: "║",
|
|
33
|
+
bottomRight: "╝",
|
|
34
|
+
bottom: "═",
|
|
35
|
+
bottomLeft: "╚",
|
|
36
|
+
left: "║",
|
|
37
|
+
},
|
|
38
|
+
round: {
|
|
39
|
+
topLeft: "╭",
|
|
40
|
+
top: "─",
|
|
41
|
+
topRight: "╮",
|
|
42
|
+
right: "│",
|
|
43
|
+
bottomRight: "╯",
|
|
44
|
+
bottom: "─",
|
|
45
|
+
bottomLeft: "╰",
|
|
46
|
+
left: "│",
|
|
47
|
+
},
|
|
48
|
+
bold: {
|
|
49
|
+
topLeft: "┏",
|
|
50
|
+
top: "━",
|
|
51
|
+
topRight: "┓",
|
|
52
|
+
right: "┃",
|
|
53
|
+
bottomRight: "┛",
|
|
54
|
+
bottom: "━",
|
|
55
|
+
bottomLeft: "┗",
|
|
56
|
+
left: "┃",
|
|
57
|
+
},
|
|
58
|
+
singleDouble: {
|
|
59
|
+
topLeft: "╓",
|
|
60
|
+
top: "─",
|
|
61
|
+
topRight: "╖",
|
|
62
|
+
right: "║",
|
|
63
|
+
bottomRight: "╜",
|
|
64
|
+
bottom: "─",
|
|
65
|
+
bottomLeft: "╙",
|
|
66
|
+
left: "║",
|
|
67
|
+
},
|
|
68
|
+
doubleSingle: {
|
|
69
|
+
topLeft: "╒",
|
|
70
|
+
top: "═",
|
|
71
|
+
topRight: "╕",
|
|
72
|
+
right: "│",
|
|
73
|
+
bottomRight: "╛",
|
|
74
|
+
bottom: "═",
|
|
75
|
+
bottomLeft: "╘",
|
|
76
|
+
left: "│",
|
|
77
|
+
},
|
|
78
|
+
classic: {
|
|
79
|
+
topLeft: "+",
|
|
80
|
+
top: "-",
|
|
81
|
+
topRight: "+",
|
|
82
|
+
right: "|",
|
|
83
|
+
bottomRight: "+",
|
|
84
|
+
bottom: "-",
|
|
85
|
+
bottomLeft: "+",
|
|
86
|
+
left: "|",
|
|
87
|
+
},
|
|
88
|
+
arrow: {
|
|
89
|
+
topLeft: "↘",
|
|
90
|
+
top: "↓",
|
|
91
|
+
topRight: "↙",
|
|
92
|
+
right: "←",
|
|
93
|
+
bottomRight: "↖",
|
|
94
|
+
bottom: "↑",
|
|
95
|
+
bottomLeft: "↗",
|
|
96
|
+
left: "→",
|
|
97
|
+
},
|
|
98
|
+
} as const satisfies Record<string, BoxStyle>;
|
|
99
|
+
|
|
100
|
+
export type Boxes = typeof boxes;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// Derived from `code-excerpt` + `convert-to-spaces` (MIT, Vadim Demedes).
|
|
2
|
+
|
|
3
|
+
export type CodeExcerpt = {
|
|
4
|
+
line: number;
|
|
5
|
+
value: string;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
const tabsToSpaces = (input: string, spaces = 2): string =>
|
|
9
|
+
input.replaceAll(/^\t+/gm, (tabs) => " ".repeat(tabs.length * spaces));
|
|
10
|
+
|
|
11
|
+
const generateLineNumbers = (line: number, around: number): number[] => {
|
|
12
|
+
const lineNumbers: number[] = [];
|
|
13
|
+
|
|
14
|
+
for (let lineNumber = line - around; lineNumber <= line + around; lineNumber++) {
|
|
15
|
+
lineNumbers.push(lineNumber);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
return lineNumbers;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export const codeExcerpt = (
|
|
22
|
+
source: string,
|
|
23
|
+
line: number,
|
|
24
|
+
options: { around?: number } = {},
|
|
25
|
+
): CodeExcerpt[] | undefined => {
|
|
26
|
+
if (!line || line < 1) {
|
|
27
|
+
throw new TypeError("Line number must start from `1`.");
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const lines = tabsToSpaces(source).split(/\r?\n/);
|
|
31
|
+
|
|
32
|
+
if (line > lines.length) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return generateLineNumbers(line, options.around ?? 3)
|
|
37
|
+
.filter((lineNumber) => lines[lineNumber - 1] !== undefined)
|
|
38
|
+
.map((lineNumber) => ({ line: lineNumber, value: lines[lineNumber - 1]! }));
|
|
39
|
+
};
|