unparser 0.1.5 → 0.1.6
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.
- checksums.yaml +4 -4
- data/.gitignore +38 -2
- data/.travis.yml +3 -6
- data/Changelog.md +7 -0
- data/Gemfile +1 -0
- data/Gemfile.devtools +22 -17
- data/README.md +3 -4
- data/TODO +3 -2
- data/bin/unparser +10 -0
- data/circle.yml +2 -0
- data/config/flay.yml +1 -1
- data/config/flog.yml +1 -1
- data/config/reek.yml +10 -0
- data/config/rubocop.yml +9 -17
- data/lib/unparser.rb +8 -1
- data/lib/unparser/buffer.rb +2 -0
- data/lib/unparser/cli.rb +113 -0
- data/lib/unparser/cli/differ.rb +30 -0
- data/lib/unparser/cli/preprocessor.rb +196 -0
- data/lib/unparser/cli/source.rb +118 -0
- data/lib/unparser/comments.rb +64 -23
- data/lib/unparser/constants.rb +19 -7
- data/lib/unparser/emitter.rb +23 -25
- data/lib/unparser/emitter/alias.rb +2 -0
- data/lib/unparser/emitter/argument.rb +2 -0
- data/lib/unparser/emitter/assignment.rb +3 -1
- data/lib/unparser/emitter/begin.rb +3 -6
- data/lib/unparser/emitter/binary.rb +2 -0
- data/lib/unparser/emitter/block.rb +2 -0
- data/lib/unparser/emitter/break.rb +4 -5
- data/lib/unparser/emitter/case.rb +2 -0
- data/lib/unparser/emitter/cbase.rb +2 -0
- data/lib/unparser/emitter/class.rb +4 -3
- data/lib/unparser/emitter/def.rb +2 -0
- data/lib/unparser/emitter/defined.rb +2 -0
- data/lib/unparser/emitter/empty.rb +2 -0
- data/lib/unparser/emitter/ensure.rb +2 -0
- data/lib/unparser/emitter/flipflop.rb +2 -0
- data/lib/unparser/emitter/for.rb +2 -0
- data/lib/unparser/emitter/hookexe.rb +2 -0
- data/lib/unparser/emitter/if.rb +2 -0
- data/lib/unparser/emitter/literal.rb +2 -0
- data/lib/unparser/emitter/literal/array.rb +33 -0
- data/lib/unparser/emitter/literal/dynamic.rb +21 -1
- data/lib/unparser/emitter/literal/dynamic_body.rb +9 -5
- data/lib/unparser/emitter/literal/execute_string.rb +2 -0
- data/lib/unparser/emitter/literal/hash.rb +136 -0
- data/lib/unparser/emitter/literal/primitive.rb +4 -2
- data/lib/unparser/emitter/literal/range.rb +2 -0
- data/lib/unparser/emitter/literal/regexp.rb +4 -2
- data/lib/unparser/emitter/literal/singleton.rb +2 -0
- data/lib/unparser/emitter/match.rb +2 -0
- data/lib/unparser/emitter/module.rb +2 -3
- data/lib/unparser/emitter/next.rb +2 -0
- data/lib/unparser/emitter/op_assign.rb +3 -1
- data/lib/unparser/emitter/redo.rb +2 -0
- data/lib/unparser/emitter/repetition.rb +2 -0
- data/lib/unparser/emitter/resbody.rb +2 -0
- data/lib/unparser/emitter/rescue.rb +2 -0
- data/lib/unparser/emitter/retry.rb +2 -0
- data/lib/unparser/emitter/return.rb +19 -4
- data/lib/unparser/emitter/root.rb +13 -0
- data/lib/unparser/emitter/send.rb +26 -22
- data/lib/unparser/emitter/send/arguments.rb +46 -0
- data/lib/unparser/emitter/send/attribute_assignment.rb +35 -0
- data/lib/unparser/emitter/send/binary.rb +2 -0
- data/lib/unparser/emitter/send/index.rb +2 -0
- data/lib/unparser/emitter/send/regular.rb +4 -2
- data/lib/unparser/emitter/send/unary.rb +3 -1
- data/lib/unparser/emitter/splat.rb +2 -0
- data/lib/unparser/emitter/super.rb +2 -0
- data/lib/unparser/emitter/undef.rb +2 -0
- data/lib/unparser/emitter/variable.rb +2 -0
- data/lib/unparser/emitter/yield.rb +2 -0
- data/lib/unparser/finalize.rb +2 -0
- data/lib/unparser/node_helpers.rb +19 -0
- data/spec/spec_helper.rb +10 -0
- data/spec/unit/unparser/buffer/append_spec.rb +2 -0
- data/spec/unit/unparser/buffer/append_without_prefix_spec.rb +2 -0
- data/spec/unit/unparser/buffer/capture_content_spec.rb +2 -0
- data/spec/unit/unparser/buffer/content_spec.rb +3 -1
- data/spec/unit/unparser/buffer/fresh_line_spec.rb +2 -0
- data/spec/unit/unparser/buffer/indent_spec.rb +3 -1
- data/spec/unit/unparser/buffer/nl_spec.rb +2 -0
- data/spec/unit/unparser/buffer/unindent_spec.rb +2 -0
- data/spec/unit/unparser/comments/consume_spec.rb +2 -1
- data/spec/unit/unparser/comments/take_all_spec.rb +2 -1
- data/spec/unit/unparser/comments/take_before_spec.rb +6 -5
- data/spec/unit/unparser/comments/take_eol_comments_spec.rb +2 -1
- data/spec/unit/unparser/emitter/class_methods/handle_spec.rb +2 -0
- data/spec/unit/unparser_spec.rb +110 -57
- data/unparser.gemspec +5 -4
- metadata +32 -12
- data/bin/test-unparser +0 -26
- data/lib/unparser/emitter/literal/composed.rb +0 -64
- data/spec/unit/unparser/comments/skip_eol_comment_spec.rb +0 -29
| @@ -0,0 +1,30 @@ | |
| 1 | 
            +
            # encoding: utf-8
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module Unparser
         | 
