rak 1.0 → 1.2
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.
- data/bin/rak +281 -351
- data/lib/rak.rb +1 -1
- data/spec/help_spec.rb +33 -27
- data/spec/rak_spec.rb +435 -363
- metadata +31 -61
- data/Rakefile +0 -14
data/bin/rak
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
+
# encoding: binary
|
2
3
|
|
3
4
|
require 'rubygems'
|
5
|
+
require "pathname"
|
4
6
|
|
5
7
|
if RUBY_PLATFORM =~ /mswin|mingw/
|
6
8
|
begin
|
@@ -20,8 +22,16 @@ rescue LoadError
|
|
20
22
|
$use_onig = false
|
21
23
|
end
|
22
24
|
|
25
|
+
class String
|
26
|
+
def expand_tabs(shift=0)
|
27
|
+
expanded = dup
|
28
|
+
1 while expanded.sub!(/\t+/){ " "*($&.size*8 - ($`.size+shift)%8) }
|
29
|
+
expanded
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
23
33
|
class Rak
|
24
|
-
VERSION = "
|
34
|
+
VERSION = "1.2"
|
25
35
|
|
26
36
|
FILE_COLOUR = "\033[1;31m"
|
27
37
|
MATCH_COLOUR = "\033[1;37m\033[41m"
|
@@ -30,7 +40,7 @@ class Rak
|
|
30
40
|
VERSION_INFO=<<END
|
31
41
|
rak #{VERSION}
|
32
42
|
|
33
|
-
Copyright 2008 Daniel Lucraft, all rights reserved.
|
43
|
+
Copyright 2008-#{Time.now.year} Daniel Lucraft, all rights reserved.
|
34
44
|
Based on the perl tool 'ack' by Andy Lester.
|
35
45
|
|
36
46
|
This program is free software; you can redistribute it and/or modify it
|
@@ -38,40 +48,57 @@ under the same terms as Ruby.
|
|
38
48
|
END
|
39
49
|
|
40
50
|
FILE_TYPES = {
|
41
|
-
:
|
42
|
-
:
|
43
|
-
:
|
44
|
-
:
|
45
|
-
:
|
46
|
-
:
|
47
|
-
:
|
48
|
-
:
|
49
|
-
:
|
50
|
-
:
|
51
|
-
:
|
52
|
-
:
|
53
|
-
:
|
54
|
-
:
|
55
|
-
:
|
56
|
-
:
|
57
|
-
:
|
58
|
-
:
|
59
|
-
:
|
60
|
-
:
|
61
|
-
:
|
62
|
-
:
|
63
|
-
:
|
64
|
-
:
|
65
|
-
:
|
66
|
-
:
|
67
|
-
:
|
68
|
-
:
|
69
|
-
:
|
70
|
-
:
|
71
|
-
:
|
72
|
-
:
|
73
|
-
:
|
74
|
-
:
|
51
|
+
:actionscript => %w( .as .mxml ),
|
52
|
+
:ada => %w( .ada .adb .ads ),
|
53
|
+
:asm => %w( .S .asm .s ),
|
54
|
+
:awk => %w( .awk ),
|
55
|
+
:batch => %w( .bat .cmd ),
|
56
|
+
:cc => %w( .c .h .l .xs .y ),
|
57
|
+
:cfmx => %w( .cfc .cfm .cfml ),
|
58
|
+
:cpp => %w( .C .H .cc .cpp .cxx .h .hh .hpp .hxx .m ),
|
59
|
+
:csharp => %w( .cs ),
|
60
|
+
:css => %w( .css ),
|
61
|
+
:elisp => %w( .el ),
|
62
|
+
:erlang => %w( .erl .hrl ),
|
63
|
+
:fortran => %w( .f .f03 .f77 .f90 .f95 .for .fpp .ftn ),
|
64
|
+
:haskell => %w( .hs .lhs ),
|
65
|
+
:hh => %w( .h ),
|
66
|
+
:html => %w( .htm .html .shtml .xhtml ),
|
67
|
+
:java => %w( .java .properties properties ),
|
68
|
+
:js => %w( .js ),
|
69
|
+
:jsp => %w( .jhtm .jhtml .jsp .jspx ),
|
70
|
+
:lisp => %w( .lisp .lsp ),
|
71
|
+
:lua => %w( .lua ),
|
72
|
+
:make => %w( .mk Makefile ),
|
73
|
+
:mason => %w( .mas .mhtml .mpl .mtxt ),
|
74
|
+
:matlab => %w( .m .oct ),
|
75
|
+
:objc => %w( .h .m ),
|
76
|
+
:objcpp => %w( .h .mm ),
|
77
|
+
:ocaml => %w( .ml .mli .mll .mly ),
|
78
|
+
:parrot => %w( .ops .pasm .pg .pir .pmc .pod .tg ),
|
79
|
+
:perl => %w( .pl .pm .pod .t ),
|
80
|
+
:php => %w( .php .php3 .php4 .php5 .phpt .phtml ),
|
81
|
+
:plone => %w( .cpt .cpy .metadata .pt .py ),
|
82
|
+
:prolog => %w( .ecl .pl ),
|
83
|
+
:python => %w( .py ),
|
84
|
+
:ruby => %w( .erb .haml .rake .rb .rhtml .rjs .rxml Rakefile ),
|
85
|
+
:scala => %w( .scala ),
|
86
|
+
:scheme => %w( .scm .ss ),
|
87
|
+
:sed => %w( .sed ),
|
88
|
+
:shell => %w( .bash .csh .ksh .sh .tcsh .zsh ),
|
89
|
+
:smalltalk => %w( .st ),
|
90
|
+
:sml => %w( .sml .cm .sig ),
|
91
|
+
:sql => %w( .ctl .sql ),
|
92
|
+
:tcl => %w( .itcl .itk .tcl ),
|
93
|
+
:tex => %w( .cls .sty .tex ),
|
94
|
+
:text => %w( .text .txt README ),
|
95
|
+
:tt => %w( .tt .tt2 .ttml ),
|
96
|
+
:vala => %w( .vala .vapi ),
|
97
|
+
:vb => %w( .bas .cls .ctl .frm .resx .vb ),
|
98
|
+
:verilog => %w( .v .verilog ),
|
99
|
+
:vim => %w( .vim ),
|
100
|
+
:xml => %w( .dtd .ent .xml .xslt ),
|
101
|
+
:yaml => %w( .yaml .yml ),
|
75
102
|
}
|
76
103
|
|
77
104
|
VC_DIRS = %w(blib CVS _darcs .git .pc RCS SCCS .svn pkg)
|
@@ -80,6 +107,14 @@ END
|
|
80
107
|
attr_reader :opt
|
81
108
|
end
|
82
109
|
|
110
|
+
def self.compile_regexp(str)
|
111
|
+
if $use_onig
|
112
|
+
Oniguruma::ORegexp.new(str)
|
113
|
+
else
|
114
|
+
Regexp.new(str)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
83
118
|
def self.search
|
84
119
|
@opt = {}
|
85
120
|
|
@@ -104,6 +139,7 @@ END
|
|
104
139
|
[ '--help', GetoptLong::OPTIONAL_ARGUMENT ],
|
105
140
|
[ '--max-count', '-m', GetoptLong::REQUIRED_ARGUMENT ],
|
106
141
|
[ '--files', '-f', GetoptLong::NO_ARGUMENT ],
|
142
|
+
[ '--skipped', GetoptLong::NO_ARGUMENT ],
|
107
143
|
[ '--output', GetoptLong::REQUIRED_ARGUMENT ],
|
108
144
|
[ '--version', GetoptLong::NO_ARGUMENT ],
|
109
145
|
[ '-c', '--count', GetoptLong::NO_ARGUMENT ],
|
@@ -136,16 +172,14 @@ END
|
|
136
172
|
[ '-k', GetoptLong::REQUIRED_ARGUMENT ],
|
137
173
|
[ '-x', '--line-regexp', GetoptLong::NO_ARGUMENT ],
|
138
174
|
[ '-s', '--line-start', GetoptLong::NO_ARGUMENT ],
|
139
|
-
[ '-e', '--line-end', GetoptLong::NO_ARGUMENT ]
|
175
|
+
[ '-e', '--line-end', GetoptLong::NO_ARGUMENT ],
|
176
|
+
[ '--eval', GetoptLong::REQUIRED_ARGUMENT ]
|
140
177
|
)
|
141
178
|
|
142
|
-
dir = nil
|
143
|
-
|
144
179
|
opt[:max_count] = nil
|
145
|
-
opt[:
|
180
|
+
opt[:only_print_filelist] = false
|
146
181
|
opt[:print_filename] = true
|
147
182
|
opt[:print_line_number] = true
|
148
|
-
opt[:print_filelist] = false
|
149
183
|
opt[:print_output] = nil
|
150
184
|
opt[:print_highlighted] = true
|
151
185
|
opt[:print_num_matches] = false
|
@@ -171,10 +205,12 @@ END
|
|
171
205
|
opt[:collect_context] = false
|
172
206
|
opt[:filename_regex] = nil
|
173
207
|
opt[:neg_filename_regex] = nil
|
208
|
+
opt[:reverse_relevance] = false
|
209
|
+
opt[:eval] = nil
|
174
210
|
|
175
211
|
# if redirected (RAK_TEST allows us to redirect in testing and still
|
176
212
|
# get the non-redirected defaults).
|
177
|
-
|
213
|
+
unless STDOUT.isatty or ENV['RAK_TEST'] == "true"
|
178
214
|
opt[:colour] = false
|
179
215
|
opt[:print_file_each_line] = true
|
180
216
|
opt[:print_filename] = false
|
@@ -189,13 +225,20 @@ END
|
|
189
225
|
puts USAGE_HELP
|
190
226
|
elsif arg == "types" or arg == "type"
|
191
227
|
puts TYPES_HELP
|
228
|
+
FILE_TYPES.sort_by{|type,exts| type.to_s}.each do |type, exts|
|
229
|
+
puts " --[no]%-13s %s\n" % [type, exts.join(" ")]
|
230
|
+
end
|
192
231
|
end
|
193
232
|
exit
|
233
|
+
when '--eval'
|
234
|
+
opt[:eval] = arg
|
194
235
|
when '--max-count'
|
195
236
|
opt[:max_count] = arg.to_i
|
196
237
|
when '--files'
|
197
|
-
opt[:
|
198
|
-
|
238
|
+
opt[:only_print_filelist] = true
|
239
|
+
when '--skipped'
|
240
|
+
opt[:reverse_relevance] = true
|
241
|
+
opt[:only_print_filelist] = true
|
199
242
|
when '--output'
|
200
243
|
opt[:print_filename] = false
|
201
244
|
opt[:print_line_number] = false
|
@@ -291,9 +334,9 @@ END
|
|
291
334
|
opt[:before_context] = val
|
292
335
|
opt[:after_context] = val
|
293
336
|
when '-g'
|
294
|
-
opt[:filename_regex] = arg
|
337
|
+
opt[:filename_regex] = compile_regexp(arg)
|
295
338
|
when '-k'
|
296
|
-
opt[:neg_filename_regex] = arg
|
339
|
+
opt[:neg_filename_regex] = compile_regexp(arg)
|
297
340
|
when '-x'
|
298
341
|
opt[:match_whole_lines] = true
|
299
342
|
when '-s'
|
@@ -309,230 +352,149 @@ END
|
|
309
352
|
exit
|
310
353
|
end
|
311
354
|
|
312
|
-
if ARGV.empty? and opt[:print_filelist] == false
|
313
|
-
puts USAGE_HELP
|
314
|
-
exit
|
315
|
-
end
|
316
|
-
|
317
|
-
files = get_files
|
318
|
-
|
319
|
-
compile_match_file
|
320
|
-
|
321
355
|
unless opt[:colour]
|
322
356
|
FILE_COLOUR.replace ""
|
323
357
|
CLEAR_COLOURS.replace ""
|
324
358
|
MATCH_COLOUR.replace ""
|
325
359
|
end
|
326
360
|
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
361
|
+
if opt[:only_print_filelist]
|
362
|
+
each_file(ARGV) do |fn|
|
363
|
+
puts fn
|
364
|
+
end
|
365
|
+
elsif ARGV.empty? and !opt[:eval]
|
366
|
+
puts USAGE_HELP
|
367
|
+
exit
|
368
|
+
else
|
369
|
+
unless opt[:eval]
|
370
|
+
re = compile_pattern(ARGV.shift)
|
371
|
+
end
|
372
|
+
compiled = false
|
373
|
+
file_separator = ""
|
374
|
+
each_file(ARGV) do |fn|
|
375
|
+
# each_file might turn off printing file name, but only before first yield
|
376
|
+
unless compiled
|
377
|
+
compile_match_file
|
378
|
+
compiled = true
|
379
|
+
end
|
380
|
+
match_file(re, fn, file_separator)
|
381
|
+
end
|
334
382
|
end
|
335
383
|
end
|
336
384
|
|
337
|
-
def self.
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
385
|
+
def self.extension_regexp(extensions)
|
386
|
+
return nil if extensions.empty?
|
387
|
+
Regexp.compile('(?:' + extensions.map{|x| Regexp.escape(x)}.join("|") + ')\z')
|
388
|
+
end
|
389
|
+
|
390
|
+
def self.file_relevant?(fn)
|
391
|
+
# These don't change at this point
|
392
|
+
@types_rx ||= extension_regexp(FILE_TYPES.values.flatten)
|
393
|
+
@includes_rx ||= extension_regexp(opt[:includes])
|
394
|
+
@excludes_rx ||= extension_regexp(opt[:excludes])
|
395
|
+
|
396
|
+
ext = fn.basename.to_s
|
397
|
+
ext = shebang_matches(fn) unless ext =~ @types_rx
|
398
|
+
|
399
|
+
return false if !opt[:all_files] and !ext or fn.to_s =~ /[~#]\z/
|
400
|
+
return false if @includes_rx and (ext||"") !~ @includes_rx
|
401
|
+
return false if @excludes_rx and (ext||"") =~ @excludes_rx
|
402
|
+
return false if opt[:filename_regex] and fn.to_s !~ opt[:filename_regex]
|
403
|
+
return false if opt[:neg_filename_regex] and fn.to_s =~ opt[:neg_filename_regex]
|
404
|
+
return true
|
405
|
+
end
|
406
|
+
|
407
|
+
def self.find_all_files(path, &blk)
|
408
|
+
return if path.socket?
|
409
|
+
return unless path.readable?
|
410
|
+
|
411
|
+
if path.file?
|
412
|
+
relevant = file_relevant?(path)
|
413
|
+
relevant = !relevant if opt[:reverse_relevance]
|
414
|
+
yield(path.to_s.sub(/\A\.\/+/, "").gsub(/\/+/, "/")) if relevant
|
415
|
+
elsif path.directory?
|
416
|
+
path.children.each do |fn|
|
417
|
+
next if VC_DIRS.any?{|vc| vc == fn.basename.to_s}
|
418
|
+
next if fn.directory? and not opt[:descend]
|
419
|
+
next if fn.symlink? and not opt[:follow_symlinks]
|
420
|
+
find_all_files(fn, &blk)
|
360
421
|
end
|
361
422
|
end
|
362
|
-
files
|
363
423
|
end
|
364
|
-
|
365
|
-
def self.
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
if !files_and_dirs or files_and_dirs.empty?
|
371
|
-
if STDIN.isatty == false
|
372
|
-
io_files << STDIN
|
373
|
-
opt[:print_filename] = false
|
374
|
-
files = []
|
424
|
+
|
425
|
+
def self.each_file(todo, &blk)
|
426
|
+
todo = todo.map{|path| Pathname(path)}
|
427
|
+
if todo.empty?
|
428
|
+
if STDIN.isatty
|
429
|
+
todo = [Pathname(".")]
|
375
430
|
else
|
376
|
-
files = get_all_files
|
377
|
-
end
|
378
|
-
else
|
379
|
-
files = []
|
380
|
-
files_and_dirs.each do |thing|
|
381
|
-
if File.file? thing
|
382
|
-
files << thing
|
383
|
-
end
|
384
|
-
if File.directory? thing
|
385
|
-
get_all_files(files, thing)
|
386
|
-
end
|
387
|
-
end
|
388
|
-
if files_and_dirs.length == 1 and
|
389
|
-
File.file? files_and_dirs[0]
|
390
431
|
opt[:print_filename] = false
|
432
|
+
yield(STDIN)
|
433
|
+
return
|
391
434
|
end
|
435
|
+
elsif todo.size == 1 and todo[0].file?
|
436
|
+
opt[:print_filename] = false
|
392
437
|
end
|
393
438
|
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
fn[-1..-1] != "#"
|
400
|
-
end
|
401
|
-
new_files = []
|
402
|
-
files.each do |fn|
|
403
|
-
if types.any? {|ext| File.extname(fn) == ext or fn.split("/").last == ext}
|
404
|
-
new_files << fn
|
405
|
-
elsif ext = shebang_matches(fn)
|
406
|
-
shebang_files << [fn, ext]
|
407
|
-
end
|
408
|
-
end
|
409
|
-
files = new_files
|
410
|
-
end
|
411
|
-
|
412
|
-
# select based on file type
|
413
|
-
if not opt[:includes].empty?
|
414
|
-
files = files.select do |fn|
|
415
|
-
opt[:includes].any? {|ext| fn.include? ext}
|
416
|
-
end
|
417
|
-
shebang_files2 = []
|
418
|
-
shebang_files.each do |fn, ext|
|
419
|
-
if opt[:includes].include? ext
|
420
|
-
shebang_files2 << [fn, ext]
|
439
|
+
if opt[:sort_files]
|
440
|
+
sortme = []
|
441
|
+
todo.each do |item|
|
442
|
+
find_all_files(item) do |fn|
|
443
|
+
sortme << fn
|
421
444
|
end
|
422
445
|
end
|
446
|
+
sortme.sort_by{|fn|fn.downcase}.each(&blk)
|
423
447
|
else
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
if not opt[:excludes].empty?
|
428
|
-
files = files.select do |fn|
|
429
|
-
not opt[:excludes].any? {|ext| fn.include? ext}
|
430
|
-
end
|
431
|
-
shebang_files3 = []
|
432
|
-
shebang_files2.each do |fn, ext|
|
433
|
-
unless opt[:excludes].any? {|ext| fn.include? ext}
|
434
|
-
shebang_files3 << [fn, ext]
|
435
|
-
end
|
448
|
+
todo.each do |item|
|
449
|
+
find_all_files(item, &blk)
|
436
450
|
end
|
437
|
-
else
|
438
|
-
shebang_files3 = shebang_files2
|
439
|
-
end
|
440
|
-
|
441
|
-
files += shebang_files3.map {|fn, ext| fn}
|
442
|
-
|
443
|
-
# reject version control dirs
|
444
|
-
files.reject! do |fn|
|
445
|
-
VC_DIRS.any? {|vc| fn.include? vc}
|
446
451
|
end
|
447
|
-
|
448
|
-
# filter based on -g=REGEX
|
449
|
-
if opt[:filename_regex]
|
450
|
-
if $use_onig
|
451
|
-
fn_re = Oniguruma::ORegexp.new(opt[:filename_regex])
|
452
|
-
else
|
453
|
-
fn_re = Regexp.new(opt[:filename_regex])
|
454
|
-
end
|
455
|
-
files = files.select {|fn| fn =~ fn_re }
|
456
|
-
end
|
457
|
-
|
458
|
-
# filter based on -k=REGEX
|
459
|
-
if opt[:neg_filename_regex]
|
460
|
-
if $use_onig
|
461
|
-
fn_re = Oniguruma::ORegexp.new(opt[:neg_filename_regex])
|
462
|
-
else
|
463
|
-
fn_re = Regexp.new(opt[:neg_filename_regex])
|
464
|
-
end
|
465
|
-
files = files.reject {|fn| fn =~ fn_re }
|
466
|
-
end
|
467
|
-
|
468
|
-
# remove the "./"
|
469
|
-
files.map! {|fn| fn[0..1] == "./" ? fn[2..-1] : fn}
|
470
|
-
|
471
|
-
if opt[:sort_files]
|
472
|
-
files = files.sort_by {|fn| fn.downcase}
|
473
|
-
end
|
474
|
-
|
475
|
-
files + io_files
|
476
452
|
end
|
477
|
-
|
453
|
+
|
478
454
|
def self.shebang_matches(fn)
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
if line =~ /^#!/
|
487
|
-
if line =~ /\b(ruby|perl|php|python)\b/
|
488
|
-
return "."+FILE_TYPES[$1.intern].first
|
489
|
-
elsif line =~ /\b(bash|csh|ksh|zsh)\b/
|
490
|
-
return ".sh"
|
491
|
-
end
|
455
|
+
begin
|
456
|
+
line = fn.open.readline
|
457
|
+
if line =~ /^#!/
|
458
|
+
if line =~ /\b(ruby|perl|php|python|make)[0-9.]*\b/i
|
459
|
+
FILE_TYPES[$1.downcase.intern].first
|
460
|
+
elsif line =~ /\b(sh|bash|csh|ksh|zsh)\b/
|
461
|
+
".sh"
|
492
462
|
else
|
493
|
-
|
494
|
-
return ".xml"
|
495
|
-
end
|
463
|
+
".other"
|
496
464
|
end
|
497
|
-
|
498
|
-
|
465
|
+
elsif line =~ /^<\?xml\b/
|
466
|
+
".xml"
|
467
|
+
else
|
468
|
+
false
|
499
469
|
end
|
500
|
-
|
470
|
+
rescue
|
471
|
+
nil
|
501
472
|
end
|
502
473
|
end
|
503
474
|
|
504
|
-
def self.
|
475
|
+
def self.compile_pattern(str)
|
505
476
|
if opt[:literal]
|
506
477
|
str = Regexp.quote(str)
|
507
478
|
end
|
508
479
|
if opt[:match_whole_words]
|
509
|
-
str = "\\b" + str + "\\b"
|
480
|
+
str = "\\b(?:" + str + ")\\b"
|
510
481
|
end
|
511
482
|
if opt[:match_whole_lines]
|
512
|
-
str = "^" + str + "$"
|
483
|
+
str = "^(?:" + str + ")$"
|
513
484
|
end
|
514
485
|
if opt[:match_line_starts]
|
515
|
-
str = "^" + str
|
486
|
+
str = "^(?:" + str + ")"
|
516
487
|
end
|
517
488
|
if opt[:match_line_ends]
|
518
|
-
str = str + "$"
|
489
|
+
str = "(?:" + str + ")$"
|
519
490
|
end
|
520
491
|
str = str.gsub("~bang", "!").gsub("~ques", "?")
|
521
492
|
if $use_onig
|
522
|
-
|
523
|
-
|
524
|
-
else
|
525
|
-
re = Oniguruma::ORegexp.new(str)
|
526
|
-
end
|
493
|
+
flags = opt[:ignore_case] ? Oniguruma::OPTION_IGNORECASE : nil
|
494
|
+
Oniguruma::ORegexp.new(str, :options => flags)
|
527
495
|
else
|
528
|
-
|
529
|
-
|
530
|
-
else
|
531
|
-
re = Regexp.new(str)
|
532
|
-
end
|
533
|
-
end
|
534
|
-
files.each do |fn|
|
535
|
-
match_file(re, fn)
|
496
|
+
flags = opt[:ignore_case] ? Regexp::IGNORECASE : nil
|
497
|
+
Regexp.new(str, flags)
|
536
498
|
end
|
537
499
|
end
|
538
500
|
|
@@ -541,67 +503,88 @@ END
|
|
541
503
|
# the match method based on them. This is gives a 2x speedup over
|
542
504
|
# the alternative.
|
543
505
|
def self.compile_match_file
|
506
|
+
needs_no_more_matches = (opt[:max_count] or opt[:eval])
|
507
|
+
needs_inverse_count = (opt[:print_num_matches] and opt[:invert_match])
|
508
|
+
|
544
509
|
code = []
|
545
|
-
code << %{def self.match_file(re, fn) }
|
510
|
+
code << %{def self.match_file(re, fn, file_separator) }
|
546
511
|
code << %{ displayed_filename = false }
|
547
512
|
code << %{ count = 0 }
|
548
|
-
|
549
|
-
code << %{
|
513
|
+
if needs_inverse_count
|
514
|
+
code << %{ inverse_count = 0 }
|
515
|
+
end
|
550
516
|
code << %{ print_num = 0 }
|
551
|
-
|
517
|
+
if needs_no_more_matches
|
518
|
+
code << %{ no_more_matches = false }
|
519
|
+
end
|
520
|
+
if opt[:print_file_each_line]
|
521
|
+
code << %{ fn_str = (fn.is_a?(String) ? FILE_COLOUR + fn + CLEAR_COLOURS + ":" : "") }
|
522
|
+
end
|
552
523
|
if opt[:before_context] > 0
|
553
524
|
code << %{ before_context = [] }
|
554
|
-
code << %{ before_context_size = opt[:before_context] }
|
555
525
|
end
|
556
526
|
code << %{ if fn.is_a? String }
|
557
|
-
code << %{ f = File.open(fn, "
|
527
|
+
code << %{ f = File.open(fn, "rb") }
|
558
528
|
code << %{ elsif fn.is_a? IO }
|
559
529
|
code << %{ f = fn }
|
560
530
|
code << %{ end }
|
561
|
-
|
562
|
-
code << %{ all = f.read }
|
563
|
-
# code << %{ p fn }
|
564
|
-
# code << %{ all =~ re rescue p all }
|
565
|
-
code << %{ if all !~ re and not opt[:invert_match] and not opt[:print_entire_line_if_no_match] }
|
566
|
-
if opt[:print_file_if_no_match]
|
567
|
-
code << %{ if count == 0 }
|
568
|
-
code << %{ puts fn }
|
569
|
-
code << %{ end }
|
570
|
-
end
|
571
|
-
code << %{ return }
|
572
|
-
code << %{ end }
|
573
531
|
|
574
|
-
code << %{
|
575
|
-
code << %{
|
576
|
-
|
577
|
-
code << %{
|
578
|
-
|
579
|
-
|
580
|
-
code << %{
|
532
|
+
code << %{ f.each_line do |line| }
|
533
|
+
code << %{ matches = [] }
|
534
|
+
|
535
|
+
code << %{ unless no_more_matches } if needs_no_more_matches
|
536
|
+
if opt[:eval]
|
537
|
+
code << %{ $_ = line.chomp }
|
538
|
+
code << %{ $~ = nil }
|
539
|
+
code << %{ no_more_matches = true }
|
540
|
+
code << %{ eval_executed = false }
|
541
|
+
code << %{ loop do }
|
542
|
+
code << %{ if eval_executed }
|
543
|
+
code << %{ no_more_matches = false }
|
544
|
+
code << %{ break }
|
545
|
+
code << %{ end }
|
546
|
+
code << %{ eval_executed = true }
|
547
|
+
code << %{ #{opt[:eval]} }
|
548
|
+
code << %{ if matches.empty? }
|
549
|
+
code << %{ $_ =~ /^.*$/ unless $~ }
|
550
|
+
code << %{ matches << $~ }
|
551
|
+
code << %{ end }
|
552
|
+
code << %{ line = $_ }
|
553
|
+
code << %{ no_more_matches = false }
|
554
|
+
code << %{ break }
|
555
|
+
code << %{ end }
|
581
556
|
else
|
582
|
-
|
557
|
+
if opt[:print_output]
|
558
|
+
code << %{ line.scan(re){ matches << eval(opt[:print_output]) } }
|
559
|
+
else
|
560
|
+
code << %{ line.scan(re){ matches << $~ } }
|
561
|
+
end
|
583
562
|
end
|
584
563
|
if opt[:print_match]
|
585
|
-
code << %{ puts md.to_s
|
564
|
+
code << %{ matches.each{|md| puts md.to_s } }
|
586
565
|
end
|
587
|
-
code << %{ count +=
|
588
|
-
code << %{ end
|
566
|
+
code << %{ count += matches.size }
|
567
|
+
code << %{ end } if needs_no_more_matches
|
589
568
|
if opt[:invert_match]
|
590
569
|
if opt[:print_filename]
|
591
570
|
code << %{ unless displayed_filename }
|
571
|
+
code << %{ print file_separator }
|
572
|
+
code << %{ file_separator.replace("\\n") }
|
592
573
|
code << %{ puts FILE_COLOUR + fn + CLEAR_COLOURS }
|
593
574
|
code << %{ displayed_filename = true }
|
594
575
|
code << %{ end }
|
595
576
|
end
|
596
577
|
code << %{ if matches.empty? }
|
578
|
+
if needs_inverse_count
|
579
|
+
code << %{ inverse_count += 1 }
|
580
|
+
end
|
597
581
|
if opt[:print_highlighted]
|
598
582
|
if opt[:print_line_number]
|
599
|
-
code << %{ print "\#{
|
583
|
+
code << %{ print "\#{$..to_s.rjust(4)}|" }
|
600
584
|
end
|
601
|
-
code << %{ puts
|
585
|
+
code << %{ puts line.expand_tabs }
|
602
586
|
end
|
603
587
|
code << %{ end }
|
604
|
-
code << %{ matches.clear }
|
605
588
|
else
|
606
589
|
code << %{ if matches.empty? }
|
607
590
|
if opt[:print_entire_line_if_no_match]
|
@@ -610,9 +593,9 @@ END
|
|
610
593
|
if opt[:use_context]
|
611
594
|
if opt[:before_context] > 0
|
612
595
|
code << %{ if print_num == 0 }
|
613
|
-
code << %{ before_context << [
|
614
|
-
code << %{ if before_context.length
|
615
|
-
code << %{ before_context
|
596
|
+
code << %{ before_context << [$., line] }
|
597
|
+
code << %{ if before_context.length > #{opt[:before_context]} }
|
598
|
+
code << %{ before_context.shift }
|
616
599
|
code << %{ end }
|
617
600
|
code << %{ end }
|
618
601
|
end
|
@@ -620,9 +603,9 @@ END
|
|
620
603
|
code << %{ print_num -= 1 }
|
621
604
|
if opt[:print_highlighted]
|
622
605
|
if opt[:print_line_number]
|
623
|
-
code << %{ print "\#{
|
606
|
+
code << %{ print "\#{$..to_s.rjust(4)}|" }
|
624
607
|
end
|
625
|
-
code << %{ puts
|
608
|
+
code << %{ puts line.expand_tabs }
|
626
609
|
code << %{ end }
|
627
610
|
end
|
628
611
|
end
|
@@ -630,6 +613,8 @@ END
|
|
630
613
|
code << %{ print_num = opt[:after_context] }
|
631
614
|
if opt[:print_filename]
|
632
615
|
code << %{ unless displayed_filename }
|
616
|
+
code << %{ print file_separator }
|
617
|
+
code << %{ file_separator.replace("\\n") }
|
633
618
|
code << %{ puts FILE_COLOUR + fn + CLEAR_COLOURS }
|
634
619
|
code << %{ displayed_filename = true }
|
635
620
|
code << %{ end }
|
@@ -639,7 +624,7 @@ END
|
|
639
624
|
if opt[:print_line_number]
|
640
625
|
code << %{ print "\#{before_i.to_s.rjust(4)}|" }
|
641
626
|
end
|
642
|
-
code << %{ puts before_line
|
627
|
+
code << %{ puts before_line.expand_tabs }
|
643
628
|
code << %{ end }
|
644
629
|
code << %{ before_context = [] }
|
645
630
|
end
|
@@ -648,76 +633,52 @@ END
|
|
648
633
|
end
|
649
634
|
if opt[:print_highlighted]
|
650
635
|
if opt[:print_line_number]
|
651
|
-
code << %{
|
652
|
-
|
653
|
-
|
654
|
-
code << %{ fn_str = (fn.is_a?(String) ? FILE_COLOUR + fn + CLEAR_COLOURS + " " : "") }
|
655
|
-
code << %{ prefix = fn_str + "\#{i.to_s.rjust(4)}|" }
|
656
|
-
else
|
657
|
-
code << %{ prefix = "" }
|
658
|
-
end
|
636
|
+
code << %{ print "\#{$..to_s.rjust(4)}|" }
|
637
|
+
elsif opt[:print_file_each_line]
|
638
|
+
code << %{ print fn_str + "\#{$.}:" }
|
659
639
|
end
|
660
|
-
code << %{ print_highlighted(
|
640
|
+
code << %{ print_highlighted(line, matches) }
|
661
641
|
end
|
662
642
|
code << %{ end }
|
663
643
|
if opt[:max_count]
|
664
|
-
code << %{ if count >= opt[:max_count]
|
665
|
-
|
666
|
-
|
644
|
+
code << %{ no_more_matches = true if count >= opt[:max_count] }
|
645
|
+
end
|
646
|
+
if needs_no_more_matches and not needs_inverse_count
|
647
|
+
code << %{ break if no_more_matches and print_num == 0 }
|
667
648
|
end
|
668
|
-
code << %{ matches.clear }
|
669
649
|
end
|
670
650
|
code << %{ end }
|
671
|
-
if opt[:print_highlighted] and opt[:print_filename]
|
672
|
-
code << %{ if count > 0 }
|
673
|
-
code << %{ puts }
|
674
|
-
code << %{ end }
|
675
|
-
end
|
676
651
|
code << %{ f.close if f === File }
|
677
652
|
if opt[:print_num_matches]
|
678
653
|
if opt[:invert_match]
|
679
|
-
code << %{ puts "\#{fn}:\#{
|
654
|
+
code << %{ puts "\#{fn}:\#{inverse_count}" }
|
680
655
|
else
|
681
|
-
code << %{ if count > 0
|
682
|
-
code << %{ puts "\#{fn}:\#{count}" }
|
683
|
-
code << %{ end }
|
656
|
+
code << %{ puts "\#{fn}:\#{count}" if count > 0 }
|
684
657
|
end
|
685
658
|
end
|
686
659
|
if opt[:print_file_if_match]
|
687
|
-
code << %{ if count > 0 }
|
688
|
-
code << %{ puts fn }
|
689
|
-
code << %{ end }
|
660
|
+
code << %{ puts fn if count > 0 }
|
690
661
|
end
|
691
|
-
|
692
662
|
if opt[:print_file_if_no_match]
|
693
|
-
code << %{ if count == 0 }
|
694
|
-
code << %{ puts fn }
|
695
|
-
code << %{ end }
|
663
|
+
code << %{ puts fn if count == 0 }
|
696
664
|
end
|
697
665
|
code << %{end }
|
698
|
-
module_eval code.join("\n")
|
666
|
+
module_eval code.join("\n"), "#{__FILE__} (#{__LINE__})"
|
699
667
|
end
|
700
668
|
|
701
|
-
def self.print_highlighted(
|
702
|
-
|
703
|
-
|
704
|
-
|
705
|
-
|
706
|
-
|
707
|
-
|
708
|
-
|
709
|
-
|
710
|
-
|
711
|
-
print
|
712
|
-
line[(from+ptr)..(to+ptr-1)] +
|
713
|
-
CLEAR_COLOURS
|
714
|
-
ptr += to
|
715
|
-
if n == matches.length - 1
|
716
|
-
puts matches.last.post_match
|
717
|
-
else
|
718
|
-
print line[ptr..(matches[n+1].begin(0)-1+ptr)]
|
719
|
-
end
|
669
|
+
def self.print_highlighted(line, matches)
|
670
|
+
matches = matches.map{|md| md.offset(0)}
|
671
|
+
cuts = [0, matches, line.size].flatten
|
672
|
+
column = 0
|
673
|
+
0.upto(cuts.size-2) do |i|
|
674
|
+
part = line[cuts[i]...cuts[i+1]]
|
675
|
+
part = part.expand_tabs(column)
|
676
|
+
column += part.size
|
677
|
+
print MATCH_COLOUR if i%2 == 1
|
678
|
+
print part
|
679
|
+
print CLEAR_COLOURS if i%2 == 1
|
720
680
|
end
|
681
|
+
print "\n" unless line[-1,1] == "\n"
|
721
682
|
end
|
722
683
|
|
723
684
|
def self.unknown_type(type)
|
@@ -743,12 +704,15 @@ Searching:
|
|
743
704
|
-x, --line-regexp Force PATTERN to match only whole lines
|
744
705
|
-Q, --literal Quote all metacharacters; expr is literal
|
745
706
|
-s, --line-start Match only at the start of a line
|
746
|
-
-e, --line-
|
707
|
+
-e, --line-end Match only at the end of a line
|
708
|
+
|
709
|
+
--eval CODE Match with Ruby code instead of regex.
|
710
|
+
[Highly experimental]
|
747
711
|
|
748
712
|
Search output:
|
749
713
|
-l, --files-with-matches
|
750
714
|
Only print filenames containing matches
|
751
|
-
-L, --files-without-
|
715
|
+
-L, --files-without-matches
|
752
716
|
Only print filenames with no match
|
753
717
|
-o Show only the part of a line matching PATTERN
|
754
718
|
(turns off text highlighting)
|
@@ -778,8 +742,9 @@ Search output:
|
|
778
742
|
Print NUM lines (default 2) of output context.
|
779
743
|
|
780
744
|
File finding:
|
781
|
-
-f
|
745
|
+
-f, --files Only print the files found, without searching.
|
782
746
|
The PATTERN must not be specified.
|
747
|
+
--skipped Like -f but print files that were skipped.
|
783
748
|
--sort-files Sort the found files lexically.
|
784
749
|
|
785
750
|
File inclusion/exclusion:
|
@@ -810,41 +775,6 @@ format. For example, both --type=ruby and --ruby work.
|
|
810
775
|
Note that some extensions may appear in multiple types. For example,
|
811
776
|
.pod files are both Perl and Parrot.
|
812
777
|
|
813
|
-
--[no]asm .s
|
814
|
-
--[no]cc .c .h .xs
|
815
|
-
--[no]cpp .cpp .m .h
|
816
|
-
--[no]csharp .cs
|
817
|
-
--[no]css .css
|
818
|
-
--[no]elisp .el
|
819
|
-
--[no]erlang .erl
|
820
|
-
--[no]fortran .f .f77 .f90 .f95 .f03 .for .ftn .fpp
|
821
|
-
--[no]haskell .hs .lhs
|
822
|
-
--[no]hh .h
|
823
|
-
--[no]html .htm .html .shtml
|
824
|
-
--[no]java .java .properties
|
825
|
-
--[no]js .js
|
826
|
-
--[no]jsp .jsp .jspx .jhtm .jhtml
|
827
|
-
--[no]lisp .lisp
|
828
|
-
--[no]make Makefile
|
829
|
-
--[no]mason .mas .mhtml .mpl .mtxt
|
830
|
-
--[no]ocaml .ml .mli
|
831
|
-
--[no]parrot .pir .pasm .pmc .ops .pod .pg .tg
|
832
|
-
--[no]perl .pl .pm .pod .t
|
833
|
-
--[no]php .php .phpt .php3 .php4 .php5
|
834
|
-
--[no]prolog .pl .ecl
|
835
|
-
--[no]python .py
|
836
|
-
--[no]ruby .rb .rhtml .rjs .rxml Rakefile .rake .erb .haml
|
837
|
-
--[no]scheme .scm
|
838
|
-
--[no]shell .sh .bash .csh .ksh .zsh
|
839
|
-
--[no]sql .sql .ctl
|
840
|
-
--[no]tcl .tcl
|
841
|
-
--[no]tex .tex .cls .sty
|
842
|
-
--[no]text .txt .text
|
843
|
-
--[no]tt .tt .tt2 .ttml
|
844
|
-
--[no]vb .bas .cls .frm .ctl .vb .resx
|
845
|
-
--[no]vim .vim
|
846
|
-
--[no]xml .xml .dtd .xslt
|
847
|
-
--[no]yaml .yaml .yml
|
848
778
|
END
|
849
779
|
|
850
780
|
Rak.search
|