Vim the Next Generation

So I figured I should modernize my Vim skills, from 1995 to 2023. A lot's changed since I last configured Vim.

Installed a modern MacVim, in my case sudo port install MacVim. It's launched with mvim, but I just change alias v=mvim in my .zshrc

In the code blocks below, ~% is my shell prompt, ## filename shows the contents of a file, cat into it or whatever. Neither of those lines belong in the file!

To start, I want to use vim9script. So my old .vimrc now starts with that mode command, then I changed all my comments from " to #. Not much else had to change. The way to detect MacVim etc is clearer now, and I can get ligatures from Fira Code!

Syntax highlighting files can just be dropped in ~/.vim/syntax/

Update 2023-04-11: added statusline highlight colors, under syntax loading

## .vimrc
vim9script
# Mark Damon Hughes vimrc file.
# Updated for Vim9, 2023-04-09
#
# To use it, copy it to ~/.vimrc
# Note: create ~/tmp, ~/.vim, see source commands below.

set nocompatible    # Use Vim defaults (much better!)
filetype plugin on
set magic
set nrformats=

set errorbells
set nomore wrapscan noignorecase noincsearch nohlsearch noshowmatch
set backspace=indent,eol,start

set nosmarttab noexpandtab shiftwidth=8 tabstop=8

set encoding=utf-8 fileencoding=utf-8
set listchars=tab:__,eol:$,nbsp:@

set backup backupdir=~/tmp dir=~/tmp
set viminfo='100,f1,<100

set popt=header:2,number:y  # 2=always

set tw=80       # I use this default, and override it in the autogroups below

# ctrl-] is used by telnet/ssh, so tags are unusable; i use ctrl-j instead.
set tags=./tags;/
map <c-j> <c-]>

# Don't use Ex mode, use Q for formatting
map Q gq

map <Tab> >>
vmap <Tab> >
map <S-Tab> <<
vmap <S-Tab> <

# Always have syntax highlighting on
syntax on

# https://github.com/mr-ubik/vim-hackerman-syntax
# changed:
# let s:colors.cyan         = { 'gui': '#cccccc', 'cterm': 45 } " mdh edit
# let s:colors.blue         = { 'gui': '#406090', 'cterm': 23 } " mdh edit
source $HOME/.vim/syntax/hackerman.vim

set laststatus=2    # 2=always
# %ESC: t=filename, m=modified, r=readonly, y=filetype, q=quickfix, ff=lineending
# =:right side, c=column, l=line, b=buffer, 1*=highlight user1..9, 0=normal
set statusline=\ %t\ %m%r%y%q\ [%{&ff}]\ %=%(c:%02c\ l:%04l\ b:%n\ %)
set termguicolors
hi statusline guibg=darkblue ctermbg=1 guifg=white ctermfg=15
hi statuslinenc guibg=blue ctermbg=9 guifg=white ctermfg=15

hi Todo term=bold guifg=red
# Use `:set guifont=*` to pick a font, then `:set guifont` to find its exact name
set guifont=FiraCode-Regular:h16
if has("gui_macvim")
    set macligatures
    set number
elseif has("gui_gtk")
    set guiligatures
    set number
endif
set guioptions=aAcdeimr
set mousemodel=popup_setpos
set numberwidth=5
set showtabline=2

augroup c
    au!
    autocmd BufRead,BufNewFile *.c set ai tw=0
augroup END

augroup html
    au!
    autocmd BufRead,BufNewFile *.html set tw=0 ai
augroup END

augroup java
    au!
    autocmd BufRead,BufNewFile *.java set tw=0 ai
augroup END

augroup objc
    au!
    autocmd BufRead,BufNewFile *.m,*.h set ai tw=0
augroup END

augroup php
    au!
    autocmd BufRead,BufNewFile *.php,*.inc set tw=0 ai et
augroup END

augroup python
    au!
    autocmd BufRead,BufNewFile *.py set ai tw=0
augroup END

augroup scheme
    au!
    autocmd BufRead,BufNewFile *.sls setf scheme
    autocmd BufRead,BufNewFile *.rkt,*.scm,*.sld,*.sls,*.ss set ai tw=0 sw=4 ts=4
augroup END

Package Managers & Snippets

