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
|
@@ -6,8 +6,12 @@ export type TitleWords = { readonly endings: readonly string[] };
|
|
|
6
6
|
|
|
7
7
|
/** 題を囲む括弧。鉤括弧と、見出しや分類の名前を囲む隅付き括弧。 */
|
|
8
8
|
const TITLE_MARKS = { "「": "」", "『": "』", "【": "】" };
|
|
9
|
-
/**
|
|
10
|
-
|
|
9
|
+
/**
|
|
10
|
+
* 名前のすぐ後ろ(同じ行の空白を挟んでもよい)に付く、その名前の補い(就職氷河期世代、<創業型>)、略称(以下「奨励金」という。)、
|
|
11
|
+
* 日付や番号、ファイルの種類と大きさ([PDFファイル/333KB])の括弧。角括弧は同じ行で閉じ、Markdown のリンク([文字](…))でないもの。
|
|
12
|
+
* 半角の < は HTML の札と見分けられないので読まない。
|
|
13
|
+
*/
|
|
14
|
+
const QUALIFIER = /^[ \t\u3000]*(?:[((<〈[]|\[[^\]\n]*\](?![([]))/u;
|
|
11
15
|
/** 閉じる語の前に要る字数。1 字なら、題ではなく普通の語(新法、本事業)。 */
|
|
12
16
|
const MIN_BODY_LENGTH = 2;
|
|
13
17
|
const BLANK = /^\s*$/u;
|
|
@@ -27,11 +31,12 @@ export const endsInTitle = (run: readonly Token[], words: TitleWords): boolean =
|
|
|
27
31
|
/**
|
|
28
32
|
* 文(text)の start から end までの連なりが、題の名前の置き方をしているか。
|
|
29
33
|
* 括弧の中身の終わりで閉じる(「令和8年度高度統計人材育成強化拠点形成事業」)、すぐ後ろに補いの括弧が付く
|
|
30
|
-
*
|
|
34
|
+
* (国家公務員中途採用者選考試験(就職氷河期世代)、持続化補助金<創業型>、登録申込書 [PDFファイル/333KB])、
|
|
35
|
+
* または文がその連なりだけ(要綱の題を並べた行)。
|
|
31
36
|
*/
|
|
32
37
|
export const isTitlePosition = (text: string, start: number, end: number): boolean => {
|
|
33
38
|
const closesBracket = quotedSpans(text, TITLE_MARKS).some((span) => span.end === end && span.start <= start);
|
|
34
|
-
const qualified =
|
|
39
|
+
const qualified = QUALIFIER.test(text.slice(end));
|
|
35
40
|
const alone = BLANK.test(text.slice(0, start)) && BLANK.test(text.slice(end));
|
|
36
41
|
return closesBracket || qualified || alone;
|
|
37
42
|
};
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { hasParticle, isClosed, lastContent } from "../sentence-shape.ts";
|
|
2
2
|
import { isWithinAny, quotedIn } from "../quoted-span.ts";
|
|
3
|
+
import { chronologyRows } from "../chronology-rows.ts";
|
|
4
|
+
import { externalLinkTitleSpans } from "../link-title.ts";
|
|
3
5
|
import type { Detector, Finding, Span, Token } from "../plugin.ts";
|
|
4
6
|
|
|
5
7
|
/**
|
|
6
8
|
* 文末が名詞で終わる(体言止め)。箇条書きでは普通だが、本文では文が途中で切れて読める。
|
|
7
|
-
* 実文書で測ったら、指摘の 9
|
|
9
|
+
* 実文書で測ったら、指摘の 9 割が箇条書きの項目だった。箇条書きは除く。年表の行(1950年4月 設立。)も名詞で止める書き方なので除く。
|
|
8
10
|
*/
|
|
9
11
|
const NOMINAL = new Set(["NOUN", "PROPN"]);
|
|
10
12
|
|
|
@@ -14,8 +16,9 @@ const isDependent = (token: Token): boolean => token.features?.["NounType"] ===
|
|
|
14
16
|
const inside = (span: Span, spans: readonly Span[]): boolean => spans.some((other) => span.start >= other.start && span.start < other.end);
|
|
15
17
|
|
|
16
18
|
export const nounEnding: Detector = (doc, options): Finding[] => {
|
|
19
|
+
const rows = chronologyRows(doc);
|
|
17
20
|
const ended = doc.sentences.filter((sentence) => {
|
|
18
|
-
if (inside(sentence.span, doc.listSpans) || !isClosed(sentence) || !hasParticle(sentence)) return false;
|
|
21
|
+
if (inside(sentence.span, doc.listSpans) || rows.has(sentence) || !isClosed(sentence) || !hasParticle(sentence)) return false;
|
|
19
22
|
const last = lastContent(sentence);
|
|
20
23
|
return last !== undefined && NOMINAL.has(last.pos) && !isDependent(last);
|
|
21
24
|
});
|
|
@@ -62,19 +65,25 @@ const BREAK: Run = { surface: "", tokens: [] };
|
|
|
62
65
|
*
|
|
63
66
|
* 鉤括弧で引いたもの(括弧ごと)は、名詞と同じく連なりの中身として飛ばす。引いた題名(「食品中のウイルスの制御のための…ガイドライン」)や
|
|
64
67
|
* 発言は書き手が言い換えられないので、中の助詞は数えない。題名も名前なので、外の連なり(弊社の「新製品」の販売の計画)は続けて数える。
|
|
68
|
+
* 外のページの題名を引いたリンクの文字(- [サービスの料金の計算の方法](https://…))も、引いた題名と同じに読む。
|
|
65
69
|
*/
|
|
66
|
-
const runsOf = (tokens: readonly Token[], nesting: readonly string[], quoted: readonly Span[]): Run[] =>
|
|
70
|
+
const runsOf = (tokens: readonly Token[], nesting: readonly string[], breaks: readonly string[], quoted: readonly Span[]): Run[] =>
|
|
67
71
|
tokens.reduce<Run[]>((acc, token) => {
|
|
68
72
|
if (isWithinAny(quoted, token.span)) return acc;
|
|
69
73
|
if (nesting.includes(token.surface)) return extend(acc, token);
|
|
70
|
-
if (token.pos === "PUNCT" || PARTICLE.has(token.pos)) acc.push(BREAK);
|
|
74
|
+
if (token.pos === "PUNCT" || PARTICLE.has(token.pos) || breaks.includes(token.surface)) acc.push(BREAK);
|
|
71
75
|
return acc;
|
|
72
76
|
}, []);
|
|
73
77
|
|
|
78
|
+
/** 句と句の間の記号(制限=タグの制限、A→B)。半角の = や : は名詞として読まれ、句読点のようには切れない。 */
|
|
79
|
+
const RUN_BREAK_LEXICON = "particle-run-break";
|
|
80
|
+
|
|
74
81
|
export const doubledParticle: Detector = (doc, options): Finding[] => {
|
|
75
82
|
const nesting = (options.lexicon ?? []).map((entry) => entry.pattern);
|
|
83
|
+
const titles = externalLinkTitleSpans(doc.source, doc.links);
|
|
84
|
+
const breaks = (doc.lexicons[RUN_BREAK_LEXICON] ?? []).map((entry) => entry.pattern);
|
|
76
85
|
return doc.sentences.flatMap((sentence) =>
|
|
77
|
-
runsOf(sentence.tokens ?? [], nesting, quotedIn(sentence))
|
|
86
|
+
runsOf(sentence.tokens ?? [], nesting, breaks, [...quotedIn(sentence), ...titles])
|
|
78
87
|
.filter((run) => run.tokens.length > options.limit)
|
|
79
88
|
.map((run) => ({
|
|
80
89
|
rule: "no-doubled-joshi",
|
|
@@ -8,8 +8,11 @@ import { escapeRegExp } from "../orthography.ts";
|
|
|
8
8
|
import type { Detector, Finding, Lexicon, LexiconEntry, Sentence } from "../plugin.ts";
|
|
9
9
|
import { comparableText, comparableWords, entryRanges } from "./lexicon-match.ts";
|
|
10
10
|
import { rateOver } from "./lexicon.ts";
|
|
11
|
+
import { specifiedByBracket } from "./bracket-specified.ts";
|
|
11
12
|
|
|
12
13
|
const TERMS_OF_ART = "vague-word-term-of-art";
|
|
14
|
+
/** The words that start an aside giving a name (以下「HR」という, hereinafter): such an aside pins nothing down. */
|
|
15
|
+
const NAME_MARKERS = "definition-marker";
|
|
13
16
|
|
|
14
17
|
type Place = { readonly start: number; readonly end: number };
|
|
15
18
|
|
|
@@ -36,13 +39,25 @@ const covers = (outer: Place, inner: Place): boolean => outer.start <= inner.sta
|
|
|
36
39
|
|
|
37
40
|
const isLonger = (outer: Match, inner: Match): boolean => outer.place.end - outer.place.start > inner.place.end - inner.place.start;
|
|
38
41
|
|
|
39
|
-
/**
|
|
40
|
-
|
|
42
|
+
/** Whether a bracket right after the match pins the word down; token places are in the document, text places in the comparable text. */
|
|
43
|
+
const pinnedDown = (sentence: Sentence, match: Match, byTokens: boolean, nameMarkers: readonly string[]): boolean =>
|
|
44
|
+
byTokens
|
|
45
|
+
? specifiedByBracket(sentence.text, match.place.end - sentence.span.start, nameMarkers)
|
|
46
|
+
: specifiedByBracket(comparableText(sentence), match.place.end, nameMarkers);
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* The vague words of one sentence, in the order written, each once: none inside a longer vague word or a term of art,
|
|
50
|
+
* and none pinned down by the bracket right after it (一定の事項(…)). nameMarkers: the words that start a name in an aside.
|
|
51
|
+
*/
|
|
52
|
+
export const vagueWordsIn = (sentence: Sentence, vague: Lexicon, termsOfArt: Lexicon, nameMarkers: readonly string[] = []): string[] => {
|
|
41
53
|
const byTokens = readsTokens(sentence, [...vague, ...termsOfArt]);
|
|
42
54
|
const matches = matchesOf(sentence, vague, byTokens);
|
|
43
55
|
const terms = matchesOf(sentence, termsOfArt, byTokens);
|
|
44
56
|
const kept = matches.filter(
|
|
45
|
-
(match) =>
|
|
57
|
+
(match) =>
|
|
58
|
+
!matches.some((other) => isLonger(other, match) && covers(other.place, match.place)) &&
|
|
59
|
+
!terms.some((term) => covers(term.place, match.place)) &&
|
|
60
|
+
!pinnedDown(sentence, match, byTokens, nameMarkers),
|
|
46
61
|
);
|
|
47
62
|
return [...new Set(kept.toSorted((left, right) => left.place.start - right.place.start).map((match) => match.pattern))];
|
|
48
63
|
};
|
|
@@ -51,8 +66,9 @@ type SentenceHits = { readonly sentence: Sentence; readonly words: readonly stri
|
|
|
51
66
|
|
|
52
67
|
export const vagueDensity: Detector = (doc, options): Finding[] => {
|
|
53
68
|
const termsOfArt = entriesForGenre(doc.lexicons[TERMS_OF_ART] ?? [], options.genre);
|
|
69
|
+
const nameMarkers = (doc.lexicons[NAME_MARKERS] ?? []).map((entry) => entry.pattern);
|
|
54
70
|
const hits: SentenceHits[] = doc.sentences
|
|
55
|
-
.map((sentence) => ({ sentence, words: vagueWordsIn(sentence, options.lexicon ?? [], termsOfArt) }))
|
|
71
|
+
.map((sentence) => ({ sentence, words: vagueWordsIn(sentence, options.lexicon ?? [], termsOfArt, nameMarkers) }))
|
|
56
72
|
.filter((hit) => hit.words.length > 0);
|
|
57
73
|
const count = hits.reduce((sum, hit) => sum + hit.words.length, 0);
|
|
58
74
|
const density = rateOver(doc, count, options.limit);
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { documentLineParagraphs } from "./line-paragraphs.ts";
|
|
2
|
+
import { listItemParagraphs } from "./list-item-paragraphs.ts";
|
|
3
|
+
import type { Paragraph, Sentence, Span } from "./plugin.ts";
|
|
4
|
+
|
|
5
|
+
/** sentences(並び順)の中で、start が offset 以上の最初の添字。1 行 1 段落の何万行でも、段落ごとに全部をなめない。 */
|
|
6
|
+
const firstStartingAt = (sentences: readonly Sentence[], offset: number): number => {
|
|
7
|
+
const search = (low: number, high: number): number => {
|
|
8
|
+
if (low >= high) return low;
|
|
9
|
+
const middle = (low + high) >> 1;
|
|
10
|
+
return (sentences[middle]?.span.start ?? Number.POSITIVE_INFINITY) >= offset ? search(low, middle) : search(middle + 1, high);
|
|
11
|
+
};
|
|
12
|
+
return search(0, sentences.length);
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const sentencesWithin = (sentences: readonly Sentence[], span: Span): Sentence[] =>
|
|
16
|
+
sentences.slice(firstStartingAt(sentences, span.start), firstStartingAt(sentences, span.end));
|
|
17
|
+
|
|
18
|
+
/** 文書の段落と、その中の文。spans は Markdown の段落、listSpans は箇条書きの項目と同じに読む範囲。 */
|
|
19
|
+
export const paragraphsOf = (prose: string, spans: readonly Span[], sentences: readonly Sentence[], listSpans: readonly Span[]): Paragraph[] => {
|
|
20
|
+
// 箇条書きの中の段落は「段落」として数えない。項目 1 つを 1 段落と読むと、
|
|
21
|
+
// 段落あたりの文数も長さのばらつきも、箇条書きの多い文書で壊れる。
|
|
22
|
+
const outsideLists = spans.filter((span) => !listSpans.some((list) => span.start >= list.start && span.start < list.end));
|
|
23
|
+
const withSentences = outsideLists.map((span) => ({ span, sentences: sentencesWithin(sentences, span).map((sentence) => sentence.span) }));
|
|
24
|
+
// 1 行 1 段落の書き方かは、行頭の印の項目で割る前の段落で決める。割った後の段落は、その手がかりにならない。
|
|
25
|
+
const items = documentLineParagraphs(prose, withSentences).flatMap((span) => listItemParagraphs(span, sentencesWithin(sentences, span)));
|
|
26
|
+
return items.map((span) => ({ span, sentences: sentencesWithin(sentences, span) }));
|
|
27
|
+
};
|
package/src/document.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { hasTitle } from "./heading-title.ts";
|
|
|
4
4
|
import { maskSpans } from "./mask.ts";
|
|
5
5
|
import { spansWithin, unmaskedSoftBreaks } from "./soft-break.ts";
|
|
6
6
|
import { segmentJoined } from "./joined-view.ts";
|
|
7
|
-
import {
|
|
7
|
+
import { paragraphsOf } from "./document-paragraphs.ts";
|
|
8
8
|
import { closingRun, standaloneLines, subheadingPieces, type StandsAlone } from "./subheading-line.ts";
|
|
9
9
|
import { isLinkLine } from "./link-line.ts";
|
|
10
10
|
import { speakerLabels } from "./speaker-labels.ts";
|
|
@@ -26,7 +26,7 @@ import { markdownFigures } from "./text-figures.ts";
|
|
|
26
26
|
import { documentMarkup } from "./markup.ts";
|
|
27
27
|
import { BARE_URL } from "./bare-url.ts";
|
|
28
28
|
import { lazyCells, tableCellsOf } from "./table-cells.ts";
|
|
29
|
-
import type { BulletList, LanguageAdapter, Markup,
|
|
29
|
+
import type { BulletList, LanguageAdapter, Markup, ProseDocument, Sentence, Span, StructureNode, DocumentProfile } from "./plugin.ts";
|
|
30
30
|
|
|
31
31
|
/**
|
|
32
32
|
* 本文として数えないもの。
|
|
@@ -234,30 +234,10 @@ const sentencesOf = (prose: string, paragraphs: readonly Span[], adapter: Langua
|
|
|
234
234
|
...(sentence.tokens === undefined ? {} : { tokens: sentence.tokens.map((token) => ({ ...token, span: shift(token.span, paragraph.start) })) }),
|
|
235
235
|
...(sentence.wrapBreaks === undefined ? {} : { wrapBreaks: sentence.wrapBreaks.map((span) => shift(span, paragraph.start)) }),
|
|
236
236
|
...(sentence.embeddedLanguage === undefined ? {} : { embeddedLanguage: sentence.embeddedLanguage }),
|
|
237
|
+
...(sentence.listItem === undefined ? {} : { listItem: sentence.listItem }),
|
|
237
238
|
})),
|
|
238
239
|
);
|
|
239
240
|
|
|
240
|
-
/** sentences(並び順)の中で、start が offset 以上の最初の添字。1 行 1 段落の何万行でも、段落ごとに全部をなめない。 */
|
|
241
|
-
const firstStartingAt = (sentences: readonly Sentence[], offset: number): number => {
|
|
242
|
-
const search = (low: number, high: number): number => {
|
|
243
|
-
if (low >= high) return low;
|
|
244
|
-
const middle = (low + high) >> 1;
|
|
245
|
-
return (sentences[middle]?.span.start ?? Number.POSITIVE_INFINITY) >= offset ? search(low, middle) : search(middle + 1, high);
|
|
246
|
-
};
|
|
247
|
-
return search(0, sentences.length);
|
|
248
|
-
};
|
|
249
|
-
|
|
250
|
-
const sentencesWithin = (sentences: readonly Sentence[], span: Span): Sentence[] =>
|
|
251
|
-
sentences.slice(firstStartingAt(sentences, span.start), firstStartingAt(sentences, span.end));
|
|
252
|
-
|
|
253
|
-
const paragraphsOf = (prose: string, spans: readonly Span[], sentences: readonly Sentence[], listSpans: readonly Span[]): Paragraph[] => {
|
|
254
|
-
// 箇条書きの中の段落は「段落」として数えない。項目 1 つを 1 段落と読むと、
|
|
255
|
-
// 段落あたりの文数も長さのばらつきも、箇条書きの多い文書で壊れる。
|
|
256
|
-
const outsideLists = spans.filter((span) => !listSpans.some((list) => span.start >= list.start && span.start < list.end));
|
|
257
|
-
const withSentences = outsideLists.map((span) => ({ span, sentences: sentencesWithin(sentences, span).map((sentence) => sentence.span) }));
|
|
258
|
-
return documentLineParagraphs(prose, withSentences).map((span) => ({ span, sentences: sentencesWithin(sentences, span) }));
|
|
259
|
-
};
|
|
260
|
-
|
|
261
241
|
/** 箇条書きは list ノードの直下の項目を数える。入れ子の項目は内側の list のものとして数える。目次のような案内だけの箇条書きは数えない。 */
|
|
262
242
|
const listsOf = (root: Node, source: string, anchors: InPageAnchors): BulletList[] => {
|
|
263
243
|
const found: BulletList[] = [];
|
package/src/figure-references.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { escapeRegExp } from "./orthography.ts";
|
|
2
2
|
import { lowerBound } from "./detectors/token-column.ts";
|
|
3
3
|
import type { DocumentNamer, Span } from "./plugin.ts";
|
|
4
|
+
import { isTitleLine, type TitleNotion } from "./title-line.ts";
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* 本文で番号を指した図・表・付録(図3、Table 2、Appendix B)と、それを行の頭に書いた所(キャプション、見出し)。
|
|
@@ -79,16 +80,20 @@ const HEADING = /^[ \t]{0,3}#/u;
|
|
|
79
80
|
*/
|
|
80
81
|
const CAPTION_AFTER = /^(?:[ \t]*$|[::..—–\-|()()【】\]*_]|[ \t\u3000]+[^\p{Script=Hiragana}\p{Ll}])/u;
|
|
81
82
|
|
|
83
|
+
/** A caption's title (Exhibit A to Merger Agreement): every long word capitalised, since Figure 3 shows the trend is a sentence. */
|
|
84
|
+
export const CAPTION_TITLE: TitleNotion = { sentenceEnd: /[.!?。.?!]$/u, capitalised: true, edge: "any-whitespace" };
|
|
85
|
+
|
|
82
86
|
/**
|
|
83
87
|
* 行の頭に書いた番号。前にあるのが飾り(#、**、![、|、【、箇条書きの印)だけで、見出しの中か、後ろが題の区切りならキャプション。
|
|
84
|
-
*
|
|
88
|
+
* 行の頭から始まる本文の文(図3の例では、…)は、図の置き場所ではない。題だけの行(Exhibit A to Merger Agreement)も見出し。
|
|
85
89
|
*/
|
|
86
90
|
const isLabelled = (source: string, mention: Mention): boolean => {
|
|
87
91
|
const lineStart = source.lastIndexOf("\n", mention.start - 1) + 1;
|
|
88
92
|
const lineEnd = source.indexOf("\n", mention.end);
|
|
89
93
|
const before = source.slice(lineStart, mention.start);
|
|
90
94
|
if (LETTER.test(outsideMarkup(before))) return false;
|
|
91
|
-
|
|
95
|
+
const rest = source.slice(mention.end, lineEnd === -1 ? source.length : lineEnd);
|
|
96
|
+
return HEADING.test(before) || CAPTION_AFTER.test(rest) || isTitleLine(source, lineStart, lineEnd, rest, CAPTION_TITLE);
|
|
92
97
|
};
|
|
93
98
|
|
|
94
99
|
const LATIN_WORD = /^[A-Za-z]/u;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// A code block that holds only a format template: named placeholders joined by separators (<MAJOR>.<MINOR>.<PATCH>,
|
|
2
|
+
// {year}-{month}). It shows the shape of a value, not code in any language, so it has no language to name. Pure.
|
|
3
|
+
|
|
4
|
+
const PLACEHOLDER = /<[A-Za-z][\w-]*>|\{[A-Za-z][\w-]*\}/gu;
|
|
5
|
+
const SEPARATORS = /^\s*[.,:;/_+\-|][\s.,:;/_+\-|]*$/u;
|
|
6
|
+
/** A lone tag (`<div>`) or two tags side by side are markup; a template joins two or more parts with a separator. */
|
|
7
|
+
const MIN_PARTS = 2;
|
|
8
|
+
|
|
9
|
+
const isTemplateLine = (line: string): boolean => (line.match(PLACEHOLDER) ?? []).length >= MIN_PARTS && SEPARATORS.test(line.replace(PLACEHOLDER, ""));
|
|
10
|
+
|
|
11
|
+
/** Whether every non-blank line is two or more placeholders joined by separators, with at least one such line. */
|
|
12
|
+
export const isFormatTemplate = (lines: readonly string[]): boolean => {
|
|
13
|
+
const filled = lines.filter((line) => line.trim() !== "");
|
|
14
|
+
return filled.length > 0 && filled.every(isTemplateLine);
|
|
15
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { Span } from "./plugin.ts";
|
|
2
|
+
|
|
3
|
+
// Round brackets that group an arithmetic formula (賃金 × (1 - 時間 ÷ 月平均)) rather than set off an aside in prose.
|
|
4
|
+
|
|
5
|
+
const OPENING = new Set(["(", "("]);
|
|
6
|
+
const CLOSING = new Set([")", ")"]);
|
|
7
|
+
/** Inside the brackets: a multiplication or division sign, or a plus, minus or equals sign with a space on each side. Unspaced, - and = are hyphens and labels (A-1, 2026-04). */
|
|
8
|
+
const OPERATOR_INSIDE = /[×÷]|[ \u3000][++\-−-==][ \u3000]/u;
|
|
9
|
+
/** Just before the opening bracket, past spaces: a multiplication or division sign, or a spaced sign. */
|
|
10
|
+
const OPERATOR_BEFORE = /(?:[×÷]|[ \u3000][++\-−-==])[ \u3000]*$/u;
|
|
11
|
+
/** Just after the closing bracket, past spaces. An asterisk is not a sign here: in prose it marks a note (*(注)). */
|
|
12
|
+
const OPERATOR_AFTER = /^[ \u3000]*(?:[×÷]|[++\-−-==][ \u3000])/u;
|
|
13
|
+
|
|
14
|
+
type Scan = { readonly open: number[]; readonly pairs: Span[] };
|
|
15
|
+
|
|
16
|
+
const step = (scan: Scan, char: string, at: number): Scan => {
|
|
17
|
+
if (OPENING.has(char)) scan.open.push(at);
|
|
18
|
+
const start = CLOSING.has(char) ? scan.open.pop() : undefined;
|
|
19
|
+
if (start !== undefined) scan.pairs.push({ start, end: at + 1 });
|
|
20
|
+
return scan;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const isFormula = (text: string, pair: Span): boolean =>
|
|
24
|
+
OPERATOR_INSIDE.test(text.slice(pair.start + 1, pair.end - 1)) ||
|
|
25
|
+
OPERATOR_BEFORE.test(text.slice(0, pair.start)) ||
|
|
26
|
+
OPERATOR_AFTER.test(text.slice(pair.end));
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* The bracket pairs in text that belong to a formula, each span covering both brackets. Full-width and half-width brackets
|
|
30
|
+
* pair with each other. A bracket left unclosed or closing nothing is not part of any pair. Positions count from 0 in text.
|
|
31
|
+
*/
|
|
32
|
+
export const formulaBracketSpans = (text: string): Span[] =>
|
|
33
|
+
text
|
|
34
|
+
.split("")
|
|
35
|
+
.reduce<Scan>(step, { open: [], pairs: [] })
|
|
36
|
+
.pairs.filter((pair) => isFormula(text, pair));
|
|
@@ -37,6 +37,7 @@ import { withoutFootnoteMarks } from "./html-footnote-marks.ts";
|
|
|
37
37
|
import { withoutHeadingSelfLinks } from "./html-heading-links.ts";
|
|
38
38
|
import { withLostParagraphTagsRestored } from "./html-lost-tags.ts";
|
|
39
39
|
import { withoutReaderOnlyText } from "./html-reader-only.ts";
|
|
40
|
+
import { withoutSiteChrome } from "./html-site-chrome.ts";
|
|
40
41
|
import { withRunInHeadingsRead } from "./html-run-in-headings.ts";
|
|
41
42
|
import { withoutWidgets } from "./html-widgets.ts";
|
|
42
43
|
import { withPreformattedRestored, withPreformattedStashed } from "./html-preformatted.ts";
|
|
@@ -465,7 +466,7 @@ export const htmlToMarkdown = (html: string): string => {
|
|
|
465
466
|
const uncommented = withLostParagraphTagsRestored(withAttributeMarkupEscaped(html).replace(/<!--[\s\S]*?-->|<\?[\s\S]*?\?>/gu, ""));
|
|
466
467
|
const preformatted = withPreformattedStashed(withTablesRead(DROPPED.reduce(withoutElement, withoutRubyText(uncommented))));
|
|
467
468
|
const kept = withoutFootnoteMarks(
|
|
468
|
-
withoutHeadingSelfLinks(withoutWidgets(withoutButtons(withoutReaderOnlyText(withoutHiddenElements(mainContent(preformatted.html)))))),
|
|
469
|
+
withoutHeadingSelfLinks(withoutWidgets(withoutButtons(withoutReaderOnlyText(withoutHiddenElements(mainContent(withoutSiteChrome(preformatted.html))))))),
|
|
469
470
|
).replace(MARKUP_SPACE, " ");
|
|
470
471
|
const content = withoutClosingLinks(
|
|
471
472
|
withoutLinkGroups(withoutNavigation(withoutIconMenus(withoutSiteHeader(withoutNavigationLandmarks(withoutClosingAddress(kept)))))),
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
// The site's own chrome around a page, read from what the markup says a block is rather than from
|
|
2
|
+
// any site's wording. A landmark role (banner, search, contentinfo), a <search> element, a <header> that belongs to the
|
|
3
|
+
// page rather than to a section, and a block named by a convention CMSs share: a breadcrumb trail (breadcrumb, pankuzu,
|
|
4
|
+
// topicpath), a search box, the header or footer block (id or class exactly "header" or "footer"), and the U.S.
|
|
5
|
+
// government's ".gov" banner (usa-banner). A block holding the page's <h1> is never chrome. Pure; a regular-expression
|
|
6
|
+
// reading, not a parser.
|
|
7
|
+
import { ATTRIBUTES, elementRanges, isInside, withoutElementsOpening, withoutRanges, type ElementRange } from "./html-elements.ts";
|
|
8
|
+
|
|
9
|
+
const CHROME_ROLES = new Set(["banner", "search", "contentinfo"]);
|
|
10
|
+
|
|
11
|
+
// A name segment, so that "pankuzu_wrap", "foot_pankuzu" and "cse-search-box" match and "research" does not.
|
|
12
|
+
const CHROME_NAME = /(?:^|[-_])(?:breadcrumbs?|pankuzu|topic-?path|search)(?:[-_\d]|$)/iu;
|
|
13
|
+
|
|
14
|
+
const REGION_NAME = /^(?:header|footer|usa-banner)$/iu;
|
|
15
|
+
|
|
16
|
+
const NAMED_OPENING = /<([a-z][a-z0-9-]*)\b[^>]*\s(?:class|id|role)\s*=/giu;
|
|
17
|
+
|
|
18
|
+
const attributeValue = (openTag: string, name: string): string => {
|
|
19
|
+
const value = new RegExp(String.raw`^<[a-z][a-z0-9-]*${ATTRIBUTES}\s+${name}\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s"'>]+))`, "iu").exec(openTag);
|
|
20
|
+
return value?.[1] ?? value?.[2] ?? value?.[3] ?? "";
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const tokens = (value: string): string[] => value.split(/\s+/u).filter((token) => token !== "");
|
|
24
|
+
|
|
25
|
+
const holdsTitle = (range: ElementRange): boolean => /<h1\b/iu.test(range.inner);
|
|
26
|
+
|
|
27
|
+
/** A role is a list of tokens and the first one counts, as for role="main". */
|
|
28
|
+
const hasChromeRole = (openTag: string): boolean => CHROME_ROLES.has((tokens(attributeValue(openTag, "role"))[0] ?? "").toLowerCase());
|
|
29
|
+
|
|
30
|
+
const hasChromeName = (openTag: string): boolean =>
|
|
31
|
+
[...tokens(attributeValue(openTag, "class")), ...tokens(attributeValue(openTag, "id"))].some((name) => CHROME_NAME.test(name) || REGION_NAME.test(name));
|
|
32
|
+
|
|
33
|
+
/** A block the markup names or marks as the site's chrome, unless it holds the page's title. */
|
|
34
|
+
export const isSiteChrome = (range: ElementRange): boolean => !holdsTitle(range) && (hasChromeRole(range.openTag) || hasChromeName(range.openTag));
|
|
35
|
+
|
|
36
|
+
const SECTIONING = ["article", "section", "main", "aside", "nav"];
|
|
37
|
+
|
|
38
|
+
/** A <header> inside an article or a section heads that part; one outside them is the page's banner. */
|
|
39
|
+
const withoutPageHeaders = (html: string): string => {
|
|
40
|
+
const sections = SECTIONING.flatMap((tag) => elementRanges(html, tag));
|
|
41
|
+
const banners = elementRanges(html, "header").filter((header) => !holdsTitle(header) && !sections.some((section) => isInside(section, header)));
|
|
42
|
+
return withoutRanges(html, banners);
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const withoutSearchElements = (html: string): string => withoutRanges(html, elementRanges(html, "search"));
|
|
46
|
+
|
|
47
|
+
export const withoutSiteChrome = (html: string): string => withoutPageHeaders(withoutSearchElements(withoutElementsOpening(html, NAMED_OPENING, isSiteChrome)));
|
package/src/joined-view.ts
CHANGED
|
@@ -62,6 +62,7 @@ const sentenceInSource =
|
|
|
62
62
|
text: text.slice(span.start, span.end),
|
|
63
63
|
...(sentence.tokens === undefined ? {} : { tokens: sentence.tokens.map(tokenInSource(view)) }),
|
|
64
64
|
...(sentence.embeddedLanguage === undefined ? {} : { embeddedLanguage: sentence.embeddedLanguage }),
|
|
65
|
+
...(sentence.listItem === undefined ? {} : { listItem: sentence.listItem }),
|
|
65
66
|
};
|
|
66
67
|
};
|
|
67
68
|
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
// A long kanji compound the document writes several times, and once or twice with one kanji changed (就職氷河期世代 ×5,
|
|
2
|
+
// 就職永河期世代 ×2). Pure: the compounds come in with the words the analyser cut them into; which kanji may differ by
|
|
3
|
+
// design (一, 第, 条) comes from a word list.
|
|
4
|
+
|
|
5
|
+
/** One word of the analyser inside a compound, by kanji index: [start, end). `known` is false for a word not in its dictionary. */
|
|
6
|
+
export type CompoundWord = {
|
|
7
|
+
readonly start: number;
|
|
8
|
+
readonly end: number;
|
|
9
|
+
readonly known: boolean;
|
|
10
|
+
/** The word reaches past the run (並びに after 受遺者); only its part inside is given. */
|
|
11
|
+
readonly crossesRun?: boolean;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export type CompoundUse = {
|
|
15
|
+
readonly compound: string;
|
|
16
|
+
readonly offset: number;
|
|
17
|
+
/** Absent where nothing was analysed. */
|
|
18
|
+
readonly words?: readonly CompoundWord[] | undefined;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
type CompoundSlip = {
|
|
22
|
+
readonly written: string;
|
|
23
|
+
readonly usual: string;
|
|
24
|
+
readonly usualCount: number;
|
|
25
|
+
readonly offset: number;
|
|
26
|
+
readonly index: number;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const MIN_COMPOUND_LENGTH = 4;
|
|
30
|
+
const MIN_USUAL_COUNT = 3;
|
|
31
|
+
/** A slip copied once (a link and its twin) is still a slip; the usual form must then be written more than twice as often. */
|
|
32
|
+
const MAX_SLIP_COUNT = 2;
|
|
33
|
+
|
|
34
|
+
/** The one position where two compounds of the same length differ, or undefined when they differ in none or several. */
|
|
35
|
+
export const singleDifference = (left: readonly string[], right: readonly string[]): number | undefined => {
|
|
36
|
+
if (left.length !== right.length) return undefined;
|
|
37
|
+
const differing = left.flatMap((char, index) => (char === right[index] ? [] : [index]));
|
|
38
|
+
return differing.length === 1 ? differing[0] : undefined;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
/** The first and last kanji of a run are where a suffix (等, 者) or a conjunction written in kanji (及, 又) joins, not where a word is mistyped. */
|
|
42
|
+
const isInside = (index: number, length: number): boolean => index > 0 && index < length - 1;
|
|
43
|
+
|
|
44
|
+
const wordAt = (use: CompoundUse, index: number): CompoundWord | undefined => use.words?.find((word) => word.start <= index && index < word.end);
|
|
45
|
+
|
|
46
|
+
const isLooseWithin = (word: CompoundWord | undefined, outer: CompoundWord): boolean =>
|
|
47
|
+
word !== undefined && word.crossesRun !== true && (!word.known || word.end - word.start === 1) && word.start >= outer.start && word.end <= outer.end;
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* The analyser cannot read the changed word: where the usual form holds the kanji inside one word (氷河期), the slip
|
|
51
|
+
* falls apart there into single kanji or words it does not know (永 / 河 / 期). Returns the slip's kanji over that word.
|
|
52
|
+
*/
|
|
53
|
+
export const fallenWord = (slip: CompoundUse, usualUses: readonly CompoundUse[], index: number): string | undefined => {
|
|
54
|
+
const usualWord = usualUses.map((use) => wordAt(use, index)).find((word) => word !== undefined && word.end - word.start > 1);
|
|
55
|
+
if (usualWord === undefined) return undefined;
|
|
56
|
+
const positions = Array.from({ length: usualWord.end - usualWord.start }, (_, offset) => usualWord.start + offset);
|
|
57
|
+
const apart = positions.every((position) => isLooseWithin(wordAt(slip, position), usualWord));
|
|
58
|
+
return apart ? Array.from(slip.compound).slice(usualWord.start, usualWord.end).join("") : undefined;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
/** The changed word written in another kanji run (質権 in 質権者は beside 当該質権者): a word the document knows. */
|
|
62
|
+
const isWrittenElsewhere = (word: string, slip: string, uses: readonly CompoundUse[]): boolean =>
|
|
63
|
+
uses.some((use) => use.compound !== slip && use.compound.includes(word));
|
|
64
|
+
|
|
65
|
+
type SlipContext = { readonly ignored: ReadonlySet<string>; readonly uses: readonly CompoundUse[] };
|
|
66
|
+
|
|
67
|
+
type Usual = { readonly compound: string; readonly count: number; readonly uses: readonly CompoundUse[] };
|
|
68
|
+
|
|
69
|
+
const countsOf = (uses: readonly CompoundUse[]): ReadonlyMap<string, number> =>
|
|
70
|
+
uses.reduce((counts, use) => counts.set(use.compound, (counts.get(use.compound) ?? 0) + 1), new Map<string, number>());
|
|
71
|
+
|
|
72
|
+
const usualsOf = (uses: readonly CompoundUse[], counts: ReadonlyMap<string, number>): Usual[] =>
|
|
73
|
+
[...counts]
|
|
74
|
+
.filter(([compound, count]) => count >= MIN_USUAL_COUNT && Array.from(compound).length >= MIN_COMPOUND_LENGTH)
|
|
75
|
+
.map(([compound, count]) => ({ compound, count, uses: uses.filter((use) => use.compound === compound) }));
|
|
76
|
+
|
|
77
|
+
const slipAgainst = (use: CompoundUse, count: number, usual: Usual, context: SlipContext): CompoundSlip | undefined => {
|
|
78
|
+
const [written, other] = [Array.from(use.compound), Array.from(usual.compound)];
|
|
79
|
+
const index = usual.count > count * 2 ? singleDifference(written, other) : undefined;
|
|
80
|
+
if (index === undefined || !isInside(index, written.length)) return undefined;
|
|
81
|
+
if (context.ignored.has(written[index] ?? "") || context.ignored.has(other[index] ?? "")) return undefined;
|
|
82
|
+
const fallen = fallenWord(use, usual.uses, index);
|
|
83
|
+
if (fallen === undefined || isWrittenElsewhere(fallen, use.compound, context.uses)) return undefined;
|
|
84
|
+
return { written: use.compound, usual: usual.compound, usualCount: usual.count, offset: use.offset, index };
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
/** Each compound written once or twice that differs in one kanji from a compound of the same length written at least three times. */
|
|
88
|
+
export const compoundSlipsIn = (uses: readonly CompoundUse[], ignored: ReadonlySet<string>): CompoundSlip[] => {
|
|
89
|
+
const counts = countsOf(uses);
|
|
90
|
+
const usuals = usualsOf(uses, counts);
|
|
91
|
+
if (usuals.length === 0) return [];
|
|
92
|
+
return uses.flatMap((use) => {
|
|
93
|
+
const count = counts.get(use.compound) ?? 0;
|
|
94
|
+
if (count > MAX_SLIP_COUNT || Array.from(use.compound).length < MIN_COMPOUND_LENGTH) return [];
|
|
95
|
+
const slips = usuals.flatMap((usual) => slipAgainst(use, count, usual, { ignored, uses }) ?? []);
|
|
96
|
+
return slips.length === 1 ? slips : [];
|
|
97
|
+
});
|
|
98
|
+
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { Span } from "./plugin.ts";
|
|
2
|
+
|
|
3
|
+
const FULLWIDTH_OFFSET = 0xfee0;
|
|
4
|
+
const LATIN_LETTER = /[A-Za-z]/u;
|
|
5
|
+
const SPACE = "[\\s\\u3000]";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* 形は名前の後ろに立つ。前は大文字か数字で始まる名前の語か括弧で、あいだは空白か「,」だけ(AOBA INC.、AOBA,INC.、
|
|
9
|
+
* (THAILAND) CO.,LTD.)。語の中(Zinc の inc)、略語の中(U.S.A. の S.A.)、名前の無い所(the LLC)の同じ字は形ではない。
|
|
10
|
+
*/
|
|
11
|
+
const NAME_WORD = "(?<![A-Za-zA-Za-z])[A-ZA-Z0-90-9][A-Za-zA-Za-z0-90-9&&]*";
|
|
12
|
+
const BEFORE = `(?<=${NAME_WORD}(?:${SPACE}|[,,]${SPACE}?)|[))]${SPACE}?)`;
|
|
13
|
+
const AFTER = "(?![A-Za-zA-Za-z0-90-9&&])";
|
|
14
|
+
|
|
15
|
+
const escaped = (char: string): string => char.replace(/[.*+?^${}()|[\]\\]/u, "\\$&");
|
|
16
|
+
|
|
17
|
+
const widths = (char: string): string[] => [char, String.fromCodePoint((char.codePointAt(0) ?? 0) + FULLWIDTH_OFFSET)];
|
|
18
|
+
|
|
19
|
+
/** 一字の形。英字は大文字・小文字と全角・半角のどれでも、点と「,」は全角・半角のどちらでも合う。空白は無くても合う。 */
|
|
20
|
+
const charPattern = (char: string): string => {
|
|
21
|
+
if (char === " ") return `${SPACE}?`;
|
|
22
|
+
if (LATIN_LETTER.test(char)) return `[${[char.toUpperCase(), char.toLowerCase()].flatMap(widths).join("")}]`;
|
|
23
|
+
return /[.,]/u.test(char) ? `[${widths(char).map(escaped).join("")}]` : escaped(char);
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
/** 形は点まで書いたとおりに照らす。点の無い LTD(the LTD fund)は形と読まない。英字で終わる形(LLC)は語の終わりで閉じる。 */
|
|
27
|
+
const formPattern = (form: string): string => [...form].map(charPattern).join("") + (LATIN_LETTER.test(form.slice(-1)) ? AFTER : "");
|
|
28
|
+
|
|
29
|
+
/** 形の語彙表を、一つの正規表現にする。長い形を先に試す。名前の語は大文字で始まるので、大文字・小文字は形の側だけで問わない。 */
|
|
30
|
+
export const latinCompanyFormRegExp = (forms: readonly string[]): RegExp | undefined => {
|
|
31
|
+
if (forms.length === 0) return undefined;
|
|
32
|
+
const longestFirst = [...forms].sort((a, b) => b.length - a.length);
|
|
33
|
+
return new RegExp(`${BEFORE}(?:${longestFirst.map(formPattern).join("|")})`, "gu");
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
/** text の中の、英字の会社の名前の後ろに立つ形(CO.,LTD.、Inc.)の位置。大文字・小文字と全角・半角を問わない。 */
|
|
37
|
+
export const latinCompanyFormSpans = (text: string, pattern: RegExp | undefined): Span[] =>
|
|
38
|
+
pattern === undefined ? [] : [...text.matchAll(pattern)].map((match) => ({ start: match.index, end: match.index + match[0].length }));
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { Boundary } from "./orthography.ts";
|
|
2
|
+
import type { Span, Token } from "./plugin.ts";
|
|
3
|
+
|
|
4
|
+
// 英字の語の側で決まる、空け方の好みでない境目。pure: 語は語彙表から受け取る。
|
|
5
|
+
|
|
6
|
+
/** 英字の語を作る字。vs. の点も語の一部。 */
|
|
7
|
+
const LATIN_WORD_CHAR = /[A-Za-z.]/u;
|
|
8
|
+
const LATIN_WORD_AFTER = /^[A-Za-z.]+/u;
|
|
9
|
+
const JAPANESE = /[\p{Script=Han}\p{Script=Hiragana}\p{Script=Katakana}ー]/u;
|
|
10
|
+
|
|
11
|
+
/** 境目の英字の側の語の範囲(「名前 or 任意」の or)。 */
|
|
12
|
+
const latinWordAt = (text: string, boundary: Boundary): Span => {
|
|
13
|
+
if (LATIN_WORD_CHAR.test(text[boundary.offset - 1] ?? "")) {
|
|
14
|
+
const reversed = Array.from(text.slice(0, boundary.offset)).toReversed().join("");
|
|
15
|
+
return { start: boundary.offset - (LATIN_WORD_AFTER.exec(reversed)?.[0].length ?? 0), end: boundary.offset };
|
|
16
|
+
}
|
|
17
|
+
const start = boundary.offset + (boundary.spaced ? 1 : 0);
|
|
18
|
+
return { start, end: start + (LATIN_WORD_AFTER.exec(text.slice(start))?.[0].length ?? 0) };
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
/** 語の前後が空白一つで、その外がどちらも日本語の字。 */
|
|
22
|
+
const standsBetweenJapanese = (text: string, word: Span): boolean =>
|
|
23
|
+
text[word.start - 1] === " " && text[word.end] === " " && JAPANESE.test(text[word.start - 2] ?? "") && JAPANESE.test(text[word.end + 1] ?? "");
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* 日本語の句と句をつなぐ英語の語(「名前 or メッセージ」「甲案 vs. 乙案」の or、vs.)の前後の空白。or を詰めて書くことは無く、
|
|
27
|
+
* 語を区切る空白で、日本語と英字のあいだの空け方の票ではない。片側が英字の語(A or 任意、名前 or API)なら数える。
|
|
28
|
+
* joiners は語彙表 latin-joiner の語(小文字)。
|
|
29
|
+
*/
|
|
30
|
+
export const isJoinerSpace = (text: string, boundary: Boundary, joiners: ReadonlySet<string>): boolean => {
|
|
31
|
+
if (boundary.kind !== "letter" || !boundary.spaced) return false;
|
|
32
|
+
const word = latinWordAt(text, boundary);
|
|
33
|
+
return joiners.has(text.slice(word.start, word.end).toLowerCase()) && standsBetweenJapanese(text, word);
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* 英字の語に付けて一つの語にする日本語の接尾の語(PDF版、IT化、Apple製)の前の境目。空けて書くことはほとんど無く、
|
|
38
|
+
* 詰めたのは空け方の好みではない。語の頭の字が同じでも別の語(製品、型番)なら数える。suffixes は語彙表 latin-bound-suffix の語。
|
|
39
|
+
* tokens は文書全体の座標で、base は text の先頭の位置。
|
|
40
|
+
*/
|
|
41
|
+
export const isBoundSuffix = (text: string, boundary: Boundary, tokens: readonly Token[] | undefined, base: number, suffixes: ReadonlySet<string>): boolean => {
|
|
42
|
+
if (tokens === undefined || boundary.kind !== "letter" || !LATIN_WORD_CHAR.test(text[boundary.offset - 1] ?? "")) return false;
|
|
43
|
+
const start = base + boundary.offset + (boundary.spaced ? 1 : 0);
|
|
44
|
+
const suffix = tokens.find((token) => token.span.start === start);
|
|
45
|
+
return suffix !== undefined && suffixes.has(suffix.surface);
|
|
46
|
+
};
|
package/src/lexicon-genres.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { Lexicon } from "./plugin.ts";
|
|
2
2
|
|
|
3
3
|
/** A genre is in a target when it is the target or one of its genres (legal/contract is in legal). */
|
|
4
|
-
const isIn = (genre: string, target: string): boolean => genre === target || genre.startsWith(`${target}/`);
|
|
4
|
+
export const isIn = (genre: string, target: string): boolean => genre === target || genre.startsWith(`${target}/`);
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* The entries of a word list that hold for the document's genre: an entry without genres holds everywhere, one with genres only
|
package/src/link-shape.ts
CHANGED
|
@@ -60,3 +60,22 @@ export const isFileNameAlt = (alt: string, prefixes: readonly string[]): boolean
|
|
|
60
60
|
const lowered = written.toLowerCase();
|
|
61
61
|
return prefixes.some((prefix) => lowered.startsWith(prefix.toLowerCase()) && COUNTER.test(written.slice(prefix.length)));
|
|
62
62
|
};
|
|
63
|
+
|
|
64
|
+
/** A list item's marker before a line's text (`- `, `* `, `1. `). */
|
|
65
|
+
const LIST_MARKER = /^\s*(?:[-*+]|\d{1,3}[.)])\s+/u;
|
|
66
|
+
/**
|
|
67
|
+
* A field's label: a few words in bold (`**Licence text**`, a colon after it or not), or ending in a colon (`Website:`).
|
|
68
|
+
* Sentence punctuation or a comma in it makes it prose (`For example, visit:`).
|
|
69
|
+
*/
|
|
70
|
+
const BOLD_LABEL = /^(?:\*\*[^*\n.!?。!?,,、::]{1,40}\*\*|__[^_\n.!?。!?,,、::]{1,40}__)\s*[::]?$/u;
|
|
71
|
+
const COLON_LABEL = /^[^.!?。!?,,、::]{1,40}[::]$/u;
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Whether a URL is the value of a field (`**Licence text** https://…`, `Website: https://…`): on its line only a label comes
|
|
75
|
+
* before it and nothing after. There the address is the content, as in a table cell, so writing it bare is right.
|
|
76
|
+
*/
|
|
77
|
+
export const isFieldValueUrl = (lineBefore: string, lineAfter: string): boolean => {
|
|
78
|
+
if (lineAfter.trim() !== "") return false;
|
|
79
|
+
const label = lineBefore.replace(LIST_MARKER, "").trim();
|
|
80
|
+
return BOLD_LABEL.test(label) || COLON_LABEL.test(label);
|
|
81
|
+
};
|