| 4 | 
            +
              class CLI
         | 
| 5 | 
            +
                # Unparser CLI specific differ
         | 
| 6 | 
            +
                class Differ < Mutant::Differ
         | 
| 7 | 
            +
                  include Procto.call(:colorized_diff)
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                  # Return source diff
         | 
| 10 | 
            +
                  #
         | 
| 11 | 
            +
                  # FIXME: Multiple diffs get screwed up!
         | 
| 12 | 
            +
                  #
         | 
| 13 | 
            +
                  # @return [String]
         | 
| 14 | 
            +
                  #   if there is a diff
         | 
| 15 | 
            +
                  #
         | 
| 16 | 
            +
                  # @return [nil]
         | 
| 17 | 
            +
                  #   otherwise
         | 
| 18 | 
            +
                  #
         | 
| 19 | 
            +
                  # @api private
         | 
| 20 | 
            +
                  #
         | 
| 21 | 
            +
                  def diff
         | 
| 22 | 
            +
                    diffs.map do |piece|
         | 
| 23 | 
            +
                      Diff::LCS::Hunk.new(old, new, piece, max_length, old.length - new.length).diff(:unified) << "\n"
         | 
| 24 | 
            +
                    end.join
         | 
| 25 | 
            +
                  end
         | 
| 26 | 
            +
                  memoize :diff
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                end # Differ
         | 
| 29 | 
            +
              end # CLI
         | 
| 30 | 
            +
            end # Unparser
         | 
| @@ -0,0 +1,196 @@ | |
| 1 | 
            +
            # encoding: utf-8
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module Unparser
         | 
| 4 | 
            +
              class CLI
         | 
| 5 | 
            +
             | 
| 6 | 
            +
                # CLI Specific preprocessor used for equivalency testing
         | 
| 7 | 
            +
                class Preprocessor
         | 
| 8 | 
            +
                  include Adamantium::Flat, NodeHelpers, AbstractType, Concord.new(:node), Procto.call(:result)
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                  # Return preprocessor result
         | 
| 11 | 
            +
                  #
         | 
| 12 | 
            +
                  # @return [Parser::AST::Node]
         | 
| 13 | 
            +
                  #
         | 
| 14 | 
            +
                  # @api private
         | 
| 15 | 
            +
                  #
         | 
