rak 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
data/History.txt ADDED
@@ -0,0 +1,5 @@
1
+ == 0.8.0 / 2007-10-30
2
+
3
+ * Initial release.
4
+
5
+
data/Manifest.txt ADDED
@@ -0,0 +1,8 @@
1
+ History.txt
2
+ Manifest.txt
3
+ README.txt
4
+ Rakefile
5
+ bin/rak
6
+ lib/rak.rb
7
+ spec/rak_spec.rb
8
+ spec/help_spec.rb
data/README.txt ADDED
@@ -0,0 +1,66 @@
1
+ rak
2
+ by Daniel Lucraft
3
+ http://rubyforge.org/projects/rak
4
+
5
+ == DESCRIPTION:
6
+
7
+ Replacement for grep. Recursively scans directories to match a given
8
+ Ruby regular expression. Prints highlighted results.
9
+
10
+ Based on the Perl tool 'ack' by Andy Lester.
11
+
12
+ Examples with similar grep:
13
+
14
+ $ rak pattern
15
+ $ grep pattern $(find . | grep -v .svn)
16
+
17
+ $ rak --ruby pattern
18
+ $ grep pattern $(find . -name '*.rb' | grep -v .svn)
19
+
20
+ == FEATURES/PROBLEMS:
21
+
22
+ * Ruby regular expression syntax (uses oniguruma gem if installed).
23
+ * Highlighted output.
24
+ * Automatically recurses down the current directory or any given
25
+ directories.
26
+ * Skips version control directories, backups like '~' and '#' and your
27
+ * ruby project's pkg directory.
28
+ * Allows inclusion and exclusion of files based on types.
29
+ * Many options similar to grep.
30
+
31
+ == SYNOPSIS:
32
+
33
+ See 'rak --help' for usage information.
34
+
35
+ == REQUIREMENTS:
36
+
37
+ * Ruby
38
+
39
+ == INSTALL:
40
+
41
+ * gem install rak
42
+
43
+ == LICENSE:
44
+
45
+ (The MIT License)
46
+
47
+ Copyright (c) 2007 Daniel Lucraft
48
+
49
+ Permission is hereby granted, free of charge, to any person obtaining
50
+ a copy of this software and associated documentation files (the
51
+ 'Software'), to deal in the Software without restriction, including
52
+ without limitation the rights to use, copy, modify, merge, publish,
53
+ distribute, sublicense, and/or sell copies of the Software, and to
54
+ permit persons to whom the Software is furnished to do so, subject to
55
+ the following conditions:
56
+
57
+ The above copyright notice and this permission notice shall be
58
+ included in all copies or substantial portions of the Software.
59
+
60
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
61
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
62
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
63
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
64
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
65
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
66
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,14 @@
1
+
2
+ require 'rubygems'
3
+ require 'hoe'
4
+ require 'lib/rak'
5
+
6
+ Hoe.new('rak', Rak::VERSION) do |p|
7
+ p.rubyforge_name = 'rak'
8
+ p.author = 'Daniel Lucraft'
9
+ p.email = 'dan@fluentradical.com'
10
+ p.summary = 'A grep replacement in Ruby, type "$rak pattern".'
11
+ p.description = p.paragraphs_of('README.txt', 2..5).join("\n\n")
12
+ p.url = p.paragraphs_of('README.txt', 0).first.split(/\n/)[1..-1]
13
+ p.changes = p.paragraphs_of('History.txt', 0..1).join("\n\n")
14
+ end
data/bin/rak ADDED
@@ -0,0 +1,797 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'getoptlong'
4
+
5
+ begin
6
+ require 'rubygems'
7
+ require 'oniguruma'
8
+ require 'inline'
9
+ $use_onig = true
10
+ rescue LoadError
11
+ $use_onig = false
12
+ end
13
+
14
+ $use_onig = false
15
+
16
+ class Rak
17
+ VERSION = "0.0.1"
18
+
19
+ FILE_COLOUR = "\033[1;31m"
20
+ MATCH_COLOUR = "\033[1;37m\033[41m"
21
+ CLEAR_COLOURS = "\033[0m"
22
+
23
+ VERSION_INFO=<<END
24
+ rak #{VERSION}
25
+
26
+ Copyright 2007 Daniel Lucraft, all rights reserved.
27
+ Based on the perl tool 'ack' by Andy Lester.
28
+
29
+ This program is free software; you can redistribute it and/or modify it
30
+ under the same terms as Ruby.
31
+ END
32
+
33
+ FILE_TYPES = {
34
+ :asm => %w( .s .S ),
35
+ # :binary => q{Binary files, as defined by Perl's -B op (default: off)},
36
+ :cc => %w( .c .h .xs ),
37
+ :cpp => %w( .cpp .m .h .C .H ),
38
+ :csharp => %w( .cs ),
39
+ :css => %w( .css ),
40
+ :elisp => %w( .el ),
41
+ :haskell => %w( .hs .lhs ),
42
+ :hh => %w( .h ),
43
+ :html => %w( .htm .html .shtml ),
44
+ # :skipped => q{Files, but not directories, normally skipped by ack (default: off)},
45
+ :lisp => %w( .lisp ),
46
+ :java => %w( .java properties ),
47
+ :js => %w( .js ),
48
+ :jsp => %w( .jsp .jspx .jhtm .jhtml ),
49
+ :make => %w( Makefile ),
50
+ :mason => %w( .mas .mhtml .mpl .mtxt ),
51
+ :ocaml => %w( .ml .mli ),
52
+ :parrot => %w( .pir .pasm .pmc .ops .pod .pg .tg ),
53
+ :perl => %w( .pl .pm .pod .t ),
54
+ :php => %w( .php .phpt .php3 .php4 .php5 ),
55
+ :prolog => %w( .pl .ecl ),
56
+ :python => %w( .py ),
57
+ :ruby => %w( .rb .rhtml .rjs .rxml Rakefile ),
58
+ :scheme => %w( .scm ),
59
+ :shell => %w( .sh .bash .csh .ksh .zsh ),
60
+ :sql => %w( .sql .ctl ),
61
+ :tcl => %w( .tcl ),
62
+ :tex => %w( .tex .cls .sty ),
63
+ :text => %w( .txt .text ),
64
+ :tt => %w( .tt .tt2 .ttml ),
65
+ :vb => %w( .bas .cls .frm .ctl .vb .resx ),
66
+ :vim => %w( .vim ),
67
+ :yaml => %w( .yaml .yml ),
68
+ :xml => %w( .xml .dtd .xslt )
69
+ }
70
+
71
+ VC_DIRS = %w(blib CVS _darcs .git .pc RCS SCCS .svn pkg)
72
+
73
+ class << self
74
+ attr_reader :opt
75
+ end
76
+
77
+ def self.search
78
+ @opt = {}
79
+
80
+ # file types
81
+ opt[:includes] = []
82
+ opt[:excludes] = []
83
+
84
+ FILE_TYPES.each do |type, exts|
85
+ if ARGV.delete('--'+type.to_s)
86
+ exts.each do |ext|
87
+ opt[:includes] << ext
88
+ end
89
+ end
90
+ if ARGV.delete('--no'+type.to_s)
91
+ exts.each do |ext|
92
+ opt[:excludes] << ext
93
+ end
94
+ end
95
+ end
96
+
97
+ opts = GetoptLong.new(
98
+ [ '--help', GetoptLong::OPTIONAL_ARGUMENT ],
99
+ [ '--max-count', '-m', GetoptLong::REQUIRED_ARGUMENT ],
100
+ [ '--files', '-f', GetoptLong::NO_ARGUMENT ],
101
+ [ '--output', GetoptLong::REQUIRED_ARGUMENT ],
102
+ [ '--version', GetoptLong::NO_ARGUMENT ],
103
+ [ '-c', '--count', GetoptLong::NO_ARGUMENT ],
104
+ [ '-h', '--no-filename', GetoptLong::NO_ARGUMENT ],
105
+ [ '-i', '--ignore-case', GetoptLong::NO_ARGUMENT ],
106
+ [ '-v', '--invert-match', GetoptLong::NO_ARGUMENT ],
107
+ [ '-n', GetoptLong::NO_ARGUMENT ],
108
+ [ '-Q', '--literal', GetoptLong::NO_ARGUMENT ],
109
+ [ '-o', GetoptLong::NO_ARGUMENT ],
110
+ [ '-w', '--word-regexp', GetoptLong::NO_ARGUMENT ],
111
+ [ '--group', GetoptLong::NO_ARGUMENT ],
112
+ [ '--nogroup', GetoptLong::NO_ARGUMENT ],
113
+ [ '-l', '--files-with-matches', GetoptLong::NO_ARGUMENT ],
114
+ [ '-L', '--files-without-matches', GetoptLong::NO_ARGUMENT ],
115
+ [ '--passthru', GetoptLong::NO_ARGUMENT ],
116
+ [ '-H', '--with-filename', GetoptLong::NO_ARGUMENT ],
117
+ [ '--colour', GetoptLong::NO_ARGUMENT ],
118
+ [ '--nocolour', GetoptLong::NO_ARGUMENT ],
119
+ [ '--color', GetoptLong::NO_ARGUMENT ],
120
+ [ '--nocolor', GetoptLong::NO_ARGUMENT ],
121
+ [ '-a', '--all', GetoptLong::NO_ARGUMENT ],
122
+ [ '--type', GetoptLong::REQUIRED_ARGUMENT ],
123
+ [ '--sort-files', GetoptLong::NO_ARGUMENT ],
124
+ [ '--follow', GetoptLong::NO_ARGUMENT ],
125
+ [ '--nofollow', GetoptLong::NO_ARGUMENT ],
126
+ [ '--after-context', '-A', GetoptLong::REQUIRED_ARGUMENT ],
127
+ [ '--before-context', '-B', GetoptLong::REQUIRED_ARGUMENT ],
128
+ [ '--context', '-C', GetoptLong::OPTIONAL_ARGUMENT ],
129
+ [ '-g', GetoptLong::REQUIRED_ARGUMENT ],
130
+ [ '-x', '--line-regexp', GetoptLong::NO_ARGUMENT ]
131
+ )
132
+
133
+ dir = nil
134
+
135
+ opt[:max_count] = nil
136
+ opt[:do_search] = true
137
+ opt[:print_filename] = true
138
+ opt[:print_line_number] = true
139
+ opt[:print_filelist] = false
140
+ opt[:print_output] = nil
141
+ opt[:print_highlighted] = true
142
+ opt[:print_num_matches] = false
143
+ opt[:ignore_case] = false
144
+ opt[:invert_match] = false
145
+ opt[:descend] = true
146
+ opt[:literal] = false
147
+ opt[:print_match] = false
148
+ opt[:match_whole_words] = false
149
+ opt[:match_whole_lines] = false
150
+ opt[:print_file_each_line] = false
151
+ opt[:print_file_if_match] = false
152
+ opt[:print_file_if_no_match] = false
153
+ opt[:print_entire_line_if_no_match] = false
154
+ opt[:colour] = true
155
+ opt[:all_files] = false
156
+ opt[:sort_files] = false
157
+ opt[:follow_symlinks] = false
158
+ opt[:after_context] = 0
159
+ opt[:before_context] = 0
160
+ opt[:collect_context] = false
161
+ opt[:filename_regex] = nil
162
+
163
+ # if redirected (RAK_TEST allows us to redirect in testing and still
164
+ # get the non-redirected defaults).
165
+ if STDOUT.isatty == false and ENV['RAK_TEST'] != "true"
166
+ opt[:colour] = false
167
+ opt[:print_file_each_line] = true
168
+ opt[:print_filename] = false
169
+ opt[:print_line_number] = false
170
+ end
171
+
172
+ begin
173
+ opts.each do |option, arg|
174
+ case option
175
+ when '--help'
176
+ if arg == ""
177
+ puts USAGE_HELP
178
+ elsif arg == "types"
179
+ puts TYPES_HELP
180
+ end
181
+ exit
182
+ when '--max-count'
183
+ opt[:max_count] = arg.to_i
184
+ when '--files'
185
+ opt[:do_search] = false
186
+ opt[:print_filelist] = true
187
+ when '--output'
188
+ opt[:print_filename] = false
189
+ opt[:print_line_number] = false
190
+ opt[:print_output] = arg
191
+ opt[:print_highlighted] = false
192
+ when '--version'
193
+ puts VERSION_INFO
194
+ exit
195
+ when '-c'
196
+ opt[:print_num_matches] = true
197
+ opt[:print_filename] = false
198
+ opt[:print_line_number] = false
199
+ opt[:print_highlighted] = false
200
+ when '-h'
201
+ opt[:print_filename] = false
202
+ opt[:print_line_number] = false
203
+ opt[:print_file_each_line] = false
204
+ when '-i'
205
+ opt[:ignore_case] = true
206
+ when '-v'
207
+ opt[:invert_match] = true
208
+ when '-n'
209
+ opt[:descend] = false
210
+ when '-Q'
211
+ opt[:literal] = true
212
+ when '-o'
213
+ opt[:print_match] = true
214
+ opt[:print_filename] = false
215
+ opt[:print_line_number] = false
216
+ opt[:print_highlighted] = false
217
+ when '-w'
218
+ opt[:match_whole_words] = true
219
+ when '--group'
220
+ opt[:print_filename] = true
221
+ opt[:print_file_each_line] = false
222
+ when '--nogroup'
223
+ opt[:print_file_each_line] = true
224
+ opt[:print_filename] = false
225
+ opt[:print_line_number] = false
226
+ when '-l'
227
+ opt[:print_filename] = false
228
+ opt[:print_line_number] = false
229
+ opt[:print_highlighted] = false
230
+ opt[:print_file_if_match] = true
231
+ when '-L'
232
+ opt[:print_filename] = false
233
+ opt[:print_line_number] = false
234
+ opt[:print_highlighted] = false
235
+ opt[:print_file_if_no_match] = true
236
+ when '--passthru'
237
+ opt[:print_entire_line_if_no_match] = true
238
+ when '-H'
239
+ opt[:print_filename] = true
240
+ opt[:print_line_number] = true
241
+ when '--nocolour', '--nocolor'
242
+ opt[:colour] = false
243
+ when '--colour', '--color'
244
+ opt[:colour] = true
245
+ when '-a'
246
+ opt[:all_files] = true
247
+ when '--type'
248
+ if arg[0..1] == "no"
249
+ type = arg[2..-1]
250
+ arr = opt[:excludes]
251
+ else
252
+ type = arg
253
+ arr = opt[:includes]
254
+ end
255
+ exts = FILE_TYPES[type.intern]
256
+ unknown_type(type) unless exts
257
+ exts.each do |ext|
258
+ arr << ext
259
+ end
260
+ when '--sort-files'
261
+ opt[:sort_files] = true
262
+ when '--follow'
263
+ opt[:follow_symlinks] = true
264
+ when '--nofollow'
265
+ opt[:follow_symlinks] = false
266
+ when '--after-context'
267
+ opt[:use_context] = true
268
+ opt[:after_context] = arg.to_i
269
+ when '--before-context'
270
+ opt[:use_context] = true
271
+ opt[:before_context] = arg.to_i
272
+ when '--context'
273
+ opt[:use_context] = true
274
+ if arg == ""
275
+ val = 2
276
+ else
277
+ val = arg.to_i
278
+ end
279
+ opt[:before_context] = val
280
+ opt[:after_context] = val
281
+ when '-g'
282
+ opt[:filename_regex] = arg
283
+ when '-x'
284
+ opt[:match_whole_lines] = true
285
+ end
286
+ end
287
+ rescue GetoptLong::InvalidOption => ex
288
+ puts "rak: see rak --help for usage."
289
+ exit
290
+ rescue SystemExit
291
+ exit
292
+ end
293
+
294
+ if ARGV.empty? and opt[:print_filelist] == false
295
+ puts USAGE_HELP
296
+ exit
297
+ end
298
+
299
+ files = get_files
300
+
301
+ compile_match_file
302
+
303
+ unless opt[:colour]
304
+ FILE_COLOUR.replace ""
305
+ CLEAR_COLOURS.replace ""
306
+ MATCH_COLOUR.replace ""
307
+ end
308
+
309
+ print_files(files) if opt[:print_filelist]
310
+ search2(ARGV[0], files) if opt[:do_search]
311
+ end
312
+
313
+ def self.print_files(files)
314
+ files.each do |fn|
315
+ puts fn
316
+ end
317
+ end
318
+
319
+ def self.get_all_files(files=[], dir="./")
320
+ Dir[dir+"*"].each do |fn|
321
+ if File.directory? fn
322
+ if opt[:descend]
323
+ if File.symlink? fn
324
+ if opt[:follow_symlinks]
325
+ get_all_files(files, fn+"/")
326
+ end
327
+ else
328
+ get_all_files(files, fn+"/")
329
+ end
330
+ end
331
+ else
332
+ if File.symlink? fn
333
+ if opt[:follow_symlinks]
334
+ files << fn
335
+ end
336
+ else
337
+ files << fn
338
+ end
339
+ end
340
+ end
341
+ files
342
+ end
343
+
344
+ def self.get_files(dir="./")
345
+ types_files = FILE_TYPES.invert
346
+ types = types_files.keys.flatten
347
+ files_and_dirs = ARGV[1..-1]
348
+ io_files = []
349
+ if !files_and_dirs or files_and_dirs.empty?
350
+ if STDIN.isatty == false
351
+ io_files << STDIN
352
+ opt[:print_filename] = false
353
+ files = []
354
+ else
355
+ files = get_all_files
356
+ end
357
+ else
358
+ files = []
359
+ files_and_dirs.each do |thing|
360
+ if File.file? thing
361
+ files << thing
362
+ end
363
+ if File.directory? thing
364
+ get_all_files(files, thing)
365
+ end
366
+ end
367
+ if files_and_dirs.length == 1 and
368
+ File.file? files_and_dirs[0]
369
+ opt[:print_filename] = false
370
+ end
371
+ end
372
+
373
+ # strip out based on extension
374
+ shebang_files = []
375
+ unless opt[:all_files]
376
+ files = files.select do |fn|
377
+ fn[-1..-1] != "~" and
378
+ fn[-1..-1] != "#"
379
+ end
380
+ new_files = []
381
+ files.each do |fn|
382
+ if types.any? {|ext| File.extname(fn) == ext or fn.split("/").last == ext}
383
+ new_files << fn
384
+ elsif ext = shebang_matches(fn)
385
+ shebang_files << [fn, ext]
386
+ end
387
+ end
388
+ files = new_files
389
+ end
390
+
391
+ # select based on file type
392
+ if not opt[:includes].empty?
393
+ files = files.select do |fn|
394
+ opt[:includes].any? {|ext| fn.include? ext}
395
+ end
396
+ shebang_files2 = []
397
+ shebang_files.each do |fn, ext|
398
+ if opt[:includes].include? ext
399
+ shebang_files2 << [fn, ext]
400
+ end
401
+ end
402
+ else
403
+ shebang_files2 = shebang_files
404
+ end
405
+
406
+ if not opt[:excludes].empty?
407
+ files = files.select do |fn|
408
+ not opt[:excludes].any? {|ext| fn.include? ext}
409
+ end
410
+ shebang_files3 = []
411
+ shebang_files2.each do |fn, ext|
412
+ unless opt[:excludes].any? {|ext| fn.include? ext}
413
+ shebang_files3 << [fn, ext]
414
+ end
415
+ end
416
+ else
417
+ shebang_files3 = shebang_files2
418
+ end
419
+
420
+ files += shebang_files3.map {|fn, ext| fn}
421
+
422
+ # reject version control dirs
423
+ files.reject! do |fn|
424
+ VC_DIRS.any? {|vc| fn.include? vc}
425
+ end
426
+
427
+ # filter based on -g=REGEX
428
+ if opt[:filename_regex]
429
+ if $use_onig
430
+ fn_re = Oniguruma::ORegexp.new(opt[:filename_regex])
431
+ else
432
+ fn_re = Regexp.new(opt[:filename_regex])
433
+ end
434
+ files = files.select {|fn| fn =~ fn_re }
435
+ end
436
+
437
+ # remove the "./"
438
+ files.map! {|fn| fn[0..1] == "./" ? fn[2..-1] : fn}
439
+
440
+ if opt[:sort_files]
441
+ files = files.sort_by {|fn| fn.downcase}
442
+ end
443
+
444
+ files + io_files
445
+ end
446
+
447
+ def self.shebang_matches(fn)
448
+ File.open(fn) do |f|
449
+ begin
450
+ line = f.readline
451
+ rescue
452
+ line = ""
453
+ end
454
+ if line =~ /^#!/
455
+ if line =~ /\b(ruby|perl|php|python)\b/
456
+ return "."+FILE_TYPES[$1.intern].first
457
+ elsif line =~ /\b(bash|csh|ksh|zsh)\b/
458
+ return ".sh"
459
+ end
460
+ else
461
+ if line =~ /\Q<?xml /
462
+ return ".xml"
463
+ end
464
+ end
465
+ return false
466
+ end
467
+ end
468
+
469
+ def self.search2(str, files)
470
+ if opt[:literal]
471
+ str = Regexp.quote(str)
472
+ end
473
+ if opt[:match_whole_words]
474
+ str = "\\b" + str + "\\b"
475
+ end
476
+ if opt[:match_whole_lines]
477
+ str = "^" + str + "$"
478
+ end
479
+ if $use_onig
480
+ if opt[:ignore_case]
481
+ re = Oniguruma::ORegexp.new(str, :options => Oniguruma::OPTION_IGNORECASE)
482
+ else
483
+ re = Oniguruma::ORegexp.new(str)
484
+ end
485
+ else
486
+ if opt[:ignore_case]
487
+ re = Regexp.new(str, Regexp::IGNORECASE)
488
+ else
489
+ re = Regexp.new(str)
490
+ end
491
+ end
492
+ files.each do |fn|
493
+ match_file(re, fn)
494
+ end
495
+ end
496
+
497
+ # this is tricky thing. Ignore the "code <<" to see the
498
+ # logic. Because the options never change we conditionally compile
499
+ # the match method based on them. This is gives a 2x speedup over
500
+ # the alternative.
501
+ def self.compile_match_file
502
+ code = ""
503
+ def code.<<(str)
504
+ self.concat(str + "\n")
505
+ end
506
+ code << %{def self.match_file(re, fn) }
507
+ code << %{ displayed_filename = false }
508
+ code << %{ count = 0 }
509
+ code << %{ i = 0 }
510
+ code << %{ matches = [] }
511
+ code << %{ print_num = 0 }
512
+
513
+ if opt[:before_context] > 0
514
+ code << %{ before_context = [] }
515
+ code << %{ before_context_size = opt[:before_context] }
516
+ end
517
+ code << %{ if fn.is_a? String }
518
+ code << %{ f = File.open(fn, "r") }
519
+ code << %{ elsif fn.is_a? IO }
520
+ code << %{ f = fn }
521
+ code << %{ end }
522
+
523
+ code << %{ f.each_line do |line| }
524
+ code << %{ i += 1 }
525
+ code << %{ rest = line }
526
+ code << %{ while md = re.match(rest) }
527
+ code << %{ rest = md.post_match }
528
+ if opt[:print_output]
529
+ code << %{ matches << eval(opt[:print_output]) }
530
+ else
531
+ code << %{ matches << md }
532
+ end
533
+ if opt[:print_match]
534
+ code << %{ puts md.to_s }
535
+ end
536
+ code << %{ count += 1 }
537
+ code << %{ end }
538
+ if opt[:invert_match]
539
+ if opt[:print_filename]
540
+ code << %{ unless displayed_filename }
541
+ code << %{ puts FILE_COLOUR + fn + CLEAR_COLOURS }
542
+ code << %{ displayed_filename = true }
543
+ code << %{ end }
544
+ end
545
+ code << %{ if matches.empty? }
546
+ if opt[:print_highlighted]
547
+ if opt[:print_line_number]
548
+ code << %{ print "\#{i.to_s.rjust(4)}|" }
549
+ end
550
+ code << %{ puts "\#{line}" }
551
+ end
552
+ code << %{ end }
553
+ code << %{ matches.clear }
554
+ else
555
+ code << %{ if matches.empty? }
556
+ if opt[:print_entire_line_if_no_match]
557
+ code << %{ puts line }
558
+ end
559
+ if opt[:use_context]
560
+ if opt[:before_context] > 0
561
+ code << %{ if print_num == 0 }
562
+ code << %{ before_context << [i, line] }
563
+ code << %{ if before_context.length == before_context_size+1 }
564
+ code << %{ before_context = before_context[1..-1] }
565
+ code << %{ end }
566
+ code << %{ end }
567
+ end
568
+ code << %{ if print_num > 0 }
569
+ code << %{ print_num -= 1 }
570
+ if opt[:print_highlighted]
571
+ if opt[:print_line_number]
572
+ code << %{ print "\#{i.to_s.rjust(4)}|" }
573
+ end
574
+ code << %{ puts "\#{line}" }
575
+ code << %{ end }
576
+ end
577
+ end
578
+ code << %{ else }
579
+ code << %{ print_num = opt[:after_context] }
580
+ if opt[:print_filename]
581
+ code << %{ unless displayed_filename }
582
+ code << %{ puts FILE_COLOUR + fn + CLEAR_COLOURS }
583
+ code << %{ displayed_filename = true }
584
+ code << %{ end }
585
+ end
586
+ if opt[:before_context] > 0
587
+ code << %{ before_context.each do |before_i, before_line| }
588
+ if opt[:print_line_number]
589
+ code << %{ print "\#{before_i.to_s.rjust(4)}|" }
590
+ end
591
+ code << %{ puts before_line }
592
+ code << %{ end }
593
+ code << %{ before_context = [] }
594
+ end
595
+ if opt[:print_output]
596
+ code << %{ matches.each {|m| puts m} }
597
+ end
598
+ if opt[:print_highlighted]
599
+ if opt[:print_line_number]
600
+ code << %{ prefix = "\#{i.to_s.rjust(4)}|" }
601
+ else
602
+ if opt[:print_file_each_line]
603
+ code << %{ fn_str = (fn.is_a?(String) ? FILE_COLOUR + fn + CLEAR_COLOURS + " " : "") }
604
+ code << %{ prefix = fn_str + "\#{i.to_s.rjust(4)}|" }
605
+ else
606
+ code << %{ prefix = "" }
607
+ end
608
+ end
609
+ code << %{ print_highlighted(prefix, line, matches) }
610
+ end
611
+ code << %{ end }
612
+ if opt[:max_count]
613
+ code << %{ if count >= opt[:max_count] }
614
+ code << %{ break }
615
+ code << %{ end }
616
+ end
617
+ code << %{ matches.clear }
618
+ end
619
+ code << %{ end }
620
+ if opt[:print_highlighted] and opt[:print_filename]
621
+ code << %{ if count > 0 }
622
+ code << %{ puts }
623
+ code << %{ end }
624
+ end
625
+ code << %{ f.close if f === File }
626
+ if opt[:print_num_matches]
627
+ if opt[:invert_match]
628
+ code << %{ puts "\#{fn}:\#{i-count}" }
629
+ else
630
+ code << %{ puts "\#{fn}:\#{count}" }
631
+ end
632
+ end
633
+ if opt[:print_file_if_match]
634
+ code << %{ if count > 0 }
635
+ code << %{ puts fn }
636
+ code << %{ end }
637
+ end
638
+
639
+ if opt[:print_file_if_no_match]
640
+ code << %{ if count == 0 }
641
+ code << %{ puts fn }
642
+ code << %{ end }
643
+ end
644
+ code << %{end }
645
+ # if ENV['RACK_TEST'] == "true"
646
+ # File.open("compiled_method.rb", "w") {|f| f.puts code}
647
+ # end
648
+ module_eval code
649
+ end
650
+
651
+ def self.print_highlighted(prefix, line, matches)
652
+ from1 = matches[0].begin(0)
653
+ pre = ""
654
+ pre = line[0..(from1-1)] if from1 > 0
655
+ print prefix
656
+ print pre
657
+ ptr = 0
658
+ 0.upto(matches.length-1) do |n|
659
+ from = matches[n].begin(0)
660
+ to = matches[n].end(0)
661
+ print MATCH_COLOUR +
662
+ line[(from+ptr)..(to+ptr-1)] +
663
+ CLEAR_COLOURS
664
+ ptr += to
665
+ if n == matches.length - 1
666
+ puts matches.last.post_match
667
+ else
668
+ print line[ptr..(matches[n+1].begin(0)-1+ptr)]
669
+ end
670
+ end
671
+ end
672
+
673
+ def self.unknown_type(type)
674
+ puts "rak: Unknown --type \"#{type}\""
675
+ puts "rak: See rak --help types"
676
+ exit
677
+ end
678
+ end
679
+
680
+ USAGE_HELP=<<END
681
+ Usage: rak [OPTION]... PATTERN [FILES]
682
+
683
+ Search for PATTERN in each source file in the tree from cwd on down.
684
+ If [FILES] is specified, then only those files/directories are checked.
685
+ rak will search STDIN only if no FILES are specified.
686
+
687
+ Example: rak -i select
688
+
689
+ Searching:
690
+ -i, --ignore-case Ignore case distinctions
691
+ -v, --invert-match Invert match: select non-matching lines
692
+ -w, --word-regexp Force PATTERN to match only whole words
693
+ -x, --line-regexp Force PATTERN to match only whole lines
694
+ -Q, --literal Quote all metacharacters; expr is literal
695
+
696
+ Search output:
697
+ -l, --files-with-matches
698
+ Only print filenames containing matches
699
+ -L, --files-without-match
700
+ Only print filenames with no match
701
+ -o Show only the part of a line matching PATTERN
702
+ (turns off text highlighting)
703
+ --passthru Print all lines, whether matching or not
704
+ --output=expr Output the evaluation of expr for each line
705
+ (turns off text highlighting)
706
+ -m, --max-count=NUM Stop searching in a file after NUM matches
707
+ -H, --with-filename Print the filename for each match
708
+ -h, --no-filename Suppress the prefixing filename on output
709
+ -c, --count Show number of lines matching per file
710
+
711
+ --group Group matches by file name.
712
+ (default: on when used interactively)
713
+ --nogroup One result per line, including filename, like grep
714
+ (default: on when the output is redirected)
715
+
716
+ --[no]colour Highlight the matching text (default: on unless
717
+ output is redirected, or on Windows)
718
+
719
+ -A NUM, --after-context=NUM
720
+ Print NUM lines of trailing context after matching
721
+ lines.
722
+ -B NUM, --before-context=NUM
723
+ Print NUM lines of leading context before matching
724
+ lines.
725
+ -C [NUM], --context[=NUM]
726
+ Print NUM lines (default 2) of output context.
727
+
728
+ File finding:
729
+ -f Only print the files found, without searching.
730
+ The PATTERN must not be specified.
731
+ --sort-files Sort the found files lexically.
732
+
733
+ File inclusion/exclusion:
734
+ -n No descending into subdirectories
735
+ -g REGEX Only search in file matching REGEX.
736
+ -a, --all All files, regardless of extension (but still skips
737
+ blib, pkg, CVS, _darcs, .git, .pc, RCS, SCCS and .svn dirs)
738
+ --ruby Include only Ruby files.
739
+ --type=ruby Include only Ruby files.
740
+ --noruby Exclude Ruby files.
741
+ --type=noruby Exclude Ruby files.
742
+ See "rak --help type" for supported filetypes.
743
+ --[no]follow Follow symlinks. Default is off.
744
+
745
+ Miscellaneous:
746
+ --help This help
747
+ --version Display version & copyright
748
+ END
749
+
750
+ TYPES_HELP=<<END
751
+ Usage: rak [OPTION]... PATTERN [FILES]
752
+
753
+ The following is the list of filetypes supported by rak. You can
754
+ specify a file type with the --type=TYPE format, or the --TYPE
755
+ format. For example, both --type=ruby and --ruby work.
756
+
757
+ Note that some extensions may appear in multiple types. For example,
758
+ .pod files are both Perl and Parrot.
759
+
760
+ --[no]asm .s
761
+ --[no]cc .c .h .xs
762
+ --[no]cpp .cpp .m .h
763
+ --[no]csharp .cs
764
+ --[no]css .css
765
+ --[no]elisp .el
766
+ --[no]erlang .erl
767
+ --[no]fortran .f .f77 .f90 .f95 .f03 .for .ftn .fpp
768
+ --[no]haskell .hs .lhs
769
+ --[no]hh .h
770
+ --[no]html .htm .html .shtml
771
+ --[no]java .java .properties
772
+ --[no]js .js
773
+ --[no]jsp .jsp .jspx .jhtm .jhtml
774
+ --[no]lisp .lisp
775
+ --[no]make Makefile
776
+ --[no]mason .mas .mhtml .mpl .mtxt
777
+ --[no]ocaml .ml .mli
778
+ --[no]parrot .pir .pasm .pmc .ops .pod .pg .tg
779
+ --[no]perl .pl .pm .pod .t
780
+ --[no]php .php .phpt .php3 .php4 .php5
781
+ --[no]prolog .pl .ecl
782
+ --[no]python .py
783
+ --[no]ruby .rb .rhtml .rjs .rxml Rakefile
784
+ --[no]scheme .scm
785
+ --[no]shell .sh .bash .csh .ksh .zsh
786
+ --[no]sql .sql .ctl
787
+ --[no]tcl .tcl
788
+ --[no]tex .tex .cls .sty
789
+ --[no]text .txt .text
790
+ --[no]tt .tt .tt2 .ttml
791
+ --[no]vb .bas .cls .frm .ctl .vb .resx
792
+ --[no]vim .vim
793
+ --[no]xml .xml .dtd .xslt
794
+ --[no]yaml .yaml .yml
795
+ END
796
+
797
+ Rak.search