(Vim) 입력모드의 자동완성

vim은 사실 기본적으로 현재 버퍼에 입력된 키워드를 기반으로 자동완성을 제공한다. 다만 그게 <Tab> 키가 아닐 뿐이다. 어떤 내용이든 입력하는 사이에, 입력모드에서 <C-n>을 입력해보자. (Ctrl + N) 그러면 커서 왼쪽의 단어의 일부로 시작하는 현재 버퍼 내의 키워드들로 자동완성이 시작된다. (후보가 2개 이상이면 팝업으로 표시된다.)

이렇게 자동완성 모드가 시작되면 <C-n>은 다음 후보로 진행하며, 이전 후보로 진행하는 것은 <C-p>이다.

이 자동완성 후보 목록은 'complete' 옵션에서 정할 수 있는데, 이 옵션은 콤마로 연결되는 문자들의 목록이고 다음과 같은 의미를 갖는다. 생각보다 엄청많고 별도의 설명이 또 필요할 것 같지만.. 여기서는 생략한다. 기본적으로 이 값은 . u b t i로 정해진다.

  • . : 현재 버퍼의 키워드
  • w : 다른 창에 표시되는 버퍼들의 키워드
  • b : 버퍼 리스트에 있는 다른 로딩된 버퍼들의 키워드
  • u : 버퍼 리스트에 있는 언로드된 버퍼들의 키워드
  • t : 태그 자동 완성
  • i : 현재 파일과 include 된 파일들 (이상 디폴트 설정)
  • U : 버퍼 리스트에 없는 버퍼의 키워드
  • k : 자동완성 사전의 키워드. 이 사전은 'dictionary' 옵션으로 설정할 수 있다.
  • kspell : 활성화된 문법 체크 데이터
  • k{dict} : 별도 지정한 사전 파일
  • s : 동의어 사전의 파일. 동의어 사전은 thesaurus 옵션으로 설정한다.
  • s{tsr} : 지정한 동의어 사전 파일
  • d : 현재 파일과, include 파일에서 정의된 이름과 매크로
  • ] : t와 같다.

입력모드에서 <C-n>, <C-p> 완성은 일종의 키워드 확장과 비슷하다 볼 수 있다. 사실 vim의 자동완성은 이것으로 끝이 아니다. 입력 중에 <C-x>를 눌러보자. 명령줄에 다음과 같은 내용이 표시될 것이다.

-- ^X mode (^]^D^E^F^I^K^L^N^O^Ps^U^V^Y)

이 표시는 입력 중에 다시 Ctrl-X 서브 모드로 진입했다는 것이다. 이 모드는 여러 타입의 자동완성을 비롯해서 여러 가지 편집에 대한 편의 기능을 제공한다. 일종의 입력 모드에서 발동할 수 있는 빠른 추가 기능이라 할 수 있다. 사실 위에서 괄호안에 있는 ^D 와 같은 표현이 모두 Ctrl+D 처럼 키를 누르면 된다는 의미이다. (참고로 여기서 표기는 대문자로 했지만, 모두 <C-d>와 같이 소문자를 그대로 누르면 된다.

  1. ^E, ^Y : 입력중에 아래/위로 스크롤 할 수 있다. 다른 키를 누르면 다시 입력모드로 복귀한다. 만약 다른 서브모드에서 팝업을 띄운 상태라면, 이 키들을 눌렀을 때 자동완성을 중단하고 다시 입력모드로 복귀하게 된다.
  2. ^L : 현재 입력중인 라인과 앞부분이 같은 이전의 전체 라인을 열려있는 버퍼들로부터 가져와 바로 완성한다. 참고로 ^L을 계속 누르면 다음 후보로 넘어갈 수 있으며, ^N, ^P를 사용해서 다음/이전 선택을 할 수도 있다. 이는 다른 자동완성 모드에서도 동일하게 적용된다.
  3. ^N : 현재 파일에서의 키워드에서 자동완성
  4. ^I : 현재파일 + include 파일에서 자동완성
  5. ^K , ^T : 사전, 동의어 사전에서 자동완성
  6. ^] : 태그에서 자동완성
  7. ^F : 현재 디렉토리의 파일 이름에서 자동완성
  8. ^D : 이름 정의 및 매크로에서 자동완성 (C)
  9. ^V : Vim 명령으로부터 자동완성
  10. ^U : 사용자 정의 함수로부터 자동완성. 사용자 정의 함수는 completefunc 옵션에서 지정한 함수이다.
  11. ^O : omni 함수로부터 자동완성. 이 함수도 자동완성에 사용되는 사용자 설정함수인데, 주로 파일 타이별 자동완성에 사용된다. 대부분의 자동완성 플러그인들이 이 omnifunc 값을 만들어준다고 보면 된다.
  12. s : Ctrl 키 없이 그냥 s 만 누른다. 철자체크 데이터로부터 자동완성한다.

물론 기본 상태에서는 omni 함수나 completefunc 함수도 없고 사전이나 철자 데이터가 없기 때문에 제한적이라 느낄 수 있으나, 사실 라인완성이나 파일이름 완성, 명령어 완성등은 엄청나게 편리하게 사용될 수 있다. 이중에 몇 가지는 기억해두면 좋겠다.


자동완성 함수에 대해

