|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145 |
- const diff = require('fast-diff');
-
- const LINE_ENDING_RE = /\r\n|[\r\n\u2028\u2029]/;
-
- /**
- * 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 containing { 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: generateDifferences.REPLACE,
- insertText: aheadInsertText,
- deleteText: aheadDeleteText,
- });
- } else if (!aheadDeleteText && aheadInsertText) {
- differences.push({
- offset,
- operation: generateDifferences.INSERT,
- insertText: aheadInsertText,
- });
- } else if (aheadDeleteText && !aheadInsertText) {
- differences.push({
- offset,
- operation: generateDifferences.DELETE,
- deleteText: aheadDeleteText,
- });
- }
- offset += aheadDeleteText.length;
- }
- }
-
- generateDifferences.INSERT = 'insert';
- generateDifferences.DELETE = 'delete';
- generateDifferences.REPLACE = 'replace';
-
- module.exports = {
- showInvisibles,
- generateDifferences,
- };
|