@sushichan044/kg-core 0.3.0 → 0.4.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/dist/index.mjs CHANGED
@@ -513,17 +513,9 @@ function parseManuscript(source, options = {}) {
513
513
  });
514
514
  }
515
515
  //#endregion
516
- //#region src/parser/internal/pixiv-notation.ts
517
- function trimContent(text, sourceStart) {
518
- const trimmedStart = text.trimStart();
519
- const leadingLength = text.length - trimmedStart.length;
520
- return {
521
- text: trimmedStart.trimEnd(),
522
- sourceStart: sourceStart + leadingLength
523
- };
524
- }
516
+ //#region src/parser/internal/line-end.ts
525
517
  /**
526
- * Tags never span lines, so a candidate closing delimiter past the line end does not count.
518
+ * Returns the first line terminator at or after start. Both halves of CRLF remain outside the line.
527
519
  */
528
520
  function lineEnd(source, start) {
529
521
  const lf = source.indexOf("\n", start);
@@ -532,6 +524,249 @@ function lineEnd(source, start) {
532
524
  if (cr === -1) return lf;
533
525
  return Math.min(lf, cr);
534
526
  }
527
+ //#endregion
528
+ //#region src/parser/internal/notation-warning.ts
529
+ function firstCoveredIndex(manuscript, start) {
530
+ let low = 0;
531
+ let high = manuscript.graphemes.length;
532
+ while (low < high) {
533
+ const middle = Math.floor((low + high) / 2);
534
+ const grapheme = manuscript.graphemes[middle];
535
+ if (grapheme === void 0) return manuscript.graphemes.length;
536
+ if (grapheme.range.source.end <= start) low = middle + 1;
537
+ else high = middle;
538
+ }
539
+ return low;
540
+ }
541
+ function lastCoveredIndex(manuscript, end) {
542
+ let low = 0;
543
+ let high = manuscript.graphemes.length;
544
+ while (low < high) {
545
+ const middle = Math.floor((low + high) / 2);
546
+ const grapheme = manuscript.graphemes[middle];
547
+ if (grapheme === void 0) return -1;
548
+ if (grapheme.range.source.start < end) low = middle + 1;
549
+ else high = middle;
550
+ }
551
+ return low - 1;
552
+ }
553
+ function spanRange(manuscript, span) {
554
+ const first = manuscript.graphemes[firstCoveredIndex(manuscript, span.start)];
555
+ const last = manuscript.graphemes[lastCoveredIndex(manuscript, span.end)];
556
+ if (first === void 0 || last === void 0 || first.range.source.start >= span.end || last.range.source.end <= span.start) return ManuscriptRange.of({
557
+ source: span,
558
+ display: {
559
+ start: 0,
560
+ end: 0
561
+ },
562
+ graphemes: {
563
+ start: 0,
564
+ end: 0
565
+ }
566
+ });
567
+ return ManuscriptRange.of({
568
+ source: span,
569
+ display: {
570
+ start: first.range.display.start,
571
+ end: last.range.display.end
572
+ },
573
+ graphemes: {
574
+ start: first.range.graphemes.start,
575
+ end: last.range.graphemes.end
576
+ }
577
+ });
578
+ }
579
+ function unrecognizedNotationWarnings(source, parserId, manuscript, spans) {
580
+ return spans.map((span, order) => ManuscriptDiagnostic.of({
581
+ source,
582
+ origin: {
583
+ kind: "parser",
584
+ id: parserId
585
+ },
586
+ severity: "warning",
587
+ code: `unrecognized-notation-${order}`,
588
+ message: "認識できない記法を原文として扱いました",
589
+ range: spanRange(manuscript, span)
590
+ }));
591
+ }
592
+ //#endregion
593
+ //#region src/parser/kakuyomu-parser.ts
594
+ const PARSER_ID$1 = NamespacedId.of("kg/kakuyomu");
595
+ const MAX_BASE_GRAPHEMES = 20;
596
+ const MAX_READING_GRAPHEMES = 50;
597
+ const HAN = /^\p{Script=Han}$/u;
598
+ function graphemeLength(text) {
599
+ return [...graphemeSegmenter.segment(text)].length;
600
+ }
601
+ function validRuby(parent, reading) {
602
+ return parent.length > 0 && reading.length > 0 && !parent.includes("《") && !parent.includes("》") && !reading.includes("《") && !reading.includes("》") && graphemeLength(parent) <= MAX_BASE_GRAPHEMES && graphemeLength(reading) <= MAX_READING_GRAPHEMES;
603
+ }
604
+ function parseRuby(source, parentStart, parentEnd, opening) {
605
+ const endOfLine = lineEnd(source, opening);
606
+ const closing = source.indexOf("》", opening + 1);
607
+ if (closing === -1 || closing >= endOfLine) return void 0;
608
+ const parent = source.slice(parentStart, parentEnd);
609
+ const reading = source.slice(opening + 1, closing);
610
+ if (!validRuby(parent, reading)) return void 0;
611
+ return {
612
+ parentStart,
613
+ parentEnd,
614
+ reading,
615
+ end: closing + 1
616
+ };
617
+ }
618
+ function parseExplicitRuby(source, start) {
619
+ const endOfLine = lineEnd(source, start + 1);
620
+ const opening = source.indexOf("《", start + 1);
621
+ if (opening === -1 || opening >= endOfLine) return void 0;
622
+ return parseRuby(source, start + 1, opening, opening);
623
+ }
624
+ function parseEmphasis(source, start) {
625
+ const endOfLine = lineEnd(source, start + 2);
626
+ const closing = source.indexOf("》》", start + 2);
627
+ if (closing === -1 || closing >= endOfLine) return void 0;
628
+ const content = source.slice(start + 2, closing);
629
+ if (content.length === 0 || content.includes("《") || content.includes("》")) return void 0;
630
+ return {
631
+ contentStart: start + 2,
632
+ contentEnd: closing,
633
+ end: closing + 2
634
+ };
635
+ }
636
+ function codePointLength(source, index) {
637
+ const codePoint = source.codePointAt(index);
638
+ return codePoint === void 0 || codePoint <= 65535 ? 1 : 2;
639
+ }
640
+ function isHanAt(source, index) {
641
+ return HAN.test(source.slice(index, index + codePointLength(source, index)));
642
+ }
643
+ function invalidNotationEnd(source, start) {
644
+ return lineEnd(source, start + 1);
645
+ }
646
+ /**
647
+ * Understands Kakuyomu's ruby and emphasis-mark notation. Invalid notation stays literal so source
648
+ * ranges remain inspectable, while clear notation candidates receive a parser warning.
649
+ */
650
+ const kakuyomuParser = {
651
+ id: PARSER_ID$1,
652
+ parse: (source) => {
653
+ const draft = createManuscriptDraft(source);
654
+ const unrecognized = [];
655
+ let index = 0;
656
+ let literalStart = 0;
657
+ let hanRunStart = -1;
658
+ const appendBefore = (end) => {
659
+ if (literalStart < end) draft.append(source.slice(literalStart, end), literalStart);
660
+ };
661
+ const appendNotationWarning = (start, end) => {
662
+ unrecognized.push({
663
+ start,
664
+ end
665
+ });
666
+ };
667
+ while (index < source.length) {
668
+ const character = source[index];
669
+ if (character === "|" || character === "|") {
670
+ if (source[index + 1] === "《") {
671
+ appendBefore(index);
672
+ draft.append("《", index + 1);
673
+ index += 2;
674
+ literalStart = index;
675
+ hanRunStart = -1;
676
+ continue;
677
+ }
678
+ const ruby = parseExplicitRuby(source, index);
679
+ if (ruby !== void 0) {
680
+ appendBefore(index);
681
+ const range = draft.append(source.slice(ruby.parentStart, ruby.parentEnd), ruby.parentStart);
682
+ draft.annotate({
683
+ kind: "ruby",
684
+ reading: ruby.reading,
685
+ range: {
686
+ ...range,
687
+ source: {
688
+ start: index,
689
+ end: ruby.end
690
+ }
691
+ }
692
+ });
693
+ index = ruby.end;
694
+ literalStart = index;
695
+ hanRunStart = -1;
696
+ continue;
697
+ }
698
+ const opening = source.indexOf("《", index + 1);
699
+ if (opening !== -1 && opening < lineEnd(source, index + 1)) {
700
+ const end = invalidNotationEnd(source, index);
701
+ appendNotationWarning(index, end);
702
+ index = end;
703
+ hanRunStart = -1;
704
+ continue;
705
+ }
706
+ }
707
+ if (character === "《") {
708
+ const emphasis = source.startsWith("《《", index) ? parseEmphasis(source, index) : void 0;
709
+ if (emphasis !== void 0) {
710
+ appendBefore(index);
711
+ const range = draft.append(source.slice(emphasis.contentStart, emphasis.contentEnd), emphasis.contentStart);
712
+ draft.annotate({
713
+ kind: "emphasis",
714
+ mark: "・",
715
+ range: {
716
+ ...range,
717
+ source: {
718
+ start: index,
719
+ end: emphasis.end
720
+ }
721
+ }
722
+ });
723
+ index = emphasis.end;
724
+ literalStart = index;
725
+ hanRunStart = -1;
726
+ continue;
727
+ }
728
+ const ruby = hanRunStart === -1 ? void 0 : parseRuby(source, hanRunStart, index, index);
729
+ if (ruby !== void 0) {
730
+ appendBefore(hanRunStart);
731
+ const range = draft.append(source.slice(ruby.parentStart, ruby.parentEnd), ruby.parentStart);
732
+ draft.annotate({
733
+ kind: "ruby",
734
+ reading: ruby.reading,
735
+ range: {
736
+ ...range,
737
+ source: {
738
+ start: ruby.parentStart,
739
+ end: ruby.end
740
+ }
741
+ }
742
+ });
743
+ index = ruby.end;
744
+ literalStart = index;
745
+ hanRunStart = -1;
746
+ continue;
747
+ }
748
+ if (source.startsWith("《《", index) || hanRunStart !== -1) appendNotationWarning(hanRunStart === -1 ? index : hanRunStart, invalidNotationEnd(source, index));
749
+ hanRunStart = -1;
750
+ } else if (isHanAt(source, index)) {
751
+ if (hanRunStart === -1) hanRunStart = index;
752
+ } else hanRunStart = -1;
753
+ index += codePointLength(source, index);
754
+ }
755
+ appendBefore(source.length);
756
+ const manuscript = draft.build();
757
+ return ManuscriptResult.succeed(manuscript, unrecognizedNotationWarnings(source, PARSER_ID$1, manuscript, unrecognized));
758
+ }
759
+ };
760
+ //#endregion
761
+ //#region src/parser/internal/pixiv-notation.ts
762
+ function trimContent(text, sourceStart) {
763
+ const trimmedStart = text.trimStart();
764
+ const leadingLength = text.length - trimmedStart.length;
765
+ return {
766
+ text: trimmedStart.trimEnd(),
767
+ sourceStart: sourceStart + leadingLength
768
+ };
769
+ }
535
770
  function closingIndex(source, start, closing) {
536
771
  const end = source.indexOf(closing, start);
537
772
  return end !== -1 && end < lineEnd(source, start) ? end : void 0;
@@ -607,49 +842,6 @@ function literalEnd(source, start) {
607
842
  //#region src/parser/pixiv-parser.ts
608
843
  const PARSER_ID = NamespacedId.of("kg/pixiv");
609
844
  /**
610
- * Locates an unrecognised source span in display and grapheme coordinates for the warning.
611
- */
612
- function spanRange(manuscript, span) {
613
- const covered = manuscript.graphemes.filter(({ range }) => range.source.start < span.end && range.source.end > span.start);
614
- const first = covered[0];
615
- const last = covered.at(-1);
616
- if (first === void 0 || last === void 0) return ManuscriptRange.of({
617
- source: span,
618
- display: {
619
- start: 0,
620
- end: 0
621
- },
622
- graphemes: {
623
- start: 0,
624
- end: 0
625
- }
626
- });
627
- return ManuscriptRange.of({
628
- source: span,
629
- display: {
630
- start: first.range.display.start,
631
- end: last.range.display.end
632
- },
633
- graphemes: {
634
- start: first.range.graphemes.start,
635
- end: last.range.graphemes.end
636
- }
637
- });
638
- }
639
- function unrecognizedNotationWarning(source, manuscript, span, index) {
640
- return ManuscriptDiagnostic.of({
641
- source,
642
- origin: {
643
- kind: "parser",
644
- id: PARSER_ID
645
- },
646
- severity: "warning",
647
- code: `unrecognized-notation-${index}`,
648
- message: "認識できない記法を原文として扱いました",
649
- range: spanRange(manuscript, span)
650
- });
651
- }
652
- /**
653
845
  * Understands Pixiv's ruby, bold, italic and emphasis-mark notation. Anything bracket-shaped that
654
846
  * does not parse is kept as literal text and reported as a warning rather than dropped.
655
847
  */
@@ -692,7 +884,7 @@ const pixivParser = {
692
884
  index = end;
693
885
  }
694
886
  const manuscript = draft.build();
695
- return ManuscriptResult.succeed(manuscript, unrecognized.map((span, order) => unrecognizedNotationWarning(source, manuscript, span, order)));
887
+ return ManuscriptResult.succeed(manuscript, unrecognizedNotationWarnings(source, PARSER_ID, manuscript, unrecognized));
696
888
  }
697
889
  };
698
890
  //#endregion
@@ -1497,4 +1689,4 @@ function createDefaultProofreadingRules() {
1497
1689
  ];
1498
1690
  }
