@peaceroad/markdown-it-cjk-breaks-mod 0.1.0 → 0.1.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.
Files changed (3) hide show
  1. package/README.md +2 -2
  2. package/index.js +193 -77
  3. package/package.json +8 -3
package/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  ### Punctuation spacing options
6
6
 
7
- Fine-tune the trigger list with `spaceAfterPunctuationTargets`. Provide either a single string or an array and every exact match becomes eligible for automatic spacing. Defaults remain `['!', '?', '⁉', '!?', '?!', '!?', '?!']`.
7
+ Fine-tune the trigger list with `spaceAfterPunctuationTargets`. Provide either a single string or an array and every exact match becomes eligible for automatic spacing; this option replaces the defaults. Defaults remain `['!', '?', '⁉', '!?', '?!', '!?', '?!', '.', ':']`. To disable punctuation spacing while still setting `spaceAfterPunctuation`, pass `spaceAfterPunctuationTargets: []` (or `null`/`false`). Use `spaceAfterPunctuationTargetsAdd` to append triggers and `spaceAfterPunctuationTargetsRemove` to drop items from the resolved list.
8
8
 
9
9
  Use `spaceAfterPunctuation` to inject a space every time this plugin suppresses a line break after punctuation. Accepts `'half'` for ASCII space, `'full'` for an ideographic space, or any custom string via a literal value.
10
10
 
@@ -125,4 +125,4 @@ ui
125
125
 
