syntax_tree 2.4.0 → 2.6.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.
@@ -123,7 +123,7 @@ module SyntaxTree
123
123
  end
124
124
 
125
125
  def construct_keys
126
- PP.format(+"") { |q| Visitor::MatchVisitor.new(q).visit(self) }
126
+ PrettierPrint.format(+"") { |q| Visitor::MatchVisitor.new(q).visit(self) }
127
127
  end
128
128
  end
129
129
 
@@ -597,10 +597,30 @@ module SyntaxTree
597
597
  q.indent do
598
598
  q.breakable("")
599
599
  q.format(arguments)
600
+ q.if_break { q.text(",") } if q.trailing_comma? && trailing_comma?
600
601
  end
601
602
  q.breakable("")
602
603
  end
603
604
  end
605
+
606
+ private
607
+
608
+ def trailing_comma?
609
+ case arguments
610
+ in Args[parts: [*, ArgBlock]]
611
+ # If the last argument is a block, then we can't put a trailing comma
612
+ # after it without resulting in a syntax error.
613
+ false
614
+ in Args[parts: [Command | CommandCall]]
615
+ # If the only argument is a command or command call, then a trailing
616
+ # comma would be parsed as part of that expression instead of on this
617
+ # one, so we don't want to add a trailing comma.
618
+ false
619
+ else
620
+ # Otherwise, we should be okay to add a trailing comma.
621
+ true
622
+ end
623
+ end
604
624
  end
605
625
 
606
626
  # Args represents a list of arguments being passed to a method call or array
@@ -859,6 +879,7 @@ module SyntaxTree
859
879
  end
860
880
 
861
881
  q.seplist(contents.parts, separator) { |part| q.format(part) }
882
+ q.if_break { q.text(",") } if q.trailing_comma?
862
883
  end
863
884
  q.breakable("")
864
885
  end
@@ -954,6 +975,7 @@ module SyntaxTree
954
975
  q.indent do
955
976
  q.breakable("")
956
977
  q.format(contents)
978
+ q.if_break { q.text(",") } if q.trailing_comma?
957
979
  end
958
980
  end
959
981
 
@@ -1388,7 +1410,7 @@ module SyntaxTree
1388
1410
  module HashKeyFormatter
1389
1411
  # Formats the keys of a hash literal using labels.
1390
1412
  class Labels
1391
- LABEL = /^[@$_A-Za-z]([_A-Za-z0-9]*)?([!_=?A-Za-z0-9])?$/
1413
+ LABEL = /\A[A-Za-z_](\w*[\w!?])?\z/
1392
1414
 
1393
1415
  def format_key(q, key)
1394
1416
  case key
@@ -1666,52 +1688,6 @@ module SyntaxTree
1666
1688
  end
1667
1689
  end
1668
1690
 
1669
- # This module will remove any breakables from the list of contents so that no
1670
- # newlines are present in the output.
1671
- module RemoveBreaks
1672
- class << self
1673
- def call(doc)
1674
- marker = Object.new
1675
- stack = [doc]
1676
-
1677
- while stack.any?
1678
- doc = stack.pop
1679
-
1680
- if doc == marker
1681
- stack.pop
1682
- next
1683
- end
1684
-
1685
- stack += [doc, marker]
1686
-
1687
- case doc
1688
- when PrettyPrint::Align, PrettyPrint::Indent, PrettyPrint::Group
1689
- doc.contents.map! { |child| remove_breaks(child) }
1690
- stack += doc.contents.reverse
1691
- when PrettyPrint::IfBreak
1692
- doc.flat_contents.map! { |child| remove_breaks(child) }
1693
- stack += doc.flat_contents.reverse
1694
- end
1695
- end
1696
- end
1697
-
1698
- private
1699
-
1700
- def remove_breaks(doc)
1701
- case doc
1702
- when PrettyPrint::Breakable
1703
- text = PrettyPrint::Text.new
1704
- text.add(object: doc.force? ? "; " : doc.separator, width: doc.width)
1705
- text
1706
- when PrettyPrint::IfBreak
1707
- PrettyPrint::Align.new(indent: 0, contents: doc.flat_contents)
1708
- else
1709
- doc
1710
- end
1711
- end
1712
- end
1713
- end
1714
-
1715
1691
  # BlockVar represents the parameters being declared for a block. Effectively
1716
1692
  # this node is everything contained within the pipes. This includes all of the
