securemark 0.296.0 → 0.296.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 +9 -1
- package/dist/index.js +196 -187
- package/package.json +1 -1
- package/src/combinator/control/constraint/block.ts +2 -2
- package/src/combinator/control/constraint/contract.ts +4 -3
- package/src/combinator/control/constraint/line.ts +5 -5
- package/src/combinator/control/manipulation/fence.ts +4 -4
- package/src/combinator/control/manipulation/scope.ts +1 -1
- package/src/combinator/control/manipulation/surround.ts +31 -15
- package/src/combinator/data/delimiter.ts +82 -6
- package/src/combinator/data/parser/context.ts +1 -34
- package/src/parser/api/normalize.ts +5 -1
- package/src/parser/block/reply/cite.ts +1 -1
- package/src/parser/block/reply/quote.ts +1 -1
- package/src/parser/block/reply.ts +1 -1
- package/src/parser/inline/annotation.ts +3 -3
- package/src/parser/inline/deletion.ts +1 -1
- package/src/parser/inline/emphasis.ts +4 -4
- package/src/parser/inline/emstrong.test.ts +1 -0
- package/src/parser/inline/emstrong.ts +3 -3
- package/src/parser/inline/extension/index.ts +2 -3
- package/src/parser/inline/extension/placeholder.ts +2 -2
- package/src/parser/inline/html.ts +3 -3
- package/src/parser/inline/htmlentity.ts +2 -2
- package/src/parser/inline/insertion.ts +1 -1
- package/src/parser/inline/italic.test.ts +1 -0
- package/src/parser/inline/italic.ts +2 -2
- package/src/parser/inline/link.test.ts +0 -1
- package/src/parser/inline/link.ts +3 -3
- package/src/parser/inline/mark.test.ts +1 -0
- package/src/parser/inline/mark.ts +4 -4
- package/src/parser/inline/media.test.ts +0 -1
- package/src/parser/inline/media.ts +1 -1
- package/src/parser/inline/reference.ts +3 -3
- package/src/parser/inline/ruby.test.ts +5 -0
- package/src/parser/inline/ruby.ts +2 -2
- package/src/parser/inline/strong.ts +4 -4
- package/src/parser/node.ts +4 -4
- package/src/parser/source/escapable.ts +2 -1
- package/src/parser/source/str.ts +4 -4
- package/src/parser/source/text.ts +1 -1
- package/src/parser/source/unescapable.ts +2 -1
- package/src/parser/util.ts +7 -4
- package/src/parser/visibility.ts +41 -89
|
@@ -84,7 +84,6 @@ describe('Unit: parser/inline/link', () => {
|
|
|
84
84
|
assert.deepStrictEqual(inspect(parser, input('[\\ ]{b}', new Context())), undefined);
|
|
85
85
|
assert.deepStrictEqual(inspect(parser, input('[\\\n]{b}', new Context())), undefined);
|
|
86
86
|
assert.deepStrictEqual(inspect(parser, input('[	]{b}', new Context())), undefined);
|
|
87
|
-
assert.deepStrictEqual(inspect(parser, input('[‍]{b}', new Context())), undefined);
|
|
88
87
|
assert.deepStrictEqual(inspect(parser, input('[[]{b}', new Context())), undefined);
|
|
89
88
|
assert.deepStrictEqual(inspect(parser, input('[]]{b}', new Context())), undefined);
|
|
90
89
|
assert.deepStrictEqual(inspect(parser, input('[a]{}', new Context())), undefined);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { LinkParser } from '../inline';
|
|
2
2
|
import { Context, State, Backtrack, Command } from '../context';
|
|
3
3
|
import { List, Node } from '../../combinator/data/parser';
|
|
4
|
-
import { union, inits, sequence, subsequence, some, consume, precedence, state, constraint, surround, open, setBacktrack, dup, lazy, fmap, bind } from '../../combinator';
|
|
4
|
+
import { union, inits, sequence, subsequence, some, consume, precedence, state, constraint, surround, open, close, setBacktrack, dup, lazy, fmap, bind } from '../../combinator';
|
|
5
5
|
import { inline, media, shortmedia } from '../inline';
|
|
6
6
|
import { attributes } from './html';
|
|
7
7
|
import { str } from '../source';
|
|
@@ -18,9 +18,9 @@ Object.setPrototypeOf(optspec, null);
|
|
|
18
18
|
export const textlink: LinkParser.TextLinkParser = lazy(() => bind(
|
|
19
19
|
subsequence([
|
|
20
20
|
constraint(State.link, state(State.linkers, dup(surround(
|
|
21
|
-
'[',
|
|
21
|
+
close('[', beforeNonblank),
|
|
22
22
|
precedence(1,
|
|
23
|
-
|
|
23
|
+
some(union([inline]), ']', [[']', 1]])),
|
|
24
24
|
']',
|
|
25
25
|
true,
|
|
26
26
|
[3 | Backtrack.common | Backtrack.link, 2 | Backtrack.ruby],
|
|
@@ -44,6 +44,7 @@ describe('Unit: parser/inline/mark', () => {
|
|
|
44
44
|
assert.deepStrictEqual(inspect(parser, input('==a	==b====', new Context())), [['<mark id="mark::a_b=33Mw2l">a\t<mark>b</mark></mark>', '<a href="#mark::a_b=33Mw2l"></a>'], '']);
|
|
45
45
|
assert.deepStrictEqual(inspect(parser, input('==a<wbr>==b====', new Context())), [['<mark id="mark::ab">a<wbr><mark>b</mark></mark>', '<a href="#mark::ab"></a>'], '']);
|
|
46
46
|
assert.deepStrictEqual(inspect(parser, input('==*==a==*==', new Context())), [['<mark id="mark::a"><em><mark>a</mark></em></mark>', '<a href="#mark::a"></a>'], '']);
|
|
47
|
+
assert.deepStrictEqual(inspect(parser, input('====a== b==', new Context())), [['<mark id="mark::a_b"><mark>a</mark> b</mark>', '<a href="#mark::a_b"></a>'], '']);
|
|
47
48
|
});
|
|
48
49
|
|
|
49
50
|
});
|
|
@@ -9,16 +9,16 @@ import { unwrap, repeat } from '../util';
|
|
|
9
9
|
import { html, define, defrag } from 'typed-dom/dom';
|
|
10
10
|
|
|
11
11
|
export const mark: MarkParser = lazy(() =>
|
|
12
|
-
precedence(0, recursion(Recursion.inline, repeat('==', surround(
|
|
12
|
+
precedence(0, recursion(Recursion.inline, repeat('==', beforeNonblank, surround(
|
|
13
13
|
'',
|
|
14
|
-
|
|
14
|
+
state(State.mark, some(union([inline]), '==', afterNonblank)),
|
|
15
15
|
'==',
|
|
16
16
|
false, [],
|
|
17
17
|
([, bs], { buffer }) => buffer.import(bs),
|
|
18
18
|
([, bs], { buffer }) => bs && buffer.import(bs).push(new Node(Command.Cancel)) && buffer),
|
|
19
|
-
(nodes, { id, state }) => {
|
|
19
|
+
(nodes, { id, state }, nest) => {
|
|
20
20
|
const el = html('mark', defrag(unwrap(nodes)));
|
|
21
|
-
if (state & State.linkers) return new List([new Node(el)]);
|
|
21
|
+
if (state & State.linkers || nest) return new List([new Node(el)]);
|
|
22
22
|
define(el, { id: identity('mark', id, signature(el)) });
|
|
23
23
|
return el.id
|
|
24
24
|
? new List([new Node(el), new Node(html('a', { href: `#${el.id}` }))])
|
|
@@ -49,7 +49,6 @@ describe('Unit: parser/inline/media', () => {
|
|
|
49
49
|
assert.deepStrictEqual(inspect(parser, input('![\\ ]{b}', new Context())), undefined);
|
|
50
50
|
assert.deepStrictEqual(inspect(parser, input('![\\\n]{b}', new Context())), undefined);
|
|
51
51
|
assert.deepStrictEqual(inspect(parser, input('![	]{b}', new Context())), undefined);
|
|
52
|
-
assert.deepStrictEqual(inspect(parser, input('![‍]{b}', new Context())), undefined);
|
|
53
52
|
assert.deepStrictEqual(inspect(parser, input('![&a;]{b}', new Context())), [['<a href="b" target="_blank"><img class="media" data-src="b" alt="&a;"></a>'], '']);
|
|
54
53
|
assert.deepStrictEqual(inspect(parser, input('![[]{b}', new Context())), undefined);
|
|
55
54
|
assert.deepStrictEqual(inspect(parser, input('![]]{b}', new Context())), undefined);
|
|
@@ -53,7 +53,7 @@ export const media: MediaParser = lazy(() => constraint(State.media, open(
|
|
|
53
53
|
? new List<Node<List<Node<string>>>>([new Node(new List([new Node('')])), nodes.delete(nodes.head!)])
|
|
54
54
|
: new List<Node<List<Node<string>>>>([new Node(new List([new Node(nodes.head!.value.foldl((acc, { value }) => acc + value, ''), nodes.head!.value.head?.flags)])), nodes.delete(nodes.last!)])),
|
|
55
55
|
([{ value: [{ value: text, flags }] }, { value: params }], context) => {
|
|
56
|
-
if (flags & Flag.
|
|
56
|
+
if (flags & Flag.blank) return;
|
|
57
57
|
if (text) {
|
|
58
58
|
const tmp = text;
|
|
59
59
|
text = text.trim();
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ReferenceParser } from '../inline';
|
|
2
2
|
import { State, Backtrack, Command } from '../context';
|
|
3
3
|
import { List, Node } from '../../combinator/data/parser';
|
|
4
|
-
import { union, subsequence, some, precedence, state, constraint, surround, isBacktrack, setBacktrack, lazy } from '../../combinator';
|
|
4
|
+
import { union, subsequence, some, precedence, state, constraint, surround, open, isBacktrack, setBacktrack, lazy } from '../../combinator';
|
|
5
5
|
import { inline } from '../inline';
|
|
6
6
|
import { textlink } from './link';
|
|
7
7
|
import { str } from '../source';
|
|
@@ -10,11 +10,11 @@ import { unwrap, invalid } from '../util';
|
|
|
10
10
|
import { html, defrag } from 'typed-dom/dom';
|
|
11
11
|
|
|
12
12
|
export const reference: ReferenceParser = lazy(() => constraint(State.reference, surround(
|
|
13
|
-
|
|
13
|
+
'[[',
|
|
14
14
|
precedence(1, state(State.annotation | State.reference,
|
|
15
15
|
subsequence([
|
|
16
16
|
abbr,
|
|
17
|
-
beforeNonblank
|
|
17
|
+
open(beforeNonblank, some(inline, ']', [[']', 1]])),
|
|
18
18
|
]))),
|
|
19
19
|
']]',
|
|
20
20
|
false,
|
|
@@ -46,7 +46,12 @@ describe('Unit: parser/inline/ruby', () => {
|
|
|
46
46
|
assert.deepStrictEqual(inspect(parser, input('[AB](a b c)', new Context())), [['<ruby>AB<rp>(</rp><rt>a b c</rt><rp>)</rp></ruby>'], '']);
|
|
47
47
|
assert.deepStrictEqual(inspect(parser, input('[A B](ab)', new Context())), [['<ruby>A<rp>(</rp><rt>ab</rt><rp>)</rp>B<rt></rt></ruby>'], '']);
|
|
48
48
|
assert.deepStrictEqual(inspect(parser, input('[A B](a b)', new Context())), [['<ruby>A<rp>(</rp><rt>a</rt><rp>)</rp>B<rp>(</rp><rt>b</rt><rp>)</rp></ruby>'], '']);
|
|
49
|
+
assert.deepStrictEqual(inspect(parser, input('[A B](a b )', new Context())), [['<ruby>A B<rp>(</rp><rt>a b</rt><rp>)</rp></ruby>'], '']);
|
|
50
|
+
assert.deepStrictEqual(inspect(parser, input('[ABC](a )', new Context())), [['<ruby>A<rp>(</rp><rt>a</rt><rp>)</rp>B<rt></rt>C<rt></rt></ruby>'], '']);
|
|
51
|
+
assert.deepStrictEqual(inspect(parser, input('[ABC](a )', new Context())), [['<ruby>A<rp>(</rp><rt>a</rt><rp>)</rp>B<rt></rt>C<rt></rt></ruby>'], '']);
|
|
52
|
+
assert.deepStrictEqual(inspect(parser, input('[ABC]( b)', new Context())), [['<ruby>A<rt></rt>B<rp>(</rp><rt>b</rt><rp>)</rp>C<rt></rt></ruby>'], '']);
|
|
49
53
|
assert.deepStrictEqual(inspect(parser, input('[ABC]( b )', new Context())), [['<ruby>A<rt></rt>B<rp>(</rp><rt>b</rt><rp>)</rp>C<rt></rt></ruby>'], '']);
|
|
54
|
+
assert.deepStrictEqual(inspect(parser, input('[ABC]( c)', new Context())), [['<ruby>A<rt></rt>B<rt></rt>C<rp>(</rp><rt>c</rt><rp>)</rp></ruby>'], '']);
|
|
50
55
|
assert.deepStrictEqual(inspect(parser, input('[ABC](a c)', new Context())), [['<ruby>A<rp>(</rp><rt>a</rt><rp>)</rp>B<rt></rt>C<rp>(</rp><rt>c</rt><rp>)</rp></ruby>'], '']);
|
|
51
56
|
assert.deepStrictEqual(inspect(parser, input('[東方](とう ほう)', new Context())), [['<ruby>東<rp>(</rp><rt>とう</rt><rp>)</rp>方<rp>(</rp><rt>ほう</rt><rp>)</rp></ruby>'], '']);
|
|
52
57
|
assert.deepStrictEqual(inspect(parser, input('[秦 \\ こころ](はた の こころ)', new Context())), [['<ruby>秦<rp>(</rp><rt>はた</rt><rp>)</rp> <rp>(</rp><rt>の</rt><rp>)</rp>こころ<rp>(</rp><rt>こころ</rt><rp>)</rp></ruby>'], '']);
|
|
@@ -4,7 +4,7 @@ import { List, Node } from '../../combinator/data/parser';
|
|
|
4
4
|
import { inits, surround, setBacktrack, dup, lazy, bind } from '../../combinator';
|
|
5
5
|
import { unsafehtmlentity } from './htmlentity';
|
|
6
6
|
import { txt } from '../source';
|
|
7
|
-
import {
|
|
7
|
+
import { isNonblankNodeStart } from '../visibility';
|
|
8
8
|
import { unwrap } from '../util';
|
|
9
9
|
import { html, defrag } from 'typed-dom/dom';
|
|
10
10
|
|
|
@@ -16,7 +16,7 @@ export const ruby: RubyParser = lazy(() => bind(
|
|
|
16
16
|
[1 | Backtrack.common, 3 | Backtrack.ruby],
|
|
17
17
|
([, ns]) => {
|
|
18
18
|
ns && ns.last?.value === '' && ns.pop();
|
|
19
|
-
return
|
|
19
|
+
return isNonblankNodeStart(ns) ? ns : undefined;
|
|
20
20
|
})),
|
|
21
21
|
dup(surround(
|
|
22
22
|
'(', text, ')',
|
|
@@ -5,17 +5,17 @@ import { union, some, recursion, precedence, surround, lazy } from '../../combin
|
|
|
5
5
|
import { inline } from '../inline';
|
|
6
6
|
import { emphasis } from './emphasis';
|
|
7
7
|
import { str } from '../source';
|
|
8
|
-
import {
|
|
8
|
+
import { beforeNonblankWith, afterNonblank } from '../visibility';
|
|
9
9
|
import { unwrap } from '../util';
|
|
10
10
|
import { html, defrag } from 'typed-dom/dom';
|
|
11
11
|
|
|
12
12
|
export const strong: StrongParser = lazy(() => surround(
|
|
13
|
-
str('**', (
|
|
13
|
+
str('**', beforeNonblankWith(/(?!\*)/)),
|
|
14
14
|
precedence(0, recursion(Recursion.inline,
|
|
15
|
-
|
|
15
|
+
some(union([
|
|
16
16
|
some(inline, '*', afterNonblank),
|
|
17
17
|
emphasis,
|
|
18
|
-
]))))
|
|
18
|
+
])))),
|
|
19
19
|
str('**'),
|
|
20
20
|
false, [],
|
|
21
21
|
([, bs]) => new List([new Node(html('strong', defrag(unwrap(bs))))]),
|
package/src/parser/node.ts
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { invisibleBlankHTMLEntityNames } from './api/normalize';
|
|
2
2
|
|
|
3
3
|
export const enum Flag {
|
|
4
4
|
none,
|
|
5
|
-
|
|
5
|
+
blank,
|
|
6
6
|
}
|
|
7
7
|
|
|
8
|
-
export const
|
|
8
|
+
export const isBlankHTMLEntityName: (name: string) => boolean = eval([
|
|
9
9
|
'name => {',
|
|
10
10
|
'switch(name){',
|
|
11
|
-
|
|
11
|
+
invisibleBlankHTMLEntityNames.map(name => `case '${name}':`).join(''),
|
|
12
12
|
'return true;',
|
|
13
13
|
'default:',
|
|
14
14
|
'return false;',
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { EscapableSourceParser } from '../source';
|
|
2
2
|
import { Command } from '../context';
|
|
3
|
+
import { Flag } from '../node';
|
|
3
4
|
import { List, Node } from '../../combinator/data/parser';
|
|
4
5
|
import { consume } from '../../combinator';
|
|
5
6
|
import { next } from './text';
|
|
@@ -34,7 +35,7 @@ export const escsource: EscapableSourceParser = ({ context }) => {
|
|
|
34
35
|
return new List();
|
|
35
36
|
case '\n':
|
|
36
37
|
context.linebreak ||= source.length - position;
|
|
37
|
-
return new List([new Node(html('br'))]);
|
|
38
|
+
return new List([new Node(html('br'), Flag.blank)]);
|
|
38
39
|
default:
|
|
39
40
|
assert(char !== '\n');
|
|
40
41
|
if (context.sequential) return new List([new Node(char)]);
|
package/src/parser/source/str.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { StrParser } from '../source';
|
|
2
2
|
import { Parser, List, Node } from '../../combinator/data/parser';
|
|
3
|
-
import { matcher } from '../../combinator';
|
|
3
|
+
import { matcher, tester } from '../../combinator/data/delimiter';
|
|
4
4
|
|
|
5
|
-
export function str(pattern: string | RegExp,
|
|
6
|
-
export function str(pattern: string | RegExp,
|
|
7
|
-
return matcher(pattern, true,
|
|
5
|
+
export function str(pattern: string | RegExp, after?: string | RegExp): StrParser;
|
|
6
|
+
export function str(pattern: string | RegExp, after?: string | RegExp): Parser<string> {
|
|
7
|
+
return matcher(pattern, true, after ? tester(after, false) : undefined);
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
export function strs(pattern: string, limit?: number): StrParser;
|
|
@@ -33,7 +33,7 @@ export const text: TextParser = input => {
|
|
|
33
33
|
return new List();
|
|
34
34
|
case '\n':
|
|
35
35
|
context.linebreak ||= source.length - position;
|
|
36
|
-
return new List([new Node(html('br'), Flag.
|
|
36
|
+
return new List([new Node(html('br'), Flag.blank)]);
|
|
37
37
|
default:
|
|
38
38
|
assert(char !== '\n');
|
|
39
39
|
if (context.sequential) return new List([new Node(char)]);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { UnescapableSourceParser } from '../source';
|
|
2
2
|
import { Command } from '../context';
|
|
3
|
+
import { Flag } from '../node';
|
|
3
4
|
import { List, Node } from '../../combinator/data/parser';
|
|
4
5
|
import { consume } from '../../combinator';
|
|
5
6
|
import { nonWhitespace, canSkip, next } from './text';
|
|
@@ -22,7 +23,7 @@ export const unescsource: UnescapableSourceParser = ({ context }) => {
|
|
|
22
23
|
return new List();
|
|
23
24
|
case '\n':
|
|
24
25
|
context.linebreak ||= source.length - position;
|
|
25
|
-
return new List([new Node(html('br'))]);
|
|
26
|
+
return new List([new Node(html('br'), Flag.blank)]);
|
|
26
27
|
default:
|
|
27
28
|
assert(char !== '\n');
|
|
28
29
|
if (context.sequential) return new List([new Node(char)]);
|
package/src/parser/util.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Parser, Result, List, Node, failsafe } from '../combinator/data/parser';
|
|
2
|
+
import { tester } from '../combinator/data/delimiter';
|
|
2
3
|
import { Context, Command } from './context';
|
|
3
4
|
import { min } from 'spica/alias';
|
|
4
5
|
import { define } from 'typed-dom/dom';
|
|
@@ -10,8 +11,8 @@ export function* unwrap<N>(nodes: List<Node<N>> | undefined): Iterable<N> {
|
|
|
10
11
|
}
|
|
11
12
|
}
|
|
12
13
|
|
|
13
|
-
export function repeat<P extends Parser<HTMLElement | string, Context>>(symbol: string, parser: P, cons: (nodes: List<Node<Parser.Node<P>>>, context: Parser.Context<P
|
|
14
|
-
export function repeat<N extends HTMLElement | string>(symbol: string, parser: Parser<N>, cons: (nodes: List<Node<N>>, context: Context) => List<Node<N>>, termination: (acc: List<Node<N>>, context: Context, prefix: number, postfix: number, state: boolean) => Result<string | N, Context> = (nodes, context, prefix, postfix) => {
|
|
14
|
+
export function repeat<P extends Parser<HTMLElement | string, Context>>(symbol: string, after: string | RegExp, parser: P, cons: (nodes: List<Node<Parser.Node<P>>>, context: Parser.Context<P>, nest: boolean) => List<Node<Parser.Node<P>>>, termination?: (acc: List<Node<Parser.Node<P>>>, context: Context, prefix: number, postfix: number, state: boolean) => Result<string | Parser.Node<P>>): P;
|
|
15
|
+
export function repeat<N extends HTMLElement | string>(symbol: string, after: string | RegExp, parser: Parser<N>, cons: (nodes: List<Node<N>>, context: Context, nest: boolean) => List<Node<N>>, termination: (acc: List<Node<N>>, context: Context, prefix: number, postfix: number, state: boolean) => Result<string | N, Context> = (nodes, context, prefix, postfix) => {
|
|
15
16
|
const acc = new List<Node<string | N>>();
|
|
16
17
|
if (prefix > 0) {
|
|
17
18
|
acc.push(new Node(symbol[0].repeat(prefix)));
|
|
@@ -24,6 +25,7 @@ export function repeat<N extends HTMLElement | string>(symbol: string, parser: P
|
|
|
24
25
|
}
|
|
25
26
|
return acc;
|
|
26
27
|
}): Parser<string | N, Context> {
|
|
28
|
+
const test = tester(after, false);
|
|
27
29
|
return failsafe(input => {
|
|
28
30
|
const { context } = input;
|
|
29
31
|
const { source, position } = context;
|
|
@@ -32,10 +34,11 @@ export function repeat<N extends HTMLElement | string>(symbol: string, parser: P
|
|
|
32
34
|
let i = symbol.length;
|
|
33
35
|
for (; source[context.position + i] === source[context.position];) ++i;
|
|
34
36
|
context.position += i;
|
|
37
|
+
if (test(input) === undefined) return;
|
|
35
38
|
let state = false;
|
|
36
39
|
for (; i >= symbol.length; i -= symbol.length) {
|
|
37
40
|
if (nodes.length > 0 && source.startsWith(symbol, context.position)) {
|
|
38
|
-
nodes = cons(nodes, context);
|
|
41
|
+
nodes = cons(nodes, context, i > symbol.length);
|
|
39
42
|
context.position += symbol.length;
|
|
40
43
|
continue;
|
|
41
44
|
}
|
|
@@ -57,7 +60,7 @@ export function repeat<N extends HTMLElement | string>(symbol: string, parser: P
|
|
|
57
60
|
state = true;
|
|
58
61
|
continue;
|
|
59
62
|
default:
|
|
60
|
-
nodes = cons(nodes, context);
|
|
63
|
+
nodes = cons(nodes, context, i > symbol.length);
|
|
61
64
|
state = true;
|
|
62
65
|
continue;
|
|
63
66
|
}
|
package/src/parser/visibility.ts
CHANGED
|
@@ -1,103 +1,73 @@
|
|
|
1
|
-
import { Parser,
|
|
2
|
-
import {
|
|
1
|
+
import { Parser, List, Node, failsafe } from '../combinator/data/parser';
|
|
2
|
+
import { Command } from './context';
|
|
3
3
|
import { Flag } from './node';
|
|
4
4
|
import { convert, fmap } from '../combinator';
|
|
5
|
-
import {
|
|
5
|
+
import { invisibleBlankHTMLEntityNames } from './api/normalize';
|
|
6
6
|
|
|
7
|
-
namespace
|
|
7
|
+
namespace blank {
|
|
8
8
|
export const line = new RegExp(
|
|
9
|
-
/((?:^|\n)[^\S\n]*(?=\S))((?:[^\S\n]|\\(?=$|\s)|&
|
|
10
|
-
.replace('
|
|
9
|
+
/((?:^|\n)[^\S\n]*(?=\S))((?:[^\S\n]|\\(?=$|\s)|&IBHN;|<wbr ?>)+(?=$|\n))/g.source
|
|
10
|
+
.replace('IBHN', `(?:${invisibleBlankHTMLEntityNames.join('|')})`),
|
|
11
11
|
'g');
|
|
12
12
|
export const start = new RegExp(
|
|
13
|
-
/(?:[^\S\n]|\\(?=$|\s)|&
|
|
14
|
-
.replace('
|
|
13
|
+
/(?:[^\S\n]|\\(?=$|\s)|&IBHN;|<wbr ?>)+/y.source
|
|
14
|
+
.replace('IBHN', `(?:${invisibleBlankHTMLEntityNames.join('|')})`),
|
|
15
15
|
'y');
|
|
16
16
|
export const unit = new RegExp(
|
|
17
|
-
/(?:[^\S\n]|\\(?=$|\s)|&
|
|
18
|
-
.replace('
|
|
17
|
+
/(?:[^\S\n]|\\(?=$|\s)|&IBHN;|<wbr ?>)/y.source
|
|
18
|
+
.replace('IBHN', `(?:${invisibleBlankHTMLEntityNames.join('|')})`),
|
|
19
19
|
'y');
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
export function visualize<P extends Parser>(parser: P): P {
|
|
23
23
|
return convert(
|
|
24
|
-
source => source.replace(
|
|
24
|
+
source => source.replace(blank.line, `$1${Command.Escape}$2`),
|
|
25
25
|
parser);
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
export const
|
|
28
|
+
export const beforeNonblank = beforeNonblankWith('');
|
|
29
|
+
export const afterNonblank = afterNonblankWith('');
|
|
29
30
|
export function blankWith(starts: '\n', delimiter: string | RegExp): RegExp {
|
|
30
31
|
return new RegExp([
|
|
31
32
|
// 空行除去
|
|
32
33
|
// 完全な空行はエスケープ済みなので再帰的バックトラックにはならない。
|
|
33
|
-
String.raw`(?:${starts}(?:\\?\s|&(?:${
|
|
34
|
+
String.raw`(?:${starts}(?:\\?\s|&(?:${invisibleBlankHTMLEntityNames.join('|')});|<wbr ?>)*)?`,
|
|
34
35
|
typeof delimiter === 'string'
|
|
35
36
|
? delimiter.replace(/[*+()\[\]]/g, '\\$&')
|
|
36
37
|
: delimiter.source,
|
|
37
38
|
].join(''), 'y');
|
|
38
39
|
}
|
|
39
|
-
function
|
|
40
|
+
export function beforeNonblankWith(delimiter: string | RegExp): RegExp {
|
|
40
41
|
return new RegExp([
|
|
41
|
-
String.raw`(?<!\s|&(?:${invisibleHTMLEntityNames.join('|')});|<wbr ?>)`,
|
|
42
42
|
typeof delimiter === 'string'
|
|
43
43
|
? delimiter.replace(/[*+()\[\]]/g, '\\$&')
|
|
44
44
|
: delimiter.source,
|
|
45
|
+
String.raw`(?!\\?\s|&(?:${invisibleBlankHTMLEntityNames.join('|')});|<wbr ?>)`,
|
|
45
46
|
].join(''), 'y');
|
|
46
47
|
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
//}
|
|
55
|
-
//const isLooseStart = reduce(({ source, context }: Input<Context>): boolean => {
|
|
56
|
-
// return isTightStart({ source: source.replace(invisible.start, ''), context });
|
|
57
|
-
//}, ({ source }) => `${source}${Command.Separator}`);
|
|
58
|
-
|
|
59
|
-
export function beforeNonblank<P extends Parser>(parser: P): P;
|
|
60
|
-
export function beforeNonblank<N>(parser: Parser<N>): Parser<N, Context> {
|
|
61
|
-
return input =>
|
|
62
|
-
isTightStart(input)
|
|
63
|
-
? parser(input)
|
|
64
|
-
: undefined;
|
|
65
|
-
}
|
|
66
|
-
function isTightStart(input: Input<Context>): boolean {
|
|
67
|
-
const { context } = input;
|
|
68
|
-
const { source, position } = context;
|
|
69
|
-
if (position === source.length) return true;
|
|
70
|
-
switch (source[position]) {
|
|
71
|
-
case ' ':
|
|
72
|
-
case ' ':
|
|
73
|
-
case '\t':
|
|
74
|
-
case '\n':
|
|
75
|
-
return false;
|
|
76
|
-
default:
|
|
77
|
-
const reg = invisible.unit;
|
|
78
|
-
reg.lastIndex = position;
|
|
79
|
-
return !reg.test(source);
|
|
80
|
-
}
|
|
48
|
+
function afterNonblankWith(delimiter: string | RegExp): RegExp {
|
|
49
|
+
return new RegExp([
|
|
50
|
+
String.raw`(?<!\s|&(?:${invisibleBlankHTMLEntityNames.join('|')});|<wbr ?>)`,
|
|
51
|
+
typeof delimiter === 'string'
|
|
52
|
+
? delimiter.replace(/[*+()\[\]]/g, '\\$&')
|
|
53
|
+
: delimiter.source,
|
|
54
|
+
].join(''), 'y');
|
|
81
55
|
}
|
|
82
56
|
|
|
83
|
-
export function
|
|
57
|
+
export function isNonblankFirstLine(nodes: List<Node<HTMLElement | string>>): boolean {
|
|
84
58
|
if (nodes.length === 0) return true;
|
|
85
59
|
for (const node of nodes) {
|
|
86
|
-
if (
|
|
87
|
-
if (typeof node.value === 'object' && node.value.tagName === 'BR') break;
|
|
60
|
+
if (isNonblank(node)) return true;
|
|
61
|
+
if (node.flags & Flag.blank && typeof node.value === 'object' && node.value.tagName === 'BR') break;
|
|
88
62
|
}
|
|
89
63
|
return false;
|
|
90
64
|
}
|
|
91
|
-
export function
|
|
65
|
+
export function isNonblankNodeStart(nodes: List<Node<HTMLElement | string>>): boolean {
|
|
92
66
|
if (nodes.length === 0) return true;
|
|
93
|
-
return
|
|
67
|
+
return isNonblank(nodes.head!, 0);
|
|
94
68
|
}
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
// return isVisible(nodes.at(-1)!, -1);
|
|
98
|
-
//}
|
|
99
|
-
function isVisible({ value: node, flags }: Node<HTMLElement | string>, strpos?: number): boolean {
|
|
100
|
-
if (flags & Flag.invisible) return false;
|
|
69
|
+
function isNonblank({ value: node, flags }: Node<HTMLElement | string>, strpos?: number): boolean {
|
|
70
|
+
if (flags & Flag.blank) return false;
|
|
101
71
|
if (typeof node !== 'string') return true;
|
|
102
72
|
const str = node && strpos !== undefined
|
|
103
73
|
? node[strpos >= 0 ? strpos : node.length + strpos]
|
|
@@ -124,7 +94,7 @@ function trimBlankStart<N>(parser: Parser<N>): Parser<N> {
|
|
|
124
94
|
const { context } = input;
|
|
125
95
|
const { source, position } = context;
|
|
126
96
|
if (position === source.length) return;
|
|
127
|
-
const reg =
|
|
97
|
+
const reg = blank.start;
|
|
128
98
|
reg.lastIndex = position;
|
|
129
99
|
reg.test(source);
|
|
130
100
|
context.position = reg.lastIndex || position;
|
|
@@ -137,41 +107,23 @@ export function trimBlankEnd<P extends Parser<HTMLElement | string>>(parser: P):
|
|
|
137
107
|
export function trimBlankEnd<N extends HTMLElement>(parser: Parser<N>): Parser<string | N> {
|
|
138
108
|
return fmap(parser, trimBlankNodeEnd);
|
|
139
109
|
}
|
|
140
|
-
//export function trimBlankNode<N extends HTMLElement | string>(nodes: N[]): N[] {
|
|
141
|
-
// return trimBlankNodeStart(trimBlankNodeEnd(nodes));
|
|
142
|
-
//}
|
|
143
|
-
//function trimBlankNodeStart<N extends HTMLElement | string>(nodes: N[]): N[] {
|
|
144
|
-
// for (let node = nodes[0]; nodes.length > 0 && !isVisible(node = nodes[0], 0);) {
|
|
145
|
-
// if (typeof node === 'string') {
|
|
146
|
-
// const pos = node.trimStart().length;
|
|
147
|
-
// if (pos > 0) {
|
|
148
|
-
// nodes[0] = node.slice(-pos) as N;
|
|
149
|
-
// break;
|
|
150
|
-
// }
|
|
151
|
-
// }
|
|
152
|
-
// else if (nodes.length === 1 && node.className === 'indexer') {
|
|
153
|
-
// break;
|
|
154
|
-
// }
|
|
155
|
-
// nodes.shift();
|
|
156
|
-
// }
|
|
157
|
-
// return nodes;
|
|
158
|
-
//}
|
|
159
110
|
export function trimBlankNodeEnd<N extends HTMLElement>(nodes: List<Node<string | N>>): List<Node<string | N>> {
|
|
160
|
-
const skip = nodes.last && ~nodes.last.flags & Flag.
|
|
111
|
+
const skip = nodes.last && ~nodes.last.flags & Flag.blank && typeof nodes.last.value === 'object'
|
|
161
112
|
? nodes.last.value.className === 'indexer'
|
|
162
113
|
: false;
|
|
163
114
|
for (let node = skip ? nodes.last?.prev : nodes.last; node;) {
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
115
|
+
if (~node.flags & Flag.blank) {
|
|
116
|
+
if (typeof node.value === 'string') {
|
|
117
|
+
const str = node.value.trimEnd();
|
|
118
|
+
if (str.length > 0) {
|
|
119
|
+
node.value = str;
|
|
120
|
+
break;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
169
124
|
break;
|
|
170
125
|
}
|
|
171
126
|
}
|
|
172
|
-
else if (visible) {
|
|
173
|
-
break;
|
|
174
|
-
}
|
|
175
127
|
const target = node;
|
|
176
128
|
node = node.prev;
|
|
177
129
|
nodes.delete(target);
|