chaffjs 0.20.0 → 0.22.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 +7 -5
- 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 +18 -5
- package/dist/cli-text.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +8 -11
- 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/commands/init-command.d.ts +4 -0
- package/dist/commands/init-command.d.ts.map +1 -0
- package/dist/commands/init-command.js +15 -0
- package/dist/commands/init-command.js.map +1 -0
- package/dist/commands/plugin-init.d.ts +20 -0
- package/dist/commands/plugin-init.d.ts.map +1 -0
- package/dist/commands/plugin-init.js +129 -0
- package/dist/commands/plugin-init.js.map +1 -0
- package/dist/commands/plugin-test.d.ts +11 -0
- package/dist/commands/plugin-test.d.ts.map +1 -0
- package/dist/commands/plugin-test.js +81 -0
- package/dist/commands/plugin-test.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 +10 -1
- package/dist/custom/problems.js.map +1 -1
- package/dist/detectors/amount-for-count.d.ts +21 -0
- package/dist/detectors/amount-for-count.d.ts.map +1 -0
- package/dist/detectors/amount-for-count.js +55 -0
- package/dist/detectors/amount-for-count.js.map +1 -0
- 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/currency-notation.d.ts +29 -0
- package/dist/detectors/currency-notation.d.ts.map +1 -0
- package/dist/detectors/currency-notation.js +58 -0
- package/dist/detectors/currency-notation.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/dangling-opener.d.ts +14 -0
- package/dist/detectors/dangling-opener.d.ts.map +1 -0
- package/dist/detectors/dangling-opener.js +55 -0
- package/dist/detectors/dangling-opener.js.map +1 -0
- 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/footnote-mismatch.d.ts +11 -0
- package/dist/detectors/footnote-mismatch.d.ts.map +1 -0
- package/dist/detectors/footnote-mismatch.js +55 -0
- package/dist/detectors/footnote-mismatch.js.map +1 -0
- package/dist/detectors/form-minority.d.ts +13 -0
- package/dist/detectors/form-minority.d.ts.map +1 -0
- package/dist/detectors/form-minority.js +19 -0
- package/dist/detectors/form-minority.js.map +1 -0
- package/dist/detectors/heading-end-mark.d.ts +10 -0
- package/dist/detectors/heading-end-mark.d.ts.map +1 -0
- package/dist/detectors/heading-end-mark.js +59 -0
- package/dist/detectors/heading-end-mark.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/list-marker.d.ts +7 -0
- package/dist/detectors/list-marker.d.ts.map +1 -0
- package/dist/detectors/list-marker.js +50 -0
- package/dist/detectors/list-marker.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/markdown-slip.d.ts +19 -0
- package/dist/detectors/markdown-slip.d.ts.map +1 -0
- package/dist/detectors/markdown-slip.js +122 -0
- package/dist/detectors/markdown-slip.js.map +1 -0
- package/dist/detectors/numeral-opener.d.ts +10 -0
- package/dist/detectors/numeral-opener.d.ts.map +1 -0
- package/dist/detectors/numeral-opener.js +65 -0
- package/dist/detectors/numeral-opener.js.map +1 -0
- package/dist/detectors/ordinal-suffix.d.ts +12 -0
- package/dist/detectors/ordinal-suffix.d.ts.map +1 -0
- package/dist/detectors/ordinal-suffix.js +33 -0
- package/dist/detectors/ordinal-suffix.js.map +1 -0
- package/dist/detectors/range-notation.d.ts +15 -0
- package/dist/detectors/range-notation.d.ts.map +1 -0
- package/dist/detectors/range-notation.js +58 -0
- package/dist/detectors/range-notation.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/amount-for-count.d.ts +3 -0
- package/dist/detectors/registry/amount-for-count.d.ts.map +1 -0
- package/dist/detectors/registry/amount-for-count.js +3 -0
- package/dist/detectors/registry/amount-for-count.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/currency-notation.d.ts +3 -0
- package/dist/detectors/registry/currency-notation.d.ts.map +1 -0
- package/dist/detectors/registry/currency-notation.js +3 -0
- package/dist/detectors/registry/currency-notation.js.map +1 -0
- package/dist/detectors/registry/dangling-opener.d.ts +3 -0
- package/dist/detectors/registry/dangling-opener.d.ts.map +1 -0
- package/dist/detectors/registry/dangling-opener.js +3 -0
- package/dist/detectors/registry/dangling-opener.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/footnote-mismatch.d.ts +3 -0
- package/dist/detectors/registry/footnote-mismatch.d.ts.map +1 -0
- package/dist/detectors/registry/footnote-mismatch.js +3 -0
- package/dist/detectors/registry/footnote-mismatch.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/heading-end-punctuation.d.ts +3 -0
- package/dist/detectors/registry/heading-end-punctuation.d.ts.map +1 -0
- package/dist/detectors/registry/heading-end-punctuation.js +3 -0
- package/dist/detectors/registry/heading-end-punctuation.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/list-marker.d.ts +3 -0
- package/dist/detectors/registry/list-marker.d.ts.map +1 -0
- package/dist/detectors/registry/list-marker.js +3 -0
- package/dist/detectors/registry/list-marker.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/numeral-opener.d.ts +3 -0
- package/dist/detectors/registry/numeral-opener.d.ts.map +1 -0
- package/dist/detectors/registry/numeral-opener.js +3 -0
- package/dist/detectors/registry/numeral-opener.js.map +1 -0
- package/dist/detectors/registry/ordinal-suffix.d.ts +3 -0
- package/dist/detectors/registry/ordinal-suffix.d.ts.map +1 -0
- package/dist/detectors/registry/ordinal-suffix.js +3 -0
- package/dist/detectors/registry/ordinal-suffix.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/range-notation.d.ts +3 -0
- package/dist/detectors/registry/range-notation.d.ts.map +1 -0
- package/dist/detectors/registry/range-notation.js +3 -0
- package/dist/detectors/registry/range-notation.js.map +1 -0
- package/dist/detectors/registry/relative-pronoun-mix.d.ts +3 -0
- package/dist/detectors/registry/relative-pronoun-mix.d.ts.map +1 -0
- package/dist/detectors/registry/relative-pronoun-mix.js +3 -0
- package/dist/detectors/registry/relative-pronoun-mix.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/share-over-hundred.d.ts +3 -0
- package/dist/detectors/registry/share-over-hundred.d.ts.map +1 -0
- package/dist/detectors/registry/share-over-hundred.js +3 -0
- package/dist/detectors/registry/share-over-hundred.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-reference.d.ts +3 -0
- package/dist/detectors/registry/step-reference.d.ts.map +1 -0
- package/dist/detectors/registry/step-reference.js +3 -0
- package/dist/detectors/registry/step-reference.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/table-row-overflow.d.ts +3 -0
- package/dist/detectors/registry/table-row-overflow.d.ts.map +1 -0
- package/dist/detectors/registry/table-row-overflow.js +3 -0
- package/dist/detectors/registry/table-row-overflow.js.map +1 -0
- package/dist/detectors/registry/toc-heading.d.ts +3 -0
- package/dist/detectors/registry/toc-heading.d.ts.map +1 -0
- package/dist/detectors/registry/toc-heading.js +3 -0
- package/dist/detectors/registry/toc-heading.js.map +1 -0
- package/dist/detectors/registry/unclosed-code-fence.d.ts +3 -0
- package/dist/detectors/registry/unclosed-code-fence.d.ts.map +1 -0
- package/dist/detectors/registry/unclosed-code-fence.js +3 -0
- package/dist/detectors/registry/unclosed-code-fence.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/registry/unrendered-emphasis.d.ts +3 -0
- package/dist/detectors/registry/unrendered-emphasis.d.ts.map +1 -0
- package/dist/detectors/registry/unrendered-emphasis.js +3 -0
- package/dist/detectors/registry/unrendered-emphasis.js.map +1 -0
- package/dist/detectors/relative-pronoun-mix.d.ts +19 -0
- package/dist/detectors/relative-pronoun-mix.d.ts.map +1 -0
- package/dist/detectors/relative-pronoun-mix.js +60 -0
- package/dist/detectors/relative-pronoun-mix.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/share-over-hundred.d.ts +20 -0
- package/dist/detectors/share-over-hundred.d.ts.map +1 -0
- package/dist/detectors/share-over-hundred.js +55 -0
- package/dist/detectors/share-over-hundred.js.map +1 -0
- package/dist/detectors/step-reference.d.ts +20 -0
- package/dist/detectors/step-reference.d.ts.map +1 -0
- package/dist/detectors/step-reference.js +57 -0
- package/dist/detectors/step-reference.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/detectors/toc-heading.d.ts +10 -0
- package/dist/detectors/toc-heading.d.ts.map +1 -0
- package/dist/detectors/toc-heading.js +99 -0
- package/dist/detectors/toc-heading.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/list-marks.d.ts +21 -0
- package/dist/list-marks.d.ts.map +1 -0
- package/dist/list-marks.js +82 -0
- package/dist/list-marks.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 +28 -0
- package/dist/rule-fields.d.ts.map +1 -0
- package/dist/rule-fields.js +85 -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/cliche.yaml +67 -0
- 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/currency-notation-consistency.yaml +77 -0
- package/rules/cushion-phrase-density.yaml +8 -1
- package/rules/dangling-figure-reference.yaml +3 -1
- package/rules/dangling-participle.yaml +58 -0
- 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/footnote-mismatch.yaml +91 -0
- 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-end-punctuation-mix.yaml +158 -0
- 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/less-for-fewer.yaml +59 -0
- package/rules/link-text-url-mismatch.yaml +72 -0
- package/rules/list-item-form-mix.yaml +7 -1
- package/rules/list-marker-mix.yaml +111 -0
- 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/nonstandard-word.yaml +80 -0
- 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/numeral-sentence-start.yaml +56 -0
- package/rules/one-sentence-paragraph-run.yaml +5 -1
- package/rules/ordinal-suffix-mismatch.yaml +65 -0
- 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/range-notation-consistency.yaml +73 -0
- 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/share-over-hundred.yaml +65 -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-reference-missing.yaml +85 -0
- 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/table-row-overflow.yaml +90 -0
- 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/toc-heading-mismatch.yaml +136 -0
- 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/unclosed-code-fence.yaml +107 -0
- package/rules/uncomparable-graded.yaml +65 -0
- 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/unrendered-emphasis.yaml +73 -0
- 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/which-that-consistency.yaml +64 -0
- package/rules/wordy-phrase.yaml +6 -1
- package/rules/yori-as-from.yaml +61 -0
- package/skills/chaff/SKILL.md +7 -7
- package/src/api.ts +13 -1
- package/src/cli-text.ts +22 -7
- package/src/cli.ts +8 -9
- 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/commands/init-command.ts +13 -0
- package/src/commands/plugin-init.ts +150 -0
- package/src/commands/plugin-test.ts +111 -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 +16 -2
- package/src/detectors/amount-for-count.ts +90 -0
- 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/currency-notation.ts +90 -0
- package/src/detectors/custom.ts +10 -2
- package/src/detectors/dangling-opener.ts +74 -0
- package/src/detectors/defined-term-form.ts +200 -0
- package/src/detectors/email-letter.ts +262 -0
- package/src/detectors/footnote-mismatch.ts +72 -0
- package/src/detectors/form-minority.ts +21 -0
- package/src/detectors/heading-end-mark.ts +82 -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/list-marker.ts +60 -0
- package/src/detectors/mark-shape.ts +202 -0
- package/src/detectors/markdown-slip.ts +140 -0
- package/src/detectors/numeral-opener.ts +72 -0
- package/src/detectors/ordinal-suffix.ts +40 -0
- package/src/detectors/range-notation.ts +76 -0
- package/src/detectors/registry/adverb-polarity.ts +4 -0
- package/src/detectors/registry/amount-for-count.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/currency-notation.ts +4 -0
- package/src/detectors/registry/dangling-opener.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/footnote-mismatch.ts +4 -0
- package/src/detectors/registry/heading-command.ts +4 -0
- package/src/detectors/registry/heading-end-punctuation.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/list-marker.ts +4 -0
- package/src/detectors/registry/mark-consistency.ts +4 -0
- package/src/detectors/registry/numeral-opener.ts +4 -0
- package/src/detectors/registry/ordinal-suffix.ts +4 -0
- package/src/detectors/registry/origin-particle.ts +4 -0
- package/src/detectors/registry/range-notation.ts +4 -0
- package/src/detectors/registry/relative-pronoun-mix.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/share-over-hundred.ts +4 -0
- package/src/detectors/registry/shell-prompt.ts +4 -0
- package/src/detectors/registry/step-reference.ts +4 -0
- package/src/detectors/registry/step-statement.ts +4 -0
- package/src/detectors/registry/table-row-overflow.ts +4 -0
- package/src/detectors/registry/toc-heading.ts +4 -0
- package/src/detectors/registry/unclosed-code-fence.ts +4 -0
- package/src/detectors/registry/unpaired-parallel.ts +4 -0
- package/src/detectors/registry/unrendered-emphasis.ts +4 -0
- package/src/detectors/relative-pronoun-mix.ts +79 -0
- package/src/detectors/request-owner.ts +141 -0
- package/src/detectors/share-over-hundred.ts +75 -0
- package/src/detectors/step-reference.ts +83 -0
- package/src/detectors/technical-docs.ts +230 -0
- package/src/detectors/toc-heading.ts +125 -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/list-marks.ts +106 -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 +123 -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/custom/parse.ts
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
|
-
import type { CustomSpec,
|
|
1
|
+
import type { CustomSpec, Localized, RuleDefinition, TokenCondition } from "../plugin.ts";
|
|
2
2
|
import { regexRefusal, type RegexRefusal } from "./regex-safety.ts";
|
|
3
3
|
import { posTags } from "./token-pattern.ts";
|
|
4
4
|
import { modulePathOf, type ModulePathRefusal } from "./module-path.ts";
|
|
5
|
+
import type { FieldProblem } from "../rule-fields.ts";
|
|
6
|
+
import { failed, ok, type Checked } from "./checked.ts";
|
|
7
|
+
import { describedOf, exampleSides, levelsOf, type Described, type RuleLevels } from "./dsl-fields.ts";
|
|
5
8
|
|
|
6
9
|
// custom_rules in chaff.yaml: a team's own deterministic rules, written without code. Each becomes a RuleDefinition like the
|
|
7
10
|
// built-in ones, so findings, explain, rules --json, stet, the baseline and SARIF treat it the same. Pure: the YAML comes in parsed.
|
|
@@ -26,18 +29,25 @@ export type CustomProblem =
|
|
|
26
29
|
| { readonly kind: "bad-pattern"; readonly at: string; readonly refusal: RegexRefusal }
|
|
27
30
|
| { readonly kind: "no-tokens"; readonly at: string }
|
|
28
31
|
| { readonly kind: "bad-token"; readonly at: string; readonly index: number }
|
|
29
|
-
| { readonly kind: "unknown-pos"; readonly at: string; readonly written: string }
|
|
32
|
+
| { readonly kind: "unknown-pos"; readonly at: string; readonly written: string }
|
|
33
|
+
| { readonly kind: "level-and-levels"; readonly at: string }
|
|
34
|
+
| { readonly kind: "bad-levels"; readonly at: string; readonly written: string }
|
|
35
|
+
| (FieldProblem & { readonly at: string });
|
|
30
36
|
|
|
31
37
|
export type CustomRules = { readonly rules: readonly RuleDefinition[]; readonly problems: readonly CustomProblem[] };
|
|
32
38
|
|
|
33
39
|
/**
|
|
34
|
-
* What parsing needs to know: the built-in rule ids (a team's rule may not take one), the genres a rule applies to
|
|
35
|
-
* the folder a module's path is relative to (where chaff.yaml is).
|
|
40
|
+
* What parsing needs to know: the built-in rule ids (a team's rule may not take one), the genres a rule applies to when
|
|
41
|
+
* it does not say (useFor), every genre a use_for may name, and the folder a module's path is relative to (where chaff.yaml is).
|
|
36
42
|
*/
|
|
37
|
-
export type CustomContext = {
|
|
43
|
+
export type CustomContext = {
|
|
44
|
+
readonly builtIn: ReadonlySet<string>;
|
|
45
|
+
readonly useFor: readonly string[];
|
|
46
|
+
readonly genres: readonly string[];
|
|
47
|
+
readonly baseDir: string;
|
|
48
|
+
};
|
|
38
49
|
|
|
39
50
|
const RULE_ID = /^[a-z][a-z0-9-]*$/u;
|
|
40
|
-
const SEVERITIES: readonly Severity[] = ["info", "warning", "error"];
|
|
41
51
|
|
|
42
52
|
const isRecord = (value: unknown): value is Record<string, unknown> => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
43
53
|
|
|
@@ -58,12 +68,6 @@ const EVERY_LANGUAGE = "*";
|
|
|
58
68
|
|
|
59
69
|
const exampleSideOf = (value: unknown): Localized | undefined => (nonEmpty(value) ? { [EVERY_LANGUAGE]: value.trim() } : byLanguageOf(value));
|
|
60
70
|
|
|
61
|
-
/** One rule's own problems, with the rule's id (or its place in the list) as where. */
|
|
62
|
-
type Checked<T> = { readonly value: T | undefined; readonly problems: readonly CustomProblem[] };
|
|
63
|
-
|
|
64
|
-
const ok = <T>(value: T): Checked<T> => ({ value, problems: [] });
|
|
65
|
-
const failed = <T>(...problems: CustomProblem[]): Checked<T> => ({ value: undefined, problems });
|
|
66
|
-
|
|
67
71
|
type Pair = { readonly avoid: string; readonly use: string };
|
|
68
72
|
|
|
69
73
|
/** words as a map (avoid: use) or a list (words to point at, with nothing to use instead). */
|
|
@@ -125,7 +129,8 @@ const moduleOf = (raw: Record<string, unknown>, at: string, baseDir: string): Ch
|
|
|
125
129
|
};
|
|
126
130
|
|
|
127
131
|
const specOf = (raw: Record<string, unknown>, type: string, at: string, baseDir: string): Checked<CustomSpec> => {
|
|
128
|
-
|
|
132
|
+
// A words rule may take its words from a word list instead, one per language (a rule pack's lexicons/<language>/).
|
|
133
|
+
if (type === "words") return takesWordList(raw, type) ? ok({ type: "words", words: [] }) : wordsOf(raw["words"], at);
|
|
129
134
|
if (type === "pattern") return patternOf(raw, at);
|
|
130
135
|
if (type === "module") return moduleOf(raw, at, baseDir);
|
|
131
136
|
return tokensOf(raw["tokens"], at);
|
|
@@ -137,28 +142,16 @@ const typeOf = (raw: Record<string, unknown>, at: string): Checked<string> => {
|
|
|
137
142
|
return CUSTOM_TYPES.some((known) => known === type) ? ok(type) : failed({ kind: "unknown-type", at, written: type });
|
|
138
143
|
};
|
|
139
144
|
|
|
140
|
-
const severityOf = (raw: unknown, at: string): Checked<Severity> => {
|
|
141
|
-
if (raw === undefined) return ok("warning");
|
|
142
|
-
const severity = SEVERITIES.find((entry) => entry === raw);
|
|
143
|
-
return severity === undefined ? failed({ kind: "bad-level", at, written: typeof raw === "string" ? raw : printed(raw) }) : ok(severity);
|
|
144
|
-
};
|
|
145
|
-
|
|
146
|
-
/** The four words as severities: normal is the rule's own level, relaxed one lower, strict one higher, where there is one. */
|
|
147
|
-
const levelsFor = (severity: Severity): LevelTable => {
|
|
148
|
-
const rank = SEVERITIES.indexOf(severity) + 1;
|
|
149
|
-
return { ...(rank < SEVERITIES.length ? { strict: rank + 1 } : {}), normal: rank, ...(rank > 1 ? { relaxed: rank - 1 } : {}) };
|
|
150
|
-
};
|
|
151
|
-
|
|
152
145
|
type Texts = { readonly name: Localized; readonly why: Localized; readonly how_to_fix: Localized; readonly before: Localized; readonly after: Localized };
|
|
153
146
|
|
|
154
147
|
const textsOf = (raw: Record<string, unknown>, at: string): Checked<Texts> => {
|
|
155
|
-
const example =
|
|
148
|
+
const example = exampleSides(raw["example"]);
|
|
156
149
|
const found = {
|
|
157
150
|
name: localizedOf(raw["name"]),
|
|
158
151
|
why: localizedOf(raw["why"]),
|
|
159
152
|
how_to_fix: localizedOf(raw["how_to_fix"]),
|
|
160
|
-
before: exampleSideOf(example
|
|
161
|
-
after: exampleSideOf(example
|
|
153
|
+
before: exampleSideOf(example.before),
|
|
154
|
+
after: exampleSideOf(example.after),
|
|
162
155
|
};
|
|
163
156
|
const missing = Object.entries(found).flatMap(([field, text]) =>
|
|
164
157
|
text === undefined ? [{ kind: "missing" as const, at, field: field === "before" || field === "after" ? `example.${field}` : field }] : [],
|
|
@@ -179,9 +172,13 @@ const requiresOf = (raw: unknown, type: string, at: string): Checked<readonly st
|
|
|
179
172
|
return unknown === undefined ? ok([...new Set(written)]) : failed({ kind: "bad-requires", at, written: unknown });
|
|
180
173
|
};
|
|
181
174
|
|
|
182
|
-
/** A module
|
|
183
|
-
const
|
|
184
|
-
|
|
175
|
+
/** A module reads a word list; so does a words rule that writes word_list in place of words. The others carry their words. */
|
|
176
|
+
const takesWordList = (raw: Record<string, unknown>, type: string): boolean =>
|
|
177
|
+
raw["word_list"] !== undefined && (type === "module" || (type === "words" && raw["words"] === undefined));
|
|
178
|
+
|
|
179
|
+
/** The word list by name; the detector gets it, in the document's language, as options.lexicon. */
|
|
180
|
+
const wordListOf = (raw: unknown, takesList: boolean, at: string): Checked<string | undefined> => {
|
|
181
|
+
if (raw === undefined || !takesList) return ok(undefined);
|
|
185
182
|
return nonEmpty(raw) ? ok(raw.trim()) : failed({ kind: "bad-word-list", at, written: printed(raw) });
|
|
186
183
|
};
|
|
187
184
|
|
|
@@ -190,7 +187,7 @@ type Needs = { readonly requires: readonly string[]; readonly wordList: string |
|
|
|
190
187
|
|
|
191
188
|
const needsOf = (raw: Record<string, unknown>, type: string, at: string): Checked<Needs> => {
|
|
192
189
|
const requires = requiresOf(raw["requires"], type, at);
|
|
193
|
-
const wordList = wordListOf(raw["word_list"], type, at);
|
|
190
|
+
const wordList = wordListOf(raw["word_list"], takesWordList(raw, type), at);
|
|
194
191
|
if (requires.value === undefined || wordList.problems.length > 0) return failed(...requires.problems, ...wordList.problems);
|
|
195
192
|
return ok({ requires: requires.value, wordList: wordList.value });
|
|
196
193
|
};
|
|
@@ -226,10 +223,11 @@ type Example = { readonly before: string; readonly after: string };
|
|
|
226
223
|
type Parts = {
|
|
227
224
|
readonly spec: CustomSpec;
|
|
228
225
|
readonly needs: Needs;
|
|
229
|
-
readonly
|
|
226
|
+
readonly levels: RuleLevels;
|
|
230
227
|
readonly texts: Texts;
|
|
231
228
|
readonly languages: readonly string[] | undefined;
|
|
232
229
|
readonly examples: Readonly<Record<string, Example>>;
|
|
230
|
+
readonly described: Described;
|
|
233
231
|
};
|
|
234
232
|
|
|
235
233
|
const READER_LANGUAGES: readonly string[] = ["ja", "en"];
|
|
@@ -253,7 +251,7 @@ const definitionOf = (id: string, raw: Record<string, unknown>, parts: Parts, us
|
|
|
253
251
|
message: localizedOf(raw["message"]) ?? defaultMessage(parts.spec, parts.texts.name),
|
|
254
252
|
messages: {},
|
|
255
253
|
placeholders: PLACEHOLDERS,
|
|
256
|
-
levels:
|
|
254
|
+
levels: parts.levels.table,
|
|
257
255
|
level_sets: "severity",
|
|
258
256
|
by_genre: {},
|
|
259
257
|
how_to_find: HOW_TO_FIND[parts.spec.type],
|
|
@@ -266,18 +264,19 @@ const definitionOf = (id: string, raw: Record<string, unknown>, parts: Parts, us
|
|
|
266
264
|
uses: [],
|
|
267
265
|
from: [],
|
|
268
266
|
languages: parts.languages,
|
|
269
|
-
use_for: useFor,
|
|
270
|
-
severity: parts.severity,
|
|
267
|
+
use_for: parts.described.useFor ?? useFor,
|
|
268
|
+
severity: parts.levels.severity,
|
|
271
269
|
custom: parts.spec,
|
|
272
270
|
// The reference and explain read a rule's plain-language fields from guide; a team's rule is listed with the team's words.
|
|
273
271
|
guide: {
|
|
274
|
-
group:
|
|
275
|
-
summary: parts.texts.name,
|
|
272
|
+
group: parts.described.group,
|
|
273
|
+
summary: parts.described.summary ?? parts.texts.name,
|
|
276
274
|
examples: parts.examples,
|
|
277
275
|
notFlagged: {},
|
|
278
276
|
levelMeaning: {},
|
|
279
277
|
sources: [],
|
|
280
|
-
rewrite:
|
|
278
|
+
rewrite: parts.described.rewrite,
|
|
279
|
+
rewriteDepth: parts.described.rewriteDepth,
|
|
281
280
|
},
|
|
282
281
|
});
|
|
283
282
|
|
|
@@ -289,24 +288,47 @@ const idOf = (raw: Record<string, unknown>, index: number, seen: ReadonlySet<str
|
|
|
289
288
|
return seen.has(id) ? failed({ kind: "duplicate-id", at }) : ok(id);
|
|
290
289
|
};
|
|
291
290
|
|
|
291
|
+
/** Each field of one rule, checked. */
|
|
292
|
+
type Checks = {
|
|
293
|
+
readonly id: Checked<string>;
|
|
294
|
+
readonly spec: Checked<CustomSpec>;
|
|
295
|
+
readonly needs: Checked<Needs>;
|
|
296
|
+
readonly levels: Checked<RuleLevels>;
|
|
297
|
+
readonly texts: Checked<Texts>;
|
|
298
|
+
readonly languages: Checked<readonly string[] | undefined>;
|
|
299
|
+
readonly described: Checked<Described>;
|
|
300
|
+
};
|
|
301
|
+
|
|
302
|
+
/** The rule's parts once every check passed; undefined when one has no value. */
|
|
303
|
+
const partsOf = (checks: Checks, examples: Readonly<Record<string, Example>>): { readonly id: string; readonly parts: Parts } | undefined => {
|
|
304
|
+
const { id, spec, needs, levels, texts, described } = checks;
|
|
305
|
+
if (id.value === undefined || spec.value === undefined || needs.value === undefined) return undefined;
|
|
306
|
+
if (levels.value === undefined || texts.value === undefined || described.value === undefined) return undefined;
|
|
307
|
+
const parts = { spec: spec.value, needs: needs.value, levels: levels.value, texts: texts.value, languages: checks.languages.value, examples };
|
|
308
|
+
return { id: id.value, parts: { ...parts, described: described.value } };
|
|
309
|
+
};
|
|
310
|
+
|
|
292
311
|
const ruleOf = (raw: unknown, index: number, seen: ReadonlySet<string>, context: CustomContext): Checked<RuleDefinition> => {
|
|
293
312
|
if (!isRecord(raw)) return failed({ kind: "not-a-map", at: `#${String(index + 1)}` });
|
|
294
313
|
const id = idOf(raw, index, seen, context.builtIn);
|
|
295
314
|
const at = id.value ?? (nonEmpty(raw["id"]) ? raw["id"] : `#${String(index + 1)}`);
|
|
296
315
|
const type = typeOf(raw, at);
|
|
297
|
-
const
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
316
|
+
const checks: Checks = {
|
|
317
|
+
id,
|
|
318
|
+
spec: type.value === undefined ? failed<CustomSpec>() : specOf(raw, type.value, at, context.baseDir),
|
|
319
|
+
needs: needsOf(raw, type.value ?? "", at),
|
|
320
|
+
levels: levelsOf(raw, at),
|
|
321
|
+
texts: textsOf(raw, at),
|
|
322
|
+
languages: languagesOf(raw["languages"], at),
|
|
323
|
+
described: describedOf(raw, at, context.genres),
|
|
324
|
+
};
|
|
325
|
+
const { texts, languages } = checks;
|
|
302
326
|
const examples = texts.value === undefined ? {} : examplesOf(texts.value, languages.value);
|
|
303
327
|
const unpaired: CustomProblem[] = texts.value !== undefined && Object.keys(examples).length === 0 ? [{ kind: "unpaired-example", at }] : [];
|
|
304
|
-
const
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
const parts = { spec: spec.value, needs: needs.value, severity: severity.value, texts: texts.value, languages: languages.value, examples };
|
|
309
|
-
return ok(definitionOf(id.value, raw, parts, context.useFor));
|
|
328
|
+
const ordered: Checked<unknown>[] = [id, type, checks.spec, checks.needs, checks.levels, texts, languages, checks.described];
|
|
329
|
+
const problems = [...ordered.flatMap((checked) => checked.problems), ...unpaired];
|
|
330
|
+
const ready = problems.length > 0 ? undefined : partsOf(checks, examples);
|
|
331
|
+
return ready === undefined ? failed(...problems) : ok(definitionOf(ready.id, raw, ready.parts, context.useFor));
|
|
310
332
|
};
|
|
311
333
|
|
|
312
334
|
/** custom_rules as written. Every problem is reported; a rule with a problem is left out, and the run is stopped by the caller. */
|
package/src/custom/problems.ts
CHANGED
|
@@ -5,10 +5,13 @@ import { customRulesOf } from "./load.ts";
|
|
|
5
5
|
import { MAX_PATTERN_LENGTH, MAX_REPEATS, type RegexRefusal } from "./regex-safety.ts";
|
|
6
6
|
import { POS_WRITTEN_NAMES } from "./token-pattern.ts";
|
|
7
7
|
import type { ModulePathRefusal } from "./module-path.ts";
|
|
8
|
+
import { FIELD_KINDS, fieldProblemSentence, type FieldKind, type FieldProblem } from "../rule-fields.ts";
|
|
8
9
|
|
|
9
10
|
/** Each problem's sentence, with {at}, {written}, {field}, {index}, {refusal} and {names} filled in from the problem. */
|
|
10
11
|
type Text = {
|
|
11
|
-
readonly problems: Readonly<Record<CustomProblem["kind"], string>>;
|
|
12
|
+
readonly problems: Readonly<Record<Exclude<CustomProblem["kind"], FieldKind>, string>>;
|
|
13
|
+
/** What comes before a shared field's own sentence (rule-fields.ts). */
|
|
14
|
+
readonly fieldAt: string;
|
|
12
15
|
readonly refusal: Readonly<Record<RegexRefusal, string>>;
|
|
13
16
|
readonly modulePath: Readonly<Record<ModulePathRefusal, string>>;
|
|
14
17
|
};
|
|
@@ -45,7 +48,11 @@ const TEXT: Texts<Text> = {
|
|
|
45
48
|
"bad-token": "{scope} の {at}: tokens の {index} 番目に pos・base・surface のどれもありません",
|
|
46
49
|
"unknown-pos": "{scope} の {at}: 品詞 {written} は知りません({names})",
|
|
47
50
|
"bad-pattern": "{scope} の {at}: pattern を使えません。{refusal}",
|
|
51
|
+
"level-and-levels": "{scope} の {at}: level と levels の両方があります。どちらか一つにしてください",
|
|
52
|
+
"bad-levels":
|
|
53
|
+
"{scope} の {at}: levels: {written} は読めません。strict・normal・relaxed に重さ(error / warning / info)を書き、normal は必ず書きます。チームのルールは箇所を出すので、数は書けません",
|
|
48
54
|
},
|
|
55
|
+
fieldAt: "{scope} の {at}: ",
|
|
49
56
|
},
|
|
50
57
|
en: {
|
|
51
58
|
modulePath: {
|
|
@@ -78,7 +85,11 @@ const TEXT: Texts<Text> = {
|
|
|
78
85
|
"bad-token": "{scope} {at}: token {index} has none of pos, base and surface",
|
|
79
86
|
"unknown-pos": "{scope} {at}: unknown part of speech {written} ({names})",
|
|
80
87
|
"bad-pattern": "{scope} {at}: the pattern cannot be used: {refusal}",
|
|
88
|
+
"level-and-levels": "{scope} {at}: has both level and levels; keep one",
|
|
89
|
+
"bad-levels":
|
|
90
|
+
"{scope} {at}: cannot read levels: {written}. Give strict, normal and relaxed a severity (error / warning / info), normal always. A team's rule reports places, so a number has nothing to count",
|
|
81
91
|
},
|
|
92
|
+
fieldAt: "{scope} {at}: ",
|
|
82
93
|
},
|
|
83
94
|
};
|
|
84
95
|
|
|
@@ -100,10 +111,13 @@ const valuesOf = (problem: CustomProblem, text: Text, scope: string): Readonly<R
|
|
|
100
111
|
names: POS_WRITTEN_NAMES.join(" / "),
|
|
101
112
|
});
|
|
102
113
|
|
|
114
|
+
const isFieldProblem = (problem: CustomProblem): problem is FieldProblem & { readonly at: string } => FIELD_KINDS.has(problem.kind);
|
|
115
|
+
|
|
103
116
|
/** One problem as a sentence. scope names the list the rule is in: custom_rules, or a plugin's rules. */
|
|
104
117
|
export const customProblemSentence = (problem: CustomProblem, ui: UiLanguage, scope = "custom_rules"): string => {
|
|
105
118
|
const values = valuesOf(problem, TEXT[ui], scope);
|
|
106
|
-
|
|
119
|
+
const fill = (template: string): string => template.replace(PLACEHOLDER, (_whole, key: string) => values[key] ?? "");
|
|
120
|
+
return isFieldProblem(problem) ? `${fill(TEXT[ui].fieldAt)}${fieldProblemSentence(problem, ui)}` : fill(TEXT[ui].problems[problem.kind]);
|
|
107
121
|
};
|
|
108
122
|
|
|
109
123
|
/** What in custom_rules cannot run. Each stops the run: a team's rule that silently does not run looks like a clean document. */
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import type { Detector, Finding, Lexicon, Token } from "../plugin.ts";
|
|
2
|
+
import { endsNounPhrase } from "./agreement-slip.ts";
|
|
3
|
+
import { isPartOfLongerWord } from "./doubled-word.ts";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* 量を言う語を、数えられる名詞の複数に付けた所(less errors、less customer complaints)。語は語彙表 amount-quantifier が言う。
|
|
7
|
+
* 前が名詞か数なら「引く」の less(revenue less costs、$500 less fees)なので読まない。
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
export type AmountLists = {
|
|
11
|
+
/** 量を言う語(less)と、数を言う語(fewer, rewrite)。 */
|
|
12
|
+
readonly quantifiers: Lexicon;
|
|
13
|
+
/** 複数の形でも数えない名詞。単数と複数が同じ綴りの語(series)と、金額を言う複数(earnings)。 */
|
|
14
|
+
readonly uncounted: ReadonlySet<string>;
|
|
15
|
+
/** 名詞にならない be / have の形。名詞句がそこで終わる。 */
|
|
16
|
+
readonly finite: ReadonlySet<string>;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export type AmountSlip = { readonly quantifier: Token; readonly noun: Token; readonly rewrite: string };
|
|
20
|
+
|
|
21
|
+
const UPPER_START = /^\p{Lu}/u;
|
|
22
|
+
|
|
23
|
+
/** 前に立てば、量の語が限定詞として読める品詞。名詞・数・記号の後ろは「引く」の less。 */
|
|
24
|
+
const OPENS_PHRASE = new Set(["VERB", "AUX", "ADP", "CCONJ", "SCONJ", "ADV", "PART"]);
|
|
25
|
+
const OPENING_MARK = new Set(["(", "[", "{", '"', "'", "“", "‘"]);
|
|
26
|
+
|
|
27
|
+
const lower = (token: Token): string => token.surface.toLowerCase();
|
|
28
|
+
|
|
29
|
+
/** 解析器は三人称の動詞を複数の名詞と読むことがある(The parser reports less errors の reports)。 */
|
|
30
|
+
const mayBeVerb = (token: Token): boolean => token.pos === "NOUN" && token.features?.["AlsoVerb"] === "Yes" && token.features["Number"] === "Plur";
|
|
31
|
+
|
|
32
|
+
const opensPhrase = (previous: Token | undefined): boolean =>
|
|
33
|
+
previous === undefined || OPENS_PHRASE.has(previous.pos) || OPENING_MARK.has(previous.surface) || mayBeVerb(previous);
|
|
34
|
+
|
|
35
|
+
const isCommonNoun = (token: Token | undefined): token is Token =>
|
|
36
|
+
token?.pos === "NOUN" && token.features?.["Guess"] !== "Yes" && !UPPER_START.test(token.surface);
|
|
37
|
+
|
|
38
|
+
const isPlural = (token: Token): boolean => token.features?.["Number"] === "Plur";
|
|
39
|
+
|
|
40
|
+
/** 複数の形がはっきりした、数えられる普通名詞。形から当てた語(stimuli)と、数えない語は読まない。 */
|
|
41
|
+
const isCountedPlural = (noun: Token | undefined, uncounted: ReadonlySet<string>): noun is Token =>
|
|
42
|
+
isCommonNoun(noun) && isPlural(noun) && !uncounted.has(lower(noun));
|
|
43
|
+
|
|
44
|
+
/** less の後ろの名詞の頭。単数の名詞を一つ重ねた語(less customer complaints)は、その後ろの語。 */
|
|
45
|
+
const headAt = (tokens: readonly Token[], at: number): number => {
|
|
46
|
+
const next = tokens[at + 1];
|
|
47
|
+
return isCommonNoun(next) && !isPlural(next) ? at + 2 : at + 1;
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const slipAt = (source: string, tokens: readonly Token[], at: number, lists: AmountLists): AmountSlip | undefined => {
|
|
51
|
+
const quantifier = tokens[at];
|
|
52
|
+
const entry = quantifier === undefined ? undefined : lists.quantifiers.find((candidate) => candidate.pattern.toLowerCase() === lower(quantifier));
|
|
53
|
+
if (quantifier?.pos !== "ADJ" || entry === undefined || !opensPhrase(tokens[at - 1])) return undefined;
|
|
54
|
+
const head = headAt(tokens, at);
|
|
55
|
+
const noun = tokens[head];
|
|
56
|
+
if (!isCountedPlural(noun, lists.uncounted) || !endsNounPhrase(tokens[head + 1], lists.finite) || isPartOfLongerWord(source, quantifier, noun))
|
|
57
|
+
return undefined;
|
|
58
|
+
return { quantifier, noun, rewrite: entry.rewrite ?? "" };
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
export const amountSlipsIn = (source: string, tokens: readonly Token[], lists: AmountLists): AmountSlip[] =>
|
|
62
|
+
tokens.flatMap((_, at) => slipAt(source, tokens, at, lists) ?? []);
|
|
63
|
+
|
|
64
|
+
const patternsOf = (lexicon: Lexicon): string[] => lexicon.map((entry) => entry.pattern.toLowerCase());
|
|
65
|
+
|
|
66
|
+
const listsOf = (lexicons: Readonly<Record<string, Lexicon>>): AmountLists => {
|
|
67
|
+
const words = lexicons["amount-quantifier"] ?? [];
|
|
68
|
+
return {
|
|
69
|
+
quantifiers: words.filter((entry) => entry.group === "quantifier"),
|
|
70
|
+
uncounted: new Set([...patternsOf(lexicons["invariant-noun"] ?? []), ...patternsOf(words.filter((entry) => entry.group === "amount"))]),
|
|
71
|
+
finite: new Set(patternsOf(lexicons["finite-auxiliary"] ?? [])),
|
|
72
|
+
};
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
export const amountForCount: Detector = (doc): Finding[] => {
|
|
76
|
+
const lists = listsOf(doc.lexicons);
|
|
77
|
+
return doc.sentences.flatMap((sentence) =>
|
|
78
|
+
amountSlipsIn(doc.source, sentence.tokens ?? [], lists).map((slip): Finding => {
|
|
79
|
+
const rest = doc.source.slice(slip.quantifier.span.end, slip.noun.span.end).replace(/\s+/gu, " ");
|
|
80
|
+
return {
|
|
81
|
+
rule: "",
|
|
82
|
+
severity: "warning",
|
|
83
|
+
line: 0,
|
|
84
|
+
column: 0,
|
|
85
|
+
quote: sentence.text.trim(),
|
|
86
|
+
values: { word: `${slip.quantifier.surface}${rest}`, suggestion: `${slip.rewrite}${rest}`, offset: slip.quantifier.span.start },
|
|
87
|
+
};
|
|
88
|
+
}),
|
|
89
|
+
);
|
|
90
|
+
};
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import type { Detector, Finding, ProseDocument, Span } from "../plugin.ts";
|
|
4
4
|
import { readMarkdown } from "../markdown-read.ts";
|
|
5
5
|
import { spanOf, type MarkdownNode } from "../markdown-node.ts";
|
|
6
|
-
import { eachPreOrder } from "../tree-walk.ts";
|
|
6
|
+
import { eachPreOrder, foldPostOrder } from "../tree-walk.ts";
|
|
7
7
|
import { quoteAt } from "./structure-tree.ts";
|
|
8
8
|
|
|
9
9
|
/** Fewer body rows than this, and one blank is as likely a choice as a gap. */
|
|
@@ -21,14 +21,22 @@ const EMPHASIS = /[*_]/gu;
|
|
|
21
21
|
const COMMENT = /^<!--[\s\S]*-->$/u;
|
|
22
22
|
const VISIBLE_LEAVES = new Set(["inlineCode", "image", "imageReference"]);
|
|
23
23
|
|
|
24
|
-
/**
|
|
25
|
-
const
|
|
24
|
+
/** What a leaf shows on its own; undefined for a node whose children decide. */
|
|
25
|
+
const leafShows = (node: MarkdownNode): boolean | undefined => {
|
|
26
26
|
if (node.type === "text") return (node.value ?? "").replace(EMPHASIS, "").trim() !== "";
|
|
27
27
|
if (node.type === "html") return !COMMENT.test((node.value ?? "").trim());
|
|
28
28
|
if (VISIBLE_LEAVES.has(node.type)) return true;
|
|
29
|
-
return
|
|
29
|
+
return undefined;
|
|
30
30
|
};
|
|
31
31
|
|
|
32
|
+
/** Whether a node shows the reader anything: text that is not blank, code, an image, or HTML that is not a comment. */
|
|
33
|
+
const shows = (node: MarkdownNode): boolean =>
|
|
34
|
+
foldPostOrder<MarkdownNode, boolean>(
|
|
35
|
+
node,
|
|
36
|
+
(each) => each.children ?? [],
|
|
37
|
+
(each, children) => leafShows(each) ?? children.some((child) => child),
|
|
38
|
+
);
|
|
39
|
+
|
|
32
40
|
const sourceOf = (source: string, node: MarkdownNode | undefined): string => {
|
|
33
41
|
const span = node === undefined ? undefined : spanOf(node);
|
|
34
42
|
return span === undefined ? "" : source.slice(span.start, span.end).replace(/\|/gu, "").trim();
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import type { Detector, DetectorOptions, Finding, Lexicon, ProseDocument, Sentence, Token } from "../plugin.ts";
|
|
2
|
+
import { isWithinAny, quotedIn } from "../quoted-span.ts";
|
|
3
|
+
|
|
4
|
+
// The shape of a clause read from its words: how it ends, a paired word left alone, an adverb that needs a
|
|
5
|
+
// negation, a particle used where another one belongs. Every word comes from a lexicon; this file knows parts of speech.
|
|
6
|
+
|
|
7
|
+
const findingAt = (sentence: Sentence, offset: number, values: Readonly<Record<string, string | number>>): Finding => ({
|
|
8
|
+
rule: "",
|
|
9
|
+
severity: "info",
|
|
10
|
+
line: 0,
|
|
11
|
+
column: 0,
|
|
12
|
+
quote: sentence.text.trim(),
|
|
13
|
+
values: { ...values, offset },
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
const patternsOf = (lexicon: Lexicon | undefined, group?: string): ReadonlySet<string> =>
|
|
17
|
+
new Set((lexicon ?? []).filter((entry) => group === undefined || entry.group === group).map((entry) => entry.pattern));
|
|
18
|
+
|
|
19
|
+
const isSpace = (token: Token | undefined): boolean => token !== undefined && token.surface.trim() === "";
|
|
20
|
+
|
|
21
|
+
/** The tokens of a sentence outside anything quoted in 「」『』, with their index in the sentence. */
|
|
22
|
+
const ownTokens = (sentence: Sentence): { readonly token: Token; readonly at: number }[] => {
|
|
23
|
+
const quoted = quotedIn(sentence);
|
|
24
|
+
return (sentence.tokens ?? []).flatMap((token, at) => (isWithinAny(quoted, token.span) ? [] : [{ token, at }]));
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const wordOf = (token: Token): string => token.lemma ?? token.surface;
|
|
28
|
+
|
|
29
|
+
const isListed = (words: ReadonlySet<string>, token: Token | undefined): boolean =>
|
|
30
|
+
token !== undefined && (words.has(token.surface) || words.has(wordOf(token)));
|
|
31
|
+
|
|
32
|
+
// --- ending-run: sentences in a row that end with the same words ---------------------------------------------
|
|
33
|
+
|
|
34
|
+
const TRAILING_POS: ReadonlySet<string> = new Set(["PUNCT", "SYM"]);
|
|
35
|
+
const TAIL_POS: ReadonlySet<string> = new Set(["AUX", "PART"]);
|
|
36
|
+
const VERBAL_POS: ReadonlySet<string> = new Set(["VERB"]);
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* How a sentence ends: its closing auxiliaries, and the verb before them as written (しました, あります, 思います).
|
|
40
|
+
* A noun before them is left out, so 必要です and 重要です end the same way. No auxiliary, no ending to compare.
|
|
41
|
+
*/
|
|
42
|
+
export const endingOf = (sentence: Sentence): string | undefined => {
|
|
43
|
+
const tokens = (sentence.tokens ?? []).filter((token) => !isSpace(token));
|
|
44
|
+
const last = tokens.findLastIndex((token) => !TRAILING_POS.has(token.pos));
|
|
45
|
+
const head = tokens.slice(0, last + 1).findLastIndex((token) => !TAIL_POS.has(token.pos));
|
|
46
|
+
if (head === last) return undefined;
|
|
47
|
+
const headToken = tokens[head];
|
|
48
|
+
const tail = tokens.slice(head + 1, last + 1).map((token) => token.surface);
|
|
49
|
+
return [headToken !== undefined && VERBAL_POS.has(headToken.pos) ? headToken.surface : "", ...tail].join("");
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
type Ended = { readonly sentence: Sentence; readonly ending: string | undefined };
|
|
53
|
+
|
|
54
|
+
/** Runs of consecutive sentences with the same ending. A sentence with no ending to compare closes a run. */
|
|
55
|
+
export const endingRuns = (sentences: readonly Sentence[]): Ended[][] =>
|
|
56
|
+
sentences
|
|
57
|
+
.map((sentence) => ({ sentence, ending: endingOf(sentence) }))
|
|
58
|
+
.reduce<Ended[][]>((runs, ended) => {
|
|
59
|
+
const last = runs.at(-1) ?? [];
|
|
60
|
+
const continues = ended.ending !== undefined && last[0]?.ending === ended.ending;
|
|
61
|
+
return continues ? [...runs.slice(0, -1), [...last, ended]] : [...runs, [ended]];
|
|
62
|
+
}, [])
|
|
63
|
+
.filter((run) => run[0]?.ending !== undefined);
|
|
64
|
+
|
|
65
|
+
const runFinding = (run: readonly Ended[], limit: number): Finding => {
|
|
66
|
+
const first = run[0];
|
|
67
|
+
return {
|
|
68
|
+
rule: "",
|
|
69
|
+
severity: "info",
|
|
70
|
+
line: 0,
|
|
71
|
+
column: 0,
|
|
72
|
+
quote: run.map(({ sentence }) => sentence.text.trim()).join(""),
|
|
73
|
+
values: { word: first?.ending ?? "", count: run.length, limit, offset: first?.sentence.span.start ?? 0 },
|
|
74
|
+
};
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
/** More than limit sentences in a row within one paragraph that end with the same words. */
|
|
78
|
+
export const endingRun: Detector = (doc, options): Finding[] =>
|
|
79
|
+
doc.paragraphs.flatMap((paragraph) =>
|
|
80
|
+
endingRuns(paragraph.sentences)
|
|
81
|
+
.filter((run) => run.length > options.limit)
|
|
82
|
+
.map((run) => runFinding(run, options.limit)),
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
// --- unpaired-parallel: a word that lists by pairs (〜たり) used once, followed by another clause -----------------
|
|
86
|
+
|
|
87
|
+
const CONTENT_POS: ReadonlySet<string> = new Set(["VERB", "ADJ", "NOUN", "PROPN"]);
|
|
88
|
+
|
|
89
|
+
const nextWord = (tokens: readonly Token[], at: number): Token | undefined => tokens.slice(at + 1).find((token) => !isSpace(token));
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* The marker is left alone when the sentence holds it once and what follows starts a new clause: a comma,
|
|
93
|
+
* or a content word other than the lexicon's closer (〜たり、映画を見る / 〜たり見る). 〜たりする and 〜たりもする are whole.
|
|
94
|
+
*/
|
|
95
|
+
const leftAlone = (tokens: readonly Token[], at: number, closers: ReadonlySet<string>, commas: ReadonlySet<string>): boolean => {
|
|
96
|
+
const next = nextWord(tokens, at);
|
|
97
|
+
if (next === undefined) return false;
|
|
98
|
+
if (commas.has(next.surface)) return true;
|
|
99
|
+
return CONTENT_POS.has(next.pos) && !isListed(closers, next);
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
const CONJUNCTION = "CCONJ";
|
|
103
|
+
|
|
104
|
+
const markersIn = (sentence: Sentence, markers: ReadonlySet<string>): { readonly token: Token; readonly at: number }[] =>
|
|
105
|
+
ownTokens(sentence).filter(({ token }) => token.pos === CONJUNCTION && markers.has(token.surface));
|
|
106
|
+
|
|
107
|
+
/** A sentence that uses the lexicon's paired marker once and goes on without its partner. */
|
|
108
|
+
export const unpairedParallel: Detector = (doc, options): Finding[] => {
|
|
109
|
+
const markers = patternsOf(options.lexicon, "marker");
|
|
110
|
+
const closers = patternsOf(options.lexicon, "closer");
|
|
111
|
+
const commas = patternsOf(options.lexicon, "comma");
|
|
112
|
+
return doc.sentences.flatMap((sentence) => {
|
|
113
|
+
const found = markersIn(sentence, markers);
|
|
114
|
+
const only = found.length === 1 ? found[0] : undefined;
|
|
115
|
+
if (only === undefined || !leftAlone(sentence.tokens ?? [], only.at, closers, commas)) return [];
|
|
116
|
+
return [findingAt(sentence, only.token.span.start, { word: only.token.surface })];
|
|
117
|
+
});
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
// --- adverb-polarity: an adverb that wants a negation, in a sentence that has none ------------------------------
|
|
121
|
+
|
|
122
|
+
type Negations = { readonly words: ReadonlySet<string>; readonly prefixes: readonly string[] };
|
|
123
|
+
|
|
124
|
+
const negationsOf = (lexicon: Lexicon | undefined): Negations => ({
|
|
125
|
+
words: new Set((lexicon ?? []).filter((entry) => entry.group === undefined).map((entry) => entry.pattern)),
|
|
126
|
+
prefixes: [...patternsOf(lexicon, "prefix")],
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
/** A negating word (ない, 違う) or a word opening with a negating prefix (不十分, 無理). */
|
|
130
|
+
const isNegation = (token: Token, negations: Negations): boolean =>
|
|
131
|
+
isListed(negations.words, token) || negations.prefixes.some((prefix) => token.surface.length > prefix.length && token.surface.startsWith(prefix));
|
|
132
|
+
|
|
133
|
+
type Placed = { readonly token: Token; readonly at: number };
|
|
134
|
+
|
|
135
|
+
/** A negation of the writer's own after the adverb; one inside a quotation does not answer it. */
|
|
136
|
+
const hasNegation = (own: readonly Placed[], from: number, negations: Negations): boolean =>
|
|
137
|
+
own.some(({ token, at }) => at > from && isNegation(token, negations));
|
|
138
|
+
|
|
139
|
+
/** Each of the lexicon's adverbs (全然, 決して) with no word of the negation list after it in its sentence. */
|
|
140
|
+
export const adverbPolarity: Detector = (doc, options): Finding[] => {
|
|
141
|
+
const adverbs = patternsOf(options.lexicon);
|
|
142
|
+
const negations = negationsOf(doc.lexicons["negation-word"]);
|
|
143
|
+
return doc.sentences.flatMap((sentence) => {
|
|
144
|
+
const own = ownTokens(sentence);
|
|
145
|
+
return own
|
|
146
|
+
.filter(({ token, at }) => adverbs.has(token.surface) && !hasNegation(own, at, negations))
|
|
147
|
+
.map(({ token }) => findingAt(sentence, token.span.start, { word: token.surface }));
|
|
148
|
+
});
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
// --- origin-particle: a comparison particle (より) used for "from" -------------------------------------------------
|
|
152
|
+
|
|
153
|
+
/** Adverbs (順次) and punctuation (本日より、発送) stand between the particle and its head. */
|
|
154
|
+
const SKIPPED_POS: ReadonlySet<string> = new Set(["ADV", "PUNCT"]);
|
|
155
|
+
const PARTICLE = "ADP";
|
|
156
|
+
const NOMINAL_POS: ReadonlySet<string> = new Set(["NOUN", "PROPN"]);
|
|
157
|
+
|
|
158
|
+
/** The first word after the particle, past adverbs (順次) and a bound honorific prefix (ご連絡). */
|
|
159
|
+
const headAfter = (tokens: readonly Token[], at: number): number =>
|
|
160
|
+
tokens.findIndex((token, index) => index > at && !isSpace(token) && !SKIPPED_POS.has(token.pos) && token.features?.["Bound"] !== "Yes");
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* The head names a start or a sending (開始, 参る, 連絡). A noun head must be the action itself: followed by an object
|
|
164
|
+
* marker or a light verb (受付を開始, 開始します), not a noun being compared (昨年より販売が増えた).
|
|
165
|
+
*/
|
|
166
|
+
const isOriginHead = (tokens: readonly Token[], head: number, heads: ReadonlySet<string>, objectMarkers: ReadonlySet<string>): boolean => {
|
|
167
|
+
const token = tokens[head];
|
|
168
|
+
if (token === undefined || !isListed(heads, token)) return false;
|
|
169
|
+
if (!NOMINAL_POS.has(token.pos)) return true;
|
|
170
|
+
const next = nextWord(tokens, head);
|
|
171
|
+
return next !== undefined && (objectMarkers.has(next.surface) || next.features?.["VerbType"] === "Light");
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
type ParticleWords = { readonly particles: ReadonlySet<string>; readonly heads: ReadonlySet<string>; readonly objectMarkers: ReadonlySet<string> };
|
|
175
|
+
|
|
176
|
+
const originsIn = (sentence: Sentence, words: ParticleWords): Token[] => {
|
|
177
|
+
const tokens = sentence.tokens ?? [];
|
|
178
|
+
return ownTokens(sentence)
|
|
179
|
+
.filter(
|
|
180
|
+
({ token, at }) =>
|
|
181
|
+
token.pos === PARTICLE && words.particles.has(token.surface) && isOriginHead(tokens, headAfter(tokens, at), words.heads, words.objectMarkers),
|
|
182
|
+
)
|
|
183
|
+
.map(({ token }) => token);
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
const particleWords = (doc: ProseDocument, options: DetectorOptions): ParticleWords => ({
|
|
187
|
+
particles: patternsOf(options.lexicon),
|
|
188
|
+
heads: patternsOf(doc.lexicons["origin-head"], "head"),
|
|
189
|
+
objectMarkers: patternsOf(doc.lexicons["origin-head"], "object"),
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
/** The lexicon's particle where it marks a starting point (4月1日より開始), which the lexicon's rewrite says instead. */
|
|
193
|
+
export const originParticle: Detector = (doc, options): Finding[] => {
|
|
194
|
+
const words = particleWords(doc, options);
|
|
195
|
+
const rewrites = new Map((options.lexicon ?? []).map((entry) => [entry.pattern, entry.rewrite ?? ""]));
|
|
196
|
+
return doc.sentences.flatMap((sentence) =>
|
|
197
|
+
originsIn(sentence, words).map((token) => findingAt(sentence, token.span.start, { word: token.surface, suggestion: rewrites.get(token.surface) ?? "" })),
|
|
198
|
+
);
|
|
199
|
+
};
|