Add gap support

Gaps are nodes of (re)generated content, without positional
information, which are therefore impossible to warn about.
This feature adds support of the detection of those, and ignores
warnings for already removed content.

Closes GH-8.
This commit is contained in:
Titus Wormer 2015-06-29 19:26:15 +02:00
parent b7017cbe0d
commit 136e7608b4
7 changed files with 145 additions and 26 deletions

View File

@ -4,29 +4,81 @@
* @module Filter
* @fileoverview mdast plug-in used internally by
* mdast-lint to filter ruleIds by enabled and disabled
* ranges.
* ranges, or by gaps.
* @todo Externalise into its own repository.
*/
'use strict';
var position = require('./utilities/position');
var visit = require('./utilities/visit');
/**
* Sort all `file`s messages by line/column. Note that
* this works as a plugin, and will also sort warnings
* added by other plug-ins before `mdast-lint` was added.
* 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;
if (!file || !file.messages || !file.messages.length) {
return;
}
/**
* Patch a new position.
*
* @param {number?} [latest] - Last found position.
*/
function update(latest) {
if (latest === undefined) {
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 = file.lintRanges[message.ruleId];
var index = ranges && ranges.length;
var gapIndex = gaps.length;
var length = -1;
var pos;
var range;
if (!message.line) {
@ -37,6 +89,17 @@ function transformer(ast, file) {
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];

View File

@ -10,16 +10,17 @@ module.exports = require('./lib');
* @module Filter
* @fileoverview mdast plug-in used internally by
* mdast-lint to filter ruleIds by enabled and disabled
* ranges.
* ranges, or by gaps.
* @todo Externalise into its own repository.
*/
'use strict';
var position = require('./utilities/position');
var visit = require('./utilities/visit');
/**
* Sort all `file`s messages by line/column. Note that
* this works as a plugin, and will also sort warnings
* added by other plug-ins before `mdast-lint` was added.
* Remove warnings which are disabled, or are in gaps.
*
* @param {Node} ast - Root node.
* @param {File} file - Virtual file.
@ -29,6 +30,19 @@ function transformer(ast, file) {
return;
}
visit(ast, function (node) {
var start = position.start(node);
var end = position.end(node);
if (!start || start.offset === undefined) {
// console.log('start: ', start, node);
}
if (!end || end.offset === undefined) {
// console.log('end: ', end, node);
}
});
file.messages = file.messages.filter(function (message) {
var ranges = file.lintRanges[message.ruleId];
var index = ranges && ranges.length;
@ -77,7 +91,7 @@ function attacher() {
module.exports = attacher;
},{}],3:[function(require,module,exports){
},{"./utilities/position":63,"./utilities/visit":65}],3:[function(require,module,exports){
/**
* @author Titus Wormer
* @copyright 2015 Titus Wormer. All rights reserved.

2
mdast-lint.min.js vendored

File diff suppressed because one or more lines are too long

View File

@ -143,21 +143,6 @@ but the third is re-enabled):
### Hello
```
## Combining with other plug-ins
As noted above, **mdast-lint** is just an **mdast** plugin. Meaning, you
can use other plug-ins together with it. Such as [mdast-toc](https://github.com/wooorm/mdast-toc),
which will generate a table of contents for you.
However, these plug-ins will generate new nodes in the syntax tree, nodes
which previously werent available: thus, they have no positional information,
and **mdast-lint** cannot warn you about them.
Therefore, you need to do two things:
* Process the files twice (this is similar to how LaTeX works);
* Ensure **mdast-lint** runs before the plug-ins which generate content.
## Using mdast to fix your markdown
One of **mdast**s cool parts is that it compiles to very clean, and highly

10
test/fixtures/gaps-toc-final.md vendored Normal file
View File

@ -0,0 +1,10 @@
# Foo
## Table of Contents
This paragraph is removed by mdast-toc. However, a rule such as
`no-consecutive-blank-lines` cannot see this node as it has no
position. **mdast-lint** knows that this node has no positional
information and can ignore the space between the ToC heading
and end of the document, thus ignoring any messages between
nodes.

11
test/fixtures/gaps-toc-internal.md vendored Normal file
View File

@ -0,0 +1,11 @@
# Foo
## Table of Contents
This paragraph is removed by mdast-toc. However, a rule such as
`no-consecutive-blank-lines` cannot see this node as it has no
position. **mdast-lint** knows that this node has no positional
information and can ignore the space between the ToC heading
and next node, thus ignoring any messages between nodes.
## Bar

View File

@ -11,9 +11,10 @@
var fs = require('fs');
var path = require('path');
var assert = require('assert');
var mdast = require('mdast');
var File = require('mdast/lib/file');
var assert = require('assert');
var toc = require('mdast-toc');
var lint = require('..');
var plural = require('../lib/utilities/plural');
var clean = require('./clean');
@ -256,6 +257,41 @@ describe('External', function () {
});
});
/*
* Validate gaps are ignored.
*/
describe('Gaps', function () {
it('should supports gaps in a document', function (done) {
var file = toFile('gaps-toc-internal.md');
var processor = mdast().use(toc).use(lint);
file.quiet = true;
processor.process(file, function (err) {
assert(file.messages.length === 0);
done(err);
});
// assertFile('gaps-toc-final.md', []);
});
it('should supports gaps at the end of a document', function (done) {
var file = toFile('gaps-toc-final.md');
var processor = mdast().use(toc).use(lint);
file.quiet = true;
processor.process(file, function (err) {
assert(file.messages.length === 0);
done(err);
});
});
});
/*
* Validate inline en- and disabling.
*/