chaffjs 0.18.0 → 0.19.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 (682) hide show
  1. package/README.md +52 -56
  2. package/bin/chaff.js +17 -2
  3. package/dist/acronym-expansions.d.ts +26 -0
  4. package/dist/acronym-expansions.d.ts.map +1 -0
  5. package/dist/acronym-expansions.js +88 -0
  6. package/dist/acronym-expansions.js.map +1 -0
  7. package/dist/announced-count.d.ts.map +1 -1
  8. package/dist/announced-count.js +44 -10
  9. package/dist/announced-count.js.map +1 -1
  10. package/dist/announced-list.d.ts +30 -0
  11. package/dist/announced-list.d.ts.map +1 -0
  12. package/dist/announced-list.js +95 -0
  13. package/dist/announced-list.js.map +1 -0
  14. package/dist/api.d.ts +168 -0
  15. package/dist/api.d.ts.map +1 -0
  16. package/dist/api.js +10 -0
  17. package/dist/api.js.map +1 -0
  18. package/dist/cli-text.d.ts +0 -1
  19. package/dist/cli-text.d.ts.map +1 -1
  20. package/dist/cli-text.js +6 -14
  21. package/dist/cli-text.js.map +1 -1
  22. package/dist/cli.d.ts.map +1 -1
  23. package/dist/cli.js +58 -62
  24. package/dist/cli.js.map +1 -1
  25. package/dist/closing-paragraph.d.ts +7 -0
  26. package/dist/closing-paragraph.d.ts.map +1 -0
  27. package/dist/closing-paragraph.js +22 -0
  28. package/dist/closing-paragraph.js.map +1 -0
  29. package/dist/commands/compare.d.ts +8 -1
  30. package/dist/commands/compare.d.ts.map +1 -1
  31. package/dist/commands/compare.js +9 -17
  32. package/dist/commands/compare.js.map +1 -1
  33. package/dist/commands/document-commands.d.ts +9 -0
  34. package/dist/commands/document-commands.d.ts.map +1 -0
  35. package/dist/commands/document-commands.js +17 -0
  36. package/dist/commands/document-commands.js.map +1 -0
  37. package/dist/commands/eval.d.ts.map +1 -1
  38. package/dist/commands/eval.js +3 -2
  39. package/dist/commands/eval.js.map +1 -1
  40. package/dist/commands/facts.d.ts +8 -0
  41. package/dist/commands/facts.d.ts.map +1 -0
  42. package/dist/commands/facts.js +30 -0
  43. package/dist/commands/facts.js.map +1 -0
  44. package/dist/commands/feedback.d.ts +10 -0
  45. package/dist/commands/feedback.d.ts.map +1 -1
  46. package/dist/commands/feedback.js +25 -1
  47. package/dist/commands/feedback.js.map +1 -1
  48. package/dist/commands/fix-plan.d.ts +13 -0
  49. package/dist/commands/fix-plan.d.ts.map +1 -0
  50. package/dist/commands/fix-plan.js +44 -0
  51. package/dist/commands/fix-plan.js.map +1 -0
  52. package/dist/commands/outline.d.ts +8 -0
  53. package/dist/commands/outline.d.ts.map +1 -0
  54. package/dist/commands/outline.js +43 -0
  55. package/dist/commands/outline.js.map +1 -0
  56. package/dist/commands/read-document.d.ts +14 -0
  57. package/dist/commands/read-document.d.ts.map +1 -0
  58. package/dist/commands/read-document.js +21 -0
  59. package/dist/commands/read-document.js.map +1 -0
  60. package/dist/commands/suppressions.d.ts +7 -0
  61. package/dist/commands/suppressions.d.ts.map +1 -0
  62. package/dist/commands/suppressions.js +9 -0
  63. package/dist/commands/suppressions.js.map +1 -0
  64. package/dist/commands/test.js +1 -1
  65. package/dist/commands/test.js.map +1 -1
  66. package/dist/compare/clock-time.d.ts.map +1 -1
  67. package/dist/compare/clock-time.js +5 -2
  68. package/dist/compare/clock-time.js.map +1 -1
  69. package/dist/compare/distinct.d.ts +9 -0
  70. package/dist/compare/distinct.d.ts.map +1 -0
  71. package/dist/compare/distinct.js +12 -0
  72. package/dist/compare/distinct.js.map +1 -0
  73. package/dist/compare/extract.d.ts.map +1 -1
  74. package/dist/compare/extract.js +7 -5
  75. package/dist/compare/extract.js.map +1 -1
  76. package/dist/compare/fact-key.d.ts +17 -0
  77. package/dist/compare/fact-key.d.ts.map +1 -0
  78. package/dist/compare/fact-key.js +25 -0
  79. package/dist/compare/fact-key.js.map +1 -0
  80. package/dist/compare/fact-text.d.ts +5 -0
  81. package/dist/compare/fact-text.d.ts.map +1 -1
  82. package/dist/compare/fact-text.js +16 -2
  83. package/dist/compare/fact-text.js.map +1 -1
  84. package/dist/compare/facts-render.d.ts +17 -0
  85. package/dist/compare/facts-render.d.ts.map +1 -0
  86. package/dist/compare/facts-render.js +43 -0
  87. package/dist/compare/facts-render.js.map +1 -0
  88. package/dist/compare/facts-text.d.ts +15 -0
  89. package/dist/compare/facts-text.d.ts.map +1 -0
  90. package/dist/compare/facts-text.js +27 -0
  91. package/dist/compare/facts-text.js.map +1 -0
  92. package/dist/compare/match.d.ts +1 -0
  93. package/dist/compare/match.d.ts.map +1 -1
  94. package/dist/compare/match.js +1 -1
  95. package/dist/compare/match.js.map +1 -1
  96. package/dist/compare/outcome.d.ts +4 -1
  97. package/dist/compare/outcome.d.ts.map +1 -1
  98. package/dist/compare/outcome.js +10 -4
  99. package/dist/compare/outcome.js.map +1 -1
  100. package/dist/compare/proper-nouns.d.ts +3 -3
  101. package/dist/compare/proper-nouns.d.ts.map +1 -1
  102. package/dist/compare/proper-nouns.js +2 -3
  103. package/dist/compare/proper-nouns.js.map +1 -1
  104. package/dist/compare/render.d.ts +4 -0
  105. package/dist/compare/render.d.ts.map +1 -1
  106. package/dist/compare/render.js +2 -2
  107. package/dist/compare/render.js.map +1 -1
  108. package/dist/compare/text-atoms.d.ts +5 -2
  109. package/dist/compare/text-atoms.d.ts.map +1 -1
  110. package/dist/compare/text-atoms.js +7 -5
  111. package/dist/compare/text-atoms.js.map +1 -1
  112. package/dist/compare/text.d.ts.map +1 -1
  113. package/dist/compare/text.js +2 -2
  114. package/dist/compare/text.js.map +1 -1
  115. package/dist/config/load.d.ts +8 -0
  116. package/dist/config/load.d.ts.map +1 -1
  117. package/dist/config/load.js +1 -0
  118. package/dist/config/load.js.map +1 -1
  119. package/dist/config/option-problems.d.ts +3 -2
  120. package/dist/config/option-problems.d.ts.map +1 -1
  121. package/dist/config/option-problems.js +4 -3
  122. package/dist/config/option-problems.js.map +1 -1
  123. package/dist/config/style.d.ts +5 -0
  124. package/dist/config/style.d.ts.map +1 -1
  125. package/dist/config/style.js +13 -1
  126. package/dist/config/style.js.map +1 -1
  127. package/dist/custom/load.d.ts +6 -3
  128. package/dist/custom/load.d.ts.map +1 -1
  129. package/dist/custom/load.js +10 -2
  130. package/dist/custom/load.js.map +1 -1
  131. package/dist/custom/module-path.d.ts +11 -0
  132. package/dist/custom/module-path.d.ts.map +1 -0
  133. package/dist/custom/module-path.js +15 -0
  134. package/dist/custom/module-path.js.map +1 -0
  135. package/dist/custom/parse.d.ts +17 -3
  136. package/dist/custom/parse.d.ts.map +1 -1
  137. package/dist/custom/parse.js +57 -14
  138. package/dist/custom/parse.js.map +1 -1
  139. package/dist/custom/problems.d.ts +4 -1
  140. package/dist/custom/problems.d.ts.map +1 -1
  141. package/dist/custom/problems.js +56 -39
  142. package/dist/custom/problems.js.map +1 -1
  143. package/dist/detect-language.js +1 -1
  144. package/dist/detect.d.ts.map +1 -1
  145. package/dist/detect.js +9 -3
  146. package/dist/detect.js.map +1 -1
  147. package/dist/detectors/ai-phrasing.d.ts +4 -1
  148. package/dist/detectors/ai-phrasing.d.ts.map +1 -1
  149. package/dist/detectors/ai-phrasing.js +6 -2
  150. package/dist/detectors/ai-phrasing.js.map +1 -1
  151. package/dist/detectors/bold-label.d.ts +10 -0
  152. package/dist/detectors/bold-label.d.ts.map +1 -0
  153. package/dist/detectors/bold-label.js +44 -0
  154. package/dist/detectors/bold-label.js.map +1 -0
  155. package/dist/detectors/dangling-figure.d.ts +5 -2
  156. package/dist/detectors/dangling-figure.d.ts.map +1 -1
  157. package/dist/detectors/dangling-figure.js +5 -3
  158. package/dist/detectors/dangling-figure.js.map +1 -1
  159. package/dist/detectors/definition-use.d.ts +8 -0
  160. package/dist/detectors/definition-use.d.ts.map +1 -0
  161. package/dist/detectors/definition-use.js +32 -0
  162. package/dist/detectors/definition-use.js.map +1 -0
  163. package/dist/detectors/doubled-punctuation.d.ts.map +1 -1
  164. package/dist/detectors/doubled-punctuation.js +4 -2
  165. package/dist/detectors/doubled-punctuation.js.map +1 -1
  166. package/dist/detectors/duplicate-heading.d.ts +14 -0
  167. package/dist/detectors/duplicate-heading.d.ts.map +1 -0
  168. package/dist/detectors/duplicate-heading.js +39 -0
  169. package/dist/detectors/duplicate-heading.js.map +1 -0
  170. package/dist/detectors/empty-section.d.ts +8 -0
  171. package/dist/detectors/empty-section.d.ts.map +1 -0
  172. package/dist/detectors/empty-section.js +16 -0
  173. package/dist/detectors/empty-section.js.map +1 -0
  174. package/dist/detectors/fullwidth-alnum.d.ts +24 -0
  175. package/dist/detectors/fullwidth-alnum.d.ts.map +1 -0
  176. package/dist/detectors/fullwidth-alnum.js +88 -0
  177. package/dist/detectors/fullwidth-alnum.js.map +1 -0
  178. package/dist/detectors/hankaku-kana.d.ts +9 -0
  179. package/dist/detectors/hankaku-kana.d.ts.map +1 -0
  180. package/dist/detectors/hankaku-kana.js +27 -0
  181. package/dist/detectors/hankaku-kana.js.map +1 -0
  182. package/dist/detectors/heading-code-words.js +2 -2
  183. package/dist/detectors/heading-code-words.js.map +1 -1
  184. package/dist/detectors/index.d.ts.map +1 -1
  185. package/dist/detectors/index.js +36 -0
  186. package/dist/detectors/index.js.map +1 -1
  187. package/dist/detectors/invisible-character.d.ts +20 -0
  188. package/dist/detectors/invisible-character.d.ts.map +1 -0
  189. package/dist/detectors/invisible-character.js +143 -0
  190. package/dist/detectors/invisible-character.js.map +1 -0
  191. package/dist/detectors/kutoten-consistency.js +2 -2
  192. package/dist/detectors/kutoten-consistency.js.map +1 -1
  193. package/dist/detectors/lexicon-match.d.ts +10 -1
  194. package/dist/detectors/lexicon-match.d.ts.map +1 -1
  195. package/dist/detectors/lexicon-match.js +26 -1
  196. package/dist/detectors/lexicon-match.js.map +1 -1
  197. package/dist/detectors/lexicon.d.ts +6 -1
  198. package/dist/detectors/lexicon.d.ts.map +1 -1
  199. package/dist/detectors/lexicon.js +31 -13
  200. package/dist/detectors/lexicon.js.map +1 -1
  201. package/dist/detectors/list-sentence.d.ts +3 -1
  202. package/dist/detectors/list-sentence.d.ts.map +1 -1
  203. package/dist/detectors/list-sentence.js +26 -1
  204. package/dist/detectors/list-sentence.js.map +1 -1
  205. package/dist/detectors/long-vowel.d.ts.map +1 -1
  206. package/dist/detectors/long-vowel.js +18 -4
  207. package/dist/detectors/long-vowel.js.map +1 -1
  208. package/dist/detectors/markup-finding.d.ts +3 -1
  209. package/dist/detectors/markup-finding.d.ts.map +1 -1
  210. package/dist/detectors/markup-finding.js +10 -0
  211. package/dist/detectors/markup-finding.js.map +1 -1
  212. package/dist/detectors/modal-conflict.d.ts +4 -0
  213. package/dist/detectors/modal-conflict.d.ts.map +1 -0
  214. package/dist/detectors/modal-conflict.js +22 -0
  215. package/dist/detectors/modal-conflict.js.map +1 -0
  216. package/dist/detectors/name-variant.d.ts +4 -0
  217. package/dist/detectors/name-variant.d.ts.map +1 -0
  218. package/dist/detectors/name-variant.js +13 -0
  219. package/dist/detectors/name-variant.js.map +1 -0
  220. package/dist/detectors/nominalization.d.ts +11 -0
  221. package/dist/detectors/nominalization.d.ts.map +1 -0
  222. package/dist/detectors/nominalization.js +56 -0
  223. package/dist/detectors/nominalization.js.map +1 -0
  224. package/dist/detectors/orthography.d.ts.map +1 -1
  225. package/dist/detectors/orthography.js +59 -28
  226. package/dist/detectors/orthography.js.map +1 -1
  227. package/dist/detectors/outside-announced-list.d.ts +4 -0
  228. package/dist/detectors/outside-announced-list.d.ts.map +1 -0
  229. package/dist/detectors/outside-announced-list.js +24 -0
  230. package/dist/detectors/outside-announced-list.js.map +1 -0
  231. package/dist/detectors/oxford-comma.d.ts.map +1 -1
  232. package/dist/detectors/oxford-comma.js +1 -0
  233. package/dist/detectors/oxford-comma.js.map +1 -1
  234. package/dist/detectors/phrase-match.d.ts +4 -1
  235. package/dist/detectors/phrase-match.d.ts.map +1 -1
  236. package/dist/detectors/phrase-match.js +26 -7
  237. package/dist/detectors/phrase-match.js.map +1 -1
  238. package/dist/detectors/ra-nuki.d.ts +9 -0
  239. package/dist/detectors/ra-nuki.d.ts.map +1 -0
  240. package/dist/detectors/ra-nuki.js +30 -0
  241. package/dist/detectors/ra-nuki.js.map +1 -0
  242. package/dist/detectors/requirement-modal.d.ts +5 -0
  243. package/dist/detectors/requirement-modal.d.ts.map +1 -0
  244. package/dist/detectors/requirement-modal.js +54 -0
  245. package/dist/detectors/requirement-modal.js.map +1 -0
  246. package/dist/detectors/requirement-smell.d.ts +4 -0
  247. package/dist/detectors/requirement-smell.d.ts.map +1 -0
  248. package/dist/detectors/requirement-smell.js +41 -0
  249. package/dist/detectors/requirement-smell.js.map +1 -0
  250. package/dist/detectors/space-before-punctuation.d.ts +8 -0
  251. package/dist/detectors/space-before-punctuation.d.ts.map +1 -0
  252. package/dist/detectors/space-before-punctuation.js +24 -0
  253. package/dist/detectors/space-before-punctuation.js.map +1 -0
  254. package/dist/detectors/spelling-variety.d.ts +23 -0
  255. package/dist/detectors/spelling-variety.d.ts.map +1 -0
  256. package/dist/detectors/spelling-variety.js +59 -0
  257. package/dist/detectors/spelling-variety.js.map +1 -0
  258. package/dist/detectors/structure-tree.d.ts.map +1 -1
  259. package/dist/detectors/structure-tree.js +3 -1
  260. package/dist/detectors/structure-tree.js.map +1 -1
  261. package/dist/detectors/structure.d.ts.map +1 -1
  262. package/dist/detectors/structure.js +16 -2
  263. package/dist/detectors/structure.js.map +1 -1
  264. package/dist/detectors/superlative-comparison.d.ts +20 -0
  265. package/dist/detectors/superlative-comparison.d.ts.map +1 -0
  266. package/dist/detectors/superlative-comparison.js +32 -0
  267. package/dist/detectors/superlative-comparison.js.map +1 -0
  268. package/dist/detectors/url-run-on.d.ts.map +1 -1
  269. package/dist/detectors/url-run-on.js +6 -1
  270. package/dist/detectors/url-run-on.js.map +1 -1
  271. package/dist/detectors/vague-figure-pointer.d.ts +10 -0
  272. package/dist/detectors/vague-figure-pointer.d.ts.map +1 -0
  273. package/dist/detectors/vague-figure-pointer.js +46 -0
  274. package/dist/detectors/vague-figure-pointer.js.map +1 -0
  275. package/dist/detectors/word-list.d.ts +2 -0
  276. package/dist/detectors/word-list.d.ts.map +1 -1
  277. package/dist/detectors/word-list.js +4 -1
  278. package/dist/detectors/word-list.js.map +1 -1
  279. package/dist/document.d.ts +9 -3
  280. package/dist/document.d.ts.map +1 -1
  281. package/dist/document.js +55 -32
  282. package/dist/document.js.map +1 -1
  283. package/dist/extension/document-view.d.ts +7 -0
  284. package/dist/extension/document-view.d.ts.map +1 -0
  285. package/dist/extension/document-view.js +91 -0
  286. package/dist/extension/document-view.js.map +1 -0
  287. package/dist/extension/failure-text.d.ts +5 -0
  288. package/dist/extension/failure-text.d.ts.map +1 -0
  289. package/dist/extension/failure-text.js +33 -0
  290. package/dist/extension/failure-text.js.map +1 -0
  291. package/dist/extension/import-default.d.ts +7 -0
  292. package/dist/extension/import-default.d.ts.map +1 -0
  293. package/dist/extension/import-default.js +21 -0
  294. package/dist/extension/import-default.js.map +1 -0
  295. package/dist/extension/load.d.ts +28 -0
  296. package/dist/extension/load.d.ts.map +1 -0
  297. package/dist/extension/load.js +51 -0
  298. package/dist/extension/load.js.map +1 -0
  299. package/dist/extension/module-detector.d.ts +21 -0
  300. package/dist/extension/module-detector.d.ts.map +1 -0
  301. package/dist/extension/module-detector.js +49 -0
  302. package/dist/extension/module-detector.js.map +1 -0
  303. package/dist/extension/plugin-load.d.ts +9 -0
  304. package/dist/extension/plugin-load.d.ts.map +1 -0
  305. package/dist/extension/plugin-load.js +68 -0
  306. package/dist/extension/plugin-load.js.map +1 -0
  307. package/dist/extension/plugin-name.d.ts +8 -0
  308. package/dist/extension/plugin-name.d.ts.map +1 -0
  309. package/dist/extension/plugin-name.js +22 -0
  310. package/dist/extension/plugin-name.js.map +1 -0
  311. package/dist/extension/plugin-parse.d.ts +41 -0
  312. package/dist/extension/plugin-parse.d.ts.map +1 -0
  313. package/dist/extension/plugin-parse.js +156 -0
  314. package/dist/extension/plugin-parse.js.map +1 -0
  315. package/dist/extension/problem-text.d.ts +5 -0
  316. package/dist/extension/problem-text.d.ts.map +1 -0
  317. package/dist/extension/problem-text.js +74 -0
  318. package/dist/extension/problem-text.js.map +1 -0
  319. package/dist/extension/real-path.d.ts +3 -0
  320. package/dist/extension/real-path.d.ts.map +1 -0
  321. package/dist/extension/real-path.js +8 -0
  322. package/dist/extension/real-path.js.map +1 -0
  323. package/dist/extension/returned-findings.d.ts +17 -0
  324. package/dist/extension/returned-findings.d.ts.map +1 -0
  325. package/dist/extension/returned-findings.js +48 -0
  326. package/dist/extension/returned-findings.js.map +1 -0
  327. package/dist/extension/rule-export.d.ts +13 -0
  328. package/dist/extension/rule-export.d.ts.map +1 -0
  329. package/dist/extension/rule-export.js +16 -0
  330. package/dist/extension/rule-export.js.map +1 -0
  331. package/dist/feedback/draft.d.ts +2 -0
  332. package/dist/feedback/draft.d.ts.map +1 -1
  333. package/dist/feedback/draft.js +3 -0
  334. package/dist/feedback/draft.js.map +1 -1
  335. package/dist/figure-references.d.ts +11 -2
  336. package/dist/figure-references.d.ts.map +1 -1
  337. package/dist/figure-references.js +50 -13
  338. package/dist/figure-references.js.map +1 -1
  339. package/dist/finding-order.d.ts +9 -0
  340. package/dist/finding-order.d.ts.map +1 -0
  341. package/dist/finding-order.js +3 -0
  342. package/dist/finding-order.js.map +1 -0
  343. package/dist/fix-plan/mode.d.ts +21 -0
  344. package/dist/fix-plan/mode.d.ts.map +1 -0
  345. package/dist/fix-plan/mode.js +16 -0
  346. package/dist/fix-plan/mode.js.map +1 -0
  347. package/dist/fix-plan/plan.d.ts +77 -0
  348. package/dist/fix-plan/plan.d.ts.map +1 -0
  349. package/dist/fix-plan/plan.js +100 -0
  350. package/dist/fix-plan/plan.js.map +1 -0
  351. package/dist/fix-plan/render.d.ts +6 -0
  352. package/dist/fix-plan/render.d.ts.map +1 -0
  353. package/dist/fix-plan/render.js +101 -0
  354. package/dist/fix-plan/render.js.map +1 -0
  355. package/dist/fix-plan/text.d.ts +35 -0
  356. package/dist/fix-plan/text.d.ts.map +1 -0
  357. package/dist/fix-plan/text.js +95 -0
  358. package/dist/fix-plan/text.js.map +1 -0
  359. package/dist/language-sample.d.ts +2 -0
  360. package/dist/language-sample.d.ts.map +1 -0
  361. package/dist/language-sample.js +41 -0
  362. package/dist/language-sample.js.map +1 -0
  363. package/dist/link-line.d.ts +8 -0
  364. package/dist/link-line.d.ts.map +1 -0
  365. package/dist/link-line.js +12 -0
  366. package/dist/link-line.js.map +1 -0
  367. package/dist/long-vowel.d.ts +9 -6
  368. package/dist/long-vowel.d.ts.map +1 -1
  369. package/dist/long-vowel.js +15 -8
  370. package/dist/long-vowel.js.map +1 -1
  371. package/dist/markup.d.ts.map +1 -1
  372. package/dist/markup.js +4 -2
  373. package/dist/markup.js.map +1 -1
  374. package/dist/name-variants.d.ts +25 -0
  375. package/dist/name-variants.d.ts.map +1 -0
  376. package/dist/name-variants.js +181 -0
  377. package/dist/name-variants.js.map +1 -0
  378. package/dist/node-version.d.ts +7 -0
  379. package/dist/node-version.d.ts.map +1 -0
  380. package/dist/node-version.js +19 -0
  381. package/dist/node-version.js.map +1 -0
  382. package/dist/not-run.d.ts +14 -0
  383. package/dist/not-run.d.ts.map +1 -0
  384. package/dist/not-run.js +12 -0
  385. package/dist/not-run.js.map +1 -0
  386. package/dist/orthography.d.ts +8 -1
  387. package/dist/orthography.d.ts.map +1 -1
  388. package/dist/orthography.js +33 -12
  389. package/dist/orthography.js.map +1 -1
  390. package/dist/outline/render.d.ts +18 -0
  391. package/dist/outline/render.d.ts.map +1 -0
  392. package/dist/outline/render.js +60 -0
  393. package/dist/outline/render.js.map +1 -0
  394. package/dist/outline/shape.d.ts +26 -0
  395. package/dist/outline/shape.d.ts.map +1 -0
  396. package/dist/outline/shape.js +37 -0
  397. package/dist/outline/shape.js.map +1 -0
  398. package/dist/outline/text.d.ts +21 -0
  399. package/dist/outline/text.d.ts.map +1 -0
  400. package/dist/outline/text.js +30 -0
  401. package/dist/outline/text.js.map +1 -0
  402. package/dist/plugin.d.ts +24 -2
  403. package/dist/plugin.d.ts.map +1 -1
  404. package/dist/reasons.d.ts +1 -0
  405. package/dist/reasons.d.ts.map +1 -1
  406. package/dist/reasons.js +2 -0
  407. package/dist/reasons.js.map +1 -1
  408. package/dist/render/compact.d.ts +3 -1
  409. package/dist/render/compact.d.ts.map +1 -1
  410. package/dist/render/compact.js +8 -2
  411. package/dist/render/compact.js.map +1 -1
  412. package/dist/render/explain.d.ts +3 -1
  413. package/dist/render/explain.d.ts.map +1 -1
  414. package/dist/render/explain.js +32 -15
  415. package/dist/render/explain.js.map +1 -1
  416. package/dist/render/rules-json.d.ts.map +1 -1
  417. package/dist/render/rules-json.js +16 -12
  418. package/dist/render/rules-json.js.map +1 -1
  419. package/dist/render/suppressions.d.ts +3 -0
  420. package/dist/render/suppressions.d.ts.map +1 -1
  421. package/dist/render/suppressions.js +20 -1
  422. package/dist/render/suppressions.js.map +1 -1
  423. package/dist/rule-guide.d.ts +19 -0
  424. package/dist/rule-guide.d.ts.map +1 -1
  425. package/dist/rule-guide.js +14 -0
  426. package/dist/rule-guide.js.map +1 -1
  427. package/dist/run-semantic.d.ts.map +1 -1
  428. package/dist/run-semantic.js +2 -1
  429. package/dist/run-semantic.js.map +1 -1
  430. package/dist/run.d.ts +5 -1
  431. package/dist/run.d.ts.map +1 -1
  432. package/dist/run.js +31 -14
  433. package/dist/run.js.map +1 -1
  434. package/dist/setting-problems.d.ts.map +1 -1
  435. package/dist/setting-problems.js +3 -1
  436. package/dist/setting-problems.js.map +1 -1
  437. package/dist/spacing-minority.d.ts +16 -0
  438. package/dist/spacing-minority.d.ts.map +1 -0
  439. package/dist/spacing-minority.js +23 -0
  440. package/dist/spacing-minority.js.map +1 -0
  441. package/dist/stet-block.d.ts +5 -0
  442. package/dist/stet-block.d.ts.map +1 -0
  443. package/dist/stet-block.js +59 -0
  444. package/dist/stet-block.js.map +1 -0
  445. package/dist/stet.d.ts +4 -0
  446. package/dist/stet.d.ts.map +1 -1
  447. package/dist/stet.js +31 -11
  448. package/dist/stet.js.map +1 -1
  449. package/dist/structure/date-range.d.ts +10 -0
  450. package/dist/structure/date-range.d.ts.map +1 -1
  451. package/dist/structure/date-range.js +33 -1
  452. package/dist/structure/date-range.js.map +1 -1
  453. package/dist/structure/definition-use.d.ts +32 -0
  454. package/dist/structure/definition-use.d.ts.map +1 -0
  455. package/dist/structure/definition-use.js +102 -0
  456. package/dist/structure/definition-use.js.map +1 -0
  457. package/dist/structure/lazy-tree.d.ts +8 -0
  458. package/dist/structure/lazy-tree.d.ts.map +1 -0
  459. package/dist/structure/lazy-tree.js +14 -0
  460. package/dist/structure/lazy-tree.js.map +1 -0
  461. package/dist/structure/modal-conflict.d.ts +28 -0
  462. package/dist/structure/modal-conflict.d.ts.map +1 -0
  463. package/dist/structure/modal-conflict.js +74 -0
  464. package/dist/structure/modal-conflict.js.map +1 -0
  465. package/dist/structure/numbered-clauses.d.ts +4 -0
  466. package/dist/structure/numbered-clauses.d.ts.map +1 -0
  467. package/dist/structure/numbered-clauses.js +9 -0
  468. package/dist/structure/numbered-clauses.js.map +1 -0
  469. package/dist/structure/universal.d.ts.map +1 -1
  470. package/dist/structure/universal.js +8 -2
  471. package/dist/structure/universal.js.map +1 -1
  472. package/dist/style-parse.d.ts +6 -0
  473. package/dist/style-parse.d.ts.map +1 -1
  474. package/dist/style-parse.js +32 -4
  475. package/dist/style-parse.js.map +1 -1
  476. package/dist/subheading-line.d.ts +19 -2
  477. package/dist/subheading-line.d.ts.map +1 -1
  478. package/dist/subheading-line.js +39 -11
  479. package/dist/subheading-line.js.map +1 -1
  480. package/dist/tag-coverage.d.ts +8 -0
  481. package/dist/tag-coverage.d.ts.map +1 -0
  482. package/dist/tag-coverage.js +7 -0
  483. package/dist/tag-coverage.js.map +1 -0
  484. package/genres.yaml +42 -0
  485. package/package.json +7 -3
  486. package/rules/acronym-expansion-conflict.yaml +82 -0
  487. package/rules/adverb-overuse.yaml +2 -2
  488. package/rules/agentless-passive.yaml +28 -0
  489. package/rules/ai-tell.yaml +34 -0
  490. package/rules/announced-count-mismatch.yaml +2 -2
  491. package/rules/announcing-opener.yaml +31 -0
  492. package/rules/assistant-residue.yaml +30 -0
  493. package/rules/bold-density.yaml +28 -0
  494. package/rules/bold-label-list.yaml +81 -0
  495. package/rules/closing-cliche.yaml +27 -2
  496. package/rules/colon-lead-in.yaml +28 -4
  497. package/rules/contrast-framing.yaml +33 -2
  498. package/rules/cushion-phrase-density.yaml +32 -5
  499. package/rules/dangling-reference.yaml +1 -0
  500. package/rules/date-order.yaml +1 -0
  501. package/rules/date-range-reversed.yaml +2 -2
  502. package/rules/double-negative.yaml +51 -0
  503. package/rules/doubled-punctuation.yaml +2 -2
  504. package/rules/doubled-word.yaml +2 -2
  505. package/rules/duplicate-heading.yaml +78 -0
  506. package/rules/emoji-density.yaml +5 -4
  507. package/rules/empty-intensifier.yaml +25 -0
  508. package/rules/empty-section.yaml +74 -0
  509. package/rules/excessive-hedging.yaml +32 -4
  510. package/rules/fullwidth-alnum-consistency.yaml +53 -0
  511. package/rules/hankaku-kana.yaml +41 -0
  512. package/rules/image-alt-text.yaml +1 -1
  513. package/rules/invisible-character.yaml +63 -0
  514. package/rules/katakana-long-vowel.yaml +10 -9
  515. package/rules/latin-spacing.yaml +12 -5
  516. package/rules/max-kanji-continuous.yaml +1 -0
  517. package/rules/max-paragraph-length.yaml +1 -0
  518. package/rules/max-sentence-length.yaml +26 -0
  519. package/rules/modal-conflict.yaml +106 -0
  520. package/rules/name-variant.yaml +88 -0
  521. package/rules/no-doubled-joshi.yaml +1 -0
  522. package/rules/no-em-dash.yaml +26 -2
  523. package/rules/no-mixed-desumasu.yaml +1 -0
  524. package/rules/nominalization.yaml +89 -0
  525. package/rules/outside-announced-list.yaml +80 -0
  526. package/rules/padded-intro.yaml +25 -0
  527. package/rules/paragraph-length-variance.yaml +26 -2
  528. package/rules/preferred-term.yaml +1 -0
  529. package/rules/proper-noun-density.yaml +14 -5
  530. package/rules/ra-nuki.yaml +43 -0
  531. package/rules/requirement-modal.yaml +94 -0
  532. package/rules/requirement-smell.yaml +70 -0
  533. package/rules/rule-of-three.yaml +46 -0
  534. package/rules/section-length-uniformity.yaml +26 -2
  535. package/rules/sentence-rhythm.yaml +26 -0
  536. package/rules/space-before-punctuation.yaml +41 -0
  537. package/rules/spelling-consistency.yaml +47 -0
  538. package/rules/stock-transition.yaml +33 -2
  539. package/rules/taigen-dome-in-prose.yaml +14 -0
  540. package/rules/total-mismatch.yaml +1 -0
  541. package/rules/undefined-acronym.yaml +1 -0
  542. package/rules/unqualified-superlative.yaml +5 -4
  543. package/rules/unused-definition.yaml +75 -0
  544. package/rules/use-before-definition.yaml +77 -0
  545. package/rules/vague-figure-reference.yaml +66 -0
  546. package/skills/chaff/SKILL.md +108 -8
  547. package/src/acronym-expansions.ts +106 -0
  548. package/src/announced-count.ts +53 -11
  549. package/src/announced-list.ts +120 -0
  550. package/src/api.ts +154 -0
  551. package/src/cli-text.ts +6 -11
  552. package/src/cli.ts +64 -63
  553. package/src/closing-paragraph.ts +24 -0
  554. package/src/commands/compare.ts +11 -18
  555. package/src/commands/document-commands.ts +19 -0
  556. package/src/commands/eval.ts +3 -2
  557. package/src/commands/facts.ts +33 -0
  558. package/src/commands/feedback.ts +33 -1
  559. package/src/commands/fix-plan.ts +53 -0
  560. package/src/commands/outline.ts +49 -0
  561. package/src/commands/read-document.ts +24 -0
  562. package/src/commands/suppressions.ts +19 -0
  563. package/src/commands/test.ts +1 -1
  564. package/src/compare/clock-time.ts +5 -2
  565. package/src/compare/distinct.ts +13 -0
  566. package/src/compare/extract.ts +7 -5
  567. package/src/compare/fact-key.ts +31 -0
  568. package/src/compare/fact-text.ts +21 -2
  569. package/src/compare/facts-render.ts +63 -0
  570. package/src/compare/facts-text.ts +41 -0
  571. package/src/compare/match.ts +1 -1
  572. package/src/compare/outcome.ts +14 -4
  573. package/src/compare/proper-nouns.ts +4 -4
  574. package/src/compare/render.ts +2 -2
  575. package/src/compare/text-atoms.ts +7 -6
  576. package/src/compare/text.ts +3 -2
  577. package/src/config/load.ts +9 -0
  578. package/src/config/option-problems.ts +6 -4
  579. package/src/config/style.ts +14 -1
  580. package/src/custom/load.ts +11 -3
  581. package/src/custom/module-path.ts +23 -0
  582. package/src/custom/parse.ts +67 -18
  583. package/src/custom/problems.ts +63 -41
  584. package/src/detect-language.ts +1 -1
  585. package/src/detect.ts +10 -3
  586. package/src/detectors/ai-phrasing.ts +6 -2
  587. package/src/detectors/bold-label.ts +55 -0
  588. package/src/detectors/dangling-figure.ts +8 -4
  589. package/src/detectors/definition-use.ts +41 -0
  590. package/src/detectors/doubled-punctuation.ts +5 -2
  591. package/src/detectors/duplicate-heading.ts +47 -0
  592. package/src/detectors/empty-section.ts +21 -0
  593. package/src/detectors/fullwidth-alnum.ts +118 -0
  594. package/src/detectors/hankaku-kana.ts +38 -0
  595. package/src/detectors/heading-code-words.ts +2 -2
  596. package/src/detectors/index.ts +36 -0
  597. package/src/detectors/invisible-character.ts +190 -0
  598. package/src/detectors/kutoten-consistency.ts +2 -2
  599. package/src/detectors/lexicon-match.ts +33 -1
  600. package/src/detectors/lexicon.ts +40 -13
  601. package/src/detectors/list-sentence.ts +45 -4
  602. package/src/detectors/long-vowel.ts +20 -4
  603. package/src/detectors/markup-finding.ts +16 -1
  604. package/src/detectors/modal-conflict.ts +26 -0
  605. package/src/detectors/name-variant.ts +15 -0
  606. package/src/detectors/nominalization.ts +73 -0
  607. package/src/detectors/orthography.ts +77 -27
  608. package/src/detectors/outside-announced-list.ts +25 -0
  609. package/src/detectors/oxford-comma.ts +1 -0
  610. package/src/detectors/phrase-match.ts +27 -7
  611. package/src/detectors/ra-nuki.ts +38 -0
  612. package/src/detectors/requirement-modal.ts +67 -0
  613. package/src/detectors/requirement-smell.ts +52 -0
  614. package/src/detectors/space-before-punctuation.ts +32 -0
  615. package/src/detectors/spelling-variety.ts +81 -0
  616. package/src/detectors/structure-tree.ts +3 -1
  617. package/src/detectors/structure.ts +17 -3
  618. package/src/detectors/superlative-comparison.ts +47 -0
  619. package/src/detectors/url-run-on.ts +7 -1
  620. package/src/detectors/vague-figure-pointer.ts +58 -0
  621. package/src/detectors/word-list.ts +6 -1
  622. package/src/document.ts +88 -41
  623. package/src/extension/document-view.ts +109 -0
  624. package/src/extension/failure-text.ts +46 -0
  625. package/src/extension/import-default.ts +13 -0
  626. package/src/extension/load.ts +97 -0
  627. package/src/extension/module-detector.ts +61 -0
  628. package/src/extension/plugin-load.ts +82 -0
  629. package/src/extension/plugin-name.ts +26 -0
  630. package/src/extension/plugin-parse.ts +229 -0
  631. package/src/extension/problem-text.ts +103 -0
  632. package/src/extension/real-path.ts +10 -0
  633. package/src/extension/returned-findings.ts +62 -0
  634. package/src/extension/rule-export.ts +23 -0
  635. package/src/feedback/draft.ts +6 -0
  636. package/src/figure-references.ts +73 -12
  637. package/src/finding-order.ts +3 -0
  638. package/src/fix-plan/mode.ts +34 -0
  639. package/src/fix-plan/plan.ts +181 -0
  640. package/src/fix-plan/render.ts +128 -0
  641. package/src/fix-plan/text.ts +133 -0
  642. package/src/language-sample.ts +48 -0
  643. package/src/link-line.ts +13 -0
  644. package/src/long-vowel.ts +18 -8
  645. package/src/markup.ts +4 -2
  646. package/src/name-variants.ts +217 -0
  647. package/src/node-version.ts +22 -0
  648. package/src/not-run.ts +16 -0
  649. package/src/orthography.ts +40 -11
  650. package/src/outline/render.ts +85 -0
  651. package/src/outline/shape.ts +72 -0
  652. package/src/outline/text.ts +50 -0
  653. package/src/plugin.ts +21 -4
  654. package/src/reasons.ts +3 -0
  655. package/src/render/compact.ts +13 -3
  656. package/src/render/explain.ts +47 -20
  657. package/src/render/rules-json.ts +18 -12
  658. package/src/render/suppressions.ts +30 -2
  659. package/src/rule-guide.ts +38 -0
  660. package/src/run-semantic.ts +2 -1
  661. package/src/run.ts +37 -17
  662. package/src/setting-problems.ts +3 -1
  663. package/src/spacing-minority.ts +27 -0
  664. package/src/stet-block.ts +60 -0
  665. package/src/stet.ts +41 -15
  666. package/src/structure/date-range.ts +49 -1
  667. package/src/structure/definition-use.ts +130 -0
  668. package/src/structure/lazy-tree.ts +15 -0
  669. package/src/structure/modal-conflict.ts +86 -0
  670. package/src/structure/numbered-clauses.ts +11 -0
  671. package/src/structure/universal.ts +10 -2
  672. package/src/style-parse.ts +38 -3
  673. package/src/subheading-line.ts +51 -13
  674. package/src/tag-coverage.ts +13 -0
  675. package/styles/ieice.yaml +5 -4
  676. package/styles/jis-z8301-2011.yaml +5 -2
  677. package/styles/jis-z8301-2019.yaml +31 -0
  678. package/styles/koyobun.yaml +35 -0
  679. package/dist/legal-address.d.ts +0 -2
  680. package/dist/legal-address.d.ts.map +0 -1
  681. package/dist/legal-address.js +0 -16
  682. package/dist/legal-address.js.map +0 -1
