securemark 0.293.2 → 0.293.4

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.
Files changed (45) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/README.md +7 -10
  3. package/dist/index.js +349 -167
  4. package/package.json +1 -1
  5. package/src/combinator/control/manipulation/indent.test.ts +6 -1
  6. package/src/combinator/control/manipulation/indent.ts +1 -1
  7. package/src/combinator/control/manipulation/surround.ts +2 -1
  8. package/src/combinator/data/parser/context/delimiter.ts +7 -12
  9. package/src/combinator/data/parser/some.ts +4 -8
  10. package/src/combinator/data/parser.ts +0 -3
  11. package/src/parser/api/bind.ts +1 -1
  12. package/src/parser/api/parse.ts +1 -1
  13. package/src/parser/block/dlist.test.ts +1 -1
  14. package/src/parser/block/heading.test.ts +1 -0
  15. package/src/parser/block/olist.test.ts +9 -6
  16. package/src/parser/block/olist.ts +2 -2
  17. package/src/parser/block/ulist.test.ts +1 -0
  18. package/src/parser/block.ts +38 -36
  19. package/src/parser/inline/annotation.test.ts +1 -1
  20. package/src/parser/inline/bracket.test.ts +4 -2
  21. package/src/parser/inline/bracket.ts +114 -88
  22. package/src/parser/inline/emphasis.test.ts +1 -0
  23. package/src/parser/inline/html.test.ts +3 -3
  24. package/src/parser/inline/html.ts +32 -21
  25. package/src/parser/inline/italic.test.ts +1 -0
  26. package/src/parser/inline/link.test.ts +10 -8
  27. package/src/parser/inline/link.ts +18 -18
  28. package/src/parser/inline/mark.test.ts +1 -0
  29. package/src/parser/inline/math.ts +2 -2
  30. package/src/parser/inline/media.test.ts +6 -7
  31. package/src/parser/inline/media.ts +6 -5
  32. package/src/parser/inline/reference.test.ts +1 -1
  33. package/src/parser/inline/remark.test.ts +3 -1
  34. package/src/parser/inline/remark.ts +2 -2
  35. package/src/parser/inline/strong.test.ts +1 -0
  36. package/src/parser/inline/template.ts +1 -1
  37. package/src/parser/inline.test.ts +16 -16
  38. package/src/parser/inline.ts +46 -47
  39. package/src/parser/segment.ts +12 -12
  40. package/src/parser/source/escapable.test.ts +1 -0
  41. package/src/parser/source/escapable.ts +3 -9
  42. package/src/parser/source/text.test.ts +5 -4
  43. package/src/parser/source/text.ts +175 -24
  44. package/src/parser/source/unescapable.test.ts +1 -0
  45. package/src/parser/source/unescapable.ts +2 -3