Next I need a package manager. I've settled on vim-plug as complete enough to be useful, not a giant blob, and is maintained. There's at least 7 or 8 others! Complete madness out there
(I've already picked one, I don't need further advice, and will actively resent you if you give me any. I'm just pointing at the situation being awful.)
Install's easy, drop it in autoload, mkdir -p ~/.vim/plugged

The first thing I want is a snippet manager, and SnipMate's the best of those. Edit .vimrc at the end, set your "author" name, it's used by several snippets.

## .vimrc
call plug#begin()

Plug 'https://github.com/MarcWeber/vim-addon-mw-utils'
Plug 'https://github.com/tomtom/tlib_vim'
Plug 'https://github.com/garbas/vim-snipmate'
Plug 'https://github.com/honza/vim-snippets'

g:snips_author = 'Mark Damon Hughes'
g:snipMate = { 'snippet_version': 1,
        'always_choose_first': 0,
        'description_in_completion': 1,
    }

call plug#end()

Next part's super annoying. It needs a microsoft shithub account; I made a new one on a throwaway email, but I don't want rando checkouts using my real name. includeIf lets you choose between multiple config sections, so now I have:

## .gitconfig
[include]
    path = ~/.gitconfig-kami
[includeIf "gitdir:~/Code/"]
    path = ~/.gitconfig-mark

## .gitconfig-kami
[user]
    name = Kamikaze Mark
    email = foo@bar

## .gitconfig-mark
[user]
    name = Mark Damon Hughes
    email = bar@foo

~% git config user.name
Kamikaze Mark
~% cd ~/Code/CodeChez
~/Code/CodeChez% git config user.name
Mark Damon Hughes

But shithub no longer has password logins! FUCK.

~% sudo port install gh
~% gh auth login

Follow the prompts and it creates a key pair in the system keychain. I hate this, but it works (on Mac; Linux install the package however you do, it works the same; Windows you have my condolences).

Now vim, :PlugInstall, and it should read them all. I had to do it a couple times! Then :PlugStatus should show:

Finished. 0 error(s).
[====]

- vim-addon-mw-utils: OK
- vim-snipmate: OK
- vim-snippets: OK
- tlib_vim: OK

Let's create a snippet!

~% mkdir .vim/snippets

## .vim/snippets/_.snippets
snippet line
    #________________________________________

snippet header
    /* `expand('%:t')`
    * ${1:description}
    * Created `strftime("%Y-%m-%d %H:%M")`
    * Copyright © `strftime("%Y")` ${2:`g:snips_author`}. All Rights Reserved.
    */

And if I make a new file, hit i (insert), line<TAB>, it fills in the snippet! If I type c)<TAB>, it writes a copyright line with my "author" name; it's highlighted, so hit <ESC> to accept it (help says <CR> should work? But it does not). Basically like any programmer's editor from this Millennium.

Update 2023-07-24: Added header, which is my standard document header, expand is filename with extension, rest are self-explanatory. Sometimes I add a license, which SnipMate preloads as BSD3, etc.

Use :SnipMateOpenSnippetFiles to see all the defined snippet files.

File Tree

NERDTree seems useful; read the page or :help NERDTree for docs. Add another plugin in .vimrc just before call plug#end(), do a :PlugUpdate, and it's that easy. But I want to hit a key to toggle the tree, and another key to focus the file, which takes me into the exciting world of vim9 functions.

## ~/.vimrc
Plug 'https://github.com/preservim/nerdtree'

# open/close tree
def g:Nerdtog()
    :NERDTreeToggle
    wincmd p
enddef
nnoremap <F2> :call Nerdtog()<CR>
# focus current file
nnoremap <S-F2> :NERDTreeFind<CR>

Update 2023-04-11: In NERDTree, on a file, hit m for a menu, and you can quicklook, open in Finder, or reveal in Finder, and much more. Doesn't seem to be a right-click or anything functionality, so it was not immediately obvious how to make it open my image files, etc.

And I think that's got me up to a baseline modern functionality.

Printing Code Like It's 1989

Which made me laugh, the 2016-2021 dialogue and still not implemented feature in that bug report is everything you love about Microsoft-controlled "open source but not really".

I've only ever printed code from BBEdit in the last couple decades, let's see how other editors fare:

  • BBEdit: Perfect. Print command in menu (it's a native Mac app!), prints headers, page & line numbers, nice margins. Looks perfect, like an old-timey print job. I should probably have switched from dark to light theme, but it'd be fine. ★★★★★
  • Geany: Print command, several settings in Preferences, prints headers, page & line numbers (in settings, choose either headers or page numbers at bottom, or it'll be duplicated). Hideous non-native GTK app and dialogs, but does the job perfectly well. ★★★★½
  • MacVim: Print command, produces a postscript file which immediately opens in Preview, spins a while, converts to PDF. Default has no line numbers, add set popt=header:2,number:y to your .vimrc. ★★★½☆
  • Atom: No print command in the menu, but there's multiple packages 5-6 years old, they work fine. Only shows line numbers, bad margins, it's literally a web page being printed. ★★★☆☆
  • Sublime Text: Print command, but no settings (in the horrible JSON text editor where you do settings) to make it nice. Literally dumps a web page, no page or line numbers. What's the least you could do and check off the feature? That's how subl does everything. ★★☆☆☆
  • Xcode: Takes forever to start. Prints in grayscale, which is nice, but no headers or line numbers. Fail. ★★☆☆☆
  • Panic Nova: … My demo expired, and cleaning the configs out doesn't let me try again, so who knows. Can't even give them a second try without paying $100. Trashed.

I'd be interested in seeing how others fare, and on non-Mac platforms if they have any consistent print command.

Here's a zip of PDF prints - maze2.py is a silly Python program, but at just 3 pages it's a good test case.

Sublime or Sub-prime?

Because I'm endlessly evading responsibility^W^W looking for more efficient ways to work at nothin' all day^A^K look, I play with new editors.

Ugly themes, right away, install Package Control, and then the theme Dracula, and it looks like something I can stand. Not as good as Hackerman for Atom.

It's still ludicrously using stacks of JSON files for settings; 700+ lines in the default, OS overrides that, then your user prefs override that. Read lines, figure out if the default is useful, copy & modify in your own.

It does have the advantage it works reasonably fast, and runs on multiple platforms. It has a Lisp syntax highlighter that doesn't completely suck on Scheme.

But it has this terrible minimap, and no setting for it! You have to disable it from the menu every time you make a new window. OR, you can hack on it. Prefs, Browse Packages, make a Python file User/minimap.py (found on StackOverflow, ugh):

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import sublime
import sublime_plugin

class MinimapSetting(sublime_plugin.EventListener):
    def on_activated(self, view):
        show_minimap = view.settings().get("show_minimap")
        view.window().set_minimap_visible(show_minimap)

Then add "show_minimap": false, to the prefs file, and the stupid minimap never shows back up.

Problem 2, I wanted to insert timestamps in my headers. No datetime function or snippet or whatever.

Make a folder Datetime next to User, file Datetime/Datetime.py:

import sublime
import sublime_plugin
import datetime

class DatetimeCommand(sublime_plugin.TextCommand):
    def run(self, edit):
        self.view.insert(edit, self.view.sel()[0].b, datetime.datetime.now().isoformat())

Note all the name cases matter. See that horrible self.view.sel()[0].b? That's how you get the current cursor position. WTF!

Next to it, make Default (OSX).sublime-keymap:

[
    { "keys": ["shift+ctrl+t"], "command": "datetime" }
]

And now Sh-^T will insert a chunky ISO8601 datetime. Good enough. (originally I did Sh-Cmd-D, but turns out that's duplicate line which I use sometimes)

So, I'm giving Sublime 4 a ★★☆☆☆ out of the box, the API is ugly as hell and they no longer have example code up so ★★★☆☆, but it's pretty hackable if you don't mind getting your hands covered in code guts, so maybe ★★★★☆

I'll give it another week and see if it's worth using longer term. BBEdit's infinitely better for giant text files, but I do have needs other than that.


Man, that Bachman & Turner concert… they're super old, and that was 2012. But Paul Schaeffer, minus the World's Most Dangerous Band, jamming out on the keyboard! The audience are too old to dance. But they still rock out pretty good for guys who are even older than me.

How to Configure Vim

I just had the unfortunate experience of using the standard vimrc on ubuntu (which I loathe, but it's the most stable OS on this VPS), and now I sympathize with people who think they can't use Vim. So here's my much less annoying vimrc. You'll want to make a ~/tmp folder if you don't have one. Adjust the augroups for the languages you use; I was forcing some syntax files to load because they had bad defaults like PHP as HTML, no idea if that's improved.

" Mark Damon Hughes vimrc file.
"
" To use it, copy it to ~/.vimrc

set nocompatible    " Use Vim defaults (much better!)
filetype plugin on
set magic
set nrformats=

set errorbells
set nomore wrapscan noignorecase noincsearch nohlsearch noshowmatch
set backspace=indent,eol,start

"set smarttab expandtab shiftwidth=4
set nosmarttab noexpandtab shiftwidth=8 tabstop=8

set encoding=utf-8 fileencoding=utf-8
set listchars=tab:__,eol:$,nbsp:@

set backup backupdir=~/tmp dir=~/tmp
set viminfo='100,f1,<100

set popt=header:2,number:y

set tw=80       " I use this default, and override it in the autogroups below

" ctrl-] is used by telnet/ssh, so tags are unusable; I use Ctrl-J instead.
map <C-J> <C-]>

" Don't use Ex mode, use Q for formatting
map Q gq

map <Tab> >>
vmap <Tab> >
map <S-Tab> <<
vmap <S-Tab> <

" Always have syntax highlighting on
syntax on
hi Identifier guifg=blue
hi Statement term=bold guifg=blue
set guifont=Menlo_Regular:h14
set guioptions=aAemr
set showtabline=2
set mousemodel=popup_setpos

augroup c
    au!
    autocmd BufRead,BufNewFile *.c set ai tw=0
augroup END

augroup html
    au!
    autocmd BufRead,BufNewFile *.html set tw=0 ai
    autocmd BufRead,BufNewFile *.html source $VIMRUNTIME/syntax/html.vim
augroup END

augroup java
    au!
    autocmd BufRead,BufNewFile *.java set tw=0 ai
augroup END

augroup objc
    au!
    autocmd BufRead,BufNewFile *.m,*.h set ai tw=0
augroup END

augroup php
    au!
    autocmd BufRead,BufNewFile *.php,*.inc set tw=0 ai et
    autocmd BufRead,BufNewFile *.php,*.inc source $VIMRUNTIME/syntax/php.vim
augroup END

augroup python
    au!
    autocmd BufRead,BufNewFile *.py set ai tw=0
augroup END

augroup scheme
    au!
    autocmd BufRead,BufNewFile *.scm,*.ss,*.rkt set ai tw=0 sw=4 ts=4
augroup END

The Future of Programming is Text

You can't grep or diff binary trees. You can't get a Smalltalk IDE on your iPad. You can't write an operating system or a big application in Scratch or the Mindstorms IDE. And even a small program in these won't work in the next version.

But you can edit plain text with any text editor, whether that's ed, nano, Vim, emacs, BBEdit, Atom, Textastic, Editorial, Eclipse, AppCode, whatever. You can save it safely and easily in any source control system. You can run an awk or sed script over your entire codebase and it just works.

See also:

If your language (or non-linguistic programming environment in some cases) is only usable from a single IDE, you've cut yourself off from every other analysis and editing tool in the world, you're dependent on that one tool to do everything you want.

I have old Mac and iPhone NIB files which can't be read with any current version of Xcode/Interface Builder, the file format was only supported by one dev tool and it's changed, and the old tools don't run on modern OS X. These NIB files "work", in that they deserialize into objects, but there's no way to edit them. Where possible, I now do most UI work in code; this isn't great fun, I end up with a ton of builder functions to avoid repetitive code blocks, but it'll still compile and work in 10 years.

This is also why I don't use a WYSIWYG word processor, I use MultiMarkdown. I've lost documents to proprietary WPs, and of course there's no way to run tools over them (except, sort of, MS Word with BASIC).

None of this has stopped people from making new non-text environments, or weird experiments. Experiments can be useful even when they fail, telling us what doesn't work. But they don't catch on because the tools aren't as good as text, and won't work in the future when the experimenter gets bored and quits.