Class: FuzzyFileFinder
- Defined in:
- lib/diakonos/vendor/fuzzy_file_finder.rb
Overview
The “fuzzy” file finder provides a way for searching a directory tree with only a partial name. This is similar to the “cmd-T” feature in TextMate (macromates.com).
Usage:
finder = FuzzyFileFinder.new
finder.search("app/blogcon") do |match|
puts match[:highlighted_path]
end
In the above example, all files matching “app/blogcon” will be yielded to the block. The given pattern is reduced to a regular expression internally, so that any file that contains those characters in that order (even if there are other characters in between) will match.
In other words, “app/blogcon” would match any of the following (parenthesized strings indicate how the match was made):
-
(app)/controllers/(blog)_(con)troller.rb
-
lib/c(ap)_(p)ool/(bl)ue_(o)r_(g)reen_(co)loratio(n)
-
test/(app)/(blog)_(con)troller_test.rb
And so forth.
Defined Under Namespace
Classes: CharacterRun, Directory, FileSystemEntry, TooManyEntries
Instance Attribute Summary collapse
-
#ceiling ⇒ Object
readonly
The maximum number of files beneath all
roots
. -
#files ⇒ Object
readonly
The list of files beneath all
roots
. -
#ignores ⇒ Object
readonly
The list of glob patterns to ignore.
-
#roots ⇒ Object
readonly
The roots directory trees to search.
-
#shared_prefix ⇒ Object
readonly
The prefix shared by all
roots
.
Instance Method Summary collapse
-
#find(pattern, max = nil) ⇒ Object
Takes the given
pattern
(which must be a string, formatted as described in #search), and returns up tomax
matches in an Array. -
#initialize(params = {}) ⇒ FuzzyFileFinder
constructor
Initializes a new FuzzyFileFinder.
-
#inspect ⇒ Object
Displays the finder object in a sane, non-explosive manner.
-
#rescan! ⇒ Object
Rescans the subtree.
-
#search(pattern, &block) ⇒ Object
Takes the given
pattern
(which must be a string) and searches all files beneathroot
, yielding each match.
Constructor Details
#initialize(params = {}) ⇒ FuzzyFileFinder
Initializes a new FuzzyFileFinder. This will scan the given directories
, using ceiling
as the maximum number of entries to scan. If there are more than ceiling
entries a TooManyEntries exception will be raised.
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 130 131 132 133 134 135 |
# File 'lib/diakonos/vendor/fuzzy_file_finder.rb', line 105 def initialize( params = {} ) @ceiling = params[:ceiling] || 10_000 @ignores = Array(params[:ignores]) if params[:directories] directories = Array(params[:directories]) directories << "." if directories.empty? else directories = ['.'] end @recursive = params[:recursive].nil? ? true : params[:recursive] # expand any paths with ~ root_dirnames = directories.map { |d| File.realpath(d) }.select { |d| File.directory?(d) }.uniq @roots = root_dirnames.map { |d| Directory.new(d, true) } @shared_prefix = determine_shared_prefix @shared_prefix_re = Regexp.new("^#{Regexp.escape(shared_prefix)}" + (shared_prefix.empty? ? "" : "/")) @sorted = params[:sorted] @files = [] @directories = {} # To detect link cycles @dirs_with_many = [] rescan! end |
Instance Attribute Details
#ceiling ⇒ Object (readonly)
The maximum number of files beneath all roots
93 94 95 |
# File 'lib/diakonos/vendor/fuzzy_file_finder.rb', line 93 def ceiling @ceiling end |
#files ⇒ Object (readonly)
The list of files beneath all roots
90 91 92 |
# File 'lib/diakonos/vendor/fuzzy_file_finder.rb', line 90 def files @files end |
#ignores ⇒ Object (readonly)
The list of glob patterns to ignore.
99 100 101 |
# File 'lib/diakonos/vendor/fuzzy_file_finder.rb', line 99 def ignores @ignores end |
#roots ⇒ Object (readonly)
The roots directory trees to search.
87 88 89 |
# File 'lib/diakonos/vendor/fuzzy_file_finder.rb', line 87 def roots @roots end |
#shared_prefix ⇒ Object (readonly)
The prefix shared by all roots
.
96 97 98 |
# File 'lib/diakonos/vendor/fuzzy_file_finder.rb', line 96 def shared_prefix @shared_prefix end |
Instance Method Details
#find(pattern, max = nil) ⇒ Object
Takes the given pattern
(which must be a string, formatted as described in #search), and returns up to max
matches in an Array. If max
is nil, all matches will be returned.
204 205 206 207 208 209 210 211 212 213 214 215 216 217 |
# File 'lib/diakonos/vendor/fuzzy_file_finder.rb', line 204 def find(pattern, max=nil) results = [] search(pattern) do |match| results << match break if max && results.length >= max end if @sorted results.sort_by { |m| m[:path] } else results end end |
#inspect ⇒ Object
Displays the finder object in a sane, non-explosive manner.
220 221 222 |
# File 'lib/diakonos/vendor/fuzzy_file_finder.rb', line 220 def inspect #:nodoc: "#<%s:0x%x roots=%s, files=%d>" % [self.class.name, object_id, roots.map { |r| r.name.inspect }.join(", "), files.length] end |
#rescan! ⇒ Object
Rescans the subtree. If the directory contents every change, you’ll need to call this to force the finder to be aware of the changes.
140 141 142 143 |
# File 'lib/diakonos/vendor/fuzzy_file_finder.rb', line 140 def rescan! @files.clear roots.each { |root| follow_tree(root) } end |
#search(pattern, &block) ⇒ Object
Takes the given pattern
(which must be a string) and searches all files beneath root
, yielding each match.
pattern
is interpreted thus:
-
“foo” : look for any file with the characters ‘f’, ‘o’, and ‘o’ in its basename (discounting directory names). The characters must be in that order.
-
“foo/bar” : look for any file with the characters ‘b’, ‘a’, and ‘r’ in its basename (discounting directory names). Also, any successful match must also have at least one directory element matching the characters ‘f’, ‘o’, and ‘o’ (in that order.
-
“foo/bar/baz” : same as “foo/bar”, but matching two directory elements in addition to a file name of “baz”.
Each yielded match will be a hash containing the following keys:
-
:path refers to the full path to the file
-
:directory refers to the directory of the file
-
:name refers to the name of the file (without directory)
-
:highlighted_directory refers to the directory of the file with matches highlighted in parentheses.
-
:highlighted_name refers to the name of the file with matches highlighted in parentheses
-
:highlighted_path refers to the full path of the file with matches highlighted in parentheses
-
:abbr refers to an abbreviated form of :highlighted_path, where path segments without matches are compressed to just their first character.
-
:score refers to a value between 0 and 1 indicating how closely the file matches the given pattern. A score of 1 means the pattern matches the file exactly.
178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 |
# File 'lib/diakonos/vendor/fuzzy_file_finder.rb', line 178 def search(pattern, &block) path_parts = pattern.strip.split("/") path_parts.push "" if pattern[-1,1] == "/" file_name_part = path_parts.pop || "" if path_parts.any? path_regex_raw = "^(.*?)" + path_parts.map { |part| make_pattern(part) }.join("(.*?/.*?)") + "(.*?)$" path_regex = Regexp.new(path_regex_raw, Regexp::IGNORECASE) end file_regex_raw = "^(.*?)" << make_pattern(file_name_part) << "(.*)$" file_regex = Regexp.new(file_regex_raw, Regexp::IGNORECASE) path_matches = {} files.each do |file| path_match = match_path(file.parent, path_matches, path_regex, path_parts.length) next if path_match[:missed] match_file(file, file_regex, path_match, &block) end end |