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.
Files changed (700) hide show
  1. package/README.ja.md +12 -0
  2. package/README.md +15 -0
  3. package/dist/announced-count.d.ts +2 -0
  4. package/dist/announced-count.d.ts.map +1 -1
  5. package/dist/announced-count.js +29 -7
  6. package/dist/announced-count.js.map +1 -1
  7. package/dist/chronology-rows.d.ts +9 -0
  8. package/dist/chronology-rows.d.ts.map +1 -0
  9. package/dist/chronology-rows.js +79 -0
  10. package/dist/chronology-rows.js.map +1 -0
  11. package/dist/company-name-run.d.ts +8 -0
  12. package/dist/company-name-run.d.ts.map +1 -0
  13. package/dist/company-name-run.js +30 -0
  14. package/dist/company-name-run.js.map +1 -0
  15. package/dist/compound-head.d.ts +7 -0
  16. package/dist/compound-head.d.ts.map +1 -0
  17. package/dist/compound-head.js +19 -0
  18. package/dist/compound-head.js.map +1 -0
  19. package/dist/date-format.d.ts +8 -3
  20. package/dist/date-format.d.ts.map +1 -1
  21. package/dist/date-format.js +36 -3
  22. package/dist/date-format.js.map +1 -1
  23. package/dist/derived/count-per-period.d.ts +23 -0
  24. package/dist/derived/count-per-period.d.ts.map +1 -0
  25. package/dist/derived/count-per-period.js +14 -0
  26. package/dist/derived/count-per-period.js.map +1 -0
  27. package/dist/detectors/acronym-context.d.ts +4 -0
  28. package/dist/detectors/acronym-context.d.ts.map +1 -1
  29. package/dist/detectors/acronym-context.js +29 -1
  30. package/dist/detectors/acronym-context.js.map +1 -1
  31. package/dist/detectors/acronym-expansion.d.ts +5 -1
  32. package/dist/detectors/acronym-expansion.d.ts.map +1 -1
  33. package/dist/detectors/acronym-expansion.js +35 -3
  34. package/dist/detectors/acronym-expansion.js.map +1 -1
  35. package/dist/detectors/acronym-known.d.ts +19 -0
  36. package/dist/detectors/acronym-known.d.ts.map +1 -0
  37. package/dist/detectors/acronym-known.js +32 -0
  38. package/dist/detectors/acronym-known.js.map +1 -0
  39. package/dist/detectors/acronym-link.d.ts +14 -0
  40. package/dist/detectors/acronym-link.d.ts.map +1 -0
  41. package/dist/detectors/acronym-link.js +32 -0
  42. package/dist/detectors/acronym-link.js.map +1 -0
  43. package/dist/detectors/acronym-name.d.ts +8 -1
  44. package/dist/detectors/acronym-name.d.ts.map +1 -1
  45. package/dist/detectors/acronym-name.js +23 -4
  46. package/dist/detectors/acronym-name.js.map +1 -1
  47. package/dist/detectors/addressee-edge.d.ts +13 -0
  48. package/dist/detectors/addressee-edge.d.ts.map +1 -0
  49. package/dist/detectors/addressee-edge.js +13 -0
  50. package/dist/detectors/addressee-edge.js.map +1 -0
  51. package/dist/detectors/announced-count.d.ts.map +1 -1
  52. package/dist/detectors/announced-count.js +1 -0
  53. package/dist/detectors/announced-count.js.map +1 -1
  54. package/dist/detectors/bare-comparative.d.ts +4 -0
  55. package/dist/detectors/bare-comparative.d.ts.map +1 -1
  56. package/dist/detectors/bare-comparative.js +28 -3
  57. package/dist/detectors/bare-comparative.js.map +1 -1
  58. package/dist/detectors/blank-structure.d.ts +7 -1
  59. package/dist/detectors/blank-structure.d.ts.map +1 -1
  60. package/dist/detectors/blank-structure.js +13 -8
  61. package/dist/detectors/blank-structure.js.map +1 -1
  62. package/dist/detectors/bracket-specified.d.ts +6 -0
  63. package/dist/detectors/bracket-specified.d.ts.map +1 -0
  64. package/dist/detectors/bracket-specified.js +23 -0
  65. package/dist/detectors/bracket-specified.js.map +1 -0
  66. package/dist/detectors/broken-link.d.ts +6 -0
  67. package/dist/detectors/broken-link.d.ts.map +1 -1
  68. package/dist/detectors/broken-link.js +23 -2
  69. package/dist/detectors/broken-link.js.map +1 -1
  70. package/dist/detectors/caps-caption.d.ts +7 -6
  71. package/dist/detectors/caps-caption.d.ts.map +1 -1
  72. package/dist/detectors/caps-caption.js +38 -0
  73. package/dist/detectors/caps-caption.js.map +1 -1
  74. package/dist/detectors/caps-phrase.d.ts +6 -0
  75. package/dist/detectors/caps-phrase.d.ts.map +1 -0
  76. package/dist/detectors/caps-phrase.js +16 -0
  77. package/dist/detectors/caps-phrase.js.map +1 -0
  78. package/dist/detectors/char-shape.d.ts +1 -0
  79. package/dist/detectors/char-shape.d.ts.map +1 -1
  80. package/dist/detectors/char-shape.js +29 -16
  81. package/dist/detectors/char-shape.js.map +1 -1
  82. package/dist/detectors/cited-title.d.ts +3 -2
  83. package/dist/detectors/cited-title.d.ts.map +1 -1
  84. package/dist/detectors/cited-title.js +14 -3
  85. package/dist/detectors/cited-title.js.map +1 -1
  86. package/dist/detectors/counter-edge.d.ts +2 -1
  87. package/dist/detectors/counter-edge.d.ts.map +1 -1
  88. package/dist/detectors/counter-edge.js +17 -2
  89. package/dist/detectors/counter-edge.js.map +1 -1
  90. package/dist/detectors/date-any-year.d.ts +7 -3
  91. package/dist/detectors/date-any-year.d.ts.map +1 -1
  92. package/dist/detectors/date-any-year.js +25 -5
  93. package/dist/detectors/date-any-year.js.map +1 -1
  94. package/dist/detectors/date-format.d.ts.map +1 -1
  95. package/dist/detectors/date-format.js +2 -1
  96. package/dist/detectors/date-format.js.map +1 -1
  97. package/dist/detectors/date-year.d.ts +9 -3
  98. package/dist/detectors/date-year.d.ts.map +1 -1
  99. package/dist/detectors/date-year.js +45 -9
  100. package/dist/detectors/date-year.js.map +1 -1
  101. package/dist/detectors/deadline-unit.d.ts +20 -0
  102. package/dist/detectors/deadline-unit.d.ts.map +1 -0
  103. package/dist/detectors/deadline-unit.js +19 -0
  104. package/dist/detectors/deadline-unit.js.map +1 -0
  105. package/dist/detectors/defined-term-form.d.ts.map +1 -1
  106. package/dist/detectors/defined-term-form.js +33 -7
  107. package/dist/detectors/defined-term-form.js.map +1 -1
  108. package/dist/detectors/definition-use.d.ts.map +1 -1
  109. package/dist/detectors/definition-use.js +6 -3
  110. package/dist/detectors/definition-use.js.map +1 -1
  111. package/dist/detectors/derived-numbers.d.ts.map +1 -1
  112. package/dist/detectors/derived-numbers.js +17 -4
  113. package/dist/detectors/derived-numbers.js.map +1 -1
  114. package/dist/detectors/doubled-across-aside.d.ts +9 -0
  115. package/dist/detectors/doubled-across-aside.d.ts.map +1 -0
  116. package/dist/detectors/doubled-across-aside.js +27 -0
  117. package/dist/detectors/doubled-across-aside.js.map +1 -0
  118. package/dist/detectors/doubled-punctuation.d.ts +3 -3
  119. package/dist/detectors/doubled-punctuation.d.ts.map +1 -1
  120. package/dist/detectors/doubled-punctuation.js +23 -4
  121. package/dist/detectors/doubled-punctuation.js.map +1 -1
  122. package/dist/detectors/doubled-word.d.ts.map +1 -1
  123. package/dist/detectors/doubled-word.js +2 -1
  124. package/dist/detectors/doubled-word.js.map +1 -1
  125. package/dist/detectors/ellipsis.d.ts.map +1 -1
  126. package/dist/detectors/ellipsis.js +13 -1
  127. package/dist/detectors/ellipsis.js.map +1 -1
  128. package/dist/detectors/email-letter.d.ts +31 -1
  129. package/dist/detectors/email-letter.d.ts.map +1 -1
  130. package/dist/detectors/email-letter.js +76 -14
  131. package/dist/detectors/email-letter.js.map +1 -1
  132. package/dist/detectors/field-label.d.ts +7 -0
  133. package/dist/detectors/field-label.d.ts.map +1 -0
  134. package/dist/detectors/field-label.js +23 -0
  135. package/dist/detectors/field-label.js.map +1 -0
  136. package/dist/detectors/footnote-mismatch.d.ts +6 -1
  137. package/dist/detectors/footnote-mismatch.d.ts.map +1 -1
  138. package/dist/detectors/footnote-mismatch.js +28 -7
  139. package/dist/detectors/footnote-mismatch.js.map +1 -1
  140. package/dist/detectors/graded-step.d.ts +20 -0
  141. package/dist/detectors/graded-step.d.ts.map +1 -0
  142. package/dist/detectors/graded-step.js +33 -0
  143. package/dist/detectors/graded-step.js.map +1 -0
  144. package/dist/detectors/gram-openers.d.ts +8 -0
  145. package/dist/detectors/gram-openers.d.ts.map +1 -0
  146. package/dist/detectors/gram-openers.js +16 -0
  147. package/dist/detectors/gram-openers.js.map +1 -0
  148. package/dist/detectors/gram-place.d.ts +3 -1
  149. package/dist/detectors/gram-place.d.ts.map +1 -1
  150. package/dist/detectors/gram-place.js +8 -0
  151. package/dist/detectors/gram-place.js.map +1 -1
  152. package/dist/detectors/gram-predicate.d.ts.map +1 -1
  153. package/dist/detectors/gram-predicate.js +37 -3
  154. package/dist/detectors/gram-predicate.js.map +1 -1
  155. package/dist/detectors/heading-echo.d.ts.map +1 -1
  156. package/dist/detectors/heading-echo.js +28 -2
  157. package/dist/detectors/heading-echo.js.map +1 -1
  158. package/dist/detectors/ime-residue.d.ts +1 -1
  159. package/dist/detectors/ime-residue.d.ts.map +1 -1
  160. package/dist/detectors/ime-residue.js +4 -3
  161. package/dist/detectors/ime-residue.js.map +1 -1
  162. package/dist/detectors/kanji-compound-slip.d.ts +3 -0
  163. package/dist/detectors/kanji-compound-slip.d.ts.map +1 -0
  164. package/dist/detectors/kanji-compound-slip.js +50 -0
  165. package/dist/detectors/kanji-compound-slip.js.map +1 -0
  166. package/dist/detectors/kutoten-consistency.d.ts +10 -2
  167. package/dist/detectors/kutoten-consistency.d.ts.map +1 -1
  168. package/dist/detectors/kutoten-consistency.js +76 -12
  169. package/dist/detectors/kutoten-consistency.js.map +1 -1
  170. package/dist/detectors/lexicon.d.ts.map +1 -1
  171. package/dist/detectors/lexicon.js +8 -2
  172. package/dist/detectors/lexicon.js.map +1 -1
  173. package/dist/detectors/link-shape.d.ts.map +1 -1
  174. package/dist/detectors/link-shape.js +9 -3
  175. package/dist/detectors/link-shape.js.map +1 -1
  176. package/dist/detectors/list-item-ending.d.ts +18 -0
  177. package/dist/detectors/list-item-ending.d.ts.map +1 -0
  178. package/dist/detectors/list-item-ending.js +35 -0
  179. package/dist/detectors/list-item-ending.js.map +1 -0
  180. package/dist/detectors/list-parallel.d.ts +18 -5
  181. package/dist/detectors/list-parallel.d.ts.map +1 -1
  182. package/dist/detectors/list-parallel.js +116 -36
  183. package/dist/detectors/list-parallel.js.map +1 -1
  184. package/dist/detectors/list-sentence.d.ts +4 -0
  185. package/dist/detectors/list-sentence.d.ts.map +1 -1
  186. package/dist/detectors/list-sentence.js +1 -1
  187. package/dist/detectors/list-sentence.js.map +1 -1
  188. package/dist/detectors/mark-shape.d.ts +11 -4
  189. package/dist/detectors/mark-shape.d.ts.map +1 -1
  190. package/dist/detectors/mark-shape.js +58 -10
  191. package/dist/detectors/mark-shape.js.map +1 -1
  192. package/dist/detectors/mentioned-phrase.d.ts +9 -0
  193. package/dist/detectors/mentioned-phrase.d.ts.map +1 -0
  194. package/dist/detectors/mentioned-phrase.js +25 -0
  195. package/dist/detectors/mentioned-phrase.js.map +1 -0
  196. package/dist/detectors/middle-dot.d.ts +2 -2
  197. package/dist/detectors/middle-dot.d.ts.map +1 -1
  198. package/dist/detectors/middle-dot.js +4 -2
  199. package/dist/detectors/middle-dot.js.map +1 -1
  200. package/dist/detectors/modal-conflict.d.ts.map +1 -1
  201. package/dist/detectors/modal-conflict.js +5 -2
  202. package/dist/detectors/modal-conflict.js.map +1 -1
  203. package/dist/detectors/notice-paragraph.d.ts +9 -0
  204. package/dist/detectors/notice-paragraph.d.ts.map +1 -0
  205. package/dist/detectors/notice-paragraph.js +21 -0
  206. package/dist/detectors/notice-paragraph.js.map +1 -0
  207. package/dist/detectors/number-style.d.ts.map +1 -1
  208. package/dist/detectors/number-style.js +15 -2
  209. package/dist/detectors/number-style.js.map +1 -1
  210. package/dist/detectors/number-unit.d.ts.map +1 -1
  211. package/dist/detectors/number-unit.js +24 -1
  212. package/dist/detectors/number-unit.js.map +1 -1
  213. package/dist/detectors/numeral-opener.d.ts +6 -0
  214. package/dist/detectors/numeral-opener.d.ts.map +1 -1
  215. package/dist/detectors/numeral-opener.js +24 -1
  216. package/dist/detectors/numeral-opener.js.map +1 -1
  217. package/dist/detectors/obligation-without-deadline.d.ts +5 -1
  218. package/dist/detectors/obligation-without-deadline.d.ts.map +1 -1
  219. package/dist/detectors/obligation-without-deadline.js +44 -23
  220. package/dist/detectors/obligation-without-deadline.js.map +1 -1
  221. package/dist/detectors/open-end-class.d.ts +16 -0
  222. package/dist/detectors/open-end-class.d.ts.map +1 -0
  223. package/dist/detectors/open-end-class.js +65 -0
  224. package/dist/detectors/open-end-class.js.map +1 -0
  225. package/dist/detectors/open-end-condition.d.ts +7 -0
  226. package/dist/detectors/open-end-condition.d.ts.map +1 -0
  227. package/dist/detectors/open-end-condition.js +35 -0
  228. package/dist/detectors/open-end-condition.js.map +1 -0
  229. package/dist/detectors/open-end-names.d.ts +9 -3
  230. package/dist/detectors/open-end-names.d.ts.map +1 -1
  231. package/dist/detectors/open-end-names.js +5 -2
  232. package/dist/detectors/open-end-names.js.map +1 -1
  233. package/dist/detectors/open-end-question.d.ts +15 -0
  234. package/dist/detectors/open-end-question.d.ts.map +1 -0
  235. package/dist/detectors/open-end-question.js +37 -0
  236. package/dist/detectors/open-end-question.js.map +1 -0
  237. package/dist/detectors/open-end-scope.d.ts +16 -0
  238. package/dist/detectors/open-end-scope.d.ts.map +1 -0
  239. package/dist/detectors/open-end-scope.js +47 -0
  240. package/dist/detectors/open-end-scope.js.map +1 -0
  241. package/dist/detectors/opening-date.d.ts +10 -0
  242. package/dist/detectors/opening-date.d.ts.map +1 -0
  243. package/dist/detectors/opening-date.js +33 -0
  244. package/dist/detectors/opening-date.js.map +1 -0
  245. package/dist/detectors/ordinary-phrase.d.ts +20 -0
  246. package/dist/detectors/ordinary-phrase.d.ts.map +1 -0
  247. package/dist/detectors/ordinary-phrase.js +54 -0
  248. package/dist/detectors/ordinary-phrase.js.map +1 -0
  249. package/dist/detectors/organisation-run.d.ts +5 -1
  250. package/dist/detectors/organisation-run.d.ts.map +1 -1
  251. package/dist/detectors/organisation-run.js +7 -0
  252. package/dist/detectors/organisation-run.js.map +1 -1
  253. package/dist/detectors/orthographic-variant.d.ts.map +1 -1
  254. package/dist/detectors/orthographic-variant.js +56 -20
  255. package/dist/detectors/orthographic-variant.js.map +1 -1
  256. package/dist/detectors/orthography.d.ts.map +1 -1
  257. package/dist/detectors/orthography.js +6 -1
  258. package/dist/detectors/orthography.js.map +1 -1
  259. package/dist/detectors/oxford-comma.d.ts.map +1 -1
  260. package/dist/detectors/oxford-comma.js +38 -1
  261. package/dist/detectors/oxford-comma.js.map +1 -1
  262. package/dist/detectors/person-consistency.d.ts +2 -1
  263. package/dist/detectors/person-consistency.d.ts.map +1 -1
  264. package/dist/detectors/person-consistency.js +5 -3
  265. package/dist/detectors/person-consistency.js.map +1 -1
  266. package/dist/detectors/phrase-match.d.ts +2 -0
  267. package/dist/detectors/phrase-match.d.ts.map +1 -1
  268. package/dist/detectors/phrase-match.js +11 -4
  269. package/dist/detectors/phrase-match.js.map +1 -1
  270. package/dist/detectors/placeholder-text.d.ts.map +1 -1
  271. package/dist/detectors/placeholder-text.js +18 -3
  272. package/dist/detectors/placeholder-text.js.map +1 -1
  273. package/dist/detectors/placeholder.d.ts +3 -1
  274. package/dist/detectors/placeholder.d.ts.map +1 -1
  275. package/dist/detectors/placeholder.js +8 -3
  276. package/dist/detectors/placeholder.js.map +1 -1
  277. package/dist/detectors/postcode-end.d.ts +3 -0
  278. package/dist/detectors/postcode-end.d.ts.map +1 -0
  279. package/dist/detectors/postcode-end.js +15 -0
  280. package/dist/detectors/postcode-end.js.map +1 -0
  281. package/dist/detectors/prose-total-words.d.ts +5 -0
  282. package/dist/detectors/prose-total-words.d.ts.map +1 -0
  283. package/dist/detectors/prose-total-words.js +14 -0
  284. package/dist/detectors/prose-total-words.js.map +1 -0
  285. package/dist/detectors/registry/kanji-compound-slip.d.ts +3 -0
  286. package/dist/detectors/registry/kanji-compound-slip.d.ts.map +1 -0
  287. package/dist/detectors/registry/kanji-compound-slip.js +3 -0
  288. package/dist/detectors/registry/kanji-compound-slip.js.map +1 -0
  289. package/dist/detectors/registry/phrase-in-use.d.ts +3 -0
  290. package/dist/detectors/registry/phrase-in-use.d.ts.map +1 -0
  291. package/dist/detectors/registry/phrase-in-use.js +3 -0
  292. package/dist/detectors/registry/phrase-in-use.js.map +1 -0
  293. package/dist/detectors/requirement-smell.d.ts.map +1 -1
  294. package/dist/detectors/requirement-smell.js +29 -5
  295. package/dist/detectors/requirement-smell.js.map +1 -1
  296. package/dist/detectors/scaled-figure-unit.d.ts +28 -0
  297. package/dist/detectors/scaled-figure-unit.d.ts.map +1 -0
  298. package/dist/detectors/scaled-figure-unit.js +78 -0
  299. package/dist/detectors/scaled-figure-unit.js.map +1 -0
  300. package/dist/detectors/sentence-counts.d.ts +2 -0
  301. package/dist/detectors/sentence-counts.d.ts.map +1 -1
  302. package/dist/detectors/sentence-counts.js +18 -7
  303. package/dist/detectors/sentence-counts.js.map +1 -1
  304. package/dist/detectors/sentence-ending.d.ts +2 -0
  305. package/dist/detectors/sentence-ending.d.ts.map +1 -1
  306. package/dist/detectors/sentence-ending.js +10 -3
  307. package/dist/detectors/sentence-ending.js.map +1 -1
  308. package/dist/detectors/sentence-load.d.ts +4 -1
  309. package/dist/detectors/sentence-load.d.ts.map +1 -1
  310. package/dist/detectors/sentence-load.js +9 -2
  311. package/dist/detectors/sentence-load.js.map +1 -1
  312. package/dist/detectors/signals.d.ts.map +1 -1
  313. package/dist/detectors/signals.js +30 -50
  314. package/dist/detectors/signals.js.map +1 -1
  315. package/dist/detectors/structure-tree.d.ts.map +1 -1
  316. package/dist/detectors/structure-tree.js +14 -13
  317. package/dist/detectors/structure-tree.js.map +1 -1
  318. package/dist/detectors/structure.d.ts.map +1 -1
  319. package/dist/detectors/structure.js +2 -1
  320. package/dist/detectors/structure.js.map +1 -1
  321. package/dist/detectors/subject-name-line.d.ts +8 -0
  322. package/dist/detectors/subject-name-line.d.ts.map +1 -0
  323. package/dist/detectors/subject-name-line.js +53 -0
  324. package/dist/detectors/subject-name-line.js.map +1 -0
  325. package/dist/detectors/table-header.d.ts.map +1 -1
  326. package/dist/detectors/table-header.js +13 -1
  327. package/dist/detectors/table-header.js.map +1 -1
  328. package/dist/detectors/technical-docs.d.ts.map +1 -1
  329. package/dist/detectors/technical-docs.js +4 -1
  330. package/dist/detectors/technical-docs.js.map +1 -1
  331. package/dist/detectors/term-mention-sentence.d.ts +4 -0
  332. package/dist/detectors/term-mention-sentence.d.ts.map +1 -0
  333. package/dist/detectors/term-mention-sentence.js +28 -0
  334. package/dist/detectors/term-mention-sentence.js.map +1 -0
  335. package/dist/detectors/titled-run.d.ts +2 -1
  336. package/dist/detectors/titled-run.d.ts.map +1 -1
  337. package/dist/detectors/titled-run.js +9 -4
  338. package/dist/detectors/titled-run.js.map +1 -1
  339. package/dist/detectors/token-shape.d.ts.map +1 -1
  340. package/dist/detectors/token-shape.js +13 -5
  341. package/dist/detectors/token-shape.js.map +1 -1
  342. package/dist/detectors/vague-density.d.ts +5 -2
  343. package/dist/detectors/vague-density.d.ts.map +1 -1
  344. package/dist/detectors/vague-density.js +17 -4
  345. package/dist/detectors/vague-density.js.map +1 -1
  346. package/dist/document-paragraphs.d.ts +4 -0
  347. package/dist/document-paragraphs.d.ts.map +1 -0
  348. package/dist/document-paragraphs.js +24 -0
  349. package/dist/document-paragraphs.js.map +1 -0
  350. package/dist/document.d.ts.map +1 -1
  351. package/dist/document.js +2 -19
  352. package/dist/document.js.map +1 -1
  353. package/dist/figure-references.d.ts +3 -0
  354. package/dist/figure-references.d.ts.map +1 -1
  355. package/dist/figure-references.js +6 -2
  356. package/dist/figure-references.js.map +1 -1
  357. package/dist/format-template.d.ts +3 -0
  358. package/dist/format-template.d.ts.map +1 -0
  359. package/dist/format-template.js +13 -0
  360. package/dist/format-template.js.map +1 -0
  361. package/dist/formula-brackets.d.ts +7 -0
  362. package/dist/formula-brackets.d.ts.map +1 -0
  363. package/dist/formula-brackets.js +29 -0
  364. package/dist/formula-brackets.js.map +1 -0
  365. package/dist/html/html-markdown.d.ts.map +1 -1
  366. package/dist/html/html-markdown.js +2 -1
  367. package/dist/html/html-markdown.js.map +1 -1
  368. package/dist/html/html-site-chrome.d.ts +5 -0
  369. package/dist/html/html-site-chrome.d.ts.map +1 -0
  370. package/dist/html/html-site-chrome.js +33 -0
  371. package/dist/html/html-site-chrome.js.map +1 -0
  372. package/dist/joined-view.d.ts.map +1 -1
  373. package/dist/joined-view.js +1 -0
  374. package/dist/joined-view.js.map +1 -1
  375. package/dist/kanji-compound-slip.d.ts +32 -0
  376. package/dist/kanji-compound-slip.d.ts.map +1 -0
  377. package/dist/kanji-compound-slip.js +63 -0
  378. package/dist/kanji-compound-slip.js.map +1 -0
  379. package/dist/latin-company-form.d.ts +6 -0
  380. package/dist/latin-company-form.d.ts.map +1 -0
  381. package/dist/latin-company-form.js +32 -0
  382. package/dist/latin-company-form.js.map +1 -0
  383. package/dist/latin-word-boundary.d.ts +15 -0
  384. package/dist/latin-word-boundary.d.ts.map +1 -0
  385. package/dist/latin-word-boundary.js +40 -0
  386. package/dist/latin-word-boundary.js.map +1 -0
  387. package/dist/lexicon-genres.d.ts +2 -0
  388. package/dist/lexicon-genres.d.ts.map +1 -1
  389. package/dist/lexicon-genres.js +1 -1
  390. package/dist/lexicon-genres.js.map +1 -1
  391. package/dist/link-shape.d.ts +5 -0
  392. package/dist/link-shape.d.ts.map +1 -1
  393. package/dist/link-shape.js +18 -0
  394. package/dist/link-shape.js.map +1 -1
  395. package/dist/link-title.d.ts +4 -0
  396. package/dist/link-title.d.ts.map +1 -0
  397. package/dist/link-title.js +33 -0
  398. package/dist/link-title.js.map +1 -0
  399. package/dist/list-item-paragraphs.d.ts +15 -0
  400. package/dist/list-item-paragraphs.d.ts.map +1 -0
  401. package/dist/list-item-paragraphs.js +19 -0
  402. package/dist/list-item-paragraphs.js.map +1 -0
  403. package/dist/literal-quote.d.ts +12 -0
  404. package/dist/literal-quote.d.ts.map +1 -0
  405. package/dist/literal-quote.js +43 -0
  406. package/dist/literal-quote.js.map +1 -0
  407. package/dist/nado-closed-list.d.ts.map +1 -1
  408. package/dist/nado-closed-list.js +39 -5
  409. package/dist/nado-closed-list.js.map +1 -1
  410. package/dist/name-separator.d.ts.map +1 -1
  411. package/dist/name-separator.js +25 -2
  412. package/dist/name-separator.js.map +1 -1
  413. package/dist/nominal-stem.d.ts +14 -1
  414. package/dist/nominal-stem.d.ts.map +1 -1
  415. package/dist/nominal-stem.js +42 -4
  416. package/dist/nominal-stem.js.map +1 -1
  417. package/dist/person-forms.d.ts +8 -0
  418. package/dist/person-forms.d.ts.map +1 -1
  419. package/dist/person-forms.js +12 -0
  420. package/dist/person-forms.js.map +1 -1
  421. package/dist/plugin.d.ts +5 -0
  422. package/dist/plugin.d.ts.map +1 -1
  423. package/dist/preamble-paragraphs.d.ts +3 -2
  424. package/dist/preamble-paragraphs.d.ts.map +1 -1
  425. package/dist/preamble-paragraphs.js +8 -2
  426. package/dist/preamble-paragraphs.js.map +1 -1
  427. package/dist/spacing-code.d.ts +1 -0
  428. package/dist/spacing-code.d.ts.map +1 -1
  429. package/dist/spacing-code.js +4 -1
  430. package/dist/spacing-code.js.map +1 -1
  431. package/dist/split-noun.d.ts +10 -0
  432. package/dist/split-noun.d.ts.map +1 -0
  433. package/dist/split-noun.js +50 -0
  434. package/dist/split-noun.js.map +1 -0
  435. package/dist/structure/attached-parts.d.ts +11 -0
  436. package/dist/structure/attached-parts.d.ts.map +1 -0
  437. package/dist/structure/attached-parts.js +31 -0
  438. package/dist/structure/attached-parts.js.map +1 -0
  439. package/dist/structure/build.d.ts.map +1 -1
  440. package/dist/structure/build.js +7 -4
  441. package/dist/structure/build.js.map +1 -1
  442. package/dist/structure/contents-lines.d.ts +7 -0
  443. package/dist/structure/contents-lines.d.ts.map +1 -0
  444. package/dist/structure/contents-lines.js +51 -0
  445. package/dist/structure/contents-lines.js.map +1 -0
  446. package/dist/structure/defining-names.d.ts +37 -0
  447. package/dist/structure/defining-names.d.ts.map +1 -0
  448. package/dist/structure/defining-names.js +122 -0
  449. package/dist/structure/defining-names.js.map +1 -0
  450. package/dist/structure/definition-use.d.ts +3 -2
  451. package/dist/structure/definition-use.d.ts.map +1 -1
  452. package/dist/structure/definition-use.js +99 -31
  453. package/dist/structure/definition-use.js.map +1 -1
  454. package/dist/structure/issues.d.ts +4 -1
  455. package/dist/structure/issues.d.ts.map +1 -1
  456. package/dist/structure/issues.js +5 -2
  457. package/dist/structure/issues.js.map +1 -1
  458. package/dist/structure/list-lead-in.d.ts +19 -0
  459. package/dist/structure/list-lead-in.d.ts.map +1 -0
  460. package/dist/structure/list-lead-in.js +85 -0
  461. package/dist/structure/list-lead-in.js.map +1 -0
  462. package/dist/structure/modal-conflict.d.ts +7 -2
  463. package/dist/structure/modal-conflict.d.ts.map +1 -1
  464. package/dist/structure/modal-conflict.js +36 -7
  465. package/dist/structure/modal-conflict.js.map +1 -1
  466. package/dist/structure/negated-act.d.ts +17 -0
  467. package/dist/structure/negated-act.d.ts.map +1 -0
  468. package/dist/structure/negated-act.js +32 -0
  469. package/dist/structure/negated-act.js.map +1 -0
  470. package/dist/structure/numbering.d.ts +3 -1
  471. package/dist/structure/numbering.d.ts.map +1 -1
  472. package/dist/structure/numbering.js +20 -8
  473. package/dist/structure/numbering.js.map +1 -1
  474. package/dist/structure/prose-total.d.ts +4 -0
  475. package/dist/structure/prose-total.d.ts.map +1 -1
  476. package/dist/structure/prose-total.js +16 -6
  477. package/dist/structure/prose-total.js.map +1 -1
  478. package/dist/structure/repeated-number.d.ts +24 -0
  479. package/dist/structure/repeated-number.d.ts.map +1 -0
  480. package/dist/structure/repeated-number.js +89 -0
  481. package/dist/structure/repeated-number.js.map +1 -0
  482. package/dist/structure/rounded-amount.d.ts +8 -0
  483. package/dist/structure/rounded-amount.d.ts.map +1 -0
  484. package/dist/structure/rounded-amount.js +48 -0
  485. package/dist/structure/rounded-amount.js.map +1 -0
  486. package/dist/structure/side-step.d.ts +8 -0
  487. package/dist/structure/side-step.d.ts.map +1 -0
  488. package/dist/structure/side-step.js +21 -0
  489. package/dist/structure/side-step.js.map +1 -0
  490. package/dist/structure/spanned-label-blank.d.ts +8 -0
  491. package/dist/structure/spanned-label-blank.d.ts.map +1 -0
  492. package/dist/structure/spanned-label-blank.js +37 -0
  493. package/dist/structure/spanned-label-blank.js.map +1 -0
  494. package/dist/structure/universal.d.ts +3 -1
  495. package/dist/structure/universal.d.ts.map +1 -1
  496. package/dist/structure/universal.js +9 -2
  497. package/dist/structure/universal.js.map +1 -1
  498. package/dist/structure/unspaced-heading.d.ts +10 -0
  499. package/dist/structure/unspaced-heading.d.ts.map +1 -0
  500. package/dist/structure/unspaced-heading.js +24 -0
  501. package/dist/structure/unspaced-heading.js.map +1 -0
  502. package/dist/structure/written-list-path.d.ts +10 -0
  503. package/dist/structure/written-list-path.d.ts.map +1 -0
  504. package/dist/structure/written-list-path.js +37 -0
  505. package/dist/structure/written-list-path.js.map +1 -0
  506. package/dist/structure/written-resolve.d.ts +1 -1
  507. package/dist/structure/written-resolve.d.ts.map +1 -1
  508. package/dist/structure/written-resolve.js +15 -9
  509. package/dist/structure/written-resolve.js.map +1 -1
  510. package/dist/title-line.d.ts +21 -0
  511. package/dist/title-line.d.ts.map +1 -0
  512. package/dist/title-line.js +27 -0
  513. package/dist/title-line.js.map +1 -0
  514. package/package.json +3 -3
  515. package/rules/all-caps-shouting.yaml +2 -2
  516. package/rules/announced-count-mismatch.yaml +2 -2
  517. package/rules/bare-url-mix.yaml +2 -2
  518. package/rules/bracket-nesting.yaml +2 -2
  519. package/rules/bracket-width-consistency.yaml +2 -2
  520. package/rules/broken-link.yaml +2 -2
  521. package/rules/cliche.yaml +4 -3
  522. package/rules/code-fence-language-mix.yaml +2 -2
  523. package/rules/comparative-without-baseline.yaml +2 -2
  524. package/rules/dangling-figure-reference.yaml +2 -2
  525. package/rules/date-without-year.yaml +10 -2
  526. package/rules/defined-term-form.yaml +2 -2
  527. package/rules/doubled-punctuation.yaml +4 -4
  528. package/rules/doubled-word.yaml +2 -2
  529. package/rules/duplicate-definition.yaml +2 -2
  530. package/rules/duration-mismatch.yaml +2 -2
  531. package/rules/email-greeting-closing.yaml +2 -2
  532. package/rules/email-subject-length.yaml +2 -2
  533. package/rules/empty-table-cell.yaml +2 -2
  534. package/rules/footnote-mismatch.yaml +2 -2
  535. package/rules/heading-echo.yaml +2 -2
  536. package/rules/ime-residue.yaml +2 -2
  537. package/rules/kanji-compound-slip.yaml +67 -0
  538. package/rules/kutoten-consistency.yaml +4 -4
  539. package/rules/latin-spacing.yaml +2 -2
  540. package/rules/list-item-form-mix.yaml +2 -2
  541. package/rules/max-kanji-continuous.yaml +6 -4
  542. package/rules/modal-conflict.yaml +1 -0
  543. package/rules/nado-closed-list.yaml +2 -2
  544. package/rules/ngram-repetition.yaml +2 -2
  545. package/rules/no-doubled-joshi.yaml +2 -2
  546. package/rules/no-mixed-desumasu.yaml +3 -3
  547. package/rules/no-nakaguro-parallel.yaml +2 -0
  548. package/rules/number-without-unit.yaml +9 -4
  549. package/rules/numbering-gap.yaml +7 -2
  550. package/rules/numeral-sentence-start.yaml +2 -2
  551. package/rules/obligation-without-deadline.yaml +2 -2
  552. package/rules/orthographic-variant.yaml +2 -2
  553. package/rules/oxford-comma-consistency.yaml +3 -3
  554. package/rules/preamble-length.yaml +2 -2
  555. package/rules/requirement-smell.yaml +3 -3
  556. package/rules/table-header-variant.yaml +2 -2
  557. package/rules/teki-overuse.yaml +3 -2
  558. package/rules/total-mismatch.yaml +2 -2
  559. package/rules/translationese-density.yaml +2 -2
  560. package/rules/undefined-acronym.yaml +4 -2
  561. package/rules/unfilled-placeholder.yaml +4 -4
  562. package/rules/use-before-definition.yaml +2 -2
  563. package/rules/vague-word-density.yaml +2 -2
  564. package/skills/chaff/SKILL.md +7 -1
  565. package/src/announced-count.ts +35 -8
  566. package/src/chronology-rows.ts +86 -0
  567. package/src/company-name-run.ts +37 -0
  568. package/src/compound-head.ts +25 -0
  569. package/src/date-format.ts +43 -4
  570. package/src/derived/count-per-period.ts +28 -0
  571. package/src/detectors/acronym-context.ts +30 -1
  572. package/src/detectors/acronym-expansion.ts +47 -6
  573. package/src/detectors/acronym-known.ts +40 -0
  574. package/src/detectors/acronym-link.ts +37 -0
  575. package/src/detectors/acronym-name.ts +27 -4
  576. package/src/detectors/addressee-edge.ts +16 -0
  577. package/src/detectors/announced-count.ts +1 -0
  578. package/src/detectors/bare-comparative.ts +44 -3
  579. package/src/detectors/blank-structure.ts +21 -10
  580. package/src/detectors/bracket-specified.ts +25 -0
  581. package/src/detectors/broken-link.ts +26 -2
  582. package/src/detectors/caps-caption.ts +42 -0
  583. package/src/detectors/caps-phrase.ts +17 -0
  584. package/src/detectors/char-shape.ts +30 -9
  585. package/src/detectors/cited-title.ts +15 -3
  586. package/src/detectors/counter-edge.ts +17 -2
  587. package/src/detectors/date-any-year.ts +27 -6
  588. package/src/detectors/date-format.ts +2 -1
  589. package/src/detectors/date-year.ts +64 -16
  590. package/src/detectors/deadline-unit.ts +40 -0
  591. package/src/detectors/defined-term-form.ts +32 -12
  592. package/src/detectors/definition-use.ts +7 -3
  593. package/src/detectors/derived-numbers.ts +19 -4
  594. package/src/detectors/doubled-across-aside.ts +36 -0
  595. package/src/detectors/doubled-punctuation.ts +28 -7
  596. package/src/detectors/doubled-word.ts +2 -1
  597. package/src/detectors/ellipsis.ts +16 -1
  598. package/src/detectors/email-letter.ts +97 -21
  599. package/src/detectors/field-label.ts +25 -0
  600. package/src/detectors/footnote-mismatch.ts +39 -7
  601. package/src/detectors/graded-step.ts +52 -0
  602. package/src/detectors/gram-openers.ts +21 -0
  603. package/src/detectors/gram-place.ts +10 -1
  604. package/src/detectors/gram-predicate.ts +43 -3
  605. package/src/detectors/heading-echo.ts +31 -3
  606. package/src/detectors/ime-residue.ts +4 -3
  607. package/src/detectors/kanji-compound-slip.ts +61 -0
  608. package/src/detectors/kutoten-consistency.ts +93 -14
  609. package/src/detectors/lexicon.ts +12 -3
  610. package/src/detectors/link-shape.ts +10 -3
  611. package/src/detectors/list-item-ending.ts +51 -0
  612. package/src/detectors/list-parallel.ts +158 -33
  613. package/src/detectors/list-sentence.ts +5 -1
  614. package/src/detectors/mark-shape.ts +64 -10
  615. package/src/detectors/mentioned-phrase.ts +38 -0
  616. package/src/detectors/middle-dot.ts +7 -2
  617. package/src/detectors/modal-conflict.ts +5 -2
  618. package/src/detectors/notice-paragraph.ts +33 -0
  619. package/src/detectors/number-style.ts +14 -2
  620. package/src/detectors/number-unit.ts +28 -1
  621. package/src/detectors/numeral-opener.ts +27 -1
  622. package/src/detectors/obligation-without-deadline.ts +29 -4
  623. package/src/detectors/open-end-class.ts +78 -0
  624. package/src/detectors/open-end-condition.ts +40 -0
  625. package/src/detectors/open-end-names.ts +9 -3
  626. package/src/detectors/open-end-question.ts +50 -0
  627. package/src/detectors/open-end-scope.ts +62 -0
  628. package/src/detectors/opening-date.ts +47 -0
  629. package/src/detectors/ordinary-phrase.ts +62 -0
  630. package/src/detectors/organisation-run.ts +13 -2
  631. package/src/detectors/orthographic-variant.ts +85 -25
  632. package/src/detectors/orthography.ts +10 -1
  633. package/src/detectors/oxford-comma.ts +40 -1
  634. package/src/detectors/person-consistency.ts +5 -3
  635. package/src/detectors/phrase-match.ts +16 -5
  636. package/src/detectors/placeholder-text.ts +18 -3
  637. package/src/detectors/placeholder.ts +10 -3
  638. package/src/detectors/postcode-end.ts +16 -0
  639. package/src/detectors/prose-total-words.ts +18 -0
  640. package/src/detectors/registry/kanji-compound-slip.ts +4 -0
  641. package/src/detectors/registry/phrase-in-use.ts +4 -0
  642. package/src/detectors/requirement-smell.ts +38 -5
  643. package/src/detectors/scaled-figure-unit.ts +103 -0
  644. package/src/detectors/sentence-counts.ts +24 -8
  645. package/src/detectors/sentence-ending.ts +12 -3
  646. package/src/detectors/sentence-load.ts +9 -2
  647. package/src/detectors/signals.ts +33 -51
  648. package/src/detectors/structure-tree.ts +14 -14
  649. package/src/detectors/structure.ts +2 -1
  650. package/src/detectors/subject-name-line.ts +66 -0
  651. package/src/detectors/table-header.ts +15 -1
  652. package/src/detectors/technical-docs.ts +5 -1
  653. package/src/detectors/term-mention-sentence.ts +31 -0
  654. package/src/detectors/titled-run.ts +9 -4
  655. package/src/detectors/token-shape.ts +14 -5
  656. package/src/detectors/vague-density.ts +20 -4
  657. package/src/document-paragraphs.ts +27 -0
  658. package/src/document.ts +3 -23
  659. package/src/figure-references.ts +7 -2
  660. package/src/format-template.ts +15 -0
  661. package/src/formula-brackets.ts +36 -0
  662. package/src/html/html-markdown.ts +2 -1
  663. package/src/html/html-site-chrome.ts +47 -0
  664. package/src/joined-view.ts +1 -0
  665. package/src/kanji-compound-slip.ts +98 -0
  666. package/src/latin-company-form.ts +38 -0
  667. package/src/latin-word-boundary.ts +46 -0
  668. package/src/lexicon-genres.ts +1 -1
  669. package/src/link-shape.ts +19 -0
  670. package/src/link-title.ts +38 -0
  671. package/src/list-item-paragraphs.ts +22 -0
  672. package/src/literal-quote.ts +55 -0
  673. package/src/nado-closed-list.ts +48 -5
  674. package/src/name-separator.ts +27 -2
  675. package/src/nominal-stem.ts +62 -5
  676. package/src/person-forms.ts +15 -0
  677. package/src/plugin.ts +5 -0
  678. package/src/preamble-paragraphs.ts +13 -3
  679. package/src/spacing-code.ts +5 -1
  680. package/src/split-noun.ts +63 -0
  681. package/src/structure/attached-parts.ts +32 -0
  682. package/src/structure/build.ts +7 -4
  683. package/src/structure/contents-lines.ts +63 -0
  684. package/src/structure/defining-names.ts +155 -0
  685. package/src/structure/definition-use.ts +116 -25
  686. package/src/structure/issues.ts +7 -3
  687. package/src/structure/list-lead-in.ts +94 -0
  688. package/src/structure/modal-conflict.ts +42 -7
  689. package/src/structure/negated-act.ts +49 -0
  690. package/src/structure/numbering.ts +24 -9
  691. package/src/structure/prose-total.ts +20 -6
  692. package/src/structure/repeated-number.ts +110 -0
  693. package/src/structure/rounded-amount.ts +55 -0
  694. package/src/structure/side-step.ts +21 -0
  695. package/src/structure/spanned-label-blank.ts +39 -0
  696. package/src/structure/universal.ts +11 -2
  697. package/src/structure/unspaced-heading.ts +27 -0
  698. package/src/structure/written-list-path.ts +46 -0
  699. package/src/structure/written-resolve.ts +16 -9
  700. package/src/title-line.ts +52 -0
