chaffjs 0.14.0 → 0.16.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 (602) hide show
  1. package/README.md +27 -2
  2. package/dist/address-chain.d.ts +4 -1
  3. package/dist/address-chain.d.ts.map +1 -1
  4. package/dist/address-chain.js +6 -3
  5. package/dist/address-chain.js.map +1 -1
  6. package/dist/backends/anthropic.d.ts +2 -1
  7. package/dist/backends/anthropic.d.ts.map +1 -1
  8. package/dist/backends/anthropic.js +4 -1
  9. package/dist/backends/anthropic.js.map +1 -1
  10. package/dist/backends/openai.d.ts +2 -1
  11. package/dist/backends/openai.d.ts.map +1 -1
  12. package/dist/backends/openai.js +1 -1
  13. package/dist/backends/openai.js.map +1 -1
  14. package/dist/backends/types.d.ts +2 -1
  15. package/dist/backends/types.d.ts.map +1 -1
  16. package/dist/backends/types.js +1 -1
  17. package/dist/backends/types.js.map +1 -1
  18. package/dist/baseline.js +1 -1
  19. package/dist/baseline.js.map +1 -1
  20. package/dist/calendar-number.d.ts +26 -0
  21. package/dist/calendar-number.d.ts.map +1 -0
  22. package/dist/calendar-number.js +51 -0
  23. package/dist/calendar-number.js.map +1 -0
  24. package/dist/cli-args.d.ts +0 -5
  25. package/dist/cli-args.d.ts.map +1 -1
  26. package/dist/cli-args.js +2 -2
  27. package/dist/cli-args.js.map +1 -1
  28. package/dist/cli-text.d.ts +10 -3
  29. package/dist/cli-text.d.ts.map +1 -1
  30. package/dist/cli-text.js +33 -14
  31. package/dist/cli-text.js.map +1 -1
  32. package/dist/cli.d.ts.map +1 -1
  33. package/dist/cli.js +53 -47
  34. package/dist/cli.js.map +1 -1
  35. package/dist/commands/cite.d.ts.map +1 -1
  36. package/dist/commands/cite.js +4 -4
  37. package/dist/commands/cite.js.map +1 -1
  38. package/dist/commands/eval.d.ts +4 -0
  39. package/dist/commands/eval.d.ts.map +1 -1
  40. package/dist/commands/eval.js +21 -7
  41. package/dist/commands/eval.js.map +1 -1
  42. package/dist/commands/feedback.d.ts.map +1 -1
  43. package/dist/commands/feedback.js +2 -1
  44. package/dist/commands/feedback.js.map +1 -1
  45. package/dist/commands/init-ask.d.ts +19 -0
  46. package/dist/commands/init-ask.d.ts.map +1 -0
  47. package/dist/commands/init-ask.js +43 -0
  48. package/dist/commands/init-ask.js.map +1 -0
  49. package/dist/commands/test.d.ts +8 -0
  50. package/dist/commands/test.d.ts.map +1 -1
  51. package/dist/commands/test.js +73 -40
  52. package/dist/commands/test.js.map +1 -1
  53. package/dist/commands/tree.d.ts +1 -8
  54. package/dist/commands/tree.d.ts.map +1 -1
  55. package/dist/commands/tree.js +9 -7
  56. package/dist/commands/tree.js.map +1 -1
  57. package/dist/config/by-path.d.ts.map +1 -1
  58. package/dist/config/by-path.js +10 -9
  59. package/dist/config/by-path.js.map +1 -1
  60. package/dist/config/load.d.ts +4 -0
  61. package/dist/config/load.d.ts.map +1 -1
  62. package/dist/config/load.js +19 -0
  63. package/dist/config/load.js.map +1 -1
  64. package/dist/config/name-problems.d.ts +5 -0
  65. package/dist/config/name-problems.d.ts.map +1 -0
  66. package/dist/config/name-problems.js +11 -0
  67. package/dist/config/name-problems.js.map +1 -0
  68. package/dist/date-stamp.js +1 -1
  69. package/dist/date-stamp.js.map +1 -1
  70. package/dist/detectors/acronym-compound.d.ts +8 -0
  71. package/dist/detectors/acronym-compound.d.ts.map +1 -0
  72. package/dist/detectors/acronym-compound.js +8 -0
  73. package/dist/detectors/acronym-compound.js.map +1 -0
  74. package/dist/detectors/acronym-context.d.ts +4 -0
  75. package/dist/detectors/acronym-context.d.ts.map +1 -1
  76. package/dist/detectors/acronym-context.js +17 -1
  77. package/dist/detectors/acronym-context.js.map +1 -1
  78. package/dist/detectors/acronym-expansion.d.ts +10 -0
  79. package/dist/detectors/acronym-expansion.d.ts.map +1 -1
  80. package/dist/detectors/acronym-expansion.js +63 -5
  81. package/dist/detectors/acronym-expansion.js.map +1 -1
  82. package/dist/detectors/char-shape.d.ts.map +1 -1
  83. package/dist/detectors/char-shape.js +47 -15
  84. package/dist/detectors/char-shape.js.map +1 -1
  85. package/dist/detectors/citation-key.d.ts +13 -0
  86. package/dist/detectors/citation-key.d.ts.map +1 -0
  87. package/dist/detectors/citation-key.js +9 -0
  88. package/dist/detectors/citation-key.js.map +1 -0
  89. package/dist/detectors/cited-name.d.ts +11 -0
  90. package/dist/detectors/cited-name.d.ts.map +1 -0
  91. package/dist/detectors/cited-name.js +15 -0
  92. package/dist/detectors/cited-name.js.map +1 -0
  93. package/dist/detectors/concrete-evidence.d.ts +7 -0
  94. package/dist/detectors/concrete-evidence.d.ts.map +1 -0
  95. package/dist/detectors/concrete-evidence.js +21 -0
  96. package/dist/detectors/concrete-evidence.js.map +1 -0
  97. package/dist/detectors/counter-edge.d.ts +8 -0
  98. package/dist/detectors/counter-edge.d.ts.map +1 -0
  99. package/dist/detectors/counter-edge.js +27 -0
  100. package/dist/detectors/counter-edge.js.map +1 -0
  101. package/dist/detectors/dotted-name.d.ts +13 -0
  102. package/dist/detectors/dotted-name.d.ts.map +1 -0
  103. package/dist/detectors/dotted-name.js +10 -0
  104. package/dist/detectors/dotted-name.js.map +1 -0
  105. package/dist/detectors/doubled-word.d.ts +6 -1
  106. package/dist/detectors/doubled-word.d.ts.map +1 -1
  107. package/dist/detectors/doubled-word.js +22 -2
  108. package/dist/detectors/doubled-word.js.map +1 -1
  109. package/dist/detectors/en-shape.d.ts +1 -1
  110. package/dist/detectors/en-shape.d.ts.map +1 -1
  111. package/dist/detectors/en-shape.js +112 -29
  112. package/dist/detectors/en-shape.js.map +1 -1
  113. package/dist/detectors/enumerated-runs.d.ts.map +1 -1
  114. package/dist/detectors/enumerated-runs.js +4 -2
  115. package/dist/detectors/enumerated-runs.js.map +1 -1
  116. package/dist/detectors/gram-place.d.ts +0 -4
  117. package/dist/detectors/gram-place.d.ts.map +1 -1
  118. package/dist/detectors/gram-place.js +7 -2
  119. package/dist/detectors/gram-place.js.map +1 -1
  120. package/dist/detectors/gram-predicate.d.ts +7 -0
  121. package/dist/detectors/gram-predicate.d.ts.map +1 -0
  122. package/dist/detectors/gram-predicate.js +44 -0
  123. package/dist/detectors/gram-predicate.js.map +1 -0
  124. package/dist/detectors/heading-case.d.ts +21 -0
  125. package/dist/detectors/heading-case.d.ts.map +1 -0
  126. package/dist/detectors/heading-case.js +40 -0
  127. package/dist/detectors/heading-case.js.map +1 -0
  128. package/dist/detectors/heading-echo.d.ts.map +1 -1
  129. package/dist/detectors/heading-echo.js +4 -1
  130. package/dist/detectors/heading-echo.js.map +1 -1
  131. package/dist/detectors/index.d.ts.map +1 -1
  132. package/dist/detectors/index.js +4 -3
  133. package/dist/detectors/index.js.map +1 -1
  134. package/dist/detectors/lettered-index.d.ts +9 -0
  135. package/dist/detectors/lettered-index.d.ts.map +1 -0
  136. package/dist/detectors/lettered-index.js +54 -0
  137. package/dist/detectors/lettered-index.js.map +1 -0
  138. package/dist/detectors/lexicon.d.ts +5 -1
  139. package/dist/detectors/lexicon.d.ts.map +1 -1
  140. package/dist/detectors/lexicon.js +51 -5
  141. package/dist/detectors/lexicon.js.map +1 -1
  142. package/dist/detectors/name-numeral.d.ts +16 -0
  143. package/dist/detectors/name-numeral.d.ts.map +1 -0
  144. package/dist/detectors/name-numeral.js +30 -0
  145. package/dist/detectors/name-numeral.js.map +1 -0
  146. package/dist/detectors/name-part.d.ts +8 -0
  147. package/dist/detectors/name-part.d.ts.map +1 -0
  148. package/dist/detectors/name-part.js +20 -0
  149. package/dist/detectors/name-part.js.map +1 -0
  150. package/dist/detectors/name-run.d.ts +8 -0
  151. package/dist/detectors/name-run.d.ts.map +1 -0
  152. package/dist/detectors/name-run.js +13 -0
  153. package/dist/detectors/name-run.js.map +1 -0
  154. package/dist/detectors/orthography.d.ts.map +1 -1
  155. package/dist/detectors/orthography.js +35 -7
  156. package/dist/detectors/orthography.js.map +1 -1
  157. package/dist/detectors/phrase-match.d.ts +3 -1
  158. package/dist/detectors/phrase-match.d.ts.map +1 -1
  159. package/dist/detectors/phrase-match.js +20 -15
  160. package/dist/detectors/phrase-match.js.map +1 -1
  161. package/dist/detectors/placeholder.d.ts +14 -0
  162. package/dist/detectors/placeholder.d.ts.map +1 -0
  163. package/dist/detectors/placeholder.js +24 -0
  164. package/dist/detectors/placeholder.js.map +1 -0
  165. package/dist/detectors/quoted-variant.d.ts +7 -0
  166. package/dist/detectors/quoted-variant.d.ts.map +1 -0
  167. package/dist/detectors/quoted-variant.js +18 -0
  168. package/dist/detectors/quoted-variant.js.map +1 -0
  169. package/dist/detectors/repeated-head.d.ts.map +1 -1
  170. package/dist/detectors/repeated-head.js +5 -4
  171. package/dist/detectors/repeated-head.js.map +1 -1
  172. package/dist/detectors/series-label.d.ts +13 -0
  173. package/dist/detectors/series-label.d.ts.map +1 -0
  174. package/dist/detectors/series-label.js +26 -0
  175. package/dist/detectors/series-label.js.map +1 -0
  176. package/dist/detectors/signals.d.ts.map +1 -1
  177. package/dist/detectors/signals.js +44 -29
  178. package/dist/detectors/signals.js.map +1 -1
  179. package/dist/detectors/stacked-hedge.d.ts +23 -0
  180. package/dist/detectors/stacked-hedge.d.ts.map +1 -0
  181. package/dist/detectors/stacked-hedge.js +64 -0
  182. package/dist/detectors/stacked-hedge.js.map +1 -0
  183. package/dist/detectors/structure-tree.js +1 -1
  184. package/dist/detectors/structure-tree.js.map +1 -1
  185. package/dist/detectors/superlative-amount.d.ts +9 -0
  186. package/dist/detectors/superlative-amount.d.ts.map +1 -0
  187. package/dist/detectors/superlative-amount.js +20 -0
  188. package/dist/detectors/superlative-amount.js.map +1 -0
  189. package/dist/detectors/superlative-clause.d.ts +18 -0
  190. package/dist/detectors/superlative-clause.d.ts.map +1 -0
  191. package/dist/detectors/superlative-clause.js +61 -0
  192. package/dist/detectors/superlative-clause.js.map +1 -0
  193. package/dist/detectors/superlative-name.d.ts +9 -0
  194. package/dist/detectors/superlative-name.d.ts.map +1 -0
  195. package/dist/detectors/superlative-name.js +24 -0
  196. package/dist/detectors/superlative-name.js.map +1 -0
  197. package/dist/detectors/superlative-scope.d.ts +6 -0
  198. package/dist/detectors/superlative-scope.d.ts.map +1 -1
  199. package/dist/detectors/superlative-scope.js +3 -3
  200. package/dist/detectors/superlative-scope.js.map +1 -1
  201. package/dist/detectors/team.d.ts +0 -6
  202. package/dist/detectors/team.d.ts.map +1 -1
  203. package/dist/detectors/team.js +17 -1
  204. package/dist/detectors/team.js.map +1 -1
  205. package/dist/detectors/token-shape.d.ts.map +1 -1
  206. package/dist/detectors/token-shape.js +15 -5
  207. package/dist/detectors/token-shape.js.map +1 -1
  208. package/dist/document.d.ts +3 -0
  209. package/dist/document.d.ts.map +1 -1
  210. package/dist/document.js +28 -7
  211. package/dist/document.js.map +1 -1
  212. package/dist/eval.js +1 -1
  213. package/dist/eval.js.map +1 -1
  214. package/dist/feedback/draft.js +1 -1
  215. package/dist/feedback/draft.js.map +1 -1
  216. package/dist/feedback/excerpt.d.ts +0 -1
  217. package/dist/feedback/excerpt.d.ts.map +1 -1
  218. package/dist/feedback/excerpt.js +2 -2
  219. package/dist/feedback/excerpt.js.map +1 -1
  220. package/dist/file-header.d.ts +20 -0
  221. package/dist/file-header.d.ts.map +1 -0
  222. package/dist/file-header.js +21 -0
  223. package/dist/file-header.js.map +1 -0
  224. package/dist/files.js +3 -3
  225. package/dist/files.js.map +1 -1
  226. package/dist/genre-check.d.ts +34 -0
  227. package/dist/genre-check.d.ts.map +1 -0
  228. package/dist/genre-check.js +21 -0
  229. package/dist/genre-check.js.map +1 -0
  230. package/dist/genre-load.d.ts +6 -0
  231. package/dist/genre-load.d.ts.map +1 -0
  232. package/dist/genre-load.js +22 -0
  233. package/dist/genre-load.js.map +1 -0
  234. package/dist/genre-parse.d.ts +40 -0
  235. package/dist/genre-parse.d.ts.map +1 -0
  236. package/dist/genre-parse.js +104 -0
  237. package/dist/genre-parse.js.map +1 -0
  238. package/dist/genre-suggest.d.ts +15 -0
  239. package/dist/genre-suggest.d.ts.map +1 -0
  240. package/dist/genre-suggest.js +26 -0
  241. package/dist/genre-suggest.js.map +1 -0
  242. package/dist/genre.d.ts +8 -1
  243. package/dist/genre.d.ts.map +1 -1
  244. package/dist/genre.js +19 -5
  245. package/dist/genre.js.map +1 -1
  246. package/dist/init-choice.d.ts +12 -0
  247. package/dist/init-choice.d.ts.map +1 -0
  248. package/dist/init-choice.js +41 -0
  249. package/dist/init-choice.js.map +1 -0
  250. package/dist/init.d.ts +0 -1
  251. package/dist/init.d.ts.map +1 -1
  252. package/dist/init.js +16 -17
  253. package/dist/init.js.map +1 -1
  254. package/dist/joined-view.d.ts +25 -0
  255. package/dist/joined-view.d.ts.map +1 -0
  256. package/dist/joined-view.js +76 -0
  257. package/dist/joined-view.js.map +1 -0
  258. package/dist/judge.d.ts +2 -1
  259. package/dist/judge.d.ts.map +1 -1
  260. package/dist/judge.js +1 -1
  261. package/dist/judge.js.map +1 -1
  262. package/dist/line-continues.d.ts +6 -0
  263. package/dist/line-continues.d.ts.map +1 -0
  264. package/dist/line-continues.js +33 -0
  265. package/dist/line-continues.js.map +1 -0
  266. package/dist/line-paragraphs.d.ts +4 -0
  267. package/dist/line-paragraphs.d.ts.map +1 -0
  268. package/dist/line-paragraphs.js +69 -0
  269. package/dist/line-paragraphs.js.map +1 -0
  270. package/dist/mask.d.ts.map +1 -1
  271. package/dist/mask.js +7 -6
  272. package/dist/mask.js.map +1 -1
  273. package/dist/measure.d.ts +1 -0
  274. package/dist/measure.d.ts.map +1 -1
  275. package/dist/measure.js +5 -1
  276. package/dist/measure.js.map +1 -1
  277. package/dist/number-name.d.ts +6 -3
  278. package/dist/number-name.d.ts.map +1 -1
  279. package/dist/number-name.js +31 -14
  280. package/dist/number-name.js.map +1 -1
  281. package/dist/orthography.d.ts +4 -0
  282. package/dist/orthography.d.ts.map +1 -1
  283. package/dist/orthography.js +11 -9
  284. package/dist/orthography.js.map +1 -1
  285. package/dist/plain-source.d.ts +6 -0
  286. package/dist/plain-source.d.ts.map +1 -0
  287. package/dist/plain-source.js +6 -0
  288. package/dist/plain-source.js.map +1 -0
  289. package/dist/plugin.d.ts +22 -1
  290. package/dist/plugin.d.ts.map +1 -1
  291. package/dist/profile/check.d.ts +28 -0
  292. package/dist/profile/check.d.ts.map +1 -0
  293. package/dist/profile/check.js +12 -0
  294. package/dist/profile/check.js.map +1 -0
  295. package/dist/profile/for-file.d.ts +2 -2
  296. package/dist/profile/for-file.d.ts.map +1 -1
  297. package/dist/profile/for-file.js +9 -2
  298. package/dist/profile/for-file.js.map +1 -1
  299. package/dist/profile/load.js +1 -1
  300. package/dist/profile/load.js.map +1 -1
  301. package/dist/profile/select.d.ts +4 -2
  302. package/dist/profile/select.d.ts.map +1 -1
  303. package/dist/profile/select.js +8 -3
  304. package/dist/profile/select.js.map +1 -1
  305. package/dist/quoted-span.d.ts +22 -0
  306. package/dist/quoted-span.d.ts.map +1 -0
  307. package/dist/quoted-span.js +25 -0
  308. package/dist/quoted-span.js.map +1 -0
  309. package/dist/reasons.d.ts +1 -0
  310. package/dist/reasons.d.ts.map +1 -1
  311. package/dist/reasons.js +4 -1
  312. package/dist/reasons.js.map +1 -1
  313. package/dist/render/compact.d.ts.map +1 -1
  314. package/dist/render/compact.js +1 -2
  315. package/dist/render/compact.js.map +1 -1
  316. package/dist/render/eval.d.ts +2 -1
  317. package/dist/render/eval.d.ts.map +1 -1
  318. package/dist/render/eval.js +85 -43
  319. package/dist/render/eval.js.map +1 -1
  320. package/dist/render/friendly.d.ts +2 -1
  321. package/dist/render/friendly.d.ts.map +1 -1
  322. package/dist/render/friendly.js +14 -6
  323. package/dist/render/friendly.js.map +1 -1
  324. package/dist/render/genres.d.ts +5 -0
  325. package/dist/render/genres.d.ts.map +1 -0
  326. package/dist/render/genres.js +28 -0
  327. package/dist/render/genres.js.map +1 -0
  328. package/dist/render/plural.d.ts +3 -0
  329. package/dist/render/plural.d.ts.map +1 -0
  330. package/dist/render/plural.js +5 -0
  331. package/dist/render/plural.js.map +1 -0
  332. package/dist/render/rules-json.d.ts.map +1 -1
  333. package/dist/render/rules-json.js +9 -3
  334. package/dist/render/rules-json.js.map +1 -1
  335. package/dist/render/semantic.d.ts +3 -2
  336. package/dist/render/semantic.d.ts.map +1 -1
  337. package/dist/render/semantic.js +94 -34
  338. package/dist/render/semantic.js.map +1 -1
  339. package/dist/render/summary.d.ts.map +1 -1
  340. package/dist/render/summary.js +4 -3
  341. package/dist/render/summary.js.map +1 -1
  342. package/dist/render/suppressions.d.ts.map +1 -1
  343. package/dist/render/suppressions.js +3 -2
  344. package/dist/render/suppressions.js.map +1 -1
  345. package/dist/render/text.d.ts +2 -0
  346. package/dist/render/text.d.ts.map +1 -1
  347. package/dist/render/text.js +11 -3
  348. package/dist/render/text.js.map +1 -1
  349. package/dist/resolve-genre.d.ts +11 -0
  350. package/dist/resolve-genre.d.ts.map +1 -0
  351. package/dist/resolve-genre.js +18 -0
  352. package/dist/resolve-genre.js.map +1 -0
  353. package/dist/rule-load.d.ts.map +1 -1
  354. package/dist/rule-load.js +4 -1
  355. package/dist/rule-load.js.map +1 -1
  356. package/dist/run-semantic.js +1 -1
  357. package/dist/run-semantic.js.map +1 -1
  358. package/dist/run.d.ts +2 -0
  359. package/dist/run.d.ts.map +1 -1
  360. package/dist/run.js +25 -15
  361. package/dist/run.js.map +1 -1
  362. package/dist/sentence-shape.d.ts +0 -1
  363. package/dist/sentence-shape.d.ts.map +1 -1
  364. package/dist/sentence-shape.js +3 -5
  365. package/dist/sentence-shape.js.map +1 -1
  366. package/dist/setting-problems.d.ts +5 -0
  367. package/dist/setting-problems.d.ts.map +1 -0
  368. package/dist/setting-problems.js +15 -0
  369. package/dist/setting-problems.js.map +1 -0
  370. package/dist/soft-break.d.ts +19 -0
  371. package/dist/soft-break.d.ts.map +1 -0
  372. package/dist/soft-break.js +78 -0
  373. package/dist/soft-break.js.map +1 -0
  374. package/dist/structure/build.d.ts.map +1 -1
  375. package/dist/structure/build.js +24 -14
  376. package/dist/structure/build.js.map +1 -1
  377. package/dist/structure/cite.js +1 -1
  378. package/dist/structure/cite.js.map +1 -1
  379. package/dist/structure/date-order.d.ts +3 -0
  380. package/dist/structure/date-order.d.ts.map +1 -1
  381. package/dist/structure/date-order.js +9 -1
  382. package/dist/structure/date-order.js.map +1 -1
  383. package/dist/structure/issues.d.ts +1 -1
  384. package/dist/structure/issues.d.ts.map +1 -1
  385. package/dist/structure/issues.js +19 -8
  386. package/dist/structure/issues.js.map +1 -1
  387. package/dist/structure/listed-tags.d.ts +3 -0
  388. package/dist/structure/listed-tags.d.ts.map +1 -0
  389. package/dist/structure/listed-tags.js +6 -0
  390. package/dist/structure/listed-tags.js.map +1 -0
  391. package/dist/structure/markdown-path.d.ts.map +1 -1
  392. package/dist/structure/markdown-path.js +2 -2
  393. package/dist/structure/markdown-path.js.map +1 -1
  394. package/dist/structure/number-in-sentence.d.ts +15 -0
  395. package/dist/structure/number-in-sentence.d.ts.map +1 -0
  396. package/dist/structure/number-in-sentence.js +13 -0
  397. package/dist/structure/number-in-sentence.js.map +1 -0
  398. package/dist/structure/numbering.d.ts.map +1 -1
  399. package/dist/structure/numbering.js +1 -1
  400. package/dist/structure/numbering.js.map +1 -1
  401. package/dist/structure/relative-find.js +1 -1
  402. package/dist/structure/relative-find.js.map +1 -1
  403. package/dist/structure/sexp.js +1 -1
  404. package/dist/structure/sexp.js.map +1 -1
  405. package/dist/structure/unit-word.d.ts +8 -0
  406. package/dist/structure/unit-word.d.ts.map +1 -0
  407. package/dist/structure/unit-word.js +10 -0
  408. package/dist/structure/unit-word.js.map +1 -0
  409. package/dist/structure/unnumbered.js +2 -2
  410. package/dist/structure/unnumbered.js.map +1 -1
  411. package/dist/structure/wrapped-tail.d.ts +18 -0
  412. package/dist/structure/wrapped-tail.d.ts.map +1 -0
  413. package/dist/structure/wrapped-tail.js +11 -0
  414. package/dist/structure/wrapped-tail.js.map +1 -0
  415. package/dist/team-names.d.ts +9 -0
  416. package/dist/team-names.d.ts.map +1 -0
  417. package/dist/team-names.js +34 -0
  418. package/dist/team-names.js.map +1 -0
  419. package/dist/ui.d.ts +2 -0
  420. package/dist/ui.d.ts.map +1 -1
  421. package/dist/ui.js +6 -0
  422. package/dist/ui.js.map +1 -1
  423. package/dist/version.js +1 -1
  424. package/dist/version.js.map +1 -1
  425. package/dist/watch.d.ts +2 -2
  426. package/dist/watch.d.ts.map +1 -1
  427. package/dist/watch.js +5 -5
  428. package/dist/watch.js.map +1 -1
  429. package/genres.yaml +363 -0
  430. package/package.json +4 -3
  431. package/rules/adverb-overuse.yaml +2 -2
  432. package/rules/agentless-passive.yaml +1 -1
  433. package/rules/ai-generated-composite.yaml +2 -2
  434. package/rules/ai-tell.yaml +2 -2
  435. package/rules/bold-density.yaml +2 -2
  436. package/rules/closing-cliche.yaml +1 -1
  437. package/rules/concrete-evidence-density.yaml +2 -2
  438. package/rules/contraction-consistency.yaml +1 -1
  439. package/rules/cushion-phrase-density.yaml +1 -1
  440. package/rules/dangling-reference.yaml +3 -3
  441. package/rules/date-order.yaml +3 -3
  442. package/rules/date-weekday-mismatch.yaml +1 -1
  443. package/rules/double-keigo.yaml +1 -1
  444. package/rules/doubled-word.yaml +1 -1
  445. package/rules/duplicate-definition.yaml +1 -1
  446. package/rules/emoji-density.yaml +1 -1
  447. package/rules/empty-conclusion.yaml +2 -1
  448. package/rules/empty-intensifier.yaml +1 -1
  449. package/rules/excessive-hedging.yaml +16 -6
  450. package/rules/expletive-construction.yaml +2 -2
  451. package/rules/heading-echo.yaml +1 -1
  452. package/rules/hiragana-fukushi.yaml +1 -1
  453. package/rules/internal-jargon.yaml +2 -2
  454. package/rules/latin-spacing.yaml +6 -3
  455. package/rules/max-kanji-continuous.yaml +5 -4
  456. package/rules/max-paragraph-length.yaml +9 -2
  457. package/rules/max-sentence-length.yaml +14 -2
  458. package/rules/ngram-repetition.yaml +4 -4
  459. package/rules/no-doubled-joshi.yaml +4 -4
  460. package/rules/no-em-dash.yaml +2 -2
  461. package/rules/no-mixed-desumasu.yaml +1 -1
  462. package/rules/no-nakaguro-parallel.yaml +2 -2
  463. package/rules/numbering-gap.yaml +1 -1
  464. package/rules/oxford-comma-consistency.yaml +2 -1
  465. package/rules/padded-intro.yaml +2 -1
  466. package/rules/paragraph-length-variance.yaml +1 -1
  467. package/rules/preamble-length.yaml +2 -2
  468. package/rules/preferred-term.yaml +1 -1
  469. package/rules/proper-noun-density.yaml +3 -2
  470. package/rules/repeated-conjunction.yaml +2 -2
  471. package/rules/repeated-sentence-head.yaml +2 -2
  472. package/rules/required-sections.yaml +1 -1
  473. package/rules/rule-of-three.yaml +2 -2
  474. package/rules/sasete-itadaku.yaml +3 -3
  475. package/rules/section-length-uniformity.yaml +1 -1
  476. package/rules/sentence-initial-conjunction-run.yaml +2 -2
  477. package/rules/sentence-rhythm.yaml +1 -1
  478. package/rules/taigen-dome-in-prose.yaml +2 -2
  479. package/rules/title-case-consistency.yaml +3 -3
  480. package/rules/total-mismatch.yaml +1 -1
  481. package/rules/undefined-acronym.yaml +18 -5
  482. package/rules/unqualified-superlative.yaml +3 -3
  483. package/skills/chaff/SKILL.md +28 -9
  484. package/src/address-chain.ts +6 -3
  485. package/src/backends/anthropic.ts +5 -1
  486. package/src/backends/openai.ts +2 -1
  487. package/src/backends/types.ts +3 -2
  488. package/src/baseline.ts +1 -1
  489. package/src/calendar-number.ts +75 -0
  490. package/src/cli-args.ts +2 -2
  491. package/src/cli-text.ts +48 -18
  492. package/src/cli.ts +51 -47
  493. package/src/commands/cite.ts +4 -4
  494. package/src/commands/eval.ts +27 -6
  495. package/src/commands/feedback.ts +2 -1
  496. package/src/commands/init-ask.ts +53 -0
  497. package/src/commands/test.ts +105 -47
  498. package/src/commands/tree.ts +9 -7
  499. package/src/config/by-path.ts +8 -9
  500. package/src/config/load.ts +24 -0
  501. package/src/config/name-problems.ts +15 -0
  502. package/src/date-stamp.ts +1 -1
  503. package/src/detectors/acronym-compound.ts +9 -0
  504. package/src/detectors/acronym-context.ts +22 -1
  505. package/src/detectors/acronym-expansion.ts +72 -5
  506. package/src/detectors/char-shape.ts +56 -16
  507. package/src/detectors/citation-key.ts +13 -0
  508. package/src/detectors/cited-name.ts +23 -0
  509. package/src/detectors/concrete-evidence.ts +31 -0
  510. package/src/detectors/counter-edge.ts +29 -0
  511. package/src/detectors/dotted-name.ts +14 -0
  512. package/src/detectors/doubled-word.ts +25 -2
  513. package/src/detectors/en-shape.ts +123 -38
  514. package/src/detectors/enumerated-runs.ts +3 -2
  515. package/src/detectors/gram-place.ts +9 -2
  516. package/src/detectors/gram-predicate.ts +55 -0
  517. package/src/detectors/heading-case.ts +44 -0
  518. package/src/detectors/heading-echo.ts +5 -1
  519. package/src/detectors/index.ts +4 -3
  520. package/src/detectors/lettered-index.ts +78 -0
  521. package/src/detectors/lexicon.ts +68 -8
  522. package/src/detectors/name-numeral.ts +37 -0
  523. package/src/detectors/name-part.ts +24 -0
  524. package/src/detectors/name-run.ts +16 -0
  525. package/src/detectors/orthography.ts +43 -10
  526. package/src/detectors/phrase-match.ts +23 -12
  527. package/src/detectors/placeholder.ts +38 -0
  528. package/src/detectors/quoted-variant.ts +20 -0
  529. package/src/detectors/repeated-head.ts +3 -4
  530. package/src/detectors/series-label.ts +33 -0
  531. package/src/detectors/signals.ts +49 -35
  532. package/src/detectors/stacked-hedge.ts +93 -0
  533. package/src/detectors/structure-tree.ts +2 -2
  534. package/src/detectors/superlative-amount.ts +24 -0
  535. package/src/detectors/superlative-clause.ts +82 -0
  536. package/src/detectors/superlative-name.ts +30 -0
  537. package/src/detectors/superlative-scope.ts +3 -3
  538. package/src/detectors/team.ts +18 -2
  539. package/src/detectors/token-shape.ts +13 -6
  540. package/src/document.ts +41 -8
  541. package/src/eval.ts +1 -1
  542. package/src/feedback/draft.ts +1 -1
  543. package/src/feedback/excerpt.ts +2 -2
  544. package/src/file-header.ts +31 -0
  545. package/src/files.ts +3 -3
  546. package/src/genre-check.ts +46 -0
  547. package/src/genre-load.ts +24 -0
  548. package/src/genre-parse.ts +126 -0
  549. package/src/genre-suggest.ts +37 -0
  550. package/src/genre.ts +25 -5
  551. package/src/init-choice.ts +47 -0
  552. package/src/init.ts +20 -19
  553. package/src/joined-view.ts +96 -0
  554. package/src/judge.ts +3 -1
  555. package/src/line-continues.ts +43 -0
  556. package/src/line-paragraphs.ts +83 -0
  557. package/src/mask.ts +5 -6
  558. package/src/measure.ts +9 -1
  559. package/src/number-name.ts +35 -13
  560. package/src/orthography.ts +15 -9
  561. package/src/plain-source.ts +5 -0
  562. package/src/plugin.ts +27 -2
  563. package/src/profile/check.ts +34 -0
  564. package/src/profile/for-file.ts +10 -3
  565. package/src/profile/load.ts +1 -1
  566. package/src/profile/select.ts +11 -4
  567. package/src/quoted-span.ts +39 -0
  568. package/src/reasons.ts +5 -1
  569. package/src/render/compact.ts +1 -3
  570. package/src/render/eval.ts +102 -38
  571. package/src/render/friendly.ts +21 -6
  572. package/src/render/genres.ts +31 -0
  573. package/src/render/plural.ts +6 -0
  574. package/src/render/rules-json.ts +10 -3
  575. package/src/render/semantic.ts +117 -34
  576. package/src/render/summary.ts +4 -3
  577. package/src/render/suppressions.ts +3 -2
  578. package/src/render/text.ts +18 -3
  579. package/src/resolve-genre.ts +20 -0
  580. package/src/rule-load.ts +6 -1
  581. package/src/run-semantic.ts +1 -1
  582. package/src/run.ts +28 -15
  583. package/src/sentence-shape.ts +3 -7
  584. package/src/setting-problems.ts +24 -0
  585. package/src/soft-break.ts +94 -0
  586. package/src/structure/build.ts +24 -15
  587. package/src/structure/cite.ts +1 -1
  588. package/src/structure/date-order.ts +16 -1
  589. package/src/structure/issues.ts +24 -8
  590. package/src/structure/listed-tags.ts +7 -0
  591. package/src/structure/markdown-path.ts +2 -2
  592. package/src/structure/number-in-sentence.ts +20 -0
  593. package/src/structure/numbering.ts +1 -1
  594. package/src/structure/relative-find.ts +1 -1
  595. package/src/structure/sexp.ts +1 -1
  596. package/src/structure/unit-word.ts +11 -0
  597. package/src/structure/unnumbered.ts +2 -2
  598. package/src/structure/wrapped-tail.ts +22 -0
  599. package/src/team-names.ts +39 -0
  600. package/src/ui.ts +7 -0
  601. package/src/version.ts +1 -1
  602. package/src/watch.ts +5 -5
