tools/vim

Learn Vimscript The Hard Way - Grep Operator, Part Threee

seul chan 2020. 4. 13. 21:51

Case Study: Grep Operator, Part Three

우리가 만든 "grep operator"는 잘 작동하지만, 두가지를 더 추가하여 vim 생태계에서 잘 사용되게 해보자

Saving Registers

이전에 만들었던 명령어

nnoremap <leader>g :set operatorfunc=GrepOperator<cr>g@
vnoremap <leader>g :<c-u>call GrepOperator(visualmode())<cr>

function! GrepOperator(type)
    if a:type ==# 'v'
        normal! `<v`>y"
    elseif a:type ==# 'char'
        normal! `[y`]
    else
        return
    endif

    silent execute "grep! -R " . shellescape(@@) . " ."
    copen
endfunction

이전에 만든 명령어에서 우리는 unnamed register를 사용하였다 - 이는 이전에 사용자가 사용하던 register를 제거해버리게 된다. 뿐만 아니라 visual select를 이용함으로서 가장 최근에 사용한 visual selection도 덮어씌워진다.

이는 사용자 입장에서는 좋지 않기 때문에 이를 방지해보자

nnoremap <leader>g :set operatorfunc=GrepOperator<cr>g@
vnoremap <leader>g :<c-u>call GrepOperator(visualmode())<cr>

function! GrepOperator(type)
    let saved_unnamed_register = @@

    if a:type ==# 'v'
        normal! `<v`>y"
    elseif a:type ==# 'char'
        normal! `[y`]
    else
        return
    endif

    silent execute "grep! -R " . shellescape(@@) . " ."
    copen

    let @@ = saved_unnamed_register
endfunction

기존 명령어와 달라진 점은 시작과 끝에 let을 사용하여 변수를 지정해 준 것 뿐이다. 처음에 우리는 @@에 있는 내용을 saved_unnamed_register 변수에 할당한다.

그리고 if문에서 복사한 내용을 이후에 다시 기존 register로 덮어씌워준다

이제 기존에 복사한 텍스트를 <leader>g 명령어가 덮어씌우지 않는 것을 볼 수 있다.

혹시 os의 클립보드와 unnamed register를 공유하는 경우에는 위 내용이 적용되지 않는다. 나도 평소에 set clipboard=unnamed를 추가하여 vim의 unnamed 레지스터를 clipboard로 사용하는데, 이럴 경우 <leader>g 안에서 복사한 텍스트는 clipboard register에 적용되지 않기 때문으로 보인다. 기존과 같이 :set clipboard="*로 변경시킨 후 실행해보면 정상적으로 작동하는 것을 볼 수 있다. (:set clipbaord=unnamedplus를 사용하고 있다면 문제가 없다)

Namespacing

우리가 만든 스크립트의 함수명인 GrepOperator는 global namespace를 사용하고 있다. 별 일이 아닐 수도 있지만, vimscript를 작성할 때에는 최대한 안전하게 작성하는 것이 좋다.

코드에 몇 줄만 추가하여 이 문제를 해결할 수 있다.

nnoremap <leader>g :set operatorfunc=<SID>GrepOperator<cr>g@
vnoremap <leader>g :<c-u>call <SID>GrepOperator(visualmode())<cr>

function! s:GrepOperator(type)
    let saved_unnamed_register = @@

    if a:type ==# 'v'
        normal! `<v`>y
    elseif a:type ==# 'char'
        normal! `[y`]
    else
        return
    endif

    silent execute "grep! -R " . shellescape(@@) . " ."
    copen

    let @@ = saved_unnamed_register
endfunction

처음 세 줄만이 변경된 것을 볼 수 있다.

함수명을 s:와 함께 시작하여 현재 script의 namespace를 사용하게 하였다.

그리고 GrepOperator 함수를 부를 때 <SID>를 사용하여 함수를 찾을 수 있게 하였다.

축하한다! 우리의 grep-operator.vim이 드디어 완성되었다!

Excersize

  • Read :help <SID>
  • Treat yourself to a snack. You deserve it!
    맥주한잔하러간다