chaffjs 0.17.0 → 0.18.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 (840) hide show
  1. package/dist/announced-count.d.ts +26 -0
  2. package/dist/announced-count.d.ts.map +1 -0
  3. package/dist/announced-count.js +104 -0
  4. package/dist/announced-count.js.map +1 -0
  5. package/dist/bare-url.d.ts +14 -0
  6. package/dist/bare-url.d.ts.map +1 -0
  7. package/dist/bare-url.js +28 -0
  8. package/dist/bare-url.js.map +1 -0
  9. package/dist/baseline.d.ts +5 -0
  10. package/dist/baseline.d.ts.map +1 -1
  11. package/dist/baseline.js +16 -1
  12. package/dist/baseline.js.map +1 -1
  13. package/dist/body-section.d.ts +7 -0
  14. package/dist/body-section.d.ts.map +1 -0
  15. package/dist/body-section.js +6 -0
  16. package/dist/body-section.js.map +1 -0
  17. package/dist/calendar-number.d.ts +2 -0
  18. package/dist/calendar-number.d.ts.map +1 -1
  19. package/dist/calendar-number.js +9 -2
  20. package/dist/calendar-number.js.map +1 -1
  21. package/dist/cli-args.d.ts +4 -0
  22. package/dist/cli-args.d.ts.map +1 -1
  23. package/dist/cli-args.js +5 -0
  24. package/dist/cli-args.js.map +1 -1
  25. package/dist/cli-text.d.ts.map +1 -1
  26. package/dist/cli-text.js +8 -4
  27. package/dist/cli-text.js.map +1 -1
  28. package/dist/cli.js +33 -30
  29. package/dist/cli.js.map +1 -1
  30. package/dist/commands/compare.d.ts +8 -0
  31. package/dist/commands/compare.d.ts.map +1 -0
  32. package/dist/commands/compare.js +72 -0
  33. package/dist/commands/compare.js.map +1 -0
  34. package/dist/commands/eval.d.ts.map +1 -1
  35. package/dist/commands/eval.js +17 -9
  36. package/dist/commands/eval.js.map +1 -1
  37. package/dist/commands/feedback.d.ts +5 -2
  38. package/dist/commands/feedback.d.ts.map +1 -1
  39. package/dist/commands/feedback.js +11 -5
  40. package/dist/commands/feedback.js.map +1 -1
  41. package/dist/commands/test.d.ts.map +1 -1
  42. package/dist/commands/test.js +2 -4
  43. package/dist/commands/test.js.map +1 -1
  44. package/dist/commands/tree.d.ts +13 -0
  45. package/dist/commands/tree.d.ts.map +1 -1
  46. package/dist/commands/tree.js +35 -5
  47. package/dist/commands/tree.js.map +1 -1
  48. package/dist/compare/atom.d.ts +31 -0
  49. package/dist/compare/atom.d.ts.map +1 -0
  50. package/dist/compare/atom.js +4 -0
  51. package/dist/compare/atom.js.map +1 -0
  52. package/dist/compare/clock-time.d.ts +8 -0
  53. package/dist/compare/clock-time.d.ts.map +1 -0
  54. package/dist/compare/clock-time.js +65 -0
  55. package/dist/compare/clock-time.js.map +1 -0
  56. package/dist/compare/extract.d.ts +15 -0
  57. package/dist/compare/extract.d.ts.map +1 -0
  58. package/dist/compare/extract.js +58 -0
  59. package/dist/compare/extract.js.map +1 -0
  60. package/dist/compare/fact-text.d.ts +23 -0
  61. package/dist/compare/fact-text.d.ts.map +1 -0
  62. package/dist/compare/fact-text.js +47 -0
  63. package/dist/compare/fact-text.js.map +1 -0
  64. package/dist/compare/markup-atoms.d.ts +11 -0
  65. package/dist/compare/markup-atoms.d.ts.map +1 -0
  66. package/dist/compare/markup-atoms.js +23 -0
  67. package/dist/compare/markup-atoms.js.map +1 -0
  68. package/dist/compare/match.d.ts +19 -0
  69. package/dist/compare/match.d.ts.map +1 -0
  70. package/dist/compare/match.js +84 -0
  71. package/dist/compare/match.js.map +1 -0
  72. package/dist/compare/outcome.d.ts +33 -0
  73. package/dist/compare/outcome.d.ts.map +1 -0
  74. package/dist/compare/outcome.js +40 -0
  75. package/dist/compare/outcome.js.map +1 -0
  76. package/dist/compare/proper-nouns.d.ts +19 -0
  77. package/dist/compare/proper-nouns.d.ts.map +1 -0
  78. package/dist/compare/proper-nouns.js +44 -0
  79. package/dist/compare/proper-nouns.js.map +1 -0
  80. package/dist/compare/render.d.ts +9 -0
  81. package/dist/compare/render.d.ts.map +1 -0
  82. package/dist/compare/render.js +64 -0
  83. package/dist/compare/render.js.map +1 -0
  84. package/dist/compare/spans.d.ts +9 -0
  85. package/dist/compare/spans.d.ts.map +1 -0
  86. package/dist/compare/spans.js +11 -0
  87. package/dist/compare/spans.js.map +1 -0
  88. package/dist/compare/text-atoms.d.ts +24 -0
  89. package/dist/compare/text-atoms.d.ts.map +1 -0
  90. package/dist/compare/text-atoms.js +67 -0
  91. package/dist/compare/text-atoms.js.map +1 -0
  92. package/dist/compare/text.d.ts +24 -0
  93. package/dist/compare/text.d.ts.map +1 -0
  94. package/dist/compare/text.js +90 -0
  95. package/dist/compare/text.js.map +1 -0
  96. package/dist/compare/tree-atoms.d.ts +20 -0
  97. package/dist/compare/tree-atoms.d.ts.map +1 -0
  98. package/dist/compare/tree-atoms.js +58 -0
  99. package/dist/compare/tree-atoms.js.map +1 -0
  100. package/dist/compare/written-names.d.ts +8 -0
  101. package/dist/compare/written-names.d.ts.map +1 -0
  102. package/dist/compare/written-names.js +24 -0
  103. package/dist/compare/written-names.js.map +1 -0
  104. package/dist/config/load.d.ts +15 -0
  105. package/dist/config/load.d.ts.map +1 -1
  106. package/dist/config/load.js +5 -0
  107. package/dist/config/load.js.map +1 -1
  108. package/dist/config/option-problems.d.ts +16 -0
  109. package/dist/config/option-problems.d.ts.map +1 -0
  110. package/dist/config/option-problems.js +58 -0
  111. package/dist/config/option-problems.js.map +1 -0
  112. package/dist/config/read.d.ts +4 -0
  113. package/dist/config/read.d.ts.map +1 -0
  114. package/dist/config/read.js +8 -0
  115. package/dist/config/read.js.map +1 -0
  116. package/dist/config/rule-problems.d.ts +2 -4
  117. package/dist/config/rule-problems.d.ts.map +1 -1
  118. package/dist/config/rule-problems.js +7 -1
  119. package/dist/config/rule-problems.js.map +1 -1
  120. package/dist/config/style.d.ts +16 -0
  121. package/dist/config/style.d.ts.map +1 -0
  122. package/dist/config/style.js +36 -0
  123. package/dist/config/style.js.map +1 -0
  124. package/dist/config/warnings.d.ts +6 -0
  125. package/dist/config/warnings.d.ts.map +1 -0
  126. package/dist/config/warnings.js +10 -0
  127. package/dist/config/warnings.js.map +1 -0
  128. package/dist/config/write.d.ts.map +1 -1
  129. package/dist/config/write.js +17 -4
  130. package/dist/config/write.js.map +1 -1
  131. package/dist/custom/bounded-match.d.ts +15 -0
  132. package/dist/custom/bounded-match.d.ts.map +1 -0
  133. package/dist/custom/bounded-match.js +43 -0
  134. package/dist/custom/bounded-match.js.map +1 -0
  135. package/dist/custom/load.d.ts +8 -0
  136. package/dist/custom/load.d.ts.map +1 -0
  137. package/dist/custom/load.js +11 -0
  138. package/dist/custom/load.js.map +1 -0
  139. package/dist/custom/parse.d.ts +70 -0
  140. package/dist/custom/parse.d.ts.map +1 -0
  141. package/dist/custom/parse.js +215 -0
  142. package/dist/custom/parse.js.map +1 -0
  143. package/dist/custom/problems.d.ts +5 -0
  144. package/dist/custom/problems.d.ts.map +1 -0
  145. package/dist/custom/problems.js +78 -0
  146. package/dist/custom/problems.js.map +1 -0
  147. package/dist/custom/regex-safety.d.ts +11 -0
  148. package/dist/custom/regex-safety.d.ts.map +1 -0
  149. package/dist/custom/regex-safety.js +110 -0
  150. package/dist/custom/regex-safety.js.map +1 -0
  151. package/dist/custom/token-pattern.d.ts +8 -0
  152. package/dist/custom/token-pattern.d.ts.map +1 -0
  153. package/dist/custom/token-pattern.js +75 -0
  154. package/dist/custom/token-pattern.js.map +1 -0
  155. package/dist/detect.d.ts.map +1 -1
  156. package/dist/detect.js +24 -2
  157. package/dist/detect.js.map +1 -1
  158. package/dist/detectors/acronym-context.d.ts +2 -0
  159. package/dist/detectors/acronym-context.d.ts.map +1 -1
  160. package/dist/detectors/acronym-context.js +12 -4
  161. package/dist/detectors/acronym-context.js.map +1 -1
  162. package/dist/detectors/ai-phrasing.d.ts +20 -0
  163. package/dist/detectors/ai-phrasing.d.ts.map +1 -0
  164. package/dist/detectors/ai-phrasing.js +112 -0
  165. package/dist/detectors/ai-phrasing.js.map +1 -0
  166. package/dist/detectors/announced-count.d.ts +4 -0
  167. package/dist/detectors/announced-count.d.ts.map +1 -0
  168. package/dist/detectors/announced-count.js +20 -0
  169. package/dist/detectors/announced-count.js.map +1 -0
  170. package/dist/detectors/broken-link.d.ts +17 -0
  171. package/dist/detectors/broken-link.d.ts.map +1 -0
  172. package/dist/detectors/broken-link.js +66 -0
  173. package/dist/detectors/broken-link.js.map +1 -0
  174. package/dist/detectors/cited-name.d.ts.map +1 -1
  175. package/dist/detectors/cited-name.js +4 -4
  176. package/dist/detectors/cited-name.js.map +1 -1
  177. package/dist/detectors/cited-title.d.ts +11 -0
  178. package/dist/detectors/cited-title.d.ts.map +1 -0
  179. package/dist/detectors/cited-title.js +75 -0
  180. package/dist/detectors/cited-title.js.map +1 -0
  181. package/dist/detectors/concrete-evidence.d.ts +1 -1
  182. package/dist/detectors/concrete-evidence.d.ts.map +1 -1
  183. package/dist/detectors/concrete-evidence.js +2 -1
  184. package/dist/detectors/concrete-evidence.js.map +1 -1
  185. package/dist/detectors/contrast-frame.d.ts +17 -0
  186. package/dist/detectors/contrast-frame.d.ts.map +1 -0
  187. package/dist/detectors/contrast-frame.js +32 -0
  188. package/dist/detectors/contrast-frame.js.map +1 -0
  189. package/dist/detectors/custom.d.ts +8 -0
  190. package/dist/detectors/custom.d.ts.map +1 -0
  191. package/dist/detectors/custom.js +71 -0
  192. package/dist/detectors/custom.js.map +1 -0
  193. package/dist/detectors/dangling-figure.d.ts +4 -0
  194. package/dist/detectors/dangling-figure.d.ts.map +1 -0
  195. package/dist/detectors/dangling-figure.js +18 -0
  196. package/dist/detectors/dangling-figure.js.map +1 -0
  197. package/dist/detectors/doubled-punctuation.d.ts +9 -0
  198. package/dist/detectors/doubled-punctuation.d.ts.map +1 -0
  199. package/dist/detectors/doubled-punctuation.js +33 -0
  200. package/dist/detectors/doubled-punctuation.js.map +1 -0
  201. package/dist/detectors/doubled-word.d.ts +5 -0
  202. package/dist/detectors/doubled-word.d.ts.map +1 -1
  203. package/dist/detectors/doubled-word.js +24 -11
  204. package/dist/detectors/doubled-word.js.map +1 -1
  205. package/dist/detectors/enumerated-runs.d.ts +1 -1
  206. package/dist/detectors/enumerated-runs.d.ts.map +1 -1
  207. package/dist/detectors/enumerated-runs.js +15 -4
  208. package/dist/detectors/enumerated-runs.js.map +1 -1
  209. package/dist/detectors/gram-furniture.d.ts +6 -0
  210. package/dist/detectors/gram-furniture.d.ts.map +1 -0
  211. package/dist/detectors/gram-furniture.js +21 -0
  212. package/dist/detectors/gram-furniture.js.map +1 -0
  213. package/dist/detectors/gram-place.d.ts +2 -0
  214. package/dist/detectors/gram-place.d.ts.map +1 -1
  215. package/dist/detectors/gram-place.js +9 -7
  216. package/dist/detectors/gram-place.js.map +1 -1
  217. package/dist/detectors/gram-windows.d.ts +9 -0
  218. package/dist/detectors/gram-windows.d.ts.map +1 -0
  219. package/dist/detectors/gram-windows.js +27 -0
  220. package/dist/detectors/gram-windows.js.map +1 -0
  221. package/dist/detectors/heading-case.d.ts +2 -0
  222. package/dist/detectors/heading-case.d.ts.map +1 -1
  223. package/dist/detectors/heading-case.js +29 -2
  224. package/dist/detectors/heading-case.js.map +1 -1
  225. package/dist/detectors/heading-code-words.d.ts +6 -0
  226. package/dist/detectors/heading-code-words.d.ts.map +1 -0
  227. package/dist/detectors/heading-code-words.js +29 -0
  228. package/dist/detectors/heading-code-words.js.map +1 -0
  229. package/dist/detectors/heading-echo.d.ts.map +1 -1
  230. package/dist/detectors/heading-echo.js +7 -4
  231. package/dist/detectors/heading-echo.js.map +1 -1
  232. package/dist/detectors/heading-level-skip.d.ts +12 -0
  233. package/dist/detectors/heading-level-skip.d.ts.map +1 -0
  234. package/dist/detectors/heading-level-skip.js +11 -0
  235. package/dist/detectors/heading-level-skip.js.map +1 -0
  236. package/dist/detectors/image-alt-text.d.ts +3 -0
  237. package/dist/detectors/image-alt-text.d.ts.map +1 -0
  238. package/dist/detectors/image-alt-text.js +9 -0
  239. package/dist/detectors/image-alt-text.js.map +1 -0
  240. package/dist/detectors/index.d.ts.map +1 -1
  241. package/dist/detectors/index.js +34 -1
  242. package/dist/detectors/index.js.map +1 -1
  243. package/dist/detectors/kutoten-consistency.d.ts +24 -0
  244. package/dist/detectors/kutoten-consistency.d.ts.map +1 -0
  245. package/dist/detectors/kutoten-consistency.js +77 -0
  246. package/dist/detectors/kutoten-consistency.js.map +1 -0
  247. package/dist/detectors/lead-in.d.ts +1 -0
  248. package/dist/detectors/lead-in.d.ts.map +1 -1
  249. package/dist/detectors/lead-in.js +2 -1
  250. package/dist/detectors/lead-in.js.map +1 -1
  251. package/dist/detectors/lexicon-match.d.ts +4 -0
  252. package/dist/detectors/lexicon-match.d.ts.map +1 -1
  253. package/dist/detectors/lexicon-match.js +7 -2
  254. package/dist/detectors/lexicon-match.js.map +1 -1
  255. package/dist/detectors/list-item.d.ts +45 -0
  256. package/dist/detectors/list-item.d.ts.map +1 -0
  257. package/dist/detectors/list-item.js +88 -0
  258. package/dist/detectors/list-item.js.map +1 -0
  259. package/dist/detectors/list-items.d.ts +31 -0
  260. package/dist/detectors/list-items.d.ts.map +1 -0
  261. package/dist/detectors/list-items.js +113 -0
  262. package/dist/detectors/list-items.js.map +1 -0
  263. package/dist/detectors/list-lead-in.d.ts +7 -0
  264. package/dist/detectors/list-lead-in.d.ts.map +1 -0
  265. package/dist/detectors/list-lead-in.js +28 -0
  266. package/dist/detectors/list-lead-in.js.map +1 -0
  267. package/dist/detectors/list-sentence.d.ts +65 -0
  268. package/dist/detectors/list-sentence.d.ts.map +1 -0
  269. package/dist/detectors/list-sentence.js +105 -0
  270. package/dist/detectors/list-sentence.js.map +1 -0
  271. package/dist/detectors/list-view.d.ts +34 -0
  272. package/dist/detectors/list-view.d.ts.map +1 -0
  273. package/dist/detectors/list-view.js +137 -0
  274. package/dist/detectors/list-view.js.map +1 -0
  275. package/dist/detectors/long-vowel.d.ts +7 -0
  276. package/dist/detectors/long-vowel.d.ts.map +1 -0
  277. package/dist/detectors/long-vowel.js +57 -0
  278. package/dist/detectors/long-vowel.js.map +1 -0
  279. package/dist/detectors/markup-finding.d.ts +6 -0
  280. package/dist/detectors/markup-finding.d.ts.map +1 -0
  281. package/dist/detectors/markup-finding.js +19 -0
  282. package/dist/detectors/markup-finding.js.map +1 -0
  283. package/dist/detectors/name-numeral.d.ts +1 -1
  284. package/dist/detectors/name-numeral.d.ts.map +1 -1
  285. package/dist/detectors/name-numeral.js +2 -7
  286. package/dist/detectors/name-numeral.js.map +1 -1
  287. package/dist/detectors/orthography.d.ts.map +1 -1
  288. package/dist/detectors/orthography.js +14 -10
  289. package/dist/detectors/orthography.js.map +1 -1
  290. package/dist/detectors/oxford-comma.d.ts +7 -1
  291. package/dist/detectors/oxford-comma.d.ts.map +1 -1
  292. package/dist/detectors/oxford-comma.js +135 -229
  293. package/dist/detectors/oxford-comma.js.map +1 -1
  294. package/dist/detectors/place-run.d.ts.map +1 -1
  295. package/dist/detectors/place-run.js +10 -6
  296. package/dist/detectors/place-run.js.map +1 -1
  297. package/dist/detectors/placeholder-text.d.ts +9 -0
  298. package/dist/detectors/placeholder-text.d.ts.map +1 -0
  299. package/dist/detectors/placeholder-text.js +18 -0
  300. package/dist/detectors/placeholder-text.js.map +1 -0
  301. package/dist/detectors/quote-around.d.ts +3 -0
  302. package/dist/detectors/quote-around.d.ts.map +1 -0
  303. package/dist/detectors/quote-around.js +8 -0
  304. package/dist/detectors/quote-around.js.map +1 -0
  305. package/dist/detectors/register.d.ts +11 -3
  306. package/dist/detectors/register.d.ts.map +1 -1
  307. package/dist/detectors/register.js +28 -9
  308. package/dist/detectors/register.js.map +1 -1
  309. package/dist/detectors/roman-numeral.d.ts +2 -0
  310. package/dist/detectors/roman-numeral.d.ts.map +1 -1
  311. package/dist/detectors/roman-numeral.js +2 -0
  312. package/dist/detectors/roman-numeral.js.map +1 -1
  313. package/dist/detectors/sentence-ending.d.ts +17 -1
  314. package/dist/detectors/sentence-ending.d.ts.map +1 -1
  315. package/dist/detectors/sentence-ending.js +13 -7
  316. package/dist/detectors/sentence-ending.js.map +1 -1
  317. package/dist/detectors/sentence-length.d.ts.map +1 -1
  318. package/dist/detectors/sentence-length.js +13 -4
  319. package/dist/detectors/sentence-length.js.map +1 -1
  320. package/dist/detectors/signals.d.ts +8 -0
  321. package/dist/detectors/signals.d.ts.map +1 -1
  322. package/dist/detectors/signals.js +67 -46
  323. package/dist/detectors/signals.js.map +1 -1
  324. package/dist/detectors/structure-tree.d.ts +6 -0
  325. package/dist/detectors/structure-tree.d.ts.map +1 -1
  326. package/dist/detectors/structure-tree.js +37 -1
  327. package/dist/detectors/structure-tree.js.map +1 -1
  328. package/dist/detectors/structure.d.ts +1 -3
  329. package/dist/detectors/structure.d.ts.map +1 -1
  330. package/dist/detectors/structure.js +3 -4
  331. package/dist/detectors/structure.js.map +1 -1
  332. package/dist/detectors/superlative-name.d.ts.map +1 -1
  333. package/dist/detectors/superlative-name.js +8 -5
  334. package/dist/detectors/superlative-name.js.map +1 -1
  335. package/dist/detectors/superlative-scope.d.ts.map +1 -1
  336. package/dist/detectors/superlative-scope.js +9 -4
  337. package/dist/detectors/superlative-scope.js.map +1 -1
  338. package/dist/detectors/token-column.d.ts +46 -0
  339. package/dist/detectors/token-column.d.ts.map +1 -0
  340. package/dist/detectors/token-column.js +95 -0
  341. package/dist/detectors/token-column.js.map +1 -0
  342. package/dist/detectors/unbalanced-bracket.d.ts +22 -0
  343. package/dist/detectors/unbalanced-bracket.d.ts.map +1 -0
  344. package/dist/detectors/unbalanced-bracket.js +119 -0
  345. package/dist/detectors/unbalanced-bracket.js.map +1 -0
  346. package/dist/detectors/url-run-on.d.ts +9 -0
  347. package/dist/detectors/url-run-on.d.ts.map +1 -0
  348. package/dist/detectors/url-run-on.js +19 -0
  349. package/dist/detectors/url-run-on.js.map +1 -0
  350. package/dist/document-sections.d.ts +13 -0
  351. package/dist/document-sections.d.ts.map +1 -0
  352. package/dist/document-sections.js +32 -0
  353. package/dist/document-sections.js.map +1 -0
  354. package/dist/document.d.ts +11 -4
  355. package/dist/document.d.ts.map +1 -1
  356. package/dist/document.js +90 -98
  357. package/dist/document.js.map +1 -1
  358. package/dist/email-parts.d.ts +28 -0
  359. package/dist/email-parts.d.ts.map +1 -0
  360. package/dist/email-parts.js +230 -0
  361. package/dist/email-parts.js.map +1 -0
  362. package/dist/eval.d.ts +2 -1
  363. package/dist/eval.d.ts.map +1 -1
  364. package/dist/eval.js +16 -4
  365. package/dist/eval.js.map +1 -1
  366. package/dist/figure-references.d.ts +30 -0
  367. package/dist/figure-references.d.ts.map +1 -0
  368. package/dist/figure-references.js +76 -0
  369. package/dist/figure-references.js.map +1 -0
  370. package/dist/files.d.ts +2 -0
  371. package/dist/files.d.ts.map +1 -1
  372. package/dist/files.js +4 -0
  373. package/dist/files.js.map +1 -1
  374. package/dist/heading-label.d.ts +22 -0
  375. package/dist/heading-label.d.ts.map +1 -0
  376. package/dist/heading-label.js +50 -0
  377. package/dist/heading-label.js.map +1 -0
  378. package/dist/in-page-nav.d.ts +4 -1
  379. package/dist/in-page-nav.d.ts.map +1 -1
  380. package/dist/in-page-nav.js +53 -18
  381. package/dist/in-page-nav.js.map +1 -1
  382. package/dist/joined-view.d.ts.map +1 -1
  383. package/dist/joined-view.js +1 -0
  384. package/dist/joined-view.js.map +1 -1
  385. package/dist/levels.d.ts +5 -1
  386. package/dist/levels.d.ts.map +1 -1
  387. package/dist/levels.js +9 -0
  388. package/dist/levels.js.map +1 -1
  389. package/dist/line-paragraphs.d.ts +8 -0
  390. package/dist/line-paragraphs.d.ts.map +1 -1
  391. package/dist/line-paragraphs.js +33 -10
  392. package/dist/line-paragraphs.js.map +1 -1
  393. package/dist/long-vowel.d.ts +35 -0
  394. package/dist/long-vowel.d.ts.map +1 -0
  395. package/dist/long-vowel.js +71 -0
  396. package/dist/long-vowel.js.map +1 -0
  397. package/dist/markdown-node.d.ts +19 -0
  398. package/dist/markdown-node.d.ts.map +1 -0
  399. package/dist/markdown-node.js +6 -0
  400. package/dist/markdown-node.js.map +1 -0
  401. package/dist/markdown-read.d.ts +17 -0
  402. package/dist/markdown-read.d.ts.map +1 -0
  403. package/dist/markdown-read.js +37 -0
  404. package/dist/markdown-read.js.map +1 -0
  405. package/dist/markup.d.ts +22 -0
  406. package/dist/markup.d.ts.map +1 -0
  407. package/dist/markup.js +180 -0
  408. package/dist/markup.js.map +1 -0
  409. package/dist/mask.js +8 -5
  410. package/dist/mask.js.map +1 -1
  411. package/dist/number-name.d.ts +21 -5
  412. package/dist/number-name.d.ts.map +1 -1
  413. package/dist/number-name.js +119 -17
  414. package/dist/number-name.js.map +1 -1
  415. package/dist/orthography.d.ts +2 -0
  416. package/dist/orthography.d.ts.map +1 -1
  417. package/dist/orthography.js +9 -4
  418. package/dist/orthography.js.map +1 -1
  419. package/dist/page-furniture.d.ts +5 -2
  420. package/dist/page-furniture.d.ts.map +1 -1
  421. package/dist/page-furniture.js +8 -2
  422. package/dist/page-furniture.js.map +1 -1
  423. package/dist/plain-source.d.ts +1 -0
  424. package/dist/plain-source.d.ts.map +1 -1
  425. package/dist/plain-source.js +2 -1
  426. package/dist/plain-source.js.map +1 -1
  427. package/dist/plugin.d.ts +115 -0
  428. package/dist/plugin.d.ts.map +1 -1
  429. package/dist/profile/for-file.d.ts.map +1 -1
  430. package/dist/profile/for-file.js +3 -1
  431. package/dist/profile/for-file.js.map +1 -1
  432. package/dist/quoted-span.d.ts.map +1 -1
  433. package/dist/quoted-span.js +8 -3
  434. package/dist/quoted-span.js.map +1 -1
  435. package/dist/reasons.d.ts +3 -0
  436. package/dist/reasons.d.ts.map +1 -1
  437. package/dist/reasons.js +6 -0
  438. package/dist/reasons.js.map +1 -1
  439. package/dist/reference-lists.d.ts +3 -0
  440. package/dist/reference-lists.d.ts.map +1 -0
  441. package/dist/reference-lists.js +75 -0
  442. package/dist/reference-lists.js.map +1 -0
  443. package/dist/render/explain.d.ts +7 -1
  444. package/dist/render/explain.d.ts.map +1 -1
  445. package/dist/render/explain.js +36 -12
  446. package/dist/render/explain.js.map +1 -1
  447. package/dist/render/friendly.d.ts.map +1 -1
  448. package/dist/render/friendly.js +19 -9
  449. package/dist/render/friendly.js.map +1 -1
  450. package/dist/render/options.d.ts +7 -0
  451. package/dist/render/options.d.ts.map +1 -0
  452. package/dist/render/options.js +50 -0
  453. package/dist/render/options.js.map +1 -0
  454. package/dist/render/rules-json.d.ts +4 -1
  455. package/dist/render/rules-json.d.ts.map +1 -1
  456. package/dist/render/rules-json.js +102 -12
  457. package/dist/render/rules-json.js.map +1 -1
  458. package/dist/render/rules-table.d.ts +4 -0
  459. package/dist/render/rules-table.d.ts.map +1 -0
  460. package/dist/render/rules-table.js +70 -0
  461. package/dist/render/rules-table.js.map +1 -0
  462. package/dist/render/sarif.js +4 -4
  463. package/dist/render/sarif.js.map +1 -1
  464. package/dist/render/semantic.js +3 -3
  465. package/dist/render/semantic.js.map +1 -1
  466. package/dist/render/severity-name.d.ts +6 -0
  467. package/dist/render/severity-name.d.ts.map +1 -0
  468. package/dist/render/severity-name.js +7 -0
  469. package/dist/render/severity-name.js.map +1 -0
  470. package/dist/render/text.d.ts +7 -2
  471. package/dist/render/text.d.ts.map +1 -1
  472. package/dist/render/text.js +15 -4
  473. package/dist/render/text.js.map +1 -1
  474. package/dist/resolve-genre.d.ts +1 -1
  475. package/dist/resolve-genre.d.ts.map +1 -1
  476. package/dist/resolve-genre.js +4 -1
  477. package/dist/resolve-genre.js.map +1 -1
  478. package/dist/rule-genres.d.ts +24 -0
  479. package/dist/rule-genres.d.ts.map +1 -0
  480. package/dist/rule-genres.js +11 -0
  481. package/dist/rule-genres.js.map +1 -0
  482. package/dist/rule-guide.d.ts +54 -0
  483. package/dist/rule-guide.d.ts.map +1 -0
  484. package/dist/rule-guide.js +81 -0
  485. package/dist/rule-guide.js.map +1 -0
  486. package/dist/rule-load.d.ts.map +1 -1
  487. package/dist/rule-load.js +87 -33
  488. package/dist/rule-load.js.map +1 -1
  489. package/dist/rule-options.d.ts +50 -0
  490. package/dist/rule-options.d.ts.map +1 -0
  491. package/dist/rule-options.js +106 -0
  492. package/dist/rule-options.js.map +1 -0
  493. package/dist/run.d.ts +13 -0
  494. package/dist/run.d.ts.map +1 -1
  495. package/dist/run.js +54 -10
  496. package/dist/run.js.map +1 -1
  497. package/dist/sentence-shape.d.ts.map +1 -1
  498. package/dist/sentence-shape.js +4 -4
  499. package/dist/sentence-shape.js.map +1 -1
  500. package/dist/setting-problems.d.ts +2 -1
  501. package/dist/setting-problems.d.ts.map +1 -1
  502. package/dist/setting-problems.js +6 -1
  503. package/dist/setting-problems.js.map +1 -1
  504. package/dist/span-cut.d.ts +6 -0
  505. package/dist/span-cut.d.ts.map +1 -0
  506. package/dist/span-cut.js +22 -0
  507. package/dist/span-cut.js.map +1 -0
  508. package/dist/structure/aside-depth.d.ts +21 -0
  509. package/dist/structure/aside-depth.d.ts.map +1 -0
  510. package/dist/structure/aside-depth.js +65 -0
  511. package/dist/structure/aside-depth.js.map +1 -0
  512. package/dist/structure/build.d.ts +5 -1
  513. package/dist/structure/build.d.ts.map +1 -1
  514. package/dist/structure/build.js +29 -17
  515. package/dist/structure/build.js.map +1 -1
  516. package/dist/structure/closed-pairs.d.ts +8 -0
  517. package/dist/structure/closed-pairs.d.ts.map +1 -0
  518. package/dist/structure/closed-pairs.js +22 -0
  519. package/dist/structure/closed-pairs.js.map +1 -0
  520. package/dist/structure/date-range.d.ts +21 -0
  521. package/dist/structure/date-range.d.ts.map +1 -0
  522. package/dist/structure/date-range.js +45 -0
  523. package/dist/structure/date-range.js.map +1 -0
  524. package/dist/structure/known-mentions.d.ts +18 -0
  525. package/dist/structure/known-mentions.d.ts.map +1 -0
  526. package/dist/structure/known-mentions.js +78 -0
  527. package/dist/structure/known-mentions.js.map +1 -0
  528. package/dist/structure/numbering.d.ts +1 -0
  529. package/dist/structure/numbering.d.ts.map +1 -1
  530. package/dist/structure/numbering.js +11 -6
  531. package/dist/structure/numbering.js.map +1 -1
  532. package/dist/structure/of.d.ts +3 -1
  533. package/dist/structure/of.d.ts.map +1 -1
  534. package/dist/structure/of.js +5 -1
  535. package/dist/structure/of.js.map +1 -1
  536. package/dist/structure/percent-sum.d.ts +16 -0
  537. package/dist/structure/percent-sum.d.ts.map +1 -0
  538. package/dist/structure/percent-sum.js +104 -0
  539. package/dist/structure/percent-sum.js.map +1 -0
  540. package/dist/structure/quoted-number.d.ts +8 -0
  541. package/dist/structure/quoted-number.d.ts.map +1 -0
  542. package/dist/structure/quoted-number.js +34 -0
  543. package/dist/structure/quoted-number.js.map +1 -0
  544. package/dist/structure/relative-find.d.ts.map +1 -1
  545. package/dist/structure/relative-find.js +34 -46
  546. package/dist/structure/relative-find.js.map +1 -1
  547. package/dist/structure/relative-resolve.d.ts.map +1 -1
  548. package/dist/structure/relative-resolve.js +76 -30
  549. package/dist/structure/relative-resolve.js.map +1 -1
  550. package/dist/structure/runs.d.ts +2 -0
  551. package/dist/structure/runs.d.ts.map +1 -1
  552. package/dist/structure/runs.js +2 -2
  553. package/dist/structure/runs.js.map +1 -1
  554. package/dist/structure/sexp.d.ts +1 -1
  555. package/dist/structure/sexp.d.ts.map +1 -1
  556. package/dist/structure/sexp.js +18 -6
  557. package/dist/structure/sexp.js.map +1 -1
  558. package/dist/structure/total.d.ts +3 -0
  559. package/dist/structure/total.d.ts.map +1 -1
  560. package/dist/structure/total.js +2 -2
  561. package/dist/structure/total.js.map +1 -1
  562. package/dist/structure/universal.d.ts +14 -2
  563. package/dist/structure/universal.d.ts.map +1 -1
  564. package/dist/structure/universal.js +17 -2
  565. package/dist/structure/universal.js.map +1 -1
  566. package/dist/structure/wrapped-tail.d.ts +14 -7
  567. package/dist/structure/wrapped-tail.d.ts.map +1 -1
  568. package/dist/structure/wrapped-tail.js +33 -8
  569. package/dist/structure/wrapped-tail.js.map +1 -1
  570. package/dist/style-load.d.ts +4 -0
  571. package/dist/style-load.d.ts.map +1 -0
  572. package/dist/style-load.js +28 -0
  573. package/dist/style-load.js.map +1 -0
  574. package/dist/style-parse.d.ts +19 -0
  575. package/dist/style-parse.d.ts.map +1 -0
  576. package/dist/style-parse.js +60 -0
  577. package/dist/style-parse.js.map +1 -0
  578. package/dist/subheading-line.d.ts +6 -0
  579. package/dist/subheading-line.d.ts.map +1 -0
  580. package/dist/subheading-line.js +57 -0
  581. package/dist/subheading-line.js.map +1 -0
  582. package/dist/template-syntax.d.ts +17 -0
  583. package/dist/template-syntax.d.ts.map +1 -0
  584. package/dist/template-syntax.js +140 -0
  585. package/dist/template-syntax.js.map +1 -0
  586. package/dist/text-above.d.ts +19 -0
  587. package/dist/text-above.d.ts.map +1 -0
  588. package/dist/text-above.js +43 -0
  589. package/dist/text-above.js.map +1 -0
  590. package/dist/text-figures.d.ts +10 -0
  591. package/dist/text-figures.d.ts.map +1 -0
  592. package/dist/text-figures.js +131 -0
  593. package/dist/text-figures.js.map +1 -0
  594. package/dist/trailing-aside.d.ts +2 -0
  595. package/dist/trailing-aside.d.ts.map +1 -0
  596. package/dist/trailing-aside.js +53 -0
  597. package/dist/trailing-aside.js.map +1 -0
  598. package/dist/tree-walk.d.ts +18 -0
  599. package/dist/tree-walk.d.ts.map +1 -0
  600. package/dist/tree-walk.js +45 -0
  601. package/dist/tree-walk.js.map +1 -0
  602. package/genres.yaml +26 -0
  603. package/package.json +5 -3
  604. package/reference-headings.yaml +17 -0
  605. package/rules/adverb-overuse.yaml +19 -0
  606. package/rules/agentless-passive.yaml +19 -0
  607. package/rules/agreement-slip.yaml +14 -1
  608. package/rules/ai-generated-composite.yaml +102 -0
  609. package/rules/ai-tell.yaml +23 -0
  610. package/rules/announced-count-mismatch.yaml +57 -0
  611. package/rules/announcing-opener.yaml +51 -0
  612. package/rules/assistant-residue.yaml +52 -0
  613. package/rules/bold-density.yaml +31 -0
  614. package/rules/broken-link.yaml +65 -0
  615. package/rules/closing-cliche.yaml +33 -0
  616. package/rules/colon-lead-in.yaml +59 -0
  617. package/rules/concrete-evidence-density.yaml +41 -0
  618. package/rules/contraction-consistency.yaml +18 -0
  619. package/rules/contrast-framing.yaml +57 -0
  620. package/rules/cushion-phrase-density.yaml +25 -0
  621. package/rules/dangling-figure-reference.yaml +61 -0
  622. package/rules/dangling-reference.yaml +53 -1
  623. package/rules/date-order.yaml +33 -1
  624. package/rules/date-range-reversed.yaml +46 -0
  625. package/rules/date-weekday-mismatch.yaml +17 -1
  626. package/rules/double-keigo.yaml +16 -0
  627. package/rules/doubled-punctuation.yaml +45 -0
  628. package/rules/doubled-word.yaml +17 -1
  629. package/rules/duplicate-definition.yaml +59 -1
  630. package/rules/emoji-density.yaml +25 -0
  631. package/rules/empty-conclusion.yaml +28 -0
  632. package/rules/empty-intensifier.yaml +19 -0
  633. package/rules/excessive-hedging.yaml +19 -0
  634. package/rules/expletive-construction.yaml +19 -0
  635. package/rules/heading-echo.yaml +31 -0
  636. package/rules/heading-level-skip.yaml +78 -0
  637. package/rules/hiragana-fukushi.yaml +16 -0
  638. package/rules/image-alt-text.yaml +54 -0
  639. package/rules/internal-jargon.yaml +21 -0
  640. package/rules/katakana-long-vowel.yaml +98 -0
  641. package/rules/kutoten-consistency.yaml +53 -0
  642. package/rules/latin-spacing.yaml +23 -4
  643. package/rules/max-kanji-continuous.yaml +16 -0
  644. package/rules/max-paragraph-length.yaml +33 -0
  645. package/rules/max-sentence-length.yaml +32 -0
  646. package/rules/ngram-repetition.yaml +29 -2
  647. package/rules/no-doubled-joshi.yaml +16 -0
  648. package/rules/no-em-dash.yaml +25 -0
  649. package/rules/no-mixed-desumasu.yaml +27 -2
  650. package/rules/no-nakaguro-parallel.yaml +20 -0
  651. package/rules/numbering-gap.yaml +53 -1
  652. package/rules/oxford-comma-consistency.yaml +18 -0
  653. package/rules/padded-intro.yaml +19 -0
  654. package/rules/paragraph-length-variance.yaml +49 -0
  655. package/rules/percent-sum-mismatch.yaml +58 -0
  656. package/rules/preamble-length.yaml +56 -0
  657. package/rules/preferred-term.yaml +27 -0
  658. package/rules/proper-noun-density.yaml +26 -0
  659. package/rules/repeated-conjunction.yaml +47 -0
  660. package/rules/repeated-sentence-head.yaml +23 -0
  661. package/rules/required-sections.yaml +49 -0
  662. package/rules/risk-disclosure.yaml +22 -0
  663. package/rules/rule-of-three.yaml +97 -0
  664. package/rules/sasete-itadaku.yaml +18 -0
  665. package/rules/section-length-uniformity.yaml +89 -0
  666. package/rules/sentence-initial-conjunction-run.yaml +18 -0
  667. package/rules/sentence-rhythm.yaml +25 -0
  668. package/rules/stock-transition.yaml +55 -0
  669. package/rules/stray-space.yaml +18 -0
  670. package/rules/taigen-dome-in-prose.yaml +19 -0
  671. package/rules/title-case-consistency.yaml +42 -0
  672. package/rules/total-mismatch.yaml +37 -1
  673. package/rules/unbalanced-bracket.yaml +56 -0
  674. package/rules/undefined-acronym.yaml +22 -0
  675. package/rules/unfilled-placeholder.yaml +50 -0
  676. package/rules/unqualified-superlative.yaml +19 -0
  677. package/rules/unsourced-number.yaml +16 -0
  678. package/rules/url-run-on.yaml +45 -0
  679. package/skills/chaff/SKILL.md +70 -0
  680. package/src/announced-count.ts +148 -0
  681. package/src/bare-url.ts +35 -0
  682. package/src/baseline.ts +17 -1
  683. package/src/body-section.ts +7 -0
  684. package/src/calendar-number.ts +18 -4
  685. package/src/cli-args.ts +6 -0
  686. package/src/cli-text.ts +8 -4
  687. package/src/cli.ts +37 -37
  688. package/src/commands/compare.ts +80 -0
  689. package/src/commands/eval.ts +19 -8
  690. package/src/commands/feedback.ts +12 -6
  691. package/src/commands/test.ts +2 -4
  692. package/src/commands/tree.ts +41 -5
  693. package/src/compare/atom.ts +33 -0
  694. package/src/compare/clock-time.ts +86 -0
  695. package/src/compare/extract.ts +78 -0
  696. package/src/compare/fact-text.ts +68 -0
  697. package/src/compare/markup-atoms.ts +29 -0
  698. package/src/compare/match.ts +105 -0
  699. package/src/compare/outcome.ts +70 -0
  700. package/src/compare/proper-nouns.ts +65 -0
  701. package/src/compare/render.ts +102 -0
  702. package/src/compare/spans.ts +17 -0
  703. package/src/compare/text-atoms.ts +95 -0
  704. package/src/compare/text.ts +120 -0
  705. package/src/compare/tree-atoms.ts +80 -0
  706. package/src/compare/written-names.ts +26 -0
  707. package/src/config/load.ts +21 -0
  708. package/src/config/option-problems.ts +70 -0
  709. package/src/config/read.ts +8 -0
  710. package/src/config/rule-problems.ts +12 -2
  711. package/src/config/style.ts +42 -0
  712. package/src/config/warnings.ts +13 -0
  713. package/src/config/write.ts +18 -4
  714. package/src/custom/bounded-match.ts +58 -0
  715. package/src/custom/load.ts +15 -0
  716. package/src/custom/parse.ts +274 -0
  717. package/src/custom/problems.ts +89 -0
  718. package/src/custom/regex-safety.ts +118 -0
  719. package/src/custom/token-pattern.ts +84 -0
  720. package/src/detect.ts +28 -2
  721. package/src/detectors/acronym-context.ts +16 -4
  722. package/src/detectors/ai-phrasing.ts +137 -0
  723. package/src/detectors/announced-count.ts +25 -0
  724. package/src/detectors/broken-link.ts +90 -0
  725. package/src/detectors/cited-name.ts +6 -5
  726. package/src/detectors/cited-title.ts +96 -0
  727. package/src/detectors/concrete-evidence.ts +3 -2
  728. package/src/detectors/contrast-frame.ts +54 -0
  729. package/src/detectors/custom.ts +89 -0
  730. package/src/detectors/dangling-figure.ts +22 -0
  731. package/src/detectors/doubled-punctuation.ts +49 -0
  732. package/src/detectors/doubled-word.ts +19 -4
  733. package/src/detectors/enumerated-runs.ts +13 -4
  734. package/src/detectors/gram-furniture.ts +30 -0
  735. package/src/detectors/gram-place.ts +9 -5
  736. package/src/detectors/gram-windows.ts +31 -0
  737. package/src/detectors/heading-case.ts +30 -2
  738. package/src/detectors/heading-code-words.ts +35 -0
  739. package/src/detectors/heading-echo.ts +8 -4
  740. package/src/detectors/heading-level-skip.ts +19 -0
  741. package/src/detectors/image-alt-text.ts +11 -0
  742. package/src/detectors/index.ts +43 -1
  743. package/src/detectors/kutoten-consistency.ts +96 -0
  744. package/src/detectors/lead-in.ts +3 -1
  745. package/src/detectors/lexicon-match.ts +9 -2
  746. package/src/detectors/list-item.ts +122 -0
  747. package/src/detectors/list-items.ts +163 -0
  748. package/src/detectors/list-lead-in.ts +33 -0
  749. package/src/detectors/list-sentence.ts +157 -0
  750. package/src/detectors/list-view.ts +157 -0
  751. package/src/detectors/long-vowel.ts +66 -0
  752. package/src/detectors/markup-finding.ts +24 -0
  753. package/src/detectors/name-numeral.ts +4 -2
  754. package/src/detectors/orthography.ts +23 -17
  755. package/src/detectors/oxford-comma.ts +178 -283
  756. package/src/detectors/place-run.ts +7 -4
  757. package/src/detectors/placeholder-text.ts +27 -0
  758. package/src/detectors/quote-around.ts +9 -0
  759. package/src/detectors/register.ts +37 -10
  760. package/src/detectors/roman-numeral.ts +3 -0
  761. package/src/detectors/sentence-ending.ts +14 -7
  762. package/src/detectors/sentence-length.ts +16 -5
  763. package/src/detectors/signals.ts +80 -46
  764. package/src/detectors/structure-tree.ts +45 -1
  765. package/src/detectors/structure.ts +3 -4
  766. package/src/detectors/superlative-name.ts +7 -4
  767. package/src/detectors/superlative-scope.ts +8 -3
  768. package/src/detectors/token-column.ts +149 -0
  769. package/src/detectors/unbalanced-bracket.ts +153 -0
  770. package/src/detectors/url-run-on.ts +27 -0
  771. package/src/document-sections.ts +46 -0
  772. package/src/document.ts +106 -138
  773. package/src/email-parts.ts +269 -0
  774. package/src/eval.ts +14 -4
  775. package/src/figure-references.ts +112 -0
  776. package/src/files.ts +5 -0
  777. package/src/heading-label.ts +55 -0
  778. package/src/in-page-nav.ts +48 -19
  779. package/src/joined-view.ts +1 -0
  780. package/src/levels.ts +14 -1
  781. package/src/line-paragraphs.ts +52 -9
  782. package/src/long-vowel.ts +99 -0
  783. package/src/markdown-node.ts +19 -0
  784. package/src/markdown-read.ts +44 -0
  785. package/src/markup.ts +210 -0
  786. package/src/mask.ts +8 -8
  787. package/src/number-name.ts +151 -24
  788. package/src/orthography.ts +10 -3
  789. package/src/page-furniture.ts +11 -2
  790. package/src/plain-source.ts +2 -1
  791. package/src/plugin.ts +101 -1
  792. package/src/profile/for-file.ts +3 -1
  793. package/src/quoted-span.ts +9 -3
  794. package/src/reasons.ts +9 -0
  795. package/src/reference-lists.ts +91 -0
  796. package/src/render/explain.ts +47 -13
  797. package/src/render/friendly.ts +20 -9
  798. package/src/render/options.ts +58 -0
  799. package/src/render/rules-json.ts +130 -14
  800. package/src/render/rules-table.ts +91 -0
  801. package/src/render/sarif.ts +4 -4
  802. package/src/render/semantic.ts +3 -3
  803. package/src/render/severity-name.ts +10 -0
  804. package/src/render/text.ts +21 -7
  805. package/src/resolve-genre.ts +4 -1
  806. package/src/rule-genres.ts +25 -0
  807. package/src/rule-guide.ts +135 -0
  808. package/src/rule-load.ts +91 -34
  809. package/src/rule-options.ts +131 -0
  810. package/src/run.ts +74 -11
  811. package/src/sentence-shape.ts +4 -5
  812. package/src/setting-problems.ts +7 -1
  813. package/src/span-cut.ts +28 -0
  814. package/src/structure/aside-depth.ts +67 -0
  815. package/src/structure/build.ts +38 -19
  816. package/src/structure/closed-pairs.ts +20 -0
  817. package/src/structure/date-range.ts +69 -0
  818. package/src/structure/known-mentions.ts +96 -0
  819. package/src/structure/numbering.ts +9 -5
  820. package/src/structure/of.ts +11 -3
  821. package/src/structure/percent-sum.ts +135 -0
  822. package/src/structure/quoted-number.ts +36 -0
  823. package/src/structure/relative-find.ts +34 -47
  824. package/src/structure/relative-resolve.ts +77 -30
  825. package/src/structure/runs.ts +2 -2
  826. package/src/structure/sexp.ts +17 -5
  827. package/src/structure/total.ts +2 -2
  828. package/src/structure/universal.ts +24 -4
  829. package/src/structure/wrapped-tail.ts +38 -13
  830. package/src/style-load.ts +28 -0
  831. package/src/style-parse.ts +77 -0
  832. package/src/subheading-line.ts +68 -0
  833. package/src/template-syntax.ts +164 -0
  834. package/src/text-above.ts +49 -0
  835. package/src/text-figures.ts +147 -0
  836. package/src/trailing-aside.ts +50 -0
  837. package/src/tree-walk.ts +48 -0
  838. package/styles/bunkacho.yaml +25 -0
  839. package/styles/ieice.yaml +29 -0
  840. package/styles/jis-z8301-2011.yaml +25 -0
