Skip to content

Commit

Permalink
Add no-duplicate-headings-in-section rule
Browse files Browse the repository at this point in the history
This new rule warns when headings exist on the same
level, in the same section.  This rule is not yet in
any preset.

Closes GH-84.
  • Loading branch information
wooorm committed Nov 1, 2016
1 parent 1c3f0a3 commit f113f59
Show file tree
Hide file tree
Showing 3 changed files with 122 additions and 1 deletion.
45 changes: 44 additions & 1 deletion doc/rules.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

# List of Rules

This document describes all (58)
This document describes all (59)
available rules, what they check for, examples of
what they warn for, and how to fix their warnings.

Expand Down Expand Up @@ -93,6 +93,7 @@ For example, as follows:
- [no-blockquote-without-caret](#no-blockquote-without-caret)
- [no-consecutive-blank-lines](#no-consecutive-blank-lines)
- [no-duplicate-definitions](#no-duplicate-definitions)
- [no-duplicate-headings-in-section](#no-duplicate-headings-in-section)
- [no-duplicate-headings](#no-duplicate-headings)
- [no-emphasis-as-heading](#no-emphasis-as-heading)
- [no-file-name-articles](#no-file-name-articles)
Expand Down Expand Up @@ -1392,6 +1393,48 @@ When this rule is turned on, the following file
2:1-2:11: Do not use definitions with the same identifier (1:1)
```

## `no-duplicate-headings-in-section`

Warn when duplicate headings are found,
but only when on the same level, “in”
the same section.

When this rule is turned on, the following file
`valid.md` is ok:

```markdown
## Alpha

### Bravo

## Charlie

### Bravo

### Delta

#### Bravo

#### Echo

##### Bravo
```

When this rule is turned on, the following file
`invalid.md` is **not** ok:

```markdown
## Foxtrot

### Golf

### Golf
```

```text
5:1-5:9: Do not use headings with similar content per section (3:1)
```

## `no-duplicate-headings`

Warn when duplicate headings are found.
Expand Down
1 change: 1 addition & 0 deletions packages/remark-lint/lib/rules.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ module.exports = {
'no-blockquote-without-caret': require('./rules/no-blockquote-without-caret.js'),
'no-consecutive-blank-lines': require('./rules/no-consecutive-blank-lines.js'),
'no-duplicate-definitions': require('./rules/no-duplicate-definitions.js'),
'no-duplicate-headings-in-section': require('./rules/no-duplicate-headings-in-section.js'),
'no-duplicate-headings': require('./rules/no-duplicate-headings.js'),
'no-emphasis-as-heading': require('./rules/no-emphasis-as-heading.js'),
'no-file-name-articles': require('./rules/no-file-name-articles.js'),
Expand Down
77 changes: 77 additions & 0 deletions packages/remark-lint/lib/rules/no-duplicate-headings-in-section.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/**
* @author Titus Wormer
* @copyright 2015 Titus Wormer
* @license MIT
* @module no-duplicate-headings-in-section
* @fileoverview
* Warn when duplicate headings are found,
* but only when on the same level, “in”
* the same section.
*
* @example {"name": "valid.md"}
*
* ## Alpha
*
* ### Bravo
*
* ## Charlie
*
* ### Bravo
*
* ### Delta
*
* #### Bravo
*
* #### Echo
*
* ##### Bravo
*
* @example {"name": "invalid.md", "label": "input"}
*
* ## Foxtrot
*
* ### Golf
*
* ### Golf
*
* @example {"name": "invalid.md", "label": "output"}
*
* 5:1-5:9: Do not use headings with similar content per section (3:1)
*/

'use strict';

/* Dependencies. */
var position = require('unist-util-position');
var visit = require('unist-util-visit');
var toString = require('mdast-util-to-string');

/* Expose. */
module.exports = noDuplicateHeadingsInSection;

/* Warn when headings with equal content are found in
* a section. Case-insensitive. */
function noDuplicateHeadingsInSection(tree, file) {
var stack = [{}];

visit(tree, 'heading', function (node) {
var depth = node.depth;
var siblings = stack[depth - 1] || {};
var value = toString(node).toUpperCase();
var duplicate = siblings[value];
var pos;

stack = stack.slice(0, depth);
stack[depth] = {};
siblings[value] = node;

if (!position.generated(node) && duplicate && duplicate.type === 'heading') {
pos = position.start(duplicate);
file.message(
'Do not use headings with similar content per section (' +
pos.line + ':' + pos.column + ')',
node
);
}
});
}

0 comments on commit f113f59

Please sign in to comment.