rak 1.0 → 1.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|