@@ -1,12 +1,13 @@
1
+ import { hyphenGroups, isHyphen } from "./orthography.ts";
1
2
  import type { Span, Token } from "./plugin.ts";
2
3
 
3
4
  /**
4
5
  * 数量ではなく名前として書かれた数(番号・識別子)。鉤括弧で引いた「3.1 リサーチの原則」の節番号、※1 のような注の番号、
5
- * 「〒102-0094 東京都」の郵便番号は、後ろの空白が番号と題・項目の区切りで、「3回」「3 回」のような空け方の好みではない。
6
+ * 「〒100-8916 東京都」の郵便番号、「紀尾井町1-3 東京ガーデンテラス」の番地は、後ろの空白が番号と題・項目の区切りで、「3回」「3 回」のような空け方の好みではない。
6
7
  * 空け方を数える材料にしない。
7
8
  *
8
- * 形で名前と読むのは四つだけ。0 で始まる組を含むハイフンつなぎの番号、※ や鉤括弧のすぐ後ろの番号、文頭の階層つきの節番号(2.1、3.1.2)、
9
- * 続き番号になった行の頭の番号(白書の注)。文頭でも「223 言語」「1ターン」は数量なので、階層の無い数は文頭というだけでは名前と読まない。
9
+ * 形で名前と読むのは五つだけ。0 で始まる組を含むハイフンつなぎの番号、※・〒や鉤括弧のすぐ後ろの番号、市・区・町まで下りた地名のすぐ後ろのハイフンつなぎの番号、
10
+ * 文頭の階層つきの節番号(2.1、3.1.2)、続き番号になった行の頭の番号(白書の注)。文頭でも「223 言語」「1ターン」は数量なので、階層の無い数は文頭というだけでは名前と読まない。
10
11
  * そのうえで、すぐ後ろ(空白 1 つまで)の語が数につく語(助数詞・数・助詞・助動詞)なら数量として数え続ける(「3-5 日」「26.7 万行」)。
11
12
  * 品詞が無ければ判断せず、名前とは読まない。
12
13
  */
