prettier 1.0.0.pre.rc1 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -12,12 +12,6 @@
12
12
  function printInt(path, _opts, _print) {
13
13
  const { body } = path.getValue();
14
14
 
15
- // If the number is octal and does not contain the optional "o" character
16
- // after the leading 0, add it in.
17
- if (/^0[0-9]/.test(body)) {
18
- return `0o${body.slice(1)}`;
19
- }
20
-
21
15
  // If the number is a base 10 number, is sufficiently large, and is not
22
16
  // already formatted with underscores, then add them in in between the
23
17
  // numbers every three characters starting from the right.
@@ -1,11 +1,4 @@
1
- const {
2
- concat,
3
- group,
4
- ifBreak,
5
- indent,
6
- line,
7
- softline
8
- } = require("../prettier");
1
+ const { concat, group, ifBreak, indent, line } = require("../prettier");
9
2
  const { hasAncestor } = require("../utils");
10
3
 
11
4
  // We can have our params coming in as the first child of the main lambda node,
@@ -13,31 +6,22 @@ const { hasAncestor } = require("../utils");
13
6
  // though it's possible to omit the parens if you only have one argument, we're
14
7
  // going to keep them in no matter what for consistency.
15
8
  function printLambdaParams(path, print) {
16
- const paramsPath = [print, "body", 0];
17
- let paramsNode = path.getValue().body[0];
9
+ let node = path.getValue().body[0];
18
10
 
19
11
  // In this case we had something like -> (foo) { bar } which would mean that
20
12
  // we're looking at a paren node, so we'll descend one level deeper to get at
21
13
  // the actual params node.
22
- if (paramsNode.type !== "params") {
23
- paramsPath.push("body", 0);
24
- paramsNode = paramsNode.body[0];
14
+ if (node.type !== "params") {
15
+ node = node.body[0];
25
16
  }
26
17
 
27
18
  // If we don't have any params at all, then we're just going to bail out and
28
19
  // print nothing. This is to avoid printing an empty set of parentheses.
29
- if (paramsNode.body.every((type) => !type)) {
20
+ if (node.body.every((type) => !type)) {
30
21
  return "";
31
22
  }
32
23
 
33
- return group(
34
- concat([
35
- "(",
36
- indent(concat([softline, path.call.apply(path, paramsPath)])),
37
- softline,
38
- ")"
39
- ])
40
- );
24
+ return path.call(print, "body", 0);
41
25
  }
42
26
 
43
27
  // Lambda nodes represent stabby lambda literals, which can come in a couple of
@@ -12,85 +12,85 @@ const {
12
12
  const { containsAssignment } = require("../utils");
13
13
  const inlineEnsureParens = require("../utils/inlineEnsureParens");
14
14
 
15
- const printLoop = (keyword, modifier) => (path, { rubyModifier }, print) => {
16
- const [_predicate, stmts] = path.getValue().body;
15
+ function printLoop(keyword, modifier) {
16
+ return function printLoopWithOptions(path, { rubyModifier }, print) {
17
+ const [_predicate, stmts] = path.getValue().body;
17
18
 
18
- // If the only statement inside this while loop is a void statement, then we
19
- // can shorten to just displaying the predicate and then a semicolon.
20
- if (
21
- stmts.body.length === 1 &&
22
- stmts.body[0].type === "void_stmt" &&
23
- !stmts.body[0].comments
24
- ) {
25
- return group(
26
- concat([
27
- keyword,
28
- " ",
29
- path.call(print, "body", 0),
30
- ifBreak(softline, "; "),
31
- "end"
19
+ // If the only statement inside this while loop is a void statement, then we
20
+ // can shorten to just displaying the predicate and then a semicolon.
21
+ if (
22
+ stmts.body.length === 1 &&
23
+ stmts.body[0].type === "void_stmt" &&
24
+ !stmts.body[0].comments
25
+ ) {
26
+ return group(
27
+ concat([
28
+ keyword,
29
+ " ",
30
+ path.call(print, "body", 0),
31
+ ifBreak(softline, "; "),
32
+ "end"
33
+ ])
34
+ );
35
+ }
36
+
37
+ const inlineLoop = concat(
38
+ inlineEnsureParens(path, [
39
+ path.call(print, "body", 1),
40
+ ` ${keyword} `,
41
+ path.call(print, "body", 0)
32
42
  ])
33
43
  );
34
- }
35
44
 
36
- const inlineLoop = concat(
37
- inlineEnsureParens(path, [
38
- path.call(print, "body", 1),
39
- ` ${keyword} `,
40
- path.call(print, "body", 0)
41
- ])
42
- );
45
+ // If we're in the modifier form and we're modifying a `begin`, then this is
46
+ // a special case where we need to explicitly use the modifier form because
47
+ // otherwise the semantic meaning changes. This looks like:
48
+ //
49
+ // begin
50
+ // foo
51
+ // end while bar
52
+ //
53
+ // The above is effectively a `do...while` loop (which we don't have in
54
+ // ruby).
55
+ if (modifier && path.getValue().body[1].type === "begin") {
56
+ return inlineLoop;
57
+ }
43
58
 
44
- // If we're in the modifier form and we're modifying a `begin`, then this is a
45
- // special case where we need to explicitly use the modifier form because
46
- // otherwise the semantic meaning changes. This looks like:
47
- //
48
- // begin
49
- // foo
50
- // end while bar
51
- //
52
- // The above is effectively a `do...while` loop (which we don't have in ruby).
53
- if (modifier && path.getValue().body[1].type === "begin") {
54
- return inlineLoop;
55
- }
59
+ const blockLoop = concat([
60
+ concat([
61
+ `${keyword} `,
62
+ align(keyword.length + 1, path.call(print, "body", 0))
63
+ ]),
64
+ indent(concat([softline, path.call(print, "body", 1)])),
65
+ concat([softline, "end"])
66
+ ]);
56
67
 
57
- const blockLoop = concat([
58
- concat([
59
- `${keyword} `,
60
- align(keyword.length + 1, path.call(print, "body", 0))
61
- ]),
62
- indent(concat([softline, path.call(print, "body", 1)])),
63
- concat([softline, "end"])
64
- ]);
68
+ // If we're disallowing inline loops or if the predicate of the loop
69
+ // contains an assignment (in which case we can't know for certain that that
70
+ // assignment doesn't impact the statements inside the loop) then we can't
71
+ // use the modifier form and we must use the block form.
72
+ if (!rubyModifier || containsAssignment(path.getValue().body[0])) {
73
+ return concat([breakParent, blockLoop]);
74
+ }
65
75
 
66
- // If we're disallowing inline loops or if the predicate of the loop contains
67
- // an assignment (in which case we can't know for certain that that
68
- // assignment doesn't impact the statements inside the loop) then we can't
69
- // use the modifier form and we must use the block form.
70
- if (!rubyModifier || containsAssignment(path.getValue().body[0])) {
71
- return concat([breakParent, blockLoop]);
72
- }
76
+ return group(ifBreak(blockLoop, inlineLoop));
77
+ };
78
+ }
73
79
 
74
- return group(ifBreak(blockLoop, inlineLoop));
75
- };
80
+ function printFor(path, opts, print) {
81
+ const [variable, range, stmts] = path.map(print, "body");
76
82
 
77
- // Technically this is incorrect. A `for` loop actually introduces and modifies
78
- // a local variable that then remains in the outer scope. Additionally, if the
79
- // `each` method was somehow missing from the enumerable (it's possible...),
80
- // then this transformation would fail. However - I've never actually seen a
81
- // `for` loop used in production. If someone actually calls me on it, I'll fix
82
- // this, but for now I'm leaving it.
83
- const printFor = (path, opts, print) =>
84
- group(
83
+ return group(
85
84
  concat([
86
- path.call(print, "body", 1),
87
- ".each do |",
88
- path.call(print, "body", 0),
89
- "|",
90
- indent(concat([hardline, path.call(print, "body", 2)])),
85
+ "for ",
86
+ variable,
87
+ " in ",
88
+ range,
89
+ indent(concat([hardline, stmts])),
91
90
  concat([hardline, "end"])
92
91
  ])
93
92
  );
93
+ }
94
94
 
95
95
  module.exports = {
96
96
  while: printLoop("while", false),
@@ -1,4 +1,4 @@
1
- const { concat, group, hardline, indent } = require("../prettier");
1
+ const { concat, group, hardline, indent, line } = require("../prettier");
2
2
  const { first } = require("../utils");
3
3
 
4
4
  function printMethod(offset) {
@@ -48,8 +48,17 @@ function printMethod(offset) {
48
48
  };
49
49
  }
50
50
 
51
+ function printSingleLineMethod(path, opts, print) {
52
+ const [nameDoc, stmtDoc] = path.map(print, "body");
53
+
54
+ return group(
55
+ concat(["def ", nameDoc, " =", indent(group(concat([line, stmtDoc])))])
56
+ );
57
+ }
58
+
51
59
  module.exports = {
52
60
  access_ctrl: first,
53
61
  def: printMethod(0),
54
- defs: printMethod(2)
62
+ defs: printMethod(2),
63
+ defsl: printSingleLineMethod
55
64
  };
@@ -1,4 +1,4 @@
1
- const { concat, group, join, line } = require("../prettier");
1
+ const { concat, group, join, indent, line, softline } = require("../prettier");
2
2
  const { literal } = require("../utils");
3
3
 
4
4
  function printRestParam(symbol) {
@@ -64,6 +64,8 @@ function printParams(path, opts, print) {
64
64
  parts.push(path.call(print, "body", 6));
65
65
  }
66
66
 
67
+ const contents = [join(concat([",", line]), parts)];
68
+
67
69
  // You can put an extra comma at the end of block args between pipes to
68
70
  // change what it does. Below is the difference:
69
71
  //
@@ -73,9 +75,19 @@ function printParams(path, opts, print) {
73
75
  // In ruby 2.5, the excessed comma is indicated by having a 0 in the rest
74
76
  // param position. In ruby 2.6+ it's indicated by having an "excessed_comma"
75
77
  // node in the rest position. Seems odd, but it's true.
76
- const comma = rest === 0 || (rest && rest.type === "excessed_comma");
78
+ if (rest === 0 || (rest && rest.type === "excessed_comma")) {
79
+ contents.push(",");
80
+ }
81
+
82
+ // If the parent node is a paren then we skipped printing the parentheses so
83
+ // that we could handle them here and get nicer formatting.
84
+ if (["lambda", "paren"].includes(path.getParentNode().type)) {
85
+ return group(
86
+ concat(["(", indent(concat([softline].concat(contents))), softline, ")"])
87
+ );
88
+ }
77
89
 
78
- return group(concat([join(concat([",", line]), parts), comma ? "," : ""]));
90
+ return group(concat(contents));
79
91
  }
80
92
 
81
93
  module.exports = {
@@ -48,6 +48,22 @@ function printAryPtn(path, opts, print) {
48
48
  return args;
49
49
  }
50
50
 
51
+ function printFndPtn(path, opts, print) {
52
+ const [constant] = path.getValue().body;
53
+
54
+ let args = [path.call(print, "body", 1)]
55
+ .concat(path.map(print, "body", 2))
56
+ .concat(path.call(print, "body", 3));
57
+
58
+ args = concat(["[", group(join(concat([",", line]), args)), "]"]);
59
+
60
+ if (constant) {
61
+ return concat([path.call(print, "body", 0), args]);
62
+ }
63
+
64
+ return args;
65
+ }
66
+
51
67
  function printHshPtn(path, opts, print) {
52
68
  const [constant, keyValuePairs, keyValueRest] = path.getValue().body;
53
69
  let args = [];
@@ -113,6 +129,7 @@ function printIn(path, opts, print) {
113
129
 
114
130
  module.exports = {
115
131
  aryptn: printAryPtn,
132
+ fndptn: printFndPtn,
116
133
  hshptn: printHshPtn,
117
134
  in: printIn
118
135
  };
@@ -1,21 +1,25 @@
1
1
  const { concat } = require("../prettier");
2
+ const { hasAncestor } = require("../utils");
2
3
 
3
- function isStringContent(node) {
4
- return node.type === "@tstring_content";
4
+ function hasContent(node, pattern) {
5
+ return node.body.some(
6
+ (child) => child.type === "@tstring_content" && pattern.test(child.body)
7
+ );
5
8
  }
6
9
 
7
- function shouldUseBraces(node) {
8
- const first = node.body[0];
9
-
10
- // If the first part of this regex is plain string content and we have a
11
- // space or an =, then we want to use braces because otherwise we could end up
12
- // with an ambiguous operator, e.g. foo / bar/ or foo /=bar/
13
- if (first && isStringContent(first) && [" ", "="].includes(first.body[0])) {
14
- return true;
15
- }
10
+ // If the first part of this regex is plain string content, we have a space
11
+ // or an =, and we're contained within a command or command_call node, then we
12
+ // want to use braces because otherwise we could end up with an ambiguous
13
+ // operator, e.g. foo / bar/ or foo /=bar/
14
+ function forwardSlashIsAmbiguous(path) {
15
+ const node = path.getValue();
16
+ const firstChildNode = node.body[0];
16
17
 
17
- return node.body.some(
18
- (child) => isStringContent(child) && child.body.includes("/")
18
+ return (
19
+ firstChildNode &&
20
+ firstChildNode.type === "@tstring_content" &&
21
+ [" ", "="].includes(firstChildNode.body[0]) &&
22
+ hasAncestor(path, ["command", "command_call"])
19
23
  );
20
24
  }
21
25
 
@@ -28,13 +32,23 @@ function shouldUseBraces(node) {
28
32
  // itself. In that case we switch over to using %r with braces.
29
33
  function printRegexpLiteral(path, opts, print) {
30
34
  const node = path.getValue();
31
- const useBraces = shouldUseBraces(node);
35
+ const docs = path.map(print, "body");
36
+
37
+ // We should use braces if using a forward slash would be ambiguous in the
38
+ // current context or if there's a forward slash in the content of the regexp.
39
+ const useBraces = forwardSlashIsAmbiguous(path) || hasContent(node, /\//);
32
40
 
33
- const parts = [useBraces ? "%r{" : "/"]
34
- .concat(path.map(print, "body"))
35
- .concat([useBraces ? "}" : "/", node.ending.slice(1)]);
41
+ // If we should be using braces but we have braces in the body of the regexp,
42
+ // then we're just going to resort to using whatever the original content was.
43
+ if (useBraces && hasContent(node, /[{}]/)) {
44
+ return concat([node.beging].concat(docs).concat(node.ending));
45
+ }
36
46
 
37
- return concat(parts);
47
+ return concat(
48
+ [useBraces ? "%r{" : "/"]
49
+ .concat(docs)
50
+ .concat(useBraces ? "}" : "/", node.ending.slice(1))
51
+ );
38
52
  }
39
53
 
40
54
  module.exports = {
@@ -45,35 +45,45 @@ function printBodyStmt(path, opts, print) {
45
45
  return group(concat(parts));
46
46
  }
47
47
 
48
+ const argNodeTypes = ["args", "args_add_star", "args_add_block"];
49
+
50
+ function printParen(path, opts, print) {
51
+ const contentNode = path.getValue().body[0];
52
+
53
+ if (!contentNode) {
54
+ return "()";
55
+ }
56
+
57
+ let contentDoc = path.call(print, "body", 0);
58
+
59
+ // If the content is params, we're going to let it handle its own parentheses
60
+ // so that it breaks nicely.
61
+ if (contentNode.type === "params") {
62
+ return contentDoc;
63
+ }
64
+
65
+ // If we have an arg type node as the contents, then it's going to return an
66
+ // array, so we need to explicitly join that content here.
67
+ if (argNodeTypes.includes(contentNode.type)) {
68
+ contentDoc = join(concat([",", line]), contentDoc);
69
+ }
70
+
71
+ return group(
72
+ concat([
73
+ "(",
74
+ indent(concat([softline, contentDoc])),
75
+ concat([softline, ")"])
76
+ ])
77
+ );
78
+ }
79
+
48
80
  module.exports = {
49
81
  "@__end__": (path, _opts, _print) => {
50
82
  const { body } = path.getValue();
51
83
  return concat([trim, "__END__", literalline, body]);
52
84
  },
53
85
  bodystmt: printBodyStmt,
54
- paren: (path, opts, print) => {
55
- if (!path.getValue().body[0]) {
56
- return "()";
57
- }
58
-
59
- let content = path.call(print, "body", 0);
60
-
61
- if (
62
- ["args", "args_add_star", "args_add_block"].includes(
63
- path.getValue().body[0].type
64
- )
65
- ) {
66
- content = join(concat([",", line]), content);
67
- }
68
-
69
- return group(
70
- concat([
71
- "(",
72
- indent(concat([softline, content])),
73
- concat([softline, ")"])
74
- ])
75
- );
76
- },
86
+ paren: printParen,
77
87
  program: (path, opts, print) =>
78
88
  concat([join(hardline, path.map(print, "body")), hardline]),
79
89
  stmts: (path, opts, print) => {
@@ -88,8 +98,8 @@ module.exports = {
88
98
  stmts[0].comments
89
99
  ) {
90
100
  const comments = path.map(
91
- (commentPath, index) => {
92
- stmts[0].comments[index].printed = true;
101
+ (commentPath) => {
102
+ commentPath.getValue().printed = true;
93
103
  return opts.printer.printComment(commentPath);
94
104
  },
95
105
  "body",