Skip to content

Commit

Permalink
Add gap support
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
wooorm committed Jun 29, 2015
1 parent b7017cb commit 136e760
Show file tree
Hide file tree
Showing 7 changed files with 145 additions and 26 deletions.
71 changes: 67 additions & 4 deletions lib/filter.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,29 +4,81 @@
* @module Filter
* @fileoverview mdast plug-in used internally by
* mdast-lint to filter ruleId’s 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) {
Expand All @@ -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];

Expand Down
24 changes: 19 additions & 5 deletions mdast-lint.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,17 @@ module.exports = require('./lib');
* @module Filter
* @fileoverview mdast plug-in used internally by
* mdast-lint to filter ruleId’s 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.
Expand All @@ -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;
Expand Down Expand Up @@ -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.
Expand Down
2 changes: 1 addition & 1 deletion mdast-lint.min.js

Large diffs are not rendered by default.

15 changes: 0 additions & 15 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 weren’t 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-in’s which generate content.

## Using mdast to fix your markdown

One of **mdast**’s cool parts is that it compiles to very clean, and highly
Expand Down
10 changes: 10 additions & 0 deletions test/fixtures/gaps-toc-final.md
Original file line number Diff line number Diff line change
@@ -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 changes: 11 additions & 0 deletions test/fixtures/gaps-toc-internal.md
Original file line number Diff line number Diff line change
@@ -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
38 changes: 37 additions & 1 deletion test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand Down Expand Up @@ -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.
*/
Expand Down

0 comments on commit 136e760

Please sign in to comment.