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,25 @@
1
+ // The label of a field that opens a line ("Current project status: Ongoing", 「現在の状況:継続中」). Pure.
2
+
3
+ /** A label is a short name, not a clause: this many words at most. */
4
+ const MAX_LABEL_WORDS = 5;
5
+ const MAX_LABEL_CHARS = 40;
6
+ const LIST_MARKER = /^\s*(?:[-*+]|\d+[.)])\s+/u;
7
+ const FIELD = /^([^::\n]+)[::](?!\/\/)/u;
8
+ const HAS_LETTER = /\p{L}/u;
9
+ /** A sentence ends before the colon, so what precedes it is not a label. */
10
+ const SENTENCE_END = /[.!?。!?]/u;
11
+
12
+ const isLabel = (label: string): boolean =>
13
+ HAS_LETTER.test(label) && !SENTENCE_END.test(label) && label.length <= MAX_LABEL_CHARS && label.trim().split(/\s+/u).length <= MAX_LABEL_WORDS;
14
+
15
+ /**
16
+ * Where the value of a field on the text's first line starts, just past the colon (the value may be blank), or undefined
17
+ * when the first line opens with no label. Whether the label is a name and not a clause ("The following apply:") is the
18
+ * caller's to judge from the parts of speech.
19
+ */
20
+ export const fieldValueStart = (text: string): number | undefined => {
21
+ const marker = LIST_MARKER.exec(text)?.[0] ?? "";
22
+ const field = FIELD.exec(text.slice(marker.length));
23
+ if (field?.[1] === undefined || !isLabel(field[1].replaceAll("**", ""))) return undefined;
24
+ return marker.length + field[0].length;
25
+ };
@@ -26,9 +26,12 @@ const markdownMarks = (text: string): NoteMark[] =>
26
26
  return { offset: match.index, label: `[^${match.groups?.["key"] ?? ""}]`, kind: MARKDOWN, key: (match.groups?.["key"] ?? "").toLowerCase(), note };
27
27
  });
28
28
 
29
- /** 印と一桁か二桁の番号。後ろに数字や小数が続けば年や数(※2018年度、※1.2倍)で、注の番号ではない。 */
29
+ /** 番号の後ろに続くと、番号が英語の序数(1st、2nd、23rd)になるもの。 */
30
+ const ORDINAL_SUFFIX = "(?:[sS][tT]|[nN][dD]|[rR][dD]|[tT][hH])(?![A-Za-z])";
31
+
32
+ /** 印と一桁か二桁の番号。後ろに数字や小数が続けば年や数(※2018年度、※1.2倍)、序数が続けば英語の語(※1st Party)で、注の番号ではない。 */
30
33
  const wordMarks = (text: string, mark: string): NoteMark[] =>
