koturnの日記

普通の人です.ブログ上のコードはコピペ自由です.

Vimのmessages履歴について

Vim:messages とコマンドを実行すると, :echomsg:echoerr から出力されたメッセージ履歴を閲覧することができる. :help :messages によると,tinyバージョンであれば,過去のメッセージを20行,それ以外だと200行閲覧することができる. しかし,最新のメッセージのみを閲覧したい場合,最初の方のメッセージを邪魔に感じることがある.

ターミナルの clear コマンドのように,過去のメッセージを消去するには,

function! s:clear_messages() abort
  for i in range(201)
    echomsg ''
  endfor
endfunction
command! -bar Clear  call s:clear_messages()

とコマンドを定義し, :Clear とコマンドを実行するとよい. 201回ではなく200回空文字列を出力した場合,1行分クリアできないことがあるので注意しよう.

ちなみに,

function! s:clear_message() abort
  for i in range(201)
    echomsg ' '
  endfor
endfunction
command! -bar Clear  call s:clear_message()

のように,201回半角スペースを出力した場合,201行の半角のスペースが連続してしまい,視覚的な負担は減るが,縦長の履歴になり,煩わしいことに変わりはない.

これだけでは芸が無い(この記事を書いてから,全く同じ内容の記事があることに気がついた)ので, head コマンドや tail コマンドのように,最初からxx行,最後のxx行のmessages履歴を表示できるコマンドを定義しよう.

function! s:redir(cmd) abort
  let [verbose, verbosefile] = [&verbose, &verbosefile]
  set verbose=0 verbosefile=
  redir => str
    execute 'silent!' a:cmd
  redir END
  let [&verbose, &verbosefile] = [verbose, verbosefile]
  return str
endfunction

function! s:echo(use_echomsg, lines) abort
  if a:use_echomsg
    for line in a:lines
      echomsg line
    endfor
  else
    for line in a:lines
      echo line
    endfor
  endif
endfunction

function! s:messages_head(has_bang, ...) abort
  let n = a:0 > 0 ? a:1 : 10
  let lines = filter(split(s:redir('messages'), "\n"), 'v:val !=# ""')[: n]
  call s:echo(a:has_bang, lines)
endfunction
command! -bar -bang -nargs=? MessagesHead  call s:messages_head(<bang>0, <f-args>)

function! s:messages_tail(has_bang, ...) abort
  let n = a:0 > 0 ? a:1 : 10
  let lines = filter(split(s:redir('messages'), "\n"), 'v:val !=# ""')
  if n > len(lines)
    let n = len(lines)
  endif
  let lines = lines[len(lines) - n :]
  call s:echo(a:has_bang, lines)
endfunction
command! -bar -bang -nargs=? MessagesTail  call s:messages_tail(<bang>0, <f-args>)

:MessagesHead 5:MessagesTail 20 のように,引数に指定した行数だけ, :messages の履歴を表示する. 引数に指定しなかった場合は,デフォルト値として,10行だけ表示するようになっている. ! を付加してコマンドを実行した場合は, echo ではなく, echomsg で出力する.

以下はおまけの話だ.

もし,第二引数にハイライトグループを指定したいなら,次のようにするとよい.

function! s:redir(cmd) abort
  let [verbose, verbosefile] = [&verbose, &verbosefile]
  set verbose=0 verbosefile=
  redir => str
    execute 'silent!' a:cmd
  redir END
  let [&verbose, &verbosefile] = [verbose, verbosefile]
  return str
endfunction

function! s:echo(use_echomsg, lines) abort
  if a:use_echomsg
    for line in a:lines
      echomsg line
    endfor
  else
    for line in a:lines
      echo line
    endfor
  endif
endfunction

" ハイライトグループを補完する関数
" 2つ目の引数でないと,補完候補を返却しない
" -complete=highlight では2つ目の引数のみ補完という動作が実現できないので,エミュレート関数を用意した
function! s:complete_messages(arglead, cmdline, cursorpos)
  let nargs = a:cmdline ==# '' ? 1 : len(split(split(a:cmdline, '[^\\]\zs|')[-1], '\s\+'))
  if nargs == 2 || (nargs == 3 && a:arglead !=# '')
    let _arglead = tolower(a:arglead)
    return sort(filter(map(split(s:redir('highlight'), "\n"), 'split(v:val, "\\s\\+")[0]'),
          \ '!stridx(tolower(v:val), _arglead)'))
  else
    return []
  endif
endfunction

function! s:messages_head(has_bang, ...)
  let n = a:0 > 0 ? a:1 : 10
  let lines = filter(split(s:redir('messages'), "\n"), 'v:val !=# ""')[: n]
  if a:0 > 1
    execute 'echohl' a:2
  endif
  call s:echo(a:has_bang, lines)
  if a:0 > 1
    echohl None
  endif
endfunction
command! -bar -bang -nargs=+ -complete=customlist,s:complete_messages MessagesHead
      \ call s:messages_head(<bang>0, <f-args>)

function! s:messages_tail(has_bang, ...)
  let n = a:0 > 0 ? a:1 : 10
  let lines = filter(split(s:redir('messages'), "\n"), 'v:val !=# ""')
  if n > len(lines)
    let n = len(lines)
  endif
  if a:0 > 1
    execute 'echohl' a:2
  endif
  let lines = lines[len(lines) - n :]
  call s:echo(a:has_bang, lines)
  if a:0 > 1
    echohl None
  endif
endfunction
command! -bar -bang -nargs=+ -complete=customlist,s:complete_messages MessagesTail
      \ call s:messages_tail(<bang>0, <f-args>)

2つ目の引数の補完時のみ,ハイライトグループの補完ができる. すなわち,

:MessagesHead 15 E<Tab>

:MessagesTail 25 <Tab>

<Tab> を入力すると,ハイライトグループが補完される.

参考