39 min read / 11 for this part
VIM diary
Part: 1 | 2 | 3 | 4 | 5 | 6 | 7
While learning the basics of Vim over the course of a few months I kept a simple diary. I recorded what I had been Googling for and what I'd learned during my struggles.
# December 2023
I thought this would be the "settling in" month.
Maybe it was, towards the end, in a tantalising "just a little further" sort-of way. But everything leading up to that was tinged with the regret of having started down this god-forsaken path in the first place.
As I became more familiar with Vim and gradually customised it to my liking, a peculiar thing happened: Because I still needed to swap between it and VSCode I noticed myself hating both of them more and more.
- I hated VSCode because the Vim Extension is lacking, buggy, and inconsistent.
- I hated Vim because I still couldn't quite get it to replace VSCode.
This irritation didn't seem to be passing very quickly. When one hurdle was overcome in Vim, it fed into my hatred of VSCode. When I used a snazzy feature in VSCode that I have keen muscle-memory for, it fed into my hatred of Vim needing a whole bunch of jostling to get things done.
Somehow I was still enjoying working on the note-taking project I had assigned myself (and taking actual notes with) but when it came to writing code in a large project things were pretty painful.
# Neovim again
Back in October I had tried LazyVim and decided I didn't want to learn Vim that way. However, I certainly did remember that it was very fast compared to regular Vim, at least on the hardware I was using at work.
I tried LazyVim once more. Within minutes I realised I hate tabs, animations, the peculiar futzing with standard functionality (why is the Ex prompt in the middle of the screen?) and a whole bunch of things I didn't want to go through the effort of turning off.
So I deleted the LazyVim config to see if I could instead use my own home-grown
.vimrc that I was starting to become attached to.
Perhaps I was starting to get the point a bit?
It took two attempts over the first week, but I got my hand-rolled .vimrc
working in both Vim and Neovim. This was not without problems of course: I
rather like Fuzzyy, but it is written in
VimScript9 and thus incompatible with Neovim. But fzf didn't
work
properly for me at the
office in neither Vim nor Neovim, where I used GitBash on a Windows machine.
Some conditional logic sorted that out and I was off and running with the much
snappier Neovim. It looked and worked almost exactly like Vim. I would,
however, fall-back to using Vim again in Windows due to GitBash and fzf
bugs that I just couldn't figure out. In the New Year I'd finally give up on
using fzf on Windows (just in Neovim, not the CLI) and use
Telescope in order to get
me back into using Neovim regularly.
# Taking stock
To get a handle on some of these and other issues, I spent some time in the
first week reviewing all the plugins I had installed. I enumerated them
one-by-one and tried to justify each. I dropped Copilot, vim-abolish,
vim-repeat, vim-surround, and nearly all colorschemes.
This is not a judgement about any of those plugins by the way: I just feel too green at regular Vim to be doing too much in the way of adding new motions that aren't portable.
# The UNIX Way
Into the second week I was looking into brushing-up on shell commands that work well with Vim. This was off the back of trying to use Vim pipes more.
My list looked like this:
head |
tail -n 10 - crop output
expand - convert tabs to spaces
fold -s -w 50 - hard line wrap (80 is
default, -s to wrap on spaces)
cut - cut lines by:
-c 1-5,10-2char index-d : -f 1,5fields split by delimeter (:in this case)awkcan also print columns:awk -F ':' '{print $1, $5;}'
nl - print with line-numbers. -ba to number
empty lines
wc - word count. -w words only, -l lines
only
column -t - detect and print in columns
-s =set separator for-toption (=in this case)
Before | After
---------------|----------------
one = 1 | one 1
two = 2 | two 2
three = 3 | three 3
four = 4 | four 4
five = 5 | five 5
six = 6 | six 6
seven = 7 | seven 7
eight = 9 | eight 9
- tr - translate characters.
-Cnegates.-ddelete, not replace - awk - line-by-line text processor
- sed - regular-expression based stream editor.
-i[I]n-place update (careful now) - grep -
-l= [L]ist files,-o= [O]nly matches,-v= in[V]ert search
The above four can accept [:class:] thingers in their regex substitutions.
Here's a shortlist of ones I find useful:
[:alnum:] [:alpha:] [:digit:] [:lower:] [:upper:] [:punct:] [:space:]
find - find files + folders, and can
execute commands with the results using --exec or piping into xargs
diff -u old new > file - create a patch file
patch < file - apply patch file
patch -R < file - [r]everse/undo patch
date - insert date/time
-Ijust date in [I]SO8601-I[FMT][FMT] = hours, minutes, or seconds-r %last modification/w[r]ite time of current buffer
cal - insert a nice calendar
-33 months,-A nor-B nn months after, before-y [n]calendar for a full year (no n for current)-m month- can specify number, and if so, suffix f or p for [p]receding [f]ollowing-hno [h]ighlighting
bc - arbitrary precision calculator. I eventually wrote something that integrates this
printf - more than echo
xargs - build commands from stdin / pipelines. It's basic function is to take a list of newline-separated strings (like you'd get from an output) and turn them into space-separated strings that you can pass as an input argument. For example:
find . -name 'glob' | xargs grep ...echo "https://example.com/" | xargs curl -sL
So long as your files don't have spaces, the above will work without any
special arguments passed into find/xargs (such as -print0 and --null.) I find
the curl example interesting because piping a URL into it isn't possible with
the standard command.
My starting point for looking into these was this Wikipedia article about POSIX commands. I tried to focus on the "Mantadory" ones where I could.
It was a nice exercise in discovering commands I had not heard of, plus refreshing what little knowledge I realised I knew.
I would later recognise this as the point where I started to finally grok the value of the UNIX philosophy: Where CLI commands do one job well and are interoperable. And since so many are for manipulating text, you can use them in any flavour of Vim, even the much older and more limited Vi.
Better than that, by using them in Vim instead of reaching for plugins, you're keeping your memory fresh for when you want to use them at the CLI or in a shell script.
# UNIX Philosophy
Half-way into December I stumbled upon a few ranty videos about the UNIX Philosophy. It was a serendipidous moment considering what I had been looking into earlier in the month:
While this was clawing around in my head trying to grab a finger-hold, I came up with another project: a "filter" shell-script that works (as much as it can) like the Soulver app.
It implements bc alongside various filters and token replacements to do so,
for example:
1 + 1
Becomes:
1 + 1 #= 2
It will ignore things that aren't arithmetic operators or numbers:
50 litres * 100 bags #= 5000
29.99 total price #= 29.99
You can use LINE:-n and LINE:+n to refer to the result of a line relative
to the one they appear on:
50 litres * 100 bags #= 5000
29.99 total price #= 29.99
price per litre = LINE:-1 / LINE:-2 #= .005998
Running the "filter" on the same lines will recalculate them. I eventually
called it solv-sh and popped it on GitHub.
# Pain switch
By the last week before Christmas I finally starting to find VSCode slightly more painful to live with than Vim/Neovim.
Also, Neovim in Windows isn't quite up to scatch with Vim when it comes to a bug-free experience running external shell commands, so I'm finding myself using Vim more even though Neovim is far more performant.
Looking back, I would realise this was the beginning of a long drawn-out
detachment from VSCodes built-in source control tools. I had discovered
the git worktree command, and while I had not played with it at this point
very much, it was going to become my standard way of interacting with repos,
and I would be doing so mostly via the CLI and vim-fugitive.
Until then, I was scratching my head wondering why worktrees don't seem to play well with VSCode's Source Control feature. I would later realise this was down to using bare repos, which I mistakenly thought was a requirement for using git worktree.
It's not.
# Forced Hand
As stated previously, I use a Windows machine at work. It had problems, and I was due a replacement. However, there was a mix-up with permissions and I could no longer run any developer tools on either my old or new laptop.
Except Vim. So that's what I used exclusively from that point on.
I wasn't planning on doing so: I was in the midst of a rather major project that could really do with as few distractions as possible, least of all while I was still trying to get used to a new way of working. The only thing I was doing with Vim with any regularity was taking notes, which isn't quite the same as conjuring the many special characters required when programming. There was still a lot to get used to.
But since IT support weren't exactly quick on the uptake it felt like this was an opportunity to go all-in on this "side-project" of learning a brand new development environment. Well, no, it didn't really feel like an opportunity at all. I wasn't ready, but the project had to continue.
I did learn a couple of neat tricks as a late Xmas present:
First a convenient search/replace technique discovered on Reddit:
onoremap <expr> * v:count ? '*' : '<esc>*g``' . v:operator . 'gn'
With this remap you can use c* to change (or d* to delete, etc.) the
current word. Then after hitting <esc>, either:
- Press
n/Nto skip the next occurrence, or - Press
.to repeat the previous move and change
Second, how to list files recursively excluding node_modules:
find . -path ./node_modules -prune -o -name '*.js' | xargs grep -l '^import'
# | \____/ \/
# | | OR
# | |
# | |_ If a directory, do not descend into it
# |
# |____ the ./ is important, and don't add a trailing / either
Clever! We ask find to look for a path first, but if it finds a path, not
to descend into it. Then we OR that with a search for *.js files, which
will be returned to us instead of the pruned node_modules folder.
Perhaps you have heard of ripgrep? It achieves the above in a single command. It's nice to know a portable alternative though.
That rounds out 2023!
# Commands Learned
- Finally made use of tabs, despite starting the month disliking them;
:tabe,gt,gT - A taller footer can suppress some of the more obnoxious "Press ENTER"
prompts
set cmdheight=2 - Skip to the next/previous sentence with
(and) - Learned that you can perform a substitution on the last search you did by
simply not specifying a search-term:
:s//replacement - In VISUAL mode,
ojumps between the beginning and end of your selection - Learned about the expression register
=. From INSERT mode, try<C-r>=system('date -I')and hit enter zzto center the viewport relative to the cursor I knew about, but I also learned thatztandzbwill shift the viewport down/upwards too
31 Dec, 2023