rubocop-prose 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. checksums.yaml +4 -4
  2. data/AGENTS.md +10 -2
  3. data/CHANGELOG.md +20 -0
  4. data/CONTRIBUTING.md +17 -7
  5. data/DESIGN.md +13 -5
  6. data/README.md +37 -20
  7. data/RELEASING.md +8 -8
  8. data/UPGRADING.md +16 -0
  9. data/assets/README.md +1 -1
  10. data/config/cops/markdown/code_block_style.yml +1 -1
  11. data/config/cops/markdown/emphasis_style.yml +4 -0
  12. data/config/cops/markdown/fence_style.yml +1 -1
  13. data/config/cops/markdown/first_heading.yml +1 -2
  14. data/config/cops/markdown/front_matter_keys.yml +5 -0
  15. data/config/cops/markdown/front_matter_syntax.yml +3 -0
  16. data/config/cops/markdown/hard_break_style.yml +3 -0
  17. data/config/cops/markdown/hard_tabs.yml +1 -1
  18. data/config/cops/markdown/heading_capitalization.yml +31 -0
  19. data/config/cops/markdown/heading_style.yml +1 -1
  20. data/config/cops/markdown/{syntax.yml → kramdown_syntax.yml} +1 -1
  21. data/config/cops/markdown/list_item_capitalization.yml +32 -0
  22. data/config/cops/markdown/list_item_punctuation.yml +13 -0
  23. data/config/cops/markdown/list_marker_spacing.yml +6 -5
  24. data/config/cops/markdown/ordered_list_numbering.yml +1 -1
  25. data/config/cops/markdown/reference_definitions.yml +2 -3
  26. data/config/cops/markdown/strong_style.yml +4 -0
  27. data/config/cops/markdown/structural_line_length.yml +1 -4
  28. data/config/cops/markdown/table_pipes.yml +6 -0
  29. data/config/cops/markdown/table_spacing.yml +4 -0
  30. data/config/cops/markdown/trailing_whitespace.yml +1 -0
  31. data/config/cops/markdown/unordered_list_marker.yml +1 -1
  32. data/config/cops/prose/sentence_capitalization.yml +9 -0
  33. data/config/cops/text/hard_tabs.yml +3 -0
  34. data/config/cops/text/proper_names.yml +4 -0
  35. data/config/obsoletion.yml +45 -0
  36. data/docs/cops/markdown/blockquote_spacing.md +2 -0
  37. data/docs/cops/markdown/code_block_style.md +11 -2
  38. data/docs/cops/markdown/duplicate_headings.md +51 -0
  39. data/docs/cops/markdown/emphasis_style.md +5 -1
  40. data/docs/cops/markdown/fence_style.md +11 -2
  41. data/docs/cops/markdown/first_heading.md +29 -3
  42. data/docs/cops/markdown/front_matter_keys.md +57 -0
  43. data/docs/cops/markdown/front_matter_syntax.md +32 -0
  44. data/docs/cops/markdown/hard_break_style.md +10 -5
  45. data/docs/cops/markdown/hard_tabs.md +3 -3
  46. data/docs/cops/markdown/heading_capitalization.md +176 -0
  47. data/docs/cops/markdown/heading_style.md +11 -2
  48. data/docs/cops/markdown/{syntax.md → kramdown_syntax.md} +6 -2
  49. data/docs/cops/markdown/list_item_capitalization.md +115 -0
  50. data/docs/cops/markdown/list_item_punctuation.md +70 -0
  51. data/docs/cops/markdown/list_marker_spacing.md +57 -7
  52. data/docs/cops/markdown/ordered_list_numbering.md +10 -1
  53. data/docs/cops/markdown/proper_names.md +3 -3
  54. data/docs/cops/markdown/raw_html.md +99 -6
  55. data/docs/cops/markdown/reference_definitions.md +30 -4
  56. data/docs/cops/markdown/strong_style.md +5 -1
  57. data/docs/cops/markdown/structural_line_length.md +31 -7
  58. data/docs/cops/markdown/table_pipes.md +7 -1
  59. data/docs/cops/markdown/table_spacing.md +5 -1
  60. data/docs/cops/markdown/thematic_break_style.md +2 -0
  61. data/docs/cops/markdown/trailing_whitespace.md +29 -0
  62. data/docs/cops/markdown/unordered_list_marker.md +11 -2
  63. data/docs/cops/prose/multiple_blank_lines.md +2 -0
  64. data/docs/cops/prose/sentence_capitalization.md +46 -0
  65. data/docs/cops/text/hard_tabs.md +28 -0
  66. data/docs/cops/text/proper_names.md +36 -0
  67. data/docs/settings/files.md +1 -1
  68. data/docs/settings/front_matter.md +2 -2
  69. data/lib/rubocop/cop/markdown/code_block_style.rb +1 -1
  70. data/lib/rubocop/cop/markdown/code_span_spacing.rb +2 -2
  71. data/lib/rubocop/cop/markdown/duplicate_headings.rb +55 -5
  72. data/lib/rubocop/cop/markdown/emphasis_spacing.rb +1 -1
  73. data/lib/rubocop/cop/markdown/emphasis_style.rb +7 -0
  74. data/lib/rubocop/cop/markdown/fence_style.rb +1 -1
  75. data/lib/rubocop/cop/markdown/first_heading.rb +13 -2
  76. data/lib/rubocop/cop/markdown/front_matter_keys.rb +70 -0
  77. data/lib/rubocop/cop/markdown/front_matter_syntax.rb +26 -0
  78. data/lib/rubocop/cop/markdown/hard_tabs.rb +11 -7
  79. data/lib/rubocop/cop/markdown/heading_capitalization.rb +43 -0
  80. data/lib/rubocop/cop/markdown/heading_style.rb +1 -1
  81. data/lib/rubocop/cop/markdown/{syntax.rb → kramdown_syntax.rb} +1 -1
  82. data/lib/rubocop/cop/markdown/list_item_capitalization.rb +90 -0
  83. data/lib/rubocop/cop/markdown/list_item_punctuation.rb +64 -0
  84. data/lib/rubocop/cop/markdown/list_marker_spacing.rb +13 -4
  85. data/lib/rubocop/cop/markdown/ordered_list_numbering.rb +1 -1
  86. data/lib/rubocop/cop/markdown/outside_spacing.rb +6 -6
  87. data/lib/rubocop/cop/markdown/proper_names.rb +20 -31
  88. data/lib/rubocop/cop/markdown/raw_html.rb +62 -7
  89. data/lib/rubocop/cop/markdown/reference_definitions.rb +15 -5
  90. data/lib/rubocop/cop/markdown/strong_style.rb +7 -0
  91. data/lib/rubocop/cop/markdown/structural_line_length.rb +7 -6
  92. data/lib/rubocop/cop/markdown/table_blank_lines.rb +1 -1
  93. data/lib/rubocop/cop/markdown/table_columns.rb +1 -1
  94. data/lib/rubocop/cop/markdown/table_pipes.rb +8 -1
  95. data/lib/rubocop/cop/markdown/table_spacing.rb +8 -1
  96. data/lib/rubocop/cop/markdown/thematic_break_style.rb +4 -0
  97. data/lib/rubocop/cop/markdown/trailing_whitespace.rb +6 -0
  98. data/lib/rubocop/cop/markdown/unordered_list_marker.rb +1 -1
  99. data/lib/rubocop/cop/prose/sentence_capitalization.rb +100 -0
  100. data/lib/rubocop/cop/text/hard_tabs.rb +21 -0
  101. data/lib/rubocop/cop/text/proper_names.rb +20 -0
  102. data/lib/rubocop/prose/capitalization_rules.rb +189 -0
  103. data/lib/rubocop/prose/inline_rules.rb +17 -10
  104. data/lib/rubocop/prose/link_rules.rb +34 -13
  105. data/lib/rubocop/prose/markdown_document.rb +30 -8
  106. data/lib/rubocop/prose/proper_name_rules.rb +31 -0
  107. data/lib/rubocop/prose/structural_blank_lines.rb +1 -1
  108. data/lib/rubocop/prose/table_rules.rb +1 -1
  109. data/lib/rubocop/prose/version.rb +1 -1
  110. data/lib/rubocop-prose.rb +3 -0
  111. metadata +32 -4