@@ -14,8 +15,8 @@ import type { Span, Token } from "./plugin.ts";
14
15
  /** 数と結びついて読まれる語。助数詞は NounType=Class、数は NumType=Card で渡る。 */
15
16
  const BOUND_TO_NUMBER = new Set(["ADP", "PART", "AUX", "SCONJ", "CCONJ"]);
16
17
 
17
- /** 番号が立つ位置の直前: 題を包む鉤括弧、注の印。 */
18
- const LABEL_LEAD = /[「『【※]$/u;
18
+ /** 番号が立つ位置の直前: 題を包む鉤括弧、注の印、郵便番号の印。 */
19
+ const LABEL_LEAD = /[「『【※〒]$/u;
19
20
 
20
21
  /** 文頭(前に空白と箇条書きの印だけ)。 */
21
22
  const LINE_HEAD = /^[\s\-*+・•]*$/u;
@@ -28,17 +29,18 @@ const SECTION_NUMBER = /^\d+(?:\.\d+)+$/u;
28
29
  * 数え続ける。三つ以上つないだもの(073-489-5909)は、orthography.ts が境目を作る前に外している。
29
30
  */
30
31
  const isHyphenIdentifier = (digits: string): boolean => {
31
- const parts = digits.split("-").filter((part) => part !== "");
32
+ const parts = hyphenGroups(digits);
32
33
  return parts.length > 1 && parts.some((part) => part.startsWith("0"));
33
34
  };
