A Bland Knowledge Base in Vim

I have notes on lots of things. When you write notes in markdown it's called 'a knowledge base', which sounds classy. So now I have lots of knowledge bases, and I strive to keep them bland and featureless.

A good knowledge base has no fancy features. Features are bad. They tie you down to particular software, or one workflow. Everything 'integrated' with the notes also holds onto your notes, jealously.

Properietary solutions are prone to the usual abuse, but many open source note apps aren't much better - they have their own oddities, and special markdown abilities, which won't work in other contexts. This leaves you unable to publish notes, as that special in-line image format from a fancy app won't work on a website.

The websites are no better. I publish with Hugo, and it's great, but every theme also invites you to place special snippets for graphs or video

`{{ .video | youtube.com/?url=thing }}`

It looks harmless, but it's a trap.

Hence Vim. Vim is harmless. It won't tell you how to write or organize your notes. It just makes editing easy. Because vim is easy.

The Linux Knowledge Base

The collection of text notes on how to do stuff in Linux has grown and become opinionated. I'm slowly going over the pages to try and tag every single page with knowledge dependencies, i.e. what you need to know before reading the page.

Here's the head of one page on editing files encrypted with GPG:

1---
2title: "gpg with vim"
3tags: [ "vim", "data", "GPG" ]
4requires: [ "GPG Basics", "vim basics" ]
5---
6
7The `vim-gnupg` plugin lets vim edit gpg-encrypted files as if they were unencrypted.
8
9[...]

That 'requires' tag means I can generate a note at the top of the pages saying what you need to know before reading the page.

I have not tagged all pages yet, but once I do, the website will state what's required to read an article at the front. This will make me happy, because documentation should not have links in the body.

Vim with a Knowledge Base

File Navigation

Open a file at line 20:

1vim writing/ed.md +20

Open the file while searching for the word 'Delete`:

1vim writing/ed.md +/Delete

Buffers & Tabs

You can open multiple files with Vim and navigate with 'buffers' (the word still feels strange).

1vim writing/*.md

The command :ls shows the buffer, and b2 takes you to buffer 2 (writing/vim.md) in this case.

1:ls
2  1 %a   "writing/ed.md"                line 1
3  2      "writing/vim.md"               line 0
4  3      "writing/vim_tricks.md"        line 0
5:b2

You can also cycle through them.

  • Select the next buffer with :bNext (or :bn).
  • Select the previous buffer with :bPrevious (or :bp).
  • Select the last buffer on the list with :bLast (or :bl).

Tabs are similar, but you can see the tabs at the top.

Open files in tabs with -p.

1vim -p writing/*.md

Then use :tabedit % to open a new tab.

Go-to-File

If you put the cursor on a file's name, you can go to the file immediately with gf. This is excellent, as it lets you wander your own knowledge base as if a palace.

Weird markdown formats, like Gollum sometimes demand that you leave out the .md suffix in links, so your link to [GPG](gpg.md) won't work. You have to write [GPG](gpg). Vim lets you navigate to gpg.md anyway by adding a standard suffix:

1set suffixesadd=.md

Now you can go to the word gpg and press gf, and instantly enter that file.

If the file is in another directory, you can link to it like this:

1Check [the standard editor](writing/ed.md) for why `sed` works like this.

But you might want to link to it like this:

1Check [the standard editor](ed.md) for why `sed` works like this.

In this case, you can still navigate to it by adding all the other directories to the path where Vim checks. Just add this to your vimrc file:

1set path+=**

Edit Jumps

  • g; to go to the last place you edited.
  • g, to jump back through edit-locations.

Netwr Navigator

On rare occasions, I'll use the in-built navigator: Netwr. There's an excellent setup guide here as it's not very good 'out-of-the-box'.

Editing

Mapping Keys to Output

Notes on Linux means lots of code-blocks, which look like this:

```sh
ls
ls -a
```

This is boring to type, so now I just type 'comma' then 'grave', and vim produces the code block. You can map the keypress by sticking this in ~/.vim/vimrc:

1map ,` o```<Enter>```<Esc>kA

Interactive Find and Replace

Sometimes you want to find and replace a word in a lot of files, but also confirm each change. Vim lets you do this by starting up with a 'find and replace' command:

1vim -c "%s/${nano}/${vim}/gc" -c 'wq' **/*.md

