@softwarefactory-project/re-ansi 0.6.0 → 0.7.0
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 +6 -2
- package/package.json +16 -16
- package/{bsconfig.json → rescript.json} +5 -7
- package/src/{Ansi.re → Ansi.res} +57 -54
- package/src/Ansi.res.js +919 -0
- package/src/Ansi.bs.js +0 -969
- package/src/old.js +0 -0
package/README.md
CHANGED
|
@@ -71,8 +71,8 @@ Get started by running:
|
|
|
71
71
|
```sh
|
|
72
72
|
git clone https://github.com/softwarefactory-project/re-ansi
|
|
73
73
|
cd re-ansi
|
|
74
|
-
|
|
75
|
-
|
|
74
|
+
npm install
|
|
75
|
+
npm run test
|
|
76
76
|
```
|
|
77
77
|
|
|
78
78
|
Then build and run tests with `yarn test`.
|
|
@@ -81,6 +81,10 @@ Make sure to read about [React][reason-react] and [Reason][rescript-lang] too.
|
|
|
81
81
|
|
|
82
82
|
## Changes
|
|
83
83
|
|
|
84
|
+
### 0.7.0
|
|
85
|
+
|
|
86
|
+
- Update to rescript version 11.
|
|
87
|
+
|
|
84
88
|
### 0.6.0
|
|
85
89
|
|
|
86
90
|
- Fix support for bright colors.
|
package/package.json
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@softwarefactory-project/re-ansi",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "ANSI code to HTML",
|
|
5
|
-
"files": [
|
|
6
|
-
|
|
5
|
+
"files": [
|
|
6
|
+
"README.md",
|
|
7
|
+
"LICENSE",
|
|
8
|
+
"rescript.json",
|
|
9
|
+
"src"
|
|
10
|
+
],
|
|
11
|
+
"main": "./src/Ansi.res.js",
|
|
7
12
|
"license": "Apache-2.0",
|
|
8
13
|
"homepage": "https://github.com/softwarefactory-project/re-ansi",
|
|
9
14
|
"repository": {
|
|
@@ -11,24 +16,19 @@
|
|
|
11
16
|
"url": "git+https://softwarefactory-project.io/r/software-factory/re-ansi.git"
|
|
12
17
|
},
|
|
13
18
|
"scripts": {
|
|
14
|
-
"build": "
|
|
15
|
-
"
|
|
16
|
-
"
|
|
19
|
+
"build": "rescript",
|
|
20
|
+
"clean": "rescript clean",
|
|
21
|
+
"dev": "rescript -w",
|
|
22
|
+
"test": "npm run build && node tests/Spec.res.js"
|
|
17
23
|
},
|
|
18
24
|
"keywords": [
|
|
19
|
-
"ReasonReact",
|
|
20
25
|
"ansi",
|
|
21
|
-
"reason",
|
|
22
|
-
"reason-react",
|
|
23
|
-
"reasonml",
|
|
24
26
|
"rescript",
|
|
25
|
-
"
|
|
27
|
+
"react"
|
|
26
28
|
],
|
|
27
29
|
"dependencies": {
|
|
28
|
-
"
|
|
29
|
-
"
|
|
30
|
-
|
|
31
|
-
"devDependencies": {
|
|
32
|
-
"react": "^16.14.0"
|
|
30
|
+
"@rescript/core": "^1.3.0",
|
|
31
|
+
"@rescript/react": "^0.12.1",
|
|
32
|
+
"rescript": "^11.1.0"
|
|
33
33
|
}
|
|
34
34
|
}
|
|
@@ -1,18 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@softwarefactory-project/re-ansi",
|
|
3
|
-
"
|
|
4
|
-
"
|
|
3
|
+
"jsx": {
|
|
4
|
+
"version": 4
|
|
5
5
|
},
|
|
6
6
|
"sources": ["src", { "dir": "tests", "type": "dev" }],
|
|
7
|
-
"bsc-flags": ["-
|
|
7
|
+
"bsc-flags": ["-open RescriptCore"],
|
|
8
8
|
"package-specs": [
|
|
9
9
|
{
|
|
10
10
|
"module": "commonjs",
|
|
11
11
|
"in-source": true
|
|
12
12
|
}
|
|
13
13
|
],
|
|
14
|
-
"suffix": ".
|
|
15
|
-
"bs-dependencies": ["
|
|
16
|
-
"bs-dev-dependencies": [],
|
|
17
|
-
"refmt": 3
|
|
14
|
+
"suffix": ".res.js",
|
|
15
|
+
"bs-dependencies": ["@rescript/react", "@rescript/core"]
|
|
18
16
|
}
|
package/src/{Ansi.re → Ansi.res}
RENAMED
|
@@ -15,16 +15,14 @@
|
|
|
15
15
|
// Ansi renders a log ANSI code to a React component
|
|
16
16
|
|
|
17
17
|
// Document type
|
|
18
|
-
type document = list
|
|
18
|
+
type rec document = list<atom>
|
|
19
19
|
and atom =
|
|
20
20
|
| Text(string)
|
|
21
21
|
| Link(string)
|
|
22
22
|
| LineBreak
|
|
23
23
|
| DocStyle(ReactDOM.Style.t, document);
|
|
24
24
|
|
|
25
|
-
type parser
|
|
26
|
-
|
|
27
|
-
open Belt;
|
|
25
|
+
type parser<'a> = (int, option<'a>);
|
|
28
26
|
|
|
29
27
|
module AnsiCode = {
|
|
30
28
|
type code =
|
|
@@ -35,7 +33,7 @@ module AnsiCode = {
|
|
|
35
33
|
| Style(ReactDOM.Style.t);
|
|
36
34
|
|
|
37
35
|
// Convert a 4 bits color code to its css color: https://en.wikipedia.org/wiki/ANSI_escape_code#3-bit_and_4-bit
|
|
38
|
-
let fourBitColors = (code: int): option
|
|
36
|
+
let fourBitColors = (code: int): option<string> =>
|
|
39
37
|
switch (code) {
|
|
40
38
|
| 00 => "black"->Some
|
|
41
39
|
| 01 => "red"->Some
|
|
@@ -59,7 +57,7 @@ module AnsiCode = {
|
|
|
59
57
|
None;
|
|
60
58
|
};
|
|
61
59
|
|
|
62
|
-
let threeBitColors = (code: int): option
|
|
60
|
+
let threeBitColors = (code: int): option<string> =>
|
|
63
61
|
switch (code) {
|
|
64
62
|
| 00 => "grey"->Some
|
|
65
63
|
| 01 => "red"->Some
|
|
@@ -87,7 +85,7 @@ module AnsiCode = {
|
|
|
87
85
|
| Foreground(int)
|
|
88
86
|
| BrightForeground(int)
|
|
89
87
|
| Background(int);
|
|
90
|
-
let getColorStyle = (colorMode: int, colorValue: int): option
|
|
88
|
+
let getColorStyle = (colorMode: int, colorValue: int): option<t> =>
|
|
91
89
|
switch (colorMode) {
|
|
92
90
|
| 3 => colorValue->Foreground->Some
|
|
93
91
|
| 9 => colorValue->BrightForeground->Some
|
|
@@ -98,7 +96,7 @@ module AnsiCode = {
|
|
|
98
96
|
None;
|
|
99
97
|
};
|
|
100
98
|
|
|
101
|
-
let getColorStyleCss = (color: t): option
|
|
99
|
+
let getColorStyleCss = (color: t): option<ReactDOM.Style.t> =>
|
|
102
100
|
switch (color) {
|
|
103
101
|
| Foreground(v) =>
|
|
104
102
|
v
|
|
@@ -116,7 +114,7 @@ module AnsiCode = {
|
|
|
116
114
|
)
|
|
117
115
|
};
|
|
118
116
|
|
|
119
|
-
let get = (colorMode: int, colorValue: int): option
|
|
117
|
+
let get = (colorMode: int, colorValue: int): option<ReactDOM.Style.t> =>
|
|
120
118
|
colorMode
|
|
121
119
|
->int_of_cp
|
|
122
120
|
->getColorStyle(colorValue->int_of_cp)
|
|
@@ -129,7 +127,7 @@ module AnsiCode = {
|
|
|
129
127
|
| Regular
|
|
130
128
|
| FontStyle(ReactDOM.Style.t);
|
|
131
129
|
|
|
132
|
-
let getFontStyle = (fontMode: int): option
|
|
130
|
+
let getFontStyle = (fontMode: int): option<t> =>
|
|
133
131
|
switch (fontMode) {
|
|
134
132
|
| 0 => Regular->Some
|
|
135
133
|
| 1 => "bold"->addWeight->FontStyle->Some
|
|
@@ -143,61 +141,66 @@ module AnsiCode = {
|
|
|
143
141
|
| _ => None
|
|
144
142
|
};
|
|
145
143
|
|
|
146
|
-
let getFontStyleCss = (font: t): option
|
|
144
|
+
let getFontStyleCss = (font: t): option<ReactDOM.Style.t> =>
|
|
147
145
|
switch (font) {
|
|
148
146
|
| Regular => None
|
|
149
147
|
| FontStyle(css) => css->Some
|
|
150
148
|
};
|
|
151
149
|
|
|
152
|
-
let get = (fontMode: int): option
|
|
150
|
+
let get = (fontMode: int): option<ReactDOM.Style.t> =>
|
|
153
151
|
fontMode->int_of_cp->getFontStyle->Option.flatMap(getFontStyleCss);
|
|
154
152
|
};
|
|
155
153
|
|
|
156
154
|
// Link management
|
|
157
155
|
module HttpLink = {
|
|
158
|
-
let linkRe =
|
|
156
|
+
let linkRe = RegExp.fromString("^(http(s)?:\\/\\/[^\\s]+)");
|
|
159
157
|
|
|
160
|
-
let get = (txt: string): parser
|
|
158
|
+
let get = (txt: string): parser<code> =>
|
|
161
159
|
linkRe
|
|
162
|
-
->
|
|
163
|
-
->Option.flatMap(res =>
|
|
164
|
-
(
|
|
165
|
-
switch (res->Js.Re.captures) {
|
|
166
|
-
| [|url, _, _|] => url->Js.Nullable.toOption
|
|
167
|
-
| _ => None
|
|
168
|
-
}
|
|
169
|
-
)
|
|
160
|
+
->RegExp.exec(txt)
|
|
161
|
+
->Option.flatMap(res => res->Array.get(0)
|
|
170
162
|
->Option.flatMap(url =>
|
|
171
163
|
(url->Js.String.length, url->HRef->Some)->Some
|
|
172
164
|
)
|
|
173
165
|
)
|
|
174
|
-
->Option.
|
|
166
|
+
->Option.getOr((1, None));
|
|
175
167
|
};
|
|
176
168
|
|
|
177
169
|
// Parse an ANSI code, returning the length of the sequence
|
|
178
|
-
let parse = (txt: string, pos: int): parser
|
|
170
|
+
let parse = (txt: string, pos: int): parser<code> =>
|
|
179
171
|
switch (Js.String.codePointAt(pos, txt)) {
|
|
180
172
|
| Some(0x68) => HttpLink.get(txt->Js.String.slice(~from=pos, ~to_=512))
|
|
181
173
|
| Some(0x0a) => (1, CarriageReturn->Some)
|
|
182
174
|
| Some(0x0d)
|
|
183
175
|
| Some(0x1b) =>
|
|
184
176
|
// escape sequence begin
|
|
185
|
-
let
|
|
177
|
+
let codePoints = [];
|
|
178
|
+
|
|
179
|
+
let rec readCodePoints = (idx: int) =>
|
|
186
180
|
switch (idx > 10, Js.String.codePointAt(pos + idx, txt)) {
|
|
187
|
-
| (false, Some(109)) =>
|
|
188
|
-
| (false, Some(n)) =>
|
|
189
|
-
|
|
181
|
+
| (false, Some(109)) => ()
|
|
182
|
+
| (false, Some(n)) => {
|
|
183
|
+
codePoints->Array.push(n);
|
|
184
|
+
readCodePoints(idx + 1)
|
|
185
|
+
}
|
|
186
|
+
| _ => ()
|
|
190
187
|
};
|
|
188
|
+
readCodePoints(1);
|
|
189
|
+
|
|
190
|
+
// use get for pattern match value, it's fine to use 'getUnsafe' because we'll get undefined value.
|
|
191
|
+
let get = (idx) => codePoints->Array.getUnsafe(idx)
|
|
191
192
|
|
|
192
|
-
|
|
193
|
-
let length = codePoints->
|
|
193
|
+
// Add 2 to take the 0x1b and 109 into account
|
|
194
|
+
let length = codePoints->Array.length + 2;
|
|
194
195
|
switch (codePoints) {
|
|
195
196
|
// \n\x0d[1A\x0d[J
|
|
196
|
-
|
|
|
197
|
+
| _ when
|
|
198
|
+
get(0) == 10 && get(1) == 27 && get(2) == 91 && get(3) == 49 &&
|
|
199
|
+
get(4) == 65 && get(5) == 27 && get(6) == 91 && get(7) == 74 => (9, EraseLine->Some)
|
|
197
200
|
// [_K
|
|
198
|
-
|
|
|
201
|
+
| _ when get(0) == 91 && get(2) == 75 => (4, EraseLine->Some)
|
|
199
202
|
// [K
|
|
200
|
-
|
|
|
203
|
+
| _ when get(0) == 91 && get(1) == 75 => (3, EraseLine->Some)
|
|
201
204
|
// [00m
|
|
202
205
|
| [91, 48, 48]
|
|
203
206
|
// [0m
|
|
@@ -216,7 +219,7 @@ module AnsiCode = {
|
|
|
216
219
|
length,
|
|
217
220
|
ColorCss.get(
|
|
218
221
|
colorMode,
|
|
219
|
-
colorValue + (xs->
|
|
222
|
+
colorValue + (xs->Array.length == 4 ? 10 : 0),
|
|
220
223
|
)
|
|
221
224
|
->Option.flatMap(colorCss => colorCss->Style->Some),
|
|
222
225
|
)
|
|
@@ -246,7 +249,7 @@ module AnsiCode = {
|
|
|
246
249
|
length,
|
|
247
250
|
ColorCss.get(cm1, cv1)
|
|
248
251
|
->Option.flatMap(colorCss1 =>
|
|
249
|
-
ColorCss.get(cm2, cv2 + (xs->
|
|
252
|
+
ColorCss.get(cm2, cv2 + (xs->Array.length == 9 ? 10 : 0))
|
|
250
253
|
->Option.flatMap(colorCss2 => {
|
|
251
254
|
let css = combine(colorCss1, colorCss2);
|
|
252
255
|
switch (style->FontCss.get) {
|
|
@@ -270,26 +273,26 @@ module Document = {
|
|
|
270
273
|
txt->Js.String.slice(~from, ~to_)->Text;
|
|
271
274
|
|
|
272
275
|
// Parse a document
|
|
273
|
-
let parse = (txt: string, length: int, pos: int): parser
|
|
276
|
+
let parse = (txt: string, length: int, pos: int): parser<document> => {
|
|
274
277
|
let rec go = (pos: int, prev: int) =>
|
|
275
278
|
switch (pos == length, txt->AnsiCode.parse(pos)) {
|
|
276
279
|
// we reached the end of the txt
|
|
277
|
-
| (true, _) => (pos,
|
|
280
|
+
| (true, _) => (pos, list{text(txt, prev, pos)}->Some)
|
|
278
281
|
// current codepoint is an ANSI code or a HRef
|
|
279
282
|
| (_, (length, Some(code))) =>
|
|
280
283
|
let prevElem = txt->text(prev, pos);
|
|
281
284
|
let pos = pos + length;
|
|
282
285
|
switch (code) {
|
|
283
|
-
| Clear => (pos,
|
|
286
|
+
| Clear => (pos, list{prevElem}->Some)
|
|
284
287
|
| EraseLine => pos->go(pos)
|
|
285
|
-
| CarriageReturn => (pos,
|
|
286
|
-
| HRef(link) => (pos,
|
|
288
|
+
| CarriageReturn => (pos, list{prevElem, LineBreak}->Some)
|
|
289
|
+
| HRef(link) => (pos, list{prevElem, link->Link}->Some)
|
|
287
290
|
| Style(style) =>
|
|
288
291
|
// recursively parse the stylized block
|
|
289
292
|
switch (pos->go(pos)) {
|
|
290
293
|
| (pos, Some(styled)) => (
|
|
291
294
|
pos,
|
|
292
|
-
|
|
295
|
+
list{prevElem, DocStyle(style, styled)}->Some,
|
|
293
296
|
)
|
|
294
297
|
| _ => (pos, None)
|
|
295
298
|
}
|
|
@@ -303,7 +306,7 @@ module Document = {
|
|
|
303
306
|
|
|
304
307
|
// Convert a string to a document
|
|
305
308
|
let parse = (txt: string): document => {
|
|
306
|
-
let rec go = (txt: string, acc: list
|
|
309
|
+
let rec go = (txt: string, acc: list<document>) => {
|
|
307
310
|
let length = txt->Js.String.length;
|
|
308
311
|
switch (txt->Document.parse(length, 0)) {
|
|
309
312
|
| (pos, Some(doc)) when pos == length => acc->List.add(doc)
|
|
@@ -312,39 +315,39 @@ let parse = (txt: string): document => {
|
|
|
312
315
|
| _ => acc
|
|
313
316
|
};
|
|
314
317
|
};
|
|
315
|
-
txt->go(
|
|
318
|
+
txt->go(list{})->Belt.List.reverse->Belt.List.flatten;
|
|
316
319
|
};
|
|
317
320
|
|
|
318
321
|
// Convert a document to a React.element
|
|
319
322
|
let render = (doc: document): React.element => {
|
|
320
323
|
let rec go =
|
|
321
|
-
(xs: document, idx: int, acc: list
|
|
324
|
+
(xs: document, idx: int, acc: list<React.element>): React.element =>
|
|
322
325
|
switch (xs) {
|
|
323
|
-
|
|
|
324
|
-
|
|
|
326
|
+
| list{} => acc->List.reverse->List.toArray->React.array
|
|
327
|
+
| list{LineBreak, ...xs} =>
|
|
325
328
|
xs->go(idx + 1, acc->List.add(<br key={idx->string_of_int} />))
|
|
326
|
-
|
|
|
327
|
-
xs->go(idx + 1, acc->List.add(
|
|
328
|
-
|
|
|
329
|
+
| list{Text(txt), ...xs} =>
|
|
330
|
+
xs->go(idx + 1, acc->List.add(React.string(txt)))
|
|
331
|
+
| list{Link(href), ...xs} =>
|
|
329
332
|
xs->go(
|
|
330
333
|
idx + 1,
|
|
331
334
|
acc->List.add(
|
|
332
|
-
<a key={idx->string_of_int} href> href->React.string </a>,
|
|
335
|
+
<a key={idx->string_of_int} href> {href->React.string} </a>,
|
|
333
336
|
),
|
|
334
337
|
)
|
|
335
|
-
|
|
|
338
|
+
| list{DocStyle(style, elems), ...xs} =>
|
|
336
339
|
xs->go(
|
|
337
340
|
idx + 1,
|
|
338
341
|
acc->List.add(
|
|
339
|
-
<span key={idx->string_of_int} style> {elems->go(0,
|
|
342
|
+
<span key={idx->string_of_int} style> {elems->go(0, list{})} </span>,
|
|
340
343
|
),
|
|
341
344
|
)
|
|
342
345
|
};
|
|
343
|
-
doc->go(0,
|
|
346
|
+
doc->go(0, list{});
|
|
344
347
|
};
|
|
345
348
|
|
|
346
349
|
// The react component
|
|
347
|
-
|
|
350
|
+
@react.component
|
|
348
351
|
let make = (~log: string) => {
|
|
349
352
|
<div> {log->parse->render} </div>;
|
|
350
353
|
};
|