@sushichan044/kg-core 0.6.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/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$5 = "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()),
@@ -1598,7 +1598,7 @@ const DEFAULT_PAIRS = [
1598
1598
  * two spellings are a deliberate distinction is the author's call, so the finding is a warning.
1599
1599
  */
1600
1600
  function build$3(pairs) {
1601
- return defineParsedRule(RULE_ID$5, MESSAGE, (manuscript, report) => {
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);
@@ -1619,7 +1619,7 @@ function createConsistentKanjiOpeningRule(options = {}) {
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$5,
1622
+ ruleId: RULE_ID$6,
1623
1623
  option: "pairs",
1624
1624
  issues: ValidationIssue.from(pairs.issues)
1625
1625
  });
@@ -1664,7 +1664,7 @@ const consistentNumeralWidthRule = () => defineMixedWidthRule({
1664
1664
  });
1665
1665
  //#endregion
1666
1666
  //#region src/proofreading/rules/dash.ts
1667
- const RULE_ID$4 = "kg/dash";
1667
+ const RULE_ID$5 = "kg/dash";
1668
1668
  const DEFAULT_PREFERRED = "―";
1669
1669
  const DashCharacterSchema = v.picklist([
1670
1670
  "―",
@@ -1686,7 +1686,7 @@ function build$2(preferred) {
1686
1686
  return {
1687
1687
  kind: "parsed",
1688
1688
  meta: {
1689
- id: RULE_ID$4,
1689
+ id: RULE_ID$5,
1690
1690
  messages
1691
1691
  },
1692
1692
  check: (manuscript, context) => {
@@ -1715,7 +1715,7 @@ function createDashRule(options = {}) {
1715
1715
  const preferred = v.safeParse(DashCharacterSchema, options.preferred);
1716
1716
  if (!preferred.success) return ManuscriptResult.fail({
1717
1717
  kind: "InvalidRuleOptions",
1718
- ruleId: RULE_ID$4,
1718
+ ruleId: RULE_ID$5,
1719
1719
  option: "preferred",
1720
1720
  issues: ValidationIssue.from(preferred.issues)
1721
1721
  });
@@ -1742,8 +1742,8 @@ const evenEllipsisRule = () => defineMatchRule({
1742
1742
  });
1743
1743
  //#endregion
1744
1744
  //#region src/proofreading/rules/fullwidth-japanese-punctuation.ts
1745
- const RULE_ID$3 = "kg/fullwidth-japanese-punctuation";
1746
- const MESSAGES$2 = {
1745
+ const RULE_ID$4 = "kg/fullwidth-japanese-punctuation";
1746
+ const MESSAGES$3 = {
1747
1747
  "halfwidth-japanese-punctuation": "半角の「{{ character }}」があります。日本語の句読点・括弧・感嘆符・疑問符は全角にしてください",
1748
1748
  "halfwidth-near-japanese": "日本語の前後に半角の「{{ character }}」があります。全角にすべきか確認してください"
1749
1749
  };
@@ -1788,8 +1788,8 @@ function isEllipsisSubstitute(text, index) {
1788
1788
  const fullwidthJapanesePunctuationRule = () => ({
1789
1789
  kind: "parsed",
1790
1790
  meta: {
1791
- id: RULE_ID$3,
1792
- messages: MESSAGES$2
1791
+ id: RULE_ID$4,
1792
+ messages: MESSAGES$3
1793
1793
  },
1794
1794
  check: (manuscript, context) => {
1795
1795
  for (const line of splitDisplayLines(manuscript.displayText)) for (const { pattern, messageId, severity, requiresJapanese } of CHECKS) for (const match of line.text.matchAll(pattern)) {
@@ -1807,12 +1807,12 @@ const fullwidthJapanesePunctuationRule = () => ({
1807
1807
  });
1808
1808
  //#endregion
1809
1809
  //#region src/proofreading/rules/max-arabic-numeral-digits.ts
1810
- const RULE_ID$2 = "kg/max-arabic-numeral-digits";
1810
+ const RULE_ID$3 = "kg/max-arabic-numeral-digits";
1811
1811
  const DEFAULT_MAX_DIGITS = 2;
1812
1812
  const MaxDigitsSchema = v.pipe(v.number(), v.finite(), v.integer(), v.minValue(1));
1813
1813
  function build$1(maxDigits) {
1814
1814
  return defineMatchRule({
1815
- id: RULE_ID$2,
1815
+ id: RULE_ID$3,
1816
1816
  pattern: /([0-90-9]+)(?:[..]([0-90-9]+))?/gu,
1817
1817
  accept: (match) => (match[1]?.length ?? 0) > maxDigits || (match[2]?.length ?? 0) > maxDigits,
1818
1818
  message: `${maxDigits}桁を超えるアラビア数字が使われています`
@@ -1824,7 +1824,7 @@ function createMaxArabicNumeralDigitsRule(options = {}) {
1824
1824
  const maxDigits = v.safeParse(MaxDigitsSchema, options.maxDigits);
1825
1825
  if (!maxDigits.success) return ManuscriptResult.fail({
1826
1826
  kind: "InvalidRuleOptions",
1827
- ruleId: RULE_ID$2,
1827
+ ruleId: RULE_ID$3,
1828
1828
  option: "maxDigits",
1829
1829
  issues: ValidationIssue.from(maxDigits.issues)
1830
1830
  });
@@ -1891,8 +1891,8 @@ function leadingSpaces(text) {
1891
1891
  }
1892
1892
  //#endregion
1893
1893
  //#region src/proofreading/rules/paragraph-opening.ts
1894
- const RULE_ID$1 = "kg/paragraph-opening";
1895
- const MESSAGES$1 = {
1894
+ const RULE_ID$2 = "kg/paragraph-opening";
1895
+ const MESSAGES$2 = {
1896
1896
  "leading-character": "段落の先頭には全角スペースまたは開き括弧が必要です",
1897
1897
  "indent-width": "段落冒頭の字下げは全角スペース1字にしてください",
1898
1898
  "unexpected-indent": "括弧から始まる段落を字下げすることはできません"
@@ -1907,8 +1907,8 @@ function build(openingBrackets) {
1907
1907
  return {
1908
1908
  kind: "parsed",
1909
1909
  meta: {
1910
- id: RULE_ID$1,
1911
- messages: MESSAGES$1
1910
+ id: RULE_ID$2,
1911
+ messages: MESSAGES$2
1912
1912
  },
1913
1913
  check: (manuscript, context) => {
1914
1914
  const report = (start, end, messageId) => {
@@ -1938,7 +1938,7 @@ function createParagraphOpeningRule(options = {}) {
1938
1938
  const brackets = v.safeParse(OpeningBracketsSchema, options.openingBrackets);
1939
1939
  if (!brackets.success) return ManuscriptResult.fail({
1940
1940
  kind: "InvalidRuleOptions",
1941
- ruleId: RULE_ID$1,
1941
+ ruleId: RULE_ID$2,
1942
1942
  option: "openingBrackets",
1943
1943
  issues: ValidationIssue.from(brackets.issues)
1944
1944
  });
@@ -1954,8 +1954,8 @@ const punctuationBeforeClosingQuoteRule = () => defineMatchRule({
1954
1954
  });
1955
1955
  //#endregion
1956
1956
  //#region src/proofreading/rules/space-after-question-or-exclamation.ts
1957
- const RULE_ID = "kg/space-after-question-or-exclamation";
1958
- const MESSAGES = {
1957
+ const RULE_ID$1 = "kg/space-after-question-or-exclamation";
1958
+ const MESSAGES$1 = {
1959
1959
  default: "感嘆符または疑問符の直後には全角スペースか閉じ括弧が必要です",
1960
1960
  "space-width": "感嘆符または疑問符の直後の空白は全角スペース1字にしてください",
1961
1961
  "before-closing-bracket": "閉じ括弧の直前に空白を置くことはできません"
@@ -1971,8 +1971,8 @@ const MARKS = /[?!]+/gu;
1971
1971
  const spaceAfterQuestionOrExclamationRule = () => ({
1972
1972
  kind: "parsed",
1973
1973
  meta: {
1974
- id: RULE_ID,
1975
- messages: MESSAGES
1974
+ id: RULE_ID$1,
1975
+ messages: MESSAGES$1
1976
1976
  },
1977
1977
  check: (manuscript, context) => {
1978
1978
  const report = (start, end, messageId) => {
@@ -2000,22 +2000,74 @@ const spaceAfterQuestionOrExclamationRule = () => ({
2000
2000
  });
2001
2001
  //#endregion
2002
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
+ }
2003
2015
  /**
2004
- * 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.
2005
2017
  */
2018
+ function isCompatibilityIdeograph(codePoint) {
2019
+ return codePoint >= 63744 && codePoint <= 64255 || codePoint >= 194560 && codePoint <= 195103;
2020
+ }
2006
2021
  function isVariantCharacter(character) {
2007
2022
  const codePoint = character.codePointAt(0);
2008
2023
  if (codePoint === void 0) return false;
2009
- 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;
2010
2042
  }
2011
2043
  /**
2012
2044
  * Scans the source rather than the display text: notation removal must not hide a variant.
2013
2045
  */
2014
- const variantCharacterRule = () => defineParsedRule("kg/variant-character", "異体字または字形選択子が使われています", (manuscript, report) => {
2015
- for (const { index, segment } of graphemeSegmenter.segment(manuscript.source)) if (Array.from(segment).some(isVariantCharacter)) report(sourceRange(manuscript, {
2016
- start: index,
2017
- end: index + segment.length
2018
- }));
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
+ }
2019
2071
  });
2020
2072
  //#endregion
2021
2073
  //#region src/proofreading/rules/default-rules.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sushichan044/kg-core",
3
- "version": "0.6.0",
3
+ "version": "0.6.1",
4
4
  "description": "Framework-independent parsing, composition, and proofreading for Japanese manuscripts",
5
5
  "keywords": [
6
6
  "japanese",