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