| 16 | 
            +
                  abstract_method :result
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                  # Run preprocessor for node
         | 
| 19 | 
            +
                  #
         | 
| 20 | 
            +
                  # @param [Parser::AST::Node, nil] node
         | 
| 21 | 
            +
                  #
         | 
| 22 | 
            +
                  # @return [Parser::AST::Node, nil]
         | 
| 23 | 
            +
                  #
         | 
| 24 | 
            +
                  # @api private
         | 
| 25 | 
            +
                  #
         | 
| 26 | 
            +
                  def self.run(node)
         | 
| 27 | 
            +
                    return if node.nil?
         | 
| 28 | 
            +
                    REGISTRY.fetch(node.type, Noop).new(node).result
         | 
| 29 | 
            +
                  end
         | 
| 30 | 
            +
             | 
| 31 | 
            +
                  REGISTRY = {}
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                  # Register preprocessor
         | 
| 34 | 
            +
                  #
         | 
| 35 | 
            +
                  # @param [Symbol] type
         | 
| 36 | 
            +
                  #
         | 
| 37 | 
            +
                  # @return [undefined]
         | 
| 38 | 
            +
                  #
         | 
| 39 | 
            +
                  # @api private
         | 
| 40 | 
            +
                  #
         | 
| 41 | 
            +
                  def self.register(type)
         | 
| 42 | 
            +
                    REGISTRY[type] = self
         | 
| 43 | 
            +
                  end
         | 
| 44 | 
            +
                  private_class_method :register
         | 
| 45 | 
            +
             | 
| 46 | 
            +
                private
         | 
| 47 | 
            +
             | 
| 48 | 
            +
                  # Visit node
         | 
| 49 | 
            +
                  #
         | 
| 50 | 
            +
                  # @param [Parser::AST::Node]
         | 
| 51 | 
            +
                  #
         | 
| 52 | 
            +
                  # @api private
         | 
| 53 | 
            +
                  #
         | 
| 54 | 
            +
                  def visit(node)
         | 
| 55 | 
            +
                    self.class.run(node)
         | 
| 56 | 
            +
                  end
         | 
| 57 | 
            +
             | 
| 58 | 
            +
                  # Return children
         | 
| 59 | 
            +
                  #
         | 
| 60 | 
            +
                  # @return [Array<Parser::AST::Node>]
         | 
| 61 | 
            +
                  #
         | 
| 62 | 
            +
                  # @api private
         | 
| 63 | 
            +
                  #
         | 
| 64 | 
            +
                  def children
         | 
| 65 | 
            +
                    node.children
         | 
| 66 | 
            +
                  end
         | 
| 67 | 
            +
             | 
| 68 | 
            +
                  # Return mapped children
         | 
| 69 | 
            +
                  #
         | 
| 70 | 
            +
                  # @return [Array<Parser::Ast::Node>]
         | 
| 71 | 
            +
                  #
         | 
| 72 | 
            +
                  # @api private
         | 
| 73 | 
            +
                  #
         | 
| 74 | 
            +
                  def mapped_children
         | 
| 75 | 
            +
                    children.map do |node|
         | 
| 76 | 
            +
                      if node.kind_of?(Parser::AST::Node)
         | 
| 77 | 
            +
                        visit(node)
         | 
| 78 | 
            +
                      else
         | 
| 79 | 
            +
                        node
         | 
| 80 | 
            +
                      end
         | 
| 81 | 
            +
                    end
         | 
| 82 | 
            +
                  end
         | 
| 83 | 
            +
             | 
| 84 | 
            +
                  # Noop preprocessor that just passes through noode.
         | 
| 85 | 
            +
                  class Noop < self
         | 
| 86 | 
            +
             | 
| 87 | 
            +
                    # Return preprocessor result
         | 
| 88 | 
            +
                    #
         | 
| 89 | 
            +
                    # @return [Parser::AST::Node]
         | 
| 90 | 
            +
                    #
         | 
| 91 | 
            +
                    # @api private
         | 
| 92 | 
            +
                    #
         | 
| 93 | 
            +
                    def result
         | 
| 94 | 
            +
                      s(node.type, *mapped_children)
         | 
| 95 | 
            +
                    end
         | 
