Wireframe

vim9script 로 autoload 함수 작성하기

vim의 플러그인 스크립트는 런타입 디렉토리 내 plugin 디렉토리 아래에 위치하며, vim이 시작될 때 해당 디렉토리 하위의 모든 vim 파일을 로딩하게 됩니다. 만약 라인 수가 많은 스크립트가 있거나 혹은 읽어들여야 하는 스크립트 파일이 많다면 vim의 초기 시작 시간에 영향을 주게 됩니다. 따라서 많은 플러그인들은 이러한 시작 성능의 영향을 최소화하기 위해서 스크립트의 로딩 시점을 최대한 뒤로 미루는 방법을 적용합니다.

  1. 특정한 타입의 파일을 편집하려할 때 필요한 기능이 있다면 ftplugin 디렉토리 내에 vim 파일을 위치 시킵니다. 여기에 있는 스크립트들은 그 이름과 동일한 타입의 버퍼가 열릴 때 로드됩니다.
  2. 공통적으로 사용하는 함수라면 autoload 디렉토리에 작성합니다. 이 디렉토리에 있는 파일은, 특별한 패턴의 이름으로 함수를 정의하며, 해당 함수가 실제로 호출되기 전까지는 파일이 로드되지 않습니다

vim9script 에서 autoload 사용하기

vim9script 에서는 autoload 파일을 작성하는 방식이 약간 다릅니다. 기존 레거시 스크립트에서는 path#filename#funcname 과 같은 식의 이름으로 된 함수를 호출하면 vim은 앞부분의 path#filename# 부분을 autoload/path/filename.vim 으로 해석하여 해당 파일을 찾고, 이 파일을 로딩하여 해당 이름의 함수를 호출하게 됩니다. 이렇게 해서 함수 코드의 로드 시점을 실제 필요한 함수를 최초로 호출하는 시점으로 미루게 되는 것입니다.

기본적으로 다음과 같은 방식으로 스크립트를 작성했다고 합시다.

" ~/vimfiles/plugin/foo.vim
command -nargs=0 Foo call temp#foo()

" ~/vimfiles/autoload/temp.vim
function temp#foo()
  echom "Hello world!"
endfunction

위와 같이 두 개의 파일을 각각 작성한 후에 vim을 로드하면 vim은 먼저 plugin/foo.vim 파일만 로드하고 사용자 정의 명령 Foo 를 생성합니다. 그리고 :Foo 가 실행되는 시점에 temp#foo 함수를 호출하기 위해서 autoload/temp.vim 파일을 로딩힙니다.

vim9script의 autoload 매커니즘은 약간 다른 방식을 취합니다. 먼저 autoload 디렉토리 내에 있는 파일 역시 vim9script로 작성합니다. vim9script의 함수 이름은 기존의 autoload 방식 이름의 패턴을 따르지 않습니다. 대신, 해당 파일의 함수를 호출해야 하는 스크립트인 플러그인 본체에서 미리 autoload 방식으로 해당 스크립트를 import 합니다.

그러면 플러그인 본체가 로드될 때 autoload 파일이 로딩되는 것이 아니라, 해당 함수를 최초로 호출하는 시점에, autoload 스크립트를 로딩하고 해당 함수를 컴파일하게 됩니다. import autoload '파일' 이라는 명령으로 이를 구현합니다. 그 예는 아래와 같아요.

import autoload 'for/search.vim'
command -nargs=1 SearchForStuff search.Stuff(<f-args>)

이런 코드를 포함하는 플러그인이 로드되었다면, 이제 SearchForStuff 명령은 사용자에게 노출됩니다. :import 명령에서 autoload 인자는 해당 항목이 실제로 사용되는 시점까지는 로딩이 발생하지 않음을 지시합니다. 여기서 언급된 스크립트는 import 디렉토리가 아닌, ‘runtimepath’ 옵션 내의 디렉토리들에서 “autoload” 디렉토리에서 찾게 됩니다.


autoload 파일의 전체 또는 일부를 포팅하려면

이렇게 본체와 autoload 스크립트를 분리하여 플러그인을 작성한 경우, 일부분만 vim9script로 포팅하는 경우가 있을 수 있습니다.

  1. autoload 파일은 레거시 스크립트로 작성해두고, 플러그인 본체만 vim9script로 포팅하는 경우
  2. autoload 파일만 vim9script로 포팅하는 경우