@@ -0,0 +1,53 @@
1
+ import { existsSync } from "node:fs";
2
+ import { outlineOf } from "../outline/shape.ts";
3
+ import { buildFixPlan } from "../fix-plan/plan.ts";
4
+ import { renderFixPlanJson, renderFixPlanMarkdown } from "../fix-plan/render.ts";
5
+ import { FIX_PLAN_TEXT } from "../fix-plan/text.ts";
6
+ import type { Checked } from "./feedback.ts";
7
+ import { readDocument } from "./read-document.ts";
8
+ import type { TreeContext } from "./tree.ts";
9
+
10
+ /** Options whose value is the next argument. That value is not a file. */
11
+ const VALUED: ReadonlySet<string> = new Set(["--language", "--genre"]);
12
+
13
+ export const fixPlanTargets = (argv: readonly string[]): string[] =>
14
+ argv.slice(1).filter((arg, index, all) => !arg.startsWith("--") && !VALUED.has(all[index - 1] ?? ""));
15
+
16
+ export type FixPlanContext = TreeContext & {
17
+ /** The file's findings as lint finds them, shelved ones included. */
18
+ readonly check: (path: string) => Promise<Omit<Checked, "conditions">>;
19
+ };
20
+
21
+ /**
22
+ * An instruction document for whoever rewrites the file: the findings grouped by rule, each rule's rewrite direction,
23
+ * the document-level signals, a recommended mode and the checks to run after. Deterministic; sends nothing anywhere.
24
+ */
25
+ export const runFixPlan = async (targets: readonly string[], argv: readonly string[], context: FixPlanContext): Promise<number> => {
26
+ const [path] = targets;
27
+ if (path === undefined || targets.length > 1) {
28
+ console.error(FIX_PLAN_TEXT[context.ui ?? "ja"].usage);
29
+ return 1;
30
+ }
31
+ if (!existsSync(path)) {
32
+ console.error(FIX_PLAN_TEXT[context.ui ?? "ja"].notFound(path));
33
+ return 1;
34
+ }
35
+ const checked = await context.check(path);
36
+ // The outline is read in the language lint chose, so the plan's findings and its outline describe one reading.
37
+ const prose = await readDocument(path, ["fix-plan", "--language", checked.language, ...argv.slice(1)], context, false);
38
+ if (prose === undefined) return 1;
39
+ const plan = buildFixPlan({
40
+ path,
41
+ language: checked.language,
42
+ genre: checked.genre,
43
+ experimental: context.config.experimental || argv.includes("--experimental"),
44
+ genreFlag: context.flag(argv, "--genre"),
45
+ findings: checked.findings,
46
+ rules: checked.rules,
47
+ skipped: checked.skipped,
48
+ outline: outlineOf(prose.doc),
49
+ phrases: prose.doc.lexicons["ai-tell"] ?? [],
50
+ });
51
+ console.log(argv.includes("--json") ? renderFixPlanJson(plan) : renderFixPlanMarkdown(plan));
52
+ return 0;
53
+ };
@@ -0,0 +1,49 @@
1
+ import { uiLanguageOf } from "../ui.ts";
2
+ import { outlineOf } from "../outline/shape.ts";
3
+ import { renderOutlineCompact, renderOutlineFriendly, renderOutlineJson, type DocumentOutline } from "../outline/render.ts";
4
+ import { OUTLINE_TEXT } from "../outline/text.ts";
5
+ import { readDocument } from "./read-document.ts";
6
+ import type { TreeContext } from "./tree.ts";
7
+
8
+ /** Options whose value is the next argument. That value is not a file. */
9
+ const VALUED: ReadonlySet<string> = new Set(["--language", "--genre"]);
10
+
11
+ export const outlineTargets = (argv: readonly string[]): string[] =>
12
+ argv.slice(1).filter((arg, index, all) => !arg.startsWith("--") && !VALUED.has(all[index - 1] ?? ""));
13
+
14
+ const MOST_FILES = 2;
15
+
16
+ const readOutline = async (path: string, argv: readonly string[], context: TreeContext): Promise<DocumentOutline | undefined> => {
17
+ const prose = await readDocument(path, argv, context, false);
18
+ return prose === undefined ? undefined : { path, language: prose.language, outline: outlineOf(prose.doc) };
19
+ };
20
+
21
+ /** The files in the order given, or undefined once one cannot be read (its error is already printed). */
22
+ const readAll = async (paths: readonly string[], argv: readonly string[], context: TreeContext): Promise<DocumentOutline[] | undefined> =>
23
+ paths.reduce<Promise<DocumentOutline[] | undefined>>(async (previous, path) => {
24
+ const earlier = await previous;
25
+ if (earlier === undefined) return undefined;
26
+ const next = await readOutline(path, argv, context);
27
+ return next === undefined ? undefined : [...earlier, next];
28
+ }, Promise.resolve([]));
29
+
30
+ const renderFor = (argv: readonly string[], documents: readonly DocumentOutline[]): string => {
31
+ const text = OUTLINE_TEXT[uiLanguageOf(documents[0]?.language)];
32
+ if (argv.includes("--json")) return renderOutlineJson(documents);
33
+ return argv.includes("--compact") ? renderOutlineCompact(documents, text) : renderOutlineFriendly(documents, text);
34
+ };
35
+
36
+ /**
37
+ * A document's outline and shape (headings, average section length, text in lists, bold), or two documents' side by
38
+ * side: a restructure measured by machine. Measures; never judges, so it always ends with 0 once the files are read.
39
+ */
40
+ export const runOutline = async (targets: readonly string[], argv: readonly string[], context: TreeContext): Promise<number> => {
41
+ if (targets.length === 0 || targets.length > MOST_FILES) {
42
+ console.error(OUTLINE_TEXT[context.ui ?? "ja"].usage);
43
+ return 1;
44
+ }
45
+ const documents = await readAll(targets, argv, context);
46
+ if (documents === undefined) return 1;
47
+ console.log(renderFor(argv, documents));
48
+ return 0;
49
+ };
@@ -0,0 +1,24 @@
1
+ import { loadAdapter } from "../adapter-load.ts";
2
+ import { buildDocument, teamRules } from "../document.ts";
3
+ import { profileFor } from "../profile/for-file.ts";
4
+ import { resolveGenre } from "../resolve-genre.ts";
5
+ import type { ProseDocument, StructurePatterns } from "../plugin.ts";
6
+ import { readSource, treeLanguage, type TreeContext } from "./tree.ts";
7
+
8
+ /** One document as lint reads it, and its language package's structure reader. */
9
+ export type ReadDocument = { readonly doc: ProseDocument; readonly language: string; readonly structure: StructurePatterns | undefined };
10
+
11
+ /**
12
+ * Reads one file as lint does: its language, genre, document profile and the team's rules. `pos` loads the
13
+ * part-of-speech tagger, which costs time, so only a command that reads names asks for it.
14
+ */
15
+ export const readDocument = async (path: string, argv: readonly string[], context: TreeContext, pos: boolean): Promise<ReadDocument | undefined> => {
16
+ const source = await readSource(path, context);
17
+ if (source === undefined) return undefined;
18
+ const language = treeLanguage(path, source, argv, context);
19
+ const adapter = await loadAdapter(language);
20
+ await adapter.prepare?.({ pos });
21
+ const genre = resolveGenre(path, source, context.config, context.flag(argv, "--genre")).genre;
22
+ const doc = buildDocument(path, source, adapter, teamRules(context.config), profileFor(context.config, path, source, language, genre));
23
+ return { doc, language, structure: adapter.structure };
24
+ };
@@ -0,0 +1,19 @@
1
+ import { collectTargets } from "../files.ts";
2
+ import { renderSuppressions, type PerFile } from "../render/suppressions.ts";
3
+ import type { UiLanguage } from "../ui.ts";
4
+
5
+ /** `chaff suppressions`: what the stets in these files (this folder by default) silence, and the stets whose rule did not run. */
6
+ export const runSuppressions = async (
7
+ targets: readonly string[],
8
+ inspect: (path: string) => Promise<{ readonly perFile: PerFile }>,
9
+ ui: UiLanguage,
10
+ ): Promise<number> => {
11
+ const inspected = await Promise.all(collectTargets(targets.length > 0 ? targets : ["."]).map(inspect));
12
+ console.log(
13
+ renderSuppressions(
14
+ inspected.map((file) => file.perFile),
15
+ ui,
16
+ ),
17
+ );
18
+ return 0;
19
+ };
@@ -91,7 +91,7 @@ const semanticDocument = async (path: string, config: Config, resolveGenre: Test
91
91
  const { genre } = resolveGenre(path, source, config);
92
92
  const rules = loadRules(language);
93
93
  await adapter.prepare?.(semanticNeeds(rules, config.rules, genre));
94
- const doc = buildDocument(path, source, adapter, teamRules(config), profileFor(config, path, source, language, genre));
94
+ const doc = buildDocument(path, source, adapter, teamRules(config, language), profileFor(config, path, source, language, genre));
95
95
  return { doc, rules, language, genre };
96
96
  };