34
35
 
35
- const RUN_CHAR = /[\d.-]/u;
36
+ const DIGIT_OR_POINT = /[\d.]/u;
37
+ const isRunChar = (char: string | undefined): boolean => DIGIT_OR_POINT.test(char ?? "") || isHyphen(char);
36
38
 
37
- /** at を含む、数字・小数点・ハイフンの並び。at が並びの中に無ければ undefined。 */
39
+ /** at を含む、数字・小数点・ハイフン(全角の「-」なども)の並び。at が並びの中に無ければ undefined。 */
38
40
  export const digitRunAround = (text: string, at: number): Span | undefined => {
39
- if (!RUN_CHAR.test(text[at] ?? "")) return undefined;
40
- const startAt = (index: number): number => (index > 0 && RUN_CHAR.test(text[index - 1] ?? "") ? startAt(index - 1) : index);
41
- const endAt = (index: number): number => (index < text.length && RUN_CHAR.test(text[index] ?? "") ? endAt(index + 1) : index);
41
+ if (!isRunChar(text[at])) return undefined;
42
+ const startAt = (index: number): number => (index > 0 && isRunChar(text[index - 1]) ? startAt(index - 1) : index);
43
+ const endAt = (index: number): number => (index < text.length && isRunChar(text[index]) ? endAt(index + 1) : index);
42
44
  return { start: startAt(at), end: endAt(at) };
43
45
  };