@@ -1,8 +1,7 @@
1
1
  Markdown/ReferenceDefinitions:
2
2
  Description: 'Report unused and duplicate reference definitions.'
3
3
  Enabled: true
4
- CheckUnused: false
5
- CheckDuplicates: true
6
- AllowIdenticalDuplicates: false
4
+ Duplicates: forbid
5
+ Unused: false
7
6
  IgnoredDefinitions:
8
7
  - '//'
@@ -2,3 +2,7 @@ Markdown/StrongStyle:
2
2
  Description: 'Use a configured or consistent strong delimiter.'
3
3
  Enabled: true
4
4
  Style: consistent
5
+ SupportedStyles:
6
+ - consistent
7
+ - asterisk
8
+ - underscore
@@ -1,9 +1,6 @@
1
1
  Markdown/StructuralLineLength:
2
2
  Description: Report overlong heading, code-block, and table source lines without reflowing them.
3
3
  Enabled: false
4
- Max: 80
5
- Headings: true
6
- HeadingMax:
4
+ Headings: 80
7
5
  CodeBlocks: false
8
- CodeBlockMax:
9
6
  Tables: false
@@ -2,3 +2,9 @@ Markdown/TablePipes:
2
2
  Description: 'Use consistent leading and trailing table pipes.'
3
3
  Enabled: true