플러그인의 규모가 크다면 그 중 일부만 포팅하는 시나리오는 현실적으로 얼마든지 가능하므로, 이 두 경우 모두 작동해야 할 것입니다.

autoload 파일은 그대로 유지하는 경우

autoload 파일을 그대로 유지하는 경우, autoload 함수를 로딩하는 시나리오는 달라질 것이 아무것도 없습니다. vim9script 파일에서 레거시 autoload 스크립트를 사용하려 한다면 import autoload "temp.vim" 과 같은 임포트 구문을 사용하지 않아도 되며, 키 맵이나 사용자 정의 명령 등에서 temp#foo() 와 같은 기존의 호출 문법을 그대로 사용할 수 있습니다. vim9script 에서 새로운 함수를 정의할 때(def ~ enddef)에는 기존의 파일 이름을 사용할 수 없지만, 레거시 함수를 호출할 때에는 이러한 제약이 없습니다.

autoload 파일을 포팅하는 경우

autoload 파일을 vim9script로 포팅하는 경우에는 다시 두 가지의 선택지가 있습니다. 어떤 함수는 def ... enddef 를 사용하여 vim9script의 문법으로 재작성 될 수 있습니다. 그리고 여전히 function ... endfunction 을 사용하여 기존 문법을 사용하는 함수를 그대로 유지할 수도 있습니다. 하나의 스크립트에서 이 두 가지 방식을 사용하는 것은 여전히 유효합니다.

  1. def 를 사용하여 함수를 정의하는 경우에는 일반적인 함수 이름을 사용해야 합니다. temp#foo() 형태의 함수 이름은 허용되지 않으며, Foo() 를 사용해야 합니다.
  2. export def Foo() 와 같이 반드시 export 를 붙여야 합니다. 이를 붙이지 않으면 해당 스크립트 외부에서 함수를 참조할 수 없습니다.
  3. function 명령을 사용해서 함수를 정의하는 경우에는 legacy function 으로만 명령을 교체해줍니다. 해당 함수 정의 블럭은 레거시 코드로 해석되므로 기존과 동일하게 작동할 것입니다.
vim9script
# file: ~/.vim/autoload/temp.vim 

export def Foo()
  ...
enddef

legacy function temp#bar()
  ...
endfunction

/plugin/foo.vim 파일은 레거시 스크립트로 작성되었다면 다음과 같습니다. vim9script로 작성된 함수라 하더라도 레거시 autoload에서 작성했을 것으로 기대되는 모양으로 함수를 호출하면 됩니다. 사실 이 방식은 foo.vim 이 vim9script 모드이든, 레거시 모드이든 상관없이 작동합니다. export def Bar() 라고 정의한 함수는 외부에서는 temp.Bar 의 이름 혹은 temp#Bar 의 이름으로 노출됩니다. 단, export를 붙이지 않았다면 외부로 노출되지 않으니 없는 함수라고 에러가 나게 됩니다.

" ~/.vim/plugin/foo.vim
command -nargs=0 Foo call temp#Foo()
command -nargs=0 Bar call temp#bar()

반대로 autoload 스크립트에 두 가지 모드에서 작성된 함수가 혼재하는 경우에 foo.vim 파일을 vim9script로 포팅하는 것은 다음과 같습니다.

" ~/.vim/plugin/foo.vim
import autoload "temp.vim"
command -nargs=0 Foo call temp#Foo()
command -nargs=0 Bar call temp.bar()

다만 export def 를 사용해 정의한 Bar 의 경우에는 temp.Bar(), temp#Bar() 두 가지 호출 방식이 모두 사용될 수 있지만, legacy function 을 사용하여 정의한 Foo() 의 경우에는 temp#Foo() 로만 호출할 수 있다는 차이가 있습니다.

결론

  1. autoload 스크립트를 포팅할 때에는 export def FuncName / legacy function filename#funcname 의 방식으로 각각 함수를 정의할 수 있음
  2. 두 가지 모두 filename#FuncName() 의 형태로 호출할 수 있음.
  3. vim9script 모드에서는 import autoload "temp.vim" 을 사용하여 오토로드 스크립트를 임포트 한 후, def 함수는 temp.Foo() 로 호출하며, 레거시 함수는 동일하게 temp#Foo() 의 꼴로 호출하면 됨

Exit mobile version