Input Command Outputs

Vim can run a command and type out the results for you:

1:r!date
2
3Thu 16 Apr 13:08:48 CEST 2026

Writing with Markdown

Set up a Spell Checker

I have the standard spell-checking commands in my vimrc, but the command to actually use the spell-checker hides in the markdown-only file, because you don't always want a spell-checker on.

1
2set spelllang=en_gb
3set complete+=kspell
4
5hi clear SpellBad
6hi SpellBad cterm=underline
7hi SpellCap cterm=underline
8hi SpellLocal cterm=underline
9hi SpellRare cterm=underline

All those hi-light commands make Vim's spell-highlights less nauseating.

In the markdown-only setting, add this to actually use those spell-checking commands:

1setlocal spell

Once you have the spell-checker working, it just has a few commands:

  • z= asks the wordlist for suggestions on better spelling.
  • zg accepts a word as a word, and adds it to your dictionary in ~/.vim/spell/.
  • zug removes a word which is not a word.

If, like me, you speak a kind of English which people describe as 'not default', you should share this spelling file with all your computers, so you don't have to rebuilt it every time.

This next command is amazing. Once you start using it, you won't stop, and may look back on all the years you never had it and weep.

1inoremap <C-l> <c-g>u<Esc>[s1z=`]a<c-g>u

If you type a typo, you can press Control + L to jump back, fix the typo (with Vim's first guess at the correct spelling) and then jump back, all during insert mode.

Formatting Text Lines

I still don't know how to do this. On the one hand, writing one sentence per line helps when using git, and feels natural. But it feels less natural to read.

As an example, that last paragraph has a new line on each sentence:

1I still don't know how to do this.
2On the one hand, writing one sentence per line helps when using `git`, and feels natural.
3But it feels less natural to read.

Option 1: have a separate markdown reader.1 Markdown is supposed to wrap lines for you, so you can break anywhere.

Option 2: wrap the lines with vim.

I can press gq2j to reformat two lines down, and it looks better.

1I still don't know how to do this.  On the one hand, writing one sentence per
2line helps when using `git`, and feels natural.  But it feels less natural to
3read.

However, every time you edit, you need to wrap the lines again.

Not Vim with a Knowledge Base

The best thing about using Vim is not using Vim. After all, it's just a text editor. As mentioned above, features are bad. Vim happily works with other tools by integrating those command. Like the Tao, it does nothing, and accomplishes everything.

Having plain text means easy parsing. I have a little bash script which searches markdown files for links, and checks to see if they work, then spits out a list of dead links. I'll stick the script at the end in case anyone wants to use it for their weighty collection of markdown files.

Make Stuff

The Makefile at the root of knowledge base automates the standard commands. The first command is a help menu. I want the project to be friendly in case anyone else wants to join me in my quest to rid documentation of links inside the body.

1make
2
3article         Write a new article
4clean           Remove all generated files
5cmd             Big lists of commands
6database        Make a recfiles database
7help            Print the help message
8map             Show knowledge dependency map

make article will create a standard article template then commit it to the git repository.

Recfiles

Recfiles are plain-text files which are also a relational database. They look like this:

 1%rec: guide
 2%key: title
 3%type: requires rec guide
 4%type: provides rec guide
 5%type: wordcount int
 6%sort: wordcount
 7
 8file: data/gpg.md
 9title: gpg
10tag: data
11tag: gpg 
12wordcount: 13
13
14file: hardware/brightness.md
15title: brightness
16tag: hardware
17tag: laptop 
18wordcount: 16
19
20[...]

This file is a record of every file in the knowledge base, along with the word-count.

The Commands List

Recently, I've begun another recfile database for commands. I have a boat-load of little commands that don't merit their own page/post/article, and really just exist so I can search for them quickly.

 1  $ recsel ~/LK/command.rec -q qr
 2
 3aim: Make a QR Code image:
 4cmd: qrencode 'https://play.google.com/store/apps/details?id=org.briarproject.briar.android' -o "$FILE".png
 5bin: qrencode
 6tag: qr
 7
 8aim: Make a QR Coded message in the terminal:
 9cmd: qrencode -t ansi "Hello World"
10bin: qrencode
11tag: qr
12
13aim: Read a QR Code image:
14cmd: zbarimg $FILE
15bin: qrencode
16tag: qr
17
18aim: Show wifi QR code (only with Network Manager):
19cmd: nmcli device wifi show-password
20bin: qrencode
21tag: qr
22tag: wifi

Links

If you want to check the project, it's available over ssh:

1ssh -p 2222 soft.dmz.rs -t lk

...or at Gitlab.

Thanks to the lazy bear for hosting a Vim carnival on knowledge bases. I can't wait to see what the other posts look like.

Appendix A

 1
 2#!/bin/sh
 3
 4set -e
 5
 6timeout=4
 7
 8find_links(){
 9    echo "$1"
10    sed '/```/,//d' "$1" | \
11    sed -nr 's/.*\[.+\]\(([^ )]+).*/\1/p ; s/^\[[^\^]*\]:\s(.*)/\1/p'
12}
13
14check_link(){
15    while read link; do
16        if [ "${link##*:*}" ]; then
17            check_file "$link" "$file"
18        else
19            prefix="${link%%:*}"
20            case "${prefix}" in 
21                http) check_http_link "$link"
22                ;;
23                https) check_http_link "$link"
24                ;;
25                gemini) check_gemini_link "$link"
26                ;;
27                mailto) echo "ignoring email: $link"
28                ;;
29                magnet) echo "ignoring torrent: $link"
30                ;;
31                *) echo "$file: unknown protocol $link"
32                ;;
33            esac
34        fi || dead_link_error "$link" "$file"
35    done
36}
37
38dead_link_error(){
39    echo "$2: $1"
40}
41
42check_file(){
43    path="${1#/}"
44    path="${path%#*}"
45    test -f "$path" \
46    || test -f "$path".md \
47    || (
48        cd "$(dirname "$2")" && \
49        test -f "$path" || test -f "$path".md
50    )
51}
52
53check_gemini_link(){
54	gemget --max-time "$timeout" "$1" -o- >/dev/null
55}
56
57check_http_link(){
58    timeout=$(( timeout + 1 ))
59	curl --connect-timeout "$timeout" -Is "$1" >/dev/null
60}
61
62check_markdown_links(){
63    find_links "$1" | check_link
64}
65
66#################
67
68test -z "$1" && targets="*.md" || targets="$@"
69
70for file in $targets; do
71    test "${file##*.}" != "md" || check_markdown_links "$file" &
72done
73
74wait

The Dependencies Map

A lot of work remains, but right now the dependencies map looks like this:

  1
  2                            ┌──────────────────────┐     ┌−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−┐     ┌−−−−−−−−−−−−−−−−−−−−┐
  3                                  GPG Basics       ──>               pass               ──>    pass with otp    
  4                            └──────────────────────┘     └−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−┘     └−−−−−−−−−−−−−−−−−−−−┘
  5                              
  6                              
  7                              
  8                            ┌−−−−−−−−−−−−−−−−−−−−−−┐
  9  ┌───────────────────────>      gpg with vim     
 10                           └−−−−−−−−−−−−−−−−−−−−−−┘
 11                           ┌──────────────────────┐     ┌−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−┐     ┌−−−−−−−−−−−−−−−−−−−−┐
 12                              Kubernetes Setup    ──>        Kubernetes Basics         ──>   Kubernetes Docs   
 13                           └──────────────────────┘     └−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−┘     └−−−−−−−−−−−−−−−−−−−−┘
 14                                                          
 15                                                          
 16                                                          
 17    ┌−−−−−−−−−−−−−−−−┐     ┌──────────────────────┐     ┌−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−┐
 18       virtualbox    <──    Managing Groups                    Proxy API            
 19    └−−−−−−−−−−−−−−−−┘     └──────────────────────┘     └−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−┘
 20                             
 21                             
 22                             
 23                           ┌−−−−−−−−−−−−−−−−−−−−−−┐     ┌−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−┐
 24                                   Docker         ──>       Ansible with Docker       
 25                           └−−−−−−−−−−−−−−−−−−−−−−┘     └−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−┘
 26                           ┌──────────────────────┐     ┌−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−┐                                ┌−−−−−−−−−−−−−−−−−−−−−−−−−−┐
 27                               LaTeX Packages     ──>             Calendar                                             nginx logs with recfiles  <───────────────────────────────────┐
 28                           └──────────────────────┘     └−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−┘                                └−−−−−−−−−−−−−−−−−−−−−−−−−−┘                                     
 29                                                                                                                                                                                          
 30                                                                                                                                                                                          
 31                                                                                                                                                                                          
 32                           ┌──────────────────────┐     ┌−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−┐     ┌────────────────────┐     ┌──────────────────────────┐     ┌−−−−−−−−−−−−−−−−−−−−−−−−−−−−┐  
 33                                  Makefile        ──>   Recfile Bibliography for TeX   <──         TeX                                         ──>  Board Games with Recfiles    
 34                           └──────────────────────┘     └−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−┘     └────────────────────┘                                    └−−−−−−−−−−−−−−−−−−−−−−−−−−−−┘  
 35                                                                                                                                                                                        
 36                                                          └──────────────────────────────────────────────────────────────          Recfiles                                              
 37                                                                                                                                                                                         
 38                           ┌──────────────────────┐     ┌−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−┐                                                               ┌−−−−−−−−−−−−−−−−−−−−−−−−−−−−┐  
 39                                 Makefiles        ──>  Python Projects with Makefiles                                                            ──>  IP Addresses with Recfiles   
 40                           └──────────────────────┘     └−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−┘                                └──────────────────────────┘     └−−−−−−−−−−−−−−−−−−−−−−−−−−−−┘  
 41                                                                                                                                                                                          
 42                                                                                                                                                                                          
 43                                                                                                                                                                                          
 44                           ┌──────────────────────┐     ┌−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−┐                                ┌−−−−−−−−−−−−−−−−−−−−−−−−−−┐                                     
 45                               Shell Scripts      ──>             Newsraft                                                     Recfixes                                              
 46                           └──────────────────────┘     └−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−┘                                └−−−−−−−−−−−−−−−−−−−−−−−−−−┘                                     
 47                           ┌──────────────────────┐     ┌−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−┐                                                                                                 
 48                                Taskwarrior       ──>    Taskwarrior Configuration                                                                                                     
 49                           └──────────────────────┘     └−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−┘                                                                                                 
 50                                                                                                                                                                                          
 51                                                                                                                                                                                          
 52                                                                                                                                                                                          
 53                           ┌−−−−−−−−−−−−−−−−−−−−−−┐                                                                                                                                        
 54                            Taskwarrior Contexts                                                                                                                                         
 55                           └−−−−−−−−−−−−−−−−−−−−−−┘                                                                                                                                        
 56                           ┌──────────────────────┐     ┌−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−┐                                                                                                 
 57                                  certbot         ──>        radicale and nginx        <─────┐                                                                                         
 58                           └──────────────────────┘     └−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−┘                                                                                                
 59                           ┌──────────────────────┐     ┌−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−┐                                                                                                
 60                                    cron          ──>          Search System                                                                                                          
 61                           └──────────────────────┘     └−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−┘                                                                                                
 62                                                                                                                                                                                          
 63                             ┌───────────────────────────────────────────────────────────────────┼──────────────────────────┐                                                              
 64                                                                                                                                                                                        
 65                           ┌──────────────────────┐     ┌−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−┐     ┌────────────────────┐     ┌−−−−−−−−−−−−−−−−−−−−−−−−−−┐                                     
 66                                                  ──>      Soft Serve Maintenance      <──                      ──>  Soft Serve through https                                      
 67                                                      └−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−┘                              └−−−−−−−−−−−−−−−−−−−−−−−−−−┘                                     
 68                                                                                                                                                                                       
 69                                    git           ────────────────────────────────────────┐         nginx         ─────────────────────────────────────────────────────────────────────┘
 70                                                                                                                
 71                                                      ┌−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−┐                             ┌−−−−−−−−−−−−−−−−−−−−−−−−−−┐
 72                                                  ──>            git stash                                     ──>         Soft-Serve        
 73                           └──────────────────────┘     └−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−┘    └────────────────────┘     └−−−−−−−−−−−−−−−−−−−−−−−−−−┘
 74                                                                                                                          
 75                                                                                           └───────────────────────────────┘
 76                             
 77                           ┌−−−−−−−−−−−−−−−−−−−−−−┐
 78                                  git-lfs        
 79                           └−−−−−−−−−−−−−−−−−−−−−−┘
 80                           ┌──────────────────────┐     ┌−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−┐     ┌────────────────────┐
 81                                 partitions       ──>           Install Arch           <──         time        
 82                           └──────────────────────┘     └−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−┘     └────────────────────┘
 83  
 84                             ┌───────────────────────────────────────────────────────────────────┐
 85                                                                                                
 86    ┌−−−−−−−−−−−−−−−−┐     ┌──────────────────────┐     ┌−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−┐     ┌−−−−−−−−−−−−−−−−−−−−┐
 87       ssh-tricks    <──          ssh           ──>          Ansible Basics                     sshfs        
 88    └−−−−−−−−−−−−−−−−┘     └──────────────────────┘     └−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−┘     └−−−−−−−−−−−−−−−−−−−−┘
 89                             
 90                             
 91                             
 92                           ┌−−−−−−−−−−−−−−−−−−−−−−┐
 93                                  fail2ban       
 94                           └−−−−−−−−−−−−−−−−−−−−−−┘
 95  
 96  └───────────────────────────┐
 97                              
 98     ┌−−−−−−−−−−−−−−−−┐     ┌─────────────────────────────────────────────────────────────┐     ┌−−−−−−−−−−−−−−−−−−−−┐
 99       vim linewrap   <──                                                               ──>  How to Learn `vim` 
100     └−−−−−−−−−−−−−−−−┘                                                                       └−−−−−−−−−−−−−−−−−−−−┘
101     ┌−−−−−−−−−−−−−−−−┐                                                                       ┌−−−−−−−−−−−−−−−−−−−−┐
102      vim navigation  <──                          vim basics                           ──>   find and replace  
103     └−−−−−−−−−−−−−−−−┘                                                                       └−−−−−−−−−−−−−−−−−−−−┘
104     ┌−−−−−−−−−−−−−−−−┐                                                                       ┌−−−−−−−−−−−−−−−−−−−−┐
105       vim windows    <──                                                               ──>        sc-im        
106     └−−−−−−−−−−−−−−−−┘     └─────────────────────────────────────────────────────────────┘     └−−−−−−−−−−−−−−−−−−−−┘
107                                                          
108                                                          
109                                                          
110                            ┌−−−−−−−−−−−−−−−−−−−−−−┐     ┌−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−┐
111                                vim completion                   vim in bash           
112                            └−−−−−−−−−−−−−−−−−−−−−−┘     └−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−┘

  1. My favourite reader is mdless (which is just a link to mdcat -p). ↩︎