4
4
  Style: consistent
5
+ SupportedStyles:
6
+ - consistent
7
+ - leading_and_trailing
8
+ - leading_only
9
+ - trailing_only
10
+ - no_leading_or_trailing
@@ -2,3 +2,7 @@ Markdown/TableSpacing:
2
2
  Description: 'Use configured spacing around table cell content.'
3
3
  Enabled: true
4
4
  Style: aligned
5
+ SupportedStyles:
6
+ - aligned
7
+ - compact
8
+ - tight
@@ -2,4 +2,5 @@ Markdown/TrailingWhitespace:
2
2
  Description: 'Remove trailing whitespace while retaining Markdown hard breaks.'
3
3
  Enabled: true
4
4
  HardBreakSpaces: 2
5
+ AllowEmptyBlockquoteSpace: false
5
6
  CodeBlocks: false
@@ -1,7 +1,7 @@
1
1
  Markdown/UnorderedListMarker:
2
2
  Description: Use a configured or consistent unordered list marker.
3
3
  Enabled: true
4
- EnforcedStyle: consistent
4
+ Style: consistent
5
5
  SupportedStyles:
6
6
  - consistent
7
7
  - asterisk
@@ -0,0 +1,9 @@
1
+ Prose/SentenceCapitalization:
2
+ Description: Check capitalization of paragraph openings without rewriting literal terms.
3
+ Enabled: false
4
+ SafeAutoCorrect: false
5
+ Style: initial_capital
6
+ PreserveMixedCase: true
7
+ IgnoredWords: []
8
+ SupportedStyles:
9
+ - initial_capital
@@ -0,0 +1,3 @@
1
+ Text/HardTabs:
2
+ Description: 'Report hard tabs on every plain-text line without correcting them.'
3
+ Enabled: true
@@ -0,0 +1,4 @@
1
+ Text/ProperNames:
2
+ Description: 'Use configured capitalization for proper names in literal text.'
3
+ Enabled: false
4
+ Names: []
@@ -0,0 +1,45 @@
1
+ changed_parameters:
2
+ - cops: Markdown/StructuralLineLength
3
+ parameters:
4
+ - Max
5
+ - HeadingMax
6
+ - CodeBlockMax
7
+ reason: >-
8
+ Replace the shared limit and enable/value pairs with a positive integer width (or false) for each element:
9
+ Headings, CodeBlocks, and Tables. For example, use Headings: 120, CodeBlocks: false, and Tables: false.
10
+ - cops: Markdown/ReferenceDefinitions
11
+ parameters:
12
+ - CheckDuplicates
13
+ - AllowIdenticalDuplicates
14
+ reason: >-
15
+ Replace CheckDuplicates and AllowIdenticalDuplicates with Duplicates.
16
+ CheckDuplicates: false maps to Duplicates: allow, regardless of AllowIdenticalDuplicates.
17
+ CheckDuplicates: true (or omitted) with AllowIdenticalDuplicates: true maps to Duplicates: allow_identical.
18
+ Otherwise, the default maps to Duplicates: forbid.
19
+ Remove both legacy keys; combinations are not translated automatically.
20
+ - cops: Markdown/ReferenceDefinitions
21
+ parameters: CheckUnused
22
+ reason: 'Replace CheckUnused with Unused, preserving its boolean value (default: false).'
23
+ - cops:
24
+ - Markdown/CodeBlockStyle
25
+ - Markdown/FenceStyle
26
+ - Markdown/HeadingStyle
27
+ - Markdown/OrderedListNumbering
28
+ - Markdown/UnorderedListMarker
29
+ parameters: EnforcedStyle
30
+ alternative: Style
31
+ - cops: Markdown/ListMarkerSpacing
32
+ parameters:
33
+ - OrderedSingle
34
+ - OrderedMulti
35
+ - UnorderedSingle
36
+ - UnorderedMulti
37
+ reason: >-
38
+ Use Style (fixed or aligned) and MinSpaces instead.
39
+ These settings now apply to all list items, regardless of marker type or multiline content.
40
+ Different legacy spacing values cannot be preserved by a single policy; choose the desired shared spacing.
41
+ - cops: Markdown/FirstHeading
42
+ parameters:
43
+ - TitleKey
44
+ reason: >-
45
+ Set FrontMatterTitle to the desired metadata key, or false to disable the front-matter title exemption.
@@ -4,6 +4,8 @@ Requires one space after each nonempty blockquote marker, including nested marke
4
4
 
