Vim and Note-taking/Writing

· 03.11.2021 · etc

A while ago I posted about research tools I've built and a few parts focused on enhancements to vim (taking screenshots, opening media and links, automatically formatting text from pdfs), which is still my preferred program for writing and note-taking. Here are a few other enhancements that make it a more pleasant experience.

I have all of the following snippets enabled for markdown only (in ~/.vim/ftplugin/markdown.vim).

Spelling correction

I still make a lot of typos and don't notice until a bit later. This snippet lets you hit <ctrl-f> while in insert or normal mode to automatically fix the closest speling error. This is an example.

setlocal spell

" quickly fix the closest previous spelling error.
imap <c-f> <c-g>u<Esc>[s1z=`]a<c-g>u
nmap <c-f> [s1z=``

The only shortcoming is that the default vim dictionary is really lacking, so a lot of common words come up as spelling errors. You can easily add words to the dictionary by moving over them and hitting zg by default.

Quickly fixing a typo in vim

Preview markdown as HTML

Sometimes it's easier to read through a markdown file as HTML. This lets you use <leader>v to compile the file and open it in a browser using nom.

" compile and open in browser
nnoremap <leader>v :r !nom view "%:p"<cr>

Previewing markdown with nom

Create footnote from text

I try my best to keep my citations organized in anything public-facing, but if you have a lot of footnotes it can be troublesome to manage them all in markdown. This lets me highlight a citation, hit <ctrl-f>, and then fills my clipboard with a markdown-formatted citation to paste in.

" make footnote from selected text
vnoremap <C-f> y:! cite "<C-r>0" \| xsel -b<cr>

Automatically generating a markdown footnote from a selection

Writing mode

One challenge with writing in vim is that the text width can be too wide. There's a popular plugin called goyo that helps with this by artificially constraining the current vim window to a smaller number of columns, but I always had issues with it. Fortunately I found a much simpler implementation that I adapted below.

" writing mode
" <https://stackoverflow.com/a/59955784>
function! ToggleWriteMode()
  let l:name = '_writeroom_'
  if bufwinnr(l:name) > 0
    colorscheme dark
    :bwipeout _writeroom_
  else
    colorscheme light

    " hide vertical split
    hi VertSplit ctermfg=bg ctermbg=NONE cterm=NONE

    " auto-close writeroom buffers when the text buffer closes
    autocmd QuitPre <buffer> :bwipeout _writeroom_

    " target column width
    let l:target = 90
    let l:width = (&columns - l:target) / 2
    silent! execute 'topleft' l:width . 'vsplit +setlocal\ nobuflisted' l:name | wincmd p
    silent! execute 'botright' l:width . 'vsplit +setlocal\ nobuflisted' l:name | wincmd p
    endif
endfunction
nnoremap <silent> <leader>w :call ToggleWriteMode()<cr>

Toggling writing mode

Table of contents navigation

In vim it's harder to get a sense of the high-level document structure and navigate through sections. There's a plugin called VOoM that basically adds a table of contents pane for markdown documents. It's nice but a bit cumbersome to jump to it and make your section selection there.

Instead I've set up a way to pull up section headings and quickly jump to them using the fzf.vim plugin (which I'll describe in more detail in the next section) and ripgrep.

" Table of Contents (for markdown files)
" Jump to line match from ripgrep
" Expects that the line is delimited with ':'
" and the first field is the line number.
function! s:line_handler(line)
    let keys = split(a:line, ':')
    execute "normal! " . keys[0] . "gg"
endfunction

" Args
" - Source: user ripgrep to search for one or more '#' at the start of a line
"   in the current file
" - Sink: use the line hanlder function above to jump to the selected line
" - Options:
"   - reverse output
"   - split on ':' and take all fields from the 2nd on
"   (i.e. skip the line number)
command! -bang -complete=dir -nargs=? TOC
    \ call fzf#run(fzf#wrap('toc', {
        \'source': 'rg -Tcsv --line-number --no-heading "^#+" '.expand('%:p'),
        \'sink': function('s:line_handler'),
        \'options': '--reverse --delimiter=: --with-nth=2..'
    \}, <bang>0))

nnoremap <leader>e :TOC<cr>

Pulling up the table of contents and jumping to a section

fzf.vim

I put very little effort into keeping my notes organized these days because of fzf.vim. It makes it very easy to quickly search through files by filename or contents from within vim (using ripgrep).

Using fzf.vim to quickly search files by filename and contents

You can get a pseudo-wiki functionality with the following, which searches files for the word under the cursor:

" Search for word under cursor
nnoremap <silent> <Leader>g :Rg <C-R><C-W><CR>

Using fzf.vim to quickly search files for the word under the cursor

Honorable mention: Opening files

This isn't anything I've added to vim, but is really useful. If your cursor is over a file path, you can hit gf to open that file in vim. It gives you a wiki-like functionality (though more cumbersome) or a way of making indices for research topics.

An example project index (gf to open file under cursor and <ctrl-o> to jump back to the index)