콘텐츠로 건너뛰기
Home » 텍스트 오브젝트

텍스트 오브젝트

vim의 가장 독특한 기능 중 하나를 꼽으라면 “텍스트 오브젝트”를 취급하는 기능입니다. 예를 들어 di) 같은 명령으로 괄호 내부의 텍스트를 삭제하거나, dd 를 사용하여 한 줄 전체를 지우는 것 등이 있습니다. 이러한 동작을 커스텀 함수를 통해서 사용할 수 있을까요?

예를 들어 \u 라는 키 맵을 URL을 브라우저로 여는 동작으로 만든다고 칩시다. 그렇다면 \ui] 를 사용해서 대괄호로 둘러싸인 내부의 URL을 브라우저로 열 수 있다면 좋겠습니다. \u$ 를 써서 현재 커서 위치로부터 줄 끝까지를 URL로 잡을 수도 있겠고, 혹은 \uu를 사용하여 한 줄 전체를 사용할 수 있겠죠.

how it works

텍스트 오브젝트를 사용하는 노멀 명령은 흔히 ‘연산자’라고 부릅니다. d3w 라는 명령은 delete 3 * word 로 해석되어 커서 위치로부터 3개의 단어를 삭제한다는 뜻이지요. 텍스트 오브젝트를 피연산자로 보는 이 관점은 다시 연산자와 오브젝트가 각각 카운트 값을 가지는 형태로 정말이지 온갖 방법으로 확장 가능해집니다. 이러한 연산자에는 다음과 같은 것들이 있습니다.

  • c (변경), d (삭제), y (복사)
  • ~ (대소문자 반전) g~(대소문자 반전) gu(소문자화) gU(대문자화)
  • gq(텍스트 포맷) gw(텍스트 포맷, 커서 이동 안함)
  • >(들여쓰기) <(내어쓰기)
  • zf(폴드 정의하기)
  • g@(커스텀 동작)

커스텀 동작은 'operatorfunc' 옵션이 있는 경우, 이 옵션의 값을 사용해서 텍스트 오브젝트를 조작할 수 있게 합니다. operatorfunc( 줄여서 'opfunc') 옵션의 값은 다름 아닌 함수가 됩니다. 특정한 사용자 정의 함수를 작성하고, 함수를 이 옵션의 값으로 지정해 놓으면 “g@” 명령을 사용해서 조작합니다.

  1. opfunc 옵션으로 지정된 함수는 텍스트 오브젝트의 ‘타입’을 인자로 받습니다. 이는 텍스트 오브젝트가 글자 단위인지, 줄단위인지, 비주얼블럭 단위인지 등을 나타냅니다.
  2. 함수 내에서 텍스트 오브젝트의 시작과 끝은 각각, '[, '] 마커로 참조할 수 있습니다.

이러한 기능을 주로 키맵에 연결하고, 여러 기능이 이런식으로 작동하려면 결국 키 맵에서 opfunc를 설정하고 g@ 를 실행하도록 해야 합니다.1g@ 명령은 ‘operatorfunc’ 옵션에서 지정한 함수를 호출하는 명령입니다. 이를 위해 키맵이 어떤 함수를 실행하면서 함수 내에서 normal g@ 를 쓰는 방법도 있겠지만, expression 키 맵을 쓰는 것이 가장 깔끔합니다.

간단한 예제를 하나 보겠습니다. 아래 s:objfunc() 함수는 텍스트 오브젝트를 처리하는 연산자로 작동하는 기능입니다. 예제에서는 이 함수를 <F5> 키를 누를 때 발동하는 것으로 설정합니다.

이 설정 아래에서 <F5>iw를 누르게 되면 다음과 같이 작동하게 됩니다.

  1. 우선 <F5> 를 누르는 시점에 키 맵에 의해 s:objfunc() 가 인자 없이 먼저 호출됩니다. 이 동작에서는 함수 내부에서 'operatorfunc' 옵션에 s:objcfunc 함수를 세팅하고 'g@' 를 리턴합니다.
  2. 표현식 키맵의 결과로, <F5> 키를 누르고 나면 g@ 를 누른 것과 같은 효과가 납니다. 이 상태에서는 연산할 텍스트 오브젝트 입력을 기다립니다.
  3. iw 가 입력됩니다. 결과적으로는 g@iw 가 입력된 것이니 <SID>objfunc(type='char') 가 호출되는 셈입니다.

아래는 예제 코드입니다. 대부분의 텍스트 오브젝트 연산자는 이 방식대로 만들게 됩니다.

function s:txtobj(type='')
    if a:type == ''
        let &operatorfunc = expand('<SID>') .. 'txtobj'
        return 'g@'
    endif
    call popup_dialog('triggered: ' .. a:type, #{time: 1000})
endfunction


nnoremap <expr> <F5> <SID>txtobj()