normik 0.1.0__tar.gz

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 (313) hide show
  1. normik-0.1.0/.gitignore +50 -0
  2. normik-0.1.0/LICENSE +21 -0
  3. normik-0.1.0/Makefile +25 -0
  4. normik-0.1.0/PKG-INFO +140 -0
  5. normik-0.1.0/README.md +115 -0
  6. normik-0.1.0/docs/contributing.md +328 -0
  7. normik-0.1.0/docs/index.md +57 -0
  8. normik-0.1.0/docs/installation.md +37 -0
  9. normik-0.1.0/docs/integrations.md +79 -0
  10. normik-0.1.0/docs/profiles.md +84 -0
  11. normik-0.1.0/docs/rules/G732-4-elements-order.md +79 -0
  12. normik-0.1.0/docs/rules/G732-4-required-element-missing.md +77 -0
  13. normik-0.1.0/docs/rules/G732-5.3.2-abstract-volume-info.md +63 -0
  14. normik-0.1.0/docs/rules/G732-5.3.2.1-keywords-count.md +61 -0
  15. normik-0.1.0/docs/rules/G732-5.5.2-terms-opening.md +59 -0
  16. normik-0.1.0/docs/rules/G732-5.6.1-abbreviations-opening.md +59 -0
  17. normik-0.1.0/docs/rules/G732-6.12.1-abstract-volume-inline.md +67 -0
  18. normik-0.1.0/docs/rules/G732-6.12.2-keywords-final-dot.md +49 -0
  19. normik-0.1.0/docs/rules/G732-6.12.2-keywords-uppercase.md +50 -0
  20. normik-0.1.0/docs/rules/G732-6.14-terms-dash.md +63 -0
  21. normik-0.1.0/docs/rules/G732-6.15-abbreviations-dash.md +59 -0
  22. normik-0.1.0/docs/rules/G732-6.16-bibitem-uncited.md +56 -0
  23. normik-0.1.0/docs/rules/G732-6.16-bibliography-order.md +58 -0
  24. normik-0.1.0/docs/rules/G732-6.16-bibtex-order-unverifiable.md +57 -0
  25. normik-0.1.0/docs/rules/G732-6.17.2-appendix-no-reference.md +65 -0
  26. normik-0.1.0/docs/rules/G732-6.17.3-appendix-page-break.md +64 -0
  27. normik-0.1.0/docs/rules/G732-6.17.4-appendix-letter.md +56 -0
  28. normik-0.1.0/docs/rules/G732-6.17.4-appendix-sequence.md +56 -0
  29. normik-0.1.0/docs/rules/G732-6.17.6-appendix-numbering.md +67 -0
  30. normik-0.1.0/docs/rules/G732-6.2.1-structural-heading-case.md +58 -0
  31. normik-0.1.0/docs/rules/G732-6.2.1-structural-heading-numbered.md +62 -0
  32. normik-0.1.0/docs/rules/G732-6.2.2-heading-empty.md +58 -0
  33. normik-0.1.0/docs/rules/G732-6.2.3-heading-dot.md +57 -0
  34. normik-0.1.0/docs/rules/G732-6.2.4-heading-hyphenation.md +48 -0
  35. normik-0.1.0/docs/rules/G732-6.4.1-manual-section-number.md +62 -0
  36. normik-0.1.0/docs/rules/G732-6.4.5-heading-depth.md +69 -0
  37. normik-0.1.0/docs/rules/G732-6.4.6-enumeration-letters.md +61 -0
  38. normik-0.1.0/docs/rules/G732-6.5.1-figure-no-reference.md +72 -0
  39. normik-0.1.0/docs/rules/G732-6.5.1-reference-word.md +50 -0
  40. normik-0.1.0/docs/rules/G732-6.5.4-numbering-scheme-mixed.md +80 -0
  41. normik-0.1.0/docs/rules/G732-6.5.5-appendix-numbering.md +73 -0
  42. normik-0.1.0/docs/rules/G732-6.5.7-caption-dot.md +94 -0
  43. normik-0.1.0/docs/rules/G732-6.5.7-caption-manual-number.md +76 -0
  44. normik-0.1.0/docs/rules/G732-6.5.7-caption-position.md +65 -0
  45. normik-0.1.0/docs/rules/G732-6.5.8-caption-capital.md +73 -0
  46. normik-0.1.0/docs/rules/G732-6.5.8-caption-hyphenation.md +60 -0
  47. normik-0.1.0/docs/rules/G732-6.6.2-reference-word.md +46 -0
  48. normik-0.1.0/docs/rules/G732-6.6.2-table-no-reference.md +78 -0
  49. normik-0.1.0/docs/rules/G732-6.6.3-caption-capital.md +56 -0
  50. normik-0.1.0/docs/rules/G732-6.6.3-caption-dot.md +63 -0
  51. normik-0.1.0/docs/rules/G732-6.6.3-caption-manual-number.md +72 -0
  52. normik-0.1.0/docs/rules/G732-6.6.3-caption-position.md +64 -0
  53. normik-0.1.0/docs/rules/G732-6.6.4-appendix-numbering.md +81 -0
  54. normik-0.1.0/docs/rules/G732-6.7.2-note-capital.md +59 -0
  55. normik-0.1.0/docs/rules/G732-6.7.3-note-dash.md +57 -0
  56. normik-0.1.0/docs/rules/G732-6.7.4-footnote-space.md +48 -0
  57. normik-0.1.0/docs/rules/G732-6.8.1-blank-line-around.md +66 -0
  58. normik-0.1.0/docs/rules/G732-6.8.2-where-colon.md +59 -0
  59. normik-0.1.0/docs/rules/G732-6.8.3-formula-no-reference.md +72 -0
  60. normik-0.1.0/docs/rules/G732-6.8.4-formula-reference-format.md +51 -0
  61. normik-0.1.0/docs/rules/G732-6.8.5-appendix-numbering.md +75 -0
  62. normik-0.1.0/docs/rules/G732-6.9.1-cite-unresolved.md +56 -0
  63. normik-0.1.0/docs/rules/NK-STYLE-dash.md +61 -0
  64. normik-0.1.0/docs/rules/NK-STYLE-initials-nbsp.md +56 -0
  65. normik-0.1.0/docs/rules/NK-STYLE-particle-nbsp.md +52 -0
  66. normik-0.1.0/docs/rules/NK-STYLE-preposition-nbsp.md +76 -0
  67. normik-0.1.0/docs/rules/NK-STYLE-quotes.md +51 -0
  68. normik-0.1.0/docs/rules/NK-STYLE-reference-nbsp.md +54 -0
  69. normik-0.1.0/docs/rules/NK-STYLE-unit-nbsp.md +55 -0
  70. normik-0.1.0/docs/rules/SUMMARY.md +64 -0
  71. normik-0.1.0/docs/rules/index.md +73 -0
  72. normik-0.1.0/docs/usage.md +236 -0
  73. normik-0.1.0/profiles/example-university.toml +35 -0
  74. normik-0.1.0/properdocs.yml +79 -0
  75. normik-0.1.0/pyproject.toml +100 -0
  76. normik-0.1.0/src/nk/__init__.py +3 -0
  77. normik-0.1.0/src/nk/cli.py +331 -0
  78. normik-0.1.0/src/nk/core/__init__.py +1 -0
  79. normik-0.1.0/src/nk/core/baseline.py +94 -0
  80. normik-0.1.0/src/nk/core/diagnostics.py +44 -0
  81. normik-0.1.0/src/nk/core/document.py +219 -0
  82. normik-0.1.0/src/nk/core/finding.py +97 -0
  83. normik-0.1.0/src/nk/core/fixer.py +135 -0
  84. normik-0.1.0/src/nk/core/numbering.py +82 -0
  85. normik-0.1.0/src/nk/core/position.py +50 -0
  86. normik-0.1.0/src/nk/core/profile.py +197 -0
  87. normik-0.1.0/src/nk/core/registry.py +91 -0
  88. normik-0.1.0/src/nk/core/rule.py +225 -0
  89. normik-0.1.0/src/nk/core/runner.py +197 -0
  90. normik-0.1.0/src/nk/core/suppressions.py +51 -0
  91. normik-0.1.0/src/nk/parse/__init__.py +1 -0
  92. normik-0.1.0/src/nk/parse/issues.py +60 -0
  93. normik-0.1.0/src/nk/parse/numbering.py +194 -0
  94. normik-0.1.0/src/nk/parse/structure.py +336 -0
  95. normik-0.1.0/src/nk/parse/suppressions.py +48 -0
  96. normik-0.1.0/src/nk/parse/tex.py +236 -0
  97. normik-0.1.0/src/nk/profiles/__init__.py +1 -0
  98. normik-0.1.0/src/nk/profiles/base.toml +17 -0
  99. normik-0.1.0/src/nk/py.typed +0 -0
  100. normik-0.1.0/src/nk/report/__init__.py +1 -0
  101. normik-0.1.0/src/nk/report/agent.py +78 -0
  102. normik-0.1.0/src/nk/report/common.py +57 -0
  103. normik-0.1.0/src/nk/report/examples.py +69 -0
  104. normik-0.1.0/src/nk/report/human.py +81 -0
  105. normik-0.1.0/src/nk/report/json.py +55 -0
  106. normik-0.1.0/src/nk/report/rules_docs.py +213 -0
  107. normik-0.1.0/src/nk/rules/__init__.py +14 -0
  108. normik-0.1.0/src/nk/rules/_shared.py +324 -0
  109. normik-0.1.0/src/nk/rules/_text.py +46 -0
  110. normik-0.1.0/src/nk/rules/elements/__init__.py +1 -0
  111. normik-0.1.0/src/nk/rules/elements/appendix_figure_numbering.py +46 -0
  112. normik-0.1.0/src/nk/rules/elements/appendix_formula_numbering.py +46 -0
  113. normik-0.1.0/src/nk/rules/elements/appendix_table_numbering.py +46 -0
  114. normik-0.1.0/src/nk/rules/elements/bibliography_bibtex.py +48 -0
  115. normik-0.1.0/src/nk/rules/elements/bibliography_cite_unresolved.py +66 -0
  116. normik-0.1.0/src/nk/rules/elements/bibliography_order.py +69 -0
  117. normik-0.1.0/src/nk/rules/elements/bibliography_uncited.py +47 -0
  118. normik-0.1.0/src/nk/rules/elements/figure_caption_capital.py +50 -0
  119. normik-0.1.0/src/nk/rules/elements/figure_caption_dot.py +44 -0
  120. normik-0.1.0/src/nk/rules/elements/figure_caption_hyphenation.py +50 -0
  121. normik-0.1.0/src/nk/rules/elements/figure_caption_manual_number.py +55 -0
  122. normik-0.1.0/src/nk/rules/elements/figure_caption_position.py +45 -0
  123. normik-0.1.0/src/nk/rules/elements/figure_reference.py +54 -0
  124. normik-0.1.0/src/nk/rules/elements/figure_reference_word.py +44 -0
  125. normik-0.1.0/src/nk/rules/elements/footnote_space.py +53 -0
  126. normik-0.1.0/src/nk/rules/elements/formula_blank_lines.py +62 -0
  127. normik-0.1.0/src/nk/rules/elements/formula_reference.py +51 -0
  128. normik-0.1.0/src/nk/rules/elements/formula_reference_format.py +59 -0
  129. normik-0.1.0/src/nk/rules/elements/formula_where_colon.py +46 -0
  130. normik-0.1.0/src/nk/rules/elements/note_capital.py +50 -0
  131. normik-0.1.0/src/nk/rules/elements/note_dash.py +68 -0
  132. normik-0.1.0/src/nk/rules/elements/numbering_scheme_mixed.py +48 -0
  133. normik-0.1.0/src/nk/rules/elements/table_caption_capital.py +49 -0
  134. normik-0.1.0/src/nk/rules/elements/table_caption_dot.py +42 -0
  135. normik-0.1.0/src/nk/rules/elements/table_caption_manual_number.py +53 -0
  136. normik-0.1.0/src/nk/rules/elements/table_caption_position.py +43 -0
  137. normik-0.1.0/src/nk/rules/elements/table_reference.py +52 -0
  138. normik-0.1.0/src/nk/rules/elements/table_reference_word.py +41 -0
  139. normik-0.1.0/src/nk/rules/layout/__init__.py +1 -0
  140. normik-0.1.0/src/nk/rules/layout/enumeration_letters.py +55 -0
  141. normik-0.1.0/src/nk/rules/layout/heading_depth.py +49 -0
  142. normik-0.1.0/src/nk/rules/layout/heading_dot.py +47 -0
  143. normik-0.1.0/src/nk/rules/layout/heading_empty.py +47 -0
  144. normik-0.1.0/src/nk/rules/layout/heading_hyphenation.py +51 -0
  145. normik-0.1.0/src/nk/rules/layout/manual_section_number.py +71 -0
  146. normik-0.1.0/src/nk/rules/layout/structural_heading_case.py +52 -0
  147. normik-0.1.0/src/nk/rules/layout/structural_heading_numbered.py +49 -0
  148. normik-0.1.0/src/nk/rules/structure/__init__.py +1 -0
  149. normik-0.1.0/src/nk/rules/structure/abbreviations_opening.py +55 -0
  150. normik-0.1.0/src/nk/rules/structure/abstract_volume_info.py +53 -0
  151. normik-0.1.0/src/nk/rules/structure/abstract_volume_inline.py +91 -0
  152. normik-0.1.0/src/nk/rules/structure/appendix_internal_numbering.py +54 -0
  153. normik-0.1.0/src/nk/rules/structure/appendix_letter.py +52 -0
  154. normik-0.1.0/src/nk/rules/structure/appendix_page_break.py +59 -0
  155. normik-0.1.0/src/nk/rules/structure/appendix_reference.py +67 -0
  156. normik-0.1.0/src/nk/rules/structure/appendix_sequence.py +66 -0
  157. normik-0.1.0/src/nk/rules/structure/elements_order.py +56 -0
  158. normik-0.1.0/src/nk/rules/structure/keywords_count.py +57 -0
  159. normik-0.1.0/src/nk/rules/structure/keywords_final_dot.py +38 -0
  160. normik-0.1.0/src/nk/rules/structure/keywords_uppercase.py +43 -0
  161. normik-0.1.0/src/nk/rules/structure/required_elements.py +66 -0
  162. normik-0.1.0/src/nk/rules/structure/terms_opening.py +54 -0
  163. normik-0.1.0/src/nk/rules/typography/__init__.py +6 -0
  164. normik-0.1.0/src/nk/rules/typography/abbreviations_dash.py +55 -0
  165. normik-0.1.0/src/nk/rules/typography/initials_nbsp.py +81 -0
  166. normik-0.1.0/src/nk/rules/typography/particle_nbsp.py +56 -0
  167. normik-0.1.0/src/nk/rules/typography/preposition_nbsp.py +79 -0
  168. normik-0.1.0/src/nk/rules/typography/reference_nbsp.py +53 -0
  169. normik-0.1.0/src/nk/rules/typography/terms_dash.py +55 -0
  170. normik-0.1.0/src/nk/rules/typography/text_dash.py +83 -0
  171. normik-0.1.0/src/nk/rules/typography/text_quotes.py +64 -0
  172. normik-0.1.0/src/nk/rules/typography/unit_nbsp.py +69 -0
  173. normik-0.1.0/tests/conftest.py +30 -0
  174. normik-0.1.0/tests/fixtures/G732-4-elements-order/bad.tex +11 -0
  175. normik-0.1.0/tests/fixtures/G732-4-elements-order/good.tex +21 -0
  176. normik-0.1.0/tests/fixtures/G732-4-required-element-missing/bad.tex +9 -0
  177. normik-0.1.0/tests/fixtures/G732-4-required-element-missing/good.tex +20 -0
  178. normik-0.1.0/tests/fixtures/G732-5.3.2-abstract-volume-info/bad.tex +7 -0
  179. normik-0.1.0/tests/fixtures/G732-5.3.2-abstract-volume-info/good.tex +9 -0
  180. normik-0.1.0/tests/fixtures/G732-5.3.2.1-keywords-count/bad.tex +3 -0
  181. normik-0.1.0/tests/fixtures/G732-5.3.2.1-keywords-count/good.tex +3 -0
  182. normik-0.1.0/tests/fixtures/G732-5.5.2-terms-opening/bad.tex +5 -0
  183. normik-0.1.0/tests/fixtures/G732-5.5.2-terms-opening/good.tex +8 -0
  184. normik-0.1.0/tests/fixtures/G732-5.6.1-abbreviations-opening/bad.tex +5 -0
  185. normik-0.1.0/tests/fixtures/G732-5.6.1-abbreviations-opening/good.tex +7 -0
  186. normik-0.1.0/tests/fixtures/G732-6.12.1-abstract-volume-inline/bad.tex +11 -0
  187. normik-0.1.0/tests/fixtures/G732-6.12.1-abstract-volume-inline/good.tex +11 -0
  188. normik-0.1.0/tests/fixtures/G732-6.12.2-keywords-final-dot/bad.tex +3 -0
  189. normik-0.1.0/tests/fixtures/G732-6.12.2-keywords-final-dot/good.tex +3 -0
  190. normik-0.1.0/tests/fixtures/G732-6.12.2-keywords-uppercase/bad.tex +3 -0
  191. normik-0.1.0/tests/fixtures/G732-6.12.2-keywords-uppercase/good.tex +3 -0
  192. normik-0.1.0/tests/fixtures/G732-6.14-terms-dash/bad.tex +8 -0
  193. normik-0.1.0/tests/fixtures/G732-6.14-terms-dash/good.tex +12 -0
  194. normik-0.1.0/tests/fixtures/G732-6.15-abbreviations-dash/bad.tex +7 -0
  195. normik-0.1.0/tests/fixtures/G732-6.15-abbreviations-dash/good.tex +9 -0
  196. normik-0.1.0/tests/fixtures/G732-6.16-bibitem-uncited/bad.tex +6 -0
  197. normik-0.1.0/tests/fixtures/G732-6.16-bibitem-uncited/good.tex +6 -0
  198. normik-0.1.0/tests/fixtures/G732-6.16-bibliography-order/bad.tex +6 -0
  199. normik-0.1.0/tests/fixtures/G732-6.16-bibliography-order/good.tex +6 -0
  200. normik-0.1.0/tests/fixtures/G732-6.16-bibtex-order-unverifiable/bad.tex +4 -0
  201. normik-0.1.0/tests/fixtures/G732-6.16-bibtex-order-unverifiable/good.tex +5 -0
  202. normik-0.1.0/tests/fixtures/G732-6.17.2-appendix-no-reference/bad.tex +9 -0
  203. normik-0.1.0/tests/fixtures/G732-6.17.2-appendix-no-reference/good.tex +9 -0
  204. normik-0.1.0/tests/fixtures/G732-6.17.3-appendix-page-break/bad.tex +7 -0
  205. normik-0.1.0/tests/fixtures/G732-6.17.3-appendix-page-break/good.tex +13 -0
  206. normik-0.1.0/tests/fixtures/G732-6.17.4-appendix-letter/bad.tex +5 -0
  207. normik-0.1.0/tests/fixtures/G732-6.17.4-appendix-letter/good.tex +5 -0
  208. normik-0.1.0/tests/fixtures/G732-6.17.4-appendix-sequence/bad.tex +5 -0
  209. normik-0.1.0/tests/fixtures/G732-6.17.4-appendix-sequence/good.tex +5 -0
  210. normik-0.1.0/tests/fixtures/G732-6.17.6-appendix-numbering/bad.tex +8 -0
  211. normik-0.1.0/tests/fixtures/G732-6.17.6-appendix-numbering/good.tex +13 -0
  212. normik-0.1.0/tests/fixtures/G732-6.2.1-structural-heading-case/bad.tex +3 -0
  213. normik-0.1.0/tests/fixtures/G732-6.2.1-structural-heading-case/good.tex +9 -0
  214. normik-0.1.0/tests/fixtures/G732-6.2.1-structural-heading-numbered/bad.tex +5 -0
  215. normik-0.1.0/tests/fixtures/G732-6.2.1-structural-heading-numbered/good.tex +11 -0
  216. normik-0.1.0/tests/fixtures/G732-6.2.2-heading-empty/bad.tex +3 -0
  217. normik-0.1.0/tests/fixtures/G732-6.2.2-heading-empty/good.tex +9 -0
  218. normik-0.1.0/tests/fixtures/G732-6.2.3-heading-dot/bad.tex +5 -0
  219. normik-0.1.0/tests/fixtures/G732-6.2.3-heading-dot/good.tex +7 -0
  220. normik-0.1.0/tests/fixtures/G732-6.2.4-heading-hyphenation/bad.tex +1 -0
  221. normik-0.1.0/tests/fixtures/G732-6.2.4-heading-hyphenation/good.tex +3 -0
  222. normik-0.1.0/tests/fixtures/G732-6.4.1-manual-section-number/bad.tex +5 -0
  223. normik-0.1.0/tests/fixtures/G732-6.4.1-manual-section-number/good.tex +7 -0
  224. normik-0.1.0/tests/fixtures/G732-6.4.5-heading-depth/bad.tex +9 -0
  225. normik-0.1.0/tests/fixtures/G732-6.4.5-heading-depth/good.tex +7 -0
  226. normik-0.1.0/tests/fixtures/G732-6.4.6-enumeration-letters/bad.tex +5 -0
  227. normik-0.1.0/tests/fixtures/G732-6.4.6-enumeration-letters/good.tex +10 -0
  228. normik-0.1.0/tests/fixtures/G732-6.5.1-figure-no-reference/bad.tex +12 -0
  229. normik-0.1.0/tests/fixtures/G732-6.5.1-figure-no-reference/good.tex +14 -0
  230. normik-0.1.0/tests/fixtures/G732-6.5.1-reference-word/bad.tex +2 -0
  231. normik-0.1.0/tests/fixtures/G732-6.5.1-reference-word/good.tex +4 -0
  232. normik-0.1.0/tests/fixtures/G732-6.5.4-numbering-scheme-mixed/bad.tex +17 -0
  233. normik-0.1.0/tests/fixtures/G732-6.5.4-numbering-scheme-mixed/good.tex +19 -0
  234. normik-0.1.0/tests/fixtures/G732-6.5.5-appendix-numbering/bad.tex +13 -0
  235. normik-0.1.0/tests/fixtures/G732-6.5.5-appendix-numbering/good.tex +15 -0
  236. normik-0.1.0/tests/fixtures/G732-6.5.7-caption-dot/bad.tex +17 -0
  237. normik-0.1.0/tests/fixtures/G732-6.5.7-caption-dot/good.tex +33 -0
  238. normik-0.1.0/tests/fixtures/G732-6.5.7-caption-manual-number/bad.tex +17 -0
  239. normik-0.1.0/tests/fixtures/G732-6.5.7-caption-manual-number/good.tex +13 -0
  240. normik-0.1.0/tests/fixtures/G732-6.5.7-caption-position/bad.tex +5 -0
  241. normik-0.1.0/tests/fixtures/G732-6.5.7-caption-position/good.tex +15 -0
  242. normik-0.1.0/tests/fixtures/G732-6.5.8-caption-capital/bad.tex +11 -0
  243. normik-0.1.0/tests/fixtures/G732-6.5.8-caption-capital/good.tex +19 -0
  244. normik-0.1.0/tests/fixtures/G732-6.5.8-caption-hyphenation/bad.tex +5 -0
  245. normik-0.1.0/tests/fixtures/G732-6.5.8-caption-hyphenation/good.tex +13 -0
  246. normik-0.1.0/tests/fixtures/G732-6.6.2-reference-word/bad.tex +2 -0
  247. normik-0.1.0/tests/fixtures/G732-6.6.2-reference-word/good.tex +2 -0
  248. normik-0.1.0/tests/fixtures/G732-6.6.2-table-no-reference/bad.tex +16 -0
  249. normik-0.1.0/tests/fixtures/G732-6.6.2-table-no-reference/good.tex +18 -0
  250. normik-0.1.0/tests/fixtures/G732-6.6.3-caption-capital/bad.tex +7 -0
  251. normik-0.1.0/tests/fixtures/G732-6.6.3-caption-capital/good.tex +7 -0
  252. normik-0.1.0/tests/fixtures/G732-6.6.3-caption-dot/bad.tex +7 -0
  253. normik-0.1.0/tests/fixtures/G732-6.6.3-caption-dot/good.tex +14 -0
  254. normik-0.1.0/tests/fixtures/G732-6.6.3-caption-manual-number/bad.tex +13 -0
  255. normik-0.1.0/tests/fixtures/G732-6.6.3-caption-manual-number/good.tex +15 -0
  256. normik-0.1.0/tests/fixtures/G732-6.6.3-caption-position/bad.tex +7 -0
  257. normik-0.1.0/tests/fixtures/G732-6.6.3-caption-position/good.tex +14 -0
  258. normik-0.1.0/tests/fixtures/G732-6.6.4-appendix-numbering/bad.tex +17 -0
  259. normik-0.1.0/tests/fixtures/G732-6.6.4-appendix-numbering/good.tex +19 -0
  260. normik-0.1.0/tests/fixtures/G732-6.7.2-note-capital/bad.tex +8 -0
  261. normik-0.1.0/tests/fixtures/G732-6.7.2-note-capital/good.tex +8 -0
  262. normik-0.1.0/tests/fixtures/G732-6.7.3-note-dash/bad.tex +3 -0
  263. normik-0.1.0/tests/fixtures/G732-6.7.3-note-dash/good.tex +8 -0
  264. normik-0.1.0/tests/fixtures/G732-6.7.4-footnote-space/bad.tex +1 -0
  265. normik-0.1.0/tests/fixtures/G732-6.7.4-footnote-space/good.tex +3 -0
  266. normik-0.1.0/tests/fixtures/G732-6.8.1-blank-line-around/bad.tex +5 -0
  267. normik-0.1.0/tests/fixtures/G732-6.8.1-blank-line-around/good.tex +15 -0
  268. normik-0.1.0/tests/fixtures/G732-6.8.2-where-colon/bad.tex +8 -0
  269. normik-0.1.0/tests/fixtures/G732-6.8.2-where-colon/good.tex +8 -0
  270. normik-0.1.0/tests/fixtures/G732-6.8.3-formula-no-reference/bad.tex +12 -0
  271. normik-0.1.0/tests/fixtures/G732-6.8.3-formula-no-reference/good.tex +16 -0
  272. normik-0.1.0/tests/fixtures/G732-6.8.4-formula-reference-format/bad.tex +2 -0
  273. normik-0.1.0/tests/fixtures/G732-6.8.4-formula-reference-format/good.tex +4 -0
  274. normik-0.1.0/tests/fixtures/G732-6.8.5-appendix-numbering/bad.tex +11 -0
  275. normik-0.1.0/tests/fixtures/G732-6.8.5-appendix-numbering/good.tex +19 -0
  276. normik-0.1.0/tests/fixtures/G732-6.9.1-cite-unresolved/bad.tex +5 -0
  277. normik-0.1.0/tests/fixtures/G732-6.9.1-cite-unresolved/good.tex +6 -0
  278. normik-0.1.0/tests/fixtures/NK-STYLE-dash/bad.tex +1 -0
  279. normik-0.1.0/tests/fixtures/NK-STYLE-dash/good.tex +15 -0
  280. normik-0.1.0/tests/fixtures/NK-STYLE-initials-nbsp/bad.tex +1 -0
  281. normik-0.1.0/tests/fixtures/NK-STYLE-initials-nbsp/good.tex +9 -0
  282. normik-0.1.0/tests/fixtures/NK-STYLE-particle-nbsp/bad.tex +2 -0
  283. normik-0.1.0/tests/fixtures/NK-STYLE-particle-nbsp/good.tex +6 -0
  284. normik-0.1.0/tests/fixtures/NK-STYLE-preposition-nbsp/bad.tex +2 -0
  285. normik-0.1.0/tests/fixtures/NK-STYLE-preposition-nbsp/good.tex +13 -0
  286. normik-0.1.0/tests/fixtures/NK-STYLE-quotes/bad.tex +1 -0
  287. normik-0.1.0/tests/fixtures/NK-STYLE-quotes/good.tex +5 -0
  288. normik-0.1.0/tests/fixtures/NK-STYLE-reference-nbsp/bad.tex +4 -0
  289. normik-0.1.0/tests/fixtures/NK-STYLE-reference-nbsp/good.tex +6 -0
  290. normik-0.1.0/tests/fixtures/NK-STYLE-unit-nbsp/bad.tex +2 -0
  291. normik-0.1.0/tests/fixtures/NK-STYLE-unit-nbsp/good.tex +6 -0
  292. normik-0.1.0/tests/support.py +77 -0
  293. normik-0.1.0/tests/test_baseline.py +115 -0
  294. normik-0.1.0/tests/test_cli.py +309 -0
  295. normik-0.1.0/tests/test_docs.py +75 -0
  296. normik-0.1.0/tests/test_document.py +56 -0
  297. normik-0.1.0/tests/test_finding.py +54 -0
  298. normik-0.1.0/tests/test_fixer.py +139 -0
  299. normik-0.1.0/tests/test_numbering.py +118 -0
  300. normik-0.1.0/tests/test_parse/test_structure.py +150 -0
  301. normik-0.1.0/tests/test_parse/test_tex.py +140 -0
  302. normik-0.1.0/tests/test_profile.py +184 -0
  303. normik-0.1.0/tests/test_registry.py +126 -0
  304. normik-0.1.0/tests/test_report/conftest.py +31 -0
  305. normik-0.1.0/tests/test_report/helpers.py +33 -0
  306. normik-0.1.0/tests/test_report/test_agent.py +86 -0
  307. normik-0.1.0/tests/test_report/test_human.py +50 -0
  308. normik-0.1.0/tests/test_report/test_json.py +86 -0
  309. normik-0.1.0/tests/test_rule.py +170 -0
  310. normik-0.1.0/tests/test_rules.py +107 -0
  311. normik-0.1.0/tests/test_runner.py +277 -0
  312. normik-0.1.0/tests/test_suppressions.py +102 -0
  313. normik-0.1.0/uv.lock +736 -0
