@yozora/tokenizer-link 1.2.0 → 2.0.0-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/cjs/index.js +154 -139
- package/lib/esm/index.js +154 -139
- package/lib/types/index.d.ts +3 -5
- package/lib/types/tokenizer.d.ts +6 -32
- package/lib/types/types.d.ts +12 -12
- package/lib/types/util/{link-text.d.ts → check-brackets.d.ts} +3 -3
- package/lib/types/util/link-destination.d.ts +2 -2
- package/lib/types/util/link-title.d.ts +2 -2
- package/package.json +5 -5
package/lib/cjs/index.js
CHANGED
|
@@ -2,13 +2,50 @@
|
|
|
2
2
|
|
|
3
3
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
4
|
|
|
5
|
-
var ast = require('@yozora/ast');
|
|
6
5
|
var character = require('@yozora/character');
|
|
7
6
|
var coreTokenizer = require('@yozora/core-tokenizer');
|
|
7
|
+
var ast = require('@yozora/ast');
|
|
8
8
|
|
|
9
|
-
const
|
|
9
|
+
const checkBalancedBracketsStatus = (startIndex, endIndex, internalTokens, nodePoints) => {
|
|
10
|
+
let i = startIndex;
|
|
11
|
+
let bracketCount = 0;
|
|
12
|
+
const updateBracketCount = () => {
|
|
13
|
+
const c = nodePoints[i].codePoint;
|
|
14
|
+
switch (c) {
|
|
15
|
+
case character.AsciiCodePoint.BACKSLASH:
|
|
16
|
+
i += 1;
|
|
17
|
+
break;
|
|
18
|
+
case character.AsciiCodePoint.OPEN_BRACKET:
|
|
19
|
+
bracketCount += 1;
|
|
20
|
+
break;
|
|
21
|
+
case character.AsciiCodePoint.CLOSE_BRACKET:
|
|
22
|
+
bracketCount -= 1;
|
|
23
|
+
break;
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
for (const token of internalTokens) {
|
|
27
|
+
if (token.startIndex < startIndex)
|
|
28
|
+
continue;
|
|
29
|
+
if (token.endIndex > endIndex)
|
|
30
|
+
break;
|
|
31
|
+
for (; i < token.startIndex; ++i) {
|
|
32
|
+
updateBracketCount();
|
|
33
|
+
if (bracketCount < 0)
|
|
34
|
+
return -1;
|
|
35
|
+
}
|
|
36
|
+
i = token.endIndex;
|
|
37
|
+
}
|
|
38
|
+
for (; i < endIndex; ++i) {
|
|
39
|
+
updateBracketCount();
|
|
40
|
+
if (bracketCount < 0)
|
|
41
|
+
return -1;
|
|
42
|
+
}
|
|
43
|
+
return bracketCount > 0 ? 1 : 0;
|
|
44
|
+
};
|
|
10
45
|
|
|
11
46
|
function eatLinkDestination(nodePoints, startIndex, endIndex) {
|
|
47
|
+
if (startIndex >= endIndex)
|
|
48
|
+
return -1;
|
|
12
49
|
let i = startIndex;
|
|
13
50
|
switch (nodePoints[i].codePoint) {
|
|
14
51
|
case character.AsciiCodePoint.OPEN_ANGLE: {
|
|
@@ -54,44 +91,9 @@ function eatLinkDestination(nodePoints, startIndex, endIndex) {
|
|
|
54
91
|
}
|
|
55
92
|
}
|
|
56
93
|
|
|
57
|
-
const checkBalancedBracketsStatus = (startIndex, endIndex, internalTokens, nodePoints) => {
|
|
58
|
-
let i = startIndex;
|
|
59
|
-
let bracketCount = 0;
|
|
60
|
-
const updateBracketCount = () => {
|
|
61
|
-
const c = nodePoints[i].codePoint;
|
|
62
|
-
switch (c) {
|
|
63
|
-
case character.AsciiCodePoint.BACKSLASH:
|
|
64
|
-
i += 1;
|
|
65
|
-
break;
|
|
66
|
-
case character.AsciiCodePoint.OPEN_BRACKET:
|
|
67
|
-
bracketCount += 1;
|
|
68
|
-
break;
|
|
69
|
-
case character.AsciiCodePoint.CLOSE_BRACKET:
|
|
70
|
-
bracketCount -= 1;
|
|
71
|
-
break;
|
|
72
|
-
}
|
|
73
|
-
};
|
|
74
|
-
for (const token of internalTokens) {
|
|
75
|
-
if (token.startIndex < startIndex)
|
|
76
|
-
continue;
|
|
77
|
-
if (token.endIndex > endIndex)
|
|
78
|
-
break;
|
|
79
|
-
for (; i < token.startIndex; ++i) {
|
|
80
|
-
updateBracketCount();
|
|
81
|
-
if (bracketCount < 0)
|
|
82
|
-
return -1;
|
|
83
|
-
}
|
|
84
|
-
i = token.endIndex;
|
|
85
|
-
}
|
|
86
|
-
for (; i < endIndex; ++i) {
|
|
87
|
-
updateBracketCount();
|
|
88
|
-
if (bracketCount < 0)
|
|
89
|
-
return -1;
|
|
90
|
-
}
|
|
91
|
-
return bracketCount > 0 ? 1 : 0;
|
|
92
|
-
};
|
|
93
|
-
|
|
94
94
|
function eatLinkTitle(nodePoints, startIndex, endIndex) {
|
|
95
|
+
if (startIndex >= endIndex)
|
|
96
|
+
return -1;
|
|
95
97
|
let i = startIndex;
|
|
96
98
|
const titleWrapSymbol = nodePoints[i].codePoint;
|
|
97
99
|
switch (titleWrapSymbol) {
|
|
@@ -149,6 +151,8 @@ function eatLinkTitle(nodePoints, startIndex, endIndex) {
|
|
|
149
151
|
return -1;
|
|
150
152
|
}
|
|
151
153
|
|
|
154
|
+
const uniqueName = '@yozora/tokenizer-link';
|
|
155
|
+
|
|
152
156
|
class LinkTokenizer extends coreTokenizer.BaseInlineTokenizer {
|
|
153
157
|
constructor(props = {}) {
|
|
154
158
|
var _a, _b;
|
|
@@ -156,116 +160,127 @@ class LinkTokenizer extends coreTokenizer.BaseInlineTokenizer {
|
|
|
156
160
|
name: (_a = props.name) !== null && _a !== void 0 ? _a : uniqueName,
|
|
157
161
|
priority: (_b = props.priority) !== null && _b !== void 0 ? _b : coreTokenizer.TokenizerPriority.LINKS,
|
|
158
162
|
});
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
const
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
163
|
+
this.match = api => {
|
|
164
|
+
return {
|
|
165
|
+
findDelimiter: () => coreTokenizer.genFindDelimiter(_findDelimiter),
|
|
166
|
+
isDelimiterPair,
|
|
167
|
+
processDelimiterPair,
|
|
168
|
+
};
|
|
169
|
+
function _findDelimiter(startIndex, endIndex) {
|
|
170
|
+
const nodePoints = api.getNodePoints();
|
|
171
|
+
const blockEndIndex = api.getBlockEndIndex();
|
|
172
|
+
for (let i = startIndex; i < endIndex; ++i) {
|
|
173
|
+
const p = nodePoints[i];
|
|
174
|
+
switch (p.codePoint) {
|
|
175
|
+
case character.AsciiCodePoint.BACKSLASH:
|
|
176
|
+
i += 1;
|
|
177
|
+
break;
|
|
178
|
+
case character.AsciiCodePoint.OPEN_BRACKET: {
|
|
179
|
+
const delimiter = {
|
|
180
|
+
type: 'opener',
|
|
181
|
+
startIndex: i,
|
|
182
|
+
endIndex: i + 1,
|
|
183
|
+
};
|
|
184
|
+
return delimiter;
|
|
185
|
+
}
|
|
186
|
+
case character.AsciiCodePoint.CLOSE_BRACKET: {
|
|
187
|
+
if (i + 1 >= endIndex ||
|
|
188
|
+
nodePoints[i + 1].codePoint !== character.AsciiCodePoint.OPEN_PARENTHESIS)
|
|
189
|
+
break;
|
|
190
|
+
const destinationStartIndex = coreTokenizer.eatOptionalWhitespaces(nodePoints, i + 2, blockEndIndex);
|
|
191
|
+
const destinationEndIndex = eatLinkDestination(nodePoints, destinationStartIndex, blockEndIndex);
|
|
192
|
+
if (destinationEndIndex < 0)
|
|
193
|
+
break;
|
|
194
|
+
const titleStartIndex = coreTokenizer.eatOptionalWhitespaces(nodePoints, destinationEndIndex, blockEndIndex);
|
|
195
|
+
const titleEndIndex = eatLinkTitle(nodePoints, titleStartIndex, blockEndIndex);
|
|
196
|
+
if (titleEndIndex < 0)
|
|
197
|
+
break;
|
|
198
|
+
const _startIndex = i;
|
|
199
|
+
const _endIndex = coreTokenizer.eatOptionalWhitespaces(nodePoints, titleEndIndex, blockEndIndex) + 1;
|
|
200
|
+
if (_endIndex > blockEndIndex ||
|
|
201
|
+
nodePoints[_endIndex - 1].codePoint !== character.AsciiCodePoint.CLOSE_PARENTHESIS)
|
|
202
|
+
break;
|
|
203
|
+
return {
|
|
204
|
+
type: 'closer',
|
|
205
|
+
startIndex: _startIndex,
|
|
206
|
+
endIndex: _endIndex,
|
|
207
|
+
destinationContent: destinationStartIndex < destinationEndIndex
|
|
208
|
+
? {
|
|
209
|
+
startIndex: destinationStartIndex,
|
|
210
|
+
endIndex: destinationEndIndex,
|
|
211
|
+
}
|
|
212
|
+
: undefined,
|
|
213
|
+
titleContent: titleStartIndex < titleEndIndex
|
|
214
|
+
? { startIndex: titleStartIndex, endIndex: titleEndIndex }
|
|
215
|
+
: undefined,
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
}
|
|
175
219
|
}
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
character.AsciiCodePoint.CLOSE_PARENTHESIS)
|
|
193
|
-
break;
|
|
194
|
-
return {
|
|
195
|
-
type: 'closer',
|
|
196
|
-
startIndex: _startIndex,
|
|
197
|
-
endIndex: _endIndex,
|
|
198
|
-
destinationContent: destinationStartIndex < destinationEndIndex
|
|
199
|
-
? {
|
|
200
|
-
startIndex: destinationStartIndex,
|
|
201
|
-
endIndex: destinationEndIndex,
|
|
202
|
-
}
|
|
203
|
-
: undefined,
|
|
204
|
-
titleContent: titleStartIndex < titleEndIndex
|
|
205
|
-
? { startIndex: titleStartIndex, endIndex: titleEndIndex }
|
|
206
|
-
: undefined,
|
|
207
|
-
};
|
|
220
|
+
return null;
|
|
221
|
+
}
|
|
222
|
+
function isDelimiterPair(openerDelimiter, closerDelimiter, internalTokens) {
|
|
223
|
+
const nodePoints = api.getNodePoints();
|
|
224
|
+
const hasInternalLinkToken = internalTokens.find(coreTokenizer.isLinkToken) != null;
|
|
225
|
+
if (hasInternalLinkToken) {
|
|
226
|
+
return { paired: false, opener: false, closer: false };
|
|
227
|
+
}
|
|
228
|
+
const balancedBracketsStatus = checkBalancedBracketsStatus(openerDelimiter.endIndex, closerDelimiter.startIndex, internalTokens, nodePoints);
|
|
229
|
+
switch (balancedBracketsStatus) {
|
|
230
|
+
case -1:
|
|
231
|
+
return { paired: false, opener: false, closer: true };
|
|
232
|
+
case 0:
|
|
233
|
+
return { paired: true };
|
|
234
|
+
case 1:
|
|
235
|
+
return { paired: false, opener: true, closer: false };
|
|
208
236
|
}
|
|
209
237
|
}
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
return { paired: false, opener: false, closer: true };
|
|
222
|
-
case 0:
|
|
223
|
-
return { paired: true };
|
|
224
|
-
case 1:
|
|
225
|
-
return { paired: false, opener: true, closer: false };
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
processDelimiterPair(openerDelimiter, closerDelimiter, internalTokens, nodePoints, api) {
|
|
229
|
-
const children = api.resolveInternalTokens(internalTokens, openerDelimiter.endIndex, closerDelimiter.startIndex, nodePoints);
|
|
230
|
-
const token = {
|
|
231
|
-
nodeType: ast.LinkType,
|
|
232
|
-
startIndex: openerDelimiter.startIndex,
|
|
233
|
-
endIndex: closerDelimiter.endIndex,
|
|
234
|
-
destinationContent: closerDelimiter.destinationContent,
|
|
235
|
-
titleContent: closerDelimiter.titleContent,
|
|
236
|
-
children,
|
|
237
|
-
};
|
|
238
|
-
return { tokens: [token] };
|
|
239
|
-
}
|
|
240
|
-
processToken(token, children, nodePoints) {
|
|
241
|
-
let url = '';
|
|
242
|
-
if (token.destinationContent != null) {
|
|
243
|
-
let { startIndex, endIndex } = token.destinationContent;
|
|
244
|
-
if (nodePoints[startIndex].codePoint === character.AsciiCodePoint.OPEN_ANGLE) {
|
|
245
|
-
startIndex += 1;
|
|
246
|
-
endIndex -= 1;
|
|
238
|
+
function processDelimiterPair(openerDelimiter, closerDelimiter, internalTokens) {
|
|
239
|
+
const children = api.resolveInternalTokens(internalTokens, openerDelimiter.endIndex, closerDelimiter.startIndex);
|
|
240
|
+
const token = {
|
|
241
|
+
nodeType: ast.LinkType,
|
|
242
|
+
startIndex: openerDelimiter.startIndex,
|
|
243
|
+
endIndex: closerDelimiter.endIndex,
|
|
244
|
+
destinationContent: closerDelimiter.destinationContent,
|
|
245
|
+
titleContent: closerDelimiter.titleContent,
|
|
246
|
+
children,
|
|
247
|
+
};
|
|
248
|
+
return { tokens: [token] };
|
|
247
249
|
}
|
|
248
|
-
const destination = character.calcEscapedStringFromNodePoints(nodePoints, startIndex, endIndex, true);
|
|
249
|
-
url = coreTokenizer.encodeLinkDestination(destination);
|
|
250
|
-
}
|
|
251
|
-
let title;
|
|
252
|
-
if (token.titleContent != null) {
|
|
253
|
-
const { startIndex, endIndex } = token.titleContent;
|
|
254
|
-
title = character.calcEscapedStringFromNodePoints(nodePoints, startIndex + 1, endIndex - 1);
|
|
255
|
-
}
|
|
256
|
-
const result = {
|
|
257
|
-
type: ast.LinkType,
|
|
258
|
-
url,
|
|
259
|
-
title,
|
|
260
|
-
children: children || [],
|
|
261
250
|
};
|
|
262
|
-
|
|
251
|
+
this.parse = api => ({
|
|
252
|
+
parse: (token, children) => {
|
|
253
|
+
const nodePoints = api.getNodePoints();
|
|
254
|
+
let url = '';
|
|
255
|
+
if (token.destinationContent != null) {
|
|
256
|
+
let { startIndex, endIndex } = token.destinationContent;
|
|
257
|
+
if (nodePoints[startIndex].codePoint === character.AsciiCodePoint.OPEN_ANGLE) {
|
|
258
|
+
startIndex += 1;
|
|
259
|
+
endIndex -= 1;
|
|
260
|
+
}
|
|
261
|
+
const destination = character.calcEscapedStringFromNodePoints(nodePoints, startIndex, endIndex, true);
|
|
262
|
+
url = coreTokenizer.encodeLinkDestination(destination);
|
|
263
|
+
}
|
|
264
|
+
let title;
|
|
265
|
+
if (token.titleContent != null) {
|
|
266
|
+
const { startIndex, endIndex } = token.titleContent;
|
|
267
|
+
title = character.calcEscapedStringFromNodePoints(nodePoints, startIndex + 1, endIndex - 1);
|
|
268
|
+
}
|
|
269
|
+
const result = {
|
|
270
|
+
type: ast.LinkType,
|
|
271
|
+
url,
|
|
272
|
+
title,
|
|
273
|
+
children,
|
|
274
|
+
};
|
|
275
|
+
return result;
|
|
276
|
+
},
|
|
277
|
+
});
|
|
263
278
|
}
|
|
264
279
|
}
|
|
265
280
|
|
|
266
281
|
exports.LinkTokenizer = LinkTokenizer;
|
|
267
282
|
exports.LinkTokenizerName = uniqueName;
|
|
268
283
|
exports.checkBalancedBracketsStatus = checkBalancedBracketsStatus;
|
|
269
|
-
exports[
|
|
284
|
+
exports["default"] = LinkTokenizer;
|
|
270
285
|
exports.eatLinkDestination = eatLinkDestination;
|
|
271
286
|
exports.eatLinkTitle = eatLinkTitle;
|
package/lib/esm/index.js
CHANGED
|
@@ -1,10 +1,47 @@
|
|
|
1
|
+
import { AsciiCodePoint, isWhitespaceCharacter, isAsciiControlCharacter, VirtualCodePoint, calcEscapedStringFromNodePoints } from '@yozora/character';
|
|
2
|
+
import { eatOptionalBlankLines, BaseInlineTokenizer, TokenizerPriority, genFindDelimiter, eatOptionalWhitespaces, isLinkToken, encodeLinkDestination } from '@yozora/core-tokenizer';
|
|
1
3
|
import { LinkType } from '@yozora/ast';
|
|
2
|
-
import { isWhitespaceCharacter, isAsciiControlCharacter, AsciiCodePoint, VirtualCodePoint, calcEscapedStringFromNodePoints } from '@yozora/character';
|
|
3
|
-
import { eatOptionalBlankLines, BaseInlineTokenizer, TokenizerPriority, eatOptionalWhitespaces, isLinkToken, encodeLinkDestination } from '@yozora/core-tokenizer';
|
|
4
4
|
|
|
5
|
-
const
|
|
5
|
+
const checkBalancedBracketsStatus = (startIndex, endIndex, internalTokens, nodePoints) => {
|
|
6
|
+
let i = startIndex;
|
|
7
|
+
let bracketCount = 0;
|
|
8
|
+
const updateBracketCount = () => {
|
|
9
|
+
const c = nodePoints[i].codePoint;
|
|
10
|
+
switch (c) {
|
|
11
|
+
case AsciiCodePoint.BACKSLASH:
|
|
12
|
+
i += 1;
|
|
13
|
+
break;
|
|
14
|
+
case AsciiCodePoint.OPEN_BRACKET:
|
|
15
|
+
bracketCount += 1;
|
|
16
|
+
break;
|
|
17
|
+
case AsciiCodePoint.CLOSE_BRACKET:
|
|
18
|
+
bracketCount -= 1;
|
|
19
|
+
break;
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
for (const token of internalTokens) {
|
|
23
|
+
if (token.startIndex < startIndex)
|
|
24
|
+
continue;
|
|
25
|
+
if (token.endIndex > endIndex)
|
|
26
|
+
break;
|
|
27
|
+
for (; i < token.startIndex; ++i) {
|
|
28
|
+
updateBracketCount();
|
|
29
|
+
if (bracketCount < 0)
|
|
30
|
+
return -1;
|
|
31
|
+
}
|
|
32
|
+
i = token.endIndex;
|
|
33
|
+
}
|
|
34
|
+
for (; i < endIndex; ++i) {
|
|
35
|
+
updateBracketCount();
|
|
36
|
+
if (bracketCount < 0)
|
|
37
|
+
return -1;
|
|
38
|
+
}
|
|
39
|
+
return bracketCount > 0 ? 1 : 0;
|
|
40
|
+
};
|
|
6
41
|
|
|
7
42
|
function eatLinkDestination(nodePoints, startIndex, endIndex) {
|
|
43
|
+
if (startIndex >= endIndex)
|
|
44
|
+
return -1;
|
|
8
45
|
let i = startIndex;
|
|
9
46
|
switch (nodePoints[i].codePoint) {
|
|
10
47
|
case AsciiCodePoint.OPEN_ANGLE: {
|
|
@@ -50,44 +87,9 @@ function eatLinkDestination(nodePoints, startIndex, endIndex) {
|
|
|
50
87
|
}
|
|
51
88
|
}
|
|
52
89
|
|
|
53
|
-
const checkBalancedBracketsStatus = (startIndex, endIndex, internalTokens, nodePoints) => {
|
|
54
|
-
let i = startIndex;
|
|
55
|
-
let bracketCount = 0;
|
|
56
|
-
const updateBracketCount = () => {
|
|
57
|
-
const c = nodePoints[i].codePoint;
|
|
58
|
-
switch (c) {
|
|
59
|
-
case AsciiCodePoint.BACKSLASH:
|
|
60
|
-
i += 1;
|
|
61
|
-
break;
|
|
62
|
-
case AsciiCodePoint.OPEN_BRACKET:
|
|
63
|
-
bracketCount += 1;
|
|
64
|
-
break;
|
|
65
|
-
case AsciiCodePoint.CLOSE_BRACKET:
|
|
66
|
-
bracketCount -= 1;
|
|
67
|
-
break;
|
|
68
|
-
}
|
|
69
|
-
};
|
|
70
|
-
for (const token of internalTokens) {
|
|
71
|
-
if (token.startIndex < startIndex)
|
|
72
|
-
continue;
|
|
73
|
-
if (token.endIndex > endIndex)
|
|
74
|
-
break;
|
|
75
|
-
for (; i < token.startIndex; ++i) {
|
|
76
|
-
updateBracketCount();
|
|
77
|
-
if (bracketCount < 0)
|
|
78
|
-
return -1;
|
|
79
|
-
}
|
|
80
|
-
i = token.endIndex;
|
|
81
|
-
}
|
|
82
|
-
for (; i < endIndex; ++i) {
|
|
83
|
-
updateBracketCount();
|
|
84
|
-
if (bracketCount < 0)
|
|
85
|
-
return -1;
|
|
86
|
-
}
|
|
87
|
-
return bracketCount > 0 ? 1 : 0;
|
|
88
|
-
};
|
|
89
|
-
|
|
90
90
|
function eatLinkTitle(nodePoints, startIndex, endIndex) {
|
|
91
|
+
if (startIndex >= endIndex)
|
|
92
|
+
return -1;
|
|
91
93
|
let i = startIndex;
|
|
92
94
|
const titleWrapSymbol = nodePoints[i].codePoint;
|
|
93
95
|
switch (titleWrapSymbol) {
|
|
@@ -145,6 +147,8 @@ function eatLinkTitle(nodePoints, startIndex, endIndex) {
|
|
|
145
147
|
return -1;
|
|
146
148
|
}
|
|
147
149
|
|
|
150
|
+
const uniqueName = '@yozora/tokenizer-link';
|
|
151
|
+
|
|
148
152
|
class LinkTokenizer extends BaseInlineTokenizer {
|
|
149
153
|
constructor(props = {}) {
|
|
150
154
|
var _a, _b;
|
|
@@ -152,110 +156,121 @@ class LinkTokenizer extends BaseInlineTokenizer {
|
|
|
152
156
|
name: (_a = props.name) !== null && _a !== void 0 ? _a : uniqueName,
|
|
153
157
|
priority: (_b = props.priority) !== null && _b !== void 0 ? _b : TokenizerPriority.LINKS,
|
|
154
158
|
});
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
const
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
159
|
+
this.match = api => {
|
|
160
|
+
return {
|
|
161
|
+
findDelimiter: () => genFindDelimiter(_findDelimiter),
|
|
162
|
+
isDelimiterPair,
|
|
163
|
+
processDelimiterPair,
|
|
164
|
+
};
|
|
165
|
+
function _findDelimiter(startIndex, endIndex) {
|
|
166
|
+
const nodePoints = api.getNodePoints();
|
|
167
|
+
const blockEndIndex = api.getBlockEndIndex();
|
|
168
|
+
for (let i = startIndex; i < endIndex; ++i) {
|
|
169
|
+
const p = nodePoints[i];
|
|
170
|
+
switch (p.codePoint) {
|
|
171
|
+
case AsciiCodePoint.BACKSLASH:
|
|
172
|
+
i += 1;
|
|
173
|
+
break;
|
|
174
|
+
case AsciiCodePoint.OPEN_BRACKET: {
|
|
175
|
+
const delimiter = {
|
|
176
|
+
type: 'opener',
|
|
177
|
+
startIndex: i,
|
|
178
|
+
endIndex: i + 1,
|
|
179
|
+
};
|
|
180
|
+
return delimiter;
|
|
181
|
+
}
|
|
182
|
+
case AsciiCodePoint.CLOSE_BRACKET: {
|
|
183
|
+
if (i + 1 >= endIndex ||
|
|
184
|
+
nodePoints[i + 1].codePoint !== AsciiCodePoint.OPEN_PARENTHESIS)
|
|
185
|
+
break;
|
|
186
|
+
const destinationStartIndex = eatOptionalWhitespaces(nodePoints, i + 2, blockEndIndex);
|
|
187
|
+
const destinationEndIndex = eatLinkDestination(nodePoints, destinationStartIndex, blockEndIndex);
|
|
188
|
+
if (destinationEndIndex < 0)
|
|
189
|
+
break;
|
|
190
|
+
const titleStartIndex = eatOptionalWhitespaces(nodePoints, destinationEndIndex, blockEndIndex);
|
|
191
|
+
const titleEndIndex = eatLinkTitle(nodePoints, titleStartIndex, blockEndIndex);
|
|
192
|
+
if (titleEndIndex < 0)
|
|
193
|
+
break;
|
|
194
|
+
const _startIndex = i;
|
|
195
|
+
const _endIndex = eatOptionalWhitespaces(nodePoints, titleEndIndex, blockEndIndex) + 1;
|
|
196
|
+
if (_endIndex > blockEndIndex ||
|
|
197
|
+
nodePoints[_endIndex - 1].codePoint !== AsciiCodePoint.CLOSE_PARENTHESIS)
|
|
198
|
+
break;
|
|
199
|
+
return {
|
|
200
|
+
type: 'closer',
|
|
201
|
+
startIndex: _startIndex,
|
|
202
|
+
endIndex: _endIndex,
|
|
203
|
+
destinationContent: destinationStartIndex < destinationEndIndex
|
|
204
|
+
? {
|
|
205
|
+
startIndex: destinationStartIndex,
|
|
206
|
+
endIndex: destinationEndIndex,
|
|
207
|
+
}
|
|
208
|
+
: undefined,
|
|
209
|
+
titleContent: titleStartIndex < titleEndIndex
|
|
210
|
+
? { startIndex: titleStartIndex, endIndex: titleEndIndex }
|
|
211
|
+
: undefined,
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
}
|
|
171
215
|
}
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
AsciiCodePoint.CLOSE_PARENTHESIS)
|
|
189
|
-
break;
|
|
190
|
-
return {
|
|
191
|
-
type: 'closer',
|
|
192
|
-
startIndex: _startIndex,
|
|
193
|
-
endIndex: _endIndex,
|
|
194
|
-
destinationContent: destinationStartIndex < destinationEndIndex
|
|
195
|
-
? {
|
|
196
|
-
startIndex: destinationStartIndex,
|
|
197
|
-
endIndex: destinationEndIndex,
|
|
198
|
-
}
|
|
199
|
-
: undefined,
|
|
200
|
-
titleContent: titleStartIndex < titleEndIndex
|
|
201
|
-
? { startIndex: titleStartIndex, endIndex: titleEndIndex }
|
|
202
|
-
: undefined,
|
|
203
|
-
};
|
|
216
|
+
return null;
|
|
217
|
+
}
|
|
218
|
+
function isDelimiterPair(openerDelimiter, closerDelimiter, internalTokens) {
|
|
219
|
+
const nodePoints = api.getNodePoints();
|
|
220
|
+
const hasInternalLinkToken = internalTokens.find(isLinkToken) != null;
|
|
221
|
+
if (hasInternalLinkToken) {
|
|
222
|
+
return { paired: false, opener: false, closer: false };
|
|
223
|
+
}
|
|
224
|
+
const balancedBracketsStatus = checkBalancedBracketsStatus(openerDelimiter.endIndex, closerDelimiter.startIndex, internalTokens, nodePoints);
|
|
225
|
+
switch (balancedBracketsStatus) {
|
|
226
|
+
case -1:
|
|
227
|
+
return { paired: false, opener: false, closer: true };
|
|
228
|
+
case 0:
|
|
229
|
+
return { paired: true };
|
|
230
|
+
case 1:
|
|
231
|
+
return { paired: false, opener: true, closer: false };
|
|
204
232
|
}
|
|
205
233
|
}
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
return { paired: false, opener: false, closer: true };
|
|
218
|
-
case 0:
|
|
219
|
-
return { paired: true };
|
|
220
|
-
case 1:
|
|
221
|
-
return { paired: false, opener: true, closer: false };
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
processDelimiterPair(openerDelimiter, closerDelimiter, internalTokens, nodePoints, api) {
|
|
225
|
-
const children = api.resolveInternalTokens(internalTokens, openerDelimiter.endIndex, closerDelimiter.startIndex, nodePoints);
|
|
226
|
-
const token = {
|
|
227
|
-
nodeType: LinkType,
|
|
228
|
-
startIndex: openerDelimiter.startIndex,
|
|
229
|
-
endIndex: closerDelimiter.endIndex,
|
|
230
|
-
destinationContent: closerDelimiter.destinationContent,
|
|
231
|
-
titleContent: closerDelimiter.titleContent,
|
|
232
|
-
children,
|
|
233
|
-
};
|
|
234
|
-
return { tokens: [token] };
|
|
235
|
-
}
|
|
236
|
-
processToken(token, children, nodePoints) {
|
|
237
|
-
let url = '';
|
|
238
|
-
if (token.destinationContent != null) {
|
|
239
|
-
let { startIndex, endIndex } = token.destinationContent;
|
|
240
|
-
if (nodePoints[startIndex].codePoint === AsciiCodePoint.OPEN_ANGLE) {
|
|
241
|
-
startIndex += 1;
|
|
242
|
-
endIndex -= 1;
|
|
234
|
+
function processDelimiterPair(openerDelimiter, closerDelimiter, internalTokens) {
|
|
235
|
+
const children = api.resolveInternalTokens(internalTokens, openerDelimiter.endIndex, closerDelimiter.startIndex);
|
|
236
|
+
const token = {
|
|
237
|
+
nodeType: LinkType,
|
|
238
|
+
startIndex: openerDelimiter.startIndex,
|
|
239
|
+
endIndex: closerDelimiter.endIndex,
|
|
240
|
+
destinationContent: closerDelimiter.destinationContent,
|
|
241
|
+
titleContent: closerDelimiter.titleContent,
|
|
242
|
+
children,
|
|
243
|
+
};
|
|
244
|
+
return { tokens: [token] };
|
|
243
245
|
}
|
|
244
|
-
const destination = calcEscapedStringFromNodePoints(nodePoints, startIndex, endIndex, true);
|
|
245
|
-
url = encodeLinkDestination(destination);
|
|
246
|
-
}
|
|
247
|
-
let title;
|
|
248
|
-
if (token.titleContent != null) {
|
|
249
|
-
const { startIndex, endIndex } = token.titleContent;
|
|
250
|
-
title = calcEscapedStringFromNodePoints(nodePoints, startIndex + 1, endIndex - 1);
|
|
251
|
-
}
|
|
252
|
-
const result = {
|
|
253
|
-
type: LinkType,
|
|
254
|
-
url,
|
|
255
|
-
title,
|
|
256
|
-
children: children || [],
|
|
257
246
|
};
|
|
258
|
-
|
|
247
|
+
this.parse = api => ({
|
|
248
|
+
parse: (token, children) => {
|
|
249
|
+
const nodePoints = api.getNodePoints();
|
|
250
|
+
let url = '';
|
|
251
|
+
if (token.destinationContent != null) {
|
|
252
|
+
let { startIndex, endIndex } = token.destinationContent;
|
|
253
|
+
if (nodePoints[startIndex].codePoint === AsciiCodePoint.OPEN_ANGLE) {
|
|
254
|
+
startIndex += 1;
|
|
255
|
+
endIndex -= 1;
|
|
256
|
+
}
|
|
257
|
+
const destination = calcEscapedStringFromNodePoints(nodePoints, startIndex, endIndex, true);
|
|
258
|
+
url = encodeLinkDestination(destination);
|
|
259
|
+
}
|
|
260
|
+
let title;
|
|
261
|
+
if (token.titleContent != null) {
|
|
262
|
+
const { startIndex, endIndex } = token.titleContent;
|
|
263
|
+
title = calcEscapedStringFromNodePoints(nodePoints, startIndex + 1, endIndex - 1);
|
|
264
|
+
}
|
|
265
|
+
const result = {
|
|
266
|
+
type: LinkType,
|
|
267
|
+
url,
|
|
268
|
+
title,
|
|
269
|
+
children,
|
|
270
|
+
};
|
|
271
|
+
return result;
|
|
272
|
+
},
|
|
273
|
+
});
|
|
259
274
|
}
|
|
260
275
|
}
|
|
261
276
|
|
package/lib/types/index.d.ts
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
export * from './util/check-brackets';
|
|
2
2
|
export * from './util/link-destination';
|
|
3
|
-
export * from './util/link-text';
|
|
4
3
|
export * from './util/link-title';
|
|
5
|
-
export { LinkTokenizer } from './tokenizer';
|
|
4
|
+
export { LinkTokenizer, LinkTokenizer as default } from './tokenizer';
|
|
6
5
|
export { uniqueName as LinkTokenizerName } from './types';
|
|
7
|
-
export type {
|
|
8
|
-
export default LinkTokenizer;
|
|
6
|
+
export type { IToken as ILinkToken, ITokenizerProps as ILinkTokenizerProps } from './types';
|
package/lib/types/tokenizer.d.ts
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type { NodePoint } from '@yozora/character';
|
|
3
|
-
import type { MatchInlinePhaseApi, ResultOfIsDelimiterPair, ResultOfProcessDelimiterPair, Tokenizer, TokenizerMatchInlineHook, TokenizerParseInlineHook, YastInlineToken } from '@yozora/core-tokenizer';
|
|
1
|
+
import type { IInlineTokenizer, IMatchInlineHookCreator, IParseInlineHookCreator } from '@yozora/core-tokenizer';
|
|
4
2
|
import { BaseInlineTokenizer } from '@yozora/core-tokenizer';
|
|
5
|
-
import type {
|
|
3
|
+
import type { IDelimiter, INode, IToken, ITokenizerProps, T } from './types';
|
|
6
4
|
/**
|
|
7
5
|
* Lexical Analyzer for InlineLink.
|
|
8
6
|
*
|
|
@@ -33,32 +31,8 @@ import type { Delimiter, Node, T, Token, TokenizerProps } from './types';
|
|
|
33
31
|
* @see https://github.com/syntax-tree/mdast#link
|
|
34
32
|
* @see https://github.github.com/gfm/#links
|
|
35
33
|
*/
|
|
36
|
-
export declare class LinkTokenizer extends BaseInlineTokenizer<
|
|
37
|
-
constructor(props?:
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
* parenthesis '(', optional whitespace, an optional link destination, an
|
|
41
|
-
* optional link title separated from the link destination by whitespace,
|
|
42
|
-
* optional whitespace, and a right parenthesis ')'
|
|
43
|
-
* @see https://github.github.com/gfm/#inline-link
|
|
44
|
-
*
|
|
45
|
-
* @override
|
|
46
|
-
* @see TokenizerMatchInlineHook
|
|
47
|
-
*/
|
|
48
|
-
protected _findDelimiter(startIndex: number, endIndex: number, nodePoints: ReadonlyArray<NodePoint>, api: Readonly<MatchInlinePhaseApi>): Delimiter | null;
|
|
49
|
-
/**
|
|
50
|
-
* @override
|
|
51
|
-
* @see TokenizerMatchInlineHook
|
|
52
|
-
*/
|
|
53
|
-
isDelimiterPair(openerDelimiter: Delimiter, closerDelimiter: Delimiter, internalTokens: ReadonlyArray<YastInlineToken>, nodePoints: ReadonlyArray<NodePoint>): ResultOfIsDelimiterPair;
|
|
54
|
-
/**
|
|
55
|
-
* @override
|
|
56
|
-
* @see TokenizerMatchInlineHook
|
|
57
|
-
*/
|
|
58
|
-
processDelimiterPair(openerDelimiter: Delimiter, closerDelimiter: Delimiter, internalTokens: ReadonlyArray<YastInlineToken>, nodePoints: ReadonlyArray<NodePoint>, api: Readonly<MatchInlinePhaseApi>): ResultOfProcessDelimiterPair<T, Token, Delimiter>;
|
|
59
|
-
/**
|
|
60
|
-
* @override
|
|
61
|
-
* @see TokenizerParseInlineHook
|
|
62
|
-
*/
|
|
63
|
-
processToken(token: Token, children: YastNode[] | undefined, nodePoints: ReadonlyArray<NodePoint>): Node;
|
|
34
|
+
export declare class LinkTokenizer extends BaseInlineTokenizer<T, IDelimiter, IToken, INode> implements IInlineTokenizer<T, IDelimiter, IToken, INode> {
|
|
35
|
+
constructor(props?: ITokenizerProps);
|
|
36
|
+
readonly match: IMatchInlineHookCreator<T, IDelimiter, IToken>;
|
|
37
|
+
readonly parse: IParseInlineHookCreator<T, IToken, INode>;
|
|
64
38
|
}
|
package/lib/types/types.d.ts
CHANGED
|
@@ -1,31 +1,31 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type {
|
|
3
|
-
import type {
|
|
1
|
+
import type { ILink, LinkType } from '@yozora/ast';
|
|
2
|
+
import type { INodeInterval } from '@yozora/character';
|
|
3
|
+
import type { IBaseInlineTokenizerProps, IPartialYastInlineToken, IYastTokenDelimiter } from '@yozora/core-tokenizer';
|
|
4
4
|
export declare type T = LinkType;
|
|
5
|
-
export declare type
|
|
5
|
+
export declare type INode = ILink;
|
|
6
6
|
export declare const uniqueName = "@yozora/tokenizer-link";
|
|
7
|
-
export interface
|
|
7
|
+
export interface IToken extends IPartialYastInlineToken<T> {
|
|
8
8
|
/**
|
|
9
9
|
* Link destination interval.
|
|
10
10
|
*/
|
|
11
|
-
destinationContent?:
|
|
11
|
+
destinationContent?: INodeInterval;
|
|
12
12
|
/**
|
|
13
13
|
* Link title interval.
|
|
14
14
|
*/
|
|
15
|
-
titleContent?:
|
|
15
|
+
titleContent?: INodeInterval;
|
|
16
16
|
}
|
|
17
|
-
export interface
|
|
17
|
+
export interface IDelimiter extends IYastTokenDelimiter {
|
|
18
18
|
/**
|
|
19
|
-
*
|
|
19
|
+
* IDelimiter type.
|
|
20
20
|
*/
|
|
21
21
|
type: 'opener' | 'closer';
|
|
22
22
|
/**
|
|
23
23
|
* Link destination interval.
|
|
24
24
|
*/
|
|
25
|
-
destinationContent?:
|
|
25
|
+
destinationContent?: INodeInterval;
|
|
26
26
|
/**
|
|
27
27
|
* Link title interval.
|
|
28
28
|
*/
|
|
29
|
-
titleContent?:
|
|
29
|
+
titleContent?: INodeInterval;
|
|
30
30
|
}
|
|
31
|
-
export declare type
|
|
31
|
+
export declare type ITokenizerProps = Partial<IBaseInlineTokenizerProps>;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type {
|
|
1
|
+
import type { INodePoint } from '@yozora/character';
|
|
2
|
+
import type { IYastInlineToken } from '@yozora/core-tokenizer';
|
|
3
3
|
/**
|
|
4
4
|
* The link text may contain balanced brackets, but not unbalanced ones,
|
|
5
5
|
* unless they are escaped
|
|
@@ -9,4 +9,4 @@ import type { YastInlineToken } from '@yozora/core-tokenizer';
|
|
|
9
9
|
* @see https://github.github.com/gfm/#example-522
|
|
10
10
|
* @see https://github.github.com/gfm/#example-523
|
|
11
11
|
*/
|
|
12
|
-
export declare const checkBalancedBracketsStatus: (startIndex: number, endIndex: number, internalTokens: ReadonlyArray<
|
|
12
|
+
export declare const checkBalancedBracketsStatus: (startIndex: number, endIndex: number, internalTokens: ReadonlyArray<IYastInlineToken>, nodePoints: ReadonlyArray<INodePoint>) => -1 | 0 | 1;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { INodePoint } from '@yozora/character';
|
|
2
2
|
/**
|
|
3
3
|
* A link destination consists of either
|
|
4
4
|
* - a sequence of zero or more characters between an opening '<' and a closing '>'
|
|
@@ -12,4 +12,4 @@ import type { NodePoint } from '@yozora/character';
|
|
|
12
12
|
* @see https://github.github.com/gfm/#link-destination
|
|
13
13
|
* @return position at next iteration
|
|
14
14
|
*/
|
|
15
|
-
export declare function eatLinkDestination(nodePoints: ReadonlyArray<
|
|
15
|
+
export declare function eatLinkDestination(nodePoints: ReadonlyArray<INodePoint>, startIndex: number, endIndex: number): number;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { INodePoint } from '@yozora/character';
|
|
2
2
|
/**
|
|
3
3
|
* A link title consists of either
|
|
4
4
|
*
|
|
@@ -11,4 +11,4 @@ import type { NodePoint } from '@yozora/character';
|
|
|
11
11
|
* - a sequence of zero or more characters between matching parentheses '(...)',
|
|
12
12
|
* including a '(' or ')' character only if it is backslash-escaped.
|
|
13
13
|
*/
|
|
14
|
-
export declare function eatLinkTitle(nodePoints: ReadonlyArray<
|
|
14
|
+
export declare function eatLinkTitle(nodePoints: ReadonlyArray<INodePoint>, startIndex: number, endIndex: number): number;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yozora/tokenizer-link",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0-alpha.0",
|
|
4
4
|
"author": {
|
|
5
5
|
"name": "guanghechen",
|
|
6
6
|
"url": "https://github.com/guanghechen/"
|
|
@@ -35,9 +35,9 @@
|
|
|
35
35
|
"test": "cross-env TS_NODE_FILES=true jest --config ../../jest.config.js --rootDir ."
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@yozora/ast": "^
|
|
39
|
-
"@yozora/character": "^
|
|
40
|
-
"@yozora/core-tokenizer": "^
|
|
38
|
+
"@yozora/ast": "^2.0.0-alpha.0",
|
|
39
|
+
"@yozora/character": "^2.0.0-alpha.0",
|
|
40
|
+
"@yozora/core-tokenizer": "^2.0.0-alpha.0"
|
|
41
41
|
},
|
|
42
|
-
"gitHead": "
|
|
42
|
+
"gitHead": "0171501339c49ffd02ed16a63447fa20a47a29a7"
|
|
43
43
|
}
|