Add no-duplicate-headings-in-section rule

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.
This commit is contained in:
Titus Wormer 2016-11-01 17:51:46 +01:00
parent 1c3f0a3446
commit f113f596c4
3 changed files with 122 additions and 1 deletions

View File

@ -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.
@ -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)
@ -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.

View File

@ -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'),

View File

@ -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
);
}
});
}