prettier 0.21.0 → 0.22.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 628031f901d9500c43e16890affeb303afcd9fc6241a5adde35ea59e2e8848da
4
- data.tar.gz: 3e138dd11805ad5de42ebcd3d25541b1b897f183492fd488e91ca906ba51d784
3
+ metadata.gz: c098ce338da8191c1a6a46d6817aa38997aa0fbc318dbc045ae191600654d1a0
4
+ data.tar.gz: d75cc10cf135ffbcbd35f2bb0404dc07b5f645024d57fefdb598cbe99d54bcb1
5
5
  SHA512:
6
- metadata.gz: 7b92c23628247bc53f6ebe5b39f4b7b961a38c0bccc2eb6ec701b52f8be8df7d507ec6dab28b3f48b0600cdafbc9ab51fcf52001c726535f191dab67a1d301aa
7
- data.tar.gz: 0335f2171298ebe46ca5c960a252ec49abc315bfc0438bb75a28ff4204c5dda340532c93af936b27d861ab867308f7f149c3b0eebf26cdd8d3475ba133e52854
6
+ metadata.gz: 7a4c41e7c19215101dee83152d3fd0dab79ee4d43c485254e4f65d963ffbab55e8b68cc5f6a670375552caba832071bfcf609668249139edd81d89a7960274c1
7
+ data.tar.gz: 655bd92259ffb6346cfd1120b8533dfb0ccf37bc78e7d9ed7041fc14c915abdbe7413a451f9bc9061153721110670603e4a4b5fbc638497033b5c0305a17144b
@@ -6,6 +6,21 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) a
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ # [0.22.0] - 2020-12-08
10
+
11
+ ### Changed
12
+
13
+ - [@flyerhzm] - Print method chains by one indentation.
14
+ - [@mmcnl], [@kddeisz] - Handle heredocs and blocks being passed to the same method.
15
+ - [@johncsnyder], [@kddeisz] - Ensure correct formatting when breaking up conditionals with `inlineConditionals: false`.
16
+ - [@Rsullivan00] - Ensure that when ternaries as command arguments get broken into multiple lines we add the necessary parentheses.
17
+ - [@jbielick] - Maintain parse order during if/unless modifier expressions
18
+ - [@flyerhzm] - Slight prettifying of wrapped args if doc length is under a certain value.
19
+ - [@github0013], [@kddeisz] - Ensure `not` keeps parentheses if they are being used.
20
+ - [@jbielick] - Print heredocs consistently.
21
+ - [@kddeisz] - Completely revamp the way we handle comments.
22
+ - [@kddeisz] - Support `hshptn` and the remaining missing pattern matching syntax.
23
+
9
24
  ## [0.21.0] - 2020-12-02
10
25
 
11
26
  ### Changed
@@ -862,7 +877,8 @@ would previously result in `array[]`, but now prints properly.
862
877
 
863
878
  - Initial release 🎉
864
879
 
865
- [unreleased]: https://github.com/prettier/plugin-ruby/compare/v0.21.0...HEAD
880
+ [unreleased]: https://github.com/prettier/plugin-ruby/compare/v0.22.0...HEAD
881
+ [0.22.0]: https://github.com/prettier/plugin-ruby/compare/v0.21.0...v0.22.0
866
882
  [0.21.0]: https://github.com/prettier/plugin-ruby/compare/v0.20.1...v0.21.0
867
883
  [0.20.1]: https://github.com/prettier/plugin-ruby/compare/v0.20.0...v0.20.1
868
884
  [0.20.0]: https://github.com/prettier/plugin-ruby/compare/v0.19.1...v0.20.0
@@ -964,3 +980,4 @@ would previously result in `array[]`, but now prints properly.
964
980
  [@steobrien]: https://github.com/steobrien
965
981
  [@jbielick]: https://github.com/jbielick
966
982
  [@coiti]: https://github.com/coiti
983
+ [@johncsnyder]: https://github.com/johncsnyder
@@ -22,7 +22,7 @@ In order to get printed, the code goes through a couple of transformations. The
22
22
 
