ffast 0.1.3 → 0.1.4

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: 49dd19ccc728102aba01cf03b72c5a4ce194e036bb7a489abee40bf488bd9821
4
- data.tar.gz: 390d27be9261ea3333fe91d8561965ee9127965bf9fd1080134e08fba1914410
3
+ metadata.gz: fd904f00cff76125f3b6b79455b1fda97040acbe1e631d4842a26d5e126defde
4
+ data.tar.gz: 426d0dfd4d17fe9e491921ae431f75d28c683da1bf6f37e27d64d9808ff86f0a
5
5
  SHA512:
6
- metadata.gz: 4b73de237734f1799832f4f724b8a2ebf4dfcf116ade615469af205cb61204d777a4e9f05b8483e7647251f20daeef28edf61e72cec2172319b3b1261beee4b0
7
- data.tar.gz: 330f5f62da86cfa18508f53c13231bcf55993e44d0bea4a9e4fd59427fae1d1aabd22a27b352adac78d6fe24a8abe1ebaf21ea33ef0c63dbafb7a4fdf3883e12
6
+ metadata.gz: 773ec583e5c2dc7edf71d519ed0a09ace7f3714b85483f78a4ace150a85479a6cfcef20cf3724f5105204b3f26a4fb4f2a8e4040aebf366f049a3d11a7e84fcf
7
+ data.tar.gz: 2da3d58ad7036f74e245de58e654d736cc0f97f30bf116934166b1393132f3820e4b55f40039b99ea1589369dd650b694d874763fc36fd995bd521498b5e8257
data/Fastfile CHANGED
@@ -18,7 +18,7 @@ Fast.shortcut(:parser, '(class (const nil ExpressionParser)', 'lib/fast.rb')
18
18
 
19
19
  # Use `fast .bump_version` to rewrite the version file
20
20
  Fast.shortcut :bump_version do
21
- rewrite_file('lib/fast/version.rb', '(casgn nil VERSION (str _)') do |node|
21
+ rewrite_file('(casgn nil VERSION (str _)', 'lib/fast/version.rb') do |node|
22
22
  target = node.children.last.loc.expression
23
23
  pieces = target.source.split('.').map(&:to_i)
24
24
  pieces.reverse.each_with_index do |fragment, i|
@@ -32,8 +32,3 @@ Fast.shortcut :bump_version do
32
32
  replace(target, "'#{pieces.join('.')}'")
33
33
  end
34
34
  end
35
-
36
- # And now a shortcut to print the other shortcuts :)
37
- Fast.shortcut :shortcuts do
38
- report(shortcuts.keys)
39
- end
data/README.md CHANGED
@@ -79,14 +79,14 @@ It's corresponding s-expression would be:
79
79
  If we wanted to find this particular assignment somewhere in our AST, we can use
80
80
  Fast to look for a local variable named `value` with a value `42`:
81
81
 
82
- Fast.match?(ast, '(lvasgn value (int 42))') # => true
82
+ Fast.match? '(lvasgn value (int 42))', ast # => true
83
83
 
84
84
  ### Wildcard Token
85
85
 
86
86
  If we wanted to find a variable named `value` that was assigned any integer value
87
87
  we could replace `42` in our query with an underscore ( `_` ) as a shortcut:
88
88
 
89
- Fast.match?(ast, '(lvasgn value (int _))') # => true
89
+ Fast.match? '(lvasgn value (int _))', ast # => true
90
90
 
91
91
  ### Set Inclusion Token
92
92
 
@@ -94,7 +94,7 @@ If we weren't sure the type of the value we're assigning, we can use our set
94
94
  inclusion token (`{}`) from earlier to tell Fast that we expect either a `Float`
95
95
  or an `Integer`:
96
96
 
97
- Fast.match?(ast, '(lvasgn value ({float int} _))') # => true
97
+ Fast.match? '(lvasgn value ({float int} _))', ast # => true
98
98
 
99
99
  ### All Matching Token
100
100
 
@@ -103,28 +103,28 @@ all matching token (`[]`) to express multiple conditions that need to be true.
103
103
  In this case we don't want the value to be a `String`, `Hash`, or an `Array` by
104
104
  prefixing all of the types with `!`:
105
105
 
106
- Fast.match?(ast, '(lvasgn value ([!str !hash !array] _))') # => true
106
+ Fast.match? '(lvasgn value ([!str !hash !array] _))', ast # => true
107
107
 
108
108
  ### Node Child Token
109
109
 
110
110
  We can match any node with children by using the child token ( `...` ):
111
111
 
112
- Fast.match?(ast, '(lvasgn value ...)') # => true
112
+ Fast.match? '(lvasgn value ...)', ast # => true
113
113
 
114
114
  We could even match any local variable assignment combining both `_` and `...`:
115
115
 
116
- Fast.match?(ast, '(lvasgn _ ...)') # => true
116
+ Fast.match? '(lvasgn _ ...)', ast # => true
117
117
 
118
118
  ### Capturing the Value of an Expression
119
119
 
120
120
  You can use `$` to capture the contents of an expression for later use:
121
121
 
122
- Fast.match?(ast, '(lvasgn value $...)') # => [s(:int, 42)]
122
+ Fast.match?('(lvasgn value $...)', ast) # => [s(:int, 42)]
123
123
 
124
124
  Captures can be used in any position as many times as you want to capture whatever
125
125
  information you might need:
126
126
 
127
- Fast.match?(ast, '(lvasgn $_ $...)') # => [:value, s(:int, 42)]
127
+ Fast.match?('(lvasgn $_ $...)', ast) # => [:value, s(:int, 42)]
128
128
 
129
129
  > Keep in mind that `_` means something not nil and `...` means a node with
130
130
  > children.
@@ -142,7 +142,7 @@ method names and guarantee they are unique.
142
142
  already_exists
143
143
  end
144
144
 
145
- puts Fast.search_file( '(def #duplicated)', 'example.rb')
145
+ puts Fast.search_file('(def #duplicated)', 'example.rb')
146
146
 
147
147
  The same principle can be used in the node level or for debugging purposes.
148
148
 
@@ -195,35 +195,31 @@ a single `Object`, like `a.b.c.d`. Its corresponding s-expression would be:
195
195
  You can also search using nested arrays with **pure values**, or **shortcuts** or
196
196
  **procs**:
197
197
 
198
- Fast.match?(ast, [:send, [:send, '...'], :d]) # => true
199
- Fast.match?(ast, [:send, [:send, '...'], :c]) # => false
200
- Fast.match?(ast, [:send, [:send, [:send, '...'], :c], :d]) # => true
198
+ Fast.match? [:send, [:send, '...'], :d], ast # => true
199
+ Fast.match? [:send, [:send, '...'], :c], ast # => false
201
200
 
