Repository: prettier/prettier (JavaScript)
Date: 2017-02-12 03:24:16+00:00
Task type: Feature

Issue:
Add escape hatch
`prettier` at this point is still outputting invalid JavaScript or badly formatted output in many cases. This will go down as time goes but in the meantime, it would be great to be able to disable it for just an expression.

Proposal:

```js
// prettier-ignore-next-block
function a() { 
  iCanDoWhateverIdent
    (a, b,); }
```

and prettier will keep the next AST node as is.


Issue discussion:
When talking to you before you had another compelling use case for an escape hatch:

```js
matrix(
  1, 0, 0,
  0, 1, 0,
  0, 0, 1
)
```

For some projects, there are going to be forms like that that just simply want special formatting.

For the use case in the top post, a comment might work. I'm not sure what to do about the matrix case though.
@jlongster could follow ESLint's example:

```js
/* prettier-disable */
matrix(
  1, 0, 0,
  0, 1, 0,
  0, 0, 1
)
/* prettier-enable */
```

EDIT: Alternatively:

```js
// prettier-ignore-next-statement
matrix(
  1, 0, 0,
  0, 1, 0,
  0, 0, 1
)
```

Because it's being run through an AST, statements should be easy to isolate regardless of formatting.
The thing about `matrix` though is if you are doing that kind of code (heavy math) you're probably doing a lot. So you really don't know to have to add an ignore comment every single time.

But without more config, I'm really not sure about that case.
I'm using scalafmt in a project at work and we've had these cases where it formats things badly. I've at least not found it to be so bad to just read through the code during review or even just with git diff and spotting these edge cases and format them back the way I want and then adding a surrounding comment.

I'd think it's an acceptable workflow, although obviously not ideal, but like you mentioned, these things are really hard/impossible to detect.
Maybe a way to whitelist certain functions at the top of the file?

```js
/* prettier-ignore-function: matrix, matrix2d, etc */
```
> Maybe a way to whitelist certain functions at the top of the file?

Yeah, that could work. It could be annoying to have to do that in every file. I really don't want to add this as a config option though. Nobody has complained about this and the matrix example is really one of the very few I can think of, so we can delay doing that until enough people complain.

I'm not convinced that avoiding bugs is a good enough use case for an expression-level escape hatch, as the more restrictive we can be the better. I'd like to see if anyone else comes up with a use case for it. I'd rather projects report bugs and hold off for a couple weeks until we fix them, instead of leaving a comment that they'll most likely forget to remove later.
Here's an array that describes state transitions. The first two columns represent beginning state and end state, and the objects represent the action.

```js
    return [
      [ null              , AGENT.PENDING     , {role: 'candidate', action: 'apply'}       ],
      [ AGENT.PENDING     , AGENT.REJECTED    , {role: 'agent',     action: 'reject'}      ],
      [ AGENT.PENDING     , HQ.PENDING        , {role: 'agent',     action: 'submit'}      ],
      [ HQ.PENDING        , HQ.REJECTED       , {role: 'hq',        action: 'decline'}     ],
      [ HQ.APPROVED       , HQ.REJECTED       , {role: 'hq',        action: 'decline'}     ],
      [ null              , EMPLOYER.PENDING  , {role: 'hq',        action: 'submit'}      ],
      [ HQ.PENDING        , HQ.APPROVED       , {role: 'hq',        action: 'approve'}     ],
      [ HQ.REJECTED       , HQ.APPROVED       , {role: 'hq',        action: 'approve'}     ],
      [ HQ.APPROVED       , EMPLOYER.PENDING  , {role: 'hq',        action: 'submit'}      ],
      [ EMPLOYER.PENDING  , EMPLOYER.REMOVED  , {role: 'employer',  action: 'decline'}     ],
      [ EMPLOYER.ACCEPTED , EMPLOYER.REMOVED  , {role: 'employer',  action: 'remove'}      ],
      [ EMPLOYER.PENDING  , EMPLOYER.ACCEPTED , {role: 'employer',  action: 'accept'}      ],
      [ EMPLOYER.ACCEPTED , EMPLOYER.HIRED    , {role: 'employer',  action: 'hire'}        ],
      [ EMPLOYER.REMOVED  , EMPLOYER.PENDING  , {role: 'employer',  action: 'undoDecline'} ],
      [ EMPLOYER.REMOVED  , EMPLOYER.ACCEPTED , {role: 'employer',  action: 'undoRemove'}  ]
    ];
```

Tables or tabular layouts are sometimes great for readability. It's a communication tool I wouldn't want to give up. I'm a fan of a one-off escape hatch.

