prettier 0.20.1 → 0.21.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.
@@ -7,7 +7,9 @@ const {
7
7
  indent,
8
8
  softline
9
9
  } = require("../prettier");
10
+
10
11
  const { containsAssignment } = require("../utils");
12
+ const inlineEnsureParens = require("../utils/inlineEnsureParens");
11
13
 
12
14
  const printLoop = (keyword, modifier) => (path, { inlineLoops }, print) => {
13
15
  const [_predicate, statements] = path.getValue().body;
@@ -26,30 +28,13 @@ const printLoop = (keyword, modifier) => (path, { inlineLoops }, print) => {
26
28
  );
27
29
  }
28
30
 
29
- let inlineParts = [
30
- path.call(print, "body", 1),
31
- ` ${keyword} `,
32
- path.call(print, "body", 0)
33
- ];
34
-
35
- // If the return value of this loop expression is being assigned to anything
36
- // besides a local variable then we can't inline the entire expression
37
- // without wrapping it in parentheses. This is because the following
38
- // expressions have different semantic meaning:
39
- //
40
- // hash[:key] = break :value while false
41
- // hash[:key] = while false do break :value end
42
- //
43
- // The first one will not result in an empty hash, whereas the second one
44
- // will result in `{ key: nil }`. In this case what we need to do for the
45
- // first expression to align is wrap it in parens, as in:
46
- //
47
- // hash[:key] = (break :value while false)
48
- if (["assign", "massign"].includes(path.getParentNode().type)) {
49
- inlineParts = ["("].concat(inlineParts).concat(")");
50
- }
51
-
52
- const inlineLoop = concat(inlineParts);
31
+ const inlineLoop = concat(
32
+ inlineEnsureParens(path, [
33
+ path.call(print, "body", 1),
34
+ ` ${keyword} `,
35
+ path.call(print, "body", 0)
36
+ ])
37
+ );
53
38
 
54
39
  // If we're in the modifier form and we're modifying a `begin`, then this is a
55
40
  // special case where we need to explicitly use the modifier form because
