unparser 0.4.7 → 0.4.8
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/.github/workflows/ci.yml +90 -0
- data/.rubocop.yml +122 -5
- data/Changelog.md +6 -0
- data/Gemfile.lock +35 -105
- data/README.md +1 -2
- data/lib/unparser.rb +21 -3
- data/lib/unparser/cli.rb +65 -45
- data/lib/unparser/{cli/color.rb → color.rb} +0 -10
- data/lib/unparser/diff.rb +115 -0
- data/lib/unparser/emitter.rb +2 -1
- data/lib/unparser/validation.rb +149 -0
- data/spec/integration/unparser/corpus_spec.rb +33 -19
- data/spec/integrations.yml +1 -1
- data/spec/spec_helper.rb +26 -4
- data/spec/unit/unparser/color_spec.rb +40 -0
- data/spec/unit/unparser/diff_spec.rb +189 -0
- data/spec/unit/unparser/validation_spec.rb +327 -0
- data/spec/unit/unparser_spec.rb +74 -3
- data/unparser.gemspec +11 -8
- metadata +78 -31
- data/.circleci/config.yml +0 -49
- data/config/rubocop.yml +0 -122
- data/lib/unparser/cli/differ.rb +0 -152
- data/lib/unparser/cli/source.rb +0 -267
data/lib/unparser/cli/differ.rb
DELETED
@@ -1,152 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Unparser
|
4
|
-
class CLI
|
5
|
-
# Class to create diffs from source code
|
6
|
-
class Differ
|
7
|
-
include Adamantium::Flat, Concord.new(:old, :new), Procto.call(:colorized_diff)
|
8
|
-
|
9
|
-
CONTEXT_LINES = 5
|
10
|
-
|
11
|
-
# Return new object
|
12
|
-
#
|
13
|
-
# @param [String] old
|
14
|
-
# @param [String] new
|
15
|
-
#
|
16
|
-
# @return [Differ]
|
17
|
-
#
|
18
|
-
# @api private
|
19
|
-
#
|
20
|
-
def self.build(old, new)
|
21
|
-
new(lines(old), lines(new))
|
22
|
-
end
|
23
|
-
|
24
|
-
# Return colorized diff line
|
25
|
-
#
|
26
|
-
# @param [String] line
|
27
|
-
#
|
28
|
-
# @return [String]
|
29
|
-
#
|
30
|
-
# @api private
|
31
|
-
#
|
32
|
-
def self.colorize_line(line)
|
33
|
-
case line[0]
|
34
|
-
when '+'
|
35
|
-
Color::GREEN
|
36
|
-
when '-'
|
37
|
-
Color::RED
|
38
|
-
else
|
39
|
-
Color::NONE
|
40
|
-
end.format(line)
|
41
|
-
end
|
42
|
-
|
43
|
-
# Break up source into lines
|
44
|
-
#
|
45
|
-
# @param [String] source
|
46
|
-
#
|
47
|
-
# @return [Array<String>]
|
48
|
-
#
|
49
|
-
# @api private
|
50
|
-
#
|
51
|
-
def self.lines(source)
|
52
|
-
source.lines.map(&:chomp)
|
53
|
-
end
|
54
|
-
private_class_method :lines
|
55
|
-
|
56
|
-
# Return hunks
|
57
|
-
#
|
58
|
-
# @return [Array<Diff::LCS::Hunk>]
|
59
|
-
#
|
60
|
-
# @api private
|
61
|
-
#
|
62
|
-
def hunks
|
63
|
-
file_length_difference = new.length - old.length
|
64
|
-
diffs.map do |piece|
|
65
|
-
hunk = Diff::LCS::Hunk.new(old, new, piece, CONTEXT_LINES, file_length_difference)
|
66
|
-
file_length_difference = hunk.file_length_difference
|
67
|
-
hunk
|
68
|
-
end
|
69
|
-
end
|
70
|
-
|
71
|
-
# Return collapsed hunks
|
72
|
-
#
|
73
|
-
# @return [Enumerable<Diff::LCS::Hunk>]
|
74
|
-
#
|
75
|
-
# @api private
|
76
|
-
#
|
77
|
-
def collapsed_hunks
|
78
|
-
hunks.each_with_object([]) do |hunk, output|
|
79
|
-
last = output.last
|
80
|
-
|
81
|
-
if last && hunk.merge(last)
|
82
|
-
output.pop
|
83
|
-
end
|
84
|
-
|
85
|
-
output << hunk
|
86
|
-
end
|
87
|
-
end
|
88
|
-
|
89
|
-
# Return source diff
|
90
|
-
#
|
91
|
-
# @return [String]
|
92
|
-
# if there is a diff
|
93
|
-
#
|
94
|
-
# @return [nil]
|
95
|
-
# otherwise
|
96
|
-
#
|
97
|
-
# @api private
|
98
|
-
#
|
99
|
-
def diff
|
100
|
-
output = +''
|
101
|
-
|
102
|
-
collapsed_hunks.each do |hunk|
|
103
|
-
output << hunk.diff(:unified) << "\n"
|
104
|
-
end
|
105
|
-
|
106
|
-
output
|
107
|
-
end
|
108
|
-
memoize :diff
|
109
|
-
|
110
|
-
# Return colorized source diff
|
111
|
-
#
|
112
|
-
# @return [String]
|
113
|
-
# if there is a diff
|
114
|
-
#
|
115
|
-
# @return [nil]
|
116
|
-
# otherwise
|
117
|
-
#
|
118
|
-
# @api private
|
119
|
-
#
|
120
|
-
def colorized_diff
|
121
|
-
diff.lines.map do |line|
|
122
|
-
self.class.colorize_line(line)
|
123
|
-
end.join
|
124
|
-
end
|
125
|
-
memoize :colorized_diff
|
126
|
-
|
127
|
-
private
|
128
|
-
|
129
|
-
# Return diffs
|
130
|
-
#
|
131
|
-
# @return [Array<Array>]
|
132
|
-
#
|
133
|
-
# @api private
|
134
|
-
#
|
135
|
-
def diffs
|
136
|
-
Diff::LCS.diff(old, new)
|
137
|
-
end
|
138
|
-
memoize :diffs
|
139
|
-
|
140
|
-
# Return max length
|
141
|
-
#
|
142
|
-
# @return [Fixnum]
|
143
|
-
#
|
144
|
-
# @api private
|
145
|
-
#
|
146
|
-
def max_length
|
147
|
-
[old, new].map(&:length).max
|
148
|
-
end
|
149
|
-
|
150
|
-
end # CLI
|
151
|
-
end # Differ
|
152
|
-
end # Unparser
|
data/lib/unparser/cli/source.rb
DELETED
@@ -1,267 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Unparser
|
4
|
-
class CLI
|
5
|
-
# Source representation for CLI sources
|
6
|
-
#
|
7
|
-
# ignore :reek:TooManyMethods
|
8
|
-
class Source
|
9
|
-
include AbstractType, Adamantium::Flat, NodeHelpers
|
10
|
-
|
11
|
-
# Source state generated after first unparse
|
12
|
-
class Generated
|
13
|
-
include Concord::Public.new(:source, :ast, :error)
|
14
|
-
|
15
|
-
# Test if source was generated successfully
|
16
|
-
#
|
17
|
-
# @return [Boolean]
|
18
|
-
#
|
19
|
-
# @api private
|
20
|
-
#
|
21
|
-
def success?
|
22
|
-
!error
|
23
|
-
end
|
24
|
-
|
25
|
-
# Build generated source
|
26
|
-
#
|
27
|
-
# @param [Parser::AST::Node] ast
|
28
|
-
#
|
29
|
-
# @api private
|
30
|
-
#
|
31
|
-
def self.build(ast)
|
32
|
-
source = Unparser.unparse(ast)
|
33
|
-
new(source, ast, nil)
|
34
|
-
rescue StandardError => exception
|
35
|
-
new(nil, ast, exception)
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
# Test if source could be unparsed successfully
|
40
|
-
#
|
41
|
-
# @return [Boolean]
|
42
|
-
#
|
43
|
-
# @api private
|
44
|
-
#
|
45
|
-
def success?
|
46
|
-
generated.success? && original_ast && generated_ast && original_ast.eql?(generated_ast)
|
47
|
-
end
|
48
|
-
|
49
|
-
# Return error report
|
50
|
-
#
|
51
|
-
# @return [String]
|
52
|
-
#
|
53
|
-
# @api private
|
54
|
-
#
|
55
|
-
def report
|
56
|
-
if original_ast && generated_ast
|
57
|
-
report_with_ast_diff
|
58
|
-
elsif !original_ast
|
59
|
-
report_original
|
60
|
-
elsif !generated.success?
|
61
|
-
report_unparser
|
62
|
-
elsif !generated_ast
|
63
|
-
report_generated
|
64
|
-
else
|
65
|
-
raise
|
66
|
-
end
|
67
|
-
end
|
68
|
-
memoize :report
|
69
|
-
|
70
|
-
private
|
71
|
-
|
72
|
-
# Return generated source
|
73
|
-
#
|
74
|
-
# @return [String]
|
75
|
-
#
|
76
|
-
# @api private
|
77
|
-
#
|
78
|
-
def generated
|
79
|
-
Source::Generated.build(original_ast)
|
80
|
-
end
|
81
|
-
memoize :generated
|
82
|
-
|
83
|
-
# Return stripped source
|
84
|
-
#
|
85
|
-
# @param [String] source
|
86
|
-
#
|
87
|
-
# @return [String]
|
88
|
-
#
|
89
|
-
# @api private
|
90
|
-
#
|
91
|
-
# ignore :reek:UtilityFunction
|
92
|
-
def strip(source)
|
93
|
-
source = source.rstrip
|
94
|
-
indent = source.scan(/^\s*/).first
|
95
|
-
source.gsub(/^#{indent}/, '')
|
96
|
-
end
|
97
|
-
|
98
|
-
# Return error report for parsing original
|
99
|
-
#
|
100
|
-
# @return [String]
|
101
|
-
#
|
102
|
-
# @api private
|
103
|
-
#
|
104
|
-
def report_original
|
105
|
-
strip(<<-MESSAGE)
|
106
|
-
Parsing of original source failed:
|
107
|
-
#{original_source}
|
108
|
-
MESSAGE
|
109
|
-
end
|
110
|
-
|
111
|
-
# Report unparser bug
|
112
|
-
#
|
113
|
-
# @return [String]
|
114
|
-
#
|
115
|
-
# @api private
|
116
|
-
#
|
117
|
-
def report_unparser
|
118
|
-
message = ['Unparsing parsed AST failed']
|
119
|
-
error = generated.error
|
120
|
-
message << error
|
121
|
-
error.backtrace.take(20).each(&message.method(:<<))
|
122
|
-
message << 'Original-AST:'
|
123
|
-
message << original_ast.inspect
|
124
|
-
message.join("\n")
|
125
|
-
end
|
126
|
-
|
127
|
-
# Return error report for parsing generated
|
128
|
-
#
|
129
|
-
# @return [String]
|
130
|
-
#
|
131
|
-
# @api private
|
132
|
-
#
|
133
|
-
def report_generated
|
134
|
-
strip(<<-MESSAGE)
|
135
|
-
Parsing of generated source failed:
|
136
|
-
Original-source:
|
137
|
-
#{original_source}
|
138
|
-
Original-AST:
|
139
|
-
#{original_ast.inspect}
|
140
|
-
Source:
|
141
|
-
#{generated.source}
|
142
|
-
MESSAGE
|
143
|
-
end
|
144
|
-
|
145
|
-
# Return error report with AST difference
|
146
|
-
#
|
147
|
-
# @return [String]
|
148
|
-
#
|
149
|
-
# @api private
|
150
|
-
#
|
151
|
-
def report_with_ast_diff
|
152
|
-
strip(<<-MESSAGE)
|
153
|
-
#{ast_diff}
|
154
|
-
Original-Source:\n#{original_source}
|
155
|
-
Original-AST:\n#{original_ast.inspect}
|
156
|
-
Generated-Source:\n#{generated.source}
|
157
|
-
Generated-AST:\n#{generated_ast.inspect}
|
158
|
-
MESSAGE
|
159
|
-
end
|
160
|
-
|
161
|
-
# Return ast diff
|
162
|
-
#
|
163
|
-
# @return [String]
|
164
|
-
#
|
165
|
-
# @api private
|
166
|
-
#
|
167
|
-
def ast_diff
|
168
|
-
Differ.call(
|
169
|
-
original_ast.inspect.lines.map(&:chomp),
|
170
|
-
generated_ast.inspect.lines.map(&:chomp)
|
171
|
-
)
|
172
|
-
end
|
173
|
-
|
174
|
-
# Return generated AST
|
175
|
-
#
|
176
|
-
# @return [Parser::AST::Node]
|
177
|
-
# if parser was sucessful for generated ast
|
178
|
-
#
|
179
|
-
# @return [nil]
|
180
|
-
# otherwise
|
181
|
-
#
|
182
|
-
# @api private
|
183
|
-
#
|
184
|
-
def generated_ast
|
185
|
-
generated.success? && Preprocessor.run(Unparser.parse(generated.source))
|
186
|
-
rescue Parser::SyntaxError
|
187
|
-
nil
|
188
|
-
end
|
189
|
-
memoize :generated_ast
|
190
|
-
|
191
|
-
# Return original AST
|
192
|
-
#
|
193
|
-
# @return [Parser::AST::Node]
|
194
|
-
#
|
195
|
-
# @api private
|
196
|
-
#
|
197
|
-
def original_ast
|
198
|
-
Preprocessor.run(Unparser.parse(original_source))
|
199
|
-
rescue Parser::SyntaxError
|
200
|
-
nil
|
201
|
-
end
|
202
|
-
memoize :original_ast
|
203
|
-
|
204
|
-
# CLI source from string
|
205
|
-
class String < self
|
206
|
-
include Concord.new(:original_source)
|
207
|
-
|
208
|
-
# Return identification
|
209
|
-
#
|
210
|
-
# @return [String]
|
211
|
-
#
|
212
|
-
# @api private
|
213
|
-
#
|
214
|
-
def identification
|
215
|
-
'(string)'
|
216
|
-
end
|
217
|
-
|
218
|
-
end # String
|
219
|
-
|
220
|
-
# CLI source from file
|
221
|
-
class File < self
|
222
|
-
include Concord.new(:file_name)
|
223
|
-
|
224
|
-
# Return identification
|
225
|
-
#
|
226
|
-
# @return [String]
|
227
|
-
#
|
228
|
-
# @api private
|
229
|
-
#
|
230
|
-
def identification
|
231
|
-
"(#{file_name})"
|
232
|
-
end
|
233
|
-
|
234
|
-
private
|
235
|
-
|
236
|
-
# Return original source
|
237
|
-
#
|
238
|
-
# @return [String]
|
239
|
-
#
|
240
|
-
# @api private
|
241
|
-
#
|
242
|
-
def original_source
|
243
|
-
::File.read(file_name)
|
244
|
-
end
|
245
|
-
memoize :original_source
|
246
|
-
|
247
|
-
end # File
|
248
|
-
|
249
|
-
# Source passed in as node
|
250
|
-
class Node < self
|
251
|
-
include Concord.new(:original_ast)
|
252
|
-
|
253
|
-
# Return original source
|
254
|
-
#
|
255
|
-
# @return [String]
|
256
|
-
#
|
257
|
-
# @api private
|
258
|
-
#
|
259
|
-
def original_source
|
260
|
-
Unparser.unparse(original_ast)
|
261
|
-
end
|
262
|
-
memoize :original_source
|
263
|
-
end # Node
|
264
|
-
|
265
|
-
end # Source
|
266
|
-
end # CLI
|
267
|
-
end # Unparser
|