97
97
 
@@ -16,8 +16,11 @@ const COLON_TIME = new RegExp(
16
16
  /** 3 p.m., 11am. */
17
17
  const MERIDIEM_TIME = new RegExp(`(?<![0-90-9::])(${DIGIT}{1,2})\\s?([ap])\\.?m\\.?(?![a-z])`, "giu");
18
18
 
19
- /** 午後3時30分, 10時半, 9時. */
20
- const JAPANESE_TIME = new RegExp(`(午前|午後)?(${DIGIT}{1,2})時(?:(${DIGIT}{1,2})分|(半))?`, "gu");
19
+ /**
20
+ * 午後3時30分, 10時半, 9時. 時間 is a length of time (8時間), not a time of day, and the hour is not the end of a longer
21
+ * number (1.2時間, 123時).
22
+ */
23
+ const JAPANESE_TIME = new RegExp(`(午前|午後)?(?<![0-90-9..])(${DIGIT}{1,2})時(?!間)(?:(${DIGIT}{1,2})分|(半))?`, "gu");
21
24
 
22
25
  const LAST_HOUR = 24;
23
26
  const MINUTES_PER_HOUR = 60;
@@ -0,0 +1,13 @@
1
+ import type { Atom } from "./atom.ts";
2
+ import { identity } from "./match.ts";
3
+
4
+ /**
5
+ * The changes left when facts are compared as sets (--distinct): a fact the other document states at least once is
6
+ * kept, however many times either states it. A summary that repeated the body can be cut without its repeats reading
7
+ * as dropped. A name counts as stated when its spelling is in the other document's text (otherNameText), as when
8
+ * names are counted.
9
+ */
10
+ export const unstatedChanges = (changes: readonly Atom[], other: readonly Atom[], otherNameText: string): Atom[] => {
11
+ const stated = new Set(other.map(identity));
12
+ return changes.filter((atom) => !stated.has(identity(atom)) && !(atom.kind === "name" && otherNameText.includes(atom.key)));
13
+ };
@@ -5,7 +5,8 @@ import type { Atom, Extraction, Unread } from "./atom.ts";
5
5
  import { clockTimes } from "./clock-time.ts";
6
6
  import { factTextOf, type FactText } from "./fact-text.ts";
7
7
  import { codeAtoms, headingAtoms, urlAtoms } from "./markup-atoms.ts";
8
- import { nameKey, properNouns, readsProperNouns } from "./proper-nouns.ts";
8
+ import { properNouns, readsProperNouns } from "./proper-nouns.ts";
9
+ import { factKey, seenTextOf, wholeSpanOf } from "./fact-key.ts";
9
10
  import { spanIndex } from "./spans.ts";
10
11
  import { atomsOf, bareNumbers, footnotes, numericDates, quotations, teamNames, type TextInput } from "./text-atoms.ts";
11
12
  import { treeFacts, type TreeFacts } from "./tree-atoms.ts";
@@ -39,16 +40,17 @@ const proseFacts = (input: ExtractInput, readers: Readers, tree: TreeFacts, time
39
40
  const { facts, text, lineOf } = readers;
40
41
  const marks = footnotes(facts.codeless);
41
42
  const dates = numericDates(facts.text, spanIndex([...tree.taken, ...times]));
42
- const names = teamNames(facts.text, input.names);
43
+ const seen = seenTextOf(facts.text, facts.unseen);
44
+ const names = teamNames(seen.text, input.names).map((found) => wholeSpanOf(seen, found));
43
45
  const taken = [...tree.taken, ...times, ...marks, ...dates];
44
46
  return [
45
47
  ...atomsOf(dates, "date", text),
46
48
  ...atomsOf(marks, "footnote", text),
47
49
  ...atomsOf(names, "name", text),
48
50
  ...bareNumbers(text, spanIndex([...taken, ...facts.listMarkers])),
49
- ...quotations(text),
51
+ ...quotations(text, facts.unseen),
50
52
  // "April" in a date and "Section" in a reference are read with them, not again as names.
51
- ...properNouns({ doc: input.doc, taken: spanIndex([...taken, ...names, ...facts.blanked]), lineOf }),
53
+ ...properNouns({ doc: input.doc, taken: spanIndex([...taken, ...names, ...facts.blanked]), lineOf, unseen: facts.unseen }),
52
54
  ];
53
55
  };
54
56
 
@@ -74,5 +76,5 @@ export const extractFacts = (input: ExtractInput): Extraction => {
74
76
  ...(input.root === undefined ? [] : codeAtoms(input.root, lineOf)),
75
77
  ...headingAtoms(input.doc.markup, lineOf),
76
78
  ];
77
- return { atoms, unread: unreadOf(input), nameText: nameKey(facts.text) };
79
+ return { atoms, unread: unreadOf(input), nameText: factKey(facts.text, { start: 0, end: source.length }, facts.unseen) };
78
80
  };
