(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옵션으로 지정해주면 된다.

Vim – 레지스터에 관해

vim에서 문자열을 복사하는 것을 매뉴얼에서는 copy가 아니라 yank로 설명한다. 이는 전통적인 이유가 있긴한데, 어쨌든 보통의 응용프로그램에서의 ‘복사’는 클립보드에 콘텐츠를 써 넣는 것을 의미한다. 하지만 vim은 항상 클립보드를 사용할 수 있는 것은 아니다. 대신에 ‘레지스터’라는 별도의 메모리 공간을 사용해서 텍스트를 따로 보관했다가 나중에 사용한다. 이는 sed의 홀드 영역과 비슷하다고 할 수 있다. 대신에 vim은 단순히 하나의 저장 영역을 사용하는 것이 아니라 매우 다양하고 많은 레지스터를 사용한다.

레지스터 사용방법

레지스터는 각각의 이름이 있고, 각 이름은 모두 키 하나에 맵핑되어 있다. 레지스터는 기본적으로 편집 명령을 통해서 쓰거나 읽을 수 있다. 복사(y)는 물론 삭제(d, x) 및 교체 (r, s)시에도 제거되는 내용은 즉시 폐기된다기 보다는 특정한 레지스터에 복사할 수 있다. 특정 레지스터에 복사된 내용을 p 등으로 붙여넣는 경우에 레지스터를 사용할 수 있다.

Vim – 레지스터에 관해 더보기

컬러스킴을 전환하기

vim에서 컬러 스킴을 변경하고 싶을 때에 :colorscheme 명령을 사용한다. (줄여서 :color로 쓸 수 있다.) 이 명령으로 컬러 스킴을 변경할 때에는 테마 이름을 알아야 하는데, 사실 몰라도 상관없다. :color<space> 한 후에 탭 키를 누르면 테마 이름은 자동완성되기 때문이다. 근데 이것보다도 키 하나만 눌러서 다른 테마로 적용되는 것을 보면서 전환하도록 하는 것이 더 편리할 것 같다. 이 기능을 vim 안에서 어떻게 만들면 좋을지 알아보자.

컬러스킴을 전환하기 더보기

jump 관련 명령 정리 – vim

vim에서 h,j,k,l 명령에만 익숙해지는 것도 사실 쉽지 않고, 일단 익숙해지면 이 키들을 연타하는 식으로 커서를 움직이는데, 특정한 위치로 바로 이동할 수 있는 jump관련 명령이 손에 익으면 그만큼 편리할 수가 없더라. jump와 관련된 명령들을 한 번은 정리하고 가자.

마커

m을 이용해서 현재 위치를 특정한 마커로 지정할 수 있고, 백팃 ` 이나 '작은 따옴표를 이용해서 그 위치로 되돌아 갈 수 있다. 보통 마크는 a-z, A-Z, 0-9 와 몇 가지 특수문자가 적용되는데, 특수문자들은 특별한 의미를 가지는 것들이다. 다음은 vim에서 사용되는 마커의 특징이다. 이 중에서 사용자가 임의로 설정할 수 있는 마커는 0-9,a-z,A-Z 이며, 이 각각은 문자의 종류에 따라 또 의미가 조금씩 다르다. 그 외에 편집/선택과 관련하여 특징적인 마커가 있으니 살펴봐두자.

  • a-z 영어 소문자는 일반적인 마크. 파일마다 따로 관리된다.
  • A-Z는 여러 파일간에 유지된다.
  • 0-9는 역시 여러 파일간에 유지되는데 … .viminfo 파일 내에 저장된다고 한다. 따라서 vim을 종료/재시작한 후에도 위치를 기억할 수 있다. (실제로 이는 vim을 빠져나갔을 때 위치를 기억한다고 한다.)
  • < > 는 이전 선택 영역의 처음과 끝을 가리킨다.
  • " 는 최종적으로 수정한 위치를 가리킨다.
  • ' 는 점프하기 이전 위치로 돌아간다.
  • ^는 삽입모드가 최종적으로 종료된 위치로 돌아간다.
  • .은 최종 변경이 시작된 위치이다.
  • [ ] 는 최종적으로 수정한 영역의 처음과 끝을 가리킨다.

:marks를 사용하면 현재 이동할 수 있는 마크들을 보여준다. jump 관련 명령 정리 – vim 더보기

스크립트에서 파일명 확장 – vim

vim 명령줄 모드에서 %는 보통 현재 파일의 전체 범위1현재 파일 이름의 의미가 된다. 예를 들어 C 파일을 작성하던 중에 현재 파일을 컴파일하고 싶다면 :!gcc -c % 라고 할 수 있다. 그렇다면 함수와 같은 스크립트 문맥에서는 어떨까? 스크립트 문맥에서는 파일명확장이 이루어지지 않고, 대신에 expand()함수를 써서 수동으로 처리해야 한다.

let current_file_name = expand('%')
let current_file_prefix = expand('%<')

그외 몇 가지 옵션이 있는데 이는 :h expand()로 찾아보면 된다.

  • % : 현재 파일이름
  • # : 대체 이름 (아마 이전 파일?)
  • #n : 대체이름 (n번째 이전 파일)
  • <cfile>: 커서가 있는 위치의 파일 이름
  • <cword> : 커서가 있는 위치의 단어
  • <cWORD> : 커서가 있는 위치의 단어를 대문자로

그외에 확장자가 붙어서 이를 변환할 수 있다. 그 중 일부만 소개하면 아래와 같다. (전체 목록은 도움말 내용을 확인하자.)

  • :p : full path로 확장한다.
  • :h : 헤드 (마지막 패스 요소를 제거한다.)
  • :t: 꼬리 (첫 패스 요소를 제거한다.)

  1. %s/ ... // 등에서 범위로 사용될 때는 전체 범위를 의미한다.