5
5
  Empty quote lines and indentation inside quoted lists and code are preserved. Missing separators are autocorrected only when reparsing confirms the Markdown structure is unchanged; otherwise, the offense is report-only.
6
6
 
7
+ [Markdown/TrailingWhitespace](trailing_whitespace.md) independently trims trailing whitespace on empty quote lines; its opt-in `AllowEmptyBlockquoteSpace` permits one trailing space.
8
+
7
9
  ## Configuration
8
10
 
9
11
  ```yaml
@@ -9,7 +9,7 @@ Autocorrection is limited to standalone root-level blocks with blank boundaries.
9
9
  ```yaml
10
10
  Markdown/CodeBlockStyle:
11
11
  Enabled: false
12
- EnforcedStyle: consistent # consistent, fenced or indented.
12
+ Style: consistent # consistent, fenced or indented.
13
13
  ```
14
14
 
15
15
  ## Example
@@ -19,7 +19,7 @@ For this configuration:
19
19
  ```yaml
20
20
  Markdown/CodeBlockStyle:
21
21
  Enabled: true
22
- EnforcedStyle: fenced
22
+ Style: fenced
23
23
  ```
24
24
 
25
25
  ### Bad
@@ -35,3 +35,12 @@ Markdown/CodeBlockStyle:
35
35
  puts 'hello'
36
36
  ```
37
37
  ````
38
+
39
+ ## Migrating From 0.1.0
40
+
41
+ Replace `EnforcedStyle` with `Style`:
42
+
43
+ ```yaml
44
+ Markdown/CodeBlockStyle:
45
+ Style: fenced
46
+ ```
@@ -12,6 +12,57 @@ Markdown/DuplicateHeadings:
12
12
  Scope: siblings # Or document to compare headings across the entire document.
13
13
  ```
14
14
 
15
+ ## Level-Specific Scopes
16
+
17
+ `Scope` accepts `document`, `siblings`, or `false` (disable duplicate checks). It also accepts a mapping with a required `Default` and level selectors. Unlisted levels inherit `Default`. Every value accepts the same three choices.
18
+
19
+ Selectors can be integer levels from 1 through 6, quoted inclusive ranges such as `"4-6"`, or quoted strict comparisons such as `">4"` (levels five and six) and `"<3"` (levels one and two). Overlapping selectors, reversed or empty ranges, missing defaults, and invalid scopes are configuration errors; there is no selector precedence.
20
+
21
+ For this configuration:
22
+
23
+ ```yaml
24
+ Markdown/DuplicateHeadings:
25
+ Scope:
26
+ Default: document
27
+ 4: siblings
28
+ 5: siblings
29
+ 6: siblings
30
+ ```
31
+
32
+ Levels one through three require document-wide uniqueness. Deeper headings require uniqueness only among siblings:
33
+
34
+ ```markdown
35
+ # Guide
36
+
37
+ ## Services
38
+
39
+ ### Service Overview
40
+
41
+ #### Details
42
+
43
+ ## Clients
44
+
45
+ ### Client Overview
46
+
47
+ #### Details
48
+ ```
49
+
50
+ Both `Details` headings are allowed because they have different parents. Replacing `Client Overview` with `Service Overview` is reported because level three uses document scope. Repeating `Details` under the same parent is still reported.
51
+
52
+ Each heading's effective scope determines which earlier headings it is compared against, including headings at other levels or with different configured scopes. This preserves existing cross-level comparison semantics: document scope compares against all earlier headings, while sibling scope compares against earlier headings sharing the same parent, including skipped levels.
53
+
54
+ To disable checks for deeper headings:
55
+
56
+ ```yaml
57
+ Markdown/DuplicateHeadings:
58
+ Scope:
59
+ Default: document
60
+ 4: siblings
61
+ ">4": false
62
+ ```
63
+
64
+ Alternatively, `"4-6": false` disables levels four through six. Disabled headings still establish parent ancestry and remain available for comparison when a later heading's effective scope checks them; they simply do not receive duplicate diagnostics themselves.
65
+
15
66
  ## Example
16
67
 
17
68
  ### Bad
@@ -9,7 +9,11 @@ Autocorrection preserves content. Intraword asterisks are not changed to undersc
9
9
  ```yaml
