ffast 0.1.4 → 0.1.5

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
  SHA256:
3
- metadata.gz: fd904f00cff76125f3b6b79455b1fda97040acbe1e631d4842a26d5e126defde
4
- data.tar.gz: 426d0dfd4d17fe9e491921ae431f75d28c683da1bf6f37e27d64d9808ff86f0a
3
+ metadata.gz: 97309977be2dabc51b9113ee555aa7fe94d21a52426bb7386bf1133696fc4b81
4
+ data.tar.gz: 5851dd6da013f0561803b73d7c44a96b35960a8323399d012718eb712cf42b43
5
5
  SHA512:
6
- metadata.gz: 773ec583e5c2dc7edf71d519ed0a09ace7f3714b85483f78a4ace150a85479a6cfcef20cf3724f5105204b3f26a4fb4f2a8e4040aebf366f049a3d11a7e84fcf
7
- data.tar.gz: 2da3d58ad7036f74e245de58e654d736cc0f97f30bf116934166b1393132f3820e4b55f40039b99ea1589369dd650b694d874763fc36fd995bd521498b5e8257
6
+ metadata.gz: 192e53811678fac65eb03d87145be85971079b832f0675c80d0644e1686765995aa591e955adcb7fad31f28b0a8a2cdea0cfeaee7db9fee9067f19a36f65fd5b
7
+ data.tar.gz: 024c2b9a6db910e16516bf54b71f9857af40edec50c180488c312e82b177968f13e9b11d795036ffdc0541e2413b55e2efd016f0a383b06265fc7d28f99dc01f
@@ -0,0 +1,6 @@
1
+
2
+ rewriter = Fast::Rewriter.new
3
+ rewriter.ast = Fast.ast("a = 1")
4
+ rewriter.search ='(lvasgn _ ...)'
5
+ rewriter.replacement = -> (node) { replace(node.location.name, 'variable_renamed') }
6
+ puts rewriter.rewrite!
@@ -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
  #
@@ -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
- def highlight(node, show_sexp: false)
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Fast
4
- VERSION = '0.1.4'
4
+ VERSION = '0.1.5'
5
5
  end
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
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-10-28 00:00:00.000000000 Z
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