package/src/rule-load.ts CHANGED
@@ -2,7 +2,10 @@ import { readFileSync, readdirSync } from "node:fs";
2
2
  import { fileURLToPath } from "node:url";
3
3
  import { dirname, join } from "node:path";
4
4
  import { parse } from "yaml";
5
- import type { LevelTable, RuleDefinition, Severity } from "./plugin.ts";
5
+ import type { LanguageLevels, LevelSets, LevelTable, RuleDefinition, Severity } from "./plugin.ts";
6
+ import { rankOfSeverity, severityAt } from "./levels.ts";
7
+ import { optionsOf } from "./rule-options.ts";
8
+ import { ruleGuideOf } from "./rule-guide.ts";
6
9
 
7
10
  const RULES_DIR = join(dirname(fileURLToPath(import.meta.url)), "..", "rules");
8
11
 
@@ -10,13 +13,8 @@ const isRecord = (value: unknown): value is Record<string, unknown> => typeof va
10
13
 
11
14
  const isLocalized = (value: unknown): value is Record<string, string> => isRecord(value) && Object.values(value).every((entry) => typeof entry === "string");
12
15
 
13
- const SEVERITY_BY_NAME: Readonly<Record<string, number>> = { info: 1, warning: 2, error: 3 };
14
-
15
- /** L4 は件数ではなく深刻度を段で持つ。error/warning/info を数に写して同じ器に入れる。 */
16
- const asNumber = (entry: unknown): number | undefined => {
17
- if (typeof entry === "number") return entry;
18
- return typeof entry === "string" ? SEVERITY_BY_NAME[entry] : undefined;
19
- };
16
+ /** 数えるものの無い rule(L4、番号の抜け)は件数ではなく重さを段で持つ。error/warning/info を数に写して同じ器に入れる。 */
17
+ const asNumber = (entry: unknown): number | undefined => (typeof entry === "number" ? entry : rankOfSeverity(entry));
20
18
 
