securemark 0.298.5 → 0.298.7
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 +246 -215
- package/package.json +1 -1
- package/src/combinator/data/delimiter.ts +40 -14
- package/src/combinator/data/parser/context.ts +5 -3
- package/src/combinator/data/parser/some.ts +2 -0
- package/src/combinator/data/parser/union.ts +1 -4
- package/src/combinator/data/parser.ts +7 -0
- package/src/parser/api/parse.test.ts +3 -21
- package/src/parser/context.ts +0 -2
- package/src/parser/inline/annotation.test.ts +6 -10
- package/src/parser/inline/annotation.ts +31 -113
- package/src/parser/inline/autolink/url.test.ts +1 -0
- package/src/parser/inline/deletion.ts +5 -4
- package/src/parser/inline/emstrong.ts +11 -4
- package/src/parser/inline/extension/placeholder.ts +1 -1
- package/src/parser/inline/insertion.ts +5 -4
- package/src/parser/inline/italic.ts +5 -4
- package/src/parser/inline/mark.ts +7 -6
- package/src/parser/inline.test.ts +2 -2
- package/src/parser/repeat.ts +133 -0
- package/src/parser/util.ts +1 -70
package/package.json
CHANGED
|
@@ -161,13 +161,20 @@ export function matcher(pattern: string | RegExp, advance: boolean, after?: Pars
|
|
|
161
161
|
const count = typeof pattern === 'object'
|
|
162
162
|
? /[^^\\*+][*+]|{\d+,}/.test(pattern.source)
|
|
163
163
|
: false;
|
|
164
|
+
let sid = 0, pos = 0, index = -1;
|
|
164
165
|
switch (typeof pattern) {
|
|
165
166
|
case 'string':
|
|
166
167
|
if (pattern === '') return () => new List([new Node(pattern)]);
|
|
167
168
|
return input => {
|
|
168
169
|
const context = input;
|
|
169
|
-
const { source, position } = context;
|
|
170
|
-
|
|
170
|
+
const { SID, source, position } = context;
|
|
171
|
+
const hit = SID === sid && position === pos;
|
|
172
|
+
index = hit
|
|
173
|
+
? index
|
|
174
|
+
: source.startsWith(pattern, position) ? position : -1;
|
|
175
|
+
sid = SID;
|
|
176
|
+
pos = position;
|
|
177
|
+
if (index === -1) return;
|
|
171
178
|
if (advance) {
|
|
172
179
|
context.position += pattern.length;
|
|
173
180
|
}
|
|
@@ -180,13 +187,19 @@ export function matcher(pattern: string | RegExp, advance: boolean, after?: Pars
|
|
|
180
187
|
assert(pattern.sticky);
|
|
181
188
|
return input => {
|
|
182
189
|
const context = input;
|
|
183
|
-
const { source, position } = context;
|
|
190
|
+
const { SID, source, position } = context;
|
|
191
|
+
const hit = SID === sid && position === pos;
|
|
184
192
|
pattern.lastIndex = position;
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
193
|
+
index = hit
|
|
194
|
+
? index
|
|
195
|
+
: pattern.test(source) ? pattern.lastIndex : -1;
|
|
196
|
+
sid = SID;
|
|
197
|
+
pos = position;
|
|
198
|
+
if (index === -1) return;
|
|
199
|
+
const src = source.slice(position, index);
|
|
200
|
+
count && !hit && consume(src.length, context);
|
|
188
201
|
if (advance) {
|
|
189
|
-
context.position
|
|
202
|
+
context.position = index;
|
|
190
203
|
}
|
|
191
204
|
const next = after?.(input);
|
|
192
205
|
return after
|
|
@@ -201,13 +214,20 @@ export function tester(pattern: string | RegExp, advance: boolean, after?: Parse
|
|
|
201
214
|
const count = typeof pattern === 'object'
|
|
202
215
|
? /[^^\\*+][*+]|{\d+,}/.test(pattern.source)
|
|
203
216
|
: false;
|
|
217
|
+
let sid = 0, pos = 0, index = -1;
|
|
204
218
|
switch (typeof pattern) {
|
|
205
219
|
case 'string':
|
|
206
220
|
if (pattern === '') return () => new List();
|
|
207
221
|
return input => {
|
|
208
222
|
const context = input;
|
|
209
|
-
const { source, position } = context;
|
|
210
|
-
|
|
223
|
+
const { SID, source, position } = context;
|
|
224
|
+
const hit = SID === sid && position === pos;
|
|
225
|
+
index = hit
|
|
226
|
+
? index
|
|
227
|
+
: source.startsWith(pattern, position) ? position : -1;
|
|
228
|
+
sid = SID;
|
|
229
|
+
pos = position;
|
|
230
|
+
if (index === -1) return;
|
|
211
231
|
if (advance) {
|
|
212
232
|
context.position += pattern.length;
|
|
213
233
|
}
|
|
@@ -218,13 +238,19 @@ export function tester(pattern: string | RegExp, advance: boolean, after?: Parse
|
|
|
218
238
|
assert(pattern.sticky);
|
|
219
239
|
return input => {
|
|
220
240
|
const context = input;
|
|
221
|
-
const { source, position } = context;
|
|
241
|
+
const { SID, source, position } = context;
|
|
242
|
+
const hit = SID === sid && position === pos;
|
|
222
243
|
pattern.lastIndex = position;
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
244
|
+
index = hit
|
|
245
|
+
? index
|
|
246
|
+
: pattern.test(source) ? pattern.lastIndex : -1;
|
|
247
|
+
sid = SID;
|
|
248
|
+
pos = position;
|
|
249
|
+
if (index === -1) return;
|
|
250
|
+
const len = index - position;
|
|
251
|
+
count && !hit && consume(len, context);
|
|
226
252
|
if (advance) {
|
|
227
|
-
context.position
|
|
253
|
+
context.position = index;
|
|
228
254
|
}
|
|
229
255
|
if (after && after(input) === undefined) return;
|
|
230
256
|
return new List();
|
|
@@ -122,13 +122,15 @@ export function recursions(rs: readonly number[], parser: Parser): Parser {
|
|
|
122
122
|
assert(recursions.length > 0);
|
|
123
123
|
for (const recursion of rs) {
|
|
124
124
|
const rec = min(recursion, recursions.length - 1);
|
|
125
|
-
if (rec
|
|
126
|
-
rec
|
|
125
|
+
if (rec === -1) continue;
|
|
126
|
+
if (recursions[rec] < 1) throw new Error('Too much recursion');
|
|
127
|
+
--recursions[rec];
|
|
127
128
|
}
|
|
128
129
|
const result = parser(input);
|
|
129
130
|
for (const recursion of rs) {
|
|
130
131
|
const rec = min(recursion, recursions.length - 1);
|
|
131
|
-
rec
|
|
132
|
+
if (rec === -1) continue;
|
|
133
|
+
++recursions[rec];
|
|
132
134
|
}
|
|
133
135
|
return result;
|
|
134
136
|
};
|
|
@@ -38,8 +38,10 @@ export function some<N>(parser: Parser<N>, delimiter?: number | string | RegExp
|
|
|
38
38
|
for (const len = source.length; context.position < len;) {
|
|
39
39
|
if (match(input)) break;
|
|
40
40
|
if (context.delimiters.test(input)) break;
|
|
41
|
+
const pos = context.position;
|
|
41
42
|
const result = parser(input);
|
|
42
43
|
if (result === undefined) break;
|
|
44
|
+
if (context.position === pos) break;
|
|
43
45
|
nodes = nodes?.import(result) ?? result;
|
|
44
46
|
if (limit >= 0 && context.position - position > limit) break;
|
|
45
47
|
}
|
|
@@ -10,12 +10,9 @@ export function union<N, D extends Parser<N>[]>(parsers: D): Parser<N, Context,
|
|
|
10
10
|
return parsers[0];
|
|
11
11
|
default:
|
|
12
12
|
return eval([
|
|
13
|
-
'((',
|
|
14
|
-
parsers.map((_, i) => `parser${i},`).join(''),
|
|
15
|
-
') =>',
|
|
13
|
+
'(', parsers.map((_, i) => `parser${i},`).join(''), ') =>',
|
|
16
14
|
'input =>',
|
|
17
15
|
parsers.map((_, i) => `|| parser${i}(input)`).join('').slice(2),
|
|
18
|
-
')',
|
|
19
16
|
].join(''))(...parsers);
|
|
20
17
|
}
|
|
21
18
|
}
|
|
@@ -26,6 +26,10 @@ export class Node<N> implements List.Node {
|
|
|
26
26
|
public next?: this = undefined;
|
|
27
27
|
public prev?: this = undefined;
|
|
28
28
|
}
|
|
29
|
+
let SID = 0;
|
|
30
|
+
function sid(): number {
|
|
31
|
+
return SID = ++SID >>> 0 || 1;
|
|
32
|
+
}
|
|
29
33
|
export class Context {
|
|
30
34
|
constructor(
|
|
31
35
|
{
|
|
@@ -54,6 +58,7 @@ export class Context {
|
|
|
54
58
|
this.offset = offset ?? 0;
|
|
55
59
|
this.backtracks = backtracks ?? {};
|
|
56
60
|
}
|
|
61
|
+
public SID: number = sid();
|
|
57
62
|
public source: string;
|
|
58
63
|
public position: number;
|
|
59
64
|
public segment: number;
|
|
@@ -110,6 +115,7 @@ export const enum Segment {
|
|
|
110
115
|
}
|
|
111
116
|
|
|
112
117
|
export function input<C extends Context>(source: string, context: C): Input<C> {
|
|
118
|
+
context.SID = sid();
|
|
113
119
|
context.source = source;
|
|
114
120
|
context.position = 0;
|
|
115
121
|
return context;
|
|
@@ -118,6 +124,7 @@ export function input<C extends Context>(source: string, context: C): Input<C> {
|
|
|
118
124
|
export function subinput<C extends Context>(source: string, context: C): Input<C> {
|
|
119
125
|
return {
|
|
120
126
|
...context,
|
|
127
|
+
SID: sid(),
|
|
121
128
|
source,
|
|
122
129
|
position: 0,
|
|
123
130
|
offset: 0,
|
|
@@ -297,15 +297,6 @@ describe('Unit: parser/api/parse', () => {
|
|
|
297
297
|
});
|
|
298
298
|
|
|
299
299
|
it('recursion', () => {
|
|
300
|
-
//assert.deepStrictEqual(
|
|
301
|
-
// [...parse(`${'{ '.repeat(20)}0`).children].map(el => el.tagName),
|
|
302
|
-
// ['P']);
|
|
303
|
-
//assert.deepStrictEqual(
|
|
304
|
-
// [...parse(`${'{ '.repeat(21)}0`).children].map(el => el.outerHTML.replace(/:\w+/, ':rnd')),
|
|
305
|
-
// [
|
|
306
|
-
// '<h1 id="error:rnd" class="error">Error: Too much recursion</h1>',
|
|
307
|
-
// `<pre class="error" translate="no">${'{ '.repeat(21)}0</pre>`,
|
|
308
|
-
// ]);
|
|
309
300
|
assert.deepStrictEqual(
|
|
310
301
|
[...parse(`${'['.repeat(20)}0`).children].map(el => el.outerHTML),
|
|
311
302
|
[`<p>${'['.repeat(20)}0</p>`]);
|
|
@@ -327,24 +318,12 @@ describe('Unit: parser/api/parse', () => {
|
|
|
327
318
|
assert.deepStrictEqual(
|
|
328
319
|
[...parse(`${'(('.repeat(3)}0${'))'.repeat(3)}`).children].map(el => el.tagName),
|
|
329
320
|
['H1', 'PRE']);
|
|
330
|
-
assert.deepStrictEqual(
|
|
331
|
-
[...parse(`${'(('.repeat(2)}!${'))'.repeat(2)}`).children].map(el => el.tagName),
|
|
332
|
-
['P', 'OL']);
|
|
333
|
-
assert.deepStrictEqual(
|
|
334
|
-
[...parse(`${'(('.repeat(3)}!${'))'.repeat(3)}`).children].map(el => el.tagName),
|
|
335
|
-
['H1', 'PRE']);
|
|
336
321
|
assert.deepStrictEqual(
|
|
337
322
|
[...parse(`(${'(('.repeat(2)}0${'))'.repeat(2)}`).children].map(el => el.tagName),
|
|
338
323
|
['P', 'OL']);
|
|
339
324
|
assert.deepStrictEqual(
|
|
340
325
|
[...parse(`(${'(('.repeat(3)}0${'))'.repeat(3)}`).children].map(el => el.tagName),
|
|
341
326
|
['H1', 'PRE']);
|
|
342
|
-
assert.deepStrictEqual(
|
|
343
|
-
[...parse(`(${'(('.repeat(2)}!${'))'.repeat(2)}`).children].map(el => el.tagName),
|
|
344
|
-
['P', 'OL']);
|
|
345
|
-
assert.deepStrictEqual(
|
|
346
|
-
[...parse(`(${'(('.repeat(3)}!${'))'.repeat(3)}`).children].map(el => el.tagName),
|
|
347
|
-
['H1', 'PRE']);
|
|
348
327
|
assert.deepStrictEqual(
|
|
349
328
|
[...parse(`${'(('.repeat(2)}0${'))'.repeat(2)}${'(('.repeat(2)}0${'))'.repeat(2)}`).children].map(el => el.tagName),
|
|
350
329
|
['P', 'OL']);
|
|
@@ -357,6 +336,9 @@ describe('Unit: parser/api/parse', () => {
|
|
|
357
336
|
assert.deepStrictEqual(
|
|
358
337
|
[...parse(`${'(('.repeat(2)}0${'))'.repeat(2)}${'(('.repeat(9)}0${'))'.repeat(3)}`).children].map(el => el.tagName),
|
|
359
338
|
['H1', 'PRE']);
|
|
339
|
+
assert.deepStrictEqual(
|
|
340
|
+
[...parse(`${'(('.repeat(3)}0))((1))))))`).children].map(el => el.tagName),
|
|
341
|
+
['H1', 'PRE']);
|
|
360
342
|
});
|
|
361
343
|
|
|
362
344
|
it('recovery', () => {
|
package/src/parser/context.ts
CHANGED
|
@@ -28,7 +28,6 @@ export class Context extends Ctx {
|
|
|
28
28
|
20 || Recursion.blockquote,
|
|
29
29
|
40 || Recursion.listitem,
|
|
30
30
|
20 || Recursion.inline,
|
|
31
|
-
20 || Recursion.annotation,
|
|
32
31
|
20 || Recursion.bracket,
|
|
33
32
|
20 || Recursion.terminal,
|
|
34
33
|
],
|
|
@@ -116,7 +115,6 @@ export const enum Recursion {
|
|
|
116
115
|
blockquote,
|
|
117
116
|
listitem,
|
|
118
117
|
inline,
|
|
119
|
-
annotation,
|
|
120
118
|
bracket,
|
|
121
119
|
terminal,
|
|
122
120
|
}
|
|
@@ -10,18 +10,15 @@ describe('Unit: parser/inline/annotation', () => {
|
|
|
10
10
|
|
|
11
11
|
it('invalid', () => {
|
|
12
12
|
assert.deepStrictEqual(inspect(parser, input('', new Context())), undefined);
|
|
13
|
-
assert.deepStrictEqual(inspect(parser, input('(', new Context())),
|
|
14
|
-
assert.deepStrictEqual(inspect(parser, input('()', new Context())),
|
|
15
|
-
assert.deepStrictEqual(inspect(parser, input('((', new Context())),
|
|
13
|
+
assert.deepStrictEqual(inspect(parser, input('(', new Context())), [['<span class="paren">(</span>'], '']);
|
|
14
|
+
assert.deepStrictEqual(inspect(parser, input('()', new Context())), [ [ '<span class="paren">()</span>' ], '' ]);
|
|
15
|
+
assert.deepStrictEqual(inspect(parser, input('((', new Context())), [ [ '<span class="paren">(<span class="paren">(</span></span>' ], '' ]);
|
|
16
16
|
assert.deepStrictEqual(inspect(parser, input('(())', new Context())), [['<span class="paren">(<span class="paren">()</span>)</span>'], '']);
|
|
17
17
|
assert.deepStrictEqual(inspect(parser, input('(()))', new Context())), [['<span class="paren">(<span class="paren">()</span>)</span>'], ')']);
|
|
18
18
|
assert.deepStrictEqual(inspect(parser, input('(("))', new Context())), [['<span class="paren">(<span class="paren">("))</span></span>'], '']);
|
|
19
19
|
assert.deepStrictEqual(inspect(parser, input('((a', new Context())), [['<span class="paren">(<span class="paren">(a</span></span>'], '']);
|
|
20
|
-
assert.deepStrictEqual(inspect(parser, input('((!', new Context())), [['<span class="paren">(<span class="paren">(!</span></span>'], '']);
|
|
21
20
|
assert.deepStrictEqual(inspect(parser, input('((a)', new Context())), [['<span class="paren">(<span class="paren">(a)</span></span>'], '']);
|
|
22
|
-
assert.deepStrictEqual(inspect(parser, input('((!)', new Context())), [['<span class="paren">(<span class="paren">(!)</span></span>'], '']);
|
|
23
21
|
assert.deepStrictEqual(inspect(parser, input('((a)b)', new Context())), [['<span class="paren">(<span class="paren">(a)</span>b)</span>'], '']);
|
|
24
|
-
assert.deepStrictEqual(inspect(parser, input('((!)b)', new Context())), [['<span class="paren">(<span class="paren">(!)</span>b)</span>'], '']);
|
|
25
22
|
assert.deepStrictEqual(inspect(parser, input('(([))', new Context())), [['<span class="paren">(<span class="paren">([))</span></span>'], '']);
|
|
26
23
|
assert.deepStrictEqual(inspect(parser, input('(([%))', new Context())), [['<span class="paren">(<span class="paren">([%))</span></span>'], '']);
|
|
27
24
|
assert.deepStrictEqual(inspect(parser, input('(( ))', new Context())), undefined);
|
|
@@ -39,11 +36,10 @@ describe('Unit: parser/inline/annotation', () => {
|
|
|
39
36
|
assert.deepStrictEqual(inspect(parser, input('((*a\nb*))', new Context())), [['<span class="bracket">(<span class="bracket">(<em>a<br>b</em>)</span>)</span>'], '']);
|
|
40
37
|
assert.deepStrictEqual(inspect(parser, input('((\\))', new Context())), [['<span class="paren">(<span class="paren">())</span></span>'], '']);
|
|
41
38
|
assert.deepStrictEqual(inspect(parser, input('(((a))', new Context())), [['<span class="paren">(<sup class="annotation"><span>a</span></sup></span>'], '']);
|
|
42
|
-
assert.deepStrictEqual(inspect(parser, input('(((
|
|
43
|
-
assert.deepStrictEqual(inspect(parser, input('(((*a*))', new Context())), [['<span class="paren">(<sup class="annotation"><span><em>a</em></span></sup></span>'], '']);
|
|
39
|
+
assert.deepStrictEqual(inspect(parser, input('((((a)))', new Context())), [['<span class="paren">(<sup class="annotation"><span><span class="paren">(a)</span></span></sup></span>'], '']);
|
|
44
40
|
assert.deepStrictEqual(inspect(parser, input('(((((a))))', new Context())), [['<span class="paren">(<sup class="annotation"><span><sup class="annotation"><span>a</span></sup></span></sup></span>'], '']);
|
|
45
|
-
assert.deepStrictEqual(inspect(parser, input('(((((
|
|
46
|
-
assert.deepStrictEqual(inspect(parser, input('(((((
|
|
41
|
+
assert.deepStrictEqual(inspect(parser, input('((((((a)))b)))', new Context())), [['<sup class="annotation"><span><span class="paren">(<sup class="annotation"><span><span class="paren">(a)</span></span></sup>b)</span></span></sup>'], '']);
|
|
42
|
+
assert.deepStrictEqual(inspect(parser, input('(((((((a)))b)))', new Context())), [['<span class="paren">(<sup class="annotation"><span><span class="paren">(<sup class="annotation"><span><span class="paren">(a)</span></span></sup>b)</span></span></sup></span>'], '']);
|
|
47
43
|
assert.deepStrictEqual(inspect(parser, input(' ((a))', new Context())), undefined);
|
|
48
44
|
});
|
|
49
45
|
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { AnnotationParser } from '../inline';
|
|
2
|
-
import { State, Recursion } from '../context';
|
|
2
|
+
import { State, Recursion, Command } from '../context';
|
|
3
3
|
import { List, Node } from '../../combinator/data/parser';
|
|
4
|
-
import { union, some,
|
|
4
|
+
import { union, some, precedence, constraint, surround, lazy } from '../../combinator';
|
|
5
5
|
import { inline } from '../inline';
|
|
6
6
|
import { bracketname } from './bracket';
|
|
7
|
+
import { repeat } from '../repeat';
|
|
7
8
|
import { beforeNonblank, trimBlankNodeEnd } from '../visibility';
|
|
8
9
|
import { unwrap } from '../util';
|
|
9
10
|
import { html, defrag } from 'typed-dom/dom';
|
|
@@ -23,118 +24,35 @@ import { html, defrag } from 'typed-dom/dom';
|
|
|
23
24
|
// 修正する必要があるためほぼ完全な二重処理が必要になり三重以上の注釈という不適切な使用のために
|
|
24
25
|
// 常に非常に非効率な処理を行い常時低速化するより三重以上の注釈を禁止して効率性を維持するのが妥当である。
|
|
25
26
|
const MAX_DEPTH = 20;
|
|
26
|
-
export const annotation: AnnotationParser = lazy(() => constraint(State.annotation,
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
new Node(
|
|
39
|
-
{ class: bracketname(context, 1, 1) },
|
|
40
|
-
['(', html('span', { class: bracketname(context, 2, 2) }, defrag(unwrap(ns))), ')']))
|
|
41
|
-
]);
|
|
42
|
-
}
|
|
43
|
-
const depth = MAX_DEPTH - (resources?.recursions[Recursion.annotation] ?? resources?.recursions.at(-1) ?? MAX_DEPTH);
|
|
44
|
-
recursion.add(depth);
|
|
45
|
-
return new List([
|
|
46
|
-
new Node(html('sup', { class: 'annotation' }, [html('span', defrag(unwrap(trimBlankNodeEnd(ns))))]))
|
|
47
|
-
]);
|
|
48
|
-
},
|
|
49
|
-
([, bs], context) => {
|
|
50
|
-
const { source, position, linebreak, recursion, resources } = context;
|
|
51
|
-
const depth = MAX_DEPTH - (resources?.recursions[Recursion.annotation] ?? resources?.recursions.at(-1) ?? MAX_DEPTH);
|
|
52
|
-
if (linebreak === 0 && bs && bs.length === 1 && source[position] === ')' && typeof bs.head?.value === 'object') {
|
|
53
|
-
const { className } = bs.head.value;
|
|
54
|
-
if (className === 'paren' || className === 'bracket') {
|
|
55
|
-
const { firstChild, lastChild } = bs.head.value;
|
|
56
|
-
assert(firstChild instanceof Text);
|
|
57
|
-
if (firstChild!.nodeValue!.length === 1) {
|
|
58
|
-
firstChild!.remove();
|
|
59
|
-
}
|
|
60
|
-
else {
|
|
61
|
-
firstChild!.nodeValue = firstChild!.nodeValue!.slice(1);
|
|
62
|
-
}
|
|
63
|
-
assert(lastChild instanceof Text);
|
|
64
|
-
if (lastChild!.nodeValue!.length === 1) {
|
|
65
|
-
lastChild!.remove();
|
|
66
|
-
}
|
|
67
|
-
else {
|
|
68
|
-
lastChild!.nodeValue = lastChild!.nodeValue!.slice(0, -1);
|
|
69
|
-
}
|
|
70
|
-
context.position += 1;
|
|
71
|
-
recursion.add(depth);
|
|
72
|
-
return new List([
|
|
73
|
-
new Node(html('span',
|
|
74
|
-
{ class: 'paren' },
|
|
75
|
-
['(', html('sup', { class: 'annotation' }, [html('span', bs.head.value.childNodes)])]))
|
|
76
|
-
]);
|
|
77
|
-
}
|
|
78
|
-
if (className === 'annotation' && deepunwrap(bs)) {
|
|
79
|
-
context.position += 1;
|
|
80
|
-
recursion.add(depth);
|
|
27
|
+
export const annotation: AnnotationParser = lazy(() => constraint(State.annotation,
|
|
28
|
+
repeat('(', beforeNonblank, ')', [Recursion.bracket], precedence(1, surround(
|
|
29
|
+
'',
|
|
30
|
+
some(union([inline]), ')', [[')', 1]]),
|
|
31
|
+
')',
|
|
32
|
+
false, [],
|
|
33
|
+
([, bs], { buffer }) => buffer.import(bs),
|
|
34
|
+
([, bs], { buffer }) => bs && buffer.import(bs).push(new Node(Command.Cancel)) && buffer)),
|
|
35
|
+
(nodes, context, lead, follow) => {
|
|
36
|
+
const { linebreak, recursion, resources } = context;
|
|
37
|
+
if (linebreak !== 0 || nodes.length === 0 || lead === 0 || follow % 2 === 0) {
|
|
38
|
+
nodes.unshift(new Node('('));
|
|
39
|
+
nodes.push(new Node(')'));
|
|
81
40
|
return new List([
|
|
82
|
-
new Node(html('span',
|
|
83
|
-
{ class: 'paren' },
|
|
84
|
-
['(', html('sup', { class: 'annotation' }, [html('span', [bs.head.value])])]))
|
|
41
|
+
new Node(html('span', { class: bracketname(context, 1, 1) }, defrag(unwrap(nodes))))
|
|
85
42
|
]);
|
|
86
43
|
}
|
|
87
|
-
|
|
88
|
-
bs ??= new List();
|
|
89
|
-
bs.unshift(new Node('('));
|
|
90
|
-
if (source[context.position] === ')') {
|
|
91
|
-
bs.push(new Node(')'));
|
|
92
|
-
context.position += 1;
|
|
93
|
-
context.range += 1;
|
|
94
|
-
}
|
|
95
|
-
bs = new List([
|
|
96
|
-
new Node(html('span',
|
|
97
|
-
{ class: bracketname(context, 2, context.position - position) },
|
|
98
|
-
defrag(unwrap(bs))))
|
|
99
|
-
]);
|
|
100
|
-
bs.unshift(new Node('('));
|
|
101
|
-
const cs = parser(context);
|
|
102
|
-
if (source[context.position] === ')') {
|
|
103
|
-
cs && bs.import(cs);
|
|
104
|
-
bs.push(new Node(')'));
|
|
44
|
+
recursion.add(MAX_DEPTH - (resources?.recursions[Recursion.bracket] ?? resources?.recursions.at(-1) ?? MAX_DEPTH));
|
|
105
45
|
context.position += 1;
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
if (el !== el?.parentNode?.lastChild) break;
|
|
120
|
-
if (el instanceof HTMLElement === false) break;
|
|
121
|
-
if (el?.className !== 'annotation') break;
|
|
122
|
-
bottom = el;
|
|
123
|
-
}
|
|
124
|
-
const el = bottom.firstChild!.firstChild;
|
|
125
|
-
if (el instanceof Element === false) return false;
|
|
126
|
-
if (el === el?.parentNode?.lastChild) {
|
|
127
|
-
const { className, firstChild, lastChild } = el;
|
|
128
|
-
if (className === 'paren' || className === 'bracket') {
|
|
129
|
-
firstChild!.nodeValue!.length === 1
|
|
130
|
-
? firstChild!.remove()
|
|
131
|
-
: firstChild!.nodeValue = firstChild!.nodeValue!.slice(1);
|
|
132
|
-
lastChild!.nodeValue!.length === 1
|
|
133
|
-
? lastChild!.remove()
|
|
134
|
-
: lastChild!.nodeValue = lastChild!.nodeValue!.slice(0, -1);
|
|
135
|
-
el.replaceWith(...el.childNodes);
|
|
136
|
-
return true;
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
return false;
|
|
140
|
-
}
|
|
46
|
+
return new List([
|
|
47
|
+
new Node(html('sup', { class: 'annotation' }, [html('span', defrag(unwrap(trimBlankNodeEnd(nodes))))]))
|
|
48
|
+
]);
|
|
49
|
+
},
|
|
50
|
+
(nodes, context, prefix, postfix) => {
|
|
51
|
+
assert(postfix === 0);
|
|
52
|
+
for (let i = 0; i < prefix; ++i) {
|
|
53
|
+
nodes.unshift(new Node('('));
|
|
54
|
+
nodes = new List([new Node(html('span', { class: bracketname(context, 1, 0) }, defrag(unwrap(nodes))))]);
|
|
55
|
+
context.range += 1;
|
|
56
|
+
}
|
|
57
|
+
return nodes;
|
|
58
|
+
})));
|
|
@@ -83,6 +83,7 @@ describe('Unit: parser/inline/autolink/url', () => {
|
|
|
83
83
|
assert.deepStrictEqual(inspect(parser, input(' http://host>', new Context())), [['<a class="url" href="http://host" target="_blank">http://host</a>'], '>']);
|
|
84
84
|
assert.deepStrictEqual(inspect(parser, input(' http://host(', new Context())), [['<a class="url" href="http://host" target="_blank">http://host</a>'], '(']);
|
|
85
85
|
assert.deepStrictEqual(inspect(parser, input(' http://host)', new Context())), [['<a class="url" href="http://host" target="_blank">http://host</a>'], ')']);
|
|
86
|
+
assert.deepStrictEqual(inspect(parser, input(` http://host${'+'.repeat(5e5)}`, new Context())), [['<a class="url" href="http://host" target="_blank">http://host</a>'], '+'.repeat(5e5)]);
|
|
86
87
|
});
|
|
87
88
|
|
|
88
89
|
it('trailing entities', () => {
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import { DeletionParser } from '../inline';
|
|
2
2
|
import { Recursion, Command } from '../context';
|
|
3
3
|
import { List, Node } from '../../combinator/data/parser';
|
|
4
|
-
import { union, some,
|
|
4
|
+
import { union, some, precedence, surround, open, lazy } from '../../combinator';
|
|
5
5
|
import { inline } from '../inline';
|
|
6
|
+
import { repeat } from '../repeat';
|
|
6
7
|
import { blankWith } from '../visibility';
|
|
7
|
-
import { unwrap
|
|
8
|
+
import { unwrap } from '../util';
|
|
8
9
|
import { html, defrag } from 'typed-dom/dom';
|
|
9
10
|
|
|
10
11
|
export const deletion: DeletionParser = lazy(() =>
|
|
11
|
-
|
|
12
|
+
repeat('~~', '', '~~', [Recursion.inline], precedence(0, surround(
|
|
12
13
|
'',
|
|
13
14
|
some(union([
|
|
14
15
|
some(inline, blankWith('\n', '~~')),
|
|
@@ -18,4 +19,4 @@ export const deletion: DeletionParser = lazy(() =>
|
|
|
18
19
|
false, [],
|
|
19
20
|
([, bs], { buffer }) => buffer.import(bs),
|
|
20
21
|
([, bs], { buffer }) => bs && buffer.import(bs).push(new Node(Command.Cancel)) && buffer)),
|
|
21
|
-
nodes => new List([new Node(html('del', defrag(unwrap(nodes))))])))
|
|
22
|
+
nodes => new List([new Node(html('del', defrag(unwrap(nodes))))])));
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { EmStrongParser, EmphasisParser, StrongParser } from '../inline';
|
|
2
2
|
import { Recursion, Command } from '../context';
|
|
3
3
|
import { Parser, Result, List, Node } from '../../combinator/data/parser';
|
|
4
|
-
import { union, some,
|
|
4
|
+
import { union, some, precedence, surround, lazy, bind } from '../../combinator';
|
|
5
5
|
import { inline } from '../inline';
|
|
6
6
|
import { strong } from './strong';
|
|
7
7
|
import { emphasis } from './emphasis';
|
|
8
8
|
import { strs } from '../source';
|
|
9
|
+
import { repeat } from '../repeat';
|
|
9
10
|
import { beforeNonblank, afterNonblank } from '../visibility';
|
|
10
|
-
import { unwrap
|
|
11
|
+
import { unwrap } from '../util';
|
|
11
12
|
import { html, defrag } from 'typed-dom/dom';
|
|
12
13
|
|
|
13
14
|
const substrong: Parser.IntermediateParser<StrongParser> = lazy(() => some(union([
|
|
@@ -23,7 +24,7 @@ const subemphasis: Parser.IntermediateParser<EmphasisParser> = lazy(() => some(u
|
|
|
23
24
|
// 可能な限り早く閉じるよう解析しなければならない。
|
|
24
25
|
// このため終端記号の後ろを見て終端を中止し同じ構文を再帰的に適用してはならない。
|
|
25
26
|
export const emstrong: EmStrongParser = lazy(() =>
|
|
26
|
-
|
|
27
|
+
repeat('***', beforeNonblank, '***', [Recursion.inline], precedence(0, surround(
|
|
27
28
|
'',
|
|
28
29
|
some(union([some(inline, '*', afterNonblank)])),
|
|
29
30
|
strs('*', 1, 3),
|
|
@@ -80,6 +81,7 @@ export const emstrong: EmStrongParser = lazy(() =>
|
|
|
80
81
|
nodes => new List([new Node(html('em', [html('strong', defrag(unwrap(nodes)))]))]),
|
|
81
82
|
(nodes, context, prefix, postfix, state) => {
|
|
82
83
|
context.position += postfix;
|
|
84
|
+
context.range += postfix;
|
|
83
85
|
assert(postfix < 3);
|
|
84
86
|
if (state) {
|
|
85
87
|
switch (postfix) {
|
|
@@ -96,6 +98,7 @@ export const emstrong: EmStrongParser = lazy(() =>
|
|
|
96
98
|
}
|
|
97
99
|
prefix -= postfix;
|
|
98
100
|
postfix -= postfix;
|
|
101
|
+
context.range += postfix;
|
|
99
102
|
switch (prefix) {
|
|
100
103
|
case 0:
|
|
101
104
|
break;
|
|
@@ -114,6 +117,7 @@ export const emstrong: EmStrongParser = lazy(() =>
|
|
|
114
117
|
})
|
|
115
118
|
(context) ?? prepend('*', nodes);
|
|
116
119
|
prefix -= 1;
|
|
120
|
+
context.range += 1;
|
|
117
121
|
break;
|
|
118
122
|
case 2:
|
|
119
123
|
nodes = bind<StrongParser>(
|
|
@@ -130,14 +134,17 @@ export const emstrong: EmStrongParser = lazy(() =>
|
|
|
130
134
|
})
|
|
131
135
|
(context) ?? prepend('**', nodes);
|
|
132
136
|
prefix -= 2;
|
|
137
|
+
context.range += 2;
|
|
133
138
|
break;
|
|
134
139
|
}
|
|
135
140
|
}
|
|
136
141
|
if (prefix > postfix) {
|
|
137
142
|
nodes = prepend('*'.repeat(prefix - postfix), nodes);
|
|
143
|
+
prefix = 0;
|
|
144
|
+
context.range += prefix - postfix;
|
|
138
145
|
}
|
|
139
146
|
return nodes;
|
|
140
|
-
}))
|
|
147
|
+
}));
|
|
141
148
|
|
|
142
149
|
function prepend<N>(prefix: string, nodes: List<Node<N>>): List<Node<N>> {
|
|
143
150
|
if (typeof nodes.head?.value === 'string') {
|
|
@@ -15,7 +15,7 @@ import { html } from 'typed-dom/dom';
|
|
|
15
15
|
export const placeholder: ExtensionParser.PlaceholderParser = lazy(() => surround(
|
|
16
16
|
// ^はabbrで使用済みだが^:などのようにして分離使用可能
|
|
17
17
|
str(/\[[:^|]/y, beforeNonblank),
|
|
18
|
-
precedence(1, recursion(Recursion.
|
|
18
|
+
precedence(1, recursion(Recursion.bracket,
|
|
19
19
|
some(union([inline]), ']', [[']', 1]]))),
|
|
20
20
|
str(']'),
|
|
21
21
|
false,
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import { InsertionParser } from '../inline';
|
|
2
2
|
import { Recursion, Command } from '../context';
|
|
3
3
|
import { List, Node } from '../../combinator/data/parser';
|
|
4
|
-
import { union, some,
|
|
4
|
+
import { union, some, precedence, surround, open, lazy } from '../../combinator';
|
|
5
5
|
import { inline } from '../inline';
|
|
6
|
+
import { repeat } from '../repeat';
|
|
6
7
|
import { blankWith } from '../visibility';
|
|
7
|
-
import { unwrap
|
|
8
|
+
import { unwrap } from '../util';
|
|
8
9
|
import { html, defrag } from 'typed-dom/dom';
|
|
9
10
|
|
|
10
11
|
export const insertion: InsertionParser = lazy(() =>
|
|
11
|
-
|
|
12
|
+
repeat('++', '', '++', [Recursion.inline], precedence(0, surround(
|
|
12
13
|
'',
|
|
13
14
|
some(union([
|
|
14
15
|
some(inline, blankWith('\n', '++')),
|
|
@@ -18,4 +19,4 @@ export const insertion: InsertionParser = lazy(() =>
|
|
|
18
19
|
false, [],
|
|
19
20
|
([, bs], { buffer }) => buffer.import(bs),
|
|
20
21
|
([, bs], { buffer }) => bs && buffer.import(bs).push(new Node(Command.Cancel)) && buffer)),
|
|
21
|
-
nodes => new List([new Node(html('ins', defrag(unwrap(nodes))))])))
|
|
22
|
+
nodes => new List([new Node(html('ins', defrag(unwrap(nodes))))])));
|
|
@@ -1,21 +1,22 @@
|
|
|
1
1
|
import { ItalicParser } from '../inline';
|
|
2
2
|
import { Recursion, Command } from '../context';
|
|
3
3
|
import { List, Node } from '../../combinator/data/parser';
|
|
4
|
-
import { union, some,
|
|
4
|
+
import { union, some, precedence, surround, lazy } from '../../combinator';
|
|
5
5
|
import { inline } from '../inline';
|
|
6
|
+
import { repeat } from '../repeat';
|
|
6
7
|
import { beforeNonblank, afterNonblank } from '../visibility';
|
|
7
|
-
import { unwrap
|
|
8
|
+
import { unwrap } from '../util';
|
|
8
9
|
import { html, defrag } from 'typed-dom/dom';
|
|
9
10
|
|
|
10
11
|
// 可読性のため実際にはオブリーク体を指定する。
|
|
11
12
|
// 斜体は単語に使うとかえって見づらく読み飛ばしやすくなるため使わないべきであり
|
|
12
13
|
// ある程度の長さのある文に使うのが望ましい。
|
|
13
14
|
export const italic: ItalicParser = lazy(() =>
|
|
14
|
-
|
|
15
|
+
repeat('///', beforeNonblank, '///', [Recursion.inline], precedence(0, surround(
|
|
15
16
|
'',
|
|
16
17
|
some(union([inline]), '///', afterNonblank),
|
|
17
18
|
'///',
|
|
18
19
|
false, [],
|
|
19
20
|
([, bs], { buffer }) => buffer.import(bs),
|
|
20
21
|
([, bs], { buffer }) => bs && buffer.import(bs).push(new Node(Command.Cancel)) && buffer)),
|
|
21
|
-
nodes => new List([new Node(html('i', defrag(unwrap(nodes))))])))
|
|
22
|
+
nodes => new List([new Node(html('i', defrag(unwrap(nodes))))])));
|