Update applyFix/es to preserve the dominant line ending for each input.

This commit is contained in:
David Anson 2019-09-14 22:31:08 -07:00
parent 220a1d78a9
commit b77a56255f
2 changed files with 112 additions and 6 deletions

View file

@ -2,9 +2,11 @@
"use strict"; "use strict";
const os = require("os");
// Regular expression for matching common newline characters // Regular expression for matching common newline characters
// See NEWLINES_RE in markdown-it/lib/rules_core/normalize.js // See NEWLINES_RE in markdown-it/lib/rules_core/normalize.js
const newLineRe = /\r\n?|\n/; const newLineRe = /\r\n?|\n/g;
module.exports.newLineRe = newLineRe; module.exports.newLineRe = newLineRe;
// Regular expression for matching common front matter (YAML and TOML) // Regular expression for matching common front matter (YAML and TOML)
@ -404,6 +406,40 @@ module.exports.frontMatterHasTitle =
frontMatterLines.some((line) => frontMatterTitleRe.test(line)); frontMatterLines.some((line) => frontMatterTitleRe.test(line));
}; };
// Gets the most common line ending, falling back to platform default
function getPreferredLineEnding(input) {
let cr = 0;
let lf = 0;
let crlf = 0;
const endings = input.match(newLineRe) || [];
endings.forEach((ending) => {
// eslint-disable-next-line default-case
switch (ending) {
case "\r":
cr++;
break;
case "\n":
lf++;
break;
case "\r\n":
crlf++;
break;
}
});
let preferredLineEnding = null;
if (!cr && !lf && !crlf) {
preferredLineEnding = os.EOL;
} else if ((lf >= crlf) && (lf >= cr)) {
preferredLineEnding = "\n";
} else if (crlf >= cr) {
preferredLineEnding = "\r\n";
} else {
preferredLineEnding = "\r";
}
return preferredLineEnding;
}
module.exports.getPreferredLineEnding = getPreferredLineEnding;
// Normalizes the fields of a fixInfo object // Normalizes the fields of a fixInfo object
function normalizeFixInfo(fixInfo, lineNumber) { function normalizeFixInfo(fixInfo, lineNumber) {
return { return {
@ -415,19 +451,20 @@ function normalizeFixInfo(fixInfo, lineNumber) {
} }
// Fixes the specifide error on a line // Fixes the specifide error on a line
function applyFix(line, fixInfo) { function applyFix(line, fixInfo, lineEnding) {
const { editColumn, deleteCount, insertText } = normalizeFixInfo(fixInfo); const { editColumn, deleteCount, insertText } = normalizeFixInfo(fixInfo);
const editIndex = editColumn - 1; const editIndex = editColumn - 1;
return (deleteCount === -1) ? return (deleteCount === -1) ?
null : null :
line.slice(0, editIndex) + line.slice(0, editIndex) +
insertText + insertText.replace("\n", lineEnding) +
line.slice(editIndex + deleteCount); line.slice(editIndex + deleteCount);
} }
module.exports.applyFix = applyFix; module.exports.applyFix = applyFix;
// Applies as many fixes as possible to the input lines // Applies as many fixes as possible to the input lines
module.exports.applyFixes = function applyFixes(input, errors) { module.exports.applyFixes = function applyFixes(input, errors) {
const lineEnding = getPreferredLineEnding(input);
const lines = input.split(newLineRe); const lines = input.split(newLineRe);
// Normalize fixInfo objects // Normalize fixInfo objects
let fixInfos = errors let fixInfos = errors
@ -484,11 +521,11 @@ module.exports.applyFixes = function applyFixes(input, errors) {
((editIndex + deleteCount) < lastEditIndex) || ((editIndex + deleteCount) < lastEditIndex) ||
(deleteCount === -1) (deleteCount === -1)
) { ) {
lines[lineIndex] = applyFix(lines[lineIndex], fixInfo); lines[lineIndex] = applyFix(lines[lineIndex], fixInfo, lineEnding);
} }
lastLineIndex = lineIndex; lastLineIndex = lineIndex;
lastEditIndex = editIndex; lastEditIndex = editIndex;
}); });
// Return corrected input // Return corrected input
return lines.filter((line) => line !== null).join("\n"); return lines.filter((line) => line !== null).join(lineEnding);
}; };

View file

@ -1,6 +1,7 @@
"use strict"; "use strict";
const fs = require("fs"); const fs = require("fs");
const os = require("os");
const path = require("path"); const path = require("path");
const { URL } = require("url"); const { URL } = require("url");
const md = require("markdown-it")(); const md = require("markdown-it")();
@ -1889,8 +1890,37 @@ module.exports.forEachInlineCodeSpan = function forEachInlineCodeSpan(test) {
test.done(); test.done();
}; };
module.exports.getPreferredLineEnding = function getPreferredLineEnding(test) {
test.expect(17);
const testCases = [
[ "", os.EOL ],
[ "\r", "\r" ],
[ "\n", "\n" ],
[ "\r\n", "\r\n" ],
[ "t\rt\nt", "\n" ],
[ "t\nt\rt", "\n" ],
[ "t\r\nt\nt", "\n" ],
[ "t\nt\r\nt", "\n" ],
[ "t\r\nt\rt", "\r\n" ],
[ "t\rt\r\nt", "\r\n" ],
[ "t\r\nt\rt\nt", "\n" ],
[ "t\r\nt\r\nt\r\nt", "\r\n" ],
[ "t\nt\nt\nt", "\n" ],
[ "t\rt\rt\rt", "\r" ],
[ "t\r\nt\nt\r\nt", "\r\n" ],
[ "t\nt\r\nt\nt", "\n" ],
[ "t\rt\t\rt", "\r" ]
];
testCases.forEach((testCase) => {
const [ input, expected ] = testCase;
const actual = helpers.getPreferredLineEnding(input);
test.equal(actual, expected, "Incorrect line ending returned.");
});
test.done();
};
module.exports.applyFixes = function applyFixes(test) { module.exports.applyFixes = function applyFixes(test) {
test.expect(24); test.expect(27);
const testCases = [ const testCases = [
[ [
"Hello world.", "Hello world.",
@ -2266,6 +2296,45 @@ module.exports.applyFixes = function applyFixes(test) {
} }
], ],
"Hello zorld" "Hello zorld"
],
[
"Hello\nworld\nhello\rworld",
[
{
"fixInfo": {
"lineNumber": 4,
"editColumn": 6,
"insertText": "\n"
}
}
],
"Hello\nworld\nhello\nworld\n"
],
[
"Hello\r\nworld\r\nhello\nworld",
[
{
"fixInfo": {
"lineNumber": 4,
"editColumn": 6,
"insertText": "\n"
}
}
],
"Hello\r\nworld\r\nhello\r\nworld\r\n"
],
[
"Hello\rworld\rhello\nworld",
[
{
"fixInfo": {
"lineNumber": 4,
"editColumn": 6,
"insertText": "\n"
}
}
],
"Hello\rworld\rhello\rworld\r"
] ]
]; ];
testCases.forEach((testCase) => { testCases.forEach((testCase) => {