tools/vim

Learn Vimscript The Hard Way - 38. Toggling

seul chan 2020. 4. 18. 01:19

Toggling

첫번째 장에서 vim에서 옵션을 세팅하는 방법을 다뤘었다. set someoption!으로 해당 옵션을 "toggle"할 수 있다. 이는 우리가 매핑을 만들때 더욱 잘 쓰인다.

다음 명령어를 실행시켜보자.

:nnoremap <leader>N :setlocal number!<cr>

이제 <leader>N으로 number 옵션을 키고 끌 수 있다.

relativenumber 옵션이 활성화 된 경우에는 number가 꺼져도 relative number가 출력되므로 이를 끄고 테스트해보자. setlocal norelativenumber

토글 매핑을 만드는 것은 우리가 별개의 두 키를 매핑시키지 않아도 되기 때문에 아주 편리하다.

Toggling Options

토글 옵션을 하나 만들어보자. 다음을 ~/.vimrc에 추가해보자. (~/.vim/plugin/에 분리된 파일을 만들어도 무방)

nnoremap <leader>f :call FoldColumnToggle()<cr>

function! FoldColumnToggle()
    echom &foldcolumn
endfunction

<leader>f를 누르면 현재 foldcolumn 옵션의 값을 출력해줄것이다. 해당 옵션에 익숙하지 않다면 :help foldcolumn을 읽어보자.

실제 토글 기능을 넣어보자. 다음 코드를 수정

nnoremap <leader>f :call FoldColumnToggle()<cr>

function! FoldColumnToggle()
    if &foldcolumn
        setlocal foldcolumn=0
    else
        setlocal foldcolumn=4
    endif
endfunction

실제로 실행시켜보면 <leader>f 키를 누를 때마다 fold column을 감추고 보여준다.

특정 텍스트를 fold 해 본 다음에 테스트 하면 더 직관적이다. fold 하고 싶은 텍스트를 선택한 다음 :fold로 접을 수 있다.

Toggling Other Things

옵션 뿐 아니라 quickfix window 등도 토글하기에 좋다. 이전에 만든 뼈대와 비슷하게 만들어보자.

nnoremap <leader>q :call QuickfixToggle()<cr>

function! QuickfixToggle()
    return
endfunction

이 매핑은 지금은 아무것도 하지 않는다. 이를 조금 더 유용한 형태로 변경시켜보자.

nnoremap <leader>q :call QuickfixToggle()<cr>

function! QuickfixToggle()
    copen
endfunction

이제 quickfix window가 열리는 것을 볼 수 있다.

이를 "toggling" 하기 위해서 일단 빠르고 dirty한 해결책을 사용해보자. "global variable"이다.

nnoremap <leader>q :call QuickfixToggle()<cr>

let g:quickfix_is_open = 0

function! QuickfixToggle()
    if g:quickfix_is_open
        cclose
        let g:quickfix_is_open = 0
    else
        copen
        let g:quickfix_is_open = 1
    endif
endfunction

이제 <leader>f로 quickfix window를 토글할 수 있게 되었다.

Improvements

위에서 만든 토글은 잘 작동하지만, 몇몇 문제점을 가지고 있다.

우선 사용자가 :copen이나 :cclose를 사용해도 우리의 전역변수 (quickfix_is_open)는 업데이트 되지 않는다.

이는 vimscript 코드 작성의 중요한 포인트를 보여준다: 만약 모든 케이스를 다루려고 한다면 늪에 빠지게 될것이고, 결 코 작업을 마무리하지 못할 것이다.

실제로 작동하게 한 후에 다시 돌아와서 이를 수정하는 것이 100% 완벽하게 하기 위해 시간을 소모하는 것보다 낫다. 많은 사람들이 사용하는 플러그인을 만들 때에는 예외이다. 이런 경우 버그등을 줄이기 위해 많은 시간이 소요된다.

Restoring Windows/Buffers

우리의 함수가 가지고 있는 문제는 이미 quickfix window가 열린 경우에 해당 매핑을 사용하는 경우이다.

이를 해결하기 위해 많은 플러그인에서 사용되는 방식을 차용해보자.

nnoremap <leader>q :call QuickfixToggle()<cr>

let g:quickfix_is_open = 0

function! QuickfixToggle()
    if g:quickfix_is_open
        cclose
        let g:quickfix_is_open = 0
        execute g:quickfix_return_to_window . "wincmd w"
    else
        let g:quickfix_return_to_window = winnr()
        copen
        let g:quickfix_is_open = 1
    endif
endfunction

두 줄을 추가하였다. 하나는 현재 사용중인 윈도우의 번호를 새로운 변수로 정의한 것이다. (else절)

두 번 째는 wincmd w를 수행하여 vim이 해당 윈도우로 이동할 수 있게 한 것이다. (if절)

여전히 우리의 해결책은 사용자가 quickfix window를 열어두었을 때 매핑으로 토글이 되지는 않지만, 그래도 많은 문제를 해결해준다. quickfix window를 사용하면서 작업중인 기존 윈도우로 커서를 이동해주는 역할을 한다.

Exercises

  • Read :help foldcolumn.

  • Read :help winnr()

  • Read :help ctrl-w_w.

  • Read :help wincmd.

  • Namespace the functions by adding s: and <SID> where necessary.

nnoremap <leader>q :call <SID>QuickfixToggle()<cr>

let g:quickfix_is_open = 0

function! s:QuickfixToggle()
    if g:quickfix_is_open
        cclose
        let g:quickfix_is_open = 0
        execute g:quickfix_return_to_window . "wincmd w"
    else
        let g:quickfix_return_to_window = winnr()
        copen
        let g:quickfix_is_open = 1
    endif
endfunction