126
126
  - markdown-it/markdown-it-cjk-breaks: [MIT](https://github.com/markdown-it/markdown-it-cjk-breaks/blob/master/LICENSE)
127
127
  - @sup39/markdown-it-cjk-breaks: [MIT](https://www.npmjs.com/package/@sup39/markdown-it-cjk-breaks?activeTab=code)
128
- - @peaceroad/markdown-it-cjk-breaks-mod: [MIT](https://github.com/peaceroad/p7d-markdown-it-cjk-breaks-mod/blob/main/LICENSE
128
+ - @peaceroad/markdown-it-cjk-breaks-mod: [MIT](https://github.com/peaceroad/p7d-markdown-it-cjk-breaks-mod/blob/main/LICENSE)
package/index.js CHANGED
@@ -4,7 +4,7 @@ const { eastAsianWidth } = eastAsianWidthModule;
4
4
  const ASCII_PRINTABLE_MIN = 0x21;
5
5
  const ASCII_PRINTABLE_MAX = 0x7E;
6
6
  const IDEOGRAPHIC_SPACE = '\u3000';
7
- const DEFAULT_PUNCTUATION_TARGETS = ['!', '?', '⁉', '!?', '?!', '!?', '?!'];
7
+ const DEFAULT_PUNCTUATION_TARGETS = ['!', '?', '⁉', '!?', '?!', '!?', '?!', '.', ':'];
8
8
  const DEFAULT_PUNCTUATION_CONFIG = create_punctuation_config(DEFAULT_PUNCTUATION_TARGETS);
9
9
 
10
10
 
@@ -48,14 +48,60 @@ function resolve_punctuation_space_option(opts) {
48
48
 
49
49
 
50
50
  function resolve_punctuation_targets(opts) {
51
- if (!opts || !opts.spaceAfterPunctuationTargets) return DEFAULT_PUNCTUATION_CONFIG;
51
+ if (!opts) return DEFAULT_PUNCTUATION_CONFIG;
52
+
53
+ var hasCustomTargets = Object.prototype.hasOwnProperty.call(opts, 'spaceAfterPunctuationTargets');
54
+ var baseTargets;
55
+
56
+ if (!hasCustomTargets) {
57
+ baseTargets = DEFAULT_PUNCTUATION_TARGETS.slice();
58
+ } else {
59
+ var customTargets = opts.spaceAfterPunctuationTargets;
60
+ if (customTargets === null || customTargets === false) return null;
61
+ if (typeof customTargets === 'string') {
62
+ if (customTargets.length === 0) return null;
63
+ baseTargets = [ customTargets ];
64
+ } else if (Array.isArray(customTargets)) {
65
+ if (customTargets.length === 0) return null;
66
+ baseTargets = customTargets.slice();
67
+ } else {
68
+ baseTargets = DEFAULT_PUNCTUATION_TARGETS.slice();
69
+ }
70
+ }
71
+
72
+ var addTargets = opts.spaceAfterPunctuationTargetsAdd;
73
+ if (addTargets !== undefined) {
74
+ var addList = [];
75
+ if (typeof addTargets === 'string') {
76
+ if (addTargets.length > 0) addList = [ addTargets ];
77
+ } else if (Array.isArray(addTargets)) {
78
+ addList = addTargets;
79
+ }
80
+ if (addList.length > 0) {
81
+ baseTargets = baseTargets.concat(addList);
82
+ }
83
+ }
52
84
 
53
- var customTargets = opts.spaceAfterPunctuationTargets;
54
- if (typeof customTargets === 'string') customTargets = [ customTargets ];
55
- if (!Array.isArray(customTargets) || customTargets.length === 0) return DEFAULT_PUNCTUATION_CONFIG;
85
+ var removeTargets = opts.spaceAfterPunctuationTargetsRemove;
86
+ if (removeTargets !== undefined) {
87
+ var removeList = [];
88
+ if (typeof removeTargets === 'string') {
89
+ if (removeTargets.length > 0) removeList = [ removeTargets ];
90
+ } else if (Array.isArray(removeTargets)) {
91
+ removeList = removeTargets;
92
+ }
93
+ if (removeList.length > 0) {
94
+ var removeConfig = create_punctuation_config(removeList);
95
+ if (removeConfig.sequences.size > 0) {
96
+ baseTargets = baseTargets.filter(function (target) {
97
+ return !removeConfig.sequences.has(target);
98
+ });
99
+ }
100
+ }
101
+ }
56
102
 
57
- var config = create_punctuation_config(customTargets);
58
- return config.sequences.size === 0 ? DEFAULT_PUNCTUATION_CONFIG : config;
103
+ var config = create_punctuation_config(baseTargets);
104
+ return config.sequences.size === 0 ? null : config;
59
105
  }
60
106
 
61
107
 
@@ -94,96 +140,153 @@ function get_cjk_width_class(ch) {
94
140
  return width === 'F' || width === 'W' || width === 'H' ? width : '';
95
141
  }
96
142
 
143
+ function build_next_text_info(tokens) {
144
+ var nextTextIndex = new Array(tokens.length);
145
+ var nextSkippedEmpty = new Array(tokens.length);
146
+ var nextNonEmpty = -1;
147
+ var sawEmpty = false;
148
+
149
+ for (var idx = tokens.length - 1; idx >= 0; idx--) {
150
+ nextTextIndex[idx] = nextNonEmpty;
151
+ nextSkippedEmpty[idx] = sawEmpty;
152
+
153
+ var token = tokens[idx];
154
+ if (!token || token.type !== 'text') continue;
155
+
156
+ if (!token.content) {
157
+ sawEmpty = true;
158
+ continue;
159
+ }
160
+
161
+ nextNonEmpty = idx;
162
+ sawEmpty = false;
163
+ }
164
+
165
+ return {
166
+ nextTextIndex: nextTextIndex,
167
+ nextSkippedEmpty: nextSkippedEmpty
168
+ };
169
+ }
170
+
97
171
 
98
172
  function process_inlines(tokens, state, ctx, inlineToken) {
99
- var i, j, last, trailing, next, c1, c2, remove_break;
173
+ var i, last, trailing, next, c1, c2, remove_break;
100
174
  var either = ctx.either;
101
175
  var normalizeSoftBreaks = ctx.normalizeSoftBreaks;
102
176
  var punctuationSpace = ctx.punctuationSpace;
103
177
  var punctuationConfig = ctx.punctuationConfig;
104
178
  var maxPunctuationLength = ctx.maxPunctuationLength;
105
179
  var considerInlineBoundaries = ctx.considerInlineBoundaries;
180
+ var needsPunctuation = punctuationSpace && punctuationConfig && maxPunctuationLength > 0;
106
181
 
107
182
  if (normalizeSoftBreaks) normalize_text_tokens(tokens);
108
183
 
109
- for (i = 0; i < tokens.length; i++) {
110
- var isSoftbreakToken = tokens[i].type === 'softbreak';
111
- var isTextBreakToken = tokens[i].type === 'text' && tokens[i].content === '\n';
112
- if (!isSoftbreakToken && !isTextBreakToken) continue;
113
-
114
- // default last/next character to space
115
- last = next = ' ';
116
- trailing = '';
117
- var trailingMatchesPunctuation = false;
118
-
119
- var skippedEmptyBefore = false;
120
- var skippedEmptyAfter = false;
184
+ var nextInfo = build_next_text_info(tokens);
185
+ var nextTextIndex = nextInfo.nextTextIndex;
186
+ var nextSkippedEmpty = nextInfo.nextSkippedEmpty;
187
+
188
+ var widthCache = Object.create(null);
189
+ function get_cached_width_class(ch) {
190
+ if (!ch) return '';
191
+ var cached = widthCache[ch];
192
+ if (cached !== undefined) return cached;
193
+ var width = get_cjk_width_class(ch);
194
+ widthCache[ch] = width;
195
+ return width;
196
+ }
121
197
 
122
- for (j = i - 1; j >= 0; j--) {
123
- if (tokens[j].type !== 'text') continue;
198
+ var lastTextContent = '';
199
+ var hasLastText = false;
200
+ var sawEmptySinceLast = false;
124
201
 
125
- var textContent = tokens[j].content;
126
- if (!textContent) {
127
- skippedEmptyBefore = true;
128
- continue;
129
- }
130
- c1 = textContent.charCodeAt(textContent.length - 2);
131
- c2 = textContent.charCodeAt(textContent.length - 1);
132
-
133
- last = textContent.slice(is_surrogate(c1, c2) ? -2 : -1);
134
- trailing = maxPunctuationLength > 0 ?
135
- textContent.slice(-maxPunctuationLength) :
136
- textContent.slice(-1);
137
- if (!trailingMatchesPunctuation && punctuationSpace && punctuationConfig && maxPunctuationLength > 0 && trailing) {
138
- trailingMatchesPunctuation = matches_punctuation_sequence(trailing, punctuationConfig);
202
+ for (i = 0; i < tokens.length; i++) {
203
+ var token = tokens[i];
204
+ var isSoftbreakToken = token.type === 'softbreak';
205
+ var isTextBreakToken = token.type === 'text' && token.content === '\n';
206
+ if (isSoftbreakToken || isTextBreakToken) {
207
+ // default last/next character to space
208
+ last = next = ' ';
209
+ trailing = '';
210
+ var trailingMatchesPunctuation = false;
211
+
212
+ var skippedEmptyBefore = sawEmptySinceLast;
213
+ var skippedEmptyAfter = nextSkippedEmpty[i];
214
+
215
+ if (hasLastText) {
216
+ c1 = lastTextContent.charCodeAt(lastTextContent.length - 2);
217
+ c2 = lastTextContent.charCodeAt(lastTextContent.length - 1);
218
+ last = lastTextContent.slice(is_surrogate(c1, c2) ? -2 : -1);
219
+ if (needsPunctuation) {
220
+ trailing = lastTextContent.slice(-maxPunctuationLength);
221
+ trailingMatchesPunctuation = matches_punctuation_sequence(trailing, punctuationConfig);
222
+ }
139
223
  }
140
- break;
141
- }
142
224
 
143
- for (j = i + 1; j < tokens.length; j++) {
144
- if (tokens[j].type !== 'text') continue;
145
-
146
- if (!tokens[j].content) {
147
- skippedEmptyAfter = true;
148
- continue;
225
+ var nextIdx = nextTextIndex[i];
226
+ if (nextIdx !== -1) {
227
+ var nextContent = tokens[nextIdx].content || '';
228
+ if (nextContent) {
229
+ c1 = nextContent.charCodeAt(0);
230
+ c2 = nextContent.charCodeAt(1);
231
+ next = nextContent.slice(0, is_surrogate(c1, c2) ? 2 : 1);
232
+ }
149
233
  }
150
234
 
151
- c1 = tokens[j].content.charCodeAt(0);
152
- c2 = tokens[j].content.charCodeAt(1);
235
+ remove_break = false;
153
236
 
154
- next = tokens[j].content.slice(0, is_surrogate(c1, c2) ? 2 : 1);
155
- break;
156
- }
237
+ // remove newline if it's adjacent to ZWSP
238
+ if (last === '\u200b' || next === '\u200b') remove_break = true;
157
239
 
158
- remove_break = false;
240
+ var lastWidthClass = '';
241
+ var nextWidthClass = '';
159
242
 
160
- // remove newline if it's adjacent to ZWSP
161
- if (last === '\u200b' || next === '\u200b') remove_break = true;
243
+ // remove newline if both characters AND/OR fullwidth (F), wide (W) or
244
+ // halfwidth (H), but not Hangul
245
+ var tLast = false;
246
+ var tNext = false;
162
247
 
163
- var lastWidthClass = get_cjk_width_class(last);
164
- var nextWidthClass = get_cjk_width_class(next);
248
+ var needsWidthForRemoval = !remove_break;
249
+ var needsWidthForPunctuation = punctuationSpace && trailingMatchesPunctuation && last && next && next !== '\u200b';
165
250
 
166
- // remove newline if both characters AND/OR fullwidth (F), wide (W) or
167
- // halfwidth (H), but not Hangul
168
- var tLast = lastWidthClass !== '';
169
- var tNext = nextWidthClass !== '';
251
+ if (needsWidthForRemoval || needsWidthForPunctuation) {
252
+ if (needsWidthForRemoval) {
253
+ lastWidthClass = get_cached_width_class(last);
254
+ }
255
+ nextWidthClass = get_cached_width_class(next);
256
+
257
+ if (needsWidthForRemoval) {
258
+ tLast = lastWidthClass !== '';
259
+ tNext = nextWidthClass !== '';
260
+
261
+ if (considerInlineBoundaries && (skippedEmptyBefore || skippedEmptyAfter) && tLast && tNext) {
262
+ tLast = false;
263
+ tNext = false;
264
+ }
265
+ if (either ? tLast || tNext : tLast && tNext) {
266
+ if (!is_hangul(last) && !is_hangul(next)) remove_break = true;
267
+ }
268
+ }
269
+ }
170
270
 
171
- if (considerInlineBoundaries && (skippedEmptyBefore || skippedEmptyAfter) && tLast && tNext) {
172
- tLast = false;
173
- tNext = false;
174
- }
175
- if (either ? tLast || tNext : tLast && tNext) {
176
- if (!is_hangul(last) && !is_hangul(next)) remove_break = true;
271
+ if (remove_break) {
272
+ var insertPunctuationSpace = false;
273
+ if (punctuationSpace && punctuationConfig && trailingMatchesPunctuation && last && next && next !== '\u200b') {
274
+ var nextIsFullwidthOrWide = nextWidthClass === 'F' || nextWidthClass === 'W';
275
+ if (is_printable_ascii(next) || nextIsFullwidthOrWide) insertPunctuationSpace = true;
276
+ }
277
+ token.type = 'text';
278
+ token.content = insertPunctuationSpace ? punctuationSpace : '';
279
+ }
177
280
  }
178
281
 
179
- if (remove_break) {
180
- var insertPunctuationSpace = false;
181
- var nextIsFullwidthOrWide = nextWidthClass === 'F' || nextWidthClass === 'W';
182
- if (punctuationSpace && punctuationConfig && trailingMatchesPunctuation && last && next && next !== '\u200b') {
183
- if (is_printable_ascii(next) || nextIsFullwidthOrWide) insertPunctuationSpace = true;
282
+ if (token.type === 'text') {
283
+ if (!token.content) {
284
+ sawEmptySinceLast = true;
285
+ } else {
286
+ lastTextContent = token.content;
287
+ hasLastText = true;
288
+ sawEmptySinceLast = false;
184
289
  }
185
- tokens[i].type = 'text';
186
- tokens[i].content = insertPunctuationSpace ? punctuationSpace : '';
187
290
  }
188
291
  }
189
292
 
@@ -194,16 +297,29 @@ function process_inlines(tokens, state, ctx, inlineToken) {
194
297
 
195
298
 
196
299
  function normalize_text_tokens(tokens) {
300
+ var normalized = null;
301
+
197
302
  for (var idx = 0; idx < tokens.length; idx++) {
198
303
  var token = tokens[idx];
199
- if (token.type !== 'text') continue;
200
- if (!token.content || token.content.indexOf('\n') === -1) continue;
304
+ if (token.type !== 'text' || !token.content || token.content.indexOf('\n') === -1) {
305
+ if (normalized) normalized.push(token);
306
+ continue;
307
+ }
308
+
309
+ if (!normalized) {
310
+ normalized = tokens.slice(0, idx);
311
+ }
201
312
 
202
313
  var replacement = split_text_token(token);
203
- tokens.splice(idx, 1, replacement[0]);
204
- if (replacement.length > 1) {
205
- Array.prototype.splice.apply(tokens, [idx + 1, 0].concat(replacement.slice(1)));
206
- idx += replacement.length - 1;
314
+ for (var r = 0; r < replacement.length; r++) {
315
+ normalized.push(replacement[r]);
316
+ }
317
+ }
318
+
319
+ if (normalized) {
320
+ tokens.length = 0;
321
+ for (var j = 0; j < normalized.length; j++) {
322
+ tokens.push(normalized[j]);
207
323
  }
208
324
  }
209
325
  }
package/package.json CHANGED
@@ -1,10 +1,14 @@
1
1
  {
2
2
  "name": "@peaceroad/markdown-it-cjk-breaks-mod",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "type": "module",
5
5
  "description": "Suppress linebreaks between east asian (Especially Japanese) characters",
6
- "repository": "https://github.com/peaceroad/markdown-it-cjk-breaks-mod.git",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/peaceroad/p7d-markdown-it-cjk-breaks-mod.git"
9
+ },
7
10
  "license": "MIT",
11
+ "author": "peaceroad <peaceroad@gmail.com>",
8
12
  "scripts": {
9
13
  "test": "node test/test.js"
10
14
  },
@@ -13,11 +17,12 @@
13
17
  "README.md",
14
18
  "LICENSE"
15
19
  ],
20
+ "main": "index.js",
16
21
  "dependencies": {
17
22
  "eastasianwidth": "^0.3.0"
18
23
  },
19
24
  "devDependencies": {
20
- "@peaceroad/markdown-it-strong-ja": "^0.5.3",
25
+ "@peaceroad/markdown-it-strong-ja": "^0.5.5",
21
26
  "markdown-it": "^14.1.0"
22
27
  }
23
28
  }