chaffjs 0.35.0 → 0.36.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.ja.md +12 -0
- package/README.md +15 -0
- package/dist/announced-count.d.ts +2 -0
- package/dist/announced-count.d.ts.map +1 -1
- package/dist/announced-count.js +29 -7
- package/dist/announced-count.js.map +1 -1
- package/dist/chronology-rows.d.ts +9 -0
- package/dist/chronology-rows.d.ts.map +1 -0
- package/dist/chronology-rows.js +79 -0
- package/dist/chronology-rows.js.map +1 -0
- package/dist/company-name-run.d.ts +8 -0
- package/dist/company-name-run.d.ts.map +1 -0
- package/dist/company-name-run.js +30 -0
- package/dist/company-name-run.js.map +1 -0
- package/dist/compound-head.d.ts +7 -0
- package/dist/compound-head.d.ts.map +1 -0
- package/dist/compound-head.js +19 -0
- package/dist/compound-head.js.map +1 -0
- package/dist/date-format.d.ts +8 -3
- package/dist/date-format.d.ts.map +1 -1
- package/dist/date-format.js +36 -3
- package/dist/date-format.js.map +1 -1
- package/dist/derived/count-per-period.d.ts +23 -0
- package/dist/derived/count-per-period.d.ts.map +1 -0
- package/dist/derived/count-per-period.js +14 -0
- package/dist/derived/count-per-period.js.map +1 -0
- 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 +29 -1
- package/dist/detectors/acronym-context.js.map +1 -1
- package/dist/detectors/acronym-expansion.d.ts +5 -1
- package/dist/detectors/acronym-expansion.d.ts.map +1 -1
- package/dist/detectors/acronym-expansion.js +35 -3
- package/dist/detectors/acronym-expansion.js.map +1 -1
- package/dist/detectors/acronym-known.d.ts +19 -0
- package/dist/detectors/acronym-known.d.ts.map +1 -0
- package/dist/detectors/acronym-known.js +32 -0
- package/dist/detectors/acronym-known.js.map +1 -0
- package/dist/detectors/acronym-link.d.ts +14 -0
- package/dist/detectors/acronym-link.d.ts.map +1 -0
- package/dist/detectors/acronym-link.js +32 -0
- package/dist/detectors/acronym-link.js.map +1 -0
- package/dist/detectors/acronym-name.d.ts +8 -1
- package/dist/detectors/acronym-name.d.ts.map +1 -1
- package/dist/detectors/acronym-name.js +23 -4
- package/dist/detectors/acronym-name.js.map +1 -1
- package/dist/detectors/addressee-edge.d.ts +13 -0
- package/dist/detectors/addressee-edge.d.ts.map +1 -0
- package/dist/detectors/addressee-edge.js +13 -0
- package/dist/detectors/addressee-edge.js.map +1 -0
- package/dist/detectors/announced-count.d.ts.map +1 -1
- package/dist/detectors/announced-count.js +1 -0
- package/dist/detectors/announced-count.js.map +1 -1
- package/dist/detectors/bare-comparative.d.ts +4 -0
- package/dist/detectors/bare-comparative.d.ts.map +1 -1
- package/dist/detectors/bare-comparative.js +28 -3
- package/dist/detectors/bare-comparative.js.map +1 -1
- package/dist/detectors/blank-structure.d.ts +7 -1
- package/dist/detectors/blank-structure.d.ts.map +1 -1
- package/dist/detectors/blank-structure.js +13 -8
- package/dist/detectors/blank-structure.js.map +1 -1
- package/dist/detectors/bracket-specified.d.ts +6 -0
- package/dist/detectors/bracket-specified.d.ts.map +1 -0
- package/dist/detectors/bracket-specified.js +23 -0
- package/dist/detectors/bracket-specified.js.map +1 -0
- package/dist/detectors/broken-link.d.ts +6 -0
- package/dist/detectors/broken-link.d.ts.map +1 -1
- package/dist/detectors/broken-link.js +23 -2
- package/dist/detectors/broken-link.js.map +1 -1
- package/dist/detectors/caps-caption.d.ts +7 -6
- package/dist/detectors/caps-caption.d.ts.map +1 -1
- package/dist/detectors/caps-caption.js +38 -0
- package/dist/detectors/caps-caption.js.map +1 -1
- package/dist/detectors/caps-phrase.d.ts +6 -0
- package/dist/detectors/caps-phrase.d.ts.map +1 -0
- package/dist/detectors/caps-phrase.js +16 -0
- package/dist/detectors/caps-phrase.js.map +1 -0
- package/dist/detectors/char-shape.d.ts +1 -0
- package/dist/detectors/char-shape.d.ts.map +1 -1
- package/dist/detectors/char-shape.js +29 -16
- package/dist/detectors/char-shape.js.map +1 -1
- package/dist/detectors/cited-title.d.ts +3 -2
- package/dist/detectors/cited-title.d.ts.map +1 -1
- package/dist/detectors/cited-title.js +14 -3
- package/dist/detectors/cited-title.js.map +1 -1
- package/dist/detectors/counter-edge.d.ts +2 -1
- package/dist/detectors/counter-edge.d.ts.map +1 -1
- package/dist/detectors/counter-edge.js +17 -2
- package/dist/detectors/counter-edge.js.map +1 -1
- package/dist/detectors/date-any-year.d.ts +7 -3
- package/dist/detectors/date-any-year.d.ts.map +1 -1
- package/dist/detectors/date-any-year.js +25 -5
- package/dist/detectors/date-any-year.js.map +1 -1
- package/dist/detectors/date-format.d.ts.map +1 -1
- package/dist/detectors/date-format.js +2 -1
- package/dist/detectors/date-format.js.map +1 -1
- package/dist/detectors/date-year.d.ts +9 -3
- package/dist/detectors/date-year.d.ts.map +1 -1
- package/dist/detectors/date-year.js +45 -9
- package/dist/detectors/date-year.js.map +1 -1
- package/dist/detectors/deadline-unit.d.ts +20 -0
- package/dist/detectors/deadline-unit.d.ts.map +1 -0
- package/dist/detectors/deadline-unit.js +19 -0
- package/dist/detectors/deadline-unit.js.map +1 -0
- package/dist/detectors/defined-term-form.d.ts.map +1 -1
- package/dist/detectors/defined-term-form.js +33 -7
- package/dist/detectors/defined-term-form.js.map +1 -1
- package/dist/detectors/definition-use.d.ts.map +1 -1
- package/dist/detectors/definition-use.js +6 -3
- package/dist/detectors/definition-use.js.map +1 -1
- package/dist/detectors/derived-numbers.d.ts.map +1 -1
- package/dist/detectors/derived-numbers.js +17 -4
- package/dist/detectors/derived-numbers.js.map +1 -1
- package/dist/detectors/doubled-across-aside.d.ts +9 -0
- package/dist/detectors/doubled-across-aside.d.ts.map +1 -0
- package/dist/detectors/doubled-across-aside.js +27 -0
- package/dist/detectors/doubled-across-aside.js.map +1 -0
- package/dist/detectors/doubled-punctuation.d.ts +3 -3
- package/dist/detectors/doubled-punctuation.d.ts.map +1 -1
- package/dist/detectors/doubled-punctuation.js +23 -4
- package/dist/detectors/doubled-punctuation.js.map +1 -1
- package/dist/detectors/doubled-word.d.ts.map +1 -1
- package/dist/detectors/doubled-word.js +2 -1
- package/dist/detectors/doubled-word.js.map +1 -1
- package/dist/detectors/ellipsis.d.ts.map +1 -1
- package/dist/detectors/ellipsis.js +13 -1
- package/dist/detectors/ellipsis.js.map +1 -1
- package/dist/detectors/email-letter.d.ts +31 -1
- package/dist/detectors/email-letter.d.ts.map +1 -1
- package/dist/detectors/email-letter.js +76 -14
- package/dist/detectors/email-letter.js.map +1 -1
- package/dist/detectors/field-label.d.ts +7 -0
- package/dist/detectors/field-label.d.ts.map +1 -0
- package/dist/detectors/field-label.js +23 -0
- package/dist/detectors/field-label.js.map +1 -0
- package/dist/detectors/footnote-mismatch.d.ts +6 -1
- package/dist/detectors/footnote-mismatch.d.ts.map +1 -1
- package/dist/detectors/footnote-mismatch.js +28 -7
- package/dist/detectors/footnote-mismatch.js.map +1 -1
- package/dist/detectors/graded-step.d.ts +20 -0
- package/dist/detectors/graded-step.d.ts.map +1 -0
- package/dist/detectors/graded-step.js +33 -0
- package/dist/detectors/graded-step.js.map +1 -0
- package/dist/detectors/gram-openers.d.ts +8 -0
- package/dist/detectors/gram-openers.d.ts.map +1 -0
- package/dist/detectors/gram-openers.js +16 -0
- package/dist/detectors/gram-openers.js.map +1 -0
- package/dist/detectors/gram-place.d.ts +3 -1
- package/dist/detectors/gram-place.d.ts.map +1 -1
- package/dist/detectors/gram-place.js +8 -0
- package/dist/detectors/gram-place.js.map +1 -1
- package/dist/detectors/gram-predicate.d.ts.map +1 -1
- package/dist/detectors/gram-predicate.js +37 -3
- package/dist/detectors/gram-predicate.js.map +1 -1
- package/dist/detectors/heading-echo.d.ts.map +1 -1
- package/dist/detectors/heading-echo.js +28 -2
- package/dist/detectors/heading-echo.js.map +1 -1
- package/dist/detectors/ime-residue.d.ts +1 -1
- package/dist/detectors/ime-residue.d.ts.map +1 -1
- package/dist/detectors/ime-residue.js +4 -3
- package/dist/detectors/ime-residue.js.map +1 -1
- package/dist/detectors/kanji-compound-slip.d.ts +3 -0
- package/dist/detectors/kanji-compound-slip.d.ts.map +1 -0
- package/dist/detectors/kanji-compound-slip.js +50 -0
- package/dist/detectors/kanji-compound-slip.js.map +1 -0
- package/dist/detectors/kutoten-consistency.d.ts +10 -2
- package/dist/detectors/kutoten-consistency.d.ts.map +1 -1
- package/dist/detectors/kutoten-consistency.js +76 -12
- package/dist/detectors/kutoten-consistency.js.map +1 -1
- package/dist/detectors/lexicon.d.ts.map +1 -1
- package/dist/detectors/lexicon.js +8 -2
- package/dist/detectors/lexicon.js.map +1 -1
- package/dist/detectors/link-shape.d.ts.map +1 -1
- package/dist/detectors/link-shape.js +9 -3
- package/dist/detectors/link-shape.js.map +1 -1
- package/dist/detectors/list-item-ending.d.ts +18 -0
- package/dist/detectors/list-item-ending.d.ts.map +1 -0
- package/dist/detectors/list-item-ending.js +35 -0
- package/dist/detectors/list-item-ending.js.map +1 -0
- package/dist/detectors/list-parallel.d.ts +18 -5
- package/dist/detectors/list-parallel.d.ts.map +1 -1
- package/dist/detectors/list-parallel.js +116 -36
- package/dist/detectors/list-parallel.js.map +1 -1
- package/dist/detectors/list-sentence.d.ts +4 -0
- package/dist/detectors/list-sentence.d.ts.map +1 -1
- package/dist/detectors/list-sentence.js +1 -1
- package/dist/detectors/list-sentence.js.map +1 -1
- package/dist/detectors/mark-shape.d.ts +11 -4
- package/dist/detectors/mark-shape.d.ts.map +1 -1
- package/dist/detectors/mark-shape.js +58 -10
- package/dist/detectors/mark-shape.js.map +1 -1
- package/dist/detectors/mentioned-phrase.d.ts +9 -0
- package/dist/detectors/mentioned-phrase.d.ts.map +1 -0
- package/dist/detectors/mentioned-phrase.js +25 -0
- package/dist/detectors/mentioned-phrase.js.map +1 -0
- package/dist/detectors/middle-dot.d.ts +2 -2
- package/dist/detectors/middle-dot.d.ts.map +1 -1
- package/dist/detectors/middle-dot.js +4 -2
- package/dist/detectors/middle-dot.js.map +1 -1
- package/dist/detectors/modal-conflict.d.ts.map +1 -1
- package/dist/detectors/modal-conflict.js +5 -2
- package/dist/detectors/modal-conflict.js.map +1 -1
- package/dist/detectors/notice-paragraph.d.ts +9 -0
- package/dist/detectors/notice-paragraph.d.ts.map +1 -0
- package/dist/detectors/notice-paragraph.js +21 -0
- package/dist/detectors/notice-paragraph.js.map +1 -0
- package/dist/detectors/number-style.d.ts.map +1 -1
- package/dist/detectors/number-style.js +15 -2
- package/dist/detectors/number-style.js.map +1 -1
- package/dist/detectors/number-unit.d.ts.map +1 -1
- package/dist/detectors/number-unit.js +24 -1
- package/dist/detectors/number-unit.js.map +1 -1
- package/dist/detectors/numeral-opener.d.ts +6 -0
- package/dist/detectors/numeral-opener.d.ts.map +1 -1
- package/dist/detectors/numeral-opener.js +24 -1
- package/dist/detectors/numeral-opener.js.map +1 -1
- package/dist/detectors/obligation-without-deadline.d.ts +5 -1
- package/dist/detectors/obligation-without-deadline.d.ts.map +1 -1
- package/dist/detectors/obligation-without-deadline.js +44 -23
- package/dist/detectors/obligation-without-deadline.js.map +1 -1
- package/dist/detectors/open-end-class.d.ts +16 -0
- package/dist/detectors/open-end-class.d.ts.map +1 -0
- package/dist/detectors/open-end-class.js +65 -0
- package/dist/detectors/open-end-class.js.map +1 -0
- package/dist/detectors/open-end-condition.d.ts +7 -0
- package/dist/detectors/open-end-condition.d.ts.map +1 -0
- package/dist/detectors/open-end-condition.js +35 -0
- package/dist/detectors/open-end-condition.js.map +1 -0
- package/dist/detectors/open-end-names.d.ts +9 -3
- package/dist/detectors/open-end-names.d.ts.map +1 -1
- package/dist/detectors/open-end-names.js +5 -2
- package/dist/detectors/open-end-names.js.map +1 -1
- package/dist/detectors/open-end-question.d.ts +15 -0
- package/dist/detectors/open-end-question.d.ts.map +1 -0
- package/dist/detectors/open-end-question.js +37 -0
- package/dist/detectors/open-end-question.js.map +1 -0
- package/dist/detectors/open-end-scope.d.ts +16 -0
- package/dist/detectors/open-end-scope.d.ts.map +1 -0
- package/dist/detectors/open-end-scope.js +47 -0
- package/dist/detectors/open-end-scope.js.map +1 -0
- package/dist/detectors/opening-date.d.ts +10 -0
- package/dist/detectors/opening-date.d.ts.map +1 -0
- package/dist/detectors/opening-date.js +33 -0
- package/dist/detectors/opening-date.js.map +1 -0
- package/dist/detectors/ordinary-phrase.d.ts +20 -0
- package/dist/detectors/ordinary-phrase.d.ts.map +1 -0
- package/dist/detectors/ordinary-phrase.js +54 -0
- package/dist/detectors/ordinary-phrase.js.map +1 -0
- package/dist/detectors/organisation-run.d.ts +5 -1
- package/dist/detectors/organisation-run.d.ts.map +1 -1
- package/dist/detectors/organisation-run.js +7 -0
- package/dist/detectors/organisation-run.js.map +1 -1
- package/dist/detectors/orthographic-variant.d.ts.map +1 -1
- package/dist/detectors/orthographic-variant.js +56 -20
- package/dist/detectors/orthographic-variant.js.map +1 -1
- package/dist/detectors/orthography.d.ts.map +1 -1
- package/dist/detectors/orthography.js +6 -1
- package/dist/detectors/orthography.js.map +1 -1
- package/dist/detectors/oxford-comma.d.ts.map +1 -1
- package/dist/detectors/oxford-comma.js +38 -1
- package/dist/detectors/oxford-comma.js.map +1 -1
- package/dist/detectors/person-consistency.d.ts +2 -1
- package/dist/detectors/person-consistency.d.ts.map +1 -1
- package/dist/detectors/person-consistency.js +5 -3
- package/dist/detectors/person-consistency.js.map +1 -1
- package/dist/detectors/phrase-match.d.ts +2 -0
- package/dist/detectors/phrase-match.d.ts.map +1 -1
- package/dist/detectors/phrase-match.js +11 -4
- package/dist/detectors/phrase-match.js.map +1 -1
- package/dist/detectors/placeholder-text.d.ts.map +1 -1
- package/dist/detectors/placeholder-text.js +18 -3
- package/dist/detectors/placeholder-text.js.map +1 -1
- package/dist/detectors/placeholder.d.ts +3 -1
- package/dist/detectors/placeholder.d.ts.map +1 -1
- package/dist/detectors/placeholder.js +8 -3
- package/dist/detectors/placeholder.js.map +1 -1
- package/dist/detectors/postcode-end.d.ts +3 -0
- package/dist/detectors/postcode-end.d.ts.map +1 -0
- package/dist/detectors/postcode-end.js +15 -0
- package/dist/detectors/postcode-end.js.map +1 -0
- package/dist/detectors/prose-total-words.d.ts +5 -0
- package/dist/detectors/prose-total-words.d.ts.map +1 -0
- package/dist/detectors/prose-total-words.js +14 -0
- package/dist/detectors/prose-total-words.js.map +1 -0
- package/dist/detectors/registry/kanji-compound-slip.d.ts +3 -0
- package/dist/detectors/registry/kanji-compound-slip.d.ts.map +1 -0
- package/dist/detectors/registry/kanji-compound-slip.js +3 -0
- package/dist/detectors/registry/kanji-compound-slip.js.map +1 -0
- package/dist/detectors/registry/phrase-in-use.d.ts +3 -0
- package/dist/detectors/registry/phrase-in-use.d.ts.map +1 -0
- package/dist/detectors/registry/phrase-in-use.js +3 -0
- package/dist/detectors/registry/phrase-in-use.js.map +1 -0
- package/dist/detectors/requirement-smell.d.ts.map +1 -1
- package/dist/detectors/requirement-smell.js +29 -5
- package/dist/detectors/requirement-smell.js.map +1 -1
- package/dist/detectors/scaled-figure-unit.d.ts +28 -0
- package/dist/detectors/scaled-figure-unit.d.ts.map +1 -0
- package/dist/detectors/scaled-figure-unit.js +78 -0
- package/dist/detectors/scaled-figure-unit.js.map +1 -0
- package/dist/detectors/sentence-counts.d.ts +2 -0
- package/dist/detectors/sentence-counts.d.ts.map +1 -1
- package/dist/detectors/sentence-counts.js +18 -7
- package/dist/detectors/sentence-counts.js.map +1 -1
- package/dist/detectors/sentence-ending.d.ts +2 -0
- package/dist/detectors/sentence-ending.d.ts.map +1 -1
- package/dist/detectors/sentence-ending.js +10 -3
- package/dist/detectors/sentence-ending.js.map +1 -1
- package/dist/detectors/sentence-load.d.ts +4 -1
- package/dist/detectors/sentence-load.d.ts.map +1 -1
- package/dist/detectors/sentence-load.js +9 -2
- package/dist/detectors/sentence-load.js.map +1 -1
- package/dist/detectors/signals.d.ts.map +1 -1
- package/dist/detectors/signals.js +30 -50
- package/dist/detectors/signals.js.map +1 -1
- package/dist/detectors/structure-tree.d.ts.map +1 -1
- package/dist/detectors/structure-tree.js +14 -13
- package/dist/detectors/structure-tree.js.map +1 -1
- package/dist/detectors/structure.d.ts.map +1 -1
- package/dist/detectors/structure.js +2 -1
- package/dist/detectors/structure.js.map +1 -1
- package/dist/detectors/subject-name-line.d.ts +8 -0
- package/dist/detectors/subject-name-line.d.ts.map +1 -0
- package/dist/detectors/subject-name-line.js +53 -0
- package/dist/detectors/subject-name-line.js.map +1 -0
- package/dist/detectors/table-header.d.ts.map +1 -1
- package/dist/detectors/table-header.js +13 -1
- package/dist/detectors/table-header.js.map +1 -1
- package/dist/detectors/technical-docs.d.ts.map +1 -1
- package/dist/detectors/technical-docs.js +4 -1
- package/dist/detectors/technical-docs.js.map +1 -1
- package/dist/detectors/term-mention-sentence.d.ts +4 -0
- package/dist/detectors/term-mention-sentence.d.ts.map +1 -0
- package/dist/detectors/term-mention-sentence.js +28 -0
- package/dist/detectors/term-mention-sentence.js.map +1 -0
- package/dist/detectors/titled-run.d.ts +2 -1
- package/dist/detectors/titled-run.d.ts.map +1 -1
- package/dist/detectors/titled-run.js +9 -4
- package/dist/detectors/titled-run.js.map +1 -1
- package/dist/detectors/token-shape.d.ts.map +1 -1
- package/dist/detectors/token-shape.js +13 -5
- package/dist/detectors/token-shape.js.map +1 -1
- package/dist/detectors/vague-density.d.ts +5 -2
- package/dist/detectors/vague-density.d.ts.map +1 -1
- package/dist/detectors/vague-density.js +17 -4
- package/dist/detectors/vague-density.js.map +1 -1
- package/dist/document-paragraphs.d.ts +4 -0
- package/dist/document-paragraphs.d.ts.map +1 -0
- package/dist/document-paragraphs.js +24 -0
- package/dist/document-paragraphs.js.map +1 -0
- package/dist/document.d.ts.map +1 -1
- package/dist/document.js +2 -19
- package/dist/document.js.map +1 -1
- package/dist/figure-references.d.ts +3 -0
- package/dist/figure-references.d.ts.map +1 -1
- package/dist/figure-references.js +6 -2
- package/dist/figure-references.js.map +1 -1
- package/dist/format-template.d.ts +3 -0
- package/dist/format-template.d.ts.map +1 -0
- package/dist/format-template.js +13 -0
- package/dist/format-template.js.map +1 -0
- package/dist/formula-brackets.d.ts +7 -0
- package/dist/formula-brackets.d.ts.map +1 -0
- package/dist/formula-brackets.js +29 -0
- package/dist/formula-brackets.js.map +1 -0
- package/dist/html/html-markdown.d.ts.map +1 -1
- package/dist/html/html-markdown.js +2 -1
- package/dist/html/html-markdown.js.map +1 -1
- package/dist/html/html-site-chrome.d.ts +5 -0
- package/dist/html/html-site-chrome.d.ts.map +1 -0
- package/dist/html/html-site-chrome.js +33 -0
- package/dist/html/html-site-chrome.js.map +1 -0
- 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/kanji-compound-slip.d.ts +32 -0
- package/dist/kanji-compound-slip.d.ts.map +1 -0
- package/dist/kanji-compound-slip.js +63 -0
- package/dist/kanji-compound-slip.js.map +1 -0
- package/dist/latin-company-form.d.ts +6 -0
- package/dist/latin-company-form.d.ts.map +1 -0
- package/dist/latin-company-form.js +32 -0
- package/dist/latin-company-form.js.map +1 -0
- package/dist/latin-word-boundary.d.ts +15 -0
- package/dist/latin-word-boundary.d.ts.map +1 -0
- package/dist/latin-word-boundary.js +40 -0
- package/dist/latin-word-boundary.js.map +1 -0
- package/dist/lexicon-genres.d.ts +2 -0
- package/dist/lexicon-genres.d.ts.map +1 -1
- package/dist/lexicon-genres.js +1 -1
- package/dist/lexicon-genres.js.map +1 -1
- package/dist/link-shape.d.ts +5 -0
- package/dist/link-shape.d.ts.map +1 -1
- package/dist/link-shape.js +18 -0
- package/dist/link-shape.js.map +1 -1
- package/dist/link-title.d.ts +4 -0
- package/dist/link-title.d.ts.map +1 -0
- package/dist/link-title.js +33 -0
- package/dist/link-title.js.map +1 -0
- package/dist/list-item-paragraphs.d.ts +15 -0
- package/dist/list-item-paragraphs.d.ts.map +1 -0
- package/dist/list-item-paragraphs.js +19 -0
- package/dist/list-item-paragraphs.js.map +1 -0
- package/dist/literal-quote.d.ts +12 -0
- package/dist/literal-quote.d.ts.map +1 -0
- package/dist/literal-quote.js +43 -0
- package/dist/literal-quote.js.map +1 -0
- package/dist/nado-closed-list.d.ts.map +1 -1
- package/dist/nado-closed-list.js +39 -5
- package/dist/nado-closed-list.js.map +1 -1
- package/dist/name-separator.d.ts.map +1 -1
- package/dist/name-separator.js +25 -2
- package/dist/name-separator.js.map +1 -1
- package/dist/nominal-stem.d.ts +14 -1
- package/dist/nominal-stem.d.ts.map +1 -1
- package/dist/nominal-stem.js +42 -4
- package/dist/nominal-stem.js.map +1 -1
- package/dist/person-forms.d.ts +8 -0
- package/dist/person-forms.d.ts.map +1 -1
- package/dist/person-forms.js +12 -0
- package/dist/person-forms.js.map +1 -1
- package/dist/plugin.d.ts +5 -0
- package/dist/plugin.d.ts.map +1 -1
- package/dist/preamble-paragraphs.d.ts +3 -2
- package/dist/preamble-paragraphs.d.ts.map +1 -1
- package/dist/preamble-paragraphs.js +8 -2
- package/dist/preamble-paragraphs.js.map +1 -1
- package/dist/spacing-code.d.ts +1 -0
- package/dist/spacing-code.d.ts.map +1 -1
- package/dist/spacing-code.js +4 -1
- package/dist/spacing-code.js.map +1 -1
- package/dist/split-noun.d.ts +10 -0
- package/dist/split-noun.d.ts.map +1 -0
- package/dist/split-noun.js +50 -0
- package/dist/split-noun.js.map +1 -0
- package/dist/structure/attached-parts.d.ts +11 -0
- package/dist/structure/attached-parts.d.ts.map +1 -0
- package/dist/structure/attached-parts.js +31 -0
- package/dist/structure/attached-parts.js.map +1 -0
- package/dist/structure/build.d.ts.map +1 -1
- package/dist/structure/build.js +7 -4
- package/dist/structure/build.js.map +1 -1
- package/dist/structure/contents-lines.d.ts +7 -0
- package/dist/structure/contents-lines.d.ts.map +1 -0
- package/dist/structure/contents-lines.js +51 -0
- package/dist/structure/contents-lines.js.map +1 -0
- package/dist/structure/defining-names.d.ts +37 -0
- package/dist/structure/defining-names.d.ts.map +1 -0
- package/dist/structure/defining-names.js +122 -0
- package/dist/structure/defining-names.js.map +1 -0
- package/dist/structure/definition-use.d.ts +3 -2
- package/dist/structure/definition-use.d.ts.map +1 -1
- package/dist/structure/definition-use.js +99 -31
- package/dist/structure/definition-use.js.map +1 -1
- package/dist/structure/issues.d.ts +4 -1
- package/dist/structure/issues.d.ts.map +1 -1
- package/dist/structure/issues.js +5 -2
- package/dist/structure/issues.js.map +1 -1
- package/dist/structure/list-lead-in.d.ts +19 -0
- package/dist/structure/list-lead-in.d.ts.map +1 -0
- package/dist/structure/list-lead-in.js +85 -0
- package/dist/structure/list-lead-in.js.map +1 -0
- package/dist/structure/modal-conflict.d.ts +7 -2
- package/dist/structure/modal-conflict.d.ts.map +1 -1
- package/dist/structure/modal-conflict.js +36 -7
- package/dist/structure/modal-conflict.js.map +1 -1
- package/dist/structure/negated-act.d.ts +17 -0
- package/dist/structure/negated-act.d.ts.map +1 -0
- package/dist/structure/negated-act.js +32 -0
- package/dist/structure/negated-act.js.map +1 -0
- package/dist/structure/numbering.d.ts +3 -1
- package/dist/structure/numbering.d.ts.map +1 -1
- package/dist/structure/numbering.js +20 -8
- package/dist/structure/numbering.js.map +1 -1
- package/dist/structure/prose-total.d.ts +4 -0
- package/dist/structure/prose-total.d.ts.map +1 -1
- package/dist/structure/prose-total.js +16 -6
- package/dist/structure/prose-total.js.map +1 -1
- package/dist/structure/repeated-number.d.ts +24 -0
- package/dist/structure/repeated-number.d.ts.map +1 -0
- package/dist/structure/repeated-number.js +89 -0
- package/dist/structure/repeated-number.js.map +1 -0
- package/dist/structure/rounded-amount.d.ts +8 -0
- package/dist/structure/rounded-amount.d.ts.map +1 -0
- package/dist/structure/rounded-amount.js +48 -0
- package/dist/structure/rounded-amount.js.map +1 -0
- package/dist/structure/side-step.d.ts +8 -0
- package/dist/structure/side-step.d.ts.map +1 -0
- package/dist/structure/side-step.js +21 -0
- package/dist/structure/side-step.js.map +1 -0
- package/dist/structure/spanned-label-blank.d.ts +8 -0
- package/dist/structure/spanned-label-blank.d.ts.map +1 -0
- package/dist/structure/spanned-label-blank.js +37 -0
- package/dist/structure/spanned-label-blank.js.map +1 -0
- package/dist/structure/universal.d.ts +3 -1
- package/dist/structure/universal.d.ts.map +1 -1
- package/dist/structure/universal.js +9 -2
- package/dist/structure/universal.js.map +1 -1
- package/dist/structure/unspaced-heading.d.ts +10 -0
- package/dist/structure/unspaced-heading.d.ts.map +1 -0
- package/dist/structure/unspaced-heading.js +24 -0
- package/dist/structure/unspaced-heading.js.map +1 -0
- package/dist/structure/written-list-path.d.ts +10 -0
- package/dist/structure/written-list-path.d.ts.map +1 -0
- package/dist/structure/written-list-path.js +37 -0
- package/dist/structure/written-list-path.js.map +1 -0
- package/dist/structure/written-resolve.d.ts +1 -1
- package/dist/structure/written-resolve.d.ts.map +1 -1
- package/dist/structure/written-resolve.js +15 -9
- package/dist/structure/written-resolve.js.map +1 -1
- package/dist/title-line.d.ts +21 -0
- package/dist/title-line.d.ts.map +1 -0
- package/dist/title-line.js +27 -0
- package/dist/title-line.js.map +1 -0
- package/package.json +3 -3
- package/rules/all-caps-shouting.yaml +2 -2
- package/rules/announced-count-mismatch.yaml +2 -2
- package/rules/bare-url-mix.yaml +2 -2
- package/rules/bracket-nesting.yaml +2 -2
- package/rules/bracket-width-consistency.yaml +2 -2
- package/rules/broken-link.yaml +2 -2
- package/rules/cliche.yaml +4 -3
- package/rules/code-fence-language-mix.yaml +2 -2
- package/rules/comparative-without-baseline.yaml +2 -2
- package/rules/dangling-figure-reference.yaml +2 -2
- package/rules/date-without-year.yaml +10 -2
- package/rules/defined-term-form.yaml +2 -2
- package/rules/doubled-punctuation.yaml +4 -4
- package/rules/doubled-word.yaml +2 -2
- package/rules/duplicate-definition.yaml +2 -2
- package/rules/duration-mismatch.yaml +2 -2
- package/rules/email-greeting-closing.yaml +2 -2
- package/rules/email-subject-length.yaml +2 -2
- package/rules/empty-table-cell.yaml +2 -2
- package/rules/footnote-mismatch.yaml +2 -2
- package/rules/heading-echo.yaml +2 -2
- package/rules/ime-residue.yaml +2 -2
- package/rules/kanji-compound-slip.yaml +67 -0
- package/rules/kutoten-consistency.yaml +4 -4
- package/rules/latin-spacing.yaml +2 -2
- package/rules/list-item-form-mix.yaml +2 -2
- package/rules/max-kanji-continuous.yaml +6 -4
- package/rules/modal-conflict.yaml +1 -0
- package/rules/nado-closed-list.yaml +2 -2
- package/rules/ngram-repetition.yaml +2 -2
- package/rules/no-doubled-joshi.yaml +2 -2
- package/rules/no-mixed-desumasu.yaml +3 -3
- package/rules/no-nakaguro-parallel.yaml +2 -0
- package/rules/number-without-unit.yaml +9 -4
- package/rules/numbering-gap.yaml +7 -2
- package/rules/numeral-sentence-start.yaml +2 -2
- package/rules/obligation-without-deadline.yaml +2 -2
- package/rules/orthographic-variant.yaml +2 -2
- package/rules/oxford-comma-consistency.yaml +3 -3
- package/rules/preamble-length.yaml +2 -2
- package/rules/requirement-smell.yaml +3 -3
- package/rules/table-header-variant.yaml +2 -2
- package/rules/teki-overuse.yaml +3 -2
- package/rules/total-mismatch.yaml +2 -2
- package/rules/translationese-density.yaml +2 -2
- package/rules/undefined-acronym.yaml +4 -2
- package/rules/unfilled-placeholder.yaml +4 -4
- package/rules/use-before-definition.yaml +2 -2
- package/rules/vague-word-density.yaml +2 -2
- package/skills/chaff/SKILL.md +7 -1
- package/src/announced-count.ts +35 -8
- package/src/chronology-rows.ts +86 -0
- package/src/company-name-run.ts +37 -0
- package/src/compound-head.ts +25 -0
- package/src/date-format.ts +43 -4
- package/src/derived/count-per-period.ts +28 -0
- package/src/detectors/acronym-context.ts +30 -1
- package/src/detectors/acronym-expansion.ts +47 -6
- package/src/detectors/acronym-known.ts +40 -0
- package/src/detectors/acronym-link.ts +37 -0
- package/src/detectors/acronym-name.ts +27 -4
- package/src/detectors/addressee-edge.ts +16 -0
- package/src/detectors/announced-count.ts +1 -0
- package/src/detectors/bare-comparative.ts +44 -3
- package/src/detectors/blank-structure.ts +21 -10
- package/src/detectors/bracket-specified.ts +25 -0
- package/src/detectors/broken-link.ts +26 -2
- package/src/detectors/caps-caption.ts +42 -0
- package/src/detectors/caps-phrase.ts +17 -0
- package/src/detectors/char-shape.ts +30 -9
- package/src/detectors/cited-title.ts +15 -3
- package/src/detectors/counter-edge.ts +17 -2
- package/src/detectors/date-any-year.ts +27 -6
- package/src/detectors/date-format.ts +2 -1
- package/src/detectors/date-year.ts +64 -16
- package/src/detectors/deadline-unit.ts +40 -0
- package/src/detectors/defined-term-form.ts +32 -12
- package/src/detectors/definition-use.ts +7 -3
- package/src/detectors/derived-numbers.ts +19 -4
- package/src/detectors/doubled-across-aside.ts +36 -0
- package/src/detectors/doubled-punctuation.ts +28 -7
- package/src/detectors/doubled-word.ts +2 -1
- package/src/detectors/ellipsis.ts +16 -1
- package/src/detectors/email-letter.ts +97 -21
- package/src/detectors/field-label.ts +25 -0
- package/src/detectors/footnote-mismatch.ts +39 -7
- package/src/detectors/graded-step.ts +52 -0
- package/src/detectors/gram-openers.ts +21 -0
- package/src/detectors/gram-place.ts +10 -1
- package/src/detectors/gram-predicate.ts +43 -3
- package/src/detectors/heading-echo.ts +31 -3
- package/src/detectors/ime-residue.ts +4 -3
- package/src/detectors/kanji-compound-slip.ts +61 -0
- package/src/detectors/kutoten-consistency.ts +93 -14
- package/src/detectors/lexicon.ts +12 -3
- package/src/detectors/link-shape.ts +10 -3
- package/src/detectors/list-item-ending.ts +51 -0
- package/src/detectors/list-parallel.ts +158 -33
- package/src/detectors/list-sentence.ts +5 -1
- package/src/detectors/mark-shape.ts +64 -10
- package/src/detectors/mentioned-phrase.ts +38 -0
- package/src/detectors/middle-dot.ts +7 -2
- package/src/detectors/modal-conflict.ts +5 -2
- package/src/detectors/notice-paragraph.ts +33 -0
- package/src/detectors/number-style.ts +14 -2
- package/src/detectors/number-unit.ts +28 -1
- package/src/detectors/numeral-opener.ts +27 -1
- package/src/detectors/obligation-without-deadline.ts +29 -4
- package/src/detectors/open-end-class.ts +78 -0
- package/src/detectors/open-end-condition.ts +40 -0
- package/src/detectors/open-end-names.ts +9 -3
- package/src/detectors/open-end-question.ts +50 -0
- package/src/detectors/open-end-scope.ts +62 -0
- package/src/detectors/opening-date.ts +47 -0
- package/src/detectors/ordinary-phrase.ts +62 -0
- package/src/detectors/organisation-run.ts +13 -2
- package/src/detectors/orthographic-variant.ts +85 -25
- package/src/detectors/orthography.ts +10 -1
- package/src/detectors/oxford-comma.ts +40 -1
- package/src/detectors/person-consistency.ts +5 -3
- package/src/detectors/phrase-match.ts +16 -5
- package/src/detectors/placeholder-text.ts +18 -3
- package/src/detectors/placeholder.ts +10 -3
- package/src/detectors/postcode-end.ts +16 -0
- package/src/detectors/prose-total-words.ts +18 -0
- package/src/detectors/registry/kanji-compound-slip.ts +4 -0
- package/src/detectors/registry/phrase-in-use.ts +4 -0
- package/src/detectors/requirement-smell.ts +38 -5
- package/src/detectors/scaled-figure-unit.ts +103 -0
- package/src/detectors/sentence-counts.ts +24 -8
- package/src/detectors/sentence-ending.ts +12 -3
- package/src/detectors/sentence-load.ts +9 -2
- package/src/detectors/signals.ts +33 -51
- package/src/detectors/structure-tree.ts +14 -14
- package/src/detectors/structure.ts +2 -1
- package/src/detectors/subject-name-line.ts +66 -0
- package/src/detectors/table-header.ts +15 -1
- package/src/detectors/technical-docs.ts +5 -1
- package/src/detectors/term-mention-sentence.ts +31 -0
- package/src/detectors/titled-run.ts +9 -4
- package/src/detectors/token-shape.ts +14 -5
- package/src/detectors/vague-density.ts +20 -4
- package/src/document-paragraphs.ts +27 -0
- package/src/document.ts +3 -23
- package/src/figure-references.ts +7 -2
- package/src/format-template.ts +15 -0
- package/src/formula-brackets.ts +36 -0
- package/src/html/html-markdown.ts +2 -1
- package/src/html/html-site-chrome.ts +47 -0
- package/src/joined-view.ts +1 -0
- package/src/kanji-compound-slip.ts +98 -0
- package/src/latin-company-form.ts +38 -0
- package/src/latin-word-boundary.ts +46 -0
- package/src/lexicon-genres.ts +1 -1
- package/src/link-shape.ts +19 -0
- package/src/link-title.ts +38 -0
- package/src/list-item-paragraphs.ts +22 -0
- package/src/literal-quote.ts +55 -0
- package/src/nado-closed-list.ts +48 -5
- package/src/name-separator.ts +27 -2
- package/src/nominal-stem.ts +62 -5
- package/src/person-forms.ts +15 -0
- package/src/plugin.ts +5 -0
- package/src/preamble-paragraphs.ts +13 -3
- package/src/spacing-code.ts +5 -1
- package/src/split-noun.ts +63 -0
- package/src/structure/attached-parts.ts +32 -0
- package/src/structure/build.ts +7 -4
- package/src/structure/contents-lines.ts +63 -0
- package/src/structure/defining-names.ts +155 -0
- package/src/structure/definition-use.ts +116 -25
- package/src/structure/issues.ts +7 -3
- package/src/structure/list-lead-in.ts +94 -0
- package/src/structure/modal-conflict.ts +42 -7
- package/src/structure/negated-act.ts +49 -0
- package/src/structure/numbering.ts +24 -9
- package/src/structure/prose-total.ts +20 -6
- package/src/structure/repeated-number.ts +110 -0
- package/src/structure/rounded-amount.ts +55 -0
- package/src/structure/side-step.ts +21 -0
- package/src/structure/spanned-label-blank.ts +39 -0
- package/src/structure/universal.ts +11 -2
- package/src/structure/unspaced-heading.ts +27 -0
- package/src/structure/written-list-path.ts +46 -0
- package/src/structure/written-resolve.ts +16 -9
- package/src/title-line.ts +52 -0
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// A date written without its year where the reader cannot tell which year it is: the document's dated dates span more
|
|
2
2
|
// than one year, or its dates cross the turn of a year. Pure; reads the date nodes of the structure tree.
|
|
3
|
-
import type { Detector, Finding, LexiconEntry, ProseDocument, StructureNode } from "../plugin.ts";
|
|
3
|
+
import type { Detector, DetectorOptions, Finding, LexiconEntry, ProseDocument, StructureNode } from "../plugin.ts";
|
|
4
|
+
import { isIn } from "../lexicon-genres.ts";
|
|
4
5
|
import { inDocumentOrder } from "../structure/issues.ts";
|
|
5
6
|
import { quoteAt } from "./structure-tree.ts";
|
|
6
7
|
import {
|
|
@@ -13,17 +14,29 @@ import {
|
|
|
13
14
|
type MarkerReach,
|
|
14
15
|
} from "./date-any-year.ts";
|
|
15
16
|
import { datesUnderYearHeadings, yearLabelOf, type HeadingYearWords } from "./date-heading-year.ts";
|
|
17
|
+
import { openingDateOf } from "./opening-date.ts";
|
|
18
|
+
import { withNearestYear } from "../structure/weekday.ts";
|
|
16
19
|
|
|
17
|
-
/**
|
|
18
|
-
|
|
20
|
+
/**
|
|
21
|
+
* The words that make a yearless date any year's (毎年, 例えば, "each"), that join a period's two ends (から, "to"), and
|
|
22
|
+
* that join the names of one list (及び, "and"), for a marker named in one list with the date.
|
|
23
|
+
*/
|
|
24
|
+
export type AnyYearWords = { readonly markers: readonly AnyYearMarker[]; readonly joiners: readonly string[]; readonly listJoiners?: readonly string[] };
|
|
19
25
|
|
|
20
26
|
const NO_ANY_YEAR_WORDS: AnyYearWords = { markers: [], joiners: [] };
|
|
21
27
|
|
|
22
28
|
/**
|
|
23
29
|
* A date as the tree reads it: "2026-10-14" with its year, "10-14" without. `yearHeaded`: the heading of its section
|
|
24
|
-
* names a year ("FY27-Q2 (May 1)", 「2026年度の予定」), which the date takes.
|
|
30
|
+
* names a year ("FY27-Q2 (May 1)", 「2026年度の予定」), which the date takes. `nearStamp`: the document's own date (minutes
|
|
31
|
+
* dated "January 17, 2024") lies within a few months of it in one year only, which the date takes.
|
|
25
32
|
*/
|
|
26
|
-
export type DatePoint = {
|
|
33
|
+
export type DatePoint = {
|
|
34
|
+
readonly offset: number;
|
|
35
|
+
readonly end: number;
|
|
36
|
+
readonly value: string;
|
|
37
|
+
readonly yearHeaded?: boolean;
|
|
38
|
+
readonly nearStamp?: boolean;
|
|
39
|
+
};
|
|
27
40
|
|
|
28
41
|
/** The first and the last year of the document's dated dates. */
|
|
29
42
|
export type YearSpan = { readonly from: number; readonly to: number };
|
|
@@ -118,7 +131,8 @@ const hasYearWord = (point: DatePoint, source: string, yearWords: readonly YearW
|
|
|
118
131
|
*/
|
|
119
132
|
const yearGiven = (point: DatePoint, anchors: readonly DatePoint[], source: string, yearWords: readonly YearWord[]): boolean => {
|
|
120
133
|
const start = lineStart(source, point.offset);
|
|
121
|
-
|
|
134
|
+
const placed = point.yearHeaded === true || point.nearStamp === true;
|
|
135
|
+
return placed || hasYearWord(point, source, yearWords) || anchors.some((other) => other.offset >= start && other.offset < point.offset);
|
|
122
136
|
};
|
|
123
137
|
|
|
124
138
|
const DAY_MS = 24 * 60 * 60 * 1000;
|
|
@@ -153,10 +167,14 @@ export const yearGaps = (
|
|
|
153
167
|
const previousOf = new Map(points.map((point, index) => [point, points[index - 1]]));
|
|
154
168
|
const yearlySpan = (start: DatePoint | undefined, end: DatePoint | undefined): boolean =>
|
|
155
169
|
start !== undefined && end !== undefined && wholeYear(start.value, end.value) && opensSpan(start, end, source, anyYear.joiners);
|
|
170
|
+
const markedAnyYear = (point: DatePoint | undefined): boolean => point !== undefined && anyYearDate(point, source, anyYear.markers, anyYear.listJoiners);
|
|
156
171
|
const yearOfAny = (point: DatePoint): boolean => {
|
|
157
172
|
const next = nextOf.get(point);
|
|
158
|
-
|
|
159
|
-
if (
|
|
173
|
+
const previous = previousOf.get(point);
|
|
174
|
+
if (markedAnyYear(point)) return true;
|
|
175
|
+
if (yearlySpan(point, next) || yearlySpan(previous, point)) return true;
|
|
176
|
+
// The end of a period whose start is any year's is too: 「祝休日及び12月29日から1月3日まで」.
|
|
177
|
+
if (previous !== undefined && opensSpan(previous, point, source, anyYear.joiners) && markedAnyYear(previous)) return true;
|
|
160
178
|
return next !== undefined && opensSpan(point, next, source, anyYear.joiners) && hasYearWord(next, source, yearWords);
|
|
161
179
|
};
|
|
162
180
|
// A recurring day or an example's date has no year to write, and is not the document's way of writing dates either.
|
|
@@ -172,15 +190,41 @@ export const yearGaps = (
|
|
|
172
190
|
|
|
173
191
|
const NO_HEADING_YEAR_WORDS: HeadingYearWords = { eras: [], labels: [] };
|
|
174
192
|
|
|
175
|
-
|
|
193
|
+
/** A yearless date that the document's own date ("2024-01-17") places in one year: within a few months of it. */
|
|
194
|
+
const nearStampOf = (value: string, stamp: string | undefined): boolean => stamp !== undefined && withNearestYear(value, stamp) !== undefined;
|
|
195
|
+
|
|
196
|
+
export const datePoints = (tree: StructureNode, headingWords: HeadingYearWords = NO_HEADING_YEAR_WORDS, stamp?: string): DatePoint[] => {
|
|
176
197
|
const yearHeaded = new Set(datesUnderYearHeadings(tree, headingWords));
|
|
177
|
-
return inDocumentOrder(tree).flatMap((node) =>
|
|
178
|
-
node.kind
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
);
|
|
198
|
+
return inDocumentOrder(tree).flatMap((node) => {
|
|
199
|
+
if (node.kind !== "date") return [];
|
|
200
|
+
const value = String(node.attrs["value"]);
|
|
201
|
+
return [{ offset: node.span.start, end: node.span.end, value, yearHeaded: yearHeaded.has(node.span.start), nearStamp: nearStampOf(value, stamp) }];
|
|
202
|
+
});
|
|
182
203
|
};
|
|
183
204
|
|
|
205
|
+
/**
|
|
206
|
+
* The document's own date: a date alone, or labelled, on a line of its title block. A date-only paragraph further down
|
|
207
|
+
* (an agenda's "April 4, 2023") is a date the text talks about, not the document's.
|
|
208
|
+
*/
|
|
209
|
+
const stampOf = (doc: ProseDocument, tree: StructureNode): string | undefined => {
|
|
210
|
+
const dates = inDocumentOrder(tree).flatMap((node) => (node.kind === "date" ? [{ ...node.span, value: String(node.attrs["value"]) }] : []));
|
|
211
|
+
const labels = (doc.lexicons["date-stamp-label"] ?? []).map((entry: LexiconEntry) => entry.pattern);
|
|
212
|
+
return openingDateOf(doc.source, dates, labels);
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* The genres whose date at the top is the occasion the text is about (minutes, a letter), from the rule's dated_genres.
|
|
217
|
+
* A web page's date at the top says when it was posted, and its days and months may be any year's.
|
|
218
|
+
*/
|
|
219
|
+
const datedByOccasion = (options: DetectorOptions): boolean => {
|
|
220
|
+
const genres = options.settings?.["dated_genres"];
|
|
221
|
+
const genre = options.genre;
|
|
222
|
+
return genre !== undefined && Array.isArray(genres) && genres.some((target: string) => isIn(genre, target));
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
const pointsOf = (doc: ProseDocument, tree: StructureNode, options: DetectorOptions): DatePoint[] =>
|
|
226
|
+
datePoints(tree, headingYearWordsOf(doc), datedByOccasion(options) ? stampOf(doc, tree) : undefined);
|
|
227
|
+
|
|
184
228
|
const yearWordsOf = (doc: ProseDocument): YearWord[] =>
|
|
185
229
|
(doc.lexicons[YEAR_REFERENCE_LEXICON] ?? []).map((entry: LexiconEntry) => ({ word: entry.pattern, position: entry.position }));
|
|
186
230
|
|
|
@@ -192,17 +236,21 @@ const headingYearWordsOf = (doc: ProseDocument): HeadingYearWords => ({
|
|
|
192
236
|
),
|
|
193
237
|
});
|
|
194
238
|
|
|
239
|
+
/** The words that join the names of one list (及び, "and"): a `listed` marker stands before the date with these between. */
|
|
240
|
+
const LIST_JOINER_LEXICON = "enumeration-joiner";
|
|
241
|
+
|
|
195
242
|
const reachOf = (group: string | undefined): MarkerReach => MARKER_REACHES.find((reach) => reach === group) ?? "sentence";
|
|
196
243
|
|
|
197
244
|
const anyYearWordsOf = (doc: ProseDocument): AnyYearWords => ({
|
|
198
245
|
markers: (doc.lexicons[ANY_YEAR_MARKER_LEXICON] ?? []).map((entry: LexiconEntry) => ({ word: entry.pattern, reach: reachOf(entry.group) })),
|
|
199
246
|
joiners: (doc.lexicons[YEAR_SPAN_JOINER_LEXICON] ?? []).map((entry: LexiconEntry) => entry.pattern),
|
|
247
|
+
listJoiners: (doc.lexicons[LIST_JOINER_LEXICON] ?? []).map((entry: LexiconEntry) => entry.pattern),
|
|
200
248
|
});
|
|
201
249
|
|
|
202
|
-
export const dateWithoutYear: Detector = (doc: ProseDocument): Finding[] =>
|
|
250
|
+
export const dateWithoutYear: Detector = (doc: ProseDocument, options: DetectorOptions): Finding[] =>
|
|
203
251
|
doc.structure === undefined
|
|
204
252
|
? []
|
|
205
|
-
: yearGaps(
|
|
253
|
+
: yearGaps(pointsOf(doc, doc.structure, options), doc.source, yearWordsOf(doc), anyYearWordsOf(doc)).map((gap) => ({
|
|
206
254
|
rule: "date-without-year",
|
|
207
255
|
severity: "warning",
|
|
208
256
|
line: 0,
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { Span } from "../plugin.ts";
|
|
2
|
+
|
|
3
|
+
// The unit a duty is written in: its article, else its list, else its paragraph. A duty in a unit that names the dated
|
|
4
|
+
// deadline of its own act is timed by it: 「乙は代金を支払う。」 beside 「支払期日までに支払うことができないときは…」. A date
|
|
5
|
+
// for another act (a delivery date beside a duty to pay) times nothing. Pure.
|
|
6
|
+
|
|
7
|
+
/** The spans a document is cut into, from the widest kind that counts as one unit to the narrowest. */
|
|
8
|
+
export type Units = { readonly articles: readonly Span[]; readonly lists: readonly Span[]; readonly paragraphs: readonly Span[] };
|
|
9
|
+
|
|
10
|
+
/** A dated deadline's name (支払期日, due date) and the act it times (支払, pay). */
|
|
11
|
+
export type NamedDeadline = { readonly name: string; readonly act: string };
|
|
12
|
+
|
|
13
|
+
const within = (span: Span, offset: number): boolean => offset >= span.start && offset < span.end;
|
|
14
|
+
|
|
15
|
+
/** The innermost span holding the offset: an article nested in another is a unit of its own. */
|
|
16
|
+
const innermost = (spans: readonly Span[], offset: number): Span | undefined =>
|
|
17
|
+
spans
|
|
18
|
+
.filter((span) => within(span, offset))
|
|
19
|
+
.reduce<Span | undefined>((inner, span) => (inner === undefined || span.start > inner.start ? span : inner), undefined);
|
|
20
|
+
|
|
21
|
+
/** The unit that holds the offset: its article, else its list, else its paragraph; undefined when none does. */
|
|
22
|
+
export const unitAt = (units: Units, offset: number): Span | undefined =>
|
|
23
|
+
[units.articles, units.lists, units.paragraphs].map((spans) => innermost(spans, offset)).find((span) => span !== undefined);
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* The acts whose dated deadline the unit holding the offset names. holds: whether a text holds a name (as a phrase, so
|
|
27
|
+
* that a Latin name is read as whole words).
|
|
28
|
+
*/
|
|
29
|
+
export const actsTimedAt = (
|
|
30
|
+
source: string,
|
|
31
|
+
units: Units,
|
|
32
|
+
offset: number,
|
|
33
|
+
deadlines: readonly NamedDeadline[],
|
|
34
|
+
holds: (text: string, name: string) => boolean,
|
|
35
|
+
): string[] => {
|
|
36
|
+
const unit = unitAt(units, offset);
|
|
37
|
+
if (unit === undefined) return [];
|
|
38
|
+
const text = source.slice(unit.start, unit.end);
|
|
39
|
+
return [...new Set(deadlines.filter((deadline) => holds(text, deadline.name)).map((deadline) => deadline.act))];
|
|
40
|
+
};
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
// A defined term used later in another form, and a long name used again after its short name was defined. Pure. The
|
|
2
2
|
// definitions come from the document's structure tree (以下「甲」という, ("Seller")); which words may stand in a long
|
|
3
3
|
// name is read from the tokens' parts of speech.
|
|
4
|
+
import { inEntryOfTerm } from "./term-mention-sentence.ts";
|
|
4
5
|
import { definedTerms, usesOf, type BodyText, type DefinedTerm } from "../structure/definition-use.ts";
|
|
5
6
|
import { escapeRegExp } from "../orthography.ts";
|
|
6
7
|
import { prefixGroupsOf, prefixVariants, variantUses } from "../structure/term-prefix.ts";
|
|
7
8
|
import { quoteAt } from "./structure-tree.ts";
|
|
8
9
|
import { isPartOfAddress } from "./address-word.ts";
|
|
9
10
|
import { exactOffsets, extendedAfter, extendedBefore } from "./term-extension.ts";
|
|
11
|
+
import { headsKindPhrase, insideLongerTerm, isNamedAsTerm, isNarrowedUse } from "./ordinary-phrase.ts";
|
|
10
12
|
import { CLOSES_SENTENCE, isNameField, isSignatureLine, opensClosingBlock, SLACK } from "./name-field.ts";
|
|
11
13
|
import type { Detector, Finding, ProseDocument, Sentence, Span, Token } from "../plugin.ts";
|
|
12
14
|
|
|
@@ -146,18 +148,36 @@ const prefixFindings = (doc: ProseDocument, texts: readonly BodyText[], term: De
|
|
|
146
148
|
),
|
|
147
149
|
);
|
|
148
150
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
151
|
+
const patternsOf = (doc: ProseDocument, lexicon: string): string[] => (doc.lexicons[lexicon] ?? []).map((entry) => entry.pattern);
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* The term set in quotes again, unless the document's profile says a quoted term is the term mentioned (「株主」とあるのは),
|
|
155
|
+
* or the words before the quote name it as a term (the definition of "Material Adverse Effect").
|
|
156
|
+
*/
|
|
157
|
+
const quotedFindings = (doc: ProseDocument, term: DefinedTerm, uses: readonly number[], definingEnd: number): Finding[] => {
|
|
158
|
+
if (doc.profile?.quoteMentionsTerm === true) return [];
|
|
159
|
+
const cues = patternsOf(doc, "term-mention-cue");
|
|
160
|
+
return quotedUses(doc.source, term.term, uses, definingEnd)
|
|
161
|
+
.filter((offset) => !isNamedAsTerm(doc.source, offset - 1, cues))
|
|
162
|
+
.map((offset) => finding(doc, "defined-term-form", offset, "quoted", { term: term.term, line: term.line }));
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* A lower-case use that is the ordinary word in a phrase of its own: inside another defined term (certificate of merger),
|
|
167
|
+
* narrowed by the word before it (the common parent), or heading a kind of thing (certificates of deposit).
|
|
168
|
+
*/
|
|
169
|
+
const isOrdinaryPhraseAt = (doc: ProseDocument, offset: number, term: string, defined: readonly string[]): boolean => {
|
|
170
|
+
if (insideLongerTerm(doc.source, offset, term, defined)) return true;
|
|
171
|
+
const { tokens, at } = tokensAt(doc, offset);
|
|
172
|
+
if (at === -1) return false;
|
|
173
|
+
const last = tokens.findLastIndex((token) => token.span.start < offset + term.length);
|
|
174
|
+
return isNarrowedUse(tokens, at, patternsOf(doc, "defined-term-pointer")) || headsKindPhrase(tokens, last);
|
|
175
|
+
};
|
|
156
176
|
|
|
157
177
|
/** The term written longer with a word of its definition (文書管理責任者, the Application), after the sentence that defines it. */
|
|
158
178
|
const extendedFindings = (doc: ProseDocument, texts: readonly BodyText[], term: DefinedTerm, spans: readonly Span[], defined: readonly string[]): Finding[] => {
|
|
159
179
|
const definition = definingSentence(doc, term.span)?.text ?? "";
|
|
160
|
-
const pointers = (doc
|
|
180
|
+
const pointers = patternsOf(doc, "defined-term-pointer");
|
|
161
181
|
// Every sentence that defines the term (a second definition too) holds the long words it is defined from.
|
|
162
182
|
const defining = spans.map((span) => definingSentence(doc, span)?.span ?? span);
|
|
163
183
|
const definingEnd = definingSentenceEnd(doc, term.span);
|
|
@@ -182,11 +202,11 @@ export const definedTermForm: Detector = (doc, options): Finding[] => {
|
|
|
182
202
|
const uses = usesOf(term.term, texts, spans, "loose");
|
|
183
203
|
const definingEnd = definingSentenceEnd(doc, term.span);
|
|
184
204
|
const quoted = quotedFindings(doc, term, uses, definingEnd);
|
|
185
|
-
const caseUses = withLoweredWords(doc, term.term, texts, spans, uses);
|
|
205
|
+
const caseUses = withLoweredWords(doc, term.term, texts, spans, uses).filter((offset) => !inEntryOfTerm(doc.sentences, term.term, offset));
|
|
186
206
|
const nounUses = caseUses.filter((offset) => !isVerbAt(doc, offset));
|
|
187
|
-
const lower = lowerCaseUses(doc.source, term.term, nounUses, definingEnd, options.limit)
|
|
188
|
-
|
|
189
|
-
|
|
207
|
+
const lower = lowerCaseUses(doc.source, term.term, nounUses, definingEnd, options.limit)
|
|
208
|
+
.filter((offset) => !isOrdinaryPhraseAt(doc, offset, term.term, defined))
|
|
209
|
+
.map((offset) => finding(doc, "defined-term-form", offset, "case", { term: term.term, line: term.line }));
|
|
190
210
|
return [...quoted, ...lower, ...prefixFindings(doc, texts, term, definingEnd, defined), ...extendedFindings(doc, texts, term, spans, defined)];
|
|
191
211
|
});
|
|
192
212
|
};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { Detector, Finding, ProseDocument } from "../plugin.ts";
|
|
2
2
|
import { definedTerms, unusedDefinitions, usesBeforeDefinition, type BodyText, type IsTitle, type Mentioned } from "../structure/definition-use.ts";
|
|
3
3
|
import { titleLineNumbers } from "../structure/title-lines.ts";
|
|
4
|
+
import { contentsLineNumbers } from "../structure/contents-lines.ts";
|
|
4
5
|
import { expansionConflicts, expansionsIn } from "../acronym-expansions.ts";
|
|
5
6
|
import { linesOf, lineNumberAt } from "../structure/lines.ts";
|
|
6
7
|
import { isQuotedAlone } from "../quoted-span.ts";
|
|
@@ -34,10 +35,13 @@ export const unusedDefinition: Detector = (doc): Finding[] =>
|
|
|
34
35
|
findingAt(doc, defined.span.start, { term: defined.term }),
|
|
35
36
|
);
|
|
36
37
|
|
|
37
|
-
|
|
38
|
+
const patternsOf = (doc: ProseDocument, lexicon: string): string[] => (doc.lexicons[lexicon] ?? []).map((entry) => entry.pattern);
|
|
39
|
+
|
|
40
|
+
/** 題・見出し(Markdown の見出し、章・節の行、条の前の見出しの行)と、目次・索引の行の上の位置か。 */
|
|
38
41
|
const titleIn = (doc: ProseDocument): IsTitle => {
|
|
39
42
|
const lines = linesOf(doc.source);
|
|
40
|
-
const
|
|
43
|
+
const headings = doc.markup?.headings ?? [];
|
|
44
|
+
const numbers = new Set([...titleLineNumbers(doc.structure, lines, headings), ...contentsLineNumbers(lines, headings, patternsOf(doc, "toc-heading"))]);
|
|
41
45
|
return (offset) => numbers.has(lineNumberAt(lines, offset) ?? 0);
|
|
42
46
|
};
|
|
43
47
|
|
|
@@ -45,7 +49,7 @@ const titleIn = (doc: ProseDocument): IsTitle => {
|
|
|
45
49
|
export const useBeforeDefinition: Detector = (doc): Finding[] =>
|
|
46
50
|
doc.structure === undefined
|
|
47
51
|
? []
|
|
48
|
-
: usesBeforeDefinition(definedTerms(doc.structure), bodyOf(doc), mentionedIn(doc), titleIn(doc)).map((use) =>
|
|
52
|
+
: usesBeforeDefinition(definedTerms(doc.structure), bodyOf(doc), mentionedIn(doc), titleIn(doc), patternsOf(doc, "self-prefix")).map((use) =>
|
|
49
53
|
findingAt(doc, use.offset, { term: use.term.term, defined: use.term.line }),
|
|
50
54
|
);
|
|
51
55
|
|
|
@@ -4,6 +4,8 @@ import { documentDateOf } from "./document-date.ts";
|
|
|
4
4
|
import { overlapsAny, spanIndex } from "../compare/spans.ts";
|
|
5
5
|
import { yearOf, type DurationUnit } from "../derived/date-arithmetic.ts";
|
|
6
6
|
import { durationMismatches, type DatedValue, type Duration } from "../derived/durations.ts";
|
|
7
|
+
import { isCountPerPeriod, type PeriodWord, type PerPeriodWords } from "../derived/count-per-period.ts";
|
|
8
|
+
import { occurrences } from "../structure/stated-multiple.ts";
|
|
7
9
|
import { elapsedMismatches, type Elapsed, type OriginWord, type Year } from "../derived/elapsed.ts";
|
|
8
10
|
import { numberWordCounts } from "../derived/number-word-counts.ts";
|
|
9
11
|
import { hyphenatedCounts, withoutArticle } from "../derived/hyphenated-counts.ts";
|
|
@@ -91,12 +93,25 @@ const isApproximate = (doc: ProseDocument, span: Attributed): boolean =>
|
|
|
91
93
|
positioned(doc, "approximate-marker", "before").some((word) => textBefore(doc, span).endsWith(word)) ||
|
|
92
94
|
positioned(doc, "approximate-marker", "after").some((word) => textAfter(doc, span).startsWith(word));
|
|
93
95
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
96
|
+
const isDurationUnit = (group: string | undefined): group is DurationUnit => DURATION_LEXICONS.some(([, unit]) => unit === group);
|
|
97
|
+
|
|
98
|
+
const perPeriodWordsOf = (doc: ProseDocument): PerPeriodWords => ({
|
|
99
|
+
periods: (doc.lexicons["duration-per-period"] ?? []).flatMap((entry): PeriodWord[] =>
|
|
100
|
+
isDurationUnit(entry.group) ? [{ word: entry.pattern.toLowerCase(), position: entry.position === "after" ? "after" : "before", unit: entry.group }] : [],
|
|
101
|
+
),
|
|
102
|
+
schedules: spanIndex(occurrences(doc.source, patternsOf(doc, "weekly-days-off"))),
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
/** 木の数量(3 months)と、名詞の前に書いた期間(a 3-month trial)。期間あたりの数(週3日)は期間ではない。 */
|
|
106
|
+
const durationsOf = (doc: ProseDocument, quantities: readonly Quantity[], text: string): Duration[] => {
|
|
107
|
+
const perPeriod = perPeriodWordsOf(doc);
|
|
108
|
+
return [...quantities, ...hyphenatedCounts(text, patternsOf(doc, ...DURATION_LEXICONS.map(([id]) => id)))].flatMap((quantity) => {
|
|
97
109
|
const unit = DURATION_LEXICONS.find(([id]) => patternsOf(doc, id).some((pattern) => pattern.normalize("NFKC") === quantity.unit))?.[1];
|
|
98
|
-
|
|
110
|
+
if (unit === undefined || isApproximate(doc, quantity)) return [];
|
|
111
|
+
const duration = { ...quantity, unit };
|
|
112
|
+
return isCountPerPeriod(duration, textBefore(doc, quantity), textAfter(doc, quantity), perPeriod) ? [] : [duration];
|
|
99
113
|
});
|
|
114
|
+
};
|
|
100
115
|
|
|
101
116
|
const sentencesOf = (doc: ProseDocument): Span[] => doc.sentences.map((sentence) => sentence.span);
|
|
102
117
|
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { Token } from "../plugin.ts";
|
|
2
|
+
|
|
3
|
+
// 同じ助詞のあいだに括弧の注だけがある(「本利用規約は(以下、本規約)は」)。注を書き足したときに、前の助詞を消し忘れた跡。
|
|
4
|
+
// 「AはBは」の対比は、あいだに語があるので当たらない。
|
|
5
|
+
|
|
6
|
+
const MAX_ASIDE = 80;
|
|
7
|
+
/** 注の前後に置ける空白(行の中だけ)。 */
|
|
8
|
+
const MAX_SPACES = 2;
|
|
9
|
+
const SPACES = `[ \\t\\u3000]{0,${String(MAX_SPACES)}}`;
|
|
10
|
+
/** 助詞の後ろの、括弧ひとつの注(入れ子でない)とその前後の空白。 */
|
|
11
|
+
const ASIDE = new RegExp(`^${SPACES}[((][^(())\\n]{1,${String(MAX_ASIDE)}}[))]${SPACES}`, "u");
|
|
12
|
+
/** 括弧と注と前後の空白が収まる長さ。これより先は読まない。 */
|
|
13
|
+
const LOOK_AHEAD = MAX_ASIDE + 2 + MAX_SPACES * 2;
|
|
14
|
+
|
|
15
|
+
/** 「別表の(1)の欄」の「の」は、括弧の番号を挟んで名前を続ける書き方なので数えない。 */
|
|
16
|
+
const JOINS_NAMES = "の";
|
|
17
|
+
|
|
18
|
+
const isParticle = (token: Token): boolean => token.pos === "ADP" && token.surface !== JOINS_NAMES;
|
|
19
|
+
|
|
20
|
+
/** 二つの同じ助詞と、そのあいだの括弧の注。 */
|
|
21
|
+
export type AsidePair = { readonly first: Token; readonly second: Token };
|
|
22
|
+
|
|
23
|
+
/** first の後ろが括弧の注だけで、その直後に同じ助詞があれば、その助詞。source は文書全体で、token の span もその座標。 */
|
|
24
|
+
const particleAfterAside = (source: string, tokens: readonly Token[], first: Token): Token | undefined => {
|
|
25
|
+
const aside = ASIDE.exec(source.slice(first.span.end, first.span.end + LOOK_AHEAD))?.[0];
|
|
26
|
+
if (aside === undefined) return undefined;
|
|
27
|
+
const second = tokens.find((token) => token.span.start === first.span.end + aside.length);
|
|
28
|
+
return second !== undefined && isParticle(second) && second.surface === first.surface ? second : undefined;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
/** 文の語のうち、括弧の注を挟んで同じ助詞が二度ある所。Pure. */
|
|
32
|
+
export const doubledAcrossAside = (source: string, tokens: readonly Token[]): AsidePair[] =>
|
|
33
|
+
tokens.flatMap((first) => {
|
|
34
|
+
const second = isParticle(first) ? particleAfterAside(source, tokens, first) : undefined;
|
|
35
|
+
return second === undefined ? [] : [{ first, second }];
|
|
36
|
+
});
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import type { Detector, Finding } from "../plugin.ts";
|
|
1
|
+
import type { Detector, Finding, Span } from "../plugin.ts";
|
|
2
|
+
import { latinCompanyFormRegExp, latinCompanyFormSpans } from "../latin-company-form.ts";
|
|
3
|
+
import { firstEndingAfter } from "../soft-break.ts";
|
|
2
4
|
import { quoteAround } from "./quote-around.ts";
|
|
3
5
|
|
|
4
6
|
/**
|
|
@@ -32,16 +34,35 @@ const withoutWordDot = (text: string, found: DoubledMarks): DoubledMarks =>
|
|
|
32
34
|
? { start: found.start, run: found.run.slice(0, -1) }
|
|
33
35
|
: found;
|
|
34
36
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
+
/**
|
|
38
|
+
* 並びのうち、英字の会社の形(CO.,LTD.、INC.)の中の印の数。形の点や「,」は名前の一部で、並びの印ではない。
|
|
39
|
+
* names は昇順で重ならない(正規表現の順の一致)。並びに掛かりうるのは、並びの終わる前に始まる高々 run.length 個の形。
|
|
40
|
+
*/
|
|
41
|
+
const marksInNames = ({ start, run }: DoubledMarks, names: readonly Span[]): number => {
|
|
42
|
+
const first = firstEndingAfter(names, start);
|
|
43
|
+
const near = names.slice(first, first + run.length);
|
|
44
|
+
return [...run].filter((_, at) => near.some((name) => start + at >= name.start && start + at < name.end)).length;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const isSlip = (text: string, found: DoubledMarks, names: readonly Span[]): boolean => {
|
|
48
|
+
const { start, run } = found;
|
|
49
|
+
return (
|
|
50
|
+
run.length - marksInNames(found, names) >= 2 &&
|
|
51
|
+
!isDrawnOut(run) &&
|
|
52
|
+
!isDrawnOutThenClosed(run) &&
|
|
53
|
+
!AFTER_ABBREVIATION.test(run) &&
|
|
54
|
+
!isJoinedDots(text, start, run)
|
|
55
|
+
);
|
|
56
|
+
};
|
|
37
57
|
|
|
38
|
-
/** text の中の、句読点が重なった並び(位置と並び)。 */
|
|
39
|
-
export const doubledMarks = (text: string): DoubledMarks[] =>
|
|
40
|
-
[...text.matchAll(PUNCTUATION_RUN)].map((match) => withoutWordDot(text, { start: match.index, run: match[0] })).filter((found) => isSlip(text, found));
|
|
58
|
+
/** text の中の、句読点が重なった並び(位置と並び)。names は数えない名前(英字の会社の形)の範囲。 */
|
|
59
|
+
export const doubledMarks = (text: string, names: readonly Span[] = []): DoubledMarks[] =>
|
|
60
|
+
[...text.matchAll(PUNCTUATION_RUN)].map((match) => withoutWordDot(text, { start: match.index, run: match[0] })).filter((found) => isSlip(text, found, names));
|
|
41
61
|
|
|
42
62
|
export const doubledPunctuation: Detector = (doc): Finding[] => {
|
|
43
63
|
const text = doc.prose ?? doc.source;
|
|
44
|
-
|
|
64
|
+
const forms = latinCompanyFormRegExp((doc.lexicons["company-form-latin"] ?? []).map((entry) => entry.pattern));
|
|
65
|
+
return doubledMarks(text, latinCompanyFormSpans(text, forms)).map(({ start, run }) => ({
|
|
45
66
|
rule: "",
|
|
46
67
|
severity: "warning",
|
|
47
68
|
line: 0,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { Detector, Finding, Lexicon, Sentence, Token } from "../plugin.ts";
|
|
2
2
|
import { citedNamesOf, endsCitedName } from "./cited-name.ts";
|
|
3
|
+
import { doubledAcrossAside } from "./doubled-across-aside.ts";
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* 語を書き損じて二度書いた(the the / 資料をを)。消し忘れと、書き換えの途中で残った語(our the platform)の二つ。
|
|
@@ -159,7 +160,7 @@ export const doubledWord: Detector = (doc, options): Finding[] => {
|
|
|
159
160
|
const kinds = new Set((doc.lexicons[DOCUMENT_KIND] ?? []).map((entry) => entry.pattern));
|
|
160
161
|
const phrases = doc.lexicons[FIXED_PHRASE] ?? [];
|
|
161
162
|
return doc.sentences.flatMap((sentence) =>
|
|
162
|
-
doubledIn(doc.source, sentence.tokens ?? [], spaced, allowed, phrases)
|
|
163
|
+
[...doubledIn(doc.source, sentence.tokens ?? [], spaced, allowed, phrases), ...(spaced ? [] : doubledAcrossAside(doc.source, sentence.tokens ?? []))]
|
|
163
164
|
.filter((doubled) => !endsCitedName(doubled.first, doubled.second, cited, kinds))
|
|
164
165
|
.map((doubled) => findingOf(sentence, doubled, spaced)),
|
|
165
166
|
);
|
|
@@ -35,6 +35,19 @@ const wordAround = (text: string, start: number, end: number): string => {
|
|
|
35
35
|
return text.slice(from, after === -1 ? text.length : end + after);
|
|
36
36
|
};
|
|
37
37
|
|
|
38
|
+
/**
|
|
39
|
+
* A leader pads a label out to a column: the label opens a line or follows a sentence end, holds no space or punctuation,
|
|
40
|
+
* and two spaces or more follow the dots (化学品事業…… then two full-width spaces, then the description). One such place
|
|
41
|
+
* may be an ellipsis followed by spaces; MIN_LEADERS of them are a list of labels.
|
|
42
|
+
*/
|
|
43
|
+
const LEADER_LABEL = /(?:^|[\n。.!?!?])[^\S\n]*[^\s\p{P}]{1,20}$/u;
|
|
44
|
+
const LEADER_PADDING = /^[^\S\n]{2}/u;
|
|
45
|
+
const LABEL_REACH = 24;
|
|
46
|
+
const MIN_LEADERS = 2;
|
|
47
|
+
|
|
48
|
+
const isLeaderShaped = (text: string, start: number, end: number): boolean =>
|
|
49
|
+
LEADER_PADDING.test(text.slice(end)) && LEADER_LABEL.test(text.slice(Math.max(0, start - LABEL_REACH), start));
|
|
50
|
+
|
|
38
51
|
/** Dots that are not an ellipsis in running text: a range, a path or address, a table of contents leader. */
|
|
39
52
|
const isNotProse = (text: string, start: number, end: number): boolean =>
|
|
40
53
|
NOT_PROSE_BESIDE.test(text.charAt(start - 1)) ||
|
|
@@ -50,9 +63,11 @@ export const ellipsesIn = (text: string, forms: readonly string[]): WrittenEllip
|
|
|
50
63
|
.map(escapeRegExp)
|
|
51
64
|
.join("|");
|
|
52
65
|
const pattern = new RegExp(`(?<!${ELLIPSIS_PART})(?:${alternatives})(?!${ELLIPSIS_PART})`, "gu");
|
|
53
|
-
|
|
66
|
+
const found = [...text.matchAll(pattern)].flatMap((match) =>
|
|
54
67
|
isNotProse(text, match.index, match.index + match[0].length) ? [] : [{ offset: match.index, form: match[0] }],
|
|
55
68
|
);
|
|
69
|
+
const leaders = found.filter((ellipsis) => isLeaderShaped(text, ellipsis.offset, ellipsis.offset + ellipsis.form.length));
|
|
70
|
+
return leaders.length < MIN_LEADERS ? found : found.filter((ellipsis) => !leaders.includes(ellipsis));
|
|
56
71
|
};
|
|
57
72
|
|
|
58
73
|
export const ellipsisConsistency: Detector = (doc, options): Finding[] => {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// A work email or letter's own parts: a greeting and a closing, a subject line, and an attachment the text mentions.
|
|
2
2
|
// Pure. The words (お世話になっております, Dear, Best regards, 件名, attached) come from the language's email-greeting,
|
|
3
|
-
// email-closing, email-sentence-end, email-subject-field, attachment-mention, attachment-not-mention and attachment-line lexicons. The
|
|
3
|
+
// email-closing, email-sentence-end, memo-header-label, email-subject-field, attachment-mention, attachment-not-mention and attachment-line lexicons. The
|
|
4
4
|
// header and the signature are masked in doc.prose; a quoted reply is someone else's words and is left out.
|
|
5
5
|
import { escapeRegExp } from "../orthography.ts";
|
|
6
6
|
import { quoteAt } from "./structure-tree.ts";
|
|
@@ -141,13 +141,49 @@ export const frameSlips = (
|
|
|
141
141
|
];
|
|
142
142
|
};
|
|
143
143
|
|
|
144
|
+
/** The marks around a label line: a heading's #, emphasis, 【】. */
|
|
145
|
+
const LINE_MARKS = new Set("#*_【】 \t\u3000");
|
|
146
|
+
|
|
147
|
+
const withoutMarks = (line: string): string => {
|
|
148
|
+
const chars = [...line];
|
|
149
|
+
const first = chars.findIndex((char) => !LINE_MARKS.has(char));
|
|
150
|
+
return first === -1 ? "" : chars.slice(first, chars.findLastIndex((char) => !LINE_MARKS.has(char)) + 1).join("");
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
const LABEL_END = /^(?:[::]|$)/u;
|
|
154
|
+
|
|
155
|
+
/** An inline HTML tag around a label (<h3> FROM: <br/>, <b>TO</b>:), dropped so the colon still follows the label. */
|
|
156
|
+
const INLINE_HTML_TAG = /<\/?[A-Za-z][A-Za-z0-9-]*(?:\s[^<>]*)?\/?>/gu;
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Whether a line is a memo's header label (TO:, SUBJECT:, MEMORANDUM, 発信者:): a label as the lexicon writes it, case
|
|
160
|
+
* included, then a colon or the line's end. A memo sets its labels in capitals; a mail program writes "To:" and "Subject:".
|
|
161
|
+
*/
|
|
162
|
+
export const isMemoLabel = (line: string, labels: readonly string[]): boolean => {
|
|
163
|
+
const bare = withoutMarks(line.replace(INLINE_HTML_TAG, ""));
|
|
164
|
+
return labels.some((label) => bare.startsWith(label) && LABEL_END.test(bare.slice(label.length)));
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* A memo: one of its header labels among the source's lines before the first sentence (the first lines, when no line ends
|
|
169
|
+
* one). The source, not the body, since a block of TO: / FROM: lines is masked as an email's header. A memo takes no
|
|
170
|
+
* greeting and no closing.
|
|
171
|
+
*/
|
|
172
|
+
export const isMemo = (source: string, labels: readonly string[], sentenceEnds: readonly string[]): boolean => {
|
|
173
|
+
const lines = source.split("\n").filter((line) => line.trim() !== "" && !line.trimStart().startsWith(">"));
|
|
174
|
+
const firstSentence = lines.findIndex((line) => endsSentence(line, sentenceEnds));
|
|
175
|
+
return lines.slice(0, firstSentence === -1 ? HEADER_LINES : firstSentence).some((line) => isMemoLabel(line, labels));
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
const frameSlipsOf = (doc: ProseDocument): FrameSlip[] => {
|
|
179
|
+
const sentenceEnds = patternsOf(doc, "email-sentence-end");
|
|
180
|
+
if (isMemo(doc.source, patternsOf(doc, "memo-header-label"), sentenceEnds)) return [];
|
|
181
|
+
const lines = bodyLines(doc.source, doc.prose ?? doc.source, doc.replyQuotes ?? []);
|
|
182
|
+
return frameSlips(lines, patternsOf(doc, "email-greeting"), patternsOf(doc, "email-closing"), sentenceEnds);
|
|
183
|
+
};
|
|
184
|
+
|
|
144
185
|
export const emailFrame: Detector = (doc): Finding[] =>
|
|
145
|
-
|
|
146
|
-
bodyLines(doc.source, doc.prose ?? doc.source, doc.replyQuotes ?? []),
|
|
147
|
-
patternsOf(doc, "email-greeting"),
|
|
148
|
-
patternsOf(doc, "email-closing"),
|
|
149
|
-
patternsOf(doc, "email-sentence-end"),
|
|
150
|
-
).map((slip) => ({
|
|
186
|
+
frameSlipsOf(doc).map((slip) => ({
|
|
151
187
|
rule: "email-greeting-closing",
|
|
152
188
|
severity: "info",
|
|
153
189
|
line: 0,
|
|
@@ -180,14 +216,53 @@ export const fieldOf = (line: string): { readonly name: string; readonly value:
|
|
|
180
216
|
};
|
|
181
217
|
};
|
|
182
218
|
|
|
219
|
+
/** The subject field's names and reply marks, and what tells the body's first line from a subject: a greeting, a sentence's end. */
|
|
220
|
+
export type SubjectVocabulary = {
|
|
221
|
+
readonly fields: readonly string[];
|
|
222
|
+
readonly replyMarks: readonly string[];
|
|
223
|
+
readonly headerFields: readonly string[];
|
|
224
|
+
readonly greetings: readonly string[];
|
|
225
|
+
readonly sentenceEnds: readonly string[];
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
/** The text of the first line with text after `index`, trimmed. Scans only the blank lines between, so many empty subjects stay linear. */
|
|
229
|
+
const nextText = (lines: readonly SourceLine[], index: number): string => {
|
|
230
|
+
for (let at = index + 1; at < lines.length; at += 1) {
|
|
231
|
+
const text = lines[at]?.text.trim() ?? "";
|
|
232
|
+
if (text !== "") return text;
|
|
233
|
+
}
|
|
234
|
+
return "";
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
/** A header field's line (From: Ann). Not any colon: a subject may hold one (Re: Lunch, 10:00 meeting, Q3: budget). */
|
|
238
|
+
const isHeaderField = (line: string, headerFields: readonly string[]): boolean => {
|
|
239
|
+
const name = fieldOf(line)?.name.toLowerCase();
|
|
240
|
+
return name !== undefined && headerFields.some((field) => field.toLowerCase() === name);
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* The value under a subject label with nothing after its colon: the next line with text, as a form that sets the label and
|
|
245
|
+
* the value in two columns reads once it is text ("SUBJECT:" over "Car park repairs"). A quoted line, a header field, a
|
|
246
|
+
* greeting or a sentence there is the body, and the subject stays empty.
|
|
247
|
+
*/
|
|
248
|
+
export const valueBelow = (lines: readonly SourceLine[], index: number, vocabulary: SubjectVocabulary): string => {
|
|
249
|
+
const text = nextText(lines, index);
|
|
250
|
+
const isBody =
|
|
251
|
+
text.startsWith(">") || isHeaderField(text, vocabulary.headerFields) || lineHas(text, vocabulary.greetings) || endsSentence(text, vocabulary.sentenceEnds);
|
|
252
|
+
return isBody ? "" : text;
|
|
253
|
+
};
|
|
254
|
+
|
|
183
255
|
/** The subject lines (Subject: …, 件名:…), outside a quoted reply, with reply and forward marks (Re:, Fwd:) taken off the value. */
|
|
184
|
-
export const subjectLines = (source: string,
|
|
185
|
-
const names = new Set(fields.map((field) => field.toLowerCase()));
|
|
256
|
+
export const subjectLines = (source: string, vocabulary: SubjectVocabulary, replyQuotes: readonly Span[]): SubjectLine[] => {
|
|
257
|
+
const names = new Set(vocabulary.fields.map((field) => field.toLowerCase()));
|
|
186
258
|
const quoted = insideCursor(replyQuotes);
|
|
187
|
-
|
|
259
|
+
const lines = linesWithOffsets(source);
|
|
260
|
+
return lines.flatMap(({ start, text }, index): SubjectLine[] => {
|
|
188
261
|
const field = fieldOf(text);
|
|
189
262
|
const inQuote = quoted(start);
|
|
190
|
-
|
|
263
|
+
if (field === undefined || inQuote || !names.has(field.name.toLowerCase())) return [];
|
|
264
|
+
const value = field.value === "" ? valueBelow(lines, index, vocabulary) : field.value;
|
|
265
|
+
return [{ start, subject: withoutReplyMarks(value, vocabulary.replyMarks) }];
|
|
191
266
|
});
|
|
192
267
|
};
|
|
193
268
|
|
|
@@ -201,7 +276,17 @@ export const withoutReplyMarks = (subject: string, marks: readonly string[]): st
|
|
|
201
276
|
};
|
|
202
277
|
|
|
203
278
|
export const emailSubject: Detector = (doc, options): Finding[] =>
|
|
204
|
-
subjectLines(
|
|
279
|
+
subjectLines(
|
|
280
|
+
doc.source,
|
|
281
|
+
{
|
|
282
|
+
fields: patternsOf(doc, "email-subject-field"),
|
|
283
|
+
replyMarks: patternsOf(doc, "email-reply-mark"),
|
|
284
|
+
headerFields: patternsOf(doc, "email-header-field"),
|
|
285
|
+
greetings: patternsOf(doc, "email-greeting"),
|
|
286
|
+
sentenceEnds: patternsOf(doc, "email-sentence-end"),
|
|
287
|
+
},
|
|
288
|
+
doc.replyQuotes ?? [],
|
|
289
|
+
).flatMap((line): Finding[] => {
|
|
205
290
|
const length = [...line.subject].length;
|
|
206
291
|
if (length > 0 && length <= options.limit) return [];
|
|
207
292
|
return [
|
|
@@ -228,15 +313,6 @@ export const firstMention = (lines: readonly BodyLine[], mentions: readonly stri
|
|
|
228
313
|
return line === undefined ? undefined : line.start + line.text.length - line.text.trimStart().length;
|
|
229
314
|
};
|
|
230
315
|
|
|
231
|
-
/** The marks around a label line: a heading's #, emphasis, 【】. */
|
|
232
|
-
const LINE_MARKS = new Set("#*_【】 \t\u3000");
|
|
233
|
-
|
|
234
|
-
const withoutMarks = (line: string): string => {
|
|
235
|
-
const chars = [...line];
|
|
236
|
-
const first = chars.findIndex((char) => !LINE_MARKS.has(char));
|
|
237
|
-
return first === -1 ? "" : chars.slice(first, chars.findLastIndex((char) => !LINE_MARKS.has(char)) + 1).join("");
|
|
238
|
-
};
|
|
239
|
-
|
|
240
316
|
/** A line that labels what is attached: the label and a colon (Attachment: quote.pdf), or the label alone (### Enclosures, 【添付資料】). */
|
|
241
317
|
const labelsAttachment = (line: string, labels: readonly string[]): boolean => {
|
|
242
318
|
const bare = withoutMarks(line).toLowerCase();
|