@@ -0,0 +1,16 @@
1
+ import type { Token } from "../plugin.ts";
2
+
3
+ /** 連なりの尻の、宛先に付く語。surface はその語、start は文書全体の座標でその語の始まり。 */
4
+ export type AddresseeSuffix = { readonly surface: string; readonly start: number };
5
+
6
+ /**
7
+ * 漢字の連なり run(文書全体の座標で end に終わる)の尻が、宛先の名前に付く語(志木市役所市政情報課宛ての「宛」、御中)なら、
8
+ * その語。その語は名前の後ろに付くもので、名前の一部ではない。語の切れ目で始まるときだけ(宛ての語の頭が「宛」)。
9
+ * 折り返しをまたぐ語(御\n中)は span が surface より長いので、end から字数を引かず、end の直前の字を覆う語の始まりを使う。
10
+ */
11
+ export const trailingAddressee = (tokens: readonly Token[], end: number, run: string, suffixes: readonly string[]): AddresseeSuffix | undefined => {
12
+ const suffix = suffixes.find((word) => word !== "" && run.length > word.length && run.endsWith(word));
13
+ if (suffix === undefined) return undefined;
14
+ const token = tokens.find((candidate) => candidate.span.start < end && end <= candidate.span.end);
15
+ return token !== undefined && token.surface.startsWith(suffix) ? { surface: suffix, start: token.span.start } : undefined;
16
+ };
@@ -13,6 +13,7 @@ const countWordsOf = (doc: ProseDocument): CountWords => ({
13
13
  frames: patternsOf(doc, "count-frame"),
14
14
  hedgesBefore: patternsOf(doc, "count-hedge", "before"),
15
15
  hedgesAfter: patternsOf(doc, "count-hedge", "after"),
16
+ titles: patternsOf(doc, "count-title"),
16
17
  });