202
201
  Shortcut tokens like child nodes `...` and wildcards `_` are just placeholders
203
202
  for procs. If you want, you can even use procs directly like so:
204
203
 
205
- Fast.match?(ast, [
204
+ Fast.match?([
206
205
  :send, [
207
206
  -> (node) { node.type == :send },
208
207
  [:send, '...'],
209
208
  :c
210
209
  ],
211
210
  :d
212
- ]) # => true
211
+ ], ast) # => true
213
212
 
214
213
  This also works with expressions:
215
214
 
216
- Fast.match?(
217
- ast,
218
- '(send (send (send (send nil $_) $_) $_) $_)'
219
- ) # => [:a, :b, :c, :d]
215
+ Fast.match?('(send (send (send (send nil $_) $_) $_) $_)', ast) # => [:a, :b, :c, :d]
220
216
 
221
217
  ### Debugging
222
218
 
223
219
  If you find that a particular expression isn't working, you can use `debug` to
224
220
  take a look at what Fast is doing:
225
221
 
226
- Fast.debug { Fast.match?(s(:int, 1), [:int, 1]) }
222
+ Fast.debug { Fast.match?([:int, 1], s(:int, 1)) }
227
223
 
228
224
  Each comparison made while searching will be logged to your console (STDOUT) as
229
225
  Fast goes through the AST:
@@ -237,7 +233,7 @@ We can also dynamically interpolate arguments into our queries using the
237
233
  interpolation token `%`. This works much like `sprintf` using indexes starting
238
234
  from `1`:
239
235
 
240
- Fast.match?(code('a = 1'), '(lvasgn %1 (int _))', :a) # => true
236
+ Fast.match? '(lvasgn %1 (int _))', ('a = 1'), :a # => true
241
237
 
242
238
  ## Using previous captures in search
243
239
 
@@ -264,18 +260,18 @@ We can create a query that searches for such a method:
264
260
  Search allows you to go search the entire AST, collecting nodes that matches given
265
261
  expression. Any matching node is then returned:
266
262
 
267
- Fast.search(code('a = 1'), '(int _)') # => s(:int, 1)
263
+ Fast.search(Fast.ast('a = 1'), '(int _)') # => s(:int, 1)
268
264
 
269
265
  If you use captures along with a search, both the matching nodes and the
270
266
  captures will be returned:
271
267
 
272
- Fast.search(code('a = 1'), '(int $_)') # => [s(:int, 1), 1]
268
+ Fast.search(Fast.ast('a = 1'), '(int $_)') # => [s(:int, 1), 1]
273
269
 
274
270
  ## Fast.capture
275
271
 
276
272
  To only pick captures and ignore the nodes, use `Fast.capture`:
277
273
 
278
- Fast.capture(code('a = 1'), '(int $_)') # => 1
274
+ Fast.capture(Fast.ast('a = 1'), '(int $_)') # => 1
279
275
 
280
276
  ## Fast.replace
281
277
 
@@ -325,7 +321,7 @@ Or only the method name:
325
321
  In the context of the rewriter, the objective is removing the method and inserting the new
326
322
  delegate content. Then, the scope is `node.location.expression`:
327
323
 
328
- Fast.replace ast, '(def $_ ... (send (send nil $_) \1))' do |node, captures|
324
+ Fast.replace '(def $_ ... (send (send nil $_) \1))', ast do |node, captures|
329
325
  attribute, object = captures
330
326
 
