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
|
@@ -2,10 +2,12 @@ import type { Span, StructureNode } from "../plugin.ts";
|
|
|
2
2
|
import { inDocumentOrder, type StructureIssue } from "./issues.ts";
|
|
3
3
|
import { numbersQuotedInside } from "./quoted-number.ts";
|
|
4
4
|
import { isVersionHistory } from "./version-order.ts";
|
|
5
|
+
import { withRepeatsNamed } from "./repeated-number.ts";
|
|
5
6
|
|
|
6
7
|
const labelOf = (node: StructureNode): string => String(node.attrs["label"] ?? node.address);
|
|
7
8
|
|
|
8
|
-
|
|
9
|
+
/** sideSteps は、この番号の直前にある番号の無い手順(GOV.UK の「and」)の数。それぞれが番号を一つ使っていてよい。 */
|
|
10
|
+
type Ordered = { readonly node: StructureNode; readonly ordinal: number; readonly last: number; readonly sideSteps: number };
|
|
9
11
|
|
|
10
12
|
const OPENING_BRACKET = /^[([{〔【〈《]/u;
|
|
11
13
|
|
|
@@ -17,15 +19,21 @@ const isBracketed = (label: string): boolean => OPENING_BRACKET.test(label.norma
|
|
|
17
19
|
* 法令の第 1 項は番号が無いので、その号(一、二)と第 2 項(2)は同じ条の直下に並ぶ。深さで分けないと「二の次が2」に見える。
|
|
18
20
|
* 社内規程は号の(1)(2)と第 2 項の「2」を同じ深さに読むことがあるので、括弧で囲んだ番号は囲まない番号と別の並びにする。
|
|
19
21
|
*/
|
|
20
|
-
const sequencesOf = (parent: StructureNode, quoting: ReadonlySet<StructureNode
|
|
22
|
+
const sequencesOf = (parent: StructureNode, quoting: ReadonlySet<StructureNode>, isSideStep: (node: StructureNode) => boolean): Ordered[][] => {
|
|
21
23
|
const groups = new Map<string, Ordered[]>();
|
|
24
|
+
let sideSteps = 0;
|
|
22
25
|
parent.children.forEach((node) => {
|
|
23
|
-
if (node.ordinal === undefined
|
|
26
|
+
if (node.ordinal === undefined) {
|
|
27
|
+
if (isSideStep(node)) sideSteps += 1;
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
if (quoting.has(node)) return;
|
|
24
31
|
const key = `${node.kind}/${String(node.level ?? "")}/${isBracketed(labelOf(node)) ? "bracketed" : "bare"}`;
|
|
25
32
|
// 配列を作り直さずに足す。条が何千もある法令で二乗にしない。
|
|
26
33
|
const group = groups.get(key) ?? [];
|
|
27
|
-
group.push({ node, ordinal: node.ordinal, last: node.ordinalTo ?? node.ordinal });
|
|
34
|
+
group.push({ node, ordinal: node.ordinal, last: node.ordinalTo ?? node.ordinal, sideSteps });
|
|
28
35
|
groups.set(key, group);
|
|
36
|
+
sideSteps = 0;
|
|
29
37
|
});
|
|
30
38
|
return [...groups.values()];
|
|
31
39
|
};
|
|
@@ -59,6 +67,7 @@ const breaksIn =
|
|
|
59
67
|
// 範囲をまとめた行(第四十三条から第五十五条まで)の次は、範囲の最後から数える。
|
|
60
68
|
if (previous === undefined || current.ordinal === previous.last + 1 || current.ordinal === 1) return [];
|
|
61
69
|
if (current.ordinal > previous.last + 1 && unreadBetween(unread, previous, current)) return [];
|
|
70
|
+
if (current.ordinal > previous.last + 1 && current.ordinal <= previous.last + 1 + current.sideSteps) return [];
|
|
62
71
|
return [
|
|
63
72
|
{
|
|
64
73
|
offset: current.node.span.start,
|
|
@@ -73,13 +82,19 @@ const breaksIn =
|
|
|
73
82
|
* 1 に戻った番号は新しい並びの始まりとして扱うので、「(a) の後の (a)」の重なりは見逃す。
|
|
74
83
|
* 同じ番号の節を中に持つ節(ほかの文書の条を引いた手本の見出し)は、並びに入れない。
|
|
75
84
|
* 変更履歴の版の見出し(## 3.1.0)は番号の並びではない。版の順番は version-order が見る。
|
|
85
|
+
* 同じ番号が二度続いて次が飛ぶ(4.1、4.1、4.3)ものは、重なりと抜けに分けず、二つ目で一度だけ言う(repeated-number.ts)。
|
|
76
86
|
* unread は、読み手には見えるが木に入らない範囲(Markdown の HTML ブロックの中の文字)を、始まりの順に並べたもの。
|
|
87
|
+
* isSideStep は、番号の代わりに「and」と書いた手順(前の手順と並べて行う)。その数だけ、次の番号は飛んでよい。
|
|
77
88
|
*/
|
|
78
|
-
export const numberingBreaks = (
|
|
89
|
+
export const numberingBreaks = (
|
|
90
|
+
tree: StructureNode,
|
|
91
|
+
unread: readonly Span[] = [],
|
|
92
|
+
isSideStep: (node: StructureNode) => boolean = () => false,
|
|
93
|
+
): StructureIssue[] => {
|
|
79
94
|
const quoting = numbersQuotedInside(tree);
|
|
80
|
-
|
|
81
|
-
.flatMap((parent) => sequencesOf(parent, quoting))
|
|
95
|
+
const breaks = inDocumentOrder(tree)
|
|
96
|
+
.flatMap((parent) => sequencesOf(parent, quoting, isSideStep))
|
|
82
97
|
.filter((sequence) => !isVersionHistory(sequence.map((ordered) => labelOf(ordered.node))))
|
|
83
|
-
.flatMap(breaksIn(unread))
|
|
84
|
-
|
|
98
|
+
.flatMap(breaksIn(unread));
|
|
99
|
+
return withRepeatsNamed(tree, quoting, breaks, unread);
|
|
85
100
|
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { StructureIssue } from "./issues.ts";
|
|
2
|
+
import { coarsestStep, roundingAllowance } from "./rounded-amount.ts";
|
|
2
3
|
import { shownTotal, type Amount } from "./total.ts";
|
|
3
4
|
|
|
4
5
|
/**
|
|
@@ -22,6 +23,10 @@ export type TotalWords = {
|
|
|
22
23
|
readonly leads: readonly string[];
|
|
23
24
|
/** 合計と内訳のあいだにあれば、後ろの金額は合計の内訳のすべてとは限らないと読む語(including、うち、or)。 */
|
|
24
25
|
readonly partials: readonly string[];
|
|
26
|
+
/** partials のうち、並べた金額が主なものだけだと言う語(主な、mainly)。合計の前に並べた内訳の前に書いても、内訳は合計のすべてではない。 */
|
|
27
|
+
readonly mainOnly: readonly string[];
|
|
28
|
+
/** 丸めた桁で書く金額の単位(百万円、千円、million)。どの金額もこの単位なら、丸めのずれ(内訳の数 − 1 桁まで)は合うとみなす。 */
|
|
29
|
+
readonly roundedUnits: readonly string[];
|
|
25
30
|
/** 次の文で内訳の語の前に書いて、前の文の合計を指す語(This、It、その)。何も書かなくてもよい。 */
|
|
26
31
|
readonly backReferences: readonly string[];
|
|
27
32
|
};
|
|
@@ -85,13 +90,15 @@ const signedValues = (source: string, parts: readonly Amount[], from: number, di
|
|
|
85
90
|
|
|
86
91
|
type Breakdown = { readonly total: Amount; readonly parts: readonly Amount[]; readonly from: number; readonly nearEnd: boolean };
|
|
87
92
|
|
|
88
|
-
const mismatch = (source: string, breakdown: Breakdown,
|
|
93
|
+
const mismatch = (source: string, breakdown: Breakdown, words: TotalWords): StructureIssue[] => {
|
|
89
94
|
const { total, from, nearEnd } = breakdown;
|
|
90
95
|
const parts = breakdown.parts.filter((part) => part.unit === total.unit);
|
|
91
96
|
if (parts.length < MIN_PARTS) return [];
|
|
92
|
-
const values = signedValues(source, parts, from, discounts);
|
|
97
|
+
const values = signedValues(source, parts, from, words.discounts);
|
|
93
98
|
const written = centsOf(total);
|
|
94
|
-
|
|
99
|
+
const step = coarsestStep(source, [total, ...parts], words.roundedUnits) * CENTS;
|
|
100
|
+
const matches = (run: readonly number[]): boolean => Math.abs(sumOf(run) - written) <= Math.round(roundingAllowance(step, run.length));
|
|
101
|
+
if (nearRuns(values, nearEnd).some(matches)) return [];
|
|
95
102
|
return [{ offset: total.offset, values: shownTotal(source, total, sumOf(values)) }];
|
|
96
103
|
};
|
|
97
104
|
|
|
@@ -106,6 +113,10 @@ const breakdownEnd = (source: string, total: Amount, marker: Span, sentence: Spa
|
|
|
106
113
|
|
|
107
114
|
const inside = (amounts: readonly Amount[], start: number, end: number): Amount[] => amounts.filter((amount) => amount.offset >= start && amount.end <= end);
|
|
108
115
|
|
|
116
|
+
/** 内訳の前(start から end まで)に、主なものだけを並べると言う語があるか(「主な設備投資は、…」「主な内訳は」)。 */
|
|
117
|
+
const saysMainOnly = (source: string, start: number, end: number, words: TotalWords): boolean =>
|
|
118
|
+
phraseSpans(source.slice(start, end), start, words.mainOnly).length > 0;
|
|
119
|
+
|
|
109
120
|
/** 合計の語のすぐ後ろの金額を、その前の内訳と比べる。内訳は、前の合計の語か内訳の語より後ろから。 */
|
|
110
121
|
const totalsAfterParts = (source: string, sentence: Span, amounts: readonly Amount[], words: TotalWords): StructureIssue[] => {
|
|
111
122
|
const text = source.slice(sentence.start, sentence.end);
|
|
@@ -115,7 +126,9 @@ const totalsAfterParts = (source: string, sentence: Span, amounts: readonly Amou
|
|
|
115
126
|
const total = amounts.find((amount) => amount.offset >= marker.end && isTotalGap(gapBefore(source, marker.end, amount)));
|
|
116
127
|
if (total === undefined) return [];
|
|
117
128
|
const from = Math.max(sentence.start, markers[index - 1]?.end ?? 0, ...breakdowns.filter((span) => span.end <= marker.start).map((span) => span.end));
|
|
118
|
-
|
|
129
|
+
const parts = inside(amounts, from, marker.start);
|
|
130
|
+
if (saysMainOnly(source, from, parts[0]?.offset ?? from, words)) return [];
|
|
131
|
+
return mismatch(source, { total, parts, from, nearEnd: true }, words);
|
|
119
132
|
});
|
|
120
133
|
};
|
|
121
134
|
|
|
@@ -126,9 +139,10 @@ const totalsBeforeParts = (source: string, sentence: Span, amounts: readonly Amo
|
|
|
126
139
|
return phraseSpans(text, sentence.start, words.breakdowns).flatMap((marker) => {
|
|
127
140
|
const total = amounts.findLast((amount) => amount.end <= marker.start);
|
|
128
141
|
if (total === undefined || phraseSpans(source.slice(total.end, marker.start), total.end, words.joiners).length > 0) return [];
|
|
142
|
+
if (saysMainOnly(source, total.end, marker.start, words)) return [];
|
|
129
143
|
const nextTotal = totalMarkers.find((span) => span.start >= marker.end)?.start ?? sentence.end;
|
|
130
144
|
const parts = inside(amounts, marker.end, Math.min(nextTotal, breakdownEnd(source, total, marker, sentence)));
|
|
131
|
-
return mismatch(source, { total, parts, from: marker.end, nearEnd: false }, words
|
|
145
|
+
return mismatch(source, { total, parts, from: marker.end, nearEnd: false }, words);
|
|
132
146
|
});
|
|
133
147
|
};
|
|
134
148
|
|
|
@@ -152,7 +166,7 @@ const fullBreakdown = (source: string, total: Amount, listed: readonly Amount[],
|
|
|
152
166
|
if (last === undefined || listed.some((part) => part.unit !== total.unit)) return [];
|
|
153
167
|
const between = source.slice(total.end, last.offset);
|
|
154
168
|
if (PERCENT.test(between) || phraseSpans(between, total.end, words.partials).length > 0) return [];
|
|
155
|
-
return mismatch(source, { total, parts: listed, from, nearEnd: false }, words
|
|
169
|
+
return mismatch(source, { total, parts: listed, from, nearEnd: false }, words);
|
|
156
170
|
};
|
|
157
171
|
|
|
158
172
|
/** 文の中で、合計の語の金額のすぐ後ろに内訳を始める記号や語(": ")があれば、その後ろから文の終わりか次の記号までが内訳。 */
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import type { Span, StructureNode } from "../plugin.ts";
|
|
2
|
+
import type { StructureIssue } from "./issues.ts";
|
|
3
|
+
import { isVersionHistory } from "./version-order.ts";
|
|
4
|
+
|
|
5
|
+
type Placed = { readonly node: StructureNode; readonly parent: StructureNode; readonly ordinal: number };
|
|
6
|
+
|
|
7
|
+
const labelOf = (node: StructureNode): string => String(node.attrs["label"] ?? node.address);
|
|
8
|
+
|
|
9
|
+
/** 「4.1」「4.1」の、最後の番号より前(「4」)。点で区切った番号でなければ無い。 */
|
|
10
|
+
const DOTTED = /^(.*\d)[..](\d+)$/u;
|
|
11
|
+
const dottedPrefixOf = (label: string): string | undefined => DOTTED.exec(label.normalize("NFKC").trim())?.[1];
|
|
12
|
+
|
|
13
|
+
const OPENING_BRACKET = /^[([{〔【〈《]/u;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* 番号の並びの単位。点で区切った番号(4.1、4.3)は、前の番号(4)が同じものどうしを、親が違っても一つの並びに読む。
|
|
17
|
+
* 番号の無い見出し(## Approved Office Supplies List)が間に入ると、4.3 は 4.1 と別の親の下に入るが、読み手には 4 の続きに見える。
|
|
18
|
+
* それ以外の番号(第3条、(a))は、同じ親の子どうしだけを並べる。
|
|
19
|
+
*/
|
|
20
|
+
const familyOf = (placed: Placed, parentIds: ReadonlyMap<StructureNode, number>): string => {
|
|
21
|
+
const label = labelOf(placed.node);
|
|
22
|
+
const shape = `${placed.node.kind}/${String(placed.node.level ?? "")}/${OPENING_BRACKET.test(label.normalize("NFKC")) ? "bracketed" : "bare"}`;
|
|
23
|
+
const prefix = dottedPrefixOf(label);
|
|
24
|
+
return prefix === undefined ? `${shape}/parent:${String(parentIds.get(placed.parent))}` : `${shape}/dotted:${prefix}`;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const placedNodes = (tree: StructureNode, quoting: ReadonlySet<StructureNode>): Placed[] => {
|
|
28
|
+
const placed: Placed[] = [];
|
|
29
|
+
const pending: StructureNode[] = [tree];
|
|
30
|
+
for (let parent = pending.pop(); parent !== undefined; parent = pending.pop()) {
|
|
31
|
+
parent.children.forEach((node) => {
|
|
32
|
+
pending.push(node);
|
|
33
|
+
if (node.ordinal !== undefined && node.ordinalTo === undefined && !quoting.has(node)) placed.push({ node, parent, ordinal: node.ordinal });
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
return placed.toSorted((left, right) => left.node.span.start - right.node.span.start);
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const familiesOf = (placed: readonly Placed[]): Placed[][] => {
|
|
40
|
+
const parentIds = new Map<StructureNode, number>();
|
|
41
|
+
placed.forEach(({ parent }) => parentIds.set(parent, parentIds.get(parent) ?? parentIds.size));
|
|
42
|
+
const families = new Map<string, Placed[]>();
|
|
43
|
+
placed.forEach((entry) => {
|
|
44
|
+
const key = familyOf(entry, parentIds);
|
|
45
|
+
const family = families.get(key) ?? [];
|
|
46
|
+
family.push(entry);
|
|
47
|
+
families.set(key, family);
|
|
48
|
+
});
|
|
49
|
+
return [...families.values()];
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
/** 番号の最後の数字を一つ進めた書き方(4.1 → 4.2、Section 2 → Section 3)。数字で書いていなければ、進めた番号だけ。 */
|
|
53
|
+
export const nextLabelOf = (label: string, ordinal: number): string => {
|
|
54
|
+
const normalized = label.normalize("NFKC");
|
|
55
|
+
const digits = [...normalized.matchAll(/\d+/gu)].at(-1);
|
|
56
|
+
if (digits === undefined || Number(digits[0]) !== ordinal) return String(ordinal + 1);
|
|
57
|
+
return `${normalized.slice(0, digits.index)}${String(ordinal + 1)}${normalized.slice(digits.index + digits[0].length)}`;
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
/** explains は、この誤りで説明がつく、並びの指摘の位置(二つ目、同じ親の下なら次の番号)。 */
|
|
61
|
+
export type RepeatedNumber = { readonly explains: readonly number[]; readonly between: Span; readonly issue: StructureIssue };
|
|
62
|
+
|
|
63
|
+
const repeatsIn = (family: readonly Placed[]): RepeatedNumber[] =>
|
|
64
|
+
family.flatMap((first, index) => {
|
|
65
|
+
const second = family[index + 1];
|
|
66
|
+
const next = family[index + 2];
|
|
67
|
+
if (second === undefined || next === undefined || second.parent !== first.parent) return [];
|
|
68
|
+
if (second.ordinal !== first.ordinal || next.ordinal !== first.ordinal + 2) return [];
|
|
69
|
+
const label = labelOf(second.node);
|
|
70
|
+
return [
|
|
71
|
+
{
|
|
72
|
+
between: { start: second.node.span.start, end: next.node.span.start },
|
|
73
|
+
explains: next.parent === second.parent ? [second.node.span.start, next.node.span.start] : [second.node.span.start],
|
|
74
|
+
issue: {
|
|
75
|
+
offset: second.node.span.start,
|
|
76
|
+
variant: "repeated",
|
|
77
|
+
values: { previous: labelOf(first.node), label, expected: first.ordinal + 1, found: second.ordinal, probably: nextLabelOf(label, second.ordinal) },
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
];
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* 同じ番号が二度続き、その次が一つ飛ぶ(4.1、4.1、4.3)。二つ目は、振り間違えた 4.2 である見込みが高い。
|
|
85
|
+
* 重なりと抜けの二つに見えるが、誤りは一つなので、二つ目の番号で一度だけ言う。
|
|
86
|
+
* 同じ親の下で隣り合う二つが同じ番号のときだけ。変更履歴の版の見出し(## 3.1.0)は番号の並びではないので見ない。
|
|
87
|
+
* 別の部の 1.1 から振り直した並び(1.1、1.2 の後の 1.1、1.2)は、次が飛ばないので言わない。
|
|
88
|
+
*/
|
|
89
|
+
export const repeatedNumbers = (tree: StructureNode, quoting: ReadonlySet<StructureNode>): RepeatedNumber[] =>
|
|
90
|
+
familiesOf(placedNodes(tree, quoting))
|
|
91
|
+
.filter((family) => !isVersionHistory(family.map(({ node }) => labelOf(node))))
|
|
92
|
+
.flatMap(repeatsIn);
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* 並びの抜けと重なり(issues)のうち、同じ番号が二度続いたもの(repeatedNumbers)で説明がつくものを、その一つにまとめる。
|
|
96
|
+
* 二つ目と次の番号のあいだに読めなかった範囲(unread)があれば、飛んだ番号はそこにあるかもしれないので、まとめずに元の指摘を残す。
|
|
97
|
+
* 二つ目の番号への指摘は置き換え、次の番号への抜けの指摘は、二つ目と同じ親の下にあるときだけ(二つ目の次として数えたもの)消す。
|
|
98
|
+
*/
|
|
99
|
+
export const withRepeatsNamed = (
|
|
100
|
+
tree: StructureNode,
|
|
101
|
+
quoting: ReadonlySet<StructureNode>,
|
|
102
|
+
issues: readonly StructureIssue[],
|
|
103
|
+
unread: readonly Span[] = [],
|
|
104
|
+
): StructureIssue[] => {
|
|
105
|
+
const repeats = repeatedNumbers(tree, quoting).filter(({ between }) => !unread.some((span) => span.start > between.start && span.start < between.end));
|
|
106
|
+
const explained = new Set(repeats.flatMap(({ explains }) => explains));
|
|
107
|
+
return [...issues.filter((issue) => !explained.has(issue.offset)), ...repeats.map(({ issue }) => issue)].toSorted(
|
|
108
|
+
(left, right) => left.offset - right.offset,
|
|
109
|
+
);
|
|
110
|
+
};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type { Amount } from "./total.ts";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* 丸めた単位(百万円、千円、億円、$3.1 million)で書いた金額の、最後の桁一つ分。丸めて書いた内訳と合計は、
|
|
5
|
+
* 四捨五入でも切り捨てでも切り上げでも、内訳の数 − 1 桁までずれる(3,074百万円 + 2,434百万円 + 1,146百万円 と 総額6,655百万円)。
|
|
6
|
+
* 有効数字が 3 桁以上の金額だけを丸めたと読む。最後の桁一つ分がその金額の 1% 以下になり、1億円 + 2億円 = 4億円 のような
|
|
7
|
+
* 金額と同じ大きさのずれを丸めとは読まない。
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/** 書いた数(6,655、3.1)。漢数字だけの金額は読まない。 */
|
|
11
|
+
const NUMERAL = /\d[\d,,]*(?:[..]\d+)?/u;
|
|
12
|
+
/** 金額のすぐ後ろに書いた単位の語($3.1 million の million)までのあいだ。 */
|
|
13
|
+
const UNIT_AFTER = /^[ \t]?([A-Za-z]+)/u;
|
|
14
|
+
const DECIMAL_BASE = 10;
|
|
15
|
+
/** 金額の後ろで単位の語を探す長さ。 */
|
|
16
|
+
const UNIT_REACH = 16;
|
|
17
|
+
/** 丸めたと読む金額の、書いた数の有効数字の最小の桁数。 */
|
|
18
|
+
const MIN_SIGNIFICANT_DIGITS = 3;
|
|
19
|
+
|
|
20
|
+
/** 書いた数と、その小数の桁。 */
|
|
21
|
+
type Numeral = { readonly value: number; readonly decimals: number; readonly significant: number };
|
|
22
|
+
|
|
23
|
+
/** 書いた数と、その小数の桁と有効数字の桁数(頭の 0 を除いた数字の数。6,655 なら 4、3.1 なら 2、0.25 なら 2)。 */
|
|
24
|
+
const numeralOf = (written: string): Numeral | undefined => {
|
|
25
|
+
const match = NUMERAL.exec(written.normalize("NFKC"));
|
|
26
|
+
if (match === null) return undefined;
|
|
27
|
+
const [whole = "", fraction = ""] = match[0].replace(/,/gu, "").split(".");
|
|
28
|
+
const significant = `${whole}${fraction}`.replace(/^0+/u, "").length;
|
|
29
|
+
return { value: Number(`${whole}.${fraction || "0"}`), decimals: fraction.length, significant };
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
/** 金額が丸めた単位で書いてあるか。和文の単位は金額の中に(6,655百万円)、英語の単位は金額のすぐ後ろに($5.6 million)ある。 */
|
|
33
|
+
const inRoundedUnit = (written: string, after: string, units: readonly string[]): boolean => {
|
|
34
|
+
const word = UNIT_AFTER.exec(after)?.[1]?.toLowerCase();
|
|
35
|
+
return units.some((unit) => written.includes(unit) || unit.toLowerCase() === word);
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
/** 丸めた単位で書いた金額の最後の桁一つ分を、金額の値の単位で。丸めた単位でないか、有効数字が 3 桁未満なら undefined。 */
|
|
39
|
+
export const displayStep = (source: string, amount: Amount, units: readonly string[]): number | undefined => {
|
|
40
|
+
const written = source.slice(amount.offset, amount.end);
|
|
41
|
+
if (!inRoundedUnit(written, source.slice(amount.end, amount.end + UNIT_REACH), units)) return undefined;
|
|
42
|
+
const numeral = numeralOf(written);
|
|
43
|
+
if (numeral === undefined || numeral.value === 0 || numeral.significant < MIN_SIGNIFICANT_DIGITS) return undefined;
|
|
44
|
+
return (amount.value / numeral.value) * DECIMAL_BASE ** -numeral.decimals;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
/** 金額のどれもが丸めた単位で書いてあれば、その一番粗い桁一つ分。一つでも丸めた単位でなければ 0(ずれを許さない)。 */
|
|
48
|
+
export const coarsestStep = (source: string, amounts: readonly Amount[], units: readonly string[]): number => {
|
|
49
|
+
const steps = amounts.map((amount) => displayStep(source, amount, units));
|
|
50
|
+
const known = steps.filter((step) => step !== undefined);
|
|
51
|
+
return known.length === steps.length && known.length > 0 ? Math.max(...known) : 0;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
/** 丸めた桁 step で書いた parts 個の内訳の和が、合計からずれてよい幅。 */
|
|
55
|
+
export const roundingAllowance = (step: number, parts: number): number => step * Math.max(0, parts - 1);
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { StructureNode } from "../plugin.ts";
|
|
2
|
+
|
|
3
|
+
/** The label, then a gap of two or more spaces or a tab (the label column of a step list), then the step's own text. */
|
|
4
|
+
const LABEL_COLUMN = /^(?<word>\S+)(?: {2}|\t)/u;
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* A heading labelled with a word (side-step-label: "and", "or") where a step list puts a number: a step done alongside
|
|
8
|
+
* the one before it (GOV.UK's step-by-step pages). The word is matched as written, and must stand in a label column
|
|
9
|
+
* like a step's number does: "and more details" is an ordinary heading.
|
|
10
|
+
*/
|
|
11
|
+
export const sideStepReader = (labels: readonly string[]): ((node: StructureNode) => boolean) => {
|
|
12
|
+
const words = new Set(labels);
|
|
13
|
+
return (node) => {
|
|
14
|
+
const heading = node.attrs["heading"];
|
|
15
|
+
if (typeof heading !== "string") return false;
|
|
16
|
+
const label = LABEL_COLUMN.exec(heading);
|
|
17
|
+
const word = label?.groups?.["word"];
|
|
18
|
+
if (label === null || word === undefined) return false;
|
|
19
|
+
return words.has(word) && heading.slice(label[0].length).trim() !== "";
|
|
20
|
+
};
|
|
21
|
+
};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// A row label that spans two columns in the table it was converted from (| Total score | | 5 |): Markdown has no column
|
|
2
|
+
// span, so the cell right after the label is left blank and the value follows. Pure; the caller passes the total words.
|
|
3
|
+
|
|
4
|
+
/** A note in parentheses at the end of a label (Total score (count of all "yes" answers above)), dropped before reading it. */
|
|
5
|
+
const TRAILING_NOTE = /[((][^()()]*[))]$/u;
|
|
6
|
+
const EMPHASIS = /[*_]/gu;
|
|
7
|
+
const COLON_END = /[::]$/u;
|
|
8
|
+
const LATIN_LETTER = /\p{Script=Latin}/u;
|
|
9
|
+
|
|
10
|
+
const labelText = (cell: string): string => cell.replace(EMPHASIS, "").trim().replace(TRAILING_NOTE, "").trim();
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Whether `word` stands at the start or the end of `label` as a word of its own: no Latin letter runs on from it. A word of
|
|
14
|
+
* one character (計) must be the whole label, since it starts or ends many words (計画, 設計).
|
|
15
|
+
*/
|
|
16
|
+
const startsOrEndsWith = (label: string, word: string): boolean => {
|
|
17
|
+
const lower = label.toLowerCase();
|
|
18
|
+
const target = word.toLowerCase();
|
|
19
|
+
if (Array.from(target).length === 1) return lower === target;
|
|
20
|
+
const after = lower.charAt(target.length);
|
|
21
|
+
const before = lower.charAt(lower.length - target.length - 1);
|
|
22
|
+
return (lower.startsWith(target) && !LATIN_LETTER.test(after)) || (lower.endsWith(target) && !LATIN_LETTER.test(before));
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
/** A label: it ends in a colon (Total:), or a total word starts or ends it (Total score, 得点合計). */
|
|
26
|
+
export const isSpanningLabel = (cell: string, totalWords: readonly string[]): boolean => {
|
|
27
|
+
const label = labelText(cell);
|
|
28
|
+
if (label === "") return false;
|
|
29
|
+
return COLON_END.test(label) || totalWords.some((word) => word !== "" && startsOrEndsWith(label, word));
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Whether the blank at `column` is the second half of a label that spans two columns: the cell before it holds such a label
|
|
34
|
+
* and the cell after it is filled. Cells are undefined when blank.
|
|
35
|
+
*/
|
|
36
|
+
export const isSpannedLabelGap = (cells: readonly (string | undefined)[], column: number, totalWords: readonly string[]): boolean => {
|
|
37
|
+
const label = cells[column - 1];
|
|
38
|
+
return cells[column] === undefined && cells[column + 1] !== undefined && label !== undefined && isSpanningLabel(label, totalWords);
|
|
39
|
+
};
|
|
@@ -51,10 +51,19 @@ export const dottedNumber = (line: string, context: NumberingContext, plainText
|
|
|
51
51
|
export const isAmount = (dotted: DottedLine, countedAfter: StructurePatterns["countedAfter"]): boolean =>
|
|
52
52
|
!dotted.closedByDot && countedAfter?.(dotted.number, dotted.rest) === true;
|
|
53
53
|
|
|
54
|
-
/**
|
|
54
|
+
/** 年まで書いた日付の値(2022-12-08)。年の無い「2 May」は、番号 2 の題「May」とも読める。 */
|
|
55
|
+
const WITH_YEAR = /^\d{4}-/u;
|
|
56
|
+
|
|
57
|
+
/** 番号が年まで書いた日付の頭(「8 December 2022」の 8)か。日付を言語の dates が読む。年表の見出しの日は番号の並びではない。 */
|
|
58
|
+
export const startsDate = (text: string, dates: StructurePatterns["dates"]): boolean => {
|
|
59
|
+
const at = text.length - text.trimStart().length;
|
|
60
|
+
return (dates?.(text) ?? []).some((date) => date.start <= at && at < date.end && WITH_YEAR.test(String(date.attrs["value"] ?? "")));
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
/** 言語を問わない通し番号。後ろが単位なら数量、日付なら日、本文の文の続きなら文の中の数なので、番号にしない。 */
|
|
55
64
|
export const universalNumber = (patterns: StructurePatterns, text: string, context: NumberingContext, plainText: boolean): NumberedLine | undefined => {
|
|
56
65
|
const dotted = dottedNumber(text, context, plainText);
|
|
57
|
-
if (dotted === undefined || isAmount(dotted, patterns.countedAfter)) return undefined;
|
|
66
|
+
if (dotted === undefined || isAmount(dotted, patterns.countedAfter) || startsDate(text, patterns.dates)) return undefined;
|
|
58
67
|
const numbered = { number: dotted.number, rest: dotted.rest, isHeading: context.isHeading };
|
|
59
68
|
return numberInSentence(numbered, patterns.continuesSentence) ? undefined : dotted;
|
|
60
69
|
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { Heading } from "../document.ts";
|
|
2
|
+
import type { NumberingContext, StructurePatterns } from "../plugin.ts";
|
|
3
|
+
import type { Line } from "./lines.ts";
|
|
4
|
+
|
|
5
|
+
/** Hashes with no space before the words: 「##第一四条 (存続条項)」. CommonMark reads the line as a paragraph. */
|
|
6
|
+
const UNSPACED = /^ {0,3}(?<hashes>#{1,6})(?<words>[^#\s].*)$/u;
|
|
7
|
+
|
|
8
|
+
const ON_HEADING: NumberingContext = { open: [], isHeading: true };
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Lines (by number) written as a heading without the space after the hashes, kept only when the language reads the words as an article or
|
|
12
|
+
* a chapter. The writer's neighbours are headings of the same shape, so reading this one as a paragraph puts a gap in the
|
|
13
|
+
* article numbers that is not in the document. 「#タグ」 and 「#1 の件」 stay text.
|
|
14
|
+
*/
|
|
15
|
+
export const unspacedHeadings = (lines: readonly Line[], headed: ReadonlyMap<number, Heading>, patterns: StructurePatterns): [number, Heading][] =>
|
|
16
|
+
lines.flatMap((line) => {
|
|
17
|
+
if (headed.has(line.number)) return [];
|
|
18
|
+
const groups = UNSPACED.exec(line.text)?.groups;
|
|
19
|
+
const hashes = groups?.["hashes"];
|
|
20
|
+
const words = groups?.["words"]?.trim();
|
|
21
|
+
if (hashes === undefined || words === undefined) return [];
|
|
22
|
+
const kind = patterns.numbered(words, ON_HEADING)?.kind;
|
|
23
|
+
if (kind !== "article" && kind !== "chapter") return [];
|
|
24
|
+
const heading: Heading = { depth: hashes.length, text: words, start: line.start, end: line.start + line.text.length };
|
|
25
|
+
const entry: [number, Heading] = [line.number, heading];
|
|
26
|
+
return [entry];
|
|
27
|
+
});
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
// 箇条書きで書いた小見出し。「- (b) Composition: …」の下に字下げして「- i. one representative …」と書いた文書は、
|
|
2
|
+
// 「Section 8(b)(i)」の (b) と i を本文の括弧ではなく行の頭の札で書く。札の入れ子は字下げで読む。
|
|
3
|
+
|
|
4
|
+
/** 行の頭の札。indent は札の前の字下げの幅(タブは 4)。 */
|
|
5
|
+
export type ListLabel = { readonly label: string; readonly indent: number };
|
|
6
|
+
|
|
7
|
+
/** 字下げ、箇条書きの記号(-、*、+)、札、空白、本文。 */
|
|
8
|
+
const LIST_LINE = /^(?<indent>[ \t]*)(?:[-*+][ \t]+)?(?<mark>\S+)[ \t]+\S/u;
|
|
9
|
+
/** 札「(b)」「i.」「2)」。 */
|
|
10
|
+
const MARK = /^\(?(?<label>[a-zA-Z]{1,4}|\d{1,3})[.)]$/u;
|
|
11
|
+
|
|
12
|
+
/** 札の中身。開き括弧で始まる札は閉じ括弧で終わる(「(b.」は札ではない)。 */
|
|
13
|
+
const labelOf = (mark: string): string | undefined => {
|
|
14
|
+
const label = MARK.exec(mark)?.groups?.["label"];
|
|
15
|
+
return mark.startsWith("(") && !mark.endsWith(")") ? undefined : label;
|
|
16
|
+
};
|
|
17
|
+
const TAB_WIDTH = 4;
|
|
18
|
+
|
|
19
|
+
const widthOf = (indent: string): number => [...indent].reduce((width, char) => width + (char === "\t" ? TAB_WIDTH : 1), 0);
|
|
20
|
+
|
|
21
|
+
/** 本文の行の頭に書かれた札を、文書の順に。Pure. */
|
|
22
|
+
export const listLabels = (body: string): ListLabel[] =>
|
|
23
|
+
body.split("\n").flatMap((line) => {
|
|
24
|
+
const groups = LIST_LINE.exec(line)?.groups;
|
|
25
|
+
const label = labelOf(groups?.["mark"] ?? "");
|
|
26
|
+
return label === undefined ? [] : [{ label, indent: widthOf(groups?.["indent"] ?? "") }];
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
/** 札 index の下(次に同じか浅い字下げの札が来るまで)の終わり。 */
|
|
30
|
+
const scopeEnd = (items: readonly ListLabel[], index: number, end: number): number => {
|
|
31
|
+
const depth = items[index]?.indent ?? 0;
|
|
32
|
+
const next = items.findIndex((item, at) => at > index && at < end && item.indent <= depth);
|
|
33
|
+
return next === -1 ? end : next;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
/** items[from, end) の一番浅い字下げの札(直下の札)から、labels を入れ子の順にたどれるか。深い札を飛ばして親にしない。 */
|
|
37
|
+
const pathFrom = (items: readonly ListLabel[], labels: readonly string[], from: number, end: number): boolean => {
|
|
38
|
+
const [first, ...rest] = labels;
|
|
39
|
+
if (first === undefined) return true;
|
|
40
|
+
const scope = items.slice(from, end);
|
|
41
|
+
const top = Math.min(...scope.map((item) => item.indent));
|
|
42
|
+
return scope.some((item, at) => item.indent === top && item.label === first && pathFrom(items, rest, from + at + 1, scopeEnd(items, from + at, end)));
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
/** 札「b」「iv」が、行の頭の札として「(b)」の下に「iv.」と入れ子で書かれているか。札が無ければ false。Pure. */
|
|
46
|
+
export const hasListPath = (items: readonly ListLabel[], labels: readonly string[]): boolean => labels.length > 0 && pathFrom(items, labels, 0, items.length);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { StructureNode } from "../plugin.ts";
|
|
2
2
|
import { headingRegions, parentAndLabels, writtenHeadings, writtenSubdivisions, type WrittenHeading } from "./written-headings.ts";
|
|
3
|
+
import { hasListPath, listLabels } from "./written-list-path.ts";
|
|
3
4
|
|
|
4
5
|
// 木が番地にしない平文の見出しと、本文の中に書いた括弧の小見出し(「6.1 … that (i) the Software …」)を、参照の行き先として引く。
|
|
5
6
|
|
|
@@ -31,27 +32,33 @@ const treeRegions = (nodes: readonly StructureNode[], headings: readonly Written
|
|
|
31
32
|
return regions;
|
|
32
33
|
};
|
|
33
34
|
|
|
35
|
+
/** 範囲の本文を read で読んだ結果。範囲ごとに、要るときに一度だけ読む。 */
|
|
36
|
+
const perRegion = <T>(source: string, read: (body: string) => T): ((region: Region) => T) => {
|
|
37
|
+
const cache = new Map<string, T>();
|
|
38
|
+
return ([start, end]) => {
|
|
39
|
+
const key = `${String(start)}:${String(end)}`;
|
|
40
|
+
const found = cache.get(key) ?? read(source.slice(start, end));
|
|
41
|
+
cache.set(key, found);
|
|
42
|
+
return found;
|
|
43
|
+
};
|
|
44
|
+
};
|
|
45
|
+
|
|
34
46
|
/**
|
|
35
47
|
* 平文の見出しの番地と、参照(Section 6.1(i))の親(6.1)が木の条か平文の見出しとしてあり、その範囲の本文に札「(i)」が
|
|
36
|
-
*
|
|
48
|
+
* 書かれているか(札を行の頭に入れ子で書いた箇条書きも読む)を引く関数。Pure.
|
|
37
49
|
*/
|
|
38
50
|
export const writtenReading = (nodes: readonly StructureNode[], source: string): WrittenReading => {
|
|
39
51
|
const references = nodes.filter((node) => node.kind === "reference").map((node) => ({ start: node.span.start, end: node.span.end, target: targetOf(node) }));
|
|
40
52
|
const headings = writtenHeadings(references, source);
|
|
41
53
|
const written = headingRegions(headings, source.length);
|
|
42
54
|
const tree = treeRegions(nodes, headings);
|
|
43
|
-
const
|
|
44
|
-
const
|
|
45
|
-
const key = `${String(start)}:${String(end)}`;
|
|
46
|
-
const found = labels.get(key) ?? writtenSubdivisions(source.slice(start, end));
|
|
47
|
-
labels.set(key, found);
|
|
48
|
-
return found;
|
|
49
|
-
};
|
|
55
|
+
const labelsIn = perRegion(source, writtenSubdivisions);
|
|
56
|
+
const listIn = perRegion(source, listLabels);
|
|
50
57
|
const regionsOf = (address: string): Region[] => [...(tree.get(address) ?? []), ...(written.get(address) ?? [])];
|
|
51
58
|
const subdivision = (reference: StructureNode): boolean => {
|
|
52
59
|
const wanted = parentAndLabels(targetOf(reference), String(reference.attrs["label"] ?? ""));
|
|
53
60
|
if (wanted === undefined) return false;
|
|
54
|
-
return regionsOf(wanted.parent).some((region) => wanted.labels.every((label) => labelsIn(region).has(label)));
|
|
61
|
+
return regionsOf(wanted.parent).some((region) => wanted.labels.every((label) => labelsIn(region).has(label)) || hasListPath(listIn(region), wanted.labels));
|
|
55
62
|
};
|
|
56
63
|
return { addresses: headings.map((heading) => heading.address), subdivision };
|
|
57
64
|
};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
// A title written on a line of its own, as a plain-text heading (Exhibit A to Merger Agreement, ANNEX 1): a few words with
|
|
2
|
+
// no full stop, and a blank line or the document's edge on both sides. Pure.
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* How a reader tells a title from a sentence. dangling-figure-reference reads a caption of any labelled thing; duplicate-
|
|
6
|
+
* definition reads where an attached instrument starts. Their notions differ, so each says how.
|
|
7
|
+
*/
|
|
8
|
+
export type TitleNotion = {
|
|
9
|
+
/** A title ending in one of these is a sentence or runs on. duplicate-definition also refuses a trailing `: ; ,`. */
|
|
10
|
+
readonly sentenceEnd: RegExp;
|
|
11
|
+
/** Every word longer than three letters starts with a capital. dangling-figure-reference only: Figure 3 shows the trend. */
|
|
12
|
+
readonly capitalised: boolean;
|
|
13
|
+
/**
|
|
14
|
+
* "any-whitespace": the document's edge may be reached across any whitespace (a line of U+3000 or NBSP before the
|
|
15
|
+
* first line). "blank-line": only lines of spaces and tabs count. dangling-figure-reference takes the first.
|
|
16
|
+
*/
|
|
17
|
+
readonly edge: "any-whitespace" | "blank-line";
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
/** A title runs at most this many words after its label and number. */
|
|
21
|
+
const TITLE_MAX_WORDS = 6;
|
|
22
|
+
/** A word this long or shorter may stay in lower case in a title (to, of, the, and). */
|
|
23
|
+
const TITLE_SMALL_WORD = 3;
|
|
24
|
+
const BLANK_LINE = /\n[ \t\r]*\n[ \t\r]*$/u;
|
|
25
|
+
const ENDS_LINE = /^[ \t\r]*\n/u;
|
|
26
|
+
const BLANK = /^[ \t\r]*$/u;
|
|
27
|
+
const LOWER_START = /^\p{Ll}/u;
|
|
28
|
+
|
|
29
|
+
const isShortTitle = (rest: string, notion: TitleNotion): boolean => {
|
|
30
|
+
const title = rest.trim();
|
|
31
|
+
const words = title.split(/\s+/u).filter((word) => word !== "");
|
|
32
|
+
return (
|
|
33
|
+
words.length <= TITLE_MAX_WORDS &&
|
|
34
|
+
!notion.sentenceEnd.test(title) &&
|
|
35
|
+
(!notion.capitalised || words.every((word) => word.length <= TITLE_SMALL_WORD || !LOWER_START.test(word)))
|
|
36
|
+
);
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const isAloneBefore = (before: string, notion: TitleNotion): boolean =>
|
|
40
|
+
before === "" || BLANK_LINE.test(`\n${before}`) || (notion.edge === "any-whitespace" && before.trim() === "");
|
|
41
|
+
|
|
42
|
+
const isAloneAfter = (after: string, isLastLine: boolean, notion: TitleNotion): boolean =>
|
|
43
|
+
isLastLine || ENDS_LINE.test(after) || BLANK.test(after) || (notion.edge === "any-whitespace" && after.trim() === "");
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* The line from lineStart to lineEnd (-1: the last line) is a title: rest, the words after its label and number, are a
|
|
47
|
+
* short title, and a blank line or the document's edge is on both sides.
|
|
48
|
+
*/
|
|
49
|
+
export const isTitleLine = (source: string, lineStart: number, lineEnd: number, rest: string, notion: TitleNotion): boolean =>
|
|
50
|
+
isShortTitle(rest, notion) &&
|
|
51
|
+
isAloneBefore(source.slice(0, lineStart), notion) &&
|
|
52
|
+
isAloneAfter(lineEnd === -1 ? "" : source.slice(lineEnd + 1), lineEnd === -1, notion);
|