@@ -0,0 +1,31 @@
1
+ import { spansWithin, withoutSpans } from "../soft-break.ts";
2
+ import type { Span } from "../plugin.ts";
3
+
4
+ /**
5
+ * The text of a span as one spelling: what a reader never sees removed (unseen is sorted and does not overlap: a line
6
+ * break that vanishes between two wide characters, the marks of bold), any other run of white space one space,
7
+ * full-width and half-width forms alike.
8
+ */
9
+ export const factKey = (text: string, span: Span, unseen: readonly Span[]): string => {
10
+ const inside = spansWithin(unseen, span).map((part) => ({ start: part.start - span.start, end: part.end - span.start }));
11
+ return withoutSpans(text.slice(span.start, span.end), inside).normalize("NFKC").replace(/\s+/gu, " ");
12
+ };
13
+
14
+ /** A text without its unseen parts, and where each of its characters stands in the whole text. */
15
+ export type SeenText = { readonly text: string; readonly offsets: readonly number[] };
16
+
17
+ /** The text as a reader sees it, for finding words that a vanishing line break or bold marks split (日本\n銀行). */
18
+ export const seenTextOf = (text: string, unseen: readonly Span[]): SeenText => {
19
+ const kept = [...unseen, { start: text.length, end: text.length }].map((part, index) => ({ start: unseen[index - 1]?.end ?? 0, end: part.start }));
20
+ return {
21
+ text: kept.map((part) => text.slice(part.start, part.end)).join(""),
22
+ offsets: kept.flatMap((part) => Array.from({ length: part.end - part.start }, (_, index) => part.start + index)),
23
+ };
24
+ };
25
+
26
+ /** A span found in the seen text, as a span of the whole text: from its first character to just after its last. */
27
+ export const wholeSpanOf = <T extends Span>(seen: SeenText, found: T): T => ({
28
+ ...found,
29
+ start: seen.offsets[found.start] ?? found.start,
30
+ end: (seen.offsets[found.end - 1] ?? found.end - 1) + 1,
31
+ });
@@ -1,8 +1,9 @@
1
1
  import { bareUrls, type BareUrl } from "../bare-url.ts";