331
327
  replace(
@@ -360,7 +356,7 @@ To refactor and reach the proposed example, follow a few steps:
360
356
  #### Entire example
361
357
 
362
358
  assignment = nil
363
- Fast.replace_file 'sample.rb', '({ lvasgn lvar } message )', -> (node, _) {
359
+ Fast.replace_file '({ lvasgn lvar } message )', 'sample.rb' do |node, _|
364
360
  # Find a variable assignment
365
361
  if node.type == :lvasgn
366
362
  assignment = node.children.last
@@ -466,7 +462,7 @@ and add the snippet to your library fixing the filename.
466
462
 
467
463
  ```ruby
468
464
  Fast.shortcut :bump_version do
469
- rewrite_file('lib/fast/version.rb', '(casgn nil VERSION (str _)') do |node|
465
+ rewrite_file('(casgn nil VERSION (str _)', 'lib/fast/version.rb') do |node|
470
466
  target = node.children.last.loc.expression
471
467
  pieces = target.source.split(".").map(&:to_i)
472
468
  pieces.reverse.each_with_index do |fragment,i|
@@ -6,7 +6,6 @@ and you can use it to search and find code using the concept:
6
6
  ```
7
7
  $ fast '(def match?)' lib/fast.rb
8
8
  ```
9
-
10
9
  - Use `-d` or `--debug` for enable debug mode.
11
10
  - Use `--ast` to output the AST instead of the original code
12
11
  - Use `--pry` to jump debugging the first result with pry
@@ -35,12 +34,8 @@ Getting all `it` blocks without description:
35
34
 
36
35
  ```ruby
37
36
  # spec/fast_spec.rb:166
38
- it { expect(described_class).to be_match(s(:int, 1), '(...)') }
39
- # spec/fast_spec.rb:167
40
- it { expect(described_class).to be_match(s(:int, 1), '(_ _)') }
41
- # spec/fast_spec.rb:168
42
- it { expect(described_class).to be_match(code['"string"'], '(str "string")') }
43
- # ... more results
37
+ it { expect(described_class).to be_match('(...)', s(:int, 1)) }
38
+ ...
44
39
  ```
45
40
 
46
41
  ## `--debug`
@@ -122,12 +117,12 @@ You can define a `Fastfile` in any project with your custom shortcuts and easy
122
117
  check some code or run some task.
123
118
 
124
119
 
125
- ## Shortcuts
120
+ ## Shortcut examples
126
121
 
127
122
  Create shortcuts with blocks enables introduce custom coding in
128
123
  the scope of the `Fast` module.
129
124
 
130
- ## Example: Print library version.
125
+ ### Print library version.
131
126
 
132
127
  Let's say you'd like to show the version of your library. Your regular params
133
128
  in the command line will look like:
@@ -152,7 +147,7 @@ We can also always override the files params passing some other target file
152
147
  like `fast .version lib/other/file.rb` and it will reuse the other arguments
153
148
  from command line but replace the target files.
154
149
 
155
- ### Example: Bumping a gem version
150
+ ### Bumping a gem version
156
151
 
157
152
  While releasing a new gem version, we always need to mechanical go through the
158
153
  `lib/<your_gem>/version.rb` and change the string value to bump the version
@@ -210,5 +205,33 @@ Fast.shortcut :shortcuts do
210
205
  end
211
206
  ```
212
207
 
213
- You can find these examples in the [Fastfile](https://github.com/jonatas/fast/tree/master/Fastfile).
208
+ Or we can make it a bit more friendly and also use Fast to process the shortcut
209
+ positions and pick the comment that each shortcut have in the previous line:
210
+
211
+ ```ruby
212
+ # List all shortcut with comments
213
+ Fast.shortcut :shortcuts do
214
+ fast_files.each do |file|
215
+ lines = File.readlines(file).map{|line|line.chomp.gsub(/\s*#/,'').strip}
216
+ result = capture_file('(send ... shortcut $(sym _))', file)
217
+ result = [result] unless result.is_a?Array
218
+ result.each do |capture|
219
+ target = capture.loc.expression
220
+ puts "fast .#{target.source[1..-1].ljust(30)} # #{lines[target.line-2]}"
221
+ end
222
+ end
223
+ end
224
+ ```
225
+
226
+ And it will be printing all loaded shortcuts with comments:
227
+
228
+ ```
229
+ $ fast .shortcuts
230
+ fast .version # Let's say you'd like to show the version that is over the version file
231
+ fast .parser # Simple shortcut that I used often to show how the expression parser works
232
+ fast .bump_version # Use `fast .bump_version` to rewrite the version file
233
+ fast .shortcuts # List all shortcut with comments
234
+ ```
235
+
236
+ You can find more examples in the [Fastfile](https://github.com/jonatas/fast/tree/master/Fastfile).
214
237
 
@@ -64,9 +64,36 @@ The current version cover the following elements:
64
64
 
65
65
  Jump to [Syntax](syntax.md).
66
66
 
67
- ## Fast.match?
67
+ ## ast
68
68
 
69
- `match?` is the most granular function that tries to compare a node with an
69
+ Use `Fast.ast` to convert simple code to AST objects. You can use it as
70
+ `ruby-parse` but directly from the console.
71
+
72
+ ```ruby
73
+ Fast.ast("1") # => s(:int, 1)
74
+ Fast.ast("method") # => s(:send, nil, :method)
75
+ Fast.ast("a.b") # => s(:send, s(:send, nil, :a), :b)
76
+ Fast.ast("1 + 1") # => s(:send, s(:int, 1), :+, s(:int, 1))
77
+ Fast.ast("a = 2") # => s(:lvasgn, :a, s(:int, 2))
78
+ Fast.ast("b += 2") # => s(:op_asgn, s(:lvasgn, :b), :+, s(:int, 2))
79
+ ```
80
+
81
+ It uses [astrolable](https://github.com/yujinakayama/astrolabe) gem behind the scenes:
82
+
83
+ ```ruby
84
+ Fast.ast(Fast.ast("1")).class
85
+ => Astrolabe::Node
86
+ Fast.ast(Fast.ast("1")).type
87
+ => :int
88
+ Fast.ast(Fast.ast("1")).children
89
+ => [1]
90
+ ```
91
+
92
+ See also [ast_from_file](#ast_from_file).
93
+
94
+ ## match?
95
+
96
+ `Fast.match?` is the most granular function that tries to compare a node with an
70
97
  expression. It returns true or false and some node captures case it find
71
98
  something.
72
99
 
@@ -107,25 +134,25 @@ ast = s(:lvasgn, :value, s(:int, 42))
107
134
  Now, lets find local variable named `value` with an value `42`:
108
135
 
109
136
  ```ruby
110
- Fast.match?(ast, '(lvasgn value (int 42))') # true
137
+ Fast.match?('(lvasgn value (int 42))', ast) # true
111
138
  ```
112
139
 
113
140
  Lets abstract a bit and allow some integer value using `_` as a shortcut:
114
141
 
115
142
  ```ruby
116
- Fast.match?(ast, '(lvasgn value (int _))') # true
143
+ Fast.match?('(lvasgn value (int _))', ast) # true
117
144
  ```
118
145
 
119
146
  Lets abstract more and allow float or integer:
120
147
 
121
148
  ```ruby
122
- Fast.match?(ast, '(lvasgn value ({float int} _))') # true
149
+ Fast.match?('(lvasgn value ({float int} _))', ast) # true
123
150
  ```
124
151
 
125
152
  Or combine multiple assertions using `[]` to join conditions:
126
153
 
127
154
  ```ruby
128
- Fast.match?(ast, '(lvasgn value ([!str !hash !array] _))') # true
155
+ Fast.match?('(lvasgn value ([!str !hash !array] _))', ast) # true
129
156
  ```
130
157
 
131
158
  Matches all local variables not string **and** not hash **and** not array.
@@ -133,25 +160,25 @@ Matches all local variables not string **and** not hash **and** not array.
133
160
  We can match "a node with children" using `...`:
134
161
 
135
162
  ```ruby
136
- Fast.match?(ast, '(lvasgn value ...)') # true
163
+ Fast.match?('(lvasgn value ...)', ast) # true
137
164
  ```
138
165
 
139
166
  You can use `$` to capture a node:
140
167
 
141
168
  ```ruby
142
- Fast.match?(ast, '(lvasgn value $...)') # => [s(:int, 42)]
169
+ Fast.match?('(lvasgn value $...)', ast) # => [s(:int), 42]
143
170
  ```
144
171
 
145
172
  Or match whatever local variable assignment combining both `_` and `...`:
146
173
 
147
174
  ```ruby
148
- Fast.match?(ast, '(lvasgn _ ...)') # true
175
+ Fast.match?('(lvasgn _ ...)', ast) # true
149
176
  ```
150
177
 
151
178
  You can also use captures in any levels you want:
152
179
 
153
180
  ```ruby
154
- Fast.match?(ast, '(lvasgn $_ $...)') # [:value, s(:int, 42)]
181
+ Fast.match?('(lvasgn $_ $...)', ast) # [:value, s(:int), 42]
155
182
  ```
156
183
 
157
184
  Keep in mind that `_` means something not nil and `...` means a node with
@@ -196,31 +223,28 @@ You can search using sub-arrays with **pure values**, or **shortcuts** or
196
223
  **procs**:
197
224
 
198
225
  ```ruby
199
- Fast.match?(ast, [:send, [:send, '...'], :d]) # => true
200
- Fast.match?(ast, [:send, [:send, '...'], :c]) # => false
201
- Fast.match?(ast, [:send, [:send, [:send, '...'], :c], :d]) # => true
226
+ Fast.match?([:send, [:send, '...'], :d], ast) # => true
227
+ Fast.match?([:send, [:send, '...'], :c], ast) # => false
228
+ Fast.match?([:send, [:send, [:send, '...'], :c], :d], ast) # => true
202
229
  ```
203
230
 
204
231
  Shortcuts like `...` and `_` are just literals for procs. Then you can use
205
232
  procs directly too:
206
233
 
207
234
  ```ruby
208
- Fast.match?(ast, [:send, [ -> (node) { node.type == :send }, [:send, '...'], :c], :d]) # => true
235
+ Fast.match?([:send, [ -> (node) { node.type == :send }, [:send, '...'], :c], :d], ast) # => true
209
236
  ```
210
237
 
211
238
  And also work with expressions:
212
239
 
213
240
  ```ruby
214
- Fast.match?(
215
- ast,
216
- '(send (send (send (send nil $_) $_) $_) $_)'
217
- ) # => [:a, :b, :c, :d]
241
+ Fast.match?('(send (send (send (send nil $_) $_) $_) $_)', ast) # => [:a, :b, :c, :d]
218
242
  ```
219
243
 
220
244
  If something does not work you can debug with a block:
221
245
 
222
246
  ```ruby
223
- Fast.debug { Fast.match?(s(:int, 1), [:int, 1]) }
247
+ Fast.debug { Fast.match?([:int, 1], s(:int, 1)) }
224
248
  ```
225
249
 
226
250
  It will output each comparison to stdout:
@@ -230,70 +254,40 @@ int == (int 1) # => true
230
254
  1 == 1 # => true
231
255
  ```
232
256
 
233
- ## Use previous captures in search
234
-
235
- Imagine you're looking for a method that is just delegating something to
236
- another method, like:
237
-
238
- ```ruby
239
- def name
240
- person.name
241
- end
242
- ```
243
-
244
- This can be represented as the following AST:
245
-
246
- ```
247
- (def :name
248
- (args)
249
- (send
250
- (send nil :person) :name))
251
- ```
252
-
253
- Then, let's build a search for methods that calls an attribute with the same
254
- name:
255
-
256
- ```ruby
257
- Fast.match?(ast,'(def $_ ... (send (send nil _) \1))') # => [:name]
258
- ```
259
-
260
- ## Fast.search
257
+ ## search
261
258
 
262
259
  Search allows you to go deeply in the AST, collecting nodes that matches with
263
260
  the expression. It also returns captures if they exist.
264
261
 
265
262
  ```ruby
266
- Fast.search(code('a = 1'), '(int _)') # => s(:int, 1)
263
+ Fast.search('(int _)', Fast.ast('a = 1')) # => s(:int, 1)
267
264
  ```
268
265
 
269
266
  If you use captures, it returns the node and the captures respectively:
270
267
 
271
268
  ```ruby
272
- Fast.search(code('a = 1'), '(int $_)') # => [s(:int, 1), 1]
269
+ Fast.search('(int $_)', Fast.ast('a = 1')) # => [s(:int, 1), 1]
273
270
  ```
274
271
 
275
- ## Fast.capture
272
+ ## capture
276
273
 
277
274
  To pick just the captures and ignore the nodes, use `Fast.capture`:
278
275
 
279
276
  ```ruby
280
- Fast.capture(code('a = 1'), '(int $_)') # => 1
277
+ Fast.capture('(int $_)', Fast.ast('a = 1')) # => 1
281
278
  ```
282
- ## Fast.replace
279
+ ## replace
283
280
 
284
281
  And if I want to refactor a code and use `delegate <attribute>, to: <object>`, try with replace:
285
282
 
286
283
  ```ruby
287
- Fast.replace ast, '(def $_ ... (send (send nil $_) \1))' do |node, captures|
288
- attribute, object = captures
289
- replace(
290
- node.location.expression,
291
- "delegate :#{attribute}, to: :#{object}"
292
- )
293
- end
284
+ Fast.replace '(def $_ ... (send (send nil $_) \1))', ast do |node, captures|
285
+ attribute, object = captures
286
+ replace(node.location.expression, "delegate :#{attribute}, to: :#{object}")
287
+ end
294
288
  ```
295
289
 
296
- ## Fast.replace_file
290
+ ## replace_file
297
291
 
298
292
  Now let's imagine we have real files like `sample.rb` with the following code:
299
293
 
@@ -312,19 +306,49 @@ is being called.
312
306
 
313
307
  ```ruby
314
308
  assignment = nil
315
- Fast.replace_file('sample.rb', '({ lvasgn lvar } message )',
316
- -> (node, _) {
317
- if node.type == :lvasgn
318
- assignment = node.children.last
319
- remove(node.location.expression)
320
- elsif node.type == :lvar
321
- replace(node.location.expression, assignment.location.expression.source)
322
- end
323
- }
324
- )
309
+ Fast.replace_file('({ lvasgn lvar } message )','sample.rb') do |node, _|
310
+ if node.type == :lvasgn
311
+ assignment = node.children.last
312
+ remove(node.location.expression)
313
+ elsif node.type == :lvar
314
+ replace(node.location.expression, assignment.location.expression.source)
315
+ end
316
+ end
325
317
  ```
326
318
 
327
- ## Fast.ast_from_File(file)
319
+ It will return an output of the new source code with the changes but not save
320
+ the file. You can use ()[#rewrite_file] if you're confident about the changes.
321
+
322
+ ## capture_file
323
+
324
+ `Fast.capture_file` can be used to combine [capture](#capture) and file system.
325
+
326
+ ```ruby
327
+ Fast.capture_file("$(casgn)", "lib/fast/version.rb") # => s(:casgn, nil, :VERSION, s(:str, "0.1.3"))
328
+ Fast.capture_file("(casgn nil _ (str $_))", "lib/fast/version.rb") # => "0.1.3"
329
+ ```
330
+
331
+ ## capture_all
332
+
333
+ `Fast.capture_all` can be used to combine [capture_file](#capture_file) from multiple sources:
334
+
335
+ ```ruby
336
+ Fast.capture_all("(casgn nil $_)") # => { "./lib/fast/version.rb"=>:VERSION, "./lib/fast.rb"=>[:LITERAL, :TOKENIZER], ...}
337
+ ```
338
+
339
+ The second parameter can also be passed with to filter specific folders:
340
+
341
+ ```ruby
342
+ Fast.capture_all("(casgn nil $_)", "lib/fast") # => {"lib/fast/shortcut.rb"=>:LOOKUP_FAST_FILES_DIRECTORIES, "lib/fast/version.rb"=>:VERSION}
343
+ ```
344
+
345
+
346
+ ## rewrite_file
347
+
348
+ `Fast.rewrite_file` works exactly as the `replace` but it will override the file
349
+ from the input.
350
+
351
+ ## ast_from_file
328
352
 
329
353
  This method parses the code and load into a AST representation.
330
354
 
@@ -332,7 +356,7 @@ This method parses the code and load into a AST representation.
332
356
  Fast.ast_from_file('sample.rb')
333
357
  ```
334
358
 
335
- ## Fast.search_file
359
+ ## search_file
336
360
 
337
361
  You can use `search_file` and pass the path for search for expressions inside
338
362
  files.
@@ -343,7 +367,7 @@ Fast.search_file(expression, 'file.rb')
343
367
 
344
368
  It's simple combination of `Fast.ast_from_file` with `Fast.search`.
345
369
 
346
- ## Fast.ruby_files_from(arguments)
370
+ ## ruby_files_from
347
371
 
348
372
  You'll be probably looking for multiple ruby files, then this method fetches
349
373
  all internal `.rb` files
@@ -352,3 +376,22 @@ all internal `.rb` files
352
376
  Fast.ruby_files_from(['lib']) # => ["lib/fast.rb"]
353
377
  ```
354
378
 
379
+ ## search_all
380
+
381
+ Combines the [search_file](#search_file) with [ruby_files_from](#ruby_files_from)
382
+ multiple locations and returns tuples with files and results.
383
+
384
+ ```ruby
385
+ Fast.search_all("(def ast_from_file)")
386
+ => {"./lib/fast.rb"=>[s(:def, :ast_from_file,
387
+ s(:args,
388
+ s(:arg, :file)),
389
+ s(:begin,
390
+ ```
391
+
392
+ You can also override the second param and pass target files or folders:
393
+
394
+ ```ruby
395
+ Fast.search_all("(def _)", '../other-folder')
396
+ ```
397
+
@@ -53,7 +53,7 @@ observe some specific node types and try to group the similarities
53
53
  using the pattern generated.
54
54
 
55
55
  ```ruby
56
- Fast.search_file('lib/fast.rb', 'class')
56
+ Fast.search_file('class', 'lib/fast.rb')
57
57
  ```
58
58
  Capturing the constant name and filtering only for symbols is easy and we can
59
59
  see that we have a few classes defined in the the same file.
@@ -251,56 +251,8 @@ def duplicate(value)
251
251
  # example.rb:
252
252
  duplicate
253
253
  ```
254
- One extra method name was printed because of `$` is capturing the element.
255
-
256
- Let's use the `--pry` for inspecting the results.
257
-
258
- $ fast '(def $_)' example.rb --pry
259
-
260
- It will open pry with access to `result` as the first result and
261
- `results` with all matching results.
262
-
263
- ```
264
- From: /Users/jonatasdp/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/ffast-0.0.2/bin/fast @ line 60 :
265
-
266
- 55:
267
- 56: results.each do |result|
268
- 57: next if result.nil? || result == []
269
- 58: if pry
270
- 59: require 'pry'
271
- => 60: binding.pry # rubocop:disable Lint/Debugger
272
- 61: else
273
- 62: Fast.report(result, file: file, show_sexp: show_sexp)
274
- 63: end
275
- 64: end
276
- 65: end
277
- ```
278
254
 
279
- Inspecting the results you can see that they are mixing AST nodes and the
280
- captures.
281
-
282
- ```ruby
283
- [1] pry(main)> results
284
- => [s(:def, :magic,
285
- s(:args),
286
- s(:send, nil, :rand,
287
- s(:const, nil, :ANSWER))),
288
- :magic,
289
- s(:def, :duplicate,
290
- s(:args,
291
- s(:arg, :value)),
292
- s(:send,
293
- s(:lvar, :value), :*,
294
- s(:int, 2))),
295
- :duplicate]
296
- ```
297
-
298
- We can filter the captures to make it easy to analyze.
299
-
300
- ```ruby
301
- [2] pry(main)> results.grep(Symbol)
302
- => [:magic, :duplicate]
303
- ```
255
+ One extra method name was printed because of `$` is capturing the element.
304
256
 
305
257
  ## `nil` matches exactly **nil**
306
258
 
@@ -368,7 +320,7 @@ ANSWER = 42
368
320
  ANSWER
369
321
  ```
370
322
 
371
- ## Calling Custom Methods
323
+ ## `#` for custom methods
372
324
 
373
325
  Custom methods can let you into ruby doman for more complicated rules. Let's say
374
326
  we're looking for duplicated methods in the same class. We need to collect
@@ -403,10 +355,41 @@ more details of the node
403
355
  ```ruby
404
356
  puts Fast.search_file('[ (def a) #debug ]', 'example.rb')
405
357
  ```
406
- ## Calling Instance Methods
358
+
359
+ ## `.` for instance methods
407
360
 
408
361
  You can also call instance methods using `.<method-name>`.
409
362
 
410
363
  Example `nil` is the same of calling `nil?` and you can also use `(int .odd?)`
411
364
  to pick only odd integers. The `int` fragment can also be `int_type?`.
412
365
 
366
+ ## `\1` for first previous capture
367
+
368
+ Imagine you're looking for a method that is just delegating something to
369
+ another method, like:
370
+
371
+ ```ruby
372
+ def name
373
+ person.name
374
+ end
375
+ ```
376
+
377
+ This can be represented as the following AST:
378
+
379
+ ```
380
+ (def :name
381
+ (args)
382
+ (send
383
+ (send nil :person) :name))
384
+ ```
385
+
386
+ Then, let's build a search for methods that calls an attribute with the same
387
+ name:
388
+
389
+ ```ruby
390
+ Fast.match?('(def $_ ... (send (send nil _) \1))', ast) # => [:name]
391
+ ```
392
+
393
+ With the method name being captured with `$_` it can be later referenced in the
394
+ expression with `\1`. If the search contains multiple captures, the `\2`,`\3`
395
+ can be used as the sequence of captures.
@@ -89,9 +89,9 @@ module Fast
89
89
  # Verify if a given AST matches with a specific pattern
90
90
  # @return [Boolean] case matches ast with the current expression
91
91
  # @example
92
- # Fast.match?(Fast.ast("1"),"int") # => true
93
- def match?(ast, pattern, *args)
94
- Matcher.new(ast, pattern, *args).match?
92
+ # Fast.match?("int", Fast.ast("1")) # => true
93
+ def match?(pattern, ast, *args)
94
+ Matcher.new(pattern, ast, *args).match?
95
95
  end
96
96
 
97
97
  # Replaces content based on a pattern.
@@ -104,10 +104,10 @@ module Fast
104
104
  # end # => variable_renamed = 1
105
105
  # @return [String] with the new source code after apply the replacement
106
106
  # @see Fast::Rewriter
107
- def replace(ast, pattern, source = nil, &replacement)
107
+ def replace(pattern, ast, source = nil, &replacement)
108
108
  buffer = Parser::Source::Buffer.new('replacement')
109
109
  buffer.source = source || ast.loc.expression.source
110
- to_replace = search(ast, pattern)
110
+ to_replace = search(pattern, ast)
111
111
  types = to_replace.grep(Parser::AST::Node).map(&:type).uniq
112
112
 
113
113
  rewriter = Rewriter.new
@@ -122,21 +122,46 @@ module Fast
122
122
  # @return [Array<Astrolabe::Node>] that matches the pattern
123
123
  def search_file(pattern, file)
124
124
  node = ast_from_file(file)
125
- search node, pattern
125
+ search pattern, node
126
+ end
127
+
128
+ # Search with pattern on a directory or multiple files
129
+ # @param [String] pattern
130
+ # @param [Array<String>] *locations where to search. Default is '.'
131
+ # @return [Hash<String,Array<Astrolabe::Node>>] with files and results
132
+ def search_all(pattern, locations = ['.'])
133
+ search_pattern = method(:search_file).curry.call(pattern)
134
+ group_results(search_pattern, locations)
135
+ end
136
+
137
+ # Capture with pattern on a directory or multiple files
138
+ # @param [String] pattern
139
+ # @param [Array<String>] locations where to search. Default is '.'
140
+ # @return [Hash<String,Object>] with files and captures
141
+ def capture_all(pattern, locations = ['.'])
142
+ capture_pattern = method(:capture_file).curry.call(pattern)
143
+ group_results(capture_pattern, locations)
144
+ end
145
+
146
+ def group_results(search_pattern, locations)
147
+ ruby_files_from(*locations)
148
+ .map { |f| { f => search_pattern.call(f) } }
149
+ .reject { |results| results.values.all?(&:empty?) }
150
+ .inject(&:merge!)
126
151
  end
127
152
 
128
153
  # Replaces the source of an {Fast#ast_from_file} with
129
154
  # and the same source if the pattern does not match.
130
- def replace_file(file, pattern, &replacement)
155
+ def replace_file(pattern, file, &replacement)
131
156
  ast = ast_from_file(file)
132
- replace(ast, pattern, IO.read(file), &replacement)
157
+ replace(pattern, ast, IO.read(file), &replacement)
133
158
  end
134
159
 
135
160
  # Combines #replace_file output overriding the file if the output is different
136
161
  # from the original file content.
137
- def rewrite_file(file, pattern, &replacement)
162
+ def rewrite_file(pattern, file, &replacement)
138
163
  previous_content = IO.read(file)
139
- content = replace_file(file, pattern, &replacement)
164
+ content = replace_file(pattern, file, &replacement)
140
165
  File.open(file, 'w+') { |f| f.puts content } if content != previous_content
141
166
  end
142
167
 
@@ -144,21 +169,20 @@ module Fast
144
169
  # in the pattern to make it work.
145
170
  # @return [Array<Object>] captured from the pattern matched in the file
146
171
  def capture_file(pattern, file)
147
- node = ast_from_file(file)
148
- capture node, pattern
172
+ capture pattern, ast_from_file(file)
149
173
  end
150
174
 
151
175
  # Search recursively into a node and its children.
152
176
  # If the node matches with the pattern it returns the node,
153
177
  # otherwise it recursively collect possible children nodes
154
178
  # @yield node and capture if block given
155
- def search(node, pattern)
156
- if (match = match?(node, pattern))
179
+ def search(pattern, node)
180
+ if (match = match?(pattern, node))
157
181
  yield node, match if block_given?
158
182
  match != true ? [node, match] : [node]
159
183
  else
160
184
  node.each_child_node
161
- .flat_map { |e| search(e, pattern) }
185
+ .flat_map { |child| search(pattern, child) }
162
186
  .compact.flatten
163
187
  end
164
188
  end
@@ -166,16 +190,16 @@ module Fast
166
190
  # Return only captures from a search
167
191
  # @return [Array<Object>] with all captured elements.
168
192
  # @return [Object] with single element when single capture.
169
- def capture(node, pattern)
170
- res =
171
- if (match = match?(node, pattern))
172
- match == true ? node : match
173
- else
174
- node.each_child_node
175
- .flat_map { |child| capture(child, pattern) }
176
- .compact.flatten
177
- end
178
- res&.size == 1 ? res[0] : res
193
+ def capture(pattern, node)
194
+ res = if (match = match?(pattern, node))
195
+ match == true ? node : match
196
+ else
197
+ node.each_child_node
198
+ .flat_map { |child| capture(pattern, child) }
199
+ .compact.flatten
200
+ end
201
+ res = [res] unless res.is_a?(Array)
202
+ res.one? ? res.first : res
179
203
  end
180
204
 
181
205
  def expression(string)
@@ -190,7 +214,7 @@ module Fast
190
214
  #
191
215
  # @example
192
216
  # Fast.debug do
193
- # Fast.match?(s(:int, 1), [:int, 1])
217
+ # Fast.match?([:int, 1], s(:int, 1))
194
218
  # end
195
219
  # int == (int 1) # => true
196
220
  # 1 == 1 # => true
@@ -268,7 +292,7 @@ module Fast
268
292
  end
269
293
 
270
294
  def match?(node)
271
- Fast.match?(node, search)
295
+ Fast.match?(search, node)
272
296
  end
273
297
 
274
298
  # Generate methods for all affected types.
@@ -382,24 +406,24 @@ module Fast
382
406
  end
383
407
 
384
408
  def match?(node)
385
- match_recursive(node, valuate(token))
409
+ match_recursive(valuate(token), node)
386
410
  end
387
411
 
388
- def match_recursive(node, expression)
412
+ def match_recursive(expression, node)
389
413
  case expression
390
414
  when Proc then expression.call(node)
391
415
  when Find then expression.match?(node)
392
- when Symbol then compare_symbol_or_head(node, expression)
416
+ when Symbol then compare_symbol_or_head(expression, node)
393
417
  when Enumerable
394
418
  expression.each_with_index.all? do |exp, i|
395
- match_recursive(i.zero? ? node : node.children[i - 1], exp)
419
+ match_recursive(exp, i.zero? ? node : node.children[i - 1])
396
420
  end
397
421
  else
398
422
  node == expression
399
423
  end
400
424
  end
401
425
 
402
- def compare_symbol_or_head(node, expression)
426
+ def compare_symbol_or_head(expression, node)
403
427
  case node
404
428
  when Parser::AST::Node
405
429
  node.type == expression.to_sym
@@ -410,13 +434,13 @@ module Fast
410
434
  end
411
435
  end
412
436
 
413
- def debug_match_recursive(node, expression)
414
- match = original_match_recursive(node, expression)
415
- debug(node, expression, match)
437
+ def debug_match_recursive(expression, node)
438
+ match = original_match_recursive(expression, node)
439
+ debug(expression, node, match)
416
440
  match
417
441
  end
418
442
 
419
- def debug(node, expression, match)
443
+ def debug(expression, node, match)
420
444
  puts "#{expression} == #{node} # => #{match}"
421
445
  end
422
446
 
@@ -490,7 +514,7 @@ module Fast
490
514
  #
491
515
  # @example check comparision of integers that will always return true
492
516
  # ast = Fast.ast("1 == 1") => s(:send, s(:int, 1), :==, s(:int, 1))
493
- # Fast.match?(ast, "(send $(int _) == \1)") # => [s(:int, 1)]
517
+ # Fast.match?("(send $(int _) == \1)", ast) # => [s(:int, 1)]
494
518
  class FindWithCapture < Find
495
519
  attr_writer :previous_captures
496
520
 
@@ -514,10 +538,10 @@ module Fast
514
538
  # Use `%1` in the expression and the Matcher#prepare_arguments will
515
539
  # interpolate the argument in the expression.
516
540
  # @example interpolate the node value 1
517
- # Fast.match?(Fast.ast("1"), "(int %1)", 1) # => true
518
- # Fast.match?(Fast.ast("1"), "(int %1)", 2) # => false
541
+ # Fast.match?("(int %1)", Fast.ast("1"), 1) # => true
542
+ # Fast.match?("(int %1)", Fast.ast("1"), 2) # => false
519
543
  # @example interpolate multiple arguments
520
- # Fast.match?(Fast.ast("1"), "(%1 %2)", :int, 1) # => true
544
+ # Fast.match?("(%1 %2)", Fast.ast("1"), :int, 1) # => true
521
545
  class FindFromArgument < Find
522
546
  attr_writer :arguments
523
547
 
@@ -532,7 +556,7 @@ module Fast
532
556
  def match?(node)
533
557
  raise 'You must define arguments to match' unless @arguments
534
558
 
535
- compare_symbol_or_head node, @arguments[@capture_argument]
559
+ compare_symbol_or_head @arguments[@capture_argument], node
536
560
  end
537
561
 
538
562
  def to_s
@@ -605,7 +629,7 @@ module Fast
605
629
  # Fast.expression("{int float}")
606
630
  class Any < Find
607
631
  def match?(node)
608
- token.any? { |expression| Fast.match?(node, expression) }
632
+ token.any? { |expression| Fast.match?(expression, node) }
609
633
  end
610
634
 
611
635
  def to_s
@@ -651,51 +675,51 @@ module Fast
651
675
  # @example simple match
652
676
  # ast = Fast.ast("a = 1")
653
677
  # expression = Fast.expression("(lvasgn _ (int _))")
654
- # Matcher.new(ast,expression).match? # true
678
+ # Matcher.new(expression, ast).match? # true
655
679
  #
656
680
  # @example simple capture
657
681
  # ast = Fast.ast("a = 1")
658
682
  # expression = Fast.expression("(lvasgn _ (int $_))")
659
- # Matcher.new(ast,expression).match? # => [1]
683
+ # Matcher.new(expression, ast).match? # => [1]
660
684
  #
661
685
  class Matcher
662
- def initialize(ast, fast, *args)
686
+ def initialize(pattern, ast, *args)
663
687
  @ast = ast
664
- @fast = if fast.is_a?(String)
665
- Fast.expression(fast)
666
- else
667
- [*fast].map(&Find.method(:new))
668
- end
688
+ @expression = if pattern.is_a?(String)
689
+ Fast.expression(pattern)
690
+ else
691
+ [*pattern].map(&Find.method(:new))
692
+ end
669
693
  @captures = []
670
- prepare_arguments(@fast, args) if args.any?
694
+ prepare_arguments(@expression, args) if args.any?
671
695
  end
672
696
 
673
697
  # @return [true] if the @param ast recursively matches with expression.
674
698
  # @return #find_captures case matches
675
- def match?(ast = @ast, fast = @fast)
676
- head, *tail = fast
699
+ def match?(expression = @expression, ast = @ast)
700
+ head, *tail_expression = expression
677
701
  return false unless head.match?(ast)
678
- return find_captures if tail.empty?
702
+ return find_captures if tail_expression.empty?
679
703
 
680
- match_tail?(ast.children, tail)
704
+ match_tail?(tail_expression, ast.children)
681
705
  end
682
706
 
683
707
  # @return [true] if all children matches with tail
684
- def match_tail?(child, tail)
708
+ def match_tail?(tail, child)
685
709
  tail.each_with_index.all? do |token, i|
686
710
  prepare_token(token)
687
- token.is_a?(Array) ? match?(child[i], token) : token.match?(child[i])
711
+ token.is_a?(Array) ? match?(token, child[i]) : token.match?(child[i])
688
712
  end && find_captures
689
713
  end
690
714
 
691
- # Look recursively into @param fast to check if the expression is have
715
+ # Look recursively into @param expression to check if the expression is have
692
716
  # captures.
693
717
  # @return [true] if any sub expression have captures.
694
- def captures?(fast = @fast)
695
- case fast
718
+ def captures?(expression = @expression)
719
+ case expression
696
720
  when Capture then true
697
- when Array then fast.any?(&method(:captures?))
698
- when Find then captures?(fast.token)
721
+ when Array then expression.any?(&method(:captures?))
722
+ when Find then captures?(expression.token)
699
723
  end
700
724
  end
701
725
 
@@ -705,13 +729,13 @@ module Fast
705
729
  # @return [true] in case of no captures in the expression
706
730
  # @see Fast::Capture
707
731
  # @see Fast::FindFromArgument
708
- def find_captures(fast = @fast)
709
- return true if fast == @fast && !captures?(fast)
732
+ def find_captures(expression = @expression)
733
+ return true if expression == @expression && !captures?(expression)
710
734
 
711
- case fast
712
- when Capture then fast.captures
713
- when Array then fast.flat_map(&method(:find_captures)).compact
714
- when Find then find_captures(fast.token)
735
+ case expression
736
+ when Capture then expression.captures
737
+ when Array then expression.flat_map(&method(:find_captures)).compact
738
+ when Find then find_captures(expression.token)
715
739
  end
716
740
  end
717
741
 
@@ -35,19 +35,26 @@ module Fast
35
35
  def report(result, show_sexp: false, file: nil, headless: false)
36
36
  if file
37
37
  line = result.loc.expression.line if result.is_a?(Parser::AST::Node)
38
- puts(Fast.highlight("# #{file}:#{line}")) unless headless
38
+ puts(highlight("# #{file}:#{line}")) unless headless
39
39
  end
40
- puts Fast.highlight(result, show_sexp: show_sexp)
40
+ puts highlight(result, show_sexp: show_sexp)
41
41
  end
42
42
 
43
43
  # Command Line Interface for Fast
44
44
  class Cli # rubocop:disable Metrics/ClassLength
45
45
  attr_reader :pattern, :show_sexp, :pry, :from_code, :similar, :help
46
-
47
- # rubocop:disable Metrics/MethodLength
48
- # rubocop:disable Metrics/AbcSize
49
46
  def initialize(args)
50
- @opt = OptionParser.new do |opts| # rubocop:disable Metrics/BlockLength
47
+ args = replace_args_with_shortcut(args) if args.first&.start_with?('.')
48
+
49
+ @pattern, *@files = args.reject { |arg| arg.start_with? '-' }
50
+
51
+ option_parser.parse! args
52
+
53
+ @files = [*@files].reject { |arg| arg.start_with?('-') }
54
+ end
55
+
56
+ def option_parser # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
57
+ @option_parser ||= OptionParser.new do |opts| # rubocop:disable Metrics/BlockLength
51
58
  opts.banner = 'Usage: fast expression <files> [options]'
52
59
  opts.on('-d', '--debug', 'Debug fast engine') do
53
60
  @debug = true
@@ -67,6 +74,7 @@ module Fast
67
74
 
68
75
  opts.on('--pry', 'Jump into a pry session with results') do
69
76
  @pry = true
77
+ require 'pry'
70
78
  end
71
79
 
72
80
  opts.on('-c', '--code', 'Create a pattern from code example') do
@@ -92,25 +100,17 @@ module Fast
92
100
  @help = true
93
101
  end
94
102
  end
103
+ end
95
104
 
96
- if args.first&.start_with?('.') # shortcut! :tada:
97
- shortcut = find_shortcut args.first[1..-1]
98
- if shortcut.single_run_with_block?
99
- shortcut.run
100
- exit
101
- else
102
- args = args.one? ? shortcut.args : shortcut.merge_args(args[1..-1])
103
- end
105
+ def replace_args_with_shortcut(args)
106
+ shortcut = find_shortcut args.first[1..-1]
107
+ if shortcut.single_run_with_block?
108
+ shortcut.run
109
+ exit
110
+ else
111
+ args.one? ? shortcut.args : shortcut.merge_args(args[1..-1])
104
112
  end
105
-
106
- @pattern, *@files = args.reject { |arg| arg.start_with? '-' }
107
-
108
- @opt.parse! args
109
-
110
- @files = [*@files].reject { |arg| arg.start_with?('-') }
111
113
  end
112
- # rubocop:enable Metrics/MethodLength
113
- # rubocop:enable Metrics/AbcSize
114
114
 
115
115
  # Run a new command line interface digesting the arguments
116
116
  def self.run!(argv)
@@ -121,7 +121,7 @@ module Fast
121
121
  # Show help or search for node patterns
122
122
  def run!
123
123
  if @help || @files.empty? && @pattern.nil?
124
- puts @opt.help
124
+ puts option_parser.help
125
125
  else
126
126
  search
127
127
  end
@@ -136,43 +136,25 @@ module Fast
136
136
  # If -d (debug option) is enabled, it will output details of each search.
137
137
  # If capture option is enabled it will only print the captures, otherwise it
138
138
  # prints all the results.
139
- def search_file(file)
139
+ def search
140
140
  if debug_mode?
141
- Fast.debug { Fast.search_file(expression, file) }
141
+ Fast.debug { execute_search }
142
142
  else
143
- begin
144
- Fast.public_send(@captures ? :capture_file : :search_file, expression, file)
145
- rescue StandardError
146
- debug "Ops! An error occurred trying to search in #{expression.inspect} in #{file}", $ERROR_INFO, $ERROR_POSITION
147
- []
148
- end
149
- end
150
- end
151
-
152
- # Search for the {#expression} on all the {#files}.
153
- # It {#report} results if no options are passed.
154
- # It binds pry if option "--pry" is passed.
155
- def search
156
- files.each do |file|
157
- results = [*search_file(file)]
158
-
159
- next if results.empty?
160
-
161
- results.each do |result|
162
- if @pry
163
- require 'pry'
164
- binding.pry # rubocop:disable Lint/Debugger
165
- else
143
+ execute_search do |file, results|
144
+ results.each do |result|
145
+ binding.pry if @pry # rubocop:disable Lint/Debugger
166
146
  report(result, file)
167
147
  end
168
148
  end
169
149
  end
170
150
  end
171
151
 
172
- # @return [Array<String>] with files from command line expression.
173
- # @see Fast.ruby_files_from
174
- def files
175
- Fast.ruby_files_from(*@files)
152
+ def execute_search
153
+ method_name = @captures ? :capture_all : :search_all
154
+ (Fast.public_send(method_name, expression, @files) || []).each do |file, results|
155
+ results = [results] unless results.is_a?(Array)
156
+ yield file, results
157
+ end
176
158
  end
177
159
 
178
160
  # @return [Boolean] true when "-d" or "--debug" option is passed
@@ -287,7 +287,7 @@ module Fast
287
287
 
288
288
  # @return [Array<Astrolabe::Node>]
289
289
  def search_cases
290
- Fast.search(@ast, experiment.expression) || []
290
+ Fast.search(experiment.expression, @ast) || []
291
291
  end
292
292
 
293
293
  # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
@@ -297,7 +297,7 @@ module Fast
297
297
  # @return [void]
298
298
  def partial_replace(*indices)
299
299
  replacement = experiment.replacement
300
- new_content = Fast.replace_file @file, experiment.expression do |node, *captures|
300
+ new_content = Fast.replace_file experiment.expression, @file do |node, *captures|
301
301
  if indices.nil? || indices.empty? || indices.include?(match_index)
302
302
  if replacement.parameters.length == 1
303
303
  instance_exec node, &replacement
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Fast
4
- VERSION = '0.1.3'
4
+ VERSION = '0.1.4'
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.3
4
+ version: 0.1.4
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-26 00:00:00.000000000 Z
11
+ date: 2019-10-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: astrolabe