unparser 0.1.2 → 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 791317b7c757ff21dccc078c1dc82a53690194bb
4
- data.tar.gz: 41cdfad7c83f3e88eae3cdf3351bb3830014345f
3
+ metadata.gz: 46f62b35c534e13da34486c1650bb14e406fe48b
4
+ data.tar.gz: 3bdedfa5f88178cd4ebeafedb72e2ce47a5552a2
5
5
  SHA512:
6
- metadata.gz: ed041a27c4747aacd924ed08fc8118eb13be9d82ca598c597f9dbbc9d5e8de28c67a2812b285cf4cacb7c0b4dc2925c52c3f1940060cde6eeb0788b5d1c1137d
7
- data.tar.gz: 1c55d2b791abf9ca336d78745f08f56e75d1ed92106c0fd08317f3d69524080a72a5b2a3e2102c0dd93e31ef027c267334d3e1caa0e7726fd3cc733d58be5f09
6
+ metadata.gz: cdfc4fb2f77e221b2af12dc14cb7504e3220e6e08c4da8919617f9b3cd9f9082d6b69129bf46aa7caba119681aed1255240eaf97b888a3b274c2432def82155e
7
+ data.tar.gz: efd08e3645878e75d316e07b2fb58de2c254c6588183e725c48af4739079856d41d547354114e33acd2c29c010fffea17cb211065c8823fde55692adc475e30a
data/README.md CHANGED
@@ -21,6 +21,15 @@ require 'unparser'
21
21
  Unparser.unparse(your_ast) # => "the code"
22
22
  ```
23
23
 
24
+ To preserve the comments from the source:
25
+
26
+ ```ruby
27
+ require 'parser/current'
28
+ require 'unparser'
29
+ ast, comments = Parser::CurrentRuby.parse_with_comments(your_source)
30
+ Unparser.unparse(ast, comments) # => "the code # with comments"
31
+ ```
32
+
24
33
  Equivalent vs identical:
25
34
 
26
35
  ```ruby
@@ -45,10 +54,11 @@ Installation
45
54
 
46
55
  Install the gem `unparser` via your prefered method.
47
56
 
48
- Credits
49
- -------
57
+ People
58
+ ------
50
59
 