I can see that wouldn't be ideal if you need to do it all the time, like with the matrix example. Perhaps that sort of simple tabular layout could be detected, but I still think a manual escape hatch will be desirable for more complex cases.

Here's an example from the game of life. I think the visual layout here helps communicate the intent of the code: to navigate a cell's neighbors. The layout also makes it easier to verify correctness.

```js
var evolveCell = function(board, w, h, x, y) {
    var neighbors = 0;
    _.each([[-1,-1], [0,-1], [1,-1],
            [-1, 0],         [1, 0],
            [-1, 1], [0, 1], [1, 1]], function(off) {
        neighbors += board.idx( (x + off[0]) % w ).idx( (y + off[1]) % h );
    });
    return neighbors === 3 || (board[x][y] && neighbors === 2) ? 1 : 0;
};
```

As another example of real-world code, here is the only case I've come across in my own code so far where I've used an unusual code formatting: Defining a keyboard layout.

```
const layout = [
  [['1', '!'], ['2', '"'], ['3', '#'], ['4', '@'], ['5', '%'], ['6', '&'], ['7', '/'], ['8', '('], ['9', ')'], ['0', '='], ['+', '?'], ['´', '`'], ['%BACKSPACE']],
  [['q', 'Q'], ['w', 'W'], ['e', 'E'], ['r', 'R'], ['t', 'T'], ['y', 'Y'], ['u', 'U'], ['i', 'I'], ['o', 'O'], ['p', 'P'], ['å', 'Å'], ['¨', '^'], ['~', '|']],
  [['a', 'A'], ['s', 'S'], ['d', 'D'], ['f', 'F'], ['g', 'G'], ['h', 'H'], ['j', 'J'], ['k', 'K'], ['l', 'L'], ['ö', 'Ö'], ['ä', 'Ä'], ["'", '*'], ['%NEWLINE']],
  [['%SHIFT'], ['z', 'Z'], ['x', 'X'], ['c', 'C'], ['v', 'V'], ['b', 'B'], ['n', 'N'], ['m', 'M'], [',', ';'], ['.', ':'], ['-', '_'], ['<', '>'], ['%SHIFT']],
  [[' '], ['%LEFT'], ['%RIGHT'], ['%DONE']],
]
```

Each row represents a real row on the keyboard.


Solution patch:
diff --git a/src/printer.js b/src/printer.js
index e78ec9718059..f3fce17ee3d5 100644
--- a/src/printer.js
+++ b/src/printer.js
@@ -46,6 +46,16 @@ function genericPrint(path, options, printPath) {
     return linesWithoutParens;
   }
 
+  // Escape hatch
+  if (node.comments &&
+      node.comments.length > 0 &&
+      node.comments[0].value.trim() === 'prettier-ignore') {
+    return options.originalText.slice(
+      util.locStart(node),
+      util.locEnd(node)
+    );
+  }
+
   if (
     node.decorators &&
       node.decorators.length > 0 &&


Test patch:
diff --git a/tests/ignore/__snapshots__/jsfmt.spec.js.snap b/tests/ignore/__snapshots__/jsfmt.spec.js.snap
new file mode 100644
index 000000000000..86da34b2f2df
--- /dev/null
+++ b/tests/ignore/__snapshots__/jsfmt.spec.js.snap
@@ -0,0 +1,103 @@
+exports[`test ignore.js 1`] = `
+"function a() {
+  // prettier-ignore
+  var fnString =
+    \'\"\' + this.USE + \' \' + this.STRICT + \'\";\\n\' +
+    this.filterPrefix() +
+    \'var fn=\' + this.generateFunction(\'fn\', \'s,l,a,i\') +
+    extra +
+    this.watchFns() +
+    \'return fn;\';
+
+  // prettier-ignore
+  const identity = Matrix.create(
+    1, 0, 0,
+    0, 1, 0,
+    0, 0, 0
+  );
+
+  // prettier-ignore
+  console.error(
+    \'In order to use \' + prompt + \', you need to configure a \' +
+    \'few environment variables to be able to commit to the \' +
+    \'repository. Follow those steps to get you setup:\\n\' +
+    \'\\n\' +
+    \'Go to https://github.com/settings/tokens/new\\n\' +
+    \' - Fill \"Token description\" with \"\' + prompt + \' for \' +
+      repoSlug + \'\"\\n\' +
+    \' - Check \"public_repo\"\\n\' +
+    \' - Press \"Generate Token\"\\n\' +
+    \'\\n\' +
+    \'In a different tab, go to https://travis-ci.org/\' +
+      repoSlug + \'/settings\\n\' +
+    \' - Make sure \"Build only if .travis.yml is present\" is ON\\n\' +
+    \' - Fill \"Name\" with \"GITHUB_USER\" and \"Value\" with the name of the \' +
+      \'account you generated the token with. Press \"Add\"\\n\' +
+    \'\\n\' +
+    \'Once this is done, commit anything to the repository to restart \' +
+      \'Travis and it should work :)\'
+  );
+
+  // Incorrectly indented on purpose
+      function f</* prettier-ignore */ T    :    B>(
+        a : Array  <   number   > // prettier-ignore
+      ) {
+
+        call(
+          f(         1          )
+          // prettier-ignore
+        )
+      }
+}
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+function a() {
+  // prettier-ignore
+  var fnString =
+    \'\"\' + this.USE + \' \' + this.STRICT + \'\";\\n\' +
+    this.filterPrefix() +
+    \'var fn=\' + this.generateFunction(\'fn\', \'s,l,a,i\') +
+    extra +
+    this.watchFns() +
+    \'return fn;\';
+
+  // prettier-ignore
+  const identity = Matrix.create(
+    1, 0, 0,
+    0, 1, 0,
+    0, 0, 0
+  );
+
+  // prettier-ignore
+  console.error(
+    \'In order to use \' + prompt + \', you need to configure a \' +
+    \'few environment variables to be able to commit to the \' +
+    \'repository. Follow those steps to get you setup:\\n\' +
+    \'\\n\' +
+    \'Go to https://github.com/settings/tokens/new\\n\' +
+    \' - Fill \"Token description\" with \"\' + prompt + \' for \' +
+      repoSlug + \'\"\\n\' +
+    \' - Check \"public_repo\"\\n\' +
+    \' - Press \"Generate Token\"\\n\' +
+    \'\\n\' +
+    \'In a different tab, go to https://travis-ci.org/\' +
+      repoSlug + \'/settings\\n\' +
+    \' - Make sure \"Build only if .travis.yml is present\" is ON\\n\' +
+    \' - Fill \"Name\" with \"GITHUB_USER\" and \"Value\" with the name of the \' +
+      \'account you generated the token with. Press \"Add\"\\n\' +
+    \'\\n\' +
+    \'Once this is done, commit anything to the repository to restart \' +
+      \'Travis and it should work :)\'
+  );
+
+  // Incorrectly indented on purpose
+  function f</* prettier-ignore */ T    :    B>(
+    a : Array  <   number   > // prettier-ignore
+  ) {
+    call(
+      f(         1          )
+      // prettier-ignore
+    );
+  }
+}
+"
+`;
diff --git a/tests/ignore/ignore.js b/tests/ignore/ignore.js
new file mode 100644
index 000000000000..737a0688c1de
--- /dev/null
+++ b/tests/ignore/ignore.js
@@ -0,0 +1,50 @@
+function a() {
+  // prettier-ignore
+  var fnString =
+    '"' + this.USE + ' ' + this.STRICT + '";\n' +
+    this.filterPrefix() +
+    'var fn=' + this.generateFunction('fn', 's,l,a,i') +
+    extra +
+    this.watchFns() +
+    'return fn;';
+
+  // prettier-ignore
+  const identity = Matrix.create(
+    1, 0, 0,
+    0, 1, 0,
+    0, 0, 0
+  );
+
+  // prettier-ignore
+  console.error(
+    'In order to use ' + prompt + ', you need to configure a ' +
+    'few environment variables to be able to commit to the ' +
+    'repository. Follow those steps to get you setup:\n' +
+    '\n' +
+    'Go to https://github.com/settings/tokens/new\n' +
+    ' - Fill "Token description" with "' + prompt + ' for ' +
+      repoSlug + '"\n' +
+    ' - Check "public_repo"\n' +
+    ' - Press "Generate Token"\n' +
+    '\n' +
+    'In a different tab, go to https://travis-ci.org/' +
+      repoSlug + '/settings\n' +
+    ' - Make sure "Build only if .travis.yml is present" is ON\n' +
+    ' - Fill "Name" with "GITHUB_USER" and "Value" with the name of the ' +
+      'account you generated the token with. Press "Add"\n' +
+    '\n' +
+    'Once this is done, commit anything to the repository to restart ' +
+      'Travis and it should work :)'
+  );
+
+  // Incorrectly indented on purpose
+      function f</* prettier-ignore */ T    :    B>(
+        a : Array  <   number   > // prettier-ignore
+      ) {
+
+        call(
+          f(         1          )
+          // prettier-ignore
+        )
+      }
+}
diff --git a/tests/ignore/jsfmt.spec.js b/tests/ignore/jsfmt.spec.js
new file mode 100644
index 000000000000..989047bccc52
--- /dev/null
+++ b/tests/ignore/jsfmt.spec.js
@@ -0,0 +1,1 @@
+run_spec(__dirname);