syntax_tree 2.1.0 → 2.3.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.
@@ -414,12 +414,19 @@ module SyntaxTree
414
414
  # (false | untyped) block
415
415
  # ) -> Args
416
416
  def on_args_add_block(arguments, block)
417
+ # First, see if there is an & operator that could potentially be
418
+ # associated with the block part of this args_add_block. If there is not,
419
+ # then just return the arguments.
417
420
  operator = find_token(Op, "&", consume: false)
418
-
419
- # If we can't find the & operator, then there's no block to add to the
420
- # list, so we're just going to return the arguments as-is.
421
421
  return arguments unless operator
422
422
 
423
+ # If there are any arguments and the operator we found from the list is
424
+ # not after them, then we're going to return the arguments as-is because
425
+ # we're looking at an & that occurs before the arguments are done.
426
+ if arguments.parts.any? && operator.location.start_char < arguments.location.end_char
427
+ return arguments
428
+ end
429
+
423
430
  # Now we know we have an & operator, so we're going to delete it from the
424
431
  # list of tokens to make sure it doesn't get confused with anything else.
425
432
  tokens.delete(operator)
@@ -428,13 +435,6 @@ module SyntaxTree
428
435
  location = operator.location
429
436
  location = operator.location.to(block.location) if block
430
437
 
431
- # If there are any arguments and the operator we found from the list is
432
- # not after them, then we're going to return the arguments as-is because
433
- # we're looking at an & that occurs before the arguments are done.
434
- if arguments.parts.any? && location.start_char < arguments.location.end_char
435
- return arguments
436
- end
437
-
438
438
  # Otherwise, we're looking at an actual block argument (with or without a
439
439
  # block, which could be missing because it could be a bare & since 3.1.0).
440
440
  arg_block = ArgBlock.new(value: block, location: location)
@@ -516,12 +516,46 @@ module SyntaxTree
516
516
  def on_aryptn(constant, requireds, rest, posts)
517
517
  parts = [constant, *requireds, rest, *posts].compact
518
518
 
519
+ # If there aren't any parts (no constant, no positional arguments), then
520
+ # we're matching an empty array. In this case, we're going to look for the
521
+ # left and right brackets explicitly. Otherwise, we'll just use the bounds
522
+ # of the various parts.
523
+ location =
524
+ if parts.empty?
525
+ find_token(LBracket).location.to(find_token(RBracket).location)
526
+ else
527
+ parts[0].location.to(parts[-1].location)
528
+ end
529
+
530
+ # If there's the optional then keyword, then we'll delete that and use it
531
+ # as the end bounds of the location.
532
+ if token = find_token(Kw, "then", consume: false)
533
+ tokens.delete(token)
534
+ location = location.to(token.location)
535
+ end
536
+
537
+ # If there is a plain *, then we're going to fix up the location of it
538
+ # here because it currently doesn't have anything to use for its precise
539
+ # location. If we hit a comma, then we've gone too far.
540
+ if rest.is_a?(VarField) && rest.value.nil?
541
+ tokens.rindex do |token|
542
+ case token
543
+ in Op[value: "*"]
544
+ rest = VarField.new(value: nil, location: token.location)
545
+ break
546
+ in Comma
547
+ break
548
+ else
549
+ end
550
+ end
551
+ end
552
+
519
553
  AryPtn.new(
520
554
  constant: constant,
521
555
  requireds: requireds || [],
522
556
  rest: rest,
523
557
  posts: posts || [],
524
- location: parts[0].location.to(parts[-1].location)
558
+ location: location
525
559
  )
526
560
  end
527
561
 
@@ -761,8 +795,14 @@ module SyntaxTree
761
795
  # (:call | Backtick | Const | Ident | Op) message
762
796
  # ) -> Call
763
797
  def on_call(receiver, operator, message)
764
- ending = message
765
- ending = operator if message == :call
798
+ ending =
799
+ if message != :call
800
+ message
801
+ elsif operator != :"::"
802
+ operator
803
+ else
804
+ receiver
805
+ end
766
806
 