2
- import { linkChrome } from "../document.ts";
2
+ import { emphasisSpans, linkChrome } from "../document.ts";
3
3
  import { opaqueSpans } from "../markdown-read.ts";
4
4
  import { spanOf, type MarkdownNode } from "../markdown-node.ts";
5
5
  import { maskSpans } from "../mask.ts";
6
+ import { unmaskedSoftBreaks } from "../soft-break.ts";
6
7
  import { eachPreOrder } from "../tree-walk.ts";
7
8
  import type { Span } from "../plugin.ts";
8
9
  import { overlapsAny, spanIndex, type SpanIndex } from "./spans.ts";
@@ -22,6 +23,11 @@ export type FactText = {
22
23
  readonly bareUrls: readonly BareUrl[];
23
24
  /** Ordered-list markers (`1.`): they number the list, they do not state a number. */
24
25
  readonly listMarkers: readonly Span[];
26
+ /**
27
+ * What a reader of the rendered Markdown never sees, sorted: a line break between two wide characters (「系の\nシステム」)
28
+ * and the marks of bold. A plain-text document shows its line breaks, so it has none.
29
+ */
30
+ readonly unseen: readonly Span[];
25
31
  };
26
32
 
27
33
  /** Nodes whose whole span is not read as prose: a link reference definition's URL is read as a URL, an image is not text. */