| 96 | 
            +
                  end # Noop
         | 
| 97 | 
            +
             | 
| 98 | 
            +
                  # Preprocessor for dynamic string nodes. Collapses adjacent string segments into one.
         | 
| 99 | 
            +
                  class Dstr < self
         | 
| 100 | 
            +
             | 
| 101 | 
            +
                    register :dstr
         | 
| 102 | 
            +
             | 
| 103 | 
            +
                    # Return preprocessor result
         | 
| 104 | 
            +
                    #
         | 
| 105 | 
            +
                    # @return [Parser::AST::Node]
         | 
| 106 | 
            +
                    #
         | 
| 107 | 
            +
                    # @api private
         | 
| 108 | 
            +
                    #
         | 
| 109 | 
            +
                    def result
         | 
| 110 | 
            +
                      if collapsed_children.all? { |node| node.type == :str }
         | 
| 111 | 
            +
                        s(:str, collapsed_children.map { |node| node.children.first }.join)
         | 
| 112 | 
            +
                      else
         | 
| 113 | 
            +
                        node.updated(nil, collapsed_children)
         | 
| 114 | 
            +
                      end
         | 
| 115 | 
            +
                    end
         | 
| 116 | 
            +
             | 
| 117 | 
            +
                  private
         | 
| 118 | 
            +
             | 
| 119 | 
            +
                    # Return collapsed children
         | 
| 120 | 
            +
                    #
         | 
| 121 | 
            +
                    # @return [Array<Parser::AST::Node>]
         | 
| 122 | 
            +
                    #
         | 
| 123 | 
            +
                    # @api private
         | 
| 124 | 
            +
                    #
         | 
| 125 | 
            +
                    def collapsed_children
         | 
| 126 | 
            +
                      chunked_children.each_with_object([]) do |(type, nodes), aggregate|
         | 
| 127 | 
            +
                        if type == :str
         | 
| 128 | 
            +
                          aggregate << s(:str, nodes.map { |node| node.children.first }.join)
         | 
| 129 | 
            +
                        else
         | 
| 130 | 
            +
                          aggregate.concat(nodes)
         | 
| 131 | 
            +
                        end
         | 
| 132 | 
            +
                      end
         | 
| 133 | 
            +
                    end
         | 
| 134 | 
            +
                    memoize :collapsed_children
         | 
| 135 | 
            +
             | 
| 136 | 
            +
                    # Return chunked children
         | 
| 137 | 
            +
                    #
         | 
| 138 | 
            +
                    # @return [Array<Parser::AST::Node>]
         | 
| 139 | 
            +
                    #
         | 
| 140 | 
            +
                    # @api private
         | 
| 141 | 
            +
                    #
         | 
| 142 | 
            +
                    def chunked_children
         | 
| 143 | 
            +
                      mapped_children.chunk do |item|
         | 
| 144 | 
            +
                        item.type
         | 
| 145 | 
            +
                      end
         | 
| 146 | 
            +
                    end
         | 
| 147 | 
            +
             | 
| 148 | 
            +
                  end # Begin
         | 
| 149 | 
            +
             | 
| 150 | 
            +
                  # Preprocessor for regexp nodes. Normalizes quoting.
         | 
| 151 | 
            +
                  class Regexp < self
         | 
| 152 | 
            +
             | 
| 153 | 
            +
                    register :regexp
         | 
| 154 | 
            +
             | 
| 155 | 
            +
                    # Return preprocesso result
         | 
| 156 | 
            +
                    #
         | 
| 157 | 
            +
                    # @return [Parser::AST::Node]
         | 
| 158 | 
            +
                    #
         | 
| 159 | 
            +
                    # @api private
         | 
| 160 | 
            +
                    #
         | 
| 161 | 
            +
                    def result
         | 
| 162 | 
            +
                      location = node.location
         | 
| 163 | 
            +
                      if location && location.begin.source.start_with?('%r')
         | 
| 164 | 
            +
                        Parser::CurrentRuby.parse(Unparser.unparse(node))
         | 
| 165 | 
            +
                      else
         | 
| 166 | 
            +
                        node
         | 
| 167 | 
            +
                      end
         | 
| 168 | 
            +
                    end
         | 
| 169 | 
            +
                  end
         | 
