Update support for configuration comments

*   Add a new `ignore` keyword which ignores warnings in the following
    node;
*   Externalise `lib/filter` to `remark-message-control`.
This commit is contained in:
Titus Wormer 2016-02-03 16:14:31 +01:00
parent b6309f0c09
commit 870cb99a3e
6 changed files with 43 additions and 278 deletions

View File

@ -11,13 +11,15 @@
],
"repository": "wooorm/remark-lint",
"dependencies": {
"sindresorhus/plur": "^2.0.0",
"wooorm/remark-range": "^2.0.0",
"sindresorhus/decamelize": "^1.0.0",
"wooorm/mdast-util-heading-style": "^1.0.0",
"wooorm/mdast-util-position": "^1.0.0",
"wooorm/mdast-util-to-string": "^1.0.0",
"eush77/npm-prefix": "^1.1.1",
"sindresorhus/plur": "^2.0.0",
"wooorm/remark-message-control": "^1.0.1",
"wooorm/remark-range": "^2.0.0",
"wooorm/unist-util-visit": "^1.0.0",
"wooorm/mdast-zone": "^2.0.0",
"wooorm/vfile-sort": "^1.0.0"
},
"scripts": [

View File

@ -1,139 +0,0 @@
/**
* @author Titus Wormer
* @copyright 2015 Titus Wormer
* @license MIT
* @module remark:lint:filter
* @fileoverview remark plug-in used internally by
* remark-lint to filter ruleIds by enabled and disabled
* ranges, or by gaps.
* @todo Externalise into its own repository.
*/
'use strict';
/* eslint-env commonjs */
var position = require('mdast-util-position');
var visit = require('unist-util-visit');
/**
* Remove warnings which are disabled, or are in gaps.
*
* @param {Node} ast - Root node.
* @param {File} file - Virtual file.
*/
function transformer(ast, file) {
var lastNode = ast.children[ast.children.length - 1];
var gaps = [];
var offset = 0;
var isGap = false;
var scope = file.namespace('remark-lint').ranges;
if (!file || !file.messages || !file.messages.length) {
return;
}
/**
* Patch a new position.
*
* @param {number?} [latest] - Last found position.
*/
function update(latest) {
if (latest === undefined || latest === null) {
isGap = true;
return;
}
if (offset > latest) {
return;
}
if (isGap) {
gaps.push({
'start': offset,
'end': latest
});
isGap = false;
}
offset = latest;
}
visit(ast, function (node) {
var start = position.start(node);
var end = position.end(node);
update(start && start.offset);
if (!node.children) {
update(end && end.offset);
}
});
if (offset === position.end(lastNode).offset) {
update();
update(file.toString().length - 1);
}
file.messages = file.messages.filter(function (message) {
var ranges = scope[message.ruleId];
var index = ranges && ranges.length;
var gapIndex = gaps.length;
var length = -1;
var pos;
var range;
if (!message.line) {
message.line = 1;
}
if (!message.column) {
message.column = 1;
}
pos = file.positionToOffset(message);
while (gapIndex--) {
if (
gaps[gapIndex].start <= pos &&
gaps[gapIndex].end > pos
) {
return false;
}
}
while (--index > length) {
range = ranges[index];
if (
range.position.line < message.line ||
(
range.position.line === message.line &&
range.position.column < message.column
)
) {
return range.state === true;
}
}
/* xistanbul ignore next - Just to be safe */
return true;
});
}
/**
* Return `transformer`.
*
* @return {Function} - See `transformer`.
*/
function attacher() {
return transformer;
}
/*
* Expose.
*/
module.exports = attacher;

View File

@ -24,9 +24,8 @@ var SOURCE = 'remark-lint';
var decamelize = require('decamelize');
var sort = require('vfile-sort');
var range = require('remark-range');
var zone = require('mdast-zone');
var control = require('remark-message-control');
var internals = require('./rules');
var filter = require('./filter');
var npmPrefix = require('npm-prefix')();
/*
@ -249,10 +248,13 @@ function decamelizeSettings(source) {
*/
function lint(remark, options) {
var settings = decamelizeSettings(options || {});
var reset = settings.reset;
var rules = loadExternals(settings.external);
var id;
var reset = options && options.reset;
var enable = [];
var disable = [];
var known = [];
var setting;
var id;
/*
* Ensure offset information is added.
@ -260,151 +262,47 @@ function lint(remark, options) {
remark.use(range);
/**
* Get the latest state of a rule.
*
* @param {string} ruleId - Unique rule name.
* @param {File} [file] - File (optional)
*/
function getState(ruleId, file) {
var scope = file && file.namespace('remark-lint');
var ranges = scope && scope.ranges && scope.ranges[ruleId];
if (ranges) {
return ranges[ranges.length - 1].state;
}
setting = settings[ruleId];
if (setting === false) {
return false;
}
return !reset || (setting !== null && setting !== undefined);
}
/**
* Store settings on `file`.
*
* @param {File} file - Virtual file.
*/
function store(file) {
var scope = file.namespace('remark-lint');
var ranges = scope.ranges;
var ruleId;
if (!ranges) {
ranges = {};
for (ruleId in rules) {
ranges[ruleId] = [{
'state': getState(ruleId),
'position': {
'line': 0,
'column': 0
}
}];
}
scope.ranges = ranges;
}
}
remark.use(function () {
return function (ast, file) {
store(file);
};
});
/*
* Add each rule as a seperate plugin.
*/
for (id in rules) {
remark.use(attachFactory(id, rules[id], settings[id]));
setting = settings[id];
known.push(id);
if (!(setting === null || setting === undefined)) {
if (setting === false) {
disable.push(id);
} else {
enable.push(id);
}
}
remark.use(attachFactory(id, rules[id], setting));
}
/**
* Handle a rule.
*
* @param {VFile} file - Virtual file.
* @param {Object} marker - Marker context.
* @param {string} type - Type to toggle to.
* @param {*} ruleId - Rule to toggle.
*/
function toggle(file, marker, type, ruleId) {
var scope = file.namespace('remark-lint');
var markers;
var currentState;
var previousState;
if (!(ruleId in rules)) {
file.fail('Unknown rule: cannot ' + type + ' `\'' + ruleId + '\'`', marker.node);
return;
}
markers = scope.ranges[ruleId];
previousState = getState(ruleId, file);
currentState = type === 'enable';
if (currentState !== previousState) {
markers.push({
'state': currentState,
'position': marker.node.position.start
});
}
}
/**
* Handle a new-found marker.
*
* @param {Object} marker - Marker context.
* @param {Object} parser - Parser instance.
*/
function onparse(marker, parser) {
var file = parser.file;
var attributes = marker.attributes.split(' ');
var type = attributes[0];
var ids = attributes.slice(1);
var length = ids.length;
var index = -1;
if (type !== 'disable' && type !== 'enable') {
file.fail('Unknown lint keyword `' + type + '`: use either `\'enable\'` or `\'disable\'`', marker.node);
return;
}
store(file);
while (++index < length) {
toggle(file, marker, type, ids[index]);
}
}
remark.use(zone({
'name': 'lint',
'onparse': onparse
}));
/*
* Filter.
* Allow comments to toggle messages.
*/
remark.use(filter);
remark.use(control, {
'name': 'lint',
'source': SOURCE,
'reset': reset,
'known': known,
'enable': enable,
'disable': disable
});
/**
* Transformer sort messages.
*
* @param {Node} node - Syntax tree.
* @param {VFile} file - Virtual file.
* @param {Function} next - Completion handler.
*/
return function (node, file, next) {
return function (node, file) {
sort(file);
next();
};
}

View File

@ -17,13 +17,13 @@
"author": "Titus Wormer <tituswormer@gmail.com>",
"dependencies": {
"decamelize": "^1.0.0",
"remark-range": "^2.0.0",
"mdast-util-heading-style": "^1.0.0",
"mdast-util-position": "^1.0.0",
"mdast-util-to-string": "^1.0.0",
"mdast-zone": "^2.0.0",
"npm-prefix": "^1.1.1",
"plur": "^2.0.0",
"remark-message-control": "^1.0.1",
"remark-range": "^2.0.0",
"unist-util-visit": "^1.0.0",
"vfile-sort": "^1.0.0"
},

View File

@ -114,8 +114,10 @@ markdown code. Read more about the latter on [**remark**s
readme][remark-process].
In addition, you can also provide configuration comments to turn a rule
on or off inside a file (note that you cannot change what a setting, such as
`maximum-line-length`, youre either enabling or disabling warnings).
on or off inside a file. Note that you cannot change what a setting,
such as `maximum-line-length`, checks for, as youre either enabling
or disabling warnings). Read more about configuration comments in
[**remark-message-control**][message-control]s documentation.
The following file will warn twice for the duplicate headings:
@ -234,3 +236,5 @@ excluding `remark-lint-no-` or `remark-lint-`
[remark-process]: https://github.com/wooorm/remark#remarkprocessvalue-options-done
[linter-markdown]: https://atom.io/packages/linter-markdown
[message-control]: https://github.com/wooorm/remark-message-control#markers

View File

@ -357,7 +357,7 @@ describe('Comments', function () {
describe('Invalid comments', function () {
assertFile('comments-invalid-keyword.md', [
'comments-invalid-keyword.md:3:1-3:20: Unknown lint keyword `foo`: use either `\'enable\'` or `\'disable\'`'
'comments-invalid-keyword.md:3:1-3:20: Unknown keyword `foo`: expected `\'enable\'`, `\'disable\'`, or `\'ignore\'`'
], null, {
'reset': true
});