chaffjs 0.20.0 → 0.21.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 +5 -4
- package/dist/api.d.ts +16 -1
- package/dist/api.d.ts.map +1 -1
- package/dist/api.js.map +1 -1
- package/dist/cli-text.d.ts +2 -0
- package/dist/cli-text.d.ts.map +1 -1
- package/dist/cli-text.js +14 -5
- package/dist/cli-text.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +4 -0
- package/dist/cli.js.map +1 -1
- package/dist/command-help.d.ts +7 -0
- package/dist/command-help.d.ts.map +1 -0
- package/dist/command-help.js +18 -0
- package/dist/command-help.js.map +1 -0
- package/dist/commands/eval.js +2 -2
- package/dist/commands/eval.js.map +1 -1
- package/dist/commands/fix-plan.d.ts.map +1 -1
- package/dist/commands/fix-plan.js +11 -1
- package/dist/commands/fix-plan.js.map +1 -1
- package/dist/commands/help.d.ts +4 -0
- package/dist/commands/help.d.ts.map +1 -0
- package/dist/commands/help.js +7 -0
- package/dist/commands/help.js.map +1 -0
- package/dist/config/load.d.ts +2 -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.js +2 -2
- package/dist/config/option-problems.js.map +1 -1
- package/dist/config/rule-problems.js +2 -2
- package/dist/config/rule-problems.js.map +1 -1
- package/dist/custom/checked.d.ts +9 -0
- package/dist/custom/checked.d.ts.map +1 -0
- package/dist/custom/checked.js +3 -0
- package/dist/custom/checked.js.map +1 -0
- package/dist/custom/dsl-fields.d.ts +27 -0
- package/dist/custom/dsl-fields.d.ts.map +1 -0
- package/dist/custom/dsl-fields.js +86 -0
- package/dist/custom/dsl-fields.js.map +1 -0
- package/dist/custom/load.d.ts.map +1 -1
- package/dist/custom/load.js +2 -0
- package/dist/custom/load.js.map +1 -1
- package/dist/custom/parse.d.ts +14 -3
- package/dist/custom/parse.d.ts.map +1 -1
- package/dist/custom/parse.js +44 -40
- package/dist/custom/parse.js.map +1 -1
- package/dist/custom/problems.d.ts.map +1 -1
- package/dist/custom/problems.js +11 -1
- package/dist/custom/problems.js.map +1 -1
- package/dist/detectors/blank-structure.d.ts.map +1 -1
- package/dist/detectors/blank-structure.js +6 -4
- package/dist/detectors/blank-structure.js.map +1 -1
- package/dist/detectors/clause-shape.d.ts +22 -0
- package/dist/detectors/clause-shape.d.ts.map +1 -0
- package/dist/detectors/clause-shape.js +149 -0
- package/dist/detectors/clause-shape.js.map +1 -0
- package/dist/detectors/code-fences.d.ts +34 -0
- package/dist/detectors/code-fences.d.ts.map +1 -0
- package/dist/detectors/code-fences.js +100 -0
- package/dist/detectors/code-fences.js.map +1 -0
- package/dist/detectors/cross-definitions.d.ts +4 -0
- package/dist/detectors/cross-definitions.d.ts.map +1 -0
- package/dist/detectors/cross-definitions.js +43 -0
- package/dist/detectors/cross-definitions.js.map +1 -0
- package/dist/detectors/cross-facts.d.ts +4 -0
- package/dist/detectors/cross-facts.d.ts.map +1 -0
- package/dist/detectors/cross-facts.js +53 -0
- package/dist/detectors/cross-facts.js.map +1 -0
- package/dist/detectors/cross-registry/cross-doc-duplicate-definition.d.ts +3 -0
- package/dist/detectors/cross-registry/cross-doc-duplicate-definition.d.ts.map +1 -0
- package/dist/detectors/cross-registry/cross-doc-duplicate-definition.js +3 -0
- package/dist/detectors/cross-registry/cross-doc-duplicate-definition.js.map +1 -0
- package/dist/detectors/cross-registry/cross-doc-fact-conflict.d.ts +3 -0
- package/dist/detectors/cross-registry/cross-doc-fact-conflict.d.ts.map +1 -0
- package/dist/detectors/cross-registry/cross-doc-fact-conflict.js +3 -0
- package/dist/detectors/cross-registry/cross-doc-fact-conflict.js.map +1 -0
- package/dist/detectors/custom.d.ts.map +1 -1
- package/dist/detectors/custom.js +4 -1
- package/dist/detectors/custom.js.map +1 -1
- package/dist/detectors/defined-term-form.d.ts +25 -0
- package/dist/detectors/defined-term-form.d.ts.map +1 -0
- package/dist/detectors/defined-term-form.js +170 -0
- package/dist/detectors/defined-term-form.js.map +1 -0
- package/dist/detectors/email-letter.d.ts +46 -0
- package/dist/detectors/email-letter.d.ts.map +1 -0
- package/dist/detectors/email-letter.js +234 -0
- package/dist/detectors/email-letter.js.map +1 -0
- package/dist/detectors/impossible-date.d.ts +20 -0
- package/dist/detectors/impossible-date.d.ts.map +1 -0
- package/dist/detectors/impossible-date.js +105 -0
- package/dist/detectors/impossible-date.js.map +1 -0
- package/dist/detectors/impossible-time.d.ts +18 -0
- package/dist/detectors/impossible-time.d.ts.map +1 -0
- package/dist/detectors/impossible-time.js +116 -0
- package/dist/detectors/impossible-time.js.map +1 -0
- package/dist/detectors/link-shape.d.ts +11 -0
- package/dist/detectors/link-shape.d.ts.map +1 -0
- package/dist/detectors/link-shape.js +56 -0
- package/dist/detectors/link-shape.js.map +1 -0
- package/dist/detectors/mark-shape.d.ts +53 -0
- package/dist/detectors/mark-shape.d.ts.map +1 -0
- package/dist/detectors/mark-shape.js +160 -0
- package/dist/detectors/mark-shape.js.map +1 -0
- package/dist/detectors/registry/adverb-polarity.d.ts +3 -0
- package/dist/detectors/registry/adverb-polarity.d.ts.map +1 -0
- package/dist/detectors/registry/adverb-polarity.js +3 -0
- package/dist/detectors/registry/adverb-polarity.js.map +1 -0
- package/dist/detectors/registry/attachment-missing.d.ts +3 -0
- package/dist/detectors/registry/attachment-missing.d.ts.map +1 -0
- package/dist/detectors/registry/attachment-missing.js +3 -0
- package/dist/detectors/registry/attachment-missing.js.map +1 -0
- package/dist/detectors/registry/bare-url-mix.d.ts +3 -0
- package/dist/detectors/registry/bare-url-mix.d.ts.map +1 -0
- package/dist/detectors/registry/bare-url-mix.js +3 -0
- package/dist/detectors/registry/bare-url-mix.js.map +1 -0
- package/dist/detectors/registry/caps-shouting.d.ts +3 -0
- package/dist/detectors/registry/caps-shouting.d.ts.map +1 -0
- package/dist/detectors/registry/caps-shouting.js +3 -0
- package/dist/detectors/registry/caps-shouting.js.map +1 -0
- package/dist/detectors/registry/code-fence-language.d.ts +3 -0
- package/dist/detectors/registry/code-fence-language.d.ts.map +1 -0
- package/dist/detectors/registry/code-fence-language.js +3 -0
- package/dist/detectors/registry/code-fence-language.js.map +1 -0
- package/dist/detectors/registry/defined-name-repeated.d.ts +3 -0
- package/dist/detectors/registry/defined-name-repeated.d.ts.map +1 -0
- package/dist/detectors/registry/defined-name-repeated.js +3 -0
- package/dist/detectors/registry/defined-name-repeated.js.map +1 -0
- package/dist/detectors/registry/defined-term-form.d.ts +3 -0
- package/dist/detectors/registry/defined-term-form.d.ts.map +1 -0
- package/dist/detectors/registry/defined-term-form.js +3 -0
- package/dist/detectors/registry/defined-term-form.js.map +1 -0
- package/dist/detectors/registry/email-frame.d.ts +3 -0
- package/dist/detectors/registry/email-frame.d.ts.map +1 -0
- package/dist/detectors/registry/email-frame.js +3 -0
- package/dist/detectors/registry/email-frame.js.map +1 -0
- package/dist/detectors/registry/email-subject.d.ts +3 -0
- package/dist/detectors/registry/email-subject.d.ts.map +1 -0
- package/dist/detectors/registry/email-subject.js +3 -0
- package/dist/detectors/registry/email-subject.js.map +1 -0
- package/dist/detectors/registry/ending-run.d.ts +3 -0
- package/dist/detectors/registry/ending-run.d.ts.map +1 -0
- package/dist/detectors/registry/ending-run.js +3 -0
- package/dist/detectors/registry/ending-run.js.map +1 -0
- package/dist/detectors/registry/heading-command.d.ts +3 -0
- package/dist/detectors/registry/heading-command.d.ts.map +1 -0
- package/dist/detectors/registry/heading-command.js +3 -0
- package/dist/detectors/registry/heading-command.js.map +1 -0
- package/dist/detectors/registry/hyphen-dash.d.ts +3 -0
- package/dist/detectors/registry/hyphen-dash.d.ts.map +1 -0
- package/dist/detectors/registry/hyphen-dash.js +3 -0
- package/dist/detectors/registry/hyphen-dash.js.map +1 -0
- package/dist/detectors/registry/image-file-name-alt.d.ts +3 -0
- package/dist/detectors/registry/image-file-name-alt.d.ts.map +1 -0
- package/dist/detectors/registry/image-file-name-alt.js +3 -0
- package/dist/detectors/registry/image-file-name-alt.js.map +1 -0
- package/dist/detectors/registry/impossible-date.d.ts +3 -0
- package/dist/detectors/registry/impossible-date.d.ts.map +1 -0
- package/dist/detectors/registry/impossible-date.js +3 -0
- package/dist/detectors/registry/impossible-date.js.map +1 -0
- package/dist/detectors/registry/impossible-time.d.ts +3 -0
- package/dist/detectors/registry/impossible-time.d.ts.map +1 -0
- package/dist/detectors/registry/impossible-time.js +3 -0
- package/dist/detectors/registry/impossible-time.js.map +1 -0
- package/dist/detectors/registry/latin-abbreviation.d.ts +3 -0
- package/dist/detectors/registry/latin-abbreviation.d.ts.map +1 -0
- package/dist/detectors/registry/latin-abbreviation.js +3 -0
- package/dist/detectors/registry/latin-abbreviation.js.map +1 -0
- package/dist/detectors/registry/link-text-url-mismatch.d.ts +3 -0
- package/dist/detectors/registry/link-text-url-mismatch.d.ts.map +1 -0
- package/dist/detectors/registry/link-text-url-mismatch.js +3 -0
- package/dist/detectors/registry/link-text-url-mismatch.js.map +1 -0
- package/dist/detectors/registry/mark-consistency.d.ts +3 -0
- package/dist/detectors/registry/mark-consistency.d.ts.map +1 -0
- package/dist/detectors/registry/mark-consistency.js +3 -0
- package/dist/detectors/registry/mark-consistency.js.map +1 -0
- package/dist/detectors/registry/origin-particle.d.ts +3 -0
- package/dist/detectors/registry/origin-particle.d.ts.map +1 -0
- package/dist/detectors/registry/origin-particle.js +3 -0
- package/dist/detectors/registry/origin-particle.js.map +1 -0
- package/dist/detectors/registry/request-without-deadline.d.ts +3 -0
- package/dist/detectors/registry/request-without-deadline.d.ts.map +1 -0
- package/dist/detectors/registry/request-without-deadline.js +3 -0
- package/dist/detectors/registry/request-without-deadline.js.map +1 -0
- package/dist/detectors/registry/sentence-spacing.d.ts +3 -0
- package/dist/detectors/registry/sentence-spacing.d.ts.map +1 -0
- package/dist/detectors/registry/sentence-spacing.js +3 -0
- package/dist/detectors/registry/sentence-spacing.js.map +1 -0
- package/dist/detectors/registry/shell-prompt.d.ts +3 -0
- package/dist/detectors/registry/shell-prompt.d.ts.map +1 -0
- package/dist/detectors/registry/shell-prompt.js +3 -0
- package/dist/detectors/registry/shell-prompt.js.map +1 -0
- package/dist/detectors/registry/step-statement.d.ts +3 -0
- package/dist/detectors/registry/step-statement.d.ts.map +1 -0
- package/dist/detectors/registry/step-statement.js +3 -0
- package/dist/detectors/registry/step-statement.js.map +1 -0
- package/dist/detectors/registry/unpaired-parallel.d.ts +3 -0
- package/dist/detectors/registry/unpaired-parallel.d.ts.map +1 -0
- package/dist/detectors/registry/unpaired-parallel.js +3 -0
- package/dist/detectors/registry/unpaired-parallel.js.map +1 -0
- package/dist/detectors/request-owner.d.ts +23 -0
- package/dist/detectors/request-owner.d.ts.map +1 -0
- package/dist/detectors/request-owner.js +110 -0
- package/dist/detectors/request-owner.js.map +1 -0
- package/dist/detectors/technical-docs.d.ts +32 -0
- package/dist/detectors/technical-docs.d.ts.map +1 -0
- package/dist/detectors/technical-docs.js +199 -0
- package/dist/detectors/technical-docs.js.map +1 -0
- package/dist/extension/pack-read.d.ts +21 -0
- package/dist/extension/pack-read.d.ts.map +1 -0
- package/dist/extension/pack-read.js +117 -0
- package/dist/extension/pack-read.js.map +1 -0
- package/dist/extension/plugin-load.d.ts.map +1 -1
- package/dist/extension/plugin-load.js +44 -10
- package/dist/extension/plugin-load.js.map +1 -1
- package/dist/extension/plugin-parse.d.ts.map +1 -1
- package/dist/extension/plugin-parse.js +10 -3
- package/dist/extension/plugin-parse.js.map +1 -1
- package/dist/facts/address-values.d.ts +5 -0
- package/dist/facts/address-values.d.ts.map +1 -0
- package/dist/facts/address-values.js +64 -0
- package/dist/facts/address-values.js.map +1 -0
- package/dist/facts/cross-facts.d.ts +20 -0
- package/dist/facts/cross-facts.d.ts.map +1 -0
- package/dist/facts/cross-facts.js +41 -0
- package/dist/facts/cross-facts.js.map +1 -0
- package/dist/fix-plan/chosen-depth.d.ts +10 -0
- package/dist/fix-plan/chosen-depth.d.ts.map +1 -0
- package/dist/fix-plan/chosen-depth.js +24 -0
- package/dist/fix-plan/chosen-depth.js.map +1 -0
- package/dist/fix-plan/mode.d.ts +13 -4
- package/dist/fix-plan/mode.d.ts.map +1 -1
- package/dist/fix-plan/mode.js +38 -7
- package/dist/fix-plan/mode.js.map +1 -1
- package/dist/fix-plan/plan.d.ts +20 -0
- package/dist/fix-plan/plan.d.ts.map +1 -1
- package/dist/fix-plan/plan.js +23 -5
- package/dist/fix-plan/plan.js.map +1 -1
- package/dist/fix-plan/render.d.ts.map +1 -1
- package/dist/fix-plan/render.js +24 -0
- package/dist/fix-plan/render.js.map +1 -1
- package/dist/fix-plan/text.d.ts +8 -0
- package/dist/fix-plan/text.d.ts.map +1 -1
- package/dist/fix-plan/text.js +36 -10
- package/dist/fix-plan/text.js.map +1 -1
- package/dist/genre-load.d.ts +1 -1
- package/dist/genre-load.d.ts.map +1 -1
- package/dist/genre-load.js +7 -4
- package/dist/genre-load.js.map +1 -1
- package/dist/genre-parse.d.ts +6 -0
- package/dist/genre-parse.d.ts.map +1 -1
- package/dist/genre-parse.js +18 -0
- package/dist/genre-parse.js.map +1 -1
- package/dist/init.js +10 -10
- package/dist/init.js.map +1 -1
- package/dist/known-genres.d.ts +8 -0
- package/dist/known-genres.d.ts.map +1 -0
- package/dist/known-genres.js +17 -0
- package/dist/known-genres.js.map +1 -0
- package/dist/link-shape.d.ts +20 -0
- package/dist/link-shape.d.ts.map +1 -0
- package/dist/link-shape.js +58 -0
- package/dist/link-shape.js.map +1 -0
- package/dist/reasons.d.ts.map +1 -1
- package/dist/reasons.js +15 -4
- package/dist/reasons.js.map +1 -1
- package/dist/render/explain.d.ts.map +1 -1
- package/dist/render/explain.js +11 -2
- package/dist/render/explain.js.map +1 -1
- package/dist/render/friendly.js +1 -1
- package/dist/render/friendly.js.map +1 -1
- package/dist/render/rules-json.d.ts.map +1 -1
- package/dist/render/rules-json.js +12 -3
- package/dist/render/rules-json.js.map +1 -1
- package/dist/render/rules-table.js +6 -6
- package/dist/render/rules-table.js.map +1 -1
- package/dist/render/semantic.js +2 -2
- package/dist/render/semantic.js.map +1 -1
- package/dist/render/suppressions.js +2 -2
- package/dist/render/suppressions.js.map +1 -1
- package/dist/rewrite-depth.d.ts +22 -0
- package/dist/rewrite-depth.d.ts.map +1 -0
- package/dist/rewrite-depth.js +42 -0
- package/dist/rewrite-depth.js.map +1 -0
- package/dist/rule-fields.d.ts +23 -0
- package/dist/rule-fields.d.ts.map +1 -0
- package/dist/rule-fields.js +64 -0
- package/dist/rule-fields.js.map +1 -0
- package/dist/rule-guide.d.ts +3 -0
- package/dist/rule-guide.d.ts.map +1 -1
- package/dist/rule-guide.js +6 -0
- package/dist/rule-guide.js.map +1 -1
- package/dist/rule-load.d.ts.map +1 -1
- package/dist/rule-load.js +13 -5
- package/dist/rule-load.js.map +1 -1
- package/dist/rule-offs.d.ts +14 -0
- package/dist/rule-offs.d.ts.map +1 -0
- package/dist/rule-offs.js +45 -0
- package/dist/rule-offs.js.map +1 -0
- package/dist/structure/build.d.ts.map +1 -1
- package/dist/structure/build.js +14 -1
- package/dist/structure/build.js.map +1 -1
- package/dist/structure/cross-definitions.d.ts +23 -0
- package/dist/structure/cross-definitions.d.ts.map +1 -0
- package/dist/structure/cross-definitions.js +34 -0
- package/dist/structure/cross-definitions.js.map +1 -0
- package/genres.yaml +14 -313
- package/package.json +3 -3
- package/rules/absolute-exception.yaml +3 -1
- package/rules/abstract-length.yaml +7 -1
- package/rules/acronym-expansion-conflict.yaml +2 -1
- package/rules/adverb-overuse.yaml +7 -1
- package/rules/adverb-without-negation.yaml +60 -0
- package/rules/adversative-ga-repeat.yaml +2 -1
- package/rules/agentless-passive.yaml +10 -1
- package/rules/agreement-slip.yaml +4 -1
- package/rules/ai-generated-composite.yaml +6 -1
- package/rules/ai-structure.yaml +8 -2
- package/rules/ai-tell.yaml +7 -1
- package/rules/all-caps-shouting.yaml +61 -0
- package/rules/announced-count-mismatch.yaml +3 -1
- package/rules/announcing-opener.yaml +7 -1
- package/rules/article-sound.yaml +6 -2
- package/rules/assistant-residue.yaml +4 -1
- package/rules/attachment-not-attached.yaml +112 -0
- package/rules/baito-keigo.yaml +64 -0
- package/rules/bare-url-mix.yaml +86 -0
- package/rules/bold-density.yaml +1 -0
- package/rules/bold-label-list.yaml +9 -1
- package/rules/bracket-nesting.yaml +2 -1
- package/rules/bracket-width-consistency.yaml +64 -0
- package/rules/broken-link.yaml +1 -1
- package/rules/chat-citation-residue.yaml +2 -1
- package/rules/citation-reference-mismatch.yaml +7 -1
- package/rules/citation-style-mix.yaml +7 -1
- package/rules/clause-chain.yaml +6 -1
- package/rules/closing-cliche.yaml +7 -0
- package/rules/code-fence-language-mix.yaml +176 -0
- package/rules/colloquial-opener.yaml +62 -0
- package/rules/colon-lead-in.yaml +7 -1
- package/rules/concrete-evidence-density.yaml +6 -0
- package/rules/contraction-consistency.yaml +4 -1
- package/rules/contrast-framing.yaml +7 -1
- package/rules/cross-doc-duplicate-definition.yaml +70 -0
- package/rules/cross-doc-fact-conflict.yaml +64 -0
- package/rules/cushion-phrase-density.yaml +8 -1
- package/rules/dangling-figure-reference.yaml +3 -1
- package/rules/dangling-reference.yaml +3 -1
- package/rules/date-format-consistency.yaml +3 -2
- package/rules/date-order.yaml +3 -1
- package/rules/date-range-reversed.yaml +3 -1
- package/rules/date-weekday-mismatch.yaml +3 -1
- package/rules/date-without-year.yaml +4 -3
- package/rules/defined-name-repeated.yaml +97 -0
- package/rules/defined-term-form.yaml +113 -0
- package/rules/demonstrative-opener-run.yaml +2 -1
- package/rules/double-keigo.yaml +6 -1
- package/rules/double-negative.yaml +5 -1
- package/rules/doubled-nado.yaml +2 -1
- package/rules/doubled-punctuation.yaml +1 -1
- package/rules/doubled-word.yaml +4 -1
- package/rules/duplicate-definition.yaml +3 -1
- package/rules/duplicate-heading.yaml +1 -1
- package/rules/duration-mismatch.yaml +2 -1
- package/rules/elapsed-years-mismatch.yaml +4 -1
- package/rules/email-greeting-closing.yaml +122 -0
- package/rules/email-subject-length.yaml +112 -0
- package/rules/emoji-density.yaml +3 -1
- package/rules/emoji-heading.yaml +7 -1
- package/rules/empty-intensifier.yaml +4 -0
- package/rules/empty-list-item.yaml +2 -1
- package/rules/empty-section.yaml +1 -1
- package/rules/empty-table-cell.yaml +2 -1
- package/rules/excessive-hedging.yaml +7 -1
- package/rules/exclamation-width-consistency.yaml +64 -0
- package/rules/expletive-construction.yaml +7 -1
- package/rules/fact-conflict.yaml +4 -1
- package/rules/figure-reference-order.yaml +7 -1
- package/rules/fullwidth-alnum-consistency.yaml +2 -2
- package/rules/hankaku-kana.yaml +1 -1
- package/rules/heading-command-missing.yaml +109 -0
- package/rules/heading-echo.yaml +6 -1
- package/rules/heading-level-skip.yaml +3 -3
- package/rules/heading-numbering-mix.yaml +4 -3
- package/rules/hiragana-fukushi.yaml +4 -1
- package/rules/homophone-slip.yaml +5 -1
- package/rules/humble-for-others.yaml +63 -0
- package/rules/hyphen-as-dash.yaml +59 -0
- package/rules/idiom-misuse.yaml +4 -1
- package/rules/image-file-name-alt.yaml +83 -0
- package/rules/impossible-date.yaml +79 -0
- package/rules/impossible-time.yaml +76 -0
- package/rules/internal-jargon.yaml +1 -1
- package/rules/invisible-character.yaml +1 -1
- package/rules/katakana-density.yaml +2 -1
- package/rules/katakana-long-vowel.yaml +1 -1
- package/rules/known-misspelling.yaml +2 -1
- package/rules/kutoten-consistency.yaml +1 -1
- package/rules/latin-abbreviation-form.yaml +58 -0
- package/rules/latin-spacing.yaml +6 -2
- package/rules/link-text-url-mismatch.yaml +72 -0
- package/rules/list-item-form-mix.yaml +7 -1
- package/rules/max-kanji-continuous.yaml +10 -2
- package/rules/max-paragraph-length.yaml +4 -1
- package/rules/max-sentence-length.yaml +29 -21
- package/rules/max-ten.yaml +4 -1
- package/rules/misconversion.yaml +2 -1
- package/rules/modal-conflict.yaml +2 -1
- package/rules/name-variant.yaml +4 -3
- package/rules/ngram-repetition.yaml +5 -0
- package/rules/no-doubled-joshi.yaml +5 -1
- package/rules/no-em-dash.yaml +4 -1
- package/rules/no-lead.yaml +6 -1
- package/rules/no-mixed-desumasu.yaml +5 -2
- package/rules/no-nakaguro-parallel.yaml +3 -0
- package/rules/nominalization.yaml +5 -1
- package/rules/number-style-consistency.yaml +6 -1
- package/rules/number-without-unit.yaml +2 -1
- package/rules/numbering-gap.yaml +1 -1
- package/rules/one-sentence-paragraph-run.yaml +5 -1
- package/rules/orthographic-variant.yaml +4 -1
- package/rules/outside-announced-list.yaml +2 -1
- package/rules/oxford-comma-consistency.yaml +3 -1
- package/rules/padded-intro.yaml +7 -0
- package/rules/paragraph-length-variance.yaml +7 -1
- package/rules/paragraph-restatement.yaml +6 -1
- package/rules/percent-sum-mismatch.yaml +3 -1
- package/rules/person-consistency.yaml +4 -1
- package/rules/preamble-length.yaml +6 -0
- package/rules/preferred-term.yaml +1 -1
- package/rules/proper-noun-density.yaml +8 -1
- package/rules/quote-style-consistency.yaml +66 -0
- package/rules/ra-nuki.yaml +4 -1
- package/rules/redundant-expression.yaml +4 -1
- package/rules/reference-title-mismatch.yaml +4 -3
- package/rules/reference-topic-missing.yaml +2 -1
- package/rules/relative-date-mismatch.yaml +2 -1
- package/rules/repeated-conjunction.yaml +4 -1
- package/rules/repeated-sentence-ending.yaml +62 -0
- package/rules/repeated-sentence-head.yaml +4 -0
- package/rules/request-without-deadline.yaml +92 -0
- package/rules/required-sections.yaml +1 -1
- package/rules/requirement-modal.yaml +1 -1
- package/rules/requirement-smell.yaml +12 -4
- package/rules/rule-of-three.yaml +8 -1
- package/rules/sasete-itadaku.yaml +7 -1
- package/rules/section-length-uniformity.yaml +7 -1
- package/rules/sentence-initial-conjunction-run.yaml +5 -1
- package/rules/sentence-initial-so.yaml +6 -1
- package/rules/sentence-rhythm.yaml +8 -1
- package/rules/sentence-spacing-consistency.yaml +63 -0
- package/rules/shell-prompt-mix.yaml +128 -0
- package/rules/space-before-punctuation.yaml +1 -1
- package/rules/spelling-consistency.yaml +3 -1
- package/rules/step-statement-mix.yaml +76 -0
- package/rules/stock-transition.yaml +8 -1
- package/rules/stray-space.yaml +2 -2
- package/rules/summary-fact-mismatch.yaml +4 -1
- package/rules/table-header-variant.yaml +2 -1
- package/rules/taigen-dome-in-prose.yaml +6 -0
- package/rules/tari-unpaired.yaml +58 -0
- package/rules/teki-overuse.yaml +2 -1
- package/rules/title-case-consistency.yaml +1 -1
- package/rules/title-length.yaml +7 -1
- package/rules/topic-predicate-distance.yaml +6 -1
- package/rules/total-mismatch.yaml +3 -1
- package/rules/translationese-density.yaml +4 -1
- package/rules/unbalanced-bracket.yaml +3 -3
- package/rules/undefined-acronym.yaml +8 -1
- package/rules/unfilled-placeholder.yaml +1 -1
- package/rules/unit-mismatch.yaml +2 -1
- package/rules/unknown-word.yaml +4 -1
- package/rules/unqualified-superlative.yaml +6 -1
- package/rules/unused-definition.yaml +4 -3
- package/rules/url-run-on.yaml +1 -1
- package/rules/use-before-definition.yaml +4 -3
- package/rules/vague-figure-reference.yaml +3 -1
- package/rules/vague-link-text.yaml +2 -1
- package/rules/vague-word-density.yaml +2 -1
- package/rules/very-adjective.yaml +5 -1
- package/rules/weasel-word.yaml +6 -1
- package/rules/wordy-phrase.yaml +6 -1
- package/rules/yori-as-from.yaml +61 -0
- package/src/api.ts +13 -1
- package/src/cli-text.ts +18 -7
- package/src/cli.ts +3 -0
- package/src/command-help.ts +26 -0
- package/src/commands/eval.ts +2 -2
- package/src/commands/fix-plan.ts +13 -1
- package/src/commands/help.ts +8 -0
- package/src/config/load.ts +3 -0
- package/src/config/option-problems.ts +2 -2
- package/src/config/rule-problems.ts +2 -2
- package/src/custom/checked.ts +8 -0
- package/src/custom/dsl-fields.ts +102 -0
- package/src/custom/load.ts +2 -0
- package/src/custom/parse.ts +72 -50
- package/src/custom/problems.ts +18 -2
- package/src/detectors/blank-structure.ts +12 -4
- package/src/detectors/clause-shape.ts +199 -0
- package/src/detectors/code-fences.ts +113 -0
- package/src/detectors/cross-definitions.ts +49 -0
- package/src/detectors/cross-facts.ts +60 -0
- package/src/detectors/cross-registry/cross-doc-duplicate-definition.ts +4 -0
- package/src/detectors/cross-registry/cross-doc-fact-conflict.ts +4 -0
- package/src/detectors/custom.ts +10 -2
- package/src/detectors/defined-term-form.ts +200 -0
- package/src/detectors/email-letter.ts +262 -0
- package/src/detectors/impossible-date.ts +141 -0
- package/src/detectors/impossible-time.ts +151 -0
- package/src/detectors/link-shape.ts +70 -0
- package/src/detectors/mark-shape.ts +202 -0
- package/src/detectors/registry/adverb-polarity.ts +4 -0
- package/src/detectors/registry/attachment-missing.ts +4 -0
- package/src/detectors/registry/bare-url-mix.ts +4 -0
- package/src/detectors/registry/caps-shouting.ts +4 -0
- package/src/detectors/registry/code-fence-language.ts +4 -0
- package/src/detectors/registry/defined-name-repeated.ts +4 -0
- package/src/detectors/registry/defined-term-form.ts +4 -0
- package/src/detectors/registry/email-frame.ts +4 -0
- package/src/detectors/registry/email-subject.ts +4 -0
- package/src/detectors/registry/ending-run.ts +4 -0
- package/src/detectors/registry/heading-command.ts +4 -0
- package/src/detectors/registry/hyphen-dash.ts +4 -0
- package/src/detectors/registry/image-file-name-alt.ts +4 -0
- package/src/detectors/registry/impossible-date.ts +4 -0
- package/src/detectors/registry/impossible-time.ts +4 -0
- package/src/detectors/registry/latin-abbreviation.ts +4 -0
- package/src/detectors/registry/link-text-url-mismatch.ts +4 -0
- package/src/detectors/registry/mark-consistency.ts +4 -0
- package/src/detectors/registry/origin-particle.ts +4 -0
- package/src/detectors/registry/request-without-deadline.ts +4 -0
- package/src/detectors/registry/sentence-spacing.ts +4 -0
- package/src/detectors/registry/shell-prompt.ts +4 -0
- package/src/detectors/registry/step-statement.ts +4 -0
- package/src/detectors/registry/unpaired-parallel.ts +4 -0
- package/src/detectors/request-owner.ts +141 -0
- package/src/detectors/technical-docs.ts +230 -0
- package/src/extension/pack-read.ts +128 -0
- package/src/extension/plugin-load.ts +49 -9
- package/src/extension/plugin-parse.ts +10 -3
- package/src/facts/address-values.ts +74 -0
- package/src/facts/cross-facts.ts +54 -0
- package/src/fix-plan/chosen-depth.ts +28 -0
- package/src/fix-plan/mode.ts +57 -11
- package/src/fix-plan/plan.ts +37 -5
- package/src/fix-plan/render.ts +24 -0
- package/src/fix-plan/text.ts +47 -10
- package/src/genre-load.ts +7 -4
- package/src/genre-parse.ts +19 -0
- package/src/init.ts +10 -10
- package/src/known-genres.ts +19 -0
- package/src/link-shape.ts +62 -0
- package/src/reasons.ts +16 -4
- package/src/render/explain.ts +14 -2
- package/src/render/friendly.ts +1 -1
- package/src/render/rules-json.ts +14 -3
- package/src/render/rules-table.ts +6 -6
- package/src/render/semantic.ts +2 -2
- package/src/render/suppressions.ts +2 -2
- package/src/rewrite-depth.ts +60 -0
- package/src/rule-fields.ts +95 -0
- package/src/rule-guide.ts +9 -0
- package/src/rule-load.ts +10 -4
- package/src/rule-offs.ts +50 -0
- package/src/structure/build.ts +21 -1
- package/src/structure/cross-definitions.ts +60 -0
package/src/render/explain.ts
CHANGED
|
@@ -5,6 +5,7 @@ import { uiLanguageOf, type Texts } from "../ui.ts";
|
|
|
5
5
|
import type { Level, RuleDefinition } from "../plugin.ts";
|
|
6
6
|
import { optionLines } from "./options.ts";
|
|
7
7
|
import type { OptionLayer } from "../rule-options.ts";
|
|
8
|
+
import { depthMeaning } from "../rewrite-depth.ts";
|
|
8
9
|
|
|
9
10
|
const TEXT: Texts<{
|
|
10
11
|
readonly off: string;
|
|
@@ -13,6 +14,7 @@ const TEXT: Texts<{
|
|
|
13
14
|
readonly experimental: string;
|
|
14
15
|
readonly alone: (id: string) => string;
|
|
15
16
|
readonly howToFix: string;
|
|
17
|
+
readonly depth: (depth: string, meaning: string) => string;
|
|
16
18
|
readonly example: string;
|
|
17
19
|
readonly definedIn: (plugin: string | undefined) => string;
|
|
18
20
|
readonly values: string;
|
|
@@ -30,6 +32,8 @@ const TEXT: Texts<{
|
|
|
30
32
|
experimental: "このルールはまだ試験中で、既定では動きません(--experimental で動きます)。",
|
|
31
33
|
alone: (id) => `このルールだけを動かす: npx chaffjs enable ${id}(chaff.yaml の rules に ${id}: normal と書くのと同じです)`,
|
|
32
34
|
howToFix: "直しかた",
|
|
35
|
+
depth: (depth, meaning) =>
|
|
36
|
+
`書き直しの深さ: ${depth}(${meaning})。chaff fix-plan --depth でこれより浅い深さを選ぶと、計画はこのルールを直さず、名前だけを挙げます。`,
|
|
33
37
|
example: "例",
|
|
34
38
|
definedIn: (plugin) =>
|
|
35
39
|
plugin === undefined ? "このルールはチームが chaff.yaml の custom_rules で決めたものです。" : `このルールはプラグイン ${plugin} のものです。`,
|
|
@@ -39,7 +43,7 @@ const TEXT: Texts<{
|
|
|
39
43
|
now: (level) => `いまは ${level} です。`,
|
|
40
44
|
nowNumber: (limit) => `いまは段階ではなく数です: ${limit}。`,
|
|
41
45
|
from: (source) => `(${source} で決めています)`,
|
|
42
|
-
change: (id) => `変える: npx
|
|
46
|
+
change: (id) => `変える: npx chaffjs relax ${id} --why "理由"`,
|
|
43
47
|
},
|
|
44
48
|
en: {
|
|
45
49
|
off: "not checked",
|
|
@@ -48,6 +52,7 @@ const TEXT: Texts<{
|
|
|
48
52
|
experimental: "This rule is still experimental and does not run by default (--experimental runs it).",
|
|
49
53
|
alone: (id) => `Turn on this rule alone: npx chaffjs enable ${id} (the same as rules: { ${id}: normal } in chaff.yaml)`,
|
|
50
54
|
howToFix: "How to fix",
|
|
55
|
+
depth: (depth, meaning) => `Rewrite depth: ${depth} (${meaning}). With a shallower chaff fix-plan --depth, the plan names this rule without fixing it.`,
|
|
51
56
|
example: "Example",
|
|
52
57
|
definedIn: (plugin) =>
|
|
53
58
|
plugin === undefined ? "The team defined this rule under custom_rules in chaff.yaml." : `This rule comes from the plugin ${plugin}.`,
|
|
@@ -57,7 +62,7 @@ const TEXT: Texts<{
|
|
|
57
62
|
now: (level) => `Now: ${level}.`,
|
|
58
63
|
nowNumber: (limit) => `Now: ${limit}, set as a number rather than a level.`,
|
|
59
64
|
from: (source) => ` (set by ${source})`,
|
|
60
|
-
change: (id) => `Change it: npx
|
|
65
|
+
change: (id) => `Change it: npx chaffjs relax ${id} --why "reason"`,
|
|
61
66
|
},
|
|
62
67
|
};
|
|
63
68
|
|
|
@@ -98,6 +103,12 @@ const exampleLines = (rule: RuleDefinition, language: string, text: (typeof TEXT
|
|
|
98
103
|
return ["", ` ${text.example}: ${example.before}`, ` → ${example.after}`, "", ` ${text.definedIn(rule.plugin)}`];
|
|
99
104
|
};
|
|
100
105
|
|
|
106
|
+
/** How deep the rule's rewrite direction reaches, for a rule that has one: what chaff fix-plan --depth compares. */
|
|
107
|
+
const depthLines = (rule: RuleDefinition, language: string, text: (typeof TEXT)["ja"]): string[] => {
|
|
108
|
+
const depth = rule.guide?.rewriteDepth;
|
|
109
|
+
return depth === undefined ? [] : ["", ` ${text.depth(depth, depthMeaning(depth, uiLanguageOf(language)))}`];
|
|
110
|
+
};
|
|
111
|
+
|
|
101
112
|
/** Where the rule's settings come from: the option layers, strongest first, and the source of its level when a style set it. */
|
|
102
113
|
/** limit: a number chaff.yaml or its style set for the rule (rules: { id: 80 }, a style's limits), which a level does not show. */
|
|
103
114
|
export type ExplainSettings = {
|
|
@@ -124,6 +135,7 @@ export const renderExplain = (rule: RuleDefinition, current: Level, language: st
|
|
|
124
135
|
` ${readableText(rule, rule.why, language)}`,
|
|
125
136
|
"",
|
|
126
137
|
` ${text.howToFix}: ${readableText(rule, rule.how_to_fix, language)}`,
|
|
138
|
+
...depthLines(rule, language, text),
|
|
127
139
|
...exampleLines(rule, language, text),
|
|
128
140
|
"",
|
|
129
141
|
` ${rule.level_sets === "severity" ? text.severities : text.values}`,
|
package/src/render/friendly.ts
CHANGED
|
@@ -90,7 +90,7 @@ const block = (finding: Finding, rule: RuleDefinition, language: string, quoted:
|
|
|
90
90
|
"",
|
|
91
91
|
...indent(`→ ${filledText(rule.how_to_fix, finding, language)}`, " "),
|
|
92
92
|
"",
|
|
93
|
-
` ${TEXT[uiLanguageOf(language)].relax}: npx
|
|
93
|
+
` ${TEXT[uiLanguageOf(language)].relax}: npx chaffjs relax ${finding.rule}`,
|
|
94
94
|
"",
|
|
95
95
|
];
|
|
96
96
|
|
package/src/render/rules-json.ts
CHANGED
|
@@ -13,6 +13,7 @@ import { limitsFor, styleLevelSource } from "../config/style.ts";
|
|
|
13
13
|
import { loadStyles } from "../style-load.ts";
|
|
14
14
|
import type { OptionLayer } from "../rule-options.ts";
|
|
15
15
|
import { CUSTOM_TYPES } from "../custom/parse.ts";
|
|
16
|
+
import { DEFAULT_DEPTH, REWRITE_DEPTHS, depthMeaning } from "../rewrite-depth.ts";
|
|
16
17
|
|
|
17
18
|
const TEXT: Texts<{
|
|
18
19
|
readonly offBySetting: string;
|
|
@@ -39,7 +40,7 @@ const TEXT: Texts<{
|
|
|
39
40
|
"決まりごとに、summary と level_meaning が合う rule を選ぶ。数の決まりは levels の数と比べ、合う段階を選ぶか数を直接書く。",
|
|
40
41
|
"文書の種類が分かれば genre を決める。rules[].genres で、そのジャンルで動くか・止まるかを確かめる。",
|
|
41
42
|
"表記の決まりは prefer、社内用語は jargon、必須の見出しは required_sections に書く。",
|
|
42
|
-
"既定と同じものは書かない。書いたら npx
|
|
43
|
+
"既定と同じものは書かない。書いたら npx chaffjs explain <rule-id> と、決まりに沿った短い見本で確かめる。",
|
|
43
44
|
],
|
|
44
45
|
},
|
|
45
46
|
en: {
|
|
@@ -56,7 +57,7 @@ const TEXT: Texts<{
|
|
|
56
57
|
"For each requirement, pick the rule whose summary and level_meaning match. For a number, compare it with the rule's levels and pick a level, or write the number itself.",
|
|
57
58
|
"If the kind of document is known, set genre, and check rules[].genres for whether each rule runs or is off in it.",
|
|
58
59
|
"Put spellings under prefer, in-house words under jargon, and required headings under required_sections.",
|
|
59
|
-
"Leave out anything at its default. Then check each rule with npx
|
|
60
|
+
"Leave out anything at its default. Then check each rule with npx chaffjs explain <rule-id> and a short sample that follows the note.",
|
|
60
61
|
],
|
|
61
62
|
},
|
|
62
63
|
};
|
|
@@ -146,11 +147,20 @@ const guideOf = (rule: RuleDefinition): Record<string, unknown> => ({
|
|
|
146
147
|
example: rule.guide?.examples ?? {},
|
|
147
148
|
not_flagged: rule.guide?.notFlagged ?? {},
|
|
148
149
|
level_meaning: rule.guide?.levelMeaning ?? {},
|
|
150
|
+
// The depth chaff fix-plan files the rule's direction under; a rule that does not say is light.
|
|
151
|
+
rewrite_depth: rule.guide?.rewriteDepth ?? DEFAULT_DEPTH,
|
|
149
152
|
languages: rule.languages ?? READER_LANGUAGES,
|
|
150
153
|
requires: rule.requires,
|
|
151
154
|
genres: genresOf(rule),
|
|
152
155
|
});
|
|
153
156
|
|
|
157
|
+
/** Each rewrite depth with what it means, shallowest first: the values of rules[].rewrite_depth and of fix_plan.depth. */
|
|
158
|
+
const depthsOf = (): Record<string, unknown>[] =>
|
|
159
|
+
REWRITE_DEPTHS.map((depth) => ({
|
|
160
|
+
id: depth,
|
|
161
|
+
meaning: Object.fromEntries(READER_LANGUAGES.map((language) => [language, depthMeaning(depth, uiLanguageOf(language))])),
|
|
162
|
+
}));
|
|
163
|
+
|
|
154
164
|
const groupsOf = (): Record<string, unknown>[] =>
|
|
155
165
|
RULE_GROUPS.map((group) => ({
|
|
156
166
|
id: group,
|
|
@@ -190,6 +200,7 @@ export const rulesJson = (
|
|
|
190
200
|
values_you_can_use: ["strict", "normal", "relaxed", "off"],
|
|
191
201
|
values_note: text.valuesNote,
|
|
192
202
|
groups: groupsOf(),
|
|
203
|
+
rewrite_depths: depthsOf(),
|
|
193
204
|
...COMING,
|
|
194
205
|
rules: rules.map((rule) => ({
|
|
195
206
|
id: rule.id,
|
|
@@ -212,7 +223,7 @@ export const rulesJson = (
|
|
|
212
223
|
})),
|
|
213
224
|
how_to_write_settings_from_a_style_note: text.fromStyleNote,
|
|
214
225
|
how_to_change: {
|
|
215
|
-
by_command: ["relax", "strict", "off"].map((command) => `npx
|
|
226
|
+
by_command: ["relax", "strict", "off"].map((command) => `npx chaffjs ${command} <rule-id> --why "${text.reason}"`),
|
|
216
227
|
by_file: text.byFile,
|
|
217
228
|
options_by_file: text.optionsByFile,
|
|
218
229
|
},
|
|
@@ -21,10 +21,10 @@ const TEXT: Texts<{
|
|
|
21
21
|
ungrouped: "(グループ未設定)",
|
|
22
22
|
footer: [
|
|
23
23
|
"既定: 何も書かなくても動く 試験中: --experimental か chaff.yaml で動く",
|
|
24
|
-
"要設定: chaff.yaml に語や見出しを書いたときだけ AI: npx
|
|
24
|
+
"要設定: chaff.yaml に語や見出しを書いたときだけ AI: npx chaffjs test で動く",
|
|
25
25
|
"",
|
|
26
|
-
"詳しく: npx
|
|
27
|
-
"AI に渡す: npx
|
|
26
|
+
"詳しく: npx chaffjs explain <rule>",
|
|
27
|
+
"AI に渡す: npx chaffjs rules --json",
|
|
28
28
|
],
|
|
29
29
|
},
|
|
30
30
|
en: {
|
|
@@ -33,10 +33,10 @@ const TEXT: Texts<{
|
|
|
33
33
|
ungrouped: "(no group yet)",
|
|
34
34
|
footer: [
|
|
35
35
|
"default: runs with no settings experimental: runs with --experimental or a level in chaff.yaml",
|
|
36
|
-
"needs list: runs only on words or headings listed in chaff.yaml AI: runs with npx
|
|
36
|
+
"needs list: runs only on words or headings listed in chaff.yaml AI: runs with npx chaffjs test",
|
|
37
37
|
"",
|
|
38
|
-
"More on one rule: npx
|
|
39
|
-
"For an AI: npx
|
|
38
|
+
"More on one rule: npx chaffjs explain <rule>",
|
|
39
|
+
"For an AI: npx chaffjs rules --json",
|
|
40
40
|
],
|
|
41
41
|
},
|
|
42
42
|
};
|
package/src/render/semantic.ts
CHANGED
|
@@ -43,7 +43,7 @@ const TEXT: Texts<{
|
|
|
43
43
|
confidence: (value) => `確からしさ ${value}`,
|
|
44
44
|
disagree: [" この指摘が違うと思ったら:"],
|
|
45
45
|
silence: (rule) => ` この箇所だけ黙らせる <!-- stet: ${rule} — 理由 -->`,
|
|
46
|
-
relax: (rule) => ` ルールごとゆるめる npx
|
|
46
|
+
relax: (rule) => ` ルールごとゆるめる npx chaffjs relax ${rule}`,
|
|
47
47
|
notRunChecks: (count) => ` ${count} 件の検査は、見るところが無いため動いていません。`,
|
|
48
48
|
byMachine: "機械で絞り込み済み",
|
|
49
49
|
whole: "絞り込めず全文",
|
|
@@ -68,7 +68,7 @@ const TEXT: Texts<{
|
|
|
68
68
|
confidence: (value) => `confidence ${value}`,
|
|
69
69
|
disagree: [" If you think this finding is wrong:"],
|
|
70
70
|
silence: (rule) => ` Silence this spot only <!-- stet: ${rule} — reason -->`,
|
|
71
|
-
relax: (rule) => ` Relax the whole rule npx
|
|
71
|
+
relax: (rule) => ` Relax the whole rule npx chaffjs relax ${rule}`,
|
|
72
72
|
notRunChecks: (count) => ` ${counted(count, "check")} did not run: there was nothing for ${formFor(count, "it", "them")} to look at.`,
|
|
73
73
|
byMachine: "narrowed by machine",
|
|
74
74
|
whole: "not narrowed: the whole text",
|
|
@@ -20,7 +20,7 @@ const TEXT: Texts<{
|
|
|
20
20
|
more: (n) => ` ほか ${n} ファイル`,
|
|
21
21
|
count: (n) => `${String(n).padStart(3)} 件`,
|
|
22
22
|
reasons: (list) => `理由: ${list}`,
|
|
23
|
-
relaxAll: (rule) => `ルールごとゆるめる: npx
|
|
23
|
+
relaxAll: (rule) => `ルールごとゆるめる: npx chaffjs relax ${rule} --why "..."`,
|
|
24
24
|
none: "\n 抑制されている指摘はありません。\n",
|
|
25
25
|
total: (n) => `抑制されている指摘: ${n} 件`,
|
|
26
26
|
reasonless: (n) => `理由が書かれていない抑制: ${n} 件`,
|
|
@@ -32,7 +32,7 @@ const TEXT: Texts<{
|
|
|
32
32
|
more: (n) => ` and ${counted(n, "more file")}`,
|
|
33
33
|
count: (n) => `${String(n).padStart(3)}`,
|
|
34
34
|
reasons: (list) => `reasons: ${list}`,
|
|
35
|
-
relaxAll: (rule) => `relax the whole rule: npx
|
|
35
|
+
relaxAll: (rule) => `relax the whole rule: npx chaffjs relax ${rule} --why "..."`,
|
|
36
36
|
none: "\n No findings are silenced.\n",
|
|
37
37
|
total: (n) => `Silenced findings: ${n}`,
|
|
38
38
|
reasonless: (n) => `Silenced without a reason: ${n}`,
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import type { Texts, UiLanguage } from "./ui.ts";
|
|
2
|
+
|
|
3
|
+
// How deep a rewrite goes, shallowest first. A rule's rewrite block says how deep its direction reaches, and
|
|
4
|
+
// chaff fix-plan --depth (or fix_plan.depth in chaff.yaml) says how deep the rewrite may go. A depth includes the
|
|
5
|
+
// shallower ones: a structure rewrite also fixes words and sentences. Pure.
|
|
6
|
+
|
|
7
|
+
export const REWRITE_DEPTHS = ["light", "structure", "register"] as const;
|
|
8
|
+
|
|
9
|
+
export type RewriteDepth = (typeof REWRITE_DEPTHS)[number];
|
|
10
|
+
|
|
11
|
+
/** A rule without a depth of its own, or without a rewrite block, fixes words and sentences. */
|
|
12
|
+
export const DEFAULT_DEPTH: RewriteDepth = "light";
|
|
13
|
+
|
|
14
|
+
const isRewriteDepth = (value: unknown): value is RewriteDepth => REWRITE_DEPTHS.some((depth) => depth === value);
|
|
15
|
+
|
|
16
|
+
const isRecord = (value: unknown): value is Record<string, unknown> => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
17
|
+
|
|
18
|
+
const rankOf = (depth: RewriteDepth): number => REWRITE_DEPTHS.indexOf(depth);
|
|
19
|
+
|
|
20
|
+
/** Whether a rewrite that may go as deep as `allowed` covers a rule whose direction reaches `needed`. */
|
|
21
|
+
export const depthIncludes = (allowed: RewriteDepth, needed: RewriteDepth): boolean => rankOf(needed) <= rankOf(allowed);
|
|
22
|
+
|
|
23
|
+
/** A depth as written: none, one chaff knows, or a value to report as it was written. */
|
|
24
|
+
export type DepthRead = { readonly depth: RewriteDepth | undefined } | { readonly unknown: string };
|
|
25
|
+
|
|
26
|
+
/** rewrite.depth, chaff.yaml's fix_plan.depth or --depth as written. Nothing written is no depth; anything else must be a depth. */
|
|
27
|
+
export const readDepth = (written: unknown): DepthRead => {
|
|
28
|
+
if (written === undefined || written === null) return { depth: undefined };
|
|
29
|
+
if (isRewriteDepth(written)) return { depth: written };
|
|
30
|
+
return { unknown: typeof written === "string" ? written : (JSON.stringify(written) ?? typeof written) };
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
/** rewrite.depth of a rule's rewrite block, which is otherwise keyed by language. */
|
|
34
|
+
export const depthOfRewrite = (rewrite: unknown): DepthRead => (isRecord(rewrite) ? readDepth(rewrite["depth"]) : { depth: undefined });
|
|
35
|
+
|
|
36
|
+
const MEANING: Texts<Readonly<Record<RewriteDepth, string>>> = {
|
|
37
|
+
ja: {
|
|
38
|
+
light: "語と文を直す。構成と文体は残す",
|
|
39
|
+
structure: "節・見出し・段落を組み替える",
|
|
40
|
+
register: "文体を変える(です・ます → である、比べる言い方を外す など)",
|
|
41
|
+
},
|
|
42
|
+
en: {
|
|
43
|
+
light: "words and sentences; the structure and the voice stay",
|
|
44
|
+
structure: "sections, headings and paragraphs are reorganised",
|
|
45
|
+
register: "the style is converted: polite to plain endings, comparatives removed and so on",
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
/** What a depth means, in a reader's words. */
|
|
50
|
+
export const depthMeaning = (depth: RewriteDepth, ui: UiLanguage): string => MEANING[ui][depth];
|
|
51
|
+
|
|
52
|
+
/** Every depth with its meaning, shallowest first: "light (…) / structure (…) / register (…)". */
|
|
53
|
+
const depthChoices = (ui: UiLanguage): string =>
|
|
54
|
+
REWRITE_DEPTHS.map((depth) => (ui === "ja" ? `${depth}(${MEANING.ja[depth]})` : `${depth} (${MEANING.en[depth]})`)).join(" / ");
|
|
55
|
+
|
|
56
|
+
/** Why a written depth cannot be read, with every depth chaff knows. where: what was written ("rewrite.depth", "--depth"). */
|
|
57
|
+
export const unknownDepthSentence = (where: string, written: string, ui: UiLanguage): string =>
|
|
58
|
+
ui === "ja"
|
|
59
|
+
? `${where}: ${written} は書き直しの深さではありません。深さは ${depthChoices("ja")} のどれかで、深いほうは浅いほうを含みます。`
|
|
60
|
+
: `${where}: ${written} is not a rewrite depth. A depth is one of ${depthChoices("en")}; a deeper one includes the shallower.`;
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { RULE_GROUPS } from "./rule-guide.ts";
|
|
2
|
+
import { depthOfRewrite, unknownDepthSentence } from "./rewrite-depth.ts";
|
|
3
|
+
import type { Texts, UiLanguage } from "./ui.ts";
|
|
4
|
+
|
|
5
|
+
// The fields a rule has whoever writes it: chaff's own rules/*.yaml, a team's custom_rules and a plugin's rules all
|
|
6
|
+
// read these the same way, and all three refuse the same malformed values with the same sentence. Pure: the genres a
|
|
7
|
+
// use_for entry may name come in.
|
|
8
|
+
|
|
9
|
+
export type FieldProblem =
|
|
10
|
+
| { readonly kind: "bad-depth"; readonly written: string }
|
|
11
|
+
| { readonly kind: "bad-group"; readonly written: string }
|
|
12
|
+
| { readonly kind: "bad-use-for"; readonly written: string }
|
|
13
|
+
| { readonly kind: "bad-summary"; readonly written: string }
|
|
14
|
+
| { readonly kind: "bad-example"; readonly written: string };
|
|
15
|
+
|
|
16
|
+
export type FieldKind = FieldProblem["kind"];
|
|
17
|
+
|
|
18
|
+
const isRecord = (value: unknown): value is Record<string, unknown> => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
19
|
+
|
|
20
|
+
const isText = (value: unknown): value is string => typeof value === "string" && value.trim() !== "";
|
|
21
|
+
|
|
22
|
+
const printed = (value: unknown): string => (typeof value === "string" ? value : (JSON.stringify(value) ?? typeof value));
|
|
23
|
+
|
|
24
|
+
/** A text in every language (one string) or by language ({ ja, en }), with no empty text. */
|
|
25
|
+
const isTextByLanguage = (value: unknown): boolean =>
|
|
26
|
+
isText(value) || (isRecord(value) && Object.values(value).length > 0 && Object.values(value).every(isText));
|
|
27
|
+
|
|
28
|
+
const groupProblems = (group: unknown): FieldProblem[] =>
|
|
29
|
+
group === undefined || RULE_GROUPS.some((known) => known === group) ? [] : [{ kind: "bad-group", written: printed(group) }];
|
|
30
|
+
|
|
31
|
+
/** A use_for entry names a genre or the start of one (business covers business/report). */
|
|
32
|
+
const namesGenre = (entry: unknown, genres: readonly string[]): boolean =>
|
|
33
|
+
isText(entry) && genres.some((genre) => genre === entry || genre.startsWith(`${entry}/`));
|
|
34
|
+
|
|
35
|
+
const useForProblems = (useFor: unknown, genres: readonly string[]): FieldProblem[] => {
|
|
36
|
+
if (useFor === undefined) return [];
|
|
37
|
+
if (!Array.isArray(useFor) || useFor.length === 0) return [{ kind: "bad-use-for", written: printed(useFor) }];
|
|
38
|
+
return useFor.filter((entry: unknown) => !namesGenre(entry, genres)).map((entry: unknown) => ({ kind: "bad-use-for", written: printed(entry) }));
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const summaryProblems = (summary: unknown): FieldProblem[] =>
|
|
42
|
+
summary === undefined || isTextByLanguage(summary) ? [] : [{ kind: "bad-summary", written: printed(summary) }];
|
|
43
|
+
|
|
44
|
+
/** One example: before and after, each a text in every language or by language. */
|
|
45
|
+
const isExamplePair = (value: unknown): boolean => isRecord(value) && isTextByLanguage(value["before"]) && isTextByLanguage(value["after"]);
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* example in either form: { before, after } (a team's rule writes it so), or by language, { ja: { before, after }, en: … }
|
|
49
|
+
* (chaff's rules write it so, with config, pad and other beside before and after).
|
|
50
|
+
*/
|
|
51
|
+
const exampleProblems = (example: unknown): FieldProblem[] => {
|
|
52
|
+
if (example === undefined) return [];
|
|
53
|
+
if (isRecord(example) && ("before" in example || "after" in example))
|
|
54
|
+
return isExamplePair(example) ? [] : [{ kind: "bad-example", written: printed(example) }];
|
|
55
|
+
if (!isRecord(example) || Object.keys(example).length === 0) return [{ kind: "bad-example", written: printed(example) }];
|
|
56
|
+
return Object.entries(example).flatMap(([language, pair]) => (isExamplePair(pair) ? [] : [{ kind: "bad-example" as const, written: language }]));
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const depthProblems = (rewrite: unknown): FieldProblem[] => {
|
|
60
|
+
const read = depthOfRewrite(rewrite);
|
|
61
|
+
return "unknown" in read ? [{ kind: "bad-depth", written: read.unknown }] : [];
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
/** What is wrong with the shared fields of one rule as written, in the order the fields are listed here. */
|
|
65
|
+
export const fieldProblems = (raw: Readonly<Record<string, unknown>>, genres: readonly string[]): FieldProblem[] => [
|
|
66
|
+
...groupProblems(raw["group"]),
|
|
67
|
+
...summaryProblems(raw["summary"]),
|
|
68
|
+
...useForProblems(raw["use_for"], genres),
|
|
69
|
+
...exampleProblems(raw["example"]),
|
|
70
|
+
...depthProblems(raw["rewrite"]),
|
|
71
|
+
];
|
|
72
|
+
|
|
73
|
+
const TEXT: Texts<Readonly<Record<FieldKind, (written: string) => string>>> = {
|
|
74
|
+
ja: {
|
|
75
|
+
"bad-depth": (written) => unknownDepthSentence("rewrite.depth", written, "ja"),
|
|
76
|
+
"bad-group": (written) => `group: ${written} は分類の名前ではありません(${RULE_GROUPS.join(" / ")})。`,
|
|
77
|
+
"bad-use-for": (written) =>
|
|
78
|
+
`use_for: ${written} はジャンルではありません。ジャンルかその頭(business、business/report)を並べます。一覧は npx chaffjs genres。`,
|
|
79
|
+
"bad-summary": (written) => `summary: ${written} は読めません。一つの文字列か、言語ごとの文字列({ ja: …, en: … })で書きます。`,
|
|
80
|
+
"bad-example": (written) =>
|
|
81
|
+
`example: ${written} の before と after が揃っていません。{ before, after } か、言語ごとに { ja: { before, after }, en: { before, after } } と書きます。`,
|
|
82
|
+
},
|
|
83
|
+
en: {
|
|
84
|
+
"bad-depth": (written) => unknownDepthSentence("rewrite.depth", written, "en"),
|
|
85
|
+
"bad-group": (written) => `group: ${written} is not a group (${RULE_GROUPS.join(" / ")}).`,
|
|
86
|
+
"bad-use-for": (written) =>
|
|
87
|
+
`use_for: ${written} is not a genre. List genres or their first part (business, business/report); npx chaffjs genres lists them.`,
|
|
88
|
+
"bad-summary": (written) => `summary: cannot read ${written}. Write one string, or one per language ({ ja: …, en: … }).`,
|
|
89
|
+
"bad-example": (written) =>
|
|
90
|
+
`example: ${written} lacks a before or an after. Write { before, after }, or one per language: { ja: { before, after }, en: { before, after } }.`,
|
|
91
|
+
},
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
/** What is wrong, with the value as written and what would be right. */
|
|
95
|
+
export const fieldProblemSentence = (problem: FieldProblem, ui: UiLanguage): string => TEXT[ui][problem.kind](problem.written);
|
package/src/rule-guide.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Localized } from "./plugin.ts";
|
|
2
|
+
import { depthOfRewrite, type RewriteDepth } from "./rewrite-depth.ts";
|
|
2
3
|
|
|
3
4
|
/** How the rule reference groups rules for a reader who is not an engineer, in the order it lists them. */
|
|
4
5
|
export const RULE_GROUPS = ["readability", "wording", "slips", "consistency", "structure", "facts", "ai-tells", "team"] as const;
|
|
@@ -111,6 +112,8 @@ export type RuleGuide = {
|
|
|
111
112
|
readonly sources: readonly string[];
|
|
112
113
|
/** By language, optional per rule. */
|
|
113
114
|
readonly rewrite: Readonly<Record<string, RuleRewrite>>;
|
|
115
|
+
/** rewrite.depth: how deep the block's direction reaches. None when not written or not a depth; the loaders report the latter. */
|
|
116
|
+
readonly rewriteDepth?: RewriteDepth | undefined;
|
|
114
117
|
};
|
|
115
118
|
|
|
116
119
|
const isRecord = (value: unknown): value is Record<string, unknown> => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -160,6 +163,11 @@ const rewritesOf = (value: unknown): Readonly<Record<string, RuleRewrite>> =>
|
|
|
160
163
|
? Object.fromEntries(Object.entries(value).flatMap(([language, entry]) => (isRecord(entry) ? [[language, rewriteOf(entry)] as const] : [])))
|
|
161
164
|
: {};
|
|
162
165
|
|
|
166
|
+
const rewriteDepthOf = (rewrite: unknown): RewriteDepth | undefined => {
|
|
167
|
+
const read = depthOfRewrite(rewrite);
|
|
168
|
+
return "depth" in read ? read.depth : undefined;
|
|
169
|
+
};
|
|
170
|
+
|
|
163
171
|
/** A field that is missing or malformed reads as empty; the test on rule files names what a rule lacks. */
|
|
164
172
|
export const ruleGuideOf = (raw: Readonly<Record<string, unknown>>): RuleGuide => ({
|
|
165
173
|
group: RULE_GROUPS.find((group) => group === raw["group"]),
|
|
@@ -169,6 +177,7 @@ export const ruleGuideOf = (raw: Readonly<Record<string, unknown>>): RuleGuide =
|
|
|
169
177
|
levelMeaning: localizedOf(raw["level_meaning"]),
|
|
170
178
|
sources: textsOf(raw["sources"]),
|
|
171
179
|
rewrite: rewritesOf(raw["rewrite"]),
|
|
180
|
+
rewriteDepth: rewriteDepthOf(raw["rewrite"]),
|
|
172
181
|
});
|
|
173
182
|
|
|
174
183
|
/** The rules in each group, in the order the reference lists them. A rule with no group is in none, which the test on rule files reports. */
|
package/src/rule-load.ts
CHANGED
|
@@ -6,6 +6,8 @@ import type { LanguageLevels, LevelSets, LevelTable, RuleDefinition, Severity }
|
|
|
6
6
|
import { rankOfSeverity, severityAt } from "./levels.ts";
|
|
7
7
|
import { optionsOf } from "./rule-options.ts";
|
|
8
8
|
import { ruleGuideOf } from "./rule-guide.ts";
|
|
9
|
+
import { fieldProblemSentence, fieldProblems } from "./rule-fields.ts";
|
|
10
|
+
import { genresBeside } from "./known-genres.ts";
|
|
9
11
|
|
|
10
12
|
const RULES_DIR = join(dirname(fileURLToPath(import.meta.url)), "..", "rules");
|
|
11
13
|
|
|
@@ -142,12 +144,14 @@ const checkedSeverity = (rule: RuleDefinition, file: string): RuleDefinition =>
|
|
|
142
144
|
throw new Error(`${file}: severity ${rule.severity} が levels の normal(${severityAt(rule, "normal")})と違います`);
|
|
143
145
|
};
|
|
144
146
|
|
|
145
|
-
const toRule = (raw: unknown, language: string, file: string): RuleDefinition => {
|
|
147
|
+
const toRule = (raw: unknown, language: string, file: string, genres: readonly string[]): RuleDefinition => {
|
|
146
148
|
if (!isRecord(raw)) throw new Error(`${file}: rule は object であること`);
|
|
147
149
|
const levels = flattenLevels(raw["levels"], language);
|
|
148
150
|
const missing = missingFields(raw, levels);
|
|
149
151
|
if (missing.length > 0) throw new Error(`${file}: 必須フィールドがありません: ${missing.join(", ")}`);
|
|
150
152
|
if (levels === undefined) throw new Error(`${file}: levels を解決できません`);
|
|
153
|
+
const [problem] = fieldProblems(raw, genres);
|
|
154
|
+
if (problem !== undefined) throw new Error(`${file}: ${fieldProblemSentence(problem, "ja")}`);
|
|
151
155
|
return checkedSeverity(ruleOf(raw, levels, levelSetsOf(raw, language, file), language, file), file);
|
|
152
156
|
};
|
|
153
157
|
|
|
@@ -193,8 +197,10 @@ const parseRule = (dir: string, file: string): unknown => {
|
|
|
193
197
|
}
|
|
194
198
|
};
|
|
195
199
|
|
|
196
|
-
export const loadRules = (language: string, dir: string = RULES_DIR): RuleDefinition[] =>
|
|
197
|
-
|
|
200
|
+
export const loadRules = (language: string, dir: string = RULES_DIR): RuleDefinition[] => {
|
|
201
|
+
const genres = genresBeside(dir);
|
|
202
|
+
return readdirSync(dir)
|
|
198
203
|
.filter((file) => file.endsWith(".yaml"))
|
|
199
204
|
.toSorted((left, right) => left.localeCompare(right, "en"))
|
|
200
|
-
.map((file) => toRule(parseRule(dir, file), language, file));
|
|
205
|
+
.map((file) => toRule(parseRule(dir, file), language, file, genres));
|
|
206
|
+
};
|
package/src/rule-offs.ts
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { readFileSync, readdirSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { parse } from "yaml";
|
|
4
|
+
|
|
5
|
+
/** One rule a group or genre turns off by default, from the rule file's off_for, with why. */
|
|
6
|
+
export type RuleOff = { readonly rule: string; readonly target: string; readonly reason: string };
|
|
7
|
+
|
|
8
|
+
const isRecord = (value: unknown): value is Record<string, unknown> => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
9
|
+
|
|
10
|
+
/** A rule file's off_for block: the "off_for:" line and the indented lines under it. */
|
|
11
|
+
const OFF_FOR_BLOCK = /^off_for:\n(?:[ \t]+[^\n]*\n?)*/mu;
|
|
12
|
+
/** Any top-level off_for line, of whatever form. Only the block form is read, the form `yarn rules:measure --apply` edits. */
|
|
13
|
+
const OFF_FOR_LINE = /^off_for:.*$/gmu;
|
|
14
|
+
|
|
15
|
+
/** The off_for map of one rule file (the group or genre, then why), checked; empty when the file has none. */
|
|
16
|
+
export const ruleOffsOf = (rule: string, raw: unknown): RuleOff[] => {
|
|
17
|
+
if (raw === undefined || raw === null) return [];
|
|
18
|
+
if (!isRecord(raw)) throw new Error(`${rule}: off_for must map a group or genre to why it turns the rule off`);
|
|
19
|
+
return Object.entries(raw).map(([target, reason]) => {
|
|
20
|
+
if (typeof reason !== "string" || reason.trim() === "") throw new Error(`${rule}: off_for.${target} needs a reason`);
|
|
21
|
+
return { rule, target, reason };
|
|
22
|
+
});
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
/** The off_for block of one rule file's text, or undefined; a second block or another form stops here. */
|
|
26
|
+
const offForBlock = (rule: string, text: string): string | undefined => {
|
|
27
|
+
const lines = [...text.matchAll(OFF_FOR_LINE)].map((match) => match[0]);
|
|
28
|
+
if (lines.length === 0) return undefined;
|
|
29
|
+
if (lines.length > 1 || lines[0] !== "off_for:")
|
|
30
|
+
throw new Error(`${rule}: write off_for once, as "off_for:" with one indented "<group or genre>: <why>" line under it per off`);
|
|
31
|
+
return OFF_FOR_BLOCK.exec(text)?.[0];
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
/** The offs of one rule file. The rule is named by the file, which is named by the rule's id. */
|
|
35
|
+
const offsInFile = (dir: string, file: string): RuleOff[] => {
|
|
36
|
+
const rule = file.replace(/\.yaml$/u, "");
|
|
37
|
+
const block = offForBlock(rule, readFileSync(join(dir, file), "utf8").replaceAll("\r\n", "\n"));
|
|
38
|
+
const parsed: unknown = block === undefined ? undefined : parse(block);
|
|
39
|
+
return ruleOffsOf(rule, isRecord(parsed) ? parsed["off_for"] : undefined);
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* The offs of every rule file in `dir`. Only each file's off_for block is parsed, not the whole rule: genres are read
|
|
44
|
+
* at startup, before any rule is.
|
|
45
|
+
*/
|
|
46
|
+
export const loadRuleOffs = (dir: string): RuleOff[] =>
|
|
47
|
+
readdirSync(dir)
|
|
48
|
+
.filter((file) => file.endsWith(".yaml"))
|
|
49
|
+
.toSorted((left, right) => left.localeCompare(right, "en"))
|
|
50
|
+
.flatMap((file) => offsInFile(dir, file));
|
package/src/structure/build.ts
CHANGED
|
@@ -286,6 +286,26 @@ const numberedLine = (state: State, patterns: StructurePatterns, text: string, c
|
|
|
286
286
|
unnumberedUnit(text, openUnits(state), state.profile?.unnumbered) ??
|
|
287
287
|
universalNumber(patterns, text, context, state.plainText);
|
|
288
288
|
|
|
289
|
+
/** A dotted number alone: what is left of 「### 29.3 `chaff grade`」 once its code is blanked and the line trimmed. */
|
|
290
|
+
const NUMBER_ONLY = /^\d{1,3}(?:\.\d{1,3}){1,5}[..]?$/u;
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* A heading whose title is all inline code is numbered as written. Its rest is left empty: the code is not prose to read,
|
|
294
|
+
* and the blanked line has no place for it. A heading that is only a number as written (a changelog's 「## 46.3」) stays unnumbered.
|
|
295
|
+
*/
|
|
296
|
+
const codeTitledNumber = (
|
|
297
|
+
state: State,
|
|
298
|
+
patterns: StructurePatterns,
|
|
299
|
+
text: string,
|
|
300
|
+
heading: Heading | undefined,
|
|
301
|
+
context: NumberingContext,
|
|
302
|
+
): NumberedLine | undefined => {
|
|
303
|
+
const written = heading?.text.trim() ?? "";
|
|
304
|
+
if (!NUMBER_ONLY.test(text) || written === text) return undefined;
|
|
305
|
+
const numbered = numberedLine(state, patterns, written, context);
|
|
306
|
+
return numbered === undefined ? undefined : { ...numbered, rest: "" };
|
|
307
|
+
};
|
|
308
|
+
|
|
289
309
|
/** 番号の後ろが見出しでなく本文か。前の行から見出しが付く前の、行そのもので決める。 */
|
|
290
310
|
const carriesBody = (found: NumberedLine | undefined): boolean => found !== undefined && found.heading === "" && found.rest !== "";
|
|
291
311
|
|
|
@@ -302,7 +322,7 @@ const readLine = (state: State, patterns: StructurePatterns, line: Line, heading
|
|
|
302
322
|
const text = heading === undefined ? line.text : headingLineText(line.text);
|
|
303
323
|
const openNumbers = state.stack.flatMap((frame) => (frame.numbered === undefined ? [] : [frame.numbered]));
|
|
304
324
|
const context = { open: openNumbers, isHeading: heading !== undefined };
|
|
305
|
-
const found = around.inTable ? undefined : numberedLine(state, patterns, text, context);
|
|
325
|
+
const found = around.inTable ? undefined : (numberedLine(state, patterns, text, context) ?? codeTitledNumber(state, patterns, text, heading, context));
|
|
306
326
|
const numbered = withCaption(state, line, found);
|
|
307
327
|
const caption = numbered === undefined && heading === undefined ? captionOf(state.profile, text) : undefined;
|
|
308
328
|
if (caption !== undefined) state.captions.set(line.number, caption);
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { tallyVotes, type Vote } from "../cross-votes.ts";
|
|
2
|
+
|
|
3
|
+
// One term defined in two files of one run, in different words. Pure: the definitions come in already read from each file.
|
|
4
|
+
|
|
5
|
+
/** A definition as a file writes it: the term, what follows it up to the end of the sentence, and where it is. */
|
|
6
|
+
export type WrittenDefinition = { readonly term: string; readonly body: string; readonly offset: number };
|
|
7
|
+
|
|
8
|
+
export type FileDefinitions = { readonly path: string; readonly definitions: readonly WrittenDefinition[] };
|
|
9
|
+
|
|
10
|
+
/** A file that defines a term unlike most files that define it, and one file of the usual wording. */
|
|
11
|
+
export type DefinitionOutOfStep = {
|
|
12
|
+
readonly path: string;
|
|
13
|
+
readonly definition: WrittenDefinition;
|
|
14
|
+
readonly usual: Vote<WrittenDefinition>;
|
|
15
|
+
readonly files: number;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
/** Marks that do not change what a definition says: the punctuation at its two ends. Spaces are evened out apart. */
|
|
19
|
+
const LEADING: ReadonlySet<string> = new Set(["、", ",", ",", ":", ":"]);
|
|
20
|
+
const TRAILING: ReadonlySet<string> = new Set(["。", ".", "."]);
|
|
21
|
+
const SPACE = /\s+/gu;
|
|
22
|
+
|
|
23
|
+
const SPACE_CHAR = /\s/u;
|
|
24
|
+
|
|
25
|
+
/** The text without spaces and the marks of a set at its start, or at its end. */
|
|
26
|
+
const trimStartOf = (text: string, marks: ReadonlySet<string>): string => {
|
|
27
|
+
const first = text.split("").findIndex((char) => !marks.has(char) && !SPACE_CHAR.test(char));
|
|
28
|
+
return first === -1 ? "" : text.slice(first);
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const trimEndOf = (text: string, marks: ReadonlySet<string>): string =>
|
|
32
|
+
text.slice(0, text.split("").findLastIndex((char) => !marks.has(char) && !SPACE_CHAR.test(char)) + 1);
|
|
33
|
+
|
|
34
|
+
/** The wording compared: width, spaces and the punctuation at the ends evened out. */
|
|
35
|
+
export const definitionKey = (body: string): string =>
|
|
36
|
+
trimEndOf(trimStartOf(body.normalize("NFKC"), LEADING), TRAILING)
|
|
37
|
+
.replace(SPACE, " ")
|
|
38
|
+
.trim();
|
|
39
|
+
|
|
40
|
+
/** A file's vote for a term: its first definition of it (a second one in the same file is duplicate-definition's). */
|
|
41
|
+
const voteOf = (file: FileDefinitions, term: string): Vote<WrittenDefinition>[] => {
|
|
42
|
+
const first = file.definitions.find((definition) => definition.term === term && definitionKey(definition.body) !== "");
|
|
43
|
+
return first === undefined ? [] : [{ path: file.path, value: first }];
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
const sameWording = (left: WrittenDefinition, right: WrittenDefinition): boolean => definitionKey(left.body) === definitionKey(right.body);
|
|
47
|
+
|
|
48
|
+
/** The files that define a term in other words than most files of the run that define it. */
|
|
49
|
+
export const definitionsOutOfStep = (files: readonly FileDefinitions[]): DefinitionOutOfStep[] => {
|
|
50
|
+
const terms = [...new Set(files.flatMap((file) => file.definitions.map((definition) => definition.term)))];
|
|
51
|
+
return terms.flatMap((term) => {
|
|
52
|
+
const tally = tallyVotes(
|
|
53
|
+
files.flatMap((file) => voteOf(file, term)),
|
|
54
|
+
sameWording,
|
|
55
|
+
);
|
|
56
|
+
const [usual] = tally?.usual ?? [];
|
|
57
|
+
if (tally === undefined || usual === undefined) return [];
|
|
58
|
+
return tally.odd.map((vote) => ({ path: vote.path, definition: vote.value, usual, files: tally.usual.length }));
|
|
59
|
+
});
|
|
60
|
+
};
|