31
- [...text.matchAll(new RegExp(`${escapeRegExp(mark)}(?<key>[0-90-9]{1,2})(?![0-90-9]|[..,,][0-90-9])`, "gu"))].map((match) => ({
34
+ [...text.matchAll(new RegExp(`${escapeRegExp(mark)}(?<key>[0-90-9]{1,2})(?![0-90-9]|[..,,][0-90-9]|${ORDINAL_SUFFIX})`, "gu"))].map((match) => ({
32
35
  offset: match.index,
33
36
  label: match[0],
34
37
  kind: mark,
@@ -36,11 +39,38 @@ const wordMarks = (text: string, mark: string): NoteMark[] =>
36
39
  note: NOTE_LINE_HEAD.test(lineHeadOf(text, match.index)),
37
40
  }));
38
41
 
42
+ const QUOTE_LINE = /^[ \t]{0,3}>/u;
43
+ const QUOTE_MARKS = /^(?:[ \t]{0,3}>)+/u;
44
+ /** 引用の段落を終わらせる行(見出し、コード、箇条書き、脚注の定義)。これ以外の空でない行は、引用の段落の続き(CommonMark の lazy continuation)。 */
45
+ const BLOCK_START = /^[ \t]{0,3}(?:[#`~]|[-*+][ \t]|\d{1,9}[.)][ \t]|\[\^)/u;
46
+ const CODE_SPAN = /(`+)[\s\S]*?\1/gu;
47
+
48
+ const quotedLines = (source: string): string[] =>
49
+ source.split("\n").reduce<{ lines: string[]; open: boolean }>(
50
+ (state, line) => {
51
+ const open = QUOTE_LINE.test(line) || (state.open && line.trim() !== "" && !BLOCK_START.test(line));
52
+ return { lines: open ? [...state.lines, line.replace(QUOTE_MARKS, "")] : state.lines, open };
53
+ },
54
+ { lines: [], open: false },
55
+ ).lines;
56
+
57
+ /**
58
+ * 引用(`>`)の中の Markdown の脚注の印の番号。引用は本文から覆われるが、引用の中の印も注を指している。
59
+ * 引用の中の印に注が無くても言わない(引用元の注かもしれない)。注が指されているかだけに使う。
60
+ */
61
+ export const quotedFootnoteKeys = (source: string): ReadonlySet<string> =>
62
+ new Set(
63
+ markdownMarks(quotedLines(source).join("\n").replace(CODE_SPAN, ""))
64
+ .filter((mark) => !mark.note)
65
+ .map((mark) => mark.key),
66
+ );
67
+
39
68
  /** 一つの種類の印のうち、合わないもの。印の語(※)は、その種類の注が一つも無ければ注を別に置いた文書と読んで言わない。 */
40
- const slipsOfKind = (marks: readonly NoteMark[], kind: string): NoteSlip[] => {
69
+ const slipsOfKind = (marks: readonly NoteMark[], kind: string, quotedKeys: ReadonlySet<string>): NoteSlip[] => {
41
70
  const ofKind = marks.filter((mark) => mark.kind === kind);
42
71
  const notes = new Set(ofKind.filter((mark) => mark.note).map((mark) => mark.key));
43
- const referenced = new Set(ofKind.filter((mark) => !mark.note).map((mark) => mark.key));
72
+ const quoted = kind === MARKDOWN ? [...quotedKeys] : [];
73
+ const referenced = new Set([...ofKind.filter((mark) => !mark.note).map((mark) => mark.key), ...quoted]);
44
74
  if (notes.size === 0 && kind !== MARKDOWN) return [];
45
75
  const missing = ofKind
46
76
  .filter((mark) => !mark.note && !notes.has(mark.key))
@@ -52,15 +82,17 @@ const slipsOfKind = (marks: readonly NoteMark[], kind: string): NoteSlip[] => {
52
82
  };
53
83
 
54
84
  /** 文字列の中の、注の無い印と印の無い注。 */
55
- export const noteSlips = (text: string, words: readonly string[]): NoteSlip[] => {
85
+ export const noteSlips = (text: string, words: readonly string[], quotedKeys: ReadonlySet<string> = new Set()): NoteSlip[] => {
56
86
  const marks = [...markdownMarks(text), ...words.flatMap((word) => wordMarks(text, word))];
57
- return [...new Set(marks.map((mark) => mark.kind))].flatMap((kind) => slipsOfKind(marks, kind)).toSorted((left, right) => left.offset - right.offset);
87
+ return [...new Set(marks.map((mark) => mark.kind))]
88
+ .flatMap((kind) => slipsOfKind(marks, kind, quotedKeys))
89
+ .toSorted((left, right) => left.offset - right.offset);
58
90
  };
59
91
 
60
92
  export const footnoteMismatch: Detector = (doc): Finding[] => {
61
93
  const text = doc.prose ?? doc.source;
62
94
  const words = (doc.lexicons["note-mark"] ?? []).map((entry) => entry.pattern);
63
- return noteSlips(text, words).map((slip) => ({
95
+ return noteSlips(text, words, quotedFootnoteKeys(doc.source)).map((slip) => ({
64
96
  rule: "",
65
97
  severity: "warning",
66
98
  line: 0,
@@ -0,0 +1,52 @@
1
+ import type { Span, Token } from "../plugin.ts";
2
+ import { isWithinAny } from "../quoted-span.ts";
3
+
4
+ /**
5
+ * 段階の一つとしての比較(より良い(BESTな)改善 と 良い(BETTERな)改善、より良い実装例1 と 良い実装例2)。文書が同じ語を
6
+ * 比べる副詞なしでも書いていれば(良い改善)、「より良い」の相手はその一段下の段で、相手は書いてある。
7
+ * 段は、程度を言う語の原形と、その後ろの名詞で見分ける。間の括弧の添え書き((BESTな))は飛ばす。
8
+ */
9
+
10
+ /** 程度を言う語が語の位置 at で始まるか。bare-comparative.ts の判定を受け取る。 */
11
+ export type IsGradable = (tokens: readonly Token[], at: number) => boolean;
12
+
13
+ /** 形容動詞の語幹の後ろに来る語(豊かな)。この語の後ろから名詞を探す。 */
14
+ const ADJECTIVAL_TAIL = new Set(["な", "に", "だ", "です", "で"]);
15
+ const OPENING = new Set(["(", "("]);
16
+ const CLOSING = new Set([")", ")"]);
17
+
18
+ /** 程度を言う語(at)の後ろの最初の語の位置。形容動詞の語尾と、括弧の添え書きを飛ばす。 */
19
+ const afterWord = (tokens: readonly Token[], at: number): number => {
20
+ const next = tokens[at]?.pos === "NOUN" && ADJECTIVAL_TAIL.has(tokens[at + 1]?.surface ?? "") ? at + 2 : at + 1;
21
+ if (!OPENING.has(tokens[next]?.surface ?? "")) return next;
22
+ const close = tokens.findIndex((token, index) => index > next && CLOSING.has(token.surface));
23
+ return close === -1 ? next : close + 1;
24
+ };
25
+
26
+ /** 段の名前: 程度を言う語の原形と、後ろの名詞。後ろが名詞でなければ段ではない。 */
27
+ export const stepKeyAt = (tokens: readonly Token[], at: number): string | undefined => {
28
+ const [word, noun] = [tokens[at], tokens[afterWord(tokens, at)]];
29
+ if (word === undefined || noun === undefined || noun.pos !== "NOUN") return undefined;
30
+ return `${word.lemma ?? word.surface}|${noun.surface}`;
31
+ };
32
+
33
+ /** 文の語と、引いたもの(鉤括弧の中)の範囲。引いた語(「良い対応」という表現)は文書が書いた段ではない。 */
34
+ export type StepSentence = { readonly tokens: readonly Token[]; readonly quoted: readonly Span[] };
35
+
36
+ /** 文書が比べる副詞(markers)なしで書いた、程度を言う語と名詞の段の名前。 */
37
+ export const plainStepKeys = (sentences: readonly StepSentence[], markers: ReadonlySet<string>, isGradable: IsGradable): ReadonlySet<string> =>
38
+ new Set(
39
+ sentences.flatMap(({ tokens, quoted }) =>
40
+ tokens.flatMap((token, at) => {
41
+ if (markers.has(tokens[at - 1]?.surface ?? "") || isWithinAny(quoted, token.span) || !isGradable(tokens, at)) return [];
42
+ const key = stepKeyAt(tokens, at);
43
+ return key === undefined ? [] : [key];
44
+ }),
45
+ ),
46
+ );
47
+
48
+ /** 比べた語(word)が、文書が一段下を書いた段の一つか。 */
49
+ export const isNamedStep = (tokens: readonly Token[], word: Token, steps: ReadonlySet<string>): boolean => {
50
+ const key = stepKeyAt(tokens, tokens.indexOf(word));
51
+ return key !== undefined && steps.has(key);
52
+ };
@@ -0,0 +1,21 @@
1
+ import type { Section, Sentence } from "../plugin.ts";
2
+
3
+ /** A list item or a table row on a line after the sentence: what the sentence leads into. */
4
+ const LIST_OR_TABLE_NEXT = /^[ \t]*\n\s*(?:[-*+]\s|\d{1,3}[.)]\s|\|)/u;
5
+
6
+ /** How far past the sentence to look for that line: blank lines between, never the rest of a long document. */
7
+ const LOOKAHEAD = 200;
8
+
9
+ /**
10
+ * 見出しのある節の最初の文のうち、次の行から箇条書きか表が続くもの。参照用の文書は、節ごとに同じ書き出しで一覧を導く
11
+ * (「…によると以下の制約である。」の後ろに制約の箇条書き)。言い回しがどれもこの文にだけ出るなら節のひな形で、書き手が言い直したのではない。
12
+ * 後ろが本文の文なら、節ごとの口癖(正直なところ、…)と見分けられないので入れない。導入部(見出しの前)は節ではない。
13
+ */
14
+ export const sectionOpeners = (sections: readonly Pick<Section, "depth" | "sentences">[], source: string): Set<Sentence> =>
15
+ new Set(
16
+ sections.flatMap((section) => {
17
+ const first = section.sentences[0];
18
+ if (section.depth === 0 || first === undefined) return [];
19
+ return LIST_OR_TABLE_NEXT.test(source.slice(first.span.end, first.span.end + LOOKAHEAD)) ? [first] : [];
20
+ }),
21
+ );
@@ -1,5 +1,5 @@
1
1
  import { softBreaks } from "../soft-break.ts";
2
- import type { LengthUnit, Span } from "../plugin.ts";
2
+ import type { LengthUnit, ProseDocument, Sentence, Span } from "../plugin.ts";
3
3
 
4
4
  /** 数えるために詰めた文と、詰めた文の各文字が元の文のどこにあったか。 */
5
5
  export type Compacted = { readonly text: string; readonly offsets: readonly number[] };
@@ -50,3 +50,12 @@ export const placeOf = (source: Compacted, gram: string): Span | undefined => {
50
50
  const at = source.text.indexOf(gram);
51
51
  return at === -1 ? undefined : placeAt(source, { start: at, end: at + gram.length });
52
52
  };
53
+
54
+ /** 詰めた文の index 番目の文字が、リンクの文字か。 */
55
+ export const linkedIn = (doc: ProseDocument, sentence: Sentence, source: Compacted): ((index: number) => boolean) => {
56
+ const links = doc.links.filter((link) => link.start < sentence.span.end && sentence.span.start < link.end);
57
+ return (index) => {
58
+ const at = sentence.span.start + (source.offsets[index] ?? -1);
59
+ return links.some((link) => link.start <= at && at < link.end);
60
+ };
61
+ };
@@ -9,12 +9,28 @@ const NOUNS = new Set(["NOUN", "PROPN", "ADJ"]);
9
9
  /** 中身を持たない語。名前の前後に付いても、名前の繰り返しを言い回しに変えない。 */
10
10
  const FUNCTION_POS = new Set(["ADP", "DET", "CCONJ", "SCONJ", "PART", "PUNCT", "SYM", "X"]);
11
11
 
12
+ /** 形式名詞(とき、こと)。動詞が節を名詞にしているだけで、用語は作らない(承認又は断るとき)。 */
13
+ const isDependentNoun = (token: Token | undefined): boolean => token?.features?.["NounType"] === "Dependent";
14
+
15
+ /**
16
+ * 日本語で、名詞と接続詞(又は、及び)のすぐ後ろで名詞を飾る動詞(行為又は怠る事実)は、名詞を並べた一つの用語の中にあり、述語ではない。
17
+ * 用語は言い換えられないので、繰り返して当たり前。名詞に「する」が付いた動詞(承認又は拒否する)は、行為を並べた述語と見分けられないので外さない。
18
+ */
19
+ const inCoordinatedNoun = (tokens: readonly Token[], index: number, unit: LengthUnit): boolean =>
20
+ unit === "char" &&
21
+ tokens[index]?.pos === "VERB" &&
22
+ tokens[index]?.features?.["VerbType"] !== "Light" &&
23
+ tokens[index - 1]?.pos === "CCONJ" &&
24
+ NOUNS.has(tokens[index - 2]?.pos ?? "") &&
25
+ NOUNS.has(tokens[index + 1]?.pos ?? "") &&
26
+ !isDependentNoun(tokens[index + 1]);
27
+
12
28
  /**
13
29
  * 英語で名詞のすぐ前の動詞は、名詞を飾る語(形容詞を挟んでも同じ)(the upcoming fiscal year、the Disclosing Party、the borrow checker)で、
14
- * 述語ではない。日本語の名詞の前の動詞(〜にあるコンポーネント)は言い回しの一部になるので、word 単位だけで見る。
30
+ * 述語ではない。日本語の名詞の前の動詞(〜にあるコンポーネント)は言い回しの一部になるので、接続詞の後ろの用語の中(inCoordinatedNoun)だけを外す。
15
31
  */
16
32
  const modifiesNoun = (tokens: readonly Token[], index: number, unit: LengthUnit): boolean =>
17
- unit === "word" && tokens[index]?.pos === "VERB" && NOUNS.has(tokens[index + 1]?.pos ?? "");
33
+ (unit === "word" && tokens[index]?.pos === "VERB" && NOUNS.has(tokens[index + 1]?.pos ?? "")) || inCoordinatedNoun(tokens, index, unit);
18
34
 
19
35
  /** 英語で大文字の名前の一部になった動詞(Procedures Guide、the Location Object)も、述語ではない。 */
20
36
  const namePartAt = (tokens: readonly Token[], index: number, unit: LengthUnit): boolean => unit === "word" && isNamePart(tokens, index);
@@ -40,10 +56,34 @@ const onlyNamesBeside = (tokens: readonly Token[], predicate: number, window: Sp
40
56
  return names.length > 0 && rest.every((index) => FUNCTION_POS.has(tokens[index]?.pos ?? ""));
41
57
  };
42
58
 
59
+ const COMPOUND_POS = new Set(["NOUN", "PROPN", "ADJ", "NUM"]);
60
+ /** 動詞の前の名詞句がこれだけの語を持てば、動詞はその名詞句の最後の名詞。the contract says は 1 語なので述語のまま。 */
61
+ const MIN_COMPOUND_WORDS = 2;
62
+
63
+ /** 語形を変えていない動詞(rate)か -ing 形(subcontracting)。名詞と同じ形で、名詞の読み違いになりうる。described や says は述語の形。 */
64
+ const isNounShaped = (token: Token): boolean =>
65
+ token.features?.["VerbForm"] === "Ger" || (token.lemma !== undefined && token.lemma === token.surface.toLowerCase());
66
+
67
+ /** 動詞の目的語の始まり(models generate the text の the)。後ろにこれが続く動詞は述語。 */
68
+ const OBJECT_START = new Set(["DET", "PRON", "NUM"]);
69
+
70
+ /**
71
+ * 英語で、窓の最後の名詞の形の動詞の前が名詞と形容詞だけ(先頭の冠詞は許す)で、後ろに目的語が続かなければ、動詞は名詞の続きの
72
+ * 読み違い(the federal funds rate の rate、small business subcontracting)。窓の中に機能語があれば言い回し(your contract says)。
73
+ */
74
+ const endsNounCompound = (tokens: readonly Token[], index: number, window: Span, unit: LengthUnit): boolean => {
75
+ const verb = tokens[index];
76
+ if (unit !== "word" || verb?.pos !== "VERB" || !isNounShaped(verb) || OBJECT_START.has(tokens[index + 1]?.pos ?? "")) return false;
77
+ const touched = tokens.flatMap((token, at) => (overlaps(token, window) ? [at] : []));
78
+ if (touched.at(-1) !== index) return false;
79
+ const before = touched.slice(0, -1).filter((at, order) => !(order === 0 && tokens[at]?.pos === "DET"));
80
+ return before.length >= MIN_COMPOUND_WORDS && before.every((at) => COMPOUND_POS.has(tokens[at]?.pos ?? ""));
81
+ };
82
+
43
83
  const countsAsPredicate = (tokens: readonly Token[], index: number, window: Span, unit: LengthUnit): boolean => {
44
84
  const token = tokens[index];
45
85
  if (token === undefined || !PREDICATE_POS.has(token.pos) || !overlaps(token, window)) return false;
46
- if (modifiesNoun(tokens, index, unit) || namePartAt(tokens, index, unit)) return false;
86
+ if (modifiesNoun(tokens, index, unit) || namePartAt(tokens, index, unit) || endsNounCompound(tokens, index, window, unit)) return false;
47
87
  return !isCut(token, window) || !onlyNamesBeside(tokens, index, window, unit);
48
88
  };
49
89
 
@@ -1,13 +1,15 @@
1
1
  import { lengthOf, proseText } from "../measure.ts";
2
2
  import { newContentMorphemes } from "./content-morphemes.ts";
3
3
  import { echoedHeadingUnits, trigrams } from "./heading-overlap.ts";
4
+ import { fieldValueStart } from "./field-label.ts";
4
5
  import { formValueTokens, isFormBlock } from "./form-line.ts";
5
6
  import { titleTokens } from "../heading-label.ts";
6
7
  import { announcerWords, makesClaim } from "./heading-claim.ts";
7
8
  import { handsOver } from "./lead-in.ts";
8
9
  import { holdsLink, namesLocation } from "./location-pointer.ts";
9
10
  import { withoutQuotedVariants } from "./quoted-variant.ts";
10
- import type { Detector, Finding, LengthUnit, ProseDocument, Section } from "../plugin.ts";
11
+ import { namesHeadingSubject } from "./subject-name-line.ts";
12
+ import type { Detector, Finding, LengthUnit, ProseDocument, Section, Sentence } from "../plugin.ts";
11
13
 
12
14
  /** 見出しが短すぎると、偶然の一致で 100% になる。これ未満の見出しは見ない。 */
13
15
  const MIN_GRAMS = 4;
@@ -105,8 +107,33 @@ const opensForm = (section: Section): boolean => {
105
107
  return newContentMorphemes(heading, formValueTokens(first.text, first.span.start, first.tokens)) > 0;
106
108
  };
107
109
 
110
+ /** 最初の行が、見出しが扱うものの名前だけ(リンクや添付の札、見出しの名の一覧)。見出しを言い直す文ではない。 */
111
+ const namesSubject = (doc: ProseDocument, section: Section): boolean => {
112
+ const first = section.firstSentence;
113
+ if (first?.tokens === undefined || section.headingTokens === undefined) return false;
114
+ const heading = titleTokens(section.heading, measuredHeading(section), section.headingTokens);
115
+ const announcing = announcerWords(doc.lexicons["heading-announcer"] ?? []).announcing;
116
+ return namesHeadingSubject(measuredHeading(section), heading, first.text, first.span.start, first.tokens, announcing);
117
+ };
118
+
119
+ /** 述語の品詞。項目名にあれば、コロンの前は名前ではなく文の頭(The following expenses require receipts:)。 */
120
+ const PREDICATE_POS: ReadonlySet<string> = new Set(["VERB", "AUX"]);
121
+
122
+ /**
123
+ * 行頭の項目名(Current project status: Ongoing)を除いた文。報告の書式が見出しの語で項目を立てるのは書式で、書き手が言ったのは値。
124
+ * 品詞が読めなければ、項目名かどうか決められないので除かない。
125
+ */
126
+ const withoutFieldLabel = (sentence: Sentence): string => {
127
+ const valueStart = fieldValueStart(sentence.text);
128
+ if (valueStart === undefined || sentence.tokens === undefined) return sentence.text;
129
+ const labelEnd = sentence.span.start + valueStart;
130
+ const isClause = sentence.tokens.some((token) => token.span.start < labelEnd && PREDICATE_POS.has(token.pos));
131
+ return isClause ? sentence.text : sentence.text.slice(valueStart);
132
+ };
133
+
108
134
  /** 見出しとの重なりを測る文。用語集や表記の手引きは見出しの語の別の書き方を引用する(Not “datacentre”)ので、それは数えない。 */
109
- const echoedText = (section: Section): string => withoutQuotedVariants(section.firstSentence?.text ?? "", measuredHeading(section));
135
+ const echoedText = (section: Section): string =>
136
+ section.firstSentence === undefined ? "" : withoutQuotedVariants(withoutFieldLabel(section.firstSentence), measuredHeading(section));
110
137
 
111
138
  export const headingEcho: Detector = (doc, options): Finding[] => {
112
139
  const leadIns = (doc.lexicons["lead-in"] ?? []).map((entry) => entry.pattern);
@@ -117,7 +144,8 @@ export const headingEcho: Detector = (doc, options): Finding[] => {
117
144
  !leadsIn(doc, section, leadIns) &&
118
145
  !pointsElsewhere(doc, section) &&
119
146
  !statesFigure(section.heading, section.firstSentence?.text ?? "") &&
120
- !opensForm(section),
147
+ !opensForm(section) &&
148
+ !namesSubject(doc, section),
121
149
  )
122
150
  .map((section) => ({ section, overlap: Math.round(containment(trigrams(measuredHeading(section)), trigrams(echoedText(section))) * 100) }))
123
151
  .filter(({ overlap }) => overlap >= options.limit)
@@ -6,15 +6,16 @@ import { quoteAround } from "./quote-around.ts";
6
6
  /** One stray letter, and where it is in the text. */
7
7
  export type StrayLetter = { readonly offset: number; readonly letter: string };
8
8
 
9
- // Hiragana or kanji before (after katakana a letter is a label: サイズm, プランb), kana after.
10
- const STRAY = /(?<=[\p{Script=Hiragana}\p{Script=Han}々])[a-z](?=[\p{Script=Hiragana}\p{Script=Katakana}ー])/gu;
9
+ // Hiragana or kanji before (after katakana a letter is a label: サイズm, プランb), kana after. Only a consonant: romaji
10
+ // input turns a vowel into kana at once, so a lone vowel is a word of its own (eラーニング, iモード), never a residue.
11
+ const STRAY = /(?<=[\p{Script=Hiragana}\p{Script=Han}々])[b-df-hj-np-tv-z](?=[\p{Script=Hiragana}\p{Script=Katakana}ー])/gu;
11
12
  /** Marks that make the run around the letter a path, an address or an assignment (/docs/あaい, a=b). */
12
13
  const PATH_MARK = /[/\\=@#]/u;
13
14
 
14
15
  const runBefore = (text: string, offset: number): string => text.slice(0, offset).split(/\s/u).at(-1) ?? "";
15
16
 
16
17
  /**
17
- * One lower-case letter between Japanese: hiragana or kanji before, kana after. A letter followed by a particle names
18
+ * One lower-case consonant between Japanese: hiragana or kanji before, kana after. A letter followed by a particle names
18
19
  * something (変数xが, まずxを, をxで), so it is left alone; the particles come from the lexicon.
19
20
  */
20
21
  export const strayLettersIn = (text: string, particles: readonly string[]): StrayLetter[] =>
@@ -0,0 +1,61 @@
1
+ import type { Detector, Finding, ProseDocument, Token } from "../plugin.ts";
2
+ import { compoundSlipsIn, type CompoundUse, type CompoundWord } from "../kanji-compound-slip.ts";
3
+
4
+ // Collects the document's kanji runs, from sentences, headings and table cells, each with the words the analyser cut it
5
+ // into, and hands them to compoundSlipsIn.
6
+
7
+ const KANJI_RUN = /[\p{Script=Han}々]+/gu;
8
+
9
+ /** The analyser's words over [offset, offset + run), by kanji index. A word without a reading is one the analyser does not know. */
10
+ const wordsOver = (run: string, offset: number, tokens: readonly Token[]): CompoundWord[] | undefined => {
11
+ const chars = Array.from(run);
12
+ const starts = chars.map((_, index) => offset + chars.slice(0, index).join("").length);
13
+ const indexOf = (position: number): number => starts.findIndex((start) => start >= position);
14
+ const words = tokens
15
+ .filter((token) => token.span.start < offset + run.length && token.span.end > offset)
16
+ .map((token) => {
17
+ const start = Math.max(0, indexOf(token.span.start));
18
+ const endIndex = indexOf(token.span.end);
19
+ const crossesRun = token.span.start < offset || token.span.end > offset + run.length;
20
+ return { start, end: endIndex === -1 ? starts.length : endIndex, known: token.reading !== undefined, crossesRun };
21
+ });
22
+ const covered = starts.every((_, index) => words.some((word) => word.start <= index && index < word.end));
23
+ return covered ? words : undefined;
24
+ };
25
+
26
+ const usesIn = (text: string, base: number, tokens: readonly Token[]): CompoundUse[] =>
27
+ [...text.matchAll(KANJI_RUN)].map((match) => {
28
+ const offset = base + match.index;
29
+ return { compound: match[0], offset, words: wordsOver(match[0], offset, tokens) };
30
+ });
31
+
32
+ const shifted = (tokens: readonly Token[], by: number): Token[] =>
33
+ tokens.map((token) => ({ ...token, span: { start: token.span.start + by, end: token.span.end + by } }));
34
+
35
+ const headingUses = (doc: ProseDocument): CompoundUse[] =>
36
+ doc.sections
37
+ .filter((section) => section.depth > 0)
38
+ .flatMap((section) => {
39
+ // A section's span starts after its heading line; a heading written differently from the source falls back to that line's start.
40
+ const found = doc.source.lastIndexOf(section.heading, section.span.start);
41
+ const base = found === -1 ? doc.source.lastIndexOf("\n", section.span.start - 1) + 1 : found;
42
+ return usesIn(section.heading, base, shifted(section.headingTokens ?? [], base));
43
+ });
44
+
45
+ const compoundUsesOf = (doc: ProseDocument): CompoundUse[] => [
46
+ ...doc.sentences.flatMap((sentence) => usesIn(sentence.text, sentence.span.start, sentence.tokens ?? [])),
47
+ ...headingUses(doc),
48
+ ...(doc.tableCells?.() ?? []).flatMap((cell) => usesIn(cell.text, cell.start, cell.tokens ?? [])),
49
+ ];
50
+
51
+ export const kanjiCompoundSlip: Detector = (doc, options): Finding[] => {
52
+ const ignored = new Set((options.lexicon ?? []).flatMap((entry) => Array.from(entry.pattern)));
53
+ return compoundSlipsIn(compoundUsesOf(doc), ignored).map((slip) => ({
54
+ rule: "",
55
+ severity: "info",
56
+ line: 0,
57
+ column: 0,
58
+ quote: slip.written,
59
+ values: { written: slip.written, usual: slip.usual, count: slip.usualCount, offset: slip.offset },
60
+ }));
61
+ };
@@ -1,4 +1,5 @@
1
- import type { Detector, Finding, Sentence, Span } from "../plugin.ts";
1
+ import type { Detector, Finding, ProseDocument, Sentence, Span } from "../plugin.ts";
2
+ import { latinCompanyFormRegExp, latinCompanyFormSpans } from "../latin-company-form.ts";
2
3
  import { minorityOf } from "../orthography.ts";
3
4
  import { isWithinAny, quotedSpans } from "../quoted-span.ts";
4
5
 
@@ -14,7 +15,9 @@ const MARKS = /[、,。.]/gu;
14
15
 
15
16
  const DIGIT = /[\d0-9]/u;
16
17
 
17
- /** 番号の印(行の頭や読点・空白・括弧の後ろの「1.」「2.」)。文の中の数の後ろの点(締切は10.)は文の終わり。 */
18
+ /**
19
+ * 番号の印(行の頭や読点・空白・括弧の後ろの「1.」「2.」)。文の中の数の後ろの点(締切は10.)は文の終わり。
20
+ */
18
21
  const LIST_NUMBER = /(?:^|[\s、,,((])[\d0-9]{1,3}$/u;
19
22
  const LIST_NUMBER_REACH = 4;
20
23
 
@@ -22,29 +25,96 @@ const LIST_NUMBER_REACH = 4;
22
25
  const LATIN_LABEL = /(?:^|[^A-Za-zA-Za-z])[A-Za-zA-Za-z]{1,2}$/u;
23
26
  const LATIN_LABEL_REACH = 3;
24
27
 
28
+ /** 片仮名の印(イ.ロ.ハ.)。語彙表 item-number の 1 字の片仮名で、前が片仮名でないとき(データ.の タ は語の終わり)。 */
29
+ const KATAKANA = /^[ァ-ヺー]$/u;
30
+
31
+ /** 句読点として数えない印と名前。kana は片仮名の項目の印、companyForms は英字の会社の形(CO.,LTD.)。 */
32
+ export type MarkExceptions = { readonly kana: ReadonlySet<string>; readonly companyForms: RegExp | undefined };
33
+
34
+ /**
35
+ * 印の点の後ろには、同じ行に項目の本文が続く。行の終わりの点(対象はロ.、締切は(予定)10.)は句点。
36
+ * 文は「イ.」の点で割れることがあるので、文ではなく文書の行の続きを見る。
37
+ */
38
+ const isFollowed = (source: string, offset: number): boolean => {
39
+ const lineEnd = source.indexOf("\n", offset);
40
+ return source.slice(offset + 1, lineEnd === -1 ? source.length : lineEnd).trim() !== "";
41
+ };
42
+
43
+ type Place = { readonly text: string; readonly at: number; readonly source: string; readonly offset: number };
44
+
45
+ const isKanaLabel = ({ text, at, source, offset }: Place, kana: ReadonlySet<string>): boolean =>
46
+ kana.has(text.charAt(at - 1)) && !KATAKANA.test(text.charAt(at - 2)) && isFollowed(source, offset);
47
+
48
+ /**
49
+ * 括弧の後ろの番号。注の印の後ろ((注)2.)か、括弧で閉じた見出しの後ろの最初の番号(事項)1.)。数の後ろに本文が続くときだけ印と読む。
50
+ * 括弧で添えた語の後ろのほかの数(締切は(予定)10.)は文の終わり。
51
+ */
52
+ const NOTE_NUMBER_LABEL = /[((]注[))][\d0-9]{1,3}$/u;
53
+ const CLOSED_FIRST_NUMBER = /[。))】]\]][11]$/u;
54
+ const NOTE_REACH = 6;
55
+
56
+ const FULLWIDTH_OFFSET = 0xfee0;
57
+ const TRAILING_NUMBER = /[\d0-9]{1,3}$/u;
58
+ const asNumber = (digits: string): number => Number(digits.replace(/[0-9]/gu, (char) => String.fromCharCode(char.charCodeAt(0) - FULLWIDTH_OFFSET)));
59
+
60
+ const NUMBER_POINT = /(?<![\d0-9])([\d0-9]{1,3})[..](?![\d0-9])/gu;
61
+
62
+ /** numbers の中に、1 から順に last まで、この順で並ぶか。 */
63
+ const countsUpTo = (numbers: readonly number[], last: number): boolean => numbers.reduce((next, number) => (number === next ? next + 1 : next), 1) > last;
64
+
65
+ /**
66
+ * 行が改行を失って続いた注(…(注)1.…持株会2.株式分割…3.有償…)の番号。同じ行の前に「1.」から一つ前までの番号の点が
67
+ * 順に並び、後ろに本文が続く。文の中の数の後ろの句点(締切は10.次回は11.)は、前に「1.」からの並びが無い。
68
+ */
69
+ const isNextInRun = ({ source, offset }: Place): boolean => {
70
+ const lineStart = source.lastIndexOf("\n", offset) + 1;
71
+ const before = source.slice(lineStart, offset);
72
+ const digits = TRAILING_NUMBER.exec(before)?.[0];
73
+ if (digits === undefined || !isFollowed(source, offset)) return false;
74
+ const earlier = [...before.slice(0, -digits.length).matchAll(NUMBER_POINT)].map((match) => asNumber(match[1] ?? ""));
75
+ const previous = asNumber(digits) - 1;
76
+ return previous > 0 && countsUpTo(earlier, previous);
77
+ };
78
+
25
79
  const isInsideNumber = (text: string, at: number): boolean => DIGIT.test(text.charAt(at - 1)) && DIGIT.test(text.charAt(at + 1));
26
80
 
27
- const isLabelPeriod = (text: string, at: number): boolean =>
28
- LIST_NUMBER.test(text.slice(Math.max(0, at - LIST_NUMBER_REACH), at)) || LATIN_LABEL.test(text.slice(Math.max(0, at - LATIN_LABEL_REACH), at));
81
+ const isLabelPeriod = (place: Place, kana: ReadonlySet<string>): boolean => {
82
+ const { text, at } = place;
83
+ const beforeNumber = text.slice(Math.max(0, at - LIST_NUMBER_REACH), at);
84
+ return (
85
+ isKanaLabel(place, kana) ||
86
+ ((NOTE_NUMBER_LABEL.test(text.slice(Math.max(0, at - NOTE_REACH), at)) || CLOSED_FIRST_NUMBER.test(beforeNumber)) &&
87
+ isFollowed(place.source, place.offset)) ||
88
+ isNextInRun(place) ||
89
+ LIST_NUMBER.test(beforeNumber) ||
90
+ LATIN_LABEL.test(text.slice(Math.max(0, at - LATIN_LABEL_REACH), at))
91
+ );
92
+ };
29
93
 
30
94
  /**
31
- * 句読点として数えない「,」「.」。数の中の桁の区切りと小数点(1,000、3.5)、番号の印の点(1.はじめに、、2.確認)、
95
+ * 句読点として数えない「,」「.」。数の中の桁の区切りと小数点(1,000、3.5)、番号の印の点(1.はじめに、、2.確認、イ.)、
32
96
  * 一、二文字の英字の印の後ろの点(Q.、No.)は、日本語の文の区切りではない。
33
97
  */
34
- const isNotPunctuation = (text: string, at: number, mark: string): boolean => {
35
- if (mark === ".") return isInsideNumber(text, at) || isLabelPeriod(text, at);
36
- if (mark === ",") return isInsideNumber(text, at);
98
+ const isNotPunctuation = (place: Place, mark: string, kana: ReadonlySet<string>): boolean => {
99
+ if (mark === ".") return isInsideNumber(place.text, place.at) || isLabelPeriod(place, kana);
100
+ if (mark === ",") return isInsideNumber(place.text, place.at);
37
101
  return false;
38
102
  };
39
103
 
40
104
  export type Mark = { readonly kind: Kind["id"]; readonly western: boolean; readonly mark: string; readonly offset: number };
41
105
 
42
- /** 文の中の句読点。鉤括弧で引いたものの中は、引いた元の書き方なので数えない。offset は文書の中の位置。 */
43
- export const marksIn = (sentence: Sentence): Mark[] => {
44
- const quoted: readonly Span[] = quotedSpans(sentence.text);
106
+ /**
107
+ * 文の中の句読点。鉤括弧で引いたものの中は引いた元の書き方、英字の会社の形(CO.,LTD.)の中は名前の一部なので数えない。
108
+ * offset は文書の中の位置。
109
+ */
110
+ export const marksIn = (sentence: Sentence, source: string, exceptions: MarkExceptions): Mark[] => {
111
+ const skipped: readonly Span[] = [...quotedSpans(sentence.text), ...latinCompanyFormSpans(sentence.text, exceptions.companyForms)];
45
112
  return [...sentence.text.matchAll(MARKS)]
46
- .filter((match) => !isNotPunctuation(sentence.text, match.index, match[0]))
47
- .filter((match) => !isWithinAny(quoted, { start: match.index, end: match.index }))
113
+ .filter((match) => {
114
+ const place = { text: sentence.text, at: match.index, source, offset: sentence.span.start + match.index };
115
+ return !isNotPunctuation(place, match[0], exceptions.kana);
116
+ })
117
+ .filter((match) => !isWithinAny(skipped, { start: match.index, end: match.index }))
48
118
  .map((match) => {
49
119
  const western = match[0] === "," || match[0] === ".";
50
120
  const kind = match[0] === "、" || match[0] === "," ? "comma" : "period";
@@ -76,14 +146,23 @@ const findingOf = (sentence: Sentence, entry: Mark, kind: Kind, tally: Tally): F
76
146
  variant: entry.kind,
77
147
  });
78
148
 
149
+ const exceptionsOf = (doc: ProseDocument): MarkExceptions => {
150
+ const patterns = (name: string): string[] => (doc.lexicons[name] ?? []).map((entry) => entry.pattern);
151
+ return {
152
+ kana: new Set(patterns("item-number").filter((pattern) => KATAKANA.test(pattern))),
153
+ companyForms: latinCompanyFormRegExp(patterns("company-form-latin")),
154
+ };
155
+ };
156
+
79
157
  /**
80
158
  * 読点(、と,)と句点(。と.)を、それぞれ文書の中でどちらかにそろえているか。混ざっていれば少ないほうを指摘する。
81
159
  * latin-spacing と同じく、同数なら文書が先に使った書き方をその文書の書き方とする。
82
160
  */
83
161
  export const kutotenConsistency: Detector = (doc, options): Finding[] => {
162
+ const exceptions = exceptionsOf(doc);
84
163
  const located = doc.sentences
85
164
  .filter((sentence) => !isCitation(sentence, doc.source))
86
- .flatMap((sentence) => marksIn(sentence).map((entry) => ({ sentence, entry })));
165
+ .flatMap((sentence) => marksIn(sentence, doc.source, exceptions).map((entry) => ({ sentence, entry })));
87
166
  return KINDS.flatMap((kind) => {
88
167
  const ofKind = located.filter(({ entry }) => entry.kind === kind.id);
89
168
  const minority = minorityOf(ofKind.map(({ entry }) => entry.western));
@@ -16,10 +16,19 @@ const PER = 1000;
16
16
  /** 密度を見る rule は短い文書を測らない。単位は言語で違うので床も分ける。 */
17
17
  const FLOOR = { word: 200, char: 500 };
18
18
 
19
- type Hit = { readonly sentence: Sentence; readonly matched: string };
19
+ type Hit = { readonly sentence: Sentence; readonly matched: string; readonly group?: string | undefined };
20
20
 
21
21
  const hitsFor = (doc: ProseDocument, lexicon: Lexicon): Hit[] =>
22
- doc.sentences.flatMap((sentence) => lexicon.filter((entry) => entryIn(sentence, entry)).map((entry) => ({ sentence, matched: entry.pattern })));
22
+ doc.sentences.flatMap((sentence) =>
23
+ lexicon.filter((entry) => entryIn(sentence, entry)).map((entry) => ({ sentence, matched: entry.pattern, group: entry.group })),
24
+ );
25
+
26
+ /**
27
+ * A lexicon word in group address says how the document speaks to its reader (あなたの). Alone it is the writer's chosen
28
+ * register, so it counts only beside a phrase from outside the group.
29
+ */
30
+ const ADDRESS_GROUP = "address";
31
+ const onlyAddress = (hits: readonly Hit[]): boolean => hits.every((hit) => hit.group === ADDRESS_GROUP);
23
32
 
24
33
  /**
25
34
  * 床より短い文書の扱い。skip は測らない。at-floor は SHORT_LENGTH の長さの文書として測り、一つだけなら言わない(一つは礼儀)。
@@ -52,7 +61,7 @@ const densityRule =
52
61
  const hits = hitsFor(doc, options.lexicon ?? []);
53
62
  const length = wordsOf(doc);
54
63
  const isShort = length < FLOOR[doc.lengthUnit];
55
- if (hits.length === 0 || (isShort && (short === "skip" || hits.length < SHORT_MINIMUM_HITS))) return [];
64
+ if (onlyAddress(hits) || (isShort && (short === "skip" || hits.length < SHORT_MINIMUM_HITS))) return [];
56
65
  if (perThousand(hits.length, isShort ? SHORT_LENGTH[doc.lengthUnit] : length) <= options.limit) return [];
57
66
  const density = perThousand(hits.length, length);
58
67
  return hits.map((hit) => ({
@@ -2,7 +2,7 @@ import type { Detector, Finding, Markup, ProseDocument, Span } from "../plugin.t
2
2
  import { findingAt, markupOf, quoteOf } from "./markup-finding.ts";
3
3
  import { linkTextOf } from "../link-text.ts";
4
4
  import { bareUrls } from "../bare-url.ts";
5
- import { isFileNameAlt, isUrlText, mismatchedHost } from "../link-shape.ts";
5
+ import { isFieldValueUrl, isFileNameAlt, isUrlText, mismatchedHost } from "../link-shape.ts";
6
6
  import { minorityWithin } from "../orthography.ts";
7
7
  import { readMarkdown, spansOfType } from "../markdown-read.ts";
8
8
 
@@ -23,16 +23,23 @@ const FAILED_DESTINATION = "](";
23
23
 
24
24
  const inside = (span: Span, regions: readonly Span[]): boolean => regions.some((region) => span.start >= region.start && span.start < region.end);
25
25
 
26
+ const isFieldValue = (source: string, url: Span): boolean => {
27
+ const lineStart = source.lastIndexOf("\n", url.start - 1) + 1;
28
+ const lineEnd = source.indexOf("\n", url.end);
29
+ return isFieldValueUrl(source.slice(lineStart, url.start), source.slice(url.end, lineEnd === -1 ? source.length : lineEnd));
30
+ };
31
+
26
32
  /**
27
33
  * The bare URLs the reader sees as text (outside links, code and quotations). A URL in a table cell is a value the table lists
28
- * (an example request, a field's content), not a link the prose points to, so tables are left out.
34
+ * (an example request, a field's content), not a link the prose points to, so tables are left out, and so is a field's value
35
+ * on its own line (`**Licence text** https://…`).
29
36
  */
30
37
  const bareUrlSpans = (source: string, markup: Markup): Span[] => {
31
38
  const tables = spansOfType(readMarkdown(source).root, "table");
32
39
  return markup.texts.flatMap((text) =>
33
40
  bareUrls(source.slice(text.start, text.end))
34
41
  .map((url) => ({ start: text.start + url.start, end: text.start + url.end }))
35
- .filter((url) => !source.startsWith(FAILED_DESTINATION, url.start - FAILED_DESTINATION.length) && !inside(url, tables)),
42
+ .filter((url) => !source.startsWith(FAILED_DESTINATION, url.start - FAILED_DESTINATION.length) && !inside(url, tables) && !isFieldValue(source, url)),
36
43
  );
37
44
  };
38
45