chaffjs 0.16.0 → 0.18.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/dist/announced-count.d.ts +26 -0
- package/dist/announced-count.d.ts.map +1 -0
- package/dist/announced-count.js +104 -0
- package/dist/announced-count.js.map +1 -0
- package/dist/bare-url.d.ts +14 -0
- package/dist/bare-url.d.ts.map +1 -0
- package/dist/bare-url.js +28 -0
- package/dist/bare-url.js.map +1 -0
- package/dist/baseline.d.ts +5 -0
- package/dist/baseline.d.ts.map +1 -1
- package/dist/baseline.js +16 -1
- package/dist/baseline.js.map +1 -1
- package/dist/body-section.d.ts +7 -0
- package/dist/body-section.d.ts.map +1 -0
- package/dist/body-section.js +6 -0
- package/dist/body-section.js.map +1 -0
- package/dist/calendar-number.d.ts +9 -2
- package/dist/calendar-number.d.ts.map +1 -1
- package/dist/calendar-number.js +18 -4
- package/dist/calendar-number.js.map +1 -1
- package/dist/cli-args.d.ts +4 -0
- package/dist/cli-args.d.ts.map +1 -1
- package/dist/cli-args.js +5 -0
- package/dist/cli-args.js.map +1 -1
- package/dist/cli-text.d.ts.map +1 -1
- package/dist/cli-text.js +8 -4
- package/dist/cli-text.js.map +1 -1
- package/dist/cli.js +33 -30
- package/dist/cli.js.map +1 -1
- package/dist/closing-numbers.d.ts +20 -0
- package/dist/closing-numbers.d.ts.map +1 -0
- package/dist/closing-numbers.js +56 -0
- package/dist/closing-numbers.js.map +1 -0
- package/dist/commands/compare.d.ts +8 -0
- package/dist/commands/compare.d.ts.map +1 -0
- package/dist/commands/compare.js +72 -0
- package/dist/commands/compare.js.map +1 -0
- package/dist/commands/eval.d.ts.map +1 -1
- package/dist/commands/eval.js +17 -9
- package/dist/commands/eval.js.map +1 -1
- package/dist/commands/feedback.d.ts +5 -2
- package/dist/commands/feedback.d.ts.map +1 -1
- package/dist/commands/feedback.js +11 -5
- package/dist/commands/feedback.js.map +1 -1
- package/dist/commands/test.d.ts.map +1 -1
- package/dist/commands/test.js +13 -13
- package/dist/commands/test.js.map +1 -1
- package/dist/commands/tree.d.ts +13 -0
- package/dist/commands/tree.d.ts.map +1 -1
- package/dist/commands/tree.js +35 -5
- package/dist/commands/tree.js.map +1 -1
- package/dist/compare/atom.d.ts +31 -0
- package/dist/compare/atom.d.ts.map +1 -0
- package/dist/compare/atom.js +4 -0
- package/dist/compare/atom.js.map +1 -0
- package/dist/compare/clock-time.d.ts +8 -0
- package/dist/compare/clock-time.d.ts.map +1 -0
- package/dist/compare/clock-time.js +65 -0
- package/dist/compare/clock-time.js.map +1 -0
- package/dist/compare/extract.d.ts +15 -0
- package/dist/compare/extract.d.ts.map +1 -0
- package/dist/compare/extract.js +58 -0
- package/dist/compare/extract.js.map +1 -0
- package/dist/compare/fact-text.d.ts +23 -0
- package/dist/compare/fact-text.d.ts.map +1 -0
- package/dist/compare/fact-text.js +47 -0
- package/dist/compare/fact-text.js.map +1 -0
- package/dist/compare/markup-atoms.d.ts +11 -0
- package/dist/compare/markup-atoms.d.ts.map +1 -0
- package/dist/compare/markup-atoms.js +23 -0
- package/dist/compare/markup-atoms.js.map +1 -0
- package/dist/compare/match.d.ts +19 -0
- package/dist/compare/match.d.ts.map +1 -0
- package/dist/compare/match.js +84 -0
- package/dist/compare/match.js.map +1 -0
- package/dist/compare/outcome.d.ts +33 -0
- package/dist/compare/outcome.d.ts.map +1 -0
- package/dist/compare/outcome.js +40 -0
- package/dist/compare/outcome.js.map +1 -0
- package/dist/compare/proper-nouns.d.ts +19 -0
- package/dist/compare/proper-nouns.d.ts.map +1 -0
- package/dist/compare/proper-nouns.js +44 -0
- package/dist/compare/proper-nouns.js.map +1 -0
- package/dist/compare/render.d.ts +9 -0
- package/dist/compare/render.d.ts.map +1 -0
- package/dist/compare/render.js +64 -0
- package/dist/compare/render.js.map +1 -0
- package/dist/compare/spans.d.ts +9 -0
- package/dist/compare/spans.d.ts.map +1 -0
- package/dist/compare/spans.js +11 -0
- package/dist/compare/spans.js.map +1 -0
- package/dist/compare/text-atoms.d.ts +24 -0
- package/dist/compare/text-atoms.d.ts.map +1 -0
- package/dist/compare/text-atoms.js +67 -0
- package/dist/compare/text-atoms.js.map +1 -0
- package/dist/compare/text.d.ts +24 -0
- package/dist/compare/text.d.ts.map +1 -0
- package/dist/compare/text.js +90 -0
- package/dist/compare/text.js.map +1 -0
- package/dist/compare/tree-atoms.d.ts +20 -0
- package/dist/compare/tree-atoms.d.ts.map +1 -0
- package/dist/compare/tree-atoms.js +58 -0
- package/dist/compare/tree-atoms.js.map +1 -0
- package/dist/compare/written-names.d.ts +8 -0
- package/dist/compare/written-names.d.ts.map +1 -0
- package/dist/compare/written-names.js +24 -0
- package/dist/compare/written-names.js.map +1 -0
- package/dist/config/load.d.ts +15 -0
- package/dist/config/load.d.ts.map +1 -1
- package/dist/config/load.js +5 -0
- package/dist/config/load.js.map +1 -1
- package/dist/config/option-problems.d.ts +16 -0
- package/dist/config/option-problems.d.ts.map +1 -0
- package/dist/config/option-problems.js +58 -0
- package/dist/config/option-problems.js.map +1 -0
- package/dist/config/read.d.ts +4 -0
- package/dist/config/read.d.ts.map +1 -0
- package/dist/config/read.js +8 -0
- package/dist/config/read.js.map +1 -0
- package/dist/config/rule-problems.d.ts +2 -4
- package/dist/config/rule-problems.d.ts.map +1 -1
- package/dist/config/rule-problems.js +7 -1
- package/dist/config/rule-problems.js.map +1 -1
- package/dist/config/style.d.ts +16 -0
- package/dist/config/style.d.ts.map +1 -0
- package/dist/config/style.js +36 -0
- package/dist/config/style.js.map +1 -0
- package/dist/config/warnings.d.ts +6 -0
- package/dist/config/warnings.d.ts.map +1 -0
- package/dist/config/warnings.js +10 -0
- package/dist/config/warnings.js.map +1 -0
- package/dist/config/write.d.ts.map +1 -1
- package/dist/config/write.js +17 -4
- package/dist/config/write.js.map +1 -1
- package/dist/custom/bounded-match.d.ts +15 -0
- package/dist/custom/bounded-match.d.ts.map +1 -0
- package/dist/custom/bounded-match.js +43 -0
- package/dist/custom/bounded-match.js.map +1 -0
- package/dist/custom/load.d.ts +8 -0
- package/dist/custom/load.d.ts.map +1 -0
- package/dist/custom/load.js +11 -0
- package/dist/custom/load.js.map +1 -0
- package/dist/custom/parse.d.ts +70 -0
- package/dist/custom/parse.d.ts.map +1 -0
- package/dist/custom/parse.js +215 -0
- package/dist/custom/parse.js.map +1 -0
- package/dist/custom/problems.d.ts +5 -0
- package/dist/custom/problems.d.ts.map +1 -0
- package/dist/custom/problems.js +78 -0
- package/dist/custom/problems.js.map +1 -0
- package/dist/custom/regex-safety.d.ts +11 -0
- package/dist/custom/regex-safety.d.ts.map +1 -0
- package/dist/custom/regex-safety.js +110 -0
- package/dist/custom/regex-safety.js.map +1 -0
- package/dist/custom/token-pattern.d.ts +8 -0
- package/dist/custom/token-pattern.d.ts.map +1 -0
- package/dist/custom/token-pattern.js +75 -0
- package/dist/custom/token-pattern.js.map +1 -0
- package/dist/detect.d.ts.map +1 -1
- package/dist/detect.js +24 -2
- package/dist/detect.js.map +1 -1
- package/dist/detectors/acronym-context.d.ts +4 -0
- package/dist/detectors/acronym-context.d.ts.map +1 -1
- package/dist/detectors/acronym-context.js +25 -4
- package/dist/detectors/acronym-context.js.map +1 -1
- package/dist/detectors/acronym-expansion.d.ts +2 -0
- package/dist/detectors/acronym-expansion.d.ts.map +1 -1
- package/dist/detectors/acronym-expansion.js +104 -1
- package/dist/detectors/acronym-expansion.js.map +1 -1
- package/dist/detectors/agreement-slip.d.ts +32 -0
- package/dist/detectors/agreement-slip.d.ts.map +1 -0
- package/dist/detectors/agreement-slip.js +137 -0
- package/dist/detectors/agreement-slip.js.map +1 -0
- package/dist/detectors/ai-phrasing.d.ts +20 -0
- package/dist/detectors/ai-phrasing.d.ts.map +1 -0
- package/dist/detectors/ai-phrasing.js +112 -0
- package/dist/detectors/ai-phrasing.js.map +1 -0
- package/dist/detectors/announced-count.d.ts +4 -0
- package/dist/detectors/announced-count.d.ts.map +1 -0
- package/dist/detectors/announced-count.js +20 -0
- package/dist/detectors/announced-count.js.map +1 -0
- package/dist/detectors/broken-link.d.ts +17 -0
- package/dist/detectors/broken-link.d.ts.map +1 -0
- package/dist/detectors/broken-link.js +66 -0
- package/dist/detectors/broken-link.js.map +1 -0
- package/dist/detectors/cited-name.d.ts.map +1 -1
- package/dist/detectors/cited-name.js +4 -4
- package/dist/detectors/cited-name.js.map +1 -1
- package/dist/detectors/cited-title.d.ts +11 -0
- package/dist/detectors/cited-title.d.ts.map +1 -0
- package/dist/detectors/cited-title.js +75 -0
- package/dist/detectors/cited-title.js.map +1 -0
- package/dist/detectors/concrete-evidence.d.ts +1 -1
- package/dist/detectors/concrete-evidence.d.ts.map +1 -1
- package/dist/detectors/concrete-evidence.js +2 -1
- package/dist/detectors/concrete-evidence.js.map +1 -1
- package/dist/detectors/content-morphemes.d.ts +4 -0
- package/dist/detectors/content-morphemes.d.ts.map +1 -0
- package/dist/detectors/content-morphemes.js +12 -0
- package/dist/detectors/content-morphemes.js.map +1 -0
- package/dist/detectors/contrast-frame.d.ts +17 -0
- package/dist/detectors/contrast-frame.d.ts.map +1 -0
- package/dist/detectors/contrast-frame.js +32 -0
- package/dist/detectors/contrast-frame.js.map +1 -0
- package/dist/detectors/custom.d.ts +8 -0
- package/dist/detectors/custom.d.ts.map +1 -0
- package/dist/detectors/custom.js +71 -0
- package/dist/detectors/custom.js.map +1 -0
- package/dist/detectors/dangling-figure.d.ts +4 -0
- package/dist/detectors/dangling-figure.d.ts.map +1 -0
- package/dist/detectors/dangling-figure.js +18 -0
- package/dist/detectors/dangling-figure.js.map +1 -0
- package/dist/detectors/doubled-punctuation.d.ts +9 -0
- package/dist/detectors/doubled-punctuation.d.ts.map +1 -0
- package/dist/detectors/doubled-punctuation.js +33 -0
- package/dist/detectors/doubled-punctuation.js.map +1 -0
- package/dist/detectors/doubled-word.d.ts +5 -0
- package/dist/detectors/doubled-word.d.ts.map +1 -1
- package/dist/detectors/doubled-word.js +24 -11
- package/dist/detectors/doubled-word.js.map +1 -1
- package/dist/detectors/en-shape.d.ts +0 -1
- package/dist/detectors/en-shape.d.ts.map +1 -1
- package/dist/detectors/en-shape.js +0 -235
- package/dist/detectors/en-shape.js.map +1 -1
- package/dist/detectors/enumerated-runs.d.ts +1 -1
- package/dist/detectors/enumerated-runs.d.ts.map +1 -1
- package/dist/detectors/enumerated-runs.js +15 -4
- package/dist/detectors/enumerated-runs.js.map +1 -1
- package/dist/detectors/gram-furniture.d.ts +6 -0
- package/dist/detectors/gram-furniture.d.ts.map +1 -0
- package/dist/detectors/gram-furniture.js +21 -0
- package/dist/detectors/gram-furniture.js.map +1 -0
- package/dist/detectors/gram-place.d.ts +2 -0
- package/dist/detectors/gram-place.d.ts.map +1 -1
- package/dist/detectors/gram-place.js +9 -7
- package/dist/detectors/gram-place.js.map +1 -1
- package/dist/detectors/gram-windows.d.ts +9 -0
- package/dist/detectors/gram-windows.d.ts.map +1 -0
- package/dist/detectors/gram-windows.js +27 -0
- package/dist/detectors/gram-windows.js.map +1 -0
- package/dist/detectors/heading-case.d.ts +2 -0
- package/dist/detectors/heading-case.d.ts.map +1 -1
- package/dist/detectors/heading-case.js +29 -2
- package/dist/detectors/heading-case.js.map +1 -1
- package/dist/detectors/heading-code-words.d.ts +6 -0
- package/dist/detectors/heading-code-words.d.ts.map +1 -0
- package/dist/detectors/heading-code-words.js +29 -0
- package/dist/detectors/heading-code-words.js.map +1 -0
- package/dist/detectors/heading-echo.d.ts.map +1 -1
- package/dist/detectors/heading-echo.js +17 -20
- package/dist/detectors/heading-echo.js.map +1 -1
- package/dist/detectors/heading-level-skip.d.ts +12 -0
- package/dist/detectors/heading-level-skip.d.ts.map +1 -0
- package/dist/detectors/heading-level-skip.js +11 -0
- package/dist/detectors/heading-level-skip.js.map +1 -0
- package/dist/detectors/heading-overlap.d.ts +13 -0
- package/dist/detectors/heading-overlap.d.ts.map +1 -0
- package/dist/detectors/heading-overlap.js +52 -0
- package/dist/detectors/heading-overlap.js.map +1 -0
- package/dist/detectors/image-alt-text.d.ts +3 -0
- package/dist/detectors/image-alt-text.d.ts.map +1 -0
- package/dist/detectors/image-alt-text.js +9 -0
- package/dist/detectors/image-alt-text.js.map +1 -0
- package/dist/detectors/index.d.ts.map +1 -1
- package/dist/detectors/index.js +40 -2
- package/dist/detectors/index.js.map +1 -1
- package/dist/detectors/kutoten-consistency.d.ts +24 -0
- package/dist/detectors/kutoten-consistency.d.ts.map +1 -0
- package/dist/detectors/kutoten-consistency.js +77 -0
- package/dist/detectors/kutoten-consistency.js.map +1 -0
- package/dist/detectors/lead-in.d.ts +1 -0
- package/dist/detectors/lead-in.d.ts.map +1 -1
- package/dist/detectors/lead-in.js +2 -1
- package/dist/detectors/lead-in.js.map +1 -1
- package/dist/detectors/letter-joined-name.d.ts +13 -0
- package/dist/detectors/letter-joined-name.d.ts.map +1 -0
- package/dist/detectors/letter-joined-name.js +11 -0
- package/dist/detectors/letter-joined-name.js.map +1 -0
- package/dist/detectors/lexicon-match.d.ts +4 -0
- package/dist/detectors/lexicon-match.d.ts.map +1 -1
- package/dist/detectors/lexicon-match.js +7 -2
- package/dist/detectors/lexicon-match.js.map +1 -1
- package/dist/detectors/lexicon.d.ts.map +1 -1
- package/dist/detectors/lexicon.js +5 -2
- package/dist/detectors/lexicon.js.map +1 -1
- package/dist/detectors/list-item.d.ts +45 -0
- package/dist/detectors/list-item.d.ts.map +1 -0
- package/dist/detectors/list-item.js +88 -0
- package/dist/detectors/list-item.js.map +1 -0
- package/dist/detectors/list-items.d.ts +31 -0
- package/dist/detectors/list-items.d.ts.map +1 -0
- package/dist/detectors/list-items.js +113 -0
- package/dist/detectors/list-items.js.map +1 -0
- package/dist/detectors/list-lead-in.d.ts +7 -0
- package/dist/detectors/list-lead-in.d.ts.map +1 -0
- package/dist/detectors/list-lead-in.js +28 -0
- package/dist/detectors/list-lead-in.js.map +1 -0
- package/dist/detectors/list-sentence.d.ts +65 -0
- package/dist/detectors/list-sentence.d.ts.map +1 -0
- package/dist/detectors/list-sentence.js +105 -0
- package/dist/detectors/list-sentence.js.map +1 -0
- package/dist/detectors/list-view.d.ts +34 -0
- package/dist/detectors/list-view.d.ts.map +1 -0
- package/dist/detectors/list-view.js +137 -0
- package/dist/detectors/list-view.js.map +1 -0
- package/dist/detectors/long-vowel.d.ts +7 -0
- package/dist/detectors/long-vowel.d.ts.map +1 -0
- package/dist/detectors/long-vowel.js +57 -0
- package/dist/detectors/long-vowel.js.map +1 -0
- package/dist/detectors/markup-finding.d.ts +6 -0
- package/dist/detectors/markup-finding.d.ts.map +1 -0
- package/dist/detectors/markup-finding.js +19 -0
- package/dist/detectors/markup-finding.js.map +1 -0
- package/dist/detectors/name-numeral.d.ts +1 -1
- package/dist/detectors/name-numeral.d.ts.map +1 -1
- package/dist/detectors/name-numeral.js +2 -7
- package/dist/detectors/name-numeral.js.map +1 -1
- package/dist/detectors/orthography.d.ts.map +1 -1
- package/dist/detectors/orthography.js +16 -12
- package/dist/detectors/orthography.js.map +1 -1
- package/dist/detectors/oxford-comma.d.ts +9 -0
- package/dist/detectors/oxford-comma.d.ts.map +1 -0
- package/dist/detectors/oxford-comma.js +246 -0
- package/dist/detectors/oxford-comma.js.map +1 -0
- package/dist/detectors/place-run.d.ts.map +1 -1
- package/dist/detectors/place-run.js +10 -6
- package/dist/detectors/place-run.js.map +1 -1
- package/dist/detectors/placeholder-text.d.ts +9 -0
- package/dist/detectors/placeholder-text.d.ts.map +1 -0
- package/dist/detectors/placeholder-text.js +18 -0
- package/dist/detectors/placeholder-text.js.map +1 -0
- package/dist/detectors/quote-around.d.ts +3 -0
- package/dist/detectors/quote-around.d.ts.map +1 -0
- package/dist/detectors/quote-around.js +8 -0
- package/dist/detectors/quote-around.js.map +1 -0
- package/dist/detectors/register.d.ts +11 -3
- package/dist/detectors/register.d.ts.map +1 -1
- package/dist/detectors/register.js +28 -9
- package/dist/detectors/register.js.map +1 -1
- package/dist/detectors/roman-numeral.d.ts +2 -0
- package/dist/detectors/roman-numeral.d.ts.map +1 -1
- package/dist/detectors/roman-numeral.js +2 -0
- package/dist/detectors/roman-numeral.js.map +1 -1
- package/dist/detectors/sentence-ending.d.ts +17 -1
- package/dist/detectors/sentence-ending.d.ts.map +1 -1
- package/dist/detectors/sentence-ending.js +13 -7
- package/dist/detectors/sentence-ending.js.map +1 -1
- package/dist/detectors/sentence-length.d.ts.map +1 -1
- package/dist/detectors/sentence-length.js +13 -4
- package/dist/detectors/sentence-length.js.map +1 -1
- package/dist/detectors/signals.d.ts +8 -0
- package/dist/detectors/signals.d.ts.map +1 -1
- package/dist/detectors/signals.js +94 -54
- package/dist/detectors/signals.js.map +1 -1
- package/dist/detectors/stacked-hedge.d.ts +0 -7
- package/dist/detectors/stacked-hedge.d.ts.map +1 -1
- package/dist/detectors/stacked-hedge.js +6 -3
- package/dist/detectors/stacked-hedge.js.map +1 -1
- package/dist/detectors/stray-space.d.ts +7 -0
- package/dist/detectors/stray-space.d.ts.map +1 -0
- package/dist/detectors/stray-space.js +34 -0
- package/dist/detectors/stray-space.js.map +1 -0
- package/dist/detectors/structure-tree.d.ts +6 -0
- package/dist/detectors/structure-tree.d.ts.map +1 -1
- package/dist/detectors/structure-tree.js +37 -1
- package/dist/detectors/structure-tree.js.map +1 -1
- package/dist/detectors/structure.d.ts +4 -4
- package/dist/detectors/structure.d.ts.map +1 -1
- package/dist/detectors/structure.js +7 -6
- package/dist/detectors/structure.js.map +1 -1
- package/dist/detectors/superlative-name.d.ts.map +1 -1
- package/dist/detectors/superlative-name.js +8 -5
- package/dist/detectors/superlative-name.js.map +1 -1
- package/dist/detectors/superlative-scope.d.ts.map +1 -1
- package/dist/detectors/superlative-scope.js +9 -4
- package/dist/detectors/superlative-scope.js.map +1 -1
- package/dist/detectors/token-column.d.ts +46 -0
- package/dist/detectors/token-column.d.ts.map +1 -0
- package/dist/detectors/token-column.js +95 -0
- package/dist/detectors/token-column.js.map +1 -0
- package/dist/detectors/unbalanced-bracket.d.ts +22 -0
- package/dist/detectors/unbalanced-bracket.d.ts.map +1 -0
- package/dist/detectors/unbalanced-bracket.js +119 -0
- package/dist/detectors/unbalanced-bracket.js.map +1 -0
- package/dist/detectors/url-run-on.d.ts +9 -0
- package/dist/detectors/url-run-on.d.ts.map +1 -0
- package/dist/detectors/url-run-on.js +19 -0
- package/dist/detectors/url-run-on.js.map +1 -0
- package/dist/document-sections.d.ts +13 -0
- package/dist/document-sections.d.ts.map +1 -0
- package/dist/document-sections.js +32 -0
- package/dist/document-sections.js.map +1 -0
- package/dist/document.d.ts +11 -4
- package/dist/document.d.ts.map +1 -1
- package/dist/document.js +99 -95
- package/dist/document.js.map +1 -1
- package/dist/email-parts.d.ts +28 -0
- package/dist/email-parts.d.ts.map +1 -0
- package/dist/email-parts.js +230 -0
- package/dist/email-parts.js.map +1 -0
- package/dist/eval.d.ts +2 -1
- package/dist/eval.d.ts.map +1 -1
- package/dist/eval.js +16 -4
- package/dist/eval.js.map +1 -1
- package/dist/figure-references.d.ts +30 -0
- package/dist/figure-references.d.ts.map +1 -0
- package/dist/figure-references.js +76 -0
- package/dist/figure-references.js.map +1 -0
- package/dist/files.d.ts +2 -0
- package/dist/files.d.ts.map +1 -1
- package/dist/files.js +4 -0
- package/dist/files.js.map +1 -1
- package/dist/genre-parse.d.ts +5 -1
- package/dist/genre-parse.d.ts.map +1 -1
- package/dist/genre-parse.js +16 -3
- package/dist/genre-parse.js.map +1 -1
- package/dist/genre-suggest.d.ts.map +1 -1
- package/dist/genre-suggest.js +15 -1
- package/dist/genre-suggest.js.map +1 -1
- package/dist/heading-label.d.ts +22 -0
- package/dist/heading-label.d.ts.map +1 -0
- package/dist/heading-label.js +50 -0
- package/dist/heading-label.js.map +1 -0
- package/dist/in-page-nav.d.ts +4 -1
- package/dist/in-page-nav.d.ts.map +1 -1
- package/dist/in-page-nav.js +53 -18
- package/dist/in-page-nav.js.map +1 -1
- package/dist/joined-view.d.ts.map +1 -1
- package/dist/joined-view.js +1 -0
- package/dist/joined-view.js.map +1 -1
- package/dist/levels.d.ts +5 -1
- package/dist/levels.d.ts.map +1 -1
- package/dist/levels.js +9 -0
- package/dist/levels.js.map +1 -1
- package/dist/line-paragraphs.d.ts +8 -0
- package/dist/line-paragraphs.d.ts.map +1 -1
- package/dist/line-paragraphs.js +36 -11
- package/dist/line-paragraphs.js.map +1 -1
- package/dist/long-vowel.d.ts +35 -0
- package/dist/long-vowel.d.ts.map +1 -0
- package/dist/long-vowel.js +71 -0
- package/dist/long-vowel.js.map +1 -0
- package/dist/markdown-node.d.ts +19 -0
- package/dist/markdown-node.d.ts.map +1 -0
- package/dist/markdown-node.js +6 -0
- package/dist/markdown-node.js.map +1 -0
- package/dist/markdown-read.d.ts +17 -0
- package/dist/markdown-read.d.ts.map +1 -0
- package/dist/markdown-read.js +37 -0
- package/dist/markdown-read.js.map +1 -0
- package/dist/markup.d.ts +22 -0
- package/dist/markup.d.ts.map +1 -0
- package/dist/markup.js +180 -0
- package/dist/markup.js.map +1 -0
- package/dist/mask.d.ts.map +1 -1
- package/dist/mask.js +10 -16
- package/dist/mask.js.map +1 -1
- package/dist/number-name.d.ts +21 -5
- package/dist/number-name.d.ts.map +1 -1
- package/dist/number-name.js +120 -18
- package/dist/number-name.js.map +1 -1
- package/dist/opening-line.d.ts +3 -0
- package/dist/opening-line.d.ts.map +1 -0
- package/dist/opening-line.js +14 -0
- package/dist/opening-line.js.map +1 -0
- package/dist/orthography.d.ts +7 -1
- package/dist/orthography.d.ts.map +1 -1
- package/dist/orthography.js +16 -5
- package/dist/orthography.js.map +1 -1
- package/dist/page-furniture.d.ts +5 -2
- package/dist/page-furniture.d.ts.map +1 -1
- package/dist/page-furniture.js +8 -2
- package/dist/page-furniture.js.map +1 -1
- package/dist/paragraph-length.d.ts +7 -0
- package/dist/paragraph-length.d.ts.map +1 -0
- package/dist/paragraph-length.js +10 -0
- package/dist/paragraph-length.js.map +1 -0
- package/dist/plain-source.d.ts +1 -0
- package/dist/plain-source.d.ts.map +1 -1
- package/dist/plain-source.js +2 -1
- package/dist/plain-source.js.map +1 -1
- package/dist/plugin.d.ts +121 -0
- package/dist/plugin.d.ts.map +1 -1
- package/dist/profile/for-file.d.ts.map +1 -1
- package/dist/profile/for-file.js +3 -1
- package/dist/profile/for-file.js.map +1 -1
- package/dist/quoted-span.d.ts +0 -2
- package/dist/quoted-span.d.ts.map +1 -1
- package/dist/quoted-span.js +9 -4
- package/dist/quoted-span.js.map +1 -1
- package/dist/reasons.d.ts +3 -0
- package/dist/reasons.d.ts.map +1 -1
- package/dist/reasons.js +6 -0
- package/dist/reasons.js.map +1 -1
- package/dist/reference-lists.d.ts +3 -0
- package/dist/reference-lists.d.ts.map +1 -0
- package/dist/reference-lists.js +75 -0
- package/dist/reference-lists.js.map +1 -0
- package/dist/render/explain.d.ts +7 -1
- package/dist/render/explain.d.ts.map +1 -1
- package/dist/render/explain.js +36 -12
- package/dist/render/explain.js.map +1 -1
- package/dist/render/friendly.d.ts.map +1 -1
- package/dist/render/friendly.js +19 -9
- package/dist/render/friendly.js.map +1 -1
- package/dist/render/options.d.ts +7 -0
- package/dist/render/options.d.ts.map +1 -0
- package/dist/render/options.js +50 -0
- package/dist/render/options.js.map +1 -0
- package/dist/render/rules-json.d.ts +4 -1
- package/dist/render/rules-json.d.ts.map +1 -1
- package/dist/render/rules-json.js +102 -12
- package/dist/render/rules-json.js.map +1 -1
- package/dist/render/rules-table.d.ts +4 -0
- package/dist/render/rules-table.d.ts.map +1 -0
- package/dist/render/rules-table.js +70 -0
- package/dist/render/rules-table.js.map +1 -0
- package/dist/render/sarif.js +4 -4
- package/dist/render/sarif.js.map +1 -1
- package/dist/render/semantic.js +3 -3
- package/dist/render/semantic.js.map +1 -1
- package/dist/render/severity-name.d.ts +6 -0
- package/dist/render/severity-name.d.ts.map +1 -0
- package/dist/render/severity-name.js +7 -0
- package/dist/render/severity-name.js.map +1 -0
- package/dist/render/text.d.ts +7 -2
- package/dist/render/text.d.ts.map +1 -1
- package/dist/render/text.js +15 -4
- package/dist/render/text.js.map +1 -1
- package/dist/resolve-genre.d.ts +1 -1
- package/dist/resolve-genre.d.ts.map +1 -1
- package/dist/resolve-genre.js +4 -1
- package/dist/resolve-genre.js.map +1 -1
- package/dist/rule-genres.d.ts +24 -0
- package/dist/rule-genres.d.ts.map +1 -0
- package/dist/rule-genres.js +11 -0
- package/dist/rule-genres.js.map +1 -0
- package/dist/rule-guide.d.ts +54 -0
- package/dist/rule-guide.d.ts.map +1 -0
- package/dist/rule-guide.js +81 -0
- package/dist/rule-guide.js.map +1 -0
- package/dist/rule-load.d.ts.map +1 -1
- package/dist/rule-load.js +94 -32
- package/dist/rule-load.js.map +1 -1
- package/dist/rule-options.d.ts +50 -0
- package/dist/rule-options.d.ts.map +1 -0
- package/dist/rule-options.js +106 -0
- package/dist/rule-options.js.map +1 -0
- package/dist/run-semantic.d.ts +3 -1
- package/dist/run-semantic.d.ts.map +1 -1
- package/dist/run-semantic.js +8 -3
- package/dist/run-semantic.js.map +1 -1
- package/dist/run.d.ts +15 -0
- package/dist/run.d.ts.map +1 -1
- package/dist/run.js +57 -10
- package/dist/run.js.map +1 -1
- package/dist/semantic.d.ts +1 -1
- package/dist/semantic.d.ts.map +1 -1
- package/dist/semantic.js +10 -3
- package/dist/semantic.js.map +1 -1
- package/dist/sentence-shape.d.ts.map +1 -1
- package/dist/sentence-shape.js +4 -4
- package/dist/sentence-shape.js.map +1 -1
- package/dist/setting-problems.d.ts +2 -1
- package/dist/setting-problems.d.ts.map +1 -1
- package/dist/setting-problems.js +6 -1
- package/dist/setting-problems.js.map +1 -1
- package/dist/span-cut.d.ts +6 -0
- package/dist/span-cut.d.ts.map +1 -0
- package/dist/span-cut.js +22 -0
- package/dist/span-cut.js.map +1 -0
- package/dist/span-merge.d.ts +4 -0
- package/dist/span-merge.d.ts.map +1 -0
- package/dist/span-merge.js +12 -0
- package/dist/span-merge.js.map +1 -0
- package/dist/speaker-labels.d.ts +4 -0
- package/dist/speaker-labels.d.ts.map +1 -0
- package/dist/speaker-labels.js +106 -0
- package/dist/speaker-labels.js.map +1 -0
- package/dist/stray-space.d.ts +17 -0
- package/dist/stray-space.d.ts.map +1 -0
- package/dist/stray-space.js +66 -0
- package/dist/stray-space.js.map +1 -0
- package/dist/structure/aside-depth.d.ts +21 -0
- package/dist/structure/aside-depth.d.ts.map +1 -0
- package/dist/structure/aside-depth.js +65 -0
- package/dist/structure/aside-depth.js.map +1 -0
- package/dist/structure/build.d.ts +5 -1
- package/dist/structure/build.d.ts.map +1 -1
- package/dist/structure/build.js +29 -17
- package/dist/structure/build.js.map +1 -1
- package/dist/structure/closed-pairs.d.ts +8 -0
- package/dist/structure/closed-pairs.d.ts.map +1 -0
- package/dist/structure/closed-pairs.js +22 -0
- package/dist/structure/closed-pairs.js.map +1 -0
- package/dist/structure/date-range.d.ts +21 -0
- package/dist/structure/date-range.d.ts.map +1 -0
- package/dist/structure/date-range.js +45 -0
- package/dist/structure/date-range.js.map +1 -0
- package/dist/structure/known-mentions.d.ts +18 -0
- package/dist/structure/known-mentions.d.ts.map +1 -0
- package/dist/structure/known-mentions.js +78 -0
- package/dist/structure/known-mentions.js.map +1 -0
- package/dist/structure/numbering.d.ts +1 -0
- package/dist/structure/numbering.d.ts.map +1 -1
- package/dist/structure/numbering.js +11 -6
- package/dist/structure/numbering.js.map +1 -1
- package/dist/structure/of.d.ts +3 -1
- package/dist/structure/of.d.ts.map +1 -1
- package/dist/structure/of.js +5 -1
- package/dist/structure/of.js.map +1 -1
- package/dist/structure/percent-sum.d.ts +16 -0
- package/dist/structure/percent-sum.d.ts.map +1 -0
- package/dist/structure/percent-sum.js +104 -0
- package/dist/structure/percent-sum.js.map +1 -0
- package/dist/structure/quoted-number.d.ts +8 -0
- package/dist/structure/quoted-number.d.ts.map +1 -0
- package/dist/structure/quoted-number.js +34 -0
- package/dist/structure/quoted-number.js.map +1 -0
- package/dist/structure/relative-find.d.ts.map +1 -1
- package/dist/structure/relative-find.js +34 -46
- package/dist/structure/relative-find.js.map +1 -1
- package/dist/structure/relative-resolve.d.ts.map +1 -1
- package/dist/structure/relative-resolve.js +94 -42
- package/dist/structure/relative-resolve.js.map +1 -1
- package/dist/structure/runs.d.ts +2 -0
- package/dist/structure/runs.d.ts.map +1 -1
- package/dist/structure/runs.js +2 -2
- package/dist/structure/runs.js.map +1 -1
- package/dist/structure/sexp.d.ts +1 -1
- package/dist/structure/sexp.d.ts.map +1 -1
- package/dist/structure/sexp.js +18 -6
- package/dist/structure/sexp.js.map +1 -1
- package/dist/structure/total.d.ts +3 -0
- package/dist/structure/total.d.ts.map +1 -1
- package/dist/structure/total.js +2 -2
- package/dist/structure/total.js.map +1 -1
- package/dist/structure/universal.d.ts +14 -2
- package/dist/structure/universal.d.ts.map +1 -1
- package/dist/structure/universal.js +17 -2
- package/dist/structure/universal.js.map +1 -1
- package/dist/structure/wrapped-tail.d.ts +14 -7
- package/dist/structure/wrapped-tail.d.ts.map +1 -1
- package/dist/structure/wrapped-tail.js +33 -8
- package/dist/structure/wrapped-tail.js.map +1 -1
- package/dist/style-load.d.ts +4 -0
- package/dist/style-load.d.ts.map +1 -0
- package/dist/style-load.js +28 -0
- package/dist/style-load.js.map +1 -0
- package/dist/style-parse.d.ts +19 -0
- package/dist/style-parse.d.ts.map +1 -0
- package/dist/style-parse.js +60 -0
- package/dist/style-parse.js.map +1 -0
- package/dist/subheading-line.d.ts +6 -0
- package/dist/subheading-line.d.ts.map +1 -0
- package/dist/subheading-line.js +57 -0
- package/dist/subheading-line.js.map +1 -0
- package/dist/team-names.d.ts.map +1 -1
- package/dist/team-names.js +11 -14
- package/dist/team-names.js.map +1 -1
- package/dist/template-syntax.d.ts +17 -0
- package/dist/template-syntax.d.ts.map +1 -0
- package/dist/template-syntax.js +140 -0
- package/dist/template-syntax.js.map +1 -0
- package/dist/text-above.d.ts +19 -0
- package/dist/text-above.d.ts.map +1 -0
- package/dist/text-above.js +43 -0
- package/dist/text-above.js.map +1 -0
- package/dist/text-figures.d.ts +10 -0
- package/dist/text-figures.d.ts.map +1 -0
- package/dist/text-figures.js +131 -0
- package/dist/text-figures.js.map +1 -0
- package/dist/trailing-aside.d.ts +2 -0
- package/dist/trailing-aside.d.ts.map +1 -0
- package/dist/trailing-aside.js +53 -0
- package/dist/trailing-aside.js.map +1 -0
- package/dist/tree-walk.d.ts +18 -0
- package/dist/tree-walk.d.ts.map +1 -0
- package/dist/tree-walk.js +45 -0
- package/dist/tree-walk.js.map +1 -0
- package/genres.yaml +60 -2
- package/package.json +5 -3
- package/reference-headings.yaml +17 -0
- package/rules/adverb-overuse.yaml +19 -0
- package/rules/agentless-passive.yaml +19 -0
- package/rules/agreement-slip.yaml +49 -0
- package/rules/ai-generated-composite.yaml +102 -0
- package/rules/ai-tell.yaml +23 -0
- package/rules/announced-count-mismatch.yaml +57 -0
- package/rules/announcing-opener.yaml +51 -0
- package/rules/assistant-residue.yaml +52 -0
- package/rules/bold-density.yaml +31 -0
- package/rules/broken-link.yaml +65 -0
- package/rules/closing-cliche.yaml +33 -0
- package/rules/colon-lead-in.yaml +59 -0
- package/rules/concrete-evidence-density.yaml +41 -0
- package/rules/contraction-consistency.yaml +18 -0
- package/rules/contrast-framing.yaml +57 -0
- package/rules/cushion-phrase-density.yaml +25 -0
- package/rules/dangling-figure-reference.yaml +61 -0
- package/rules/dangling-reference.yaml +53 -1
- package/rules/date-order.yaml +33 -1
- package/rules/date-range-reversed.yaml +46 -0
- package/rules/date-weekday-mismatch.yaml +17 -1
- package/rules/double-keigo.yaml +16 -0
- package/rules/doubled-punctuation.yaml +45 -0
- package/rules/doubled-word.yaml +17 -1
- package/rules/duplicate-definition.yaml +59 -1
- package/rules/emoji-density.yaml +25 -0
- package/rules/empty-conclusion.yaml +29 -0
- package/rules/empty-intensifier.yaml +19 -0
- package/rules/excessive-hedging.yaml +19 -0
- package/rules/expletive-construction.yaml +19 -0
- package/rules/heading-echo.yaml +32 -0
- package/rules/heading-level-skip.yaml +78 -0
- package/rules/hiragana-fukushi.yaml +16 -0
- package/rules/image-alt-text.yaml +54 -0
- package/rules/internal-jargon.yaml +21 -0
- package/rules/katakana-long-vowel.yaml +98 -0
- package/rules/kutoten-consistency.yaml +53 -0
- package/rules/latin-spacing.yaml +23 -4
- package/rules/max-kanji-continuous.yaml +16 -0
- package/rules/max-paragraph-length.yaml +42 -2
- package/rules/max-sentence-length.yaml +36 -0
- package/rules/ngram-repetition.yaml +29 -2
- package/rules/no-doubled-joshi.yaml +16 -0
- package/rules/no-em-dash.yaml +25 -0
- package/rules/no-mixed-desumasu.yaml +27 -2
- package/rules/no-nakaguro-parallel.yaml +20 -0
- package/rules/numbering-gap.yaml +53 -1
- package/rules/oxford-comma-consistency.yaml +19 -1
- package/rules/padded-intro.yaml +19 -0
- package/rules/paragraph-length-variance.yaml +49 -0
- package/rules/percent-sum-mismatch.yaml +58 -0
- package/rules/preamble-length.yaml +56 -0
- package/rules/preferred-term.yaml +27 -0
- package/rules/proper-noun-density.yaml +26 -0
- package/rules/repeated-conjunction.yaml +47 -0
- package/rules/repeated-sentence-head.yaml +23 -0
- package/rules/required-sections.yaml +49 -0
- package/rules/risk-disclosure.yaml +22 -0
- package/rules/rule-of-three.yaml +97 -0
- package/rules/sasete-itadaku.yaml +18 -0
- package/rules/section-length-uniformity.yaml +89 -0
- package/rules/sentence-initial-conjunction-run.yaml +18 -0
- package/rules/sentence-rhythm.yaml +25 -0
- package/rules/stock-transition.yaml +55 -0
- package/rules/stray-space.yaml +56 -0
- package/rules/taigen-dome-in-prose.yaml +19 -0
- package/rules/title-case-consistency.yaml +42 -0
- package/rules/total-mismatch.yaml +37 -1
- package/rules/unbalanced-bracket.yaml +56 -0
- package/rules/undefined-acronym.yaml +23 -0
- package/rules/unfilled-placeholder.yaml +50 -0
- package/rules/unqualified-superlative.yaml +19 -0
- package/rules/unsourced-number.yaml +16 -0
- package/rules/url-run-on.yaml +45 -0
- package/skills/chaff/SKILL.md +70 -0
- package/src/announced-count.ts +148 -0
- package/src/bare-url.ts +35 -0
- package/src/baseline.ts +17 -1
- package/src/body-section.ts +7 -0
- package/src/calendar-number.ts +33 -10
- package/src/cli-args.ts +6 -0
- package/src/cli-text.ts +8 -4
- package/src/cli.ts +37 -37
- package/src/closing-numbers.ts +72 -0
- package/src/commands/compare.ts +80 -0
- package/src/commands/eval.ts +19 -8
- package/src/commands/feedback.ts +12 -6
- package/src/commands/test.ts +20 -17
- package/src/commands/tree.ts +41 -5
- package/src/compare/atom.ts +33 -0
- package/src/compare/clock-time.ts +86 -0
- package/src/compare/extract.ts +78 -0
- package/src/compare/fact-text.ts +68 -0
- package/src/compare/markup-atoms.ts +29 -0
- package/src/compare/match.ts +105 -0
- package/src/compare/outcome.ts +70 -0
- package/src/compare/proper-nouns.ts +65 -0
- package/src/compare/render.ts +102 -0
- package/src/compare/spans.ts +17 -0
- package/src/compare/text-atoms.ts +95 -0
- package/src/compare/text.ts +120 -0
- package/src/compare/tree-atoms.ts +80 -0
- package/src/compare/written-names.ts +26 -0
- package/src/config/load.ts +21 -0
- package/src/config/option-problems.ts +70 -0
- package/src/config/read.ts +8 -0
- package/src/config/rule-problems.ts +12 -2
- package/src/config/style.ts +42 -0
- package/src/config/warnings.ts +13 -0
- package/src/config/write.ts +18 -4
- package/src/custom/bounded-match.ts +58 -0
- package/src/custom/load.ts +15 -0
- package/src/custom/parse.ts +274 -0
- package/src/custom/problems.ts +89 -0
- package/src/custom/regex-safety.ts +118 -0
- package/src/custom/token-pattern.ts +84 -0
- package/src/detect.ts +28 -2
- package/src/detectors/acronym-context.ts +32 -4
- package/src/detectors/acronym-expansion.ts +132 -1
- package/src/detectors/agreement-slip.ts +191 -0
- package/src/detectors/ai-phrasing.ts +137 -0
- package/src/detectors/announced-count.ts +25 -0
- package/src/detectors/broken-link.ts +90 -0
- package/src/detectors/cited-name.ts +6 -5
- package/src/detectors/cited-title.ts +96 -0
- package/src/detectors/concrete-evidence.ts +3 -2
- package/src/detectors/content-morphemes.ts +19 -0
- package/src/detectors/contrast-frame.ts +54 -0
- package/src/detectors/custom.ts +89 -0
- package/src/detectors/dangling-figure.ts +22 -0
- package/src/detectors/doubled-punctuation.ts +49 -0
- package/src/detectors/doubled-word.ts +19 -4
- package/src/detectors/en-shape.ts +0 -269
- package/src/detectors/enumerated-runs.ts +13 -4
- package/src/detectors/gram-furniture.ts +30 -0
- package/src/detectors/gram-place.ts +9 -5
- package/src/detectors/gram-windows.ts +31 -0
- package/src/detectors/heading-case.ts +30 -2
- package/src/detectors/heading-code-words.ts +35 -0
- package/src/detectors/heading-echo.ts +18 -22
- package/src/detectors/heading-level-skip.ts +19 -0
- package/src/detectors/heading-overlap.ts +65 -0
- package/src/detectors/image-alt-text.ts +11 -0
- package/src/detectors/index.ts +49 -2
- package/src/detectors/kutoten-consistency.ts +96 -0
- package/src/detectors/lead-in.ts +3 -1
- package/src/detectors/letter-joined-name.ts +15 -0
- package/src/detectors/lexicon-match.ts +9 -2
- package/src/detectors/lexicon.ts +6 -2
- package/src/detectors/list-item.ts +122 -0
- package/src/detectors/list-items.ts +163 -0
- package/src/detectors/list-lead-in.ts +33 -0
- package/src/detectors/list-sentence.ts +157 -0
- package/src/detectors/list-view.ts +157 -0
- package/src/detectors/long-vowel.ts +66 -0
- package/src/detectors/markup-finding.ts +24 -0
- package/src/detectors/name-numeral.ts +4 -2
- package/src/detectors/orthography.ts +24 -18
- package/src/detectors/oxford-comma.ts +297 -0
- package/src/detectors/place-run.ts +7 -4
- package/src/detectors/placeholder-text.ts +27 -0
- package/src/detectors/quote-around.ts +9 -0
- package/src/detectors/register.ts +37 -10
- package/src/detectors/roman-numeral.ts +3 -0
- package/src/detectors/sentence-ending.ts +14 -7
- package/src/detectors/sentence-length.ts +16 -5
- package/src/detectors/signals.ts +110 -53
- package/src/detectors/stacked-hedge.ts +6 -3
- package/src/detectors/stray-space.ts +40 -0
- package/src/detectors/structure-tree.ts +45 -1
- package/src/detectors/structure.ts +13 -6
- package/src/detectors/superlative-name.ts +7 -4
- package/src/detectors/superlative-scope.ts +8 -3
- package/src/detectors/token-column.ts +149 -0
- package/src/detectors/unbalanced-bracket.ts +153 -0
- package/src/detectors/url-run-on.ts +27 -0
- package/src/document-sections.ts +46 -0
- package/src/document.ts +116 -127
- package/src/email-parts.ts +269 -0
- package/src/eval.ts +14 -4
- package/src/figure-references.ts +112 -0
- package/src/files.ts +5 -0
- package/src/genre-parse.ts +30 -5
- package/src/genre-suggest.ts +15 -2
- package/src/heading-label.ts +55 -0
- package/src/in-page-nav.ts +48 -19
- package/src/joined-view.ts +1 -0
- package/src/levels.ts +14 -1
- package/src/line-paragraphs.ts +56 -10
- package/src/long-vowel.ts +99 -0
- package/src/markdown-node.ts +19 -0
- package/src/markdown-read.ts +44 -0
- package/src/markup.ts +210 -0
- package/src/mask.ts +10 -19
- package/src/number-name.ts +152 -25
- package/src/opening-line.ts +17 -0
- package/src/orthography.ts +20 -5
- package/src/page-furniture.ts +11 -2
- package/src/paragraph-length.ts +9 -0
- package/src/plain-source.ts +2 -1
- package/src/plugin.ts +107 -1
- package/src/profile/for-file.ts +3 -1
- package/src/quoted-span.ts +10 -4
- package/src/reasons.ts +9 -0
- package/src/reference-lists.ts +91 -0
- package/src/render/explain.ts +47 -13
- package/src/render/friendly.ts +20 -9
- package/src/render/options.ts +58 -0
- package/src/render/rules-json.ts +130 -14
- package/src/render/rules-table.ts +91 -0
- package/src/render/sarif.ts +4 -4
- package/src/render/semantic.ts +3 -3
- package/src/render/severity-name.ts +10 -0
- package/src/render/text.ts +21 -7
- package/src/resolve-genre.ts +4 -1
- package/src/rule-genres.ts +25 -0
- package/src/rule-guide.ts +135 -0
- package/src/rule-load.ts +98 -33
- package/src/rule-options.ts +131 -0
- package/src/run-semantic.ts +28 -20
- package/src/run.ts +78 -11
- package/src/semantic.ts +11 -3
- package/src/sentence-shape.ts +4 -5
- package/src/setting-problems.ts +7 -1
- package/src/span-cut.ts +28 -0
- package/src/span-merge.ts +13 -0
- package/src/speaker-labels.ts +129 -0
- package/src/stray-space.ts +81 -0
- package/src/structure/aside-depth.ts +67 -0
- package/src/structure/build.ts +38 -19
- package/src/structure/closed-pairs.ts +20 -0
- package/src/structure/date-range.ts +69 -0
- package/src/structure/known-mentions.ts +96 -0
- package/src/structure/numbering.ts +9 -5
- package/src/structure/of.ts +11 -3
- package/src/structure/percent-sum.ts +135 -0
- package/src/structure/quoted-number.ts +36 -0
- package/src/structure/relative-find.ts +34 -47
- package/src/structure/relative-resolve.ts +104 -42
- package/src/structure/runs.ts +2 -2
- package/src/structure/sexp.ts +17 -5
- package/src/structure/total.ts +2 -2
- package/src/structure/universal.ts +24 -4
- package/src/structure/wrapped-tail.ts +38 -13
- package/src/style-load.ts +28 -0
- package/src/style-parse.ts +77 -0
- package/src/subheading-line.ts +68 -0
- package/src/team-names.ts +13 -13
- package/src/template-syntax.ts +164 -0
- package/src/text-above.ts +49 -0
- package/src/text-figures.ts +147 -0
- package/src/trailing-aside.ts +50 -0
- package/src/tree-walk.ts +48 -0
- package/styles/bunkacho.yaml +25 -0
- package/styles/ieice.yaml +29 -0
- package/styles/jis-z8301-2011.yaml +25 -0
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { Lexicon, LexiconEntry, Sentence } from "../plugin.ts";
|
|
2
|
+
import { escapeRegExp } from "../orthography.ts";
|
|
3
|
+
import { comparableText, comparableWords, entryIn, entryOpens, entryRanges } from "./lexicon-match.ts";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* 対比の枠の語。frames は 1 つで枠になる語(not only、だけでなく)。
|
|
7
|
+
* leads は打ち消し(it's not、単なる)で、turns(it's、but、ではなく)が後ろに来たときだけ枠になる。同じ文の後ろか、次の文の頭。
|
|
8
|
+
* 打ち消しの頭に立つ返しの語(It's not ready. It's not tested. の 2 つ目の It's)は返しではなく、次の打ち消し。
|
|
9
|
+
*/
|
|
10
|
+
export type ContrastWords = { readonly frames: Lexicon; readonly leads: Lexicon; readonly turns: Lexicon };
|
|
11
|
+
|
|
12
|
+
const lowered = (entry: LexiconEntry): string => comparableWords(entry.pattern);
|
|
13
|
+
|
|
14
|
+
type Range = { readonly start: number; readonly end: number };
|
|
15
|
+
|
|
16
|
+
/** 返しの語が、それより長い打ち消しの頭にあるか(It's not の It's)。同じ長さなら返しのまま(ではなく は打ち消しでも返しでもある)。 */
|
|
17
|
+
const opensLongerLead = (found: Range, leads: readonly Range[]): boolean => leads.some((lead) => lead.start === found.start && lead.end > found.end);
|
|
18
|
+
|
|
19
|
+
const answersAfter = (leadRange: Range, turns: readonly Range[], leads: readonly Range[]): boolean =>
|
|
20
|
+
turns.some((found) => found.start >= leadRange.end && !opensLongerLead(found, leads));
|
|
21
|
+
|
|
22
|
+
/** 文字列の中で word が現れる範囲、左から順に。 */
|
|
23
|
+
const rangesOf = (text: string, word: string): Range[] =>
|
|
24
|
+
[...text.matchAll(new RegExp(escapeRegExp(word), "gu"))].map((match) => ({ start: match.index, end: match.index + word.length }));
|
|
25
|
+
|
|
26
|
+
/** 品詞が無ければ位置を語で言えないので、空白をまとめた文字列の位置で比べる。 */
|
|
27
|
+
const textTurnAfter = (sentence: Sentence, lead: LexiconEntry, words: ContrastWords): boolean => {
|
|
28
|
+
const text = comparableText(sentence);
|
|
29
|
+
const leads = words.leads.flatMap((entry) => rangesOf(text, lowered(entry)));
|
|
30
|
+
const turns = words.turns.flatMap((entry) => rangesOf(text, lowered(entry)));
|
|
31
|
+
return rangesOf(text, lowered(lead)).some((range) => answersAfter(range, turns, leads));
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
/** 打ち消しの語より後ろに、返しの語があるか。打ち消しそのもの(it's not の it's)と、次の打ち消しの頭は返しに数えない。 */
|
|
35
|
+
const turnAfter = (sentence: Sentence, lead: LexiconEntry, words: ContrastWords): boolean => {
|
|
36
|
+
if (sentence.tokens === undefined || lead.tokens === undefined) return textTurnAfter(sentence, lead, words);
|
|
37
|
+
const leads = words.leads.flatMap((entry) => entryRanges(sentence, entry));
|
|
38
|
+
const turns = words.turns.flatMap((entry) => entryRanges(sentence, entry));
|
|
39
|
+
return entryRanges(sentence, lead).some((range) => answersAfter(range, turns, leads));
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
/** 次の文が返しの語で始まり、それ自体が打ち消しで始まっていないか。 */
|
|
43
|
+
const nextTurns = (next: Sentence | undefined, words: ContrastWords): boolean =>
|
|
44
|
+
next !== undefined && words.turns.some((turn) => entryOpens(next, turn)) && !words.leads.some((lead) => entryOpens(next, lead));
|
|
45
|
+
|
|
46
|
+
const answered = (sentence: Sentence, next: Sentence | undefined, words: ContrastWords): boolean =>
|
|
47
|
+
words.leads.some((lead) => entryIn(sentence, lead) && (turnAfter(sentence, lead, words) || nextTurns(next, words)));
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* 対比の枠で書いた文。「X ではなく Y」「It's not X. It's Y.」の形で、打ち消した側の文を返す。
|
|
51
|
+
* 2 文にまたがる枠も 1 つと数える。返しの文は数えない。
|
|
52
|
+
*/
|
|
53
|
+
export const contrastSentences = (sentences: readonly Sentence[], words: ContrastWords): Sentence[] =>
|
|
54
|
+
sentences.filter((sentence, index) => words.frames.some((frame) => entryIn(sentence, frame)) || answered(sentence, sentences[index + 1], words));
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import type { Detector, Finding, Sentence, Span } from "../plugin.ts";
|
|
2
|
+
import { occurrencesOutside } from "../orthography.ts";
|
|
3
|
+
import { tokenRuns } from "../custom/token-pattern.ts";
|
|
4
|
+
import { joinedView, type JoinedView } from "../joined-view.ts";
|
|
5
|
+
import { boundedMatches } from "../custom/bounded-match.ts";
|
|
6
|
+
|
|
7
|
+
// The detectors behind a team's custom_rules. Each reports every place it finds, at the rule's level; the spec comes from
|
|
8
|
+
// the rule (DetectorOptions.custom). Positions are the document's, like every other finding.
|
|
9
|
+
|
|
10
|
+
type Hit = { readonly sentence: Sentence; readonly offset: number; readonly matched: string; readonly preferred: string };
|
|
11
|
+
|
|
12
|
+
const findingOf = (hit: Hit): Finding => ({
|
|
13
|
+
rule: "",
|
|
14
|
+
severity: "warning",
|
|
15
|
+
line: 0,
|
|
16
|
+
column: 0,
|
|
17
|
+
quote: hit.sentence.text.trim(),
|
|
18
|
+
values: { matched: hit.matched, preferred: hit.preferred, offset: hit.sentence.span.start + hit.offset },
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* The sentence with the line breaks that fall inside a word taken out (「関\nする」 reads 「関する」), so words and patterns
|
|
23
|
+
* are found as the reader reads them. Positions found in it go back to the sentence's own.
|
|
24
|
+
*/
|
|
25
|
+
const readAsWritten = (sentence: Sentence): JoinedView =>
|
|
26
|
+
joinedView(
|
|
27
|
+
sentence.text,
|
|
28
|
+
(sentence.wrapBreaks ?? []).map((span) => ({ start: span.start - sentence.span.start, end: span.end - sentence.span.start })),
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
/** A match at [start, end) of the joined text, as a hit at the sentence's own position. */
|
|
32
|
+
const hitAt = (sentence: Sentence, view: JoinedView, start: number, end: number, preferred: string): Hit => ({
|
|
33
|
+
sentence,
|
|
34
|
+
offset: view.toSource({ start, end }).start,
|
|
35
|
+
matched: view.text.slice(start, end),
|
|
36
|
+
preferred,
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
/** source[start, end) with the line breaks inside words taken out: the text of a run of tokens as the reader reads it. */
|
|
40
|
+
const withoutBreaks = (source: string, start: number, end: number, breaks: readonly Span[]): string =>
|
|
41
|
+
breaks
|
|
42
|
+
.filter((span) => span.start >= start && span.end <= end)
|
|
43
|
+
.toReversed()
|
|
44
|
+
.reduce((text, span) => `${text.slice(0, span.start - start)}${text.slice(span.end - start)}`, source.slice(start, end));
|
|
45
|
+
|
|
46
|
+
/** words: each spelling to avoid, outside the spelling to use (「ユーザ」 inside 「ユーザー」 is not reported). */
|
|
47
|
+
export const customWords: Detector = (doc, options): Finding[] => {
|
|
48
|
+
const spec = options.custom;
|
|
49
|
+
if (spec?.type !== "words") return [];
|
|
50
|
+
return doc.sentences.flatMap((sentence) => {
|
|
51
|
+
const view = readAsWritten(sentence);
|
|
52
|
+
return spec.words.flatMap((pair) =>
|
|
53
|
+
occurrencesOutside(view.text, pair.avoid, pair.use).map((start) => findingOf(hitAt(sentence, view, start, start + pair.avoid.length, pair.use))),
|
|
54
|
+
);
|
|
55
|
+
});
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
/** pattern: each match of the team's regular expression in a sentence. Checked when chaff.yaml was read, and run with a time limit. */
|
|
59
|
+
export const customPattern: Detector = (doc, options): Finding[] => {
|
|
60
|
+
const spec = options.custom;
|
|
61
|
+
if (spec?.type !== "pattern") return [];
|
|
62
|
+
const views = doc.sentences.map(readAsWritten);
|
|
63
|
+
const matches = boundedMatches(
|
|
64
|
+
spec.pattern,
|
|
65
|
+
spec.flags,
|
|
66
|
+
views.map((view) => view.text),
|
|
67
|
+
);
|
|
68
|
+
return doc.sentences.flatMap((sentence, at) => {
|
|
69
|
+
const view = views[at];
|
|
70
|
+
if (view === undefined) return [];
|
|
71
|
+
return (matches[at] ?? []).map((match) => findingOf(hitAt(sentence, view, match.index, match.index + match.text.length, "")));
|
|
72
|
+
});
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
/** tokens: each run of tokens that meets the conditions in order, quoted from its first token to its last, line breaks inside words taken out. */
|
|
76
|
+
export const customTokens: Detector = (doc, options): Finding[] => {
|
|
77
|
+
const spec = options.custom;
|
|
78
|
+
if (spec?.type !== "tokens") return [];
|
|
79
|
+
return doc.sentences.flatMap((sentence) => {
|
|
80
|
+
const tokens = sentence.tokens ?? [];
|
|
81
|
+
return tokenRuns(tokens, spec.tokens).flatMap(([first, last]) => {
|
|
82
|
+
const [start, end] = [tokens[first]?.span.start, tokens[last]?.span.end];
|
|
83
|
+
if (start === undefined || end === undefined) return [];
|
|
84
|
+
return [
|
|
85
|
+
findingOf({ sentence, offset: start - sentence.span.start, matched: withoutBreaks(doc.source, start, end, sentence.wrapBreaks ?? []), preferred: "" }),
|
|
86
|
+
];
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { Detector, Finding, ProseDocument } from "../plugin.ts";
|
|
2
|
+
import { danglingFigures, type LabelWords } from "../figure-references.ts";
|
|
3
|
+
import { quoteAt } from "./structure-tree.ts";
|
|
4
|
+
|
|
5
|
+
const patternsOf = (doc: ProseDocument, id: string): string[] => (doc.lexicons[id] ?? []).map((entry) => entry.pattern);
|
|
6
|
+
|
|
7
|
+
const labelWordsOf = (doc: ProseDocument): LabelWords => ({
|
|
8
|
+
labels: (doc.lexicons["figure-label"] ?? []).map((entry) => ({ word: entry.pattern, kind: entry.instead_of ?? entry.pattern })),
|
|
9
|
+
elsewhere: patternsOf(doc, "figure-elsewhere"),
|
|
10
|
+
counters: patternsOf(doc, "count-counter"),
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
/** 本文が指す図・表・付録の番号が、キャプションにも見出しにも無い。 */
|
|
14
|
+
export const danglingFigure: Detector = (doc): Finding[] =>
|
|
15
|
+
danglingFigures(doc.source, doc.prose ?? doc.source, labelWordsOf(doc), doc.links).map((dangling) => ({
|
|
16
|
+
rule: "dangling-figure-reference",
|
|
17
|
+
severity: "warning",
|
|
18
|
+
line: 0,
|
|
19
|
+
column: 0,
|
|
20
|
+
quote: quoteAt(doc.source, dangling.offset),
|
|
21
|
+
values: { label: dangling.label, offset: dangling.offset },
|
|
22
|
+
}));
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { Detector, Finding } from "../plugin.ts";
|
|
2
|
+
import { quoteAround } from "./quote-around.ts";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* 句読点が二つ並んだところ(「。。」「、。」「,,」「..」)。打ち直しで残った印。
|
|
6
|
+
* 同じ印を三つ以上並べたもの(「...」「。。。」)は、わざと伸ばした書き方なので数えない。
|
|
7
|
+
* コロンは「::」(C++ の名前、IPv6)があるので見ない。
|
|
8
|
+
*/
|
|
9
|
+
const PUNCTUATION_RUN = /[、。,.,.;;]{2,}/gu;
|
|
10
|
+
|
|
11
|
+
/** 略語の点の後ろの読点やセミコロン(e.g.,、Inc.,、etc.;)。 */
|
|
12
|
+
const AFTER_ABBREVIATION = /^\.[,;]$/u;
|
|
13
|
+
|
|
14
|
+
const WORD = /[\p{L}\p{N}]/u;
|
|
15
|
+
|
|
16
|
+
/** 「..」の前後が字か数字か「/」なら、範囲や道のり(1..10、a..b、../)で、書き損じではない。 */
|
|
17
|
+
const JOINED = /[\p{L}\p{N}/]/u;
|
|
18
|
+
|
|
19
|
+
const isDrawnOut = (run: string): boolean => run.length >= 3 && [...run].every((mark) => mark === run.charAt(0));
|
|
20
|
+
|
|
21
|
+
const isJoinedDots = (text: string, start: number, run: string): boolean =>
|
|
22
|
+
run === ".." && (JOINED.test(text.charAt(start + run.length)) || text.charAt(start - 1) === "/");
|
|
23
|
+
|
|
24
|
+
export type DoubledMarks = { readonly start: number; readonly run: string };
|
|
25
|
+
|
|
26
|
+
/** 並びの終わりの点が次の語の頭(「、.NET」「。.well-known」)なら、その点は語のうち。 */
|
|
27
|
+
const withoutWordDot = (text: string, found: DoubledMarks): DoubledMarks =>
|
|
28
|
+
found.run.endsWith(".") && !isDrawnOut(found.run) && found.run !== ".." && WORD.test(text.charAt(found.start + found.run.length))
|
|
29
|
+
? { start: found.start, run: found.run.slice(0, -1) }
|
|
30
|
+
: found;
|
|
31
|
+
|
|
32
|
+
const isSlip = (text: string, { start, run }: DoubledMarks): boolean =>
|
|
33
|
+
run.length >= 2 && !isDrawnOut(run) && !AFTER_ABBREVIATION.test(run) && !isJoinedDots(text, start, run);
|
|
34
|
+
|
|
35
|
+
/** text の中の、句読点が重なった並び(位置と並び)。 */
|
|
36
|
+
export const doubledMarks = (text: string): DoubledMarks[] =>
|
|
37
|
+
[...text.matchAll(PUNCTUATION_RUN)].map((match) => withoutWordDot(text, { start: match.index, run: match[0] })).filter((found) => isSlip(text, found));
|
|
38
|
+
|
|
39
|
+
export const doubledPunctuation: Detector = (doc): Finding[] => {
|
|
40
|
+
const text = doc.prose ?? doc.source;
|
|
41
|
+
return doubledMarks(text).map(({ start, run }) => ({
|
|
42
|
+
rule: "",
|
|
43
|
+
severity: "warning",
|
|
44
|
+
line: 0,
|
|
45
|
+
column: 0,
|
|
46
|
+
quote: quoteAround(text, start, start + run.length),
|
|
47
|
+
values: { marks: run, offset: start },
|
|
48
|
+
}));
|
|
49
|
+
};
|
|
@@ -90,7 +90,18 @@ export const doubledAt = (source: string, first: Token, second: Token, spaced: b
|
|
|
90
90
|
export const isNameBefore = (first: Token, second: Token, opensSentence: boolean): boolean =>
|
|
91
91
|
!opensSentence && !isDeterminer(first) && UPPER_START.test(first.surface) && LOWER_START.test(second.surface);
|
|
92
92
|
|
|
93
|
-
|
|
93
|
+
/** 大文字 1 字の語。 */
|
|
94
|
+
const SINGLE_CAPITAL = /^\p{Lu}$/u;
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* 文の途中の大文字 1 字の冠詞(at site A the connection、Peer A our copy)は、名前にした文字。冠詞は文の途中で大文字にならない。
|
|
98
|
+
* 同じ語(site A a)と文頭の A the は書き損じのまま。
|
|
99
|
+
*/
|
|
100
|
+
export const isLetterName = (first: Token, second: Token, opensSentence: boolean): boolean =>
|
|
101
|
+
!opensSentence && SINGLE_CAPITAL.test(first.surface) && !sameWord(first, second);
|
|
102
|
+
|
|
103
|
+
/** 文の最初の、字のある語の位置。at 語目より前に字のある語が無ければ、at 語目は文の書き出し。 */
|
|
104
|
+
const firstWordIndex = (tokens: readonly Token[]): number => tokens.findIndex((token) => LETTER.test(token.surface));
|
|
94
105
|
|
|
95
106
|
const repeatsPrevious = (tokens: readonly Token[], at: number): boolean => {
|
|
96
107
|
const [previous, token] = [tokens[at - 1], tokens[at]];
|
|
@@ -110,14 +121,18 @@ export const opensPhrase = (tokens: readonly Token[], at: number, phrases: Lexic
|
|
|
110
121
|
return words.length > 1 && surfacesOf(tokens.slice(at, at + words.length)) === surfacesOf(words);
|
|
111
122
|
});
|
|
112
123
|
|
|
113
|
-
export const doubledIn = (source: string, tokens: readonly Token[], spaced: boolean, allowed: Lexicon, phrases: Lexicon = []): Doubled[] =>
|
|
114
|
-
|
|
124
|
+
export const doubledIn = (source: string, tokens: readonly Token[], spaced: boolean, allowed: Lexicon, phrases: Lexicon = []): Doubled[] => {
|
|
125
|
+
const firstWord = firstWordIndex(tokens);
|
|
126
|
+
return tokens.flatMap((second, index) => {
|
|
115
127
|
const first = tokens[index - 1];
|
|
116
128
|
if (first === undefined || !doubledAt(source, first, second, spaced)) return [];
|
|
117
129
|
if (isAllowed(tokens, index - 1, allowedAt(tokens, index - 1, allowed))) return [];
|
|
118
130
|
if (!sameWord(first, second) && opensPhrase(tokens, index, phrases)) return [];
|
|
119
|
-
|
|
131
|
+
// first は字のある語なので、firstWord は見つかっていて index - 1 以下。
|
|
132
|
+
const opens = firstWord >= index - 1;
|
|
133
|
+
return isNameBefore(first, second, opens) || isLetterName(first, second, opens) ? [] : [{ first, second }];
|
|
120
134
|
});
|
|
135
|
+
};
|
|
121
136
|
|
|
122
137
|
/** 印を挟んでいても(the [the)、見せるのは二語だけ。 */
|
|
123
138
|
const wordOf = (doubled: Doubled, spaced: boolean): string => [doubled.first.surface, doubled.second.surface].join(spaced ? " " : "");
|
|
@@ -134,272 +134,3 @@ export const titleCaseMix: Detector = (doc, options): Finding[] => {
|
|
|
134
134
|
values: { count: few.length, limit: options.limit, offset: section.span.start },
|
|
135
135
|
}));
|
|
136
136
|
};
|
|
137
|
-
|
|
138
|
-
const LIST_CONJUNCTION = new Set(["and", "or"]);
|
|
139
|
-
|
|
140
|
-
const isComma = (token: Token): boolean => token.surface === ",";
|
|
141
|
-
|
|
142
|
-
const commaBefore = (tokens: readonly Token[], at: number): boolean => tokens[at - 1]?.surface === ",";
|
|
143
|
-
|
|
144
|
-
/** 並列はこれをまたがない。セミコロンの前後は別の節。 */
|
|
145
|
-
const CLAUSE_BREAK = new Set([";", ":", "—"]);
|
|
146
|
-
|
|
147
|
-
/** 各 token の前で閉じていない括弧の数。括弧の中の読点(external users (e.g., guests), and ...)は外の並列を切らない。 */
|
|
148
|
-
const PAREN_STEP: Readonly<Record<string, number>> = { "(": 1, ")": -1 };
|
|
149
|
-
|
|
150
|
-
const depthsOf = (tokens: readonly Token[]): number[] =>
|
|
151
|
-
tokens.reduce<{ depths: number[]; open: number }>(
|
|
152
|
-
(acc, token) => ({ depths: [...acc.depths, acc.open], open: Math.max(0, acc.open + (PAREN_STEP[token.surface] ?? 0)) }),
|
|
153
|
-
{ depths: [], open: 0 },
|
|
154
|
-
).depths;
|
|
155
|
-
|
|
156
|
-
/** participleWords は、解析器が分詞と読まないが読点のあとで分詞の句を始める語(meaning)。 */
|
|
157
|
-
type Clause = { readonly tokens: readonly Token[]; readonly depths: readonly number[]; readonly participleWords: ReadonlySet<string> };
|
|
158
|
-
|
|
159
|
-
/** 冠詞や引用符を飛ばした、項目の頭の品詞。the parser と an exporter と samples を同じ形と見る。 */
|
|
160
|
-
const NOMINAL = new Set(["NOUN", "PROPN", "PRON", "NUM", "ADJ"]);
|
|
161
|
-
|
|
162
|
-
const isContent = (token: Token): boolean => token.pos !== "DET" && token.pos !== "PUNCT" && token.pos !== "X";
|
|
163
|
-
|
|
164
|
-
const shapeOf = (item: readonly Token[]): string | undefined => {
|
|
165
|
-
const head = item.find(isContent);
|
|
166
|
-
if (head === undefined) return undefined;
|
|
167
|
-
return NOMINAL.has(head.pos) ? "NOMINAL" : head.pos;
|
|
168
|
-
};
|
|
169
|
-
|
|
170
|
-
const VERBAL = new Set(["VERB", "AUX"]);
|
|
171
|
-
|
|
172
|
-
/** 主語と述語のある項目。the team fixed the bug / these are crucial。gets us more は述語だけ。 */
|
|
173
|
-
const isClause = (item: readonly Token[]): boolean => {
|
|
174
|
-
const first = item.find((token) => token.pos !== "PUNCT" && token.pos !== "X");
|
|
175
|
-
if (first === undefined || !(first.pos === "DET" || NOMINAL.has(first.pos))) return false;
|
|
176
|
-
return item.some((token) => token !== first && VERBAL.has(token.pos));
|
|
177
|
-
};
|
|
178
|
-
|
|
179
|
-
/** 節の頭から and / or の手前までを、同じ深さの読点で項目に切る。Oxford comma の読点のあとは空なので項目にならない。 */
|
|
180
|
-
const itemsBefore = (clause: Clause, at: number): Token[][] => {
|
|
181
|
-
const level = clause.depths[at] ?? 0;
|
|
182
|
-
const start = clause.tokens.slice(0, at).findLastIndex((token) => CLAUSE_BREAK.has(token.surface)) + 1;
|
|
183
|
-
return clause.tokens
|
|
184
|
-
.slice(start, at)
|
|
185
|
-
.reduce<Token[][]>(
|
|
186
|
-
(items, token, offset) => {
|
|
187
|
-
if (isComma(token) && clause.depths[start + offset] === level) return [...items, []];
|
|
188
|
-
return [...items.slice(0, -1), [...(items.at(-1) ?? []), token]];
|
|
189
|
-
},
|
|
190
|
-
[[]],
|
|
191
|
-
)
|
|
192
|
-
.filter((item) => item.length > 0);
|
|
193
|
-
};
|
|
194
|
-
|
|
195
|
-
/**
|
|
196
|
-
* and / or で始まる項目で、前の並びは閉じている(offering, giving, or receiving, directly or indirectly)。
|
|
197
|
-
* 次の並びはその項目から始まる。項目が動詞で始まれば、その目的語から(Develop, maintain, and track courses, materials and events)。
|
|
198
|
-
*/
|
|
199
|
-
const afterClosedList = (items: readonly Token[][]): Token[][] => {
|
|
200
|
-
const closed = items.findLastIndex((item) => LIST_CONJUNCTION.has(openingOf(item)?.surface.toLowerCase() ?? ""));
|
|
201
|
-
const closing = items[closed];
|
|
202
|
-
if (closing === undefined) return [...items];
|
|
203
|
-
const body = closing.slice(closing.findIndex((token) => LIST_CONJUNCTION.has(token.surface.toLowerCase())) + 1);
|
|
204
|
-
const head = body.findIndex(isContent);
|
|
205
|
-
const rest = VERBAL.has(body[head]?.pos ?? "") ? body.slice(head + 1) : body;
|
|
206
|
-
// 項目が残らなければ、後ろは新しい句の頭。導入の句(directly or indirectly,)を外す。
|
|
207
|
-
return rest.some(isContent) ? [rest, ...items.slice(closed + 1)] : [...withoutLead(items.slice(closed + 1))];
|
|
208
|
-
};
|
|
209
|
-
|
|
210
|
-
/** and / or の後ろの項目。次の読点か節の切れ目まで。 */
|
|
211
|
-
const itemAfter = (tokens: readonly Token[], at: number): Token[] => {
|
|
212
|
-
const end = tokens.findIndex((token, index) => index > at && (isComma(token) || CLAUSE_BREAK.has(token.surface)));
|
|
213
|
-
return tokens.slice(at + 1, end === -1 ? undefined : end);
|
|
214
|
-
};
|
|
215
|
-
|
|
216
|
-
/**
|
|
217
|
-
* 導入の句(After the review, / If it fails, / Finally, / To test it, / Based on the review,)は並列の項目ではない。
|
|
218
|
-
* 節の最初の項目が前置詞・接続詞・副詞・to・過去分詞で始まり、次の項目と頭の形が違えば外す。
|
|
219
|
-
* Quickly, quietly and carefully は残る。For managers, engineers and designers, も外れるが、前置詞のあとの名詞を
|
|
220
|
-
* 項目と読むと、Over this period, the subcommittees and the full committee ... を並びと誤る。こちらのほうが多い。
|
|
221
|
-
*/
|
|
222
|
-
const LEAD_POS = new Set(["ADP", "SCONJ", "ADV", "PART"]);
|
|
223
|
-
|
|
224
|
-
const isParticiple = (token: Token): boolean => token.features?.["VerbForm"] === "Part";
|
|
225
|
-
|
|
226
|
-
const openingOf = (item: readonly Token[]): Token | undefined => item.find((token) => token.pos !== "PUNCT" && token.pos !== "X");
|
|
227
|
-
|
|
228
|
-
const openingKind = (item: readonly Token[]): string | undefined => {
|
|
229
|
-
const opening = openingOf(item);
|
|
230
|
-
return opening !== undefined && isParticiple(opening) ? "PARTICIPLE" : shapeOf(item);
|
|
231
|
-
};
|
|
232
|
-
|
|
233
|
-
const withoutLead = (items: readonly Token[][]): readonly Token[][] => {
|
|
234
|
-
const [first, second] = items;
|
|
235
|
-
const opening = first === undefined ? undefined : openingOf(first);
|
|
236
|
-
if (first === undefined || second === undefined || opening === undefined) return items;
|
|
237
|
-
if (!LEAD_POS.has(opening.pos) && !isParticiple(opening)) return items;
|
|
238
|
-
return openingKind(first) === openingKind(second) ? items : items.slice(1);
|
|
239
|
-
};
|
|
240
|
-
|
|
241
|
-
/**
|
|
242
|
-
* 形容詞のあとの読点は、名詞の前で形容詞を重ねているだけのことが多い(the long, winding bridge)。
|
|
243
|
-
* 次の項目が名詞で終わるならつなげ直す。形容詞そのものの並び(quick, cheap and reliable)は切ったまま。
|
|
244
|
-
*/
|
|
245
|
-
const lastContent = (item: readonly Token[]): Token | undefined => item.findLast(isContent);
|
|
246
|
-
|
|
247
|
-
const joinAdjectives = (items: readonly Token[][]): Token[][] =>
|
|
248
|
-
items.reduce<Token[][]>((joined, item) => {
|
|
249
|
-
const previous = joined.at(-1);
|
|
250
|
-
const stacked = previous !== undefined && lastContent(previous)?.pos === "ADJ" && lastContent(item)?.pos !== "ADJ";
|
|
251
|
-
if (stacked) joined[joined.length - 1] = [...previous, ...item];
|
|
252
|
-
else joined.push(item);
|
|
253
|
-
return joined;
|
|
254
|
-
}, []);
|
|
255
|
-
|
|
256
|
-
/**
|
|
257
|
-
* 節を並べるなら、どの項目も節。Additionally, others can learn, and the mistake is rarer. の
|
|
258
|
-
* Additionally は項目ではなく、and の前の読点は節をつなぐ読点。名詞の並びは最初の項目に前置き
|
|
259
|
-
* (We shipped the parser)を抱えるので、この確かめは節の並びだけにする。
|
|
260
|
-
* and の後ろだけが節なのは主語の並び(The parser, the renderer and the exporter shipped.)なので外さない。
|
|
261
|
-
*/
|
|
262
|
-
const clausesAgree = (items: readonly Token[][], after: readonly Token[]): boolean => {
|
|
263
|
-
const last = items.at(-1);
|
|
264
|
-
if (last === undefined || !isClause(last)) return true;
|
|
265
|
-
return isClause(after) && items.every(isClause);
|
|
266
|
-
};
|
|
267
|
-
|
|
268
|
-
/**
|
|
269
|
-
* 動詞で始まる項目を並べるなら、どの項目にも動詞がある。The scope, in contrast, is larger, and covers ... の
|
|
270
|
-
* The scope は項目ではなく、後ろの述語の主語。
|
|
271
|
-
*/
|
|
272
|
-
const hasVerb = (item: readonly Token[]): boolean => item.some((token) => VERBAL.has(token.pos));
|
|
273
|
-
|
|
274
|
-
const predicatesAgree = (items: readonly Token[][], shape: string): boolean => !VERBAL.has(shape) || items.every(hasVerb);
|
|
275
|
-
|
|
276
|
-
/** -ing 形(VerbForm=Ger)と過去分詞(Part)。filling the gaps と sentenced to prison は並べても並列にならない。 */
|
|
277
|
-
const VERB_FORMS = new Set(["Ger", "Part"]);
|
|
278
|
-
|
|
279
|
-
const verbFormOf = (token: Token): string | undefined => {
|
|
280
|
-
const form = token.features?.["VerbForm"];
|
|
281
|
-
return form !== undefined && VERB_FORMS.has(form) ? form : undefined;
|
|
282
|
-
};
|
|
283
|
-
|
|
284
|
-
/** 項目の頭の分詞とその形。副詞は飛ばす(originally written)。引用符の中の語(‘modelling’)は語の例なので分詞と読まない。 */
|
|
285
|
-
const participleOpening = (item: readonly Token[], words: ReadonlySet<string>): { readonly at: number; readonly form: string } | undefined => {
|
|
286
|
-
const at = item.findIndex((token) => token.pos !== "PUNCT" && token.pos !== "ADV");
|
|
287
|
-
const opening = item[at];
|
|
288
|
-
if (opening === undefined) return undefined;
|
|
289
|
-
const form = words.has(opening.surface.toLowerCase()) ? "Ger" : verbFormOf(opening);
|
|
290
|
-
return form === undefined ? undefined : { at, form };
|
|
291
|
-
};
|
|
292
|
-
|
|
293
|
-
const hasParticiple = (item: readonly Token[], form: string): boolean => item.some((token) => verbFormOf(token) === form);
|
|
294
|
-
|
|
295
|
-
/**
|
|
296
|
-
* 最初より後ろの項目が分詞で始まるとき。最初の項目にも同じ形の分詞があれば分詞の並び
|
|
297
|
-
* (are hiring engineers, buying laptops and renting desks / Tested, reviewed and approved)。後ろの項目が動詞で始まれば述語の並びで、
|
|
298
|
-
* 分詞は解析器の読み違い(the jury, cross-examined the witnesses, presented witnesses, and made)。そうでなければ分詞の句
|
|
299
|
-
* (from Latin, meaning ship or boat / the judge, sitting without a jury, and sentenced)で、並びがあるならその句の中から始まる。
|
|
300
|
-
* 分詞より前の項目と分詞そのものは項目ではない(programs, including the office, the desk and the team)。句の中の項目の形が揃わなければ
|
|
301
|
-
* 句は挿入で、並びは句の後ろだけ(relief, including an injunction, in any court ... and without / including before the date, to a
|
|
302
|
-
* Recipient ... “confidential”, “proprietary”, or the like)。
|
|
303
|
-
*/
|
|
304
|
-
/** 解析器は過去形と過去分詞(fixed)を見分けられない。動詞のある項目のあとの「過去分詞」の並びは、述語の並びと読む。 */
|
|
305
|
-
const pastPredicates = (first: readonly Token[], form: string, after: readonly Token[], words: ReadonlySet<string>): boolean =>
|
|
306
|
-
form === "Part" && participleOpening(after, words)?.form === "Part" && hasVerb(first);
|
|
307
|
-
|
|
308
|
-
const fromParticiplePhrase = (items: readonly Token[][], after: readonly Token[], words: ReadonlySet<string>): readonly Token[][] => {
|
|
309
|
-
const at = items.findIndex((item, index) => index > 0 && participleOpening(item, words) !== undefined);
|
|
310
|
-
const phrase = items[at];
|
|
311
|
-
const first = items[0];
|
|
312
|
-
const opening = phrase === undefined ? undefined : participleOpening(phrase, words);
|
|
313
|
-
if (first === undefined || phrase === undefined || opening === undefined) return items;
|
|
314
|
-
const rest = items.slice(at + 1);
|
|
315
|
-
const predicates = rest.some((item) => VERBAL.has(shapeOf(item) ?? "")) || pastPredicates(first, opening.form, after, words);
|
|
316
|
-
if (hasParticiple(first, opening.form) || predicates) return items;
|
|
317
|
-
const inPhrase = withoutLead([phrase.slice(opening.at + 1), ...rest].filter((item) => item.some(isContent)));
|
|
318
|
-
return new Set(inPhrase.map(shapeOf)).size > 1 ? rest : inPhrase;
|
|
319
|
-
};
|
|
320
|
-
|
|
321
|
-
const APPOSITIVE_ANCHOR = new Set(["NOUN", "PROPN"]);
|
|
322
|
-
|
|
323
|
-
/**
|
|
324
|
-
* 名詞のすぐ後ろの、読点で挟まれた X and Y は同格(two HPV types, HPV16 and HPV18, that account / Governments, both state and
|
|
325
|
-
* federal, spend)。項目が 2 つに見え、and の前に読点が無く、and の後ろの項目が読点で閉じ、後ろの 2 つに動詞が無いときに同格と読む。
|
|
326
|
-
* 2 つを結ぶ同格に Oxford comma は打たないので、and の前に読点があれば 3 つの並び(the subcommittee, full committee, and
|
|
327
|
-
* chamber levels, as well as)。読点の無い本当の並び(apples, pears and plums, then ...)はこの形と見分けられず、判定から外れる。
|
|
328
|
-
*/
|
|
329
|
-
const isAppositive = (clause: Clause, at: number, items: readonly Token[][], after: readonly Token[]): boolean => {
|
|
330
|
-
const [anchor, head] = items;
|
|
331
|
-
if (items.length !== 2 || anchor === undefined || head === undefined || commaBefore(clause.tokens, at)) return false;
|
|
332
|
-
const end = at + 1 + after.length;
|
|
333
|
-
// 閉じた読点のあとに and / or が続くなら、並びのあとに節をつないでいる(apples, pears and plums, and went home)。
|
|
334
|
-
const joinsClause = LIST_CONJUNCTION.has(clause.tokens[end + 1]?.surface.toLowerCase() ?? "");
|
|
335
|
-
const closed = clause.tokens[end]?.surface === "," && clause.depths[end] === clause.depths[at] && !joinsClause;
|
|
336
|
-
return closed && APPOSITIVE_ANCHOR.has(lastContent(anchor)?.pos ?? "") && !hasVerb(head) && !hasVerb(after);
|
|
337
|
-
};
|
|
338
|
-
|
|
339
|
-
/** 副詞を飛ばした頭の形。explain and justify と ultimately ensure は同じ動詞の項目。 */
|
|
340
|
-
const leadShape = (item: readonly Token[]): string | undefined => shapeOf(item.filter((token) => token.pos !== "ADV"));
|
|
341
|
-
|
|
342
|
-
/**
|
|
343
|
-
* and の後ろの項目に読点と、同じ形の項目を連れた and / or が続くなら、この and は項目の中にある(searches and seizures, and
|
|
344
|
-
* the Eighth's ban)。頭の形か、節かどうかが違えば別の節をつなぐ and で、この and までが並び(apples, pears and plums, and went home / and figs fell)。
|
|
345
|
-
*/
|
|
346
|
-
const listContinues = (clause: Clause, at: number, after: readonly Token[]): boolean => {
|
|
347
|
-
const next = at + 1 + after.length;
|
|
348
|
-
if (clause.tokens[next]?.surface !== "," || !LIST_CONJUNCTION.has(clause.tokens[next + 1]?.surface.toLowerCase() ?? "")) return false;
|
|
349
|
-
const following = itemAfter(clause.tokens, next + 1);
|
|
350
|
-
const shape = leadShape(following);
|
|
351
|
-
return shape !== undefined && shape === leadShape(after) && isClause(following) === isClause(after);
|
|
352
|
-
};
|
|
353
|
-
|
|
354
|
-
/**
|
|
355
|
-
* 読点で区切った項目が and / or の前に 2 つ以上あり、最後の項目と and の後ろが同じ形のときだけ並列。
|
|
356
|
-
* 導入の読点(After the review, the team fixed the bug and shipped it.)や、節をつなぐ読点
|
|
357
|
-
* (We tested it, and the team shipped it.)は、Oxford comma を打つかどうかの選択を見せない。
|
|
358
|
-
*/
|
|
359
|
-
const listAt = (clause: Clause, at: number): boolean | undefined => {
|
|
360
|
-
const after = itemAfter(clause.tokens, at);
|
|
361
|
-
const items = fromParticiplePhrase(afterClosedList(withoutLead(joinAdjectives(itemsBefore(clause, at)))), after, clause.participleWords);
|
|
362
|
-
if (listContinues(clause, at, after) || isAppositive(clause, at, items, after)) return undefined;
|
|
363
|
-
const last = items.at(-1);
|
|
364
|
-
const shape = last === undefined ? undefined : shapeOf(last);
|
|
365
|
-
if (items.length < 2 || shape === undefined || shape !== shapeOf(after)) return undefined;
|
|
366
|
-
if (!clausesAgree(items, after) || !predicatesAgree(items, shape)) return undefined;
|
|
367
|
-
return commaBefore(clause.tokens, at);
|
|
368
|
-
};
|
|
369
|
-
|
|
370
|
-
/**
|
|
371
|
-
* 3 つ以上の並列の最後の and / or の前に読点を打つか。Oxford comma。
|
|
372
|
-
*
|
|
373
|
-
* どちらが正しいかは決めない。スタイルガイドで割れる論点に立場を取ると rule ごと無視される。
|
|
374
|
-
* 見るのは 1 つの文書で揃っているかだけ。spec §12.3。
|
|
375
|
-
*/
|
|
376
|
-
const oxfordIn = (tokens: readonly Token[], participleWords: ReadonlySet<string>): boolean | undefined => {
|
|
377
|
-
const clause = { tokens, depths: depthsOf(tokens), participleWords };
|
|
378
|
-
return tokens.reduce<boolean | undefined>(
|
|
379
|
-
(found, token, at) => found ?? (at > 0 && LIST_CONJUNCTION.has(token.surface.toLowerCase()) ? listAt(clause, at) : undefined),
|
|
380
|
-
undefined,
|
|
381
|
-
);
|
|
382
|
-
};
|
|
383
|
-
|
|
384
|
-
export const oxfordComma: Detector = (doc, options): Finding[] => {
|
|
385
|
-
const participleWords = new Set((doc.lexicons["participle-word"] ?? []).map((entry) => entry.pattern.toLowerCase()));
|
|
386
|
-
const judged = doc.sentences.flatMap((sentence) => {
|
|
387
|
-
const oxford = oxfordIn(sentence.tokens ?? [], participleWords);
|
|
388
|
-
return oxford === undefined ? [] : [{ sentence, oxford }];
|
|
389
|
-
});
|
|
390
|
-
const withComma = judged.filter((entry) => entry.oxford).length;
|
|
391
|
-
const minorityUsesComma = withComma <= judged.length - withComma;
|
|
392
|
-
const few = minorityUsesComma ? withComma : judged.length - withComma;
|
|
393
|
-
// 同数なら少数派は無い。
|
|
394
|
-
if (few === 0 || few * 2 === judged.length || few > options.limit) return [];
|
|
395
|
-
return judged
|
|
396
|
-
.filter((entry) => entry.oxford === minorityUsesComma)
|
|
397
|
-
.map(({ sentence }) => ({
|
|
398
|
-
rule: "oxford-comma-consistency",
|
|
399
|
-
severity: "info",
|
|
400
|
-
line: 0,
|
|
401
|
-
column: 0,
|
|
402
|
-
quote: sentence.text.trim(),
|
|
403
|
-
values: { count: few, limit: options.limit, offset: sentence.span.start },
|
|
404
|
-
}));
|
|
405
|
-
};
|
|
@@ -12,10 +12,19 @@ const labelOf = (node: StructureNode): string => {
|
|
|
12
12
|
* 条の外で、番号の付いた行(言語パッケージが項目と読んだ「(1)」など)。
|
|
13
13
|
* 条の中の番号付きの行は、条の項(条の本文の続き)でもありうるので数えない。
|
|
14
14
|
*/
|
|
15
|
-
export const numberedStarts = (
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
15
|
+
export const numberedStarts = (root: StructureNode, inArticle = false): NumberedStart[] => {
|
|
16
|
+
const found: NumberedStart[] = [];
|
|
17
|
+
const pending = [{ node: root, inArticle }];
|
|
18
|
+
while (pending.length > 0) {
|
|
19
|
+
const next = pending.pop();
|
|
20
|
+
if (next === undefined) break;
|
|
21
|
+
const { node } = next;
|
|
22
|
+
if (node.kind === "item" && !next.inArticle) found.push({ start: node.span.start, label: labelOf(node) });
|
|
23
|
+
const within = next.inArticle || node.kind === "article";
|
|
24
|
+
node.children.toReversed().forEach((child) => pending.push({ node: child, inArticle: within }));
|
|
25
|
+
}
|
|
26
|
+
return found;
|
|
27
|
+
};
|
|
19
28
|
|
|
20
29
|
const INDENT = /^[ \t\u3000]*/u;
|
|
21
30
|
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { Span } from "../plugin.ts";
|
|
2
|
+
|
|
3
|
+
// ひな形が項目ごとに繰り返す飾り(一覧の札、リンクの文字、記号の並び)。書き手の言い回しではない。
|
|
4
|
+
|
|
5
|
+
const MARK = /[\p{P}\p{S}]/gu;
|
|
6
|
+
|
|
7
|
+
const BRACKETED = /[([{([{]([^()[\]{}()[]{}]*)[)\]})]}]/gu;
|
|
8
|
+
|
|
9
|
+
/** 札の 1 項目。空白を含まない 1 語で、平仮名も含まない(日本語で平仮名を含めば、括弧の中は言い回し)。 */
|
|
10
|
+
const TAG_ITEM = /^\s*[^\s\p{Script=Hiragana}]+\s*$/u;
|
|
11
|
+
|
|
12
|
+
/** 括弧の中が、1 語か 1 語ずつの並び((replaced)、[pdf, html, other])。(if applicable) のような句は札ではない。 */
|
|
13
|
+
const isTag = (inside: string): boolean => inside.split(/[,、]/u).every((item) => TAG_ITEM.test(item));
|
|
14
|
+
|
|
15
|
+
const spansOf = (text: string, pattern: RegExp, keep: (match: RegExpExecArray) => boolean): Span[] =>
|
|
16
|
+
[...text.matchAll(pattern)].filter(keep).map((match) => ({ start: match.index, end: match.index + match[0].length }));
|
|
17
|
+
|
|
18
|
+
/** text の各位置(UTF-16 の単位)が飾りか。記号・句読点、括弧でくくった札、リンクの文字(linked が決める)。 */
|
|
19
|
+
export const furnitureMask = (text: string, linked: (index: number) => boolean): boolean[] => {
|
|
20
|
+
const mask = Array.from({ length: text.length }, (_, index) => linked(index));
|
|
21
|
+
[...spansOf(text, MARK, () => true), ...spansOf(text, BRACKETED, (match) => isTag(match[1] ?? ""))].forEach((span) => mask.fill(true, span.start, span.end));
|
|
22
|
+
return mask;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
/** 窓の中の空白でない文字の過半が飾りなら、その出現は飾り。 */
|
|
26
|
+
export const isFurniture = (text: string, mask: readonly boolean[], window: Span): boolean => {
|
|
27
|
+
const visible = Array.from({ length: window.end - window.start }, (_, step) => window.start + step).filter((index) => !/\s/u.test(text[index] ?? ""));
|
|
28
|
+
const marked = visible.filter((index) => mask[index] === true);
|
|
29
|
+
return visible.length > 0 && marked.length * 2 > visible.length;
|
|
30
|
+
};
|
|
@@ -37,12 +37,16 @@ export const compacted = (text: string, unit: LengthUnit): Compacted => {
|
|
|
37
37
|
return { text: chars.join(""), offsets };
|
|
38
38
|
};
|
|
39
39
|
|
|
40
|
+
/** 詰めた文の中の範囲を、元の文の範囲に戻す。 */
|
|
41
|
+
export const placeAt = (source: Compacted, window: Span): Span | undefined => {
|
|
42
|
+
const start = source.offsets[window.start];
|
|
43
|
+
const last = source.offsets[window.end - 1];
|
|
44
|
+
if (start === undefined || last === undefined) return undefined;
|
|
45
|
+
return { start, end: last + 1 };
|
|
46
|
+
};
|
|
47
|
+
|
|
40
48
|
/** 詰めた文の中の gram の最初の出現を、元の文の範囲に戻す。無ければ undefined。 */
|
|
41
49
|
export const placeOf = (source: Compacted, gram: string): Span | undefined => {
|
|
42
50
|
const at = source.text.indexOf(gram);
|
|
43
|
-
|
|
44
|
-
const start = source.offsets[at];
|
|
45
|
-
const last = source.offsets[at + gram.length - 1];
|
|
46
|
-
if (start === undefined || last === undefined) return undefined;
|
|
47
|
-
return { start, end: last + 1 };
|
|
51
|
+
return at === -1 ? undefined : placeAt(source, { start: at, end: at + gram.length });
|
|
48
52
|
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { Span } from "../plugin.ts";
|
|
2
|
+
|
|
3
|
+
/** 1 文字ずつずらした、幅 width の窓。 */
|
|
4
|
+
export const charWindows = (text: string, width: number): Span[] =>
|
|
5
|
+
Array.from({ length: Math.max(0, text.length - width + 1) }, (_, start) => ({ start, end: start + width }));
|
|
6
|
+
|
|
7
|
+
/** first 番目の語から数えて、minLength 文字に届く最初の語。届く前に尽きるのは minLength 文字に満たない末尾だけなので、深くならない。 */
|
|
8
|
+
const reachingWord = (words: readonly Span[], first: number, minLength: number): Span | undefined => {
|
|
9
|
+
const start = words[first]?.start ?? 0;
|
|
10
|
+
const step = (index: number): Span | undefined => {
|
|
11
|
+
const word = words[index];
|
|
12
|
+
if (word === undefined || word.end - start >= minLength) return word;
|
|
13
|
+
return step(index + 1);
|
|
14
|
+
};
|
|
15
|
+
return step(first);
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
/** 語。前後の句読点と括弧は含めない。"described," と "described" を別の語句として数えないため。 */
|
|
19
|
+
const WORD = /[\p{L}\p{N}](?:\S*[\p{L}\p{N}])?/gu;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* 語の頭で始まり語の尾で終わる窓。語を 1 つずつ足して、minLength 文字に届いた最短のもの。
|
|
23
|
+
* 文字で切ると " generat" や "roductivity change a" のような語の断片が数えられる。
|
|
24
|
+
*/
|
|
25
|
+
export const wordWindows = (text: string, minLength: number): Span[] => {
|
|
26
|
+
const words = [...text.matchAll(WORD)].map((match) => ({ start: match.index, end: match.index + match[0].length }));
|
|
27
|
+
return words.flatMap((first, index) => {
|
|
28
|
+
const last = reachingWord(words, index, minLength);
|
|
29
|
+
return last === undefined ? [] : [{ start: first.start, end: last.end }];
|
|
30
|
+
});
|
|
31
|
+
};
|