51
60
  * [Markus Schirp (mbj)](https://github.com/mbj) Author
61
+ * [Trent Ogren](https://github.com/misfo) Adding comment reproduction
52
62
 
53
63
  Contributing
54
64
  -------------
data/lib/unparser.rb CHANGED
@@ -7,20 +7,23 @@ module Unparser
7
7
 
8
8
  EMPTY_STRING = ''.freeze
9
9
 
10
- # Unparse ast into string
10
+ # Unparse an AST (and, optionally, comments) into a string
11
11
  #
12
12
  # @param [Parser::Node, nil] node
13
+ # @param [Array] comment_array
13
14
  #
14
15
  # @return [String]
15
16
  #
16
17
  # @api private
17
18
  #
18
- def self.unparse(node)
19
+ def self.unparse(node, comment_array = [])
19
20
  if node.nil?
20
21
  node = Parser::AST::Node.new(:empty)
21
22
  end
22
23
  buffer = Buffer.new
23
- Emitter.emitter(node, Emitter::Root.new(buffer)).write_to_buffer
24
+ comments = Comments.new(comment_array)
25
+ root = Emitter::Root.new(buffer, comments)
26
+ Emitter.emitter(node, root).write_to_buffer
24
27
  buffer.content
25
28
  end
26
29
 
@@ -42,6 +45,7 @@ module Unparser
42
45
  end # Unparser
43
46
 
44
47
  require 'unparser/buffer'
48
+ require 'unparser/comments'
45
49
  require 'unparser/constants'
46
50
  require 'unparser/emitter'
47
51
  require 'unparser/emitter/literal'
@@ -32,6 +32,19 @@ module Unparser
32
32
  self
33
33
  end
34
34
 
35
+ # Append a string without an indentation prefix
36
+ #
37
+ # @param [String] string
38
+ #
39
+ # @return [self]
40
+ #
41
+ # @api private
42
+ #
43
+ def append_without_prefix(string)
44
+ @content << string
45
+ self
46
+ end
47
+
35
48
  # Increase indent
36
49
  #
37
50
  # @return [self]
@@ -39,7 +52,6 @@ module Unparser
39
52
  # @api private
40
53
  #
41
54
  def indent
42
- nl
43
55
  @indent+=1
44
56
  self
45
57
  end
@@ -51,7 +63,6 @@ module Unparser
51
63
  # @api private
52
64
  #
53
65
  def unindent
54
- nl
55
66
  @indent-=1
56
67
  self
57
68
  end
@@ -67,6 +78,20 @@ module Unparser
67
78
  self
68
79
  end
69
80
 
81
+ # Test for a fresh line
82
+ #
83
+ # @return [true]
84
+ # if the buffer content ends with a fresh line
85
+ #
86
+ # @return [false]
87
+ # otherwise
88
+ #
89
+ # @api private
90
+ #
91
+ def fresh_line?
92
+ @content.empty? || @content[-1] == NL
93
+ end
94
+
70
95
  # Return content of buffer
71
96
  #
72
97
  # @return [String]
@@ -77,6 +102,18 @@ module Unparser
77
102
  @content.dup.freeze
78
103
  end
79
104
 
105
+ # Capture the content written to the buffer within the block
106
+ #
107
+ # @return [String]
108
+ #
109
+ # @api private
110
+ #
111
+ def capture_content
112
+ size_before = @content.size
113
+ yield
114
+ @content[size_before..-1]
115
+ end
116
+
80
117
  private
81
118
 
82
119
  # Write prefix
@@ -0,0 +1,112 @@
1
+ module Unparser
2
+
3
+ # Holds the comments that remain to be emitted
4
+ #
5
+ # @api private
6
+ #
7
+ class Comments
8
+
9
+ # Initialize object
10
+ #
11
+ # @param [Array] comments
12
+ #
13
+ # @return [undefined]
14
+ #
15
+ def initialize(comments)
16
+ @comments = comments.dup
17
+ @eol_text_to_skip = nil
18
+ end
19
+
20
+ # Consume part or all of the node
21
+ #
22
+ # @param [Parser::AST::Node] node
23
+ # @param [Symbol] source_part
24
+ #
25
+ # @return [undefined]
26
+ #
27
+ def consume(node, source_part = :expression)
28
+ return unless node.location
29
+ @last_range_consumed = node.location.public_send(source_part)
30
+ end
31
+
32
+ # Skip any EOL comment with the specified text next time they're taken
33
+ #
34
+ # @param [String] comment_text
35
+ #
36
+ # @return [undefined]
37
+ #
38
+ def skip_eol_comment(comment_text)
39
+ @eol_text_to_skip = comment_text
40
+ end
41
+
42
+ # Take end-of-line comments
43
+ #
44
+ # @return [Array]
45
+ #
46
+ def take_eol_comments
47
+ text_to_skip = @eol_text_to_skip
48
+ @eol_text_to_skip = nil
49
+ return [] if @last_range_consumed.nil?
50
+ comments = take_up_to_line(@last_range_consumed.end.line)
51
+ eol_comments = unshift_documents(comments)
52
+ eol_comments.reject {|comment| comment.text == text_to_skip }
53
+ end
54
+
55
+ # Take all remaining comments
56
+ #
57
+ # @return [Array]
58
+ #
59
+ def take_all
60
+ take_while { true }
61
+ end
62
+
63
+ # Take comments appear in the source before the specified part of the node
64
+ #
65
+ # @param [Parser::AST::Node] node
66
+ # @param [Symbol] source_part
67
+ #
68
+ # @return [Array]
69
+ #
70
+ def take_before(node, source_part)
71
+ loc = node.location
72
+ range = loc.public_send(source_part) if loc.respond_to?(source_part)
73
+ return [] if range.nil?
74
+ take_while { |comment| comment.location.expression.end_pos <= range.begin_pos }
75
+ end
76
+
77
+ private
78
+
79
+ # Take comments while the provided block returns true
80
+ #
81
+ # @yield [comment]
82
+ #
83
+ # @return [Array]
84
+ #
85
+ def take_while
86
+ number_to_take = @comments.index {|comment| !yield(comment) } || @comments.size
87
+ @comments.shift(number_to_take)
88
+ end
89
+
90
+ # Take comments up to the line number
91
+ #
92
+ # @param [Fixnum] line
93
+ #
94
+ # @return [Array]
95
+ #
96
+ def take_up_to_line(line)
97
+ take_while { |comment| comment.location.expression.line <= line }
98
+ end
99
+
100
+ # Unshift document comments and return the rest
101
+ #
102
+ # @param [Array] comments
103
+ #
104
+ # @return [Array]
105
+ #
106
+ def unshift_documents(comments)
107
+ doc_comments, other_comments = comments.partition(&:document?)
108
+ doc_comments.reverse_each {|comment| @comments.unshift(comment) }
109
+ other_comments
110
+ end
111
+ end
112
+ end
@@ -84,7 +84,10 @@ module Unparser
84
84
  # @api private
85
85
  #
86
86
  def write_to_buffer
87
+ emit_comments_before if buffer.fresh_line?
87
88
  dispatch
89
+ comments.consume(node)
90
+ emit_eof_comments if parent.is_a?(Root)
88
91
  self
89
92
  end
90
93
  memoize :write_to_buffer
@@ -147,6 +150,17 @@ module Unparser
147
150
  end
148
151
  memoize :buffer, :freezer => :noop
149
152
 
153
+ # Return comments
154
+ #
155
+ # @return [Comments] comments
156
+ #
157
+ # @api private
158
+ #
159
+ def comments
160
+ parent.comments
161
+ end
162
+ memoize :comments, :freezer => :noop
163
+
150
164
  private
151
165
 
152
166
  # Emit contents of block within parentheses
@@ -276,9 +290,73 @@ module Unparser
276
290
  # @api private
277
291
  #
278
292
  def nl
293
+ emit_eol_comments
279
294
  buffer.nl
280
295
  end
281
296
 
297
+ # Write comments that appeared before source_part in the source
298
+ #
299
+ # @param [Symbol] source_part
300
+ #
301
+ # @return [undefined]
302
+ #
303
+ # @api private
304
+ #
305
+ def emit_comments_before(source_part = :expression)
306
+ comments_before = comments.take_before(node, source_part)
307
+ unless comments_before.empty?
308
+ emit_comments(comments_before)
309
+ buffer.nl
310
+ end
311
+ end
312
+
313
+ # Write end-of-line comments
314
+ #
315
+ # @return [undefined]
316
+ #
317
+ # @api private
318
+ #
319
+ def emit_eol_comments
320
+ comments.take_eol_comments.each do |comment|
321
+ write(WS, comment.text)
322
+ end
323
+ end
324
+
325
+ # Write end-of-file comments
326
+ #
327
+ # @return [undefined]
328
+ #
329
+ # @api private
330
+ #
331
+ def emit_eof_comments
332
+ emit_eol_comments
333
+ comments_left = comments.take_all
334
+ unless comments_left.empty?
335
+ buffer.nl
336
+ emit_comments(comments_left)
337
+ end
338
+ end
339
+
340
+ # Write each comment to a separate line
341
+ #
342
+ # @param [Array] comment_array
343
+ #
344
+ # @return [undefined]
345
+ #
346
+ # @api private
347
+ #
348
+ def emit_comments(comment_array)
349
+ max = comment_array.size - 1
350
+ comment_array.each_with_index do |comment, index|
351
+ if comment.type == :document
352
+ buffer.append_without_prefix(comment.text.chomp)
353
+ else
354
+ write(comment.text)
355
+ end
356
+ buffer.nl if index < max
357
+ end
358
+ end
359
+
282
360
  # Write strings into buffer
283
361
  #
284
362
  # @return [undefined]
@@ -298,6 +376,9 @@ module Unparser
298
376
  # @api private
299
377
  #
300
378
  def k_end
379
+ buffer.indent
380
+ emit_comments_before(:end)
381
+ buffer.unindent
301
382
  write(K_END)
302
383
  end
303
384
 
@@ -334,7 +415,9 @@ module Unparser
334
415
  def indented
335
416
  buffer = self.buffer
336
417
  buffer.indent
418
+ nl
337
419
  yield
420
+ nl
338
421
  buffer.unindent
339
422
  end
340
423
 
@@ -348,7 +431,9 @@ module Unparser
348
431
  #
349
432
  def emit_body(body = self.body)
350
433
  unless body
434
+ buffer.indent
351
435
  nl
436
+ buffer.unindent
352
437
  return
353
438
  end
354
439
  visit_indented(body)
@@ -376,7 +461,7 @@ module Unparser
376
461
  # if parent is present
377
462
  #
378
463
  # @return [nil]
379
- # otherwiseo
464
+ # otherwise
380
465
  #
381
466
  # @api private
382
467
  #
@@ -396,14 +481,19 @@ module Unparser
396
481
  Parser::AST::Node.new(type, *children)
397
482
  end
398
483
 
399
- # Helper to introduce comment
484
+ # Helper to introduce an end-of-line comment
400
485
  #
401
486
  # @return [undefined]
402
487
  #
403
488
  # @api private
404
489
  #
405
- def comment
406
- write(WS, COMMENT, WS)
490
+ def eol_comment
491
+ write(WS)
492
+ comment = buffer.capture_content do
493
+ write(COMMENT, WS)
494
+ yield
495
+ end
496
+ comments.skip_eol_comment(comment)
407
497
  end
408
498
 
409
499
  # Emitter that fully relies on parser source maps
@@ -19,6 +19,7 @@ module Unparser
19
19
  def dispatch
20
20
  visit(send)
21
21
  write(WS, K_DO)
22
+ comments.consume(node, :begin)
22
23
  emit_block_arguments
23
24
  emit_body
24
25
  k_end
@@ -22,8 +22,7 @@ module Unparser
22
22
  emit_body
23
23
  k_end
24
24
  unless parent_type == :send
25
- comment
26
- visit(name)
25
+ eol_comment { visit(name) }
27
26
  end
28
27
  end
29
28
 
@@ -32,6 +32,7 @@ module Unparser
32
32
  def dispatch
33
33
  write(K_DEF, WS)
34
34
  emit_name
35
+ comments.consume(node, :name)
35
36
  emit_arguments
36
37
  emit_body
37
38
  k_end
@@ -21,8 +21,7 @@ module Unparser
21
21
  emit_body
22
22
  k_end
23
23
  if parent_type != :send
24
- comment
25
- visit(name)
24
+ eol_comment { visit(name) }
26
25
  end
27
26
  end
28
27
 
@@ -2,7 +2,7 @@ module Unparser
2
2
  class Emitter
3
3
  # Root emitter a special case
4
4
  class Root < self
5
- include Concord::Public.new(:buffer)
5
+ include Concord::Public.new(:buffer, :comments)
6
6
  end # Root
7
7
  end # Emitter
8
8
  end # Unparser
@@ -159,7 +159,7 @@ module Unparser
159
159
  assignment? && !arguments?
160
160
  end
161
161
 
162
- # Test for assigment
162
+ # Test for assignment
163
163
  #
164
164
  # @return [true]
165
165
  # if node represents attribute / element assignment
@@ -17,16 +17,6 @@ module Unparser
17
17
  emit_arguments
18
18
  end
19
19
 
20
- # Emit block within parentheses
21
- #
22
- # @return [undefined]
23
- #
24
- # @api private
25
- #
26
- def parentheses(&block)
27
- super(*INDEX_PARENS, &block)
28
- end
29
-
30
20
  # Emit receiver
31
21
  #
32
22
  # @return [undefined]
@@ -49,7 +39,7 @@ module Unparser
49
39
  # @api private
50
40
  #
51
41
  def emit_arguments
52
- parentheses do
42
+ parentheses(*INDEX_PARENS) do
53
43
  delimited(arguments)
54
44
  end
55
45
  end
@@ -66,7 +56,7 @@ module Unparser
66
56
  #
67
57
  def emit_arguments
68
58
  index, *assignment = arguments
69
- parentheses do
59
+ parentheses(*INDEX_PARENS) do
70
60
  delimited([index])
71
61
  end
72
62
  return if assignment.empty? # mlhs
@@ -13,6 +13,7 @@ describe Unparser::Buffer, '#append' do
13
13
  # Yeah duplicate, mutant will be improved ;)
14
14
  it 'should prefix with indentation if line is empty' do
15
15
  object.append('foo')
16
+ object.nl
16
17
  object.indent
17
18
  object.append('bar')
18
19
  object.append('baz')
@@ -0,0 +1,23 @@
1
+ require 'spec_helper'
2
+
3
+ describe Unparser::Buffer, '#append_without_prefix' do
4
+ subject { object.append_without_prefix(string) }
5
+
6
+ let(:object) { described_class.new }
7
+ let(:string) { 'foo' }
8
+
9
+ specify do
10
+ expect { subject }.to change { object.content }.from('').to('foo')
11
+ end
12
+
13
+ it 'should not prefix with indentation' do
14
+ object.append_without_prefix('foo')
15
+ object.nl
16
+ object.indent
17
+ object.append_without_prefix('bar')
18
+ object.append_without_prefix('baz')
19
+ expect(object.content).to eql("foo\nbarbaz")
20
+ end
21
+
22
+ it_should_behave_like 'a command method'
23
+ end
@@ -0,0 +1,17 @@
1
+ require 'spec_helper'
2
+
3
+ describe Unparser::Buffer, '#capture_content' do
4
+
5
+ let(:object) { described_class.new }
6
+
7
+ it 'should capture only the content appended within the block' do
8
+ object.append('foo')
9
+ object.nl
10
+ object.indent
11
+ captured = object.capture_content do
12
+ object.append('bar')
13
+ object.nl
14
+ end
15
+ expect(captured).to eql(" bar\n")
16
+ end
17
+ end
@@ -0,0 +1,20 @@
1
+ require 'spec_helper'
2
+
3
+ describe Unparser::Buffer, '#fresh_line?' do
4
+ let(:object) { described_class.new }
5
+
6
+ it 'should return true while buffer is empty' do
7
+ expect(object.fresh_line?).to eql(true)
8
+ end
9
+
10
+ it 'should return false after content has been appended' do
11
+ object.append('foo')
12
+ expect(object.fresh_line?).to eql(false)
13
+ end
14
+
15
+ it 'should return true after a nl has been appended' do
16
+ object.append('foo')
17
+ object.nl
18
+ expect(object.fresh_line?).to eql(true)
19
+ end
20
+ end
@@ -7,8 +7,10 @@ describe Unparser::Buffer, '#indent' do
7
7
 
8
8
  it 'should indent with two chars' do
9
9
  object.append('foo')
10
+ object.nl
10
11
  object.indent
11
12
  object.append('bar')
13
+ object.nl
12
14
  object.indent
13
15
  object.append('baz')
14
16
  expect(object.content).to eql("foo\n bar\n baz")
@@ -7,8 +7,10 @@ describe Unparser::Buffer, '#unindent' do
7
7
 
8
8
  it 'unindents two chars' do
9
9
  object.append('foo')
10
+ object.nl
10
11
  object.indent
11
12
  object.append('bar')
13
+ object.nl
12
14
  object.unindent
13
15
  object.append('baz')
14
16
  expect(object.content).to eql("foo\n bar\nbaz")
@@ -0,0 +1,23 @@
1
+ require 'spec_helper'
2
+ require 'parser/current'
3
+
4
+ describe Unparser::Comments, '#consume' do
5
+
6
+ let(:ast_and_comments) do
7
+ Parser::CurrentRuby.parse_with_comments(<<-RUBY)
8
+ def hi # EOL 1
9
+ end # EOL 2
10
+ RUBY
11
+ end
12
+ let(:ast) { ast_and_comments[0] }
13
+ let(:comments) { ast_and_comments[1] }
14
+ let(:object) { described_class.new(comments) }
15
+
16
+ it 'should cause further EOL comments to be returned' do
17
+ expect(object.take_eol_comments).to eql([])
18
+ object.consume(ast, :name)
19
+ expect(object.take_eol_comments).to eql([comments[0]])
20
+ object.consume(ast, :end)
21
+ expect(object.take_eol_comments).to eql([comments[1]])
22
+ end
23
+ end
@@ -0,0 +1,29 @@
1
+ require 'spec_helper'
2
+ require 'parser/current'
3
+
4
+ describe Unparser::Comments, '#skip_eol_comment' do
5
+
6
+ let(:ast_and_comments) do
7
+ Parser::CurrentRuby.parse_with_comments(<<-RUBY)
8
+ def hi # comment
9
+ end # comment
10
+ RUBY
11
+ end
12
+ let(:ast) { ast_and_comments[0] }
13
+ let(:comments) { ast_and_comments[1] }
14
+ let(:object) { described_class.new(comments) }
15
+
16
+ it 'should skip the specified comment only for one subsequent take' do
17
+ object.consume(ast, :name)
18
+ object.skip_eol_comment("# comment")
19
+ expect(object.take_eol_comments).to eql([])
20
+ object.consume(ast, :end)
21
+ expect(object.take_eol_comments).to eql([comments[1]])
22
+ end
23
+
24
+ it 'should not skip comments with different text' do
25
+ object.skip_eol_comment("# not the comment")
26
+ object.consume(ast, :end)
27
+ expect(object.take_eol_comments).to eql(comments)
28
+ end
29
+ end
@@ -0,0 +1,20 @@
1
+ require 'spec_helper'
2
+ require 'parser/current'
3
+
4
+ describe Unparser::Comments, '#take_all' do
5
+
6
+ let(:ast_and_comments) do
7
+ Parser::CurrentRuby.parse_with_comments(<<-RUBY)
8
+ def hi # EOL 1
9
+ end # EOL 2
10
+ RUBY
11
+ end
12
+ let(:ast) { ast_and_comments[0] }
13
+ let(:comments) { ast_and_comments[1] }
14
+ let(:object) { described_class.new(comments) }
15
+
16
+ it 'should take all comments' do
17
+ expect(object.take_all).to eql(comments)
18
+ expect(object.take_all).to eql([])
19
+ end
20
+ end
@@ -0,0 +1,25 @@
1
+ require 'spec_helper'
2
+ require 'parser/current'
3
+
4
+ describe Unparser::Comments, '#take_before' do
5
+
6
+ let(:ast_and_comments) do
7
+ Parser::CurrentRuby.parse_with_comments(<<-RUBY)
8
+ def hi # EOL 1
9
+ # comment
10
+ end # EOL 2
11
+ RUBY
12
+ end
13
+ let(:ast) { ast_and_comments[0] }
14
+ let(:comments) { ast_and_comments[1] }
15
+ let(:object) { described_class.new(comments) }
16
+
17
+ it 'should return no comments none are before the node' do
18
+ expect(object.take_before(ast, :expression)).to eql([])
19
+ end
20
+
21
+ it 'should only the comments that are before the specified part of the node' do
22
+ expect(object.take_before(ast, :end)).to eql(comments.first(2))
23
+ expect(object.take_all).to eql([comments[2]])
24
+ end
25
+ end
@@ -0,0 +1,33 @@
1
+ require 'spec_helper'
2
+ require 'parser/current'
3
+
4
+ describe Unparser::Comments, '#take_eol_comments' do
5
+
6
+ let(:ast_and_comments) do
7
+ Parser::CurrentRuby.parse_with_comments(<<-RUBY)
8
+ def hi # EOL 1
9
+ =begin
10
+ doc comment
11
+ =end
12
+ end # EOL 2
13
+ RUBY
14
+ end
15
+ let(:ast) { ast_and_comments[0] }
16
+ let(:comments) { ast_and_comments[1] }
17
+ let(:object) { described_class.new(comments) }
18
+
19
+ it 'should return no comments if nothing has been consumed' do
20
+ expect(object.take_eol_comments).to eql([])
21
+ end
22
+
23
+ it 'should return comments once their line has been consumed' do
24
+ object.consume(ast, :name)
25
+ expect(object.take_eol_comments).to eql([comments[0]])
26
+ end
27
+
28
+ it 'should leave doc comments to be taken later' do
29
+ object.consume(ast)
30
+ expect(object.take_eol_comments).to eql([comments[0], comments[2]])
31
+ expect(object.take_all).to eql([comments[1]])
32
+ end
33
+ end
@@ -25,6 +25,7 @@ describe Unparser do
25
25
  end
26
26
 
27
27
  def self.strip(ruby)
28
+ return ruby if ruby.empty?
28
29
  lines = ruby.lines
29
30
  line = lines.first
30
31
  match = /\A[ ]*/.match(line)
@@ -35,34 +36,39 @@ describe Unparser do
35
36
  source.chomp
36
37
  end
37
38
 
38
- def assert_round_trip(input, parser)
39
- ast = parser.parse(input)
40
- generated = Unparser.unparse(ast)
39
+ def assert_round_trip(input, parser_class)
40
+ ast, comments = parser_class.parse_with_comments(input)
41
+ generated = Unparser.unparse(ast, comments)
41
42
  generated.should eql(input)
42
43
  end
43
44
 
44
45
  def self.assert_generates_one_way(ast, expected, versions = RUBIES)
45
- with_versions(versions) do |version, parser|
46
+ with_versions(versions) do |version, parser_class|
46
47
  it "should generate #{ast.inspect} as #{expected} under #{version}" do
48
+ comments = []
47
49
  if ast.kind_of?(String)
48
- ast = parser.parse(ast)
50
+ ast, comments = parser_class.parse_with_comments(input)
49
51
  end
50
- generated = Unparser.unparse(ast)
52
+ generated = Unparser.unparse(ast, comments)
51
53
  generated.should eql(expected)
52
54
  end
53
55
  end
54
56
  end
55
57
 
56
- def self.assert_generates(ast, expected, versions = RUBIES)
57
- with_versions(versions) do |version, parser|
58
- it "should generate #{ast.inspect} as #{expected} under #{version}" do
59
- if ast.kind_of?(String)
60
- ast = parser.parse(ast)
61
- end
62
- generated = Unparser.unparse(ast)
58
+ def self.assert_generates(ast_or_string, expected, versions = RUBIES)
59
+ ast_or_string = strip(ast_or_string) if ast_or_string.is_a?(String)
60
+ expected = strip(expected)
61
+ with_versions(versions) do |version, parser_class|
62
+ it "should generate #{ast_or_string.inspect} as #{expected} under #{version}" do
63
+ ast, comments = if ast_or_string.kind_of?(String)
64
+ parser_class.parse_with_comments(ast_or_string)
65
+ else
66
+ [ast_or_string, []]
67
+ end
68
+ generated = Unparser.unparse(ast, comments)
63
69
  generated.should eql(expected)
64
- ast = parser.parse(generated)
65
- Unparser.unparse(ast).should eql(expected)
70
+ ast, comments = parser_class.parse_with_comments(generated)
71
+ Unparser.unparse(ast, comments).should eql(expected)
66
72
  end
67
73
  end
68
74
  end
@@ -1034,6 +1040,7 @@ describe Unparser do
1034
1040
  assert_source 'a &&= b'
1035
1041
  assert_source 'a ||= 2'
1036
1042
  assert_source '(a ||= 2).bar'
1043
+ assert_source '(h ||= {})[k] = v'
1037
1044
  end
1038
1045
 
1039
1046
  context 'flip flops' do
@@ -1190,5 +1197,72 @@ describe Unparser do
1190
1197
  end
1191
1198
  RUBY
1192
1199
  end
1200
+
1201
+ context 'comments' do
1202
+ assert_source <<-RUBY
1203
+ # comment before
1204
+ a_line_of_code
1205
+ RUBY
1206
+
1207
+ assert_source <<-RUBY
1208
+ a_line_of_code # comment after
1209
+ RUBY
1210
+
1211
+ assert_source <<-RUBY
1212
+ nested do # first
1213
+ # second
1214
+ something # comment
1215
+ # another
1216
+ end
1217
+ # last
1218
+ RUBY
1219
+
1220
+ assert_source <<-RUBY
1221
+ def noop
1222
+ # do nothing
1223
+ end
1224
+ RUBY
1225
+
1226
+ assert_source <<-RUBY
1227
+ =begin
1228
+ block comment
1229
+ =end
1230
+ nested do
1231
+ =begin
1232
+ another block comment
1233
+ =end
1234
+ something
1235
+ =begin
1236
+ last block comment
1237
+ =end
1238
+ end
1239
+ RUBY
1240
+
1241
+ assert_generates(<<-RUBY, <<-RUBY)
1242
+ 1 + # first
1243
+ 2 # second
1244
+ RUBY
1245
+ 1 + 2 # first # second
1246
+ RUBY
1247
+ assert_generates(<<-RUBY, <<-RUBY)
1248
+ 1 +
1249
+ # first
1250
+ 2 # second
1251
+ RUBY
1252
+ 1 + 2 # first # second
1253
+ RUBY
1254
+ assert_generates(<<-RUBY, <<-RUBY)
1255
+ 1 +
1256
+ =begin
1257
+ block comment
1258
+ =end
1259
+ 2
1260
+ RUBY
1261
+ 1 + 2
1262
+ =begin
1263
+ block comment
1264
+ =end
1265
+ RUBY
1266
+ end
1193
1267
  end
1194
1268
  end
data/unparser.gemspec CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = 'unparser'
5
- s.version = '0.1.2'
5
+ s.version = '0.1.3'
6
6
 
7
7
  s.authors = ['Markus Schirp']
8
8
  s.email = 'mbj@schir-dso.com'
@@ -17,7 +17,7 @@ Gem::Specification.new do |s|
17
17
  s.extra_rdoc_files = %w(README.md)
18
18
  s.executables = [ 'test-unparser' ]
19
19
 
20
- s.add_dependency('parser', '~> 2.0.0.pre6')
20
+ s.add_dependency('parser', '~> 2.0.0.pre7')
21
21
  s.add_dependency('concord', '~> 0.1.1' )
22
22
  s.add_dependency('adamantium', '~> 0.1.0' )
23
23
  s.add_dependency('equalizer', '~> 0.0.7' )
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: unparser
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.1.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Markus Schirp
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-09-07 00:00:00.000000000 Z
11
+ date: 2013-09-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: parser
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - ~>
18
18
  - !ruby/object:Gem::Version
19
- version: 2.0.0.pre6
19
+ version: 2.0.0.pre7
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - ~>
25
25
  - !ruby/object:Gem::Version
26
- version: 2.0.0.pre6
26
+ version: 2.0.0.pre7
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: concord
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -109,6 +109,7 @@ files:
109
109
  - config/yardstick.yml
110
110
  - lib/unparser.rb
111
111
  - lib/unparser/buffer.rb
112
+ - lib/unparser/comments.rb
112
113
  - lib/unparser/constants.rb
113
114
  - lib/unparser/emitter.rb
114
115
  - lib/unparser/emitter/alias.rb
@@ -162,10 +163,18 @@ files:
162
163
  - lib/unparser/finalize.rb
163
164
  - spec/spec_helper.rb
164
165
  - spec/unit/unparser/buffer/append_spec.rb
166
+ - spec/unit/unparser/buffer/append_without_prefix_spec.rb
167
+ - spec/unit/unparser/buffer/capture_content_spec.rb
165
168
  - spec/unit/unparser/buffer/content_spec.rb
169
+ - spec/unit/unparser/buffer/fresh_line_spec.rb
166
170
  - spec/unit/unparser/buffer/indent_spec.rb
167
171
  - spec/unit/unparser/buffer/nl_spec.rb
168
172
  - spec/unit/unparser/buffer/unindent_spec.rb
173
+ - spec/unit/unparser/comments/consume_spec.rb
174
+ - spec/unit/unparser/comments/skip_eol_comment_spec.rb
175
+ - spec/unit/unparser/comments/take_all_spec.rb
176
+ - spec/unit/unparser/comments/take_before_spec.rb
177
+ - spec/unit/unparser/comments/take_eol_comments_spec.rb
169
178
  - spec/unit/unparser/emitter/class_methods/handle_spec.rb
170
179
  - spec/unit/unparser/emitter/source_map/class_methods/emit_spec.rb
171
180
  - spec/unit/unparser_spec.rb
@@ -197,10 +206,18 @@ summary: Generate equivalent source for parser gem AST nodes
197
206
  test_files:
198
207
  - spec/spec_helper.rb
199
208
  - spec/unit/unparser/buffer/append_spec.rb
209
+ - spec/unit/unparser/buffer/append_without_prefix_spec.rb
210
+ - spec/unit/unparser/buffer/capture_content_spec.rb
200
211
  - spec/unit/unparser/buffer/content_spec.rb
212
+ - spec/unit/unparser/buffer/fresh_line_spec.rb
201
213
  - spec/unit/unparser/buffer/indent_spec.rb
202
214
  - spec/unit/unparser/buffer/nl_spec.rb
203
215
  - spec/unit/unparser/buffer/unindent_spec.rb
216
+ - spec/unit/unparser/comments/consume_spec.rb
217
+ - spec/unit/unparser/comments/skip_eol_comment_spec.rb
218
+ - spec/unit/unparser/comments/take_all_spec.rb
219
+ - spec/unit/unparser/comments/take_before_spec.rb
220
+ - spec/unit/unparser/comments/take_eol_comments_spec.rb
204
221
  - spec/unit/unparser/emitter/class_methods/handle_spec.rb
205
222
  - spec/unit/unparser/emitter/source_map/class_methods/emit_spec.rb
206
223
  - spec/unit/unparser_spec.rb