securemark 0.300.0 → 0.300.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/CHANGELOG.md +8 -0
- package/dist/index.js +237 -161
- package/index.d.ts +1 -1
- package/markdown.d.ts +3 -10
- package/package.json +1 -1
- package/src/api/bind.ts +6 -6
- package/src/api/parse.ts +2 -2
- package/src/combinator/control/inits.ts +13 -4
- package/src/combinator/control/sequence.ts +3 -1
- package/src/combinator/control/state.ts +1 -10
- package/src/combinator/control/union.ts +16 -3
- package/src/combinator/parser.ts +18 -8
- package/src/combinator/process/fence.ts +23 -33
- package/src/combinator/process/line.ts +1 -1
- package/src/combinator/process/surround.ts +2 -0
- package/src/parser/block/codeblock.test.ts +0 -2
- package/src/parser/block/codeblock.ts +4 -4
- package/src/parser/block/extension/aside.test.ts +0 -1
- package/src/parser/block/extension/aside.ts +2 -2
- package/src/parser/block/extension/example.test.ts +0 -2
- package/src/parser/block/extension/example.ts +2 -2
- package/src/parser/block/extension/fig.ts +5 -7
- package/src/parser/block/extension/figure.test.ts +0 -4
- package/src/parser/block/extension/figure.ts +6 -7
- package/src/parser/block/extension/message.test.ts +0 -1
- package/src/parser/block/extension/message.ts +2 -2
- package/src/parser/block/extension/placeholder.ts +4 -4
- package/src/parser/block/extension/table.test.ts +0 -1
- package/src/parser/block/extension/table.ts +2 -8
- package/src/parser/block/extension.ts +1 -2
- package/src/parser/block/mathblock.test.ts +0 -2
- package/src/parser/block/mathblock.ts +4 -4
- package/src/parser/context.ts +9 -12
- package/src/parser/document.ts +1 -1
- package/src/parser/inline/autolink/url.ts +4 -4
- package/src/parser/inline/math.ts +1 -1
- package/src/parser/inline/media.ts +4 -4
- package/src/parser/inline/ruby.ts +45 -8
- package/src/parser/inline/template.ts +4 -4
- package/src/parser/source/escapable.ts +0 -1
- package/src/parser/source/text.ts +14 -43
- package/src/parser/source/unescapable.ts +0 -1
- package/src/parser/source/whitespace.ts +36 -0
- package/src/parser/source.ts +1 -0
- package/src/parser/visibility.ts +2 -1
|
@@ -7,13 +7,13 @@ import { html } from 'typed-dom/dom';
|
|
|
7
7
|
const opener = /(~{3,})(?!~)[^\r\n]*(?:$|\r?\n)/y;
|
|
8
8
|
|
|
9
9
|
export const segment: ExtensionParser.PlaceholderParser.SegmentParser = block(
|
|
10
|
-
fence(opener, false
|
|
10
|
+
fence(opener, false));
|
|
11
11
|
|
|
12
12
|
export const segment_: ExtensionParser.PlaceholderParser.SegmentParser = block(
|
|
13
|
-
fence(opener, false,
|
|
13
|
+
fence(opener, false, false), false);
|
|
14
14
|
|
|
15
15
|
export const placeholder: ExtensionParser.PlaceholderParser = block(inits([
|
|
16
|
-
fence(opener, true
|
|
16
|
+
fence(opener, true),
|
|
17
17
|
(_, output) => {
|
|
18
18
|
const [body, overflow, closer, opener, delim] = unwrap(output.pop()) as string[];
|
|
19
19
|
return output.append(
|
|
@@ -28,6 +28,6 @@ export const placeholder: ExtensionParser.PlaceholderParser = block(inits([
|
|
|
28
28
|
overflow ? `Invalid trailing line after the closing delimiter "${delim}"` :
|
|
29
29
|
'Invalid argument'),
|
|
30
30
|
},
|
|
31
|
-
`${opener}${body}${
|
|
31
|
+
`${opener}${body}${closer}${overflow}`)));
|
|
32
32
|
},
|
|
33
33
|
]));
|
|
@@ -10,7 +10,6 @@ describe('Unit: parser/block/extension/table', () => {
|
|
|
10
10
|
|
|
11
11
|
it('invalid', () => {
|
|
12
12
|
assert.deepStrictEqual(inspect(parser, input('~~~table a\n-\n~~~')), [['<pre class="invalid" translate="no">~~~table a\n-\n~~~</pre>'], '']);
|
|
13
|
-
assert.deepStrictEqual(inspect(parser, input(`~~~table\n0${'\n'.repeat(10001)}~~~`), '>'), [['<pre class="invalid" translate="no">'], '']);
|
|
14
13
|
});
|
|
15
14
|
|
|
16
15
|
it('data', () => {
|
|
@@ -17,14 +17,8 @@ import CellParser = TableParser.CellParser;
|
|
|
17
17
|
|
|
18
18
|
const opener = /(~{3,})table(?:\/(\S+))?(?!\S)([^\r\n]*)(?:$|\r?\n)/y;
|
|
19
19
|
|
|
20
|
-
export const segment: TableParser.SegmentParser = block(
|
|
21
|
-
fence(opener, false, 10000));
|
|
22
|
-
|
|
23
|
-
export const segment_: TableParser.SegmentParser = block(
|
|
24
|
-
fence(opener, false, 10000, false), false);
|
|
25
|
-
|
|
26
20
|
export const table: TableParser = block(inits([
|
|
27
|
-
fence(opener, true
|
|
21
|
+
fence(opener, true),
|
|
28
22
|
(_, output) => {
|
|
29
23
|
const [body, overflow, closer, opener, delim, type, param] = unwrap(output.pop()) as string[];
|
|
30
24
|
if (!closer || overflow || param.trimStart()) return output.append(
|
|
@@ -37,7 +31,7 @@ export const table: TableParser = block(inits([
|
|
|
37
31
|
!closer ? `Missing the closing delimiter "${delim}"` :
|
|
38
32
|
overflow ? `Invalid trailing line after the closing delimiter "${delim}"` :
|
|
39
33
|
'Invalid argument'),
|
|
40
|
-
}, `${opener}${body}${
|
|
34
|
+
}, `${opener}${body}${closer}${overflow}`)));
|
|
41
35
|
switch (type) {
|
|
42
36
|
case undefined:
|
|
43
37
|
case 'grid':
|
|
@@ -2,7 +2,7 @@ import { ExtensionParser } from '../block';
|
|
|
2
2
|
import { union, lazy } from '../../combinator';
|
|
3
3
|
import { segment as seg_fig } from './extension/fig';
|
|
4
4
|
import { figure, segment as seg_figure } from './extension/figure';
|
|
5
|
-
import { table
|
|
5
|
+
import { table } from './extension/table';
|
|
6
6
|
import { message } from './extension/message';
|
|
7
7
|
import { aside } from './extension/aside';
|
|
8
8
|
import { example } from './extension/example';
|
|
@@ -11,7 +11,6 @@ import { placeholder, segment as seg_placeholder } from './extension/placeholder
|
|
|
11
11
|
export const segment: ExtensionParser.SegmentParser = union([
|
|
12
12
|
seg_fig,
|
|
13
13
|
seg_figure,
|
|
14
|
-
seg_table,
|
|
15
14
|
seg_placeholder,
|
|
16
15
|
]);
|
|
17
16
|
|
|
@@ -25,7 +25,6 @@ describe('Unit: parser/block/mathblock', () => {
|
|
|
25
25
|
assert.deepStrictEqual(inspect(parser, input('$$$\n$$')), [['<pre class="invalid" translate="no">$$$\n$$</pre>'], '']);
|
|
26
26
|
assert.deepStrictEqual(inspect(parser, input('$$$\n$$$')), [['<pre class="invalid" translate="no">$$$\n$$$</pre>'], '']);
|
|
27
27
|
assert.deepStrictEqual(inspect(parser, input(' $$\n$$')), undefined);
|
|
28
|
-
assert.deepStrictEqual(inspect(parser, input(`$$\n0${'\n'.repeat(301)}$$`), '>'), [['<pre class="invalid" translate="no">'], '']);
|
|
29
28
|
});
|
|
30
29
|
|
|
31
30
|
it('basic', () => {
|
|
@@ -40,7 +39,6 @@ describe('Unit: parser/block/mathblock', () => {
|
|
|
40
39
|
assert.deepStrictEqual(inspect(parser, input('$$\n$$\n\n$$')), [['<div class="math" translate="no">$$\n$$</div>'], '\n$$']);
|
|
41
40
|
assert.deepStrictEqual(inspect(parser, input('$$\n$$$\n$$')), [['<div class="math" translate="no">$$\n$$$\n$$</div>'], '']);
|
|
42
41
|
assert.deepStrictEqual(inspect(parser, input('$$\n$$$\n\n$$')), [['<div class="math" translate="no">$$\n$$$\n\n$$</div>'], '']);
|
|
43
|
-
assert.deepStrictEqual(inspect(parser, input(`$$\n0${'\n'.repeat(300)}$$`), '>'), [['<div class="math" translate="no">'], '']);
|
|
44
42
|
});
|
|
45
43
|
|
|
46
44
|
});
|
|
@@ -7,13 +7,13 @@ import { html } from 'typed-dom/dom';
|
|
|
7
7
|
const opener = /(\${2,})(?!\$)([^\r\n]*)(?:$|\r?\n)/y;
|
|
8
8
|
|
|
9
9
|
export const segment: MathBlockParser.SegmentParser = block(
|
|
10
|
-
fence(opener, false
|
|
10
|
+
fence(opener, false));
|
|
11
11
|
|
|
12
12
|
export const segment_: MathBlockParser.SegmentParser = block(
|
|
13
|
-
fence(opener, false,
|
|
13
|
+
fence(opener, false, false), false);
|
|
14
14
|
|
|
15
15
|
export const mathblock: MathBlockParser = block(inits([
|
|
16
|
-
fence(opener, true
|
|
16
|
+
fence(opener, true),
|
|
17
17
|
({ caches: { math: cache = undefined } = {} }, output) => {
|
|
18
18
|
const [body, overflow, closer, opener, delim, param] = unwrap(output.pop()) as string[];
|
|
19
19
|
return output.append(
|
|
@@ -32,6 +32,6 @@ export const mathblock: MathBlockParser = block(inits([
|
|
|
32
32
|
overflow ? `Invalid trailing line after the closing delimiter "${delim}"` :
|
|
33
33
|
'Invalid argument'),
|
|
34
34
|
},
|
|
35
|
-
`${opener}${body}${
|
|
35
|
+
`${opener}${body}${closer}${overflow}`)));
|
|
36
36
|
},
|
|
37
37
|
]));
|
package/src/parser/context.ts
CHANGED
|
@@ -8,13 +8,14 @@ export function input(source: string, input: Input = new Input()): Input {
|
|
|
8
8
|
export class Input<M extends object = object> extends Ipt<M> {
|
|
9
9
|
constructor(
|
|
10
10
|
options: Partial<Input> = {},
|
|
11
|
+
source?: string,
|
|
11
12
|
) {
|
|
12
13
|
super(options);
|
|
13
14
|
const {
|
|
14
15
|
segment,
|
|
15
16
|
header,
|
|
16
17
|
local,
|
|
17
|
-
|
|
18
|
+
whitespace,
|
|
18
19
|
host,
|
|
19
20
|
url,
|
|
20
21
|
id,
|
|
@@ -22,20 +23,21 @@ export class Input<M extends object = object> extends Ipt<M> {
|
|
|
22
23
|
caches,
|
|
23
24
|
test,
|
|
24
25
|
} = options;
|
|
26
|
+
this.source = source ?? options.source ?? '';
|
|
25
27
|
this.resources ??= {
|
|
26
28
|
clock: -1,
|
|
27
29
|
interval: 200,
|
|
28
30
|
recursions: [
|
|
29
|
-
10 || Recursion.
|
|
31
|
+
10 || Recursion.document,
|
|
30
32
|
100 || Recursion.block,
|
|
31
33
|
100 || Recursion.inline,
|
|
32
|
-
100 || Recursion.
|
|
34
|
+
100 || Recursion.bracket,
|
|
33
35
|
],
|
|
34
36
|
};
|
|
35
37
|
this.segment = segment ?? Segment.unknown;
|
|
36
38
|
this.header = header ?? true;
|
|
37
39
|
this.local = local ?? false;
|
|
38
|
-
this.
|
|
40
|
+
this.whitespace = whitespace ?? false;
|
|
39
41
|
this.host = host;
|
|
40
42
|
this.url = url;
|
|
41
43
|
this.id = id;
|
|
@@ -51,7 +53,7 @@ export class Input<M extends object = object> extends Ipt<M> {
|
|
|
51
53
|
public override segment: Segment;
|
|
52
54
|
public header: boolean;
|
|
53
55
|
public local: boolean;
|
|
54
|
-
public
|
|
56
|
+
public whitespace: boolean;
|
|
55
57
|
public recursion = new RecursionCounter(2);
|
|
56
58
|
public readonly host?: URL;
|
|
57
59
|
public readonly url?: URL;
|
|
@@ -115,10 +117,10 @@ export const enum State {
|
|
|
115
117
|
}
|
|
116
118
|
|
|
117
119
|
export const enum Recursion {
|
|
118
|
-
|
|
120
|
+
document,
|
|
119
121
|
block,
|
|
120
122
|
inline,
|
|
121
|
-
|
|
123
|
+
bracket,
|
|
122
124
|
}
|
|
123
125
|
|
|
124
126
|
export const enum Backtrack {
|
|
@@ -134,12 +136,7 @@ export const enum Backtrack {
|
|
|
134
136
|
}
|
|
135
137
|
|
|
136
138
|
export const enum Command {
|
|
137
|
-
Error = '\x07',
|
|
138
139
|
Cancel = '\x18',
|
|
139
140
|
Escape = '\x1B',
|
|
140
141
|
Separator = '\x1F',
|
|
141
142
|
}
|
|
142
|
-
|
|
143
|
-
export const CmdRegExp = {
|
|
144
|
-
Error: /\x07/g,
|
|
145
|
-
} as const;
|
package/src/parser/document.ts
CHANGED
|
@@ -29,7 +29,7 @@ export const document: MarkdownParser = (() => {
|
|
|
29
29
|
output.push();
|
|
30
30
|
return output.context;
|
|
31
31
|
},
|
|
32
|
-
recursion(Recursion.
|
|
32
|
+
recursion(Recursion.document, force(() => loop)),
|
|
33
33
|
(input, output) => {
|
|
34
34
|
assert(input.position === input.source.length);
|
|
35
35
|
const doc = frag(unwrap(output.pop()));
|
|
@@ -39,12 +39,12 @@ export const lineurl: AutolinkParser.UrlParser.LineUrlParser = lazy(() => focus(
|
|
|
39
39
|
])));
|
|
40
40
|
|
|
41
41
|
const bracket: AutolinkParser.UrlParser.BracketParser = lazy(() => backtrack(union([
|
|
42
|
-
surround(str('('), recursion(Recursion.
|
|
42
|
+
surround(str('('), recursion(Recursion.bracket, some(union([bracket, unescsource]), ')')), str(')'),
|
|
43
43
|
true, [3 | Backtrack.unescapable]),
|
|
44
|
-
surround(str('['), recursion(Recursion.
|
|
44
|
+
surround(str('['), recursion(Recursion.bracket, some(union([bracket, unescsource]), ']')), str(']'),
|
|
45
45
|
true, [3 | Backtrack.unescapable]),
|
|
46
|
-
surround(str('{'), recursion(Recursion.
|
|
46
|
+
surround(str('{'), recursion(Recursion.bracket, some(union([bracket, unescsource]), '}')), str('}'),
|
|
47
47
|
true, [3 | Backtrack.unescapable]),
|
|
48
|
-
surround(str('"'), precedence(2, recursion(Recursion.
|
|
48
|
+
surround(str('"'), precedence(2, recursion(Recursion.bracket, some(unescsource, '"'))), str('"'),
|
|
49
49
|
true, [3 | Backtrack.unescapable]),
|
|
50
50
|
])));
|
|
@@ -41,7 +41,7 @@ export const math: MathParser = lazy(() => rewrite(
|
|
|
41
41
|
|
|
42
42
|
const bracket: MathParser.BracketParser = lazy(() => backtrack(surround(
|
|
43
43
|
str('{'),
|
|
44
|
-
recursion(Recursion.
|
|
44
|
+
recursion(Recursion.bracket,
|
|
45
45
|
some(union([
|
|
46
46
|
bracket,
|
|
47
47
|
some(escsource, /[{}$\r\n]|(?<=[0-9A-Za-z]):\/\/[[0-9A-Za-z]/y),
|
|
@@ -107,13 +107,13 @@ export const media: MediaParser = lazy(() => constraint(State.media, backtrack(o
|
|
|
107
107
|
})))));
|
|
108
108
|
|
|
109
109
|
const bracket: MediaParser.TextParser.BracketParser = lazy(() => union([
|
|
110
|
-
surround(str('('), recursion(Recursion.
|
|
110
|
+
surround(str('('), recursion(Recursion.bracket, some(union([unsafehtmlentity, bracket, txt]), ')')), str(')'),
|
|
111
111
|
true, [], undefined, () => Result.succ),
|
|
112
|
-
surround(str('['), recursion(Recursion.
|
|
112
|
+
surround(str('['), recursion(Recursion.bracket, some(union([unsafehtmlentity, bracket, txt]), ']')), str(']'),
|
|
113
113
|
true, [], undefined, () => Result.succ),
|
|
114
|
-
surround(str('{'), recursion(Recursion.
|
|
114
|
+
surround(str('{'), recursion(Recursion.bracket, some(union([unsafehtmlentity, bracket, txt]), '}')), str('}'),
|
|
115
115
|
true, [], undefined, () => Result.succ),
|
|
116
|
-
surround(str('"'), precedence(2, recursion(Recursion.
|
|
116
|
+
surround(str('"'), precedence(2, recursion(Recursion.bracket, some(union([unsafehtmlentity, txt]), '"'))), str('"'),
|
|
117
117
|
true, [], undefined, () => Result.succ),
|
|
118
118
|
]));
|
|
119
119
|
|
|
@@ -3,7 +3,7 @@ import { Input, Backtrack } from '../context';
|
|
|
3
3
|
import { Parser, Result, List, Node } from '../../combinator/parser';
|
|
4
4
|
import { union, inits, always, backtrack, surround, setBacktrack, dup, lazy, bind } from '../../combinator';
|
|
5
5
|
import { unsafehtmlentity } from './htmlentity';
|
|
6
|
-
import { txt } from '../source';
|
|
6
|
+
import { txt, isWhitespace } from '../source';
|
|
7
7
|
import { isNonblankNodeStart } from '../visibility';
|
|
8
8
|
import { unwrap } from '../util';
|
|
9
9
|
import { html, defrag } from 'typed-dom/dom';
|
|
@@ -63,8 +63,6 @@ export const ruby: RubyParser = lazy(() => backtrack(bind(
|
|
|
63
63
|
}
|
|
64
64
|
})));
|
|
65
65
|
|
|
66
|
-
const delimiter = /[$"`\[\](){}<>()[]{}|]|\\?\r?\n/y;
|
|
67
|
-
|
|
68
66
|
interface Memory {
|
|
69
67
|
position: number;
|
|
70
68
|
state: boolean;
|
|
@@ -72,7 +70,7 @@ interface Memory {
|
|
|
72
70
|
}
|
|
73
71
|
const text: RubyParser.TextParser = always<Parser<string, Input<Memory>>>([
|
|
74
72
|
(input, output) => {
|
|
75
|
-
input.
|
|
73
|
+
input.whitespace = true;
|
|
76
74
|
input.memory = {
|
|
77
75
|
position: 0,
|
|
78
76
|
state: false,
|
|
@@ -83,7 +81,7 @@ const text: RubyParser.TextParser = always<Parser<string, Input<Memory>>>([
|
|
|
83
81
|
() => loop,
|
|
84
82
|
(input, output) => {
|
|
85
83
|
const { memory } = input;
|
|
86
|
-
input.
|
|
84
|
+
input.whitespace = false;
|
|
87
85
|
return memory.state || memory.nodes.last!.value.trimStart() !== ''
|
|
88
86
|
? output.import(memory.nodes)
|
|
89
87
|
: undefined;
|
|
@@ -94,10 +92,9 @@ const loop: Result<string, Input<Memory>> = [
|
|
|
94
92
|
const { source, memory } = input;
|
|
95
93
|
for (let { position } = input; ; position = input.position) {
|
|
96
94
|
if (position === source.length) return Result.skip;
|
|
97
|
-
|
|
98
|
-
if (delimiter.test(source)) return Result.skip;
|
|
95
|
+
if (isDelimiter(source, position)) return Result.skip;
|
|
99
96
|
assert(source[position] !== '\n');
|
|
100
|
-
if (source[position]
|
|
97
|
+
if (!isWhitespace(source[position])) break;
|
|
101
98
|
memory.state ||= memory.nodes.last!.value.trimStart() !== '';
|
|
102
99
|
memory.nodes.push(new Node(''));
|
|
103
100
|
input.position += 1;
|
|
@@ -124,3 +121,43 @@ function* zip<N extends Node<unknown>>(a: List<N>, b: List<N>): Iterable<[N | un
|
|
|
124
121
|
yield [ra.value, rb.value];
|
|
125
122
|
}
|
|
126
123
|
}
|
|
124
|
+
|
|
125
|
+
function isDelimiter(source: string, position: number): boolean {
|
|
126
|
+
switch (source[position]) {
|
|
127
|
+
case '$':
|
|
128
|
+
case '"':
|
|
129
|
+
case '`':
|
|
130
|
+
case '[':
|
|
131
|
+
case ']':
|
|
132
|
+
case '(':
|
|
133
|
+
case ')':
|
|
134
|
+
case '{':
|
|
135
|
+
case '}':
|
|
136
|
+
case '<':
|
|
137
|
+
case '>':
|
|
138
|
+
case '(':
|
|
139
|
+
case ')':
|
|
140
|
+
case '[':
|
|
141
|
+
case ']':
|
|
142
|
+
case '{':
|
|
143
|
+
case '}':
|
|
144
|
+
case '|':
|
|
145
|
+
return true;
|
|
146
|
+
case '\\':
|
|
147
|
+
switch (source[position + 1]) {
|
|
148
|
+
case '\r':
|
|
149
|
+
return source[position + 2] === '\n';
|
|
150
|
+
case '\n':
|
|
151
|
+
return true;
|
|
152
|
+
default:
|
|
153
|
+
return false;
|
|
154
|
+
}
|
|
155
|
+
case '\r':
|
|
156
|
+
return source[position + 1] === '\n';
|
|
157
|
+
case '\n':
|
|
158
|
+
return true;
|
|
159
|
+
default:
|
|
160
|
+
return false;
|
|
161
|
+
}
|
|
162
|
+
assert(false);
|
|
163
|
+
}
|
|
@@ -25,15 +25,15 @@ export const template: TemplateParser = lazy(() => backtrack(surround(
|
|
|
25
25
|
input.source.slice(input.position - input.range, input.position)))))));
|
|
26
26
|
|
|
27
27
|
const bracket: TemplateParser.BracketParser = lazy(() => union([
|
|
28
|
-
surround(str('('), recursion(Recursion.
|
|
28
|
+
surround(str('('), recursion(Recursion.bracket, some(union([bracket, escsource]), ')')), str(')'),
|
|
29
29
|
true, [], undefined, ([as, bs], _, output) => bs && output.import(as.import(bs as List<Node<string>>))),
|
|
30
|
-
surround(str('['), recursion(Recursion.
|
|
30
|
+
surround(str('['), recursion(Recursion.bracket, some(union([bracket, escsource]), ']')), str(']'),
|
|
31
31
|
true, [], undefined, ([as, bs], _, output) => bs && output.import(as.import(bs as List<Node<string>>))),
|
|
32
|
-
surround(str('{'), recursion(Recursion.
|
|
32
|
+
surround(str('{'), recursion(Recursion.bracket, some(union([bracket, escsource]), '}')), str('}'),
|
|
33
33
|
true, [], undefined, ([as, bs], _, output) => bs && output.import(as.import(bs as List<Node<string>>))),
|
|
34
34
|
surround(
|
|
35
35
|
str('"'),
|
|
36
|
-
precedence(2, recursion(Recursion.
|
|
36
|
+
precedence(2, recursion(Recursion.bracket, some(escsource, /["\n]/y, [['"', 2], ['\n', 3]]))),
|
|
37
37
|
str('"'),
|
|
38
38
|
true, [], undefined, ([as, bs], _, output) => bs && output.import(as.import(bs as List<Node<string>>))),
|
|
39
39
|
]));
|
|
@@ -34,7 +34,6 @@ export const escsource: EscapableSourceParser = (input, output) => {
|
|
|
34
34
|
return output.append(new Node(html('br'), Flag.blank));
|
|
35
35
|
default:
|
|
36
36
|
assert(char !== '\n');
|
|
37
|
-
if (input.sequential) return output.append(new Node(char));
|
|
38
37
|
let i = seek(source, position);
|
|
39
38
|
assert(i > position);
|
|
40
39
|
i -= position;
|
|
@@ -3,6 +3,7 @@ import { Result, Node } from '../../combinator/parser';
|
|
|
3
3
|
import { union, spend } from '../../combinator';
|
|
4
4
|
import { State, Command } from '../context';
|
|
5
5
|
import { Flag } from '../node';
|
|
6
|
+
import { isWhitespace } from './whitespace';
|
|
6
7
|
import { html } from 'typed-dom/dom';
|
|
7
8
|
|
|
8
9
|
export const nonWhitespace = /[^ \t ]/g;
|
|
@@ -35,14 +36,13 @@ export const text: TextParser = (input, output) => {
|
|
|
35
36
|
return output.append(new Node(html('br'), Flag.blank));
|
|
36
37
|
default:
|
|
37
38
|
assert(char !== '\n');
|
|
38
|
-
if (input.sequential) return output.append(new Node(char));
|
|
39
39
|
nonWhitespace.lastIndex = position + 1;
|
|
40
40
|
const s = canSkip(source, position);
|
|
41
41
|
let i = s
|
|
42
42
|
? nonWhitespace.test(source)
|
|
43
43
|
? nonWhitespace.lastIndex - 1
|
|
44
44
|
: source.length
|
|
45
|
-
: next(source, position, state);
|
|
45
|
+
: next(source, position, input.whitespace, state);
|
|
46
46
|
assert(i > position);
|
|
47
47
|
const lineend = 0
|
|
48
48
|
|| s && i === source.length
|
|
@@ -68,28 +68,15 @@ export function canSkip(source: string, position: number): boolean {
|
|
|
68
68
|
if (position + 1 === source.length) return true;
|
|
69
69
|
return isWhitespace(source[position + 1], true);
|
|
70
70
|
}
|
|
71
|
-
function isWhitespace(char: string, linebreak: boolean): boolean {
|
|
72
|
-
switch (char) {
|
|
73
|
-
case ' ':
|
|
74
|
-
case '\t':
|
|
75
|
-
case ' ':
|
|
76
|
-
return true;
|
|
77
|
-
case '\r':
|
|
78
|
-
case '\n':
|
|
79
|
-
return linebreak;
|
|
80
|
-
default:
|
|
81
|
-
return false;
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
71
|
|
|
85
|
-
function next(source: string, position: number, state: number): number {
|
|
86
|
-
let index= seek(source, position, state);
|
|
72
|
+
function next(source: string, position: number, space: boolean, state: number): number {
|
|
73
|
+
let index= seek(source, position, space, state);
|
|
87
74
|
assert(index > position);
|
|
88
75
|
if (index === source.length) return index;
|
|
89
76
|
const char = source[index];
|
|
90
77
|
switch (char) {
|
|
91
78
|
case '%':
|
|
92
|
-
assert(source.startsWith('%]', index) && isWhitespace(source[index - 1]
|
|
79
|
+
assert(source.startsWith('%]', index) && isWhitespace(source[index - 1]));
|
|
93
80
|
index += index - 1 > position
|
|
94
81
|
? -1
|
|
95
82
|
: 0;
|
|
@@ -167,7 +154,7 @@ export function isAlphanumeric(char: string): boolean {
|
|
|
167
154
|
return 'A' <= char && char <= 'Z';
|
|
168
155
|
}
|
|
169
156
|
|
|
170
|
-
function seek(source: string, position: number, state: number): number {
|
|
157
|
+
function seek(source: string, position: number, space: boolean, state: number): number {
|
|
171
158
|
for (let i = position + 1; i < source.length; ++i) {
|
|
172
159
|
const char = source[i];
|
|
173
160
|
switch (char) {
|
|
@@ -208,7 +195,7 @@ function seek(source: string, position: number, state: number): number {
|
|
|
208
195
|
if (source[i + 1] === char && source[i + 2] === char) return i;
|
|
209
196
|
continue;
|
|
210
197
|
case '%':
|
|
211
|
-
if (source[i + 1] === ']' && isWhitespace(source[i - 1]
|
|
198
|
+
if (source[i + 1] === ']' && isWhitespace(source[i - 1])) return i;
|
|
212
199
|
continue;
|
|
213
200
|
case ':':
|
|
214
201
|
if (source[i + 1] === '/' && source[i + 2] === '/') return i;
|
|
@@ -216,30 +203,14 @@ function seek(source: string, position: number, state: number): number {
|
|
|
216
203
|
case '&':
|
|
217
204
|
if (source[i + 1] !== ' ') return i;
|
|
218
205
|
continue;
|
|
219
|
-
case ' ':
|
|
220
|
-
case '\t':
|
|
221
|
-
case ' ':
|
|
222
|
-
if (i + 1 === source.length) return i;
|
|
223
|
-
switch (source[i + 1]) {
|
|
224
|
-
case ' ':
|
|
225
|
-
case '\t':
|
|
226
|
-
case '\r':
|
|
227
|
-
case '\n':
|
|
228
|
-
case ' ':
|
|
229
|
-
return i;
|
|
230
|
-
case '\\':
|
|
231
|
-
if (i + 2 === source.length) return i;
|
|
232
|
-
switch (source[i + 2]) {
|
|
233
|
-
case ' ':
|
|
234
|
-
case '\t':
|
|
235
|
-
case '\r':
|
|
236
|
-
case '\n':
|
|
237
|
-
case ' ':
|
|
238
|
-
return i;
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
continue;
|
|
242
206
|
default:
|
|
207
|
+
if (!isWhitespace(char)) continue;
|
|
208
|
+
if (space) return i;
|
|
209
|
+
if (i + 1 === source.length) return i;
|
|
210
|
+
if (isWhitespace(source[i + 1])) return i;
|
|
211
|
+
if (source[i + 1] !== '\\') continue;
|
|
212
|
+
if (i + 2 === source.length) return i;
|
|
213
|
+
if (isWhitespace(source[i + 2])) return i;
|
|
243
214
|
continue;
|
|
244
215
|
}
|
|
245
216
|
assert(false);
|
|
@@ -24,7 +24,6 @@ export const unescsource: UnescapableSourceParser = (input, output) => {
|
|
|
24
24
|
return output.append(new Node(html('br'), Flag.blank));
|
|
25
25
|
default:
|
|
26
26
|
assert(char !== '\n');
|
|
27
|
-
if (input.sequential) return output.append(new Node(char));
|
|
28
27
|
nonWhitespace.lastIndex = position + 1;
|
|
29
28
|
let i = canSkip(source, position)
|
|
30
29
|
? nonWhitespace.test(source)
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// https://util.unicode.org/UnicodeJsps/list-unicodeset.jsp?a=%5Cp%7BWhite_Space%7D&g=&i=
|
|
2
|
+
// https://en.wikipedia.org/wiki/Whitespace_character
|
|
3
|
+
// https://en.wikipedia.org/wiki/Newline
|
|
4
|
+
export function isWhitespace(char: string, linebreak: boolean = true): boolean {
|
|
5
|
+
switch (char) {
|
|
6
|
+
case '\u0009':
|
|
7
|
+
case '\u000B':
|
|
8
|
+
case '\u000C':
|
|
9
|
+
case '\u0020':
|
|
10
|
+
case '\u0085':
|
|
11
|
+
case '\u00A0':
|
|
12
|
+
case '\u1680':
|
|
13
|
+
case '\u2000':
|
|
14
|
+
case '\u2001':
|
|
15
|
+
case '\u2002':
|
|
16
|
+
case '\u2003':
|
|
17
|
+
case '\u2004':
|
|
18
|
+
case '\u2005':
|
|
19
|
+
case '\u2006':
|
|
20
|
+
case '\u2007':
|
|
21
|
+
case '\u2008':
|
|
22
|
+
case '\u2009':
|
|
23
|
+
case '\u200A':
|
|
24
|
+
case '\u2028':
|
|
25
|
+
case '\u2029':
|
|
26
|
+
case '\u202F':
|
|
27
|
+
case '\u205F':
|
|
28
|
+
case '\u3000':
|
|
29
|
+
return true;
|
|
30
|
+
case '\u000A':
|
|
31
|
+
case '\u000D':
|
|
32
|
+
return linebreak;
|
|
33
|
+
default:
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
}
|
package/src/parser/source.ts
CHANGED
|
@@ -15,4 +15,5 @@ export { text, txt } from './source/text';
|
|
|
15
15
|
export { escsource } from './source/escapable';
|
|
16
16
|
export { unescsource } from './source/unescapable';
|
|
17
17
|
export { str, strs } from './source/str';
|
|
18
|
+
export { isWhitespace } from './source/whitespace';
|
|
18
19
|
export { contentline, emptyline, emptysegment, anyline } from './source/line';
|
package/src/parser/visibility.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { Input, Command } from './context';
|
|
|
3
3
|
import { Flag } from './node';
|
|
4
4
|
import { always, fmap } from '../combinator';
|
|
5
5
|
import { invisibleBlankHTMLEntityNames } from '../api/normalize';
|
|
6
|
+
import { isWhitespace } from './source';
|
|
6
7
|
|
|
7
8
|
namespace blank {
|
|
8
9
|
export const line = new RegExp(
|
|
@@ -102,7 +103,7 @@ function isNonblank({ value: node, flags }: Node<HTMLElement | string>, strpos?:
|
|
|
102
103
|
case '\n':
|
|
103
104
|
return false;
|
|
104
105
|
default:
|
|
105
|
-
return str.trimStart()
|
|
106
|
+
return !isWhitespace(str.trimStart());
|
|
106
107
|
}
|
|
107
108
|
}
|
|
108
109
|
|