unparser 0.1.2 → 0.1.3

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 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