diff --git a/.vale.ini b/.vale.ini index 250e9b2dd..547f93b8a 100644 --- a/.vale.ini +++ b/.vale.ini @@ -4,7 +4,8 @@ MinAlertLevel = error Packages = https://github.com/tbhb/vale-ai-tells/releases/download/v1.29.0/ai-tells.zip [*.{rst,md}] -BasedOnStyles = ai-tells +BasedOnStyles = ai-tells, snapper +snapper.SemanticLineBreaks = error # These rules misclassify established technical, example, or release-note prose # in this repository. All other ai-tells rules remain enforced. diff --git a/docs/source/unreleased.rst b/docs/source/unreleased.rst index 22ac74723..d297d1976 100644 --- a/docs/source/unreleased.rst +++ b/docs/source/unreleased.rst @@ -1,8 +1,8 @@ Unreleased changes ================== -Changes that have landed on the main branch but are not yet part of a -tagged release. These entries are assembled into the +Changes that have landed on the main branch but are not yet part of a tagged release. +These entries are assembled into the :doc:`changelog` when the next release is published. .. towncrier-draft-entries:: diff --git a/prek.toml b/prek.toml index c013e7e46..9869ba179 100644 --- a/prek.toml +++ b/prek.toml @@ -364,6 +364,19 @@ hooks = [ additional_dependencies = ["uv==0.11.7"], stages = ["pre-commit"] }, + { + id = "snapper", + name = "snapper", + entry = "uv run --group=dev snapper --max-width 0 --in-place", + language = "python", + types_or = [ + "rst", + "markdown" + ], + require_serial = true, + additional_dependencies = ["uv==0.11.7"], + stages = ["pre-commit"] + }, { id = "vale", name = "vale", diff --git a/pyproject.toml b/pyproject.toml index d764b3dcd..57d650a43 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -75,6 +75,7 @@ dev = [ # use it to lint shell commands in GitHub workflow files. "shellcheck-py==0.11.0.1", "shfmt-py==4.1.0", + "snapper-fmt==0.11.0", "sphinx==9.1.0", "sphinx-copybutton==0.5.2", "sphinx-lint==1.0.2", diff --git a/styles/snapper/LongProseLine.yml b/styles/snapper/LongProseLine.yml new file mode 100644 index 000000000..1f5029f7d --- /dev/null +++ b/styles/snapper/LongProseLine.yml @@ -0,0 +1,11 @@ +--- +# Vale style rule: flag prose lines that are suspiciously long. +# Likely indicates wrapped text that should use semantic line breaks. +extends: existence +message: Prose line exceeds 120 characters. Consider running 'snapper' to break at + sentence boundaries. +level: suggestion +scope: paragraph +# This catches lines > 120 chars that contain a period followed by a space. +# Pure code or URLs are less likely to match. +tokens: ['.{120,}'] diff --git a/styles/snapper/SemanticLineBreaks.yml b/styles/snapper/SemanticLineBreaks.yml new file mode 100644 index 000000000..3f4edaf55 --- /dev/null +++ b/styles/snapper/SemanticLineBreaks.yml @@ -0,0 +1,11 @@ +--- +# Vale style rule: flag lines containing multiple sentences. +# These should be broken at sentence boundaries. +extends: existence +message: Line contains multiple sentences. Run 'snapper --check' or reformat with + 'snapper --in-place'. +level: warning +scope: paragraph +# Match a sentence-ending punctuation followed by a space and a capital letter +# on the same line (indicating two sentences on one line). +tokens: ['[.!?]\\s+[A-Z]']