chaffjs 0.18.0 → 0.19.0
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 +52 -56
- package/bin/chaff.js +17 -2
- package/dist/acronym-expansions.d.ts +26 -0
- package/dist/acronym-expansions.d.ts.map +1 -0
- package/dist/acronym-expansions.js +88 -0
- package/dist/acronym-expansions.js.map +1 -0
- package/dist/announced-count.d.ts.map +1 -1
- package/dist/announced-count.js +44 -10
- package/dist/announced-count.js.map +1 -1
- package/dist/announced-list.d.ts +30 -0
- package/dist/announced-list.d.ts.map +1 -0
- package/dist/announced-list.js +95 -0
- package/dist/announced-list.js.map +1 -0
- package/dist/api.d.ts +168 -0
- package/dist/api.d.ts.map +1 -0
- package/dist/api.js +10 -0
- package/dist/api.js.map +1 -0
- package/dist/cli-text.d.ts +0 -1
- package/dist/cli-text.d.ts.map +1 -1
- package/dist/cli-text.js +6 -14
- package/dist/cli-text.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +58 -62
- package/dist/cli.js.map +1 -1
- package/dist/closing-paragraph.d.ts +7 -0
- package/dist/closing-paragraph.d.ts.map +1 -0
- package/dist/closing-paragraph.js +22 -0
- package/dist/closing-paragraph.js.map +1 -0
- package/dist/commands/compare.d.ts +8 -1
- package/dist/commands/compare.d.ts.map +1 -1
- package/dist/commands/compare.js +9 -17
- package/dist/commands/compare.js.map +1 -1
- package/dist/commands/document-commands.d.ts +9 -0
- package/dist/commands/document-commands.d.ts.map +1 -0
- package/dist/commands/document-commands.js +17 -0
- package/dist/commands/document-commands.js.map +1 -0
- package/dist/commands/eval.d.ts.map +1 -1
- package/dist/commands/eval.js +3 -2
- package/dist/commands/eval.js.map +1 -1
- package/dist/commands/facts.d.ts +8 -0
- package/dist/commands/facts.d.ts.map +1 -0
- package/dist/commands/facts.js +30 -0
- package/dist/commands/facts.js.map +1 -0
- package/dist/commands/feedback.d.ts +10 -0
- package/dist/commands/feedback.d.ts.map +1 -1
- package/dist/commands/feedback.js +25 -1
- package/dist/commands/feedback.js.map +1 -1
- package/dist/commands/fix-plan.d.ts +13 -0
- package/dist/commands/fix-plan.d.ts.map +1 -0
- package/dist/commands/fix-plan.js +44 -0
- package/dist/commands/fix-plan.js.map +1 -0
- package/dist/commands/outline.d.ts +8 -0
- package/dist/commands/outline.d.ts.map +1 -0
- package/dist/commands/outline.js +43 -0
- package/dist/commands/outline.js.map +1 -0
- package/dist/commands/read-document.d.ts +14 -0
- package/dist/commands/read-document.d.ts.map +1 -0
- package/dist/commands/read-document.js +21 -0
- package/dist/commands/read-document.js.map +1 -0
- package/dist/commands/suppressions.d.ts +7 -0
- package/dist/commands/suppressions.d.ts.map +1 -0
- package/dist/commands/suppressions.js +9 -0
- package/dist/commands/suppressions.js.map +1 -0
- package/dist/commands/test.js +1 -1
- package/dist/commands/test.js.map +1 -1
- package/dist/compare/clock-time.d.ts.map +1 -1
- package/dist/compare/clock-time.js +5 -2
- package/dist/compare/clock-time.js.map +1 -1
- package/dist/compare/distinct.d.ts +9 -0
- package/dist/compare/distinct.d.ts.map +1 -0
- package/dist/compare/distinct.js +12 -0
- package/dist/compare/distinct.js.map +1 -0
- package/dist/compare/extract.d.ts.map +1 -1
- package/dist/compare/extract.js +7 -5
- package/dist/compare/extract.js.map +1 -1
- package/dist/compare/fact-key.d.ts +17 -0
- package/dist/compare/fact-key.d.ts.map +1 -0
- package/dist/compare/fact-key.js +25 -0
- package/dist/compare/fact-key.js.map +1 -0
- package/dist/compare/fact-text.d.ts +5 -0
- package/dist/compare/fact-text.d.ts.map +1 -1
- package/dist/compare/fact-text.js +16 -2
- package/dist/compare/fact-text.js.map +1 -1
- package/dist/compare/facts-render.d.ts +17 -0
- package/dist/compare/facts-render.d.ts.map +1 -0
- package/dist/compare/facts-render.js +43 -0
- package/dist/compare/facts-render.js.map +1 -0
- package/dist/compare/facts-text.d.ts +15 -0
- package/dist/compare/facts-text.d.ts.map +1 -0
- package/dist/compare/facts-text.js +27 -0
- package/dist/compare/facts-text.js.map +1 -0
- package/dist/compare/match.d.ts +1 -0
- package/dist/compare/match.d.ts.map +1 -1
- package/dist/compare/match.js +1 -1
- package/dist/compare/match.js.map +1 -1
- package/dist/compare/outcome.d.ts +4 -1
- package/dist/compare/outcome.d.ts.map +1 -1
- package/dist/compare/outcome.js +10 -4
- package/dist/compare/outcome.js.map +1 -1
- package/dist/compare/proper-nouns.d.ts +3 -3
- package/dist/compare/proper-nouns.d.ts.map +1 -1
- package/dist/compare/proper-nouns.js +2 -3
- package/dist/compare/proper-nouns.js.map +1 -1
- package/dist/compare/render.d.ts +4 -0
- package/dist/compare/render.d.ts.map +1 -1
- package/dist/compare/render.js +2 -2
- package/dist/compare/render.js.map +1 -1
- package/dist/compare/text-atoms.d.ts +5 -2
- package/dist/compare/text-atoms.d.ts.map +1 -1
- package/dist/compare/text-atoms.js +7 -5
- package/dist/compare/text-atoms.js.map +1 -1
- package/dist/compare/text.d.ts.map +1 -1
- package/dist/compare/text.js +2 -2
- package/dist/compare/text.js.map +1 -1
- package/dist/config/load.d.ts +8 -0
- package/dist/config/load.d.ts.map +1 -1
- package/dist/config/load.js +1 -0
- package/dist/config/load.js.map +1 -1
- package/dist/config/option-problems.d.ts +3 -2
- package/dist/config/option-problems.d.ts.map +1 -1
- package/dist/config/option-problems.js +4 -3
- package/dist/config/option-problems.js.map +1 -1
- package/dist/config/style.d.ts +5 -0
- package/dist/config/style.d.ts.map +1 -1
- package/dist/config/style.js +13 -1
- package/dist/config/style.js.map +1 -1
- package/dist/custom/load.d.ts +6 -3
- package/dist/custom/load.d.ts.map +1 -1
- package/dist/custom/load.js +10 -2
- package/dist/custom/load.js.map +1 -1
- package/dist/custom/module-path.d.ts +11 -0
- package/dist/custom/module-path.d.ts.map +1 -0
- package/dist/custom/module-path.js +15 -0
- package/dist/custom/module-path.js.map +1 -0
- package/dist/custom/parse.d.ts +17 -3
- package/dist/custom/parse.d.ts.map +1 -1
- package/dist/custom/parse.js +57 -14
- package/dist/custom/parse.js.map +1 -1
- package/dist/custom/problems.d.ts +4 -1
- package/dist/custom/problems.d.ts.map +1 -1
- package/dist/custom/problems.js +56 -39
- package/dist/custom/problems.js.map +1 -1
- package/dist/detect-language.js +1 -1
- package/dist/detect.d.ts.map +1 -1
- package/dist/detect.js +9 -3
- package/dist/detect.js.map +1 -1
- package/dist/detectors/ai-phrasing.d.ts +4 -1
- package/dist/detectors/ai-phrasing.d.ts.map +1 -1
- package/dist/detectors/ai-phrasing.js +6 -2
- package/dist/detectors/ai-phrasing.js.map +1 -1
- package/dist/detectors/bold-label.d.ts +10 -0
- package/dist/detectors/bold-label.d.ts.map +1 -0
- package/dist/detectors/bold-label.js +44 -0
- package/dist/detectors/bold-label.js.map +1 -0
- package/dist/detectors/dangling-figure.d.ts +5 -2
- package/dist/detectors/dangling-figure.d.ts.map +1 -1
- package/dist/detectors/dangling-figure.js +5 -3
- package/dist/detectors/dangling-figure.js.map +1 -1
- package/dist/detectors/definition-use.d.ts +8 -0
- package/dist/detectors/definition-use.d.ts.map +1 -0
- package/dist/detectors/definition-use.js +32 -0
- package/dist/detectors/definition-use.js.map +1 -0
- package/dist/detectors/doubled-punctuation.d.ts.map +1 -1
- package/dist/detectors/doubled-punctuation.js +4 -2
- package/dist/detectors/doubled-punctuation.js.map +1 -1
- package/dist/detectors/duplicate-heading.d.ts +14 -0
- package/dist/detectors/duplicate-heading.d.ts.map +1 -0
- package/dist/detectors/duplicate-heading.js +39 -0
- package/dist/detectors/duplicate-heading.js.map +1 -0
- package/dist/detectors/empty-section.d.ts +8 -0
- package/dist/detectors/empty-section.d.ts.map +1 -0
- package/dist/detectors/empty-section.js +16 -0
- package/dist/detectors/empty-section.js.map +1 -0
- package/dist/detectors/fullwidth-alnum.d.ts +24 -0
- package/dist/detectors/fullwidth-alnum.d.ts.map +1 -0
- package/dist/detectors/fullwidth-alnum.js +88 -0
- package/dist/detectors/fullwidth-alnum.js.map +1 -0
- package/dist/detectors/hankaku-kana.d.ts +9 -0
- package/dist/detectors/hankaku-kana.d.ts.map +1 -0
- package/dist/detectors/hankaku-kana.js +27 -0
- package/dist/detectors/hankaku-kana.js.map +1 -0
- package/dist/detectors/heading-code-words.js +2 -2
- package/dist/detectors/heading-code-words.js.map +1 -1
- package/dist/detectors/index.d.ts.map +1 -1
- package/dist/detectors/index.js +36 -0
- package/dist/detectors/index.js.map +1 -1
- package/dist/detectors/invisible-character.d.ts +20 -0
- package/dist/detectors/invisible-character.d.ts.map +1 -0
- package/dist/detectors/invisible-character.js +143 -0
- package/dist/detectors/invisible-character.js.map +1 -0
- package/dist/detectors/kutoten-consistency.js +2 -2
- package/dist/detectors/kutoten-consistency.js.map +1 -1
- package/dist/detectors/lexicon-match.d.ts +10 -1
- package/dist/detectors/lexicon-match.d.ts.map +1 -1
- package/dist/detectors/lexicon-match.js +26 -1
- package/dist/detectors/lexicon-match.js.map +1 -1
- package/dist/detectors/lexicon.d.ts +6 -1
- package/dist/detectors/lexicon.d.ts.map +1 -1
- package/dist/detectors/lexicon.js +31 -13
- package/dist/detectors/lexicon.js.map +1 -1
- package/dist/detectors/list-sentence.d.ts +3 -1
- package/dist/detectors/list-sentence.d.ts.map +1 -1
- package/dist/detectors/list-sentence.js +26 -1
- package/dist/detectors/list-sentence.js.map +1 -1
- package/dist/detectors/long-vowel.d.ts.map +1 -1
- package/dist/detectors/long-vowel.js +18 -4
- package/dist/detectors/long-vowel.js.map +1 -1
- package/dist/detectors/markup-finding.d.ts +3 -1
- package/dist/detectors/markup-finding.d.ts.map +1 -1
- package/dist/detectors/markup-finding.js +10 -0
- package/dist/detectors/markup-finding.js.map +1 -1
- package/dist/detectors/modal-conflict.d.ts +4 -0
- package/dist/detectors/modal-conflict.d.ts.map +1 -0
- package/dist/detectors/modal-conflict.js +22 -0
- package/dist/detectors/modal-conflict.js.map +1 -0
- package/dist/detectors/name-variant.d.ts +4 -0
- package/dist/detectors/name-variant.d.ts.map +1 -0
- package/dist/detectors/name-variant.js +13 -0
- package/dist/detectors/name-variant.js.map +1 -0
- package/dist/detectors/nominalization.d.ts +11 -0
- package/dist/detectors/nominalization.d.ts.map +1 -0
- package/dist/detectors/nominalization.js +56 -0
- package/dist/detectors/nominalization.js.map +1 -0
- package/dist/detectors/orthography.d.ts.map +1 -1
- package/dist/detectors/orthography.js +59 -28
- package/dist/detectors/orthography.js.map +1 -1
- package/dist/detectors/outside-announced-list.d.ts +4 -0
- package/dist/detectors/outside-announced-list.d.ts.map +1 -0
- package/dist/detectors/outside-announced-list.js +24 -0
- package/dist/detectors/outside-announced-list.js.map +1 -0
- package/dist/detectors/oxford-comma.d.ts.map +1 -1
- package/dist/detectors/oxford-comma.js +1 -0
- package/dist/detectors/oxford-comma.js.map +1 -1
- package/dist/detectors/phrase-match.d.ts +4 -1
- package/dist/detectors/phrase-match.d.ts.map +1 -1
- package/dist/detectors/phrase-match.js +26 -7
- package/dist/detectors/phrase-match.js.map +1 -1
- package/dist/detectors/ra-nuki.d.ts +9 -0
- package/dist/detectors/ra-nuki.d.ts.map +1 -0
- package/dist/detectors/ra-nuki.js +30 -0
- package/dist/detectors/ra-nuki.js.map +1 -0
- package/dist/detectors/requirement-modal.d.ts +5 -0
- package/dist/detectors/requirement-modal.d.ts.map +1 -0
- package/dist/detectors/requirement-modal.js +54 -0
- package/dist/detectors/requirement-modal.js.map +1 -0
- package/dist/detectors/requirement-smell.d.ts +4 -0
- package/dist/detectors/requirement-smell.d.ts.map +1 -0
- package/dist/detectors/requirement-smell.js +41 -0
- package/dist/detectors/requirement-smell.js.map +1 -0
- package/dist/detectors/space-before-punctuation.d.ts +8 -0
- package/dist/detectors/space-before-punctuation.d.ts.map +1 -0
- package/dist/detectors/space-before-punctuation.js +24 -0
- package/dist/detectors/space-before-punctuation.js.map +1 -0
- package/dist/detectors/spelling-variety.d.ts +23 -0
- package/dist/detectors/spelling-variety.d.ts.map +1 -0
- package/dist/detectors/spelling-variety.js +59 -0
- package/dist/detectors/spelling-variety.js.map +1 -0
- package/dist/detectors/structure-tree.d.ts.map +1 -1
- package/dist/detectors/structure-tree.js +3 -1
- package/dist/detectors/structure-tree.js.map +1 -1
- package/dist/detectors/structure.d.ts.map +1 -1
- package/dist/detectors/structure.js +16 -2
- package/dist/detectors/structure.js.map +1 -1
- package/dist/detectors/superlative-comparison.d.ts +20 -0
- package/dist/detectors/superlative-comparison.d.ts.map +1 -0
- package/dist/detectors/superlative-comparison.js +32 -0
- package/dist/detectors/superlative-comparison.js.map +1 -0
- package/dist/detectors/url-run-on.d.ts.map +1 -1
- package/dist/detectors/url-run-on.js +6 -1
- package/dist/detectors/url-run-on.js.map +1 -1
- package/dist/detectors/vague-figure-pointer.d.ts +10 -0
- package/dist/detectors/vague-figure-pointer.d.ts.map +1 -0
- package/dist/detectors/vague-figure-pointer.js +46 -0
- package/dist/detectors/vague-figure-pointer.js.map +1 -0
- package/dist/detectors/word-list.d.ts +2 -0
- package/dist/detectors/word-list.d.ts.map +1 -1
- package/dist/detectors/word-list.js +4 -1
- package/dist/detectors/word-list.js.map +1 -1
- package/dist/document.d.ts +9 -3
- package/dist/document.d.ts.map +1 -1
- package/dist/document.js +55 -32
- package/dist/document.js.map +1 -1
- package/dist/extension/document-view.d.ts +7 -0
- package/dist/extension/document-view.d.ts.map +1 -0
- package/dist/extension/document-view.js +91 -0
- package/dist/extension/document-view.js.map +1 -0
- package/dist/extension/failure-text.d.ts +5 -0
- package/dist/extension/failure-text.d.ts.map +1 -0
- package/dist/extension/failure-text.js +33 -0
- package/dist/extension/failure-text.js.map +1 -0
- package/dist/extension/import-default.d.ts +7 -0
- package/dist/extension/import-default.d.ts.map +1 -0
- package/dist/extension/import-default.js +21 -0
- package/dist/extension/import-default.js.map +1 -0
- package/dist/extension/load.d.ts +28 -0
- package/dist/extension/load.d.ts.map +1 -0
- package/dist/extension/load.js +51 -0
- package/dist/extension/load.js.map +1 -0
- package/dist/extension/module-detector.d.ts +21 -0
- package/dist/extension/module-detector.d.ts.map +1 -0
- package/dist/extension/module-detector.js +49 -0
- package/dist/extension/module-detector.js.map +1 -0
- package/dist/extension/plugin-load.d.ts +9 -0
- package/dist/extension/plugin-load.d.ts.map +1 -0
- package/dist/extension/plugin-load.js +68 -0
- package/dist/extension/plugin-load.js.map +1 -0
- package/dist/extension/plugin-name.d.ts +8 -0
- package/dist/extension/plugin-name.d.ts.map +1 -0
- package/dist/extension/plugin-name.js +22 -0
- package/dist/extension/plugin-name.js.map +1 -0
- package/dist/extension/plugin-parse.d.ts +41 -0
- package/dist/extension/plugin-parse.d.ts.map +1 -0
- package/dist/extension/plugin-parse.js +156 -0
- package/dist/extension/plugin-parse.js.map +1 -0
- package/dist/extension/problem-text.d.ts +5 -0
- package/dist/extension/problem-text.d.ts.map +1 -0
- package/dist/extension/problem-text.js +74 -0
- package/dist/extension/problem-text.js.map +1 -0
- package/dist/extension/real-path.d.ts +3 -0
- package/dist/extension/real-path.d.ts.map +1 -0
- package/dist/extension/real-path.js +8 -0
- package/dist/extension/real-path.js.map +1 -0
- package/dist/extension/returned-findings.d.ts +17 -0
- package/dist/extension/returned-findings.d.ts.map +1 -0
- package/dist/extension/returned-findings.js +48 -0
- package/dist/extension/returned-findings.js.map +1 -0
- package/dist/extension/rule-export.d.ts +13 -0
- package/dist/extension/rule-export.d.ts.map +1 -0
- package/dist/extension/rule-export.js +16 -0
- package/dist/extension/rule-export.js.map +1 -0
- package/dist/feedback/draft.d.ts +2 -0
- package/dist/feedback/draft.d.ts.map +1 -1
- package/dist/feedback/draft.js +3 -0
- package/dist/feedback/draft.js.map +1 -1
- package/dist/figure-references.d.ts +11 -2
- package/dist/figure-references.d.ts.map +1 -1
- package/dist/figure-references.js +50 -13
- package/dist/figure-references.js.map +1 -1
- package/dist/finding-order.d.ts +9 -0
- package/dist/finding-order.d.ts.map +1 -0
- package/dist/finding-order.js +3 -0
- package/dist/finding-order.js.map +1 -0
- package/dist/fix-plan/mode.d.ts +21 -0
- package/dist/fix-plan/mode.d.ts.map +1 -0
- package/dist/fix-plan/mode.js +16 -0
- package/dist/fix-plan/mode.js.map +1 -0
- package/dist/fix-plan/plan.d.ts +77 -0
- package/dist/fix-plan/plan.d.ts.map +1 -0
- package/dist/fix-plan/plan.js +100 -0
- package/dist/fix-plan/plan.js.map +1 -0
- package/dist/fix-plan/render.d.ts +6 -0
- package/dist/fix-plan/render.d.ts.map +1 -0
- package/dist/fix-plan/render.js +101 -0
- package/dist/fix-plan/render.js.map +1 -0
- package/dist/fix-plan/text.d.ts +35 -0
- package/dist/fix-plan/text.d.ts.map +1 -0
- package/dist/fix-plan/text.js +95 -0
- package/dist/fix-plan/text.js.map +1 -0
- package/dist/language-sample.d.ts +2 -0
- package/dist/language-sample.d.ts.map +1 -0
- package/dist/language-sample.js +41 -0
- package/dist/language-sample.js.map +1 -0
- package/dist/link-line.d.ts +8 -0
- package/dist/link-line.d.ts.map +1 -0
- package/dist/link-line.js +12 -0
- package/dist/link-line.js.map +1 -0
- package/dist/long-vowel.d.ts +9 -6
- package/dist/long-vowel.d.ts.map +1 -1
- package/dist/long-vowel.js +15 -8
- package/dist/long-vowel.js.map +1 -1
- package/dist/markup.d.ts.map +1 -1
- package/dist/markup.js +4 -2
- package/dist/markup.js.map +1 -1
- package/dist/name-variants.d.ts +25 -0
- package/dist/name-variants.d.ts.map +1 -0
- package/dist/name-variants.js +181 -0
- package/dist/name-variants.js.map +1 -0
- package/dist/node-version.d.ts +7 -0
- package/dist/node-version.d.ts.map +1 -0
- package/dist/node-version.js +19 -0
- package/dist/node-version.js.map +1 -0
- package/dist/not-run.d.ts +14 -0
- package/dist/not-run.d.ts.map +1 -0
- package/dist/not-run.js +12 -0
- package/dist/not-run.js.map +1 -0
- package/dist/orthography.d.ts +8 -1
- package/dist/orthography.d.ts.map +1 -1
- package/dist/orthography.js +33 -12
- package/dist/orthography.js.map +1 -1
- package/dist/outline/render.d.ts +18 -0
- package/dist/outline/render.d.ts.map +1 -0
- package/dist/outline/render.js +60 -0
- package/dist/outline/render.js.map +1 -0
- package/dist/outline/shape.d.ts +26 -0
- package/dist/outline/shape.d.ts.map +1 -0
- package/dist/outline/shape.js +37 -0
- package/dist/outline/shape.js.map +1 -0
- package/dist/outline/text.d.ts +21 -0
- package/dist/outline/text.d.ts.map +1 -0
- package/dist/outline/text.js +30 -0
- package/dist/outline/text.js.map +1 -0
- package/dist/plugin.d.ts +24 -2
- package/dist/plugin.d.ts.map +1 -1
- package/dist/reasons.d.ts +1 -0
- package/dist/reasons.d.ts.map +1 -1
- package/dist/reasons.js +2 -0
- package/dist/reasons.js.map +1 -1
- package/dist/render/compact.d.ts +3 -1
- package/dist/render/compact.d.ts.map +1 -1
- package/dist/render/compact.js +8 -2
- package/dist/render/compact.js.map +1 -1
- package/dist/render/explain.d.ts +3 -1
- package/dist/render/explain.d.ts.map +1 -1
- package/dist/render/explain.js +32 -15
- package/dist/render/explain.js.map +1 -1
- package/dist/render/rules-json.d.ts.map +1 -1
- package/dist/render/rules-json.js +16 -12
- package/dist/render/rules-json.js.map +1 -1
- package/dist/render/suppressions.d.ts +3 -0
- package/dist/render/suppressions.d.ts.map +1 -1
- package/dist/render/suppressions.js +20 -1
- package/dist/render/suppressions.js.map +1 -1
- package/dist/rule-guide.d.ts +19 -0
- package/dist/rule-guide.d.ts.map +1 -1
- package/dist/rule-guide.js +14 -0
- package/dist/rule-guide.js.map +1 -1
- package/dist/run-semantic.d.ts.map +1 -1
- package/dist/run-semantic.js +2 -1
- package/dist/run-semantic.js.map +1 -1
- package/dist/run.d.ts +5 -1
- package/dist/run.d.ts.map +1 -1
- package/dist/run.js +31 -14
- package/dist/run.js.map +1 -1
- package/dist/setting-problems.d.ts.map +1 -1
- package/dist/setting-problems.js +3 -1
- package/dist/setting-problems.js.map +1 -1
- package/dist/spacing-minority.d.ts +16 -0
- package/dist/spacing-minority.d.ts.map +1 -0
- package/dist/spacing-minority.js +23 -0
- package/dist/spacing-minority.js.map +1 -0
- package/dist/stet-block.d.ts +5 -0
- package/dist/stet-block.d.ts.map +1 -0
- package/dist/stet-block.js +59 -0
- package/dist/stet-block.js.map +1 -0
- package/dist/stet.d.ts +4 -0
- package/dist/stet.d.ts.map +1 -1
- package/dist/stet.js +31 -11
- package/dist/stet.js.map +1 -1
- package/dist/structure/date-range.d.ts +10 -0
- package/dist/structure/date-range.d.ts.map +1 -1
- package/dist/structure/date-range.js +33 -1
- package/dist/structure/date-range.js.map +1 -1
- package/dist/structure/definition-use.d.ts +32 -0
- package/dist/structure/definition-use.d.ts.map +1 -0
- package/dist/structure/definition-use.js +102 -0
- package/dist/structure/definition-use.js.map +1 -0
- package/dist/structure/lazy-tree.d.ts +8 -0
- package/dist/structure/lazy-tree.d.ts.map +1 -0
- package/dist/structure/lazy-tree.js +14 -0
- package/dist/structure/lazy-tree.js.map +1 -0
- package/dist/structure/modal-conflict.d.ts +28 -0
- package/dist/structure/modal-conflict.d.ts.map +1 -0
- package/dist/structure/modal-conflict.js +74 -0
- package/dist/structure/modal-conflict.js.map +1 -0
- package/dist/structure/numbered-clauses.d.ts +4 -0
- package/dist/structure/numbered-clauses.d.ts.map +1 -0
- package/dist/structure/numbered-clauses.js +9 -0
- package/dist/structure/numbered-clauses.js.map +1 -0
- package/dist/structure/universal.d.ts.map +1 -1
- package/dist/structure/universal.js +8 -2
- package/dist/structure/universal.js.map +1 -1
- package/dist/style-parse.d.ts +6 -0
- package/dist/style-parse.d.ts.map +1 -1
- package/dist/style-parse.js +32 -4
- package/dist/style-parse.js.map +1 -1
- package/dist/subheading-line.d.ts +19 -2
- package/dist/subheading-line.d.ts.map +1 -1
- package/dist/subheading-line.js +39 -11
- package/dist/subheading-line.js.map +1 -1
- package/dist/tag-coverage.d.ts +8 -0
- package/dist/tag-coverage.d.ts.map +1 -0
- package/dist/tag-coverage.js +7 -0
- package/dist/tag-coverage.js.map +1 -0
- package/genres.yaml +42 -0
- package/package.json +7 -3
- package/rules/acronym-expansion-conflict.yaml +82 -0
- package/rules/adverb-overuse.yaml +2 -2
- package/rules/agentless-passive.yaml +28 -0
- package/rules/ai-tell.yaml +34 -0
- package/rules/announced-count-mismatch.yaml +2 -2
- package/rules/announcing-opener.yaml +31 -0
- package/rules/assistant-residue.yaml +30 -0
- package/rules/bold-density.yaml +28 -0
- package/rules/bold-label-list.yaml +81 -0
- package/rules/closing-cliche.yaml +27 -2
- package/rules/colon-lead-in.yaml +28 -4
- package/rules/contrast-framing.yaml +33 -2
- package/rules/cushion-phrase-density.yaml +32 -5
- package/rules/dangling-reference.yaml +1 -0
- package/rules/date-order.yaml +1 -0
- package/rules/date-range-reversed.yaml +2 -2
- package/rules/double-negative.yaml +51 -0
- package/rules/doubled-punctuation.yaml +2 -2
- package/rules/doubled-word.yaml +2 -2
- package/rules/duplicate-heading.yaml +78 -0
- package/rules/emoji-density.yaml +5 -4
- package/rules/empty-intensifier.yaml +25 -0
- package/rules/empty-section.yaml +74 -0
- package/rules/excessive-hedging.yaml +32 -4
- package/rules/fullwidth-alnum-consistency.yaml +53 -0
- package/rules/hankaku-kana.yaml +41 -0
- package/rules/image-alt-text.yaml +1 -1
- package/rules/invisible-character.yaml +63 -0
- package/rules/katakana-long-vowel.yaml +10 -9
- package/rules/latin-spacing.yaml +12 -5
- package/rules/max-kanji-continuous.yaml +1 -0
- package/rules/max-paragraph-length.yaml +1 -0
- package/rules/max-sentence-length.yaml +26 -0
- package/rules/modal-conflict.yaml +106 -0
- package/rules/name-variant.yaml +88 -0
- package/rules/no-doubled-joshi.yaml +1 -0
- package/rules/no-em-dash.yaml +26 -2
- package/rules/no-mixed-desumasu.yaml +1 -0
- package/rules/nominalization.yaml +89 -0
- package/rules/outside-announced-list.yaml +80 -0
- package/rules/padded-intro.yaml +25 -0
- package/rules/paragraph-length-variance.yaml +26 -2
- package/rules/preferred-term.yaml +1 -0
- package/rules/proper-noun-density.yaml +14 -5
- package/rules/ra-nuki.yaml +43 -0
- package/rules/requirement-modal.yaml +94 -0
- package/rules/requirement-smell.yaml +70 -0
- package/rules/rule-of-three.yaml +46 -0
- package/rules/section-length-uniformity.yaml +26 -2
- package/rules/sentence-rhythm.yaml +26 -0
- package/rules/space-before-punctuation.yaml +41 -0
- package/rules/spelling-consistency.yaml +47 -0
- package/rules/stock-transition.yaml +33 -2
- package/rules/taigen-dome-in-prose.yaml +14 -0
- package/rules/total-mismatch.yaml +1 -0
- package/rules/undefined-acronym.yaml +1 -0
- package/rules/unqualified-superlative.yaml +5 -4
- package/rules/unused-definition.yaml +75 -0
- package/rules/use-before-definition.yaml +77 -0
- package/rules/vague-figure-reference.yaml +66 -0
- package/skills/chaff/SKILL.md +108 -8
- package/src/acronym-expansions.ts +106 -0
- package/src/announced-count.ts +53 -11
- package/src/announced-list.ts +120 -0
- package/src/api.ts +154 -0
- package/src/cli-text.ts +6 -11
- package/src/cli.ts +64 -63
- package/src/closing-paragraph.ts +24 -0
- package/src/commands/compare.ts +11 -18
- package/src/commands/document-commands.ts +19 -0
- package/src/commands/eval.ts +3 -2
- package/src/commands/facts.ts +33 -0
- package/src/commands/feedback.ts +33 -1
- package/src/commands/fix-plan.ts +53 -0
- package/src/commands/outline.ts +49 -0
- package/src/commands/read-document.ts +24 -0
- package/src/commands/suppressions.ts +19 -0
- package/src/commands/test.ts +1 -1
- package/src/compare/clock-time.ts +5 -2
- package/src/compare/distinct.ts +13 -0
- package/src/compare/extract.ts +7 -5
- package/src/compare/fact-key.ts +31 -0
- package/src/compare/fact-text.ts +21 -2
- package/src/compare/facts-render.ts +63 -0
- package/src/compare/facts-text.ts +41 -0
- package/src/compare/match.ts +1 -1
- package/src/compare/outcome.ts +14 -4
- package/src/compare/proper-nouns.ts +4 -4
- package/src/compare/render.ts +2 -2
- package/src/compare/text-atoms.ts +7 -6
- package/src/compare/text.ts +3 -2
- package/src/config/load.ts +9 -0
- package/src/config/option-problems.ts +6 -4
- package/src/config/style.ts +14 -1
- package/src/custom/load.ts +11 -3
- package/src/custom/module-path.ts +23 -0
- package/src/custom/parse.ts +67 -18
- package/src/custom/problems.ts +63 -41
- package/src/detect-language.ts +1 -1
- package/src/detect.ts +10 -3
- package/src/detectors/ai-phrasing.ts +6 -2
- package/src/detectors/bold-label.ts +55 -0
- package/src/detectors/dangling-figure.ts +8 -4
- package/src/detectors/definition-use.ts +41 -0
- package/src/detectors/doubled-punctuation.ts +5 -2
- package/src/detectors/duplicate-heading.ts +47 -0
- package/src/detectors/empty-section.ts +21 -0
- package/src/detectors/fullwidth-alnum.ts +118 -0
- package/src/detectors/hankaku-kana.ts +38 -0
- package/src/detectors/heading-code-words.ts +2 -2
- package/src/detectors/index.ts +36 -0
- package/src/detectors/invisible-character.ts +190 -0
- package/src/detectors/kutoten-consistency.ts +2 -2
- package/src/detectors/lexicon-match.ts +33 -1
- package/src/detectors/lexicon.ts +40 -13
- package/src/detectors/list-sentence.ts +45 -4
- package/src/detectors/long-vowel.ts +20 -4
- package/src/detectors/markup-finding.ts +16 -1
- package/src/detectors/modal-conflict.ts +26 -0
- package/src/detectors/name-variant.ts +15 -0
- package/src/detectors/nominalization.ts +73 -0
- package/src/detectors/orthography.ts +77 -27
- package/src/detectors/outside-announced-list.ts +25 -0
- package/src/detectors/oxford-comma.ts +1 -0
- package/src/detectors/phrase-match.ts +27 -7
- package/src/detectors/ra-nuki.ts +38 -0
- package/src/detectors/requirement-modal.ts +67 -0
- package/src/detectors/requirement-smell.ts +52 -0
- package/src/detectors/space-before-punctuation.ts +32 -0
- package/src/detectors/spelling-variety.ts +81 -0
- package/src/detectors/structure-tree.ts +3 -1
- package/src/detectors/structure.ts +17 -3
- package/src/detectors/superlative-comparison.ts +47 -0
- package/src/detectors/url-run-on.ts +7 -1
- package/src/detectors/vague-figure-pointer.ts +58 -0
- package/src/detectors/word-list.ts +6 -1
- package/src/document.ts +88 -41
- package/src/extension/document-view.ts +109 -0
- package/src/extension/failure-text.ts +46 -0
- package/src/extension/import-default.ts +13 -0
- package/src/extension/load.ts +97 -0
- package/src/extension/module-detector.ts +61 -0
- package/src/extension/plugin-load.ts +82 -0
- package/src/extension/plugin-name.ts +26 -0
- package/src/extension/plugin-parse.ts +229 -0
- package/src/extension/problem-text.ts +103 -0
- package/src/extension/real-path.ts +10 -0
- package/src/extension/returned-findings.ts +62 -0
- package/src/extension/rule-export.ts +23 -0
- package/src/feedback/draft.ts +6 -0
- package/src/figure-references.ts +73 -12
- package/src/finding-order.ts +3 -0
- package/src/fix-plan/mode.ts +34 -0
- package/src/fix-plan/plan.ts +181 -0
- package/src/fix-plan/render.ts +128 -0
- package/src/fix-plan/text.ts +133 -0
- package/src/language-sample.ts +48 -0
- package/src/link-line.ts +13 -0
- package/src/long-vowel.ts +18 -8
- package/src/markup.ts +4 -2
- package/src/name-variants.ts +217 -0
- package/src/node-version.ts +22 -0
- package/src/not-run.ts +16 -0
- package/src/orthography.ts +40 -11
- package/src/outline/render.ts +85 -0
- package/src/outline/shape.ts +72 -0
- package/src/outline/text.ts +50 -0
- package/src/plugin.ts +21 -4
- package/src/reasons.ts +3 -0
- package/src/render/compact.ts +13 -3
- package/src/render/explain.ts +47 -20
- package/src/render/rules-json.ts +18 -12
- package/src/render/suppressions.ts +30 -2
- package/src/rule-guide.ts +38 -0
- package/src/run-semantic.ts +2 -1
- package/src/run.ts +37 -17
- package/src/setting-problems.ts +3 -1
- package/src/spacing-minority.ts +27 -0
- package/src/stet-block.ts +60 -0
- package/src/stet.ts +41 -15
- package/src/structure/date-range.ts +49 -1
- package/src/structure/definition-use.ts +130 -0
- package/src/structure/lazy-tree.ts +15 -0
- package/src/structure/modal-conflict.ts +86 -0
- package/src/structure/numbered-clauses.ts +11 -0
- package/src/structure/universal.ts +10 -2
- package/src/style-parse.ts +38 -3
- package/src/subheading-line.ts +51 -13
- package/src/tag-coverage.ts +13 -0
- package/styles/ieice.yaml +5 -4
- package/styles/jis-z8301-2011.yaml +5 -2
- package/styles/jis-z8301-2019.yaml +31 -0
- package/styles/koyobun.yaml +35 -0
- package/dist/legal-address.d.ts +0 -2
- package/dist/legal-address.d.ts.map +0 -1
- package/dist/legal-address.js +0 -16
- package/dist/legal-address.js.map +0 -1
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { Detector, Finding, MarkupHeading } from "../plugin.ts";
|
|
2
|
+
import { lineNumberAt, linesOf } from "../structure/lines.ts";
|
|
3
|
+
import { findingAt, writtenHeadings } from "./markup-finding.ts";
|
|
4
|
+
|
|
5
|
+
/** 見出しの言葉を比べる形。全角と半角、大文字と小文字、空白の数は同じ見出しの違いにしない。 */
|
|
6
|
+
export const headingKey = (text: string): string => text.normalize("NFKC").toLowerCase().replace(/\s+/gu, " ").trim();
|
|
7
|
+
|
|
8
|
+
/** 見出しの親(一つ前の、それより浅い見出し)の位置。いちばん浅い見出しは文書が親で、-1。 */
|
|
9
|
+
const parentIndexes = (headings: readonly MarkupHeading[]): number[] => {
|
|
10
|
+
const open: number[] = [];
|
|
11
|
+
const depthAt = (at: number | undefined): number => (at === undefined ? 0 : (headings[at]?.depth ?? 0));
|
|
12
|
+
return headings.map((heading, index) => {
|
|
13
|
+
while (open.length > 0 && depthAt(open.at(-1)) >= heading.depth) open.pop();
|
|
14
|
+
const parent = open.at(-1) ?? -1;
|
|
15
|
+
open.push(index);
|
|
16
|
+
return parent;
|
|
17
|
+
});
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export type RepeatedHeading = { readonly heading: MarkupHeading; readonly first: MarkupHeading };
|
|
21
|
+
|
|
22
|
+
type Seen = Map<number, Map<string, MarkupHeading>>;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* 同じ親の下で、前の見出しと同じ言葉の見出し(markdownlint の MD024 siblings_only)。変更履歴の版ごとの「Added」「Fixed」のように、
|
|
26
|
+
* 親が違えば同じ言葉でもよい。深さが違っても親が同じなら兄弟として比べる(飛んだ深さの見出しも同じ親の下にある)。
|
|
27
|
+
*/
|
|
28
|
+
export const repeatedSiblingHeadings = (headings: readonly MarkupHeading[]): RepeatedHeading[] => {
|
|
29
|
+
const parents = parentIndexes(headings);
|
|
30
|
+
const seen: Seen = new Map();
|
|
31
|
+
return headings.flatMap((heading, index) => {
|
|
32
|
+
const key = headingKey(heading.text);
|
|
33
|
+
const parent = parents[index] ?? -1;
|
|
34
|
+
const siblings = seen.get(parent) ?? new Map<string, MarkupHeading>();
|
|
35
|
+
seen.set(parent, siblings);
|
|
36
|
+
const first = siblings.get(key);
|
|
37
|
+
if (first === undefined) siblings.set(key, heading);
|
|
38
|
+
return first === undefined || key === "" ? [] : [{ heading, first }];
|
|
39
|
+
});
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export const duplicateHeading: Detector = (doc): Finding[] => {
|
|
43
|
+
const lines = linesOf(doc.source);
|
|
44
|
+
return repeatedSiblingHeadings(writtenHeadings(doc)).map(({ heading, first }) =>
|
|
45
|
+
findingAt(doc, heading, { heading: heading.text, firstLine: lineNumberAt(lines, first.start) ?? 0 }),
|
|
46
|
+
);
|
|
47
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { Detector, Finding, MarkupHeading } from "../plugin.ts";
|
|
2
|
+
import { findingAt, writtenHeadings } from "./markup-finding.ts";
|
|
3
|
+
|
|
4
|
+
/** HTML のコメント(「<!-- 後で書く -->」)。読み手には見えない。一つずつ外す(間の本文ごと一つのコメントと読まない)。 */
|
|
5
|
+
const HTML_COMMENT = /<!--[\s\S]*?-->/gu;
|
|
6
|
+
|
|
7
|
+
const showsNothing = (written: string): boolean => written.replace(HTML_COMMENT, "").trim() === "";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* 中身の無い節。見出しのすぐ後ろに、同じ深さか浅い見出しが来るもの(remark-lint の no-empty-sections)。
|
|
11
|
+
* 深い見出しが続くのは、節を小さな節に分けただけなので中身がある。文書の最後の見出しは、後ろに何も無ければ空。
|
|
12
|
+
*/
|
|
13
|
+
export const emptySections = (source: string, headings: readonly MarkupHeading[]): MarkupHeading[] =>
|
|
14
|
+
headings.filter((heading, index) => {
|
|
15
|
+
const next = headings[index + 1];
|
|
16
|
+
if (next !== undefined && next.depth > heading.depth) return false;
|
|
17
|
+
return showsNothing(source.slice(heading.end, next?.start ?? source.length));
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
export const emptySection: Detector = (doc): Finding[] =>
|
|
21
|
+
emptySections(doc.source, writtenHeadings(doc)).map((heading) => findingAt(doc, heading, { heading: heading.text }));
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import type { Detector, Finding, Sentence, Span } from "../plugin.ts";
|
|
2
|
+
import { minorityWithin } from "../orthography.ts";
|
|
3
|
+
import { QUOTATION_MARKS, isWithinAny, quotedSpans } from "../quoted-span.ts";
|
|
4
|
+
import { isCitation } from "./kutoten-consistency.ts";
|
|
5
|
+
|
|
6
|
+
/** 英字と数字の並び。半角(ABC123)と全角(ABC123)。 */
|
|
7
|
+
const ALNUM_RUN = /[A-Za-z0-9A-Za-z0-9]+/gu;
|
|
8
|
+
const FULLWIDTH = /^[A-Za-z0-9]+$/u;
|
|
9
|
+
const HALFWIDTH = /^[A-Za-z0-9]+$/u;
|
|
10
|
+
const LETTER = /[A-Za-zA-Za-z]/u;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* 比べる組。英字と数字を分け、一字だけの並びと二字以上の並びも分ける。「1桁は全角、2桁以上は半角」「一字の英字(A案)は全角」と
|
|
14
|
+
* 決めている文書は、組の中ではそろっている。
|
|
15
|
+
*/
|
|
16
|
+
export type AlnumKind = "letter" | "letters" | "digit" | "digits";
|
|
17
|
+
|
|
18
|
+
const kindOf = (run: string): AlnumKind => {
|
|
19
|
+
const single = [...run].length === 1;
|
|
20
|
+
if (LETTER.test(run)) return single ? "letter" : "letters";
|
|
21
|
+
return single ? "digit" : "digits";
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
/** 項目の番号の並び(行の頭や括弧の後ろの「1.」「(1)」「1)」)。項目の印で、本文の数ではない。 */
|
|
25
|
+
const LABEL_BEFORE = /(?:^|[\s、,,(([[])$/u;
|
|
26
|
+
const LABEL_AFTER = /^[..))]\]]/u;
|
|
27
|
+
const LABEL_REACH = 1;
|
|
28
|
+
|
|
29
|
+
/** 注の印(「※1」「(※3)」)。注どうしで決まった書き方をする。 */
|
|
30
|
+
const NOTE_MARK = /[※**]$/u;
|
|
31
|
+
|
|
32
|
+
/** 箇条書きの項目の頭(「- 4「勧告」…」)。目次の番号で、本文の数ではない。 */
|
|
33
|
+
const LIST_ITEM_HEAD = /^[ \t]*(?:[-*+]|\d{1,9}[.)])[ \t]+$/u;
|
|
34
|
+
|
|
35
|
+
const isLabel = (text: string, start: number, end: number): boolean =>
|
|
36
|
+
(LABEL_BEFORE.test(text.slice(Math.max(0, start - LABEL_REACH), start)) && LABEL_AFTER.test(text.slice(end, end + LABEL_REACH))) ||
|
|
37
|
+
NOTE_MARK.test(text.slice(0, start));
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* メールアドレスやドメイン名の一部(support@example.com の support・example・com)。字の幅を変えると宛先が壊れる。
|
|
41
|
+
* 点の向こうが英字のときだけ名前の区切りと見る。数の小数点(39.4)は数として数える。
|
|
42
|
+
*/
|
|
43
|
+
const LETTER_BESIDE = /[A-Za-zA-Za-z]/u;
|
|
44
|
+
|
|
45
|
+
const isInAddress = (text: string, start: number, end: number): boolean =>
|
|
46
|
+
text.charAt(start - 1) === "@" ||
|
|
47
|
+
text.charAt(end) === "@" ||
|
|
48
|
+
(text.charAt(start - 1) === "." && LETTER_BESIDE.test(text.charAt(start - 2))) ||
|
|
49
|
+
(text.charAt(end) === "." && LETTER_BESIDE.test(text.charAt(end + 1)));
|
|
50
|
+
|
|
51
|
+
/** 英字の後ろに数字を続けた名前(TLS1、Python3)と、その後ろの点でつないだ番号(Python3.12)。版の番号の頭になる。 */
|
|
52
|
+
const VERSION_SO_FAR = /[A-Za-zA-Za-z][0-90-9]+(?:\.[0-90-9]+)*$/u;
|
|
53
|
+
|
|
54
|
+
/** 版の番号を後ろ向きに読む長さの上限。長い行で点ごとに行の頭から読み直さない。 */
|
|
55
|
+
const VERSION_REACH = 32;
|
|
56
|
+
|
|
57
|
+
/** 版の番号の続き(TLS1.2 の 2、Python3.12.4 の 12 と 4)。名前の数字と同じ幅なら、幅は名前に従い、文書の数の書き方の票にはしない(TLS1.2 の 2 は数える)。 */
|
|
58
|
+
const continuesVersion = (text: string, start: number, run: string): boolean =>
|
|
59
|
+
text.charAt(start - 1) === "." &&
|
|
60
|
+
FULLWIDTH.test(run) === FULLWIDTH.test(text.charAt(start - 2)) &&
|
|
61
|
+
VERSION_SO_FAR.test(text.slice(Math.max(0, start - 1 - VERSION_REACH), start - 1));
|
|
62
|
+
|
|
63
|
+
const isListItemNumber = (source: string, offset: number): boolean => LIST_ITEM_HEAD.test(source.slice(source.lastIndexOf("\n", offset - 1) + 1, offset));
|
|
64
|
+
|
|
65
|
+
export type AlnumRun = { readonly kind: AlnumKind; readonly fullwidth: boolean; readonly written: string; readonly offset: number };
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* 文の中の英字・数字の並び。全角と半角が混ざった並び、鉤括弧で引いたものの中、項目と注の番号、箇条書きの頭の番号、版の番号の続き(TLS1.2 の 2)は数えない。
|
|
69
|
+
* offset は文書の中の位置。
|
|
70
|
+
*/
|
|
71
|
+
export const alnumRunsIn = (sentence: Sentence, source: string): AlnumRun[] => {
|
|
72
|
+
const quoted: readonly Span[] = quotedSpans(sentence.text, QUOTATION_MARKS);
|
|
73
|
+
return [...sentence.text.matchAll(ALNUM_RUN)]
|
|
74
|
+
.filter((match) => FULLWIDTH.test(match[0]) || HALFWIDTH.test(match[0]))
|
|
75
|
+
.filter((match) => !isWithinAny(quoted, { start: match.index, end: match.index + match[0].length }))
|
|
76
|
+
.filter((match) => !isLabel(sentence.text, match.index, match.index + match[0].length))
|
|
77
|
+
.filter((match) => !isListItemNumber(source, sentence.span.start + match.index))
|
|
78
|
+
.filter((match) => !isInAddress(sentence.text, match.index, match.index + match[0].length))
|
|
79
|
+
.filter((match) => !continuesVersion(sentence.text, match.index, match[0]))
|
|
80
|
+
.map((match) => ({ kind: kindOf(match[0]), fullwidth: FULLWIDTH.test(match[0]), written: match[0], offset: sentence.span.start + match.index }));
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
const KINDS: readonly AlnumKind[] = ["letter", "letters", "digit", "digits"];
|
|
84
|
+
|
|
85
|
+
/** 半角の英数字を全角に(NFKC の逆)。 */
|
|
86
|
+
const FULLWIDTH_OFFSET = 0xfee0;
|
|
87
|
+
const toFullwidth = (text: string): string => text.replace(/[A-Za-z0-9]/gu, (char) => String.fromCodePoint((char.codePointAt(0) ?? 0) + FULLWIDTH_OFFSET));
|
|
88
|
+
|
|
89
|
+
/** 同じ並びの、もう一方の幅の書き方。 */
|
|
90
|
+
const otherWidth = (run: AlnumRun): string => (run.fullwidth ? run.written.normalize("NFKC") : toFullwidth(run.written));
|
|
91
|
+
|
|
92
|
+
type Located = { readonly sentence: Sentence; readonly run: AlnumRun };
|
|
93
|
+
|
|
94
|
+
const findingOf = ({ sentence, run }: Located, count: number, of: number, limit: number): Finding => ({
|
|
95
|
+
rule: "",
|
|
96
|
+
severity: "warning",
|
|
97
|
+
line: 0,
|
|
98
|
+
column: 0,
|
|
99
|
+
quote: sentence.text.trim(),
|
|
100
|
+
values: { written: run.written, other: otherWidth(run), count, of, limit, offset: run.offset },
|
|
101
|
+
variant: run.fullwidth ? "fullwidth" : "halfwidth",
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* 英字・数字を全角で書くか半角で書くか。組(英字一字・英字の語・数字一字・数字の並び)ごとに、文書の中で少ないほうを指摘する。
|
|
106
|
+
* 少ないほうが組の limit パーセントを超えるほど多いなら、文書が使い分けている(数え方の語や見出しの書き方)と見て言わない。
|
|
107
|
+
* どちらが正しいかは決めない。
|
|
108
|
+
*/
|
|
109
|
+
export const fullwidthAlnum: Detector = (doc, options): Finding[] => {
|
|
110
|
+
const located: Located[] = doc.sentences
|
|
111
|
+
.filter((sentence) => sentence.embeddedLanguage === undefined && !isCitation(sentence, doc.source))
|
|
112
|
+
.flatMap((sentence) => alnumRunsIn(sentence, doc.source).map((run) => ({ sentence, run })));
|
|
113
|
+
return KINDS.flatMap((kind) => {
|
|
114
|
+
const ofKind = located.filter(({ run }) => run.kind === kind);
|
|
115
|
+
const odd = minorityWithin(ofKind, ({ run }) => run.fullwidth, options.limit);
|
|
116
|
+
return odd.map((entry) => findingOf(entry, odd.length, ofKind.length, options.limit));
|
|
117
|
+
});
|
|
118
|
+
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { Detector, Finding, Span } from "../plugin.ts";
|
|
2
|
+
import { QUOTATION_MARKS, isWithinAny, quotedSpans } from "../quoted-span.ts";
|
|
3
|
+
import { quoteAround } from "./quote-around.ts";
|
|
4
|
+
|
|
5
|
+
/** 半角の片仮名と、半角の句読点・鉤括弧・中黒・長音・濁点(。「」、・ー゙゚)。 */
|
|
6
|
+
const HALFWIDTH_KANA_RUN = /[\uFF61-\uFF9F]+/gu;
|
|
7
|
+
|
|
8
|
+
export type HalfwidthRun = { readonly span: Span; readonly written: string };
|
|
9
|
+
|
|
10
|
+
/** 一行の中の、鉤括弧や引用符で引いたものの位置(行の頭を 0 とする)。引いた名前(「カナ表示」の画面名)は元の書き方のまま。 */
|
|
11
|
+
const quotedOnLine = (source: string, at: number): { readonly lineStart: number; readonly quoted: readonly Span[] } => {
|
|
12
|
+
const lineStart = source.lastIndexOf("\n", at - 1) + 1;
|
|
13
|
+
const lineEnd = source.indexOf("\n", at);
|
|
14
|
+
return { lineStart, quoted: quotedSpans(source.slice(lineStart, lineEnd === -1 ? source.length : lineEnd), QUOTATION_MARKS) };
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const isQuoted = (source: string, span: Span): boolean => {
|
|
18
|
+
const { lineStart, quoted } = quotedOnLine(source, span.start);
|
|
19
|
+
return isWithinAny(quoted, { start: span.start - lineStart, end: span.end - lineStart });
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
/** 字のまま見える範囲(texts)の中の、半角の片仮名の並び。引いたものの中は数えない。 */
|
|
23
|
+
export const halfwidthKanaRuns = (source: string, texts: readonly Span[]): HalfwidthRun[] =>
|
|
24
|
+
texts.flatMap((text) =>
|
|
25
|
+
[...source.slice(text.start, text.end).matchAll(HALFWIDTH_KANA_RUN)]
|
|
26
|
+
.map((match) => ({ span: { start: text.start + match.index, end: text.start + match.index + match[0].length }, written: match[0] }))
|
|
27
|
+
.filter((run) => !isQuoted(source, run.span)),
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
export const hankakuKana: Detector = (doc): Finding[] =>
|
|
31
|
+
halfwidthKanaRuns(doc.source, doc.markup?.texts ?? []).map(({ span, written }) => ({
|
|
32
|
+
rule: "",
|
|
33
|
+
severity: "warning",
|
|
34
|
+
line: 0,
|
|
35
|
+
column: 0,
|
|
36
|
+
quote: quoteAround(doc.source, span.start, span.end),
|
|
37
|
+
values: { kana: written, fullwidth: written.normalize("NFKC"), offset: span.start },
|
|
38
|
+
}));
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
// A code span closes at the next run of exactly as many backticks as opened it: ``a ` b`` is one span.
|
|
2
2
|
const CODE_SPAN = /(?<!`)(`+)(?!`)[\s\S]*?(?<!`)\1(?!`)/gu;
|
|
3
3
|
|
|
4
|
-
const OPENING = new Set(
|
|
5
|
-
const CLOSING = new Set(
|
|
4
|
+
const OPENING = new Set("(\"'“‘[");
|
|
5
|
+
const CLOSING = new Set(")\"'”’].,:;!?");
|
|
6
6
|
|
|
7
7
|
/** The word without the punctuation around it: `(optional)` is `optional`, `Setup:` is `Setup`. */
|
|
8
8
|
const bareWord = (token: string): string => {
|
package/src/detectors/index.ts
CHANGED
|
@@ -17,10 +17,17 @@ import { paragraphLength, paragraphVariance, preambleLength, ruleOfThree, sectio
|
|
|
17
17
|
import { concreteEvidence, dashDensity, emojiDensity, ngramRepetition, undefinedAcronym } from "./signals.ts";
|
|
18
18
|
import { aiTell, contractionMix, cushionDensity, hedging, repeatedConjunction, unqualifiedSuperlative } from "./lexicon.ts";
|
|
19
19
|
import { internalJargon, properNounDensity, requiredSections } from "./team.ts";
|
|
20
|
+
import { nameVariant } from "./name-variant.ts";
|
|
20
21
|
import { latinSpacing, preferredTerm } from "./orthography.ts";
|
|
21
22
|
import { straySpace } from "./stray-space.ts";
|
|
22
23
|
import { announcedCount } from "./announced-count.ts";
|
|
24
|
+
import { outsideAnnouncedList } from "./outside-announced-list.ts";
|
|
23
25
|
import { danglingFigure } from "./dangling-figure.ts";
|
|
26
|
+
import { nominalization } from "./nominalization.ts";
|
|
27
|
+
import { requirementSmell } from "./requirement-smell.ts";
|
|
28
|
+
import { requirementModal } from "./requirement-modal.ts";
|
|
29
|
+
import { modalConflict } from "./modal-conflict.ts";
|
|
30
|
+
import { vagueFigurePointer } from "./vague-figure-pointer.ts";
|
|
24
31
|
import {
|
|
25
32
|
danglingReference,
|
|
26
33
|
dateOrder,
|
|
@@ -38,8 +45,18 @@ import { urlRunOn } from "./url-run-on.ts";
|
|
|
38
45
|
import { unbalancedBracket } from "./unbalanced-bracket.ts";
|
|
39
46
|
import { doubledPunctuation } from "./doubled-punctuation.ts";
|
|
40
47
|
import { kutotenConsistency } from "./kutoten-consistency.ts";
|
|
48
|
+
import { hankakuKana } from "./hankaku-kana.ts";
|
|
49
|
+
import { invisibleCharacter } from "./invisible-character.ts";
|
|
50
|
+
import { spaceBeforePunctuation } from "./space-before-punctuation.ts";
|
|
51
|
+
import { duplicateHeading } from "./duplicate-heading.ts";
|
|
52
|
+
import { emptySection } from "./empty-section.ts";
|
|
53
|
+
import { fullwidthAlnum } from "./fullwidth-alnum.ts";
|
|
54
|
+
import { spellingVariety } from "./spelling-variety.ts";
|
|
55
|
+
import { raNuki } from "./ra-nuki.ts";
|
|
41
56
|
import { katakanaLongVowel } from "./long-vowel.ts";
|
|
42
57
|
import { customPattern, customTokens, customWords } from "./custom.ts";
|
|
58
|
+
import { boldLabelList } from "./bold-label.ts";
|
|
59
|
+
import { acronymExpansionConflict, unusedDefinition, useBeforeDefinition } from "./definition-use.ts";
|
|
43
60
|
import { assistantResidue, colonLeadIn, contrastFraming, openerDensity, openerPile, unfilledPlaceholder } from "./ai-phrasing.ts";
|
|
44
61
|
|
|
45
62
|
/** rule 定義の how_to_find がここを引く。rule 側は実装を知らない。 */
|
|
@@ -89,6 +106,7 @@ export const DETECTORS: Readonly<Record<string, Detector>> = {
|
|
|
89
106
|
"internal-jargon": internalJargon,
|
|
90
107
|
"required-sections": requiredSections,
|
|
91
108
|
"proper-noun-density": properNounDensity,
|
|
109
|
+
"name-variant": nameVariant,
|
|
92
110
|
"preferred-term": preferredTerm,
|
|
93
111
|
"latin-spacing": latinSpacing,
|
|
94
112
|
"stray-space": straySpace,
|
|
@@ -103,7 +121,13 @@ export const DETECTORS: Readonly<Record<string, Detector>> = {
|
|
|
103
121
|
"numbering-gap": numberingGap,
|
|
104
122
|
"duplicate-definition": duplicateDefinition,
|
|
105
123
|
"announced-count": announcedCount,
|
|
124
|
+
"outside-announced-list": outsideAnnouncedList,
|
|
106
125
|
"dangling-figure": danglingFigure,
|
|
126
|
+
nominalization: nominalization,
|
|
127
|
+
"requirement-smell": requirementSmell,
|
|
128
|
+
"requirement-modal": requirementModal,
|
|
129
|
+
"modal-conflict": modalConflict,
|
|
130
|
+
"vague-figure-pointer": vagueFigurePointer,
|
|
107
131
|
"date-range-reversed": dateRangeReversed,
|
|
108
132
|
"percent-sum-mismatch": percentSumMismatch,
|
|
109
133
|
"heading-level-skip": headingLevelSkip,
|
|
@@ -113,4 +137,16 @@ export const DETECTORS: Readonly<Record<string, Detector>> = {
|
|
|
113
137
|
"unbalanced-bracket": unbalancedBracket,
|
|
114
138
|
"doubled-punctuation": doubledPunctuation,
|
|
115
139
|
"kutoten-consistency": kutotenConsistency,
|
|
140
|
+
"hankaku-kana": hankakuKana,
|
|
141
|
+
"invisible-character": invisibleCharacter,
|
|
142
|
+
"space-before-punctuation": spaceBeforePunctuation,
|
|
143
|
+
"duplicate-heading": duplicateHeading,
|
|
144
|
+
"empty-section": emptySection,
|
|
145
|
+
"fullwidth-alnum": fullwidthAlnum,
|
|
146
|
+
"spelling-variety": spellingVariety,
|
|
147
|
+
"ra-nuki": raNuki,
|
|
148
|
+
"bold-label-list": boldLabelList,
|
|
149
|
+
"unused-definition": unusedDefinition,
|
|
150
|
+
"use-before-definition": useBeforeDefinition,
|
|
151
|
+
"acronym-expansion-conflict": acronymExpansionConflict,
|
|
116
152
|
};
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import type { Detector, Finding, Span } from "../plugin.ts";
|
|
2
|
+
import { isJapanese } from "../orthography.ts";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* 見えない字の種類。zero-width はゼロ幅の空白・接合子と途中の BOM、soft-hyphen は任意の改行位置の印、direction は文字の向きの指定、
|
|
6
|
+
* control は改行・タブ・改ページ以外の制御文字、hidden は字の後ろに隠せる印(タグ文字、二つ以上続く異体字の指定)、
|
|
7
|
+
* nbsp は普通の空白が要る所のノーブレークスペース。どれも画面では見えず、検索・コピー・リンクを壊す。向きの指定と隠せる印は、
|
|
8
|
+
* 読み手に見えるものと機械が読むものを食い違わせるのにも使える。
|
|
9
|
+
*/
|
|
10
|
+
export type InvisibleKind = "zero-width" | "soft-hyphen" | "direction" | "control" | "hidden" | "nbsp";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* 種類ごとの字(正規表現の文字クラスの中身)。制御文字(Cc)には改行・タブ・改ページも入るので、それは isIntended が外す。
|
|
14
|
+
* hidden の異体字の指定は、字に一つ付いたものを isIntended が外す。
|
|
15
|
+
*/
|
|
16
|
+
const CLASSES: readonly (readonly [string, InvisibleKind])[] = [
|
|
17
|
+
["\\u200B-\\u200D\\u2060-\\u2064\\uFEFF", "zero-width"],
|
|
18
|
+
["\\u00AD", "soft-hyphen"],
|
|
19
|
+
["\\u200E\\u200F\\u061C\\u202A-\\u202E\\u2066-\\u2069", "direction"],
|
|
20
|
+
["\\p{Cc}", "control"],
|
|
21
|
+
["\\u{E0001}\\u{E0020}-\\u{E007F}\\p{Variation_Selector}", "hidden"],
|
|
22
|
+
["\\u00A0\\u2007\\u202F", "nbsp"],
|
|
23
|
+
];
|
|
24
|
+
|
|
25
|
+
const KIND_OF: readonly (readonly [RegExp, InvisibleKind])[] = CLASSES.map(([chars, kind]) => [new RegExp(`^[${chars}]$`, "u"), kind]);
|
|
26
|
+
|
|
27
|
+
/** 一字ずつ当てる。タグ文字と異体字の指定は BMP の外にもあるので u で読む。 */
|
|
28
|
+
const CANDIDATE = new RegExp(`[${CLASSES.map(([chars]) => chars).join("")}]`, "gu");
|
|
29
|
+
|
|
30
|
+
const kindOf = (char: string): InvisibleKind | undefined => KIND_OF.find(([pattern]) => pattern.test(char))?.[1];
|
|
31
|
+
|
|
32
|
+
/** 接合子(ZWJ・ZWNJ)で字の形を変える文字体系。アラビア文字やインドの文字では、見えない接合子が綴りの一部。 */
|
|
33
|
+
const JOINING_SCRIPT = /[\p{L}\p{M}]/u;
|
|
34
|
+
const NON_JOINING_SCRIPT = /[\p{Script=Latin}\p{Script=Greek}\p{Script=Cyrillic}\p{Script=Han}\p{Script=Hiragana}\p{Script=Katakana}\p{Script=Hangul}]/u;
|
|
35
|
+
/** どの文字体系にも付くアクセント(U+0301 など)。ラテン文字の後ろの接合子を、接合子を使う文字体系のものと読まない。 */
|
|
36
|
+
const INHERITED = /\p{Script=Inherited}/u;
|
|
37
|
+
|
|
38
|
+
/** 右から左に書く文字。この字の隣の向きの印(LRM・RLM)は、英数字と混ぜて正しく並べるためのもの。 */
|
|
39
|
+
const RIGHT_TO_LEFT = /[\p{Script=Hebrew}\p{Script=Arabic}\p{Script=Syriac}\p{Script=Thaana}\p{Script=Nko}\p{Script=Samaritan}]/u;
|
|
40
|
+
|
|
41
|
+
/** 字の向きを強制する印(LRO・RLO)。右から左の文字の隣でも、見える順を入れ替えるのに使えるので、いつも指摘する。 */
|
|
42
|
+
const OVERRIDE = /[\u202D\u202E]/u;
|
|
43
|
+
|
|
44
|
+
/** 絵文字の部品。ZWJ でつないだ絵文字(家族・職業)と、肌の色や異体字の指定を挟んだもの。 */
|
|
45
|
+
const PICTOGRAPH = /[\p{Extended_Pictographic}\p{Emoji_Modifier}\uFE0F]/u;
|
|
46
|
+
|
|
47
|
+
/** 黒い旗の絵文字。後ろにタグ文字を並べて地域の旗(イングランドなど)を書く。地域の名は六字までで、旗は字の数で十四単位に収まる。 */
|
|
48
|
+
const BLACK_FLAG = String.fromCodePoint(0x1f3f4);
|
|
49
|
+
const FLAG_REACH = 14;
|
|
50
|
+
const TAG = /[\u{E0020}-\u{E007F}]/u;
|
|
51
|
+
|
|
52
|
+
const VARIATION_SELECTOR = /\p{Variation_Selector}/u;
|
|
53
|
+
|
|
54
|
+
/** 文書の形を作る制御文字(タブ・改行・改ページ)。 */
|
|
55
|
+
const LAYOUT_CONTROL = /[\t\n\r\f]/u;
|
|
56
|
+
|
|
57
|
+
/** Markdown の行の頭の印(見出し・箇条・番号・引用)。後ろの空白がノーブレークスペースだと、印として読まれない。 */
|
|
58
|
+
const BLOCK_MARKER = /^[ \t]*(?:#{1,6}|[-*+]|\d{1,9}[.)]|>)$/u;
|
|
59
|
+
const JAPANESE_MARK = /[、。「」『』()・]/u;
|
|
60
|
+
|
|
61
|
+
/** at の前の一字(サロゲートの対は一字に)。文書の頭なら空。 */
|
|
62
|
+
const charBefore = (source: string, at: number): string => {
|
|
63
|
+
if (at <= 0) return "";
|
|
64
|
+
const low = source.charCodeAt(at - 1);
|
|
65
|
+
const isLow = low >= 0xdc00 && low <= 0xdfff;
|
|
66
|
+
return isLow && at >= 2 ? source.slice(at - 2, at) : source.slice(at - 1, at);
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
/** at から始まる一字。文書の終わりなら空。 */
|
|
70
|
+
const charAt = (source: string, at: number): string => {
|
|
71
|
+
const code = source.codePointAt(at);
|
|
72
|
+
return code === undefined ? "" : String.fromCodePoint(code);
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
const isJoiningLetter = (char: string): boolean => JOINING_SCRIPT.test(char) && !NON_JOINING_SCRIPT.test(char) && !INHERITED.test(char);
|
|
76
|
+
|
|
77
|
+
const isJapaneseSide = (char: string): boolean => isJapanese(char) || JAPANESE_MARK.test(char);
|
|
78
|
+
|
|
79
|
+
const CANCEL_TAG = String.fromCodePoint(0xe007f);
|
|
80
|
+
|
|
81
|
+
/** タグ文字の並びをさかのぼった先が黒い旗で、並びが取り消しのタグ(U+E007F)で閉じていれば、地域の旗の中。 */
|
|
82
|
+
const isInFlag = (source: string, at: number): boolean => {
|
|
83
|
+
const before = [...source.slice(Math.max(0, at - FLAG_REACH), at)];
|
|
84
|
+
const after = [...source.slice(at, at + FLAG_REACH)];
|
|
85
|
+
const closing = after.findIndex((char) => !TAG.test(char) || char === CANCEL_TAG);
|
|
86
|
+
return before[before.findLastIndex((char) => !TAG.test(char))] === BLACK_FLAG && after[closing] === CANCEL_TAG;
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
/** 異体字の指定が付く字。空白や文書の頭の後ろの指定は、付く字が無い。 */
|
|
90
|
+
const SELECTOR_BASE = /\S/u;
|
|
91
|
+
|
|
92
|
+
/** 字が普通に使う異体字の指定は、字の後ろに一つ。二つ以上続く指定と、付く字の無い指定は、何かを隠した並び。 */
|
|
93
|
+
const isLoneSelector = (source: string, at: number, length: number): boolean => {
|
|
94
|
+
const before = charBefore(source, at);
|
|
95
|
+
return SELECTOR_BASE.test(before) && !VARIATION_SELECTOR.test(before) && !VARIATION_SELECTOR.test(charAt(source, at + length));
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
/** 向きの分離の開き(LRI・RLI・FSI)と閉じ(PDI)。囲んだ中に右から左の文字があれば、混ぜて並べるための印。 */
|
|
99
|
+
const ISOLATE_OPENS: readonly string[] = ["\u2066", "\u2067", "\u2068"];
|
|
100
|
+
const ISOLATE_CLOSE = "\u2069";
|
|
101
|
+
const ISOLATE_REACH = 200;
|
|
102
|
+
|
|
103
|
+
/** 分離の印 char(at の位置)が囲む中身。組になる印が近くに無ければ undefined。 */
|
|
104
|
+
const isolatedText = (source: string, at: number, char: string): string | undefined => {
|
|
105
|
+
if (ISOLATE_OPENS.includes(char)) {
|
|
106
|
+
const inside = source.slice(at + 1, at + 1 + ISOLATE_REACH);
|
|
107
|
+
const end = inside.indexOf(ISOLATE_CLOSE);
|
|
108
|
+
return end === -1 ? undefined : inside.slice(0, end);
|
|
109
|
+
}
|
|
110
|
+
if (char !== ISOLATE_CLOSE) return undefined;
|
|
111
|
+
const inside = source.slice(Math.max(0, at - ISOLATE_REACH), at);
|
|
112
|
+
const start = Math.max(...ISOLATE_OPENS.map((open) => inside.lastIndexOf(open)));
|
|
113
|
+
return start === -1 ? undefined : inside.slice(start + 1);
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
const isolatesRightToLeft = (source: string, at: number, char: string): boolean => RIGHT_TO_LEFT.test(isolatedText(source, at, char) ?? "");
|
|
117
|
+
|
|
118
|
+
/** 文書の頭の BOM はファイルの印で、本文の字ではない。 */
|
|
119
|
+
const BYTE_ORDER_MARK = "\uFEFF";
|
|
120
|
+
|
|
121
|
+
const lineBefore = (source: string, at: number): string => source.slice(source.lastIndexOf("\n", at - 1) + 1, at);
|
|
122
|
+
|
|
123
|
+
/** 見えない字一つと、その前後の字。 */
|
|
124
|
+
type Place = { readonly source: string; readonly at: number; readonly char: string; readonly before: string; readonly after: string };
|
|
125
|
+
|
|
126
|
+
const ZERO_WIDTH_JOINER = "\u200D";
|
|
127
|
+
const ZERO_WIDTH_NON_JOINER = "\u200C";
|
|
128
|
+
|
|
129
|
+
const joinsLetters = ({ before, after }: Place): boolean => isJoiningLetter(before) || isJoiningLetter(after);
|
|
130
|
+
|
|
131
|
+
/** 種類ごとの、書き手が意味を持たせて置いた見えない字。そうでないものだけを指摘する。 */
|
|
132
|
+
const INTENDED: Readonly<Record<InvisibleKind, (place: Place) => boolean>> = {
|
|
133
|
+
"zero-width": (place) => {
|
|
134
|
+
if (place.char === BYTE_ORDER_MARK) return place.at === 0;
|
|
135
|
+
if (place.char === ZERO_WIDTH_JOINER) return (PICTOGRAPH.test(place.before) && PICTOGRAPH.test(place.after)) || joinsLetters(place);
|
|
136
|
+
return place.char === ZERO_WIDTH_NON_JOINER && joinsLetters(place);
|
|
137
|
+
},
|
|
138
|
+
"soft-hyphen": () => false,
|
|
139
|
+
direction: ({ source, at, char, before, after }) =>
|
|
140
|
+
!OVERRIDE.test(char) && (RIGHT_TO_LEFT.test(before) || RIGHT_TO_LEFT.test(after) || isolatesRightToLeft(source, at, char)),
|
|
141
|
+
control: ({ char }) => LAYOUT_CONTROL.test(char),
|
|
142
|
+
hidden: ({ source, at, char }) => (TAG.test(char) ? isInFlag(source, at) : isLoneSelector(source, at, char.length)),
|
|
143
|
+
nbsp: ({ source, at, before, after }) => !BLOCK_MARKER.test(lineBefore(source, at)) && !isJapaneseSide(before) && !isJapaneseSide(after),
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
const isIntended = (source: string, at: number, char: string, kind: InvisibleKind): boolean =>
|
|
147
|
+
INTENDED[kind]({ source, at, char, before: charBefore(source, at), after: charAt(source, at + char.length) });
|
|
148
|
+
|
|
149
|
+
export type InvisibleRun = { readonly span: Span; readonly kind: InvisibleKind; readonly codes: readonly string[] };
|
|
150
|
+
|
|
151
|
+
export const codePointName = (char: string): string => `U+${(char.codePointAt(0) ?? 0).toString(16).toUpperCase().padStart(4, "0")}`;
|
|
152
|
+
|
|
153
|
+
const joinRuns = (runs: InvisibleRun[], next: InvisibleRun): InvisibleRun[] => {
|
|
154
|
+
const last = runs.at(-1);
|
|
155
|
+
if (last === undefined || last.span.end !== next.span.start || last.kind !== next.kind) return [...runs, next];
|
|
156
|
+
const codes = last.codes.includes(next.codes[0] ?? "") ? last.codes : [...last.codes, ...next.codes];
|
|
157
|
+
return [...runs.slice(0, -1), { span: { start: last.span.start, end: next.span.end }, kind: last.kind, codes }];
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
/** source の中の、見えない字の並び。隣り合う同じ種類の字は一つの並びにまとめる。 */
|
|
161
|
+
export const invisibleRuns = (source: string): InvisibleRun[] =>
|
|
162
|
+
[...source.matchAll(CANDIDATE)]
|
|
163
|
+
.flatMap((match): InvisibleRun[] => {
|
|
164
|
+
const kind = kindOf(match[0]);
|
|
165
|
+
if (kind === undefined || isIntended(source, match.index, match[0], kind)) return [];
|
|
166
|
+
return [{ span: { start: match.index, end: match.index + match[0].length }, kind, codes: [codePointName(match[0])] }];
|
|
167
|
+
})
|
|
168
|
+
.reduce<InvisibleRun[]>(joinRuns, []);
|
|
169
|
+
|
|
170
|
+
/** 見せる前後の字数。 */
|
|
171
|
+
const QUOTE_REACH = 20;
|
|
172
|
+
|
|
173
|
+
/** 見えない字を「⟨U+200B⟩」のように見える形にして、前後を引く。 */
|
|
174
|
+
export const visibleQuote = (source: string, span: Span): string =>
|
|
175
|
+
source
|
|
176
|
+
.slice(Math.max(0, span.start - QUOTE_REACH), span.end + QUOTE_REACH)
|
|
177
|
+
.replace(CANDIDATE, (char) => (LAYOUT_CONTROL.test(char) ? char : `⟨${codePointName(char)}⟩`))
|
|
178
|
+
.replace(/\s+/gu, " ")
|
|
179
|
+
.trim();
|
|
180
|
+
|
|
181
|
+
export const invisibleCharacter: Detector = (doc): Finding[] =>
|
|
182
|
+
invisibleRuns(doc.source).map((run) => ({
|
|
183
|
+
rule: "",
|
|
184
|
+
severity: "warning",
|
|
185
|
+
line: 0,
|
|
186
|
+
column: 0,
|
|
187
|
+
quote: visibleQuote(doc.source, run.span),
|
|
188
|
+
values: { code: run.codes.join(" "), count: [...doc.source.slice(run.span.start, run.span.end)].length, offset: run.span.start },
|
|
189
|
+
variant: run.kind,
|
|
190
|
+
}));
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Detector, Finding, Sentence, Span } from "../plugin.ts";
|
|
2
|
-
import {
|
|
2
|
+
import { minorityOf } from "../orthography.ts";
|
|
3
3
|
import { isWithinAny, quotedSpans } from "../quoted-span.ts";
|
|
4
4
|
|
|
5
5
|
/** 日本語の読点と句点の、二つの書き方。「、。」と、横書きの論文や公用文に多い「,.」。どちらが正しいかは決めない。 */
|
|
@@ -86,7 +86,7 @@ export const kutotenConsistency: Detector = (doc, options): Finding[] => {
|
|
|
86
86
|
.flatMap((sentence) => marksIn(sentence).map((entry) => ({ sentence, entry })));
|
|
87
87
|
return KINDS.flatMap((kind) => {
|
|
88
88
|
const ofKind = located.filter(({ entry }) => entry.kind === kind.id);
|
|
89
|
-
const minority =
|
|
89
|
+
const minority = minorityOf(ofKind.map(({ entry }) => entry.western));
|
|
90
90
|
if (minority === undefined) return [];
|
|
91
91
|
const odd = ofKind.filter(({ entry }) => entry.western === minority);
|
|
92
92
|
if (odd.length < options.limit) return [];
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { LexiconEntry, Sentence, Token } from "../plugin.ts";
|
|
2
2
|
import { proseText } from "../measure.ts";
|
|
3
|
-
import { straightApostrophes } from "../orthography.ts";
|
|
3
|
+
import { escapeRegExp, straightApostrophes } from "../orthography.ts";
|
|
4
4
|
|
|
5
5
|
/** 活用する品詞。"best" を "good" と同じ語にはしない。 */
|
|
6
6
|
const INFLECTING = new Set(["VERB", "AUX"]);
|
|
@@ -49,6 +49,26 @@ export const entryRanges = (sentence: Sentence, entry: LexiconEntry): TokenRange
|
|
|
49
49
|
return tokens.flatMap((_token, start) => (runsAt(tokens, words, start) ? [{ start, end: start + words.length }] : []));
|
|
50
50
|
};
|
|
51
51
|
|
|
52
|
+
const endsInText = (text: string, word: string): number[] =>
|
|
53
|
+
word === "" ? [] : [...text.matchAll(new RegExp(escapeRegExp(word), "gu"))].map((match) => match.index + word.length);
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* 語彙の語が文の中で終わる位置(文の中の字の位置)。出現ごとに一つ。終わりの位置で数えると、重なる二つの語
|
|
57
|
+
* (「させていただく」と「せていただく」)が同じ一回の出現を二度数えない。品詞が無ければ、空白をまとめた文字列の上の位置。
|
|
58
|
+
*/
|
|
59
|
+
export const entryEnds = (sentence: Sentence, entry: LexiconEntry): number[] => {
|
|
60
|
+
const tokens = sentence.tokens;
|
|
61
|
+
const words = entry.tokens;
|
|
62
|
+
if (tokens === undefined || words === undefined || words.length === 0) return endsInText(comparableText(sentence), comparableWords(entry.pattern));
|
|
63
|
+
return entryRanges(sentence, entry).map((range) => tokens[range.end - 1]?.span.end ?? range.end);
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
/** 語彙の語が、文の end 番目の語のちょうど手前で終わるか(「SES との最大」の「との」)。品詞が無ければ位置を言えないので当たらない。 */
|
|
67
|
+
export const entryEndsAt = (tokens: readonly Token[], entry: LexiconEntry, end: number): boolean => {
|
|
68
|
+
const words = entry.tokens;
|
|
69
|
+
return words !== undefined && words.length > 0 && end >= words.length && runsAt(tokens, words, end - words.length);
|
|
70
|
+
};
|
|
71
|
+
|
|
52
72
|
const LEADING_MARK = new Set(["PUNCT", "SYM"]);
|
|
53
73
|
|
|
54
74
|
/** 語彙の語で文が始まるか。文頭の記号は飛ばす。品詞が無ければ文字列の前方一致。 */
|
|
@@ -59,3 +79,15 @@ export const entryOpens = (sentence: Sentence, entry: LexiconEntry): boolean =>
|
|
|
59
79
|
const first = tokens.findIndex((token) => !LEADING_MARK.has(token.pos));
|
|
60
80
|
return runsAt(tokens, words, first);
|
|
61
81
|
};
|
|
82
|
+
|
|
83
|
+
/** Marks and closing brackets that may follow the last word of a sentence (こと。」, must.). */
|
|
84
|
+
const TRAILING_MARKS = new Set([..." \t\n。..!!??」』))\"'”’"]);
|
|
85
|
+
|
|
86
|
+
const withoutTrailingMarks = (text: string): string => {
|
|
87
|
+
const chars = [...text];
|
|
88
|
+
return chars.slice(0, chars.findLastIndex((char) => !TRAILING_MARKS.has(char)) + 1).join("");
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
/** Whether the sentence ends with the lexicon's word, past its closing marks (「…できること。」 ends with こと). */
|
|
92
|
+
export const entryCloses = (sentence: Sentence, entry: LexiconEntry): boolean =>
|
|
93
|
+
withoutTrailingMarks(comparableText(sentence)).endsWith(comparableWords(entry.pattern));
|