@@ -55,6 +61,18 @@ const markdownParts = (root: MarkdownNode, source: string): MarkdownParts => {
55
61
  return parts;
56
62
  };
57
63
 
64
+ /**
65
+ * Line breaks are read the way the rules read them: one next to blanked code or a link's marks is kept. Bold marks are
66
+ * read through, as a reader does not see them: 「系の\n**システム**」 joins too. The break and the marks may touch, so the
67
+ * spans are merged.
68
+ */
69
+ const unseenOf = (source: string, text: string, root: MarkdownNode | undefined): readonly Span[] => {
70
+ if (root === undefined) return [];
71
+ const emphasis = emphasisSpans(root, source);
72
+ const breaks = unmaskedSoftBreaks(maskSpans(source, emphasis), maskSpans(text, emphasis));
73
+ return spanIndex([...breaks, ...emphasis]);
74
+ };
75
+
58
76
  /** root is the Markdown tree, or undefined for a plain-text document, where nothing is code and URLs are bare. */
59
77
  export const factTextOf = (source: string, root: MarkdownNode | undefined): FactText => {
60
78
  const code = root === undefined ? [] : opaqueSpans(root);
@@ -64,5 +82,6 @@ export const factTextOf = (source: string, root: MarkdownNode | undefined): Fact
64
82
  // An autolink's text is its URL: it is read once, as the link.
65
83
  const bare = bareUrls(codeless).filter((url) => !overlapsAny(insideLinks, url));
66
84
  const blanked = [...code, ...parts.chrome, ...bare];
67
- return { text: maskSpans(source, blanked), codeless, blanked: spanIndex(blanked), bareUrls: bare, listMarkers: parts.listMarkers };
85
+ const text = maskSpans(source, blanked);
86
+ return { text, codeless, blanked: spanIndex(blanked), bareUrls: bare, listMarkers: parts.listMarkers, unseen: unseenOf(source, text, root) };
68
87
  };
