Class: Fast::Cli

Inherits:
Object
  • Object
show all
Defined in:
lib/fast/cli.rb

Overview

Command Line Interface for Fast

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(args) ⇒ Cli

Returns a new instance of Cli.



105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
# File 'lib/fast/cli.rb', line 105

def initialize(args)
  args = args.dup
  args = replace_args_with_shortcut(args) if shortcut_name_from(args)
  @colorize = STDOUT.isatty
  @headless = false
  @bodyless = false
  @captures = false
  @parallel = false
  @debug = false
  @sql = false
  @level = nil
  @show_sexp = false
  @help = false
  @similar = false
  @from_code = false
  @show_link = false
  @show_permalink = false
  @files = []
  option_parser.parse!(args)
  @pattern, @files = extract_pattern_and_files(args)
  puts "DEBUG: pattern=#{@pattern.inspect} files=#{@files.inspect}" if @debug

  @sql ||= @files.any? && @files.all? { |file| file.end_with?('.sql') }
  require 'fast/sql' if @sql
end

Instance Attribute Details

#from_codeObject (readonly)

rubocop:disable Metrics/ClassLength



104
105
106
# File 'lib/fast/cli.rb', line 104

def from_code
  @from_code
end

#helpObject (readonly)

rubocop:disable Metrics/ClassLength



104
105
106
# File 'lib/fast/cli.rb', line 104

def help
  @help
end

#levelObject (readonly)

rubocop:disable Metrics/ClassLength



104
105
106
# File 'lib/fast/cli.rb', line 104

def level
  @level
end

#patternObject (readonly)

rubocop:disable Metrics/ClassLength



104
105
106
# File 'lib/fast/cli.rb', line 104

def pattern
  @pattern
end

#pryObject (readonly)

rubocop:disable Metrics/ClassLength



104
105
106
# File 'lib/fast/cli.rb', line 104

def pry
  @pry
end

#show_sexpObject (readonly)

rubocop:disable Metrics/ClassLength



104
105
106
# File 'lib/fast/cli.rb', line 104

def show_sexp
  @show_sexp
end

#similarObject (readonly)

rubocop:disable Metrics/ClassLength



104
105
106
# File 'lib/fast/cli.rb', line 104

def similar
  @similar
end

Class Method Details

.run!(argv) ⇒ Object

Run a new command line interface digesting the arguments



227
228
229
230
# File 'lib/fast/cli.rb', line 227

def self.run!(argv)
  argv = argv.dup
  new(argv).run!
end

Instance Method Details

#debug(*info) ⇒ Object

Output information if #debug_mode? is true.



304
305
306
# File 'lib/fast/cli.rb', line 304

def debug(*info)
  puts(info) if debug_mode?
end

#debug_mode?Boolean

Returns true when "-d" or "--debug" option is passed.

Returns:

  • (Boolean)

    true when "-d" or "--debug" option is passed



299
300
301
# File 'lib/fast/cli.rb', line 299

def debug_mode?
  @debug == true
end

#execute_search {|with| ... } ⇒ Object

Executes search for all files yielding the results

Yield Parameters:

  • with (String, Array)

    file and respective search results



285
286
287
288
289
290
291
# File 'lib/fast/cli.rb', line 285

def execute_search(&on_result)
  Fast.public_send(search_method_name,
                   @pattern,
                   @files,
                   parallel: parallel?,
                   on_result: on_result)
end

#exit_shortcut_not_found(name) ⇒ Object

Exit process with warning message bolding the shortcut that was not found. Prints available shortcuts as extra help and exit with code 1.



359
360
361
362
363
364
365
366
# File 'lib/fast/cli.rb', line 359

def exit_shortcut_not_found(name)
  puts "Shortcut \033[1m#{name}\033[0m not found :("
  if Fast.shortcuts.any?
    puts "Available shortcuts are: #{Fast.shortcuts.keys.join(', ')}."
    Fast.load_fast_files!
  end
  exit 1
end

#expressionArray<Fast::Find>

Create fast expression from node pattern using the command line

Returns:

  • (Array<Fast::Find>)

    with the expression from string.



264
265
266
# File 'lib/fast/cli.rb', line 264

def expression
  Fast.expression(@pattern)
end

#extract_pattern_and_files(args) ⇒ Object



333
334
335
336
337
338
339
340
341
342
# File 'lib/fast/cli.rb', line 333

def extract_pattern_and_files(args)
  return [nil, []] if args.empty?

  files_start = args.index { |arg| File.exist?(arg) || File.directory?(arg) }
  if files_start
    [args[0...files_start].join(' '), args[files_start..]]
  else
    [args.join(' '), []]
  end
end

#find_shortcut(name) ⇒ Object

Find shortcut by name. Preloads all Fastfiles before start.

Parameters:

  • name (String)


346
347
348
349
350
351
352
353
354
355
# File 'lib/fast/cli.rb', line 346

def find_shortcut(name)
  unless defined? Fast::Shortcut
    require 'fast/shortcut'
    Fast.load_fast_files!
  end

  shortcut = Fast.shortcuts[name.to_sym]
  exit_shortcut_not_found(name) unless shortcut
  shortcut
end

#option_parserObject

rubocop:disable Metrics/MethodLength, Metrics/AbcSize



