Haskell用vimファイル

Haskell用vimファイルあれこれをもらってきて使っているうちに,が空気を読めるといいなと考え ftplugin/haskell.vim を改造しました.


Vimに馴染みがない人のための手抜き説明:
挿入モード中に,

  • Ctrl-F 前後を見て再インデント
  • Ctrl-T インデントを1段深く
  • Ctrl-D インデントを1段浅く


Haskellに馴染みがない人のための手抜き説明:
Haskellは文法にインデントブロックを採用しています.
インデントの深さが同じ行ごとにまとまりとみなすのですが,
たとえば,

where c n r = product [n-r+1 .. n] `div` fact r
      fact n = product [1 .. n]

のような位置揃えが要求されるため,固定幅のインデント増減だけでは対応しにくいことになります.


それから数ヶ月,未だにvim scriptが良く分かっていないのですが,とりあえず自分で使っていてそれなりに便利だと思うので公開します.
リンク先の indent/haskell.vim がないとおそらく使えないので,注意.


ftplugin/haskell.vim

setlocal expandtab
setlocal noignorecase
setlocal suffixesadd=.hs
setlocal cpoptions+=M

setlocal shiftwidth=1

"iabbrev case case of<Left><Left><Left>
"iabbrev W where
"iabbrev M module
"iabbrev In instance
"iabbrev Im import

"compiler ghc

"inoremap <C-T> _<BS><ESC>:call <SID>Indent()<CR>i
inoremap <C-T> _<ESC>:call <SID>Indent()<CR>a<BS>
inoremap <C-D> _<ESC>:call <SID>Unindent()<CR>a<BS>

function! s:Indent()
    let pos = getpos('.')
    let lnum = pos[1]
    let cnum = pos[2]

    if lnum == 1
        return
    endif

    let line = getline('.')

    " if line =~ '^\s*$' | execute 'normal a ' | endif
    normal ^kW

    let indent_pos = getpos('.')
    if indent_pos[1] == lnum - 1
	normal j
    else
	call cursor(lnum, 0)
	normal ^
	let indent_pos = getpos('.')
	let indent_pos[2] += g:haskell_indent_other
        " call cursor(lnum - 1, 0)
        " normal ^
        " let indent_pos = getpos('.')
    endif

    " s/^\s*/\=repeat(' ', line =~ '^\s*$' ? indent_pos[2] : indent_pos[2] - 1)/
    s/^\s*/\=repeat(' ', indent_pos[2] - 1)/
    let new_line = getline('.')

    call cursor(lnum, cnum + len(new_line) - len(line))

endfunction

function! s:Unindent()
    let pos = getpos('.')
    let lnum = pos[1]
    let cnum = pos[2]

    if lnum == 1
        return
    endif

    let line = getline('.')

    " if line =~ '^\s*$' | execute 'normal a ' | endif
    normal ^
    let cnum2 = getpos('.')[2]

    if cnum2 == 1
        call cursor(lnum, cnum)
        return
    endif

    normal B
    let indent_pos = getpos('.')
    while indent_pos[2] >= cnum2
	normal B
	let indent_pos = getpos('.')
    endwhile

    call cursor(lnum, 0)

    " s/^\s*/\=repeat(' ', line =~ '^\s*$' ? indent_pos[2] : indent_pos[2] - 1)/
    s/^\s*/\=repeat(' ', indent_pos[2] - 1)/
    let new_line = getline('.')

    call cursor(lnum, cnum + len(new_line) - len(line))

endfunction


主な改変箇所

  • で,適切にインデントを減らすように.
  • それに伴い,は右端から戻らずさらに右へ行くように.
  • shiftwidthを1にして,<,>でインデントの深さを調整しやすくした.
  • 行が空行のときに(自分の環境では?)バグらしかったので,そうならないよう修正.
    • でも,インデントの途中にカーソルがいるときの挙動はおかしくなります.そうは使わないけれど.


ツッコミなどあればお願いします.