21
19
  const isLevelTable = (value: unknown): value is LevelTable =>
22
20
  isRecord(value) && Object.entries(value).every(([key, entry]) => ["strict", "normal", "relaxed"].includes(key) && asNumber(entry) !== undefined);
@@ -41,6 +39,20 @@ const genreTables = (raw: unknown, language: string): Readonly<Record<string, Le
41
39
  return Object.fromEntries(entries);
42
40
  };
43
41
 
42
+ /** levels を言語別に書いた rule の、loaded 以外の言語の段。言語で分けていない rule は空。 */
43
+ const otherLanguages = (raw: Record<string, unknown>, loaded: string): Record<string, LanguageLevels> => {
44
+ const levels = raw["levels"];
45
+ if (!isRecord(levels) || isLevelTable(levels)) return {};
46
+ return Object.fromEntries(
47
+ Object.keys(levels)
48
+ .filter((language) => language !== loaded && language !== "default")
49
+ .flatMap((language) => {
50
+ const table = flattenLevels(levels, language);
51
+ return table === undefined ? [] : [[language, { levels: table, by_genre: genreTables(raw["by_genre"], language) }] as const];
52
+ }),
53
+ );
54
+ };
55
+
44
56
  const SEVERITIES: readonly Severity[] = ["error", "warning", "info"];
