@peaceroad/markdown-it-strong-ja 0.6.2 → 0.7.1
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/README.md +42 -13
- package/index.js +44 -2488
- package/package.json +4 -3
- package/src/token-compat.js +226 -0
- package/src/token-core.js +439 -0
- package/src/token-link-utils.js +774 -0
- package/src/token-postprocess.js +340 -0
- package/src/token-utils.js +166 -0
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
import Token from 'markdown-it/lib/token.mjs'
|
|
2
|
+
import { convertCollapsedReferenceLinks, mergeBrokenMarksAroundLinks, getMapFromTokenRange } from './token-link-utils.js'
|
|
3
|
+
import {
|
|
4
|
+
rebuildInlineLevels,
|
|
5
|
+
findLinkClose,
|
|
6
|
+
fixEmOuterStrongSequence,
|
|
7
|
+
fixLeadingAsteriskEm,
|
|
8
|
+
fixTrailingStrong
|
|
9
|
+
} from './token-core.js'
|
|
10
|
+
import { getRuntimeOpt } from './token-utils.js'
|
|
11
|
+
|
|
12
|
+
const scanBrokenRefState = (text, out) => {
|
|
13
|
+
if (!text || text.indexOf('][') === -1) {
|
|
14
|
+
out.depth = 0
|
|
15
|
+
out.brokenEnd = false
|
|
16
|
+
return out
|
|
17
|
+
}
|
|
18
|
+
let depth = 0
|
|
19
|
+
let lastOpen = -1
|
|
20
|
+
let lastClose = -1
|
|
21
|
+
for (let i = 0; i < text.length; i++) {
|
|
22
|
+
const ch = text.charCodeAt(i)
|
|
23
|
+
if (ch === 0x5B) {
|
|
24
|
+
depth++
|
|
25
|
+
lastOpen = i
|
|
26
|
+
} else if (ch === 0x5D) {
|
|
27
|
+
if (depth > 0) depth--
|
|
28
|
+
lastClose = i
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
out.depth = depth
|
|
32
|
+
out.brokenEnd = depth > 0 && lastOpen > lastClose
|
|
33
|
+
return out
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const updateBracketDepth = (text, depth) => {
|
|
37
|
+
if (!text || depth <= 0) return depth
|
|
38
|
+
for (let i = 0; i < text.length; i++) {
|
|
39
|
+
const ch = text.charCodeAt(i)
|
|
40
|
+
if (ch === 0x5B) {
|
|
41
|
+
depth++
|
|
42
|
+
} else if (ch === 0x5D) {
|
|
43
|
+
if (depth > 0) {
|
|
44
|
+
depth--
|
|
45
|
+
if (depth === 0) return 0
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return depth
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const getAttr = (token, name) => {
|
|
53
|
+
if (!token || !token.attrs) return ''
|
|
54
|
+
for (let i = 0; i < token.attrs.length; i++) {
|
|
55
|
+
if (token.attrs[i][0] === name) return token.attrs[i][1]
|
|
56
|
+
}
|
|
57
|
+
return ''
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const buildLabelText = (tokens, startIdx, endIdx) => {
|
|
61
|
+
let label = ''
|
|
62
|
+
for (let i = startIdx; i <= endIdx; i++) {
|
|
63
|
+
const t = tokens[i]
|
|
64
|
+
if (!t) continue
|
|
65
|
+
if (t.type === 'text') {
|
|
66
|
+
label += t.content
|
|
67
|
+
} else if (t.type === 'code_inline') {
|
|
68
|
+
const fence = t.markup || '`'
|
|
69
|
+
label += fence + t.content + fence
|
|
70
|
+
} else if (t.markup) {
|
|
71
|
+
label += t.markup
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return label
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const buildRawFromTokens = (tokens, startIdx, endIdx) => {
|
|
78
|
+
let raw = ''
|
|
79
|
+
for (let i = startIdx; i <= endIdx; i++) {
|
|
80
|
+
const t = tokens[i]
|
|
81
|
+
if (!t) continue
|
|
82
|
+
if (t.type === 'link_open') {
|
|
83
|
+
const closeIdx = findLinkClose(tokens, i)
|
|
84
|
+
if (closeIdx === -1 || closeIdx > endIdx) break
|
|
85
|
+
const label = buildLabelText(tokens, i + 1, closeIdx - 1)
|
|
86
|
+
const href = getAttr(t, 'href')
|
|
87
|
+
raw += `[${label}](${href || ''})`
|
|
88
|
+
i = closeIdx
|
|
89
|
+
continue
|
|
90
|
+
}
|
|
91
|
+
if (t.type === 'text') {
|
|
92
|
+
raw += t.content
|
|
93
|
+
continue
|
|
94
|
+
}
|
|
95
|
+
if (t.type === 'code_inline') {
|
|
96
|
+
const fence = t.markup || '`'
|
|
97
|
+
raw += fence + t.content + fence
|
|
98
|
+
continue
|
|
99
|
+
}
|
|
100
|
+
if (t.markup) {
|
|
101
|
+
raw += t.markup
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return raw
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const parseInlineWithFixes = (md, raw, env) => {
|
|
108
|
+
const parsed = md.parseInline(raw, env)
|
|
109
|
+
const inline = parsed && parsed.length > 0 ? parsed[0] : null
|
|
110
|
+
if (!inline || !inline.children) return null
|
|
111
|
+
const children = inline.children
|
|
112
|
+
let changed = false
|
|
113
|
+
if (fixEmOuterStrongSequence(children)) changed = true
|
|
114
|
+
if (fixLeadingAsteriskEm(children)) changed = true
|
|
115
|
+
if (fixTrailingStrong(children)) changed = true
|
|
116
|
+
if (changed) rebuildInlineLevels(children)
|
|
117
|
+
return children
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const hasUnsafeAttrs = (token) => {
|
|
121
|
+
if (!token) return false
|
|
122
|
+
if (token.meta && Object.keys(token.meta).length > 0) return true
|
|
123
|
+
if (!token.attrs || token.attrs.length === 0) return false
|
|
124
|
+
if (token.type !== 'link_open') return true
|
|
125
|
+
for (let i = 0; i < token.attrs.length; i++) {
|
|
126
|
+
const name = token.attrs[i][0]
|
|
127
|
+
if (name !== 'href' && name !== 'title') return true
|
|
128
|
+
}
|
|
129
|
+
return false
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const REPARSE_ALLOWED_TYPES = new Set([
|
|
133
|
+
'text',
|
|
134
|
+
'strong_open',
|
|
135
|
+
'strong_close',
|
|
136
|
+
'em_open',
|
|
137
|
+
'em_close',
|
|
138
|
+
'code_inline',
|
|
139
|
+
'link_open',
|
|
140
|
+
'link_close',
|
|
141
|
+
'softbreak',
|
|
142
|
+
'hardbreak'
|
|
143
|
+
])
|
|
144
|
+
|
|
145
|
+
const shouldReparseSegment = (tokens, startIdx, endIdx) => {
|
|
146
|
+
for (let i = startIdx; i <= endIdx && i < tokens.length; i++) {
|
|
147
|
+
const t = tokens[i]
|
|
148
|
+
if (!t) continue
|
|
149
|
+
if (hasUnsafeAttrs(t)) return false
|
|
150
|
+
if (t.type && !REPARSE_ALLOWED_TYPES.has(t.type)) return false
|
|
151
|
+
}
|
|
152
|
+
return true
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const fixTailAfterLinkStrongClose = (tokens, md, env) => {
|
|
156
|
+
let strongDepth = 0
|
|
157
|
+
for (let i = 0; i < tokens.length; i++) {
|
|
158
|
+
const t = tokens[i]
|
|
159
|
+
if (!t) continue
|
|
160
|
+
if (t.type === 'strong_open') strongDepth++
|
|
161
|
+
if (t.type === 'strong_close') {
|
|
162
|
+
if (strongDepth > 0) strongDepth--
|
|
163
|
+
}
|
|
164
|
+
if (t.type !== 'link_close') continue
|
|
165
|
+
if (strongDepth !== 0) continue
|
|
166
|
+
let startIdx = -1
|
|
167
|
+
let foundStrongClose = -1
|
|
168
|
+
let foundStrongOpen = -1
|
|
169
|
+
for (let j = i + 1; j < tokens.length; j++) {
|
|
170
|
+
const node = tokens[j]
|
|
171
|
+
if (!node) continue
|
|
172
|
+
if (node.type === 'strong_open') {
|
|
173
|
+
foundStrongOpen = j
|
|
174
|
+
break
|
|
175
|
+
}
|
|
176
|
+
if (node.type === 'strong_close') {
|
|
177
|
+
foundStrongClose = j
|
|
178
|
+
break
|
|
179
|
+
}
|
|
180
|
+
if (node.type === 'text' && node.content && startIdx === -1) {
|
|
181
|
+
startIdx = j
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
if (foundStrongClose === -1 || foundStrongOpen !== -1) continue
|
|
185
|
+
if (startIdx === -1) startIdx = foundStrongClose
|
|
186
|
+
const raw = buildRawFromTokens(tokens, startIdx, tokens.length - 1)
|
|
187
|
+
const children = parseInlineWithFixes(md, raw, env)
|
|
188
|
+
if (children && children.length > 0) {
|
|
189
|
+
tokens.splice(startIdx, tokens.length - startIdx, ...children)
|
|
190
|
+
return true
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
return false
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const registerTokenPostprocess = (md, baseOpt, getNoLinkMdInstance) => {
|
|
197
|
+
if (md.__strongJaTokenPostprocessRegistered) return
|
|
198
|
+
md.__strongJaTokenPostprocessRegistered = true
|
|
199
|
+
md.core.ruler.after('inline', 'strong_ja_token_postprocess', (state) => {
|
|
200
|
+
if (!state || !state.tokens) return
|
|
201
|
+
const opt = getRuntimeOpt(state, baseOpt)
|
|
202
|
+
if (opt.postprocess === false) return
|
|
203
|
+
if (state.__strongJaReferenceCount === undefined) {
|
|
204
|
+
const references = state.env && state.env.references
|
|
205
|
+
state.__strongJaReferenceCount = references ? Object.keys(references).length : 0
|
|
206
|
+
}
|
|
207
|
+
for (let i = 0; i < state.tokens.length; i++) {
|
|
208
|
+
const token = state.tokens[i]
|
|
209
|
+
if (!token || token.type !== 'inline' || !token.children || token.children.length === 0) continue
|
|
210
|
+
const children = token.children
|
|
211
|
+
let changed = false
|
|
212
|
+
let hasBracketText = false
|
|
213
|
+
let hasEmphasis = false
|
|
214
|
+
let hasLinkClose = false
|
|
215
|
+
let reparseCount = 0
|
|
216
|
+
let maxReparse = 0
|
|
217
|
+
const scanState = { depth: 0, brokenEnd: false }
|
|
218
|
+
for (let j = 0; j < children.length; j++) {
|
|
219
|
+
const child = children[j]
|
|
220
|
+
if (!child || child.type !== 'text' || !child.content) continue
|
|
221
|
+
if (scanBrokenRefState(child.content, scanState).brokenEnd) {
|
|
222
|
+
maxReparse++
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
if (maxReparse === 0) {
|
|
226
|
+
for (let j = 0; j < children.length; j++) {
|
|
227
|
+
const child = children[j]
|
|
228
|
+
if (!child) continue
|
|
229
|
+
if (child.type === 'text' && child.content) {
|
|
230
|
+
if (!hasBracketText && (child.content.indexOf('[') !== -1 || child.content.indexOf(']') !== -1)) {
|
|
231
|
+
hasBracketText = true
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
if (!hasEmphasis &&
|
|
235
|
+
(child.type === 'strong_open' || child.type === 'strong_close' || child.type === 'em_open' || child.type === 'em_close')) {
|
|
236
|
+
hasEmphasis = true
|
|
237
|
+
}
|
|
238
|
+
if (!hasLinkClose && child.type === 'link_close') {
|
|
239
|
+
hasLinkClose = true
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
} else {
|
|
243
|
+
let allowReparse = true
|
|
244
|
+
while (true) {
|
|
245
|
+
let didReparse = false
|
|
246
|
+
let brokenRefStart = -1
|
|
247
|
+
let brokenRefDepth = 0
|
|
248
|
+
hasBracketText = false
|
|
249
|
+
hasEmphasis = false
|
|
250
|
+
hasLinkClose = false
|
|
251
|
+
for (let j = 0; j < children.length; j++) {
|
|
252
|
+
const child = children[j]
|
|
253
|
+
if (!child) continue
|
|
254
|
+
if (child.type === 'text' && child.content) {
|
|
255
|
+
if (!hasBracketText && (child.content.indexOf('[') !== -1 || child.content.indexOf(']') !== -1)) {
|
|
256
|
+
hasBracketText = true
|
|
257
|
+
}
|
|
258
|
+
if (brokenRefStart === -1) {
|
|
259
|
+
const scan = scanBrokenRefState(child.content, scanState)
|
|
260
|
+
if (scan.brokenEnd) {
|
|
261
|
+
brokenRefStart = j
|
|
262
|
+
brokenRefDepth = scan.depth
|
|
263
|
+
continue
|
|
264
|
+
}
|
|
265
|
+
} else {
|
|
266
|
+
brokenRefDepth = updateBracketDepth(child.content, brokenRefDepth)
|
|
267
|
+
if (brokenRefDepth <= 0) {
|
|
268
|
+
brokenRefStart = -1
|
|
269
|
+
brokenRefDepth = 0
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
if (!hasEmphasis &&
|
|
274
|
+
(child.type === 'strong_open' || child.type === 'strong_close' || child.type === 'em_open' || child.type === 'em_close')) {
|
|
275
|
+
hasEmphasis = true
|
|
276
|
+
}
|
|
277
|
+
if (!hasLinkClose && child.type === 'link_close') {
|
|
278
|
+
hasLinkClose = true
|
|
279
|
+
}
|
|
280
|
+
if (allowReparse && brokenRefStart !== -1 && child.type === 'link_open') {
|
|
281
|
+
if (brokenRefDepth <= 0) {
|
|
282
|
+
brokenRefStart = -1
|
|
283
|
+
brokenRefDepth = 0
|
|
284
|
+
continue
|
|
285
|
+
}
|
|
286
|
+
const closeIdx = findLinkClose(children, j)
|
|
287
|
+
if (closeIdx !== -1) {
|
|
288
|
+
if (shouldReparseSegment(children, brokenRefStart, closeIdx)) {
|
|
289
|
+
const originalMap = getMapFromTokenRange(children, brokenRefStart, closeIdx)
|
|
290
|
+
const raw = buildRawFromTokens(children, brokenRefStart, closeIdx)
|
|
291
|
+
const noLink = getNoLinkMdInstance(md, opt)
|
|
292
|
+
const parsed = parseInlineWithFixes(noLink, raw, state.env)
|
|
293
|
+
if (parsed && parsed.length > 0) {
|
|
294
|
+
if (originalMap) {
|
|
295
|
+
for (let k = 0; k < parsed.length; k++) {
|
|
296
|
+
const childToken = parsed[k]
|
|
297
|
+
if (childToken && !childToken.map) childToken.map = [originalMap[0], originalMap[1]]
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
children.splice(brokenRefStart, closeIdx - brokenRefStart + 1, ...parsed)
|
|
301
|
+
} else {
|
|
302
|
+
const text = new Token('text', '', 0)
|
|
303
|
+
text.content = raw
|
|
304
|
+
if (originalMap) text.map = [originalMap[0], originalMap[1]]
|
|
305
|
+
children.splice(brokenRefStart, closeIdx - brokenRefStart + 1, text)
|
|
306
|
+
}
|
|
307
|
+
brokenRefStart = -1
|
|
308
|
+
changed = true
|
|
309
|
+
didReparse = true
|
|
310
|
+
break
|
|
311
|
+
}
|
|
312
|
+
brokenRefStart = -1
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
if (!didReparse) break
|
|
317
|
+
reparseCount++
|
|
318
|
+
if (reparseCount >= maxReparse) {
|
|
319
|
+
allowReparse = false
|
|
320
|
+
}
|
|
321
|
+
if (!allowReparse) {
|
|
322
|
+
continue
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
if (hasEmphasis) {
|
|
327
|
+
if (fixEmOuterStrongSequence(children)) changed = true
|
|
328
|
+
if (hasLinkClose && fixTailAfterLinkStrongClose(children, md, state.env)) changed = true
|
|
329
|
+
if (hasLinkClose && fixLeadingAsteriskEm(children)) changed = true
|
|
330
|
+
if (fixTrailingStrong(children)) changed = true
|
|
331
|
+
}
|
|
332
|
+
if (changed) rebuildInlineLevels(children)
|
|
333
|
+
if (!hasBracketText) continue
|
|
334
|
+
convertCollapsedReferenceLinks(children, state)
|
|
335
|
+
mergeBrokenMarksAroundLinks(children)
|
|
336
|
+
}
|
|
337
|
+
})
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
export { registerTokenPostprocess }
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
const CHAR_ASTERISK = 0x2A // *
|
|
2
|
+
const CHAR_OPEN_BRACKET = 0x5B // [
|
|
3
|
+
const CHAR_CLOSE_BRACKET = 0x5D // ]
|
|
4
|
+
const CHAR_SPACE = 0x20 // ' '
|
|
5
|
+
const CHAR_TAB = 0x09 // '\t'
|
|
6
|
+
const CHAR_NEWLINE = 0x0A // '\n'
|
|
7
|
+
const REG_JAPANESE = /[\p{Script=Hiragana}\p{Script=Katakana}\p{Script=Han}\u3000-\u303F\uFF00-\uFFEF]/u
|
|
8
|
+
const REG_ATTRS = /{[^{}\n!@#%^&*()]+?}$/
|
|
9
|
+
|
|
10
|
+
const hasJapaneseText = (str) => {
|
|
11
|
+
if (!str) return false
|
|
12
|
+
return REG_JAPANESE.test(str)
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const isJapaneseChar = (ch) => {
|
|
16
|
+
if (!ch) return false
|
|
17
|
+
const code = typeof ch === 'string' ? ch.charCodeAt(0) : ch
|
|
18
|
+
if (code < 128) return false
|
|
19
|
+
if (code >= 0x3040 && code <= 0x309F) return true
|
|
20
|
+
if (code >= 0x30A0 && code <= 0x30FF) return true
|
|
21
|
+
if (code >= 0x4E00 && code <= 0x9FAF) return true
|
|
22
|
+
return REG_JAPANESE.test(String.fromCharCode(code))
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const hasCjkBreaksRule = (md) => {
|
|
26
|
+
if (!md || !md.core || !md.core.ruler || !Array.isArray(md.core.ruler.__rules__)) return false
|
|
27
|
+
if (md.__strongJaHasCjkBreaks === true) return true
|
|
28
|
+
const found = md.core.ruler.__rules__.some((rule) => rule && typeof rule.name === 'string' && rule.name.indexOf('cjk_breaks') !== -1)
|
|
29
|
+
if (found) md.__strongJaHasCjkBreaks = true
|
|
30
|
+
return found
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const findPrevNonSpace = (src, start) => {
|
|
34
|
+
for (let i = start; i >= 0; i--) {
|
|
35
|
+
const ch = src.charCodeAt(i)
|
|
36
|
+
if (ch === CHAR_NEWLINE) return 0
|
|
37
|
+
if (ch === CHAR_SPACE || ch === CHAR_TAB) continue
|
|
38
|
+
return ch
|
|
39
|
+
}
|
|
40
|
+
return 0
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const findNextNonSpace = (src, start, max) => {
|
|
44
|
+
for (let i = start; i < max; i++) {
|
|
45
|
+
const ch = src.charCodeAt(i)
|
|
46
|
+
if (ch === CHAR_NEWLINE) return 0
|
|
47
|
+
if (ch === CHAR_SPACE || ch === CHAR_TAB) continue
|
|
48
|
+
return ch
|
|
49
|
+
}
|
|
50
|
+
return 0
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const resolveMode = (opt) => {
|
|
54
|
+
const raw = opt && typeof opt.mode === 'string' ? opt.mode : 'japanese'
|
|
55
|
+
const mode = raw.toLowerCase()
|
|
56
|
+
if (mode === 'japanese-only') return 'japanese'
|
|
57
|
+
return mode
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const shouldUseJapaneseRule = (state, opt, mode) => {
|
|
61
|
+
if (mode === 'aggressive') return true
|
|
62
|
+
if (mode === 'compatible') return false
|
|
63
|
+
let hasJapanese = state.__strongJaTokenHasJapanese
|
|
64
|
+
if (hasJapanese === undefined) {
|
|
65
|
+
hasJapanese = hasJapaneseText(state.src)
|
|
66
|
+
state.__strongJaTokenHasJapanese = hasJapanese
|
|
67
|
+
}
|
|
68
|
+
return hasJapanese
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const getRuntimeOpt = (state, baseOpt) => {
|
|
72
|
+
if (!state || !state.env || !state.env.__strongJaTokenOpt) return baseOpt
|
|
73
|
+
const override = state.env.__strongJaTokenOpt
|
|
74
|
+
if (state.__strongJaTokenRuntimeOpt &&
|
|
75
|
+
state.__strongJaTokenRuntimeBase === baseOpt &&
|
|
76
|
+
state.__strongJaTokenRuntimeOverride === override) {
|
|
77
|
+
return state.__strongJaTokenRuntimeOpt
|
|
78
|
+
}
|
|
79
|
+
const merged = { ...baseOpt, ...override }
|
|
80
|
+
state.__strongJaTokenRuntimeOpt = merged
|
|
81
|
+
state.__strongJaTokenRuntimeBase = baseOpt
|
|
82
|
+
state.__strongJaTokenRuntimeOverride = override
|
|
83
|
+
return merged
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function normalizeCoreRulesBeforePostprocess(value) {
|
|
87
|
+
if (!value) return []
|
|
88
|
+
const list = Array.isArray(value) ? value : [value]
|
|
89
|
+
const normalized = []
|
|
90
|
+
const seen = new Set()
|
|
91
|
+
for (let idx = 0; idx < list.length; idx++) {
|
|
92
|
+
const raw = list[idx]
|
|
93
|
+
if (typeof raw !== 'string') continue
|
|
94
|
+
const trimmed = raw.trim()
|
|
95
|
+
if (!trimmed || seen.has(trimmed)) continue
|
|
96
|
+
seen.add(trimmed)
|
|
97
|
+
normalized.push(trimmed)
|
|
98
|
+
}
|
|
99
|
+
return normalized
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function ensureCoreRuleOrder(md, ruleNames, targetRuleName) {
|
|
103
|
+
if (!md || !md.core || !md.core.ruler) return
|
|
104
|
+
if (!ruleNames || ruleNames.length === 0) return
|
|
105
|
+
for (let idx = 0; idx < ruleNames.length; idx++) {
|
|
106
|
+
moveRuleBefore(md.core.ruler, ruleNames[idx], targetRuleName)
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function moveRuleBefore(ruler, ruleName, beforeName) {
|
|
111
|
+
if (!ruler || !ruler.__rules__) return
|
|
112
|
+
const rules = ruler.__rules__
|
|
113
|
+
let fromIdx = -1
|
|
114
|
+
let beforeIdx = -1
|
|
115
|
+
for (let idx = 0; idx < rules.length; idx++) {
|
|
116
|
+
if (rules[idx].name === ruleName) fromIdx = idx
|
|
117
|
+
if (rules[idx].name === beforeName) beforeIdx = idx
|
|
118
|
+
if (fromIdx !== -1 && beforeIdx !== -1) break
|
|
119
|
+
}
|
|
120
|
+
// Ensure ruleName is before beforeName; keep existing order if already earlier.
|
|
121
|
+
if (fromIdx === -1 || beforeIdx === -1 || fromIdx < beforeIdx) return
|
|
122
|
+
|
|
123
|
+
const rule = rules.splice(fromIdx, 1)[0]
|
|
124
|
+
rules.splice(beforeIdx, 0, rule)
|
|
125
|
+
ruler.__cache__ = null
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function moveRuleAfter(ruler, ruleName, afterName) {
|
|
129
|
+
if (!ruler || !ruler.__rules__) return
|
|
130
|
+
const rules = ruler.__rules__
|
|
131
|
+
let fromIdx = -1
|
|
132
|
+
let afterIdx = -1
|
|
133
|
+
for (let idx = 0; idx < rules.length; idx++) {
|
|
134
|
+
if (rules[idx].name === ruleName) fromIdx = idx
|
|
135
|
+
if (rules[idx].name === afterName) afterIdx = idx
|
|
136
|
+
if (fromIdx !== -1 && afterIdx !== -1) break
|
|
137
|
+
}
|
|
138
|
+
if (fromIdx === -1 || afterIdx === -1 || fromIdx === afterIdx + 1) return
|
|
139
|
+
|
|
140
|
+
const rule = rules.splice(fromIdx, 1)[0]
|
|
141
|
+
const targetIdx = fromIdx < afterIdx ? afterIdx - 1 : afterIdx
|
|
142
|
+
rules.splice(targetIdx + 1, 0, rule)
|
|
143
|
+
ruler.__cache__ = null
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export {
|
|
147
|
+
CHAR_ASTERISK,
|
|
148
|
+
CHAR_OPEN_BRACKET,
|
|
149
|
+
CHAR_CLOSE_BRACKET,
|
|
150
|
+
CHAR_SPACE,
|
|
151
|
+
CHAR_TAB,
|
|
152
|
+
CHAR_NEWLINE,
|
|
153
|
+
REG_ATTRS,
|
|
154
|
+
hasJapaneseText,
|
|
155
|
+
isJapaneseChar,
|
|
156
|
+
hasCjkBreaksRule,
|
|
157
|
+
findPrevNonSpace,
|
|
158
|
+
findNextNonSpace,
|
|
159
|
+
resolveMode,
|
|
160
|
+
shouldUseJapaneseRule,
|
|
161
|
+
getRuntimeOpt,
|
|
162
|
+
normalizeCoreRulesBeforePostprocess,
|
|
163
|
+
ensureCoreRuleOrder,
|
|
164
|
+
moveRuleBefore,
|
|
165
|
+
moveRuleAfter
|
|
166
|
+
}
|