Skip to content

Commit

Permalink
Add checkbox-character-style rule
Browse files Browse the repository at this point in the history
New rule which warns when the checkbox character is invalid
or inconsistent.

Closes GH-4.
  • Loading branch information
wooorm committed Jun 20, 2015
1 parent 55d1128 commit 7ed4579
Show file tree
Hide file tree
Showing 11 changed files with 581 additions and 65 deletions.
44 changes: 44 additions & 0 deletions doc/rules.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ fix their warnings.
* [externals](#externals)
* [reset](#reset)
* [blockquote-indentation](#blockquote-indentation)
* [checkbox-character-style](#checkbox-character-style)
* [code-block-style](#code-block-style)
* [definition-case](#definition-case)
* [definition-spacing](#definition-spacing)
Expand Down Expand Up @@ -131,6 +132,49 @@ Options: `boolean`, default: `false`.
The default value, `consistent`, detects the first used indentation
and will warn when other blockquotes use a different indentation.

### checkbox-character-style

```md
<!-- Note: the double guillemet (`»`) and middle-dots represent a tab -->

<!-- Valid by default, `'consistent'`, or `{'checked': 'x'}` -->
- [x] List item
- [x] List item

<!-- Valid by default, `'consistent'`, or `{'checked': 'X'}` -->
- [X] List item
- [X] List item

<!-- Valid by default, `'consistent'`, or `{'unchecked': ' '}` -->
- [ ] List item
- [ ] List item

<!-- Valid by default, `'consistent'`, or `{'unchecked': '»'}` -->
- [»···] List item
- [»···] List item

<!-- Always invalid -->
- [x] List item
- [X] List item
- [ ] List item
- [»···] List item
```

Warn when list item checkboxes violate a given style.

The default value, `consistent`, detects the first used checked
and unchecked checkbox styles, and will warn when a subsequent
checkboxes uses a different style.

These values can also be passed in as an object, such as:

```json
{
"checked": 'x',
"unchecked": ' '
}
```

### code-block-style

````md
Expand Down
183 changes: 183 additions & 0 deletions lib/rules/checkbox-character-style.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
/**
* @author Titus Wormer
* @copyright 2015 Titus Wormer. All rights reserved.
* @module checkbox-character-style
* @fileoverview
* Warn when list item checkboxes violate a given style.
*
* The default value, `consistent`, detects the first used checked
* and unchecked checkbox styles, and will warn when a subsequent
* checkboxes uses a different style.
*
* These values can also be passed in as an object, such as:
*
* ```json
* {
* "checked": 'x',
* "unchecked": ' '
* }
* ```
* @example
* <!-- Note: the double guillemet (`»`) and middle-dots represent a tab -->
*
* <!-- Valid by default, `'consistent'`, or `{'checked': 'x'}` -->
* - [x] List item
* - [x] List item
*
* <!-- Valid by default, `'consistent'`, or `{'checked': 'X'}` -->
* - [X] List item
* - [X] List item
*
* <!-- Valid by default, `'consistent'`, or `{'unchecked': ' '}` -->
* - [ ] List item
* - [ ] List item
*
* <!-- Valid by default, `'consistent'`, or `{'unchecked': '»'}` -->
* - [»···] List item
* - [»···] List item
*
* <!-- Always invalid -->
* - [x] List item
* - [X] List item
* - [ ] List item
* - [»···] List item
*/

'use strict';

/*
* Dependencies.
*/

var visit = require('../utilities/visit');
var position = require('../utilities/position');

/*
* Methods.
*/

var start = position.start;
var end = position.end;

var CHECKED = {
'x': true,
'X': true
};

var UNCHECKED = {
' ': true,
' ': true
};

/**
* Warn when list item checkboxes violate a given style.
*
* @param {Node} ast - Root node.
* @param {File} file - Virtual file.
* @param {Object?} preferred - An object with `checked`
* and `unchecked` properties, each set to null to default to
* the first found style, or set to `'x'` or `'X'` for checked,
* or `' '` (space) or `'\t'` (tab) for unchecked.
* @param {Function} done - Callback.
*/
function checkboxCharacterStyle(ast, file, preferred, done) {
var contents = file.toString();

if (preferred === 'consistent' || typeof preferred !== 'object') {
preferred = {};
}

if (!preferred.unchecked) {
preferred.unchecked = null;
}

if (!preferred.checked) {
preferred.checked = null;
}

if (
preferred.unchecked !== null &&
UNCHECKED[preferred.unchecked] !== true
) {
file.fail(
'Invalid unchecked checkbox marker `' +
preferred.unchecked +
'`: use either `\'\\t\'`, or `\' \'`'
);
}

if (
preferred.checked !== null &&
CHECKED[preferred.checked] !== true
) {
file.fail(
'Invalid checked checkbox marker `' +
preferred.checked +
'`: use either `\'x\'`, or `\'X\'`'
);
}

visit(ast, 'listItem', function (node) {
var type;
var initial;
var final;
var stop;
var value;
var style;
var character;

/*
* Exit early for items without checkbox.
*/

if (
node.checked !== Boolean(node.checked) ||
position.isGenerated(node)
) {
return;
}

type = node.checked ? 'checked' : 'unchecked';

initial = start(node).offset;
final = (node.children.length ? start(node.children[0]) : end(node)).offset;

/*
* For a checkbox to be parsed, it must be followed
* by a white space.
*/

value = contents.slice(initial, final).trimRight().slice(0, -1);

/*
* The checkbox character is behind a square
* bracket.
*/

character = value.charAt(value.length - 1);
style = preferred[type];

if (style === null) {
preferred[type] = character;
} else if (character !== style) {
stop = initial + value.length;

file.warn(
type.charAt(0).toUpperCase() + type.slice(1) +
' checkboxes should use `' + style + '` as a marker',
{
'start': file.offsetToPosition(stop - 1),
'end': file.offsetToPosition(stop)
}
);
}
});

done();
}

/*
* Expose.
*/

module.exports = checkboxCharacterStyle;
1 change: 1 addition & 0 deletions lib/rules/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ module.exports = {
'blockquote-indentation': require('./blockquote-indentation'),
'no-blockquote-without-caret': require('./no-blockquote-without-caret'),
'code-block-style': require('./code-block-style'),
'checkbox-character-style': require('./checkbox-character-style'),
'definition-case': require('./definition-case'),
'definition-spacing': require('./definition-spacing'),
'no-emphasis-as-heading': require('./no-emphasis-as-heading'),
Expand Down
Loading

0 comments on commit 7ed4579

Please sign in to comment.