45
57
  const isSeverity = (value: unknown): value is Severity => SEVERITIES.some((entry) => entry === value);
46
58
 
@@ -68,8 +80,8 @@ const missingFields = (raw: Record<string, unknown>, levels: LevelTable | undefi
68
80
 
69
81
  const localizedOf = (value: unknown): Record<string, string> => (isLocalized(value) ? value : {});
70
82
 
71
- /** 見つけ方ごとの message。読めないものは捨てる。 */
72
- const messagesOf = (value: unknown): Record<string, Record<string, string>> =>
83
+ /** 見つけ方ごとの message、置き場所ごとの読みかた。読めないものは捨てる。 */
84
+ const localizedByKey = (value: unknown): Record<string, Record<string, string>> =>
73
85
  isRecord(value) ? Object.fromEntries(Object.entries(value).flatMap(([variant, text]) => (isLocalized(text) ? [[variant, text]] : []))) : {};
74
86
 
75
87
  /**
@@ -92,38 +104,83 @@ const numberFor = (raw: unknown, language: string): number | undefined => {
92
104
 
93
105
  const stringList = (value: unknown): string[] | undefined => (Array.isArray(value) ? value.map((entry) => String(entry)) : undefined);
94
106
 
107
+ /** What only some rules declare: options beyond the level, and token features the adapter computes on request. */
108
+ const extrasOf = (raw: Record<string, unknown>, file: string): Pick<RuleDefinition, "options" | "token_features"> => ({
109
+ ...(raw["options"] === undefined ? {} : { options: optionsOf(raw["options"], file) }),
110
+ ...(raw["token_features"] === undefined ? {} : { token_features: stringList(raw["token_features"]) ?? [] }),
111
+ });
112
+
113
+ /** The table flattenLevels reads, before its severities become numbers. */
114
+ const writtenTable = (raw: unknown, language: string): unknown => {
115
+ if (isLevelTable(raw) || !isRecord(raw)) return raw;
116
+ return raw[language] ?? raw["default"];
117
+ };
118
+
119
+ /** Every level value written for a language: the rule's own table and each genre's. */
120
+ const writtenValues = (raw: Record<string, unknown>, language: string): unknown[] => {
121
+ const genres = isRecord(raw["by_genre"]) ? Object.values(raw["by_genre"]) : [];
122
+ return [raw["levels"], ...genres].flatMap((written) => {
123
+ const table = writtenTable(written, language);
124
+ return isRecord(table) ? Object.values(table) : [];
125
+ });
126
+ };
127
+
128
+ /** Levels written as severities set the severity; a rule that mixes the two would say both. */
129
+ const levelSetsOf = (raw: Record<string, unknown>, language: string, file: string): LevelSets => {
130
+ const values = writtenValues(raw, language);
131
+ const severities = values.filter((value) => typeof value === "string").length;
132
+ if (severities === 0) return "limit";
133
+ if (severities < values.length) throw new Error(`${file}: levels と by_genre に重さ(error / warning / info)と数を混ぜて書けません`);
134
+ // A genre's table would give the rule a default severity other than its severity field.
135
+ if (isRecord(raw["by_genre"])) throw new Error(`${file}: 重さを段に持つ rule は by_genre を持てません`);
136
+ return "severity";
137
+ };
138
+
139
+ /** severity は normal の段の重さそのもの。食い違うと、段を書かないときと normal と書いたときで重さが変わる。 */
140
+ const checkedSeverity = (rule: RuleDefinition, file: string): RuleDefinition => {
141
+ if (rule.level_sets === "limit" || severityAt(rule, "normal") === rule.severity) return rule;
142
+ throw new Error(`${file}: severity ${rule.severity} が levels の normal(${severityAt(rule, "normal")})と違います`);
143
+ };
144
+
95
145
  const toRule = (raw: unknown, language: string, file: string): RuleDefinition => {
96
146
  if (!isRecord(raw)) throw new Error(`${file}: rule は object であること`);
97
147
  const levels = flattenLevels(raw["levels"], language);
98
148
  const missing = missingFields(raw, levels);
99
149
  if (missing.length > 0) throw new Error(`${file}: 必須フィールドがありません: ${missing.join(", ")}`);
100
150
  if (levels === undefined) throw new Error(`${file}: levels を解決できません`);
101
- return {
102
- id: String(raw["id"]),
103
- layer: pick(raw["layer"], LAYERS) ?? "L1",
104
- status: pick(raw["status"], STATUSES) ?? "experimental",
105
- name: localizedOf(raw["name"]),
106
- why: localizedOf(raw["why"]),
107
- how_to_fix: localizedOf(raw["how_to_fix"]),
108
- message: localizedOf(raw["message"]),
109
- messages: messagesOf(raw["messages"]),
110
- levels,
111
- by_genre: genreTables(raw["by_genre"], language),
112
- how_to_find: String(raw["how_to_find"]),
113
- word_list: typeof raw["word_list"] === "string" ? raw["word_list"] : undefined,
114
- extra_word_lists: stringList(raw["extra_word_lists"]) ?? [],
115
- what_to_check: isLocalized(raw["what_to_check"]) ? raw["what_to_check"] : undefined,
116
- where: typeof raw["where"] === "string" ? raw["where"] : undefined,
117
- full_sentence: numberFor(raw["full_sentence"], language),
118
- requires: stringList(raw["requires"]) ?? [],
119
- uses: stringList(raw["uses"]) ?? [],
120
- from: stringList(raw["from"]) ?? [],
121
- languages: stringList(raw["languages"]),
122
- use_for: Array.isArray(raw["use_for"]) ? raw["use_for"].map((entry) => String(entry)) : [],
123
- severity: severityOf(raw["severity"], language),
124
- };
151
+ return checkedSeverity(ruleOf(raw, levels, levelSetsOf(raw, language, file), language, file), file);
125
152
  };
126
153
 
154
+ const ruleOf = (raw: Record<string, unknown>, levels: LevelTable, levelSets: LevelSets, language: string, file: string): RuleDefinition => ({
155
+ id: String(raw["id"]),
156
+ layer: pick(raw["layer"], LAYERS) ?? "L1",
157
+ status: pick(raw["status"], STATUSES) ?? "experimental",
158
+ name: localizedOf(raw["name"]),
159
+ why: localizedOf(raw["why"]),
160
+ how_to_fix: localizedOf(raw["how_to_fix"]),
161
+ message: localizedOf(raw["message"]),
162
+ messages: localizedByKey(raw["messages"]),
163
+ placeholders: localizedByKey(raw["placeholders"]),
164
+ levels,
165
+ level_sets: levelSets,
166
+ by_genre: genreTables(raw["by_genre"], language),
167
+ other_languages: otherLanguages(raw, language),
168
+ how_to_find: String(raw["how_to_find"]),
169
+ word_list: typeof raw["word_list"] === "string" ? raw["word_list"] : undefined,
170
+ extra_word_lists: stringList(raw["extra_word_lists"]) ?? [],
171
+ what_to_check: isLocalized(raw["what_to_check"]) ? raw["what_to_check"] : undefined,
172
+ where: typeof raw["where"] === "string" ? raw["where"] : undefined,
173
+ full_sentence: numberFor(raw["full_sentence"], language),
174
+ requires: stringList(raw["requires"]) ?? [],
175
+ uses: stringList(raw["uses"]) ?? [],
176
+ from: stringList(raw["from"]) ?? [],
177
+ languages: stringList(raw["languages"]),
178
+ use_for: Array.isArray(raw["use_for"]) ? raw["use_for"].map((entry) => String(entry)) : [],
179
+ severity: severityOf(raw["severity"], language),
180
+ ...extrasOf(raw, file),
181
+ guide: ruleGuideOf(raw),
182
+ });
183
+
127
184
  /**
128
185
  * 読めない rule ファイルは、どれが何で読めないかを言う。
129
186
  * yaml の例外をそのまま投げると、スタックトレースだけが出てファイル名が出ない。
@@ -0,0 +1,131 @@
1
+ import type { Localized, OptionValue, RuleOption } from "./plugin.ts";
2
+
3
+ // A rule's options: what its YAML declares, whether a written value fits, and which setting decides each one.
4
+ // Pure: no file is read here. The settings come in as layers, strongest first (chaff.yaml, then a style, then the default).
5
+
6
+ const KINDS: readonly RuleOption["kind"][] = ["choice", "count", "words"];
7
+
8
+ const isRecord = (value: unknown): value is Record<string, unknown> => typeof value === "object" && value !== null && !Array.isArray(value);
9
+
10
+ const isLocalized = (value: unknown): value is Localized =>
11
+ isRecord(value) && Object.keys(value).length > 0 && Object.values(value).every((entry) => typeof entry === "string" && entry !== "");
12
+
13
+ const isCount = (value: unknown): value is number => typeof value === "number" && Number.isInteger(value) && value >= 1;
14
+
15
+ const isWords = (value: unknown): value is string[] => Array.isArray(value) && value.every((entry) => typeof entry === "string" && entry.trim() !== "");
16
+
17
+ /** Whether value fits the option: one of the choices, a whole number of 1 or more, or a list of words. */
18
+ export const fitsOption = (option: RuleOption, value: unknown): value is OptionValue => {
19
+ if (option.kind === "count") return isCount(value);
20
+ if (option.kind === "words") return isWords(value);
21
+ return typeof value === "string" && option.choices.includes(value);
22
+ };
23
+
24
+ /** What a value must look like, for a problem message: the choices, or the kind. */
25
+ export const expectedOf = (option: RuleOption): string => (option.kind === "choice" ? option.choices.join(" / ") : option.kind);
26
+
27
+ const kindOf = (raw: Record<string, unknown>, where: string): RuleOption["kind"] => {
28
+ const kind = KINDS.find((entry) => entry === raw["kind"]);
29
+ if (kind === undefined) throw new Error(`${where}: kind must be one of ${KINDS.join(", ")}`);
30
+ return kind;
31
+ };
32
+
33
+ const choicesOf = (raw: Record<string, unknown>, kind: RuleOption["kind"], where: string): string[] => {
34
+ if (kind !== "choice") return [];
35
+ const choices = raw["choices"];
36
+ if (!isRecord(choices) || Object.keys(choices).length < 2) throw new Error(`${where}: a choice needs two or more choices, each with its meaning`);
37
+ return Object.keys(choices);
38
+ };
39
+
40
+ const choiceNamesOf = (raw: Record<string, unknown>, where: string): Record<string, Localized> => {
41
+ const choices = raw["choices"];
42
+ if (!isRecord(choices)) return {};
43
+ return Object.fromEntries(
44
+ Object.entries(choices).map(([choice, meaning]) => {
45
+ if (!isLocalized(meaning)) throw new Error(`${where}: choice ${choice} needs its meaning in ja and/or en`);
46
+ return [choice, meaning];
47
+ }),
48
+ );
49
+ };
50
+
51
+ const optionOf = (name: string, raw: unknown, file: string): RuleOption => {
52
+ const where = `${file}: options.${name}`;
53
+ if (!isRecord(raw)) throw new Error(`${where} must be a map`);
54
+ const kind = kindOf(raw, where);
55
+ const about = raw["about"];
56
+ if (!isLocalized(about)) throw new Error(`${where}: about needs what the option decides, in ja and/or en`);
57
+ const option: RuleOption = {
58
+ kind,
59
+ choices: choicesOf(raw, kind, where),
60
+ default: kind === "words" ? [] : 0,
61
+ about,
62
+ choiceNames: kind === "choice" ? choiceNamesOf(raw, where) : {},
63
+ };
64
+ const fallback: unknown = raw["default"] ?? (kind === "words" ? [] : undefined);
65
+ if (!fitsOption(option, fallback)) throw new Error(`${where}: default must be ${expectedOf(option)}`);
66
+ return { ...option, default: fallback };
67
+ };
68
+
69
+ /** The options a rule's YAML declares. A wrong declaration is chaff's own bug, so it throws with the file and the option. */
70
+ export const optionsOf = (raw: unknown, file: string): Record<string, RuleOption> => {
71
+ if (raw === undefined) return {};
72
+ if (!isRecord(raw)) throw new Error(`${file}: options must be a map of option names`);
73
+ return Object.fromEntries(Object.entries(raw).map(([name, option]) => [name, optionOf(name, option, file)]));
74
+ };
75
+
76
+ /** One place settings come from, and what it sets: { rule id: { option: value } }. */
77
+ export type OptionLayer = { readonly from: string; readonly values: Readonly<Record<string, unknown>> };
78
+
79
+ export type SettledOption = { readonly value: OptionValue; readonly from: string };
80
+
81
+ /** The source name of an option left at its default. */
82
+ export const DEFAULT_SOURCE = "default";
83
+
84
+ const optionMap = (layer: OptionLayer, ruleId: string): Readonly<Record<string, unknown>> => {
85
+ const values = layer.values[ruleId];
86
+ return isRecord(values) ? values : {};
87
+ };
88
+
89
+ /**
90
+ * Each option of a rule at the value of the strongest layer that sets it with a value that fits, and where that came from.
91
+ * A value that does not fit falls through to the next layer; optionProblems says so, so it is never silent.
92
+ */
93
+ export const settleOptions = (ruleId: string, options: Readonly<Record<string, RuleOption>>, layers: readonly OptionLayer[]): Record<string, SettledOption> =>
94
+ Object.fromEntries(
95
+ Object.entries(options).map(([name, option]) => {
96
+ const fitting = layers.flatMap((layer): SettledOption[] => {
97
+ const value = optionMap(layer, ruleId)[name];
98
+ return fitsOption(option, value) ? [{ value, from: layer.from }] : [];
99
+ });
100
+ return [name, fitting[0] ?? { value: option.default, from: DEFAULT_SOURCE }];
101
+ }),
102
+ );
103
+
104
+ /** The values alone, as the detector gets them. */
105
+ export const optionValues = (settled: Readonly<Record<string, SettledOption>>): Record<string, OptionValue> =>
106
+ Object.fromEntries(Object.entries(settled).map(([name, entry]) => [name, entry.value]));
107
+
108
+ /** What a layer sets that has no effect: an unknown rule, a rule with no options, an unknown option, or a value that does not fit. */
109
+ export type OptionProblem =
110
+ | { readonly kind: "unknown-rule"; readonly rule: string }
111
+ | { readonly kind: "no-options"; readonly rule: string }
112
+ | { readonly kind: "not-a-map"; readonly rule: string }
113
+ | { readonly kind: "unknown-option"; readonly rule: string; readonly option: string; readonly known: readonly string[] }
114
+ | { readonly kind: "bad-value"; readonly rule: string; readonly option: string; readonly value: string; readonly expected: string };
115
+
116
+ const printed = (value: unknown): string => JSON.stringify(value) ?? String(value);
117
+
118
+ const problemsOfRule = (ruleId: string, options: Readonly<Record<string, RuleOption>> | undefined, written: unknown): OptionProblem[] => {
119
+ if (options === undefined) return [{ kind: "unknown-rule", rule: ruleId }];
120
+ if (Object.keys(options).length === 0) return [{ kind: "no-options", rule: ruleId }];
121
+ if (!isRecord(written)) return [{ kind: "not-a-map", rule: ruleId }];
122
+ return Object.entries(written).flatMap(([name, value]): OptionProblem[] => {
123
+ const option = options[name];
124
+ if (option === undefined) return [{ kind: "unknown-option", rule: ruleId, option: name, known: Object.keys(options) }];
125
+ return fitsOption(option, value) ? [] : [{ kind: "bad-value", rule: ruleId, option: name, value: printed(value), expected: expectedOf(option) }];
126
+ });
127
+ };
128
+
129
+ /** Every problem in what a layer sets, given each known rule's options (a rule with none maps to {}). */
130
+ export const optionProblems = (layer: OptionLayer, known: Readonly<Record<string, Readonly<Record<string, RuleOption>>>>): OptionProblem[] =>
131
+ Object.entries(layer.values).flatMap(([ruleId, written]) => problemsOfRule(ruleId, known[ruleId], written));
package/src/run.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { DETECTORS } from "./detectors/index.ts";
2
- import { resolve } from "./levels.ts";
3
- import type { AdapterNeeds, Finding, Level, ProseDocument, RuleDefinition } from "./plugin.ts";
2
+ import { resolve, severityAt } from "./levels.ts";
3
+ import type { AdapterNeeds, Detector, DetectorOptions, Finding, Level, ProseDocument, RuleDefinition } from "./plugin.ts";
4
4
  import { lineStarts, placeOf } from "./position.ts";
5
5
  import { REASONS, type Reasons } from "./reasons.ts";
6
6
  import { joinWords } from "./detectors/word-list.ts";
@@ -11,6 +11,9 @@ import { maskSpans } from "./mask.ts";
11
11
  import { textOutline } from "./page-furniture.ts";
12
12
  import { uiLanguageOf } from "./ui.ts";
13
13
  import { presetLevels } from "./genre-load.ts";
14
+ import { bodySectionOf } from "./body-section.ts";
15
+ import { optionValues, settleOptions, type OptionLayer } from "./rule-options.ts";
16
+ import { PatternTimeout } from "./custom/bounded-match.ts";
14
17
 
15
18
  export type Skipped = { readonly rule: string; readonly why: string };
16
19
 
@@ -68,7 +71,7 @@ const treeProblem = (doc: ProseDocument): string | undefined => {
68
71
 
69
72
  /** 木が読んだのと同じ本文。Markdown はコードを覆ったもの、.txt はページの飾りだけを覆ったもの(.txt の字下げはコードではない)。 */
70
73
  const textTheTreeRead = (doc: ProseDocument): string =>
71
- isMarkdownPath(doc.path) ? (doc.prose ?? doc.source) : maskSpans(doc.source, textOutline(doc.source).opaque);
74
+ isMarkdownPath(doc.path) ? (doc.prose ?? doc.source) : maskSpans(doc.source, textOutline(doc.source, doc.replyQuotes).opaque);
72
75
 
73
76
  const unreadByDocument = new WeakMap<ProseDocument, Unread | undefined>();
74
77
 
@@ -78,6 +81,15 @@ const unreadOf = (doc: ProseDocument): Unread | undefined => {
78
81
  return unreadByDocument.get(doc);
79
82
  };
80
83
 
84
+ /** 見出しを読む rule の要求。表題より下の見出しが無い文書では、本題の前を測れない。0 件を「前置きが短い」に見せない。 */
85
+ /** 記法を読む rule の要求(markdown)。.txt には見出しの記法も画像もリンクの記法も無いので、0 件を「問題なし」に見せない。 */
86
+ const DOCUMENT_NEEDS: ReadonlySet<string> = new Set(["headings", "markdown"]);
87
+
88
+ const documentNeed = (rule: RuleDefinition, doc: ProseDocument): string | undefined => {
89
+ if (rule.requires.includes("markdown") && !isMarkdownPath(doc.path)) return reasonsFor(doc).notMarkdown;
90
+ return rule.requires.includes("headings") && bodySectionOf(doc.sections) === undefined ? reasonsFor(doc).noHeadings : undefined;
91
+ };
92
+
81
93
  /** 知らない要求は満たされていないものとして扱う。黙って無視すると、要求なしで動いてしまう。 */
82
94
  const has = (capabilities: ProseDocument["capabilities"], need: string): boolean => {
83
95
  if (need === "pos") return capabilities.pos;
@@ -97,7 +109,7 @@ const hasTokens = (doc: ProseDocument): boolean => doc.sentences.length === 0 ||
97
109
  /** 要求を満たさない rule は動かせない。満たさないまま動かすと「指摘 0 件」が保証に見える。 */
98
110
  const unmet = (rule: RuleDefinition, doc: ProseDocument): string | undefined => {
99
111
  if (rule.languages !== undefined && !rule.languages.includes(doc.language)) return reasonsFor(doc).otherLanguage(doc.language);
100
- const missing = rule.requires.filter((need) => !TREE_NEEDS.has(need)).find((need) => !has(doc.capabilities, need));
112
+ const missing = rule.requires.filter((need) => !TREE_NEEDS.has(need) && !DOCUMENT_NEEDS.has(need)).find((need) => !has(doc.capabilities, need));
101
113
  if (missing !== undefined) return reasonsFor(doc).noCapability(missing);
102
114
  return undefined;
103
115
  };
@@ -119,12 +131,29 @@ export const wantsTags = (rule: RuleDefinition): boolean => [...rule.requires, .
119
131
  * 解析器の初期化に払う代金を決める。動く rule が 1 本も要求しないなら読み込まない。
120
132
  * capabilities は「払えばできる」の宣言なので、ここでは見ない。
121
133
  */
122
- export const neededBy = (rules: readonly RuleDefinition[], settings: Settings, experimental: boolean, genre: string, language: string): AdapterNeeds => ({
123
- pos: forGenre(rules, genre)
134
+ export const neededBy = (rules: readonly RuleDefinition[], settings: Settings, experimental: boolean, genre: string, language: string): AdapterNeeds => {
135
+ const running = forGenre(rules, genre)
124
136
  .filter((rule) => rule.layer !== "L4" && levelFor(rule, settings, experimental, presetLevels(genre)) !== "off")
125
- .filter((rule) => rule.languages === undefined || rule.languages.includes(language))
126
- .some(wantsTags),
127
- });
137
+ .filter((rule) => rule.languages === undefined || rule.languages.includes(language));
138
+ return { pos: running.some(wantsTags), features: tokenFeaturesOf(running) };
139
+ };
140
+
141
+ /** The token features the rules read (RuleDefinition.token_features), each once. */
142
+ export const tokenFeaturesOf = (rules: readonly RuleDefinition[]): string[] => [...new Set(rules.flatMap((rule) => rule.token_features ?? []))];
143
+
144
+ /** A detector's findings, or how long it ran before a team's pattern was stopped (bounded-match.ts). Other errors are chaff's bugs and propagate. */
145
+ const runDetector = (
146
+ detector: Detector,
147
+ doc: ProseDocument,
148
+ options: DetectorOptions,
149
+ ): { readonly findings: readonly Finding[] } | { readonly timedOut: number } => {
150
+ try {
151
+ return { findings: detector(doc, options) };
152
+ } catch (error) {
153
+ if (error instanceof PatternTimeout) return { timedOut: error.budget_ms };
154
+ throw error;
155
+ }
156
+ };
128
157
 
129
158
  const place = (starts: readonly number[], finding: Finding): Finding => {
130
159
  const offset = finding.values["offset"];
@@ -160,6 +189,29 @@ export type Limits = Readonly<Record<string, number>>;
160
189
 
161
190
  const limitFor = (rule: RuleDefinition, level: Level, genre: string, limits: Limits): number => limits[rule.id] ?? resolve(rule, level, genre).limit;
162
191
 
192
+ /** 文書と違う言語で書いた文の上限。chaff.yaml の数値は文書の言語の単位で書いたものなので、ここには効かせない。 */
193
+ const embeddedLimitsFor = (rule: RuleDefinition, level: Level, genre: string, embedded: readonly string[]): Record<string, number> =>
194
+ Object.fromEntries(
195
+ embedded.flatMap((language) => {
196
+ const tables = rule.other_languages?.[language];
197
+ return tables === undefined ? [] : [[language, resolve({ ...rule, ...tables }, level, genre).limit] as const];
198
+ }),
199
+ );
200
+
201
+ const embeddedLanguagesOf = (doc: ProseDocument): string[] => [
202
+ ...new Set(doc.sentences.flatMap((sentence) => (sentence.embeddedLanguage === undefined ? [] : [sentence.embeddedLanguage.id]))),
203
+ ];
204
+
205
+ /** What a run is set to: the levels chaff.yaml names, whether experimental rules run, the genre, and the numbers and options set. */
206
+ export type RunContext = {
207
+ readonly settings: Settings;
208
+ readonly experimental: boolean;
209
+ readonly genre: string;
210
+ readonly limits?: Limits;
211
+ /** Where rule options come from, strongest first (chaff.yaml). An option no layer sets is at its default. */
212
+ readonly optionLayers?: readonly OptionLayer[];
213
+ };
214
+
163
215
  export const runRules = (
164
216
  doc: ProseDocument,
165
217
  rules: readonly RuleDefinition[],
@@ -167,10 +219,14 @@ export const runRules = (
167
219
  experimental: boolean,
168
220
  genre: string,
169
221
  limits: Limits = {},
170
- ): RunResult => {
222
+ ): RunResult => runRulesWith(doc, rules, { settings, experimental, genre, limits });
223
+
224
+ export const runRulesWith = (doc: ProseDocument, rules: readonly RuleDefinition[], context: RunContext): RunResult => {
225
+ const { settings, experimental, genre, limits = {}, optionLayers = [] } = context;
171
226
  const preset = presetLevels(genre);
172
227
  const starts = lineStarts(doc.source);
173
228
  const applicable = forGenre(rules, genre);
229
+ const embedded = embeddedLanguagesOf(doc);
174
230
  const experimentalOn = applicable.filter((rule) => rule.status === "experimental" && levelFor(rule, settings, experimental, preset) !== "off");
175
231
  const forced = experimentalOn.filter((rule) => settings[rule.id] !== undefined).map((rule) => rule.id);
176
232
  const presetOn = experimentalOn.filter((rule) => settings[rule.id] === undefined && preset[rule.id] !== undefined).map((rule) => rule.id);
@@ -191,6 +247,8 @@ export const runRules = (
191
247
  // 段階を見た後で聞く。doc.structure は触れたときに木を作るので、止めている rule のために作らない。
192
248
  const noTree = treeNeed(rule, doc);
193
249
  if (noTree !== undefined) return { findings: acc.findings, skipped: [...acc.skipped, { rule: rule.id, why: noTree }] };
250
+ const noDocumentNeed = documentNeed(rule, doc);
251
+ if (noDocumentNeed !== undefined) return { findings: acc.findings, skipped: [...acc.skipped, { rule: rule.id, why: noDocumentNeed }] };
194
252
  // 複合シグナルは二段目で扱う。一段目では「検出器が無い」と言わせない。
195
253
  if (rule.from.length > 0) return acc;
196
254
  const detector = DETECTORS[rule.how_to_find];
@@ -205,8 +263,13 @@ export const runRules = (
205
263
  lexicon: rule.word_list === undefined ? undefined : doc.lexicons[rule.word_list],
206
264
  where: rule.where,
207
265
  fullSentence: rule.full_sentence,
266
+ embeddedLimits: embeddedLimitsFor(rule, level, genre, embedded),
267
+ ...(rule.options === undefined ? {} : { settings: optionValues(settleOptions(rule.id, rule.options, optionLayers)) }),
268
+ ...(rule.custom === undefined ? {} : { custom: rule.custom }),
208
269
  };
209
- const found = detector(doc, options).map((finding) => place(starts, { ...finding, rule: rule.id, severity: rule.severity }));
270
+ const ran = runDetector(detector, doc, options);
271
+ if ("timedOut" in ran) return { findings: acc.findings, skipped: [...acc.skipped, { rule: rule.id, why: reasonsFor(doc).patternTimeout(ran.timedOut) }] };
272
+ const found = ran.findings.map((finding) => place(starts, { ...finding, rule: rule.id, severity: severityAt(rule, level, genre) }));
210
273
  return { findings: [...acc.findings, ...found], skipped: acc.skipped };
211
274
  },
212
275
  { findings: [], skipped: [] },
@@ -1,4 +1,5 @@
1
1
  import type { Sentence, Token } from "./plugin.ts";
2
+ import { ownEnd } from "./trailing-aside.ts";
2
3
 
3
4
  /**
4
5
  * 「これは文か」の判定を 1 箇所に置く。
@@ -21,13 +22,11 @@ const SKIP = new Set(["PUNCT", "PART", "SYM"]);
21
22
  * 「これを最優先制約とする(§17)。」の述語は「とする」であって「17」ではない。
22
23
  * 仕様書は相互参照を括弧で添えるので、これを数えると文末が全部そこになる。
23
24
  */
24
- const TRAILING = /[((][^((]*[))][\s。.!?!?]*$/u;
25
-
26
25
  const beforeTrailing = (sentence: Sentence): readonly Token[] => {
27
26
  const tokens = sentence.tokens ?? [];
28
- const match = TRAILING.exec(sentence.text);
29
- if (match?.index === undefined) return tokens;
30
- const cut = sentence.span.start + match.index;
27
+ const end = ownEnd(sentence.text);
28
+ if (end === sentence.text.length) return tokens;
29
+ const cut = sentence.span.start + end;
31
30
  const kept = tokens.filter((token) => token.span.end <= cut);
32
31
  // 括弧を外したら何も残らない文は、括弧そのものが中身。そのまま見る。
33
32
  return kept.length === 0 ? tokens : kept;
@@ -1,5 +1,9 @@
1
1
  import type { Config } from "./config/load.ts";
2
2
  import type { CliText } from "./cli-text.ts";
3
+ import type { UiLanguage } from "./ui.ts";
4
+ import { styleProblems } from "./config/style.ts";
5
+ import { loadStyles } from "./style-load.ts";
6
+ import { customRuleProblems } from "./custom/problems.ts";
3
7
  import { GENRES } from "./genre.ts";
4
8
  import { unknownGenres, writtenGenres } from "./genre-check.ts";
5
9
  import { loadProfiles } from "./profile/load.ts";
@@ -18,7 +22,9 @@ const profileProblems = (command: string, config: Config, text: CliText): string
18
22
  };
19
23
 
20
24
  /** Settings that name something chaff does not have. Each would silently check less than was asked, so the run stops on them. */
21
- export const settingProblems = (command: string, genreFlag: string | undefined, config: Config, text: CliText): string[] => [
25
+ export const settingProblems = (command: string, genreFlag: string | undefined, config: Config, text: CliText, ui: UiLanguage = "en"): string[] => [
22
26
  ...genreProblems(command, genreFlag, config, text),
23
27
  ...profileProblems(command, config, text),
28
+ ...styleProblems(config, loadStyles(), ui),
29
+ ...customRuleProblems(config, ui),
24
30
  ];
@@ -0,0 +1,28 @@
1
+ import type { Span } from "./plugin.ts";
2
+ import { mergeSpans } from "./span-merge.ts";
3
+ import { firstEndingAfter } from "./soft-break.ts";
4
+
5
+ const piecesOutside = (span: Span, sortedCuts: readonly Span[]): Span[] => {
6
+ const overlapping = sortedCuts
7
+ .slice(firstEndingAfter(sortedCuts, span.start), firstEndingAfter(sortedCuts, span.end) + 1)
8
+ .filter((cut) => cut.start < span.end);
9
+ const { pieces, cursor } = overlapping.reduce<{ pieces: Span[]; cursor: number }>(
10
+ (acc, cut) => ({
11
+ pieces: cut.start > acc.cursor ? [...acc.pieces, { start: acc.cursor, end: cut.start }] : acc.pieces,
12
+ cursor: Math.max(acc.cursor, cut.end),
13
+ }),
14
+ { pieces: [], cursor: span.start },
15
+ );
16
+ return cursor < span.end ? [...pieces, { start: cursor, end: span.end }] : pieces;
17
+ };
18
+
19
+ /** The parts of each span that no cut covers, in order. Cuts may overlap one another and come in any order. */
20
+ export const cutSpans = (spans: readonly Span[], cuts: readonly Span[]): Span[] => {
21
+ if (cuts.length === 0) return [...spans];
22
+ const sortedCuts = mergeSpans(cuts, true);
23
+ return spans.flatMap((span) => piecesOutside(span, sortedCuts));
24
+ };
25
+
26
+ /** cutSpans, less the pieces of `source` that hold only white space. */
27
+ export const cutTextSpans = (spans: readonly Span[], cuts: readonly Span[], source: string): Span[] =>
28
+ cuts.length === 0 ? [...spans] : cutSpans(spans, cuts).filter((piece) => source.slice(piece.start, piece.end).trim() !== "");
@@ -0,0 +1,67 @@
1
+ import type { RelativeVocabulary } from "../plugin.ts";
2
+
3
+ type Aside = RelativeVocabulary["aside"];
4
+
5
+ /** 文字と、その位置(UTF-16 の単位で)。絵文字のような 2 単位の文字も 1 文字として読む。 */
6
+ export const characters = (text: string): { readonly char: string; readonly at: number }[] => {
7
+ const found: { char: string; at: number }[] = [];
8
+ let at = 0;
9
+ for (const char of text) {
10
+ found.push({ char, at });
11
+ at += char.length;
12
+ }
13
+ return found;
14
+ };
15
+
16
+ /** at の位置で開いたままの括弧書きの開きの位置。text の頭から at まで読む。 */
17
+ export const openAsidesAt = (text: string, at: number, aside: Aside): number[] => {
18
+ const opens: number[] = [];
19
+ if (aside === undefined) return opens;
20
+ characters(text.slice(0, at)).forEach(({ char, at: position }) => {
21
+ if (char === aside.open) opens.push(position);
22
+ if (char === aside.close) opens.pop();
23
+ });
24
+ return opens;
25
+ };
26
+
27
+ /** at で開いたままの括弧書きの数(depth)と、一番内側の開きの位置(innermost)。 */
28
+ export type AsideDepth = { readonly depth: (at: number) => number; readonly innermost: (at: number) => number | undefined };
29
+
30
+ /** 文字の切れ目でない位置(2 単位の文字の途中)の印。 */
31
+ const UNREAD = -2;
32
+ const NO_OPEN = -1;
33
+
34
+ /**
35
+ * 本文を一度だけ読み、文字の切れ目ごとに openAsidesAt の答えを覚える。参照ごとに頭から読み直すと、参照の数と行の長さの積になる。
36
+ * 覚えていない位置(2 単位の文字の途中)は openAsidesAt で読む。
37
+ */
38
+ export const asideDepth = (text: string, aside: Aside): AsideDepth => {
39
+ if (aside === undefined) return { depth: () => 0, innermost: () => undefined };
40
+ const depths = new Int32Array(text.length + 1).fill(UNREAD);
41
+ const innermosts = new Int32Array(text.length + 1).fill(UNREAD);
42
+ const opens: number[] = [];
43
+ depths[0] = 0;
44
+ innermosts[0] = NO_OPEN;
45
+ characters(text).forEach(({ char, at }) => {
46
+ if (char === aside.open) opens.push(at);
47
+ if (char === aside.close) opens.pop();
48
+ depths[at + char.length] = opens.length;
49
+ innermosts[at + char.length] = opens.at(-1) ?? NO_OPEN;
50
+ });
51
+ // text.slice(0, at) が切る位置。負の数は後ろから数え、本文の外は端に寄せる。文字の途中なら覚えていない。
52
+ const cut = (at: number): number => {
53
+ const whole = Math.trunc(Number.isNaN(at) ? 0 : at);
54
+ return whole < 0 ? Math.max(text.length + whole, 0) : Math.min(whole, text.length);
55
+ };
56
+ return {
57
+ depth: (at) => {
58
+ const depth = depths[cut(at)] ?? UNREAD;
59
+ return depth === UNREAD ? openAsidesAt(text, at, aside).length : depth;
60
+ },
61
+ innermost: (at) => {
62
+ const open = innermosts[cut(at)] ?? UNREAD;
63
+ if (open === UNREAD) return openAsidesAt(text, at, aside).at(-1);
64
+ return open === NO_OPEN ? undefined : open;
65
+ },
66
+ };
67
+ };