콘텐츠로 건너뛰기
Home » Vim – 사용자 정의 명령을 만드는 방법

Vim – 사용자 정의 명령을 만드는 방법

vim의 명령모드에서 입력하여 실행하는 명령들을 “Ex명령”이라고 한다. 주로 :가 앞에 붙는 명령들이 여기에 해당하며, vimscript 함수와는 별개로 작동한다. (vimscript 함수들은 엄밀히 말해 :call 명령의 인자로 전달받는 값이 되는 것이다.)

:command 명령을 사용하면 사용자가 직접 원하는 동작을 수행하는 명령을 새로 정의하여 사용할 수 있다. 간단한 예로 다음 명령은 vim 파일을 편집할 때, 현재 파일을 저장하고 vim 스크립트를 로드하는 사용자 정의 Ex명령을 생성한다.

:command -bar LoadScript w! | so %

위 명령을 실행한 후에 vim 파일을 편집하고 :LoadScript 명령을 실행하면 현재 편집중인 파일을 저장한 후 :source 명령으로 다시 불러오게 된다.

보통 이런 사용자 명령은 단순한 Ex 명령의 조합을 그대로 사용하기 보다는, vimscript 함수를 정의하고 해당 함수를 실행하도록 작성하는 경우가 많다.

:command 명령은 크게 두 가지 역할을 한다.

  1. :command 라고만 실행하면 현재 등록되어 있는 모든 사용자 정의 명령의 목록을 출력한다.
  2. :command Str 이라고 실행하면 “Str”로 시작하는 이름의 사용자 정의 명령의 목록을 출력한다.
  3. :command {cmd} {commands}{commands} 명령들을 실행하는 {cmd} 라는 명령을 새로 등록한다. :command! 라고 쓰면 이전에 같은 이름으로 등록된 명령을 무시하고 새로 등록한다.

명령을 등록하는 방법

새로운 Ex명령을 등록하는 :command 명령은 다음과 같은 문법으로 구성된다.

:command {속성} {명령이름} {명령 표현식}

명령 속성

사용자 정의 명령은 기본적으로 내장 Ex 명령과 동일하게 취급된다. 따라서 :substitute 명령처럼 특정한 범위를 지정하여 실행될 수도 있고, :source 명령처럼 인자를 받을 수도 있다. 그리고 이러한 인자는 그 종류에 따라서 파일이름, 버퍼이름 등으로 지정되어 자동완성을 지원하게 할 수도 있다. 이러한 명령의 특징 들은 명령 속성 옵션에 의해 정의되므로, 새 명령을 등록하면서 사용할 수 있다. 명령옵션은 각각 -로 시작하는 이름을 갖는다.

인자를 받는 명령 정의하기

기본적으로 사용자가 정의하는 명령은 인자가 없는 것으로 간주되며, 명령줄에서 인자와 함께 실행되면 에러가 발생한다. 인자를 받을 수 있는 명령을 정의하기 위해서는 -nargs= 속성을 사용하여 정의한다.

  -nargs=0 : 기본, 인자를 받지 않는다.
  -nargs=1 : 인자를 1개 받는다.
  -nargs=* : 인자를 0, 1 ... 여러개 받을 수 있다.
  -nargs=? : 인자를 0, 1개 받는다.
  -nargs=+ : 인자를 1개 이상 받는다.

인자를 사용하기 위해서는 명령 표현식에 <args> 를 쓰면, 이 부분은 명령 실행시에 인자값으로 치환되어 평가된다. 인자로 넘겨진 값은 문자열 그대로 명령 표현식으로 들어간다. 그런데 이 말에는 함정이 숨어 있다. 표현식으로 전달되는 시점까지는 문자열 그대로 넘어가지만, 실제 명령 표현식이 실행될 때에는 표현식 맥락에서 평가되는 것이다.

:comm -nargs=1 Foo echo <args>

위 명령은 간단한 에코 명령을 다시 작성한 것이다. 그러면 :Foo hello 라고 실행하면 “hello” 가 화면에 표시될까? 아니다. hello 라는 변수가 정의되지 않았다는 에러가 나게 된다. 따라서 이 명령이 의도한 대로 실행되려면 <args> 대신에 <q-args> 치환자를 사용해야 한다. 이 치환자는 인자 전체를 따옴표에 넣어서 하나의 문자열로 평가하도록 만들어준다. 이렇게 치환자에 대한 변형을 처리하는 것으로는 <q-<f- 가 있다. <q- 는 인자 그 자체를 하나의 문자열처럼 취급하기 위한 것이고, <f- 는 함수에 전달할 형태로 만드는 것이다. <f-args> 를 사용하면 인자들을 리스트로 만드는 것이 아니라 1) 공백 기준으로 자르고 2) 각 단어를 따옴표 처리한 후, 3) 중간에 콤마를 삽입한다.

인자의 자동완성 처리

