👉

Vimでmarkdownの箇条書き

2022/09/26に公開
- 箇条書き
- 箇条書き

1. 番号リスト
1. 番号リスト

> 引用
> 引用
> > 入れ子になった引用
> > 入れ子になった引用

  • 箇条書き
  • 箇条書き
  1. 番号リスト
  2. 番号リスト

引用
引用

入れ子になった引用
入れ子になった引用

こういう箇条書きをvimで書きやすくする設定を紹介します。

改行したときに、自動的に箇条書きが継続されるようになります。

改行したときに自動的に箇条書きを継続する設定

オプションのcommentsformatoptionsを調整します。
markdown限定の処理なので、(runtimepath)/after/ftplugin/markdown.vimに定義してください。

after/ftplugin/markdown.vim
setlocal comments=b:*,b:-,b:+,b:1.,nb:>
setlocal formatoptions-=c formatoptions+=jro

commentsの設定(:h format-comments

  • :の後ろ: コメント文字列
  • :の前: フラグ文字列
    • b: コメント文字列の後ろにホワイトスペースを必要とする指定(-1などを除外するため)
    • n: 入れ子を許可する指定

formatoptionsの設定(:h fo-table

  • j: ノーマルモードでJで行を連結するとき、連結された行の先頭のコメント文字列を削除する
  • r: 挿入モードで改行するとき、行頭に現在のコメント文字列を自動挿入する
  • o: ノーマルモードでo Oで挿入モードに入るとき、行頭に現在のコメント文字列を自動挿入する
  • c: textwidthで自動改行する

<CR>のマッピングを操作しないため、補完プラグインなどのマッピングに影響されず使用可能です。

チェックボックス記法への対応

リストに続いて[ ]または[x]を入力するとチェックボックスになる記法があります。リストマーカー直後以外の場所や、[] [a]などではチェックボックスになりません。

[ ] not check box
- list [ ] not checkbox
- [] not check box
- [a] not check box
- [ ] blank box
- [x] checked

[ ] not check box

  • list [ ] not checkbox
  • [] not check box
  • [a] not check box
  • blank box
  • checked

この場合もcommentsオプションで設定できますが、チェックボックス記法を通常のリストより先に指定します。また、スペースをバックスラッシュでエスケープする必要があります。
ここではリストマーカー(* + -)を全種類定義していますが、-しか使わないよ、といった場合は適宜減らしてください。

after/ftplugin/markdown.vim
setlocal comments=nb:>
        \ comments+=b:*\ [\ ],b:*\ [x],b:*
        \ comments+=b:+\ [\ ],b:+\ [x],b:+
        \ comments+=b:-\ [\ ],b:-\ [x],b:-
        \ comments+=b:1.\ [\ ],b:1.\ [x],b:1.
        \ formatoptions-=c formatoptions+=jro

チェックボックスを切り替えるマッピング

こちらの:MarkdownCheckboxコマンドを定義すれば、上記のチェックボックスの切り替えが簡単になります。

after/ftplugin/markdown.vim
function! s:markdown_checkbox(from, to) abort
  let from = a:from
  let to = a:to

  let another = line('v')
  if from == to && from != another
    if another < from
      let from = another
    else
      let to = another
    endif
  endif

  let curpos = getcursorcharpos()

  let lnum = from
  while lnum <= to
    let line = getline(lnum)

    let list_pattern = '\v^\s*([*+-]|\d+\.)\s+'
    if line !~ list_pattern
      " not list -> add list marker and blank box
      let line = substitute(line, '\v\S|$', '- [ ] \0', '')
      if lnum == curpos[1]
        let curpos[2] += 6
      endif
    elseif line =~ list_pattern .. '\[ \]\s+'
      " blank box -> check
      let line = substitute(line, '\[ \]', '[x]', '')
    elseif line =~ list_pattern .. '\[x\]\s+'
      " checked box -> uncheck
      let line = substitute(line, '\[x\]', '[ ]', '')
    else
      " list but no box -> add box after list marker
      let line = substitute(line, '\v\S+', '\0 [ ]', '')
      if lnum == curpos[1]
        let curpos[2] += 4
      endif
    endif

    call setline(lnum, line)
    let lnum += 1
  endwhile
  call setcursorcharpos(curpos[1], curpos[2])
endfunction
command! -buffer -range MarkdownCheckbox call s:markdown_checkbox(<line1>, <line2>)
  • リストでない場合 → リストとボックスを追加
  • リストだがチェックボックスがない場合 → ボックスを追加
  • チェックされていないボックスの場合 → チェック
  • チェックされたボックスの場合 → チェック解除

前述のcomments指定と合わせて、ftpluginでbuffer限定でマッピングを追加するのがおすすめです。以下では<C-CR>に設定しています。

after/ftplugin/markdown.vim
" MarkdownCheckboxの定義は省略
" setlocal commentsの定義は省略

nnoremap <buffer> <C-CR> <Cmd>MarkdownCheckbox<CR>
inoremap <buffer> <C-CR> <Cmd>MarkdownCheckbox<CR>
xnoremap <buffer> <C-CR> <Cmd>MarkdownCheckbox<CR>

normal/insert/visualの各モードで、<C-CR>でチェックボックスを切り替えられるようになります。

なお、visualモードに対応したマッピングについては以下の記事をご覧ください。

https://zenn.dev/kawarimidoll/articles/014973ba5b2169

Discussion