Sublime Text like fuzzy filesystem search for Mac OS X

Currently I am using Alfred's filesystem navigation capabilities. It's pretty awesome, but sometimes I want fuzzy-search to match not only the filename but the full path.

E.g. I want to find a pdf file with some very common filename (say proposal.pdf). And I have a directory structure like this:

 - dropbox
   - partners
     - supercorp
       - proposal.pdf
     - megacorp
       - proposal.pdf

It would be nice if I can type dro/meg/propos to get to the megacorp's proposal file. The answer may be the Alfred plugin or some configuration, but I'll accept any solution that allows me to quickly navigate to this file and perform some action (e.g. reveal in Finder or something). "Search-as-you-type" functionality is very important.

UPD Just found a similar question. Seems that full path matching feature is missing because mdfind is only searching the file name and all the tools are using it internally.


Solution 1:

You may try Findspot. Findspot supports these features

  1. Fuzzy search like Sublime Text's Control-P
  2. Full path search
  3. Search as you type

Here is a screenshot of Findspot when using your example:

enter image description here

Actually, you can skip the slashes and you will still get the same result.

Solution 2:

Alfred ships with an example workflow called Dynamic File Search that does something similar to fuzzy searching:

https://www.alfredapp.com/blog/tips-and-tricks/how-to-get-the-results-you-want-in-alfred-every-time/

https://www.alfredforum.com/topic/11981-searching-in-files-in-a-specific-location/?do=findComment&comment=62957

Alfred searching through directories and filenames at the same time

To add it, click the [+] at the bottom of the Workflows preferences, and choose Examples > Dynamic File Search.

  1. Type "ff" in Alfred to first select a search scope
  2. Then type the name of the file you're searching for within that folder

More documentation on Alfred's file filter feature can be found here https://www.alfredapp.com/help/workflows/inputs/file-filter/

Solution 3:

There is a kMDItemPath attribute, but it can't be used in queries. You can grep the output of mdfind though:

$ pp() { path="/${1%/*}/"; mdfind "name:${1##*/}" | grep -i "${path//\//.*\/}"; }
$ time pp desk/ante
/Library/Desktop Pictures/Antelope Canyon.jpg
0.365

Matching kMDItemFSName is often a lot slower:

$ time mdfind "kMDItemFSName=\"ante.*\"c" | grep -i '/desk.*/'
/Library/Desktop Pictures/Antelope Canyon.jpg
10.232

I tried creating a script filter like this in Alfred:

q="{query}"

shopt -s nocasematch

amp() {
  local o=${1//&/&}
  o=${o//</&lt;}
  printf %s "${o//>/&gt;}"
}

output='<?xml version="1.0"?>
<items>
'

while IFS= read -r l; do
  path=$(amp "$l")
  output+="<item>
<arg>$path</arg>
<title>$(amp "${l##*/}")</title>
<subtitle>$path</subtitle>
<icon type=\"fileicon\">$path</icon>
</item>
"
done < <(if [[ $q =~ .+/.+ ]]; then
  dir=${q%/*}
  mdfind "name:${q##*/}" | while IFS= read -r l; do
    [[ ${l%/*} = */${dir//\/*/}* ]] && echo "$l"
  done
else
  mdfind "kind:folder name:$q"
fi | head -n20)

echo "$output</items>
</xml>"

I couldn't get it to work relibaly though, and it often took multiple seconds to update the results.