Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add option to enforce certain line endings #5327

Merged
merged 26 commits into from
Nov 6, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
f782743
Temporary replace editorconfig-to-prettier with a fork that supports eol
kachkaev Nov 2, 2018
e65c3d5
Add eol option to config
kachkaev Nov 2, 2018
5ab8b48
Include eol option logic into document printing
kachkaev Nov 2, 2018
f1399f7
Slightly change how eol option is self-documented
kachkaev Nov 4, 2018
d5c00d2
Add docs for eol option
kachkaev Nov 4, 2018
4b783ef
Prettify docs
kachkaev Nov 4, 2018
8d96454
Add three test files to check end_of_line picking from .editorconfig
kachkaev Nov 4, 2018
627756b
Applying a controversial patch to runPrettier() in integration tests …
kachkaev Nov 4, 2018
77e0cd3
Add unit tests for eol option
kachkaev Nov 4, 2018
eb22c1e
Replace [[CR]] with /*CR*/ in snapshots
kachkaev Nov 4, 2018
ce4bcd6
Try removing \r while calling insertPragma to avoid having \r in jest…
kachkaev Nov 4, 2018
9e3374a
Mitigate use of os.EOL by jest-docblock
kachkaev Nov 4, 2018
1a5c747
Merge commit 'e17512adcd21c23b3bbf3c8308c2f988ec89fb56' into eol-option
kachkaev Nov 4, 2018
02e2c54
Rename eol to endOfLine
kachkaev Nov 5, 2018
336384f
Add a couple of tests for markdown files
kachkaev Nov 5, 2018
a081257
Rename eol to endOfLine in docs
kachkaev Nov 5, 2018
442cab2
Do not use cr in markdown tests because /*CR*/ without \n affects syn…
kachkaev Nov 5, 2018
6e03f03
Use css for testing endOfLine due to issues with \r being converted i…
kachkaev Nov 5, 2018
a4bd8fe
Replace /*CR*/ with \r in Jest snapshots when AST_COMPARE
kachkaev Nov 5, 2018
aeb4bef
Add a couple of tests for markdown files
kachkaev Nov 5, 2018
6f33744
Slightly improve descriptions of option values in docs
kachkaev Nov 5, 2018
baeec0d
Slightly improve descriptions of option values in --help
kachkaev Nov 5, 2018
a3b5506
Upgrade editorconfig-to-prettier to 0.1.0 🎉
kachkaev Nov 5, 2018
5233613
Remove integrity value from yarn.lock for consistency (this field was…
kachkaev Nov 5, 2018
3007428
Change order of option values both in docs and cli (auto,cr,crlf,lf →…
kachkaev Nov 5, 2018
b558013
docs: hide docs until release
ikatyang Nov 6, 2018
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ prettier.resolveConfig(filePath).then(options => {

If `options.editorconfig` is `true` and an [`.editorconfig` file](http://editorconfig.org/) is in your project, Prettier will parse it and convert its properties to the corresponding prettier configuration. This configuration will be overridden by `.prettierrc`, etc. Currently, the following EditorConfig properties are supported:

- `end_of_line`
- `indent_style`
- `indent_size`/`tab_width`
- `max_line_length`
Expand Down
42 changes: 42 additions & 0 deletions docs/options.md
Original file line number Diff line number Diff line change
Expand Up @@ -271,3 +271,45 @@ Valid options:
| Default | CLI Override | API Override |
| ------------ | ----------------------------------------------------------- | ----------------------------------------------------------- |
| `"preserve"` | <code>--prose-wrap <always&#124;never&#124;preserve></code> | <code>proseWrap: "<always&#124;never&#124;preserve>"</code> |

<!--TODO(1.15)

## End of Line

_available in 1.15.0+_

For historical reasons, there exist two commonly used flavors of line endings in text files. That is `\n` (or `LF` for _Line Feed_) and `\r\n` (or `CRLF` for _Carriage Return + Line Feed_).
The former is common on Linux and macOS, while the latter is prevalent on Windows.
Some details explaining why it is so [can be found on Wikipedia](https://en.wikipedia.org/wiki/Newline).

By default, Prettier preserves a flavor of line endings a given file has already used.
It also converts mixed line endings within one file to what it finds at the end of the first line.

When people collaborate on a project from different operating systems, it becomes easy to end up with mixed line endings in the central git repository.
It is also possible for Windows users to accidentally change line endings in an already committed file from `LF` to `CRLF`.
Doing so produces a large `git diff`, and if it get unnoticed during code review, all line-by-line history for the file (`git blame`) gets lost.

If you want to make sure that your git repository only contains Linux-style line endings in files covered by Prettier:

1. Set `endOfLine` option to `lf`
1. Configure [a pre-commit hook](./precommit.md) that will run Prettier
1. Configure Prettier to run in your CI pipeline (e.g. using [`prettier-check` npm package](https://www.npmjs.com/package/prettier-check))
1. Ask Windows users to run `git config core.autocrlf false` before working on your repo so that git did not convert `LF` to `CRLF` on checkout.
Alternatively, you can add `* text=auto eol=lf` to the repo's `.gitattributes` file to achieve this.

All modern text editors in all operating systems are able to correctly display line endings when `\n` (`LF`) is used.
However, old versions of Notepad for Windows will visually squash such lines into one.

Valid options:

- `"auto"` - Maintain existing line endings
(mixed values within one file are normalised by looking at what's used after the first line)
- `"lf"` – Line Feed only (`\n`), common on Linux and macOS as well as inside git repos
- `"crlf"` - Carriage Return + Line Feed characters (`\r\n`), common on Windows
- `"cr"` - Carriage Return character only (`\r`), used very rarely

| Default | CLI Override | API Override |
| -------- | ----------------------------------------------------------- | ---------------------------------------------------------- |
| `"auto"` | <code>--end-of-line <auto&#124;cr&#124;crlf&#124;lf></code> | <code>endOfLine: "<auto&#124;cr&#124;crlf&#124;lf>"</code> |

-->
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
"dedent": "0.7.0",
"diff": "3.2.0",
"editorconfig": "0.15.2",
"editorconfig-to-prettier": "0.0.6",
"editorconfig-to-prettier": "0.1.0",
"emoji-regex": "6.5.1",
"escape-string-regexp": "1.0.5",
"esutils": "2.0.2",
Expand Down
25 changes: 25 additions & 0 deletions src/common/end-of-line.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
"use strict";

function guessEndOfLine(text) {
const index = text.indexOf("\r");
if (index >= 0) {
return text.charAt(index + 1) === "\n" ? "crlf" : "cr";
}
return "lf";
}

function convertEndOfLineToChars(value) {
switch (value) {
case "cr":
return "\r";
case "crlf":
return "\r\n";
default:
return "\n";
}
}

module.exports = {
guessEndOfLine,
convertEndOfLineToChars
};
3 changes: 2 additions & 1 deletion src/doc/doc-printer.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"use strict";

const { getStringWidth } = require("../common/util");
const { convertEndOfLineToChars } = require("../common/end-of-line");
const { concat, fill, cursor } = require("./doc-builders");

/** @type {{[groupId: PropertyKey]: MODE}} */
Expand Down Expand Up @@ -241,7 +242,7 @@ function printDocToString(doc, options) {
groupModeMap = {};

const width = options.printWidth;
const newLine = options.newLine || "\n";
const newLine = convertEndOfLineToChars(options.endOfLine);
let pos = 0;
// cmds is basically a stack. We've turned a recursive call into a
// while loop which is much faster. The while loop below adds new
Expand Down
10 changes: 6 additions & 4 deletions src/language-js/pragma.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,12 @@ function hasPragma(text) {
function insertPragma(text) {
const parsedDocblock = docblock.parseWithComments(docblock.extract(text));
const pragmas = Object.assign({ format: "" }, parsedDocblock.pragmas);
const newDocblock = docblock.print({
pragmas,
comments: parsedDocblock.comments.replace(/^(\s+?\r?\n)+/, "") // remove leading newlines
});
const newDocblock = docblock
.print({
pragmas,
comments: parsedDocblock.comments.replace(/^(\s+?\r?\n)+/, "") // remove leading newlines
})
.replace(/(\r\n|\r)/g, "\n"); // normalise newlines (mitigate use of os.EOL by jest-docblock)
const strippedText = docblock.strip(text);
const separatingNewlines = strippedText.startsWith("\n") ? "\n" : "\n\n";
return newDocblock + separatingNewlines + strippedText;
Expand Down
30 changes: 30 additions & 0 deletions src/main/core-options.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,36 @@ const options = {
`,
cliCategory: CATEGORY_EDITOR
},
endOfLine: {
since: "1.15.0",
category: CATEGORY_GLOBAL,
type: "choice",
default: "auto",
description: "Which end of line characters to apply.",
choices: [
{
value: "auto",
description: dedent`
Maintain existing
(mixed values within one file are normalised by looking at what's used after the first line)
ikatyang marked this conversation as resolved.
Show resolved Hide resolved
`
},
{
value: "lf",
description:
"Line Feed only (\\n), common on Linux and macOS as well as inside git repos"
},
{
value: "crlf",
description:
"Carriage Return + Line Feed characters (\\r\\n), common on Windows"
},
{
value: "cr",
description: "Carriage Return character only (\\r), used very rarely"
}
]
},
filepath: {
since: "1.4.0",
category: CATEGORY_SPECIAL,
Expand Down
18 changes: 8 additions & 10 deletions src/main/core.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ const massageAST = require("./massage-ast");
const comments = require("./comments");
const parser = require("./parser");
const printAstToDoc = require("./ast-to-doc");
const {
guessEndOfLine,
convertEndOfLineToChars
} = require("../common/end-of-line");
const rangeUtil = require("./range-util");
const privateUtil = require("../common/util");
const {
Expand All @@ -18,14 +22,6 @@ const UTF8BOM = 0xfeff;

const CURSOR = Symbol("cursor");

function guessLineEnding(text) {
const index = text.indexOf("\n");
if (index >= 0 && text.charAt(index - 1) === "\r") {
return "\r\n";
}
return "\n";
}

function ensureAllCommentsPrinted(astComments) {
if (!astComments) {
return;
Expand Down Expand Up @@ -84,7 +80,9 @@ function coreFormat(text, opts, addAlignmentSize) {

const astComments = attachComments(text, ast, opts);
const doc = printAstToDoc(ast, opts, addAlignmentSize);
opts.newLine = guessLineEnding(originalText);
if (opts.endOfLine === "auto") {
opts.endOfLine = guessEndOfLine(originalText);
}

const result = printDocToString(doc, opts);

Expand All @@ -97,7 +95,7 @@ function coreFormat(text, opts, addAlignmentSize) {
result.cursorNodeStart -= result.formatted.indexOf(trimmed);
}

result.formatted = trimmed + opts.newLine;
result.formatted = trimmed + convertEndOfLineToChars(opts.endOfLine);
}

if (opts.cursorOffset >= 0) {
Expand Down
83 changes: 83 additions & 0 deletions tests/end_of_line/__snapshots__/jsfmt.spec.js.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`usingCrlf.js - babylon-verify 1`] = `
function f() {
console.log("testing line endings");
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
function f() {
console.log("testing line endings");
}

`;

exports[`usingCrlf.js - babylon-verify 2`] = `
function f() {
console.log("testing line endings");
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
function f() {/*CR*/ console.log("testing line endings");/*CR*/}/*CR*/
`;

exports[`usingCrlf.js - babylon-verify 3`] = `
function f() {
console.log("testing line endings");
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
function f() {/*CR*/
console.log("testing line endings");/*CR*/
}/*CR*/

`;

exports[`usingCrlf.js - babylon-verify 4`] = `
function f() {
console.log("testing line endings");
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
function f() {
console.log("testing line endings");
}

`;

exports[`usingLf.js - babylon-verify 1`] = `
function f() {
console.log("testing line endings");
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
function f() {
console.log("testing line endings");
}

`;

exports[`usingLf.js - babylon-verify 2`] = `
function f() {
console.log("testing line endings");
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
function f() {/*CR*/ console.log("testing line endings");/*CR*/}/*CR*/
`;

exports[`usingLf.js - babylon-verify 3`] = `
function f() {
console.log("testing line endings");
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
function f() {/*CR*/
console.log("testing line endings");/*CR*/
}/*CR*/

`;

exports[`usingLf.js - babylon-verify 4`] = `
function f() {
console.log("testing line endings");
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
function f() {
console.log("testing line endings");
}

`;
4 changes: 4 additions & 0 deletions tests/end_of_line/jsfmt.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
run_spec(__dirname, ["babylon"]);
run_spec(__dirname, ["babylon"], { endOfLine: "cr" });
run_spec(__dirname, ["babylon"], { endOfLine: "crlf" });
run_spec(__dirname, ["babylon"], { endOfLine: "lf" });
3 changes: 3 additions & 0 deletions tests/end_of_line/usingCrlf.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
function f() {
console.log("testing line endings");
}
3 changes: 3 additions & 0 deletions tests/end_of_line/usingLf.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
function f() {
console.log("testing line endings");
}
75 changes: 75 additions & 0 deletions tests/end_of_line_in_css/__snapshots__/jsfmt.spec.js.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`usingCrlf.css - css-verify 1`] = `
.foo {
margin: 42px;
}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.foo {
margin: 42px;
}

`;

exports[`usingCrlf.css - css-verify 2`] = `
.foo {
margin: 42px;
}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.foo {/*CR*/ margin: 42px;/*CR*/}/*CR*/
`;

exports[`usingCrlf.css - css-verify 3`] = `
.foo {
margin: 42px;
}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.foo {/*CR*/
margin: 42px;/*CR*/
}/*CR*/

`;

exports[`usingCrlf.css - css-verify 4`] = `
.foo {
margin: 42px;
}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.foo {
margin: 42px;
}

`;

exports[`usingLf.css - css-verify 1`] = `
.foo {
margin: 42px;
}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.foo {
margin: 42px;
}

`;

exports[`usingLf.css - css-verify 2`] = `
.foo {
margin: 42px;
}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.foo {/*CR*/ margin: 42px;/*CR*/}/*CR*/
`;

exports[`usingLf.css - css-verify 3`] = `
.foo {
margin: 42px;
}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.foo {/*CR*/
margin: 42px;/*CR*/
}/*CR*/

`;

exports[`usingLf.css - css-verify 4`] = `
.foo {
margin: 42px;
}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.foo {
margin: 42px;
}

`;
4 changes: 4 additions & 0 deletions tests/end_of_line_in_css/jsfmt.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
run_spec(__dirname, ["css"]);
run_spec(__dirname, ["css"], { endOfLine: "cr" });
run_spec(__dirname, ["css"], { endOfLine: "crlf" });
run_spec(__dirname, ["css"], { endOfLine: "lf" });
3 changes: 3 additions & 0 deletions tests/end_of_line_in_css/usingCrlf.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.foo {
margin: 42px;
}
3 changes: 3 additions & 0 deletions tests/end_of_line_in_css/usingLf.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.foo {
margin: 42px;
}
Loading