lf, or “list files”, is a single binary file manager, inspired by the ranger file manager, but written in Go. Using this tool, you can navigate really quickly, build up a mental model of the filesystem layout and make modifications with ease.

Out of the box, this is super useful on remote machines or even docker containers where you don’t have access to your normal full configuration, in my case Emacs with dired.

However, you can also have a lot of fun tricking lf out with some extra configuration.

In my case, I wanted a light-weight setup that can preview images (in the terminal!), source code, compressed archives and some other odds and ends, with fast fuzzy file and directory jumping using fzf.

This post shows how to do all of that with the minimum of effort.

Screenshots

First you would probably like to see some examples of what this could look like.

We start with the image previewer:

Figure 1: lf previewing an image using chafa and iterm2’s sixel support

Figure 1: lf previewing an image using chafa and iterm2’s sixel support

Some syntax highlighted source code using the bat previewer with the Solarized (light) theme, which works well on my light and dark mode terminal setup:

Figure 2: previewing typescript source with bat

Figure 2: previewing typescript source with bat

Inside the tmux multiplexer, we revert to ASCII rendering, which takes me back:

Figure 3: on tmux and windows terminal, we revert to chafa’s amazing ASCII rendering

Figure 3: on tmux and windows terminal, we revert to chafa’s amazing ASCII rendering

Finally, listing of archive and package contents using lesspipe:

Figure 4: tar.gz contentst using lesspipe, which supports many other formats

Figure 4: tar.gz contentst using lesspipe, which supports many other formats

Figure 5: deb package contents and useful metadata

Figure 5: deb package contents and useful metadata

Configuration

This config makes use of a small number of powerful utilities, and hooks them into lf via a preview script:

  • file: extract file’s mime type so that the rest of the preview script can decide what to preview it with
  • chafa: convert images to sixels or symbols for previewing. Such an amazing and fun project, go look!
  • bat: preview many kinds of text files with syntax highlighting
  • lesspipe: list contents and metadata of many kinds of archives
  • pdftoppm: convert PDFs to images so that they can be previewed via chafa
  • command-line fuzzy finder: Invoke with ctrl-o for rapidly opening any file or folder nested below your current location

You can install all of these using your package manager (see the preview script below for Debian / Ubuntu and homebrew invocations), after which you must install or configure your lfrc, preview and finally your shell configuration.

main lf configuration

Your ~/.config/lfrc should look as follows.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# add lfcd to your .bashrc / .zshrc

# lfcd () {
#     # `command` is needed in case `lfcd` is aliased to `lf`
#     cd "$(command lf -print-last-dir "$@")"
# }
# alias lf='lfcd'

# internal field separator: tell bash to split on newline, and not on space/tab as well
# now we can do e.g. !7zz a archive.7z $fx
set ifs "\n"
map <delete> delete
set previewer ~/.config/lf/preview
# our previewer script uses bat - re-use for the pager shortcut
map i $~/.config/lf/preview $f | less -R
# we convert images to sixel using chafa
set sixel true
# refine selected file as I type
set incsearch true

cmd fzf_jump ${{
    #res="$(find * | fzf --header='Jump to location')"
    res="$(fzf --walker=file,dir,follow,hidden --walker-skip=.git,node_modules,.Trash)"
    if [ -n "$res" ]; then
        if [ -d "$res" ]; then
            cmd="cd"
        else
            cmd="select"
        fi
        res="$(printf '%s' "$res" | sed 's/\\/\\\\/g;s/"/\\"/g')"
        lf -remote "send $id $cmd \"$res\""
    fi
}}
map <c-o> :fzf_jump

As mentioned in the config file, add the following to your ~/.bashrc or ~/.zshrc so that when you exit lf, you won’t be returned to the directory where you started lf, but rather the one you navigated to.

1
2
3
4
5
lfcd () {
    # `command` is needed in case `lfcd` is aliased to `lf`
    cd "$(command lf -print-last-dir "$@")"
}
alias lf='lfcd'

preview script

Create the file ~/.config/preview with the following contents.

Remember to mkdir ~/.cache/lf for the pdf image caching logic.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
#!/bin/sh

# light-weight preview script for gokcehan's lf file manager

# requirements:
# file bat chafa pdftoppm lesspipe

# mkdir -p ~/.cache/lf && mkdir -p ~/.config/lf
# cp preview ~/.config/lf/preview

# on ubuntu install the dependencies with
# sudo apt install file bat chafa fzf poppler-utils
# ln -s /usr/bin/batcat ~/.local/bin/bat

# on homebrew (macOS or... linux) you can do
# brew install bat chafa fzf lesspipe poppler
# ln -s /opt/homebrew/bin/lesspipe.sh ~/.local/bin/lesspipe

# this bat theme works best for me on light and dark terminals
# "bat --list-themes" to see all of them on your setup
BAT_THEME="Solarized (light)"

batorcat() {
    file="$1"
    shift
    if command -v bat >/dev/null 2>&1; then
        bat --color=always --theme="$BAT_THEME" --style=numbers --pager=never "$file" "$@"
    else
        cat "$file"
    fi
}

image() {
    if [ -n "$WT_SESSION" ] || [ -n "$TMUX" ]; then
      # windows terminal (no sixels yet) or tmux (not much success with passthrough to iterm here)
      chafa -f symbols -s "$2x$3" --animate off --polite on "$1"
    else
      chafa -f sixels -s "$2x$3" --animate off --polite on "$1"
    fi
}

# cpbotha's simplified version
CACHE="$HOME/.cache/lf/thumbnail.$(sha256sum "$1" | awk '{print $1}')"

case "$(file -Lb --mime-type -- "$1")" in
    image/*)
        #chafa -f sixel -s "$2x$3" --animate off --polite on "$1"
        image "$1" "$2" "$3" "$4" "$5"
        exit 1
        ;;
    application/pdf)
        [ ! -f "${CACHE}.jpg" ] && pdftoppm -jpeg -f 1 -singlefile "$1" "$CACHE"
        image "${CACHE}.jpg" "$2" "$3" "$4" "$5"
        ;;
    text/*|application/json)
        batorcat "$1"
        ;;
    *)
        #file -b "$1"
        # lesspipe can list many archives, and even do docx files
        # if it does not work (e.g. message/rfc822) then batorcat can try
        lesspipe "$1" || batorcat "$1" || echo "lesspipe not installed or could not open file"
        ;;
esac