class Utils::Grepper
grepper.search
grepper = Utils::Grepper.new(args: { l: true }, roots: [‘.’])
@example
types of pattern matching including regular expressions and fuzzy matching.
filtering options. It handles directory pruning, file skipping, and different
matching specified patterns, with support for various output formats and
This class provides functionality to search through file systems for content
A class for searching and matching text patterns within files.
def discover_roots(roots)
-
(Array
- an array of expanded directory paths matching the input patterns)
Parameters:
-
roots
(Array
) -- an array of root patterns or nil, nil
def discover_roots(roots) roots ||= [] roots.inject([]) { |rs, r| rs.concat Dir[r] } end
def initialize(opts = {})
-
(Utils::Grepper)
- a new grepper instance configured with the
Options Hash:
(**opts)
-
:pattern
(Hash
) -- the pattern-related options -
:config
(Utils::ConfigFile
) -- the configuration file object -
:roots
(Array
) -- the root directories to search -
:args
(Hash
) -- the command-line arguments
Parameters:
-
opts
(Hash
) -- the options hash containing configuration settings
def initialize(opts = {}) @args = opts[:args] || {} @roots = discover_roots(opts[:roots]) @config = opts[:config] || Utils::ConfigFile.new if n = @args.values_at(*%w[A B C]).compact.first if n.to_s =~ /\A\d+\Z/ and (n = n.to_i) >= 1 @queue = Queue.new n else raise ArgumentError, "needs to be an integer number >= 1" end end @paths = [] pattern_opts = opts.subhash(:pattern) | { :cset => @args[?a], :icase => @args[?i] != ?n, } @pattern = choose(@args[?p], pattern_opts, default: ?r) @name_pattern = if name_pattern = @args[?N] RegexpPattern.new(:pattern => name_pattern) elsif name_pattern = @args[?n] FuzzyPattern.new(:pattern => name_pattern) end @skip_pattern = if skip_pattern = @args[?S] RegexpPattern.new(:pattern => skip_pattern) elsif skip_pattern = @args[?s] FuzzyPattern.new(:pattern => skip_pattern) end end
def match(filename)
-
(Utils::Grepper)
- returns self to allow for method chaining
def match(filename) @filename = filename @output = [] bn, s = File.basename(filename), File.stat(filename) if !s || s.directory? && @config.search.prune?(bn) @args[?v] and warn "Pruning #{filename.inspect}." prune end if s.file? && !@config.search.skip?(bn) && (!@name_pattern || @name_pattern.match(bn)) then File.open(filename, 'rb', encoding: Encoding::UTF_8) do |file| @args[?v] and warn "Matching #{filename.inspect}." if @args[?f] @output << filename else match_lines file end end else @args[?v] and warn "Skipping #{filename.inspect}." end unless @output.empty? case when @args[?g] @output.uniq! @output.each do |l| blamer = LineBlamer.for_line(l) if blame = blamer.perform author = nil blame.sub!(/^[0-9a-f^]+/) { Term::ANSIColor.yellow($&) } blame.sub!(/\(([^)]+)\)/) { author = $1; "(#{Term::ANSIColor.red($1)})" } if !@args[?G] || author&.downcase&.match?(@args[?G].downcase) puts "#{blame.chomp} #{Term::ANSIColor.blue(l)}" end end end when @args[?l], @args[?e], @args[?E], @args[?r] @output.uniq! @paths.concat @output else STDOUT.puts @output end @output.clear end self end
def match_lines(file)
-
file
(IO
) -- the file object to be processed line by line
def match_lines(file) for line in file if m = @pattern.match(line) @skip_pattern and @skip_pattern =~ line and next line[m.begin(0)...m.end(0)] = black on_white m[0] @queue and @queue << line case when @args[?l] @output << @filename when @args[?L], @args[?r], @args[?g] @output << "#{@filename}:#{file.lineno}" when @args[?e], @args[?E] @output << "#{@filename}:#{file.lineno}" break else @output << red("#{@filename}:#{file.lineno}") if @args[?B] or @args[?C] @output.concat @queue.data else @output << line end if @args[?A] or @args[?C] where = file.tell lineno = file.lineno @queue.max_size.times do file.eof? and break line = file.readline @queue << line @output << line end file.seek where file.lineno = lineno end end else @queue and @queue << line end end end
def search
-
(Utils::Grepper)
- returns self to allow for method chaining
def search suffixes = Array(@args[?I]) visit = -> filename { s = filename.lstat bn = filename.pathname.basename if !s || s.directory? && @config.search.prune?(bn) || (s.file? || s.symlink?) && @config.search.skip?(bn) || @args[?F] && s.symlink? then @args[?v] and warn "Pruning #{filename.inspect}." prune elsif suffixes.empty? true else suffixes.include?(filename.suffix) end } find(*@roots, visit: visit) do |filename| match(filename) end @paths = @paths.sort_by(&:source_location) self end