44
46
 
@@ -71,6 +73,24 @@ export const sequenceLabelStarts = (text: string): ReadonlySet<number> => {
71
73
  return new Set(lines.flatMap((line, index) => (continues(index) ? [line.start] : [])));
72
74
  };
73
75
 
76
+ const isGeoUnit = (token: Token): boolean => token.features?.["NameType"] === "GeoUnit";
77
+ const isPlaceWord = (token: Token): boolean => isGeoUnit(token) || token.features?.["NameType"] === "Geo";
78
+
79
+ /** end で終わる、間を空けずに続く地名と地名の単位(東京都千代田区紀尾井町)。近いほうから。 */
80
+ export const placeChainBefore = (tokens: readonly Token[], end: number): Token[] => {
81
+ const last = tokens.find((token) => token.span.end === end && isPlaceWord(token));
82
+ return last === undefined ? [] : [last, ...placeChainBefore(tokens, last.span.start)];
83
+ };
84
+
85
+ /**
86
+ * 住所の番地(千代田区紀尾井町1-3、霞が関2-1)。都道府県より下の単位(市・区・町)まで下りた地名のすぐ後ろの、ハイフンつなぎの番号。
87
+ * 地名だけ(北海道2-3 営業日)や都道府県まで(東京都2-3 営業日)の後ろは、地域ごとの範囲のことがあるので読まない。
88
+ * ハイフンの無い数(千代田区23 番)も読まない。topUnits は都道府県の単位(語彙表 prefecture-unit)。
89
+ */
90
+ const isAddressNumber = (text: string, run: Span, tokens: readonly Token[], base: number, topUnits: ReadonlySet<string>): boolean =>
91
+ [...text.slice(run.start, run.end)].some(isHyphen) &&
92
+ placeChainBefore(tokens, base + run.start).some((token) => isGeoUnit(token) && !topUnits.has(token.surface));
93
+
74
94
  /** ハイフンでつないだ識別子か、番号の立つ位置の番号か、文頭の節番号か。 */