| 170 | 
            +
             | 
| 171 | 
            +
                  # Preprocessor for begin nodes. Removes begin nodes with one child.
         | 
| 172 | 
            +
                  #
         | 
| 173 | 
            +
                  # These superflownosely currently get generated by unparser.
         | 
| 174 | 
            +
                  #
         | 
| 175 | 
            +
                  class Begin < self
         | 
| 176 | 
            +
             | 
| 177 | 
            +
                    register :begin
         | 
| 178 | 
            +
             | 
| 179 | 
            +
                    # Return preprocessor result
         | 
| 180 | 
            +
                    #
         | 
| 181 | 
            +
                    # @return [Parser::AST::Node]
         | 
| 182 | 
            +
                    #
         | 
| 183 | 
            +
                    # @api private
         | 
| 184 | 
            +
                    #
         | 
| 185 | 
            +
                    def result
         | 
| 186 | 
            +
                      if children.one?
         | 
| 187 | 
            +
                        visit(children.first)
         | 
| 188 | 
            +
                      else
         | 
| 189 | 
            +
                        Noop.call(node)
         | 
| 190 | 
            +
                      end
         | 
| 191 | 
            +
                    end
         | 
| 192 | 
            +
             | 
| 193 | 
            +
                  end # Begin
         | 
| 194 | 
            +
                end # Preprocessor
         | 
| 195 | 
            +
              end # CLI
         | 
| 196 | 
            +
            end # Unparser
         | 
| @@ -0,0 +1,118 @@ | |
| 1 | 
            +
            # encoding: utf-8
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module Unparser
         | 
| 4 | 
            +
              class CLI
         | 
| 5 | 
            +
                # Source representation for CLI sources
         | 
| 6 | 
            +
                class Source
         | 
| 7 | 
            +
                  include AbstractType, Adamantium::Flat
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                  # Test if source could be unparsed successfully
         | 
| 10 | 
            +
                  #
         | 
| 11 | 
            +
                  # @return [true]
         | 
| 12 | 
            +
                  #   if source could be unparsed successfully
         | 
| 13 | 
            +
                  #
         | 
| 14 | 
            +
                  # @return [false]
         | 
| 15 | 
            +
                  #
         | 
| 16 | 
            +
                  # @api private
         | 
| 17 | 
            +
                  #
         | 
| 18 | 
            +
                  def success?
         | 
| 19 | 
            +
                    original_ast == generated_ast
         | 
| 20 | 
            +
                  end
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                  # Return error report
         | 
| 23 | 
            +
                  #
         | 
| 24 | 
            +
                  # @return [String]
         | 
| 25 | 
            +
                  #
         | 
| 26 | 
            +
                  # @api private
         | 
| 27 | 
            +
                  #
         | 
| 28 | 
            +
                  def error_report
         | 
| 29 | 
            +
                    diff = Differ.call(
         | 
| 30 | 
            +
                      original_ast.inspect.lines.map(&:chomp),
         | 
| 31 | 
            +
                      generated_ast.inspect.lines.map(&:chomp)
         | 
| 32 | 
            +
                    )
         | 
| 33 | 
            +
                    "#{diff}\nOriginal:\n#{original_source}\nGenerated:\n#{generated_source}"
         | 
| 34 | 
            +
                  end
         | 
| 35 | 
            +
                  memoize :error_report
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                private
         | 
| 38 | 
            +
             | 
| 39 | 
            +
                  # Return generated source
         | 
| 40 | 
            +
                  #
         | 
| 41 | 
            +
                  # @return [String]
         | 
| 42 | 
            +
                  #
         | 
| 43 | 
            +
                  # @api private
         | 
| 44 | 
            +
                  #
         | 
| 45 | 
            +
                  def generated_source
         | 
| 46 | 
            +
                    Unparser.unparse(original_ast)
         | 
| 47 | 
            +
                  end
         | 
| 48 | 
            +
                  memoize :generated_source
         | 
| 49 | 
            +
             | 
| 50 | 
            +
                  # Return generated AST
         | 
| 51 | 
            +
                  #
         | 
| 52 | 
            +
                  # @return [Parser::AST::Node]
         | 
| 53 | 
            +
                  #
         | 
