Skip to content

Commit

Permalink
Breaking: Extract showInvisibles and generateDifferences
Browse files Browse the repository at this point in the history
Move these two functions into a helper module 'prettier-linter-helpers'.
If you used those functions then plese reference that package directly.

Fixes #101
  • Loading branch information
BPScott authored and not-an-aardvark committed Oct 1, 2018
1 parent 478c7e5 commit bf7c40c
Show file tree
Hide file tree
Showing 4 changed files with 15 additions and 185 deletions.
155 changes: 8 additions & 147 deletions eslint-plugin-prettier.js
Expand Up @@ -9,17 +9,16 @@
// Requirements
// ------------------------------------------------------------------------------

const diff = require('fast-diff');
const {
showInvisibles,
generateDifferences
} = require('prettier-linter-helpers');

// ------------------------------------------------------------------------------
// Constants
// ------------------------------------------------------------------------------

const LINE_ENDING_RE = /\r\n|[\r\n\u2028\u2029]/;

const OPERATION_INSERT = 'insert';
const OPERATION_DELETE = 'delete';
const OPERATION_REPLACE = 'replace';
const { INSERT, DELETE, REPLACE } = generateDifferences;

// ------------------------------------------------------------------------------
// Privates
Expand All @@ -28,142 +27,6 @@ const OPERATION_REPLACE = 'replace';
// Lazily-loaded Prettier.
let prettier;

// ------------------------------------------------------------------------------
// Helpers
// ------------------------------------------------------------------------------

/**
* Converts invisible characters to a commonly recognizable visible form.
* @param {string} str - The string with invisibles to convert.
* @returns {string} The converted string.
*/
function showInvisibles(str) {
let ret = '';
for (let i = 0; i < str.length; i++) {
switch (str[i]) {
case ' ':
ret += '·'; // Middle Dot, \u00B7
break;
case '\n':
ret += '⏎'; // Return Symbol, \u23ce
break;
case '\t':
ret += '↹'; // Left Arrow To Bar Over Right Arrow To Bar, \u21b9
break;
case '\r':
ret += '␍'; // Carriage Return Symbol, \u240D
break;
default:
ret += str[i];
break;
}
}
return ret;
}

/**
* Generate results for differences between source code and formatted version.
* @param {string} source - The original source.
* @param {string} prettierSource - The Prettier formatted source.
* @returns {Array} - An array contains { operation, offset, insertText, deleteText }
*/
function generateDifferences(source, prettierSource) {
// fast-diff returns the differences between two texts as a series of
// INSERT, DELETE or EQUAL operations. The results occur only in these
// sequences:
// /-> INSERT -> EQUAL
// EQUAL | /-> EQUAL
// \-> DELETE |
// \-> INSERT -> EQUAL
// Instead of reporting issues at each INSERT or DELETE, certain sequences
// are batched together and are reported as a friendlier "replace" operation:
// - A DELETE immediately followed by an INSERT.
// - Any number of INSERTs and DELETEs where the joining EQUAL of one's end
// and another's beginning does not have line endings (i.e. issues that occur
// on contiguous lines).

const results = diff(source, prettierSource);
const differences = [];

const batch = [];
let offset = 0; // NOTE: INSERT never advances the offset.
while (results.length) {
const result = results.shift();
const op = result[0];
const text = result[1];
switch (op) {
case diff.INSERT:
case diff.DELETE:
batch.push(result);
break;
case diff.EQUAL:
if (results.length) {
if (batch.length) {
if (LINE_ENDING_RE.test(text)) {
flush();
offset += text.length;
} else {
batch.push(result);
}
} else {
offset += text.length;
}
}
break;
default:
throw new Error(`Unexpected fast-diff operation "${op}"`);
}
if (batch.length && !results.length) {
flush();
}
}

return differences;

function flush() {
let aheadDeleteText = '';
let aheadInsertText = '';
while (batch.length) {
const next = batch.shift();
const op = next[0];
const text = next[1];
switch (op) {
case diff.INSERT:
aheadInsertText += text;
break;
case diff.DELETE:
aheadDeleteText += text;
break;
case diff.EQUAL:
aheadDeleteText += text;
aheadInsertText += text;
break;
}
}
if (aheadDeleteText && aheadInsertText) {
differences.push({
offset,
operation: OPERATION_REPLACE,
insertText: aheadInsertText,
deleteText: aheadDeleteText
});
} else if (!aheadDeleteText && aheadInsertText) {
differences.push({
offset,
operation: OPERATION_INSERT,
insertText: aheadInsertText
});
} else if (aheadDeleteText && !aheadInsertText) {
differences.push({
offset,
operation: OPERATION_DELETE,
deleteText: aheadDeleteText
});
}
offset += aheadDeleteText.length;
}
}

