(Day 11 of 30 Days of Blogging)
If you don’t already use Z you absolutely need to check it out. Z is a
CLI tool that cd’s to a recently used directory, so that typing z foo
will cd /my/deeply/nested/project/foobar7
, as long as you’ve cd’ed into foobar7 sometime in the past.
Z is great for jumping to recent directories… but is there an equivalent for opening recently used files? There’s fasd, but the way it works seems a bit too magical to me. It’s also limited in that files are often opened from other programs, instead of directly from the shell.
Every desktop environment since Windows 95 already has a “Recent Files” list for their file manager. On Linux, this is often handled by GTK. Conveniently, GTK provides a simple API for accessing the recent file list, and we can use it to write a small script that reads or writes to that list:
#!/usr/bin/env python3
#
# file-history: Read or write to GTK's recent file list.
import gi
import os
import re
import sys
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk, Gio, GLib
manager = Gtk.RecentManager.get_default()
if len(sys.argv) > 1:
# Add the given files to the recent file list
for file in sys.argv[1:]:
uri = Gio.File.new_for_path(file).get_uri()
manager.add_item(uri)
GLib.idle_add(Gtk.main_quit)
Gtk.main()
else:
# Print the recent file list, starting with most recently used
home = re.compile("^"+os.environ["HOME"]+"/")
for item in sorted(manager.get_items(), key=lambda x: x.get_modified(), reverse=True):
if item.exists():
print(home.sub("~/", item.get_uri_display()))
Running file-history
with arguments will add those files to the recent file list. Running
file-history
with no arguments will print the recent file list to stdout. Now we can go to town
combining this with other scripts.
Reading History
Let’s write a key binding in zsh that displays the recent file history in an fzf menu.
_bind_recent () {
local res=`file-history | fzf --reverse --height 40% --prompt "Hist> " | sed "s|^~/|$HOME/|"`
if [ "$res" ]; then
LBUFFER="$LBUFFER ${(q)res} "
fi
zle reset-prompt
}
zle -N _bind_recent
bindkey '^j' _bind_recent
Now pressing ctrl-J will allow you to pick a recent file and add it to the end of the command line.
I use this all the time for processing Firefox downloads. After downloading a zip or whatever, I
just go to a terminal and type unzip <ctrl-J>
. Since the list is sorted, the recently downloaded
zip is the first item in the list and I just press enter to expand out to unzip ~/Downloads/LongFilename.zip
.
Another possibility is integrating with i3 or another window manager, so that pressing alt-J shows a recent file menu:
bindsym $mod+j exec --no-startup-id file-history | rofi -dmenu -p Hist |
sed "s|^~/|$HOME/|" | xargs -d '\n' xdg-open
Writing History
The other half of this is populating the recent file history with useful data. Graphical GTK apps will already do this. Ideally we want every file we “open” from the CLI to be added to the history too.
Some possibilities:
- For users of CLI file managers like LF or Ranger, call
file-history <selected-file>
as part of the file open handler. - Write a shell alias that wraps
xdg-open
and callsfile-history
. I added this to my general purpose file opener. - Write a vim autocmd to sends all opened files to
file-history
. Personally I don’t do this since vim already has an internal file history, but it might be useful for some.
So far this setup has been working pretty nicely for me! Not quite as life-changing as Z, but I still use it on a daily basis.