@@ -0,0 +1,63 @@
1
+ import { ATOM_KINDS, type Atom, type AtomKind, type Extraction, type Unread } from "./atom.ts";
2
+ import { countsOf } from "./outcome.ts";
3
+ import { atomJson, shown } from "./render.ts";
4
+ import type { FactsText } from "./facts-text.ts";
5
+
6
+ /** One document's facts as `chaff facts` lists them. */
7
+ export type FactList = { readonly path: string; readonly language: string; readonly extraction: Extraction };
8
+
9
+ /** Kind by kind in compare's order, and within a kind in the order the document states them. */
10
+ export const inChecklistOrder = (atoms: readonly Atom[]): Atom[] =>
11
+ atoms.toSorted((left, right) => ATOM_KINDS.indexOf(left.kind) - ATOM_KINDS.indexOf(right.kind) || left.line - right.line);
12
+
13
+ /** Every kind with its count, zeros too: an empty kind reads as "none stated", never as "not looked at". */
14
+ const perKind = (atoms: readonly Atom[], text: FactsText): string => {
15
+ const counts = countsOf(atoms);
16
+ return ATOM_KINDS.map((kind) => text.kindCount(text.kinds[kind], counts[kind])).join(text.separator);
17
+ };
18
+
19
+ const title = (list: FactList, text: FactsText): string => text.title(list.path, list.extraction.atoms.length, perKind(list.extraction.atoms, text));
20
+
21
+ const checklistLine = (atom: Atom, path: string): string => ` - [ ] ${shown(atom.text)} (${path}:${String(atom.line)})`;
22
+
23
+ const kindBlock = (ordered: readonly Atom[], kind: AtomKind, path: string, text: FactsText): string[] => {
24
+ const ofKind = ordered.filter((atom) => atom.kind === kind);
25
+ return ofKind.length === 0 ? [] : ["", text.kindHeading(text.kinds[kind], ofKind.length), ...ofKind.map((atom) => checklistLine(atom, path))];
26
+ };
27
+
28
+ const unreadBlock = (unread: readonly Unread[], text: FactsText): string[] =>
29
+ unread.length === 0 ? [] : ["", text.unreadHeading, ...unread.map((entry) => ` ${text.unread(entry.kind, entry.reason)}`)];
30
+
31
+ /** For a person or an AI about to rewrite: a checklist per kind, with the line each fact is on. */
32
+ export const renderFactsFriendly = (list: FactList, text: FactsText): string => {
33
+ const ordered = inChecklistOrder(list.extraction.atoms);
34
+ return [
35
+ title(list, text),
36
+ ...ATOM_KINDS.flatMap((kind) => kindBlock(ordered, kind, list.path, text)),
37
+ ...unreadBlock(list.extraction.unread, text),
38
+ "",
39
+ text.next(list.path),
40
+ ].join("\n");
41
+ };
42
+
43
+ /** For grep: one fact per line with the kind in English, then what was not read, then the counts. */
44
+ export const renderFactsCompact = (list: FactList, text: FactsText): string =>
45
+ [
46
+ ...inChecklistOrder(list.extraction.atoms).map((atom) => `${list.path}:${String(atom.line)}: ${atom.kind} ${shown(atom.text)}`),
47
+ ...list.extraction.unread.map((entry) => `${list.path}: unread ${entry.kind} (${entry.reason})`),
48
+ title(list, text),
49
+ ].join("\n");
50
+
51
+ /** For an AI to keep as its inventory: every fact with its kind, key, text and line, and the counts per kind. */
52
+ export const renderFactsJson = (list: FactList): string =>
53
+ JSON.stringify(
54
+ {
55
+ path: list.path,
56
+ language: list.language,
57
+ counts: countsOf(list.extraction.atoms),
58
+ unread: list.extraction.unread,
59
+ facts: inChecklistOrder(list.extraction.atoms).map(atomJson),
60
+ },
61
+ null,
62
+ 2,
63
+ );
@@ -0,0 +1,41 @@
1
+ import type { Texts } from "../ui.ts";
2
+ import { counted } from "../render/plural.ts";
3
+ import { COMPARE_TEXT, type CompareText } from "./text.ts";
4
+
5
+ /** What `chaff facts` says. The names of the kinds and the reasons a kind was not read are compare's own. */
6
+ export type FactsText = Pick<CompareText, "kinds" | "unread" | "unreadHeading" | "separator"> & {
7
+ readonly usage: string;
8
+ /** The first line: the file, how many facts, and the count per kind. */
9
+ readonly title: (path: string, total: number, perKind: string) => string;
10
+ readonly kindHeading: (kindName: string, count: number) => string;
11
+ /** How a kind is counted in the first line (数 12 / numbers 12). */
12
+ readonly kindCount: (kindName: string, count: number) => string;
13
+ /** The last line: the command that checks a rewrite against this list. */
14
+ readonly next: (path: string) => string;
15
+ };
16
+
17
+ const shared = (text: CompareText): Pick<CompareText, "kinds" | "unread" | "unreadHeading" | "separator"> => ({
18
+ kinds: text.kinds,
19
+ unread: text.unread,
20
+ unreadHeading: text.unreadHeading,
21
+ separator: text.separator,
22
+ });
23
+
24
+ export const FACTS_TEXT: Texts<FactsText> = {
25
+ ja: {
26
+ ...shared(COMPARE_TEXT.ja),
27
+ usage: "使い方: chaff facts <file> [--compact | --json] [--language ja|en|…] [--genre <ジャンル>]",
28
+ title: (path, total, perKind) => `${path} の事実 ${String(total)} 件(${perKind})`,
29
+ kindHeading: (kindName, count) => `${kindName} ${String(count)} 件`,
30
+ kindCount: (kindName, count) => `${kindName} ${String(count)}`,
31
+ next: (path) => `書き直したら npx chaffjs compare ${path} <書き直した後> で、この一覧が残っているかを確かめます`,
32
+ },
33
+ en: {
34
+ ...shared(COMPARE_TEXT.en),
35
+ usage: "usage: chaff facts <file> [--compact | --json] [--language ja|en|…] [--genre <genre>]",
36
+ title: (path, total, perKind) => `${path}: ${counted(total, "fact")} (${perKind})`,
37
+ kindHeading: (kindName, count) => `${kindName}: ${String(count)}`,
38
+ kindCount: (kindName, count) => `${kindName} ${String(count)}`,
39
+ next: (path) => `After rewriting, npx chaffjs compare ${path} <rewritten> checks that every fact on this list is still there`,
40
+ },
41
+ };
@@ -11,7 +11,7 @@ export type Comparison = {
11
11
  readonly reformed: readonly Reformed[];
12
12
  };
13
13
 
14
- const identity = (atom: Atom): string => `${atom.kind}\u0000${atom.key}`;
14
+ export const identity = (atom: Atom): string => `${atom.kind}\u0000${atom.key}`;
15
15
 
