unparser 0.4.9 → 0.5.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (107) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +19 -4
  3. data/bin/unparser +1 -1
  4. data/lib/unparser.rb +115 -61
  5. data/lib/unparser/ast.rb +0 -1
  6. data/lib/unparser/ast/local_variable_scope.rb +6 -76
  7. data/lib/unparser/buffer.rb +19 -16
  8. data/lib/unparser/cli.rb +26 -39
  9. data/lib/unparser/color.rb +0 -3
  10. data/lib/unparser/comments.rb +0 -26
  11. data/lib/unparser/constants.rb +4 -53
  12. data/lib/unparser/diff.rb +0 -17
  13. data/lib/unparser/dsl.rb +0 -32
  14. data/lib/unparser/emitter.rb +23 -425
  15. data/lib/unparser/emitter/alias.rb +2 -8
  16. data/lib/unparser/emitter/args.rb +45 -0
  17. data/lib/unparser/emitter/argument.rb +8 -166
  18. data/lib/unparser/emitter/array.rb +27 -0
  19. data/lib/unparser/emitter/array_pattern.rb +29 -0
  20. data/lib/unparser/emitter/assignment.rb +36 -127
  21. data/lib/unparser/emitter/begin.rb +9 -84
  22. data/lib/unparser/emitter/binary.rb +7 -20
  23. data/lib/unparser/emitter/block.rb +57 -41
  24. data/lib/unparser/emitter/case.rb +6 -48
  25. data/lib/unparser/emitter/case_guard.rb +27 -0
  26. data/lib/unparser/emitter/case_match.rb +40 -0
  27. data/lib/unparser/emitter/cbase.rb +1 -3
  28. data/lib/unparser/emitter/class.rb +6 -26
  29. data/lib/unparser/emitter/const_pattern.rb +24 -0
  30. data/lib/unparser/emitter/def.rb +7 -51
  31. data/lib/unparser/emitter/defined.rb +2 -12
  32. data/lib/unparser/emitter/dstr.rb +22 -0
  33. data/lib/unparser/emitter/dsym.rb +41 -0
  34. data/lib/unparser/emitter/flipflop.rb +11 -10
  35. data/lib/unparser/emitter/float.rb +29 -0
  36. data/lib/unparser/emitter/flow_modifier.rb +11 -55
  37. data/lib/unparser/emitter/for.rb +5 -19
  38. data/lib/unparser/emitter/hash.rb +74 -0
  39. data/lib/unparser/emitter/hash_pattern.rb +67 -0
  40. data/lib/unparser/emitter/hookexe.rb +5 -11
  41. data/lib/unparser/emitter/if.rb +15 -71
  42. data/lib/unparser/emitter/in_match.rb +21 -0
  43. data/lib/unparser/emitter/in_pattern.rb +34 -0
  44. data/lib/unparser/emitter/index.rb +21 -88
  45. data/lib/unparser/emitter/kwbegin.rb +31 -0
  46. data/lib/unparser/emitter/lambda.rb +0 -8
  47. data/lib/unparser/emitter/masgn.rb +20 -0
  48. data/lib/unparser/emitter/match.rb +3 -17
  49. data/lib/unparser/emitter/match_alt.rb +23 -0
  50. data/lib/unparser/emitter/match_as.rb +21 -0
  51. data/lib/unparser/emitter/match_rest.rb +26 -0
  52. data/lib/unparser/emitter/match_var.rb +19 -0
  53. data/lib/unparser/emitter/mlhs.rb +40 -0
  54. data/lib/unparser/emitter/module.rb +3 -9
  55. data/lib/unparser/emitter/op_assign.rb +12 -27
  56. data/lib/unparser/emitter/pin.rb +19 -0
  57. data/lib/unparser/emitter/primitive.rb +93 -0
  58. data/lib/unparser/emitter/range.rb +35 -0
  59. data/lib/unparser/emitter/regexp.rb +35 -0
  60. data/lib/unparser/emitter/repetition.rb +17 -57
  61. data/lib/unparser/emitter/rescue.rb +1 -97
  62. data/lib/unparser/emitter/root.rb +17 -1
  63. data/lib/unparser/emitter/send.rb +10 -219
  64. data/lib/unparser/emitter/simple.rb +33 -0
  65. data/lib/unparser/emitter/splat.rb +2 -18
  66. data/lib/unparser/emitter/super.rb +1 -29
  67. data/lib/unparser/emitter/undef.rb +1 -9
  68. data/lib/unparser/emitter/variable.rb +1 -31
  69. data/lib/unparser/emitter/xstr.rb +72 -0
  70. data/lib/unparser/emitter/yield.rb +1 -9
  71. data/lib/unparser/generation.rb +250 -0
  72. data/lib/unparser/node_details.rb +21 -0
  73. data/lib/unparser/node_details/send.rb +62 -0
  74. data/lib/unparser/node_helpers.rb +46 -6
  75. data/lib/unparser/validation.rb +58 -35
  76. data/lib/unparser/writer.rb +15 -0
  77. data/lib/unparser/writer/binary.rb +99 -0
  78. data/lib/unparser/writer/dynamic_string.rb +233 -0
  79. data/lib/unparser/writer/resbody.rb +40 -0
  80. data/lib/unparser/writer/rescue.rb +39 -0
  81. data/lib/unparser/writer/send.rb +124 -0
  82. data/lib/unparser/{emitter → writer}/send/attribute_assignment.rb +11 -26
  83. data/lib/unparser/writer/send/binary.rb +27 -0
  84. data/lib/unparser/writer/send/conditional.rb +25 -0
  85. data/lib/unparser/writer/send/regular.rb +33 -0
  86. data/lib/unparser/{emitter → writer}/send/unary.rb +10 -17
  87. metadata +66 -34
  88. data/lib/unparser/emitter/empty.rb +0 -23
  89. data/lib/unparser/emitter/ensure.rb +0 -37
  90. data/lib/unparser/emitter/literal.rb +0 -10
  91. data/lib/unparser/emitter/literal/array.rb +0 -29
  92. data/lib/unparser/emitter/literal/dynamic.rb +0 -53
  93. data/lib/unparser/emitter/literal/dynamic_body.rb +0 -132
  94. data/lib/unparser/emitter/literal/execute_string.rb +0 -38
  95. data/lib/unparser/emitter/literal/hash.rb +0 -156
  96. data/lib/unparser/emitter/literal/primitive.rb +0 -145
  97. data/lib/unparser/emitter/literal/range.rb +0 -36
  98. data/lib/unparser/emitter/literal/regexp.rb +0 -114
  99. data/lib/unparser/emitter/literal/singleton.rb +0 -26
  100. data/lib/unparser/emitter/meta.rb +0 -16
  101. data/lib/unparser/emitter/redo.rb +0 -25
  102. data/lib/unparser/emitter/resbody.rb +0 -76
  103. data/lib/unparser/emitter/retry.rb +0 -25
  104. data/lib/unparser/emitter/send/binary.rb +0 -57
  105. data/lib/unparser/emitter/send/conditional.rb +0 -40
  106. data/lib/unparser/emitter/send/regular.rb +0 -40
  107. data/lib/unparser/preprocessor.rb +0 -159
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 10fe213bc822f4da69795458d89fc34693b5c57f1add6e910a77f8f456f5cff4
4
- data.tar.gz: a1333c5af931e182f3fe0c5586d010f306f5b51d5e8f5204beb2bd47cce8a9d2
3
+ metadata.gz: 83c5893dd093e0f106fefd55abad7931fa775001f53a7449389e40afe0640359
4
+ data.tar.gz: d4caba221ba0b3dcd5afdff73d0df537c24c56f3219e0896e5f67b92b79f6afc
5
5
  SHA512:
