ffast 0.1.4 → 0.1.5
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/examples/simple_rewriter.rb +6 -0
- data/lib/fast.rb +2 -91
- data/lib/fast/cli.rb +14 -5
- data/lib/fast/rewriter.rb +111 -0
- data/lib/fast/version.rb +1 -1
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 97309977be2dabc51b9113ee555aa7fe94d21a52426bb7386bf1133696fc4b81
|
4
|
+
data.tar.gz: 5851dd6da013f0561803b73d7c44a96b35960a8323399d012718eb712cf42b43
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 192e53811678fac65eb03d87145be85971079b832f0675c80d0644e1686765995aa591e955adcb7fad31f28b0a8a2cdea0cfeaee7db9fee9067f19a36f65fd5b
|
7
|
+
data.tar.gz: 024c2b9a6db910e16516bf54b71f9857af40edec50c180488c312e82b177968f13e9b11d795036ffdc0541e2413b55e2efd016f0a383b06265fc7d28f99dc01f
|
data/lib/fast.rb
CHANGED
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
require 'fileutils'
|
4
4
|
require 'astrolabe/builder'
|
5
|
+
require_relative 'fast/rewriter'
|
5
6
|
|
6
7
|
# suppress output to avoid parser gem warnings'
|
7
8
|
def suppress_output
|
@@ -94,34 +95,11 @@ module Fast
|
|
94
95
|
Matcher.new(pattern, ast, *args).match?
|
95
96
|
end
|
96
97
|
|
97
|
-
# Replaces content based on a pattern.
|
98
|
-
# @param [Astrolabe::Node] ast with the current AST to search.
|
99
|
-
# @param [String] pattern with the expression to be targeting nodes.
|
100
|
-
# @param [Proc] replacement gives the [Rewriter] context in the block.
|
101
|
-
# @example
|
102
|
-
# Fast.replace?(Fast.ast("a = 1"),"lvasgn") do |node|
|
103
|
-
# replace(node.location.name, 'variable_renamed')
|
104
|
-
# end # => variable_renamed = 1
|
105
|
-
# @return [String] with the new source code after apply the replacement
|
106
|
-
# @see Fast::Rewriter
|
107
|
-
def replace(pattern, ast, source = nil, &replacement)
|
108
|
-
buffer = Parser::Source::Buffer.new('replacement')
|
109
|
-
buffer.source = source || ast.loc.expression.source
|
110
|
-
to_replace = search(pattern, ast)
|
111
|
-
types = to_replace.grep(Parser::AST::Node).map(&:type).uniq
|
112
|
-
|
113
|
-
rewriter = Rewriter.new
|
114
|
-
rewriter.buffer = buffer
|
115
|
-
rewriter.search = pattern
|
116
|
-
rewriter.replacement = replacement
|
117
|
-
rewriter.replace_on(*types)
|
118
|
-
rewriter.rewrite(buffer, ast)
|
119
|
-
end
|
120
|
-
|
121
98
|
# Search with pattern directly on file
|
122
99
|
# @return [Array<Astrolabe::Node>] that matches the pattern
|
123
100
|
def search_file(pattern, file)
|
124
101
|
node = ast_from_file(file)
|
102
|
+
return [] if node.nil?
|
125
103
|
search pattern, node
|
126
104
|
end
|
127
105
|
|
@@ -150,21 +128,6 @@ module Fast
|
|
150
128
|
.inject(&:merge!)
|
151
129
|
end
|
152
130
|
|
153
|
-
# Replaces the source of an {Fast#ast_from_file} with
|
154
|
-
# and the same source if the pattern does not match.
|
155
|
-
def replace_file(pattern, file, &replacement)
|
156
|
-
ast = ast_from_file(file)
|
157
|
-
replace(pattern, ast, IO.read(file), &replacement)
|
158
|
-
end
|
159
|
-
|
160
|
-
# Combines #replace_file output overriding the file if the output is different
|
161
|
-
# from the original file content.
|
162
|
-
def rewrite_file(pattern, file, &replacement)
|
163
|
-
previous_content = IO.read(file)
|
164
|
-
content = replace_file(pattern, file, &replacement)
|
165
|
-
File.open(file, 'w+') { |f| f.puts content } if content != previous_content
|
166
|
-
end
|
167
|
-
|
168
131
|
# Capture elements from searches in files. Keep in mind you need to use `$`
|
169
132
|
# in the pattern to make it work.
|
170
133
|
# @return [Array<Object>] captured from the pattern matched in the file
|
@@ -269,58 +232,6 @@ module Fast
|
|
269
232
|
end
|
270
233
|
end
|
271
234
|
|
272
|
-
# Rewriter encapsulates {Rewriter#match_index} to allow
|
273
|
-
# {ExperimentFile.partial_replace} in a {Fast::ExperimentFile}.
|
274
|
-
# @see https://www.rubydoc.info/github/whitequark/parser/Parser/TreeRewriter
|
275
|
-
# @note the standalone class needs to combines {Rewriter#replace_on} to properly generate the `on_<node-type>` methods depending on the expression being used.
|
276
|
-
# @example Simple Rewriter
|
277
|
-
# ast = Fast.ast("a = 1")
|
278
|
-
# buffer = Parser::Source::Buffer.new('replacement')
|
279
|
-
# buffer.source = ast.loc.expression.source
|
280
|
-
# rewriter = Rewriter.new buffer
|
281
|
-
# rewriter.search ='(lvasgn _ ...)'
|
282
|
-
# rewriter.replacement = -> (node) { replace(node.location.name, 'variable_renamed') }
|
283
|
-
# rewriter.replace_on(:lvasgn)
|
284
|
-
# rewriter.rewrite(buffer, ast) # => "variable_renamed = 1"
|
285
|
-
class Rewriter < Parser::TreeRewriter
|
286
|
-
# @return [Integer] with occurrence index
|
287
|
-
attr_reader :match_index
|
288
|
-
attr_accessor :buffer, :search, :replacement
|
289
|
-
def initialize(*args)
|
290
|
-
super
|
291
|
-
@match_index = 0
|
292
|
-
end
|
293
|
-
|
294
|
-
def match?(node)
|
295
|
-
Fast.match?(search, node)
|
296
|
-
end
|
297
|
-
|
298
|
-
# Generate methods for all affected types.
|
299
|
-
# @see Fast.replace
|
300
|
-
def replace_on(*types)
|
301
|
-
types.map do |type|
|
302
|
-
self.class.send :define_method, "on_#{type}" do |node|
|
303
|
-
if captures = match?(node) # rubocop:disable Lint/AssignmentInCondition
|
304
|
-
@match_index += 1
|
305
|
-
execute_replacement(node, captures)
|
306
|
-
end
|
307
|
-
super(node)
|
308
|
-
end
|
309
|
-
end
|
310
|
-
end
|
311
|
-
|
312
|
-
# Execute {#replacement} block
|
313
|
-
# @param [Astrolabe::Node] node that will be yield in the replacement block
|
314
|
-
# @param [Array<Object>, nil] captures are yield if {#replacement} take second argument.
|
315
|
-
def execute_replacement(node, captures)
|
316
|
-
if replacement.parameters.length == 1
|
317
|
-
instance_exec node, &replacement
|
318
|
-
else
|
319
|
-
instance_exec node, captures, &replacement
|
320
|
-
end
|
321
|
-
end
|
322
|
-
end
|
323
|
-
|
324
235
|
# ExpressionParser empowers the AST search in Ruby.
|
325
236
|
# All classes inheriting Fast::Find have a grammar shortcut that is processed here.
|
326
237
|
#
|
data/lib/fast/cli.rb
CHANGED
@@ -14,13 +14,17 @@ module Fast
|
|
14
14
|
|
15
15
|
# Highligh some source code based on the node.
|
16
16
|
# Useful for printing code with syntax highlight.
|
17
|
-
|
17
|
+
# @param show_sexp [Boolean] prints node expression instead of code
|
18
|
+
# @param colorize [Boolean] skips `CodeRay` processing when false.
|
19
|
+
def highlight(node, show_sexp: false, colorize: true)
|
18
20
|
output =
|
19
21
|
if node.respond_to?(:loc) && !show_sexp
|
20
22
|
node.loc.expression.source
|
21
23
|
else
|
22
24
|
node
|
23
25
|
end
|
26
|
+
return output unless colorize
|
27
|
+
|
24
28
|
CodeRay.scan(output, :ruby).term
|
25
29
|
end
|
26
30
|
|
@@ -32,12 +36,12 @@ module Fast
|
|
32
36
|
# @param headless [Boolean] Skip printing the file name and line before content
|
33
37
|
# @example
|
34
38
|
# Fast.highlight(Fast.search(...))
|
35
|
-
def report(result, show_sexp: false, file: nil, headless: false)
|
39
|
+
def report(result, show_sexp: false, file: nil, headless: false, colorize: true)
|
36
40
|
if file
|
37
41
|
line = result.loc.expression.line if result.is_a?(Parser::AST::Node)
|
38
|
-
puts(highlight("# #{file}:#{line}")) unless headless
|
42
|
+
puts(highlight("# #{file}:#{line}", colorize: colorize)) unless headless
|
39
43
|
end
|
40
|
-
puts highlight(result, show_sexp: show_sexp)
|
44
|
+
puts highlight(result, show_sexp: show_sexp, colorize: colorize)
|
41
45
|
end
|
42
46
|
|
43
47
|
# Command Line Interface for Fast
|
@@ -47,6 +51,7 @@ module Fast
|
|
47
51
|
args = replace_args_with_shortcut(args) if args.first&.start_with?('.')
|
48
52
|
|
49
53
|
@pattern, *@files = args.reject { |arg| arg.start_with? '-' }
|
54
|
+
@colorize = STDOUT.isatty
|
50
55
|
|
51
56
|
option_parser.parse! args
|
52
57
|
|
@@ -91,6 +96,10 @@ module Fast
|
|
91
96
|
debug "Looking for code similar to #{@pattern}"
|
92
97
|
end
|
93
98
|
|
99
|
+
opts.on('--no-color', 'Disable color output') do
|
100
|
+
@colorize = false
|
101
|
+
end
|
102
|
+
|
94
103
|
opts.on_tail('--version', 'Show version') do
|
95
104
|
puts Fast::VERSION
|
96
105
|
exit
|
@@ -170,7 +179,7 @@ module Fast
|
|
170
179
|
# Report results using the actual options binded from command line.
|
171
180
|
# @see Fast.report
|
172
181
|
def report(result, file)
|
173
|
-
Fast.report(result, file: file, show_sexp: @show_sexp, headless: @headless)
|
182
|
+
Fast.report(result, file: file, show_sexp: @show_sexp, headless: @headless, colorize: @colorize)
|
174
183
|
end
|
175
184
|
|
176
185
|
# Find shortcut by name. Preloads all `Fastfiles` before start.
|
@@ -0,0 +1,111 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Rewriter loads a set of methods related to automated replacement using
|
4
|
+
# expressions and custom blocks of code.
|
5
|
+
module Fast
|
6
|
+
class << self
|
7
|
+
# Replaces content based on a pattern.
|
8
|
+
# @param [Astrolabe::Node] ast with the current AST to search.
|
9
|
+
# @param [String] pattern with the expression to be targeting nodes.
|
10
|
+
# @param [Proc] replacement gives the [Rewriter] context in the block.
|
11
|
+
# @example
|
12
|
+
# Fast.replace?(Fast.ast("a = 1"),"lvasgn") do |node|
|
13
|
+
# replace(node.location.name, 'variable_renamed')
|
14
|
+
# end # => variable_renamed = 1
|
15
|
+
# @return [String] with the new source code after apply the replacement
|
16
|
+
# @see Fast::Rewriter
|
17
|
+
def replace(pattern, ast, source = nil, &replacement)
|
18
|
+
rewriter_for(pattern, ast, source, &replacement).rewrite!
|
19
|
+
end
|
20
|
+
|
21
|
+
# @return [Fast::Rewriter]
|
22
|
+
def rewriter_for(pattern, ast, source = nil, &replacement)
|
23
|
+
rewriter = Rewriter.new
|
24
|
+
rewriter.source = source
|
25
|
+
rewriter.ast = ast
|
26
|
+
rewriter.search = pattern
|
27
|
+
rewriter.replacement = replacement
|
28
|
+
rewriter
|
29
|
+
end
|
30
|
+
|
31
|
+
# Replaces the source of an {Fast#ast_from_file} with
|
32
|
+
# and the same source if the pattern does not match.
|
33
|
+
def replace_file(pattern, file, &replacement)
|
34
|
+
ast = ast_from_file(file)
|
35
|
+
replace(pattern, ast, IO.read(file), &replacement)
|
36
|
+
end
|
37
|
+
|
38
|
+
# Combines #replace_file output overriding the file if the output is different
|
39
|
+
# from the original file content.
|
40
|
+
def rewrite_file(pattern, file, &replacement)
|
41
|
+
previous_content = IO.read(file)
|
42
|
+
content = replace_file(pattern, file, &replacement)
|
43
|
+
File.open(file, 'w+') { |f| f.puts content } if content != previous_content
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# Rewriter encapsulates {Rewriter#match_index} to allow
|
48
|
+
# {ExperimentFile.partial_replace} in a {Fast::ExperimentFile}.
|
49
|
+
# @see https://www.rubydoc.info/github/whitequark/parser/Parser/TreeRewriter
|
50
|
+
# @note the standalone class needs to combines {Rewriter#replace_on} to properly generate the `on_<node-type>` methods depending on the expression being used.
|
51
|
+
# @example Simple Rewriter
|
52
|
+
# rewriter = Rewriter.new buffer
|
53
|
+
# rewriter.ast = Fast.ast("a = 1")
|
54
|
+
# rewriter.search ='(lvasgn _ ...)'
|
55
|
+
# rewriter.replacement = -> (node) { replace(node.location.name, 'variable_renamed') }
|
56
|
+
# rewriter.rewrite! # => "variable_renamed = 1"
|
57
|
+
class Rewriter < Parser::TreeRewriter
|
58
|
+
# @return [Integer] with occurrence index
|
59
|
+
attr_reader :match_index
|
60
|
+
attr_accessor :search, :replacement, :source, :ast
|
61
|
+
def initialize(*_args)
|
62
|
+
super()
|
63
|
+
@match_index = 0
|
64
|
+
end
|
65
|
+
|
66
|
+
def rewrite!
|
67
|
+
replace_on(*types)
|
68
|
+
rewrite(buffer, ast)
|
69
|
+
end
|
70
|
+
|
71
|
+
def buffer
|
72
|
+
buffer = Parser::Source::Buffer.new('replacement')
|
73
|
+
buffer.source = source || ast.loc.expression.source
|
74
|
+
buffer
|
75
|
+
end
|
76
|
+
|
77
|
+
# @return [Array<Symbol>] with all types that matches
|
78
|
+
def types
|
79
|
+
Fast.search(search, ast).grep(Parser::AST::Node).map(&:type).uniq
|
80
|
+
end
|
81
|
+
|
82
|
+
def match?(node)
|
83
|
+
Fast.match?(search, node)
|
84
|
+
end
|
85
|
+
|
86
|
+
# Generate methods for all affected types.
|
87
|
+
# @see Fast.replace
|
88
|
+
def replace_on(*types)
|
89
|
+
types.map do |type|
|
90
|
+
self.class.send :define_method, "on_#{type}" do |node|
|
91
|
+
if captures = match?(node) # rubocop:disable Lint/AssignmentInCondition
|
92
|
+
@match_index += 1
|
93
|
+
execute_replacement(node, captures)
|
94
|
+
end
|
95
|
+
super(node)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
# Execute {#replacement} block
|
101
|
+
# @param [Astrolabe::Node] node that will be yield in the replacement block
|
102
|
+
# @param [Array<Object>, nil] captures are yield if {#replacement} take second argument.
|
103
|
+
def execute_replacement(node, captures)
|
104
|
+
if replacement.parameters.length == 1
|
105
|
+
instance_exec node, &replacement
|
106
|
+
else
|
107
|
+
instance_exec node, captures, &replacement
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
data/lib/fast/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ffast
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jônatas Davi Paganini
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-
|
11
|
+
date: 2019-11-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: astrolabe
|
@@ -259,6 +259,7 @@ files:
|
|
259
259
|
- examples/method_complexity.rb
|
260
260
|
- examples/search_duplicated.rb
|
261
261
|
- examples/similarity_research.rb
|
262
|
+
- examples/simple_rewriter.rb
|
262
263
|
- experiments/let_it_be_experiment.rb
|
263
264
|
- experiments/remove_useless_hook.rb
|
264
265
|
- experiments/replace_create_with_build_stubbed.rb
|
@@ -266,6 +267,7 @@ files:
|
|
266
267
|
- lib/fast.rb
|
267
268
|
- lib/fast/cli.rb
|
268
269
|
- lib/fast/experiment.rb
|
270
|
+
- lib/fast/rewriter.rb
|
269
271
|
- lib/fast/shortcut.rb
|
270
272
|
- lib/fast/version.rb
|
271
273
|
- mkdocs.yml
|