10
10
  Markdown/EmphasisStyle:
11
11
  Enabled: true
12
- Style: consistent # consistent, asterisk or underscore.
12
+ Style: consistent
13
+ SupportedStyles:
14
+ - consistent
15
+ - asterisk
16
+ - underscore
13
17
  ```
14
18
 
15
19
  ## Example
@@ -9,7 +9,7 @@ Autocorrection preserves contents, language metadata, and nesting. Conversion to
9
9
  ```yaml
10
10
  Markdown/FenceStyle:
11
11
  Enabled: true
12
- EnforcedStyle: consistent # consistent, backtick or tilde.
12
+ Style: consistent # consistent, backtick or tilde.
13
13
  ```
14
14
 
15
15
  ## Example
@@ -18,7 +18,7 @@ For this configuration:
18
18
 
19
19
  ```yaml
20
20
  Markdown/FenceStyle:
21
- EnforcedStyle: backtick
21
+ Style: backtick
22
22
  ```
23
23
 
24
24
  ### Bad
@@ -36,3 +36,12 @@ puts 1
36
36
  puts 1
37
37
  ```
38
38
  ````
39
+
40
+ ## Migrating From 0.1.0
41
+
42
+ Replace `EnforcedStyle` with `Style`:
43
+
44
+ ```yaml
45
+ Markdown/FenceStyle:
46
+ Style: backtick
47
+ ```
@@ -1,6 +1,6 @@
1
1
  # Markdown/FirstHeading
2
2
 
3
- Requires the first content line to be a heading at the configured level, unless a configured front-matter title qualifies. Empty documents are accepted.
3
+ Requires the first content line to be a heading at the configured level, unless the configured front-matter metadata key contains a nonempty string. The default key is `title`; set `FrontMatterTitle: false` to require a heading even when front matter contains a title. Empty documents are accepted.
4
4
 
5
5
  Front-matter titles must be strings; YAML aliases are not accepted. Report-only: adding or moving a heading changes structure.
6
6
 
@@ -10,8 +10,7 @@ Front-matter titles must be strings; YAML aliases are not accepted. Report-only:
10
10
  Markdown/FirstHeading:
11
11
  Enabled: false
12
12
  Level: 1
13
- FrontMatterTitle: true # Accept a nonempty front-matter title instead of a heading.
14
- TitleKey: title # Exact top-level YAML key, not a regular expression.
13
+ FrontMatterTitle: title # Exact top-level YAML key, or false to disable the exemption.
15
14
  ```
16
15
 
17
16
  ## Example
@@ -29,3 +28,30 @@ An introduction to the project.
29
28
 
30
29
  An introduction to the project.
31
30
  ```