767
807
  Call.new(
768
808
  receiver: receiver,
@@ -1367,15 +1407,35 @@ module SyntaxTree
1367
1407
  # VarField right
1368
1408
  # ) -> FndPtn
1369
1409
  def on_fndptn(constant, left, values, right)
1370
- beginning = constant || find_token(LBracket)
1371
- ending = find_token(RBracket)
1410
+ # The opening of this find pattern is either going to be a left bracket, a
1411
+ # right left parenthesis, or the left splat. We're going to use this to
1412
+ # determine how to find the closing of the pattern, as well as determining
1413
+ # the location of the node.
1414
+ opening =
1415
+ find_token(LBracket, consume: false) ||
1416
+ find_token(LParen, consume: false) ||
1417
+ left
1418
+
1419
+ # The closing is based on the opening, which is either the matched
1420
+ # punctuation or the right splat.
1421
+ closing =
1422
+ case opening
1423
+ in LBracket
1424
+ tokens.delete(opening)
1425
+ find_token(RBracket)
1426
+ in LParen
1427
+ tokens.delete(opening)
1428
+ find_token(RParen)
1429
+ else
1430
+ right
1431
+ end
1372
1432
 
1373
1433
  FndPtn.new(
1374
1434
  constant: constant,
1375
1435
  left: left,
1376
1436
  values: values,
1377
1437
  right: right,
1378
- location: beginning.location.to(ending.location)
1438
+ location: (constant || opening).location.to(closing.location)
1379
1439
  )
1380
1440
  end
1381
1441
 
@@ -1462,6 +1522,7 @@ module SyntaxTree
1462
1522
  @heredocs[-1] = Heredoc.new(
1463
1523
  beginning: heredoc.beginning,
1464
1524
  ending: heredoc.ending,
1525
+ dedent: width,
1465
1526
  parts: string.parts,
1466
1527
  location: heredoc.location
1467
1528
  )
@@ -1475,6 +1536,7 @@ module SyntaxTree
1475
1536
  @heredocs[-1] = Heredoc.new(
1476
1537
  beginning: heredoc.beginning,
1477
1538
  ending: value.chomp,
1539
+ dedent: heredoc.dedent,
1478
1540
  parts: heredoc.parts,
1479
1541
  location:
1480
1542
  Location.new(
@@ -1495,13 +1557,30 @@ module SyntaxTree
1495
1557
  # (nil | VarField) keyword_rest
1496
1558
  # ) -> HshPtn
1497
1559
  def on_hshptn(constant, keywords, keyword_rest)
1498
- parts = [constant, keywords, keyword_rest].flatten(2).compact
1560
+ # Create an artificial VarField if we find an extra ** on the end
1561
+ if !keyword_rest && (token = find_token(Op, "**", consume: false))
1562
+ tokens.delete(token)
1563
+ keyword_rest = VarField.new(value: nil, location: token.location)
1564
+ end
1565
+
1566
+ # Delete the optional then keyword
1567
+ if token = find_token(Kw, "then", consume: false)
1568
+ tokens.delete(token)
1569
+ end
1570
+
1571
+ parts = [constant, *keywords&.flatten(1), keyword_rest].compact
1572
+ location =
1573
+ if parts.any?
1574
+ parts[0].location.to(parts[-1].location)
1575
+ else
1576
+ find_token(LBrace).location.to(find_token(RBrace).location)
1577
+ end
1499
1578
 
1500
1579
  HshPtn.new(
1501
1580
  constant: constant,
1502
- keywords: keywords,
1581
+ keywords: keywords || [],
1503
1582
  keyword_rest: keyword_rest,
1504
- location: parts[0].location.to(parts[-1].location)
1583
+ location: location
1505
1584
  )
1506
1585
  end
1507
1586
 
@@ -2000,12 +2079,16 @@ module SyntaxTree
2000
2079
  keyword_rest,
2001
2080
  block
2002
2081
  )
2082
+ # This is to make it so that required keyword arguments
2083
+ # have a `nil` for the value instead of a `false`.
2084
+ keywords&.map! { |(key, value)| [key, value || nil] }
2085
+
2003
2086
  parts = [
2004
2087
  *requireds,
2005
2088
  *optionals&.flatten(1),
2006
2089
  rest,
2007
2090
  *posts,
2008
- *keywords&.flat_map { |(key, value)| [key, value || nil] },
2091
+ *keywords&.flatten(1),
2009
2092
  (keyword_rest if keyword_rest != :nil),
2010
2093
  (block if block != :&)
2011
2094
  ].compact
@@ -2626,6 +2709,7 @@ module SyntaxTree
2626
2709
  Heredoc.new(
2627
2710
  beginning: heredoc.beginning,
2628
2711
  ending: heredoc.ending,
2712
+ dedent: heredoc.dedent,
2629
2713
  parts: string.parts,
2630
2714
  location: heredoc.location
2631
2715
  )
@@ -2837,19 +2921,17 @@ module SyntaxTree
2837
2921
  # parentheses they don't get reported as a paren node for some reason.
2838
2922
 
2839
2923
  beginning = find_token(Kw, "not")
2840
- ending = statement
2841
-
2842
- range = beginning.location.end_char...statement.location.start_char
2843
- paren = source[range].include?("(")
2924
+ ending = statement || beginning
2925
+ parentheses = source[beginning.location.end_char] == "("
2844
2926
 
2845
- if paren
2927
+ if parentheses
2846
2928
  find_token(LParen)
2847
2929
  ending = find_token(RParen)
2848
2930
  end
2849
2931
 
2850
2932
  Not.new(
2851
2933
  statement: statement,
2852
- parentheses: paren,
2934
+ parentheses: parentheses,
2853
2935
  location: beginning.location.to(ending.location)
2854
2936
  )
2855
2937
  else
@@ -2981,7 +3063,7 @@ module SyntaxTree
2981
3063
  # ) -> VarField
2982
3064
  def on_var_field(value)
2983
3065
  location =
2984
- if value
3066
+ if value && value != :nil
2985
3067
  value.location
2986
3068
  else
2987
3069
  # You can hit this pattern if you're assigning to a splat using
@@ -3180,6 +3262,7 @@ module SyntaxTree
3180
3262
  Heredoc.new(
3181
3263
  beginning: heredoc.beginning,
3182
3264
  ending: heredoc.ending,
3265
+ dedent: heredoc.dedent,
3183
3266
  parts: xstring.parts,
3184
3267
  location: heredoc.location
3185
3268
  )
@@ -79,7 +79,7 @@ class PrettyPrint
79
79
  end
80
80
 
81
81
  def pretty_print(q)
82
- q.group(2, "align([", "])") do
82
+ q.group(2, "align#{indent}([", "])") do
83
83
  q.seplist(contents) { |content| q.pp(content) }
84
84
  end
85
85
  end
@@ -161,7 +161,7 @@ class PrettyPrint
161
161
  end
162
162
 
163
163
  def pretty_print(q)
164
- q.group(2, "group([", "])") do
164
+ q.group(2, break? ? "breakGroup([" : "group([", "])") do
165
165
  q.seplist(contents) { |content| q.pp(content) }
166
166
  end
167
167
  end
@@ -458,6 +458,10 @@ class PrettyPrint
458
458
  IfBreakBuilder.new
459
459
  end
460
460
 
461
+ # Also effectively unnecessary, but here for compatibility.
462
+ def if_flat
463
+ end
464
+
461
465
  # A noop that immediately yields.
462
466
  def indent
463
467
  yield
@@ -759,9 +763,7 @@ class PrettyPrint
759
763
 
760
764
  # This is a linear stack instead of a mutually recursive call defined on
761
765
  # the individual doc nodes for efficiency.
762
- while commands.any?
763
- indent, mode, doc = commands.pop
764
-
766
+ while (indent, mode, doc = commands.pop)
765
767
  case doc
766
768
  when Text
767
769
  doc.objects.each { |object| buffer << object }
@@ -789,10 +791,10 @@ class PrettyPrint
789
791
  end
790
792
  end
791
793
  when IfBreak
792
- if mode == MODE_BREAK
793
- commands << [indent, mode, doc.break_contents] if doc.break_contents
794
- elsif mode == MODE_FLAT
795
- commands << [indent, mode, doc.flat_contents] if doc.flat_contents
794
+ if mode == MODE_BREAK && doc.break_contents.any?
795
+ commands << [indent, mode, doc.break_contents]
796
+ elsif mode == MODE_FLAT && doc.flat_contents.any?
797
+ commands << [indent, mode, doc.flat_contents]
796
798
  end
797
799
  when LineSuffix
798
800
  line_suffixes << [indent, mode, doc.contents, doc.priority]
@@ -1011,6 +1013,16 @@ class PrettyPrint
1011
1013
  IfBreakBuilder.new(self, doc)
1012
1014
  end
1013
1015
 
1016
+ # This is similar to if_break in that it also inserts an IfBreak node into the
1017
+ # print tree, however it's starting from the flat contents, and cannot be used
1018
+ # to build the break contents.
1019
+ def if_flat
1020
+ doc = IfBreak.new
1021
+ target << doc
1022
+
1023
+ with_target(doc.flat_contents) { yield }
1024
+ end
1025
+
1014
1026
  # Very similar to the #nest method, this indents the nested content by one
1015
1027
  # level by inserting an Indent node into the print tree. The contents of the
1016
1028
  # node are determined by the block.
@@ -1116,10 +1128,10 @@ class PrettyPrint
1116
1128
  when Group
1117
1129
  commands << [indent, doc.break? ? MODE_BREAK : mode, doc.contents]
1118
1130
  when IfBreak
1119
- if mode == MODE_BREAK
1120
- commands << [indent, mode, doc.break_contents] if doc.break_contents
1121
- else
1122
- commands << [indent, mode, doc.flat_contents] if doc.flat_contents
1131
+ if mode == MODE_BREAK && doc.break_contents.any?
1132
+ commands << [indent, mode, doc.break_contents]
1133
+ elsif mode == MODE_FLAT && doc.flat_contents.any?
1134
+ commands << [indent, mode, doc.flat_contents]
1123
1135
  end
1124
1136
  when Breakable
1125
1137
  if mode == MODE_FLAT && !doc.force?
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SyntaxTree
4
- VERSION = "2.1.0"
4
+ VERSION = "2.3.0"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: syntax_tree
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.0
4
+ version: 2.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kevin Newton
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-04-13 00:00:00.000000000 Z
11
+ date: 2022-04-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler