Originally posted 2026-10-02 06:24:30

VIM is an incredible editor, I've been using it exclusivley for many years now. I've always been more of a CLI user rather than a GUI user, Its just so simple, everything is the same size and in my case - everything is restricted to the same colors (ANSI16). There are very few exceptions to my terminal usage, the primary item would be file managers.

For quite some time I used Dolphin just because it was fast and available but recently I've found myself just using the terminal file operations instead. Using the terminal shell as a file manager works fine, if anything its more extensable than graphical editors because you have the power of scripting at your fingertips. Although this was nice I still found myself yearning for a dedicated manager that would make things simple and more visual than basic CD navigation.

The first program I tried for this was ranger, It was the only TUI file manager I could name off the top of my head. At the time ranger worked fine, it allowed for more intergrated features such as bind-based universal directory sorting and even more useful - multi-media previews. Ranger was built to use some semi-familiar vim-style bindings and mode concepts so it did come as a second nature although what I found with ranger (and most other conceptual-vim programs) was that it just dident simulate the vim workspace as much as I wanted - this and the fact that ranger was non-asyncronous and rather laggy whilst navigating around larger directories.

The next program I researched was Yazi, an asyncronous rust file manager. Yazi worked OK although it seems targeted towards non-traditional linux users as it features fancy nerd-font characters and glyphs that my puny xorg-terminus font could not handle. Yazi ended up being even worse than Ranger as it had very far-fetched keybind hints and majority of the menu glyphs showed up as unicode font errors even though I had explicitly removed them in the yazi config. Yazi was bad, ranger was better but both still missed the vim feeling..

My next journey was NNN (N^3). NNN is a small C file browser which at face value, trumped both the performance and vim-style of the prior file managers. NNN however was missing in a few features that over time I had become acustomed too, features such as multi-media previews. NNN is very extesable and following the docs I was able to setup a plugin called preview-tui which in a tmux session will open a secondary pane in which a specified program can run on each hovered file. This is inteded for use with image preview commands such as kittyimg or chafa. Because I am using the foot terminal, I ended up intergrating chafa, so now when hovering images I could see a sixel preview in the preview-tui's secondary pane. Now I did end up concluding NNN to a simple wrapper that opens and closes TMUX automatically, this may be of use to some people so here is that wrapper:
dwl/config/mkshrc
 2 #
 3 # NNN (N^3) config variables
 4 #
 5 
 6 export NNN_TMPFILE="$HOME/.cache/.nnn_lastd"
 7 export NNN_PREVIEWIMGPROG="chafa -f sixel"
 8 export NNN_PLUG='p:preview-tui'
 9 export NNN_FIFO="/tmp/nnn.fifo"
10 export NNN_PREVIEWDIR="${XDG_CACHE_HOME:-$HOME/.cache}/nnn/previews"
11 
12 # Wrapper that runs TMUX (for preview pane)
13 function n(){
14     tmux new-session "export NNN_TMPFILE='$NNN_TMPFILE'; mksh -c 'nnn -DCHE -P p'" \; set status off
15     if [ -f "$NNN_TMPFILE" ]; then
16         . "$NNN_TMPFILE"
17         rm -f -- "$NNN_TMPFILE" > /dev/null
18     fi
19 }
Anyway, NNN was nice but it too dident have exact vim-like bindings, this wasn't a big issue and I'm just crying cos I'm very picky so moving on.. My next idea was to look into NetRW. NetRW is the editor/explorer built into vim, you can open this with :Ex within vim. Because NetRW is built directly into Vim it has all the same bindings and uses the same config.. What more could you want!! One of the caveats was that I knew that sixel images (Or any terminal-image protocal for that matter) would not work within vim, so embedding a :Term window with a preview script would not work. Looking back at NNN, the preview-tui plugin simply uses the tmux multiplexer for secondary pane/window support, and luckily tmux can support sixel images and other related protocals. After some tinkering I came up with the following finished wrapper:
dwl/config/mkshrc
178 # File editor with image previews (built off of netrw)
179 function ex(){
180 
181     tmux new-session "vim -c Ex $1; killall netrw_preview" \; \
182             split-window -h '$DWL_SCRIPT_DIR/netrw_preview' \; \
183             select-pane -t 0 \; \
184             set-window-option -g window-style fg=default,bg=default \; \
185             set-window-option -g window-active-style fg=default,bg=default \; \
186             set status off \; \
187             attach
188 
189     return $?
190 }

Now this simple wrapper does a few things. Firstly it starts a tmux sessing with two panes, the first of which runs NetRW using the vim -c Ex command (Runs :Ex on vim startup). The next split window runs a special script I wrote for generating multi-media previews of the items in Netrw. The rest of that garbage just sets some vanity options for example, making the background of tmux transparent and disabling the bottom-status bar as its not needed.

The custom preview script is rather simple, we just get the current hovered item in NetRw, determine its MIME type, and based on that information we run it though a certain display command. Here is that script:
dwl/script/netrw_preview
 1 #!/bin/sh
 2 #
 3 
 4 # Path stream Location
 5 NETRW_FIFO="/tmp/netrw-current"
 6 
 7 echo "" > "$NETRW_FIFO"
 8 tail -f "$NETRW_FIFO" | while read -r line; do
 9     if [[ -n ${pid:-} ]]; then
10         kill "$pid" 2>/dev/null
11         wait "$pid" 2>/dev/null
12     fi
13 
14     clear
15 
16     # Find files mime type
17     MIME=$(file -b --mime-type "$(readlink -f "$line")")
18     lines=$(tput lines);
19     rlines=$((lines-2))
20 
21     case "$MIME" in
22         *image*)
23             chafa -f sixel "$line" &
24             pid=$!
25         ;;
26         *text*)
27             cat "$line" | head -n $rlines
28             pid=$!
29         ;;
30 
31         *directory*)
32             tree "$line" | head -n $rlines
33             pid=$!
34         ;;
35 
36         *)
37             echo $MIME
38             unset pid
39         ;;
40     esac
41 done

As you can see, the script opens a file labeled NETRW_FIFO, this is a hint as to whats next on the list of intergration schemes. Netrw does not have a native FIFO feature where events are written to a public file. This means that in order to determine the hovered file I had to write some custom Vimscript that would simply write the absolute path of the current HOVERED item (in netrw) to a specific file path. Since Vim is great and the config language is even better this was a rather trivial task. After around a half-hour of reading documentation and coffee breaks I came up with the following:
vim/vimrc
139 " My NETRW config
140 let g:netrw_altv=1
141 let g:netrw_banner = 0
142 let g:netrw_liststyle = 3
143 let g:netrw_altv = 1
144 let g:netrw_winsize = 25
145 let g:netrw_keepdir = 0
146 let g:netrw_list_hide = '\(^\|\s\s\)\zs\.\S\+'
147 let g:netrw_keepdir = 1
148 let g:netrw_sort_by = 'name'
149 let g:netrw_list_cmd = "command ls -v"
150 
151 " Enable RNU & NU in NetRW
152 autocmd FileType netrw setlocal number relativenumber
153 
154 " Auto-append the file under the cursor in netrw buffers
155 autocmd FileType netrw
156     \ let b:netrw_last_word = '' |
157     \ autocmd CursorMoved <buffer>
158     \ let word = netrw#Call('NetrwGetWord') |
159     \ if word !=# get(b:, 'netrw_last_word', '') |
160     \   let b:netrw_last_word = word |
161     \   let path = fnamemodify(b:netrw_curdir . '/' . word, ':p') |
162     \   call writefile([path], '/tmp/netrw-current', 'a') |
163     \ endif

The first section as is labeled, is just my own configuration of NetRW which mainly consists of declutering the UI and many depricated config options that either do nothing or don't work (and I guess also do nothing). The next section enables both row numbers and relative row numbers, this aligns with my vim editor setup where I have both the nu and rnu options enabled by default. You of course do not need to copy this but I find it much more easy to navigate using jumps based off of the relative line numbers :). The final part of that snippet (Full config can be found in my vim repo) is the FIFO implementation. The hovered item is captured only whilst in a netrw instance, if there has been a change in the hovered item then it is simply appended to /tmp/netrw-current which of course is activly listened to by the netrw_preview script. As I'm sure you can understand, the netrw_preview script is very easy to use, simply add a case for a desired MIME type and add your own scripting into it. I only really use image previews but I can't imagine that it would be too difficult to implement other media types such as PDFs or even animated GIF previews.


Thanks for listening to this rant.. Now that I've spent at least a days worth of my time focusing on refining my file manager setup I'm going to leave it open and simply admire it..

I'm lazy and so it is unlikley that I will update this post, for more information please look at the following sources:
git.sr.ht/~x3hy/dwl git.sr.ht/~x3hy/vim