31
+
32
+ ### Good Front Matter Title
33
+
34
+ ```markdown
35
+ ---
36
+ title: Introduction
37
+ ---
38
+
39
+ An introduction to the project.
40
+ ```
41
+
42
+ ## Migrating From 0.1.0
43
+
44
+ In 0.1.0, `FrontMatterTitle: true` enabled the exemption and `TitleKey` selected the metadata key. Replace that pair with one setting: a nonempty key string, or `false` to disable the exemption. For example, migrate a custom `name` key as follows:
45
+
46
+ ```yaml
47
+ # Before
48
+ Markdown/FirstHeading:
49
+ FrontMatterTitle: true
50
+ TitleKey: name
51
+
52
+ # After
53
+ Markdown/FirstHeading:
54
+ FrontMatterTitle: name
55
+ ```
56
+
57
+ `TitleKey` is obsolete and causes a configuration error with migration guidance. Use `FrontMatterTitle: false` when the front matter should not satisfy this cop.
@@ -0,0 +1,57 @@
1
+ # Markdown/FrontMatterKeys
2
+
3
+ Checks allowed and required top-level keys in a closed leading YAML front-matter block. Key names are exact and case-sensitive. Nested keys and values are not checked. Required keys must be present, but may have empty values.
4
+
5
+ `AllowedKeys: null` permits any key; `[]` permits none. `RequiredKeys` lists keys that must appear and must be included in `AllowedKeys` when that allowlist is configured. A document without front matter is skipped; an empty front-matter block is checked for missing keys.
6
+
7
+ Checks run even when `Markdown/IgnoreFrontMatter` is true. Malformed YAML is skipped so `Markdown/FrontMatterSyntax` can report syntax errors separately. Non-mapping YAML and complex or alias keys are reported. YAML merge keys (`<<`) are treated as literal keys, not expanded; quoted scalar keys are compared by their decoded text. Parsing uses the YAML syntax tree and never instantiates tagged Ruby objects.
8
+
9
+ Report-only: removing unknown metadata or inventing required values could lose data or change behavior.
10
+
11
+ ## Configuration
12
+
13
+ ```yaml
14
+ Markdown/FrontMatterKeys:
15
+ Enabled: false
16
+ AllowedKeys: null
17
+ RequiredKeys: []
18
+ ```
19
+
20
+ ## Example
21
+
22
+ For this configuration:
23
+
24
+ ```yaml
25
+ Markdown/FrontMatterKeys:
26
+ Enabled: true
27
+ AllowedKeys:
28
+ - title
29
+ - date
30
+ - tags
31
+ RequiredKeys:
32
+ - title
33
+ ```
34
+
35
+ ### Bad
36
+
37
+ ```markdown
38
+ ---
39
+ titel: Getting Started
40
+ date: 2026-11-05
41
+ ---
42
+
43
+ # Getting Started
44
+ ```
45
+
46
+ ### Good
47
+
48
+ ```markdown
49
+ ---
50
+ title: Getting Started
51
+ date: 2026-11-05
52
+ tags:
53
+ - ruby
54
+ ---
55
+
56
+ # Getting Started
57
+ ```
@@ -0,0 +1,32 @@
1
+ # Markdown/FrontMatterSyntax
2
+
3
+ Reports YAML syntax errors in closed leading Markdown front matter. The cop is enabled by default and report-only. It parses YAML syntax without constructing Ruby objects or validating metadata against a schema; valid scalar values such as dates and custom-tagged nodes are accepted.
4
+
5
+ The cop checks only a closed leading header and reports the YAML parser's line and column in the original Markdown source. It works independently of `Markdown: IgnoreFrontMatter`, which controls how other Markdown cops handle metadata.
6
+
7
+ ## Configuration
8
+
9
+ ```yaml
10
+ Markdown/FrontMatterSyntax:
11
+ Enabled: true
12
+ ```
13
+
14
+ ## Example
15
+
16
+ ### Bad
17
+
18
+ ```markdown
19
+ ---
20
+ title: [Unclosed list
21
+ ---
22
+ ```
23
+
24
+ ### Good
25
+
26
+ ```markdown
27
+ ---
28
+ title: A valid title
29
+ date: 2026-11-04
30
+ tags: [ruby, markdown]
31
+ ---
32
+ ```
@@ -4,24 +4,29 @@ Requires Markdown paragraph hard breaks to use the configured backslash or trail
4
4
 
5
5
  On files whose line endings cannot be preserved by RuboCop's source buffer, this cop reports style violations without autocorrecting them.
6
6
 
7
+ Trailing whitespace cleanup honors this style.
8
+
7
9
  ## Configuration
8
10
 
9
11
  ```yaml
10
12
  Markdown/HardBreakStyle:
11
13
  Enabled: true
12
- Style: backslash # backslash or spaces.
14
+ Style: backslash
15
+ SupportedStyles:
16
+ - backslash
17
+ - spaces
13
18
  ```
