tools/vim

Learn Vimscript The Hard Way - 51. Potion Section Movement part 1

seul chan 2020. 5. 5. 23:11

Potion Section Movement - part one

해당 장도 길이가 길기 때문에 두 파트로 나누어서 작성하였다.

저번 장에 우리는 어떻게 section movement가 작동하는지 살펴보았다. 이를 potion 파일에 적용하기 전에 한번 다시 생각해보자.

우선 우리는 Potion 파일에서 "section"이 어떤 것을 의미할 것인지를 결정하여야 한다. 두 쌍의 section movement 명령어가 있으므로 우리의 사용자들을 위한 두가지 "schemes"를 마련할 수 있다.

다음 두 가지 스키마를 potion의 섹션으로 결정하자.

  1. 빈 줄 이후에 오는 모든 줄 이나 파일의 첫 줄(whitespace가 아닌 문자가 있는 줄)
  2. 첫 글자로 whitespace가 아닌 문자가 있고, 줄 안에 등호 문자(=)가 있고 콜론(:)으로 끝나는 줄

조금 확장된 버전의 factorial.pn을 예시로 사용해보자.

아래는 위 규칙이 section header에 어떻게 적용되었는지 적은 예시이다.

# factorial.pn                              1
# Print some factorials, just for fun.

factorial = (n):                            1 2
    total = 1

    n to 1 (i):
        total *= i.

    total.

print_line = ():                            1 2
    "-=-=-=-=-=-=-=-\n" print.

print_factorial = (i):                      1 2
    i string print
    '! is: ' print
    factorial (i) string print
    "\n" print.

"Here are some factorials:\n\n" print       1

print_line ()                               1
10 times (i):
    print_factorial (i).
print_line ()

우리의 첫 번째 정의는 상당히 자유롭다. 이는 대략 "top-level"의 텍스트 덩어리를 정의한다.

두 번째 정의는 조금 제한적이다. 이는 (효율적으로) 함수 정의를 의미한다.

Custom Mappings

ftplugin/potion/sections.vim 파일을 플러그인 레파지토리에 만들자. 여기에 우리의 section movement와 관련된 코드를 추가할 것이다. 이 코드는 버퍼의 filetypepotion으로 설정될 떄마다 실행된다는 것을 기억하라.

우리는 모든 4가지 section movement 명령어를 다시 매핑할 것이기 때문에 뼈대를 만들어보자.

noremap <script> <buffer> <silent> [[ <nop>
noremap <script> <buffer> <silent> ]] <nop>

noremap <script> <buffer> <silent> [] <nop>
noremap <script> <buffer> <silent> ][ <nop>

nnoremap 대신에 noremap 명령어를 사용한 것을 보라. operator-pending 모드에서도 사용하기를 원하기 때문이다. 이 경우 d]]같은 명령어를 사용할 수 있다. ("여기서부터 다음 섹션까지의 코드를 모두 지워라")

우리는 매핑을 buffer-local로 추가하여 Potion 파일에만 적용되게 하였다.

또한 silent 옵션을 추가하여 어떻게 섹션간에 이동하는지에 대한 상세한 내용은 신경쓰지 않도록 하였다.

Using a Function

section movement 명령어는 다른 명령어들과 거의 흡사하기 때문에 우리가 호출할 함수를 추상적으로 만들어보자.

다른 다양한 vim 플러그인에서 매핑을 추가할 때 이런 접근 전략을 쓰는 것을 볼 수 있을 것이다. 이는 읽기 쉽고, 유지보수가 쉽다.

sectinos.vim 파일에 다음을 추가해보자

function! s:NextSection(type, backwards)
endfunction

noremap <script> <buffer> <silent> ]]
        \ :call <SID>NextSection(1, 0)<cr>

noremap <script> <buffer> <silent> [[
        \ :call <SID>NextSection(1, 1)<cr>

noremap <script> <buffer> <silent> ][
        \ :call <SID>NextSection(2, 0)<cr>

noremap <script> <buffer> <silent> []
        \ :call <SID>NextSection(2, 1)<cr>

나(저자)는 긴 줄을 싫어하기 때문에 vimscript의 long line continuation 기능을 사용하였다. 각 매핑의 두 번 째 줄의 시작에서 역슬래쉬(\)를 사용하고 있는 것을 볼 수 있다. 더 많은 정보를 보려면 :help line-continuation을 참고하여라.

script-local 함수를 사용하고 전역 변수를 오염시키는 것을 방지하기 위해 <SID>를 사용한 것에 주의하라.

각각의 매핑은 간단하게 NextSection 함수를 호출한다.

Base Movement

우리의 함수에 무엇이 필요할지 생각해보자. 우리는 커서를 다음 "section"으로 이동시켜야 한다. /? 명령어의 결과로 커서를 이동시키는 것이 쉬울것이다.

NextSection을 다음과 같이 수정해보자.

function! s:NextSection(type, backwards)
    if a:backwards
        let dir = '?'
    else
        let dir = '/'
    endif

    execute 'silent normal! ' . dir . 'foo' . "\r"
endfunction

이제 위 함수는 execute normal!을 사용하여 backwards 인자의 유무에 따라 /foo?foo를 실행시킬 것이다.

실제로는 foo가 아닌 다른 것을 찾아야 할 것이다. 그리고 이 패턴은 우리가 위에서 section heading으로 정의한 것에 따라 달라질 것이다.

NextSection을 다음과 같이 바꿔보자.

function! s:NextSection(type, backwards)
    if a:type == 1
        let pattern = 'one'
    elseif a:type == 2
        let pattern = 'two'
    endif

    if a:backwards
        let dir = '?'
    else
        let dir = '/'
    endif

    execute 'silent normal! ' . dir . pattern . "\r"
endfunction

이제 우리는 위의 패턴을 채워넣기만 하면 된다.

나머지는 다음 포스트에..