@sushichan044/kg-core 0.11.1 → 0.11.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.
@@ -180,6 +180,26 @@ function assertNever(value) {
180
180
  throw new TypeError(`unhandled variant: ${JSON.stringify(value)}`);
181
181
  }
182
182
  //#endregion
183
+ //#region src/internal/japanese-punctuation.ts
184
+ /**
185
+ * JLReq cl-04 occupies a full em, including compound marks whose Unicode East Asian Width is
186
+ * neutral.
187
+ */
188
+ const DIVIDING_PUNCTUATION = "!?‼⁇⁈⁉";
189
+ /**
190
+ * The marks a short run of which is set as one tate-chu-yoko cell, `!?` reading as `⁉`.
191
+ */
192
+ const TATE_CHU_YOKO_MARKS = "!?!?";
193
+ const SENTENCE_ENDING_MARKS = `${DIVIDING_PUNCTUATION}♡♪☆`;
194
+ const validAdditionalSentenceEndingMarks = (value) => /^[\p{S}]*$/u.test(value);
195
+ /**
196
+ * Match one symbol grapheme, allowing its text or emoji variation selector.
197
+ */
198
+ function isSentenceEndingMark(grapheme, additionalMarks = "") {
199
+ const symbol = grapheme.replace(/\p{Variation_Selector}/gu, "");
200
+ return /^.$/u.test(symbol) && (SENTENCE_ENDING_MARKS + additionalMarks).includes(symbol);
201
+ }
202
+ //#endregion
183
203
  //#region src/internal/japanese-brackets.ts
184
204
  /**
185
205
  * Brackets and quotes a Japanese paragraph may open with. Halfwidth forms are included because a
@@ -198,17 +218,6 @@ function characterClass(characters) {
198
218
  return characters.replaceAll(/[\\\]^-]/gu, "\\$&");
199
219
  }
200
220
  //#endregion
201
- //#region src/internal/japanese-punctuation.ts
202
- /**
203
- * JLReq cl-04 occupies a full em, including compound marks whose Unicode East Asian Width is
204
- * neutral.
205
- */
206
- const DIVIDING_PUNCTUATION = "!?‼⁇⁈⁉";
207
- /**
208
- * The marks a short run of which is set as one tate-chu-yoko cell, `!?` reading as `⁉`.
209
- */
210
- const TATE_CHU_YOKO_MARKS = "!?!?";
211
- //#endregion
212
221
  //#region src/internal/japanese-spaces.ts
213
222
  const LEADING_SPACES = /^[  \t]+/u;
214
223
  /**
@@ -223,14 +232,7 @@ function leadingSpaces(text) {
223
232
  /**
224
233
  * Runs, not single marks: `!?` is one mark and takes one gap, so the gap belongs after the run.
225
234
  */
226
- const MARKS = new RegExp(`[${characterClass(DIVIDING_PUNCTUATION)}]+`, "gu");
227
- function analyzeMatch(line, marks) {
228
- const start = marks.index;
229
- if (start === void 0) return void 0;
230
- const marksRange = {
231
- start,
232
- end: start + marks[0].length
233
- };
235
+ function analyzeMatch(line, marksRange) {
234
236
  const next = line[marksRange.end];
235
237
  if (next === void 0 || "」』〗〉》))”\"’']]〕}}>>".includes(next)) return void 0;
236
238
  const spaces = leadingSpaces(line.slice(marksRange.end));
@@ -264,8 +266,22 @@ function analyzeMatch(line, marks) {
264
266
  * line. Runs at the end of the line or immediately before a closing bracket need no gap and are
265
267
  * omitted from the result.
266
268
  */
267
- function questionOrExclamationSpacings(line) {
268
- return [...line.matchAll(MARKS)].map((marks) => analyzeMatch(line, marks)).filter((spacing) => spacing !== void 0);
269
+ function questionOrExclamationSpacings(line, additionalSentenceEndingMarks = "") {
270
+ const result = [];
271
+ let marks;
272
+ const flush = () => {
273
+ if (marks === void 0) return;
274
+ const spacing = analyzeMatch(line, marks);
275
+ if (spacing !== void 0) result.push(spacing);
276
+ marks = void 0;
277
+ };
278
+ for (const { segment, index } of graphemeSegmenter.segment(line)) if (isSentenceEndingMark(segment, additionalSentenceEndingMarks)) marks = {
279
+ start: marks?.start ?? index,
280
+ end: index + segment.length
281
+ };
282
+ else flush();
283
+ flush();
284
+ return result;
269
285
  }
270
286
  //#endregion
271
- export { nonNegativeInteger as _, CLOSING_BRACKETS as a, assertNever as c, ManuscriptResult as d, ManuscriptDiagnostic as f, TextRange as g, ManuscriptRange as h, TATE_CHU_YOKO_MARKS as i, graphemeSegmenter as l, NamespacedId as m, leadingSpaces as n, OPENING_BRACKETS as o, DiagnosticSeverity as p, DIVIDING_PUNCTUATION as r, characterClass as s, questionOrExclamationSpacings as t, ValidationIssue as u, readonlyArray as v, readonlyObject as y };
287
+ export { ManuscriptRange as _, characterClass as a, readonlyArray as b, isSentenceEndingMark as c, graphemeSegmenter as d, ValidationIssue as f, NamespacedId as g, DiagnosticSeverity as h, OPENING_BRACKETS as i, validAdditionalSentenceEndingMarks as l, ManuscriptDiagnostic as m, leadingSpaces as n, SENTENCE_ENDING_MARKS as o, ManuscriptResult as p, CLOSING_BRACKETS as r, TATE_CHU_YOKO_MARKS as s, questionOrExclamationSpacings as t, assertNever as u, TextRange as v, readonlyObject as x, nonNegativeInteger as y };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sushichan044/kg-core",
3
- "version": "0.11.1",
3
+ "version": "0.11.2",
4
4
  "description": "Framework-independent parsing, composition, and proofreading for Japanese manuscripts",
5
5
  "keywords": [
6
6
  "japanese",