사용자 정의 자동완성 함수는 completefunc 옵션으로 지정한다. 자동완성 함수는 하나의 함수로 두 가지 일을 수행한다. 먼저 자동완성될 문자열의 시작지점을 찾고, 두 번째로 실제 매치하는 내용을 찾는 것이다. 따라서 두 번의 호출이 발생하게 된다. 이 함수의 인자는 다음과 같다.

  • a:findstart : 1
  • a:base : empty

이는 자동완성이 시작되어야 하는 칼럼의 번호를 리턴해준다. 그 값은 0에서 현재 칼럼의 위치 사이에 와야한다. 처리해야하는 일은 커서가 위치한 곳 바로 앞의 글자들로부터 자동완성 항목의 일부가 있는지를 찾는 것이다. 자동완성을 실행하면 이 결과값과 현재 커서 위치까지의 범위가 자동완성 항목으로 대체될 것이다. 음수는 예외 케이스를 처리한다. -2는 자동완성을 취소하지만 계속 자동완성 모드로 남게된다. -3은 자동완성 모드를 떠나게 된다.

첫번째 호출 후에 vim은 이 함수를 다시 호출하는데, 이 때는 첫 인자가 1, 두 번째 인자는 매치시켜야할 내용(앞선 호출해서 리턴한 값과 현재 커서 사이의 범위에 있는 단어)을 넘겨준다. 이 호출에서의 리턴값은 words, refresh라는 키를 갖는 사전이다.

  • words : 매치된 결과들의 목록
  • refresh : 현재 가능한 값은 "always"이므로 항상 이 값으로 설정한다.

자동완성이 끝나는 시점에는 CompleteDone 이라는 오토커맨드 이벤트가 트리거된다.

words 키의 값으로는 매치 아이템의 리스트가 오는데, 각 아이템은 문자열이거나 별도의 옵션을 추가하는 경우 사전이다. 이 사전은 다시 다음의 키를 가져야 한다.

  • word : 삽입될 단어
  • abbr : 축약어, 키가 있다면 메뉴에서 이 값이 보여진다.
  • menu : 팝업 메뉴에 표시될 추가 내용
  • info : 그외 프리뷰/팝업에서 표시될 내용
  • kind : 자동완성의 타입을 알리는 문자 1개
  • icase : 0이 아닌 값이면 대소문자를 무시하고 치환
  • equal : 0이 아니면 이 항목을 비교에서 항상 같은 것으로 친다.
  • dup : 0이 아니면 메뉴에 같은 내용이 있어도 중복해서 표시한다.
  • empty : 0이 아니면 빈 값이어도 추가한다.
  • user_data : 그외 커스텀 데이터

다음은 도움말에 있는 예제 코드이다. 1월 ~ 12월 사이의 각 달의 약식 이름에 대해서 자동완성을 지원하도록 하는 기능이다. 이 코드는 다음과 같이 동작한다.

  1. a:findstart == 1 이면 현재 커서 위치에서 영문자를 앞으로 스캔하여 그 위치를 리턴한다.
  2. a:findstart == 0 이면 각 월의 리스트를 추가하고 이를 리턴한다.
" vi: ts=4:sw=4:sts=4
fun! CompleteMonths(findstart, base) abort
  if a:findstart
    let line = getline('.')
    let start = col('.') - 1
    while start > 0 && line[start - 1] =~ '\a'
      let start -= 1
    endwhile
    return start
  else
    let res = []
    for m in split("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec")
      if m =~ '^'.a:base
        call add(res, m)
      endif
    sleep 300m " 검색에 시간이 많이 걸리는 것처럼 보이게 함
    if complete_check()
      " 탐색중에 다른 키가 입력되면 취소
      return []
    endif
    endfor
    return res
  endif
endfunction

setl completefunc=CompleteMonths

스크립트를 실행한 후 “J”를 입력하고 바로 <C-x><C-u>를 입력하면 잠시 대기하다가 J로 시작하는 월의 목록이 표시된다. 만약 다른 키를 입력하면 자동완성 시퀀스가 취소된다.

Omni 자동완성

옴니 완성은 사용자 정의 자동완성의 일종인데, 파일 타입별로 동작한다는 특징이 있다. 대신 기본적인 자동완성 함수는 vim에서 지원한다. HTML, CSS, SQL 등의 언어는 바로 사용할 수 있으며, 그외의 언어는 “태그” 파일을 사용하여 기본적인 키워드 뿐만 아니라 상수이름, 함수, 클래스등에 대해서도 자동완성을 지원한다.


Omni 자동완성은 기본적으로 활성화되지 않는데, 다음 설정을 통해서 활성화할 수 있다.

:filetype plugin on
:set omnifunc=syntaxcomplete#Complete

이 옵션을 켜면 현재 편집 중인 파일 타입에 대해서 vim이 지원한다면 omni 완성을 쓸 수 있다. 기본적으로 vim은 널리 쓰이는 여러 언어에 대한 옴니완성을 지원하고 있다. 옴니 완성이 활성화되어 있다면 입력 중에 <C-x><C-o>를 눌러서 자동완성을 실행할 수 있다.

만약 vim에서 기본적으로 옴니 완성을 지원하지 않는 타입이라면 사용자 정의함수와 똑같은 방식으로 동작하는 함수를 작성하여 omnifunc옵션으로 지정해주면 된다.