// ------------------------------------------------------------------------------
// Rule Definition
// ------------------------------------------------------------------------------
Expand Down Expand Up @@ -242,8 +105,6 @@ function reportReplace(context, offset, deleteText, insertText) {
// ------------------------------------------------------------------------------

module.exports = {
showInvisibles,
generateDifferences,
configs: {
recommended: {
extends: ['prettier'],
Expand Down Expand Up @@ -354,21 +215,21 @@ module.exports = {

differences.forEach(difference => {
switch (difference.operation) {
case OPERATION_INSERT:
case INSERT:
reportInsert(
context,
difference.offset,
difference.insertText
);
break;
case OPERATION_DELETE:
case DELETE:
reportDelete(
context,
difference.offset,
difference.deleteText
);
break;
case OPERATION_REPLACE:
case REPLACE:
reportReplace(
context,
difference.offset,
Expand Down
2 changes: 1 addition & 1 deletion package.json
Expand Up @@ -28,7 +28,7 @@
},
"homepage": "https://github.com/prettier/eslint-plugin-prettier#readme",
"dependencies": {
"fast-diff": "^1.1.2"
"prettier-linter-helpers": "^1.0.0"
},
"peerDependencies": {
"prettier": ">= 1.13.0"
Expand Down
37 changes: 0 additions & 37 deletions test/prettier.js
Expand Up @@ -14,7 +14,6 @@

const fs = require('fs');
const path = require('path');
const assert = require('assert');

const eslintPluginPrettier = require('..');

Expand Down Expand Up @@ -104,42 +103,6 @@ vueRuleTester.run('prettier', rule, {
]
});

describe('showInvisibles', () => {
it('shows invisibles', () => {
assert.strictEqual(
eslintPluginPrettier.showInvisibles('1 2\n3\t4\r5'),
'1·2⏎3↹4␍5'
);
});
});

describe('generateDifferences', () => {
it('operation: insert', () => {
const differences = eslintPluginPrettier.generateDifferences(
'abc',
'abcdef'
);
assert.deepStrictEqual(differences, [
{ operation: 'insert', offset: 3, insertText: 'def' }
]);
});
it('operation: delete', () => {
const differences = eslintPluginPrettier.generateDifferences(
'abcdef',
'abc'
);
assert.deepStrictEqual(differences, [
{ operation: 'delete', offset: 3, deleteText: 'def' }
]);
});
it('operation: replace', () => {
const differences = eslintPluginPrettier.generateDifferences('abc', 'def');
assert.deepStrictEqual(differences, [
{ operation: 'replace', offset: 0, deleteText: 'abc', insertText: 'def' }
]);
});
});

// ------------------------------------------------------------------------------
// Helpers
// ------------------------------------------------------------------------------
Expand Down
6 changes: 6 additions & 0 deletions yarn.lock
Expand Up @@ -671,6 +671,12 @@ prelude-ls@~1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54"

prettier-linter-helpers@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz#d23d41fe1375646de2d0104d3454a3008802cf7b"
dependencies:
fast-diff "^1.1.2"

prettier@^1.13.0:
version "1.14.3"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.14.3.tgz#90238dd4c0684b7edce5f83b0fb7328e48bd0895"
Expand Down

0 comments on commit bf7c40c

Please sign in to comment.