@sushichan044/kg-core 0.5.0 → 0.6.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 CHANGED
@@ -94,8 +94,8 @@ or recalculate locations.
94
94
  `createDefaultProofreadingRules()` returns the rules whose answer does not
95
95
  depend on the work: how a paragraph opens and how far it is indented,
96
96
  punctuation before a closing bracket, spacing after `!` and `?`, ellipsis and
97
- dash forms and counts, repeated punctuation, repeated interpuncts and choonpu, a
98
- minus sign that no number follows, halfwidth Japanese punctuation, Arabic
97
+ dash forms and counts, repeated punctuation and interpuncts, a minus sign that
98
+ no number follows, halfwidth Japanese punctuation, Arabic
99
99
  numeral length, and Unicode variation sequences.
100
100
 
101
101
  Rules that depend on the work's own conventions are exported individually and
@@ -122,6 +122,11 @@ const rules = [
122
122
  kana pairs; like every configurable rule it returns a `ManuscriptResult` and
123
123
  fails with `InvalidRuleOptions` rather than dropping bad options.
124
124
 
125
+ `dashRule()` prefers `―` (U+2015). Use `createDashRule({ preferred: "—" })` or
126
+ `createDashRule({ preferred: "─" })` when a work uses U+2014 or U+2500 instead.
127
+ The selected character must appear in even-length runs. Repeated choonpu (`ー`)
128
+ are not treated as dashes.
129
+
125
130
  Diagnostics do not modify the source and do not contain automatic fixes.
126
131
 
127
132
  ## Browser support
package/dist/index.d.mts CHANGED
@@ -14657,12 +14657,14 @@ declare const consistentLatinWidthRule: () => ParsedProofreadingRule;
14657
14657
  //#region src/proofreading/rules/consistent-numeral-width.d.ts
14658
14658
  declare const consistentNumeralWidthRule: () => ParsedProofreadingRule;
14659
14659
  //#endregion
14660
- //#region src/proofreading/rules/dash-character.d.ts
14661
- /**
14662
- * Stand-ins for `―`. Repeated `ー` belongs to `kg/no-consecutive-choonpu` and `−` to
14663
- * `kg/minus-before-number`, so neither is reported twice.
14664
- */
14665
- declare const dashCharacterRule: () => ParsedProofreadingRule;
14660
+ //#region src/proofreading/rules/dash.d.ts
14661
+ declare const DashCharacterSchema: v.PicklistSchema<readonly ["―", "—", "─"], undefined>;
14662
+ type DashCharacter = v.InferOutput<typeof DashCharacterSchema>;
14663
+ type DashOptions = Readonly<{
14664
+ preferred?: DashCharacter;
14665
+ }>;
14666
+ declare const dashRule: () => ParsedProofreadingRule;
14667
+ declare function createDashRule(options?: DashOptions): ManuscriptResult<ParsedProofreadingRule, InvalidRuleOptions>;
14666
14668
  //#endregion
14667
14669
  //#region src/proofreading/rules/default-rules.d.ts
14668
14670
  /**
@@ -14681,9 +14683,6 @@ declare function createDefaultProofreadingRules(): readonly ParsedProofreadingRu
14681
14683
  */
14682
14684
  declare const ellipsisCharacterRule: () => ParsedProofreadingRule;
14683
14685
  //#endregion
14684
- //#region src/proofreading/rules/even-dash.d.ts
14685
- declare const evenDashRule: () => ParsedProofreadingRule;
14686
- //#endregion
14687
14686
  //#region src/proofreading/rules/even-ellipsis.d.ts
14688
14687
  declare const evenEllipsisRule: () => ParsedProofreadingRule;
14689
14688
  //#endregion
@@ -14700,9 +14699,6 @@ declare function createMaxArabicNumeralDigitsRule(options?: MaxArabicNumeralDigi
14700
14699
  //#region src/proofreading/rules/minus-before-number.d.ts
14701
14700
  declare const minusBeforeNumberRule: () => ParsedProofreadingRule;
14702
14701
  //#endregion
14703
- //#region src/proofreading/rules/no-consecutive-choonpu.d.ts
14704
- declare const noConsecutiveChoonpuRule: () => ParsedProofreadingRule;
14705
- //#endregion
14706
14702
  //#region src/proofreading/rules/no-consecutive-interpunct.d.ts
14707
14703
  declare const noConsecutiveInterpunctRule: () => ParsedProofreadingRule;
14708
14704
  //#endregion
@@ -14732,4 +14728,4 @@ declare const spaceAfterQuestionOrExclamationRule: () => ParsedProofreadingRule;
14732
14728
  */
14733
14729
  declare const variantCharacterRule: () => ParsedProofreadingRule;
14734
14730
  //#endregion
14735
- export { BoldAnnotation, ComposeError, type ComposeManuscriptOptions, type ComposedManuscript, type ComposedProofreadingRule, CompositionStatistics, type ConsistentKanjiOpeningOptions, DiagnosticOrigin, DiagnosticSeverity, DisplayRange, EmphasisAnnotation, FontPreset, FontPresetId, FontSizePt, GraphemeRange, GridCell, type GridComposedManuscript, GridLine, GridPage, GridSettings, GridStage, InvalidRuleOptions, ItalicAnnotation, type KanjiOpeningPair, LineOffset, ManuscriptAnnotation, ManuscriptAppearanceSettings, type ManuscriptComposer, ManuscriptCompositionSettings, ManuscriptDiagnostic, type ManuscriptDiagnosticInput, ManuscriptGeometry, ManuscriptGridLayout, ManuscriptOffsets, type ManuscriptParser, ManuscriptRange, type ManuscriptRangeInput, ManuscriptResult, type MaxArabicNumeralDigitsOptions, NamespacedId, PaperSize, PaperSizeId, type ParagraphOpeningOptions, ParseError, type ParseManuscriptOptions, ParsedGrapheme, ParsedManuscript, type ParsedProofreadingRule, ProofreadError, type ProofreadOptions, ProofreadingReport, type ProofreadingRule, type ProofreadingRuleContext, type ProofreadingRuleMessages, ProofreadingRuleMeta, Rejection, RubyAnnotation, SourcePosition, SourceRange, TextRange, ValidationIssue, assertNever, composeManuscript, consistentKanjiOpeningRule, consistentLatinWidthRule, consistentNumeralWidthRule, createConsistentKanjiOpeningRule, createDefaultProofreadingRules, createMaxArabicNumeralDigitsRule, createParagraphOpeningRule, dashCharacterRule, ellipsisCharacterRule, evenDashRule, evenEllipsisRule, fullwidthJapanesePunctuationRule, kakuyomuParser, manuscriptGridComposer, maxArabicNumeralDigitsRule, minusBeforeNumberRule, mmToPt, noConsecutiveChoonpuRule, noConsecutiveInterpunctRule, noConsecutivePunctuationRule, paragraphOpeningRule, parseManuscript, pixivParser, plainTextParser, proofreadManuscript, ptToMm, punctuationBeforeClosingQuoteRule, spaceAfterQuestionOrExclamationRule, variantCharacterRule };
14731
+ export { BoldAnnotation, ComposeError, type ComposeManuscriptOptions, type ComposedManuscript, type ComposedProofreadingRule, CompositionStatistics, type ConsistentKanjiOpeningOptions, type DashOptions, DiagnosticOrigin, DiagnosticSeverity, DisplayRange, EmphasisAnnotation, FontPreset, FontPresetId, FontSizePt, GraphemeRange, GridCell, type GridComposedManuscript, GridLine, GridPage, GridSettings, GridStage, InvalidRuleOptions, ItalicAnnotation, type KanjiOpeningPair, LineOffset, ManuscriptAnnotation, ManuscriptAppearanceSettings, type ManuscriptComposer, ManuscriptCompositionSettings, ManuscriptDiagnostic, type ManuscriptDiagnosticInput, ManuscriptGeometry, ManuscriptGridLayout, ManuscriptOffsets, type ManuscriptParser, ManuscriptRange, type ManuscriptRangeInput, ManuscriptResult, type MaxArabicNumeralDigitsOptions, NamespacedId, PaperSize, PaperSizeId, type ParagraphOpeningOptions, ParseError, type ParseManuscriptOptions, ParsedGrapheme, ParsedManuscript, type ParsedProofreadingRule, ProofreadError, type ProofreadOptions, ProofreadingReport, type ProofreadingRule, type ProofreadingRuleContext, type ProofreadingRuleMessages, ProofreadingRuleMeta, Rejection, RubyAnnotation, SourcePosition, SourceRange, TextRange, ValidationIssue, assertNever, composeManuscript, consistentKanjiOpeningRule, consistentLatinWidthRule, consistentNumeralWidthRule, createConsistentKanjiOpeningRule, createDashRule, createDefaultProofreadingRules, createMaxArabicNumeralDigitsRule, createParagraphOpeningRule, dashRule, ellipsisCharacterRule, evenEllipsisRule, fullwidthJapanesePunctuationRule, kakuyomuParser, manuscriptGridComposer, maxArabicNumeralDigitsRule, minusBeforeNumberRule, mmToPt, noConsecutiveInterpunctRule, noConsecutivePunctuationRule, paragraphOpeningRule, parseManuscript, pixivParser, plainTextParser, proofreadManuscript, ptToMm, punctuationBeforeClosingQuoteRule, spaceAfterQuestionOrExclamationRule, variantCharacterRule };
package/dist/index.mjs CHANGED
@@ -1544,7 +1544,7 @@ function defineMatchRule(options) {
1544
1544
  }
1545
1545
  //#endregion
1546
1546
  //#region src/proofreading/rules/consistent-kanji-opening.ts
1547
- const RULE_ID$4 = "kg/consistent-kanji-opening";
1547
+ const RULE_ID$6 = "kg/consistent-kanji-opening";
1548
1548
  const MESSAGE = "「{{ closed }}」と「{{ opened }}」が混在しています。文脈上の使い分けか表記ゆれかを確認してください";
1549
1549
  const KanjiOpeningPairSchema = v.pipe(readonlyObject({
1550
1550
  closed: v.pipe(v.string(), v.nonEmpty()),
@@ -1597,8 +1597,8 @@ const DEFAULT_PAIRS = [
1597
1597
  * Reports the first kanji occurrence of every pair the manuscript also writes in kana. Whether the
1598
1598
  * two spellings are a deliberate distinction is the author's call, so the finding is a warning.
1599
1599
  */
1600
- function build$2(pairs) {
1601
- return defineParsedRule(RULE_ID$4, MESSAGE, (manuscript, report) => {
1600
+ function build$3(pairs) {
1601
+ return defineParsedRule(RULE_ID$6, MESSAGE, (manuscript, report) => {
1602
1602
  for (const { closed, opened } of pairs) {
1603
1603
  if (!manuscript.displayText.includes(opened)) continue;
1604
1604
  const index = manuscript.displayText.indexOf(closed);
@@ -1613,17 +1613,17 @@ function build$2(pairs) {
1613
1613
  }
1614
1614
  });
1615
1615
  }
1616
- const consistentKanjiOpeningRule = () => build$2(DEFAULT_PAIRS);
1616
+ const consistentKanjiOpeningRule = () => build$3(DEFAULT_PAIRS);
1617
1617
  function createConsistentKanjiOpeningRule(options = {}) {
1618
1618
  if (options.pairs === void 0) return ManuscriptResult.succeed(consistentKanjiOpeningRule());
1619
1619
  const pairs = v.safeParse(PairsSchema, options.pairs);
1620
1620
  if (!pairs.success) return ManuscriptResult.fail({
1621
1621
  kind: "InvalidRuleOptions",
1622
- ruleId: RULE_ID$4,
1622
+ ruleId: RULE_ID$6,
1623
1623
  option: "pairs",
1624
1624
  issues: ValidationIssue.from(pairs.issues)
1625
1625
  });
1626
- return ManuscriptResult.succeed(build$2(pairs.output));
1626
+ return ManuscriptResult.succeed(build$3(pairs.output));
1627
1627
  }
1628
1628
  //#endregion
1629
1629
  //#region src/proofreading/rules/internal/mixed-width.ts
@@ -1663,16 +1663,64 @@ const consistentNumeralWidthRule = () => defineMixedWidthRule({
1663
1663
  fullwidth: /[0-9]/u
1664
1664
  });
1665
1665
  //#endregion
1666
- //#region src/proofreading/rules/dash-character.ts
1667
- /**
1668
- * Stand-ins for `―`. Repeated `ー` belongs to `kg/no-consecutive-choonpu` and `−` to
1669
- * `kg/minus-before-number`, so neither is reported twice.
1670
- */
1671
- const dashCharacterRule = () => defineMatchRule({
1672
- id: "kg/dash-character",
1673
- pattern: /[—–─━﹣-]+|-{2,}/gu,
1674
- message: "ダッシュには「―」を使ってください"
1675
- });
1666
+ //#region src/proofreading/rules/dash.ts
1667
+ const RULE_ID$5 = "kg/dash";
1668
+ const DEFAULT_PREFERRED = "―";
1669
+ const DashCharacterSchema = v.picklist([
1670
+ "―",
1671
+ "—",
1672
+ "─"
1673
+ ]);
1674
+ const DASH_LIKE_RUN = /[—–―─━﹣--]+/gu;
1675
+ const EVEN_COUNT_MESSAGE = "連続するダッシュの数は偶数にしてください";
1676
+ function messageIdFor(run, preferred) {
1677
+ if (run === "-") return void 0;
1678
+ if (run !== preferred.repeat(run.length)) return "character";
1679
+ return run.length % 2 === 1 ? "even-count" : void 0;
1680
+ }
1681
+ function build$2(preferred) {
1682
+ const messages = {
1683
+ character: `ダッシュには「${preferred}」を使ってください`,
1684
+ "even-count": EVEN_COUNT_MESSAGE
1685
+ };
1686
+ return {
1687
+ kind: "parsed",
1688
+ meta: {
1689
+ id: RULE_ID$5,
1690
+ messages
1691
+ },
1692
+ check: (manuscript, context) => {
1693
+ for (const line of splitDisplayLines(manuscript.displayText)) {
1694
+ DASH_LIKE_RUN.lastIndex = 0;
1695
+ let match = DASH_LIKE_RUN.exec(line.text);
1696
+ while (match !== null) {
1697
+ const run = match[0];
1698
+ const messageId = messageIdFor(run, preferred);
1699
+ if (messageId !== void 0) {
1700
+ const start = line.start + match.index;
1701
+ context.report({
1702
+ range: displayRange(manuscript, start, start + run.length),
1703
+ messageId
1704
+ });
1705
+ }
1706
+ match = DASH_LIKE_RUN.exec(line.text);
1707
+ }
1708
+ }
1709
+ }
1710
+ };
1711
+ }
1712
+ const dashRule = () => build$2(DEFAULT_PREFERRED);
1713
+ function createDashRule(options = {}) {
1714
+ if (options.preferred === void 0) return ManuscriptResult.succeed(dashRule());
1715
+ const preferred = v.safeParse(DashCharacterSchema, options.preferred);
1716
+ if (!preferred.success) return ManuscriptResult.fail({
1717
+ kind: "InvalidRuleOptions",
1718
+ ruleId: RULE_ID$5,
1719
+ option: "preferred",
1720
+ issues: ValidationIssue.from(preferred.issues)
1721
+ });
1722
+ return ManuscriptResult.succeed(build$2(preferred.output));
1723
+ }
1676
1724
  //#endregion
1677
1725
  //#region src/proofreading/rules/ellipsis-character.ts
1678
1726
  /**
@@ -1685,14 +1733,6 @@ const ellipsisCharacterRule = () => defineMatchRule({
1685
1733
  message: "三点リーダーには「…」を使ってください"
1686
1734
  });
1687
1735
  //#endregion
1688
- //#region src/proofreading/rules/even-dash.ts
1689
- const evenDashRule = () => defineMatchRule({
1690
- id: "kg/even-dash",
1691
- pattern: /―+/gu,
1692
- accept: (match) => match[0].length % 2 === 1,
1693
- message: "連続するダッシュの数は偶数にしてください"
1694
- });
1695
- //#endregion
1696
1736
  //#region src/proofreading/rules/even-ellipsis.ts
1697
1737
  const evenEllipsisRule = () => defineMatchRule({
1698
1738
  id: "kg/even-ellipsis",
@@ -1702,8 +1742,8 @@ const evenEllipsisRule = () => defineMatchRule({
1702
1742
  });
1703
1743
  //#endregion
1704
1744
  //#region src/proofreading/rules/fullwidth-japanese-punctuation.ts
1705
- const RULE_ID$3 = "kg/fullwidth-japanese-punctuation";
1706
- const MESSAGES$2 = {
1745
+ const RULE_ID$4 = "kg/fullwidth-japanese-punctuation";
1746
+ const MESSAGES$3 = {
1707
1747
  "halfwidth-japanese-punctuation": "半角の「{{ character }}」があります。日本語の句読点・括弧・感嘆符・疑問符は全角にしてください",
1708
1748
  "halfwidth-near-japanese": "日本語の前後に半角の「{{ character }}」があります。全角にすべきか確認してください"
1709
1749
  };
@@ -1748,8 +1788,8 @@ function isEllipsisSubstitute(text, index) {
1748
1788
  const fullwidthJapanesePunctuationRule = () => ({
1749
1789
  kind: "parsed",
1750
1790
  meta: {
1751
- id: RULE_ID$3,
1752
- messages: MESSAGES$2
1791
+ id: RULE_ID$4,
1792
+ messages: MESSAGES$3
1753
1793
  },
1754
1794
  check: (manuscript, context) => {
1755
1795
  for (const line of splitDisplayLines(manuscript.displayText)) for (const { pattern, messageId, severity, requiresJapanese } of CHECKS) for (const match of line.text.matchAll(pattern)) {
@@ -1767,12 +1807,12 @@ const fullwidthJapanesePunctuationRule = () => ({
1767
1807
  });
1768
1808
  //#endregion
1769
1809
  //#region src/proofreading/rules/max-arabic-numeral-digits.ts
1770
- const RULE_ID$2 = "kg/max-arabic-numeral-digits";
1810
+ const RULE_ID$3 = "kg/max-arabic-numeral-digits";
1771
1811
  const DEFAULT_MAX_DIGITS = 2;
1772
1812
  const MaxDigitsSchema = v.pipe(v.number(), v.finite(), v.integer(), v.minValue(1));
1773
1813
  function build$1(maxDigits) {
1774
1814
  return defineMatchRule({
1775
- id: RULE_ID$2,
1815
+ id: RULE_ID$3,
1776
1816
  pattern: /([0-90-9]+)(?:[..]([0-90-9]+))?/gu,
1777
1817
  accept: (match) => (match[1]?.length ?? 0) > maxDigits || (match[2]?.length ?? 0) > maxDigits,
1778
1818
  message: `${maxDigits}桁を超えるアラビア数字が使われています`
@@ -1784,7 +1824,7 @@ function createMaxArabicNumeralDigitsRule(options = {}) {
1784
1824
  const maxDigits = v.safeParse(MaxDigitsSchema, options.maxDigits);
1785
1825
  if (!maxDigits.success) return ManuscriptResult.fail({
1786
1826
  kind: "InvalidRuleOptions",
1787
- ruleId: RULE_ID$2,
1827
+ ruleId: RULE_ID$3,
1788
1828
  option: "maxDigits",
1789
1829
  issues: ValidationIssue.from(maxDigits.issues)
1790
1830
  });
@@ -1798,13 +1838,6 @@ const minusBeforeNumberRule = () => defineMatchRule({
1798
1838
  message: "マイナス記号の直後には数字が必要です"
1799
1839
  });
1800
1840
  //#endregion
1801
- //#region src/proofreading/rules/no-consecutive-choonpu.ts
1802
- const noConsecutiveChoonpuRule = () => defineMatchRule({
1803
- id: "kg/no-consecutive-choonpu",
1804
- pattern: /ーー+/gu,
1805
- message: "長音符が連続しています"
1806
- });
1807
- //#endregion
1808
1841
  //#region src/proofreading/rules/no-consecutive-interpunct.ts
1809
1842
  const noConsecutiveInterpunctRule = () => defineMatchRule({
1810
1843
  id: "kg/no-consecutive-interpunct",
@@ -1858,8 +1891,8 @@ function leadingSpaces(text) {
1858
1891
  }
1859
1892
  //#endregion
1860
1893
  //#region src/proofreading/rules/paragraph-opening.ts
1861
- const RULE_ID$1 = "kg/paragraph-opening";
1862
- const MESSAGES$1 = {
1894
+ const RULE_ID$2 = "kg/paragraph-opening";
1895
+ const MESSAGES$2 = {
1863
1896
  "leading-character": "段落の先頭には全角スペースまたは開き括弧が必要です",
1864
1897
  "indent-width": "段落冒頭の字下げは全角スペース1字にしてください",
1865
1898
  "unexpected-indent": "括弧から始まる段落を字下げすることはできません"
@@ -1874,8 +1907,8 @@ function build(openingBrackets) {
1874
1907
  return {
1875
1908
  kind: "parsed",
1876
1909
  meta: {
1877
- id: RULE_ID$1,
1878
- messages: MESSAGES$1
1910
+ id: RULE_ID$2,
1911
+ messages: MESSAGES$2
1879
1912
  },
1880
1913
  check: (manuscript, context) => {
1881
1914
  const report = (start, end, messageId) => {
@@ -1905,7 +1938,7 @@ function createParagraphOpeningRule(options = {}) {
1905
1938
  const brackets = v.safeParse(OpeningBracketsSchema, options.openingBrackets);
1906
1939
  if (!brackets.success) return ManuscriptResult.fail({
1907
1940
  kind: "InvalidRuleOptions",
1908
- ruleId: RULE_ID$1,
1941
+ ruleId: RULE_ID$2,
1909
1942
  option: "openingBrackets",
1910
1943
  issues: ValidationIssue.from(brackets.issues)
1911
1944
  });
@@ -1921,8 +1954,8 @@ const punctuationBeforeClosingQuoteRule = () => defineMatchRule({
1921
1954
  });
1922
1955
  //#endregion
1923
1956
  //#region src/proofreading/rules/space-after-question-or-exclamation.ts
1924
- const RULE_ID = "kg/space-after-question-or-exclamation";
1925
- const MESSAGES = {
1957
+ const RULE_ID$1 = "kg/space-after-question-or-exclamation";
1958
+ const MESSAGES$1 = {
1926
1959
  default: "感嘆符または疑問符の直後には全角スペースか閉じ括弧が必要です",
1927
1960
  "space-width": "感嘆符または疑問符の直後の空白は全角スペース1字にしてください",
1928
1961
  "before-closing-bracket": "閉じ括弧の直前に空白を置くことはできません"
@@ -1938,8 +1971,8 @@ const MARKS = /[?!]+/gu;
1938
1971
  const spaceAfterQuestionOrExclamationRule = () => ({
1939
1972
  kind: "parsed",
1940
1973
  meta: {
1941
- id: RULE_ID,
1942
- messages: MESSAGES
1974
+ id: RULE_ID$1,
1975
+ messages: MESSAGES$1
1943
1976
  },
1944
1977
  check: (manuscript, context) => {
1945
1978
  const report = (start, end, messageId) => {
@@ -1967,22 +2000,74 @@ const spaceAfterQuestionOrExclamationRule = () => ({
1967
2000
  });
1968
2001
  //#endregion
1969
2002
  //#region src/proofreading/rules/variant-character.ts
2003
+ const RULE_ID = "kg/variant-character";
2004
+ const MESSAGES = {
2005
+ default: "異体字または字形選択子が使われています",
2006
+ "with-suggestion": "異体字または字形選択子が使われています。「{{ suggestion }}」ではありませんか?"
2007
+ };
2008
+ /**
2009
+ * Mongolian free variation selectors, variation selectors (incl. emoji presentation), IVSes: all
2010
+ * combine with a preceding base character rather than standing on their own.
2011
+ */
2012
+ function isSelector(codePoint) {
2013
+ return codePoint >= 6155 && codePoint <= 6157 || codePoint === 6159 || codePoint >= 65024 && codePoint <= 65039 || codePoint >= 917760 && codePoint <= 917999;
2014
+ }
1970
2015
  /**
1971
- * Mongolian free variation selectors, variation selectors, CJK compatibility ideographs, IVSes.
2016
+ * CJK compatibility ideographs stand alone; each has a canonical unified form reachable via NFKC.
1972
2017
  */
2018
+ function isCompatibilityIdeograph(codePoint) {
2019
+ return codePoint >= 63744 && codePoint <= 64255 || codePoint >= 194560 && codePoint <= 195103;
2020
+ }
1973
2021
  function isVariantCharacter(character) {
1974
2022
  const codePoint = character.codePointAt(0);
1975
2023
  if (codePoint === void 0) return false;
1976
- return codePoint >= 6155 && codePoint <= 6157 || codePoint === 6159 || codePoint >= 65024 && codePoint <= 65039 || codePoint >= 63744 && codePoint <= 64255 || codePoint >= 194560 && codePoint <= 195103 || codePoint >= 917760 && codePoint <= 917999;
2024
+ return isSelector(codePoint) || isCompatibilityIdeograph(codePoint);
2025
+ }
2026
+ /**
2027
+ * The plain form a reader would expect: the selector stripped away, or the compatibility
2028
+ * ideograph's canonical unified form. Undefined when that plain form is identical to the original,
2029
+ * since NFKC does not touch selectors and a suggestion equal to the input helps no one.
2030
+ */
2031
+ function suggestPlainForm(segment) {
2032
+ const withoutSelectors = Array.from(segment).filter((character) => {
2033
+ const codePoint = character.codePointAt(0);
2034
+ return codePoint !== void 0 && !isSelector(codePoint);
2035
+ }).join("");
2036
+ if (withoutSelectors === "") return void 0;
2037
+ const normalized = Array.from(withoutSelectors).map((character) => {
2038
+ const codePoint = character.codePointAt(0);
2039
+ return codePoint !== void 0 && isCompatibilityIdeograph(codePoint) ? character.normalize("NFKC") : character;
2040
+ }).join("");
2041
+ return normalized === segment ? void 0 : normalized;
1977
2042
  }
1978
2043
  /**
1979
2044
  * Scans the source rather than the display text: notation removal must not hide a variant.
1980
2045
  */
1981
- const variantCharacterRule = () => defineParsedRule("kg/variant-character", "異体字または字形選択子が使われています", (manuscript, report) => {
1982
- for (const { index, segment } of graphemeSegmenter.segment(manuscript.source)) if (Array.from(segment).some(isVariantCharacter)) report(sourceRange(manuscript, {
1983
- start: index,
1984
- end: index + segment.length
1985
- }));
2046
+ const variantCharacterRule = () => ({
2047
+ kind: "parsed",
2048
+ meta: {
2049
+ id: RULE_ID,
2050
+ messages: MESSAGES
2051
+ },
2052
+ check: (manuscript, context) => {
2053
+ for (const { index, segment } of graphemeSegmenter.segment(manuscript.source)) {
2054
+ if (!Array.from(segment).some(isVariantCharacter)) continue;
2055
+ const range = sourceRange(manuscript, {
2056
+ start: index,
2057
+ end: index + segment.length
2058
+ });
2059
+ const suggestion = suggestPlainForm(segment);
2060
+ if (suggestion === void 0) context.report({
2061
+ range,
2062
+ messageId: "default"
2063
+ });
2064
+ else context.report({
2065
+ range,
2066
+ messageId: "with-suggestion",
2067
+ data: { suggestion }
2068
+ });
2069
+ }
2070
+ }
1986
2071
  });
1987
2072
  //#endregion
1988
2073
  //#region src/proofreading/rules/default-rules.ts
@@ -1999,12 +2084,10 @@ function createDefaultProofreadingRules() {
1999
2084
  punctuationBeforeClosingQuoteRule(),
2000
2085
  spaceAfterQuestionOrExclamationRule(),
2001
2086
  evenEllipsisRule(),
2002
- evenDashRule(),
2087
+ dashRule(),
2003
2088
  ellipsisCharacterRule(),
2004
- dashCharacterRule(),
2005
2089
  noConsecutivePunctuationRule(),
2006
2090
  noConsecutiveInterpunctRule(),
2007
- noConsecutiveChoonpuRule(),
2008
2091
  minusBeforeNumberRule(),
2009
2092
  maxArabicNumeralDigitsRule(),
2010
2093
  fullwidthJapanesePunctuationRule(),
@@ -2012,4 +2095,4 @@ function createDefaultProofreadingRules() {
2012
2095
  ];
2013
2096
  }
2014
2097
  //#endregion
2015
- 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, consistentKanjiOpeningRule, consistentLatinWidthRule, consistentNumeralWidthRule, createConsistentKanjiOpeningRule, createDefaultProofreadingRules, createMaxArabicNumeralDigitsRule, createParagraphOpeningRule, dashCharacterRule, ellipsisCharacterRule, evenDashRule, evenEllipsisRule, fullwidthJapanesePunctuationRule, kakuyomuParser, manuscriptGridComposer, maxArabicNumeralDigitsRule, minusBeforeNumberRule, mmToPt, noConsecutiveChoonpuRule, noConsecutiveInterpunctRule, noConsecutivePunctuationRule, paragraphOpeningRule, parseManuscript, pixivParser, plainTextParser, proofreadManuscript, ptToMm, punctuationBeforeClosingQuoteRule, spaceAfterQuestionOrExclamationRule, variantCharacterRule };
2098
+ 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, consistentKanjiOpeningRule, consistentLatinWidthRule, consistentNumeralWidthRule, createConsistentKanjiOpeningRule, createDashRule, createDefaultProofreadingRules, createMaxArabicNumeralDigitsRule, createParagraphOpeningRule, dashRule, ellipsisCharacterRule, evenEllipsisRule, fullwidthJapanesePunctuationRule, kakuyomuParser, manuscriptGridComposer, maxArabicNumeralDigitsRule, minusBeforeNumberRule, mmToPt, noConsecutiveInterpunctRule, noConsecutivePunctuationRule, paragraphOpeningRule, 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.5.0",
3
+ "version": "0.6.1",
4
4
  "description": "Framework-independent parsing, composition, and proofreading for Japanese manuscripts",
5
5
  "keywords": [
6
6
  "japanese",