1717
1693
  # various parameter types, as well as block-local variable declarations.
@@ -1752,8 +1728,7 @@ module SyntaxTree
1752
1728
 
1753
1729
  def format(q)
1754
1730
  q.group(0, "|", "|") do
1755
- doc = q.format(params)
1756
- RemoveBreaks.call(doc)
1731
+ q.remove_breaks(q.format(params))
1757
1732
 
1758
1733
  if locals.any?
1759
1734
  q.text("; ")
@@ -3077,8 +3052,20 @@ module SyntaxTree
3077
3052
  doc =
3078
3053
  q.nest(0) do
3079
3054
  q.format(receiver)
3080
- q.format(CallOperatorFormatter.new(operator), stackable: false)
3081
- q.format(message)
3055
+
3056
+ # If there are leading comments on the message then we know we have
3057
+ # a newline in the source that is forcing these things apart. In
3058
+ # this case we will have to use a trailing operator.
3059
+ if message.comments.any?(&:leading?)
3060
+ q.format(CallOperatorFormatter.new(operator), stackable: false)
3061
+ q.indent do
3062
+ q.breakable("")
3063
+ q.format(message)
3064
+ end
3065
+ else
3066
+ q.format(CallOperatorFormatter.new(operator), stackable: false)
3067
+ q.format(message)
3068
+ end
3082
3069
  end
3083
3070
 
3084
3071
  case arguments
@@ -3096,31 +3083,6 @@ module SyntaxTree
3096
3083
 
3097
3084
  private
3098
3085
 
3099
- # This is a somewhat naive method that is attempting to sum up the width of
3100
- # the doc nodes that make up the given doc node. This is used to align
3101
- # content.
3102
- def doc_width(parent)
3103
- queue = [parent]
3104
- width = 0
3105
-
3106
- until queue.empty?
3107
- doc = queue.shift
3108
-
3109
- case doc
3110
- when PrettyPrint::Text
3111
- width += doc.width
3112
- when PrettyPrint::Indent, PrettyPrint::Align, PrettyPrint::Group
3113
- queue = doc.contents + queue
3114
- when PrettyPrint::IfBreak
3115
- queue = doc.break_contents + queue
3116
- when PrettyPrint::Breakable
3117
- width = 0
3118
- end
3119
- end
3120
-
3121
- width
3122
- end
3123
-
3124
3086
  def argument_alignment(q, doc)
3125
3087
  # Very special handling case for rspec matchers. In general with rspec
3126
3088
  # matchers you expect to see something like:
@@ -3138,7 +3100,7 @@ module SyntaxTree
3138
3100
  if %w[to not_to to_not].include?(message.value)
3139
3101
  0
3140
3102
  else
3141
- width = doc_width(doc) + 1
3103
+ width = q.last_position(doc) + 1
3142
3104
  width > (q.maxwidth / 2) ? 0 : width
3143
3105
  end
3144
3106
  end
@@ -3904,7 +3866,7 @@ module SyntaxTree
3904
3866
  # quotes, then single quotes would deactivate it.)
3905
3867
  def self.locked?(node)
3906
3868
  node.parts.any? do |part|
