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
package/skills/chaff/SKILL.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: chaff
|
|
3
|
-
description: "Check prose with chaff (npx chaffjs): pick the genre preset for the kind of document (contract, statute, manual, FAQ, paper, fiction, transcript…), run it on Markdown or text, read each finding, fix the text or record why not, and tune chaff.yaml with rules --json and relax --why. Use when asked to lint, proofread or tighten documents, or to set up a team's writing rules."
|
|
3
|
+
description: "Check prose with chaff (npx chaffjs): pick the genre preset for the kind of document (contract, statute, manual, FAQ, paper, fiction, transcript…), run it on Markdown or text, read each finding, fix the text or record why not, and tune chaff.yaml with rules --json and relax --why. Use when asked to lint, proofread or tighten documents, or to set up a team's writing rules. Also use when asked to make text sound less AI-generated (「AIっぽさをなくして」「AI臭さを消して」「人間らしく書き直して」, 'make this sound human'): Light, Bold or Full mode, with Full (全面書き直し) when the request says 「全面的に」 or 'from scratch'."
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Checking writing with chaff
|
|
@@ -101,19 +101,50 @@ committing `chaff.yaml`. The guide page "Adding a rule" covers each way and how
|
|
|
101
101
|
|
|
102
102
|
## Making AI-sounding text sound human
|
|
103
103
|
|
|
104
|
+
Use this section when asked to make a text sound less generated: 「AIっぽさをなくして」「AI臭さを消して」「人間らしく
|
|
105
|
+
書き直して」, "make this sound human", "this reads like AI wrote it".
|
|
106
|
+
|
|
104
107
|
chaff marks the shapes common in generated text (group "Signs of generated text" in `npx chaffjs rules`: `ai-tell`,
|
|
105
108
|
`contrast-framing`, `stock-transition`, `announcing-opener`, `colon-lead-in` (Japanese only), `assistant-residue`, and the signals
|
|
106
109
|
`ai-generated-composite` reads). Most are experimental: run with `--experimental`. None of them says the text was
|
|
107
|
-
generated.
|
|
110
|
+
generated. chaff never rewrites; the rewriting is yours.
|
|
111
|
+
|
|
112
|
+
**Start with the plan.** Run `npx chaffjs fix-plan <file> --experimental` (with the genre's `--genre`; `--json` to
|
|
113
|
+
read it as data). It prints, in the document's language:
|
|
114
|
+
|
|
115
|
+
- the constraints: keep facts, numbers, conditions and names; add no fact; ask the writer instead of inventing a
|
|
116
|
+
specific; two passes at most;
|
|
117
|
+
- the recommended mode and why, and the document-level signals with the outline's numbers;
|
|
118
|
+
- for each rule that fired: its rewrite direction, what to keep, what to avoid, one before/after pair, hints for the
|
|
119
|
+
`ai-tell` phrases found, and the spots (line and sentence);
|
|
120
|
+
- the check commands to run on the rewrite.
|
|
121
|
+
|
|
122
|
+
Follow it. Rewrite in the mode it recommends; a request that says 「全面的に」 or "from scratch" means Full whatever it
|
|
123
|
+
says. Apply each rule's direction at its spots. Where a fix needs a specific the text does not give, leave [ ] and
|
|
124
|
+
ask. Save the result under the name the plan gives, run its check commands, and report the mode and the checks'
|
|
125
|
+
output. The mode steps below say the rest.
|
|
126
|
+
|
|
127
|
+
Pick one of three modes and say which.
|
|
128
|
+
|
|
129
|
+
| Mode | Changes | Pick it when |
|
|
130
|
+
| --- | --- | --- |
|
|
131
|
+
| **Light** | only the flagged spots | the content and the structure are fine and only some phrasing is off |
|
|
132
|
+
| **Bold** | each section's prose, keeping the outline | the outline is fixed (a report template, a manual, required sections) |
|
|
133
|
+
| **Full** | the whole document, from scratch | the request says 「全面的に」「一から」「全部書き直して」 / "from scratch" / "rewrite the whole thing"; or the genre is `blog/*` or an essay (`literature/essay`); or `ai-generated-composite` fires |
|
|
134
|
+
|
|
135
|
+
For a blog or an essay, and whenever `ai-generated-composite` fires, recommend Full. A Light or Bold pass leaves the
|
|
136
|
+
skeleton of generated text: a heading every few paragraphs, bold lead-ins, symmetric sections, a closing まとめ that
|
|
137
|
+
repeats the body. The writer usually wants the structure changed, not only the sentences.
|
|
108
138
|
|
|
109
|
-
**Light** (only the flagged spots)
|
|
139
|
+
**Light** (only the flagged spots):
|
|
110
140
|
|
|
111
|
-
1. `npx chaffjs <file> --experimental` (or with the genre's `--genre`)
|
|
112
|
-
2. Rewrite only
|
|
141
|
+
1. `npx chaffjs fix-plan <file> --experimental` (or with the genre's `--genre`).
|
|
142
|
+
2. Rewrite only the flagged spots, each by its rule's direction in the plan. Keep the meaning, numbers, conditions and
|
|
143
|
+
technical constraints.
|
|
113
144
|
3. Run chaff again. At most two rewrite passes.
|
|
114
145
|
4. Show the rewritten text and a short list of what changed and why.
|
|
115
146
|
|
|
116
|
-
**Bold** (section by section
|
|
147
|
+
**Bold** (section by section, outline kept):
|
|
117
148
|
|
|
118
149
|
1. Run chaff with `--experimental` and note the document-level signals: the `ai-generated-composite` inputs,
|
|
119
150
|
`bold-density`, `contrast-framing`, `stock-transition`, `sentence-rhythm`.
|
|
@@ -126,7 +157,75 @@ generated. When asked to make a text sound less generated, pick one of two modes
|
|
|
126
157
|
dropped fact; for one that is not a fact (a number inside a metaphor, the heading of a section you cut), say why it
|
|
127
158
|
stays out. `--allow-dropped <kind>` excludes a kind you cut on purpose. Never leave an added fact.
|
|
128
159
|
|
|
129
|
-
|
|
160
|
+
**Full** (全面書き直し, from scratch):
|
|
161
|
+
|
|
162
|
+
1. **Take inventory before writing.** `npx chaffjs facts <file> --json` lists every fact `compare` will hold you to
|
|
163
|
+
(numbers, dates, times, URLs, code, names, quotations, headings, references, footnotes). `npx chaffjs outline <file>`
|
|
164
|
+
gives the old outline and its shape. Then write down, one line each, the writer's actual claims and every concrete
|
|
165
|
+
experience, example and opinion in the original. This inventory is what you write from; the old sentences are not.
|
|
166
|
+
2. **Throw away the structure.** Decide what the piece is for and who reads it. Choose one angle or story for the
|
|
167
|
+
whole piece, and outline it the way a person would. Do these before writing:
|
|
168
|
+
- reorder: put the conclusion, or the most interesting episode, first;
|
|
169
|
+
- merge thin sections and split overloaded ones, so a section is a real unit, not one heading per paragraph;
|
|
170
|
+
- cut sections that only restate, the closing まとめ / "Conclusion" included;
|
|
171
|
+
- drop a heading that wraps a single paragraph;
|
|
172
|
+
- turn a list-of-points section into a narrative with one through-line;
|
|
173
|
+
- no symmetric three-point sections unless the content really has three parts;
|
|
174
|
+
- move the writer's concrete experience to where it carries the argument;
|
|
175
|
+
- open with the point or a concrete scene, not a generic opener.
|
|
176
|
+
3. **Show the new outline first.** Put the old outline (headings from `chaff outline`) next to the proposed one:
|
|
177
|
+
headings, and one line per section on what it says. The writer then sees the structural change at a glance. Wait
|
|
178
|
+
for a yes, unless the person asked for it to be done without asking; then go straight on.
|
|
179
|
+
4. **Write it fresh**, in the writer's voice, from the inventory:
|
|
180
|
+
- paragraphs that argue, joined by connectives; sentence lengths that vary;
|
|
181
|
+
- plain verbs instead of metaphors ("静かに壊れる", "silently fails" → what actually happens);
|
|
182
|
+
- the writer's own episodes and numbers kept; generic filler cut;
|
|
183
|
+
- bold only where a reader truly must not miss something; lists only for real enumerations, steps or commands;
|
|
184
|
+
- no hedges stacked on a claim, no announcing what you are about to say, no "it's not X, it's Y", no em dashes;
|
|
185
|
+
- the register of the writer's other paragraphs (です/ます or だ/である) and of the platform (Zenn, a company
|
|
186
|
+
report, an email);
|
|
187
|
+
- nothing the inventory does not have: no new facts, people, numbers, causes or consequences.
|
|
188
|
+
|
|
189
|
+
Three principles while writing:
|
|
190
|
+
- **Undo personification.** A thing or an idea as the subject of a verb of will (秩序が壊れる, 文化が醸成される,
|
|
191
|
+
アーキテクチャが要求する) becomes what a person or the system does. 「チームにレビューの文化が醸成された」 →
|
|
192
|
+
「チームで互いにレビューをするようになった」. If the original does not say who, ask the writer.
|
|
193
|
+
- **Turn noun endings and noun chains back into sentences with a verb.** 「キュー滞留によるメッセージ処理遅延の発生。」 →
|
|
194
|
+
「キューにメッセージが溜まり、処理が遅れた。」 A noun ending hides who did what and when.
|
|
195
|
+
- **Never invent specifics.** A vague sentence may read better with a concrete example. If the writer did not give
|
|
196
|
+
one, ask for it, or mark your guess for them to confirm. Never write it as fact.
|
|
197
|
+
「チームの雰囲気が良くなった」 stays as it is, with a question: 「雰囲気が良くなったと感じたのは、どんな場面でしたか」.
|
|
198
|
+
`chaff compare` catches an added number or name, but not added wording such as 「朝会で冗談が出るようになり」, so
|
|
199
|
+
this one is yours to keep.
|
|
200
|
+
5. **Check.**
|
|
201
|
+
- `npx chaffjs <old> --experimental` and `npx chaffjs <new> --experimental`: the AI signals before and after.
|
|
202
|
+
- `npx chaffjs outline <old> <new>`: headings, average section length, text in lists, bold, before and after. A
|
|
203
|
+
restructure shows up here, not only in rule counts.
|
|
204
|
+
- `npx chaffjs compare <old> <new> --distinct --allow-dropped heading --allow-added heading`: the headings are the
|
|
205
|
+
structure you rebuilt on purpose; every other dropped fact is restored and no other fact is added. A cut まとめ
|
|
206
|
+
restates facts the body still holds, so `--distinct` counts a fact as kept when the new text states it at least
|
|
207
|
+
once.
|
|
208
|
+
6. **Stop** when all of these hold, or after two full passes, whichever comes first:
|
|
209
|
+
- `ai-generated-composite` does not fire;
|
|
210
|
+
- the density rules (`bold-density`, `contrast-framing`, `stock-transition`, `colon-lead-in`) are under their limits;
|
|
211
|
+
- `compare` is clean, or every exclusion has a stated reason.
|
|
212
|
+
7. **Show** the new text, a short "what changed and why" list, then a table of the signals and the shape before and
|
|
213
|
+
after, taken from chaff's output.
|
|
214
|
+
|
|
215
|
+
**Read for these too.** chaff cannot detect them mechanically, so check them by reading, in every mode:
|
|
216
|
+
|
|
217
|
+
- a generic opener or closer ("本記事では〜解説します", "いかがでしたでしょうか", "In today's fast-paced world");
|
|
218
|
+
- explaining what every reader already knows;
|
|
219
|
+
- every section the same length and the same shape;
|
|
220
|
+
- a list of benefits with no cost or trade-off;
|
|
221
|
+
- no first-hand detail: nothing the writer saw, measured or did;
|
|
222
|
+
- the same enthusiasm everywhere, so nothing stands out;
|
|
223
|
+
- politeness piled on politeness;
|
|
224
|
+
- a definition nobody asked for;
|
|
225
|
+
- headings that are full sentences or slogans;
|
|
226
|
+
- a まとめ / "Conclusion" that repeats the body.
|
|
227
|
+
|
|
228
|
+
In every mode:
|
|
130
229
|
|
|
131
230
|
- Never add facts, people, numbers or causes the original does not have.
|
|
132
231
|
- Keep a domain term a word rule trips when it is right in context.
|
|
@@ -136,7 +235,8 @@ In both modes:
|
|
|
136
235
|
and commands as lists; in a business document, put the conclusion and who does what first and keep tables and
|
|
137
236
|
figures; in an essay, fold punchlines back in and replace big words ("真理", "new possibilities") with the scene.
|
|
138
237
|
|
|
139
|
-
The guide page "Making AI-sounding text sound human" has a
|
|
238
|
+
The guide page "Making AI-sounding text sound human" has worked examples of a Bold and a Full rewrite, with chaff's
|
|
239
|
+
output before and after.
|
|
140
240
|
|
|
141
241
|
## Structured documents
|
|
142
242
|
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { namesAcronym } from "./detectors/acronym-expansion.ts";
|
|
2
|
+
|
|
3
|
+
// 略語とその展開を、文書が書いたとおりに読む。同じ略語を二通りに展開していないかを比べるため。
|
|
4
|
+
|
|
5
|
+
/** 本文の一続き(文)。start は文書の中の位置。 */
|
|
6
|
+
export type ExpansionText = { readonly start: number; readonly text: string };
|
|
7
|
+
|
|
8
|
+
export type Expansion = { readonly acronym: string; readonly name: string; readonly offset: number };
|
|
9
|
+
|
|
10
|
+
const ACRONYM = String.raw`[A-Z][A-Z0-9&]*[A-Z]`;
|
|
11
|
+
const QUOTE = String.raw`["“”'‘’「」]?`;
|
|
12
|
+
|
|
13
|
+
/** 名前のあとに括弧で略語(Service Level Agreement (SLA)、(the "SLA"))。 */
|
|
14
|
+
const NAME_FIRST = new RegExp(String.raw`[((]\s*(?:the\s+)?${QUOTE}(?<acronym>${ACRONYM})${QUOTE}\s*[))]`, "gu");
|
|
15
|
+
/** 略語のあとに括弧で名前(SLA (Service Level Agreement)、CI(継続的インテグレーション))。 */
|
|
16
|
+
const ACRONYM_FIRST = new RegExp(String.raw`(?<![\p{Script=Latin}\p{N}])(?<acronym>${ACRONYM})\s?[((](?<name>[^()()\n]{2,60})[))]`, "gu");
|
|
17
|
+
|
|
18
|
+
/** 頭文字に数えない短い語(Department of Defense の of)。 */
|
|
19
|
+
const SMALL_WORDS: ReadonlySet<string> = new Set(["of", "and", "the", "for", "to", "in", "on", "a", "an", "&", "de", "by", "with"]);
|
|
20
|
+
|
|
21
|
+
const wordsBefore = (text: string): string[] =>
|
|
22
|
+
text
|
|
23
|
+
.replaceAll(/["“”'‘’]/gu, "")
|
|
24
|
+
.trim()
|
|
25
|
+
.split(/\s+/u)
|
|
26
|
+
.filter((word) => word !== "");
|
|
27
|
+
|
|
28
|
+
const initialsOf = (words: readonly string[]): string =>
|
|
29
|
+
words
|
|
30
|
+
.filter((word) => !SMALL_WORDS.has(word.toLowerCase()))
|
|
31
|
+
.flatMap((word) => word.split("-"))
|
|
32
|
+
.map((word) => word.charAt(0))
|
|
33
|
+
.join("")
|
|
34
|
+
.toUpperCase();
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* 括弧の前の語のうち、頭文字がちょうど略語になる最も短い並び(the Federal Open Market Committee (FOMC) なら
|
|
38
|
+
* Federal Open Market Committee)。頭文字の揃う並びが無ければ、展開ではない((see Table 2) や (FOMC) の前の文)。
|
|
39
|
+
*/
|
|
40
|
+
export const nameBefore = (text: string, acronym: string): string | undefined => {
|
|
41
|
+
const words = wordsBefore(text).slice(-acronym.length * 2 - 2);
|
|
42
|
+
const letters = acronym.replaceAll("&", "");
|
|
43
|
+
const width = words.findIndex((_word, index) => initialsOf(words.slice(-(index + 1))) === letters);
|
|
44
|
+
if (width < 0) return undefined;
|
|
45
|
+
const name = words.slice(-(width + 1));
|
|
46
|
+
const start = name.findIndex((word) => !SMALL_WORDS.has(word.toLowerCase()));
|
|
47
|
+
return name
|
|
48
|
+
.slice(start)
|
|
49
|
+
.join(" ")
|
|
50
|
+
.replace(/[,;:]$/u, "");
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const NON_LATIN_NAME = /^[\p{Script=Han}\p{Script=Hiragana}\p{Script=Katakana}ー・\s\p{L}\p{N}-]+$/u;
|
|
54
|
+
const HAS_KANA_OR_KANJI = /[\p{Script=Han}\p{Script=Hiragana}\p{Script=Katakana}]/u;
|
|
55
|
+
/** 括弧の中の定義の語や文(以下「CI」という。、読点のある説明)は、名前ではない。 */
|
|
56
|
+
const NOT_A_NAME = /以下|という|[、。,;:]/u;
|
|
57
|
+
|
|
58
|
+
/** 略語のあとの括弧の中が、その略語の名前か。英字の名前は頭文字が揃うとき、日本語の名前は語だけのとき。 */
|
|
59
|
+
const isNameAfter = (name: string, acronym: string): boolean => {
|
|
60
|
+
if (NOT_A_NAME.test(name)) return false;
|
|
61
|
+
if (HAS_KANA_OR_KANJI.test(name)) return NON_LATIN_NAME.test(name);
|
|
62
|
+
return namesAcronym(name, acronym);
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
/** 文の中の略語の展開。両方の形を、文書の中の位置の順に。 */
|
|
66
|
+
export const expansionsIn = (body: ExpansionText): Expansion[] => {
|
|
67
|
+
const nameFirst = [...body.text.matchAll(NAME_FIRST)].flatMap((match) => {
|
|
68
|
+
const acronym = match.groups?.["acronym"] ?? "";
|
|
69
|
+
const name = nameBefore(body.text.slice(0, match.index), acronym);
|
|
70
|
+
return name === undefined ? [] : [{ acronym, name, offset: body.start + match.index }];
|
|
71
|
+
});
|
|
72
|
+
const acronymFirst = [...body.text.matchAll(ACRONYM_FIRST)].flatMap((match) => {
|
|
73
|
+
const acronym = match.groups?.["acronym"] ?? "";
|
|
74
|
+
const name = (match.groups?.["name"] ?? "").trim();
|
|
75
|
+
return isNameAfter(name, acronym) ? [{ acronym, name, offset: body.start + match.index }] : [];
|
|
76
|
+
});
|
|
77
|
+
return [...nameFirst, ...acronymFirst].toSorted((left, right) => left.offset - right.offset);
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
/** 比べる形。大文字小文字、ハイフンと空白、& と and、語の終わりの複数の s は名前を変えない。 */
|
|
81
|
+
export const comparableName = (name: string): string =>
|
|
82
|
+
name
|
|
83
|
+
.toLowerCase()
|
|
84
|
+
.replaceAll("&", " and ")
|
|
85
|
+
.replaceAll(/[-‐‑\s]+/gu, " ")
|
|
86
|
+
.trim()
|
|
87
|
+
.replaceAll(/(?<=\p{Script=Latin}{3})s\b/gu, "");
|
|
88
|
+
|
|
89
|
+
export type ExpansionConflict = { readonly expansion: Expansion; readonly first: Expansion };
|
|
90
|
+
|
|
91
|
+
/** 同じ略語の、最初の展開と違う展開。違う書き方ごとに最初の一つだけ。 */
|
|
92
|
+
export const expansionConflicts = (expansions: readonly Expansion[]): ExpansionConflict[] => {
|
|
93
|
+
const firsts = new Map<string, Expansion>();
|
|
94
|
+
const reported = new Set<string>();
|
|
95
|
+
return expansions.flatMap((expansion) => {
|
|
96
|
+
const first = firsts.get(expansion.acronym);
|
|
97
|
+
if (first === undefined) {
|
|
98
|
+
firsts.set(expansion.acronym, expansion);
|
|
99
|
+
return [];
|
|
100
|
+
}
|
|
101
|
+
const variant = `${expansion.acronym}\u0000${comparableName(expansion.name)}`;
|
|
102
|
+
if (comparableName(first.name) === comparableName(expansion.name) || reported.has(variant)) return [];
|
|
103
|
+
reported.add(variant);
|
|
104
|
+
return [{ expansion, first }];
|
|
105
|
+
});
|
|
106
|
+
};
|
package/src/announced-count.ts
CHANGED
|
@@ -86,17 +86,34 @@ const isHedged = (sentence: string, phrase: Phrase, words: CountWords): boolean
|
|
|
86
86
|
|
|
87
87
|
const COLON_END = /[::][ \t]*$/u;
|
|
88
88
|
|
|
89
|
-
/**
|
|
90
|
-
|
|
89
|
+
/**
|
|
90
|
+
* 数字か、語の切れ目で区切った英語の数の語(two)。英字や点に続く数字は版や名前の一部(v2、1.2)なので数えない。
|
|
91
|
+
* 漢数字は「一緒」「一般」の中にもあるので数えない。
|
|
92
|
+
*/
|
|
93
|
+
const numberPattern = (words: CountWords): RegExp => {
|
|
94
|
+
const latin = words.numbers.filter((number) => isLatin(number[0]));
|
|
95
|
+
const digits = String.raw`(?<![A-Za-z.])\p{Nd}`;
|
|
96
|
+
return new RegExp(latin.length === 0 ? digits : `${digits}|(?<![A-Za-z])(?:${alternation(latin)})(?![A-Za-z])`, "iu");
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
type Patterns = { readonly phrase: RegExp; readonly number: RegExp };
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* 数える語の無い数は、先を指す語のすぐ後ろでだけ(the following two:)。「under $75:」の金額を数に読まない。
|
|
103
|
+
* 先を指す語が無く、終わりのコロンだけが予告なら、コロンが渡すのはその手前のいちばん近い数。数の後ろにまだ数があれば
|
|
104
|
+
* (「1 つのページにまとめていましたが、2 ページに分けました:」)、その数は予告の数ではない。
|
|
105
|
+
*/
|
|
106
|
+
const pointsAhead = (sentence: string, phrase: Phrase, anchors: readonly string[], number: RegExp): boolean => {
|
|
91
107
|
const before = sentence.slice(0, phrase.start).toLowerCase();
|
|
92
108
|
const lowered = anchors.map((anchor) => anchor.toLowerCase());
|
|
93
109
|
if (!phrase.counted) return lowered.some((anchor) => endsWithWord(before.trimEnd(), anchor));
|
|
94
|
-
|
|
110
|
+
if (lowered.some((anchor) => before.includes(anchor))) return true;
|
|
111
|
+
return COLON_END.test(sentence.trimEnd()) && !number.test(sentence.slice(phrase.end));
|
|
95
112
|
};
|
|
96
113
|
|
|
97
114
|
/** 最後の一文の予告。数がちょうど一つで、目安や順番でなく、先を指しているときだけ。 */
|
|
98
|
-
const announcedIn = (sentence: string, words: CountWords,
|
|
99
|
-
const phrases = [...sentence.matchAll(
|
|
115
|
+
const announcedIn = (sentence: string, words: CountWords, patterns: Patterns): Phrase | undefined => {
|
|
116
|
+
const phrases = [...sentence.matchAll(patterns.phrase)].map((match) => ({
|
|
100
117
|
start: match.index,
|
|
101
118
|
numberEnd: match.index + (match[1] ?? "").length,
|
|
102
119
|
end: match.index + match[0].length,
|
|
@@ -105,7 +122,7 @@ const announcedIn = (sentence: string, words: CountWords, pattern: RegExp): Phra
|
|
|
105
122
|
}));
|
|
106
123
|
const only = phrases.length === 1 ? phrases[0] : undefined;
|
|
107
124
|
if (only === undefined || only.announced < 1 || isHedged(sentence, only, words)) return undefined;
|
|
108
|
-
return pointsAhead(sentence, only, words.anchors) ? only : undefined;
|
|
125
|
+
return pointsAhead(sentence, only, words.anchors, patterns.number) ? only : undefined;
|
|
109
126
|
};
|
|
110
127
|
|
|
111
128
|
/** 位置から空白と改行を読み飛ばした先。 */
|
|
@@ -127,22 +144,47 @@ const neighboursOf = (source: string, lists: readonly BulletList[]): Neighbours
|
|
|
127
144
|
const hasNeighbour = (source: string, list: Span, neighbours: Neighbours): boolean =>
|
|
128
145
|
neighbours.ends.has(list.start) || neighbours.starts.has(pastSpace(source, list.end));
|
|
129
146
|
|
|
130
|
-
|
|
147
|
+
/**
|
|
148
|
+
* 分類した箇条書き(「- **実運用**: CLI / Telegram」)の行は、ラベルの後ろに項目を区切って並べる。予告の数は区切った項目の数かもしれない。
|
|
149
|
+
* 区切りは斜線だけ。読点とコンマは項目の説明の文の中にも書くので(「UI: rename the button to Save, continue」)区切りに数えない。
|
|
150
|
+
* 行内のコードの中の斜線(`a/b`)も数えない。
|
|
151
|
+
*/
|
|
152
|
+
const CATEGORY_ITEM = /^[ \t]*(?:[-*+]|\d{1,9}[.)])[ \t]+[^::\n]{1,40}[::][ \t]*(?<members>\S[^\n]*)/u;
|
|
153
|
+
const MEMBER_SEPARATOR = /[//]/u;
|
|
154
|
+
const INLINE_CODE = /`[^`\n]*`/gu;
|
|
155
|
+
|
|
156
|
+
const membersOf = (item: string): number | undefined =>
|
|
157
|
+
CATEGORY_ITEM.exec(item)
|
|
158
|
+
?.groups?.["members"]?.replace(INLINE_CODE, "code")
|
|
159
|
+
.split(MEMBER_SEPARATOR)
|
|
160
|
+
.filter((member) => member.trim() !== "").length;
|
|
161
|
+
|
|
162
|
+
/** どの行も分類の形なら、区切って並べた項目の数の和。一行でも違えば undefined。 */
|
|
163
|
+
const categorisedCount = (source: string, list: BulletList): number | undefined => {
|
|
164
|
+
const counts = list.itemSpans.map((item) => membersOf(source.slice(item.start, item.end)));
|
|
165
|
+
return counts.every((count) => count !== undefined) ? counts.reduce<number>((sum, count) => sum + (count ?? 0), 0) : undefined;
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
/** 予告の数が、行の数か、分類した行に並べた項目の数に合う。 */
|
|
169
|
+
const matchesList = (announced: number, source: string, list: BulletList): boolean =>
|
|
170
|
+
announced === list.items.length || announced === categorisedCount(source, list);
|
|
171
|
+
|
|
172
|
+
type Context = { readonly source: string; readonly words: CountWords; readonly patterns: Patterns; readonly neighbours: Neighbours };
|
|
131
173
|
|
|
132
|
-
const mismatchOf = ({ source, words,
|
|
174
|
+
const mismatchOf = ({ source, words, patterns, neighbours }: Context, list: BulletList): CountMismatch[] => {
|
|
133
175
|
if (hasNeighbour(source, list.span, neighbours)) return [];
|
|
134
176
|
const lead = leadBefore(source, list.span.start);
|
|
135
177
|
if (lead === undefined) return [];
|
|
136
178
|
const sentenceStart = lastSentenceStart(lead.text);
|
|
137
179
|
const sentence = lead.text.slice(sentenceStart);
|
|
138
|
-
const phrase = announcedIn(sentence, words,
|
|
139
|
-
if (phrase === undefined || phrase.announced
|
|
180
|
+
const phrase = announcedIn(sentence, words, patterns);
|
|
181
|
+
if (phrase === undefined || matchesList(phrase.announced, source, list)) return [];
|
|
140
182
|
const offset = lead.start + sentenceStart + phrase.start;
|
|
141
183
|
return [{ offset, phrase: sentence.slice(phrase.start, phrase.end).trim(), announced: phrase.announced, listed: list.items.length }];
|
|
142
184
|
};
|
|
143
185
|
|
|
144
186
|
export const announcedCountMismatches = (source: string, lists: readonly BulletList[], words: CountWords): CountMismatch[] => {
|
|
145
187
|
if (words.counters.length === 0) return [];
|
|
146
|
-
const context = { source, words,
|
|
188
|
+
const context = { source, words, patterns: { phrase: phrasePattern(words), number: numberPattern(words) }, neighbours: neighboursOf(source, lists) };
|
|
147
189
|
return lists.flatMap((list) => mismatchOf(context, list));
|
|
148
190
|
};
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import type { Lexicon, LexiconEntry, Sentence, Token } from "./plugin.ts";
|
|
2
|
+
import { entryRanges } from "./detectors/lexicon-match.ts";
|
|
3
|
+
|
|
4
|
+
// 一覧を言う語(supported on、に対応)の隣に並べた名前を読み、後の文が、最初に挙げた一覧に無い名前を同じ語で言っていないかを見る。
|
|
5
|
+
// 「対応 OS は Windows と macOS」と書いたあとの「Linux にも対応」。どちらが正しいかは決めず、一覧の外の名前を指す。
|
|
6
|
+
|
|
7
|
+
export type ListMember = { readonly name: string; readonly offset: number };
|
|
8
|
+
|
|
9
|
+
export type ListStatement = { readonly group: string; readonly offset: number; readonly members: readonly ListMember[] };
|
|
10
|
+
|
|
11
|
+
export type OutsideMember = { readonly member: ListMember; readonly announced: ListStatement };
|
|
12
|
+
|
|
13
|
+
const UPPER = /\p{Lu}/u;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* 名前の語。固有名詞のほか、大文字を含む名詞(macOS、Linux)。日本語の解析器は英字の名前を名詞とも固有名詞とも読むので、
|
|
17
|
+
* 品詞だけでは同じ一覧の名前が揃わない。
|
|
18
|
+
*/
|
|
19
|
+
const isNameToken = (token: Token): boolean => token.pos === "PROPN" || (token.pos === "NOUN" && UPPER.test(token.surface));
|
|
20
|
+
|
|
21
|
+
const isJoiner = (token: Token, joiners: ReadonlySet<string>): boolean => joiners.has(token.surface.toLowerCase());
|
|
22
|
+
|
|
23
|
+
/** 名前の語の並び(前へ読んだときは逆順)から、source の上の名前。 */
|
|
24
|
+
const memberOf = (run: readonly Token[], source: string): ListMember | undefined => {
|
|
25
|
+
if (run.length === 0) return undefined;
|
|
26
|
+
const start = Math.min(...run.map((token) => token.span.start));
|
|
27
|
+
const end = Math.max(...run.map((token) => token.span.end));
|
|
28
|
+
return { name: source.slice(start, end).replaceAll(/\s+/gu, " "), offset: start };
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* 並んだ名前を、語の並びの順に読む。固有名詞が続けば一つの名前、つなぐ語(and、と、、)は名前の区切り。ほかの語に当たったら止まる。
|
|
33
|
+
* 名前で始まらなければ一覧ではない(supported on the web)。
|
|
34
|
+
*/
|
|
35
|
+
export const namesInOrder = (tokens: readonly Token[], joiners: ReadonlySet<string>, source: string): ListMember[] => {
|
|
36
|
+
const runs: Token[][] = [[]];
|
|
37
|
+
tokens.every((token) => {
|
|
38
|
+
const current = runs.at(-1) ?? [];
|
|
39
|
+
if (isNameToken(token)) current.push(token);
|
|
40
|
+
else if (isJoiner(token, joiners) && current.length > 0) runs.push([]);
|
|
41
|
+
else return false;
|
|
42
|
+
return true;
|
|
43
|
+
});
|
|
44
|
+
return runs.flatMap((run) => memberOf(run, source) ?? []);
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
/** 一覧の語の、一つの現れから読んだ名前。before の語は後ろの名前を、after の語は前の名前を読む。 */
|
|
48
|
+
const membersAt = (
|
|
49
|
+
tokens: readonly Token[],
|
|
50
|
+
entry: LexiconEntry,
|
|
51
|
+
range: { start: number; end: number },
|
|
52
|
+
joiners: ReadonlySet<string>,
|
|
53
|
+
source: string,
|
|
54
|
+
): ListMember[] =>
|
|
55
|
+
entry.position === "after"
|
|
56
|
+
? namesInOrder(tokens.slice(0, range.start).toReversed(), joiners, source).toReversed()
|
|
57
|
+
: namesInOrder(tokens.slice(range.end), joiners, source);
|
|
58
|
+
|
|
59
|
+
/** 一覧を読む言葉。frames は一覧を言う語、joiners は名前をつなぐ語、negations は打ち消しの語(小文字)。 */
|
|
60
|
+
export type ListWords = { readonly frames: Lexicon; readonly joiners: ReadonlySet<string>; readonly negations: ReadonlySet<string> };
|
|
61
|
+
|
|
62
|
+
/** 打ち消した文(neither is available on Docker Hub、Linux には対応していません)は、一覧に足す名前ではなく、外す名前を言う。 */
|
|
63
|
+
const isNegated = (tokens: readonly Token[], negations: ReadonlySet<string>): boolean =>
|
|
64
|
+
tokens.some((token) => negations.has(token.surface.toLowerCase()) || negations.has((token.lemma ?? "").toLowerCase()));
|
|
65
|
+
|
|
66
|
+
/** 文の中の、一覧を言う語と、その隣の名前。group の無い語は比べる相手が決まらないので読まない。打ち消した文も読まない。 */
|
|
67
|
+
export const listStatementsIn = (sentence: Sentence, words: ListWords, source: string): ListStatement[] => {
|
|
68
|
+
const tokens = sentence.tokens ?? [];
|
|
69
|
+
const { frames, joiners } = words;
|
|
70
|
+
if (isNegated(tokens, words.negations)) return [];
|
|
71
|
+
return frames.flatMap((entry) => {
|
|
72
|
+
const group = entry.group;
|
|
73
|
+
if (group === undefined) return [];
|
|
74
|
+
return entryRanges(sentence, entry).flatMap((range) => {
|
|
75
|
+
const members = membersAt(tokens, entry, range, joiners, source);
|
|
76
|
+
const offset = tokens[range.start]?.span.start ?? sentence.span.start;
|
|
77
|
+
return members.length === 0 ? [] : [{ group, offset, members }];
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
/** 比べる形。大文字小文字・幅・空白は名前を変えない。 */
|
|
83
|
+
const comparable = (name: string): string => name.normalize("NFKC").toLowerCase().replaceAll(/\s+/gu, "");
|
|
84
|
+
|
|
85
|
+
/** 二つ以上の名前を挙げた最初の文。組ごとに一つ。 */
|
|
86
|
+
const announcements = (statements: readonly ListStatement[]): ReadonlyMap<string, ListStatement> =>
|
|
87
|
+
statements.reduce((found, statement) => {
|
|
88
|
+
if (statement.members.length >= 2 && !found.has(statement.group)) found.set(statement.group, statement);
|
|
89
|
+
return found;
|
|
90
|
+
}, new Map<string, ListStatement>());
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* 後の文が、一覧と一つも重ならない二つ以上の名前を挙げていれば、別のものの一覧(RIS は EndNote と RefWorks、BibTeX は BibDesk と LaTeX)。
|
|
94
|
+
* そこから先は、一つだけ名前を言う文がどちらの一覧の続きか決まらないので、その組は比べない。
|
|
95
|
+
*/
|
|
96
|
+
const isAnotherList = (statement: ListStatement, known: ReadonlySet<string>): boolean =>
|
|
97
|
+
statement.members.length >= 2 && !statement.members.some((member) => known.has(comparable(member.name)));
|
|
98
|
+
|
|
99
|
+
/** 最初に挙げた一覧より後の文で、同じ組の語が言う、一覧に無い名前。名前ごとに最初の一つ。 */
|
|
100
|
+
export const outsideMembers = (statements: readonly ListStatement[]): OutsideMember[] => {
|
|
101
|
+
const ordered = statements.toSorted((left, right) => left.offset - right.offset);
|
|
102
|
+
const announced = announcements(ordered);
|
|
103
|
+
const reported = new Set<string>();
|
|
104
|
+
const ambiguous = new Set<string>();
|
|
105
|
+
return ordered.flatMap((statement) => {
|
|
106
|
+
const list = announced.get(statement.group);
|
|
107
|
+
if (list === undefined || statement.offset <= list.offset || ambiguous.has(statement.group)) return [];
|
|
108
|
+
const known = new Set(list.members.map((member) => comparable(member.name)));
|
|
109
|
+
if (isAnotherList(statement, known)) {
|
|
110
|
+
ambiguous.add(statement.group);
|
|
111
|
+
return [];
|
|
112
|
+
}
|
|
113
|
+
return statement.members.flatMap((member) => {
|
|
114
|
+
const key = `${statement.group}\u0000${comparable(member.name)}`;
|
|
115
|
+
if (known.has(comparable(member.name)) || reported.has(key)) return [];
|
|
116
|
+
reported.add(key);
|
|
117
|
+
return [{ member, announced: list }];
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
};
|
package/src/api.ts
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
// The plugin API: everything a team's rule or a plugin package may rely on, and nothing else. A detector sees the
|
|
2
|
+
// document only through these shapes, so chaff can change how it reads a document without breaking a plugin. Raise
|
|
3
|
+
// API_VERSION when a change here would break a plugin written against the current one.
|
|
4
|
+
|
|
5
|
+
/** The plugin API this chaff implements. A plugin says which one it was written for; chaff refuses any other. */
|
|
6
|
+
export const API_VERSION = 1;
|
|
7
|
+
|
|
8
|
+
/** A place in doc.source: UTF-16 offsets, end exclusive. */
|
|
9
|
+
export type Span = { readonly start: number; readonly end: number };
|
|
10
|
+
|
|
11
|
+
/** One word as the language adapter read it. pos is a Universal Dependencies UPOS tag (NOUN, VERB, ADP…). */
|
|
12
|
+
export type Token = {
|
|
13
|
+
readonly span: Span;
|
|
14
|
+
readonly surface: string;
|
|
15
|
+
readonly pos: string;
|
|
16
|
+
readonly lemma?: string;
|
|
17
|
+
/** How the word is read (katakana for Japanese). Only adapters that can read it give it. */
|
|
18
|
+
readonly reading?: string;
|
|
19
|
+
/** Universal Dependencies FEATS the adapter folded in (Voice=Pass). */
|
|
20
|
+
readonly features?: Readonly<Record<string, string>>;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
/** tokens is there only when the rule asked for parts of speech (requires: [pos]) and the adapter could give them. */
|
|
24
|
+
export type Sentence = { readonly span: Span; readonly text: string; readonly tokens?: readonly Token[] };
|
|
25
|
+
|
|
26
|
+
export type Paragraph = { readonly span: Span; readonly sentences: readonly Sentence[] };
|
|
27
|
+
|
|
28
|
+
/** A heading and what follows it, up to the next heading. The text before the first heading is a section of depth 0. */
|
|
29
|
+
export type Section = { readonly depth: number; readonly heading: string; readonly span: Span; readonly sentences: readonly Sentence[] };
|
|
30
|
+
|
|
31
|
+
/** One bulleted or numbered list, with the length of each item in characters. */
|
|
32
|
+
export type List = { readonly span: Span; readonly itemLengths: readonly number[] };
|
|
33
|
+
|
|
34
|
+
/** One entry of a word list. instead_of is another way to write the same thing, for rules that check a document is consistent. */
|
|
35
|
+
export type LexiconEntry = { readonly pattern: string; readonly instead_of?: string };
|
|
36
|
+
|
|
37
|
+
export type Lexicon = readonly LexiconEntry[];
|
|
38
|
+
|
|
39
|
+
export type MarkupHeading = { readonly depth: number; readonly text: string; readonly span: Span };
|
|
40
|
+
|
|
41
|
+
/** alt is undefined for an HTML <img> with no alt attribute. */
|
|
42
|
+
export type MarkupImage = { readonly alt: string | undefined; readonly span: Span };
|
|
43
|
+
|
|
44
|
+
export type MarkupLink = { readonly destination: string; readonly span: Span };
|
|
45
|
+
|
|
46
|
+
/** The document's markup. A plain-text document has markdown: false and no headings, images or links. */
|
|
47
|
+
export type Markup = {
|
|
48
|
+
readonly markdown: boolean;
|
|
49
|
+
readonly headings: readonly MarkupHeading[];
|
|
50
|
+
readonly images: readonly MarkupImage[];
|
|
51
|
+
readonly links: readonly MarkupLink[];
|
|
52
|
+
/** The in-page names the writer gave ({#id} on a heading, HTML id and name), sorted. */
|
|
53
|
+
readonly ids: readonly string[];
|
|
54
|
+
/** What the reader sees as text: everything outside links. */
|
|
55
|
+
readonly texts: readonly Span[];
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
/** What a detector reads. Every span is a place in source. It is frozen: a detector cannot change what other rules see. */
|
|
59
|
+
export type RuleDocument = {
|
|
60
|
+
readonly path: string;
|
|
61
|
+
readonly source: string;
|
|
62
|
+
/** The language the document is checked as: "ja", "en". */
|
|
63
|
+
readonly language: string;
|
|
64
|
+
/** What a length counts: characters (ja) or words (en). */
|
|
65
|
+
readonly lengthUnit: "char" | "word";
|
|
66
|
+
readonly sentences: readonly Sentence[];
|
|
67
|
+
readonly paragraphs: readonly Paragraph[];
|
|
68
|
+
readonly sections: readonly Section[];
|
|
69
|
+
readonly lists: readonly List[];
|
|
70
|
+
/** Each list item, wherever it is. */
|
|
71
|
+
readonly listItems: readonly Span[];
|
|
72
|
+
/** Each link, as written (`[text](url)`, `<https://…>`). */
|
|
73
|
+
readonly links: readonly Span[];
|
|
74
|
+
/** The word lists for the document's language: the adapter's, the team's and the plugins'. */
|
|
75
|
+
readonly lexicons: Readonly<Record<string, Lexicon>>;
|
|
76
|
+
readonly markup: Markup;
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
/** What a detector is given besides the document. lexicon is the rule's word_list for the document's language. */
|
|
80
|
+
export type DetectorOptions = { readonly lexicon: Lexicon | undefined };
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* One place a rule found. start (and end) are offsets in doc.source. values fill the rule's message: {matched} is
|
|
84
|
+
* source from start to end unless values gives it.
|
|
85
|
+
*/
|
|
86
|
+
export type Finding = {
|
|
87
|
+
readonly start: number;
|
|
88
|
+
readonly end?: number;
|
|
89
|
+
readonly values?: Readonly<Record<string, string | number>>;
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
/** A rule's check. It must be pure and deterministic: no files, network, clock or randomness, the same findings every time. */
|
|
93
|
+
export type Detector = (doc: RuleDocument, options: DetectorOptions) => readonly Finding[];
|
|
94
|
+
|
|
95
|
+
/** Words a reader sees: one string for every language, or one per language ({ ja, en }). */
|
|
96
|
+
export type Text = string | Readonly<Record<string, string>>;
|
|
97
|
+
|
|
98
|
+
export type Severity = "error" | "warning" | "info";
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* A rule. In a type: module file only detect is read, and chaff.yaml gives the rest. In a plugin, id, name, why,
|
|
102
|
+
* how_to_fix and example are required, as they are for a rule in custom_rules.
|
|
103
|
+
*/
|
|
104
|
+
export type RuleSpec = {
|
|
105
|
+
readonly detect: Detector;
|
|
106
|
+
readonly id?: string;
|
|
107
|
+
readonly name?: Text;
|
|
108
|
+
readonly why?: Text;
|
|
109
|
+
readonly how_to_fix?: Text;
|
|
110
|
+
readonly example?: { readonly before: Text; readonly after: Text };
|
|
111
|
+
readonly message?: Text;
|
|
112
|
+
readonly level?: Severity;
|
|
113
|
+
/** The languages the rule checks. Without it, every language. */
|
|
114
|
+
readonly languages?: readonly string[];
|
|
115
|
+
/** A word list by name; the detector gets it as options.lexicon. A plugin's own lists are named without the plugin's prefix. */
|
|
116
|
+
readonly word_list?: string;
|
|
117
|
+
/** "pos" when the detector reads tokens. Without it, sentences have no tokens. */
|
|
118
|
+
readonly requires?: readonly "pos"[];
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
export type Rule = RuleSpec & { readonly apiVersion: number };
|
|
122
|
+
|
|
123
|
+
/** A rule, stamped with the API version it was written for. */
|
|
124
|
+
export const defineRule = (spec: RuleSpec): Rule => ({ ...spec, apiVersion: API_VERSION });
|
|
125
|
+
|
|
126
|
+
/** A word list in each language it has words for: { ja: [...], en: [...] }. A string is an entry with only a pattern. */
|
|
127
|
+
export type LexiconSpec = Readonly<Record<string, readonly (string | LexiconEntry)[]>>;
|
|
128
|
+
|
|
129
|
+
/** A house style a plugin ships: rule levels and options, and the guideline it follows. */
|
|
130
|
+
export type StyleSpec = {
|
|
131
|
+
readonly id: string;
|
|
132
|
+
readonly name: Text;
|
|
133
|
+
readonly summary: Text;
|
|
134
|
+
readonly source: { readonly title: Text; readonly url: string };
|
|
135
|
+
readonly rules?: Readonly<Record<string, "strict" | "normal" | "relaxed" | "off">>;
|
|
136
|
+
readonly options?: Readonly<Record<string, Readonly<Record<string, unknown>>>>;
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* A plugin package. name is the prefix of every id it ships (name/rule-id); for chaff-plugin-foo it is foo, for
|
|
141
|
+
* @scope/chaff-plugin-foo it is @scope/foo. A rule is a RuleSpec, or a custom_rules entry without code (type: words,
|
|
142
|
+
* pattern or tokens).
|
|
143
|
+
*/
|
|
144
|
+
export type PluginSpec = {
|
|
145
|
+
readonly name: string;
|
|
146
|
+
readonly rules?: readonly (Rule | RuleSpec | Readonly<Record<string, unknown>>)[];
|
|
147
|
+
readonly lexicons?: Readonly<Record<string, LexiconSpec>>;
|
|
148
|
+
readonly styles?: readonly StyleSpec[];
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
export type Plugin = PluginSpec & { readonly apiVersion: number };
|
|
152
|
+
|
|
153
|
+
/** A plugin, stamped with the API version it was written for. */
|
|
154
|
+
export const definePlugin = (spec: PluginSpec): Plugin => ({ ...spec, apiVersion: API_VERSION });
|