75
95
  const isNameShaped = (text: string, run: Span): boolean => {
76
96
  const [before, digits] = [text.slice(0, run.start), text.slice(run.start, run.end)];
@@ -78,7 +98,8 @@ const isNameShaped = (text: string, run: Span): boolean => {
78
98
  };
79
99
 
80
100
  /**
81
- * text の run が名前として書かれた数か。tokens は文書全体の座標で、base は text の先頭の位置。sequence は sequenceLabelStarts の結果。
101
+ * text の run が名前として書かれた数か。tokens は文書全体の座標で、base は text の先頭の位置。sequence は sequenceLabelStarts の結果、
102
+ * topUnits は都道府県の単位。
82
103
  * 後ろの語が読めない(品詞が無い、語の切れ目が合わない)ときは、数量として数え続ける。
83
104
  */
84
105
  export const isNumberName = (
@@ -87,9 +108,10 @@ export const isNumberName = (
87
108
  tokens: readonly Token[] | undefined,
88
109
  base: number,
89
110
  sequence: ReadonlySet<number> = new Set(),
111
+ topUnits: ReadonlySet<string> = new Set(),
90
112
  ): boolean => {
91
113
  const inSequence = sequence.has(base + run.start);
92
- if (tokens === undefined || !(inSequence || isNameShaped(text, run))) return false;
114
+ if (tokens === undefined || !(inSequence || isNameShaped(text, run) || isAddressNumber(text, run, tokens, base, topUnits))) return false;
93
115
  const next = wordAfter(tokens, text, run, base);
94
116
  if (next === undefined) return false;
95
117
  return !isBoundToNumber(next) || (inSequence && CONJUNCTIONS.has(next.pos));
@@ -125,22 +125,28 @@ const isOrdinalRun = (chars: readonly string[], index: number): boolean => {
125
125
  };
126
126
 
127
127
  /**
128
- * 「073-489-5909」「2026-06-02」「Ⅰ-4-1-3」のように - でつないだ 3 組以上の数字は、電話番号・日付・図の番号で
128
+ * 「073-489-5909」「2026-06-02」「Ⅰ-4-1-3」「5-1-1」のように - でつないだ 3 組以上の数字は、電話番号・日付・図や項目の番号で
129
129
  * 数量ではない。後ろの空白は欄の区切りで、「3回」の空け方とは別のもの。両側とも数えない。
130
130
  * 2 組(「1-3ヶ月」「20-30分」)は幅のある数量なので数える。
131
131
  */
132
132
  const MIN_CODE_GROUPS = 3;
133
- const CODE_CHAR = /[\d-]/u;
133
+
134
+ // 通達の「5-1-1」は全角の「-」でつなぐ。ハイフン「‐」やマイナス「−」も同じに読む。長音「ー」は仮名の一部なので入れない。
135
+ const HYPHEN = /[-‐‑−-]/u;
136
+
137
+ /** 数字をつなぐハイフンか。半角の - のほか、全角・和文のハイフンも。 */
138
+ export const isHyphen = (char: string | undefined): boolean => char !== undefined && HYPHEN.test(char);
139
+
140
+ /** ハイフンで区切った組(空の組は除く)。「5-1-1」は ["5", "1", "1"]。 */
141
+ export const hyphenGroups = (text: string): string[] => text.split(HYPHEN).filter((group) => group !== "");
142
+
143
+ const isCodeChar = (char: string | undefined): boolean => DIGIT.test(char ?? "") || isHyphen(char);
134
144
 
135
145
  const isCode = (chars: readonly string[], digit: number): boolean => {
136
146
  let [first, last] = [digit, digit];
137
- while (first > 0 && CODE_CHAR.test(chars[first - 1] ?? "")) first -= 1;
138
- while (last < chars.length - 1 && CODE_CHAR.test(chars[last + 1] ?? "")) last += 1;
139
- const groups = chars
140
- .slice(first, last + 1)
141
- .join("")
142
- .split("-");
143
- return groups.filter((group) => group !== "").length >= MIN_CODE_GROUPS;
147
+ while (first > 0 && isCodeChar(chars[first - 1])) first -= 1;
148
+ while (last < chars.length - 1 && isCodeChar(chars[last + 1])) last += 1;
149
+ return hyphenGroups(chars.slice(first, last + 1).join("")).length >= MIN_CODE_GROUPS;
144
150
  };
145
151
 
146
152
  /** 日本語との境目にある数字が、数えない書き方(「第3条」の番地、「073-489-5909」の符号)か。 */
@@ -0,0 +1,5 @@
1
+ /**
2
+ * The Markdown parser drops a leading BOM and reads a lone "\r" as a line break; the rest of chaff counts "\n" only.
3
+ * Reading the file this way keeps every offset, line and column on the text the writer sees.
4
+ */
5
+ export const plainSource = (raw: string): string => raw.replace(/^\uFEFF/u, "").replace(/\r\n?/gu, "\n");
package/src/plugin.ts CHANGED
@@ -8,7 +8,16 @@ export type Span = { readonly start: number; readonly end: number };
8
8
  * token の span は文の span と同じ座標系(segment に渡した文字列の先頭が 0)。
9
9
  * 2 つの基準を混ぜると、ずれたときに どちらが悪いか分からなくなる。
10
10
  */
11
- export type Sentence = { readonly span: Span; readonly text: string; readonly tokens?: readonly Token[] };
11
+ export type Sentence = {
12
+ readonly span: Span;
13
+ readonly text: string;
14
+ readonly tokens?: readonly Token[];
15
+ /**
16
+ * 行の折り返しで語の途中に入った改行(前後の行頭・行末の空白ごと)。span と同じ座標系。chaff が入れる。
17
+ * tokens は全角どうしに挟まれた段落内の改行を除いた文字列から作るので、改行をまたぐ語の span は改行ごと覆い、surface より長い。
18
+ */
19
+ readonly wrapBreaks?: readonly Span[];
20
+ };
12
21
 
13
22
  /**
14
23
  * 品詞は Universal Dependencies の UPOS に統一する。アダプタ独自の体系を露出させない。
@@ -133,7 +142,10 @@ export type StructurePatterns = {
133
142
  readonly numbered: (line: string, context: NumberingContext) => NumberedLine | undefined;
134
143
  /** 定義。attrs.term に定義された語。 */
135
144
  readonly definitions: (text: string) => readonly Mention[];
136
- /** 参照。attrs.target に正規化した番地("12.1")、attrs.label に書かれたまま。 */
145
+ /**
146
+ * 参照。attrs.target に正規化した番地("12.1")、attrs.label に書かれたまま。他の文書を指すなら attrs.document にその名。
147
+ * 書き方だけでは他の文書か決まらない角括弧の語は attrs.citedTag に入れ、文書がその語を一覧に載せていれば core が他の文書とみなす。
148
+ */
137
149
  readonly references: (text: string) => readonly Mention[];
138
150
  /** 義務・禁止・許可。attrs.marker に語、attrs.type に must / must-not / may。 */
139
151
  readonly obligations: (text: string) => readonly Mention[];
@@ -156,6 +168,11 @@ export type StructurePatterns = {
156
168
  * 数字と点だけの通し番号は core が言語を問わず読むので、それを数量と見分けられるのは言語パッケージだけ。
157
169
  */
158
170
  readonly countedAfter?: (number: string, rest: string) => boolean;
171
+ /**
172
+ * 本文の行頭の「3.11.0 を公開しました。」の 3.11.0 は、文の中の数で通し番号ではない。番号と後ろの文字列を渡し、文の続き
173
+ * (日本語なら助詞で始まる)なら true。小文字で始まる続きは core が言語を問わず読む。
174
+ */
175
+ readonly continuesSentence?: (number: string, rest: string) => boolean;
159
176
  /** 数の書き方(「二十二」「3」)を数にする。相対の参照の「前二項」「前条第二項」が使う。 */
160
177
  readonly number?: (text: string) => number | undefined;
161
178
  };
@@ -291,10 +308,14 @@ export type ProseDocument = {
291
308
  readonly paragraphs: readonly Paragraph[];
292
309
  /** 箇条書き 1 つ。項目の数と長さのばらつきを見る rule が使う。 */
293
310
  readonly lists: readonly BulletList[];
311
+ /** リンク(`[text](url)`、`<https://…>`、`[text][ref]`)の範囲。行き先が相対パスでもページ内でも、読み手が辿れる出典。 */
312
+ readonly links: readonly Span[];
294
313
  /** アダプタが持つ語彙表と、チームが chaff.yaml に足した語彙表。detector は出所を知らない。 */
295
314
  readonly lexicons: Readonly<Record<string, Lexicon>>;
296
315
  /** この種類の文書に無いと困る見出し。チームが chaff.yaml で決める。 */
297
316
  readonly requiredSections: readonly string[];
317
+ /** チームが chaff.yaml の names に並べた固有名詞。1 つの名前として読み、書き手の落ち度に数えない。 */
318
+ readonly names?: readonly string[];
298
319
  /** 番地の付いた木(§27)。adapter が structure を持たない言語では無い。 */
299
320
  readonly structure: StructureNode | undefined;
300
321
  /** 文書の種類(法令など)。選ばれなければ無い。 */
@@ -314,6 +335,8 @@ export type Finding = {
314
335
  readonly quote: string;
315
336
  /** message のプレースホルダに入れる値。 */
316
337
  readonly values: Readonly<Record<string, string | number>>;
338
+ /** 1 つの rule が 2 通りの見つけ方を持つとき、どちらで見つけたか。rule の messages から文を選ぶ。無ければ message。 */
339
+ readonly variant?: string;
317
340
  };
318
341
 
319
342
  /** rule 定義が threshold を渡す。数値は 4 語から解決済み。 */
@@ -341,6 +364,8 @@ export type RuleDefinition = {
341
364
  readonly why: Localized;
342
365
  readonly how_to_fix: Localized;
343
366
  readonly message: Localized;
367
+ /** 見つけ方ごとの message(Finding の variant で引く)。ほとんどの rule は持たない。 */
368
+ readonly messages: Readonly<Record<string, Localized>>;
344
369
  /** 4 語と数値の対応。2 つ以上。未定義の段は normal に落ちる。spec §18.1。
345
370
  * 言語別の閾値を持つ rule(max-sentence-length)は、読み込み時に言語で平坦化済み。 */
346
371
  readonly levels: LevelTable;
@@ -0,0 +1,34 @@
1
+ import { NO_PROFILE } from "./select.ts";
2
+
3
+ /** Where a profile was set: chaff.yaml's profile, or one of its by_path entries. */
4
+ export type ProfileSetting = "config" | "by_path";
5
+
6
+ type UnknownProfile = {
7
+ readonly where: ProfileSetting;
8
+ readonly profile: string;
9
+ /** The by_path entry's files, so the message can say which entry; empty for chaff.yaml's profile. */
10
+ readonly files: readonly string[];
11
+ };
12
+
13
+ type PathProfile = { readonly files: readonly string[]; readonly profile?: string | undefined };
14
+
15
+ type WrittenProfiles = { readonly config: string | undefined; readonly byPath: readonly PathProfile[] };
16
+
17
+ /** Commands that never read a document, so never choose a profile. A wrong one in chaff.yaml must not stop them. */
18
+ const PROFILE_FREE: ReadonlySet<string> = new Set(["init", "genres", "skill", "relax", "strict", "off", "rules", "explain"]);
19
+
20
+ /** The profiles a command would read from chaff.yaml: none for a command that reads no document. */
21
+ export const writtenProfiles = (command: string, config: { readonly profile: string | undefined; readonly byPath: readonly PathProfile[] }): WrittenProfiles =>
22
+ PROFILE_FREE.has(command) ? { config: undefined, byPath: [] } : { config: config.profile, byPath: config.byPath };
23
+
24
+ const setting = (where: ProfileSetting, profile: string | undefined, files: readonly string[] = []): UnknownProfile[] =>
25
+ profile === undefined ? [] : [{ where, profile, files }];
26
+
27
+ /**
28
+ * Every profile a setting names that is neither a bundled one nor none. Such a name chooses nothing and also stops the choice from the content,
29
+ * so it silently acts as none. All of them, not only the one a file would use, because a wrong setting is wrong for every file.
30
+ */
31
+ export const unknownProfiles = (written: WrittenProfiles, known: readonly string[]): UnknownProfile[] =>
32
+ [...setting("config", written.config), ...written.byPath.flatMap((entry) => setting("by_path", entry.profile, entry.files))].filter(
33
+ (entry) => entry.profile !== NO_PROFILE && !known.includes(entry.profile),
34
+ );
@@ -1,11 +1,18 @@
1
1
  import { applyByPath } from "../config/by-path.ts";
2
2
  import type { Config } from "../config/load.ts";
3
3
  import type { DocumentProfile } from "../plugin.ts";
4
+ import { presetProfile } from "../genre-load.ts";
4
5
  import { loadProfiles } from "./load.ts";
5
6
  import { chooseProfile } from "./select.ts";
6
7
 
7
8
  type ProfileSettings = Pick<Config, "profile" | "byPath" | "baseDir">;
8
9
 
9
- /** 1 ファイルの文書の種類。by_path、chaff.yaml、内容の順に選ぶ。 */
10
- export const profileFor = (config: ProfileSettings, path: string, source: string, language: string): DocumentProfile | undefined =>
11
- chooseProfile(loadProfiles(), { byPath: applyByPath(config.byPath, config.baseDir, path).profile, config: config.profile, source, language })?.profile;
10
+ /** 1 ファイルの文書の種類。by_path、chaff.yaml、ジャンル、内容の順に選ぶ。 */
11
+ export const profileFor = (config: ProfileSettings, path: string, source: string, language: string, genre?: string): DocumentProfile | undefined =>
12
+ chooseProfile(loadProfiles(), {
13
+ byPath: applyByPath(config.byPath, config.baseDir, path).profile,
14
+ config: config.profile,
15
+ genre: genre === undefined ? undefined : presetProfile(genre),
16
+ source,
17
+ language,
18
+ })?.profile;
@@ -12,7 +12,7 @@ const loaded: { value: readonly ProfileDefinition[] | undefined } = { value: und
12
12
  export const loadProfiles = (): readonly ProfileDefinition[] => {
13
13
  loaded.value ??= readdirSync(PROFILES_DIR)
14
14
  .filter((file) => file.endsWith(".yaml"))
15
- .sort((left, right) => left.localeCompare(right))
15
+ .toSorted((left, right) => left.localeCompare(right))
16
16
  .flatMap((file) => {
17
17
  const path = join(PROFILES_DIR, file);
18
18
  try {
@@ -4,13 +4,15 @@ import { detected, type ProfileDefinition } from "./parse.ts";
4
4
  /** 設定で書いた「使わない」。内容から選ぶのも止める。 */
5
5
  export const NO_PROFILE = "none";
6
6
 
7
- type ProfileSource = "by-path" | "config" | "content";
7
+ type ProfileSource = "by-path" | "config" | "genre" | "content";
8
8
 
9
9
  export type ChosenProfile = { readonly profile: DocumentProfile; readonly from: ProfileSource };
10
10
 
11
11
  export type ProfileRequest = {
12
12
  readonly byPath: string | undefined;
13
13
  readonly config: string | undefined;
14
+ /** The profile the file's genre reads with (genres.yaml), when it names one. */
15
+ readonly genre?: string | undefined;
14
16
  readonly source: string;
15
17
  readonly language: string;
16
18
  };
@@ -18,16 +20,21 @@ export type ProfileRequest = {
18
20
  const named = (definitions: readonly ProfileDefinition[], id: string, language: string): DocumentProfile | undefined =>
19
21
  definitions.find((definition) => definition.id === id)?.languages[language];
20
22
 
23
+ const writtenIn = (request: ProfileRequest): ProfileSource => {
24
+ if (request.byPath !== undefined) return "by-path";
25
+ return request.config === undefined ? "genre" : "config";
26
+ };
27
+
21
28
  /**
22
- * 強いものから: by_path、chaff.yaml の profile、内容。どれかが none と書けばそこで止め、内容からも選ばない。
29
+ * 強いものから: by_path、chaff.yaml の profile、ジャンルの profile、内容。どれかが none と書けばそこで止め、内容からも選ばない。
23
30
  * 名前で選んだ種類にその言語の中身が無ければ選ばない。黙って別の種類にしない。
24
31
  */
25
32
  export const chooseProfile = (definitions: readonly ProfileDefinition[], request: ProfileRequest): ChosenProfile | undefined => {
26
- const written = request.byPath ?? request.config;
33
+ const written = request.byPath ?? request.config ?? request.genre;
27
34
  if (written === NO_PROFILE) return undefined;
28
35
  if (written !== undefined) {
29
36
  const profile = named(definitions, written, request.language);
30
- return profile === undefined ? undefined : { profile, from: request.byPath === undefined ? "config" : "by-path" };
37
+ return profile === undefined ? undefined : { profile, from: writtenIn(request) };
31
38
  }
32
39
  const found = definitions.find((definition) => detected(definition, request.source, request.language));
33
40
  const profile = found?.languages[request.language];
@@ -0,0 +1,39 @@
1
+ import type { Sentence, Span } from "./plugin.ts";
2
+
3
+ /** 開きの字と、それを閉じる字。 */
4
+ type Marks = Readonly<Record<string, string>>;
5
+
6
+ /** 鉤括弧。日本語の引用と題名。 */
7
+ export const KAGI_MARKS: Marks = { "「": "」", "『": "』" };
8
+
9
+ /**
10
+ * 引用符も含めた、人の言葉を引く印。英語の "…" は開きと閉じが同じ字なので、開いている間に来たら閉じと読む。
11
+ * 一重の ’ は "don’t" の省略の印と見分けられないので入れない。
12
+ */
13
+ export const QUOTATION_MARKS: Marks = { ...KAGI_MARKS, "“": "”", '"': '"' };
14
+
15
+ type Open = { readonly closer: string; readonly at: number };
16
+ type Scan = { readonly open: readonly Open[]; readonly spans: readonly Span[] };
17
+
18
+ const step =
19
+ (marks: Marks) =>
20
+ (scan: Scan, char: string, at: number): Scan => {
21
+ const last = scan.open.at(-1);
22
+ if (last !== undefined && last.closer === char) return { open: scan.open.slice(0, -1), spans: [...scan.spans, { start: last.at + 1, end: at }] };
23
+ const closer = marks[char];
24
+ return closer === undefined ? scan : { open: [...scan.open, { closer, at }], spans: scan.spans };
25
+ };
26
+
27
+ /**
28
+ * 引いたものの中身の位置(既定は鉤括弧「」『』)。括弧そのものは含まない。入れ子はそれぞれを返す。
29
+ * 閉じない括弧と、開きと組にならない閉じ括弧は中身を作らない。位置は text の先頭を 0 とする。
30
+ * 括弧は BMP の字なので、UTF-16 の 1 単位ずつ見れば位置がそのまま使える。
31
+ */
32
+ export const quotedSpans = (text: string, marks: Marks = KAGI_MARKS): Span[] => [...text.split("").reduce<Scan>(step(marks), { open: [], spans: [] }).spans];
33
+
34
+ /** 文の中の、引いたものを括弧ごと、文書全体の座標で。 */
35
+ export const quotedIn = (sentence: Sentence, marks: Marks = KAGI_MARKS): Span[] =>
36
+ quotedSpans(sentence.text, marks).map((span) => ({ start: sentence.span.start + span.start - 1, end: sentence.span.start + span.end + 1 }));
37
+
38
+ /** inner が、spans のどれかにまるごと入っているか。 */
39
+ export const isWithinAny = (spans: readonly Span[], inner: Span): boolean => spans.some((span) => inner.start >= span.start && inner.end <= span.end);
package/src/reasons.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import type { Texts } from "./ui.ts";
2
+ import { formFor } from "./render/plural.ts";
2
3
 
3
4
  /** Why a rule did not run, in the document's language. Listed under the findings so "0 findings" is never a silent pass. */
4
5
  export type Reasons = {
@@ -8,6 +9,7 @@ export type Reasons = {
8
9
  readonly semantic: string;
9
10
  readonly experimental: string;
10
11
  readonly turnedOff: string;
12
+ readonly presetOff: (genre: string) => string;
11
13
  readonly noStructure: (language: string) => string;
12
14
  readonly unreadStructure: (clauses: number, units: number) => string;
13
15
  readonly noDetector: (name: string) => string;
@@ -27,6 +29,7 @@ export const REASONS: Texts<Reasons> = {
27
29
  semantic: "意味を読む検査のため(npx chaff test で動きます)",
28
30
  experimental: "まだ試験中のため",
29
31
  turnedOff: "設定で止めているため",
32
+ presetOff: (genre) => `ジャンル ${genre} では見ないため`,
30
33
  noStructure: (language) => `${language} のパッケージは文書の構造を読めないため`,
31
34
  unreadStructure: (clauses, units) =>
32
35
  `条項の番号が本文に ${String(clauses)} 個あるのに、番号として読めたのは ${String(units)} 個のため(深い字下げや、行が本文につながった文書)`,
@@ -40,9 +43,10 @@ export const REASONS: Texts<Reasons> = {
40
43
  semantic: "it reads meaning; npx chaff test runs it",
41
44
  experimental: "still experimental",
42
45
  turnedOff: "turned off in the settings",
46
+ presetOff: (genre) => `the ${genre} genre does not check it`,
43
47
  noStructure: (language) => `the ${language} package cannot read a document's structure`,
44
48
  unreadStructure: (clauses, units) =>
45
- `the text has ${String(clauses)} clause numbers but only ${String(units)} were read as numbered lines (deep indents, or lines run into the text)`,
49
+ `the text has ${String(clauses)} clause numbers but only ${String(units)} ${formFor(units, "was read as a numbered line", "were read as numbered lines")} (deep indents, or lines run into the text)`,
46
50
  noDetector: (name) => `no detector named ${name}`,
47
51
  noLexicon: (language, list) => `the ${language} package has no word list ${list}`,
48
52
  },
@@ -1,11 +1,9 @@
1
1
  import type { RuleDefinition } from "../plugin.ts";
2
2
  import type { RunResult } from "../run.ts";
3
3
  import { messageOf } from "./text.ts";
4
+ import { counted } from "./plural.ts";
4
5
  import { uiLanguageOf, type Texts } from "../ui.ts";
5
6
 
6
- /** 「1 finding」「2 findings」。 */
7
- const counted = (count: number, noun: string): string => `${String(count)} ${noun}${count === 1 ? "" : "s"}`;
8
-
9
7
  /**
10
8
  * 最後の集計の行は、画面のほかの部分と同じく文書の言語で出す。行ごとの重さ(warning / error)は、grep や別の道具が読むので英語のまま。
11
9
  */
@@ -1,62 +1,126 @@
1
- import { MIN_CORPUS, TARGET_HIT_RATE, type RuleReport } from "../eval.ts";
1
+ import { MIN_CORPUS, TARGET_HIT_RATE, type Point, type RuleReport } from "../eval.ts";
2
+ import type { Texts, UiLanguage } from "../ui.ts";
3
+ import { counted, formFor } from "./plural.ts";
2
4
 
3
5
  const pct = (part: number, whole: number): string => `${((part / Math.max(1, whole)) * 100).toFixed(1)}%`;
4
6
 
5
- const mark = (report: RuleReport, limit: number): string => {
6
- if (limit === report.current && limit === report.recommended) return " ← 現在 / 推奨";
7
- if (limit === report.current) return " ← 現在";
8
- if (limit === report.recommended) return " ← 推奨";
7
+ const TARGET = TARGET_HIT_RATE * 100;
8
+
9
+ /** In the English hint, a rule's line sits one YAML level under `rules:`. */
10
+ const EN_CONFIG_LEAD = " In chaff.yaml: ";
11
+ const YAML_INDENT = 2;
12
+
13
+ const TEXT: Texts<{
14
+ readonly current: string;
15
+ readonly recommended: string;
16
+ readonly both: string;
17
+ readonly point: (point: Point, share: string) => string;
18
+ readonly fits: string;
19
+ readonly nothingFits: string;
20
+ readonly over: (share: string) => string;
21
+ readonly recommend: (limit: number) => string;
22
+ readonly toConfig: (rule: string, limit: number) => string;
23
+ readonly measured: (paths: number, rules: number) => string;
24
+ readonly premise: readonly string[];
25
+ readonly smallCorpus: (total: number) => readonly string[];
26
+ readonly untouched: string;
27
+ }> = {
28
+ ja: {
29
+ current: " ← 現在",
30
+ recommended: " ← 推奨",
31
+ both: " ← 現在 / 推奨",
32
+ point: (point, share) =>
33
+ `${String(point.documents).padStart(3)} 文書 (${share.padStart(6)}) 指摘 ${String(point.findings).padStart(3)} 件 1万字あたり ${point.per10k.toFixed(1)}`,
34
+ fits: ` いまの閾値で目標(${TARGET}% 未満)を満たしています。`,
35
+ nothingFits: ` どの閾値でも目標を満たしません。この corpus に対して rule 自体が合っていない可能性があります。`,
36
+ over: (share) => ` いまの閾値では ${share} の文書が該当し、目標(${TARGET}% 未満)を超えています。`,
37
+ recommend: (limit) => ` 推奨: ${limit}`,
38
+ toConfig: (rule, limit) => ` chaff.yaml に書くなら: rules:\n ${rule}: ${limit}`,
39
+ measured: (paths, rules) => ` ${paths} ファイルを corpus として ${rules} 本の rule を測りました。`,
40
+ premise: [
41
+ " ここにある文書は「人間が書いて公開した良い文書」として扱っています。",
42
+ ` そこで多く発火する rule は、閾値が現実に合っていない疑いがあります(目標: ${TARGET}% 未満)。`,
43
+ ],
44
+ smallCorpus: (total) => [
45
+ ` ※ corpus が ${total} 文書しかありません。${TARGET}% を表すには ${MIN_CORPUS} 文書以上が要ります。`,
46
+ " いまは割合が「0 か全部」に振れるので、右端の密度(1万字あたりの指摘数)のほうを見てください。",
47
+ ],
48
+ untouched: ` 閾値は自動で書き換えていません。corpus が変われば答えも変わるためです。`,
49
+ },
50
+ en: {
51
+ current: " ← current",
52
+ recommended: " ← recommended",
53
+ both: " ← current / recommended",
54
+ point: (point, share) =>
55
+ `${String(point.documents).padStart(3)} ${formFor(point.documents, "doc ", "docs")} (${share.padStart(6)}) ${String(point.findings).padStart(3)} ${formFor(point.findings, "finding ", "findings")} ${point.per10k.toFixed(1)} per 10,000 characters`,
56
+ fits: ` The current limit meets the target (under ${TARGET}%).`,
57
+ nothingFits: " No limit meets the target. The rule itself may not suit this corpus.",
58
+ over: (share) => ` At the current limit ${share} of the documents are hit, over the target (under ${TARGET}%).`,
59
+ recommend: (limit) => ` Recommended: ${limit}`,
60
+ toConfig: (rule, limit) => `${EN_CONFIG_LEAD}rules:\n${" ".repeat(EN_CONFIG_LEAD.length + YAML_INDENT)}${rule}: ${limit}`,
61
+ measured: (paths, rules) => ` Measured ${counted(rules, "rule")} on ${counted(paths, "file")} as a corpus.`,
62
+ premise: [
63
+ " The documents here are taken as good documents that people wrote and published.",
64
+ ` A rule that fires often on them may have a limit that does not fit real writing (target: under ${TARGET}%).`,
65
+ ],
66
+ smallCorpus: (total) => [
67
+ ` Note: the corpus has only ${counted(total, "document")}. Showing ${TARGET}% takes at least ${MIN_CORPUS}.`,
68
+ " For now each share is either 0 or all, so read the density on the right (findings per 10,000 characters).",
69
+ ],
70
+ untouched: " No limit was changed. A different corpus gives a different answer.",
71
+ },
72
+ };
73
+
74
+ type EvalText = (typeof TEXT)[UiLanguage];
75
+
76
+ const mark = (report: RuleReport, limit: number, text: EvalText): string => {
77
+ if (limit === report.current && limit === report.recommended) return text.both;
78
+ if (limit === report.current) return text.current;
79
+ if (limit === report.recommended) return text.recommended;
9
80
  return "";
10
81
  };
11
82
 
12
- const sweepLines = (report: RuleReport): string[] =>
13
- report.sweep.map((point) => {
14
- const limit = String(point.limit).padStart(6);
15
- const docs = `${String(point.documents).padStart(3)} 文書`;
16
- const density = `1万字あたり ${point.per10k.toFixed(1)}`;
17
- return ` ${limit} ${docs} (${pct(point.documents, report.total).padStart(6)}) 指摘 ${String(point.findings).padStart(3)} 件 ${density}${mark(report, point.limit)}`;
18
- });
83
+ const sweepLines = (report: RuleReport, text: EvalText): string[] =>
84
+ report.sweep.map(
85
+ (point) => ` ${String(point.limit).padStart(6)} ${text.point(point, pct(point.documents, report.total))}${mark(report, point.limit, text)}`,
86
+ );
19
87
 
20
- const verdict = (report: RuleReport): string[] => {
88
+ const verdict = (report: RuleReport, text: EvalText): string[] => {
21
89
  const now = report.sweep.find((point) => point.limit === report.current);
22
90
  const rate = now === undefined ? 0 : now.documents / Math.max(1, report.total);
23
- if (rate <= TARGET_HIT_RATE) return [` いまの閾値で目標(${TARGET_HIT_RATE * 100}% 未満)を満たしています。`];
24
- if (report.recommended === undefined) {
25
- return [` どの閾値でも目標を満たしません。この corpus に対して rule 自体が合っていない可能性があります。`];
26
- }
27
- return [
28
- ` いまの閾値では ${pct(now?.documents ?? 0, report.total)} の文書が該当し、目標(${TARGET_HIT_RATE * 100}% 未満)を超えています。`,
29
- ` 推奨: ${report.recommended}`,
30
- "",
31
- ` chaff.yaml に書くなら: rules:\n ${report.rule}: ${report.recommended}`,
32
- ];
91
+ if (rate <= TARGET_HIT_RATE) return [text.fits];
92
+ if (report.recommended === undefined) return [text.nothingFits];
93
+ return [text.over(pct(now?.documents ?? 0, report.total)), text.recommend(report.recommended), "", text.toConfig(report.rule, report.recommended)];
33
94
  };
34
95
 
35
- const block = (report: RuleReport): string[] => ["", ` ${report.name} (${report.rule})`, "", ...sweepLines(report), "", ...verdict(report), ""];
96
+ const block = (report: RuleReport, text: EvalText): string[] => [
97
+ "",
98
+ ` ${report.name} (${report.rule})`,
99
+ "",
100
+ ...sweepLines(report, text),
101
+ "",
102
+ ...verdict(report, text),
103
+ "",
104
+ ];
36
105
 
37
106
  /**
38
107
  * 閾値は自動で書き換えない。提示して選ばせる。
39
108
  * 較正は corpus に対する答えであって、正解ではないため。
40
109
  */
41
- export const renderEval = (reports: readonly RuleReport[], total: number, paths: number): string =>
42
- [
110
+ export const renderEval = (reports: readonly RuleReport[], total: number, paths: number, ui: UiLanguage): string => {
111
+ const text = TEXT[ui];
112
+ return [
43
113
  "",
44
- ` ${paths} ファイルを corpus として ${reports.length} 本の rule を測りました。`,
114
+ text.measured(paths, reports.length),
45
115
  "",
46
- " ここにある文書は「人間が書いて公開した良い文書」として扱っています。",
47
- ` そこで多く発火する rule は、閾値が現実に合っていない疑いがあります(目標: ${TARGET_HIT_RATE * 100}% 未満)。`,
48
- ...(total < MIN_CORPUS
49
- ? [
50
- "",
51
- ` ※ corpus が ${total} 文書しかありません。${TARGET_HIT_RATE * 100}% を表すには ${MIN_CORPUS} 文書以上が要ります。`,
52
- " いまは割合が「0 か全部」に振れるので、右端の密度(1万字あたりの指摘数)のほうを見てください。",
53
- ]
54
- : []),
55
- ...reports.flatMap(block),
116
+ ...text.premise,
117
+ ...(total < MIN_CORPUS ? ["", ...text.smallCorpus(total)] : []),
118
+ ...reports.flatMap((report) => block(report, text)),
56
119
  " " + "─".repeat(58),
57
120
  "",
58
- ` 閾値は自動で書き換えていません。corpus が変われば答えも変わるためです。`,
121
+ text.untouched,
59
122
  "",
60
123
  ]
61
124
  .join("\n")
62
125
  .replace(/\{total\}/gu, String(total));
126
+ };