3907
- part.is_a?(TStringContent) && part.value.match?(/\\|#[@${]/)
3869
+ !part.is_a?(TStringContent) || part.value.match?(/\\|#[@${]/)
3908
3870
  end
3909
3871
  end
3910
3872
 
@@ -4823,6 +4785,7 @@ module SyntaxTree
4823
4785
  q.indent do
4824
4786
  q.breakable
4825
4787
  q.seplist(assocs) { |assoc| q.format(assoc) }
4788
+ q.if_break { q.text(",") } if q.trailing_comma?
4826
4789
  end
4827
4790
  q.breakable
4828
4791
  end
@@ -4891,17 +4854,9 @@ module SyntaxTree
4891
4854
  end
4892
4855
 
4893
4856
  def format(q)
4894
- # This is a very specific behavior that should probably be included in the
4895
- # prettyprint module. It's when you want to force a newline, but don't
4896
- # want to force the break parent.
4897
- breakable = -> do
4898
- q.target << PrettyPrint::Breakable.new(
4899
- " ",
4900
- 1,
4901
- indent: false,
4902
- force: true
4903
- )
4904
- end
4857
+ # This is a very specific behavior where you want to force a newline, but
4858
+ # don't want to force the break parent.
4859
+ breakable = -> { q.breakable(indent: false, force: :skip_break_parent) }
4905
4860
 
4906
4861
  q.group do
4907
4862
  q.format(beginning)
@@ -5064,13 +5019,16 @@ module SyntaxTree
5064
5019
  parts = keywords.map { |(key, value)| KeywordFormatter.new(key, value) }
5065
5020
  parts << KeywordRestFormatter.new(keyword_rest) if keyword_rest
5066
5021
 
5022
+ nested = PATTERNS.include?(q.parent.class)
5067
5023
  contents = -> do
5068
5024
  q.group { q.seplist(parts) { |part| q.format(part, stackable: false) } }
5069
5025
 
5070
5026
  # If there isn't a constant, and there's a blank keyword_rest, then we
5071
5027
  # have an plain ** that needs to have a `then` after it in order to
5072
5028
  # parse correctly on the next parse.
5073
- q.text(" then") if !constant && keyword_rest && keyword_rest.value.nil?
5029
+ if !constant && keyword_rest && keyword_rest.value.nil? && !nested
5030
+ q.text(" then")
5031
+ end
5074
5032
  end
5075
5033
 
5076
5034
  # If there is a constant, we're going to format to have the constant name
@@ -5097,7 +5055,7 @@ module SyntaxTree
5097
5055
 
5098
5056
  # If there's only one pair, then we'll just print the contents provided
5099
5057
  # we're not inside another pattern.
5100
- if !PATTERNS.include?(q.parent.class) && parts.size == 1
5058
+ if !nested && parts.size == 1
5101
5059
  contents.call
5102
5060
  return
5103
5061
  end
@@ -5194,6 +5152,8 @@ module SyntaxTree
5194
5152
  case node
5195
5153
  in predicate: Assign | Command | CommandCall | MAssign | OpAssign
5196
5154
  false
5155
+ in predicate: Not[parentheses: false]
5156
+ false
5197
5157
  in {
5198
5158
  statements: { body: [truthy] },
5199
5159
  consequent: Else[statements: { body: [falsy] }]
@@ -5322,9 +5282,8 @@ module SyntaxTree
5322
5282
  # force it into the output but we _don't_ want to explicitly
5323
5283
  # break the parent. If a break-parent shows up in the tree, then
5324
5284
  # it's going to force it all the way up to the tree, which is
5325
- # going to negate the ternary. Maybe this should be an option in
5326
- # prettyprint? As in force: :no_break_parent or something.
5327
- q.target << PrettyPrint::Breakable.new(" ", 1, force: true)
5285
+ # going to negate the ternary.
5286
+ q.breakable(force: :skip_break_parent)
5328
5287
  q.format(node.consequent.statements)
5329
5288
  end
5330
5289
  end
@@ -5953,7 +5912,7 @@ module SyntaxTree
5953
5912
  # ->(value) { value * 2 }
5954
5913
  #
5955
5914
  class Lambda < Node
5956
- # [Params | Paren] the parameter declaration for this lambda
5915
+ # [LambdaVar | Paren] the parameter declaration for this lambda
5957
5916
  attr_reader :params
5958
5917
 
5959
5918
  # [BodyStmt | Statements] the expressions to be executed in this lambda
@@ -6008,24 +5967,100 @@ module SyntaxTree
6008
5967
  node.is_a?(Command) || node.is_a?(CommandCall)
6009
5968
  end
6010
5969
 
6011
- q.text(force_parens ? "{" : "do")
6012
- q.indent do
5970
+ if force_parens
5971
+ q.text("{")
5972
+
5973
+ unless statements.empty?
5974
+ q.indent do
5975
+ q.breakable
5976
+ q.format(statements)
5977
+ end
5978
+ q.breakable
5979
+ end
5980
+
5981
+ q.text("}")
5982
+ else
5983
+ q.text("do")
5984
+
5985
+ unless statements.empty?
5986
+ q.indent do
5987
+ q.breakable
5988
+ q.format(statements)
5989
+ end
5990
+ end
5991
+
6013
5992
  q.breakable
6014
- q.format(statements)
5993
+ q.text("end")
6015
5994
  end
6016
-
6017
- q.breakable
6018
- q.text(force_parens ? "}" : "end")
6019
5995
  end
6020
5996
  .if_flat do
6021
- q.text("{ ")
6022
- q.format(statements)
6023
- q.text(" }")
5997
+ q.text("{")
5998
+
5999
+ unless statements.empty?
6000
+ q.text(" ")
6001
+ q.format(statements)
6002
+ q.text(" ")
6003
+ end
6004
+
6005
+ q.text("}")
6024
6006
  end
6025
6007
  end
6026
6008
  end
6027
6009
  end
6028
6010
 
6011
+ # LambdaVar represents the parameters being declared for a lambda. Effectively
6012
+ # this node is everything contained within the parentheses. This includes all
6013
+ # of the various parameter types, as well as block-local variable
6014
+ # declarations.
6015
+ #
6016
+ # -> (positional, optional = value, keyword:, &block; local) do
6017
+ # end
6018
+ #
6019
+ class LambdaVar < Node
6020
+ # [Params] the parameters being declared with the block
6021
+ attr_reader :params
6022
+
6023
+ # [Array[ Ident ]] the list of block-local variable declarations
6024
+ attr_reader :locals
6025
+
6026
+ # [Array[ Comment | EmbDoc ]] the comments attached to this node
6027
+ attr_reader :comments
6028
+
6029
+ def initialize(params:, locals:, location:, comments: [])
6030
+ @params = params
6031
+ @locals = locals
6032
+ @location = location
6033
+ @comments = comments
6034
+ end
6035
+
6036
+ def accept(visitor)
6037
+ visitor.visit_lambda_var(self)
6038
+ end
6039
+
6040
+ def child_nodes
6041
+ [params, *locals]
6042
+ end
6043
+
6044
+ alias deconstruct child_nodes
6045
+
6046
+ def deconstruct_keys(_keys)
6047
+ { params: params, locals: locals, location: location, comments: comments }
6048
+ end
6049
+
6050
+ def empty?
6051
+ params.empty? && locals.empty?
6052
+ end
6053
+
6054
+ def format(q)
6055
+ q.format(params)
6056
+
6057
+ if locals.any?
6058
+ q.text("; ")
6059
+ q.seplist(locals, -> { q.text(", ") }) { |local| q.format(local) }
6060
+ end
6061
+ end
6062
+ end
6063
+
6029
6064
  # LBrace represents the use of a left brace, i.e., {.
6030
6065
  class LBrace < Node
6031
6066
  # [String] the left brace
@@ -8311,8 +8346,7 @@ module SyntaxTree
8311
8346
  # same line in the source, then we're going to leave them in place and
8312
8347
  # assume that's the way the developer wanted this expression
8313
8348
  # represented.
8314
- doc = q.group(0, '#{', "}") { q.format(statements) }
8315
- RemoveBreaks.call(doc)
8349
+ q.remove_breaks(q.group(0, '#{', "}") { q.format(statements) })
8316
8350
  else
8317
8351
  q.group do
8318
8352
  q.text('#{')
@@ -1671,9 +1671,15 @@ module SyntaxTree
1671
1671
  # (nil | VarField) keyword_rest
1672
1672
  # ) -> HshPtn
1673
1673
  def on_hshptn(constant, keywords, keyword_rest)
1674
- # Create an artificial VarField if we find an extra ** on the end
1675
- if !keyword_rest && (token = find_token(Op, "**", consume: false))
1674
+ if keyword_rest
1675
+ # We're doing this to delete the token from the list so that it doesn't
1676
+ # confuse future patterns by thinking they have an extra ** on the end.
1677
+ find_token(Op, "**")
1678
+ elsif (token = find_token(Op, "**", consume: false))
1676
1679
  tokens.delete(token)
1680
+
1681
+ # Create an artificial VarField if we find an extra ** on the end. This
1682
+ # means the formatting will be a little more consistent.
1677
1683
  keyword_rest = VarField.new(value: nil, location: token.location)
1678
1684
  end
1679
1685
 
@@ -1934,6 +1940,41 @@ module SyntaxTree
1934
1940
  token.location.start_char > beginning.location.start_char
1935
1941
  end
1936
1942
 
1943
+ # We need to do some special mapping here. Since ripper doesn't support
1944
+ # capturing lambda var until 3.2, we need to normalize all of that here.
1945
+ params =
1946
+ case params
1947
+ in Paren[contents: Params]
1948
+ # In this case we've gotten to the <3.2 parentheses wrapping a set of
1949
+ # parameters case. Here we need to manually scan for lambda locals.
1950
+ range = (params.location.start_char + 1)...params.location.end_char
1951
+ locals = lambda_locals(source[range])
1952
+
1953
+ location = params.contents.location
1954
+ location = location.to(locals.last.location) if locals.any?
1955
+
1956
+ Paren.new(
1957
+ lparen: params.lparen,
1958
+ contents:
1959
+ LambdaVar.new(
1960
+ params: params.contents,
1961
+ locals: locals,
1962
+ location: location
1963
+ ),
1964
+ location: params.location,
1965
+ comments: params.comments
1966
+ )
1967
+ in Params
1968
+ # In this case we've gotten to the <3.2 plain set of parameters. In
1969
+ # this case there cannot be lambda locals, so we will wrap the
1970
+ # parameters into a lambda var that has no locals.
1971
+ LambdaVar.new(params: params, locals: [], location: params.location)
1972
+ in LambdaVar
1973
+ # In this case we've gotten to 3.2+ lambda var. In this case we don't
1974
+ # need to do anything and can just the value as given.
1975
+ params
1976
+ end
1977
+
1937
1978
  if braces
1938
1979
  opening = find_token(TLamBeg)
1939
1980
  closing = find_token(RBrace)
@@ -1956,6 +1997,83 @@ module SyntaxTree
1956
1997
  )
1957
1998
  end
1958
1999
 
2000
+ # :call-seq:
2001
+ # on_lambda_var: (Params params, Array[ Ident ] locals) -> LambdaVar
2002
+ def on_lambda_var(params, locals)
2003
+ location = params.location
2004
+ location = location.to(locals.last.location) if locals.any?
2005
+
2006
+ LambdaVar.new(params: params, locals: locals || [], location: location)
2007
+ end
2008
+
2009
+ # Ripper doesn't support capturing lambda local variables until 3.2. To
2010
+ # mitigate this, we have to parse that code for ourselves. We use the range
2011
+ # from the parentheses to find where we _should_ be looking. Then we check
2012
+ # if the resulting tokens match a pattern that we determine means that the
2013
+ # declaration has block-local variables. Once it does, we parse those out
2014
+ # and convert them into Ident nodes.
2015
+ def lambda_locals(source)
2016
+ tokens = Ripper.lex(source)
2017
+
2018
+ # First, check that we have a semi-colon. If we do, then we can start to
2019
+ # parse the tokens _after_ the semicolon.
2020
+ index = tokens.rindex { |token| token[1] == :on_semicolon }
2021
+ return [] unless index
2022
+
2023
+ # Next, map over the tokens and convert them into Ident nodes. Bail out
2024
+ # midway through if we encounter a token we didn't expect. Basically we're
2025
+ # making our own mini-parser here. To do that we'll walk through a small
2026
+ # state machine:
2027
+ #
2028
+ # ┌────────┐ ┌────────┐ ┌─────────┐
2029
+ # │ │ │ │ │┌───────┐│
2030
+ # ──> │ item │ ─── ident ──> │ next │ ─── rparen ──> ││ final ││
2031
+ # │ │ <── comma ─── │ │ │└───────┘│
2032
+ # └────────┘ └────────┘ └─────────┘
2033
+ # │ ^ │ ^
2034
+ # └──┘ └──┘
2035
+ # ignored_nl, sp nl, sp
2036
+ #
2037
+ state = :item
2038
+ transitions = {
2039
+ item: {
2040
+ on_ignored_nl: :item,
2041
+ on_sp: :item,
2042
+ on_ident: :next
2043
+ },
2044
+ next: {
2045
+ on_nl: :next,
2046
+ on_sp: :next,
2047
+ on_comma: :item,
2048
+ on_rparen: :final
2049
+ },
2050
+ final: {
2051
+ }
2052
+ }
2053
+
2054
+ tokens[(index + 1)..].each_with_object([]) do |token, locals|
2055
+ (lineno, column), type, value, = token
2056
+
2057
+ # Make the state transition for the parser. If there isn't a transition
2058
+ # from the current state to a new state for this type, then we're in a
2059
+ # pattern that isn't actually locals. In that case we can return [].
2060
+ state = transitions[state].fetch(type) { return [] }
2061
+
2062
+ # If we hit an identifier, then add it to our list.
2063
+ next if type != :on_ident
2064
+
2065
+ location =
2066
+ Location.token(
2067
+ line: lineno,
2068
+ char: line_counts[lineno - 1][column],
2069
+ column: column,
2070
+ size: value.size
2071
+ )
2072
+
2073
+ locals << Ident.new(value: value, location: location)
2074
+ end
2075
+ end
2076
+
1959
2077
  # :call-seq:
1960
2078
  # on_lbrace: (String value) -> LBrace
1961
2079
  def on_lbrace(value)
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "syntax_tree/formatter/single_quotes"
4
+ SyntaxTree::Formatter.prepend(SyntaxTree::Formatter::SingleQuotes)
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "syntax_tree/formatter/trailing_comma"
4
+ SyntaxTree::Formatter.prepend(SyntaxTree::Formatter::TrailingComma)
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rake"
4
+ require "rake/tasklib"
5
+
6
+ require "syntax_tree"
7
+ require "syntax_tree/cli"
8
+
9
+ module SyntaxTree
10
+ module Rake
11
+ # A Rake task that runs check on a set of source files.
12
+ #
13
+ # Example:
14
+ #
15
+ # require 'syntax_tree/rake/check_task'
16
+ #
17
+ # SyntaxTree::Rake::CheckTask.new do |t|
18
+ # t.source_files = '{app,config,lib}/**/*.rb'
19
+ # end
20
+ #
21
+ # This will create task that can be run with:
22
+ #
23
+ # rake stree_check
24
+ #
25
+ class CheckTask < ::Rake::TaskLib
26
+ # Name of the task.
27
+ # Defaults to :"stree:check".
28
+ attr_accessor :name
29
+
30
+ # Glob pattern to match source files.
31
+ # Defaults to 'lib/**/*.rb'.
32
+ attr_accessor :source_files
33
+
34
+ # The set of plugins to require.
35
+ # Defaults to [].
36
+ attr_accessor :plugins
37
+
38
+ def initialize(
39
+ name = :"stree:check",
40
+ source_files = ::Rake::FileList["lib/**/*.rb"],
41
+ plugins = []
42
+ )
43
+ @name = name
44
+ @source_files = source_files
45
+ @plugins = plugins
46
+
47
+ yield self if block_given?
48
+ define_task
49
+ end
50
+
51
+ private
52
+
53
+ def define_task
54
+ desc "Runs `stree check` over source files"
55
+ task(name) { run_task }
56
+ end
57
+
58
+ def run_task
59
+ arguments = ["check"]
60
+ arguments << "--plugins=#{plugins.join(",")}" if plugins.any?
61
+
62
+ SyntaxTree::CLI.run(arguments + Array(source_files))
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rake"
4
+ require "rake/tasklib"
5
+
6
+ require "syntax_tree"
7
+ require "syntax_tree/cli"
8
+
9
+ module SyntaxTree
10
+ module Rake
11
+ # A Rake task that runs format on a set of source files.
12
+ #
13
+ # Example:
14
+ #
15
+ # require 'syntax_tree/rake/write_task'
16
+ #
17
+ # SyntaxTree::Rake::WriteTask.new do |t|
18
+ # t.source_files = '{app,config,lib}/**/*.rb'
19
+ # end
20
+ #
21
+ # This will create task that can be run with:
22
+ #
23
+ # rake stree_write
24
+ #
25
+ class WriteTask < ::Rake::TaskLib
26
+ # Name of the task.
27
+ # Defaults to :"stree:write".
28
+ attr_accessor :name
29
+
30
+ # Glob pattern to match source files.
31
+ # Defaults to 'lib/**/*.rb'.
32
+ attr_accessor :source_files
33
+
34
+ # The set of plugins to require.
35
+ # Defaults to [].
36
+ attr_accessor :plugins
37
+
38
+ def initialize(
39
+ name = :"stree:write",
40
+ source_files = ::Rake::FileList["lib/**/*.rb"],
41
+ plugins = []
42
+ )
43
+ @name = name
44
+ @source_files = source_files
45
+ @plugins = plugins
46
+
47
+ yield self if block_given?
48
+ define_task
49
+ end
50
+
51
+ private
52
+
53
+ def define_task
54
+ desc "Runs `stree write` over source files"
55
+ task(name) { run_task }
56
+ end
57
+
58
+ def run_task
59
+ arguments = ["write"]
60
+ arguments << "--plugins=#{plugins.join(",")}" if plugins.any?
61
+
62
+ SyntaxTree::CLI.run(arguments + Array(source_files))
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "rake/check_task"
4
+ require_relative "rake/write_task"
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SyntaxTree
4
- VERSION = "2.4.0"
4
+ VERSION = "2.6.0"
5
5
  end