switching from joplin to vimwiki
i've been using joplin for a while now, and have been mostly happy with it. it features a simple but sufficient vim keyboard mode, notes are encrypted, and images are easily pasted in markdown.
all in all this is great, but the following pain points made me reconsider this solution:
the installation and update process is painful on linux: you need to run some shell script to get a proper install with shortcuts and icons, or you're stuck with an appimage and all the related issues accompanying it. you could also use flatpak, but at the end of the day i'd just rather
sudo apt install joplin
and move on with my life.uninstallation is even more painful. have fun running
sudo find / -iname "*joplin*" 2>/dev/null
and sort things out on your own, which is sadly a common occurence with linux apps.no autocapitalisation. this is not a very common feature with most text editors, which i find surprising because this doesn't seem hard to implement?
joplin cloud plans. i'm not interested in this and just wish to keep things simple. syncing files with syncthing is free and works amazingly well.
having to use a GUI to write text. joplin does have a CLI interface, but learning a whole new system that mixes up custom commands with only a subset of vim commands doesn't sound ideal to me.
and last but certainly not least: electron. the only apps built with it that didn't end up eating a frightening amount of RAM on my systems are vscode and discord, no other exceptions. i'm not too eager to sacrifice this just to simply type text.
vimwiki has been around for a while and has always intrigued me. as the name implies, this plugin allows you to maintain your own wiki via linked text files, with markdown support. but i would also need the following features to truly make it similar to joplin:
- seamless note encryption
- syntax highlighting
- markdown preview
- easy way of pasting / linking images in markdown notes
- autocapitalisation
- easy syncing (already covered by syncthing)
seems like some kind of deranged dream to expect all of these features from vim, but this is very doable with the following plugins:
- vim-markdown: https://github.com/plasticboy/vim-markdown
- markdown-preview: https://github.com/iamcco/markdown-preview.nvim
- markdown-image-paste: https://github.com/ferrine/md-img-paste.vim
note encryption will be done via some vim scripting. let's roll and set up everything!
plugins installation
first things first, let's install all the plugins with your .vimrc
:
Plug 'plasticboy/vim-markdown', { 'for': 'markdown' }
Plug 'ferrine/md-img-paste.vim', { 'for': 'markdown' }
Plug 'vimwiki/vimwiki'
Plug 'iamcco/markdown-preview.nvim', { 'do': { -> mkdp#util#install() }, 'for': ['markdown', 'vim-plug']}
Plug 'junegunn/goyo.vim'
goyo is optional but provides an amazing, minimalist interface for notes — i highly recommend it. note that the following snippet is based on vim-plug: adapt it accordingly if you use another plugin manager, and run the appropriate install command (:PlugInstall
in my case).
setting up encryption with markdown
let's populate our .vimrc with a few more settings to get seamless encryption. first we're going to assign the .md.gpg extension to the markdown filetype, so vim can apply highlighting and other features like it normally would:
au BufEnter *.md.gpg setlocal filetype=markdown
the seamless editing of .gpg files will be handled by an edited mix of the following scripts:
- https://vim.fandom.com/wiki/Encryption
- https://vim.fandom.com/wiki/Edit_gpg_encrypted_files#Comments
be aware that nothing is foolproof here, as vim uses temporary files when dealing with data from external programs. while those scripts harden everything as much as possible, you still might be able to retrieve some of your data with forensics—which is an accepted risk in my case.
one issue with those scripts used in conjunction with vimwiki is the management of buffers: vimwiki will open tons of them, and closing / saving them individually can prevent remaining files to remain tightly encrypted at rest.
the most practical solution i found is to always quit the wiki entirely, trust its autosaving feature and treat the bunch of buffers as a unique group this way. as such and to enforce this, i opted to remap :wq
, :q
, and disable :w
and :q
for the .md.gpg
filetype exclusively:
au VimEnter *.md.gpg execute ":cabbrev wq wqa"
au VimEnter *.md.gpg execute ":cabbrev q qa"
au VimEnter *.md.gpg execute ":cabbrev w <Nop>"
au VimEnter *.md.gpg execute ":cabbrev wa <Nop>"
some people will find this to be cursed, and if you hate autosaving i feel you. on my end that solution works perfectly but i'm always open to suggestions!
next comes the automatic handling and editing of gpg files:
" first make sure nothing is written to ~/.viminfo or backups while editing
" an encrypted file.
set backupskip+=*.gpg
set viminfo=
augroup encrypted
au!
" disable swap file, undo file and backups, and set binary file format
" before reading the file
autocmd BufReadPre,FileReadPre *.gpg
\ setlocal noswapfile noundofile nobackup bin
" decrypt contents after reading the file, reset binary file format
" and run any BufReadPost autocmds matching the filename without .gpg
autocmd BufReadPost,FileReadPost *.gpg
\ execute "%!gpg --decrypt --default-recipient-self 2>/dev/null" |
\ setlocal nobin |
\ execute "redraw!" |
\ execute "doautocmd BufReadPost " . expand("%:r")
" set binary file format and encrypt contents when leaving vim
autocmd Vimleave *.gpg
\ setlocal bin |
\ bufdo execute "%!gpg --encrypt --armor --default-recipient-self"
" contrary to the original scripts, we are not handling an undo command
" to revert encryption in buffer: vimwiki auto-saves contents when quitting,
" so we rather leave it at that and disable :w and :wa for .md.gpg files.
" if you don't like auto-saving, sorry!
augroup END
you will need to have a GPG keypair present on your system for this to work. if you don't already have one, here's how to do it:
- run
gpg --full-generate-key
- select the default key type at the prompt
- pick at least a key size of 4096 bits
- set your key to never expire, which is the default length of time at the prompt
- check your settings
- add your id information
- provide a strong passphrase when prompted
if you'd rather go crazy with ECC and ED25519 (which i encourage you to do), you can do the following instead:
- run
gpg --expert --full-generate-key
- pick
ECC and ECC
- pick
Curve 25519
- follow the same instructions as before
this part should be working now! create a .md.gpg
file, open it with vim, insert something and save it. if you cat
the file, this should return the encrypted, ascii-armored content which roughly looks like the following:
-----BEGIN PGP MESSAGE-----
hQIMA4RrtUt4dcSyAQ/8DAdDoYkJE8d4kq+dsZykX9qR2cEVjy99RLaaeT22HffP
SKc3zKXMz6IDVEx1+KBD0zsDR1EZA6tiOoXwqjZnU/CXwuGpE6yYrIjQZiyVsDhV
zeYupkji0z8JTg8QfAfgW2TvEpctuX7DaXyn26qmg6x1KVhmxDAaaaqQzJIu2dXg
mM8gnU8Qgx0/hPUf9Axm4lTU/iqZLEFOr+ysRn7BX4OSnkfWUIKGOASLqX7l42/+
s8YxKRSsJEAf/vdUxgEABTRKQ7Z/ciMn7Lp8gqz4hhLvgwG7gZz6dYgO5f3F0L+h
oMJnTKOHxkcYPRJODA7jclYqTapSkHXTs1Gl0/d2vjuzYOS9izs6p+iG5eXaZ93a
83gc4FgVAsxnLPx+M8KmmgQBM2f91NUbYdcA/8sGmzSpHBm7vVhCqm9iZLmqFgtW
C2Ygwek5KTNuQ0kNYaGz88047oaQn6jYTR146YWxZBkTOO/9xsEQxlrwBOVLW9vX
qpnRI6Bj0c7Gnb5eh3TBBKJyfKB1r41PBzAoMCr4LBFNjJhtFQmrcV/h+6kqYpE9
xSju2Ux72H3Nq1UBmGO8u2Or8BgQXRnWmTjaGFKetUac1KuHVdSl6gkTkK2TVSJh
dwMiPavB4hf6onJqGlW9746glNTRdfmHU9Bvsz6iBJ3HV6EOTCG2XLyU2fr6vvXS
UQEWWmozZNWc75PUQBSwXDeLUTZU7n3QG0O2dx6brjxbx0hrv3oYYAersvX3NrPK
mgkSHV0XVcSVvG4uLma1ilDMWKb9+DjiZDt2FvqVFV7E3A==
=zjQv
-----END PGP MESSAGE-----
open it again with vim, and you should see the plaintext message. perfect, moving on!
setting up vimwiki
by default, vimwiki uses its own syntax. markdown is however supported with a few option changes.
the plugin also needs some specific settings to be set, so let's do both:
set nocompatible
filetypelugin on
syntax on
let g:vimwiki_list = [{'path': '~/wiki/',
\ 'syntax': 'markdown', 'ext': '.md.gpg'}]
now vimwiki will identify .md.gpg
as its default filetype, and the directory for your entire wiki will be located at ~/wiki/
. easy enough.
learning everything about vimwiki is beyond the scope of this post, but we can still mention a few things. first, you need to create an index by running vim
and press <Leader>ww
.
edit the document as you like, and press <enter>
with your cursor placed on a word of your choosing: you just made a link! if you press <enter>
again, you'll open a new file which you can edit to your linking. press backspace, and you're back to your index.
this hierarchical way of handling files makes vimwiki very suitable for personal notes and even a zettelkasten system (there is an additional plugin for it). you can learn more about it in the repo's README and vimwiki's wiki.
we're done with setting the plugin, but these are some options i like to tweak:
" automatically insert a header when creating a new link
let g:vimwiki_auto_header = 1
" use underscores to replace spaces in the file names
let g:vimwiki_links_space_char = '_'
" disable all concealing
let g:vimwiki_conceal_onechar_markers = 0
" disable URL shortening
let g:vimwiki_url_maxsave = 0
" don't load vimwiki for markdown files located somewhere else
let g:vimwiki_global_ext = 0
" bold headers
hi VimwikiHeader1 cterm=bold gui=bold
hi VimwikiHeader2 cterm=bold gui=bold
hi VimwikiHeader3 cterm=bold gui=bold
hi VimwikiHeader4 cterm=bold gui=bold
hi VimwikiHeader5 cterm=bold gui=bold
hi VimwikiHeader6 cterm=bold gui=bold
autocapitalisation
this feature will be quick to implement. the following function will handle autocapitalisation wherever we want to:
func! AutoCapitalisation()
augroup SENTENCES
au!
autocmd InsertCharPre * if search('\v(%^|[.!?]\_s+|\_^\-\s|\_^\*\s|\_^#+\s|\n\n)%#', 'bcnw') != 0 | let v:char = toupper(v:char) | endif
augroup END
endfu
it has been tweaked from the following source and does a really nice job:
(thanks david moody for sharing it!)
to enable the function with vimwiki, all we need to do is the following:
autocmd FileType markdown call AutoCapitalisation()
easily pasting images in markdown files
the md-img-paste
plugin uses xclip
to work, so let's install it first:
sudo apt install xclip
the following options can be tweaked to your liking. by default, the plugin will create your image folder at the same location of the currently edited file, which works great for us:
autocmd FileType markdown nmap <buffer><silent> <leader>, :call mdip#MarkdownClipboardImage()<CR>
" there are some defaults for image directory and image name, you can change them
let g:mdip_imgdir = 'images'
let g:mdip_imgname = 'image'
markdown preview
this plugin works splendidly well out of the box. just use :MarkdownPreview
for a live preview and :MarkdownPreviewStop
to stop the service. i only added this shortcut for easy toggling:
autocmd FileType markdown nmap <leader>n :MarkdownPreviewToggle<CR>
it also supports preview of diagrams written with mermaid, katex, and others, really an amazing plugin.
optional (but amazing): goyo
goyo is a vim plugin that provides a clutter-free interface for writing in vim: i really enjoyed using it for a long time now, and nothing else quite matched the experience. i recommend checking out the GitHub repo to get a general feel of the interface.
if you want to use it automatically with vimwiki, add the following line in your .vimrc
:
au VimEnter *.md.gpg execute ":Goyo"
caveats
switching to vimwiki has a few caveats, at least with my current settings:
- file names are not encrypted
- images are not encrypted
- encrypting things through vim is... a peculiar experience to say the least
but in my use case, i can live with that.
i'm always open to suggestions and my vim scripting is not the best, but so far the experience is really pleasant. being able to quickly write notes from the CLI with the full power of vim available is way better than any alternative i could find!
tl;dr: just give me the script!
sure thing: https://gist.github.com/ovelny/72659e841c1dbcee173eb244c8609252
~ want to leave a comment about this post? you can send me a message on curiouscat without an account, or reply on twitter if you like!