🤲

Vimの<CMD>マップでrangeを使う

2022/09/28に公開

以下のEchoLinesコマンドおよびマッピングがあるとします。
rangeを受け付け、ノーマルモードでは現在のカーソル位置の行番号が、ビジュアルモードでは現在の選択範囲の行番号が、それぞれ表示されることを期待しています。

function! s:echo_lines(from, to) abort
  let from = a:from
  let to = a:to

  let lnum = from
  while lnum <= to
    echo 'L' .. lnum
    let lnum += 1
  endwhile
endfunction
command! -range EchoLines call s:echo_lines(<line1>, <line2>)

nnoremap <C-l> <Cmd>EchoLines<CR>
xnoremap <C-l> <Cmd>EchoLines<CR>

コマンドとマッピングの動作は以下のようになります。

  • ノーマルモードで:EchoLinesを実行 → 現在の行番号が表示される ✅
  • ノーマルモードで<C-l>を実行 → 現在の行番号が表示される ✅
  • ビジュアルモードで選択し、:'<,'>EchoLinesを実行 → 選択範囲の行番号が表示される ✅
  • ビジュアルモードで選択し、<C-l>を実行 → 現在の行番号が表示される

<Cmd>マッピングでは範囲を渡せないので、<C-l>押下時に期待した動作をしません。

実行される関数内部でline('v')を使用することで、これを解決できます。

function! s:echo_lines(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 lnum = from
  while lnum <= to
    echo 'L' .. lnum
    let lnum += 1
  endwhile
endfunction
command! -range EchoLines call s:echo_lines(<line1>, <line2>)

nnoremap <C-l> <Cmd>EchoLines<CR>
xnoremap <C-l> <Cmd>EchoLines<CR>

line('v')を使って「選択領域の反対側のカーソル位置」を取得できるので、現在の選択範囲を算出できます。
from == to && from != anotherの条件を入れているので、ノーマルモードで実行した場合や、明示的にrangeが指定された場合には影響しません。

Discussion