14
19
 
15
20
  ## Example
16
21
 
17
- ### Bad: trailing spaces
22
+ ### Bad: Trailing Spaces
18
23
 
19
24
  ```markdown
20
25
  First line.
21
26
  Second line.
22
27
  ```
23
28
 
24
- ### Good: backslash
29
+ ### Good: Backslash
25
30
 
26
31
  ```markdown
27
32
  First line.\
@@ -35,14 +40,14 @@ Markdown/HardBreakStyle:
35
40
  Style: spaces
36
41
  ```
37
42
 
38
- ### Bad: backslash
43
+ ### Bad: Backslash
39
44
 
40
45
  ```markdown
41
46
  First line.\
42
47
  Second line.
43
48
  ```
44
49
 
45
- ### Good: trailing spaces
50
+ ### Good: Trailing Spaces
46
51
 
47
52
  ```markdown
48
53
  First line.
@@ -1,15 +1,15 @@
1
1
  # Markdown/HardTabs
2
2
 
3
- Reports hard tabs, with configurable code-language exceptions.
3
+ Reports hard tabs in Markdown. Front matter and code-block contents are ignored by default. Enable `CodeBlocks` to inspect code contents; `AllowedCodeLanguages` exempts configured fenced languages.
4
4
 
5
- Report-only: replacing tabs can change embedded code or Makefiles.
5
+ Report-only: tabs may be intentional for alignment, Makefiles, or embedded code. Use [Text/HardTabs](../text/hard_tabs.md) for literal plain-text files.
6
6
 
7
7
  ## Configuration
8
8
 
9
9
  ```yaml
10
10
  Markdown/HardTabs:
11
11
  Enabled: true
12
- CodeBlocks: false # Preserve tabs in code examples; set true to inspect code.
12
+ CodeBlocks: false # Set true to inspect code-block contents.
13
13
  AllowedCodeLanguages: [] # Case-insensitive fenced languages to exempt, such as make.
