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
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cross-facts.js","sourceRoot":"","sources":["../../src/detectors/cross-facts.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cross-doc-duplicate-definition.d.ts","sourceRoot":"","sources":["../../../src/detectors/cross-registry/cross-doc-duplicate-definition.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAGrD,eAAO,MAAM,QAAQ,EAAE,aAA2C,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cross-doc-duplicate-definition.js","sourceRoot":"","sources":["../../../src/detectors/cross-registry/cross-doc-duplicate-definition.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,2BAA2B,EAAE,MAAM,yBAAyB,CAAC;AAEtE,MAAM,CAAC,MAAM,QAAQ,GAAkB,2BAA2B,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cross-doc-fact-conflict.d.ts","sourceRoot":"","sources":["../../../src/detectors/cross-registry/cross-doc-fact-conflict.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAGrD,eAAO,MAAM,QAAQ,EAAE,aAAoC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cross-doc-fact-conflict.js","sourceRoot":"","sources":["../../../src/detectors/cross-registry/cross-doc-fact-conflict.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAEzD,MAAM,CAAC,MAAM,QAAQ,GAAkB,oBAAoB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"custom.d.ts","sourceRoot":"","sources":["../../src/detectors/custom.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,
|
|
1
|
+
{"version":3,"file":"custom.d.ts","sourceRoot":"","sources":["../../src/detectors/custom.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAA4C,MAAM,cAAc,CAAC;AAoDvF,wGAAwG;AACxG,eAAO,MAAM,WAAW,EAAE,QAUzB,CAAC;AAEF,uIAAuI;AACvI,eAAO,MAAM,aAAa,EAAE,QAc3B,CAAC;AAEF,kJAAkJ;AAClJ,eAAO,MAAM,YAAY,EAAE,QAa1B,CAAC"}
|
package/dist/detectors/custom.js
CHANGED
|
@@ -27,14 +27,17 @@ const withoutBreaks = (source, start, end, breaks) => breaks
|
|
|
27
27
|
.filter((span) => span.start >= start && span.end <= end)
|
|
28
28
|
.toReversed()
|
|
29
29
|
.reduce((text, span) => `${text.slice(0, span.start - start)}${text.slice(span.end - start)}`, source.slice(start, end));
|
|
30
|
+
/** A words rule's own words, or its word list's in the document's language: each entry's pattern, and its rewrite as the spelling to use. */
|
|
31
|
+
const pairsOf = (spec, options) => spec.words.length > 0 ? spec.words : (options.lexicon ?? []).map((entry) => ({ avoid: entry.pattern, use: entry.rewrite ?? "" }));
|
|
30
32
|
/** words: each spelling to avoid, outside the spelling to use (「ユーザ」 inside 「ユーザー」 is not reported). */
|
|
31
33
|
export const customWords = (doc, options) => {
|
|
32
34
|
const spec = options.custom;
|
|
33
35
|
if (spec?.type !== "words")
|
|
34
36
|
return [];
|
|
37
|
+
const pairs = pairsOf(spec, options).filter((pair) => pair.avoid !== "" && pair.avoid !== pair.use);
|
|
35
38
|
return doc.sentences.flatMap((sentence) => {
|
|
36
39
|
const view = readAsWritten(sentence);
|
|
37
|
-
return
|
|
40
|
+
return pairs.flatMap((pair) => occurrencesOutside(view.text, pair.avoid, pair.use).map((start) => findingOf(hitAt(sentence, view, start, start + pair.avoid.length, pair.use))));
|
|
38
41
|
});
|
|
39
42
|
};
|
|
40
43
|
/** pattern: each match of the team's regular expression in a sentence. Checked when chaff.yaml was read, and run with a time limit. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"custom.js","sourceRoot":"","sources":["../../src/detectors/custom.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AACvD,OAAO,EAAE,UAAU,EAAmB,MAAM,mBAAmB,CAAC;AAChE,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAO5D,MAAM,SAAS,GAAG,CAAC,GAAQ,EAAW,EAAE,CAAC,CAAC;IACxC,IAAI,EAAE,EAAE;IACR,QAAQ,EAAE,SAAS;IACnB,IAAI,EAAE,CAAC;IACP,MAAM,EAAE,CAAC;IACT,KAAK,EAAE,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE;IAC/B,MAAM,EAAE,EAAE,OAAO,EAAE,GAAG,CAAC,OAAO,EAAE,SAAS,EAAE,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,GAAG,GAAG,CAAC,MAAM,EAAE;CACzG,CAAC,CAAC;AAEH;;;GAGG;AACH,MAAM,aAAa,GAAG,CAAC,QAAkB,EAAc,EAAE,CACvD,UAAU,CACR,QAAQ,CAAC,IAAI,EACb,CAAC,QAAQ,CAAC,UAAU,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,GAAG,QAAQ,CAAC,IAAI,CAAC,KAAK,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,GAAG,QAAQ,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,CAC9H,CAAC;AAEJ,2FAA2F;AAC3F,MAAM,KAAK,GAAG,CAAC,QAAkB,EAAE,IAAgB,EAAE,KAAa,EAAE,GAAW,EAAE,SAAiB,EAAO,EAAE,CAAC,CAAC;IAC3G,QAAQ;IACR,MAAM,EAAE,IAAI,CAAC,QAAQ,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC,KAAK;IAC3C,OAAO,EAAE,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,GAAG,CAAC;IACpC,SAAS;CACV,CAAC,CAAC;AAEH,0HAA0H;AAC1H,MAAM,aAAa,GAAG,CAAC,MAAc,EAAE,KAAa,EAAE,GAAW,EAAE,MAAuB,EAAU,EAAE,CACpG,MAAM;KACH,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,IAAI,KAAK,IAAI,IAAI,CAAC,GAAG,IAAI,GAAG,CAAC;KACxD,UAAU,EAAE;KACZ,MAAM,CAAC,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,GAAG,KAAK,CAAC,EAAE,EAAE,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,CAAC;AAE7H,wGAAwG;AACxG,MAAM,CAAC,MAAM,WAAW,GAAa,CAAC,GAAG,EAAE,OAAO,EAAa,EAAE;IAC/D,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC;IAC5B,IAAI,IAAI,EAAE,IAAI,KAAK,OAAO;QAAE,OAAO,EAAE,CAAC;IACtC,OAAO,GAAG,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,QAAQ,EAAE,EAAE;QACxC,MAAM,IAAI,GAAG,aAAa,CAAC,QAAQ,CAAC,CAAC;QACrC,OAAO,
|
|
1
|
+
{"version":3,"file":"custom.js","sourceRoot":"","sources":["../../src/detectors/custom.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { type DefinedTerm } from "../structure/definition-use.ts";
|
|
2
|
+
import type { Detector, ProseDocument, Sentence } from "../plugin.ts";
|
|
3
|
+
/** A use of the term set in quotes again after its definition: 「本サービス」 where 本サービス was defined. */
|
|
4
|
+
export declare const quotedUses: (source: string, term: string, uses: readonly number[], definedAt: number) => number[];
|
|
5
|
+
/**
|
|
6
|
+
* The uses of a capitalised term written in lower case ("services" where "Services" is defined), when they are the fewer
|
|
7
|
+
* and at most limit: a document that mostly writes it in lower case uses the word in its ordinary sense on purpose.
|
|
8
|
+
*/
|
|
9
|
+
export declare const lowerCaseUses: (source: string, term: string, uses: readonly number[], definedAt: number, limit: number) => number[];
|
|
10
|
+
export declare const definedTermForm: Detector;
|
|
11
|
+
/** The long name written right before an inline definition: the run of name words that ends at its opening bracket. */
|
|
12
|
+
export declare const nameBefore: (sentence: Sentence, bracket: number) => string | undefined;
|
|
13
|
+
export type RepeatedName = {
|
|
14
|
+
readonly offset: number;
|
|
15
|
+
readonly name: string;
|
|
16
|
+
readonly term: string;
|
|
17
|
+
readonly line: number;
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* Each use of a long name after the definition gave it a short name, outside a signature line. A name that is itself a
|
|
21
|
+
* defined term (during each Subscription Term ("Permitted Use")) is a clause's last words, not the thing being named.
|
|
22
|
+
*/
|
|
23
|
+
export declare const repeatedNames: (doc: ProseDocument, terms: readonly DefinedTerm[]) => RepeatedName[];
|
|
24
|
+
export declare const definedNameRepeated: Detector;
|
|
25
|
+
//# sourceMappingURL=defined-term-form.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"defined-term-form.d.ts","sourceRoot":"","sources":["../../src/detectors/defined-term-form.ts"],"names":[],"mappings":"AAGA,OAAO,EAAuC,KAAK,WAAW,EAAE,MAAM,gCAAgC,CAAC;AAGvG,OAAO,KAAK,EAAE,QAAQ,EAAW,aAAa,EAAE,QAAQ,EAAe,MAAM,cAAc,CAAC;AAgC5F,mGAAmG;AACnG,eAAO,MAAM,UAAU,GAAI,QAAQ,MAAM,EAAE,MAAM,MAAM,EAAE,MAAM,SAAS,MAAM,EAAE,EAAE,WAAW,MAAM,KAAG,MAAM,EASxG,CAAC;AAIL;;;GAGG;AACH,eAAO,MAAM,aAAa,GAAI,QAAQ,MAAM,EAAE,MAAM,MAAM,EAAE,MAAM,SAAS,MAAM,EAAE,EAAE,WAAW,MAAM,EAAE,OAAO,MAAM,KAAG,MAAM,EAM7H,CAAC;AA+BF,eAAO,MAAM,eAAe,EAAE,QAe7B,CAAC;AAaF,uHAAuH;AACvH,eAAO,MAAM,UAAU,GAAI,UAAU,QAAQ,EAAE,SAAS,MAAM,KAAG,MAAM,GAAG,SAWzE,CAAC;AAuBF,MAAM,MAAM,YAAY,GAAG;IAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAgB5H;;;GAGG;AACH,eAAO,MAAM,aAAa,GAAI,KAAK,aAAa,EAAE,OAAO,SAAS,WAAW,EAAE,KAAG,YAAY,EAgB7F,CAAC;AAEF,eAAO,MAAM,mBAAmB,EAAE,QAK3B,CAAC"}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
// A defined term used later in another form, and a long name used again after its short name was defined. Pure. The
|
|
2
|
+
// definitions come from the document's structure tree (以下「甲」という, ("Seller")); which words may stand in a long
|
|
3
|
+
// name is read from the tokens' parts of speech.
|
|
4
|
+
import { definedTerms, usesOf } from "../structure/definition-use.js";
|
|
5
|
+
import { escapeRegExp } from "../orthography.js";
|
|
6
|
+
import { quoteAt } from "./structure-tree.js";
|
|
7
|
+
const bodyOf = (doc) => doc.sentences.map((sentence) => ({ start: sentence.span.start, text: sentence.text }));
|
|
8
|
+
const finding = (doc, rule, offset, variant, values) => ({
|
|
9
|
+
rule,
|
|
10
|
+
severity: "info",
|
|
11
|
+
line: 0,
|
|
12
|
+
column: 0,
|
|
13
|
+
quote: quoteAt(doc.source, offset),
|
|
14
|
+
values: { ...values, offset },
|
|
15
|
+
variant,
|
|
16
|
+
});
|
|
17
|
+
const OPENING_QUOTES = new Set(["「", "『", '"', "“"]);
|
|
18
|
+
const CLOSING_QUOTES = new Set(["」", "』", '"', "”"]);
|
|
19
|
+
/** A quoted term right before these is defined again (「X」とは, "X" means), which duplicate-definition reads. */
|
|
20
|
+
const DEFINING_AFTER = /^(?:とは|という|\s+(?:means|shall mean|refers to|has the meaning))/u;
|
|
21
|
+
/** The ending a use may carry past the term: a plural or a possessive (Services, Service's). */
|
|
22
|
+
const WORD_ENDING = /^(?:e?s|['’]s?)/u;
|
|
23
|
+
/** The first definition of each term, with the spans of all its definitions. */
|
|
24
|
+
const firstDefinitions = (terms) => {
|
|
25
|
+
const groups = new Map();
|
|
26
|
+
terms.forEach((term) => {
|
|
27
|
+
const group = groups.get(term.term);
|
|
28
|
+
if (group === undefined)
|
|
29
|
+
groups.set(term.term, { term, spans: [term.span] });
|
|
30
|
+
else
|
|
31
|
+
group.spans.push(term.span);
|
|
32
|
+
});
|
|
33
|
+
return [...groups.values()];
|
|
34
|
+
};
|
|
35
|
+
/** A use of the term set in quotes again after its definition: 「本サービス」 where 本サービス was defined. */
|
|
36
|
+
export const quotedUses = (source, term, uses, definedAt) => uses.filter((offset) => {
|
|
37
|
+
const after = offset + term.length + (WORD_ENDING.exec(source.slice(offset + term.length, offset + term.length + 2))?.[0].length ?? 0);
|
|
38
|
+
return (offset > definedAt &&
|
|
39
|
+
OPENING_QUOTES.has(source.charAt(offset - 1)) &&
|
|
40
|
+
CLOSING_QUOTES.has(source.charAt(after)) &&
|
|
41
|
+
!DEFINING_AFTER.test(source.slice(after + 1, after + 20)));
|
|
42
|
+
});
|
|
43
|
+
const UPPER_START = /^\p{Lu}/u;
|
|
44
|
+
/**
|
|
45
|
+
* The uses of a capitalised term written in lower case ("services" where "Services" is defined), when they are the fewer
|
|
46
|
+
* and at most limit: a document that mostly writes it in lower case uses the word in its ordinary sense on purpose.
|
|
47
|
+
*/
|
|
48
|
+
export const lowerCaseUses = (source, term, uses, definedAt, limit) => {
|
|
49
|
+
if (!UPPER_START.test(term) || term.toLowerCase() === term)
|
|
50
|
+
return [];
|
|
51
|
+
const later = uses.filter((offset) => offset > definedAt);
|
|
52
|
+
const lower = later.filter((offset) => source.charAt(offset) === term.charAt(0).toLowerCase() && !isSentenceStart(source, offset));
|
|
53
|
+
const capital = later.filter((offset) => source.charAt(offset) === term.charAt(0));
|
|
54
|
+
return lower.length > 0 && lower.length < capital.length && lower.length <= limit ? lower : [];
|
|
55
|
+
};
|
|
56
|
+
/** How far back a sentence's start is looked for: past a few spaces to the mark before them. */
|
|
57
|
+
const LOOK_BACK = 40;
|
|
58
|
+
const SENTENCE_END = new Set([".", "!", "?", ":"]);
|
|
59
|
+
/** A sentence's first word is capitalised whatever it is, so only a lower-case use inside a sentence counts. */
|
|
60
|
+
const isSentenceStart = (source, offset) => {
|
|
61
|
+
const before = source.slice(Math.max(0, offset - LOOK_BACK), offset);
|
|
62
|
+
const text = before.trimEnd();
|
|
63
|
+
return (offset < LOOK_BACK && text === "") || before.slice(text.length).includes("\n") || SENTENCE_END.has(text.at(-1) ?? "");
|
|
64
|
+
};
|
|
65
|
+
/** Where the sentence holding the definition ends: a use inside it ("Customer" means the party named "Customer") belongs to the definition. */
|
|
66
|
+
const definingSentenceEnd = (doc, definition) => doc.sentences.find((sentence) => sentence.span.start <= definition.start && definition.start < sentence.span.end)?.span.end ?? definition.end;
|
|
67
|
+
const VERBAL = new Set(["VERB", "AUX"]);
|
|
68
|
+
/** A word used as a verb ("you input"), not the defined thing ("the Input"): a different word that shares the spelling. */
|
|
69
|
+
const isVerbAt = (doc, offset) => {
|
|
70
|
+
const tokens = doc.sentences.find((candidate) => candidate.span.start <= offset && offset < candidate.span.end)?.tokens ?? [];
|
|
71
|
+
const at = tokens.findIndex((candidate) => candidate.span.start <= offset && offset < candidate.span.end);
|
|
72
|
+
const token = tokens[at];
|
|
73
|
+
if (token === undefined)
|
|
74
|
+
return false;
|
|
75
|
+
// After a subject pronoun the word is its verb (you input), whatever the tagger says; after a possessive (your input) it is a noun.
|
|
76
|
+
const before = tokens[at - 1];
|
|
77
|
+
const afterSubject = before?.pos === "PRON" && before.features?.["Poss"] !== "Yes";
|
|
78
|
+
return VERBAL.has(token.pos) || afterSubject;
|
|
79
|
+
};
|
|
80
|
+
export const definedTermForm = (doc, options) => {
|
|
81
|
+
if (doc.structure === undefined)
|
|
82
|
+
return [];
|
|
83
|
+
const texts = bodyOf(doc);
|
|
84
|
+
return firstDefinitions(definedTerms(doc.structure)).flatMap(({ term, spans }) => {
|
|
85
|
+
const uses = usesOf(term.term, texts, spans, "loose");
|
|
86
|
+
const definingEnd = definingSentenceEnd(doc, term.span);
|
|
87
|
+
const quoted = quotedUses(doc.source, term.term, uses, definingEnd).map((offset) => finding(doc, "defined-term-form", offset, "quoted", { term: term.term, line: term.line }));
|
|
88
|
+
const nounUses = uses.filter((offset) => !isVerbAt(doc, offset));
|
|
89
|
+
const lower = lowerCaseUses(doc.source, term.term, nounUses, definingEnd, options.limit).map((offset) => finding(doc, "defined-term-form", offset, "case", { term: term.term, line: term.line }));
|
|
90
|
+
return [...quoted, ...lower];
|
|
91
|
+
});
|
|
92
|
+
};
|
|
93
|
+
/** The words a long name is made of: nouns and names (株式会社 みなと 製作所, Acme Corporation), and the marks inside one. */
|
|
94
|
+
const NAME_PARTS = new Set(["NOUN", "PROPN", "NUM"]);
|
|
95
|
+
const NAME_MARKS = new Set(["・", "&", "&", ".", ","]);
|
|
96
|
+
/** A long name shorter than this (法律, Customer) is an ordinary word, used in its own sense again. */
|
|
97
|
+
const MIN_NAME_CHARS = 4;
|
|
98
|
+
/** In English a long name has two words or more (Acme Corporation); one capitalised word is the term's own kind of word. */
|
|
99
|
+
const MIN_NAME_WORDS = 2;
|
|
100
|
+
const LATIN = /\p{Script=Latin}/u;
|
|
101
|
+
const isNamePart = (token) => NAME_PARTS.has(token.pos) || NAME_MARKS.has(token.surface);
|
|
102
|
+
/** The long name written right before an inline definition: the run of name words that ends at its opening bracket. */
|
|
103
|
+
export const nameBefore = (sentence, bracket) => {
|
|
104
|
+
const tokens = (sentence.tokens ?? []).filter((token) => token.span.end <= bracket);
|
|
105
|
+
const firstOfRun = tokens.findLastIndex((token) => !isNamePart(token)) + 1;
|
|
106
|
+
if (isModifier(tokens[firstOfRun - 1]))
|
|
107
|
+
return undefined;
|
|
108
|
+
const run = tokens.slice(firstOfRun).filter((token, index, all) => index > 0 || !NAME_MARKS.has(token.surface) || all.length === 1);
|
|
109
|
+
const first = run[0];
|
|
110
|
+
const end = run.at(-1);
|
|
111
|
+
if (first === undefined || end === undefined || bracket - end.span.end > 1)
|
|
112
|
+
return undefined;
|
|
113
|
+
const name = withoutTrailingMarks(sentence.text.slice(first.span.start - sentence.span.start, end.span.end - sentence.span.start));
|
|
114
|
+
const words = name.split(/\s+/u).length;
|
|
115
|
+
return [...name].length < MIN_NAME_CHARS || (LATIN.test(name) && words < MIN_NAME_WORDS) ? undefined : name;
|
|
116
|
+
};
|
|
117
|
+
/**
|
|
118
|
+
* A word that narrows the name before it: an article, an adjective, a verb, or の (妊娠中の女性従業員, the Beta Preview, 本利用規約).
|
|
119
|
+
* The words then name a kind of thing the definition picks out, not one party, and are used in their own sense again.
|
|
120
|
+
*/
|
|
121
|
+
const MODIFIERS = new Set(["DET", "ADJ", "VERB", "AUX"]);
|
|
122
|
+
const isModifier = (token) => token !== undefined && (MODIFIERS.has(token.pos) || token.surface === "の");
|
|
123
|
+
const TRAILING_MARKS = new Set([",", ".", " "]);
|
|
124
|
+
const withoutTrailingMarks = (text) => {
|
|
125
|
+
const chars = [...text];
|
|
126
|
+
return chars.slice(0, chars.findLastIndex((char) => !TRAILING_MARKS.has(char)) + 1).join("");
|
|
127
|
+
};
|
|
128
|
+
/** Where the inline definition's bracket opens: (以下「甲」という。) or ("Seller"). */
|
|
129
|
+
const bracketBefore = (source, definition) => {
|
|
130
|
+
const before = source.slice(Math.max(0, definition.start - 2), definition.start + 1);
|
|
131
|
+
const at = Math.max(before.lastIndexOf("("), before.lastIndexOf("("));
|
|
132
|
+
return at === -1 ? -1 : Math.max(0, definition.start - 2) + at;
|
|
133
|
+
};
|
|
134
|
+
/** A line that holds the short name too (甲 株式会社みなと) or little else than the long name: a signature block names both. */
|
|
135
|
+
const isNameLine = (source, offset, name, term) => {
|
|
136
|
+
const start = source.lastIndexOf("\n", offset - 1) + 1;
|
|
137
|
+
const end = source.indexOf("\n", offset);
|
|
138
|
+
const line = source.slice(start, end === -1 ? source.length : end).trim();
|
|
139
|
+
const SLACK = 12;
|
|
140
|
+
return line.includes(term) || [...line].length <= [...name].length + SLACK;
|
|
141
|
+
};
|
|
142
|
+
/** A name inside a longer word (東京大学 in 東京大学大学院, Acme in Acmeware) is part of another name. */
|
|
143
|
+
const WORD_CHAR = /[\p{Script=Han}\p{Script=Katakana}\p{Script=Latin}\p{N}ー]/u;
|
|
144
|
+
const standsAlone = (source, offset, name) => !WORD_CHAR.test(source.charAt(offset - 1)) && !WORD_CHAR.test(source.charAt(offset + name.length));
|
|
145
|
+
/**
|
|
146
|
+
* Each use of a long name after the definition gave it a short name, outside a signature line. A name that is itself a
|
|
147
|
+
* defined term (during each Subscription Term ("Permitted Use")) is a clause's last words, not the thing being named.
|
|
148
|
+
*/
|
|
149
|
+
export const repeatedNames = (doc, terms) => {
|
|
150
|
+
const defined = new Set(terms.map((term) => term.term));
|
|
151
|
+
return terms
|
|
152
|
+
.filter((term) => term.inline)
|
|
153
|
+
.flatMap((term) => {
|
|
154
|
+
const bracket = bracketBefore(doc.source, term.span);
|
|
155
|
+
const sentence = doc.sentences.find((candidate) => candidate.span.start <= bracket && bracket < candidate.span.end);
|
|
156
|
+
const name = sentence === undefined || bracket === -1 ? undefined : nameBefore(sentence, bracket);
|
|
157
|
+
if (name === undefined || name === term.term || term.term.includes(name) || defined.has(name))
|
|
158
|
+
return [];
|
|
159
|
+
const pattern = new RegExp(escapeRegExp(name), "gu");
|
|
160
|
+
return doc.sentences
|
|
161
|
+
.filter((later) => later.span.start > term.span.end)
|
|
162
|
+
.flatMap((later) => [...later.text.matchAll(pattern)].map((match) => later.span.start + match.index))
|
|
163
|
+
.filter((offset) => standsAlone(doc.source, offset, name) && !isNameLine(doc.source, offset, name, term.term))
|
|
164
|
+
.map((offset) => ({ offset, name, term: term.term, line: term.line }));
|
|
165
|
+
});
|
|
166
|
+
};
|
|
167
|
+
export const definedNameRepeated = (doc) => doc.structure === undefined
|
|
168
|
+
? []
|
|
169
|
+
: repeatedNames(doc, definedTerms(doc.structure)).map((repeated) => finding(doc, "defined-name-repeated", repeated.offset, "name", { name: repeated.name, term: repeated.term, line: repeated.line }));
|
|
170
|
+
//# sourceMappingURL=defined-term-form.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"defined-term-form.js","sourceRoot":"","sources":["../../src/detectors/defined-term-form.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { Detector, Span } from "../plugin.ts";
|
|
2
|
+
/** One line of the writer's own text. breaks: a heading, the header or the signature stood between it and the line before. */
|
|
3
|
+
export type BodyLine = {
|
|
4
|
+
readonly start: number;
|
|
5
|
+
readonly text: string;
|
|
6
|
+
readonly breaks: boolean;
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* Whether a line holds one of the words. A Latin word opens the line (Dear, Best regards) and is followed by no letter,
|
|
10
|
+
* so "Hi" does not match "History"; a Japanese word may stand anywhere in it (いつもお世話になっております).
|
|
11
|
+
*/
|
|
12
|
+
export declare const lineHas: (line: string, words: readonly string[]) => boolean;
|
|
13
|
+
/**
|
|
14
|
+
* The writer's own lines, in order: not blank once the header and signature are masked (prose), not a quoted reply, not a
|
|
15
|
+
* heading. A heading, or a line the source has and prose masks (a header, a signature), breaks the text into parts.
|
|
16
|
+
*/
|
|
17
|
+
export declare const bodyLines: (source: string, prose: string, replyQuotes: readonly Span[]) => BodyLine[];
|
|
18
|
+
export type FrameSlip = {
|
|
19
|
+
readonly variant: "greeting" | "closing";
|
|
20
|
+
readonly offset: number;
|
|
21
|
+
};
|
|
22
|
+
/** A missing greeting (none in the first lines) and a missing closing (none in the last lines of any part). */
|
|
23
|
+
export declare const frameSlips: (lines: readonly BodyLine[], greetings: readonly string[], closings: readonly string[]) => FrameSlip[];
|
|
24
|
+
export declare const emailFrame: Detector;
|
|
25
|
+
export type SubjectLine = {
|
|
26
|
+
readonly start: number;
|
|
27
|
+
readonly subject: string;
|
|
28
|
+
};
|
|
29
|
+
/** "Subject: Lunch" as its name and value; undefined for a line that is no field. */
|
|
30
|
+
export declare const fieldOf: (line: string) => {
|
|
31
|
+
readonly name: string;
|
|
32
|
+
readonly value: string;
|
|
33
|
+
} | undefined;
|
|
34
|
+
/** The subject lines (Subject: …, 件名:…), outside a quoted reply, with reply and forward marks (Re:, Fwd:) taken off the value. */
|
|
35
|
+
export declare const subjectLines: (source: string, fields: readonly string[], replyMarks: readonly string[], replyQuotes: readonly Span[]) => SubjectLine[];
|
|
36
|
+
/** "Re: Fwd: Lunch" is "Lunch": the marks a mail program adds are not what the writer wrote. Read piece by piece between colons. */
|
|
37
|
+
export declare const withoutReplyMarks: (subject: string, marks: readonly string[]) => string;
|
|
38
|
+
export declare const emailSubject: Detector;
|
|
39
|
+
/** Where the text first mentions an attachment (添付します, attached), outside the phrases that only look like one (attached to). */
|
|
40
|
+
export declare const firstMention: (lines: readonly BodyLine[], mentions: readonly string[], notMentions: readonly string[]) => number | undefined;
|
|
41
|
+
/** An attachment shown: a line that labels it, a file name with a listed extension, or the archive's note. text is the writer's own (no quoted reply). */
|
|
42
|
+
export declare const showsAttachment: (text: string, labels: readonly string[], extensions: readonly string[], notes: readonly string[]) => boolean;
|
|
43
|
+
/** The source without someone else's words: a quoted reply's spans blanked and its "> " lines dropped. An old message's attachment is not this one's. */
|
|
44
|
+
export declare const ownText: (source: string, replyQuotes: readonly Span[]) => string;
|
|
45
|
+
export declare const attachmentMissing: Detector;
|
|
46
|
+
//# sourceMappingURL=email-letter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"email-letter.d.ts","sourceRoot":"","sources":["../../src/detectors/email-letter.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,QAAQ,EAA0B,IAAI,EAAE,MAAM,cAAc,CAAC;AAO3E,8HAA8H;AAC9H,MAAM,MAAM,QAAQ,GAAG;IAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAA;CAAE,CAAC;AAOnG;;;GAGG;AACH,eAAO,MAAM,OAAO,GAAI,MAAM,MAAM,EAAE,OAAO,SAAS,MAAM,EAAE,KAAG,OAOhE,CAAC;AAoCF;;;GAGG;AACH,eAAO,MAAM,SAAS,GAAI,QAAQ,MAAM,EAAE,OAAO,MAAM,EAAE,aAAa,SAAS,IAAI,EAAE,KAAG,QAAQ,EAgB/F,CAAC;AAaF,MAAM,MAAM,SAAS,GAAG;IAAE,QAAQ,CAAC,OAAO,EAAE,UAAU,GAAG,SAAS,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAE9F,+GAA+G;AAC/G,eAAO,MAAM,UAAU,GAAI,OAAO,SAAS,QAAQ,EAAE,EAAE,WAAW,SAAS,MAAM,EAAE,EAAE,UAAU,SAAS,MAAM,EAAE,KAAG,SAAS,EAU3H,CAAC;AAEF,eAAO,MAAM,UAAU,EAAE,QAWtB,CAAC;AAEJ,MAAM,MAAM,WAAW,GAAG;IAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAM/E,qFAAqF;AACrF,eAAO,MAAM,OAAO,GAAI,MAAM,MAAM,KAAG;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG,SAc1F,CAAC;AAEF,kIAAkI;AAClI,eAAO,MAAM,YAAY,GAAI,QAAQ,MAAM,EAAE,QAAQ,SAAS,MAAM,EAAE,EAAE,YAAY,SAAS,MAAM,EAAE,EAAE,aAAa,SAAS,IAAI,EAAE,KAAG,WAAW,EAQhJ,CAAC;AAEF,oIAAoI;AACpI,eAAO,MAAM,iBAAiB,GAAI,SAAS,MAAM,EAAE,OAAO,SAAS,MAAM,EAAE,KAAG,MAM7E,CAAC;AAEF,eAAO,MAAM,YAAY,EAAE,QAevB,CAAC;AAEL,gIAAgI;AAChI,eAAO,MAAM,YAAY,GAAI,OAAO,SAAS,QAAQ,EAAE,EAAE,UAAU,SAAS,MAAM,EAAE,EAAE,aAAa,SAAS,MAAM,EAAE,KAAG,MAAM,GAAG,SAQ/H,CAAC;AAoBF,0JAA0J;AAC1J,eAAO,MAAM,eAAe,GAAI,MAAM,MAAM,EAAE,QAAQ,SAAS,MAAM,EAAE,EAAE,YAAY,SAAS,MAAM,EAAE,EAAE,OAAO,SAAS,MAAM,EAAE,KAAG,OAIlI,CAAC;AAEF,yJAAyJ;AACzJ,eAAO,MAAM,OAAO,GAAI,QAAQ,MAAM,EAAE,aAAa,SAAS,IAAI,EAAE,KAAG,MAgBtE,CAAC;AAEF,eAAO,MAAM,iBAAiB,EAAE,QAiB/B,CAAC"}
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
// A work email or letter's own parts: a greeting and a closing, a subject line, and an attachment the text mentions.
|
|
2
|
+
// Pure. The words (お世話になっております, Dear, Best regards, 件名, attached) come from the language's email-greeting,
|
|
3
|
+
// email-closing, email-subject-field, attachment-mention, attachment-not-mention and attachment-line lexicons. The
|
|
4
|
+
// header and the signature are masked in doc.prose; a quoted reply is someone else's words and is left out.
|
|
5
|
+
import { escapeRegExp } from "../orthography.js";
|
|
6
|
+
import { quoteAt } from "./structure-tree.js";
|
|
7
|
+
/** A greeting comes before the body: past the subject, the addressee (株式会社… / 総務部 山田様) and the writer's name. */
|
|
8
|
+
const GREETING_LINES = 5;
|
|
9
|
+
/** A closing comes at the end, before the writer's name, title, company, address and contact lines, or an acceptance block. */
|
|
10
|
+
const CLOSING_LINES = 10;
|
|
11
|
+
const patternsOf = (doc, id) => (doc.lexicons[id] ?? []).map((entry) => entry.pattern);
|
|
12
|
+
const LATIN_START = /^[A-Za-z]/u;
|
|
13
|
+
const LETTER = /\p{L}/u;
|
|
14
|
+
/**
|
|
15
|
+
* Whether a line holds one of the words. A Latin word opens the line (Dear, Best regards) and is followed by no letter,
|
|
16
|
+
* so "Hi" does not match "History"; a Japanese word may stand anywhere in it (いつもお世話になっております).
|
|
17
|
+
*/
|
|
18
|
+
export const lineHas = (line, words) => {
|
|
19
|
+
const trimmed = line.trim();
|
|
20
|
+
const folded = trimmed.toLowerCase();
|
|
21
|
+
return words.some((word) => {
|
|
22
|
+
if (!LATIN_START.test(word))
|
|
23
|
+
return trimmed.includes(word);
|
|
24
|
+
return folded.startsWith(word.toLowerCase()) && !LETTER.test(trimmed.charAt(word.length));
|
|
25
|
+
});
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* Whether each offset, asked in rising order, is inside one of the spans (sorted by start). One pass over the spans for
|
|
29
|
+
* the whole document: an archive with a quoted reply in every message stays linear.
|
|
30
|
+
*/
|
|
31
|
+
const insideCursor = (spans) => {
|
|
32
|
+
const sorted = spans.toSorted((left, right) => left.start - right.start);
|
|
33
|
+
const cursor = { next: 0, farthestEnd: -1 };
|
|
34
|
+
return (offset) => {
|
|
35
|
+
while (cursor.next < sorted.length && (sorted[cursor.next]?.start ?? Infinity) <= offset) {
|
|
36
|
+
cursor.farthestEnd = Math.max(cursor.farthestEnd, sorted[cursor.next]?.end ?? 0);
|
|
37
|
+
cursor.next += 1;
|
|
38
|
+
}
|
|
39
|
+
return offset < cursor.farthestEnd;
|
|
40
|
+
};
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* A line prose masks words at the head of: a header field whose value stays (Subject: …), not the writer's text. Emphasis
|
|
44
|
+
* marks are masked too, but they hold no letter.
|
|
45
|
+
*/
|
|
46
|
+
const isFieldLine = (sourceLine, proseLine) => {
|
|
47
|
+
const sourceChars = [...sourceLine];
|
|
48
|
+
const kept = [...proseLine].findIndex((char, index) => char.trim() !== "" || sourceChars[index]?.trim() === "");
|
|
49
|
+
return LETTER.test(sourceChars.slice(0, kept === -1 ? sourceChars.length : kept).join(""));
|
|
50
|
+
};
|
|
51
|
+
const linesWithOffsets = (text) => {
|
|
52
|
+
const lines = [];
|
|
53
|
+
text.split("\n").forEach((line) => lines.push({ start: (lines.at(-1)?.start ?? 0) + (lines.at(-1)?.text.length ?? -1) + 1, text: line }));
|
|
54
|
+
return lines;
|
|
55
|
+
};
|
|
56
|
+
/**
|
|
57
|
+
* The writer's own lines, in order: not blank once the header and signature are masked (prose), not a quoted reply, not a
|
|
58
|
+
* heading. A heading, or a line the source has and prose masks (a header, a signature), breaks the text into parts.
|
|
59
|
+
*/
|
|
60
|
+
export const bodyLines = (source, prose, replyQuotes) => {
|
|
61
|
+
const quoted = insideCursor(replyQuotes);
|
|
62
|
+
const lines = [];
|
|
63
|
+
const state = { broken: false };
|
|
64
|
+
linesWithOffsets(prose).forEach(({ start, text }) => {
|
|
65
|
+
const trimmed = text.trimStart();
|
|
66
|
+
const sourceLine = source.slice(start, start + text.length);
|
|
67
|
+
const masked = trimmed === "" ? sourceLine.trim() !== "" : isFieldLine(sourceLine, text);
|
|
68
|
+
const inQuote = quoted(start);
|
|
69
|
+
if (masked || trimmed.startsWith("#"))
|
|
70
|
+
state.broken = true;
|
|
71
|
+
else if (trimmed !== "" && !trimmed.startsWith(">") && !inQuote) {
|
|
72
|
+
lines.push({ start, text, breaks: state.broken });
|
|
73
|
+
state.broken = false;
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
return lines;
|
|
77
|
+
};
|
|
78
|
+
/** The lines split where a heading, a header or a signature breaks them: a letter and its appendix, or one message and the next. */
|
|
79
|
+
const partsOf = (lines) => {
|
|
80
|
+
const parts = [];
|
|
81
|
+
lines.forEach((line) => {
|
|
82
|
+
const current = parts.at(-1);
|
|
83
|
+
if (line.breaks || current === undefined)
|
|
84
|
+
parts.push([line]);
|
|
85
|
+
else
|
|
86
|
+
current.push(line);
|
|
87
|
+
});
|
|
88
|
+
return parts;
|
|
89
|
+
};
|
|
90
|
+
/** A missing greeting (none in the first lines) and a missing closing (none in the last lines of any part). */
|
|
91
|
+
export const frameSlips = (lines, greetings, closings) => {
|
|
92
|
+
const first = lines[0];
|
|
93
|
+
const last = lines.at(-1);
|
|
94
|
+
if (first === undefined || last === undefined)
|
|
95
|
+
return [];
|
|
96
|
+
const greeted = lines.slice(0, GREETING_LINES).some((line) => lineHas(line.text, greetings));
|
|
97
|
+
const closed = partsOf(lines).some((part) => part.slice(-CLOSING_LINES).some((line) => lineHas(line.text, closings)));
|
|
98
|
+
return [
|
|
99
|
+
...(greeted ? [] : [{ variant: "greeting", offset: first.start }]),
|
|
100
|
+
...(closed ? [] : [{ variant: "closing", offset: last.start }]),
|
|
101
|
+
];
|
|
102
|
+
};
|
|
103
|
+
export const emailFrame = (doc) => frameSlips(bodyLines(doc.source, doc.prose ?? doc.source, doc.replyQuotes ?? []), patternsOf(doc, "email-greeting"), patternsOf(doc, "email-closing")).map((slip) => ({
|
|
104
|
+
rule: "email-greeting-closing",
|
|
105
|
+
severity: "info",
|
|
106
|
+
line: 0,
|
|
107
|
+
column: 0,
|
|
108
|
+
quote: quoteAt(doc.source, slip.offset),
|
|
109
|
+
values: { offset: slip.offset },
|
|
110
|
+
variant: slip.variant,
|
|
111
|
+
}));
|
|
112
|
+
/** A field's name is one word of at most this many characters. */
|
|
113
|
+
const MAX_FIELD_NAME = 20;
|
|
114
|
+
const BLANK = /\s/u;
|
|
115
|
+
/** "Subject: Lunch" as its name and value; undefined for a line that is no field. */
|
|
116
|
+
export const fieldOf = (line) => {
|
|
117
|
+
const colon = [...line].findIndex((char) => char === ":" || char === ":");
|
|
118
|
+
if (colon <= 0)
|
|
119
|
+
return undefined;
|
|
120
|
+
const chars = [...line];
|
|
121
|
+
const name = chars.slice(0, colon).join("").trimEnd();
|
|
122
|
+
return name.length > MAX_FIELD_NAME || BLANK.test(name)
|
|
123
|
+
? undefined
|
|
124
|
+
: {
|
|
125
|
+
name,
|
|
126
|
+
value: chars
|
|
127
|
+
.slice(colon + 1)
|
|
128
|
+
.join("")
|
|
129
|
+
.trim(),
|
|
130
|
+
};
|
|
131
|
+
};
|
|
132
|
+
/** The subject lines (Subject: …, 件名:…), outside a quoted reply, with reply and forward marks (Re:, Fwd:) taken off the value. */
|
|
133
|
+
export const subjectLines = (source, fields, replyMarks, replyQuotes) => {
|
|
134
|
+
const names = new Set(fields.map((field) => field.toLowerCase()));
|
|
135
|
+
const quoted = insideCursor(replyQuotes);
|
|
136
|
+
return linesWithOffsets(source).flatMap(({ start, text }) => {
|
|
137
|
+
const field = fieldOf(text);
|
|
138
|
+
const inQuote = quoted(start);
|
|
139
|
+
return field === undefined || inQuote || !names.has(field.name.toLowerCase()) ? [] : [{ start, subject: withoutReplyMarks(field.value, replyMarks) }];
|
|
140
|
+
});
|
|
141
|
+
};
|
|
142
|
+
/** "Re: Fwd: Lunch" is "Lunch": the marks a mail program adds are not what the writer wrote. Read piece by piece between colons. */
|
|
143
|
+
export const withoutReplyMarks = (subject, marks) => {
|
|
144
|
+
const folded = new Set(marks.map((mark) => mark.toLowerCase()));
|
|
145
|
+
const pieces = subject.split(/[::]/u);
|
|
146
|
+
const kept = pieces.findIndex((piece, index) => index === pieces.length - 1 || !folded.has(piece.trim().toLowerCase()));
|
|
147
|
+
const dropped = pieces.slice(0, kept).reduce((length, piece) => length + piece.length + 1, 0);
|
|
148
|
+
return subject.slice(dropped).trim();
|
|
149
|
+
};
|
|
150
|
+
export const emailSubject = (doc, options) => subjectLines(doc.source, patternsOf(doc, "email-subject-field"), patternsOf(doc, "email-reply-mark"), doc.replyQuotes ?? []).flatMap((line) => {
|
|
151
|
+
const length = [...line.subject].length;
|
|
152
|
+
if (length > 0 && length <= options.limit)
|
|
153
|
+
return [];
|
|
154
|
+
return [
|
|
155
|
+
{
|
|
156
|
+
rule: "email-subject-length",
|
|
157
|
+
severity: "warning",
|
|
158
|
+
line: 0,
|
|
159
|
+
column: 0,
|
|
160
|
+
quote: quoteAt(doc.source, line.start),
|
|
161
|
+
values: { count: length, limit: options.limit, offset: line.start },
|
|
162
|
+
...(length === 0 ? { variant: "empty" } : {}),
|
|
163
|
+
},
|
|
164
|
+
];
|
|
165
|
+
});
|
|
166
|
+
/** Where the text first mentions an attachment (添付します, attached), outside the phrases that only look like one (attached to). */
|
|
167
|
+
export const firstMention = (lines, mentions, notMentions) => {
|
|
168
|
+
const folded = (text) => text.toLowerCase();
|
|
169
|
+
const isMention = (text) => {
|
|
170
|
+
const cleared = notMentions.reduce((rest, phrase) => rest.split(folded(phrase)).join(" "), folded(text));
|
|
171
|
+
return mentions.some((word) => cleared.includes(folded(word)));
|
|
172
|
+
};
|
|
173
|
+
const line = lines.find((candidate) => isMention(candidate.text));
|
|
174
|
+
return line === undefined ? undefined : line.start + line.text.length - line.text.trimStart().length;
|
|
175
|
+
};
|
|
176
|
+
/** The marks around a label line: a heading's #, emphasis, 【】. */
|
|
177
|
+
const LINE_MARKS = new Set("#*_【】 \t\u3000");
|
|
178
|
+
const withoutMarks = (line) => {
|
|
179
|
+
const chars = [...line];
|
|
180
|
+
const first = chars.findIndex((char) => !LINE_MARKS.has(char));
|
|
181
|
+
return first === -1 ? "" : chars.slice(first, chars.findLastIndex((char) => !LINE_MARKS.has(char)) + 1).join("");
|
|
182
|
+
};
|
|
183
|
+
/** A line that labels what is attached: the label and a colon (Attachment: quote.pdf), or the label alone (### Enclosures, 【添付資料】). */
|
|
184
|
+
const labelsAttachment = (line, labels) => {
|
|
185
|
+
const bare = withoutMarks(line).toLowerCase();
|
|
186
|
+
return labels.some((label) => {
|
|
187
|
+
const folded = label.toLowerCase();
|
|
188
|
+
return bare === folded || (bare.startsWith(folded) && /^\s*[::]/u.test(bare.slice(folded.length)));
|
|
189
|
+
});
|
|
190
|
+
};
|
|
191
|
+
/** An attachment shown: a line that labels it, a file name with a listed extension, or the archive's note. text is the writer's own (no quoted reply). */
|
|
192
|
+
export const showsAttachment = (text, labels, extensions, notes) => {
|
|
193
|
+
const labelled = text.split("\n").some((line) => labelsAttachment(line, labels));
|
|
194
|
+
const fileName = new RegExp(`[\\w\\-ぁ-んァ-ヶ一-龠]\\.(?:${extensions.map(escapeRegExp).join("|")})\\b`, "iu");
|
|
195
|
+
return labelled || (extensions.length > 0 && fileName.test(text)) || notes.some((note) => text.includes(note));
|
|
196
|
+
};
|
|
197
|
+
/** The source without someone else's words: a quoted reply's spans blanked and its "> " lines dropped. An old message's attachment is not this one's. */
|
|
198
|
+
export const ownText = (source, replyQuotes) => {
|
|
199
|
+
const pieces = [];
|
|
200
|
+
const cursor = { at: 0 };
|
|
201
|
+
replyQuotes
|
|
202
|
+
.toSorted((left, right) => left.start - right.start)
|
|
203
|
+
.forEach((span) => {
|
|
204
|
+
const from = Math.max(span.start, cursor.at);
|
|
205
|
+
pieces.push(source.slice(cursor.at, from), " ".repeat(Math.max(0, span.end - from)));
|
|
206
|
+
cursor.at = Math.max(cursor.at, span.end);
|
|
207
|
+
});
|
|
208
|
+
pieces.push(source.slice(cursor.at));
|
|
209
|
+
return pieces
|
|
210
|
+
.join("")
|
|
211
|
+
.split("\n")
|
|
212
|
+
.filter((line) => !line.trimStart().startsWith(">"))
|
|
213
|
+
.join("\n");
|
|
214
|
+
};
|
|
215
|
+
export const attachmentMissing = (doc) => {
|
|
216
|
+
const lines = bodyLines(doc.source, doc.prose ?? doc.source, doc.replyQuotes ?? []);
|
|
217
|
+
const mention = firstMention(lines, patternsOf(doc, "attachment-mention"), patternsOf(doc, "attachment-not-mention"));
|
|
218
|
+
if (mention === undefined)
|
|
219
|
+
return [];
|
|
220
|
+
const extensions = patternsOf(doc, "attachment-file-extension").map((extension) => extension.replace(/^\./u, ""));
|
|
221
|
+
if (showsAttachment(ownText(doc.source, doc.replyQuotes ?? []), patternsOf(doc, "attachment-line"), extensions, patternsOf(doc, "email-attachment-note")))
|
|
222
|
+
return [];
|
|
223
|
+
return [
|
|
224
|
+
{
|
|
225
|
+
rule: "attachment-not-attached",
|
|
226
|
+
severity: "warning",
|
|
227
|
+
line: 0,
|
|
228
|
+
column: 0,
|
|
229
|
+
quote: quoteAt(doc.source, mention),
|
|
230
|
+
values: { offset: mention },
|
|
231
|
+
},
|
|
232
|
+
];
|
|
233
|
+
};
|
|
234
|
+
//# sourceMappingURL=email-letter.js.map
|