@@ -0,0 +1,50 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # Virtual environments
7
+ .venv/
8
+ .env
9
+ .env.staging
10
+ .env.prod
11
+
12
+ # IDE
13
+ .vscode/
14
+ .idea/
15
+
16
+ # Python tooling
17
+ .poetry/
18
+ .mypy_cache/
19
+ .pytype/
20
+ .pytest_cache/
21
+ .coverage
22
+ htmlcov/
23
+
24
+ # Собранный сайт документации
25
+ site/
26
+
27
+ # Build
28
+ build/
29
+ dist/
30
+ *.egg-info/
31
+
32
+ # OS
33
+ .DS_Store
34
+
35
+ # Docker
36
+ *.log
37
+
38
+ # Local overrides
39
+ *.local.*
40
+ local/
41
+
42
+ # Claude
43
+ .claude/worktrees/
44
+ .claude/agent-memory/
45
+
46
+ # Ruff
47
+ .ruff_cache/
48
+
49
+ # Кэш сборки документации
50
+ .cache/
normik-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Arkadii Artamonov
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
normik-0.1.0/Makefile ADDED
@@ -0,0 +1,25 @@
1
+ .PHONY: lint format test check docs docs-serve
2
+
3
+ package ?= src tests
4
+
5
+ lint:
6
+ uv run ruff check $(package)
7
+ uv run ruff format --check $(package)
8
+ uv run ty check $(package)
9
+
10
+ format:
11
+ uv run ruff check --fix $(package)
12
+ uv run ruff format $(package)
13
+
14
+ test:
15
+ uv run pytest
16
+
17
+ check: lint test
18
+
19
+ docs:
20
+ uv run nk rules docs
21
+ uv run --group docs properdocs build --strict
22
+
23
+ docs-serve:
24
+ uv run nk rules docs
25
+ uv run --group docs properdocs serve
normik-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,140 @@
1
+ Metadata-Version: 2.5
2
+ Name: normik
3
+ Version: 0.1.0
4
+ Summary: Проверка работ на соответствие ГОСТ
5
+ Project-URL: Homepage, https://trum-ok.github.io/normik/
6
+ Project-URL: Documentation, https://trum-ok.github.io/normik/
7
+ Project-URL: Repository, https://github.com/Trum-ok/normik
8
+ Project-URL: Issues, https://github.com/Trum-ok/normik/issues
9
+ Author-email: Arkadii Artamonov <artamarkan@gmail.com>
10
+ License-Expression: MIT
11
+ License-File: LICENSE
12
+ Keywords: gost,latex,linter,гост,нир
13
+ Classifier: Development Status :: 3 - Alpha
14
+ Classifier: Environment :: Console
15
+ Classifier: Intended Audience :: Education
16
+ Classifier: Intended Audience :: Science/Research
17
+ Classifier: Natural Language :: Russian
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Topic :: Text Processing :: Markup :: LaTeX
20
+ Classifier: Typing :: Typed
21
+ Requires-Python: >=3.13.4
22
+ Requires-Dist: rich>=14.0.0
23
+ Requires-Dist: typer>=0.20.0
24
+ Description-Content-Type: text/markdown
25
+
26
+ # normik
27
+
28
+ [![ci](https://github.com/Trum-ok/normik/actions/workflows/ci.yaml/badge.svg)](https://github.com/Trum-ok/normik/actions/workflows/ci.yaml)
29
+
30
+ `nk` — линтер оформления отчёта о НИР по ГОСТ 7.32-2017 для исходников LaTeX.
31
+
32
+ **Документация: <https://trum-ok.github.io/normik/>**
33
+
34
+ Принимает `.tex` и выдаёт список нарушений с указанием пункта стандарта, файла
35
+ и строки.
36
+
37
+ ```bash
38
+ uv run nk check report.tex
39
+ ```
40
+
41
+ ```text
42
+ report.tex
43
+ 1:17 warning G732-6.5.1-reference-word
44
+ Нарушение: В ссылке на иллюстрацию использовано сокращение «рис.».
45
+ Требуется: При ссылке пишут слово «рисунок» полностью и его номер.
46
+ Исправить: Заменить «рис.» на «рисунок» в нужном падеже.
47
+ > 1 | Как показано на рис.~\ref{fig:speed}, зависимость линейная.
48
+ 2 |
49
+ 3 | \begin{figure}[h]
50
+
51
+ 5:5 error G732-6.5.7-caption-dot
52
+ Нарушение: Наименование рисунка заканчивается точкой.
53
+ Требуется: Наименование рисунка приводят с прописной буквы без точки в конце.
54
+ Исправить: \caption{Зависимость скорости от нагрузки}
55
+ 3 | \begin{figure}[h]
56
+ 4 | \includegraphics{plot.png}
57
+ > 5 | \caption{Зависимость скорости от нагрузки.}
58
+ 6 | \label{fig:speed}
59
+ 7 | \end{figure}
60
+
61
+ Итого: 1 error, 1 warning, 0 info.
62
+ Машинный вывод: --format json, вывод для агента: --format agent
63
+ ```
64
+
65
+ ## Установка
66
+
67
+ ```bash
68
+ uv sync
69
+ ```
70
+
71
+ ## Примеры запуска
72
+
73
+ Проверить весь каталог с исходниками:
74
+
75
+ ```bash
76
+ uv run nk check chapters
77
+ ```
78
+
79
+ Получить вывод, который можно скопировать в Claude Code без пояснений:
80
+
81
+ ```bash
82
+ uv run nk check chapters --format agent
83
+ ```
84
+
85
+ Проверить по профилю кафедры, показывая только ошибки:
86
+
87
+ ```bash
88
+ uv run nk check report.tex --profile profiles/example-university.toml --severity error
89
+ ```
90
+
91
+ Починить то, что чинится механически — сначала посмотреть, потом применить:
92
+
93
+ ```bash
94
+ uv run nk check chapters --diff
95
+ uv run nk check chapters --fix
96
+ ```
97
+
98
+ Включить линтер на готовой работе: зафиксировать текущие нарушения снимком
99
+ и дальше видеть только новые:
100
+
101
+ ```bash
102
+ uv run nk check chapters --write-baseline .nk-baseline.json
103
+ uv run nk check chapters --baseline .nk-baseline.json
104
+ ```
105
+
106
+ ## Команды
107
+
108
+ | Команда | Назначение |
109
+ |---|---|
110
+ | `nk check PATH...` | проверить исходники |
111
+ | `nk rules list` | перечень правил |
112
+ | `nk rules show RULE_ID` | подробности по правилу |
113
+ | `nk rules docs` | пересобрать страницы правил в `docs/rules/` |
114
+ | `nk profile show` | итоговый набор правил после применения профиля |
115
+
116
+ Ключи `check`, коды возврата и форматы вывода — на странице
117
+ [Использование](https://trum-ok.github.io/normik/usage/).
118
+
119
+ ## Документация
120
+
121
+ | Страница | О чём |
122
+ |---|---|
123
+ | [Использование](https://trum-ok.github.io/normik/usage/) | команды, ключи, форматы вывода, автоисправление, подавления, снимок |
124
+ | [Профили](https://trum-ok.github.io/normik/profiles/) | подстройка набора правил под кафедру |
125
+ | [Правила](https://trum-ok.github.io/normik/rules/) | страница на каждое правило: почему, пример, настройка |
126
+ | [Интеграции](https://trum-ok.github.io/normik/integrations/) | CI, хук, передача вывода агенту |
127
+ | [Как добавить правило](https://trum-ok.github.io/normik/contributing/) | руководство для соавторов |
128
+
129
+ Исходники документации — в каталоге
130
+ [`docs/`](https://github.com/Trum-ok/normik/tree/master/docs); каталог
131
+ [`docs/rules/`](https://github.com/Trum-ok/normik/tree/master/docs/rules)
132
+ генерируется командой `uv run nk rules docs` и руками не редактируется.
133
+
134
+ ## Разработка
135
+
136
+ ```bash
137
+ make check # ruff, ty, pytest
138
+ make docs # перегенерировать правила и собрать сайт
139
+ make docs-serve # локальный просмотр на http://127.0.0.1:8000
140
+ ```
normik-0.1.0/README.md ADDED
@@ -0,0 +1,115 @@
1
+ # normik
2
+
3
+ [![ci](https://github.com/Trum-ok/normik/actions/workflows/ci.yaml/badge.svg)](https://github.com/Trum-ok/normik/actions/workflows/ci.yaml)
4
+
5
+ `nk` — линтер оформления отчёта о НИР по ГОСТ 7.32-2017 для исходников LaTeX.
6
+
7
+ **Документация: <https://trum-ok.github.io/normik/>**
8
+
9
+ Принимает `.tex` и выдаёт список нарушений с указанием пункта стандарта, файла
10
+ и строки.
11
+
12
+ ```bash
13
+ uv run nk check report.tex
14
+ ```
15
+
16
+ ```text
17
+ report.tex
18
+ 1:17 warning G732-6.5.1-reference-word
19
+ Нарушение: В ссылке на иллюстрацию использовано сокращение «рис.».
20
+ Требуется: При ссылке пишут слово «рисунок» полностью и его номер.
21
+ Исправить: Заменить «рис.» на «рисунок» в нужном падеже.
22
+ > 1 | Как показано на рис.~\ref{fig:speed}, зависимость линейная.
23
+ 2 |
24
+ 3 | \begin{figure}[h]
25
+
26
+ 5:5 error G732-6.5.7-caption-dot
27
+ Нарушение: Наименование рисунка заканчивается точкой.
28
+ Требуется: Наименование рисунка приводят с прописной буквы без точки в конце.
29
+ Исправить: \caption{Зависимость скорости от нагрузки}
30
+ 3 | \begin{figure}[h]
31
+ 4 | \includegraphics{plot.png}
32
+ > 5 | \caption{Зависимость скорости от нагрузки.}
33
+ 6 | \label{fig:speed}
34
+ 7 | \end{figure}
35
+
36
+ Итого: 1 error, 1 warning, 0 info.
37
+ Машинный вывод: --format json, вывод для агента: --format agent
38
+ ```
39
+
40
+ ## Установка
41
+
42
+ ```bash
43
+ uv sync
44
+ ```
45
+
46
+ ## Примеры запуска
47
+
48
+ Проверить весь каталог с исходниками:
49
+
50
+ ```bash
51
+ uv run nk check chapters
52
+ ```
53
+
54
+ Получить вывод, который можно скопировать в Claude Code без пояснений:
55
+
56
+ ```bash
57
+ uv run nk check chapters --format agent
58
+ ```
59
+
60
+ Проверить по профилю кафедры, показывая только ошибки:
61
+
62
+ ```bash
63
+ uv run nk check report.tex --profile profiles/example-university.toml --severity error
64
+ ```
65
+
66
+ Починить то, что чинится механически — сначала посмотреть, потом применить:
67
+
68
+ ```bash
69
+ uv run nk check chapters --diff
70
+ uv run nk check chapters --fix
71
+ ```
72
+
73
+ Включить линтер на готовой работе: зафиксировать текущие нарушения снимком
74
+ и дальше видеть только новые:
75
+
76
+ ```bash
77
+ uv run nk check chapters --write-baseline .nk-baseline.json
78
+ uv run nk check chapters --baseline .nk-baseline.json
79
+ ```
80
+
81
+ ## Команды
82
+
83
+ | Команда | Назначение |
84
+ |---|---|
85
+ | `nk check PATH...` | проверить исходники |
86
+ | `nk rules list` | перечень правил |
87
+ | `nk rules show RULE_ID` | подробности по правилу |
88
+ | `nk rules docs` | пересобрать страницы правил в `docs/rules/` |
89
+ | `nk profile show` | итоговый набор правил после применения профиля |
90
+
91
+ Ключи `check`, коды возврата и форматы вывода — на странице
92
+ [Использование](https://trum-ok.github.io/normik/usage/).
93
+
94
+ ## Документация
95
+
96
+ | Страница | О чём |
97
+ |---|---|
98
+ | [Использование](https://trum-ok.github.io/normik/usage/) | команды, ключи, форматы вывода, автоисправление, подавления, снимок |
99
+ | [Профили](https://trum-ok.github.io/normik/profiles/) | подстройка набора правил под кафедру |
100
+ | [Правила](https://trum-ok.github.io/normik/rules/) | страница на каждое правило: почему, пример, настройка |
101
+ | [Интеграции](https://trum-ok.github.io/normik/integrations/) | CI, хук, передача вывода агенту |
102
+ | [Как добавить правило](https://trum-ok.github.io/normik/contributing/) | руководство для соавторов |
103
+
104
+ Исходники документации — в каталоге
105
+ [`docs/`](https://github.com/Trum-ok/normik/tree/master/docs); каталог
106
+ [`docs/rules/`](https://github.com/Trum-ok/normik/tree/master/docs/rules)
107
+ генерируется командой `uv run nk rules docs` и руками не редактируется.
108
+
109
+ ## Разработка
110
+
111
+ ```bash
112
+ make check # ruff, ty, pytest
113
+ make docs # перегенерировать правила и собрать сайт
114
+ make docs-serve # локальный просмотр на http://127.0.0.1:8000
115
+ ```
@@ -0,0 +1,328 @@
1
+ # Как добавить правило
2
+
3
+ Добавление правила — это один новый файл в `src/nk/rules/` и один каталог
4
+ в `tests/fixtures/`. Ничего больше править не нужно: реестр находит правило
5
+ по декоратору, тесты — по имени каталога.
6
+
7
+ ## Рецепт
8
+
9
+ 1. Найти пункт ГОСТ 7.32-2017, который проверяете, и выписать его номер.
10
+ 2. Придумать идентификатор: `G732-<пункт>-<мнемоника>`, например
11
+ `G732-6.5.7-caption-dot`. Мнемоника нужна потому, что на один пункт
12
+ приходится несколько независимых проверок.
13
+ 3. Создать файл в подпапке по разделу стандарта:
14
+ - `rules/structure/` — разделы 4 и 5: состав и требования к структурным элементам;
15
+ - `rules/layout/` — подразделы 6.2 и 6.4: построение отчёта и нумерация рубрик;
16
+ - `rules/elements/` — подразделы 6.5–6.9, 6.12, 6.16, 6.17: иллюстрации,
17
+ таблицы, формулы, ссылки, реферат, источники, приложения.
18
+ 4. Создать `tests/fixtures/<id>/bad.tex` и `tests/fixtures/<id>/good.tex`.
19
+ 5. Написать докстринг правила — из него собирается страница документации.
20
+ 6. Прогнать `make check` и `uv run nk rules docs`.
21
+
22
+ ## Полный пример
23
+
24
+ `src/nk/rules/elements/figure_caption_dot.py`:
25
+
26
+ ```python
27
+ """Точка в конце наименования рисунка."""
28
+
29
+ from collections.abc import Iterable
30
+
31
+ from nk.core.document import Document
32
+ from nk.core.finding import Finding, Severity
33
+ from nk.core.rule import rule
34
+ from nk.rules._shared import FIGURE_ENVIRONMENTS, caption_text, captions, one_line
35
+
36
+
37
+ @rule(
38
+ id="G732-6.5.7-caption-dot",
39
+ clause="6.5.7",
40
+ severity=Severity.ERROR,
41
+ title="Наименование рисунка заканчивается точкой",
42
+ )
43
+ def figure_caption_dot(doc: Document) -> Iterable[Finding]:
44
+ r"""Проверяет наименование рисунка на точку в конце. Точка внутри
45
+ наименования нарушением не является.
46
+
47
+ ## Почему это нарушение
48
+
49
+ Наименование рисунка приводят с прописной буквы без точки в конце.
50
+
51
+ ## Как исправить
52
+
53
+ Убрать точку в конце `\caption`.
54
+ """
55
+ for environment in doc.structure.find_environments(*FIGURE_ENVIRONMENTS):
56
+ for command in captions(environment):
57
+ text = caption_text(command)
58
+ if not text.endswith("."):
59
+ continue
60
+ yield figure_caption_dot.finding(
61
+ doc,
62
+ command.span,
63
+ message="Наименование рисунка заканчивается точкой.",
64
+ requirement="Наименование рисунка приводят с прописной буквы без точки в конце.",
65
+ suggestion=f"\\{command.name}{{{one_line(text[:-1])}}}",
66
+ col=command.col,
67
+ )
68
+ ```
69
+
70
+ Разбор по частям:
71
+
72
+ - **Декоратор** объявляет метаданные один раз. `id`, `clause`, `severity` и `title`
73
+ больше нигде повторять не нужно.
74
+ - **Докстринг** — источник страницы правила в документации, см.
75
+ [ниже](#описание-правила).
76
+ - **Сигнатура** фиксирована протоколом `Rule` и проверяется `ty`: правило принимает
77
+ `Document` и возвращает `Iterable[Finding]`.
78
+ - **Правило — чистая функция.** Никакого ввода-вывода, никакого состояния между
79
+ вызовами, никакого чтения файлов помимо того, что уже в `Document`.
80
+ - **`figure_caption_dot.finding(...)`** — обращение правила к самому себе по имени.
81
+ Оно разрешается в момент вызова, когда декоратор уже отработал. Метод сам
82
+ проставляет `rule_id`, `clause`, `severity` с учётом профиля, `excerpt` и `context`.
83
+ - **Позиция.** Первым позиционным аргументом идёт `Line` или `Span`; `col`
84
+ указывается отдельно, если известна колонка.
85
+
86
+ ## Что видит правило
87
+
88
+ `Document` хранит два представления одного исходника.
89
+
90
+ **Построчное** — для проверок, где важен текст строки:
91
+
92
+ ```python
93
+ for line in doc.iter_lines():
94
+ line.raw # исходная строка
95
+ line.stripped # без комментария (% с учётом экранированного \%)
96
+ line.lineno # номер строки, с единицы
97
+ line.path # файл, а не файл-агрегатор
98
+ ```
99
+
100
+ **Структурное** — для проверок, где важна вложенность:
101
+
102
+ ```python
103
+ doc.structure.find_environments("figure", "table") # окружения по именам
104
+ doc.structure.find_commands("caption", "label") # команды по именам
105
+ doc.structure.enclosing(path, lineno) # самое внутреннее окружение
106
+ environment.children # вложенные окружения
107
+ environment.commands # команды непосредственно внутри
108
+ environment.all_commands() # включая вложенные окружения
109
+ ```
110
+
111
+ Готовые помощники — в `src/nk/rules/_shared.py`: наборы имён окружений, извлечение
112
+ текста подписи, метки и ссылки, наименования структурных элементов. Прежде чем
113
+ писать свой разбор, посмотрите, нет ли там нужного.
114
+
115
+ ## Описание правила
116
+
117
+ Докстринг функции правила — источник его страницы в документации. Он пишется
118
+ на Markdown и попадает на страницу как есть, поэтому заголовки в нём — второго
119
+ уровня.
120
+
121
+ ```python
122
+ def figure_caption_dot(doc: Document) -> Iterable[Finding]:
123
+ """Проверяет наименование рисунка на точку в конце. Точка внутри
124
+ наименования нарушением не является.
125
+
126
+ ## Почему это нарушение
127
+
128
+ Наименование рисунка приводят с прописной буквы без точки в конце: оно
129
+ образует с номером единую подпись, а не предложение.
130
+
131
+ ## Как исправить
132
+
133
+ Убрать точку в конце `\caption`.
134
+ """
135
+ ```
136
+
137
+ - **Первый абзац** — что правило ищет и чего не трогает. Сюда же выносятся
138
+ известные ограничения: случаи, в которых по исходникам решить нельзя.
139
+ - **«Почему это нарушение»** — смысл требования своими словами. Не «так
140
+ написано в стандарте», а что ломается, если сделать иначе.
141
+ - **«Как исправить»** — что сделать в исходнике.
142
+
143
+ Если в докстринге есть обратная косая — а она есть, как только появляется
144
+ пример LaTeX, — объявляйте его сырым: `r"""`.
145
+
146
+ Пример нарушения и правильного оформления на страницу подставляются из фикстур
147
+ правила, писать их отдельно не нужно. Показывается файл целиком; если он
148
+ длинный, покажите его часть, обрамив её метками:
149
+
150
+ ```latex
151
+ % DOC-BEGIN
152
+ \begin{figure}
153
+ \includegraphics{img/setup.png}
154
+ \caption{Схема экспериментальной установки}
155
+ \end{figure}
156
+ % DOC-END
157
+ ```
158
+
159
+ ## Правка для `--fix`
160
+
161
+ Если исправление однозначно, правило может объявить машинную правку — тогда
162
+ `nk check --fix` применит её сам. Частый случай: `suggestion` и есть готовая
163
+ замена, тогда достаточно указать заменяемый регион:
164
+
165
+ ```python
166
+ yield figure_caption_dot.finding(
167
+ doc,
168
+ command.span,
169
+ message="Наименование рисунка заканчивается точкой.",
170
+ requirement="Наименование рисунка приводят с прописной буквы без точки в конце.",
171
+ suggestion=f"\\{command.name}{{{one_line(text[:-1])}}}",
172
+ col=command.col,
173
+ fix=command.region, # заменить команду целиком текстом suggestion
174
+ )
175
+ ```
176
+
177
+ `command.region` — точные границы команды вместе с аргументами, включая
178
+ многострочные. Правило с правками объявляется с `fixable=True`; тест сверяет
179
+ объявление с действительностью, поэтому соврать не получится.
180
+
181
+ Когда заменяемый текст отличается от подсказки — например подсказка говорит
182
+ «убрать двоеточие», а заменить нужно один символ на пустую строку, — передаётся
183
+ готовый `Fix`:
184
+
185
+ ```python
186
+ fix=Fix(Region.in_line(line.path, line.lineno, colon, colon + 1), "")
187
+ ```
188
+
189
+ Точный кусок исходника под регионом даёт `doc.slice(region)` — им удобно строить
190
+ замену «то же самое, но без чего-то»:
191
+
192
+ ```python
193
+ fix=Fix(command.region, doc.slice(command.region).replace("\\-", ""))
194
+ ```
195
+
196
+ **Правку объявляют, только когда замена однозначна.** «Перенести подпись ниже
197
+ рисунка», «добавить ссылку в текст», «заменить «рис.» на «рисунок»» правками не
198
+ являются: в первых двух нужно решение о структуре, в третьем — согласование
199
+ падежа. Такие правила ограничиваются `suggestion`.
200
+
201
+ Тест на фикстурах проверяет правки сам: применяет их к `bad.tex` в несколько
202
+ проходов и требует, чтобы находки с правками исчезли и новых не появилось.
203
+
204
+ ## Требования к тексту находки
205
+
206
+ Это самая частая точка деградации, поэтому требование проверяется на ревью.
207
+ Находка должна быть самодостаточна: по ней нарушение исправляется руками или
208
+ передачей вывода `--format agent` в Claude Code, без дополнительных пояснений.
209
+ Читающий не обязан открывать ГОСТ или `docs/RULES.md`.
210
+
211
+ - **`message`** — что не так, одной фразой, про конкретное место.
212
+ Плохо: `Нарушение п. 6.5.7`. Хорошо: `Наименование рисунка заканчивается точкой.`
213
+ - **`requirement`** — что требуется, **своими словами**. Цитировать стандарт нельзя:
214
+ ГОСТ 7.32-2017 не является общественным достоянием, и его текста в репозитории нет.
215
+ Плохо: `См. п. 6.5.7`. Хорошо: `Наименование рисунка приводят с прописной буквы
216
+ без точки в конце.`
217
+ - **`suggestion`** — конкретное действие или готовый фрагмент на замену.
218
+ Плохо: `Исправьте оформление`. Хорошо: `\caption{Схема экспериментальной установки}`.
219
+
220
+ `suggestion` обязателен. Если исправление принципиально неоднозначно, объявите
221
+ правило с `allow_missing_suggestion=True` — тест это проверяет.
222
+
223
+ ## Фикстуры
224
+
225
+ `bad.tex` — файл с нарушениями. Каждая строка, где правило обязано сработать,
226
+ помечается комментарием `% EXPECT`:
227
+
228
+ ```latex
229
+ \begin{figure}
230
+ \includegraphics{img/setup.png}
231
+ \caption{Схема экспериментальной установки.} % EXPECT
232
+ \end{figure}
233
+ ```
234
+
235
+ Сопоставление строгое в обе стороны: множество строк с находками должно совпадать
236
+ с множеством помеченных строк. Лишняя находка и пропущенная пометка одинаково
237
+ роняют тест.
238
+
239
+ Если конструкция занимает несколько строк, пометка ставится на ту строку, куда
240
+ указывает находка, — то есть на начало команды:
241
+
242
+ ```latex
243
+ \caption{Зависимость погрешности от числа % EXPECT
244
+ измерений.}
245
+ ```
246
+
247
+ Комментарий вырезается парсером и на разбор не влияет.
248
+
249
+ `good.tex` — файл, на котором правило не должно сработать ни разу. Это не формальность:
250
+ именно сюда складываются пограничные случаи, на которых легко получить ложное
251
+ срабатывание. Хороший `good.tex` содержит конструкции, похожие на нарушение, но
252
+ нарушением не являющиеся — точку внутри наименования, подпись таблицы там, где
253
+ правило проверяет рисунки, многозначное число в начале заголовка.
254
+
255
+ Оба файла обязаны разбираться парсером без замечаний.
256
+
257
+ ## Параметры
258
+
259
+ Если у правила есть порог, который кафедра захочет изменить, объявите его
260
+ параметром со значением по умолчанию:
261
+
262
+ ```python
263
+ @rule(
264
+ id="G732-5.3.2.1-keywords-count",
265
+ clause="5.3.2.1",
266
+ severity=Severity.ERROR,
267
+ title="Число ключевых слов вне допустимого диапазона",
268
+ params={"keywords_min": 5, "keywords_max": 15},
269
+ )
270
+ def keywords_count(doc: Document) -> Iterable[Finding]:
271
+ params = keywords_count.params(doc)
272
+ lower = int(params["keywords_min"])
273
+ ```
274
+
275
+ Профиль переопределяет параметр так:
276
+
277
+ ```toml
278
+ [rules."G732-5.3.2.1-keywords-count".params]
279
+ keywords_max = 20
280
+ ```
281
+
282
+ ## Проверки
283
+
284
+ ```bash
285
+ make check
286
+ ```
287
+
288
+ Прогоняет `ruff check`, `ruff format --check`, `ty check` и `pytest`. Всё должно
289
+ проходить полностью. Отдельно:
290
+
291
+ ```bash
292
+ make lint
293
+ make test
294
+ make format
295
+ ```
296
+
297
+ ## Что проверяет тест автоматически
298
+
299
+ - правило сработало на `bad.tex` ровно на помеченных строках;
300
+ - правило не сработало на `good.tex` ни разу;
301
+ - у каждой находки непустые `message`, `requirement` и `context`;
302
+ - `clause` находки совпадает с объявленным в декораторе;
303
+ - `suggestion` есть, если правило не объявлено с `allow_missing_suggestion=True`;
304
+ - у каждого правила есть каталог фикстур, у каждого каталога — правило.
305
+
306
+ Последние два пункта не дают репозиторию расползтись, поэтому обойти их нельзя.
307
+
308
+ ## После добавления правила
309
+
310
+ Перегенерируйте страницы документации:
311
+
312
+ ```bash
313
+ uv run nk rules docs
314
+ ```
315
+
316
+ Каталог `docs/rules/` руками не редактируется: он собирается из реестра, а тест
317
+ проверяет, что записанное на диске совпадает со сгенерированным.
318
+
319
+ ## Чего в правилах не делаем
320
+
321
+ - Не вызываем LLM: инструмент детерминирован.
322
+ - Не читаем PDF: источник данных — только исходники `.tex`. Проверки, требующие
323
+ скомпилированного документа (поля, гарнитуры, кегль, колонцифры), в область
324
+ видимости не входят.
325
+ - Не цитируем ГОСТ 7.32-2017 ни в коде, ни в тестах, ни в документации.
326
+ Допустимы только номера пунктов и собственные краткие формулировки.
327
+ - Не правим файлы отчёта наугад. Правка объявляется только там, где замена
328
+ однозначна; во всех остальных случаях правило ограничивается `suggestion`.