| 54 | 
            +
                  # @api private
         | 
| 55 | 
            +
                  #
         | 
| 56 | 
            +
                  def generated_ast
         | 
| 57 | 
            +
                    Preprocessor.run(Parser::CurrentRuby.parse(generated_source))
         | 
| 58 | 
            +
                  end
         | 
| 59 | 
            +
                  memoize :generated_ast
         | 
| 60 | 
            +
             | 
| 61 | 
            +
                  # Return original AST
         | 
| 62 | 
            +
                  #
         | 
| 63 | 
            +
                  # @return [Parser::AST::Node]
         | 
| 64 | 
            +
                  #
         | 
| 65 | 
            +
                  # @api private
         | 
| 66 | 
            +
                  #
         | 
| 67 | 
            +
                  def original_ast
         | 
| 68 | 
            +
                    Preprocessor.run(Parser::CurrentRuby.parse(original_source))
         | 
| 69 | 
            +
                  end
         | 
| 70 | 
            +
                  memoize :original_ast
         | 
| 71 | 
            +
             | 
| 72 | 
            +
                  # CLI source from string
         | 
| 73 | 
            +
                  class String < self
         | 
| 74 | 
            +
                    include Concord.new(:original_source)
         | 
| 75 | 
            +
             | 
| 76 | 
            +
                    # Return identification
         | 
| 77 | 
            +
                    #
         | 
| 78 | 
            +
                    # @return [String]
         | 
| 79 | 
            +
                    #
         | 
| 80 | 
            +
                    # @api private
         | 
| 81 | 
            +
                    #
         | 
| 82 | 
            +
                    def identification
         | 
| 83 | 
            +
                      '(string)'
         | 
| 84 | 
            +
                    end
         | 
| 85 | 
            +
             | 
| 86 | 
            +
                  end # String
         | 
| 87 | 
            +
             | 
| 88 | 
            +
                  # CLI source from file
         | 
| 89 | 
            +
                  class File < self
         | 
| 90 | 
            +
                    include Concord.new(:file_name)
         | 
| 91 | 
            +
             | 
| 92 | 
            +
                    # Return identification
         | 
| 93 | 
            +
                    #
         | 
| 94 | 
            +
                    # @return [String]
         | 
| 95 | 
            +
                    #
         | 
| 96 | 
            +
                    # @api private
         | 
| 97 | 
            +
                    #
         | 
| 98 | 
            +
                    def identification
         | 
| 99 | 
            +
                      "(#{file_name})"
         | 
| 100 | 
            +
                    end
         | 
| 101 | 
            +
             | 
| 102 | 
            +
                  private
         | 
| 103 | 
            +
             | 
| 104 | 
            +
                    # Return original source
         | 
| 105 | 
            +
                    #
         | 
| 106 | 
            +
                    # @return [String]
         | 
| 107 | 
            +
                    #
         | 
| 108 | 
            +
                    # @api private
         | 
| 109 | 
            +
                    #
         | 
| 110 | 
            +
                    def original_source
         | 
| 111 | 
            +
                      ::File.read(file_name)
         | 
| 112 | 
            +
                    end
         | 
| 113 | 
            +
                    memoize :original_source
         | 
| 114 | 
            +
             | 
| 115 | 
            +
                  end # File
         | 
| 116 | 
            +
                end # Source
         | 
| 117 | 
            +
              end # CLI
         | 
| 118 | 
            +
            end # Unparser
         | 
    
        data/lib/unparser/comments.rb
    CHANGED
    
    | @@ -1,17 +1,35 @@ | |
| 1 | 
            +
            # encoding: utf-8
         | 
| 2 | 
            +
             | 
| 1 3 | 
             
            module Unparser
         | 
| 2 4 |  | 
| 3 5 | 
             
              # Holds the comments that remain to be emitted
         | 
| 4 6 | 
             
              class Comments
         | 
| 5 7 |  | 
| 8 | 
            +
                # Proxy to singleton
         | 
| 9 | 
            +
                #
         | 
| 10 | 
            +
                # NOTICE:
         | 
| 11 | 
            +
                #   Delegating to stateless helpers is a pattern I saw many times in our code.
         | 