인자를 받는 사용자 정의 명령을 작성한다면, 해당 인자의 타입을 고려하여 -complete= 속성을 지정해두면 사용성을 높일 수 있다. vim의 Ex 명령에서도 예를 들면 :set 명령은 옵션의 이름에 대해 자동완성을 지원하며, :edit 명령은 파일의 경로에 대해 자동완성을 지원해준다. 사용자 정의 명령에서도 이러한 자동완성의 도움을 받도록 할 수 있다. 아래는 설정 가능한 자동완성 결합 속성의 예시이다. 특히 -complete=custom,s:MyFunc 와 같이 커스텀 명령을 사용해서 인자에 대한 자동 완성도 구현할 수 있다. (이 부분은 뒤에서 다시 다뤄보겠다.)


  -complete=augroup   autocmd groups
  -complete=buffer    buffer names
  -complete=behave    :behave suboptions
  -complete=color     color schemes
  -complete=command   Ex command (and arguments)
  -complete=compiler  compilers
  -complete=cscope    |:cscope| suboptions
  -complete=dir       directory names
  -complete=environment   environment variable names
  -complete=event     autocommand events
  -complete=expression    Vim expression
  -complete=file      file and directory names
  -complete=file_in_path  file and directory names in |'path'|
  -complete=filetype  filetype names |'filetype'|
  -complete=function  function name
  -complete=help      help subjects
  -complete=highlight highlight groups
  -complete=history   :history suboptions
  -complete=locale    locale names (as output of locale -a)
  -complete=mapping   mapping name
  -complete=menu      menus
  -complete=option    options
  -complete=shellcmd  Shell command
  -complete=sign      |:sign| suboptions
  -complete=syntax    syntax file names |'syntax'|
  -complete=syntime   |:syntime| suboptions
  -complete=tag       tags
  -complete=tag_listfiles tags, file names are shown when CTRL-D is hit
  -complete=user      user names
  -complete=var       user variables
  -complete=custom,{func} custom completion, defined via {func}
  -complete=customlist,{func} custom completion, defined via {func}

범위 다루기

Ex 명령 중에는 특정한 범위나 수량를 지정하고, 그 범위에 대해서 작동하는 명령이 있다. 버퍼에서 문자열 찾기/바꾸기에 해당하는 :s 명령이나 창을 분할하는 :split, 인자목록의 다음 파일로 넘어가는 :Next 같은 명령이 이런 명령에 속한다. 사용자 정의 명령에서도 -range-count 속성을 명시하면 해당 명령은 앞에 숫자나 범위를 붙여서 실행할 수 있게 된다. -range, -count 를 붙이는 것으로 명령에 범위를 적용하는 것이 가능해지는데, 이 옵션들에 대해서는 디폴트값을 지정해 줄 수 있다.

  -range : 범위에 따른 명령이 된다. 생략한 경우 현재줄이 그 범위가 된다. 
  -range=% : 범위 속성을 활성화하고 버퍼 전체를 디폴트 범위로 한다.
  -range=N : 특정 줄 번호를 디폴트 범위로 한다.
  -count : 수량 속성을 활성화한다. 디폴트 값은 0개이다. 
  -count=N : 수량 속성을 활성화한다. 디폴트 값을 N으로 한다.

-range-count 속성은 동시에 설정할 수 없다. 범위를 활성화한 경우 <line1>, <line2> 가 범위의 시작줄 번호, 끝 줄 번호가 된다. (한 줄인 경우에는 두 값이 같다.) -count 속성이 활성화된 경우, 명령 실행 시 주어진 수량 값은 <count> 치환자를 대체하게 된다.

범위를 사용할 때에는 숫자가 아닌 특수한 기호를 사용할 수 있다. 예를 들어 구두점(.)은 현재 라인을 의미하고 $는 마지막 라인, % 는 버퍼 전체를 의미한다. 그런데 이 문자들은 상황이나 맥락에 따라서는 다른 의미를 갖기도 한다. % 현재 버퍼 전체의 범위를 의미할 때도 있지만, 현재 버퍼의 이름을 의미할 때도 있다. 이처럼 범위가 행번호가 아닌 탭이나 창과 같이 다른 것으로 해석될 필요가 있는 경우에 이를 구분해주기 위해서 -addr= 속성을 사용한다.

  • -addr=lines : 디폴트이며, 범위는 행번호들을 의미한다.
  • arguments, buffers(buf), loaded_buffers(load), windows(win), tabs(tab), quickfix(qf), other(?) 와 같은 맥락이 있다.

기타 명령 속성

그외의 명령 속성에는 다음과 같은 것들이 있다.

  • -bang : ! 를 붙여서 사용할 수 있게 해준다. <bang> 치환자를 사용한다.
  • -bar : 사용자 정의명령이 | 를 사용해서 다른 명령과 연결될 수 있도록 허용한다.
  • -regiser : :del, :put 등의 명령처럼, 레지스터를 첫 인자로 사용하게 한다. <register> / <reg> 로 치환한다.
  • -buffer : 현재 버퍼에서만 사용되는 명령을 만든다.