1499
1691
  //#endregion
1500
- export { BoldAnnotation, ComposeError, CompositionStatistics, DiagnosticOrigin, DiagnosticSeverity, DisplayRange, EmphasisAnnotation, FontPreset, FontPresetId, FontSizePt, GraphemeRange, GridCell, GridLine, GridPage, GridSettings, GridStage, InvalidRuleOptions, ItalicAnnotation, LineOffset, ManuscriptAnnotation, ManuscriptAppearanceSettings, ManuscriptCompositionSettings, ManuscriptDiagnostic, ManuscriptGeometry, ManuscriptGridLayout, ManuscriptOffsets, ManuscriptRange, ManuscriptResult, NamespacedId, PaperSize, PaperSizeId, ParseError, ParsedGrapheme, ParsedManuscript, ProofreadError, ProofreadingReport, ProofreadingRuleMeta, Rejection, RubyAnnotation, SourcePosition, SourceRange, TextRange, ValidationIssue, assertNever, composeManuscript, createDefaultProofreadingRules, createMaxArabicNumeralDigitsRule, createParagraphLeadingCharacterRule, evenDashRule, evenEllipsisRule, manuscriptGridComposer, maxArabicNumeralDigitsRule, minusBeforeNumberRule, mmToPt, noConsecutiveChoonpuRule, noConsecutiveInterpunctRule, noConsecutivePunctuationRule, paragraphLeadingCharacterRule, parseManuscript, pixivParser, plainTextParser, proofreadManuscript, ptToMm, punctuationBeforeClosingQuoteRule, spaceAfterQuestionOrExclamationRule, variantCharacterRule };
1692
+ export { BoldAnnotation, ComposeError, CompositionStatistics, DiagnosticOrigin, DiagnosticSeverity, DisplayRange, EmphasisAnnotation, FontPreset, FontPresetId, FontSizePt, GraphemeRange, GridCell, GridLine, GridPage, GridSettings, GridStage, InvalidRuleOptions, ItalicAnnotation, LineOffset, ManuscriptAnnotation, ManuscriptAppearanceSettings, ManuscriptCompositionSettings, ManuscriptDiagnostic, ManuscriptGeometry, ManuscriptGridLayout, ManuscriptOffsets, ManuscriptRange, ManuscriptResult, NamespacedId, PaperSize, PaperSizeId, ParseError, ParsedGrapheme, ParsedManuscript, ProofreadError, ProofreadingReport, ProofreadingRuleMeta, Rejection, RubyAnnotation, SourcePosition, SourceRange, TextRange, ValidationIssue, assertNever, composeManuscript, createDefaultProofreadingRules, createMaxArabicNumeralDigitsRule, createParagraphLeadingCharacterRule, evenDashRule, evenEllipsisRule, kakuyomuParser, manuscriptGridComposer, maxArabicNumeralDigitsRule, minusBeforeNumberRule, mmToPt, noConsecutiveChoonpuRule, noConsecutiveInterpunctRule, noConsecutivePunctuationRule, paragraphLeadingCharacterRule, parseManuscript, pixivParser, plainTextParser, proofreadManuscript, ptToMm, punctuationBeforeClosingQuoteRule, spaceAfterQuestionOrExclamationRule, variantCharacterRule };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sushichan044/kg-core",
3
- "version": "0.3.0",
3
+ "version": "0.4.1",
4
4
  "description": "Framework-independent parsing, composition, and proofreading for Japanese manuscripts",
5
5
  "keywords": [
6
6
  "japanese",