| 12 | 
            +
                #   Maybe we should make another helper module? include SingletonDelegator.new(:source_range) ?
         | 
| 13 | 
            +
                #
         | 
| 14 | 
            +
                # @return [undefined]
         | 
| 15 | 
            +
                #
         | 
| 16 | 
            +
                # @api private
         | 
| 17 | 
            +
                #
         | 
| 18 | 
            +
                def source_range(*arguments)
         | 
| 19 | 
            +
                  self.class.source_range(*arguments)
         | 
| 20 | 
            +
                end
         | 
| 21 | 
            +
             | 
| 6 22 | 
             
                # Initialize object
         | 
| 7 23 | 
             
                #
         | 
| 8 24 | 
             
                # @param [Array] comments
         | 
| 9 25 | 
             
                #
         | 
| 10 26 | 
             
                # @return [undefined]
         | 
| 11 27 | 
             
                #
         | 
| 28 | 
            +
                # @api private
         | 
| 29 | 
            +
                #
         | 
| 12 30 | 
             
                def initialize(comments)
         | 
| 13 31 | 
             
                  @comments = comments.dup
         | 
| 14 | 
            -
                  @last_range_consumed =  | 
| 32 | 
            +
                  @last_range_consumed = nil
         | 
| 15 33 | 
             
                end
         | 
| 16 34 |  | 
| 17 35 | 
             
                # Consume part or all of the node
         | 
| @@ -21,39 +39,33 @@ module Unparser | |
| 21 39 | 
             
                #
         | 
| 22 40 | 
             
                # @return [undefined]
         | 
| 23 41 | 
             
                #
         | 
| 24 | 
            -
                 | 
| 25 | 
            -
                  location = node.location
         | 
| 26 | 
            -
                  return unless location
         | 
| 27 | 
            -
                  @last_range_consumed = location.public_send(source_part)
         | 
| 28 | 
            -
                end
         | 
| 29 | 
            -
             | 
| 30 | 
            -
                # Skip any EOL comment with the specified text next time they're taken
         | 
| 31 | 
            -
                #
         | 
| 32 | 
            -
                # @param [String] comment_text
         | 
| 42 | 
            +
                # @api private
         | 
| 33 43 | 
             
                #
         | 
| 34 | 
            -
                 | 
| 35 | 
            -
             | 
| 36 | 
            -
             | 
| 37 | 
            -
             | 
| 44 | 
            +
                def consume(node, source_part = :expression)
         | 
| 45 | 
            +
                  range = source_range(node, source_part)
         | 
| 46 | 
            +
                  if range
         | 
| 47 | 
            +
                    @last_range_consumed = range
         | 
| 48 | 
            +
                  end
         | 
| 38 49 | 
             
                end
         | 
| 39 50 |  | 
| 40 51 | 
             
                # Take end-of-line comments
         | 
| 41 52 | 
             
                #
         | 
| 42 53 | 
             
                # @return [Array]
         | 
| 43 54 | 
             
                #
         | 
| 55 | 
            +
                # @api private
         | 
| 56 | 
            +
                #
         | 
| 44 57 | 
             
                def take_eol_comments
         | 
| 45 | 
            -
                   | 
| 46 | 
            -
                  @eol_text_to_skip = nil
         | 
| 47 | 
            -
                  return [] unless @last_range_consumed
         | 
| 58 | 
            +
                  return EMPTY_ARRAY unless @last_range_consumed
         | 
| 48 59 | 
             
                  comments = take_up_to_line(@last_range_consumed.end.line)
         | 
| 49 | 
            -
                   | 
| 50 | 
            -
                  eol_comments.reject { |comment| comment.text == text_to_skip }
         | 
| 60 | 
            +
                  unshift_documents(comments)
         | 
| 51 61 | 
             
                end
         | 
| 52 62 |  | 
| 53 63 | 
             
                # Take all remaining comments
         | 
| 54 64 | 
             
                #
         | 
| 55 65 | 
             
                # @return [Array]
         | 
| 56 66 | 
             
                #
         | 
| 67 | 
            +
                # @api private
         | 
| 68 | 
            +
                #
         | 
| 57 69 | 
             
                def take_all
         | 
| 58 70 | 
             
                  take_while { true }
         | 