17
18
 
18
19
  const memberGroup = (doc: ProseDocument, group: string): string[] =>
@@ -1,12 +1,15 @@
1
1
  import type { Detector, Finding, Lexicon, ProseDocument, Sentence, Span, Token } from "../plugin.ts";
2
2
  import { quotedIn } from "../quoted-span.ts";
3
3
  import { followsInlineCode } from "./code-before.ts";
4
+ import { isNamedStep, plainStepKeys } from "./graded-step.ts";
4
5
 
5
6
  /**
6
7
  * 比べる相手の無い比較(より良い結果、さらに高速に、The new engine is faster.)。何より良いのか、何より速いのかが書かれていない。
7
8
  * 語は語彙表 comparative-baseline が言う。group marker は日本語の比べる副詞(より、さらに)、more は英語の比べる語、comparative は比べた形の語
8
9
  * (faster、better)、baseline は相手を言う語(従来、以前、than、previous)、exempt は比較でない決まった言い方(より多くの、より詳しく)、
9
10
  * condition は比較を条件や仮定の下に置く語(使うと、if、would)。条件を書いた比較は、その条件の無い場合と比べている。
11
+ * beyond は比べた形のすぐ前で、いま述べたものを越えると言う語(even better、still faster)。相手はその直前に書いてある。
12
+ * aim は比べた語の後ろで、今より良くする目当てを言う語(より良い社会、より良いものにする)。比べる相手は今の状態。
10
13
  * 同じ文か前の二文に相手を言う語があれば、相手は書いてある。
11
14
  */
