#!/bin/bash

#This script lets a user select a file to open by typing part of its
#name, kind of like Quicksilver (only lighter-weight).  It's meant to
#be invoked by a shortcut key such as Windows-space.
#
#Depends on:
# dmenu
# locate / updatedb
# xdg-open

#Set this variable to the directory you want to open files in
DIRECTORY_TO_INCLUDE="$HOME"
TEMP_FILE="/tmp/filenames"
TEMP_DB="/tmp/files_db"
TEMP_RESULT="/tmp/result.txt"

make_files_db() {
    #Create the database
    #Unforunately there seems to be no good way to use updatedb to create just a single database with files from
    #multiple different directories.
    #There are 2 versions of updatedb, and GNU locate has an extra option --localpaths which lets you specify
    #multiple paths with one command.  mlocate, the version I'm using, doesn't.
    #However, mlocate is apparently newer, and has the improvement that it reuses the old database
    #to save time, and apparently if you install both then they'll both build their databases in the background.
    updatedb -l 0 -U "$DIRECTORY_TO_INCLUDE" -o "$TEMP_DB" --prune-bind-mounts no

    #Save only the filenames, not the full path, in the temporary file (see below for why).
    #Skip hidden files.
    #The "while read line" trick is like a for loop, but iterates over lines instead of space-delimited
    #items.  I found the trick here:
    #http://ubergibson.com/article/iterating-through-lines-in-the-bash-shell
    locate "$DIRECTORY_TO_INCLUDE" -d "$TEMP_DB" | grep -v "/\." | sed 's/.*\///'  > "$TEMP_FILE"
}

if [ "$1" == "-r" ]
then
    #User selected to rebuild the temporary file
    make_files_db
    echo "Temporary file rebuilt"
    exit 0
elif [ ! -f "$TEMP_FILE" ]
then
    echo "Building temporary file... (Use $0 -r to rebuild it - it's recommended to set this to happen regularly, e.g. daily or when your computer starts)"
    make_files_db
fi

#Select a file to edit.
#The selection menu only displays the file's name here, not
#the full path.
#The reason for that is that otherwise there would be too many choices matching the letters you type -
#if the letters match a directory name, then all the files in that directory would also be included in the choices.
chosen_file=`cat "$TEMP_FILE" | dmenu -i`

open_file() {
    #For many programs there's a benefit to setting the current directory to the directory of the file
    cd `dirname $1`

    #Open the file according to how they configured that type of file in their desktop environment
    xdg-open "$1"
}

if [ -n "$chosen_file" ]  #If a file was selected
then
	#Prepend a / to increase the chances of matching only one file.
    #The first grep is for directories, and ensures that only the directory itself is matched and not things in the directory.
    #The second grep excludes hidden files.
    locate -d "$TEMP_DB" /$chosen_file | grep "$chosen_file$" | grep -v "/\." > "$TEMP_RESULT"

	#Count how many files have the filename that was selected.
	lines=`cat "$TEMP_RESULT" | wc -l`

	if [ "1" = "$lines" ]
	then
		#If there's exactly one file of that name, edit it.

		#Get the full path of the file
		full_file=`cat "$TEMP_RESULT"`
	
		open_file "$full_file"
	elif [ "0" = "$lines" ]
	then
		echo "Not found"
	else
		#There were multiple files that share the same filename.
		#Ask the user which file they want to edit by displaying the full path
		#this time.
		full_file=`cat "$TEMP_RESULT" | dmenu -i -p "Which $chosen_file?"`

		if [ -n "$full_file" ]
		then
			open_file "$full_file"
		fi
	fi
fi