@@ -53,70 +53,69 @@ export const inline: InlineParser = lazy(() => union([
53
53
  input => {
54
54
  const { context: { source, position } } = input;
55
55
  if (position === source.length) return;
56
- switch (source.slice(position, position + 2)) {
57
- case '((':
58
- return annotation(input)
59
- || bracket(input);
60
- case '[[':
61
- return reference(input)
62
- || textlink(input)
63
- || bracket(input);
64
- case '{{':
65
- return template(input)
66
- || bracket(input);
67
- case '[%':
68
- return remark(input)
69
- || textlink(input)
70
- || bracket(input);
71
- case '[#':
72
- case '[$':
73
- case '[:':
74
- case '[^':
75
- case '[|':
76
- return extension(input)
77
- || textlink(input)
78
- || bracket(input);
79
- case '${':
80
- return math(input);
81
- case '++':
82
- return insertion(input);
83
- case '~~':
84
- return deletion(input);
85
- case '==':
86
- return mark(input);
87
- case '//':
88
- return italic(input);
89
- case '**':
90
- return emstrong(input)
91
- || strong(input)
92
- || stars(input);
93
- }
94
56
  switch (source[position]) {
57
+ case '(':
58
+ if (source[position + 1] === '(') return annotation(input) || bracket(input);
59
+ return bracket(input);
95
60
  case '[':
61
+ switch (source[position + 1]) {
62
+ case '[':
63
+ return reference(input)
64
+ || textlink(input)
65
+ || bracket(input);
66
+ case '%':
67
+ return remark(input)
68
+ || textlink(input)
69
+ || bracket(input);
70
+ case '#':
71
+ case '$':
72
+ case ':':
73
+ case '^':
74
+ case '|':
75
+ return extension(input)
76
+ || textlink(input)
77
+ || bracket(input);
78
+ }
96
79
  return textlink(input)
97
80
  || ruby(input)
98
81
  || bracket(input);
99
82
  case '{':
83
+ if (source[position + 1] === '{') return template(input) || bracket(input);
100
84
  return textlink(input)
101
85
  || bracket(input);
86
+ case '"':
87
+ case '(':
88
+ case '[':
89
+ case '{':
90
+ return bracket(input);
102
91
  case '<':
103
92
  return html(input);
104
93
  case '$':
94
+ if (source[position + 1] === '{') return math(input);
105
95
  return extension(input)
106
96
  || math(input);
97
+ case '+':
98
+ if (source[position + 1] === '+') return insertion(input);
99
+ break;
100
+ case '~':
101
+ if (source[position + 1] === '~') return deletion(input);
102
+ break;
103
+ case '=':
104
+ if (source[position + 1] === '=') return mark(input);
105
+ break;
106
+ case '/':
107
+ if (source[position + 1] === '/' && source[position + 2] === '/') return italic(input);
108
+ break;
109
+ case '*':
110
+ return source[position + 1] === '*'
111
+ ? source[position + 2] === '*'
112
+ ? emstrong(input) || stars(input)
113
+ : strong(input) || stars(input)
114
+ : emphasis(input);
107
115
  case '`':
108
116
  return code(input);
109
- case '*':
110
- return emphasis(input)
111
- || stars(input);
112
117
  case '&':
113
118
  return htmlentity(input);
114
- case '(':
115
- case '(':
116
- case '[':
117
- case '{':
118
- case '"':
119
- return bracket(input);
120
119
  }
121
120
  },
122
121
  autolink,
@@ -17,19 +17,19 @@ const parser: SegmentParser = union([
17
17
  input => {
18
18
  const { context: { source, position } } = input;
19
19
  if (position === source.length) return;
20
- switch (source.slice(position, position + 3)) {
21
- case '~~~':
22
- return extension(input);
23
- case '```':
24
- return codeblock(input);
25
- }
26
- switch (source.slice(position, position + 2)) {
27
- case '$$':
28
- return mathblock(input);
29
- case '[$':
30
- return extension(input);
31
- }
32
20
  switch (source[position]) {
21
+ case '`':
22
+ if (source.startsWith('```', position)) return codeblock(input);
23
+ break;
24
+ case '~':
25
+ if (source.startsWith('~~~', position)) return extension(input);
26
+ break;
27
+ case '$':
28
+ if (source[position + 1] === '$') return mathblock(input);
29
+ break;
30
+ case '[':
31
+ if (source[position + 1] === '$') return extension(input);
32
+ break;
33
33
  case '#':
34
34
  return heading(input);
35
35
  case '$':
@@ -15,6 +15,7 @@ describe('Unit: parser/source/escsource', () => {
15
15
  it('basic', () => {
16
16
  assert.deepStrictEqual(inspect(parser('a'), ctx), [['a'], '']);
17
17
  assert.deepStrictEqual(inspect(parser('ab'), ctx), [['ab'], '']);
18
+ assert.deepStrictEqual(inspect(parser('a b c'), ctx), [['a b c'], '']);
18
19
  assert.deepStrictEqual(inspect(parser('09あいAZaz'), ctx), [['09あいAZaz'], '']);
19
20
  });
20
21
 
@@ -1,10 +1,10 @@
1
1
  import { EscapableSourceParser } from '../source';
2
2
  import { Command } from '../context';
3
3
  import { consume } from '../../combinator';
4
- import { nonWhitespace, isBlank, next } from './text';
4
+ import { next } from './text';
5
5
  import { html } from 'typed-dom/dom';
6
6
 
7
- const delimiter = /(?=[\\$"`\[\](){}\r\n]|\s(?:\$)|:\/\/)/g;
7
+ const delimiter = /(?=[\\$"`\[\](){}\r\n]|\s\$|:\/\/)/g;
8
8
 
9
9
  export const escsource: EscapableSourceParser = ({ context }) => {
10
10
  const { source, position } = context;
@@ -38,13 +38,7 @@ export const escsource: EscapableSourceParser = ({ context }) => {
38
38
  default:
39
39
  assert(char !== '\n');
40
40
  if (context.sequential) return [[char]];
41
- nonWhitespace.lastIndex = position + 1;
42
- const b = isBlank(source, position);
43
- let i = b
44
- ? nonWhitespace.test(source)
45
- ? nonWhitespace.lastIndex - 1
46
- : source.length
47
- : next(source, position, delimiter);
41
+ let i = next(source, position, delimiter);
48
42
  assert(i > position);
49
43
  i -= position;
50
44
  consume(i - 1, context);
@@ -15,6 +15,7 @@ describe('Unit: parser/source/text', () => {
15
15
  it('basic', () => {
16
16
  assert.deepStrictEqual(inspect(parser('a'), ctx), [['a'], '']);
17
17
  assert.deepStrictEqual(inspect(parser('ab'), ctx), [['ab'], '']);
18
+ assert.deepStrictEqual(inspect(parser('a b c'), ctx), [['a b c'], '']);
18
19
  assert.deepStrictEqual(inspect(parser('09あいAZaz'), ctx), [['09あいAZaz'], '']);
19
20
  assert.deepStrictEqual(inspect(parser('a\nb'), ctx), [['a', '<br>', 'b'], '']);
20
21
  });
@@ -39,8 +40,8 @@ describe('Unit: parser/source/text', () => {
39
40
  assert.deepStrictEqual(inspect(parser(' '), ctx), [[], '']);
40
41
  assert.deepStrictEqual(inspect(parser(' \n'), ctx), [['<br>'], '']);
41
42
  assert.deepStrictEqual(inspect(parser(' \n'), ctx), [['<br>'], '']);
42
- assert.deepStrictEqual(inspect(parser(' \\\n'), ctx), [['<br>'], '']);
43
- assert.deepStrictEqual(inspect(parser(' \\\n'), ctx), [['<br>'], '']);
43
+ assert.deepStrictEqual(inspect(parser(' \\\n'), ctx), [[' ', '<br>'], '']);
44
+ assert.deepStrictEqual(inspect(parser(' \\\n'), ctx), [[' ', ' ', '<br>'], '']);
44
45
  assert.deepStrictEqual(inspect(parser(' a'), ctx), [[' a'], '']);
45
46
  assert.deepStrictEqual(inspect(parser(' a'), ctx), [[' ', ' a'], '']);
46
47
  assert.deepStrictEqual(inspect(parser(' a'), ctx), [[' ', ' a'], '']);
@@ -48,8 +49,8 @@ describe('Unit: parser/source/text', () => {
48
49
  assert.deepStrictEqual(inspect(parser('a '), ctx), [['a'], '']);
49
50
  assert.deepStrictEqual(inspect(parser('a \n'), ctx), [['a', '<br>'], '']);
50
51
  assert.deepStrictEqual(inspect(parser('a \n'), ctx), [['a', '<br>'], '']);
51
- assert.deepStrictEqual(inspect(parser('a \\\n'), ctx), [['a', '<br>'], '']);
52
- assert.deepStrictEqual(inspect(parser('a \\\n'), ctx), [['a', '<br>'], '']);
52
+ assert.deepStrictEqual(inspect(parser('a \\\n'), ctx), [['a', ' ', '<br>'], '']);
53
+ assert.deepStrictEqual(inspect(parser('a \\\n'), ctx), [['a', ' ', '<br>'], '']);
53
54
  assert.deepStrictEqual(inspect(parser('a b'), ctx), [['a b'], '']);
54
55
  assert.deepStrictEqual(inspect(parser('a b'), ctx), [['a', ' b'], '']);
55
56
  assert.deepStrictEqual(inspect(parser('a b'), ctx), [['a', ' b'], '']);
@@ -3,8 +3,8 @@ import { Command } from '../context';
3
3
  import { union, consume, focus } from '../../combinator';
4
4
  import { html } from 'typed-dom/dom';
5
5
 
6
- export const delimiter = /(?=[\\!@#$&"`\[\](){}<>()[]{}*%|\r\n]|([+~=])\1|\/{3}|\s(?:\\?(?:$|\s)|[$%])|:\/\/)/g;
7
- export const nonWhitespace = /[\S\r\n]/g;
6
+ //const delimiter = /(?=[\\!@#$&"`\[\](){}<>()[]{}*%|\r\n]|([+~=])\1|\/{3}|\s(?:\\?(?:$|\s)|[$%])|:\/\/)/g;
7
+ export const nonWhitespace = /[^ \t ]/g;
8
8
 
9
9
  export const text: TextParser = input => {
10
10
  const { context } = input;
@@ -38,27 +38,24 @@ export const text: TextParser = input => {
38
38
  assert(char !== '\n');
39
39
  if (context.sequential) return [[char]];
40
40
  nonWhitespace.lastIndex = position + 1;
41
- const b = isBlank(source, position);
42
- let i = b
41
+ const s = canSkip(source, position);
42
+ let i = s
43
43
  ? nonWhitespace.test(source)
44
44
  ? nonWhitespace.lastIndex - 1
45
45
  : source.length
46
- : next(source, position, delimiter);
46
+ : next(source, position);
47
47
  assert(i > position);
48
48
  const lineend = 0
49
- || b && i === source.length
50
- || b && source[i] === '\n'
51
- || b && source[i] === '\\' && source[i + 1] === '\n';
49
+ || s && i === source.length
50
+ || s && source[i] === '\n';
52
51
  i -= position;
53
- i = lineend ? i : i - +b || 1;
52
+ i = lineend ? i : i - +s || 1;
54
53
  consume(i - 1, context);
55
54
  context.position += i - 1;
56
55
  const linestart = position === 0 || source[position - 1] === '\n';
57
- i = linestart && b && i >= 3 ? i - 3 : 0;
58
- i += position;
59
- return i === context.position || b && !linestart || lineend
56
+ return position === context.position || s && !linestart || lineend
60
57
  ? [[]]
61
- : [[source.slice(i, context.position)]];
58
+ : [[source.slice(position, context.position)]];
62
59
  }
63
60
  };
64
61
 
@@ -70,16 +67,58 @@ export const linebreak: LinebreakParser = focus(/[\r\n]/y, union([
70
67
  text,
71
68
  ])) as LinebreakParser;
72
69
 
73
- export function next(source: string, position: number, delimiter: RegExp): number {
74
- delimiter.lastIndex = position + 1;
75
- delimiter.test(source);
76
- let index = delimiter.lastIndex;
70
+ export function canSkip(source: string, position: number): boolean {
71
+ assert(position < source.length);
72
+ if (!isWhitespace(source[position], false)) return false;
73
+ if (position + 1 === source.length) return true;
74
+ return isWhitespace(source[position + 1], true);
75
+ }
76
+ function isWhitespace(char: string, linebreak: boolean): boolean {
77
+ switch (char) {
78
+ case ' ':
79
+ case '\t':
80
+ case ' ':
81
+ return true;
82
+ case '\r':
83
+ case '\n':
84
+ return linebreak;
85
+ default:
86
+ return false;
87
+ }
88
+ }
89
+
90
+ export function next(source: string, position: number, delimiter?: RegExp): number {
91
+ let index: number;
92
+ if (delimiter) {
93
+ delimiter.lastIndex = position + 1;
94
+ delimiter.test(source);
95
+ index = delimiter.lastIndex;
96
+ }
97
+ else {
98
+ index = seek(source, position);
99
+ }
77
100
  if (index === 0) return source.length;
78
101
  assert(index > position);
79
102
  const char = source[index];
80
103
  switch (char) {
104
+ case '$':
105
+ case '%':
106
+ case '*':
107
+ case '+':
108
+ case '~':
109
+ case '=':
110
+ case '/':
111
+ index = backToWhitespace(source, position, index);
112
+ break;
113
+ case '[':
114
+ index = source[index + 1] === '|'
115
+ ? backToWhitespace(source, position, index)
116
+ : index;
117
+ break;
81
118
  case ':':
82
- index = backToUrlHead(source, position, index);
119
+ index = source.startsWith('//', index + 1)
120
+ ? backToUrlHead(source, position, index)
121
+ : index;
83
122
  break;
84
123
  case '@':
85
124
  index = backToEmailHead(source, position, index);
@@ -88,6 +127,12 @@ export function next(source: string, position: number, delimiter: RegExp): numbe
88
127
  assert(index > position);
89
128
  return index;
90
129
  }
130
+ export function backToWhitespace(source: string, position: number, index: number): number {
131
+ const prev = index - 1;
132
+ return prev > position && /\s/.test(source[prev])
133
+ ? prev
134
+ : index;
135
+ }
91
136
  export function backToUrlHead(source: string, position: number, index: number): number {
92
137
  const delim = index;
93
138
  let state = false;
@@ -143,12 +188,6 @@ export function backToEmailHead(source: string, position: number, index: number)
143
188
  }
144
189
  return index + offset;
145
190
  }
146
-
147
- const blank = /\s(?:$|\s|\\\n)/y;
148
- export function isBlank(source: string, position: number): boolean {
149
- blank.lastIndex = position;
150
- return blank.test(source);
151
- }
152
191
  function isAlphanumeric(char: string): boolean {
153
192
  assert(char.length === 1);
154
193
  if (char < '0' || '\x7F' < char) return false;
@@ -156,3 +195,115 @@ function isAlphanumeric(char: string): boolean {
156
195
  || 'a' <= char && char <= 'z'
157
196
  || 'A' <= char && char <= 'Z';
158
197
  }
198
+
199
+ //const dict = new class {
200
+ // constructor() {
201
+ // [
202
+ // '\\',
203
+ // '!',
204
+ // '@',
205
+ // '#',
206
+ // '$',
207
+ // '&',
208
+ // '"',
209
+ // '`',
210
+ // '[',
211
+ // ']',
212
+ // '(',
213
+ // ')',
214
+ // '{',
215
+ // '}',
216
+ // '<',
217
+ // '>',
218
+ // '(',
219
+ // ')',
220
+ // '[',
221
+ // ']',
222
+ // '{',
223
+ // '}',
224
+ // '*',
225
+ // '%',
226
+ // '|',
227
+ // '\r',
228
+ // '\n',
229
+ // ].forEach(c =>
230
+ // this[c.charCodeAt(0)] = undefined);
231
+ // }
232
+ //};
233
+
234
+ function seek(source: string, position: number): number {
235
+ for (let i = position + 1; i < source.length; ++i) {
236
+ const fst = source[i];
237
+ //if (fst.charCodeAt(0) in dict) return i;
238
+ switch (fst) {
239
+ case '\\':
240
+ case '!':
241
+ case '@':
242
+ case '#':
243
+ case '$':
244
+ case '&':
245
+ case '"':
246
+ case '`':
247
+ case '[':
248
+ case ']':
249
+ case '(':
250
+ case ')':
251
+ case '{':
252
+ case '}':
253
+ case '<':
254
+ case '>':
255
+ case '(':
256
+ case ')':
257
+ case '[':
258
+ case ']':
259
+ case '{':
260
+ case '}':
261
+ case '*':
262
+ case '|':
263
+ case '\r':
264
+ case '\n':
265
+ return i;
266
+ case '+':
267
+ case '~':
268
+ case '=':
269
+ if (source[i + 1] === fst) return i;
270
+ continue;
271
+ case '/':
272
+ if (source[i + 1] === fst && source[i + 2] === fst) return i;
273
+ continue;
274
+ case '%':
275
+ if (source[i + 1] === ']') return i;
276
+ continue;
277
+ case ':':
278
+ if (source[i + 1] === '/' && source[i + 2] === '/') return i;
279
+ continue;
280
+ case ' ':
281
+ case '\t':
282
+ case ' ':
283
+ if (i + 1 === source.length) return i;
284
+ switch (source[i + 1]) {
285
+ case ' ':
286
+ case '\t':
287
+ case '\r':
288
+ case '\n':
289
+ case ' ':
290
+ return i;
291
+ case '\\':
292
+ if (i + 2 === source.length) return i;
293
+ switch (source[i + 2]) {
294
+ case ' ':
295
+ case '\t':
296
+ case '\r':
297
+ case '\n':
298
+ case ' ':
299
+ return i;
300
+ }
301
+ }
302
+ continue;
303
+ default:
304
+ continue;
305
+ }
306
+ assert(false);
307
+ }
308
+ return source.length;
309
+ }
@@ -15,6 +15,7 @@ describe('Unit: parser/source/unescapable', () => {
15
15
  it('basic', () => {
16
16
  assert.deepStrictEqual(inspect(parser('a'), ctx), [['a'], '']);
17
17
  assert.deepStrictEqual(inspect(parser('ab'), ctx), [['ab'], '']);
18
+ assert.deepStrictEqual(inspect(parser('a b c'), ctx), [['a', ' b', ' c'], '']);
18
19
  assert.deepStrictEqual(inspect(parser('09あいAZaz'), ctx), [['09', 'あいAZaz'], '']);
19
20
  });
20
21
 
@@ -1,7 +1,7 @@
1
1
  import { UnescapableSourceParser } from '../source';
2
2
  import { Command } from '../context';
3
3
  import { consume } from '../../combinator';
4
- import { nonWhitespace, isBlank, next } from './text';
4
+ import { nonWhitespace, canSkip, next } from './text';
5
5
  import { html } from 'typed-dom/dom';
6
6
 
7
7
  export const delimiter = /(?=(?=[\x00-\x7F])[^0-9A-Za-z]|(?<=[\x00-\x7F])[^\x00-\x7F])/g;
@@ -28,8 +28,7 @@ export const unescsource: UnescapableSourceParser = ({ context }) => {
28
28
  assert(char !== '\n');
29
29
  if (context.sequential) return [[char]];
30
30
  nonWhitespace.lastIndex = position + 1;
31
- const b = isBlank(source, position);
32
- let i = b
31
+ let i = canSkip(source, position)
33
32
  ? nonWhitespace.test(source)
34
33
  ? nonWhitespace.lastIndex - 1
35
34
  : source.length