12
15
 
@@ -17,6 +20,8 @@ export type ComparativeLists = {
17
20
  readonly baselines: readonly string[];
18
21
  readonly exempt: ReadonlySet<string>;
19
22
  readonly conditions: ReadonlySet<string>;
23
+ readonly beyond: ReadonlySet<string>;
24
+ readonly aims: ReadonlySet<string>;
20
25
  };
21
26
 
22
27
  export type BareComparative = { readonly first: Token; readonly last: Token };
@@ -44,13 +49,35 @@ const isGradable = (tokens: readonly Token[], at: number): boolean => {
44
49
  /** 比べる副詞が語の頭に立つか。前が助詞・記号・接続の語か文の頭のときだけ(「学年だより等」の「より」は語の一部)。 */
45
50
  const OPENS_WORD = new Set(["ADP", "PUNCT", "SCONJ", "CCONJ", "ADV"]);
46
51
 
47
- /** 日本語: 副詞の「より」「さらに」の直後の、程度を言える語。比較の決まった言い方(より多くの)は除く。 */
52
+ /** 語から続く語をつないだもの(もの、ものに、ものにする)と、それぞれの後ろの語の位置。 */
53
+ const MAX_AIM_TOKENS = 3;
54
+ const runsFrom = (tokens: readonly Token[], at: number): { readonly run: string; readonly after: number }[] =>
55
+ tokens
56
+ .slice(at, at + MAX_AIM_TOKENS)
57
+ .reduce<{ run: string; after: number }[]>(
58
+ (runs, token, index) => [...runs, { run: `${runs.at(-1)?.run ?? ""}${token.surface}`, after: at + index + 1 }],
59
+ [],
60
+ );
61
+
62
+ /** 続く語に名詞が付いて、より長い語の頭になっているか(社会保障の「社会」)。 */
63
+ const headsCompound = (tokens: readonly Token[], after: number): boolean => {
64
+ const [last, next] = [tokens[after - 1], tokens[after]];
65
+ return last?.pos === "NOUN" && next?.pos === "NOUN" && last.span.end === next.span.start;
66
+ };
67
+
68
+ /** 程度を言う語(at)の後ろが、今より良くする目当てか(より良い社会、より豊かな暮らし、より良いものにする)。 */
69
+ const namesAim = (tokens: readonly Token[], at: number, aims: ReadonlySet<string>): boolean => {
70
+ const next = tokens[at]?.pos === "NOUN" && ADJECTIVAL_TAIL.has(tokens[at + 1]?.surface ?? "") ? at + 2 : at + 1;
71
+ return runsFrom(tokens, next).some(({ run, after }) => aims.has(run) && !headsCompound(tokens, after));
72
+ };
73
+
74
+ /** 日本語: 副詞の「より」「さらに」の直後の、程度を言える語。比較の決まった言い方(より多くの)と、今より良くする目当て(より良い社会)は除く。 */
48
75
  const markedAt = (tokens: readonly Token[], at: number, lists: ComparativeLists): BareComparative | undefined => {
49
76
  const [marker, word] = [tokens[at], tokens[at + 1]];
50
77
  if (marker?.pos !== "ADV" || !lists.markers.has(marker.surface) || word === undefined || lists.exempt.has(word.surface)) return undefined;
51
78
  const previous = tokens[at - 1];
52
79
  if (previous !== undefined && !OPENS_WORD.has(previous.pos)) return undefined;
53
- return isGradable(tokens, at + 1) ? { first: marker, last: word } : undefined;
80
+ return isGradable(tokens, at + 1) && !namesAim(tokens, at + 1, lists.aims) ? { first: marker, last: word } : undefined;
54
81
  };
55
82
 
56
83
  /** 比べた形の後ろで節が終わるか。読点の後ろに形容詞が続けば、名詞の前に並べた修飾語(better, more predictable results)。 */
@@ -87,10 +114,16 @@ const predicateAt = (tokens: readonly Token[], at: number, lists: ComparativeLis
87
114
  const isConditional = (tokens: readonly Token[], found: BareComparative, conditions: ReadonlySet<string>): boolean =>
88
115
  tokens.some((token) => token.span.start < found.first.span.start && token.pos !== "CCONJ" && conditions.has(lower(token)));
89
116
 
117
+ /** 比べた形のすぐ前が、いま述べたものを越えると言う語か(performed even better)。 */
118
+ const goesBeyond = (tokens: readonly Token[], found: BareComparative, beyond: ReadonlySet<string>): boolean => {
119
+ const before = tokens.findLastIndex((token) => token.span.end <= found.first.span.start);
120
+ return beyond.has(lower(tokens[before]));
121
+ };
122
+
90
123
  export const bareComparativesIn = (tokens: readonly Token[], lists: ComparativeLists): BareComparative[] =>
91
124
  tokens
92
125
  .flatMap((_, at) => markedAt(tokens, at, lists) ?? predicateAt(tokens, at, lists) ?? [])
93
- .filter((found) => !isConditional(tokens, found, lists.conditions));
126
+ .filter((found) => !isConditional(tokens, found, lists.conditions) && !goesBeyond(tokens, found, lists.beyond));
94
127
 
95
128
  /**
96
129
  * 語が比べる相手を言う語か。語の形か原形で照らし、一字の語(旧)は語の頭だけ(旧版は相手、復旧は相手でない)。
@@ -128,6 +161,8 @@ export const listsOf = (lexicon: Lexicon): ComparativeLists => ({
128
161
  baselines: groupOf(lexicon, "baseline"),
129
162
  exempt: new Set(groupOf(lexicon, "exempt")),
130
163
  conditions: new Set(groupOf(lexicon, "condition")),
164
+ beyond: new Set(groupOf(lexicon, "beyond")),
165
+ aims: new Set(groupOf(lexicon, "aim")),
131
166
  });
132
167
 
133
168
  /** 鉤括弧の中の比較(「給与所得の確定申告がさらに簡単に!」ページ)は題や語を挙げたもので、書き手の比較ではない。 */
@@ -139,12 +174,18 @@ const isThanAfterCode = (doc: ProseDocument, found: BareComparative): boolean =>
139
174
 
140
175
  export const bareComparative: Detector = (doc): Finding[] => {
141
176
  const lists = listsOf(doc.lexicons["comparative-baseline"] ?? []);
177
+ const steps = plainStepKeys(
178
+ doc.sentences.map((sentence) => ({ tokens: sentence.tokens ?? [], quoted: quotedIn(sentence) })),
179
+ lists.markers,
180
+ isGradable,
181
+ );
142
182
  return doc.sentences.flatMap((sentence, at) => {
143
183
  const nearby = doc.sentences.slice(Math.max(0, at - BASELINE_REACH), at + 1);
144
184
  if (sentence.embeddedLanguage !== undefined || nearby.some((near) => namesBaseline(near, lists.baselines))) return [];
145
185
  const quoted = quotedIn(sentence);
146
186
  return bareComparativesIn(sentence.tokens ?? [], lists)
147
187
  .filter((found) => !quoted.some((span) => insideSpan(found, span)) && !isThanAfterCode(doc, found))
188
+ .filter((found) => !lists.markers.has(found.first.surface) || !isNamedStep(sentence.tokens ?? [], found.last, steps))
148
189
  .map((found) => ({
149
190
  rule: "",
150
191
  severity: "info",
@@ -8,6 +8,7 @@ import { quoteAt } from "./structure-tree.ts";
8
8
  import { amountColumnOf, isTotalRowGap, type BodyRow } from "../structure/total-row-blank.ts";
9
9
  import { isAmountNameLine, isTotalLine } from "../structure/total-line.ts";
10
10
  import { isTotalLabel, withoutLeadingBlankCells } from "../structure/total.ts";
11
+ import { isSpannedLabelGap } from "../structure/spanned-label-blank.ts";
11
12
 
12
13
  /** Fewer body rows than this, and one blank is as likely a choice as a gap. */
13
14
  const MIN_BODY_ROWS = 3;
@@ -67,8 +68,6 @@ const cellOffset = (cell: MarkdownNode | undefined, row: MarkdownNode | undefine
67
68
  /** Whether a row is a total, subtotal or tax row, judged from the row's source text. */
68
69
  export type IsTotalRow = (rowText: string) => boolean;
69
70
 
70
- const NO_TOTAL_ROWS: IsTotalRow = () => false;
71
-
72
71
  const rowSource = (source: string, row: MarkdownNode): string => {
73
72
  const span = spanOf(row);
74
73
  return span === undefined ? "" : source.slice(span.start, span.end);
@@ -79,30 +78,40 @@ const bodyRowOf = (source: string, row: MarkdownNode, isTotalRow: IsTotalRow): B
79
78
  cells: (row.children ?? []).map((cell) => (shows(cell) ? sourceOf(source, cell) : undefined)),
80
79
  });
81
80
 
81
+ /** How a table's rows are read: which rows are totals, and the total words that make a cell a row label (Total score). */
82
+ type TableReading = { readonly isTotalRow: IsTotalRow; readonly totalWords: readonly string[] };
83
+
84
+ const NO_TOTALS: TableReading = { isTotalRow: () => false, totalWords: [] };
85
+
82
86
  /**
83
87
  * The one blank body cell of a column whose other body rows are all filled, unless it is a total row's blank beside its
84
- * amount. Such a blank still counts as a second blank, so the rows it shares a column with stay as unreported as before.
88
+ * amount, or the second column of a label that spans two (| Total score | | 5 |). Such a blank still counts as a second
89
+ * blank, so the rows it shares a column with stay as unreported as before.
85
90
  */
86
- const columnBlank = (source: string, header: MarkdownNode, body: readonly MarkdownNode[], rows: readonly BodyRow[], column: number): Blank[] => {
91
+ const columnBlank = (source: string, table: TableRows, totalWords: readonly string[], column: number): Blank[] => {
92
+ const { header, body, rows } = table;
87
93
  const blanks = rows.flatMap((row, index) => (row.cells[column] === undefined ? [index] : []));
88
94
  const at = blanks[0];
89
95
  const row = at === undefined ? undefined : rows[at];
90
96
  if (blanks.length !== 1 || at === undefined || row === undefined) return [];
91
97
  if (isTotalRowGap(row, column, amountColumnOf(rows, header.children?.length ?? 0))) return [];
98
+ if (isSpannedLabelGap(row.cells, column, totalWords)) return [];
92
99
  const offset = cellOffset(body[at]?.children?.[column], body[at]);
93
100
  const name = sourceOf(source, header.children?.[column]);
94
101
  return offset === undefined || name === "" ? [] : [{ offset, name }];
95
102
  };
96
103
 
104
+ type TableRows = { readonly header: MarkdownNode; readonly body: readonly MarkdownNode[]; readonly rows: readonly BodyRow[] };
105
+
97
106
  /** The first column is skipped: a blank there often means "same as above". */
98
- const tableBlanksOf = (source: string, table: MarkdownNode, isTotalRow: IsTotalRow): Blank[] => {
107
+ const tableBlanksOf = (source: string, table: MarkdownNode, reading: TableReading): Blank[] => {
99
108
  const [header, ...body] = table.children ?? [];
100
109
  if (header === undefined || body.length < MIN_BODY_ROWS) return [];
101
- const rows = body.map((row) => bodyRowOf(source, row, isTotalRow));
110
+ const rows = body.map((row) => bodyRowOf(source, row, reading.isTotalRow));
102
111
  const columns = header.children?.length ?? 0;
103
112
  return Array.from({ length: columns }, (_unused, column) => column)
104
113
  .slice(1)
105
- .flatMap((column) => columnBlank(source, header, body, rows, column));
114
+ .flatMap((column) => columnBlank(source, { header, body, rows }, reading.totalWords, column));
106
115
  };
107
116
 
108
117
  /** Quoted material is someone else's: email replies the document model found, and every Markdown blockquote. */
@@ -118,8 +127,8 @@ const blocksOutsideQuotes = (source: string, type: string, replies: readonly Spa
118
127
  };
119
128
 
120
129
  /** Blank cells in every table outside quotes. */
121
- export const tableBlanks = (source: string, replies: readonly Span[] = [], isTotalRow: IsTotalRow = NO_TOTAL_ROWS): Blank[] =>
122
- blocksOutsideQuotes(source, "table", replies).flatMap((table) => tableBlanksOf(source, table, isTotalRow));
130
+ export const tableBlanks = (source: string, replies: readonly Span[] = [], reading: TableReading = NO_TOTALS): Blank[] =>
131
+ blocksOutsideQuotes(source, "table", replies).flatMap((table) => tableBlanksOf(source, table, reading));
123
132
 
124
133
  type Item = { readonly node: MarkdownNode; readonly text: string | undefined; readonly empty: boolean };
125
134
 
@@ -192,5 +201,7 @@ const totalRowOf = (doc: ProseDocument): IsTotalRow => {
192
201
  return (rowText) => isTotalLine(rowText, totals) || isAmountNameLine(rowText, totals) || isTotalLabel(withoutLeadingBlankCells(rowText), taxes);
193
202
  };
194
203
 
195
- export const emptyTableCell: Detector = findingsOf("empty-table-cell", (doc) => tableBlanks(doc.source, doc.replyQuotes ?? [], totalRowOf(doc)));
204
+ export const emptyTableCell: Detector = findingsOf("empty-table-cell", (doc) =>
205
+ tableBlanks(doc.source, doc.replyQuotes ?? [], { isTotalRow: totalRowOf(doc), totalWords: patternsOf(doc, "total-label") }),
206
+ );
196
207
  export const emptyListItem: Detector = findingsOf("empty-list-item", (doc) => listBlanks(doc.source, doc.replyQuotes ?? []));
@@ -0,0 +1,25 @@
1
+ import { asideAt } from "./open-end-names.ts";
2
+
3
+ // A vague word pinned down by the bracket right after it: 一定の事項(インボイスに記載が必要な事項), a reasonable fee (USD 50).
4
+ // The aside says what the word leaves open, so the word is not vague there. An aside that only gives the phrase a name
5
+ // (以下「措置」という, hereinafter the "Fee") pins nothing down. Pure: the words that start a name come in.
6
+
7
+ /** What may stand between the vague word and the bracket: the noun it qualifies (事項, fee), no punctuation. */
8
+ const BEFORE_BRACKET = /^[\p{L}\p{N}\p{M} ]{0,12}?(?=[((])/u;
9
+
10
+ const startsWithName = (aside: string, nameMarkers: readonly string[]): boolean => {
11
+ const lower = aside.trimStart().toLowerCase();
12
+ return nameMarkers.some((marker) => marker !== "" && lower.startsWith(marker.toLowerCase()));
13
+ };
14
+
15
+ /**
16
+ * Whether the words right after end (an offset in text) are pinned down by a bracket: at most a short noun, then an
17
+ * aside that opens and closes and holds more than a name. nameMarkers: the words that start a name (以下, hereinafter).
18
+ */
19
+ export const specifiedByBracket = (text: string, end: number, nameMarkers: readonly string[]): boolean => {
20
+ const rest = text.slice(end);
21
+ const before = BEFORE_BRACKET.exec(rest);
22
+ if (before === null) return false;
23
+ const aside = asideAt(text, end + before[0].length);
24
+ return aside !== undefined && aside.trim() !== "" && !startsWithName(aside, nameMarkers);
25
+ };
@@ -62,12 +62,36 @@ const CITATION_NUMBERS = /^[\d\s,,、–-]+$/u;
62
62
  /** 画像の参照(`![alt][label]`)の頭の印。指摘の範囲に含める。 */
63
63
  const IMAGE_MARK = "!";
64
64
 
65
+ /** 印の前の逃がし(\)を数えるのに見る字数。 */
66
+ const ESCAPE_REACH = 16;
67
+ const BACKSLASH = "\\";
68
+
69
+ /** 字の並びの終わりに続く \ の数。 */
70
+ const trailingBackslashes = (text: string): number => {
71
+ const reversed = [...text].reverse();
72
+ const other = reversed.findIndex((char) => char !== BACKSLASH);
73
+ return other === -1 ? reversed.length : other;
74
+ };
75
+
76
+ /** start の括弧の直前に、逃がしていない ! がある(「\![x][]」は画像ではなくリンク)。 */
77
+ const imageAt = (source: string, start: number): boolean => {
78
+ if (source.charAt(start - 1) !== IMAGE_MARK) return false;
79
+ return trailingBackslashes(source.slice(Math.max(0, start - 1 - ESCAPE_REACH), start - 1)) % 2 === 0;
80
+ };
81
+
82
+ /**
83
+ * 名前を省いた画像の参照(`![Complete][]`)は、サイトが全ページに定義を足す印や絵(mdBook の前処理、サイト共通の定義)。
84
+ * 画像の行き先をページの中で一度も書かずに名前だけで呼ぶのは、定義がページの外にあるとき。名前を書いた形(`![alt][logo]`)と
85
+ * リンク(`[text][]`)は指摘する。
86
+ */
87
+ export const siteDefinedImage = (source: string, start: number, label: string): boolean => label === "" && imageAt(source, start);
88
+
65
89
  export const unresolvedReferences = (source: string, markup: Markup): Span[] =>
66
90
  markup.texts.flatMap((text) =>
67
91
  [...source.slice(text.start, text.end).matchAll(REFERENCE_LIKE)]
68
- .filter((match) => !CITATION_NUMBERS.test(match[1] ?? ""))
92
+ .filter((match) => !CITATION_NUMBERS.test(match[1] ?? "") && !siteDefinedImage(source, text.start + match.index, match[2] ?? ""))
69
93
  .map((match) => ({ start: text.start + match.index, end: text.start + match.index + match[0].length }))
70
- .map((span) => (source.charAt(span.start - 1) === IMAGE_MARK ? { start: span.start - 1, end: span.end } : span)),
94
+ .map((span) => (imageAt(source, span.start) ? { start: span.start - 1, end: span.end } : span)),
71
95
  );
72
96
 
73
97
  const emptyLinks = (doc: ProseDocument, markup: Markup): Finding[] =>
@@ -4,6 +4,8 @@
4
4
  * captions this way by convention, and prose that names one ("the FOR FURTHER INFORMATION CONTACT section") repeats it.
5
5
  * A line of a hard-wrapped paragraph in capitals (a disclaimer) has text above it, so it is no caption.
6
6
  */
7
+ import { linesOf, type Line } from "../structure/lines.ts";
8
+ import type { Span } from "../plugin.ts";
7
9
 
8
10
  /** Markdown heading or list marks and spaces that may stand before a caption on its line. */
9
11
  const LINE_LEAD = /^[ \t]*(?:#{1,6}[ \t]+|[-*+][ \t]+)?$/u;
@@ -59,3 +61,43 @@ export const captionsOf = (source: string): ReadonlySet<string> => {
59
61
  }),
60
62
  );
61
63
  };
64
+
65
+ /** A line ends a sentence when its last mark, past closing quotes and brackets, is a period, a question or an exclamation mark. */
66
+ const SENTENCE_END = /[.!?]["'”’)\]]*[ \t\r]*$/u;
67
+ const UPPER = /[A-Z]/u;
68
+ const LOWER = /[a-z]/u;
69
+
70
+ const isCapitalLine = (line: Line): boolean => {
71
+ const bare = line.text.replace(LEAD, "");
72
+ return UPPER.test(bare) && !LOWER.test(bare);
73
+ };
74
+
75
+ /** The lines in paragraphs: runs of lines between blank lines. */
76
+ const paragraphsOf = (lines: readonly Line[]): Line[][] =>
77
+ lines.reduce<Line[][]>((paragraphs, line, index) => {
78
+ if (BLANK_LINE.test(line.text)) return paragraphs;
79
+ const above = lines[index - 1];
80
+ if (above !== undefined && !BLANK_LINE.test(above.text)) paragraphs.at(-1)?.push(line);
81
+ else paragraphs.push([line]);
82
+ return paragraphs;
83
+ }, []);
84
+
85
+ const spanOf = (paragraph: readonly Line[]): Span[] => {
86
+ const first = paragraph[0];
87
+ const last = paragraph.at(-1);
88
+ return first === undefined || last === undefined ? [] : [{ start: first.start, end: last.start + last.text.length }];
89
+ };
90
+
91
+ /**
92
+ * The paragraphs in capitals at the head of a document, before its first sentence: a letterhead, an office's name, an
93
+ * address block (BOARD OF GOVERNORS / WASHINGTON, D.C. 20551). Every line is in capitals and none ends a sentence, so a
94
+ * disclaimer in capitals, which is sentences, is not one. A document with no sentence has no head to tell apart.
95
+ */
96
+ export const letterheadSpans = (source: string): Span[] => {
97
+ const paragraphs = paragraphsOf(linesOf(source));
98
+ const firstSentence = paragraphs.findIndex((paragraph) => paragraph.some((line) => SENTENCE_END.test(line.text)));
99
+ return paragraphs
100
+ .slice(0, Math.max(firstSentence, 0))
101
+ .filter((paragraph) => paragraph.every(isCapitalLine))
102
+ .flatMap(spanOf);
103
+ };
@@ -0,0 +1,17 @@
1
+ // Phrases set in capitals by convention ("NOW, THEREFORE, BE IT RESOLVED" in a resolution). Pure.
2
+ import { escapeRegExp } from "../orthography.ts";
3
+
4
+ const COMMA_OR_SPACE = /,?\s+/u;
5
+
6
+ const wordsOf = (text: string): string => text.trim().split(COMMA_OR_SPACE).join(" ");
7
+
8
+ /**
9
+ * Whether a run of capitals is made only of the listed phrases of several words, one after another ("NOW, THEREFORE" then
10
+ * "BE IT RESOLVED"). A comma after a word, as a run of capitals writes it, is ignored. One-word entries (MUST) match nothing here.
11
+ */
12
+ export const isConventionalPhrase = (run: string, entries: Iterable<string>): boolean => {
13
+ const phrases = [...entries].map(wordsOf).filter((phrase) => phrase.includes(" "));
14
+ if (phrases.length === 0) return false;
15
+ const one = `(?:${phrases.map(escapeRegExp).join("|")})`;
16
+ return new RegExp(`^${one}(?: ${one})*$`, "u").test(wordsOf(run));
17
+ };
@@ -1,6 +1,7 @@
1
1
  import type { Detector, DocumentProfile, Finding, ProseDocument, Sentence, Span, Token } from "../plugin.ts";
2
2
  import { compacted } from "./gram-place.ts";
3
3
  import { leadingCounter, trailingPrefix } from "./counter-edge.ts";
4
+ import { trailingAddressee } from "./addressee-edge.ts";
4
5
  import { maskAddresses } from "../address-chain.ts";
5
6
  import { isAddressRun } from "./place-run.ts";
6
7
  import { isOneName } from "./name-run.ts";
@@ -54,7 +55,15 @@ const runsOf = (sentence: Sentence, profile: DocumentProfile | undefined, names:
54
55
  };
55
56
 
56
57
  /** 連なりを数えないと決める語彙: 住所の先頭にしか来ない単位(都道府県)と、組織の名前の形と単位と、題の名前を閉じる語。 */
57
- type NameWords = { readonly topUnits: ReadonlySet<string>; readonly organisation: OrganisationWords; readonly title: TitleWords };
58
+ type NameWords = {
59
+ readonly topUnits: ReadonlySet<string>;
60
+ readonly organisation: OrganisationWords;
61
+ readonly title: TitleWords;
62
+ /** 数の後ろで日付・時刻の位置を言う単位(月)。連なりの頭にあれば、数に付いた助数詞。 */
63
+ readonly positional: readonly string[];
64
+ /** 宛先の名前に付く語(宛、御中)。連なりの尻にあれば、名前の後ろに付いた語。 */
65
+ readonly addressee: readonly string[];
66
+ };
58
67
 
59
68
  /** 1 つの文書で連なりを読むのに要るもの。titled は、文書の中で題として置かれた名前(titledNames)。 */
60
69
  type RunContext = {
@@ -85,16 +94,19 @@ const isTitle = (tokens: readonly Token[], span: Span, text: string, placed: boo
85
94
  (placed || isTitledName(text, context.titled));
86
95
 
87
96
  /**
88
- * 数えない連なりは空。頭の、数に付いた助数詞(2日日本弁護士連合会の「日」)と、尻の、数に付く接頭の語(施行令第172条の「第」)は数えない。
97
+ * 数えない連なりは空。頭の、数に付いた助数詞(2日日本弁護士連合会の「日」)と、尻の、数に付く接頭の語(施行令第172条の「第」)と
98
+ * 宛先に付く語(市政情報課宛ての「宛」)は数えない。
89
99
  * 住所か名前かは、それを外した残りで決める(1日日本銀行の「日本銀行」)。品詞が無ければ判定しない。
90
100
  */
91
101
  const measuredRun = (tokens: readonly Token[] | undefined, run: KanjiRun, context: RunContext): string => {
92
102
  if (run.quoted) return "";
93
103
  if (tokens === undefined || run.span === undefined) return run.text;
94
- const counter = leadingCounter(tokens, run.span.start, run.text);
104
+ const counter = leadingCounter(tokens, run.span.start, run.text, context.words.positional);
95
105
  const prefix = trailingPrefix(tokens, run.span.end, run.text);
96
- const rest = { start: counter?.span.end ?? run.span.start, end: prefix?.span.start ?? run.span.end };
97
- const text = run.text.slice(counter?.surface.length ?? 0, run.text.length - (prefix?.surface.length ?? 0));
106
+ const tail =
107
+ prefix === undefined ? trailingAddressee(tokens, run.span.end, run.text, context.words.addressee) : { surface: prefix.surface, start: prefix.span.start };
108
+ const rest = { start: counter?.span.end ?? run.span.start, end: tail?.start ?? run.span.end };
109
+ const text = run.text.slice(counter?.surface.length ?? 0, run.text.length - (tail?.surface.length ?? 0));
98
110
  return isPlaceName(tokens, rest, context.words) || isTitle(tokens, rest, text, run.placed, context) ? "" : text;
99
111
  };
100
112
 
@@ -120,8 +132,14 @@ export const kanjiRun: Detector = (doc, options): Finding[] => {
120
132
  const patterns = (id: string): string[] => (doc.lexicons[id] ?? []).map((entry) => entry.pattern);
121
133
  const words: NameWords = {
122
134
  topUnits: new Set(patterns("prefecture-unit")),
123
- organisation: { forms: patterns("organisation-form"), units: patterns("organisation-unit") },
135
+ organisation: {
136
+ forms: patterns("organisation-form"),
137
+ closingForms: (doc.lexicons["organisation-form"] ?? []).filter((entry) => entry.position !== "before").map((entry) => entry.pattern),
138
+ units: patterns("organisation-unit"),
139
+ },
124
140
  title: { endings: patterns("title-ending") },
141
+ positional: patterns("calendar-unit"),
142
+ addressee: patterns("addressee-suffix"),
125
143
  };
126
144
  const names = doc.names ?? [];
127
145
  const context: RunContext = { profile: doc.profile, names, words, titled: titledNames(doc, doc.profile, names) };
@@ -141,10 +159,12 @@ export const kanjiRun: Detector = (doc, options): Finding[] => {
141
159
  /**
142
160
  * 中黒で並べると、どこまでが 1 つの項目か分からなくなる。
143
161
  * 「企画・開発・運用・保守体制」は、保守体制が 1 つなのか保守と体制なのか読めない。
162
+ * 語彙表(nakaguro-word)の、中黒を含めて 1 語の語(キャッシュ・フロー)の中黒は並べていないので数えない。
144
163
  */
145
- export const middleDot: Detector = (doc, options): Finding[] =>
146
- doc.sentences
147
- .map((sentence) => ({ sentence, count: parallelDotCount(sentence.text) }))
164
+ export const middleDot: Detector = (doc, options): Finding[] => {
165
+ const words = (options.lexicon ?? []).map((entry) => entry.pattern).toSorted((left, right) => right.length - left.length);
166
+ return doc.sentences
167
+ .map((sentence) => ({ sentence, count: parallelDotCount(sentence.text, words) }))
148
168
  .filter(({ count }) => count > options.limit)
149
169
  .map(({ sentence, count }) => ({
150
170
  rule: "no-nakaguro-parallel",
@@ -154,3 +174,4 @@ export const middleDot: Detector = (doc, options): Finding[] =>
154
174
  quote: sentence.text.trim(),
155
175
  values: { count, limit: options.limit, offset: sentence.span.start },
156
176
  }));
177
+ };
@@ -71,13 +71,24 @@ const VENUE_END = /^\s*(?:[,;.]|$)/u;
71
71
  */
72
72
  const holdsPhrase = (runs: Runs, start: number, end: number): boolean => firstIn(runs.phrasePair, { start: start + 1, end: end + 1 }) !== -1;
73
73
 
74
+ const LETTER = /\p{L}/u;
75
+
76
+ /** 語の続きのすぐ前が、題名を導く語(whose full title is、entitled)で終わるか。語の途中(retitled)からは読まない。 */
77
+ const afterTitleCue = (source: string, first: Token, titleCues: readonly string[]): boolean => {
78
+ const before = source.slice(source.lastIndexOf("\n", first.span.start - 1) + 1, first.span.start);
79
+ if (before === before.trimEnd()) return false;
80
+ const lead = before.trimEnd().toLowerCase();
81
+ return titleCues.some((cue) => cue !== "" && lead.endsWith(cue.toLowerCase()) && !LETTER.test(lead.charAt(lead.length - cue.length - 1)));
82
+ };
83
+
74
84
  /**
75
85
  * 文の and / or が、引用した題名の中にあるか。and / or の両隣が Title Case の語の続きで、2 語以上の句を含み、その続きが
76
- * 引用符か強調の印で囲まれているか、引用符で閉じた論文名のすぐ後ろにあって読点か文の終わりが続く(誌名)とき。
86
+ * 引用符か強調の印で囲まれているか、引用符で閉じた論文名のすぐ後ろにあって読点か文の終わりが続く(誌名)か、題名を導く語
87
+ * (titleCues: whose full title is、entitled)のすぐ後ろにあるとき。
77
88
  * 本文の固有名詞の並び(Login.gov, TTS Engineering and USAi)や著者の並びは囲まれていないので、題名と読まない。
78
89
  * 文を一度だけ読み、どの位置の問いにも続きを読み直さずに答える。
79
90
  */
80
- export const citedTitles = (tokens: readonly Token[], source: string): ((at: number) => boolean) => {
91
+ export const citedTitles = (tokens: readonly Token[], source: string, titleCues: readonly string[] = []): ((at: number) => boolean) => {
81
92
  const runs = runsOf(tokens, source);
82
93
  return (at) => {
83
94
  const start = leftStart(runs, at);
@@ -88,7 +99,8 @@ export const citedTitles = (tokens: readonly Token[], source: string): ((at: num
88
99
  if (!holdsPhrase(runs, start, end)) return false;
89
100
  const before = source.slice(Math.max(0, first.span.start - MARK_REACH), first.span.start);
90
101
  const after = source.slice(last.span.end, last.span.end + MARK_REACH);
91
- return (OPENING.test(before) && CLOSING.test(after)) || (AFTER_QUOTED_TITLE.test(before) && VENUE_END.test(after));
102
+ const quoted = (OPENING.test(before) && CLOSING.test(after)) || (AFTER_QUOTED_TITLE.test(before) && VENUE_END.test(after));
103
+ return quoted || afterTitleCue(source, first, titleCues);
92
104
  };
93
105
  };
94
106
 
@@ -16,15 +16,30 @@ const wordBefore = (tokens: readonly Token[], at: number): Token | undefined =>
16
16
  return beyond !== undefined && touches(beyond, previous) ? beyond : undefined;
17
17
  };
18
18
 
19
+ const DIGITS = /^[0-90-9]+$/u;
20
+
21
+ /**
22
+ * 数と一語に解析された日付の単位(全角の「4月」)。連なりは単位から始まるので、単位の分だけの語にして返す。
23
+ * positional は数の後ろに置くだけで暦や時計の位置を言う単位(月、時)。
24
+ */
25
+ const gluedUnit = (tokens: readonly Token[], start: number, run: string, positional: readonly string[]): Token | undefined => {
26
+ const token = tokens.find((candidate) => candidate.span.start < start && start < candidate.span.end);
27
+ if (token === undefined) return undefined;
28
+ const unit = positional.find((word) => token.surface.endsWith(word) && DIGITS.test(token.surface.slice(0, -word.length)));
29
+ return unit !== undefined && run.startsWith(unit) ? { ...token, surface: unit, span: { start, end: token.span.end } } : undefined;
30
+ };
31
+
19
32
  /**
20
33
  * 漢字の連なり run(文書全体の座標で start から始まる)の頭が、直前の数に付いた助数詞(2日、第76回、24時間)なら、その助数詞の語。
21
34
  * 数字が区切りを見せるので、助数詞は数と読まれ、後ろの複合語の一部ではない。そうでなければ undefined。
35
+ * 解析が助数詞と言わない日付の単位(4月の「月」)も、positional にあれば助数詞と読む。
22
36
  * 折り返しをまたぐ語(年\n度)は span が surface より長いので、連なりの残りは span の終わりから始まる。
23
37
  */
24
- export const leadingCounter = (tokens: readonly Token[], start: number, run: string): Token | undefined => {
38
+ export const leadingCounter = (tokens: readonly Token[], start: number, run: string, positional: readonly string[] = []): Token | undefined => {
25
39
  const at = tokens.findIndex((token) => token.span.start === start);
26
40
  const counter = tokens[at];
27
- if (counter === undefined || !isCounter(counter) || !isNumber(wordBefore(tokens, at))) return undefined;
41
+ if (counter === undefined) return gluedUnit(tokens, start, run, positional);
42
+ if (!(isCounter(counter) || positional.includes(counter.surface)) || !isNumber(wordBefore(tokens, at))) return undefined;
28
43
  return run.startsWith(counter.surface) ? counter : undefined;
29
44
  };
30
45
 
@@ -13,10 +13,11 @@ export const YEAR_SPAN_JOINER_LEXICON = "year-span-joiner";
13
13
  * `adjacent`: just before the date ("each December 31"). `near`: ending a few characters before it (決算日は、12月31日).
14
14
  * `label`: before the bracket the date is in (年末年始閉庁日(12月29日~1月3日)). `continues`: opening a list item just
15
15
  * after an example's item, it carries the example on ("- For example, … June 22." then "- If instead … June 25").
16
+ * `listed`: named in one list with the date, only list joiners between (祝休日及び12月29日, "public holidays and December 24").
16
17
  */
17
- export type MarkerReach = "sentence" | "example" | "adjacent" | "near" | "label" | "continues";
18
+ export type MarkerReach = "sentence" | "example" | "adjacent" | "near" | "label" | "continues" | "listed";
18
19
 
19
- export const MARKER_REACHES: readonly MarkerReach[] = ["sentence", "example", "adjacent", "near", "label", "continues"];
20
+ export const MARKER_REACHES: readonly MarkerReach[] = ["sentence", "example", "adjacent", "near", "label", "continues", "listed"];
20
21
 
21
22
  export type AnyYearMarker = { readonly word: string; readonly reach: MarkerReach };
22
23
 
@@ -151,8 +152,24 @@ const continuesExample = (date: DateSpan, before: string, source: string, marker
151
152
  return above !== undefined && exampleItem(source, above, lineStart(source, date.offset) - 1, markers, LIST_REACH);
152
153
  };
153
154
 
154
- const coversDate = (before: string, dateStart: string, intro: string | undefined, word: string, reach: MarkerReach): boolean => {
155
+ /** What is left of `gap` once the list joiners at its head and the spaces around them are taken off. */
156
+ const afterJoiners = (gap: string, joiners: readonly string[]): string => {
157
+ const trimmed = gap.trimStart();
158
+ const joiner = joiners.find((candidate) => candidate !== "" && trimmed.startsWith(candidate) && !runsOn(trimmed, candidate));
159
+ return joiner === undefined ? trimmed : afterJoiners(trimmed.slice(joiner.length), joiners);
160
+ };
161
+
162
+ /** The word stands just before the date with only list joiners between: 「祝休日及び」, "holidays, and ". */
163
+ const listedBefore = (before: string, word: string, joiners: readonly string[]): boolean => {
164
+ const found = lastWordAt(before, word);
165
+ if (found === -1) return false;
166
+ const gap = before.slice(found + word.length);
167
+ return gap.trim() !== "" && afterJoiners(gap, joiners) === "";
168
+ };
169
+
170
+ const coversDate = (before: string, dateStart: string, intro: string | undefined, word: string, reach: MarkerReach, joiners: readonly string[]): boolean => {
155
171
  if (reach === "adjacent") return isAdjacent(latinLower(before), word);
172
+ if (reach === "listed") return listedBefore(latinLower(before), word, joiners);
156
173
  if (reach === "near") return lastWordAt(latinLower(before.slice(-(word.length + NEAR_REACH))), word) !== -1;
157
174
  if (reach === "label") return latinLower(labelOf(before) ?? "").includes(word);
158
175
  if (reach === "continues") return false;
@@ -160,11 +177,15 @@ const coversDate = (before: string, dateStart: string, intro: string | undefined
160
177
  return inItem || inSentence(before, dateStart, word) || (intro !== undefined && introEndsWith(latinLower(intro), word));
161
178
  };
162
179
 
163
- /** The date is a recurring day or a worked example: a marker stands where its reach covers the date. */
164
- export const anyYearDate = (date: DateSpan, source: string, markers: readonly AnyYearMarker[]): boolean => {
180
+ /**
181
+ * The date is a recurring day or a worked example: a marker stands where its reach covers the date. `listJoiners` are the
182
+ * words a `listed` marker may stand before the date with (及び, "and").
183
+ */
184
+ export const anyYearDate = (date: DateSpan, source: string, markers: readonly AnyYearMarker[], listJoiners: readonly string[] = []): boolean => {
165
185
  const before = source.slice(lineStart(source, date.offset), date.offset);
166
186
  const intro = introOf(source, before, date.offset);
167
- const covered = markers.some(({ word, reach }) => word !== "" && coversDate(before, source.charAt(date.offset), intro, latinLower(word), reach));
187
+ const joiners = listJoiners.map(latinLower);
188
+ const covered = markers.some(({ word, reach }) => word !== "" && coversDate(before, source.charAt(date.offset), intro, latinLower(word), reach, joiners));
168
189
  return covered || continuesExample(date, before, source, markers);
169
190
  };
170
191
 
@@ -22,7 +22,8 @@ export const dateFormat: Detector = (doc, options): Finding[] => {
22
22
  eras: (doc.lexicons["calendar-era"] ?? []).map((entry) => entry.pattern),
23
23
  };
24
24
  const text = doc.prose ?? doc.source;
25
- return dateGroups(text, datesIn(text, words)).flatMap((group) => {
25
+ const stampLabels = (doc.lexicons["date-stamp-label"] ?? []).map((entry) => entry.pattern);
26
+ return dateGroups(text, datesIn(text, words), stampLabels).flatMap((group) => {
26
27
  const minority = dateMinority(group, options.limit);
27
28
  return minority === undefined ? [] : findingsOf(doc, minority);
28
29
  });