14
14
  ```
15
15
 
@@ -0,0 +1,176 @@
1
+ # Markdown/HeadingCapitalization
2
+
3
+ Checks ATX and Setext headings, ignoring emphasis delimiters, link syntax, and leading punctuation or numbers. `Style: title_case` (the default) checks each word, keeping internal minor words lowercase. `initial_capital` checks only the first prose letter; remaining words retain their capitalization. Neither style enforces strict sentence case.
4
+
5
+ Lowercase openings inside inline code or quoted terms are reported, including ``## `excalc` usage`` and `## "excalc" usage`; neither their literal contents nor later words are changed. Opaque leading content such as HTML or images is conservatively skipped. `PreserveMixedCase: true` (the default) preserves mixed-case words such as `eBay`, `iOS`, and `macOS` where an initial capital is required, in either style. Set it to `false` to require uppercase initials even for those words. `IgnoredWords` still takes precedence.
6
+
7
+ Use native RuboCop suppression directives for intentional lowercase headings. [Markdown/ProperNames](proper_names.md) independently enforces configured canonical spellings; its configuration does not exempt headings from this cop.
8
+
9
+ `IgnoredWords` skips exact, case-sensitive whole words in either style without exempting the rest of the heading. Mixed-case brands such as `macOS` already pass; use this option for additional spellings such as `npm`.
10
+
11
+ Supports unsafe autocorrection (`rubocop -A`): capitalization may express proper-name intent, and changing a heading can change its generated link anchor. Corrections preserve prefixes, closing markers, and Setext underlines. They are offered only when the rendered heading text maps to one contiguous, unique source span outside protected inline syntax. Split formatting, links, encoded entities, multiline headings, and other ambiguous mappings remain report-only.
12
+
13
+ ## Configuration
14
+
15
+ ```yaml
16
+ Markdown/HeadingCapitalization:
17
+ Enabled: false
18
+ Style: title_case
19
+ PreserveMixedCase: true
20
+ IgnoredWords: []
21
+ MinorWords:
22
+ - a
23
+ - an
24
+ - and
25
+ - as
26
+ - at
27
+ - but
28
+ - by
29
+ - for
30
+ - from
31
+ - in
32
+ - nor
33
+ - of
34
+ - 'on'
35
+ - or
36
+ - per
37
+ - the
38
+ - to
39
+ - via
40
+ - vs
41
+ - with
42
+ ```
43
+
44
+ ## Initial Capital
45
+
46
+ For this configuration:
47
+
48
+ ```yaml
49
+ Markdown/HeadingCapitalization:
50
+ Enabled: true
51
+ Style: initial_capital
52
+ ```
53
+
54
+ ### Bad
55
+
56
+ ```markdown
57
+ ## installing the package
58
+
59
+ *getting* started
60
+ -----------------
61
+ ```
62
+
63
+ ### Good
64
+
65
+ ```markdown
66
+ ## Installing the package
67
+
68
+ *Getting* started
69
+ -----------------
70
+
71
+ ## `Excalc` usage
72
+
73
+ ## eBay integration
74
+ ```
75
+
76
+ ## Title Case
77
+
78
+ For this configuration:
79
+
80
+ ```yaml
81
+ Markdown/HeadingCapitalization:
82
+ Enabled: true
83
+ Style: title_case
84
+ ```
85
+
86
+ ### Bad
87
+
88
+ ```markdown
89
+ ## Getting started with the API
90
+
91
+ ## Getting Started With The API
92
+
93
+ ## the API and
94
+ ```
95
+
96
+ ### Good
97
+
98
+ ```markdown
99
+ ## Getting Started with the API
100
+
101
+ ## The API And
102
+
103
+ ## Getting Started with macOS and the API
104
+ ```
105
+
106
+ The minor words are `a`, `an`, `and`, `as`, `at`, `but`, `by`, `for`, `from`, `in`, `nor`, `of`, `on`, `or`, `per`, `the`, `to`, `via`, `vs`, and `with`. They must be lowercase except as the first or last word. Other words must start uppercase, with mixed-case words also accepted when `PreserveMixedCase` is enabled. Acronyms with uppercase initials and uncased scripts are accepted regardless of that option. Apostrophes stay within words; hyphens separate words, as in `Don't Re-enter the API`.
107
+
108
+ This is a word-based policy, not grammatical analysis or a publishing style manual. Title case skips the entire heading when it contains inline code, HTML, images, or other opaque content rather than guessing word boundaries, except that a lowercase literal opening is reported without correction.
109
+
110
+ ### Customize Minor Words
111
+
112
+ `MinorWords` replaces the default list and matches case-insensitively. It affects only `title_case`; first and last words still require capitals. Set it to `[]` to capitalize every word. `IgnoredWords` takes precedence over minor-word rules.
113
+
114
+ For this configuration:
115
+
116
+ ```yaml
117
+ Markdown/HeadingCapitalization:
118
+ Enabled: true
119
+ Style: title_case
120
+ MinorWords:
121
+ - and
122
+ - the
123
+ ```
124
+
125
+ #### Bad
126
+
127
+ ```markdown
128
+ ## Getting Started with the API
129
+ ```
130
+
131
+ #### Good
132
+
133
+ ```markdown
134
+ ## Getting Started With the API
135
+ ```
136
+
137
+ ## Ignore Specific Words
138
+
139
+ For example, with `PreserveMixedCase: false`, `## Using macOS` is reported in title-case style unless `macOS` is included in `IgnoredWords`.
140
+
141
+ For this configuration:
142
+
143
+ ```yaml
144
+ Markdown/HeadingCapitalization:
145
+ Enabled: true
146
+ Style: title_case
147
+ IgnoredWords:
148
+ - macOS
149
+ - npm
150
+ ```
151
+
152
+ ### Bad
153
+
154
+ ```markdown
155
+ ## Getting started with macOS
156
+
157
+ ## Using npmlike on macOS
158
+ ```
159
+
160
+ ### Good
161
+
162
+ ```markdown
163
+ ## Getting Started with macOS
164
+
165
+ ## Using npm on macOS
166
+ ```
167
+
168
+ Entries are individual words, not phrases or patterns. Ignoring `npm` does not ignore `npmlike` or exempt other lowercase words.
169
+
170
+ ### Suppress an Intentional Lowercase Heading
171
+
172
+ ```markdown
173
+ <!-- # rubocop:disable Markdown/HeadingCapitalization -->
174
+ ## npm usage
175
+ <!-- # rubocop:enable Markdown/HeadingCapitalization -->
176
+ ```