chaffjs 0.14.0 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +27 -2
- package/dist/address-chain.d.ts +4 -1
- package/dist/address-chain.d.ts.map +1 -1
- package/dist/address-chain.js +6 -3
- package/dist/address-chain.js.map +1 -1
- package/dist/backends/anthropic.d.ts +2 -1
- package/dist/backends/anthropic.d.ts.map +1 -1
- package/dist/backends/anthropic.js +4 -1
- package/dist/backends/anthropic.js.map +1 -1
- package/dist/backends/openai.d.ts +2 -1
- package/dist/backends/openai.d.ts.map +1 -1
- package/dist/backends/openai.js +1 -1
- package/dist/backends/openai.js.map +1 -1
- package/dist/backends/types.d.ts +2 -1
- package/dist/backends/types.d.ts.map +1 -1
- package/dist/backends/types.js +1 -1
- package/dist/backends/types.js.map +1 -1
- package/dist/baseline.js +1 -1
- package/dist/baseline.js.map +1 -1
- package/dist/calendar-number.d.ts +26 -0
- package/dist/calendar-number.d.ts.map +1 -0
- package/dist/calendar-number.js +51 -0
- package/dist/calendar-number.js.map +1 -0
- package/dist/cli-args.d.ts +0 -5
- package/dist/cli-args.d.ts.map +1 -1
- package/dist/cli-args.js +2 -2
- package/dist/cli-args.js.map +1 -1
- package/dist/cli-text.d.ts +10 -3
- package/dist/cli-text.d.ts.map +1 -1
- package/dist/cli-text.js +33 -14
- package/dist/cli-text.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +53 -47
- package/dist/cli.js.map +1 -1
- package/dist/commands/cite.d.ts.map +1 -1
- package/dist/commands/cite.js +4 -4
- package/dist/commands/cite.js.map +1 -1
- package/dist/commands/eval.d.ts +4 -0
- package/dist/commands/eval.d.ts.map +1 -1
- package/dist/commands/eval.js +21 -7
- package/dist/commands/eval.js.map +1 -1
- package/dist/commands/feedback.d.ts.map +1 -1
- package/dist/commands/feedback.js +2 -1
- package/dist/commands/feedback.js.map +1 -1
- package/dist/commands/init-ask.d.ts +19 -0
- package/dist/commands/init-ask.d.ts.map +1 -0
- package/dist/commands/init-ask.js +43 -0
- package/dist/commands/init-ask.js.map +1 -0
- package/dist/commands/test.d.ts +8 -0
- package/dist/commands/test.d.ts.map +1 -1
- package/dist/commands/test.js +73 -40
- package/dist/commands/test.js.map +1 -1
- package/dist/commands/tree.d.ts +1 -8
- package/dist/commands/tree.d.ts.map +1 -1
- package/dist/commands/tree.js +9 -7
- package/dist/commands/tree.js.map +1 -1
- package/dist/config/by-path.d.ts.map +1 -1
- package/dist/config/by-path.js +10 -9
- package/dist/config/by-path.js.map +1 -1
- package/dist/config/load.d.ts +4 -0
- package/dist/config/load.d.ts.map +1 -1
- package/dist/config/load.js +19 -0
- package/dist/config/load.js.map +1 -1
- package/dist/config/name-problems.d.ts +5 -0
- package/dist/config/name-problems.d.ts.map +1 -0
- package/dist/config/name-problems.js +11 -0
- package/dist/config/name-problems.js.map +1 -0
- package/dist/date-stamp.js +1 -1
- package/dist/date-stamp.js.map +1 -1
- package/dist/detectors/acronym-compound.d.ts +8 -0
- package/dist/detectors/acronym-compound.d.ts.map +1 -0
- package/dist/detectors/acronym-compound.js +8 -0
- package/dist/detectors/acronym-compound.js.map +1 -0
- package/dist/detectors/acronym-context.d.ts +4 -0
- package/dist/detectors/acronym-context.d.ts.map +1 -1
- package/dist/detectors/acronym-context.js +17 -1
- package/dist/detectors/acronym-context.js.map +1 -1
- package/dist/detectors/acronym-expansion.d.ts +10 -0
- package/dist/detectors/acronym-expansion.d.ts.map +1 -1
- package/dist/detectors/acronym-expansion.js +63 -5
- package/dist/detectors/acronym-expansion.js.map +1 -1
- package/dist/detectors/char-shape.d.ts.map +1 -1
- package/dist/detectors/char-shape.js +47 -15
- package/dist/detectors/char-shape.js.map +1 -1
- package/dist/detectors/citation-key.d.ts +13 -0
- package/dist/detectors/citation-key.d.ts.map +1 -0
- package/dist/detectors/citation-key.js +9 -0
- package/dist/detectors/citation-key.js.map +1 -0
- package/dist/detectors/cited-name.d.ts +11 -0
- package/dist/detectors/cited-name.d.ts.map +1 -0
- package/dist/detectors/cited-name.js +15 -0
- package/dist/detectors/cited-name.js.map +1 -0
- package/dist/detectors/concrete-evidence.d.ts +7 -0
- package/dist/detectors/concrete-evidence.d.ts.map +1 -0
- package/dist/detectors/concrete-evidence.js +21 -0
- package/dist/detectors/concrete-evidence.js.map +1 -0
- package/dist/detectors/counter-edge.d.ts +8 -0
- package/dist/detectors/counter-edge.d.ts.map +1 -0
- package/dist/detectors/counter-edge.js +27 -0
- package/dist/detectors/counter-edge.js.map +1 -0
- package/dist/detectors/dotted-name.d.ts +13 -0
- package/dist/detectors/dotted-name.d.ts.map +1 -0
- package/dist/detectors/dotted-name.js +10 -0
- package/dist/detectors/dotted-name.js.map +1 -0
- package/dist/detectors/doubled-word.d.ts +6 -1
- package/dist/detectors/doubled-word.d.ts.map +1 -1
- package/dist/detectors/doubled-word.js +22 -2
- package/dist/detectors/doubled-word.js.map +1 -1
- package/dist/detectors/en-shape.d.ts +1 -1
- package/dist/detectors/en-shape.d.ts.map +1 -1
- package/dist/detectors/en-shape.js +112 -29
- package/dist/detectors/en-shape.js.map +1 -1
- package/dist/detectors/enumerated-runs.d.ts.map +1 -1
- package/dist/detectors/enumerated-runs.js +4 -2
- package/dist/detectors/enumerated-runs.js.map +1 -1
- package/dist/detectors/gram-place.d.ts +0 -4
- package/dist/detectors/gram-place.d.ts.map +1 -1
- package/dist/detectors/gram-place.js +7 -2
- package/dist/detectors/gram-place.js.map +1 -1
- package/dist/detectors/gram-predicate.d.ts +7 -0
- package/dist/detectors/gram-predicate.d.ts.map +1 -0
- package/dist/detectors/gram-predicate.js +44 -0
- package/dist/detectors/gram-predicate.js.map +1 -0
- package/dist/detectors/heading-case.d.ts +21 -0
- package/dist/detectors/heading-case.d.ts.map +1 -0
- package/dist/detectors/heading-case.js +40 -0
- package/dist/detectors/heading-case.js.map +1 -0
- package/dist/detectors/heading-echo.d.ts.map +1 -1
- package/dist/detectors/heading-echo.js +4 -1
- package/dist/detectors/heading-echo.js.map +1 -1
- package/dist/detectors/index.d.ts.map +1 -1
- package/dist/detectors/index.js +4 -3
- package/dist/detectors/index.js.map +1 -1
- package/dist/detectors/lettered-index.d.ts +9 -0
- package/dist/detectors/lettered-index.d.ts.map +1 -0
- package/dist/detectors/lettered-index.js +54 -0
- package/dist/detectors/lettered-index.js.map +1 -0
- package/dist/detectors/lexicon.d.ts +5 -1
- package/dist/detectors/lexicon.d.ts.map +1 -1
- package/dist/detectors/lexicon.js +51 -5
- package/dist/detectors/lexicon.js.map +1 -1
- package/dist/detectors/name-numeral.d.ts +16 -0
- package/dist/detectors/name-numeral.d.ts.map +1 -0
- package/dist/detectors/name-numeral.js +30 -0
- package/dist/detectors/name-numeral.js.map +1 -0
- package/dist/detectors/name-part.d.ts +8 -0
- package/dist/detectors/name-part.d.ts.map +1 -0
- package/dist/detectors/name-part.js +20 -0
- package/dist/detectors/name-part.js.map +1 -0
- package/dist/detectors/name-run.d.ts +8 -0
- package/dist/detectors/name-run.d.ts.map +1 -0
- package/dist/detectors/name-run.js +13 -0
- package/dist/detectors/name-run.js.map +1 -0
- package/dist/detectors/orthography.d.ts.map +1 -1
- package/dist/detectors/orthography.js +35 -7
- package/dist/detectors/orthography.js.map +1 -1
- package/dist/detectors/phrase-match.d.ts +3 -1
- package/dist/detectors/phrase-match.d.ts.map +1 -1
- package/dist/detectors/phrase-match.js +20 -15
- package/dist/detectors/phrase-match.js.map +1 -1
- package/dist/detectors/placeholder.d.ts +14 -0
- package/dist/detectors/placeholder.d.ts.map +1 -0
- package/dist/detectors/placeholder.js +24 -0
- package/dist/detectors/placeholder.js.map +1 -0
- package/dist/detectors/quoted-variant.d.ts +7 -0
- package/dist/detectors/quoted-variant.d.ts.map +1 -0
- package/dist/detectors/quoted-variant.js +18 -0
- package/dist/detectors/quoted-variant.js.map +1 -0
- package/dist/detectors/repeated-head.d.ts.map +1 -1
- package/dist/detectors/repeated-head.js +5 -4
- package/dist/detectors/repeated-head.js.map +1 -1
- package/dist/detectors/series-label.d.ts +13 -0
- package/dist/detectors/series-label.d.ts.map +1 -0
- package/dist/detectors/series-label.js +26 -0
- package/dist/detectors/series-label.js.map +1 -0
- package/dist/detectors/signals.d.ts.map +1 -1
- package/dist/detectors/signals.js +44 -29
- package/dist/detectors/signals.js.map +1 -1
- package/dist/detectors/stacked-hedge.d.ts +23 -0
- package/dist/detectors/stacked-hedge.d.ts.map +1 -0
- package/dist/detectors/stacked-hedge.js +64 -0
- package/dist/detectors/stacked-hedge.js.map +1 -0
- package/dist/detectors/structure-tree.js +1 -1
- package/dist/detectors/structure-tree.js.map +1 -1
- package/dist/detectors/superlative-amount.d.ts +9 -0
- package/dist/detectors/superlative-amount.d.ts.map +1 -0
- package/dist/detectors/superlative-amount.js +20 -0
- package/dist/detectors/superlative-amount.js.map +1 -0
- package/dist/detectors/superlative-clause.d.ts +18 -0
- package/dist/detectors/superlative-clause.d.ts.map +1 -0
- package/dist/detectors/superlative-clause.js +61 -0
- package/dist/detectors/superlative-clause.js.map +1 -0
- package/dist/detectors/superlative-name.d.ts +9 -0
- package/dist/detectors/superlative-name.d.ts.map +1 -0
- package/dist/detectors/superlative-name.js +24 -0
- package/dist/detectors/superlative-name.js.map +1 -0
- package/dist/detectors/superlative-scope.d.ts +6 -0
- package/dist/detectors/superlative-scope.d.ts.map +1 -1
- package/dist/detectors/superlative-scope.js +3 -3
- package/dist/detectors/superlative-scope.js.map +1 -1
- package/dist/detectors/team.d.ts +0 -6
- package/dist/detectors/team.d.ts.map +1 -1
- package/dist/detectors/team.js +17 -1
- package/dist/detectors/team.js.map +1 -1
- package/dist/detectors/token-shape.d.ts.map +1 -1
- package/dist/detectors/token-shape.js +15 -5
- package/dist/detectors/token-shape.js.map +1 -1
- package/dist/document.d.ts +3 -0
- package/dist/document.d.ts.map +1 -1
- package/dist/document.js +28 -7
- package/dist/document.js.map +1 -1
- package/dist/eval.js +1 -1
- package/dist/eval.js.map +1 -1
- package/dist/feedback/draft.js +1 -1
- package/dist/feedback/draft.js.map +1 -1
- package/dist/feedback/excerpt.d.ts +0 -1
- package/dist/feedback/excerpt.d.ts.map +1 -1
- package/dist/feedback/excerpt.js +2 -2
- package/dist/feedback/excerpt.js.map +1 -1
- package/dist/file-header.d.ts +20 -0
- package/dist/file-header.d.ts.map +1 -0
- package/dist/file-header.js +21 -0
- package/dist/file-header.js.map +1 -0
- package/dist/files.js +3 -3
- package/dist/files.js.map +1 -1
- package/dist/genre-check.d.ts +34 -0
- package/dist/genre-check.d.ts.map +1 -0
- package/dist/genre-check.js +21 -0
- package/dist/genre-check.js.map +1 -0
- package/dist/genre-load.d.ts +6 -0
- package/dist/genre-load.d.ts.map +1 -0
- package/dist/genre-load.js +22 -0
- package/dist/genre-load.js.map +1 -0
- package/dist/genre-parse.d.ts +40 -0
- package/dist/genre-parse.d.ts.map +1 -0
- package/dist/genre-parse.js +104 -0
- package/dist/genre-parse.js.map +1 -0
- package/dist/genre-suggest.d.ts +15 -0
- package/dist/genre-suggest.d.ts.map +1 -0
- package/dist/genre-suggest.js +26 -0
- package/dist/genre-suggest.js.map +1 -0
- package/dist/genre.d.ts +8 -1
- package/dist/genre.d.ts.map +1 -1
- package/dist/genre.js +19 -5
- package/dist/genre.js.map +1 -1
- package/dist/init-choice.d.ts +12 -0
- package/dist/init-choice.d.ts.map +1 -0
- package/dist/init-choice.js +41 -0
- package/dist/init-choice.js.map +1 -0
- package/dist/init.d.ts +0 -1
- package/dist/init.d.ts.map +1 -1
- package/dist/init.js +16 -17
- package/dist/init.js.map +1 -1
- package/dist/joined-view.d.ts +25 -0
- package/dist/joined-view.d.ts.map +1 -0
- package/dist/joined-view.js +76 -0
- package/dist/joined-view.js.map +1 -0
- package/dist/judge.d.ts +2 -1
- package/dist/judge.d.ts.map +1 -1
- package/dist/judge.js +1 -1
- package/dist/judge.js.map +1 -1
- package/dist/line-continues.d.ts +6 -0
- package/dist/line-continues.d.ts.map +1 -0
- package/dist/line-continues.js +33 -0
- package/dist/line-continues.js.map +1 -0
- package/dist/line-paragraphs.d.ts +4 -0
- package/dist/line-paragraphs.d.ts.map +1 -0
- package/dist/line-paragraphs.js +69 -0
- package/dist/line-paragraphs.js.map +1 -0
- package/dist/mask.d.ts.map +1 -1
- package/dist/mask.js +7 -6
- package/dist/mask.js.map +1 -1
- package/dist/measure.d.ts +1 -0
- package/dist/measure.d.ts.map +1 -1
- package/dist/measure.js +5 -1
- package/dist/measure.js.map +1 -1
- package/dist/number-name.d.ts +6 -3
- package/dist/number-name.d.ts.map +1 -1
- package/dist/number-name.js +31 -14
- package/dist/number-name.js.map +1 -1
- package/dist/orthography.d.ts +4 -0
- package/dist/orthography.d.ts.map +1 -1
- package/dist/orthography.js +11 -9
- package/dist/orthography.js.map +1 -1
- package/dist/plain-source.d.ts +6 -0
- package/dist/plain-source.d.ts.map +1 -0
- package/dist/plain-source.js +6 -0
- package/dist/plain-source.js.map +1 -0
- package/dist/plugin.d.ts +22 -1
- package/dist/plugin.d.ts.map +1 -1
- package/dist/profile/check.d.ts +28 -0
- package/dist/profile/check.d.ts.map +1 -0
- package/dist/profile/check.js +12 -0
- package/dist/profile/check.js.map +1 -0
- package/dist/profile/for-file.d.ts +2 -2
- package/dist/profile/for-file.d.ts.map +1 -1
- package/dist/profile/for-file.js +9 -2
- package/dist/profile/for-file.js.map +1 -1
- package/dist/profile/load.js +1 -1
- package/dist/profile/load.js.map +1 -1
- package/dist/profile/select.d.ts +4 -2
- package/dist/profile/select.d.ts.map +1 -1
- package/dist/profile/select.js +8 -3
- package/dist/profile/select.js.map +1 -1
- package/dist/quoted-span.d.ts +22 -0
- package/dist/quoted-span.d.ts.map +1 -0
- package/dist/quoted-span.js +25 -0
- package/dist/quoted-span.js.map +1 -0
- package/dist/reasons.d.ts +1 -0
- package/dist/reasons.d.ts.map +1 -1
- package/dist/reasons.js +4 -1
- package/dist/reasons.js.map +1 -1
- package/dist/render/compact.d.ts.map +1 -1
- package/dist/render/compact.js +1 -2
- package/dist/render/compact.js.map +1 -1
- package/dist/render/eval.d.ts +2 -1
- package/dist/render/eval.d.ts.map +1 -1
- package/dist/render/eval.js +85 -43
- package/dist/render/eval.js.map +1 -1
- package/dist/render/friendly.d.ts +2 -1
- package/dist/render/friendly.d.ts.map +1 -1
- package/dist/render/friendly.js +14 -6
- package/dist/render/friendly.js.map +1 -1
- package/dist/render/genres.d.ts +5 -0
- package/dist/render/genres.d.ts.map +1 -0
- package/dist/render/genres.js +28 -0
- package/dist/render/genres.js.map +1 -0
- package/dist/render/plural.d.ts +3 -0
- package/dist/render/plural.d.ts.map +1 -0
- package/dist/render/plural.js +5 -0
- package/dist/render/plural.js.map +1 -0
- package/dist/render/rules-json.d.ts.map +1 -1
- package/dist/render/rules-json.js +9 -3
- package/dist/render/rules-json.js.map +1 -1
- package/dist/render/semantic.d.ts +3 -2
- package/dist/render/semantic.d.ts.map +1 -1
- package/dist/render/semantic.js +94 -34
- package/dist/render/semantic.js.map +1 -1
- package/dist/render/summary.d.ts.map +1 -1
- package/dist/render/summary.js +4 -3
- package/dist/render/summary.js.map +1 -1
- package/dist/render/suppressions.d.ts.map +1 -1
- package/dist/render/suppressions.js +3 -2
- package/dist/render/suppressions.js.map +1 -1
- package/dist/render/text.d.ts +2 -0
- package/dist/render/text.d.ts.map +1 -1
- package/dist/render/text.js +11 -3
- package/dist/render/text.js.map +1 -1
- package/dist/resolve-genre.d.ts +11 -0
- package/dist/resolve-genre.d.ts.map +1 -0
- package/dist/resolve-genre.js +18 -0
- package/dist/resolve-genre.js.map +1 -0
- package/dist/rule-load.d.ts.map +1 -1
- package/dist/rule-load.js +4 -1
- package/dist/rule-load.js.map +1 -1
- package/dist/run-semantic.js +1 -1
- package/dist/run-semantic.js.map +1 -1
- package/dist/run.d.ts +2 -0
- package/dist/run.d.ts.map +1 -1
- package/dist/run.js +25 -15
- package/dist/run.js.map +1 -1
- package/dist/sentence-shape.d.ts +0 -1
- package/dist/sentence-shape.d.ts.map +1 -1
- package/dist/sentence-shape.js +3 -5
- package/dist/sentence-shape.js.map +1 -1
- package/dist/setting-problems.d.ts +5 -0
- package/dist/setting-problems.d.ts.map +1 -0
- package/dist/setting-problems.js +15 -0
- package/dist/setting-problems.js.map +1 -0
- package/dist/soft-break.d.ts +19 -0
- package/dist/soft-break.d.ts.map +1 -0
- package/dist/soft-break.js +78 -0
- package/dist/soft-break.js.map +1 -0
- package/dist/structure/build.d.ts.map +1 -1
- package/dist/structure/build.js +24 -14
- package/dist/structure/build.js.map +1 -1
- package/dist/structure/cite.js +1 -1
- package/dist/structure/cite.js.map +1 -1
- package/dist/structure/date-order.d.ts +3 -0
- package/dist/structure/date-order.d.ts.map +1 -1
- package/dist/structure/date-order.js +9 -1
- package/dist/structure/date-order.js.map +1 -1
- package/dist/structure/issues.d.ts +1 -1
- package/dist/structure/issues.d.ts.map +1 -1
- package/dist/structure/issues.js +19 -8
- package/dist/structure/issues.js.map +1 -1
- package/dist/structure/listed-tags.d.ts +3 -0
- package/dist/structure/listed-tags.d.ts.map +1 -0
- package/dist/structure/listed-tags.js +6 -0
- package/dist/structure/listed-tags.js.map +1 -0
- package/dist/structure/markdown-path.d.ts.map +1 -1
- package/dist/structure/markdown-path.js +2 -2
- package/dist/structure/markdown-path.js.map +1 -1
- package/dist/structure/number-in-sentence.d.ts +15 -0
- package/dist/structure/number-in-sentence.d.ts.map +1 -0
- package/dist/structure/number-in-sentence.js +13 -0
- package/dist/structure/number-in-sentence.js.map +1 -0
- package/dist/structure/numbering.d.ts.map +1 -1
- package/dist/structure/numbering.js +1 -1
- package/dist/structure/numbering.js.map +1 -1
- package/dist/structure/relative-find.js +1 -1
- package/dist/structure/relative-find.js.map +1 -1
- package/dist/structure/sexp.js +1 -1
- package/dist/structure/sexp.js.map +1 -1
- package/dist/structure/unit-word.d.ts +8 -0
- package/dist/structure/unit-word.d.ts.map +1 -0
- package/dist/structure/unit-word.js +10 -0
- package/dist/structure/unit-word.js.map +1 -0
- package/dist/structure/unnumbered.js +2 -2
- package/dist/structure/unnumbered.js.map +1 -1
- package/dist/structure/wrapped-tail.d.ts +18 -0
- package/dist/structure/wrapped-tail.d.ts.map +1 -0
- package/dist/structure/wrapped-tail.js +11 -0
- package/dist/structure/wrapped-tail.js.map +1 -0
- package/dist/team-names.d.ts +9 -0
- package/dist/team-names.d.ts.map +1 -0
- package/dist/team-names.js +34 -0
- package/dist/team-names.js.map +1 -0
- package/dist/ui.d.ts +2 -0
- package/dist/ui.d.ts.map +1 -1
- package/dist/ui.js +6 -0
- package/dist/ui.js.map +1 -1
- package/dist/version.js +1 -1
- package/dist/version.js.map +1 -1
- package/dist/watch.d.ts +2 -2
- package/dist/watch.d.ts.map +1 -1
- package/dist/watch.js +5 -5
- package/dist/watch.js.map +1 -1
- package/genres.yaml +363 -0
- package/package.json +4 -3
- package/rules/adverb-overuse.yaml +2 -2
- package/rules/agentless-passive.yaml +1 -1
- package/rules/ai-generated-composite.yaml +2 -2
- package/rules/ai-tell.yaml +2 -2
- package/rules/bold-density.yaml +2 -2
- package/rules/closing-cliche.yaml +1 -1
- package/rules/concrete-evidence-density.yaml +2 -2
- package/rules/contraction-consistency.yaml +1 -1
- package/rules/cushion-phrase-density.yaml +1 -1
- package/rules/dangling-reference.yaml +3 -3
- package/rules/date-order.yaml +3 -3
- package/rules/date-weekday-mismatch.yaml +1 -1
- package/rules/double-keigo.yaml +1 -1
- package/rules/doubled-word.yaml +1 -1
- package/rules/duplicate-definition.yaml +1 -1
- package/rules/emoji-density.yaml +1 -1
- package/rules/empty-conclusion.yaml +2 -1
- package/rules/empty-intensifier.yaml +1 -1
- package/rules/excessive-hedging.yaml +16 -6
- package/rules/expletive-construction.yaml +2 -2
- package/rules/heading-echo.yaml +1 -1
- package/rules/hiragana-fukushi.yaml +1 -1
- package/rules/internal-jargon.yaml +2 -2
- package/rules/latin-spacing.yaml +6 -3
- package/rules/max-kanji-continuous.yaml +5 -4
- package/rules/max-paragraph-length.yaml +9 -2
- package/rules/max-sentence-length.yaml +14 -2
- package/rules/ngram-repetition.yaml +4 -4
- package/rules/no-doubled-joshi.yaml +4 -4
- package/rules/no-em-dash.yaml +2 -2
- package/rules/no-mixed-desumasu.yaml +1 -1
- package/rules/no-nakaguro-parallel.yaml +2 -2
- package/rules/numbering-gap.yaml +1 -1
- package/rules/oxford-comma-consistency.yaml +2 -1
- package/rules/padded-intro.yaml +2 -1
- package/rules/paragraph-length-variance.yaml +1 -1
- package/rules/preamble-length.yaml +2 -2
- package/rules/preferred-term.yaml +1 -1
- package/rules/proper-noun-density.yaml +3 -2
- package/rules/repeated-conjunction.yaml +2 -2
- package/rules/repeated-sentence-head.yaml +2 -2
- package/rules/required-sections.yaml +1 -1
- package/rules/rule-of-three.yaml +2 -2
- package/rules/sasete-itadaku.yaml +3 -3
- package/rules/section-length-uniformity.yaml +1 -1
- package/rules/sentence-initial-conjunction-run.yaml +2 -2
- package/rules/sentence-rhythm.yaml +1 -1
- package/rules/taigen-dome-in-prose.yaml +2 -2
- package/rules/title-case-consistency.yaml +3 -3
- package/rules/total-mismatch.yaml +1 -1
- package/rules/undefined-acronym.yaml +18 -5
- package/rules/unqualified-superlative.yaml +3 -3
- package/skills/chaff/SKILL.md +28 -9
- package/src/address-chain.ts +6 -3
- package/src/backends/anthropic.ts +5 -1
- package/src/backends/openai.ts +2 -1
- package/src/backends/types.ts +3 -2
- package/src/baseline.ts +1 -1
- package/src/calendar-number.ts +75 -0
- package/src/cli-args.ts +2 -2
- package/src/cli-text.ts +48 -18
- package/src/cli.ts +51 -47
- package/src/commands/cite.ts +4 -4
- package/src/commands/eval.ts +27 -6
- package/src/commands/feedback.ts +2 -1
- package/src/commands/init-ask.ts +53 -0
- package/src/commands/test.ts +105 -47
- package/src/commands/tree.ts +9 -7
- package/src/config/by-path.ts +8 -9
- package/src/config/load.ts +24 -0
- package/src/config/name-problems.ts +15 -0
- package/src/date-stamp.ts +1 -1
- package/src/detectors/acronym-compound.ts +9 -0
- package/src/detectors/acronym-context.ts +22 -1
- package/src/detectors/acronym-expansion.ts +72 -5
- package/src/detectors/char-shape.ts +56 -16
- package/src/detectors/citation-key.ts +13 -0
- package/src/detectors/cited-name.ts +23 -0
- package/src/detectors/concrete-evidence.ts +31 -0
- package/src/detectors/counter-edge.ts +29 -0
- package/src/detectors/dotted-name.ts +14 -0
- package/src/detectors/doubled-word.ts +25 -2
- package/src/detectors/en-shape.ts +123 -38
- package/src/detectors/enumerated-runs.ts +3 -2
- package/src/detectors/gram-place.ts +9 -2
- package/src/detectors/gram-predicate.ts +55 -0
- package/src/detectors/heading-case.ts +44 -0
- package/src/detectors/heading-echo.ts +5 -1
- package/src/detectors/index.ts +4 -3
- package/src/detectors/lettered-index.ts +78 -0
- package/src/detectors/lexicon.ts +68 -8
- package/src/detectors/name-numeral.ts +37 -0
- package/src/detectors/name-part.ts +24 -0
- package/src/detectors/name-run.ts +16 -0
- package/src/detectors/orthography.ts +43 -10
- package/src/detectors/phrase-match.ts +23 -12
- package/src/detectors/placeholder.ts +38 -0
- package/src/detectors/quoted-variant.ts +20 -0
- package/src/detectors/repeated-head.ts +3 -4
- package/src/detectors/series-label.ts +33 -0
- package/src/detectors/signals.ts +49 -35
- package/src/detectors/stacked-hedge.ts +93 -0
- package/src/detectors/structure-tree.ts +2 -2
- package/src/detectors/superlative-amount.ts +24 -0
- package/src/detectors/superlative-clause.ts +82 -0
- package/src/detectors/superlative-name.ts +30 -0
- package/src/detectors/superlative-scope.ts +3 -3
- package/src/detectors/team.ts +18 -2
- package/src/detectors/token-shape.ts +13 -6
- package/src/document.ts +41 -8
- package/src/eval.ts +1 -1
- package/src/feedback/draft.ts +1 -1
- package/src/feedback/excerpt.ts +2 -2
- package/src/file-header.ts +31 -0
- package/src/files.ts +3 -3
- package/src/genre-check.ts +46 -0
- package/src/genre-load.ts +24 -0
- package/src/genre-parse.ts +126 -0
- package/src/genre-suggest.ts +37 -0
- package/src/genre.ts +25 -5
- package/src/init-choice.ts +47 -0
- package/src/init.ts +20 -19
- package/src/joined-view.ts +96 -0
- package/src/judge.ts +3 -1
- package/src/line-continues.ts +43 -0
- package/src/line-paragraphs.ts +83 -0
- package/src/mask.ts +5 -6
- package/src/measure.ts +9 -1
- package/src/number-name.ts +35 -13
- package/src/orthography.ts +15 -9
- package/src/plain-source.ts +5 -0
- package/src/plugin.ts +27 -2
- package/src/profile/check.ts +34 -0
- package/src/profile/for-file.ts +10 -3
- package/src/profile/load.ts +1 -1
- package/src/profile/select.ts +11 -4
- package/src/quoted-span.ts +39 -0
- package/src/reasons.ts +5 -1
- package/src/render/compact.ts +1 -3
- package/src/render/eval.ts +102 -38
- package/src/render/friendly.ts +21 -6
- package/src/render/genres.ts +31 -0
- package/src/render/plural.ts +6 -0
- package/src/render/rules-json.ts +10 -3
- package/src/render/semantic.ts +117 -34
- package/src/render/summary.ts +4 -3
- package/src/render/suppressions.ts +3 -2
- package/src/render/text.ts +18 -3
- package/src/resolve-genre.ts +20 -0
- package/src/rule-load.ts +6 -1
- package/src/run-semantic.ts +1 -1
- package/src/run.ts +28 -15
- package/src/sentence-shape.ts +3 -7
- package/src/setting-problems.ts +24 -0
- package/src/soft-break.ts +94 -0
- package/src/structure/build.ts +24 -15
- package/src/structure/cite.ts +1 -1
- package/src/structure/date-order.ts +16 -1
- package/src/structure/issues.ts +24 -8
- package/src/structure/listed-tags.ts +7 -0
- package/src/structure/markdown-path.ts +2 -2
- package/src/structure/number-in-sentence.ts +20 -0
- package/src/structure/numbering.ts +1 -1
- package/src/structure/relative-find.ts +1 -1
- package/src/structure/sexp.ts +1 -1
- package/src/structure/unit-word.ts +11 -0
- package/src/structure/unnumbered.ts +2 -2
- package/src/structure/wrapped-tail.ts +22 -0
- package/src/team-names.ts +39 -0
- package/src/ui.ts +7 -0
- package/src/version.ts +1 -1
- package/src/watch.ts +5 -5
package/src/render/friendly.ts
CHANGED
|
@@ -2,6 +2,7 @@ import type { Finding, RuleDefinition } from "../plugin.ts";
|
|
|
2
2
|
import type { RunResult } from "../run.ts";
|
|
3
3
|
import { MARK, localized, messageOf } from "./text.ts";
|
|
4
4
|
import { tally } from "./summary.ts";
|
|
5
|
+
import { counted } from "./plural.ts";
|
|
5
6
|
import { uiLanguageOf, type Texts } from "../ui.ts";
|
|
6
7
|
|
|
7
8
|
const RULE = 60;
|
|
@@ -13,6 +14,7 @@ const TEXT: Texts<{
|
|
|
13
14
|
readonly deterministic: string;
|
|
14
15
|
readonly untouched: string;
|
|
15
16
|
readonly forced: (n: number, ids: string) => string;
|
|
17
|
+
readonly presetOn: (n: number, ids: string) => string;
|
|
16
18
|
readonly notRun: (n: number) => string;
|
|
17
19
|
readonly because: (why: string) => string;
|
|
18
20
|
}> = {
|
|
@@ -23,6 +25,7 @@ const TEXT: Texts<{
|
|
|
23
25
|
deterministic: " (同じ文章なら何度実行しても同じ結果になります)",
|
|
24
26
|
untouched: "文章は書き換えていません。直すのは書いた人です。",
|
|
25
27
|
forced: (n, ids) => `試験中の rule を ${n} 件、設定により有効にしています: ${ids}`,
|
|
28
|
+
presetOn: (n, ids) => `試験中の rule を ${n} 件、ジャンルの既定で有効にしています: ${ids}`,
|
|
26
29
|
notRun: (n) => `${n} 件の rule は動いていません:`,
|
|
27
30
|
because: (why) => `(${why})`,
|
|
28
31
|
},
|
|
@@ -32,8 +35,9 @@ const TEXT: Texts<{
|
|
|
32
35
|
machine: "All judged by machine",
|
|
33
36
|
deterministic: " (the same text gives the same result every time)",
|
|
34
37
|
untouched: "The text was not changed. Fixing it is the writer's job.",
|
|
35
|
-
forced: (n, ids) => `${n
|
|
36
|
-
|
|
38
|
+
forced: (n, ids) => `${counted(n, "experimental rule")} turned on in the settings: ${ids}`,
|
|
39
|
+
presetOn: (n, ids) => `${counted(n, "experimental rule")} turned on by the genre: ${ids}`,
|
|
40
|
+
notRun: (n) => `${counted(n, "rule")} did not run:`,
|
|
37
41
|
because: (why) => ` (${why})`,
|
|
38
42
|
},
|
|
39
43
|
};
|
|
@@ -69,14 +73,21 @@ const block = (finding: Finding, rule: RuleDefinition, language: string): string
|
|
|
69
73
|
"",
|
|
70
74
|
];
|
|
71
75
|
|
|
72
|
-
|
|
76
|
+
/** notes: lines after the not-run list (the genre chaff suggests when none is set). */
|
|
77
|
+
export const renderFriendly = (
|
|
78
|
+
header: string,
|
|
79
|
+
result: RunResult,
|
|
80
|
+
rules: readonly RuleDefinition[],
|
|
81
|
+
language: string,
|
|
82
|
+
notes: readonly string[] = [],
|
|
83
|
+
): string => {
|
|
73
84
|
const byId = new Map(rules.map((rule) => [rule.id, rule]));
|
|
74
85
|
const blocks = result.findings.flatMap((finding) => {
|
|
75
86
|
const rule = byId.get(finding.rule);
|
|
76
87
|
return rule === undefined ? [] : block(finding, rule, language);
|
|
77
88
|
});
|
|
78
89
|
const text = TEXT[uiLanguageOf(language)];
|
|
79
|
-
const
|
|
90
|
+
const closing = [
|
|
80
91
|
"",
|
|
81
92
|
"─".repeat(RULE),
|
|
82
93
|
"",
|
|
@@ -85,10 +96,14 @@ export const renderFriendly = (header: string, result: RunResult, rules: readonl
|
|
|
85
96
|
"",
|
|
86
97
|
` ${text.untouched}`,
|
|
87
98
|
];
|
|
88
|
-
const forced =
|
|
99
|
+
const forced = [
|
|
100
|
+
...(result.forcedExperimental.length > 0 ? ["", ` ${text.forced(result.forcedExperimental.length, result.forcedExperimental.join(", "))}`] : []),
|
|
101
|
+
...(result.presetExperimental.length > 0 ? ["", ` ${text.presetOn(result.presetExperimental.length, result.presetExperimental.join(", "))}`] : []),
|
|
102
|
+
];
|
|
89
103
|
const skipped =
|
|
90
104
|
result.skipped.length > 0
|
|
91
105
|
? ["", ` ${text.notRun(result.skipped.length)}`, ...result.skipped.map((entry) => ` ${entry.rule}${text.because(entry.why)}`)]
|
|
92
106
|
: [];
|
|
93
|
-
|
|
107
|
+
const after = notes.flatMap((note) => ["", note]);
|
|
108
|
+
return ["", header, ...blocks, ...closing, ...forced, ...skipped, ...after, ""].join("\n");
|
|
94
109
|
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { GenreData } from "../genre-parse.ts";
|
|
2
|
+
import type { Texts, UiLanguage } from "../ui.ts";
|
|
3
|
+
|
|
4
|
+
const TEXT: Texts<{ readonly heading: string; readonly usage: readonly string[] }> = {
|
|
5
|
+
ja: {
|
|
6
|
+
heading: " ジャンル(文書の種類)を選ぶと、その種類の書き方に合わせて見ます:",
|
|
7
|
+
usage: [
|
|
8
|
+
" この実行だけ: npx chaffjs --genre legal/contract 契約書.md",
|
|
9
|
+
" この場所に決める: npx chaffjs init --genre legal/contract (chaff.yaml の genre に書きます)",
|
|
10
|
+
],
|
|
11
|
+
},
|
|
12
|
+
en: {
|
|
13
|
+
heading: " Pick the genre (the kind of document) and chaff checks it the way that kind is written:",
|
|
14
|
+
usage: [
|
|
15
|
+
" For one run: npx chaffjs --genre legal/contract contract.md",
|
|
16
|
+
" For this folder: npx chaffjs init --genre legal/contract (writes genre in chaff.yaml)",
|
|
17
|
+
],
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
/** `chaff genres`: every genre under its group, with what it is for, in the output language. */
|
|
22
|
+
export const renderGenres = (data: GenreData, ui: UiLanguage): string => {
|
|
23
|
+
const width = Math.max(...data.genres.map((genre) => genre.id.length)) + 2;
|
|
24
|
+
const groups = data.groups.flatMap((group) => {
|
|
25
|
+
const members = data.genres.filter((genre) => genre.id.startsWith(`${group.id}/`));
|
|
26
|
+
return members.length === 0
|
|
27
|
+
? []
|
|
28
|
+
: ["", ` ${group.name[ui] ?? group.id}`, ...members.map((genre) => ` ${genre.id.padEnd(width)}${genre.summary[ui] ?? ""}`)];
|
|
29
|
+
});
|
|
30
|
+
return ["", TEXT[ui].heading, ...groups, "", ...TEXT[ui].usage, ""].join("\n");
|
|
31
|
+
};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/** English agreement: only exactly one takes the singular ("0 findings", "1 finding", "1.5 findings"). */
|
|
2
|
+
const isOne = (count: number | string): boolean => String(count) === "1";
|
|
3
|
+
|
|
4
|
+
export const formFor = (count: number | string, singular: string, plural: string): string => (isOne(count) ? singular : plural);
|
|
5
|
+
|
|
6
|
+
export const counted = (count: number, singular: string, plural = `${singular}s`): string => `${String(count)} ${formFor(count, singular, plural)}`;
|
package/src/render/rules-json.ts
CHANGED
|
@@ -2,9 +2,12 @@ import { definedLevels, resolve } from "../levels.ts";
|
|
|
2
2
|
import type { Config } from "../config/load.ts";
|
|
3
3
|
import type { RuleDefinition } from "../plugin.ts";
|
|
4
4
|
import { uiLanguageOf, type Texts } from "../ui.ts";
|
|
5
|
+
import { presetLevels } from "../genre-load.ts";
|
|
6
|
+
import type { PresetLevels } from "../genre-parse.ts";
|
|
5
7
|
|
|
6
8
|
const TEXT: Texts<{
|
|
7
9
|
readonly offBySetting: string;
|
|
10
|
+
readonly offByGenre: (genre: string) => string;
|
|
8
11
|
readonly offExperimental: string;
|
|
9
12
|
readonly valuesNote: string;
|
|
10
13
|
readonly reason: string;
|
|
@@ -12,6 +15,7 @@ const TEXT: Texts<{
|
|
|
12
15
|
}> = {
|
|
13
16
|
ja: {
|
|
14
17
|
offBySetting: "設定で止めている",
|
|
18
|
+
offByGenre: (genre) => `ジャンル ${genre} では見ない`,
|
|
15
19
|
offExperimental: "experimental な rule は既定で動かさない",
|
|
16
20
|
valuesNote: "この 4 語のかわりに数字を直接書いてもよい。4 語のほうを勧める。",
|
|
17
21
|
reason: "<理由>",
|
|
@@ -19,6 +23,7 @@ const TEXT: Texts<{
|
|
|
19
23
|
},
|
|
20
24
|
en: {
|
|
21
25
|
offBySetting: "turned off in the settings",
|
|
26
|
+
offByGenre: (genre) => `the ${genre} genre does not check it`,
|
|
22
27
|
offExperimental: "experimental rules do not run by default",
|
|
23
28
|
valuesNote: "A number may be written instead of these four words. The words are recommended.",
|
|
24
29
|
reason: "<reason>",
|
|
@@ -26,8 +31,9 @@ const TEXT: Texts<{
|
|
|
26
31
|
},
|
|
27
32
|
};
|
|
28
33
|
|
|
29
|
-
const now = (rule: RuleDefinition, config: Config, genre: string, text: (typeof TEXT)["ja"]): Record<string, unknown> => {
|
|
30
|
-
const explicit = config.rules[rule.id];
|
|
34
|
+
const now = (rule: RuleDefinition, config: Config, genre: string, text: (typeof TEXT)["ja"], preset: PresetLevels): Record<string, unknown> => {
|
|
35
|
+
const explicit = config.rules[rule.id] ?? preset[rule.id];
|
|
36
|
+
if (explicit === "off" && config.rules[rule.id] === undefined) return { level: "off", why_off: text.offByGenre(genre) };
|
|
31
37
|
if (explicit === "off") return { level: "off", why_off: text.offBySetting };
|
|
32
38
|
const limit = rule.layer === "L4" ? undefined : config.limits[rule.id];
|
|
33
39
|
if (limit !== undefined) return { level: "normal", limit, set_as: "number" };
|
|
@@ -59,6 +65,7 @@ const levelsOf = (rule: RuleDefinition, genre: string): Record<string, unknown>
|
|
|
59
65
|
/** AI に設定を書かせるときの入口。推測せずに書けるだけの情報を 1 つに入れる。spec §19.3。 */
|
|
60
66
|
export const rulesJson = (rules: readonly RuleDefinition[], config: Config, language: string, genre: string): string => {
|
|
61
67
|
const text = TEXT[uiLanguageOf(language)];
|
|
68
|
+
const preset = presetLevels(genre);
|
|
62
69
|
return JSON.stringify(
|
|
63
70
|
{
|
|
64
71
|
schema_version: 1,
|
|
@@ -77,7 +84,7 @@ export const rulesJson = (rules: readonly RuleDefinition[], config: Config, lang
|
|
|
77
84
|
...levelsOf(rule, genre),
|
|
78
85
|
levels_you_can_set: definedLevels(rule),
|
|
79
86
|
your_setting: yourSetting(rule, config),
|
|
80
|
-
now: now(rule, config, genre, text),
|
|
87
|
+
now: now(rule, config, genre, text, preset),
|
|
81
88
|
})),
|
|
82
89
|
how_to_change: {
|
|
83
90
|
by_command: ["relax", "strict", "off"].map((command) => `npx chaff ${command} <rule-id> --why "${text.reason}"`),
|
package/src/render/semantic.ts
CHANGED
|
@@ -1,35 +1,113 @@
|
|
|
1
1
|
import type { Finding, RuleDefinition } from "../plugin.ts";
|
|
2
2
|
import type { UserCheck } from "../checks.ts";
|
|
3
|
+
import type { Narrowing } from "../look-at.ts";
|
|
3
4
|
import type { Job as Plan, SemanticResult } from "../run-semantic.ts";
|
|
5
|
+
import { uiLanguageOf, type Texts, type UiLanguage } from "../ui.ts";
|
|
4
6
|
import { MARK, localized } from "./text.ts";
|
|
7
|
+
import { counted, formFor } from "./plural.ts";
|
|
5
8
|
|
|
6
9
|
const RULE = 60;
|
|
7
10
|
|
|
11
|
+
const TEXT: Texts<{
|
|
12
|
+
readonly machineBanner: readonly string[];
|
|
13
|
+
readonly aiTitle: string;
|
|
14
|
+
readonly aiNote: readonly string[];
|
|
15
|
+
readonly read: (asked: number, seen: number) => string;
|
|
16
|
+
readonly wholeRead: (name: string) => readonly string[];
|
|
17
|
+
readonly word: (word: string) => string;
|
|
18
|
+
readonly number: string;
|
|
19
|
+
readonly narrowedRead: (name: string, what: string, kept: number, total: number) => string;
|
|
20
|
+
readonly line: (line: number) => string;
|
|
21
|
+
readonly confidence: (value: string) => string;
|
|
22
|
+
readonly disagree: readonly string[];
|
|
23
|
+
readonly silence: (rule: string) => string;
|
|
24
|
+
readonly relax: (rule: string) => string;
|
|
25
|
+
readonly notRunChecks: (count: number) => string;
|
|
26
|
+
readonly byMachine: string;
|
|
27
|
+
readonly whole: string;
|
|
28
|
+
readonly containing: (parts: string) => string;
|
|
29
|
+
readonly planLine: (count: number, name: string, how: string) => string;
|
|
30
|
+
readonly firstSample: string;
|
|
31
|
+
readonly planHead: (path: string, sentences: number, asked: number) => string;
|
|
32
|
+
}> = {
|
|
33
|
+
ja: {
|
|
34
|
+
machineBanner: ["", `═══ 機械による判定 ${"═".repeat(RULE - 18)}`, " 同じ文章なら何度実行しても同じ結果になります", ""],
|
|
35
|
+
aiTitle: `═══ AI による判定 ${"═".repeat(RULE - 17)}`,
|
|
36
|
+
aiNote: [" 文章の意味を読んでいます。実行するたび結果が変わることが", " あります。おかしいと思ったら、そのまま無視して構いません。"],
|
|
37
|
+
read: (asked, seen) => ` ${seen} 文のうち ${asked} 箇所を読みました(残りは機械が対象外と判断)`,
|
|
38
|
+
wholeRead: (name) => [` ${name}: 見るところを絞れず、全文を読みました`, ` look_at に「」で語を書くと、その語を含む文だけになります`],
|
|
39
|
+
word: (word) => `「${word}」`,
|
|
40
|
+
number: "数字",
|
|
41
|
+
narrowedRead: (name, what, kept, total) => ` ${name}: ${what} を含む ${kept} 文だけを読みました(全 ${total} 文)`,
|
|
42
|
+
line: (line) => `${line} 行目`,
|
|
43
|
+
confidence: (value) => `確からしさ ${value}`,
|
|
44
|
+
disagree: [" この指摘が違うと思ったら:"],
|
|
45
|
+
silence: (rule) => ` この箇所だけ黙らせる <!-- stet: ${rule} — 理由 -->`,
|
|
46
|
+
relax: (rule) => ` ルールごとゆるめる npx chaff relax ${rule}`,
|
|
47
|
+
notRunChecks: (count) => ` ${count} 件の検査は、見るところが無いため動いていません。`,
|
|
48
|
+
byMachine: "機械で絞り込み済み",
|
|
49
|
+
whole: "絞り込めず全文",
|
|
50
|
+
containing: (parts) => `${parts} を含む文`,
|
|
51
|
+
planLine: (count, name, how) => ` ${String(count).padStart(3)} 箇所 ${name} (${how})`,
|
|
52
|
+
firstSample: " 最初の 1 件に送る文章:",
|
|
53
|
+
planHead: (path, sentences, asked) => `${path} 全 ${sentences} 文のうち ${asked} 箇所を送ります(API は呼んでいません)`,
|
|
54
|
+
},
|
|
55
|
+
en: {
|
|
56
|
+
machineBanner: ["", `═══ Judged by machine ${"═".repeat(RULE - 21)}`, " The same text gives the same result every time", ""],
|
|
57
|
+
aiTitle: `═══ Judged by AI ${"═".repeat(RULE - 16)}`,
|
|
58
|
+
aiNote: [" These read the meaning of the text. The result can change", " from run to run. If one looks wrong, feel free to ignore it."],
|
|
59
|
+
read: (asked, seen) => ` Read ${counted(asked, "passage")} out of ${counted(seen, "sentence")} (the machine ruled out the rest)`,
|
|
60
|
+
wholeRead: (name) => [
|
|
61
|
+
` ${name}: could not narrow what to read, so read the whole text`,
|
|
62
|
+
` Put words in quotes in look_at ("like this") to read only the sentences that contain them`,
|
|
63
|
+
],
|
|
64
|
+
word: (word) => `"${word}"`,
|
|
65
|
+
number: "a number",
|
|
66
|
+
narrowedRead: (name, what, kept, total) => ` ${name}: read only the ${counted(kept, "sentence")} containing ${what} (of ${total})`,
|
|
67
|
+
line: (line) => `line ${line}`,
|
|
68
|
+
confidence: (value) => `confidence ${value}`,
|
|
69
|
+
disagree: [" If you think this finding is wrong:"],
|
|
70
|
+
silence: (rule) => ` Silence this spot only <!-- stet: ${rule} — reason -->`,
|
|
71
|
+
relax: (rule) => ` Relax the whole rule npx chaff relax ${rule}`,
|
|
72
|
+
notRunChecks: (count) => ` ${counted(count, "check")} did not run: there was nothing for ${formFor(count, "it", "them")} to look at.`,
|
|
73
|
+
byMachine: "narrowed by machine",
|
|
74
|
+
whole: "not narrowed: the whole text",
|
|
75
|
+
containing: (parts) => `sentences containing ${parts}`,
|
|
76
|
+
planLine: (count, name, how) => ` ${String(count).padStart(3)} to send ${name} (${how})`,
|
|
77
|
+
firstSample: " The text sent for the first one:",
|
|
78
|
+
planHead: (path, sentences, asked) => `${path} sends ${counted(asked, "passage")} out of ${counted(sentences, "sentence")} (the API was not called)`,
|
|
79
|
+
},
|
|
80
|
+
};
|
|
81
|
+
|
|
8
82
|
const indent = (text: string, pad: string): string[] => text.split("\n").map((line) => `${pad}${line}`);
|
|
9
83
|
|
|
10
|
-
|
|
84
|
+
/** The banner over a file's machine findings, in the file's language. */
|
|
85
|
+
export const machineBanner = (language: string): readonly string[] => TEXT[uiLanguageOf(language)].machineBanner;
|
|
86
|
+
|
|
87
|
+
/** The words a narrowing looks for, as the screen names them. */
|
|
88
|
+
const lookedFor = (narrowing: Narrowing, language: UiLanguage): string[] => {
|
|
89
|
+
const text = TEXT[language];
|
|
90
|
+
return [...narrowing.words.map((word) => text.word(word)), ...(narrowing.needsNumber ? [text.number] : [])];
|
|
91
|
+
};
|
|
11
92
|
|
|
12
93
|
/**
|
|
13
94
|
* 自分の書いた検査が、どこまで絞り込めたかを見せる。
|
|
14
95
|
* 絞り込めないまま全文を読んでいることに気づけないと、遅さの原因が分からない。
|
|
15
96
|
*/
|
|
16
|
-
const narrowingLines = (result: SemanticResult): string[] =>
|
|
97
|
+
const narrowingLines = (result: SemanticResult, language: UiLanguage): string[] =>
|
|
17
98
|
result.narrowed.flatMap(({ name, narrowing }) => {
|
|
18
|
-
if (narrowing.words.length === 0 && !narrowing.needsNumber)
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
const what = [...narrowing.words.map((word) => `「${word}」`), ...(narrowing.needsNumber ? ["数字"] : [])].join(" ");
|
|
22
|
-
return [` ${name}: ${what} を含む ${narrowing.kept} 文だけを読みました(全 ${narrowing.total} 文)`];
|
|
99
|
+
if (narrowing.words.length === 0 && !narrowing.needsNumber) return [...TEXT[language].wholeRead(name)];
|
|
100
|
+
const what = lookedFor(narrowing, language).join(language === "ja" ? " " : ", ");
|
|
101
|
+
return [TEXT[language].narrowedRead(name, what, narrowing.kept, narrowing.total)];
|
|
23
102
|
});
|
|
24
103
|
|
|
25
|
-
const aiBanner = (result: SemanticResult): string[] => [
|
|
104
|
+
const aiBanner = (result: SemanticResult, language: UiLanguage): string[] => [
|
|
26
105
|
"",
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
" あります。おかしいと思ったら、そのまま無視して構いません。",
|
|
106
|
+
TEXT[language].aiTitle,
|
|
107
|
+
...TEXT[language].aiNote,
|
|
30
108
|
"",
|
|
31
|
-
|
|
32
|
-
...narrowingLines(result),
|
|
109
|
+
TEXT[language].read(result.asked, result.sentencesSeen),
|
|
110
|
+
...narrowingLines(result, language),
|
|
33
111
|
"",
|
|
34
112
|
];
|
|
35
113
|
|
|
@@ -46,32 +124,37 @@ const nameOf = (rule: string, rules: readonly RuleDefinition[], checks: readonly
|
|
|
46
124
|
* AI の指摘にだけ確からしさを出し、納得できないときの逃げ道を 2 つ添える。
|
|
47
125
|
* 揺れる判定だと読む人が知らないと、誤検知に振り回される。
|
|
48
126
|
*/
|
|
49
|
-
const block = (finding: Finding, named: Named): string[] => {
|
|
127
|
+
const block = (finding: Finding, named: Named, language: UiLanguage): string[] => {
|
|
128
|
+
const text = TEXT[language];
|
|
50
129
|
const confidence = finding.values["confidence"];
|
|
51
130
|
const reason = String(finding.values["reason"] ?? "");
|
|
52
|
-
const sure = confidence === undefined ? "" : `
|
|
131
|
+
const sure = confidence === undefined ? "" : ` ${text.confidence(String(confidence))}`;
|
|
53
132
|
const fix = named.howToFix.length > 0 ? ["", ...indent(`→ ${named.howToFix}`, " ")] : [];
|
|
54
133
|
return [
|
|
55
134
|
"",
|
|
56
|
-
|
|
135
|
+
// "12 行目" and "line 12" take the same width on a terminal, so the rule ends in the same column in both.
|
|
136
|
+
`─── ${text.line(finding.line)} ${"─".repeat(Math.max(0, RULE - String(finding.line).length - 8))}`,
|
|
57
137
|
"",
|
|
58
138
|
` ${MARK[finding.severity] ?? "·"} ${named.name}${sure}`,
|
|
59
139
|
"",
|
|
60
140
|
...indent(reason, " "),
|
|
61
141
|
...fix,
|
|
62
142
|
"",
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
143
|
+
...text.disagree,
|
|
144
|
+
text.silence(finding.rule),
|
|
145
|
+
text.relax(finding.rule),
|
|
66
146
|
"",
|
|
67
147
|
];
|
|
68
148
|
};
|
|
69
149
|
|
|
70
|
-
export const renderSemantic = (result: SemanticResult, rules: readonly RuleDefinition[], checks: readonly UserCheck[], language: string): string[] =>
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
150
|
+
export const renderSemantic = (result: SemanticResult, rules: readonly RuleDefinition[], checks: readonly UserCheck[], language: string): string[] => {
|
|
151
|
+
const ui = uiLanguageOf(language);
|
|
152
|
+
return [
|
|
153
|
+
...aiBanner(result, ui),
|
|
154
|
+
...result.findings.flatMap((finding) => block(finding, nameOf(finding.rule, rules, checks, language), ui)),
|
|
155
|
+
...(result.skipped.length > 0 ? [TEXT[ui].notRunChecks(result.skipped.length), ""] : []),
|
|
156
|
+
];
|
|
157
|
+
};
|
|
75
158
|
|
|
76
159
|
/**
|
|
77
160
|
* 何が送られるのかを、API を呼ばずに見せる。`chaff test --dry-run`。
|
|
@@ -79,19 +162,19 @@ export const renderSemantic = (result: SemanticResult, rules: readonly RuleDefin
|
|
|
79
162
|
* 鍵が無い環境でも二段構えの 1 段目まで確かめられる。全文が送られようとしていることに
|
|
80
163
|
* 請求書で気づくのでは遅い。spec §14。
|
|
81
164
|
*/
|
|
82
|
-
const howNarrowed = (job: Plan): string => {
|
|
165
|
+
const howNarrowed = (job: Plan, language: UiLanguage): string => {
|
|
83
166
|
const narrowing = job.narrowing;
|
|
84
|
-
if (narrowing === undefined) return
|
|
85
|
-
const
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
return `${parts.join(" ")} を含む文`;
|
|
167
|
+
if (narrowing === undefined) return TEXT[language].byMachine;
|
|
168
|
+
const parts = lookedFor(narrowing, language);
|
|
169
|
+
if (parts.length === 0) return TEXT[language].whole;
|
|
170
|
+
return TEXT[language].containing(parts.join(language === "ja" ? " " : ", "));
|
|
89
171
|
};
|
|
90
172
|
|
|
91
|
-
export const renderPlan = (path: string, jobs: readonly Plan[], sentences: number): string[] => {
|
|
173
|
+
export const renderPlan = (path: string, jobs: readonly Plan[], sentences: number, language: string): string[] => {
|
|
174
|
+
const text = TEXT[uiLanguageOf(language)];
|
|
92
175
|
const asked = jobs.reduce((sum, job) => sum + job.candidates.length, 0);
|
|
93
|
-
const lines = jobs.map((job) =>
|
|
176
|
+
const lines = jobs.map((job) => text.planLine(job.candidates.length, job.name, howNarrowed(job, uiLanguageOf(language))));
|
|
94
177
|
const first = jobs.find((job) => job.candidates.length > 0);
|
|
95
|
-
const sample = first === undefined ? [] : ["",
|
|
96
|
-
return ["",
|
|
178
|
+
const sample = first === undefined ? [] : ["", text.firstSample, "", ...indent(String(first.candidates[0]?.text ?? "").slice(0, 300), " ")];
|
|
179
|
+
return ["", text.planHead(path, sentences, asked), "", ...lines, ...sample, ""];
|
|
97
180
|
};
|
package/src/render/summary.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { Finding } from "../plugin.ts";
|
|
2
2
|
import type { Texts, UiLanguage } from "../ui.ts";
|
|
3
|
+
import { counted } from "./plural.ts";
|
|
3
4
|
|
|
4
5
|
export type FileOutcome = { readonly path: string; readonly findings: readonly Finding[]; readonly notRun: number };
|
|
5
6
|
|
|
@@ -21,9 +22,9 @@ const TEXT: Texts<{
|
|
|
21
22
|
},
|
|
22
23
|
en: {
|
|
23
24
|
counts: {
|
|
24
|
-
error: (n) =>
|
|
25
|
-
warning: (n) =>
|
|
26
|
-
info: (n) =>
|
|
25
|
+
error: (n) => counted(n, "error"),
|
|
26
|
+
warning: (n) => counted(n, "warning"),
|
|
27
|
+
info: (n) => counted(n, "note"),
|
|
27
28
|
},
|
|
28
29
|
join: ", ",
|
|
29
30
|
none: "No findings",
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { Suppressed, Suppression } from "../stet.ts";
|
|
2
2
|
import type { Texts, UiLanguage } from "../ui.ts";
|
|
3
|
+
import { counted } from "./plural.ts";
|
|
3
4
|
|
|
4
5
|
const TEXT: Texts<{
|
|
5
6
|
readonly nudge: string;
|
|
@@ -23,7 +24,7 @@ const TEXT: Texts<{
|
|
|
23
24
|
},
|
|
24
25
|
en: {
|
|
25
26
|
nudge: " <- consider changing the setting instead",
|
|
26
|
-
more: (n) => ` and ${n
|
|
27
|
+
more: (n) => ` and ${counted(n, "more file")}`,
|
|
27
28
|
count: (n) => `${String(n).padStart(3)}`,
|
|
28
29
|
reasons: (list) => `reasons: ${list}`,
|
|
29
30
|
relaxAll: (rule) => `relax the whole rule: npx chaff relax ${rule} --why "..."`,
|
|
@@ -53,7 +54,7 @@ const groupByRule = (files: readonly PerFile[]): Group[] => {
|
|
|
53
54
|
reasons: [...new Set(mine.flatMap((row) => (row.reason === undefined ? [] : [row.reason])))],
|
|
54
55
|
};
|
|
55
56
|
})
|
|
56
|
-
.
|
|
57
|
+
.toSorted((left, right) => right.count - left.count);
|
|
57
58
|
};
|
|
58
59
|
|
|
59
60
|
const groupLines = (group: Group, ui: UiLanguage): string[] => {
|
package/src/render/text.ts
CHANGED
|
@@ -1,13 +1,28 @@
|
|
|
1
1
|
import type { Finding, Localized, RuleDefinition } from "../plugin.ts";
|
|
2
|
+
import { formFor } from "./plural.ts";
|
|
2
3
|
|
|
3
4
|
export const localized = (field: Localized, language: string): string => field[language] ?? field["en"] ?? Object.values(field)[0] ?? "";
|
|
4
5
|
|
|
6
|
+
/** `{count}` is the value; `{count|word|words}` is the form that agrees with it (English only; Japanese has no plural). */
|
|
7
|
+
const PLACEHOLDER = /\{(\w+)(?:\|([^|{}]*)\|([^|{}]*))?\}/gu;
|
|
8
|
+
|
|
5
9
|
const fill = (template: string, values: Readonly<Record<string, string | number>>): string =>
|
|
6
|
-
template.replace(
|
|
10
|
+
template.replace(PLACEHOLDER, (whole, key: string, singular: string | undefined, plural: string | undefined) => {
|
|
7
11
|
const value = values[key];
|
|
8
|
-
|
|
12
|
+
if (value === undefined) return whole;
|
|
13
|
+
return singular === undefined || plural === undefined ? String(value) : formFor(value, singular, plural);
|
|
9
14
|
});
|
|
10
15
|
|
|
11
|
-
|
|
16
|
+
/** A template as the rule reference shows it: each agreeing form as its plural ("{count} such words"). */
|
|
17
|
+
export const templateForReading = (template: string): string =>
|
|
18
|
+
template.replace(PLACEHOLDER, (whole, _key: string, singular: string | undefined, plural: string | undefined) =>
|
|
19
|
+
singular === undefined || plural === undefined ? whole : plural,
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
const templateOf = (rule: RuleDefinition, finding: Finding): Localized =>
|
|
23
|
+
(finding.variant === undefined ? undefined : rule.messages[finding.variant]) ?? rule.message;
|
|
24
|
+
|
|
25
|
+
export const messageOf = (rule: RuleDefinition, finding: Finding, language: string): string =>
|
|
26
|
+
fill(localized(templateOf(rule, finding), language), finding.values);
|
|
12
27
|
|
|
13
28
|
export const MARK: Readonly<Record<string, string>> = { error: "✖", warning: "⚠", info: "·" };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { applyByPath } from "./config/by-path.ts";
|
|
2
|
+
import type { Config } from "./config/load.ts";
|
|
3
|
+
import type { GenreSource } from "./cli-text.ts";
|
|
4
|
+
import { frontMatterGenre, GENRES, guessGenre } from "./genre.ts";
|
|
5
|
+
import { unknownFrontMatterGenre } from "./genre-check.ts";
|
|
6
|
+
|
|
7
|
+
/** unread: a genre the front matter wrote that chaff does not know, when the front matter was looked at. */
|
|
8
|
+
type ResolvedGenre = { readonly genre: string; readonly from: GenreSource; readonly unread?: string | undefined };
|
|
9
|
+
|
|
10
|
+
export const resolveGenre = (path: string, source: string, config: Config, cliGenre?: string): ResolvedGenre => {
|
|
11
|
+
// コマンドで指定したものが最優先。その実行だけの指定だから、設定より強い。
|
|
12
|
+
if (cliGenre !== undefined) return { genre: cliGenre, from: "--genre" };
|
|
13
|
+
// パスごとの上書きが次。「全体はこう、ここだけは違う」を書けるようにする。
|
|
14
|
+
const override = applyByPath(config.byPath, config.baseDir, path);
|
|
15
|
+
if (override.genre !== undefined) return { genre: override.genre, from: "by_path" };
|
|
16
|
+
if (config.genre !== undefined) return { genre: config.genre, from: "config" };
|
|
17
|
+
const guess = guessGenre(path, source, frontMatterGenre(source));
|
|
18
|
+
const unread = unknownFrontMatterGenre(source, GENRES);
|
|
19
|
+
return guess === undefined ? { genre: "blog/tech", from: "default", unread } : { genre: guess.genre, from: guess.from, unread };
|
|
20
|
+
};
|
package/src/rule-load.ts
CHANGED
|
@@ -68,6 +68,10 @@ const missingFields = (raw: Record<string, unknown>, levels: LevelTable | undefi
|
|
|
68
68
|
|
|
69
69
|
const localizedOf = (value: unknown): Record<string, string> => (isLocalized(value) ? value : {});
|
|
70
70
|
|
|
71
|
+
/** 見つけ方ごとの message。読めないものは捨てる。 */
|
|
72
|
+
const messagesOf = (value: unknown): Record<string, Record<string, string>> =>
|
|
73
|
+
isRecord(value) ? Object.fromEntries(Object.entries(value).flatMap(([variant, text]) => (isLocalized(text) ? [[variant, text]] : []))) : {};
|
|
74
|
+
|
|
71
75
|
/**
|
|
72
76
|
* severity も言語別に書ける。levels と同じ畳みかた。
|
|
73
77
|
* 記号の許容度は言語で大きく違う(ダッシュは英語では普通、日本語の組版では扱いが難しい)。
|
|
@@ -95,6 +99,7 @@ const toRule = (raw: unknown, language: string, file: string): RuleDefinition =>
|
|
|
95
99
|
why: localizedOf(raw["why"]),
|
|
96
100
|
how_to_fix: localizedOf(raw["how_to_fix"]),
|
|
97
101
|
message: localizedOf(raw["message"]),
|
|
102
|
+
messages: messagesOf(raw["messages"]),
|
|
98
103
|
levels,
|
|
99
104
|
by_genre: genreTables(raw["by_genre"], language),
|
|
100
105
|
how_to_find: String(raw["how_to_find"]),
|
|
@@ -126,5 +131,5 @@ const parseRule = (dir: string, file: string): unknown => {
|
|
|
126
131
|
export const loadRules = (language: string, dir: string = RULES_DIR): RuleDefinition[] =>
|
|
127
132
|
readdirSync(dir)
|
|
128
133
|
.filter((file) => file.endsWith(".yaml"))
|
|
129
|
-
.
|
|
134
|
+
.toSorted((left, right) => left.localeCompare(right, "en"))
|
|
130
135
|
.map((file) => toRule(parseRule(dir, file), language, file));
|
package/src/run-semantic.ts
CHANGED
|
@@ -129,7 +129,7 @@ export const runSemantic = async (
|
|
|
129
129
|
),
|
|
130
130
|
);
|
|
131
131
|
return {
|
|
132
|
-
findings: answers.filter((finding) => finding !== undefined).
|
|
132
|
+
findings: answers.filter((finding) => finding !== undefined).toSorted((left, right) => left.line - right.line),
|
|
133
133
|
skipped,
|
|
134
134
|
asked: live.reduce((sum, job) => sum + job.candidates.length, 0),
|
|
135
135
|
sentencesSeen: doc.sentences.length,
|
package/src/run.ts
CHANGED
|
@@ -10,6 +10,7 @@ import { isMarkdownPath } from "./structure/markdown-path.ts";
|
|
|
10
10
|
import { maskSpans } from "./mask.ts";
|
|
11
11
|
import { textOutline } from "./page-furniture.ts";
|
|
12
12
|
import { uiLanguageOf } from "./ui.ts";
|
|
13
|
+
import { presetLevels } from "./genre-load.ts";
|
|
13
14
|
|
|
14
15
|
export type Skipped = { readonly rule: string; readonly why: string };
|
|
15
16
|
|
|
@@ -18,18 +19,29 @@ export type RunResult = {
|
|
|
18
19
|
readonly skipped: readonly Skipped[];
|
|
19
20
|
/** 設定で明示的に有効にした experimental な rule。実行ごとに一度報告する。spec §18.4。 */
|
|
20
21
|
readonly forcedExperimental: readonly string[];
|
|
22
|
+
/** The experimental rules the genre's preset turns on. Reported apart from forcedExperimental: chaff.yaml did not name them. */
|
|
23
|
+
readonly presetExperimental: readonly string[];
|
|
21
24
|
};
|
|
22
25
|
|
|
23
26
|
export type Settings = Readonly<Record<string, Level>>;
|
|
24
27
|
|
|
25
|
-
const levelFor = (rule: RuleDefinition, settings: Settings, experimental: boolean): Level => {
|
|
28
|
+
const levelFor = (rule: RuleDefinition, settings: Settings, experimental: boolean, preset: Settings): Level => {
|
|
26
29
|
const explicit = settings[rule.id];
|
|
27
30
|
// 明示設定は status の既定に勝つ。名指しで有効にしたものを黙って無効にしない。
|
|
28
31
|
if (explicit !== undefined) return explicit;
|
|
32
|
+
// ジャンルの既定は chaff.yaml より弱く、status の既定より強い。--experimental でも、ジャンルが止めたものは止めたまま。
|
|
33
|
+
const fromPreset = preset[rule.id];
|
|
34
|
+
if (fromPreset !== undefined) return fromPreset;
|
|
29
35
|
if (rule.status === "experimental" && !experimental) return "off";
|
|
30
36
|
return "normal";
|
|
31
37
|
};
|
|
32
38
|
|
|
39
|
+
/** Why a rule at off did not run: chaff.yaml turned it off, the genre's preset did, or it is experimental. */
|
|
40
|
+
const offReason = (rule: RuleDefinition, settings: Settings, preset: Settings, genre: string, reasons: Reasons): string => {
|
|
41
|
+
if (settings[rule.id] !== undefined) return reasons.turnedOff;
|
|
42
|
+
return preset[rule.id] === undefined ? reasons.experimental : reasons.presetOff(genre);
|
|
43
|
+
};
|
|
44
|
+
|
|
33
45
|
const reasonsFor = (doc: ProseDocument): Reasons => REASONS[uiLanguageOf(doc.language)];
|
|
34
46
|
|
|
35
47
|
/**
|
|
@@ -41,11 +53,11 @@ const reasonsFor = (doc: ProseDocument): Reasons => REASONS[uiLanguageOf(doc.lan
|
|
|
41
53
|
* 読むので、番号を読めなかった文書でも動く。どちらも capability ではなく、adapter が木を作れるかで決まる。
|
|
42
54
|
*/
|
|
43
55
|
const TREE_NEEDS: ReadonlySet<string> = new Set(["structure", "dates", "quantities"]);
|
|
44
|
-
const VALUE_NEEDS:
|
|
56
|
+
const VALUE_NEEDS: ReadonlySet<string> = new Set(["dates", "quantities"]);
|
|
45
57
|
|
|
46
58
|
const treeNeed = (rule: RuleDefinition, doc: ProseDocument): string | undefined => {
|
|
47
59
|
if (rule.requires.includes("structure")) return treeProblem(doc);
|
|
48
|
-
return rule.requires.some((need) => VALUE_NEEDS.
|
|
60
|
+
return rule.requires.some((need) => VALUE_NEEDS.has(need)) && doc.structure === undefined ? reasonsFor(doc).noStructure(doc.language) : undefined;
|
|
49
61
|
};
|
|
50
62
|
|
|
51
63
|
const treeProblem = (doc: ProseDocument): string | undefined => {
|
|
@@ -106,7 +118,7 @@ const forGenre = (rules: readonly RuleDefinition[], genre: string): RuleDefiniti
|
|
|
106
118
|
*/
|
|
107
119
|
export const neededBy = (rules: readonly RuleDefinition[], settings: Settings, experimental: boolean, genre: string, language: string): AdapterNeeds => ({
|
|
108
120
|
pos: forGenre(rules, genre)
|
|
109
|
-
.filter((rule) => rule.layer !== "L4" && levelFor(rule, settings, experimental) !== "off")
|
|
121
|
+
.filter((rule) => rule.layer !== "L4" && levelFor(rule, settings, experimental, presetLevels(genre)) !== "off")
|
|
110
122
|
.filter((rule) => rule.languages === undefined || rule.languages.includes(language))
|
|
111
123
|
.some((rule) => [...rule.requires, ...rule.uses].some((need) => need === "pos" || need === "lemma")),
|
|
112
124
|
});
|
|
@@ -153,11 +165,12 @@ export const runRules = (
|
|
|
153
165
|
genre: string,
|
|
154
166
|
limits: Limits = {},
|
|
155
167
|
): RunResult => {
|
|
168
|
+
const preset = presetLevels(genre);
|
|
156
169
|
const starts = lineStarts(doc.source);
|
|
157
170
|
const applicable = forGenre(rules, genre);
|
|
158
|
-
const
|
|
159
|
-
|
|
160
|
-
|
|
171
|
+
const experimentalOn = applicable.filter((rule) => rule.status === "experimental" && levelFor(rule, settings, experimental, preset) !== "off");
|
|
172
|
+
const forced = experimentalOn.filter((rule) => settings[rule.id] !== undefined).map((rule) => rule.id);
|
|
173
|
+
const presetOn = experimentalOn.filter((rule) => settings[rule.id] === undefined && preset[rule.id] !== undefined).map((rule) => rule.id);
|
|
161
174
|
const outcome = applicable.reduce<{ findings: Finding[]; skipped: Skipped[] }>(
|
|
162
175
|
(acc, rule) => {
|
|
163
176
|
// L4 は意味を読む検査。chaff test が扱う。ここで「検出器が無い」と言わせない。
|
|
@@ -166,11 +179,9 @@ export const runRules = (
|
|
|
166
179
|
}
|
|
167
180
|
const blocked = unmet(rule, doc);
|
|
168
181
|
if (blocked !== undefined) return { findings: acc.findings, skipped: [...acc.skipped, { rule: rule.id, why: blocked }] };
|
|
169
|
-
const level = levelFor(rule, settings, experimental);
|
|
170
|
-
if (level === "off")
|
|
171
|
-
|
|
172
|
-
return { findings: acc.findings, skipped: [...acc.skipped, { rule: rule.id, why }] };
|
|
173
|
-
}
|
|
182
|
+
const level = levelFor(rule, settings, experimental, preset);
|
|
183
|
+
if (level === "off")
|
|
184
|
+
return { findings: acc.findings, skipped: [...acc.skipped, { rule: rule.id, why: offReason(rule, settings, preset, genre, reasonsFor(doc)) }] };
|
|
174
185
|
const noTags = untagged(rule, doc);
|
|
175
186
|
if (noTags !== undefined) return { findings: acc.findings, skipped: [...acc.skipped, { rule: rule.id, why: noTags }] };
|
|
176
187
|
// 木は capability ではなく、adapter が structure を持つかで決まる。持たない言語で動かすと「参照先が無い」が 0 件に見える。
|
|
@@ -197,8 +208,10 @@ export const runRules = (
|
|
|
197
208
|
{ findings: [], skipped: [] },
|
|
198
209
|
);
|
|
199
210
|
const composites = applicable
|
|
200
|
-
.filter((rule) => rule.from.length > 0 && levelFor(rule, settings, experimental) !== "off")
|
|
201
|
-
.flatMap((rule) =>
|
|
211
|
+
.filter((rule) => rule.from.length > 0 && levelFor(rule, settings, experimental, preset) !== "off")
|
|
212
|
+
.flatMap((rule) =>
|
|
213
|
+
compositeOf(rule, outcome.findings, limitFor(rule, levelFor(rule, settings, experimental, preset), genre, limits), starts, doc.language),
|
|
214
|
+
);
|
|
202
215
|
const all = [...outcome.findings, ...composites];
|
|
203
|
-
return { ...outcome, findings:
|
|
216
|
+
return { ...outcome, findings: all.toSorted((left, right) => left.line - right.line), forcedExperimental: forced, presetExperimental: presetOn };
|
|
204
217
|
};
|