23
23
  ### Text to AST
24
24
 
25
- When the prettier process first spins up, it examines which files it's going to print and selects an appropriate plugin for each one. Once selected, it runs that plugin's `parse` function, seen [here](src/parse.js). For the case of the Ruby plugin, that entails spawning a Ruby process that runs [parser.rb](src/parser.rb) with the input code preloaded on stdin.
25
+ When the prettier process first spins up, it examines which files it's going to print and selects an appropriate plugin for each one. Once selected, it runs that plugin's `parse` function, seen [here](src/parser.js). For the case of the Ruby plugin, that entails spawning a Ruby process that runs [parser.rb](src/parser.rb) with the input code preloaded on stdin.
26
26
 
27
27
  `parser.rb` will read the text off of stdin and then feed it to a new `Ripper` instance, which is a Ruby standard library recursive-descent parser. Briefly, the way that `Ripper` works is by tokenizing the input and then matching those tokens against a grammar to form s-expressions. To extend `Ripper`, you overwrite the methods that control how those s-expressions are formed, e.g., to modify the s-expression that is formed when `Ripper` encounters a string literal, you would override the `#on_string_literal` method. Below is an example for seeing that in action.
28
28
 
@@ -71,7 +71,7 @@ Now that the text has been transformed into an AST that we can work with, `parse
71
71
 
72
72
  ### AST to Doc
73
73
 
74
- Once prettier has a working AST, it will take it and call the selected plugin's [`print` function](src/print.js), whose purpose is to convert that AST into prettier's intermediate representation called Docs. It does this by handing the print function a `FastPath` object that keeps track of the state of the printing as it goes, and allows accessing various parts of the AST quickly.
74
+ Once prettier has a working AST, it will take it and call the selected plugin's [`printNode` function](src/printer.js), whose purpose is to convert that AST into prettier's intermediate representation called Docs. It does this by handing the print function a `FastPath` object that keeps track of the state of the printing as it goes, and allows accessing various parts of the AST quickly.
75
75
 
76
76
  Effectively, it walks the AST in the reverse direction from the way `Ripper` built it (top-down instead of bottom-up). The first node that gets passed into the `print` function is the `program` node as that's always on top. Then it is the `program` node's responsibility to recursively call print on its child nodes as it best sees fit.
77
77
 
@@ -168,8 +168,8 @@ While developing, we've built a couple of small utilities for debugging the `pre
168
168
 
169
169
  - `bin/lex [file|source]` - outputs the tokens as ripper sees them
170
170
  - `bin/sexp [file|source]` - outputs the AST that ripper builds before it gets passed back to `prettier`
171
+ - `bin/pragma [file]` - runs the `hasPragma` function against the given input file
171
172
  - `bin/print [file|source]` - outputs the printed source of a Ruby file after running it through `prettier`
172
- - `bin/has-pragma [file]` - runs the `hasPragma` function against the given input file
173
173
 
174
174
  ## Testing
175
175
 
data/README.md CHANGED
@@ -224,6 +224,10 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
224
224
  <td align="center"><a href="https://joshbielick.com/"><img src="https://avatars2.githubusercontent.com/u/1413330?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Josh Bielick</b></sub></a><br /><a href="https://github.com/prettier/plugin-ruby/issues?q=author%3Ajbielick" title="Bug reports">🐛</a> <a href="https://github.com/prettier/plugin-ruby/commits?author=jbielick" title="Code">💻</a></td>
225
225
  <td align="center"><a href="https://github.com/coiti"><img src="https://avatars3.githubusercontent.com/u/27384706?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Román Coitiño</b></sub></a><br /><a href="https://github.com/prettier/plugin-ruby/issues?q=author%3Acoiti" title="Bug reports">🐛</a></td>
226
226
  </tr>