| 59 71 | 
             
                end
         | 
| @@ -65,24 +77,49 @@ module Unparser | |
| 65 77 | 
             
                #
         | 
| 66 78 | 
             
                # @return [Array]
         | 
| 67 79 | 
             
                #
         | 
| 80 | 
            +
                # @api private
         | 
| 81 | 
            +
                #
         | 
| 68 82 | 
             
                def take_before(node, source_part)
         | 
| 69 | 
            -
                   | 
| 70 | 
            -
                  if  | 
| 71 | 
            -
                    range = location.public_send(source_part)
         | 
| 83 | 
            +
                  range = source_range(node, source_part)
         | 
| 84 | 
            +
                  if range
         | 
| 72 85 | 
             
                    take_while { |comment| comment.location.expression.end_pos <= range.begin_pos }
         | 
| 73 86 | 
             
                  else
         | 
| 74 87 | 
             
                    EMPTY_ARRAY
         | 
| 75 88 | 
             
                  end
         | 
| 76 89 | 
             
                end
         | 
| 77 90 |  | 
| 91 | 
            +
                # Return source location part
         | 
| 92 | 
            +
                #
         | 
| 93 | 
            +
                # FIXME: This method should not be needed. It does to much inline signalling.
         | 
| 94 | 
            +
                #
         | 
| 95 | 
            +
                # @param [Parser::AST::Node] node
         | 
| 96 | 
            +
                # @param [Symbol] part
         | 
| 97 | 
            +
                #
         | 
| 98 | 
            +
                # @return [Parser::Source::Range]
         | 
| 99 | 
            +
                #   if present
         | 
| 100 | 
            +
                #
         | 
| 101 | 
            +
                # @return [nil]
         | 
| 102 | 
            +
                #   otherwise
         | 
| 103 | 
            +
                #
         | 
| 104 | 
            +
                # @api private
         | 
| 105 | 
            +
                #
         | 
| 106 | 
            +
                def self.source_range(node, part)
         | 
| 107 | 
            +
                  location = node.location
         | 
| 108 | 
            +
                  if location && location.respond_to?(part)
         | 
| 109 | 
            +
                    location.public_send(part)
         | 
| 110 | 
            +
                  end
         | 
| 111 | 
            +
                end
         | 
| 112 | 
            +
             | 
| 78 113 | 
             
              private
         | 
| 79 114 |  | 
| 80 115 | 
             
                # Take comments while the provided block returns true
         | 
| 81 116 | 
             
                #
         | 
| 82 | 
            -
                # @yield [ | 
| 117 | 
            +
                # @yield [Parser::Source::Comment]
         | 
| 83 118 | 
             
                #
         | 
| 84 119 | 
             
                # @return [Array]
         | 
| 85 120 | 
             
                #
         | 
| 121 | 
            +
                # @api private
         | 
| 122 | 
            +
                #
         | 
| 86 123 | 
             
                def take_while
         | 
| 87 124 | 
             
                  number_to_take = @comments.index { |comment| !yield(comment) } || @comments.size
         | 
| 88 125 | 
             
                  @comments.shift(number_to_take)
         | 
| @@ -94,6 +131,8 @@ module Unparser | |
| 94 131 | 
             
                #
         | 
| 95 132 | 
             
                # @return [Array]
         | 
| 96 133 | 
             
                #
         | 
| 134 | 
            +
                # @api private
         | 
| 135 | 
            +
                #
         | 
| 97 136 | 
             
                def take_up_to_line(line)
         | 
| 98 137 | 
             
                  take_while { |comment| comment.location.expression.line <= line }
         | 
| 99 138 | 
             
                end
         | 
| @@ -104,6 +143,8 @@ module Unparser | |
| 104 143 | 
             
                #
         | 
| 105 144 | 
             
                # @return [Array]
         | 
| 106 145 | 
             
                #
         | 
| 146 | 
            +
                # @api private
         | 
| 147 | 
            +
                #
         | 
| 107 148 | 
             
                def unshift_documents(comments)
         | 
| 108 149 | 
             
                  doc_comments, other_comments = comments.partition(&:document?)
         | 
| 109 150 | 
             
                  doc_comments.reverse_each { |comment| @comments.unshift(comment) }
         |