@@ -133,12 +133,6 @@ module.exports = {
133
133
  const stringLiteral = path.getValue();
134
134
  const string = stringLiteral.body[0];
135
135
 
136
- // If this string is actually a heredoc, bail out and return to the print
137
- // function for heredocs
138
- if (string.type === "heredoc") {
139
- return path.call(print, "body", 0);
140
- }
141
-
142
136
  // If the string is empty, it will not have any parts, so just print out the
143
137
  // quotes corresponding to the config
144
138
  if (string.body.length === 0) {
@@ -19,7 +19,7 @@ const LANG = {
19
19
  module.exports = (text, _parsers, _opts) => {
20
20
  const child = spawnSync(
21
21
  "ruby",
22
- ["--disable-gems", path.join(__dirname, "./ripper.rb")],
22
+ ["--disable-gems", path.join(__dirname, "./parser.rb")],
23
23
  {
24
24
  env: Object.assign({}, process.env, { LANG }),
25
25
  input: text,
@@ -16,7 +16,9 @@ end
16
16
  require 'json' unless defined?(JSON)
17
17
  require 'ripper'
18
18
 
19
- class RipperJS < Ripper
19
+ module Prettier; end
20
+
21
+ class Prettier::Parser < Ripper
20
22
  attr_reader :source, :lines, :__end__
21
23
 
22
24
  def initialize(source, *args)
@@ -476,7 +478,7 @@ class RipperJS < Ripper
476
478
  def on_comment(body)
477
479
  sexp = { type: :@comment, body: body.chomp, start: lineno, end: lineno }
478
480
 
479
- case RipperJS.lex_state_name(state).gsub('EXPR_', '')
481
+ case Prettier::Parser.lex_state_name(state).gsub('EXPR_', '')
480
482
  when 'END', 'ARG|LABELED', 'ENDFN'
481
483
  last_sexp.merge!(comments: [sexp])
482
484
  when 'CMDARG', 'END|ENDARG', 'ENDARG', 'ARG', 'FNAME|FITEM', 'CLASS',
@@ -565,7 +567,7 @@ class RipperJS < Ripper
565
567
 
566
568
  def on_comment(body)
567
569
  super(body).tap do |sexp|
568
- lex_state = RipperJS.lex_state_name(state).gsub('EXPR_', '')
570
+ lex_state = Prettier::Parser.lex_state_name(state).gsub('EXPR_', '')
569
571
  block_comments << sexp if lex_state == 'BEG'
570
572
  end
571
573
  end
@@ -614,34 +616,39 @@ class RipperJS < Ripper
614
616
 
615
617
  private
616
618
 
617
- def on_embexpr_beg(body)
618
- super(body).tap { |sexp| heredoc_stack << sexp }
619
- end
620
-
621
- def on_embexpr_end(body)
622
- super(body).tap { heredoc_stack.pop }
623
- end
624
-
619
+ # This is a scanner event that represents the beginning of the heredoc.
625
620
  def on_heredoc_beg(beging)
626
- heredoc = { type: :heredoc, beging: beging, start: lineno, end: lineno }
627
- heredoc_stack << heredoc
621
+ {
622
+ type: :heredoc,
623
+ beging: beging,
624
+ start: lineno,
625
+ end: lineno,
626
+ char_start: char_pos - beging.length + 1,
627
+ char_end: char_pos
628
+ }.tap { |node| heredoc_stack << node }
628
629
  end
629
630
 
631
+ # This is a scanner event that represents the end of the heredoc.
630
632
  def on_heredoc_end(ending)
631
- heredoc_stack[-1].merge!(ending: ending.chomp, end: lineno)
633
+ heredoc_stack[-1].merge!(
634
+ ending: ending.chomp, end: lineno, char_end: char_pos
635
+ )
632
636
  end
633
637
 
638
+ # This is a parser event that occurs when you're using a heredoc with a
639
+ # tilde. These are considered `heredoc_dedent` nodes, whereas the hyphen
640
+ # heredocs show up as string literals.
634
641
  def on_heredoc_dedent(string, _width)
635
- heredoc = heredoc_stack.pop
636
- string.merge!(heredoc.slice(:type, :beging, :ending, :start, :end))
642
+ heredoc_stack[-1].merge!(string.slice(:body))
637
643
  end
638
644
 
645
+ # String literals are either going to be a normal string or they're going
646
+ # to be a heredoc with a hyphen.
639
647
  def on_string_literal(string)
640
648
  heredoc = heredoc_stack[-1]
641
649
 
642
- if heredoc && string[:type] != :heredoc && heredoc[:type] == :heredoc
643
- heredoc_stack.pop
644
- string.merge!(heredoc.slice(:type, :beging, :ending, :start, :end))
650
+ if heredoc && heredoc[:ending]
651
+ heredoc_stack.pop.merge!(string.slice(:body))
645
652
  else
646
653
  super
647
654
  end
@@ -731,36 +738,6 @@ class RipperJS < Ripper
731
738
  super(ident, params, def_bodystmt)
732
739
  end
733
740
 
734
- # By default, Ripper parses the expression `lambda { foo }` as a
735
- # `method_add_block` node, so we can't turn it back into `-> { foo }`.
736
- # This module overrides that behavior and reports it back as a `lambda`
737
- # node instead.
738
- def on_method_add_block(invocation, block)
739
- # It's possible to hit a `method_add_block` node without going through
740
- # `method_add_arg` node, ex: `super {}`. In that case we're definitely
741
- # not going to transform into a lambda.
742
- return super if invocation[:type] != :method_add_arg
743
-
744
- fcall, args = invocation[:body]
745
-
746
- # If there are arguments to the `lambda`, that means `lambda` has been
747
- # overridden as a function so we cannot transform it into a `lambda`
748
- # node.
749
- if fcall[:type] != :fcall || args[:type] != :args || args[:body].any?
750
- return super
751
- end
752
-
753
- ident = fcall.dig(:body, 0)
754
- return super if ident[:type] != :@ident || ident[:body] != 'lambda'
755
-
756
- super.tap do |sexp|
757
- params, stmts = block[:body]
758
- params ||= { type: :params, body: [] }
759
-
760
- sexp.merge!(type: :lambda, body: [params, stmts])
761
- end
762
- end
763
-
764
741
  # We need to track for `mlhs_paren` and `massign` nodes whether or not
765
742
  # there was an extra comma at the end of the expression. For some reason
766
743
  # it's not showing up in the AST in an obvious way. In this case we're
@@ -794,7 +771,7 @@ end
794
771
  # stdin and report back the AST over stdout.
795
772
 
796
773
  if $0 == __FILE__
797
- builder = RipperJS.new($stdin.read)
774
+ builder = Prettier::Parser.new($stdin.read)
798
775
  response = builder.parse
799
776
 
800
777
  if !response || builder.error?
@@ -130,7 +130,7 @@ module.exports = {
130
130
  toProcTransform: {
131
131
  type: "boolean",
132
132
  category: "Global",
133
- default: true,
133
+ default: false,
134
134
  description:
135
135
  "When possible, convert blocks to the more concise Symbol#to_proc syntax."
136
136
  }
@@ -5,6 +5,7 @@ const {
5
5
  lineSuffix,
6
6
  literalline
7
7
  } = require("./prettier");
8
+ const isEmptyStmts = require("./utils/isEmptyStmts");
8
9
 
9
10
  const concatBody = (path, opts, print) => concat(path.map(print, "body"));
10
11
 
@@ -65,27 +66,16 @@ const makeArgs = (path, opts, print, argsIndex) => {
65
66
  const heredocs = [];
66
67
 
67
68
  argNodes.body.forEach((argNode, index) => {
68
- let pattern;
69
- let heredoc;
70
-
71
69
  if (argNode.type === "heredoc") {
72
- pattern = [index, "body"];
73
- heredoc = argNode;
74
- } else if (
75
- argNode.type === "string_literal" &&
76
- argNode.body[0].type === "heredoc"
77
- ) {
78
- pattern = [index, "body", 0, "body"];
79
- [heredoc] = argNode.body;
80
- } else {
81
- return;
70
+ const content = path.map.apply(
71
+ path,
72
+ argPattern.slice().concat([index, "body"])
73
+ );
74
+ heredocs.push(
75
+ concat([literalline].concat(content).concat([argNode.ending]))
76
+ );
77
+ args[index] = argNode.beging;
82
78
  }
83
-
84
- const content = path.map.apply(path, argPattern.slice().concat(pattern));
85
- heredocs.push(
86
- concat([literalline].concat(content).concat([heredoc.ending]))
87
- );
88
- args[index] = heredoc.beging;
89
79
  });
90
80
 
91
81
  return { args, heredocs };
@@ -103,6 +93,16 @@ const makeCall = (path, opts, print) => {
103
93
 
104
94
  const makeList = (path, opts, print) => path.map(print, "body");
105
95
 
96
+ const nodeDive = (node, steps) => {
97
+ let current = node;
98
+
99
+ steps.forEach((step) => {
100
+ current = current[step];
101
+ });
102
+
103
+ return current;
104
+ };
105
+
106
106
  const prefix = (value) => (path, opts, print) =>
107
107
  concat([value, path.call(print, "body", 0)]);
108
108
 
@@ -146,10 +146,12 @@ module.exports = {
146
146
  empty,
147
147
  first,
148
148
  hasAncestor,
149
+ isEmptyStmts,
149
150
  literal,
150
151
  makeArgs,
151
152
  makeCall,
152
153
  makeList,
154
+ nodeDive,
153
155
  prefix,
154
156
  printComments,
155
157
  skipAssignIndent,
@@ -0,0 +1,42 @@
1
+ const needsParens = ["args", "assign", "assoc_new", "massign", "opassign"];
2
+
3
+ // If you have a modifier statement (for instance an inline if statement or an
4
+ // inline while loop) there are times when you need to wrap the entire statement
5
+ // in parentheses. This occurs when you have something like:
6
+ //
7
+ // foo[:foo] =
8
+ // if bar?
9
+ // baz
10
+ // end
11
+ //
12
+ // Normally we would shorten this to an inline version, which would result in:
13
+ //
14
+ // foo[:foo] = baz if bar?
15
+ //
16
+ // but this actually has different semantic meaning. The first example will
17
+ // result in a nil being inserted into the hash for the :foo key, whereas the
18
+ // second example will result in an empty hash because the if statement applies
19
+ // to the entire assignment.
20
+ //
21
+ // We can fix this in a couple of ways. We can use the then keyword, as in:
22
+ //
23
+ // foo[:foo] = if bar? then baz end
24
+ //
25
+ // but I haven't actually seen this anywhere. We can also just leave it as is
26
+ // with the multi-line version, but for a short predicate and short value it
27
+ // looks pretty silly. The last option and the one I've selected here is to add
28
+ // parentheses on both sides of the expression, as in:
29
+ //
30
+ // foo[:foo] = (baz if bar?)
31
+ //
32
+ // This approach maintains the nice conciseness of the inline version, while
33
+ // keeping the correct semantic meaning.
34
+ const inlineEnsureParens = (path, parts) => {
35
+ if (needsParens.includes(path.getParentNode().type)) {
36
+ return ["("].concat(parts, ")");
37
+ }
38
+
39
+ return parts;
40
+ };
41
+
42
+ module.exports = inlineEnsureParens;
@@ -0,0 +1,7 @@
1
+ const isEmptyStmts = (node) =>
2
+ node &&
3
+ node.type === "stmts" &&
4
+ node.body.length === 1 &&
5
+ node.body[0].type === "void_stmt";
6
+
7
+ module.exports = isEmptyStmts;
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: prettier
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.20.1
4
+ version: 0.21.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kevin Deisz
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-09-04 00:00:00.000000000 Z
11
+ date: 2020-12-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -75,6 +75,7 @@ files:
75
75
  - src/embed.js
76
76
  - src/nodes.js
77
77
  - src/nodes/alias.js
78
+ - src/nodes/aref.js
78
79
  - src/nodes/args.js
79
80
  - src/nodes/arrays.js
80
81
  - src/nodes/assign.js
@@ -102,12 +103,14 @@ files:
102
103
  - src/nodes/statements.js
103
104
  - src/nodes/strings.js
104
105
  - src/parse.js
106
+ - src/parser.rb
105
107
  - src/prettier.js
106
108
  - src/print.js
107
- - src/ripper.rb
108
109
  - src/ruby.js
109
110
  - src/toProc.js
110
111
  - src/utils.js
112
+ - src/utils/inlineEnsureParens.js
113
+ - src/utils/isEmptyStmts.js
111
114
  homepage: https://github.com/prettier/plugin-ruby#readme
112
115
  licenses:
113
116
  - MIT