227
+ <tr>
228
+ <td align="center"><a href="https://github.com/mmcnl"><img src="https://avatars2.githubusercontent.com/u/1498727?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Matt McNeil</b></sub></a><br /><a href="https://github.com/prettier/plugin-ruby/issues?q=author%3Ammcnl" title="Bug reports">🐛</a></td>
229
+ <td align="center"><a href="https://github.com/johncsnyder"><img src="https://avatars2.githubusercontent.com/u/9882099?v=4?s=100" width="100px;" alt=""/><br /><sub><b>John Snyder</b></sub></a><br /><a href="https://github.com/prettier/plugin-ruby/issues?q=author%3Ajohncsnyder" title="Bug reports">🐛</a></td>
230
+ </tr>
227
231
  </table>
228
232
 
229
233
  <!-- markdownlint-restore -->
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@prettier/plugin-ruby",
3
- "version": "0.21.0",
3
+ "version": "0.22.0",
4
4
  "description": "prettier plugin for the Ruby programming language",
5
5
  "main": "src/ruby.js",
6
6
  "scripts": {
@@ -24,8 +24,8 @@
24
24
  "devDependencies": {
25
25
  "all-contributors-cli": "^6.14.1",
26
26
  "eslint": "^7.8.1",
27
- "eslint-config-prettier": "^6.10.1",
28
- "husky": "^4.2.5",
27
+ "eslint-config-prettier": "^7.0.0",
28
+ "husky": "^5.0.4",
29
29
  "jest": "^26.0.0",
30
30
  "pretty-quick": "^3.0.0"
31
31
  },
@@ -1,12 +1,15 @@
1
1
  const {
2
2
  concat,
3
+ group,
3
4
  indent,
4
- literalline,
5
+ lineSuffix,
5
6
  mapDoc,
6
7
  markAsRoot,
7
8
  stripTrailingHardline
8
9
  } = require("./prettier");
9
10
 
11
+ const { literalLineNoBreak } = require("./utils");
12
+
10
13
  const parsers = {
11
14
  css: "css",
12
15
  javascript: "babel",
@@ -23,7 +26,7 @@ const replaceNewlines = (doc) =>
23
26
  ? concat(
24
27
  currentDoc
25
28
  .split(/(\n)/g)
26
- .map((v, i) => (i % 2 === 0 ? v : literalline))
29
+ .map((v, i) => (i % 2 === 0 ? v : literalLineNoBreak))
27
30
  )
28
31
  : currentDoc
29
32
  );
@@ -53,19 +56,31 @@ const embed = (path, _print, textToDoc, _opts) => {
53
56
  // into the embedded parser. Get back the doc node.
54
57
  const content = body.map((part) => part.body).join("");
55
58
  const formatted = concat([
56
- literalline,
59
+ literalLineNoBreak,
57
60
  replaceNewlines(stripTrailingHardline(textToDoc(content, { parser })))
58
61
  ]);
59
62
 
60
63
  // If we're using a squiggly heredoc, then we can properly handle indentation
61
64
  // ourselves.
62
65
  if (beging[2] === "~") {
63
- return concat([beging, indent(markAsRoot(formatted)), literalline, ending]);
66
+ return concat([
67
+ beging,
68
+ lineSuffix(
69
+ group(
70
+ concat([indent(markAsRoot(formatted)), literalLineNoBreak, ending])
71
+ )
72
+ )
73
+ ]);
64
74
  }
65
75
 
66
76
  // Otherwise, we need to just assume it's formatted correctly and return the
67
77
  // content as it is.
68
- return markAsRoot(concat([beging, formatted, literalline, ending]));
78
+ return markAsRoot(
79
+ concat([
80
+ beging,
81
+ lineSuffix(group(concat([formatted, literalLineNoBreak, ending])))
82
+ ])
83
+ );
69
84
  };
70
85
 
71
86
  module.exports = embed;
@@ -8,11 +8,13 @@ module.exports = Object.assign(
8
8
  require("./nodes/blocks"),
9
9
  require("./nodes/calls"),
10
10
  require("./nodes/case"),
11
+ require("./nodes/class"),
11
12
  require("./nodes/commands"),
12
13
  require("./nodes/conditionals"),
13
14
  require("./nodes/constants"),
14
15
  require("./nodes/flow"),
15
16
  require("./nodes/hashes"),
17
+ require("./nodes/heredocs"),
16
18
  require("./nodes/hooks"),
17
19
  require("./nodes/ints"),
18
20
  require("./nodes/lambdas"),
@@ -25,7 +27,8 @@ module.exports = Object.assign(
25
27
  require("./nodes/regexp"),
26
28
  require("./nodes/rescue"),
27
29
  require("./nodes/return"),
28
- require("./nodes/scopes"),
29
30
  require("./nodes/statements"),
30
- require("./nodes/strings")
31
+ require("./nodes/strings"),
32
+ require("./nodes/super"),
33
+ require("./nodes/undef")
31
34
  );
@@ -7,49 +7,47 @@ const {
7
7
  line
8
8
  } = require("../prettier");
9
9
 
10
- /* In general, return the printed doc of the argument at the provided index.
11
- * Special handling is given for symbol literals that are not bare words, as we
12
- * convert those into bare words by just pulling out the ident node.
13
- */
14
- function printAliasArgument(path, _opts, print, argIndex) {
10
+ // In general, return the printed doc of the argument at the provided index.
11
+ // Special handling is given for symbol literals that are not bare words, as we
12
+ // convert those into bare words by just pulling out the ident node.
13
+ function printAliasArgument(path, print, argIndex) {
15
14
  const node = path.getValue().body[argIndex];
16
15
 
17
- if (node.type === "symbol_literal" && node.body[0].type === "symbol") {
16
+ if (node.type === "symbol_literal") {
18
17
  // If we're going to descend into the symbol literal to grab out the ident
19
18
  // node, then we need to make sure we copy over any comments as well,
20
19
  // otherwise we could accidentally skip printing them.
21
20
  if (node.comments) {
22
21
  node.comments.forEach((comment) => {
23
- addTrailingComment(node.body[0].body[0], comment);
22
+ addTrailingComment(node.body[0], comment);
24
23
  });
25
24
  }
26
25
 
27
- return path.call(print, "body", argIndex, "body", 0, "body", 0);
26
+ return path.call(print, "body", argIndex, "body", 0);
28
27
  }
29
28
 
30
29
  return path.call(print, "body", argIndex);
31
30
  }
32
31
 
33
- /* The `alias` keyword is used to make a method respond to another name as well
34
- * as the current one. For example, to get the method `foo` to also respond to
35
- * `bar`, you would:
36
- *
37
- * alias bar foo
38
- *
39
- * Now, in the current context you can call `bar` and it will execute the `foo`
40
- * method.
41
- *
42
- * When you're aliasing two methods, you can either provide bare words (like the
43
- * example above) or you can provide symbols (note that this includes dynamic
44
- * symbols like :"foo-#{bar}-baz"). In general, to be consistent with the ruby
45
- * style guide, we prefer bare words:
46
- *
47
- * https://github.com/rubocop-hq/ruby-style-guide#alias-method-lexically
48
- *
49
- * The `alias` node contains two children. The left and right align with the
50
- * arguments passed to the keyword. So, for the above example the left would be
51
- * the symbol literal `bar` and the right could be the symbol literal `foo`.
52
- */
32
+ // The `alias` keyword is used to make a method respond to another name as well
33
+ // as the current one. For example, to get the method `foo` to also respond to
34
+ // `bar`, you would:
35
+ //
36
+ // alias bar foo
37
+ //
38
+ // Now, in the current context you can call `bar` and it will execute the `foo`
39
+ // method.
40
+ //
41
+ // When you're aliasing two methods, you can either provide bare words (like the
42
+ // example above) or you can provide symbols (note that this includes dynamic
43
+ // symbols like :"foo-#{bar}-baz"). In general, to be consistent with the ruby
44
+ // style guide, we prefer bare words:
45
+ //
46
+ // https://github.com/rubocop-hq/ruby-style-guide#alias-method-lexically
47
+ //
48
+ // The `alias` node contains two children. The left and right align with the
49
+ // arguments passed to the keyword. So, for the above example the left would be
50
+ // the symbol literal `bar` and the right could be the symbol literal `foo`.
53
51
  function printAlias(path, opts, print) {
54
52
  const keyword = "alias ";
55
53
 
@@ -57,14 +55,14 @@ function printAlias(path, opts, print) {
57
55
  // If the left child has any comments, then we need to explicitly break this
58
56
  // into two lines
59
57
  path.getValue().body[0].comments ? hardline : line,
60
- printAliasArgument(path, opts, print, 1)
58
+ printAliasArgument(path, print, 1)
61
59
  ]);
62
60
 
63
61
  return group(
64
62
  concat([
65
63
  keyword,
66
- printAliasArgument(path, opts, print, 0),
67
- group(align(keyword, rightSide))
64
+ printAliasArgument(path, print, 0),
65
+ group(align(keyword.length, rightSide))
68
66
  ])
69
67
  );
70
68
  }
@@ -1,17 +1,17 @@
1
1
  const { concat, group, indent, join, line, softline } = require("../prettier");
2
2
 
3
- /* `aref` nodes are when you're pulling a value out of a collection at a
4
- * specific index. Put another way, it's any time you're calling the method
5
- * `#[]`.
6
- *
7
- * The nodes usually contains two children, details below in the
8
- * `printArefField` function. In some cases, you don't necessarily have the
9
- * second child node, because you can call procs with a pretty esoteric syntax.
10
- * In the following example, you wouldn't have a second child, and `"foo"` would
11
- * be the first child.
12
- *
13
- * foo[]
14
- */
3
+ // `aref` nodes are when you're pulling a value out of a collection at a
4
+ // specific index. Put another way, it's any time you're calling the method
5
+ // `#[]`.
6
+ //
7
+ // The nodes usually contains two children, details below in the
8
+ // `printArefField` function. In some cases, you don't necessarily have the
9
+ // second child node, because you can call procs with a pretty esoteric syntax.
10
+ // In the following example, you wouldn't have a second child, and `"foo"` would
11
+ // be the first child.
12
+ //
13
+ // foo[]
14
+ //
15
15
  function printAref(path, opts, print) {
16
16
  const indexNode = path.getValue().body[1];
17
17
 
@@ -22,20 +22,20 @@ function printAref(path, opts, print) {
22
22
  return printArefField(path, opts, print);
23
23
  }
24
24
 
25
- /* `aref_field` nodes are for assigning values into collections at specific
26
- * indices. Put another way, it's any time you're calling the method `#[]=`.
27
- * The `aref_field` node itself is just the left side of the assignment, and
28
- * they're always wrapped in `assign` nodes.
29
- *
30
- * The nodes always contain two children, the name of the array (usually a
31
- * `vcall` node and the index (usually an `args_add_block` node). The
32
- * `args_add_block` is one of a couple nodes that has special handling where its
33
- * printed form is actually an array to make joining easier.
34
- *
35
- * So in the following example, `"foo"` is the array and `["bar"]` is the index.
36
- *
37
- * foo[bar] = baz
38
- */
25
+ // `aref_field` nodes are for assigning values into collections at specific
26
+ // indices. Put another way, it's any time you're calling the method `#[]=`.
27
+ // The `aref_field` node itself is just the left side of the assignment, and
28
+ // they're always wrapped in `assign` nodes.
29
+ //
30
+ // The nodes always contain two children, the name of the array (usually a
31
+ // `vcall` node and the index (usually an `args_add_block` node). The
32
+ // `args_add_block` is one of a couple nodes that has special handling where its
33
+ // printed form is actually an array to make joining easier.
34
+ //
35
+ // So in the following example, `"foo"` is the array and `["bar"]` is the index.
36
+ //
37
+ // foo[bar] = baz
38
+ //
39
39
  function printArefField(path, opts, print) {
40
40
  const [printedArray, printedIndex] = path.map(print, "body");
41
41
 
@@ -9,50 +9,78 @@ const {
9
9
  } = require("../prettier");
10
10
 
11
11
  const toProc = require("../toProc");
12
- const { makeArgs } = require("../utils");
12
+ const { docLength } = require("../utils");
13
13
 
14
14
  module.exports = {
15
15
  arg_paren: (path, opts, print) => {
16
- if (path.getValue().body[0] === null) {
16
+ const argsNode = path.getValue().body[0];
17
+ const { addTrailingCommas } = opts;
18
+
19
+ if (argsNode === null) {
17
20
  return "";
18
21
  }
19
22
 
20
23
  // Here we can skip the entire rest of the method by just checking if it's
21
24
  // an args_forward node, as we're guaranteed that there are no other arg
22
25
  // nodes.
23
- if (path.getValue().body[0].type === "args_forward") {
24
- return "(...)";
26
+ if (argsNode.type === "args_forward") {
27
+ return group(
28
+ concat([
29
+ "(",
30
+ indent(concat([softline, path.call(print, "body", 0)])),
31
+ softline,
32
+ ")"
33
+ ])
34
+ );
25
35
  }
26
36
 
27
- const { addTrailingCommas } = opts;
28
- const { args, heredocs } = makeArgs(path, opts, print, 0);
29
-
30
- const argsNode = path.getValue().body[0];
37
+ const args = path.call(print, "body", 0);
31
38
  const hasBlock = argsNode.type === "args_add_block" && argsNode.body[1];
32
39
 
33
- if (heredocs.length > 1) {
34
- return concat(["(", join(", ", args), ")"].concat(heredocs));
35
- }
40
+ // These are the docs representing the actual arguments, without any
41
+ // parentheses or surrounding lines yet added.
42
+ let argsDocs = [
43
+ join(concat([",", line]), args),
44
+ addTrailingCommas && !hasBlock ? ifBreak(",", "") : ""
45
+ ];
36
46
 
37
- const parenDoc = group(
38
- concat([
39
- "(",
40
- indent(
41
- concat([
42
- softline,
43
- join(concat([",", line]), args),
44
- addTrailingCommas && !hasBlock ? ifBreak(",", "") : ""
45
- ])
46
- ),
47
- concat([softline, ")"])
48
- ])
49
- );
50
-
51
- if (heredocs.length === 1) {
52
- return group(concat([parenDoc].concat(heredocs)));
47
+ // Here we're going to make a determination on whether or not we should put
48
+ // a newline before the first argument. In some cases this makes the
49
+ // appearance a little better. For example, instead of taking this input:
50
+ //
51
+ // foo(arg1, arg2).bar(arg1, arg2).baz(arg1)
52
+ //
53
+ // and transforming it into this:
54
+ //
55
+ // foo(arg1, arg2).bar(arg1, arg2).baz(
56
+ // arg1
57
+ // )
58
+ //
59
+ // it instead gets transformed into this:
60
+ //
61
+ // foo(arg1, arg2).bar(arg1, arg2)
62
+ // .baz(arg1)
63
+ //
64
+ const maxDocLength = 15;
65
+ const firstArgDoc = args[0];
66
+
67
+ // prettier-ignore
68
+ const shouldWrapLine =
69
+ (args.reduce((sum, arg) => sum + docLength(arg), 0) > maxDocLength) ||
70
+ (args.length == 1 && firstArgDoc.type === "group" && docLength(firstArgDoc) > maxDocLength) ||
71
+ (firstArgDoc.type === "concat" && firstArgDoc.parts.some((part) => part.type === "group"));
72
+
73
+ // Here we're going to get all of the docs representing the doc that's
74
+ // inside the parentheses.
75
+ if (shouldWrapLine) {
76
+ argsDocs = [indent(concat([softline].concat(argsDocs))), softline];
77
+ } else {
78
+ argsDocs = [indent(concat(argsDocs))];
53
79
  }
54
80
 
55
- return parenDoc;
81
+ // Now here we return a doc that represents the whole grouped expression,
82
+ // including the surrouding parentheses.
83
+ return group(concat(["("].concat(argsDocs).concat(")")));
56
84
  },
57
85
  args: (path, opts, print) => {
58
86
  const args = path.map(print, "body");