6
- metadata.gz: aa4d72074d1f1aeeafaf7f87cc53f5a6754dcb06ae99d1c73191b776bb10c9b58db0be2fdd8c9487e61e0cfdc1918e78aac35adccc7b706ea2b9b5b4101faaaf
7
- data.tar.gz: 336938a288a1ada6f33db990535b7d487d618a4f60725101b282c5654fd8e1c953c412c5b8dc9cad9afb936c64ddafd65b5fa0797016022c0a8ce52df35c3ec7
6
+ metadata.gz: 7c7feff0af410abca14daa35166d64b95cda39959f5f6424fb2840a8c5f63b630be4aaba9d22018d745adb2578a9ed7ffc538f4d0c3e6b5224f00e8dd6fceca0
7
+ data.tar.gz: efd0a71dc4988fcf804eab203a4fbed157629c531135d7cbaf2e4ccf5c26c9b1ea2a694c1eb1c2f451f91cfe91b6420d683ff76da6779adaef1e3d015c8ab51b
data/README.md CHANGED
@@ -4,7 +4,7 @@ unparser
4
4
  ![CI](https://github.com/mbj/unparser/workflows/CI/badge.svg)
5
5
  [![Gem Version](https://img.shields.io/gem/v/unparser.svg)](https://rubygems.org/gems/unparser)
6
6
 
7
- Generate equivalent source for ASTs from whitequarks [parser](https://github.com/whitequark/parser).
7
+ Generate equivalent source for ASTs from [parser](https://github.com/whitequark/parser).
8
8
 
9
9
  The following constraints apply:
10
10
 
@@ -12,8 +12,13 @@ The following constraints apply:
12
12
  * Only support for the [modern AST](https://github.com/whitequark/parser/#usage) format
13
13
  * Only support for Ruby >= 2.5
14
14
 
15
- It serves well for [mutant](https://github.com/mbj/mutant) mutators and the in-memory vendoring for self hosting,
16
- and other tooling.
15
+ Notable Users:
16
+
17
+ * [mutant](https://github.com/mbj/mutant) - Code review engine via mutation testing.
18
+ * [ruby-next](https://github.com/ruby-next/ruby-next) - Ruby Syntax Backports.
19
+ * May other [reverse-dependencies](https://rubygems.org/gems/unparser/reverse_dependencies).
20
+
21
+ (if you want your tool to be mentioned here please PR the addition with a TLDR of your use case).
17
22
 
18
23
  Public API:
19
24
  -----------
@@ -86,12 +91,22 @@ RUBY
86
91
 
87
92
  generated = Unparser.unparse(node) # ["foo", "bar"], NOT %w[foo bar] !
88
93
 
89
- code == generated # false, not identical code
94
+ code == generated # false, not identical code
90
95
  Unparser.parse(generated) == node # true, but identical AST
91
96
  ```
92
97
 
93
98
  Summary: unparser does not reproduce your source! It produces equivalent source.
94
99
 
100
+ Ruby Versions:
101
+ --------------
102
+
103
+ Unparsers primay reason for existance is mutant and its
104
+ supported [Ruby-Versions](https://github.com/mbj/mutant#ruby-versions).
105
+
106
+ Basically: All non EOL MRI releases.
107
+
108
+ If you need to generate Ruby Syntax outside of this band feel free to contact me (email in gemspec).
109
+
95
110
  Testing:
96
111
  --------
97
112
 
@@ -5,6 +5,6 @@ trap('INT') do |status|
5
5
  exit! 128 + status
6
6
  end
7
7
 
8
- require 'unparser/cli'
8
+ require 'unparser'
9
9
 
10
10
  exit Unparser::CLI.run(ARGV)
@@ -6,6 +6,7 @@ require 'concord'
6
6
  require 'diff/lcs'
7
7
  require 'diff/lcs/hunk'
8
8
  require 'mprelude'
9
+ require 'optparse'
9
10
  require 'parser/current'
10
11
  require 'procto'
11
12
  require 'set'
@@ -15,6 +16,12 @@ module Unparser
15
16
  # Unparser specific AST builder defaulting to modern AST format
16
17
  class Builder < Parser::Builders::Default
17
18
  modernize
19
+
20
+ def initialize
21
+ super
22
+
23
+ self.emit_file_line_as_literals = false
24
+ end
18
25
  end
19
26
 
20
27
  EMPTY_STRING = ''.freeze
@@ -32,12 +39,44 @@ module Unparser
32
39
  # @api private
33
40
  #
34
41
  def self.unparse(node, comment_array = [])
35
- node = Preprocessor.run(node)
36
- buffer = Buffer.new
37
- comments = Comments.new(comment_array)
38
- root = Emitter::Root.new(Parser::AST::Node.new(:root, [node]), buffer, comments)
39
- Emitter.emitter(node, root).write_to_buffer
40
- buffer.content
42
+ return '' if node.nil?
43
+
44
+ Buffer.new.tap do |buffer|
45
+ Emitter::Root.new(
46
+ buffer,
47
+ node,
48
+ Comments.new(comment_array)
49
+ ).write_to_buffer
50
+ end.content
51
+ end
52
+
53
+ # Unparse with validation
54
+ #
55
+ # @param [Parser::AST::Node, nil] node
56
+ # @param [Array] comment_array
57
+ #
58
+ # @return [Either<Validation,String>]
59
+ def self.unparse_validate(node, comment_array = [])
60
+ generated = unparse(node, comment_array)
61
+ validation = Validation.from_string(generated)
62
+
63
+ if validation.success?
64
+ MPrelude::Either::Right.new(generated)
65
+ else
66
+ MPrelude::Either::Left.new(validation)
67
+ end
68
+ end
69
+
70
+ # Unparse capturing errors
71
+ #
72
+ # This is mostly useful for writing testing tools against unparser.
73
+ #
74
+ # @param [Parser::AST::Node, nil] node
75
+ #
76
+ # @return [Either<Exception, String>]
77
+ def self.unparse_either(node)
78
+ MPrelude::Either
79
+ .wrap_error(Exception) { unparse(node) }
41
80
  end
42
81
 
43
82
  # Parse string into AST
@@ -74,8 +113,6 @@ module Unparser
74
113
  # @return [Parser::Base]
75
114
  #
76
115
  # @api private
77
- #
78
- # ignore :reek:NestedIterators
79
116
  def self.parser
80
117
  Parser::CurrentRuby.new(Builder.new).tap do |parser|
81
118
  parser.diagnostics.tap do |diagnostics|
@@ -100,75 +137,92 @@ module Unparser
100
137
  # @param [String] source
101
138
  #
102
139
  # @return [Parser::Source::Buffer]
103
- def self.buffer(source)
104
- Parser::Source::Buffer.new('(string)').tap do |buffer|
105
- buffer.source = source
106
- end
140
+ def self.buffer(source, identification = '(string)')
141
+ Parser::Source::Buffer.new(identification, source: source)
107
142
  end
108
143
  end # Unparser
109
144
 
110
- require 'unparser/buffer'
111
145
  require 'unparser/node_helpers'
112
- require 'unparser/preprocessor'
113
- require 'unparser/comments'
114
- require 'unparser/constants'
115
- require 'unparser/dsl'
116
146
  require 'unparser/ast'
117
147
  require 'unparser/ast/local_variable_scope'
118
- require 'unparser/diff'
148
+ require 'unparser/buffer'
149
+ require 'unparser/generation'
119
150
  require 'unparser/color'
151
+ require 'unparser/comments'
152
+ require 'unparser/constants'
153
+ require 'unparser/diff'
154
+ require 'unparser/dsl'
120
155
  require 'unparser/emitter'
121
- require 'unparser/emitter/literal'
122
- require 'unparser/emitter/literal/primitive'
123
- require 'unparser/emitter/literal/singleton'
124
- require 'unparser/emitter/literal/dynamic'
125
- require 'unparser/emitter/literal/regexp'
126
- require 'unparser/emitter/literal/array'
127
- require 'unparser/emitter/literal/hash'
128
- require 'unparser/emitter/literal/range'
129
- require 'unparser/emitter/literal/dynamic_body'
130
- require 'unparser/emitter/literal/execute_string'
131
- require 'unparser/emitter/meta'
132
- require 'unparser/emitter/send'
133
- require 'unparser/emitter/send/unary'
134
- require 'unparser/emitter/send/binary'
135
- require 'unparser/emitter/send/regular'
136
- require 'unparser/emitter/send/conditional'
137
- require 'unparser/emitter/send/attribute_assignment'
138
- require 'unparser/emitter/block'
139
- require 'unparser/emitter/assignment'
140
- require 'unparser/emitter/variable'
141
- require 'unparser/emitter/splat'
142
- require 'unparser/emitter/cbase'
156
+ require 'unparser/emitter/alias'
157
+ require 'unparser/emitter/args'
143
158
  require 'unparser/emitter/argument'
159
+ require 'unparser/emitter/array'
160
+ require 'unparser/emitter/array_pattern'
161
+ require 'unparser/emitter/assignment'
144
162
  require 'unparser/emitter/begin'
145
- require 'unparser/emitter/flow_modifier'
146
- require 'unparser/emitter/undef'
147
- require 'unparser/emitter/def'
163
+ require 'unparser/emitter/binary'
164
+ require 'unparser/emitter/block'
165
+ require 'unparser/emitter/case'
166
+ require 'unparser/emitter/case_guard'
167
+ require 'unparser/emitter/case_match'
168
+ require 'unparser/emitter/cbase'
148
169
  require 'unparser/emitter/class'
149
- require 'unparser/emitter/module'
150
- require 'unparser/emitter/op_assign'
170
+ require 'unparser/emitter/const_pattern'
171
+ require 'unparser/emitter/def'
151
172
  require 'unparser/emitter/defined'
173
+ require 'unparser/emitter/dstr'
174
+ require 'unparser/emitter/dsym'
175
+ require 'unparser/emitter/flipflop'
176
+ require 'unparser/emitter/float'
177
+ require 'unparser/emitter/flow_modifier'
178
+ require 'unparser/emitter/for'
179
+ require 'unparser/emitter/hash'
180
+ require 'unparser/emitter/hash_pattern'
152
181
  require 'unparser/emitter/hookexe'
153
- require 'unparser/emitter/super'
154
- require 'unparser/emitter/retry'
155
- require 'unparser/emitter/redo'
156
182
  require 'unparser/emitter/if'
157
- require 'unparser/emitter/alias'
158
- require 'unparser/emitter/yield'
159
- require 'unparser/emitter/binary'
160
- require 'unparser/emitter/case'
161
- require 'unparser/emitter/for'
162
- require 'unparser/emitter/repetition'
163
- require 'unparser/emitter/root'
164
- require 'unparser/emitter/match'
165
- require 'unparser/emitter/empty'
166
- require 'unparser/emitter/flipflop'
167
- require 'unparser/emitter/rescue'
168
- require 'unparser/emitter/resbody'
169
- require 'unparser/emitter/ensure'
183
+ require 'unparser/emitter/in_match'
184
+ require 'unparser/emitter/in_pattern'
170
185
  require 'unparser/emitter/index'
186
+ require 'unparser/emitter/kwbegin'
171
187
  require 'unparser/emitter/lambda'
188
+ require 'unparser/emitter/masgn'
189
+ require 'unparser/emitter/match'
190
+ require 'unparser/emitter/match_alt'
191
+ require 'unparser/emitter/match_as'
192
+ require 'unparser/emitter/match_rest'
193
+ require 'unparser/emitter/match_var'
194
+ require 'unparser/emitter/mlhs'
195
+ require 'unparser/emitter/module'
196
+ require 'unparser/emitter/op_assign'
197
+ require 'unparser/emitter/pin'
198
+ require 'unparser/emitter/primitive'
199
+ require 'unparser/emitter/range'
200
+ require 'unparser/emitter/regexp'
201
+ require 'unparser/emitter/repetition'
202
+ require 'unparser/emitter/rescue'
203
+ require 'unparser/emitter/root'
204
+ require 'unparser/emitter/send'
205
+ require 'unparser/emitter/simple'
206
+ require 'unparser/emitter/splat'
207
+ require 'unparser/emitter/super'
208
+ require 'unparser/emitter/undef'
209
+ require 'unparser/emitter/variable'
210
+ require 'unparser/emitter/xstr'
211
+ require 'unparser/emitter/yield'
212
+ require 'unparser/writer'
213
+ require 'unparser/writer/binary'
214
+ require 'unparser/writer/dynamic_string'
215
+ require 'unparser/writer/resbody'
216
+ require 'unparser/writer/rescue'
217
+ require 'unparser/writer/send'
218
+ require 'unparser/writer/send/attribute_assignment'
219
+ require 'unparser/writer/send/binary'
220
+ require 'unparser/writer/send/regular'
221
+ require 'unparser/writer/send/unary'
222
+ require 'unparser/node_details'
223
+ require 'unparser/node_details/send'
224
+ require 'unparser/cli'
225
+
172
226
  require 'unparser/validation'
173
227
  # make it easy for zombie
174
228
  require 'unparser/finalize'
@@ -2,7 +2,6 @@
2
2
 
3
3
  module Unparser
4
4
  # Namespace for AST processing tools
5
- # :reek:TooManyConstants
6
5
  module AST
7
6
 
8
7
  FIRST_CHILD = ->(node) { node.children.first }.freeze
@@ -61,35 +61,22 @@ module Unparser
61
61
  #
62
62
  # @api private
63
63
  #
64
- def first_assignment_in_body_and_used_in_condition?(body, condition)
65
- condition_reads = AST.local_variable_reads(condition)
64
+ def first_assignment_in?(left, right)
65
+ condition_reads = AST.local_variable_reads(right)
66
66
 
67
- candidates = AST.local_variable_assignments(body).select do |node|
68
- name = node.children.first
69
- condition_reads.include?(name)
67
+ candidates = AST.local_variable_assignments(left).select do |node|
68
+ condition_reads.include?(node.children.first)
70
69
  end
71
70
 
72
- candidates.any? do |node|
73
- first_assignment?(node)
74
- end
71
+ candidates.any?(&public_method(:first_assignment?))
75
72
  end
76
73
 
77
74
  private
78
75
 
79
- # Match node
80
- #
81
- # @param [Parser::AST::Node] needle
82
- # if block given
83
- #
84
- # @return [Boolean]
85
- #
86
- # @api private
87
- #
88
76
  def match(needle)
89
77
  @items.each do |node, current, before|
90
78
  return yield(current, before) if node.equal?(needle)
91
79
  end
92
- false
93
80
  end
94
81
 
95
82
  end # LocalVariableScope
@@ -137,25 +124,10 @@ module Unparser
137
124
 
138
125
  private
139
126
 
140
- # Return current set of local variables
141
- #
142
- # @return [Set<Symbol>]
143
- #
144
- # @api private
145
- #
146
127
  def current
147
128
  @stack.last
148
129
  end
149
130
 
150
- # Visit node and record local variable state
151
- #
152
- # @param [Parser::AST::Node] node
153
- #
154
- # @return [undefined]
155
- #
156
- # @api private
157
- #
158
- # ignore :reek:LongYieldList
159
131
  def visit(node, &block)
160
132
  before = current.dup
161
133
  enter(node)
@@ -166,75 +138,33 @@ module Unparser
166
138
  leave(node)
167
139
  end
168
140
 
169
- # Record local variable state
170
- #
171
- # @param [Parser::AST::Node] node
172
- #
173
- # @return [undefined]
174
- #
175
- # @api private
176
- #
177
141
  def enter(node)
178
142
  case node.type
179
143
  when *RESET_NODES
180
144
  push_reset
181
- when *ASSIGN_NODES
145
+ when ASSIGN_NODES
182
146
  define(node.children.first)
183
147
  when *INHERIT_NODES
184
148
  push_inherit
185
149
  end
186
150
  end
187
151
 
188
- # Pop from local variable state
189
- #
190
- # @param [Parser::AST::Node] node
191
- #
192
- # @return [undefined]
193
- #
194
- # @api private
195
- #
196
152
  def leave(node)
197
153
  pop if CLOSE_NODES.include?(node.type)
198
154
  end
199
155
 
200
- # Define a local variable on current stack
201
- #
202
- # @param [Symbol] name
203
- #
204
- # @return [undefined]
205
- #
206
- # @api private
207
- #
208
156
  def define(name)
209
157
  current << name
210
158
  end
211
159
 
212
- # Push reset scope on stack
213
- #
214
- # @return [undefined]
215
- #
216
- # @api private
217
- #
218
160
  def push_reset
219
161
  @stack << Set.new
220
162
  end
221
163
 
222
- # Push inherited lvar scope on stack
223
- #
224
- # @return [undefined]
225
- #
226
- # @api private
227
- #
228
164
  def push_inherit
229
165
  @stack << current.dup
230
166
  end
231
167
 
232
- # Pop lvar scope from stack
233
- #
234
- # @return [undefined]
235
- #
236
- # @api private
237
- #
238
168
  def pop
239
169
  @stack.pop
240
170
  end
@@ -3,8 +3,6 @@
3
3
  module Unparser
4
4
 
5
5
  # Buffer used to emit into
6
- #
7
- # ignore :reek:TooManyMethods
8
6
  class Buffer
9
7
 
10
8
  NL = "\n".freeze
@@ -82,6 +80,13 @@ module Unparser
82
80
  self
83
81
  end
84
82
 
83
+ def root_indent
84
+ before = @indent
85
+ @indent = 0
86
+ yield
87
+ @indent = before
88
+ end
89
+
85
90
  # Test for a fresh line
86
91
  #
87
92
  # @return [Boolean]
@@ -114,26 +119,24 @@ module Unparser
114
119
  @content[size_before..-1]
115
120
  end
116
121
 
117
- private
118
-
119
- INDENT_SPACE = ' '.freeze
120
-
121
- # Write prefix
122
- #
123
- # @return [String]
122
+ # Write raw fragment to buffer
124
123
  #
125
- # @api private
126
- #
127
- def prefix
128
- write(INDENT_SPACE * @indent)
129
- end
130
-
131
- # Write to content buffer
124
+ # Does not do indentation logic.
132
125
  #
133
126
  # @param [String] fragment
134
127
  #
128
+ # @return [self]
135
129
  def write(fragment)
136
130
  @content << fragment
131
+ self
132
+ end
133
+
134
+ private
135
+
136
+ INDENT_SPACE = ' '.freeze
137
+
138
+ def prefix
139
+ write(INDENT_SPACE * @indent)
137
140
  end
138
141
 
139
142
  end # Buffer