131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
# File 'lib/fast/cli.rb', line 131

def option_parser # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
  @option_parser ||= OptionParser.new do |opts| # rubocop:disable Metrics/BlockLength
    opts.banner = 'Usage: fast expression <files> [options]'
    opts.on('-d', '--debug', 'Debug fast engine') do
      @debug = true
    end

    opts.on('-l', '--level LEVELS', 'Maximum depth to print the AST') do |level|
      @level = level.to_i
    end

    opts.on('--ast', 'Print AST instead of code') do
      @show_sexp = true
    end

    opts.on('--link', 'Print link to repository URL') do
      require 'fast/git'
      @show_link = true
    end

    opts.on('--permalink', 'Print permalink to repository URL') do
      require 'fast/git'
      @show_permalink = true
    end

    opts.on('-p', '--parallel', 'Paralelize search') do
      @parallel = true
    end

    opts.on("--sql", "Use SQL instead of Ruby") do
      @sql = true
    end

    opts.on('-c', '--captures', 'Print only captures of the patterns and skip node results') do
      @captures = true
    end

    opts.on('--headless', 'Print results without the file name in the header') do
      @headless = true
    end

    opts.on('--bodyless', 'Print results without the code details') do
      @bodyless = true
    end

    opts.on('--pry', 'Jump into a pry session with results') do
      @pry = true
      require 'pry'
    end

    opts.on('-s', '--similar', 'Search for similar code.') do
      @similar = true
    end

    opts.on('--no-color', 'Disable color output') do
      @colorize = false
    end

    opts.on('--from-code', 'From code') do
      @from_code = true
    end

    opts.on('--validate-pattern PATTERN', 'Validate a node pattern') do |pattern|
      begin
        Fast.expression(pattern)
        puts "Pattern is valid."
      rescue StandardError => e
        puts "Invalid pattern: #{e.message}"
      end
      exit
    end

    opts.on_tail('--version', 'Show version') do
      puts Fast::VERSION
      exit
    end

    opts.on_tail('-h', '--help', 'Show help. More at https://jonatas.github.io/fast') do
      @help = true
    end
  end
end

#parallel?Boolean

Returns:

  • (Boolean)


308
309
310
# File 'lib/fast/cli.rb', line 308

def parallel?
  @parallel == true
end

#replace_args_with_shortcut(args) ⇒ Object



214
215
216
217
218
219
220
221
222
223
224
# File 'lib/fast/cli.rb', line 214

def replace_args_with_shortcut(args)
  shortcut_name = shortcut_name_from(args)
  shortcut = find_shortcut(shortcut_name)

  if shortcut.single_run_with_block?
    shortcut.run
    exit
  else
    shortcut.args
  end
end

#report(file, result) ⇒ Object

Report results using the actual options binded from command line.

See Also:



314
315
316
317
318
319
320
321
322
323
324
# File 'lib/fast/cli.rb', line 314

def report(file, result)
  Fast.report(result,
              file: file,
              show_link: @show_link,
              show_permalink: @show_permalink,
              show_sexp: @show_sexp,
              headless: @headless,
              bodyless: @bodyless,
              colorize: @colorize,
              level: @level)
end

#run!Object

Show help or search for node patterns



233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
# File 'lib/fast/cli.rb', line 233

def run!
  raise 'pry and parallel options are incompatible :(' if @parallel && @pry

  if @help || @files.empty? && @pattern.nil?
    puts option_parser.help
    return
  end

  if @similar
    ast = Fast.public_send( @sql ? :parse_sql : :ast, @pattern)
    @pattern = Fast.expression_from(ast)
    debug "Search similar to #{@pattern}"
  elsif @from_code
    ast = Fast.public_send( @sql ? :parse_sql : :ast, @pattern)
    @pattern = ast.to_sexp
    if @sql
      @pattern.gsub!(/\b-\b/,'_')
    end
    debug "Search from code to #{@pattern}"
  end

  if @files.empty?
    ast ||= Fast.public_send( @sql ? :parse_sql : :ast, @pattern)
    puts Fast.highlight(ast, show_sexp: @show_sexp, colorize: @colorize, sql: @sql, level: @level)
  else
    search
  end
end

#searchObject

Search for each file independent. If -d (debug option) is enabled, it will output details of each search. If capture option is enabled it will only print the captures, otherwise it prints all the results.



272
273
274
275
276
277
278
279
280
281
# File 'lib/fast/cli.rb', line 272

def search
  return Fast.debug(&method(:execute_search)) if debug_mode?

  execute_search do |file, results|
    results.each do |result|
      binding.pry if @pry # rubocop:disable Lint/Debugger
      report(file, result)
    end
  end
end

#search_method_nameSymbol

Returns with :capture_all or :search_all depending the command line options.

Returns:

  • (Symbol)

    with :capture_all or :search_all depending the command line options



294
295
296
# File 'lib/fast/cli.rb', line 294

def search_method_name
  @captures ? :capture_all : :search_all
end

#shortcut_name_from(args) ⇒ Object



326
327
328
329
330
331
# File 'lib/fast/cli.rb', line 326

def shortcut_name_from(args)
  command = args.find { |arg| !arg.start_with?('-') }
  return unless command&.start_with?('.')

  command[1..]
end