You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

141 lines
3.9 KiB

/**
* @fileoverview An object that caches and applies source code fixes.
* @author Nicholas C. Zakas
* @copyright 2015 Nicholas C. Zakas. All rights reserved.
* See LICENSE file in root directory for full license.
*/
"use strict";
//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------
var debug = require("debug")("eslint:text-fixer");
//------------------------------------------------------------------------------
// Helpers
//------------------------------------------------------------------------------
var BOM = "\uFEFF";
/**
* Compares items in a messages array by line and column.
* @param {Message} a The first message.
* @param {Message} b The second message.
* @returns {int} -1 if a comes before b, 1 if a comes after b, 0 if equal.
* @private
*/
function compareMessagesByLocation(a, b) {
var lineDiff = a.line - b.line;
if (lineDiff === 0) {
return a.column - b.column;
} else {
return lineDiff;
}
}
//------------------------------------------------------------------------------
// Public Interface
//------------------------------------------------------------------------------
/**
* Utility for apply fixes to source code.
* @constructor
*/
function SourceCodeFixer() {
Object.freeze(this);
}
/**
* Applies the fixes specified by the messages to the given text. Tries to be
* smart about the fixes and won't apply fixes over the same area in the text.
* @param {SourceCode} sourceCode The source code to apply the changes to.
* @param {Message[]} messages The array of messages reported by ESLint.
* @returns {Object} An object containing the fixed text and any unfixed messages.
*/
SourceCodeFixer.applyFixes = function(sourceCode, messages) {
debug("Applying fixes");
if (!sourceCode) {
debug("No source code to fix");
return {
fixed: false,
messages: messages,
output: ""
};
}
// clone the array
var remainingMessages = [],
fixes = [],
text = sourceCode.text,
lastFixPos = text.length + 1,
prefix = (sourceCode.hasBOM ? BOM : "");
messages.forEach(function(problem) {
if (problem.hasOwnProperty("fix")) {
fixes.push(problem);
} else {
remainingMessages.push(problem);
}
});
if (fixes.length) {
debug("Found fixes to apply");
// sort in reverse order of occurrence
fixes.sort(function(a, b) {
if (a.fix.range[1] <= b.fix.range[0]) {
return 1;
} else {
return -1;
}
});
// split into array of characters for easier manipulation
var chars = text.split("");
fixes.forEach(function(problem) {
var fix = problem.fix;
var start = fix.range[0];
var end = fix.range[1];
var insertionText = fix.text;
if (end < lastFixPos) {
if (start < 0) {
// Remove BOM.
prefix = "";
start = 0;
}
if (start === 0 && insertionText[0] === BOM) {
// Set BOM.
prefix = BOM;
insertionText = insertionText.slice(1);
}
chars.splice(start, end - start, insertionText);
lastFixPos = start;
} else {
remainingMessages.push(problem);
}
});
return {
fixed: true,
messages: remainingMessages.sort(compareMessagesByLocation),
output: prefix + chars.join("")
};
} else {
debug("No fixes to apply");
return {
fixed: false,
messages: messages,
output: prefix + text
};
}
};
module.exports = SourceCodeFixer;