16
16
  const groupBy = <T>(items: readonly T[], keyOf: (item: T) => string): Map<string, T[]> => {
17
17
  const groups = new Map<string, T[]>();
@@ -1,6 +1,7 @@
1
1
  import { type Atom, type AtomKind, type Extraction, type Unread } from "./atom.ts";
2
2
  import { compareAtoms, type Reformed } from "./match.ts";
3
3
  import { unwrittenNames } from "./written-names.ts";
4
+ import { unstatedChanges } from "./distinct.ts";
4
5
 
5
6
  /** One document as compared: where it is, how many facts of each kind it states, and what could not be read. */
6
7
  export type Side = {
@@ -27,6 +28,9 @@ export type Allowed = { readonly dropped: ReadonlySet<AtomKind>; readonly added:
27
28
 
28
29
  const NOTHING_ALLOWED: Allowed = { dropped: new Set(), added: new Set() };
29
30
 
31
+ /** How facts are counted: as-stated compares how many times each is stated, distinct only whether it is stated (--distinct). */
32
+ export type Counting = "as-stated" | "distinct";
33
+
30
34
  const NO_FACTS: Readonly<Record<AtomKind, number>> = {
31
35
  number: 0,
32
36
  date: 0,
@@ -40,7 +44,7 @@ const NO_FACTS: Readonly<Record<AtomKind, number>> = {
40
44
  footnote: 0,
41
45
  };
42
46
 
43
- const countsOf = (atoms: readonly Atom[]): Record<AtomKind, number> => {
47
+ export const countsOf = (atoms: readonly Atom[]): Record<AtomKind, number> => {
44
48
  const counts = { ...NO_FACTS };
45
49
  atoms.forEach((atom) => {
46
50
  counts[atom.kind] += 1;
@@ -54,11 +58,17 @@ const marked = (atoms: readonly Atom[], allowed: ReadonlySet<AtomKind>): Change[
54
58
 
55
59
  export type Compared = { readonly path: string; readonly extraction: Extraction };
56
60
 
61
+ /** The changes of one side that the other side does not state: as many times (as-stated), or at all (distinct). */
62
+ const changesOf = (changes: readonly Atom[], own: Extraction, other: Extraction, counting: Counting): Atom[] => {
63
+ const unwritten = unwrittenNames(changes, own.atoms, other.nameText);
64
+ return counting === "distinct" ? unstatedChanges(unwritten, other.atoms, other.nameText) : unwritten;
65
+ };
66
+
57
67
  /** What `chaff compare` reports: the comparison of two documents' facts, with the allowed kinds set aside. */
58
- export const outcomeOf = (before: Compared, after: Compared, allowed: Allowed = NOTHING_ALLOWED): Outcome => {
68
+ export const outcomeOf = (before: Compared, after: Compared, allowed: Allowed = NOTHING_ALLOWED, counting: Counting = "as-stated"): Outcome => {
59
69
  const comparison = compareAtoms(before.extraction.atoms, after.extraction.atoms);
60
- const dropped = marked(unwrittenNames(comparison.dropped, before.extraction.atoms, after.extraction.nameText), allowed.dropped);
61
- const added = marked(unwrittenNames(comparison.added, after.extraction.atoms, before.extraction.nameText), allowed.added);
70
+ const dropped = marked(changesOf(comparison.dropped, before.extraction, after.extraction, counting), allowed.dropped);
71
+ const added = marked(changesOf(comparison.added, after.extraction, before.extraction, counting), allowed.added);
62
72
  return {
63
73
  before: sideOf(before.path, before.extraction),
64
74
  after: sideOf(after.path, after.extraction),
@@ -1,6 +1,7 @@
1
1
  import type { ProseDocument, Span, Token } from "../plugin.ts";
2
2
  import type { Atom } from "./atom.ts";
3
3
  import { overlapsAny, type SpanIndex } from "./spans.ts";
4
+ import { factKey } from "./fact-key.ts";
4
5
 
5
6
  const PROPER_NOUN = "PROPN";
6
7
 
@@ -40,11 +41,10 @@ export type NounInput = {
40
41
  /** Facts other readers took and what is not prose: a proper noun inside one is read as that, not again here. */
41
42
  readonly taken: SpanIndex;
42
43
  readonly lineOf: (offset: number) => number;
44
+ /** What a reader never sees (FactText.unseen): a line break inside a name does not make another name. */
45
+ readonly unseen: readonly Span[];
43
46
  };
44
47
 
45
- /** A name as one spelling: full-width letters and line breaks inside a name do not make another name. */
46
- export const nameKey = (written: string): string => written.normalize("NFKC").replace(/\s+/gu, " ");
47
-
48
48
  /** Whether the language package tagged parts of speech: without them, a document with sentences has no proper nouns to read. */
49
49
  export const readsProperNouns = (doc: ProseDocument): boolean => doc.sentences.length === 0 || doc.sentences.some((sentence) => sentence.tokens !== undefined);
50
50
 
@@ -59,7 +59,7 @@ export const properNouns = (input: NounInput): Atom[] => {
59
59
  runsOf(sentence.tokens ?? [], source).flatMap((run): Atom[] => {
60
60
  const text = source.slice(run.start, run.end);
61
61
  if (overlapsAny(input.taken, run) || isCommonWord(run, text, lowerCase)) return [];
62
- return [{ kind: "name", key: nameKey(text), text, line: input.lineOf(run.start) }];
62
+ return [{ kind: "name", key: factKey(source, run, input.unseen), text, line: input.lineOf(run.start) }];
63
63
  }),
64
64
  );
65
65
  };
@@ -7,7 +7,7 @@ import type { CompareText } from "./text.ts";
7
7
  const SHOWN_WIDTH = 60;
8
8
 
9
9
  /** One line of a fact as written: a code block or a wrapped quotation is shown on one line, cut at SHOWN_WIDTH. */
10
- const shown = (text: string): string => {
10
+ export const shown = (text: string): string => {
11
11
  const oneLine = text.replace(/\s+/gu, " ").trim();
12
12
  return oneLine.length > SHOWN_WIDTH ? `${oneLine.slice(0, SHOWN_WIDTH)}…` : oneLine;
13
13
  };
@@ -84,7 +84,7 @@ export const renderCompact = (outcome: Outcome, text: CompareText): string => {
84
84
  ].join("\n");
85
85
  };
86
86
 
87
- const atomJson = (atom: Atom): Readonly<Record<string, string | number>> => ({ kind: atom.kind, key: atom.key, text: atom.text, line: atom.line });
87
+ export const atomJson = (atom: Atom): Readonly<Record<string, string | number>> => ({ kind: atom.kind, key: atom.key, text: atom.text, line: atom.line });
88
88
 
89
89
  /** For an AI to act on: every dropped and added fact with its line, the reformed pairs, and the counts per kind. */
90
90
  export const renderJson = (outcome: Outcome): string =>
@@ -2,6 +2,7 @@ import { QUOTATION_MARKS, quotedSpans } from "../quoted-span.ts";
2
2
  import type { Span } from "../plugin.ts";
3
3
  import type { Atom } from "./atom.ts";
4
4
  import { coversOffset, overlapsAny, spanIndex, type SpanIndex } from "./spans.ts";
5
+ import { factKey } from "./fact-key.ts";
5
6
 
6
7
  /** Where a reader looks, and how it says where it found something. */
7
8
  export type TextInput = { readonly text: string; readonly source: string; readonly lineOf: (offset: number) => number };
@@ -63,14 +64,14 @@ const paragraphsOf = (text: string): Span[] => {
63
64
  return [-1, ...breaks].map((from, index) => ({ start: from + 1, end: breaks[index] ?? text.length }));
64
65
  };
65
66
 
66
- /** What is quoted, as one spelling: line breaks and runs of spaces are how it was wrapped, not what it says. */
67
- const quoteKey = (inner: string): string => inner.normalize("NFKC").replace(/\s+/gu, " ").trim();
68
-
69
- /** Quoted strings: 「」『』, “” and "". The key is what is inside; the marks are how it is written. */
70
- export const quotations = (input: TextInput): Atom[] =>
67
+ /**
68
+ * Quoted strings: 「」『』, “” and "". The key is what is inside, as one spelling: line breaks, runs of spaces and bold
69
+ * marks are how it is written, not what it says. unseen: what a reader never sees (FactText.unseen).
70
+ */
71
+ export const quotations = (input: TextInput, unseen: readonly Span[]): Atom[] =>
71
72
  paragraphsOf(input.text).flatMap((paragraph) =>
72
73
  quotedSpans(input.text.slice(paragraph.start, paragraph.end), QUOTATION_MARKS).flatMap((inner) => {
73
- const key = quoteKey(input.text.slice(paragraph.start + inner.start, paragraph.start + inner.end));
74
+ const key = factKey(input.text, { start: paragraph.start + inner.start, end: paragraph.start + inner.end }, unseen).trim();
74
75
  const span = { start: paragraph.start + inner.start - 1, end: paragraph.start + inner.end + 1 };
75
76
  return key === "" ? [] : atomsOf([{ ...span, key }], "quote", input);
76
77
  }),
@@ -82,7 +82,8 @@ const UNREAD_EN: Readonly<Record<UnreadReason, (kind: AtomKind) => string>> = {
82
82
 
83
83
  export const COMPARE_TEXT: Texts<CompareText> = {
84
84
  ja: {
85
- usage: "使い方: chaff compare <書き換える前> <書き換えた後> [--compact | --json] [--allow-dropped <種類>] [--allow-added <種類>] [--language ja|en|…]",
85
+ usage:
86
+ "使い方: chaff compare <書き換える前> <書き換えた後> [--compact | --json] [--allow-dropped <種類>] [--allow-added <種類>] [--distinct] [--language ja|en|…]",
86
87
  unknownKind: (kind, kinds) => `知らない種類です: ${kind}(使えるのは ${kinds})`,
87
88
  kinds: KINDS_JA,
88
89
  kind: KINDS_JA,
@@ -100,7 +101,7 @@ export const COMPARE_TEXT: Texts<CompareText> = {
100
101
  tally: (dropped, added, reformed) => `落ちた ${String(dropped)} 件、足された ${String(added)} 件、書き方だけ ${String(reformed)} 件`,
101
102
  },
102
103
  en: {
103
- usage: "usage: chaff compare <before> <after> [--compact | --json] [--allow-dropped <kind>] [--allow-added <kind>] [--language ja|en|…]",
104
+ usage: "usage: chaff compare <before> <after> [--compact | --json] [--allow-dropped <kind>] [--allow-added <kind>] [--distinct] [--language ja|en|…]",
104
105
  unknownKind: (kind, kinds) => `Unknown kind: ${kind} (the kinds are ${kinds})`,
105
106
  kinds: KINDS_EN,
106
107
  kind: KIND_EN,
@@ -6,6 +6,8 @@ import { defaultModel } from "../judge.ts";
6
6
  import { isBackend, type BackendName } from "../backends/types.ts";
7
7
  import type { Level } from "../plugin.ts";
8
8
  import type { PathRule } from "./by-path.ts";
9
+ import type { Extensions } from "../extension/load.ts";
10
+ import type { StyleLimits } from "../style-parse.ts";
9
11
 
10
12
  export const CONFIG_FILE = "chaff.yaml";
11
13
 
@@ -47,12 +49,18 @@ export type Config = {
47
49
  readonly applied?: AppliedStyle | undefined;
48
50
  /** custom_rules as written: the team's own rules. custom/parse.ts reads and checks them. */
49
51
  readonly customRules?: unknown;
52
+ /** plugins as written: package names and paths. extension/plugin-load.ts reads and checks them. */
53
+ readonly plugins?: unknown;
54
+ /** The code chaff.yaml names, once loaded (extension/load.ts). Reading chaff.yaml does not load it. */
55
+ readonly extensions?: Extensions;
50
56
  };
51
57
 
52
58
  export type AppliedStyle = {
53
59
  readonly style: string;
54
60
  readonly levelsFrom: readonly string[];
55
61
  readonly options: Readonly<Record<string, unknown>>;
62
+ /** The style's numbers, by rule and language, for the rules whose level it decided. */
63
+ readonly limits: StyleLimits;
56
64
  };
57
65
 
58
66
  /** 判定の質が誤検知に直結するので、既定は最上位のモデル。cost は絞り込みで削る。spec §14。 */
@@ -184,5 +192,6 @@ export const loadConfig = (path: string): Config => {
184
192
  unreadableOptions: options === undefined || options === null || isRecord(options) ? undefined : printed(options),
185
193
  style: raw["style"] === undefined || raw["style"] === null ? undefined : (str(raw["style"]) ?? printed(raw["style"])),
186
194
  customRules: raw["custom_rules"],
195
+ plugins: raw["plugins"],
187
196
  };
188
197
  };
@@ -1,7 +1,7 @@
1
1
  import { CONFIG_FILE, type Config } from "./load.ts";
2
2
  import type { RuleDefinition, RuleOption } from "../plugin.ts";
3
3
  import { optionProblems, type OptionLayer, type OptionProblem } from "../rule-options.ts";
4
- import { styleLevelSource, styleSource } from "./style.ts";
4
+ import { limitsFor, styleLevelSource, styleSource } from "./style.ts";
5
5
  import type { Texts, UiLanguage } from "../ui.ts";
6
6
 
7
7
  /** Where chaff.yaml's options come from, for settleOptions: the file itself. */
@@ -10,13 +10,15 @@ export const configOptionLayer = (config: Pick<Config, "options">): OptionLayer
10
10
  values: config.options ?? {},
11
11
  });
12
12
 
13
- /** Where a rule's settings come from, for explain: the option layers, and the style when it set the level. */
13
+ /** Where a rule's settings come from, for explain: the option layers, the style when it set the level, and a number set for it. */
14
14
  export const settingSourcesOf = (
15
- config: Pick<Config, "options" | "applied">,
15
+ config: Pick<Config, "options" | "applied" | "limits">,
16
16
  ruleId: string,
17
- ): { optionLayers: OptionLayer[]; levelFrom: string | undefined } => ({
17
+ language: string,
18
+ ): { optionLayers: OptionLayer[]; levelFrom: string | undefined; limit: number | undefined } => ({
18
19
  optionLayers: optionLayersOf(config),
19
20
  levelFrom: styleLevelSource(config, ruleId),
21
+ limit: limitsFor(config, language)[ruleId],
20
22
  });
21
23
 
22
24
  /** Every place options come from, strongest first: chaff.yaml, then its style. */