*usr_41.txt* Vim version 7.4 대상. 새로 고침: 2013년 2월 20일 VIM 사용설명서 - Bram Moolenaar 저 정지용 역 Vim 스크립트 만들기 Vim 스크립트 언어는 구동 시 적용되는 vimrc 파일, 문법 파일 등 여러 곳에서 사용됩니다. 이번 장에서는 Vim 스크립트에서 사용하는 것들에 대해 설명하겠습니다. 매우 많은 것들이 있으니 아주 긴 장이 될 겁니다. |41.1| 소개 |41.2| 변수 |41.3| 표현식 |41.4| 조건문 |41.5| 표현식 실행하기 |41.6| 함수 사용하기 |41.7| 함수 정의하기 |41.8| 리스트와 사전 |41.9| 예외 |41.10| 몇 가지 주목할 사항 |41.11| 플러그인 만들기 |41.12| 파일 형식 플러그인 만들기 |41.13| 컴파일러 플러그인 만들기 |41.14| 빠르게 로드되는 플러그인 만들기 |41.15| 라이브러리 스크립트 만들기 |41.16| Vim 스크립트 배포하기 다음 장: |usr_42| 새 메뉴 추가하기 이전 장: |usr_40| 새로운 명령 만들기 차례: |usr_toc|
*41.1* 소개 *vim-script-intro* *script* Vim 스크립트를 처음 접하는 곳은 vimrc 파일입니다. Vim이 시작될 때, 이 파일을 읽어서 명령을 실행합니다. 선호하는 설정 값을 맞출 수 있습니다. 이 안에서는 모든 콜론 명령(":"으로 시작하는 명령 말입니다. Ex 명령이나 명령줄 명령으로 불리기도 합니다)을 사용할 수 있습니다. 문법파일도 Vim 스크립트입니다. 특정 파일 형식에 맞는 설정을 하는 파일이니까요. 복잡한 매크로를 별도의 Vim 스크립트 파일에서 정의할 수도 있습니다. 그 밖에도 당신이 상상할 수 있는 많은 활용방법이 있을 것입니다. 간단한 예제로 시작합시다:
:let i = 1
:while i < 5
: echo "숫자는" i
: let i += 1
:endwhile
Note:
여기서 ":" 문자는 실제로는 필요하지 않습니다. 콜론은 명령을 입력해서 사용할 때만 필요합니다. Vim 스크립트 파일에서는 쓰지 않아도 됩니다. 다만 여기서는 콜론 명령임을 명확히 하고, 다른 보통 모드 명령과 구분하기 위해 계속해서 콜론을 쓰도록 하겠습니다.Note:
위 예제는 내용이 있는 줄을 복사한 후, :@"로 직접 실행시켜 볼 수 있습니다. 예제 코드의 출력은 다음과 같습니다:숫자는 1
숫자는 2
숫자는 3
숫자는 4
첫 번째 줄에서 ":let" 명령은 변수에 값을 할당합니다. 일반적인 형태는 다음과 같습니다:
:let
{변수}
={표현식}
여기서는 변수 이름이 "i"이고, 표현식은 그냥 값인 숫자 1입니다. ":while" 명령은 루프를 시작합니다. 일반적인 형태는:
:while
{조건식}
:
{명령문}
:endwhile
":endwhile"까지의 명령문(statement)들이 조건식이 참인 동안 계속 수행됩니다. 위 예에서 사용한 조건식은 표현식 "i < 5"입니다. 변수 i가 5보다 작으면 참이지요.
Note:
만약 실수로 무한루프를 만드셨다면,CTRL-C
(마이크로소프트 윈도에서는CTRL-Break
)를 눌러서 멈출 수 있습니다. ":echo" 명령은 인자를 출력합니다. 위 예에서는 "숫자는"과 변수 i의 값입니다. i가 1이므로, 다음 내용을 출력할 겁니다:숫자는 1
그리고 ":let i += 1" 명령이 나옵니다. 이건 ":let i = i + 1"과 같습니다. 변수 i에 1을 더해서, 동일한 변수에 다시 할당합니다. 위 예는 명령을 설명하기 위한 것이었고, 이런 일을 정말로 하고 싶다면 다음과 같이 훨씬 간단하게 쓸 수 있습니다:
:for i in range(1, 4)
: echo "숫자는" i
:endfor
일단은 |
:for
|와 |range()
|의 동작에 설명하지 않겠습니다. 정말 궁금하다면 링크를 따라가 보세요. 숫자의 세 가지 종류 ------------------- 숫자는 10진수, 16진수, 8진수를 쓸 수 있습니다. 16진수는 "0x"나 "0X"로 시작합니다. 예를 들어 "0x1f"는 10진수 31입니다. 8진수는 "0"으로 시작합니다. "017"은 10진수 15입니다. 주의: 10진수 앞에 0을 넣지 마십시오. 8진수로 해석됩니다! ":echo" 명령은 항상 10진수로 출력합니다. 예:
:echo 0x7f 036
127 30
앞에 빼기 기호를 붙이면 음수입니다. 16진수와 8진수일 때도 마찬가지입니다. 빼기 기호는 뺄셈에도 쓰입니다. 앞의 예제와 비교해서 살펴보세요:
:echo 0x7f -036
97
표현식에서는 공백이 무시됩니다. 하지만 각 요소들을 공백으로 분리해서 읽기 좋게 만드는 편이 낫습니다. 예를 들어 위의 예는 음수로 혼동할 여지가 있으므로 빼기 기호와 숫자 사이에 공백을 넣는 것이 좋겠지요:
:echo 0x7f - 036
*41.2* 변수 변수명은 아스키 문자, 숫자, 밑줄로 이루어집니다. 단, 숫자로 시작할 수는 없습니다. 올바른 변수명을 보면: counter _aap3 very_long_variable_name_with_underscores FuncLength LENGTH 하지만 "foo+bar"나 "6var" 같은 변수명은 쓸 수 없습니다. 이 변수들은 전역적(global)입니다. 현재 정의되어있는 변수들을 보려면 다음 명령을 사용하십시오:
:let
전역 변수는 어디에서나 쓸 수 있습니다. 즉, 변수 "count"를 한 스크립트 파일에서 사용했을 때, 다른 파일에서도 사용할 수 있다는 말입니다. 운이 좋다면 그냥 헷갈리는 정도겠지만, 가끔은 문제가 발생할 수도 있습니다. 이런 문제를 피하려면 변수 앞에 "s:"를 붙여서 변수가 스크립트 파일 내에서만 유효하게 만들면 됩니다. 예를 들어, 한 스크립트가 다음 코드를 포함하고 있다고 해봅시다:
:let s:count = 1
:while s:count < 5
: source other.vim
: let s:count += 1
:endwhile
"s:count"는 이 스크립트 내에서만 유효하므로, "other.vim" 파일을 실행시킨다고 해서 변수 값이 바뀔 리는 없습니다. 만약 "other.vim"도 "s:count" 변수를 사용하더라도 이 변수는 "other.vim" 내에서만 유효한 또 다른 변수일 뿐입니다. 스크립트 내에서만 유효한 변수에 대한 더 자세한 내용은 |
script-variable
|을 참고하세요. 더 다양한 종류의 변수가 있습니다. |internal-variables
|를 참고하세요. 자주 쓰는 것들을 보면 다음과 같습니다: b:name 버퍼 내에서만 유효한 변수 w:name 창 내에서만 유효한 변수 g:name 전역 변수 (함수 내에서도 유효) v:name Vim에 미리 정의된 변수 변수 지우기 ----------- 변수는 메모리를 차지하기도 하고, ":let" 명령의 결과에도 계속 나옵니다. 변수를 지우려면 ":unlet" 명령을 사용하세요. 예:
:unlet s:count
스크립트 내에서만 유효한 "s:count" 변수를 지우고 변수가 사용하는 메모리를 해제합니다. 변수가 실제로 존재하는지 확실치 않고, 만약 변수가 없을 때 에러가 발생하는 것이 싫다면, 뒤에 !를 붙이세요:
:unlet! s:count
스크립트가 끝날 때 스크립트 내에서만 유효했던 변수들은 자동으로 지워지지 않습니다. 다음 번 이 스크립트가 실행될 때, 이전 값을 그대로 가지고 있습니다. 예:
:if !exists("s:call_count")
: let s:call_count = 0
:endif
:let s:call_count = s:call_count + 1
:echo s:call_count "번 실행 됨"
"exists()" 함수는 변수가 정의되어있는지 검사합니다. 인자는 검사할 변수의 이름입니다. 변수 자체가 아닙니다! 만약 다음과 같이 한다면:
:if !exists(s:call_count)
s:call_count 변수 안의 값이 exists()가 검사할 변수 이름으로 사용될 겁니다. 이걸 원한 건 아니잖아요? 느낌표 ! 는 값을 반대로 만듭니다. 값이 참이면 거짓이 되고, 거짓이었다면 참이 됩니다. "아니면"이라고 이해하시면 됩니다. 따라서 "if !exists()"는 "만약 exists()가 아니면"이라는 뜻이지요. Vim은 0이 아닌 모든 것을 참으로 간주합니다. 0은 거짓입니다.
Note:
Vim은 숫자가 필요할 때 문자열을 자동으로 숫자로 바꿉니다. 숫자로 시작하지 않는 문자열은 모두 숫자 0이 됩니다. 따라서 다음 예는::if "true"
"true"가 0으로 해석되므로, 거짓이 됩니다! 문자열 변수와 상수 ------------------ 지금까지는 변수에 숫자만 넣어왔습니다. 문자열도 넣을 수 있습니다. 숫자와 문자열은 Vim이 지원하는 변수의 기본 타입입니다. 타입은 동적(dynamic)이어서, 매번 변수에 ":let"으로 값을 할당할 때마다 지정됩니다. 타입에 관한 더 자세한 내용은 |41.8|에서 다루겠습니다. 변수에 문자열 값을 할당하려면, 문자열 상수를 사용해야 합니다. 두 가지 형식이 있습니다. 첫 번째는 따옴표로 둘러싼 문자열입니다:
:let name = "영희"
:echo name
영희
문자열 안에 따옴표를 넣고 싶다면, 따옴표 앞에 백슬래시를 넣으세요:
:let name = "\"영희\""
:echo name
"영희"
백슬래시를 쓰는 게 귀찮다면, 문자열을 작은따옴표로 둘러싸도 됩니다:
:let name = '"영희"'
:echo name
"영희"
작은따옴표 안에서는 모든 문자가 쓰인 그대로입니다. 오직 작은따옴표만이 특별 취급을 받는데, 작은따옴표 두 개를 쓰면 작은따옴표 하나가 됩니다. 백슬래시도 백슬래시 그대로 받아들여지고, 백슬래시 뒤에 오는 문자들의 의미에 영향을 주지 않습니다. 따옴표 안에서는 특수문자를 사용할 수 있습니다. 많이 쓰는 것들을 보겠습니다: \t<Tab>
\n<NL>
, 줄 바꿈 \r<CR>
,<Enter>
\e<Esc>
\b<BS>
, 백스페이스 \" " \\ \, 백슬래시 \<Esc>
<Esc>
\<C-W>
CTRL-W
마지막 둘은 하나의 예입니다. "\<name>
" 형태는 특수 키를 이름으로 입력할 때 쓸 수 있습니다. 문자열에 사용할 수 있는 전체 특수문자 목록은 |expr-quote
|에서 확인하세요.
*41.3* 표현식 Vim은 표현식을 다루기 위해 풍부하지만 단순한 방식을 사용합니다. 정의는 다음 링크에서 확인하세요: |expressions-syntax
|. 여기서는 가장 많이 쓰는 것들을 살펴보겠습니다. 위에서 살펴본 숫자, 문자열과 변수는 모두 그 자체로 표현식입니다. 따라서 표현식을 써야하는 모든 곳에 숫자나 문자열, 변수를 쓸 수 있습니다. 이 밖에 표현식의 기본 요소들은 다음과 같습니다: $NAME 환경 변수 &name 옵션 @r 레지스터 예:
:echo "'tabstop' 옵션의 값은" &ts
:echo "당신의 홈 디렉터리는" $HOME
:if @a > 5
&name 형식은 옵션 값을 저장하고, 새 값을 덮어썼다가 무언가 동작을 한 후 기존 값으로 되돌리는 방식으로 사용할 수 있습니다. 예:
:let save_ic = &ic
:set noic
:/The Start/,$delete
:let &ic = save_ic
위와 같이 하여 "The Start" 패턴이 사용될 때 'ignorecase' 옵션이 항상 꺼져있도록 할 수 있습니다. 사용자의 설정은 그대로 유지하면서요. (아니면 패턴 뒤에 "\C"를 붙여도 동일한 결과가 나옵니다. |
/\C
|를 참고하세요.) 수학 ----- 기본적인 요소들을 조합할 수 있다면 더 신나겠죠? 간단한 수학으로 시작해봅시다: a + b 더하기 a - b 빼기 a * b 곱하기 a / b 나누기 a % b 나머지 구하기 일반적인 우선순위가 적용됩니다. 예:
:echo 10 + 5 * 2
20
괄호를 써서 묶을 수도 있습니다. 당연하죠? 예:
:echo (10 + 5) * 2
30
문자열은 "."으로 연결할 수 있습니다. 예:
:echo "foo" . "bar"
foobar
":echo" 명령은 여러 인자를 받으면, 각 인자 사이를 공백 하나로 띄웁니다. 위 예에서는 인자가 하나의 표현식이므로, 공백이 들어가지 않았습니다. C에서 빌려 온 조건 표현식도 있습니다: a ? b : c "a"의 값이 참이면 "b"가, 아니면 "c"가 사용됩니다. 예:
:let i = 4
:echo i > 5 ? "i는 크다" : "i는 작다"
i 는 작다
표현식을 이루는 세 부분은 언제나 먼저 계산됩니다. 따라서 다음과 같이 동작한다고 생각하면 됩니다: (a) ? (b) : (c)
*41.4* 조건문 ":if" 명령은 조건이 성립할 때에만 ":endif"까지와의 사이에 있는 명령문들을 수행합니다. 일반형은 다음과 같습니다: :if{condition}
{statements}
:endif{condition}
표현식의 결과가 참(0이 아님)일 때만{statements}
명령문들이 수행됩니다. 명령문들은 올바른 형태여야 합니다. 만약 이상한 내용이 들어있다면, ":endif"를 제대로 찾지 못할 것입니다. ":else"도 쓸 수 있습니다. 일반형은 다음과 같습니다: :if{condition}
{statements}
:else{statements}
:endif 두 번째{statements}
는 첫 번째{statements}
가 실행되지 않을 때만 실행됩니다. 마지막으로, ":elseif"가 있습니다: :if{condition}
{statements}
:elseif{condition}
{statements}
:endif ":elseif"는 ":else"를 쓴 후 그 안에 "if"를 쓴 것과 동일하게 동작합니다. 다만 ":endif"를 하나 덜 써도 되겠지요. 유용한 예제를 하나 들어보면, vimrc 파일에서 'term' 옵션을 확인한 후 그에 따라 무언가를 할 수 있을 겁니다:
:if &term == "xterm"
: " xterm인 경우 할 일
:elseif &term == "vt100"
: " vt100 터미널인 경우 할 일
:else
: " 그 밖의 터미널인 경우 할 일
:endif
논리 연산 --------- 이미 앞서의 예에서 몇 가지 사용해봤습니다. 제일 많이 쓰는 것들은 다음과 같습니다: a == b 같다 a != b 같지 않다 a > b 크다 a >= b 크거나 같다 a < b 작다 a <= b 작거나 같다 결과는 조건에 맞으면 1 아니면 0입니다. 예:
:if v:version >= 700
: echo "축하합니다."
:else
: echo "옛날 버전을 쓰고 계시는군요. 업그레이드 하세요!"
:endif
여기서 "v:version"은 Vim이 정의하는 변수로, Vim의 버전 값을 가집니다. 버전 6.0이면 600, 버전 6.1이면 601입니다. Vim의 여러 버전에서 동작하는 스크립트를 만들어야할 때 쓰면 유용하겠죠. |
v:version
| 논리 연산자는 숫자와 문자열 모두에 적용할 수 있습니다. 문자열을 비교하면, 수학적인 비교가 이루어집니다. 각 바이트의 값을 비교하는데, 이는 몇몇 언어에서는 적절치 않을 수도 있겠지요. 문자열을 숫자와 비교하면, 문자열이 먼저 숫자로 변환됩니다. 조심할 점은 문자열이 숫자로 이루어진 것이 아니면 0으로 변환된다는 것입니다. 예:
:if 0 == "one"
: echo "예"
:endif
위 예제에서는 "예"가 출력됩니다. "one"은 숫자로 이루어지지 않았기 때문에, 숫자 0으로 변환된 것이죠. 문자열에 대해서는 두 가지가 더 있습니다: a =~ b 일치한다 a !~ b 일치하지 않는다. 왼쪽의 "a"는 문자열로 쓰입니다. 오른쪽의 "b"는 패턴으로 쓰입니다. 내용을 찾을 때 쓰는 그 패턴 맞습니다. 예:
:if str =~ " "
: echo "문자열에 공백이 있습니다."
:endif
:if str !~ '\.$'
: echo "문자열이 마침표로 끝나지 않습니다."
:endif
패턴 부분에 작은따옴표를 썼습니다. 패턴에는 보통 백슬래시가 많이 있고, 이걸 큰따옴표로 싸서 표현하려면 백슬래시가 두 배로 늘어나기 때문에 복잡해집니다. 이럴 땐 작은따옴표가 유용하죠. 문자열을 비교할 땐 'ignorecase' 옵션이 적용됩니다. 만약 옵션을 무시하고 싶다면, 대소문자를 따지려면 "#"를, 무시하려면 "?"를 뒤에 붙이세요. 즉, "==?"는 대소문자를 구분하지 않고 두 문자열이 같은지 비교합니다. 그리고 "!~#"는 대소문자를 고려하여 패턴이 일치하지 않는지를 봅니다. 전체 목록은 |
expr-==
|를 참고하세요. 그 밖의 루프 관련 ----------------- ":while" 명령은 앞에서 설명했습니다. ":while"과 ":endwhile" 사이에는 다음 두 가지 명령문이 더 들어갈 수 있습니다: :continue while 루프의 시작으로 돌아가서 루프를 계속 돕니다. :break ":endwhile"까지 건너뛰고, 루프를 벗어납니다. 예:
:while counter < 40
: call do_something()
: if skip_flag
: continue
: endif
: if finished_flag
: break
: endif
: sleep 50m
:endwhile
":sleep" 명령은 Vim이 잠깐 멈추도록 합니다. "50m"은 50밀리 초를 의미합니다. 다른 예로 ":sleep 4"는 4초간 멈추는 것입니다. 루프에 관해서 ":for" 명령을 더 봐야할 텐데요, 아래 |41.8|에서 보겠습니다.
*41.5* 표현식 실행하기 지금까지는 스크립트 안의 명령들만을 Vim이 직접 실행했습니다. ":execute" 명령은 표현식의 결과를 실행할 수 있게 해줍니다. 명령을 만들고, 실행하는 굉장히 강력한 방식이지요. 예를 들어, 변수 안에 들어있는 태그로 이동해봅시다:
:execute "tag " . tag_name
"."은 문자열 "tag "와 변수 "tag_name"의 값을 연결합니다. 만약 "tag_name" 변수의 값이 "get_cmd"이었다면, 실행되는 명령은 다음과 같을 것입니다:
:tag get_cmd
":execute" 명령은 콜론 명령만 실행할 수 있습니다. 보통 모드 명령은 ":normal" 명령으로 실행합니다. 이때 인자는 표현식이 아니고 명령을 나타내는 문자 그 자체입니다. 예:
:normal gg=G
위 명령은 첫 번째 줄로 이동한 후, "=" 오퍼레이터로 모든 줄의 형식을 맞춥니다. ":normal" 명령을 표현식과 함께 쓰려면, ":execute"와 조합해야 합니다. 예:
:execute "normal " . normal_commands
"normal_commands" 변수에 보통 모드 명령이 들어있어야겠지요. ":normal" 명령의 인자는 꼭 완전한 명령이어야 합니다. 아니면 인자의 끝을 만나는 순간 명령이 중단되어버립니다. 예를 들어, 입력 모드를 시작했다면 입력모드를 끝내야 합니다. 다음과 같이요:
:execute "normal I내용 \
<Esc>
"위 명령은 "내용 "을 현재 줄에 넣습니다. 여기서는 "\
<Esc>
"로 특수 키를 넣었는데요. 이렇게 실제<Esc>
문자를 입력하지 않고 특수 키를 넣을 수 있습니다. 문자열을 실행하지 않고 그냥 표현식의 값만 계산하고 싶다면, eval() 함수를 쓰면 됩니다:
:let optname = "path"
:let optval = eval('&' . optname)
"path" 앞에 "&" 문자가 붙습니다. 따라서 eval()의 인자는 "&path"가 되고, 그 결과는 'path' 옵션의 값이 됩니다. 아니면 다음과 같이 할 수도 있습니다:
:exe 'let optval = &' . optname
*41.6* 함수 사용하기 Vim은 많은 함수들을 정의합니다. 이는 Vim이 수많은 기능을 제공하는 방법이기도 합니다. 이 절에서는 몇 가지 예제가 나오는데요, 전체 함수 목록을 보고 싶다면 |functions
|를 참고하세요. 함수는 ":call" 명령으로 호출합니다. 인자는 괄호 안에 쉼표로 구분하여 넣습니다. 예:
:call search("날짜: ", "W")
위 예는 search() 함수를 호출합니다. 인자는 "날짜: "와 "W" 입니다. search() 함수는 첫 번째 인자를 찾기 패턴으로, 두 번째 인자를 플래그로 사용합니다. "W" 플래그는 파일의 끝에서 파일의 시작으로 넘어가서 계속 찾지 말라는 뜻입니다. 함수는 표현식 안에서도 호출할 수 있습니다. 예:
:let line = getline(".")
:let repl = substitute(line, '\a', "*", "g")
:call setline(".", repl)
getline() 함수는 현재 버퍼에서 한 줄을 얻어옵니다. 인자는 얻어올 줄 번호입니다. 여기서는 "."를 사용했는데요, 현재 커서가 있는 줄을 가리킵니다. substitute() 함수는 ":substitute" 명령과 비슷한 일을 합니다. 첫 번째 인자는 바꿀 문자열입니다. 두 번째 인자는 패턴, 세 번째 인자는 바꿔 넣을 문자열입니다. 마지막 인자는 플래그입니다. setline() 함수는 첫 번째 인자로 지정한 줄의 내용을 두 번째 인자에 들어있는 새로운 내용으로 바꿉니다. 위 예에서는 커서가 위치한 줄의 내용이 substitute()의 결과로 바뀝니다. 따라서 위 세 줄의 효과는 다음 하나와 동일합니다:
:substitute/\a/*/g
substitute() 호출을 하기 전후에 다른 일들을 더 한다면 함수를 쓰는 것이 의미가 있겠지요. 함수 *function-list* ----- 많은 함수가 있습니다. 여기서는 용도별로 묶어서 살펴보겠습니다. 알파벳순으로 정리한 목록은 |
functions
|를 참고하세요. 함수 이름에서 CTRL-]를 누르면 더 자세한 도움말로 이동합니다. 문자열 처리: *string-functions* nr2char() 아스키(ASCII) 값으로 문자 얻기 char2nr() 문자의 아스키 값 얻기 str2nr() 문자열을 숫자로 변환 str2float() 문자열을 실수(Float)로 변환 printf() 문자열을 % 표기법으로 구성 escape() 문자열 내의 문자들을 '\'를 이용하여 이스케이프 처리 shellescape() 문자열을 셸 명령에서 이용할 수 있도록 이스케이프 처리 fnameescape() 파일명을 Vim 명령에서 쓸 수 있도록 이스케이프 처리 tr() 일련의 문자들을 대응되는 문자들로 바꿈 strtrans() 문자열의 모든 문자를 화면에 표시할 수 있도록 바꿈 tolower() 영문자를 모두 소문자로 변경 toupper() 영문자를 모두 대문자로 변경 match() 문자열에서 패턴이 일치하는 곳을 찾음 matchend() 문자열에서 패턴이 일치하는 곳이 끝나는 부분을 찾음 matchstr() 문자열에서 패턴이 일치하는 곳의 내용 matchlist() matchstr()과 동일하나 부분 패턴의 내용도 함께 반환 stridx() 긴 문자열에서 찾는 문자열이 처음 나오는 곳 strridx() 긴 문자열에서 찾는 문자열이 마지막 나오는 곳 strlen() 문자열 길이 substitute() 패턴이 일치하는 곳을 다른 문자열로 바꿈 submatch() ":s" 와 substitute() 내에서 부분패턴의 내용을 얻음 strpart() 문자열의 일부를 얻음 expand() 예약된 키워드들을 실제 내용으로 바꿈 iconv() 한 인코딩의 내용을 다른 인코딩으로 바꿈 byteidx() 문자열 내에서 문자의 바이트 단위 위치 repeat() 문자열을 여러 번 반복 eval() 문자열로 된 표현식을 계산 리스트 처리: *list-functions* get() 항목을 가져오되, 잘못된 인덱스여도 에러 없음 len() 항목의 수 empty() 비어있는지 검사 insert() 특정 위치에 삽입 add() 리스트 뒤에 항목 덧붙이기 extend() 리스트 뒤에 다른 리스트 붙이기 remove() 하나 이상의 항목을 삭제 copy() 리스트를 얇게(shallow) 복사 deepcopy() 리스트 항목 속의 항목까지 전체를 복사 filter() 선택된 항목들을 삭제 map() 각 항목들을 변경 sort() 정렬 reverse() 순서를 뒤집음 split() 문자열을 쪼개서 리스트로 만듦 join() 리스트 항목들을 이어 붙여서 문자열로 만듦 range() 연속된 숫자로 이루어진 리스트를 반환 string() 리스트의 문자열(String) 표현 call() 리스트를 인자로 함수 호출 index() 리스트 내 항목의 인덱스 max() 항목들 중 최댓값 min() 항목들 중 최솟값 count() 리스트에서 값이 나타나는 횟수 repeat() 리스트를 여러 번 반복 사전 처리: *dict-functions* get() 항목을 가져오되, 잘못된 키여도 에러 없음 len() 항목의 수 has_key() 키가 존재하는지 검사 empty() 비어있는지 검사 remove() 항목을 삭제 extend() 다른 사전의 내용을 추가 filter() 선택된 항목들을 삭제 map() 각 항목들을 변경 keys() 키들의 리스트를 얻음 values() 값들의 리스트를 얻음 items() 키-값 쌍들의 리스트를 얻음 copy() 사전을 얇게(shallow) 복사 deepcopy() 사전 항목 속의 항목들까지 전체를 복사 string() 사전의 문자열 표현 max() 값들 중 최댓값 min() 값들 중 최솟값 count() 사전에서 값이 나타나는 횟수 실수 연산: *float-functions* float2nr() 실수(Float)를 정수(Number)로 변경 abs() 절댓값 (Number에도 쓸 수 있음) round() 반올림 ceil() 올림 floor() 내림 trunc() 소수점 이하를 버림 log10() 밑이 10인 로그 pow() x의 y승 sqrt() 제곱근 sin() 사인 cos() 코사인 tan() 탄젠트 asin() 아크사인 acos() 아크코사인 atan() 아크탄젠트 atan2() 아크탄젠트 sinh() 쌍곡사인 cosh() 쌍곡코사인 tanh() 쌍곡탄젠트 기타 계산: *bitwise-function* and() 비트 단위 AND invert() 비트 뒤집기 or() 비트 단위 OR xor() 비트 단위 XOR 변수: *var-functions* type() 변수의 타입 islocked() 변수가 잠겨있는지 검사 function() 함수 이름으로 함수 참조(Funcref)를 얻음 getbufvar() 특정 버퍼에서 변수 값을 얻음 setbufvar() 특정 버퍼의 변수 값을 할당 getwinvar() 특정 창에서 변수 값을 얻음 gettabvar() 특정 탭에서 변수 값을 얻음 gettabwinvar() 특정 탭 특정 창에서 변수 값을 얻음 setwinvar() 특정 창의 변수 값을 할당 settabvar() 특정 탭의 변수 값을 할당 settabwinvar() 특정 탭 특정 창의 변수 값을 할당 garbagecollect() 가능한 한 메모리 해제 커서와 마크 위치: *cursor-functions* *mark-functions* col() 커서 또는 마크의 열 번호 virtcol() 커서 또는 마크의 화면상 열 번호 line() 커서 또는 마크의 줄 번호 wincol() 커서의 창 내 열 번호 winline() 커서의 창 내 줄 번호 cursor() 커서를 지정한 열/줄로 이동 getpos() 커서, 마크 등의 위치를 얻음 setpos() 커서, 마크 등의 위치를 지정 byte2line() 특정 바이트 번째의 줄 번호를 얻음 line2byte() 특정 줄의 바이트 단위 위치를 얻음 diff_filler() 줄 위로 공간을 채우는 줄 수를 셈 현재 버퍼의 내용 다루기: *text-functions* getline() 한 줄 혹은 여러 줄들의 리스트를 버퍼에서 얻음 setline() 버퍼에서 한 줄의 내용을 바꿈 append() 한 줄 혹은 여러 줄들의 리스트를 뒤에 붙임 indent() 특정 줄의 들여쓰기 정도 cindent() C 들여쓰기에 따른 들여쓰기 정도 lispindent() Lisp 들여쓰기에 따른 들여쓰기 정도 nextnonblank() 공백이 아닌 다음 줄 찾기 prevnonblank() 공백이 아닌 이전 줄 찾기 search() 패턴으로 찾기 searchpos() 패턴으로 찾기 searchpair() 시작/다음/끝 중에 반대쪽 찾기 searchpairpos() 시작/다음/끝 중에 반대쪽 찾기 searchdecl() 이름의 선언 위치 찾기 시스템 함수 및 파일 처리: *system-functions* *file-functions* glob() 와일드카드를 확장 globpath() 여러 디렉터리에 대해서 와일드카드를 확장 findfile() 지정한 디렉터리들에서 파일 찾기 finddir() 지정한 디렉터리들에서 디렉터리 찾기 resolve() 바로가기가 가리키는 곳 찾기 fnamemodify() 규칙에 따라 파일명 변경 pathshorten() 경로의 디렉터리 명을 짧게 줄임 simplify() 경로를 가리키는 곳이 변하지 않는 상태에서 단순하게 정리 executable() 실행 프로그램이 존재하는지 확인 filereadable() 파일을 읽을 수 있는지 확인 filewritable() 파일에 쓸 수 있는지 확인 getfperm() 파일의 권한 정보를 얻음 getftype() 파일의 종류를 얻음 isdirectory() 디렉터리가 존재하는지 확인 getfsize() 파일 크기를 얻음 getcwd() 현재 작업 경로를 얻음 haslocaldir() 현재 창이 |:lcd
|를 사용하는지 확인 tempname() 임시파일 이름을 얻음 mkdir() 디렉터리 생성 delete() 파일 삭제 rename() 파일명 변경 system() 셸 명령 실행 후 결과 얻음 hostname() 호스트명 readfile() 파일 내용을 각 줄의 리스트로 반환 writefile() 각 줄의 리스트를 파일에 쓰기 날짜와 시간: *date-functions* *time-functions* getftime() 파일의 마지막 수정 시간을 얻음 localtime() 현재 시간을 초 단위로 얻음 strftime() 시간을 문자열로 표현 reltime() 현재 혹은 경과한 시간을 정확하게 얻음 reltimestr() reltime() 결과를 문자열로 변환 *buffer-functions* *window-functions* *arg-functions* 버퍼, 창 및 인자 리스트: argc() 인자 리스트의 인자 수 argidx() 인자 리스트에서 현재 위치 argv() 인자 리스트에서 한 인자를 얻음 bufexists() 버퍼가 존재하는지 확인 buflisted() 버퍼가 존재하고 나열되는지 확인 bufloaded() 버퍼가 존재하고 로드되었는지 확인 bufname() 특정 버퍼의 이름 얻음 bufnr() 특정 버퍼의 번호 얻음 tabpagebuflist() 탭의 버퍼 리스트 얻음 tabpagenr() 탭의 번호 얻음 tabpagewinnr() 특정 탭에 대한 winnr() winnr() 현재 창의 창 번호 얻음 bufwinnr() 특정 버퍼의 창 번호 얻음 winbufnr() 특정 창의 버퍼 번호 얻음 getbufline() 특정 버퍼의 내용을 각 줄의 리스트로 얻음 명령줄: *command-line-functions* getcmdline() 현재 명령줄 얻음 getcmdpos() 명령줄에서 커서의 위치를 얻음 setcmdpos() 명령줄에서 커서의 위치를 지정 getcmdtype() 현재 명령줄의 종류를 얻음 퀵픽스와 위치 리스트: *quickfix-functions* getqflist() 퀵픽스 에러 리스트 setqflist() 퀵픽스 리스트 변경 getloclist() 위치 리스트 setloclist() 위치 리스트 변경 입력 모드 자동 완성: *completion-functions* complete() 자동 완성 후보를 설정 complete_add() 자동 완성 후보를 추가 complete_check() 자동 완성을 중단해야 하는지 확인 pumvisible() 팝업메뉴를 표시해야 하는지 확인 폴드: *folding-functions* foldclosed() 특정 줄에 접힌 폴드가 있는지 확인 foldclosedend() foldclosed()와 같지만 마지막 줄을 반환 foldlevel() 특정 줄의 폴드 단계를 얻음 foldtext() 접힌 폴드 자리에 표시되는 내용을 생성 foldtextresult() 접힌 폴드 자리에 표시되는 내용을 얻음 문법과 문법 강조: *syntax-functions* *highlighting-functions* clearmatches() |matchadd()
|와 |:match
| 명령으로 정의된 모든 매치를 삭제 getmatches() |matchadd()
|와 |:match
| 명령으로 정의된 모든 매치를 얻음 hlexists() 하이라이트 그룹이 존재하는지 확인 hlID() 하이라이트 그룹의 아이디를 얻음 synID() 특정 위치의 문법 아이디를 얻음 synIDattr() 문법 아이디의 특정 속성을 얻음 synIDtrans() 해석된 문법 아이디를 얻음 synstack() 특정 위치의 문법 아이디 리스트를 얻음 synconcealed() 숨김 정보를 얻음 diff_hlID() 디프(diff) 모드에서 특정 위치의 강조 아이디를 얻음 matchadd() 강조할 패턴(매치라고 부름)를 정의 matcharg() |:match
| 인자 정보를 얻음 matchdelete() |matchadd()
|와 |:match
| 명령으로 정의된 매치를 삭제 setmatches() |getmatches()
|로 저장한 매치 목록을 불러옴 맞춤법: *spell-functions* spellbadword() 커서 위치 이후의 맞춤법 오류를 찾음 spellsuggest() 수정 후보들을 반환 soundfold() 단어와 유사한 발음인 단어들을 반환 히스토리: *history-functions* histadd() 히스토리에 항목 추가 histdel() 히스토리에서 항목 삭제 histget() 히스토리에서 항목 얻기 histnr() 히스토리 목록에서 가장 번호가 높은 항목의 번호 상호작용: *interactive-functions* browse() 파일 대화상자를 띄움 browsedir() 디렉터리 대화상자를 띄움 confirm() 사용자의 선택 입력을 받음 getchar() 사용자에게 문자 입력을 받음 getcharmod() 마지막 입력의 조합 키 정보를 받음 feedkeys() 타이프어헤드 큐(typeahead queue)에 문자를 넣음 input() 사용자에게 한 줄을 입력 받음 inputlist() 사용자가 목록에서 하나를 고르도록 함 inputsecret() 화면에 표시하지 않고 사용자에게 한 줄을 입력 받음 inputdialog() 대화상자에서 한 줄을 입력 받음 inputsave() 타이프어헤드를 저장해놓고 비움 inputrestore() 저장해놓은 타이프어헤드를 불러옴 그래픽 사용자 인터페이스: *gui-functions* getfontname() 사용 중인 글꼴 이름을 얻음 getwinposx() GUI Vim 창의 X축 위치 getwinposy() GUI Vim 창의 Y축 위치 Vim 서버: *server-functions* serverlist() 서버 이름 리스트를 반환 remote_send() Vim 서버에 명령 문자를 전송 remote_expr() Vim 서버에서 표현식을 계산 server2client() Vim 서버의 클라이언트에 응답을 보냄 remote_peek() Vim 서버에서 응답이 있는지 확인 remote_read() Vim 서버의 응답을 읽음 foreground() Vim 창을 포그라운드(foreground)로 옮김 remote_foreground() Vim 서버 창을 포그라운드로 옮김 창 크기 및 위치: *window-size-functions* winheight() 특정 창의 높이를 얻음 winwidth() 특정 창의 너비를 얻음 winrestcmd() 창 크기를 복구할 수 있는 명령을 반환 winsaveview() 현재 창의 뷰(view)를 얻음 winrestview() 현재 창의 저장한 뷰를 불러옴 매핑: *mapping-functions* hasmapto() 매핑이 존재하는지 확인 mapcheck() 일치하는 매핑이 존재하는지 확인 maparg() 매핑의 확장 결과를 얻음 wildmenumode() 와일드모드(wildmode)가 활성화 되었는지 확인 기타: *various-functions* mode() 현재 편집 모드를 얻음 visualmode() 마지막 사용한 시각 모드를 얻음 exists() 변수나 함수 등이 존재하는지 확인 has() Vim에서 기능이 지원되는지 확인 changenr() 최근에 변경한 횟수를 반환 cscope_connection() cscope 연결이 있는지 확인 did_filetype() FileType 자동명령이 사용되었는지 확인 eventhandler() 이벤트 핸들러가 동작시킨 것인지 확인 getpid() Vim의 프로세스 아이디를 얻음 libcall() 외부 라이브러리 함수를 호출 libcallnr() 상동. 숫자를 반환 getreg() 레지스터 내용을 얻음 getregtype() 레지스터에 저장된 형식을 얻음 setreg() 레지스터 내용 및 저장 형식을 지정 taglist() 일치하는 태그 리스트를 얻음 tagfiles() 태그 파일 리스트를 얻음 mzeval() |MzScheme
| 표현식을 계산
*41.7* 함수 정의하기 Vim에서는 직접 함수를 만들 수도 있습니다. 기본적인 함수 선언은 아래와 같이 시작합니다:
:function
{이름}
({변수1}
,{변수2}
, ...):
{내용}
:endfunction
Note:
함수 이름은 대문자로 시작해야 합니다. 두 수 중에서 작은 수를 반환하는 간단한 함수를 만들어봅시다. 다음과 같이 시작합니다:
:function Min(num1, num2)
함수의 이름은 "Min"이고, "num1"과 "num2" 두 개의 인자를 받습니다. 일단 어느 숫자가 작은지 봐야겠죠:
: if a:num1 < a:num2
앞에 붙은 "a:"는 변수가 함수의 인자임을 알려주는 것입니다. 작은 숫자를 "smaller" 변수에 할당합시다:
: if a:num1 < a:num2
: let smaller = a:num1
: else
: let smaller = a:num2
: endif
"smaller"는 지역 변수(local variable)입니다. 함수에서 사용되는 변수는 따로 앞에 "g:", "a:", "s:" 같은 것이 붙어있지 않으면 지역 변수입니다.
Note:
함수 안에서 전역 변수에 접근하려면 반드시 "g:"를 앞에 붙여야 합니다. 즉 함수 내에서 전역 변수 "today"에 접근하려면 "g:today"를 써야 합니다. 그냥 "today"는 함수 내에서만 유효한 다른 변수입니다. 이제 ":return" 명령문으로 작은 수를 사용자에게 반환합니다. 그리고, 함수를 끝내면 됩니다:
: return smaller
:endfunction
전체 함수 정의는 다음과 같습니다:
:function Min(num1, num2)
: if a:num1 < a:num2
: let smaller = a:num1
: else
: let smaller = a:num2
: endif
: return smaller
:endfunction
더 짧게 쓰고 싶다면, 다음과 같이 써도 동일합니다:
:function Min(num1, num2)
: if a:num1 < a:num2
: return a:num1
: endif
: return a:num2
:endfunction
사용자 정의 함수도 내장 함수와 동일한 방법으로 호출합니다. 이름만 다른 거지요. Min 함수는 다음과 같이 쓰면 됩니다:
:echo Min(5, 8)
이제 함수가 실행되면서 Vim이 함수 내용을 해석할 겁니다. 만약 정의되지 않은 변수나 함수를 썼다든가 해서 실수가 있었다면, 이제야 에러 메시지를 받게 됩니다. Vim은 함수를 정의할 때엔 오류가 있는지 확인하지 않습니다. 함수가 ":endfunction"나 인자 없이 쓰인 ":return"에 도달하면, 함수는 0을 반환합니다. 이미 존재하는 함수를 재정의하려면, ":function" 명령에 !를 붙이세요:
:function! Min(num1, num2, num3)
범위 사용하기 ------------- ":call" 명령에는 줄 범위를 줄 수 있습니다. 이 기능은 두 가지 형태로 사용되는데요. 만약 함수가 "range" 키워드와 함께 정의되었다면, 함수 자체적으로 줄 범위를 처리합니다. 함수에는 "a:firstline"과 "a:lastline" 인자가 넘어갑니다. 여기에는 함수를 호출할 때 지정한 범위의 줄 번호가 들어갑니다. 예:
:function Count_words() range
: let lnum = a:firstline
: let n = 0
: while lnum <= a:lastline
: let n = n + len(split(getline(lnum)))
: let lnum = lnum + 1
: endwhile
: echo "단어 " . n . "개가 있습니다"
:endfunction
이 함수를 다음과 같이 호출하면 됩니다:
:10,30call Count_words()
함수가 한번 실행되면서 단어의 수를 알려줄 겁니다. 두 번째 형태는 줄 범위가 "range" 키워드 없이 정의된 함수에 사용되는 것입니다. 이때는 범위 내의 모든 줄에 대해서 한번씩, 커서가 그 줄에 있는 상태로 함수가 호출됩니다. 예:
:function Number()
: echo "줄 " . line(".") . " 내용: " . getline(".")
:endfunction
이 함수를 다음과 같이 부르면:
:10,15call Number()
함수는 여섯 번 수행됩니다. 가변 인자 --------- Vim에서는 다양한 수의 인자를 받는 함수를 정의할 수 있습니다. 예를 들어 아래 명령은 최소 1개의 인자(start)를 받고, 최대 20개까지 선택적인 인자를 받을 수 있는 함수를 정의합니다:
:function Show(start, ...)
"a:1" 변수에 첫 번째 선택적인 인자가, "a:2"에 두 번째, 등등이 들어갑니다. "a:0" 변수에는 선택적인 인자의 수가 들어갑니다. 예:
:function Show(start, ...)
: echohl Title
: echo "start는 " . a:start
: echohl None
: let index = 1
: while index <= a:0
: echo " 인자" . index . "는 " . a:
{index}
: let index = index + 1
: endwhile
: echo ""
:endfunction
여기서는 이후의 ":echo" 명령에서 사용할 문법 강조를 지정하기 위해 ":echohl" 명령을 사용했습니다. ":echohl None"은 강조를 멈춥니다. ":echon" 명령은 ":echo"와 동일하지만, 마지막에 줄 바꿈을 하지 않습니다. a:000 변수를 쓸 수도 있는데, "..." 인자 전체의 리스트입니다. |
a:000
|를 참고하세요. 함수 나열하기 ------------- ":function" 명령은 모든 사용자 정의 함수의 이름과 인자를 보여줍니다:
:function
function Show(start, ...)
function GetVimIndent()
function SetSyn(name)
함수의 내용이 궁금하다면 함수 이름을 ":function"의 인자로 주면 됩니다:
:function SetSyn
1 if &syntax == ''
2 let &syntax = a:name
3 endif
endfunction
디버깅 ------ 에러 메시지를 받았을 때나 디버깅 할 때 줄 번호는 매우 유용합니다. 디버깅 모드에 대해서는 |debug-scripts
|를 참고하세요. 'verbose' 설정을 12 이상으로 놓아서 모든 함수 호출을 볼 수도 있습니다. 실행되는 각 줄을 모두 보려면 15 이상으로 놓으세요. 함수 지우기 ----------- Show() 함수를 지우려면:
:delfunction Show
함수가 존재하지 않으면 에러가 발생합니다. 함수 레퍼런스 ------------- 가끔씩 함수를 가리키는 변수 같은 것이 있으면 유용할 때가 있습니다. function() 함수를 사용하면 됩니다. 함수의 이름을 함수 레퍼런스로 바꿔줍니다:
:let result = 0 " 또는 1
:function! Right()
: return 'Right!'
:endfunc
:function! Wrong()
: return 'Wrong!'
:endfunc
:
:if result == 1
: let Afunc = function('Right')
:else
: let Afunc = function('Wrong')
:endif
:echo call(Afunc, [])
Wrong!
함수 레퍼런스를 저장할 변수의 이름이 대문자로 시작해야한다는 점에 주의하세요. 내장 함수와 헷갈리는 사태를 막기 위한 것입니다. 변수가 가리키는 함수를 실행하려면 call() 함수를 써야 합니다. 첫 번째 인자는 함수 레퍼런스이고, 두 번째 인자는 인자의 리스트입니다. 함수 레퍼런스는 사전과 함께 쓸 때 가장 유용한데요, 다음 절에서 살펴보겠습니다.
*41.8* 리스트와 사전 지금까지 기본 타입인 문자열(String)과 정수(Number)를 사용했습니다. Vim은 리스트(List)와 사전(Dictionary)이라는 두 가지 복합 타입도 지원합니다. 리스트는 순서가 있는 일련의 무언가 입니다. 무언가는 어떤 종류의 값이든 될 수 있습니다. 정수의 리스트를 만들 수도 있고, 리스트의 리스트, 더 나아가 이것 저것이 섞인 리스트도 만들 수 있습니다. 문자열 세 개를 가진 리스트를 만들려면
:let alist = ['기역', '니은', '디귿']
리스트 항목은 대괄호 안에 쉼표로 분리해서 넣습니다. 빈 리스트를 만들려면:
:let alist = []
add() 함수로 리스트에 항목을 추가할 수 있습니다:
:let alist = []
:call add(alist, '거시기')
:call add(alist, '머시기')
:echo alist
['거시기', '머시기']
리스트는 +로 이어 붙일 수 있습니다:
:echo alist + ['거시기', '머시기']
['거시기', '머시기', '거시기', '머시기']
또는 직접 리스트를 늘릴 수도 있습니다:
:let alist = ['하나']
:call extend(alist, ['둘', '셋'])
:echo alist
['하나', '둘', '셋']
add()를 사용하면 다른 결과가 나온다는 데 주의하세요:
:let alist = ['하나']
:call add(alist, ['둘', '셋'])
:echo alist
['하나', ['둘', '셋']]
add()의 두 번째 인자는 하나의 항목으로 리스트에 추가됩니다. FOR 루프 -------- 리스트로 할 수 있는 멋진 일 중 하나는 각 항목에 대해 같은 일을 반복할 수 있다는 것이지요:
:let alist = ['하나', '둘', '셋']
:for n in alist
: echo n
:endfor
하나
둘
셋
여기서는 "alist" 리스트의 각 항목에 대해 루프를 돌면서 항목의 값을 변수 "n"에 할당하고 있습니다. for 루프의 일반형은:
:for
{변수명}
in{리스트 표현식}
:
{명령문}
:endfor
정해진 횟수만큼 반복하려면, 그만한 길이를 가진 리스트가 필요합니다. range() 함수로 만들면 됩니다:
:for a in range(3)
: echo a
:endfor
0
1
2
range()에서 생기는 첫 항목이 0이고, 따라서 마지막 항목은 리스트 길이에서 하나를 뺀 값이라는 점에 주의하세요. 최댓값과 항목 간 간격을 지정할 수도 있습니다. 심지어는 거꾸로 갈 수도 있죠:
:for a in range(8, 4, -2)
: echo a
:endfor
8
6
4
쓸 만한 예제를 좀 보겠습니다. 버퍼의 줄마다 루프를 돌려면:
:for line in getline(1, 20)
: if line =~ "날짜: "
: echo matchstr(line, '날짜: \zs.*')
: endif
:endfor
위 예는 1번째 줄에서 20번째 줄까지(20번째 줄 포함)를 보고 날짜 정보를 출력합니다. 사전 ----- 사전은 키-값 쌍들을 저장합니다. 키를 알면 빠르게 찾을 수 있습니다. 사전은 중괄호로 만듭니다:
:let uk2nl =
{'one': 'een', 'two': 'twee', 'three': 'drie'}
이제 키를 대괄호 안에 넣어서 찾으면 됩니다:
:echo uk2nl['two']
twee
사전을 정의하는 일반형은:
{
<키>
:<값>
, ...}빈 사전은 키가 하나도 없는 것입니다:
{}
사전의 활용도는 무궁무진합니다. 사용할 수 있는 함수도 많습니다. 예를 들어, 키들의 리스트를 얻어 루프를 돌 수도 있습니다:
:for key in keys(uk2nl)
: echo key
:endfor
three
one
two
위에서 보듯이 키가 순서대로 나오는 것은 아닙니다. 특정한 순서를 갖도록 정렬할 수도 있습니다:
:for key in sort(keys(uk2nl))
: echo key
:endfor
one
three
two
하지만 정의한 순서대로 볼 수는 없습니다. 그럴 땐 리스트를 사용하세요. 리스트는 항목을 순서대로 저장합니다. 사전 함수 --------- 사전의 항목들은 일반적으로 대괄호 안에 키를 써서 얻을 수 있습니다:
:echo uk2nl['one']
een
같은 일을 복잡한 기호들 없이 다음과 같이 할 수도 있습니다:
:echo uk2nl.one
een
이 기능은 키가 아스키 문자, 숫자, 밑줄일 때만 동작합니다. 이 방식으로 새로운 값도 할당할 수 있습니다:
:let uk2nl.four = 'vier'
:echo uk2nl
이제 좀 특별한 걸 볼까요? 함수를 만들어서 바로 그 레퍼런스를 사전에 저장할 수 있습니다:
{'three': 'drie', 'four': 'vier', 'one': 'een', 'two': 'twee'}
:function uk2nl.translate(line) dict
: return join(map(split(a:line), 'get(self, v:val, "???")'))
:endfunction
한번 써볼까요?:
:echo uk2nl.translate('three two five one')
drie twee ??? een
먼저 눈에 띄는 부분은 ":function" 줄의 끝에 있는 "dict"입니다. 함수가 사전에서 사용된다는 것을 나타내지요. "self" 지역 변수가 해당되는 사전을 가리킵니다. 복잡한 :return 명령을 쪼개서 살펴보겠습니다:
split(a:line)
split() 함수는 문자열을 받아서 공백 단위로 쪼갠 다음, 어절들의 리스트를 반환합니다. 따라서 위 예에서는 다음과 같은 결과가 나옵니다:
:echo split('three two five one')
['three', 'two', 'five', 'one']
이 리스트가 map() 함수의 첫 번째 인자입니다. map()은 리스트를 차례대로 훑으면서, 두 번째 인자의 "v:val"에 리스트 항목의 값을 넣은 후 계산합니다. for 루프를 이용해서도 할 수 있지만 더 단순하게 표현한 것입니다. 아래 명령은:
:let alist = map(split(a:line), 'get(self, v:val, "???")')
다음과 동일합니다:
:let alist = split(a:line)
:for idx in range(len(alist))
: let alist[idx] = get(self, alist[idx], "???")
:endfor
get() 함수는 사전에 키가 존재하는지 확인합니다. 존재한다면 해당하는 값이 반환되고, 존재하지 않는다면 기본 값이 반환됩니다. 위에서는 "???" 이지요. 키가 사전에 없을 수도 있지만, 에러 메시지는 원하지 않을 때 편리한 방법입니다. join() 함수는 split()의 정반대입니다. 어절들의 리스트를 받아서 사이사이에 공백을 넣어서 붙입니다. 이 split(), map(), join()의 조합은 어절들로 이루어진 한 줄의 내용을 짧은 코드로 처리하기에 좋습니다. 객체 지향 프로그래밍 -------------------- 이제 사전에 값과 함수를 모두 넣을 수 있게 되었지요. 그렇다면 사전을 객체처럼 사용할 수도 있을 겁니다. 위에서 우리는 네덜란드어를 영어로 번역하는 사전을 사용했습니다. 동일한 일을 다른 언어에 대해서 하고 싶을 수도 있겠지요. 먼저, 번역 함수는 있지만 번역할 단어는 없는 객체(사실은 사전)를 만듭시다:
:let transdict =
{}
:function transdict.translate(line) dict
: return join(map(split(a:line), 'get(self.words, v:val, "???")'))
:endfunction
위에서 본 함수와는 약간 다릅니다. 단어를 찾기 위해 "self.words"를 사용했습니다. 하지만 우린 self.words가 없지요. 따라서 이건 추상 클래스(abstract class)라고 할 수 있을 겁니다. 이제 네덜란드어 번역 객체를 생성해봅시다:
:let uk2nl = copy(transdict)
:let uk2nl.words =
{'one': 'een', 'two': 'twee', 'three': 'drie'}
:echo uk2nl.translate('three one')
drie een
독일어 번역 객체도요:
:let uk2de = copy(transdict)
:let uk2de.words =
{'one': 'ein', 'two': 'zwei', 'three': 'drei'}
:echo uk2de.translate('three one')
drei ein
"transdict" 사전의 복사본을 만들기 위해 copy()를 사용한 후, 복사본에 단어들을 추가하고 있습니다. 원본은 물론 그대로고요. 이제 한 걸음 더 나아가서 선호하는 번역기를 사용해봅시다:
:if $LANG =~ "de"
: let trans = uk2de
:else
: let trans = uk2nl
:endif
:echo trans.translate('one two three')
een twee drie
여기서 "trans"는 두 객체(사전) 중 하나를 가리킵니다. 복사본을 만든 것은 아니에요. 리스트와 사전의 복사와 관련해서는 |list-identity
|와 |dict-identity
|를 참고하세요. 지원되지 않는 언어를 사용할 수도 있겠지요. 이때는 translate() 함수가 아무 일도 하지 않도록 만들 수도 있습니다:
:let uk2uk = copy(transdict)
:function! uk2uk.translate(line)
: return a:line
:endfunction
:echo uk2uk.translate('three one wladiwostok')
three one wladiwostok
이미 존재하는 함수 레퍼런스에 덮어쓰기 위하여 !를 사용했습니다. 이제 인식할 수 없는 언어일 때는 "uk2uk"를 씁시다:
:if $LANG =~ "de"
: let trans = uk2de
:elseif $LANG =~ "nl"
: let trans = uk2nl
:else
: let trans = uk2uk
:endif
:echo trans.translate('one two three')
one two three
더 자세한 내용은 |Lists
|와 |Dictionaries
|를 참고하세요.
*41.9* 예외 예제로 시작합시다:
:try
: read ~/templates/pascal.tmpl
:catch /E484:/
: echo "죄송합니다. 파스칼 템플릿 파일을 찾을 수 없습니다."
:endtry
":read" 명령은 파일이 존재하지 않으면 실패합니다. 이 코드에서는 에러 메시지를 생성하는 대신, 에러를 잡아서 사용자에게 깔끔한 메시지를 주고 있습니다. ":try"와 ":endtry" 사이의 명령들에서 발생하는 에러는 예외(exception)으로 바뀝니다. 모든 예외는 문자열입니다. 에러인 경우, 예외 문자열에는 에러 메시지가 들어갑니다. 그리고 모든 에러 메시지에는 번호가 있습니다. 위 예에서는 우리가 잡은 에러에 "E484:"가 있습니다. 이 번호는 어떤 일이 있어도 바뀌지 않을 것입니다(메시지 내용은 바뀔 수 있습니다. 번역 된다든지 해서요.). ":read" 명령이 다른 에러를 일으킨다면 "E484:" 패턴이 맞지 않을 겁니다. 그러면 예외가 잡히지 않고, 평소의 에러 메시지가 나갈 겁니다. 다음과 같이 하고 싶을 수도 있겠지요:
:try
: read ~/templates/pascal.tmpl
:catch
: echo "죄송합니다. 파스칼 템플릿 파일을 찾을 수 없습니다."
:endtry
이건 모든 에러를 잡는다는 의미입니다. 하지만 그렇다면 유용한 에러를 보지 못하게 될 수도 있습니다. "E21: 바꿀 수 없음, 'modifiable'이 꺼져있습니다" 같은 걸요. 다른 유용한 기능은 ":finally" 명령입니다:
:let tmp = tempname()
:try
: exe ".,$write " . tmp
: exe "!filter " . tmp
: .,$delete
: exe "$read " . tmp
:finally
: call delete(tmp)
:endtry
위 예는 커서가 있는 줄부터 파일의 끝까지를, 파일명을 인자로 받는 "filter" 프로그램으로 처리합니다. 처리가 동작했든지, ":try"와 ":finally" 사이에서 무언가 잘못되든지, 아니면 사용자가
CTRL-C
를 눌러서 처리를 취소하든지에 상관없이, "call delete(tmp)"는 항상 실행됩니다. 어떤 일이 있어도 임시 파일을 남기지 않을 수 있겠지요. 예외 처리에 대한 자세한 내용은 상세 설명서에서 확인하세요: |exception-handling
|.
*41.10* 몇 가지 주목할 사항 여기서는 Vim 스크립트에 대한 사항들을 요약해보겠습니다. 다른 곳에서도 설명하는 것들이지만, 체크 리스트로 활용하기 좋을 겁니다. 줄의 끝(end-of-line) 문자는 시스템에 따라 다릅니다. 유닉스에서는<NL>
문자 하나를 사용합니다. 마이크로소프트 도스나 윈도, OS/2 같은 시스템에서는<CR>
<LF>
를 사용합니다. 이 점은<CR>
로 끝나는 매핑을 사용할 때 특히 중요합니다. 자세한 내용은 |:source_crnl
|를 참고하세요. 공백 ----- 빈 줄을 쓸 수 있습니다. 무시됩니다. 줄 맨 앞의 공백 문자(공백이나 탭)는 언제나 무시됩니다. 요소들 사이의 여러 공백 (예를 들어 아래 예제에서 'set'과 'cpoptions' 사이)은 공백 하나로 바뀌고, 구분자 역할을 합니다. 보이는 마지막 글자 뒤의 공백은 상황에 따라 무시될 수도 안될 수도 있습니다. 아래를 보세요. 다음과 같이 "="(등호) 기호가 들어가는 ":set" 명령을 봅시다:
:set cpoptions =aABceFst
"=" 기호 앞의 공백은 무시됩니다. 하지만 "=" 뒤에는 공백이 올 수 없습니다! 옵션 값에 공백 문자를 넣으려면, "\"(백슬래시)로 다음과 같이 이스케이프 처리를 해야 합니다:
:set tags=my\ nice\ file
이 예를 다음과 같이 쓴다면:
:set tags=my nice file
에러가 발생합니다. 실제로는 다음과 같이 해석되기 때문이죠:
:set tags=my
:set nice
:set file
주석 ----- " (큰따옴표) 문자로 주석을 시작합니다. 따옴표를 포함하여 그 뒤에서부터 줄의 끝까지는 모두 주석으로 간주되어 무시됩니다. 단, 아래의 예에서와 같이 주석을 허용하지 않는 명령은 예외입니다. 주석은 줄의 어느 위치에서나 시작할 수 있습니다. 몇몇 명령은 주석을 쓰는 데 약간의 어려움이 있습니다. 예:
:abbrev dev development " 약어
:map
<F3>
o#include " include 추가:execute cmd " 실행
:!ls *.c " C 파일 보기
줄임말 'dev'는 'development " 약어'로 확장될 겁니다.
<F3>
매핑은 사실 'o# ....' 뒤의 줄 전체가 되어 뒤의 '" include 추가'도 포함됩니다. "execute" 명령은 에러가 날 겁니다. "!" 명령은 뒤의 모든 내용을 셸로 보낼 테고, 따옴표 쌍이 맞지 않는다며 에러가 날 겁니다. ":map", ":abbreviate", ":execute", "!" 뒤에는 주석이 올 수 없습니다(이 제한이 있는 명령이 몇 개 더 있습니다). ":map", ":abbreviate", ":execute" 명령은 다음과 같은 꼼수를 쓰면 됩니다:
:abbrev dev development|" 약어
:map
<F3>
o#include|" include 추가:execute cmd |" 실행
'|' 문자로 명령이 다음 명령과 분리됩니다. 다음 명령은 주석뿐인 명령이죠. 마지막 "!"에 주석을 넣으려면 |
:execute
|와 '|' 두 가지를 써야 합니다::exe '!ls *.c' |" C 파일 보기
약어와 매핑에서 '|' 앞에 공백이 없어야한다는데 주의하세요. 이 명령들은 줄의 끝이나 '|'를 만날 때까지 모든 문자가 동작에 포함됩니다. 이 때문에 끝에 오는 공백을 발견하지 못할 수도 있습니다:
:map
<F4>
o#include이 문제를 확인하려면, vimrc 파일을 편집할 때 'list' 옵션을 켜놓으면 됩니다. 유닉스에서는 줄 전체를 주석으로 만드는 특별한 방법이 있습니다. Vim 스크립트를 실행할 수 있도록 만들기 위한 것이죠:
#!/usr/bin/env vim -S
echo "Vim 스크립트입니다."
quit
"#" 명령 자체는 줄 번호로 줄 내용을 보여주는 것입니다. 하지만 그 뒤에 느낌표를 붙이면 아무 일도 안하도록 바뀝니다. 그리곤 그 뒤에 셸 명령을 넣어 파일의 나머지 부분을 실행하도록 하는 것이지요. |
:#!
| |-S
| 위험 ----- 아래 예에서는 더 큰 문제가 나타납니다:
:map ,ab o#include
:unmap ,ab
여기서 :unmap 명령은 동작하지 않습니다. ",ab " 매핑을 지우려고 하기 때문이지요. 이런 매핑은 존재하지 않으므로 에러가 발생할 겁니다. 하지만 ":unamp ,ab " 끝의 공백은 보이지 않기 때문에 문제를 찾기 힘들겠지요. 'unmap' 명령 뒤에 주석을 붙이려고 할 때도 동일한 문제가 발생합니다:
:unmap ,ab " 주석
여기서 주석 부분은 무시됩니다. 하지만, Vim은 ',ab ' 매핑을 지우려고 할 겁니다. 물론 존재하지 않겠지요. 다음과 같이 쓰세요:
:unmap ,ab| " 주석
뷰 복구하기 ----------- 가끔 무언가 수정을 한 뒤 원래 커서가 있던 곳으로 돌아가고 싶을 수 있겠지요. 이왕이면 상대적인 위치가 그대로여서 화면의 맨 위에 동일한 내용이 보이면 더 좋을 겁니다. 아래 예는 현재 줄을 복사해서, 파일의 맨 첫 줄에 넣고 다시 뷰를 복구합니다:
map ,p ma"aYHmbgg"aP`bzt`a
무슨 일을 하는지 볼까요?:
ma"aYHmbgg"aP`bzt`a
ma 현재 커서 위치에 마크 설정 "aY 현재 줄을 레지스터 a에 복사 Hmb 창의 첫 줄로 가서 마크 b 설정 gg 파일의 첫 줄로 이동 "aP 복사한 줄을 위쪽으로 붙여넣기 `b 창의 첫 줄로 돌아가기 zt 원래와 같이 보이도록 위치 조정 `a 저장한 커서 위치로 이동 패키징 ------ 다른 곳에서 추가한 함수들과 이름이 겹치는 것을 막기 위해 다음과 같은 방식을 사용하세요: - 모든 함수 이름 앞에 고유한 문자열을 붙이세요. 저는 보통 약어를 씁니다. 예를 들어, 옵션 창(Option Window) 함수에는 "OW_"를 붙입니다. - 먼저 함수들의 정의를 하나의 파일에 모으세요. 함수들이 로드 되었는지를 나타내는 전역 변수를 하나 만들고, 파일을 다시 불러올 때는 먼저 함수들을 지우세요. 예:
" XXX 패키지입니다.
if exists("XXX_loaded")
delfun XXX_one
delfun XXX_two
endif
function XXX_one(a)
... 함수 내용 ...
endfun
function XXX_two(b)
... 함수 내용 ...
endfun
let XXX_loaded = 1
*41.11* 플러그인 만들기 *write-plugin* 많은 사람들이 사용하도록 Vim 스크립트를 만들 수 있습니다. 보통 플러그인이라고 부릅니다. Vim 사용자라면 개인 플러그인 디렉터리에 당신의 스크립트를 넣고 바로 사용할 수 있습니다. |add-plugin|| 실제로는 두 가지 종류의 플러그인이 있습니다: 범용 플러그인: 모든 형식의 파일에 사용. 파일 형식 플러그인: 특정 형식의 파일에만 사용. 이 절에서는 첫 번째 종류의 플러그인을 보겠습니다. 대부분의 내용이 파일 형식 플러그인에도 해당됩니다. 파일 형식 플러그인만을 위한 자세한 내용은 다음 절 |write-filetype-plugin|에 있습니다. 이름 ----- 가장 먼저 플러그인 이름을 골라야 합니다. 이름만으로 플러그인이 제공하는 기능을 알 수 있어야 합니다. 또한 다른 사람이 다른 일을 하는 플러그인을 만들 법한 이름은 피해야 합니다. 그리고 오래된 윈도우 시스템을 위해 이름은 8자 이하로 해주세요. 오타를 교정해주는 스크립트라면 "typecorr.vim" 이라고 할 수 있을 겁니다. 이게 여기서 다룰 예제입니다. 모든 사람들을 위한 플러그인이라면, 몇 가지 지침을 따라야 합니다. 하나씩 설명하겠습니다. 예제 플러그인 전체는 맨 끝에 나옵니다. 내용 ----- 플러그인 내용부터 시작합시다. 실제 일을 하는 부분은 아래와 같습니다:
14 iabbrev teh the
15 iabbrev otehr other
16 iabbrev wnat want
17 iabbrev synchronisation
18 \ synchronization
19 let s:count = 4
물론 실제로는 목록이 훨씬 길어야겠지요. 설명을 위해서 줄 번호를 넣었습니다. 플러그인 파일에 줄 번호까지 넣지는 마세요! 헤더 ----- 플러그인을 만들다 보면 아마 계속해서 새로운 오타 교정을 추가할 것이고, 이내 이런 저런 버전들이 쌓일 것입니다. 또한 이 파일을 배포하면, 사람들은 누가 이런 환상적인 플러그인을 만들었고, 어떻게 감사를 표할 수 있는지 알고 싶을 겁니다. 따라서 플러그인의 맨 위에 다음과 같은 헤더를 넣읍시다:
1 " Vim global plugin for correcting typing mistakes
2 " Last Change: 2000 Oct 15
3 " Maintainer: Bram Moolenaar
<Bram@vim.org>
저작권과 라이선스에 대하여: 플러그인이 매우 유용하다면, 배포를 제한할 필요가 없을 겁니다. 플러그인을 공개 프로그램으로 하거나 Vim 라이선스 |license|를 사용하는 걸 고려해보세요. 위쪽에 다음과 같이 짧게 적어주기만 하면 됩니다. 예:
4 " License: This file is placed in the public domain.
줄 이어서 쓰기, 부작용 피하기 *use-cpo-save* ----------------------------- 18번째 줄 위에서 줄 이어서 쓰기|
line-continuation
| 기능을 사용했습니다. 하지만 사용자가 'compatible'을 켜놓았다면 문제가 생기는데, 아마 에러 메시지를 보게 될 겁니다. 하지만 그렇다고 'compatible' 옵션을 꺼버리면 부작용이 생기겠지요. 이런 문제를 피하기 위해서 'cpoptions' 옵션을 Vim 기본 값으로 바꿨다가 다시 복구하도록 하겠습니다. 이렇게 하면 줄 이어서 쓰기도 할 수 있고, 대부분의 환경에서 스크립트도 잘 동작할 겁니다. 다음과 같이 합니다:
11 let s:save_cpo = &cpo
12 set cpo&vim
..
42 let &cpo = s:save_cpo
43 unlet s:save_cpo
먼저 'cpoptions'의 기존 값을 s:save_cpo 변수에 저장합니다. 플러그인의 맨 마지막에서 이 값을 불러올 겁니다. 스크립트 내에서만 유효한 변수를 사용해야 합니다 |
s:var
|. 전역 변수는 다른 곳에서 이미 쓰고 있을 수 있으니까요. 이 스크립트에서만 필요한 것에는 언제나 스크립트 내에서만 유효한 변수를 사용하십시오. 로드하지 않기 ------------- 사용자가 이 플러그인을 항상 로드하고 싶지는 않을 수도 있습니다. 또는 시스템 관리자가 시스템 공용 플러그인 디렉터리에 설치했는데, 사용자는 자신만의 버전을 쓰고 싶을 수도 있고요. 따라서 사용자에겐 언제나 특정 플러그인을 끌 수 있는 방법이 있어야 합니다. 아래와 같이요:
6 if exists("g:loaded_typecorr")
7 finish
8 endif
9 let g:loaded_typecorr = 1
이렇게 함으로써 똑같은 스크립트를 두 번 로드하는 것을 막을 수 있습니다. 그러지 않으면 함수는 중복해서 정의했다고 에러가 나고, 자동명령은 두 번씩 추가되어서 문제가 생길 겁니다. 변수 이름은 "loaded_"로 시작해서 플러그인의 파일명을 뒤에 붙이는 것을 추천합니다. "g:"는 혹시나 변수를 함수 내에서 사용할까봐 실수를 피하기 위해 붙였습니다("g:"가 없으면 함수 내에서만 유효한 변수가 됩니다). "finish"는 파일의 나머지 부분을 읽지 않고 멈추도록 합니다. 전체 파일에 대고 if-endif 를 쓰는 것보다 훨씬 간편하지요. 매핑 ----- 자 이제 플러그인을 더 재미있게 만들어봅시다. 현재 커서가 위치한 단어에 대한 교정어를 추가하는 매핑을 만들 겁니다. 매핑에 쓸 키 입력을 우리가 정할 수도 있겠지만, 사용자가 이미 쓰는 것과 겹칠 수 있습니다. 사용자가 플러그인이 사용하는 매핑의 키를 직접 정의할 수 있도록,
<Leader>
요소를 사용합시다:
22 map
<unique>
<Leader>
a<Plug>
TypecorrAdd"
<Plug>
TypecorrAdd" 부분이 실제로 일을 하는 부분입니다. 자세한 내용은 뒤에서 설명하겠습니다. 사용자는 "mapleader" 변수에 이 매핑의 시작 부분에 쓰고 싶은 키 입력을 넣으면 됩니다. 따라서 만약 사용자가 다음과 같이 했다면:
let mapleader = "_"
실제 매핑은 "_a"가 됩니다. 사용자가 따로 이렇게 정의하지 않으면, 기본 값인 백슬래시가 사용됩니다. 그 때는 매핑이 "\a"가 되겠지요. 참,
<unique>
를 사용했었는데요, 만약 우연히 이미 같은 매핑이 존재한다면 에러가 나도록 합니다. |:map-<unique>
| 만약 사용자가 자신만의 키 입력을 통째로 정의하고 싶다면 어떻게 할까요? 다음과 같이 직접 정할 수 있게 해주면 됩니다:
21 if !hasmapto('
<Plug>
TypecorrAdd')22 map
<unique>
<Leader>
a<Plug>
TypecorrAdd23 endif
여기서는 "
<Plug>
TypecorrAdd"에 대한 매핑이 있는지 검사해보고, 없을 때만 "<Leader>
a"를 매핑으로 정의합니다. 사용자는 자신의 vimrc 파일에 아래와 같이 넣을 수 있겠지요:
map ,c
<Plug>
TypecorrAdd그러면 "_a"나 "\a"가 아니라 ",c"가 매핑 됩니다. 조각들 ------ 스크립트가 길어지면 작은 조각들로 쪼개고 싶어지곤 합니다. 함수와 매핑을 사용하면 쪼갤 수 있습니다. 하지만 이 함수와 매핑들이 다른 스크립트의 것들과 충돌하기를 바라지는 않을 겁니다. 예를 들어, 당신이 Add() 라는 함수를 정의했는데, 다른 스크립트에서 동일한 이름의 함수를 정의하려고 할 수도 있을 겁니다. 이런 문제를 피하기 위해 함수 앞에 "s:"를 붙여서 함수가 스크립트 내에서만 유효하도록 정의해야 합니다. 새로운 오타 교정을 추가하는 함수를 정의해봅시다:
30 function s:Add(from, correct)
31 let to = input("type the correction for " . a:from . ": ")
32 exe ":iabbrev " . a:from . " " . to
..
36 endfunction
이제 스크립트 안에서는 s:Add() 함수를 부를 수 있습니다. 만약 다른 스크립트가 s:Add()를 정의한다면, 그 함수는 그 스크립트 안에서만 유효하고 정의된 스크립트 안에서만 호출할 수 있습니다. 또 여기에 Add() 전역 함수 ("s:"가 없는)도 있을 수 있고 이는 또 다른 함수일 뿐입니다. 매핑에서는
<SID>
를 사용할 수 있습니다. 현재 스크립트를 구분할 수 있는 스크립트 아이디로 바뀝니다. 우리 오타 수정 플러그인에서는 다음과 같이 씁니다:
24 noremap
<unique>
<script>
<Plug>
TypecorrAdd<SID>
Add..
28 noremap
<SID>
Add :call<SID>
Add(expand("<cword>
"), 1)<CR>
그래서 사용자가 "\a"를 입력하면, 다음과 같은 순서로 호출됩니다:
\a ->
<Plug>
TypecorrAdd -><SID>
Add -> :call<SID>
Add()만약 다른 스크립트가
<SID>
Add 매핑을 추가하더라도 스크립트 아이디가 다르기 때문에 다른 매핑이 됩니다. s:Add()가 아니라<SID>
Add()라고 쓴 것에 주목하세요. 매핑은 사용자가 입력하는 것이므로 스크립트 밖에서 사용됩니다.<SID>
는 적절히 스크립트 아이디로 바뀌고, Vim이 알아서 스크립트의 Add() 함수를 찾아줍니다. 약간 복잡해 보이지만, 다른 플러그인들과 공존하는 플러그인을 만들려다보니 필요한 일입니다. 기본 원칙은 매핑에서는<SID>
Add()를, 다른 곳(스크립트, 자동 명령, 사용자 정의 명령)에서는 s:Add()를 쓰는 것입니다. 매핑과 동일한 일을 하는 메뉴 항목을 추가할 수도 있습니다:
26 noremenu
<script>
Plugin.Add\ Correction<SID>
Add플러그인을 위한 메뉴 항목은 "Plugin" 메뉴 밑에 넣는 것을 추천합니다. 여기서는 한 항목만 추가했습니다. 하나 이상의 항목을 추가할 때는 서브메뉴를 만드는 것이 좋습니다. 예를 들어, "Plugin.CVS" 서브메뉴를 만들고 "Plugin.CVS.checkin", "Plugin.CVS.checkout" 등의 CVS 작업 메뉴항목을 만드는 식으로요. 28번째 줄에서 ":noremap"을 썼습니다. 다른 매핑들 때문에 문제가 생기는 것을 피하기 위해서입니다. 누군가가 예를 들어 ":call"을 다시 매핑 했을 수도 있으니까요. 24번째 줄에서도 ":noremap"을 썼는데, 하지만 여기서는 "
<SID>
Add"가 다시 매핑으로 동작해야 합니다. 그래서 여기에 "<script>
"를 쓴 것입니다. 스크립트 내에서 유효한 매핑만 다시 매핑이 이루어질 수 있도록 하는 기능이지요. |:map-<script>
| 26번째 줄의 ":noremenu"에서도 같은 원리입니다. |:menu-<script>
|<SID>
와<Plug>
*using-<Plug>* ---------------<SID>
와<Plug>
모두 다른 매핑을 통해서 사용되는 매핑들을, 직접 입력하는 매핑들과 분리하기 위해서 사용합니다.<SID>
와<Plug>
의 차이를 살펴봅시다:<Plug>
는 스크립트 밖에서도 보입니다. 사용자가 키 입력을 지정하고 싶을만한 매핑들에 사용합니다.<Plug>
는 키보드로는 입력할 수 없는 특수 코드입니다. 다른 스크립트들이 사용하지 않을만한 매핑을 만들려면 다음과 같은 구조를 사용하세요:<Plug>
스크립트_이름 매핑_이름 우리 예에서는 스크립트 이름이 "Typecorr"이고 매핑 이름이 "Add"입니다. 따라서 "<Plug>
TypecorrAdd"가 되지요. 스크립트 이름과 매핑 이름의 첫 글자만 대문자로 표시해서 어디부터 매핑 이름인지 알 수 있게 했습니다.<SID>
는 스크립트 아이디로, 스크립트가 갖는 유일한 식별자입니다. 내부적으로 Vim은<SID>
를 "<SNR>
123_"으로 바꿉니다. 여기서 "123"은 어떤 숫자든 될 수 있습니다. 즉 "<SID>
Add()" 함수는 어떤 스크립트에서는 "<SNR>
11_Add()"가 되고, 또 어떤 스크립트에서는 "<SNR>
22_Add()"가 됩니다. ":function" 명령으로 함수 목록을 보면 이런 모습을 확인할 수 있습니다.<SID>
는 매핑에서도 동일하게 바뀝니다. 그렇기 때문에 매핑에서 스크립트 내에서만 유효한 함수를 호출할 수 있는 것이죠. 사용자 정의 명령 ---------------- 이제 오타 교정을 추가하는 사용자 명령을 만들어봅시다:
38 if !exists(":Correct")
39 command -nargs=1 Correct :call s:Add(
<q-args>
, 0)40 endif
사용자 정의 명령은 동일한 이름의 명령이 없어야 정의할 수 있습니다. 그렇지 않으면 에러가 나지요. 기존의 명령을 ":command!"로 덮어써버리는 것은 그리 좋은 생각이 아닙니다. 스스로도 정의한 명령이 왜 동작하지 않는지 헤매는 일이 생길 겁니다. |
:command
| 스크립트 변수 ------------- 변수가 "s:"로 시작하면 스크립트 변수입니다. 스크립트 안에서만 사용할 수 있습니다. 스크립트 밖에서는 보이지 않습니다. 다른 스크립트와 동일한 변수 이름을 써서 생기는 문제를 막아주는 것이지요. 변수는 Vim이 동작중인 동안 계속 유지됩니다. 같은 스크립트를 다시 불러오면 같은 변수가 그대로 사용됩니다. |s:var
| 재미있는 점은 이 변수들을 스크립트에서 정의하는 함수나 자동명령, 사용자 정의 명령에서도 사용할 수 있다는 것입니다. 우리 예제에 오타 교정의 수를 세기 위해 몇 줄을 추가했습니다:
19 let s:count = 4
..
30 function s:Add(from, correct)
..
34 let s:count = s:count + 1
35 echo s:count . " corrections now"
36 endfunction
먼저 s:count가 스크립트 자체적으로 4로 초기화 됩니다. 나중에 s:Add()가 호출되면, s:count를 증가시킵니다. 함수가 어디서 호출되는지는 상관없이, 스크립트 내에서 정의되었으므로 스크립트 변수를 사용할 것입니다. 결과 ----- 결과인 전체 플러그인입니다:
1 " Vim global plugin for correcting typing mistakes
2 " Last Change: 2000 Oct 15
3 " Maintainer: Bram Moolenaar
<Bram@vim.org>
4 " License: This file is placed in the public domain.
5
6 if exists("g:loaded_typecorr")
7 finish
8 endif
9 let g:loaded_typecorr = 1
10
11 let s:save_cpo = &cpo
12 set cpo&vim
13
14 iabbrev teh the
15 iabbrev otehr other
16 iabbrev wnat want
17 iabbrev synchronisation
18 \ synchronization
19 let s:count = 4
20
21 if !hasmapto('
<Plug>
TypecorrAdd')22 map
<unique>
<Leader>
a<Plug>
TypecorrAdd23 endif
24 noremap
<unique>
<script>
<Plug>
TypecorrAdd<SID>
Add25
26 noremenu
<script>
Plugin.Add\ Correction<SID>
Add27
28 noremap
<SID>
Add :call<SID>
Add(expand("<cword>
"), 1)<CR>
29
30 function s:Add(from, correct)
31 let to = input("type the correction for " . a:from . ": ")
32 exe ":iabbrev " . a:from . " " . to
33 if a:correct |
exe "normal viws\<C-R>\" \b\e"
| endif34 let s:count = s:count + 1
35 echo s:count . " corrections now"
36 endfunction
37
38 if !exists(":Correct")
39 command -nargs=1 Correct :call s:Add(
<q-args>
, 0)40 endif
41
42 let &cpo = s:save_cpo
43 unlet s:save_cpo
33번째 줄을 설명하지 않았네요. 현재 커서가 있는 위치의 단어에 새 교정을 적용합니다. 새 약어를 사용하기 위해 |
:normal
| 명령을 썼습니다. 함수가 ":noremap"으로 정의된 매핑을 통해 불리긴 했지만, 이 안에서는 매핑과 약어가 동작한다는 점에 주의하세요. 'fileformat' 옵션에는 "unix"를 사용하는 것을 추천합니다. 그러면 Vim 스크립트가 어디서나 동작할 겁니다. 'fileformat'이 "dos"로 설정되어있으면 유닉스에서 동작하지 않습니다. |:source_crnl
|을 참고하세요. 올바로 설정되었는지 확실히 하기 위해, 저장하기 전 다음 명령을 실행하세요:
:set fileformat=unix
문서화 *write-local-help* ------ 방금 만든 플러그인에 약간의 문서화를 하는 것도 좋은 생각입니다. 특히 사용자가 그 동작을 직접 바꿀 수 있다면 말이죠. 문서를 어떻게 설치하는지는 |add-local-help|를 참고하세요. 아래는 간단한 플러그인 도움말 예제입니다. 파일명은 "typecorr.txt"입니다:
1 *typecorr.txt* Plugin for correcting typing mistakes
2
3 If you make typing mistakes, this plugin will have them corrected
4 automatically.
5
6 There are currently only a few corrections. Add your own if you like.
7
8 Mappings:
9
<Leader>
a or<Plug>
TypecorrAdd10 Add a correction for the word under the cursor.
11
12 Commands:
13 :Correct
{word}
14 Add a correction for
{word}
.15
16 *typecorr-settings*
17 This plugin doesn't have any settings.
첫 번째 줄은 유일하게 형식을 지켜야 되는 부분입니다. 도움말 파일에서 추출되어 help.txt의 "LOCAL ADDITIONS:" 절에 추가됩니다 |local-additions|. 첫 "*"는 첫 줄의 첫 글자여야 합니다. 도움말 파일을 추가한 후, ":help" 명령으로 추가된 부분이 잘 보이는지 확인해보세요. 도움말 안에서 ** 안에 태그를 추가할 수도 있습니다. 하지만 이미 존재하는 도움말 태그와 겹치지 않게 주의하세요. 아마 플러그인 이름을 붙여서 쓰게 될 겁니다. 위 예제에서 "typecorr-settings"라고 한 것 같이요. 도움말의 다른 부분을 참조할 때는 ||를 사용하면 좋습니다. 사용자가 관련된 도움말을 쉽게 찾아볼 수 있도록 말이죠. 파일 형식 인식 *plugin-filetype* -------------- 만약 Vim에서 파일 형식을 제대로 인식하지 못한다면, 별도의 파일에 파일 형식 인식을 위한 코드 조각을 추가해야 합니다. 보통 파일명이 패턴에 맞으면 파일 형식을 설정하는 자동명령의 형태를 띱니다. 예:
au BufNewFile,BufRead *.foo set filetype=foofoo
이 내용을 "ftdetect/foofoo.vim"이란 한 줄짜리 파일에 넣고, 'runtimepath' 중 첫 번째 디렉터리에 넣읍시다. 유닉스라면 "~/.vim/ftdetect/foofoo.vim"입니다. 파일 형식 이름을 스크립트 이름으로 쓰는 것이 관례입니다. 원한다면 더 복잡한 검사를 거칠 수도 있습니다. 예를 들어 언어를 인식하기 위해 파일의 내용을 분석한다든가 말이죠. |
new-filetype
|을 참고하세요. 요약 *plugin-special* ----- 플러그인 내에서 사용하는 특별한 것들을 정리해봅시다: s:name 스크립트 내에서만 유효한 변수<SID>
스크립트 아이디. 스크립트 내에서만 유효한 매핑과 함수에 쓴다. hasmapto() 사용자가 스크립트에서 제공하는 기능을 위한 매핑을 정의했는지 확인하는 함수.<Leader>
"mapleader"의 값. 플러그인의 매핑 앞부분의 키를 사용자가 정의할 때 쓴다. :map<unique>
매핑이 이미 존재하면 경고를 한다. :noremap<script>
전역 매핑이 아닌 스크립트 내 매핑만 사용한다. exists(":Cmd") 사용자 정의 명령이 존재하는지 확인한다.
*41.12* 파일 형식 플러그인 만들기 *write-filetype-plugin* *ftplugin* 파일 형식 플러그인은 범용 플러그인과 비슷하지만, 오직 현재 버퍼에 대해서만 설정을 바꾸고 매핑을 정의한다는 점이 다릅니다. 파일 형식 플러그인이 어떻게 쓰이는지에 대해서는 |add-filetype-plugin|을 참고하세요. 먼저 위 |41.11|의 범용 플러그인에 대한 절을 읽고 오세요. 거기서 설명한 것은 모두 파일 형식 플러그인에서도 마찬가지입니다. 여기에서는 몇 가지 추가사항만 설명하겠습니다. 중요한 점은 파일 형식 플러그인이 현재 버퍼에서만 효과가 있다는 것입니다. 비활성화 하기 ------------- 많은 사람들이 사용하는 파일 형식 플러그인을 만든다면, 로드되지 않게 비활성화 할 수 있어야 합니다. 플러그인의 맨 위에 다음 내용을 넣으세요:
" 이 버퍼에 대해 실행시키지 않았을 때만 실행
if exists("b:did_ftplugin")
finish
endif
let b:did_ftplugin = 1
이 부분은 같은 플러그인이 한 버퍼에 대해 두 번 실행되는 것(":edit" 명령을 인자 없이 실행하거나 하는 경우)도 막아줍니다. 사용자는 딱 다음 내용만 가진 파일 형식 플러그인을 만들어서 기본 플러그인들이 로드되는 것을 완전히 막을 수 있습니다:
let b:did_ftplugin = 1
이렇게 하려면 위 파일 형식 플러그인의 디렉터리가 'runtimepath' 옵션에서 $VIMRUNTIME보다 앞에 와야 합니다. 기본 플러그인을 쓰고는 싶지만 옵션 중 하나만 덮어쓰고 싶다면, 다른 옵션을 스크립트 안에 쓰면 됩니다:
setlocal textwidth=70
이번에는 이 스크립트 디렉터리가 $VIMRUNTIME 보다 뒤(after)에 와야 배포되는 "vim.vim" 파일 형식 플러그인보다 늦게 실행됩니다 |
after-directory
|. 유닉스에서는 "~/.vim/after/ftplugin/vim.vim"가 됩니다. 기본 플러그인에서 "b:did_ftplugin"을 설정했겠지만 여기서는 무시했다는 점에 주의하세요. 옵션 ----- 파일 형식 플러그인이 현재 버퍼에만 영향을 준다는 점을 확실히 하려면 옵션을 바꿀 때 다음 명령을 사용하세요:
:setlocal
그리고 버퍼 내에만 영향을 주는 옵션에 한해서 바꾸세요 (확인해보려면 옵션의 도움말을 보세요). 전역 옵션이나 창에 영향을 주는 옵션에 대해 |
:setlocal
|을 사용하면, 여러 버퍼에 영향을 줍니다. 파일 형식 플러그인이 할 일은 아니죠. 옵션 값이 플래그나 다른 값들의 목록인 경우에는, "+="나 "-="를 써서 기존 값들을 적절히 유지하는 방법을 고려해보십시오. 사용자가 옵션 값을 이미 바꾸었을 수도 있습니다. 먼저 기본 값으로 바꾼 후, 그 값을 바꾸는 것도 좋은 방법입니다. 예::setlocal formatoptions& formatoptions+=ro
매핑 ----- 매핑이 현재 버퍼에만 동작하도록 하려면 다음 명령을 사용하십시오:
:map
<buffer>
또한 위에서 설명한 2단계에 걸친 매핑방법과 함께 사용해야 합니다. 파일 형식 플러그인에 기능을 정의하는 예제를 하나 봅시다:
if !hasmapto('
<Plug>
JavaImport')map
<buffer>
<unique>
<LocalLeader>
i<Plug>
JavaImportendif
noremap
<buffer>
<unique>
<Plug>
JavaImport oimport ""<Left>
<Esc>
|
hasmapto()
|로 먼저 사용자가<Plug>
JavaImport로 가는 매핑을 정의했는지 확인합니다. 만약 없다면 파일 형식 플러그인에서 기본 매핑을 정의합니다. 이 매핑은<LocalLeader>
로 시작하는데, 사용자가 파일 형식 플러그인의 매핑이 시작하는 키(들)를 직접 정할 수 있게 해줍니다. 기본 값은 백슬래시입니다. "<unique>
"는 이미 매핑이 존재하거나 다른 매핑과 겹칠 때 에러 메시지를 주도록 합니다. |:noremap
|은 사용자가 정의한 다른 매핑과 섞이는 것을 막기 위한 것입니다. 스크립트에서 정의한<SID>
로 시작하는 매핑으로는 다시 매핑 되도록 하려면 "noremap<script>
"를 사용해야 합니다. 사용자는 파일 형식 플러그인에서 제공하는 매핑만을 비활성화 할 수 있어야 합니다. 다른 기능까지 전부 막지는 말고요. 아래는 메일(mail) 파일 형식의 플러그인에서 구현한 예제입니다:
" 매핑을 추가, 단 사용자가 거부하지 않는다면
if !exists("no_plugin_maps") && !exists("no_mail_maps")
" "> "를 입력해서 인용 효과
if !hasmapto('
<Plug>
MailQuote')vmap
<buffer>
<LocalLeader>
q<Plug>
MailQuotenmap
<buffer>
<LocalLeader>
q<Plug>
MailQuoteendif
vnoremap
<buffer>
<Plug>
MailQuote :s/^/> /<CR>
nnoremap
<buffer>
<Plug>
MailQuote :.,$s/^/> /<CR>
endif
두 개의 전역 변수가 사용되었습니다: no_plugin_maps 모든 파일 형식 플러그인의 매핑을 비활성화 no_mail_maps 특정 파일 형식에서만 매핑을 비활성화 사용자 정의 명령 ---------------- 특정 파일 형식에 대해 사용자 정의 명령을 추가해서 한 버퍼에서만 사용되게 하려면 |
:command
| 명령에 "-buffer" 인자를 주면 됩니다. 예:
:command -buffer Make make %:r.s
변수 ----- 파일 형식 플러그인은 해당되는 각각의 버퍼들마다 로드됩니다. 스크립트 내에서만 유효한 변수들은 |
s:var
| 모든 호출 간에 공유됩니다. 한 버퍼에서만 사용할 변수를 원한다면 버퍼 내에서만 유효한 변수 |b:var
| 를 사용하세요. 함수 ----- 함수를 정의할 때는, 한 번만 동작해야 합니다. 하지만 파일 형식 플러그인은 해당 형식의 파일이 열릴 때마다 계속 로드됩니다. 아래와 같은 구조를 사용하면 함수가 딱 한 번만 정의되도록 할 수 있습니다:
:if !exists("*s:Func")
: function s:Func(arg)
: ...
: endfunction
:endif
되돌리기 *undo_ftplugin* -------- 사용자가 ":setfiletype xyz"를 하면 기존 파일 형식으로 인한 효과는 사라져야 합니다. b:undo_ftplugin 변수에 파일 형식 플러그인의 옵션을 되돌릴 명령을 넣으십시오. 예:
let b:undo_ftplugin = "setlocal fo< com< tw< commentstring<"
\ . "| unlet b:match_ignorecase b:match_words b:match_skip"
":setlocal"을 옵션 이름 뒤의 "<"와 함께 사용하면 해당 옵션의 값을 전역 값으로 되돌립니다. 보통은 이게 옵션 값을 되돌리는 가장 좋은 방법이죠. 앞 |use-cpo-save|에서 언급했듯이, 위 예제가 동작하려면 여러 줄을 이어서 쓰기위해 'cpoptions'에서 "C" 플래그를 없애야 합니다. 파일명 ------ 파일명에 파일 형식 이름이 반드시 포함되어야 합니다 |ftplugin-name|. 아래 세 가지 형식 중 하나를 사용하세요: .../ftplugin/stuff.vim .../ftplugin/stuff_foo.vim .../ftplugin/stuff/bar.vim "stuff"는 파일 형식이고 "foo"나 "bar"는 임의의 이름입니다. 요약 *ftplugin-special* ----- 파일 형식 플러그인에서 사용하는 특별한 것들의 요약:
<LocalLeader>
"maplocalleader"의 값으로, 사용자가 파일 형식 플러그인에서 정의하는 매핑이 어떤 키로 시작할지 정할 수 있게 함. :map<buffer>
버퍼에서만 유효한 매핑을 정의. :noremap<script>
해당 스크립트에서 정의되고<SID>
로 시작하는 매핑으로만 다시 매핑 허용. :setlocal 현재 버퍼에서만 유효한 옵션 값 지정. :command -buffer 현재 버퍼에서만 유효한 명령 정의. exists("*s:Func") 함수가 이미 존재하는지 확인. |plugin-special|에서 모든 플러그인에서 사용되는 특별한 것들도 확인하세요.
*41.13* 컴파일러 플러그인 만들기 *write-compiler-plugin* 컴파일러 플러그인은 특정 컴파일러와 사용할 옵션을 지정합니다. 사용자는 |:compiler
| 명령으로 로드할 수 있습니다. 주로 'errorformat'과 'makeprg' 옵션을 지정하기 위해 사용합니다. 어떤 건지 아는 가장 쉬운 방법은 예제를 보는 것이죠. 다음 명령은 모든 기본 컴파일러 플러그인을 열어줍니다:
:next $VIMRUNTIME/compiler/*.vim
|
:next
| 명령으로 다음 플러그인으로 넘어가세요. 이 파일들에는 두 가지 특별한 것이 있습니다. 첫째는 사용자가 기본 파일의 옵션에 덮어쓰거나 추가할 수 있게 하는 방식입니다. 기본 파일은 다음과 같이 시작합니다:
:if exists("current_compiler")
: finish
:endif
:let current_compiler = "mine"
직접 컴파일러 플러그인 파일을 만들어서 개인 런타임 디렉터리(예를 들어 유닉스라면 ~/.vim/compiler)에 넣으면, "current_compiler" 변수를 설정해서 기본 파일은 설정을 하지 않도록 할 수 있습니다. *:CompilerSet* 두 번째는 ":compiler!"에는 ":set"을 쓰고, ":compiler"에는 ":setlocal"을 쓰는 것입니다. Vim에서는 이를 위해 "CompilerSet" 사용자 정의 명령을 정의하고 있습니다. 하지만 Vim 예전 버전에는 빠져있으므로, 플러그인에서 직접 정의해줘야 합니다. 아래와 같이 하면 됩니다:
if exists(":CompilerSet") != 2
command -nargs=* CompilerSet setlocal
<args>
endif
CompilerSet errorformat& " use the default 'errorformat'
CompilerSet makeprg=nmake
컴파일러 플러그인을 만들어 배포하거나 시스템 전체의 런타임 디렉터리에 넣으려면 위 두 가지 방식을 꼭 사용하십시오. 사용자 플러그인에서 "current_compiler"가 설정되어있다면 아무 일도 일어나지 않을 겁니다. 기본 플러그인의 옵션 값을 덮어쓰는 컴파일러 플러그인을 만들려면 "current_compiler"를 검사하지 마십시오. 이 플러그인은 나중에 로드되어야 하므로, 'runtimepath'의 마지막에 있는 디렉터리에 있어야 합니다. 유닉스에서라면 ~/.vim/after/compiler 일 것입니다.
*41.14* 빠르게 로드되는 플러그인 만들기 *write-plugin-quickload* 플러그인은 점점 늘어나서 꽤 길어질 수도 있습니다. 시작할 때 기다리는 시간이 눈에 띄게 길어져서 플러그인을 쓰기 싫어질 수도 있습니다. 빠르게 로드되는 플러그인이 등장할 때입니다. 기본 아이디어는 플러그인을 두 번 로드하는 겁니다. 첫 번째는 기능을 제공하는 사용자 정의 명령과 매핑만 정의합니다. 두 번째는 실제 기능을 정의하는 함수를 정의합니다. 빠르게 로드되는 플러그인이란 것이 스크립트를 두 번 로드한다는 게 이상하게 느껴질 겁니다. 실제로는 일단 첫 번째 로드만 하고 대부분의 스크립트는 두 번째 로드로 남겨둡니다. 그리고 실제로 사용자가 쓸 때, 두 번째 로드가 일어납니다. 만약 이 기능을 항상 사용한다면, 더 느려지는 것입니다! Vim 7 버전부터는 다른 방법도 있습니다. 바로 |autoload
| 기능을 사용하는 겁니다 |41.15|. 아래 예는 어떻게 하는 지를 보여줍니다:
" 빠른 로드를 보여주기 위한 범용 Vim 플러그인
" Last Change: 2005 Feb 25
" Maintainer: Bram Moolenaar
<Bram@vim.org>
" License: This file is placed in the public domain.
if !exists("s:did_load")
command -nargs=* BNRead call BufNetRead(
<f-args>
)map
<F19>
:call BufNetWrite('무언가')<CR>
let s:did_load = 1
exe 'au FuncUndefined BufNet* source ' . expand('
<sfile>
')finish
endif
function BufNetRead(...)
echo 'BufNetRead(' . string(a:000) . ')'
" 읽기 기능은 여기에
endfunction
function BufNetWrite(...)
echo 'BufNetWrite(' . string(a:000) . ')'
" 쓰기 기능은 여기에
endfunction
처음 스크립트가 로드 될 때는 "s:did_load"가 정의되지 않은 상태입니다. 그래서 "if"와 "endif" 사이의 내용이 실행됩니다. |
:finish
| 명령으로 끝나기에 이후의 부분은 실행되지 않습니다. 두 번째 로드 될 때는 "s:did_load"가 존재하므로 "endif" 이후의 명령이 실행됩니다. 여기서는 (아마도 긴 내용을 가졌을) BufNetRead()와 BufNetWrite() 함수를 정의합니다. 이 스크립트를 플러그인 디렉터리에 추가하면, Vim이 구동될 때 실행됩니다. 실제 동작이 일어나는 순서는 다음과 같습니다: 1. 구동 시 스크립트가 로드될 때, "BNRead" 명령이 정의되고<F19>
키가 매핑 됩니다. |FuncUndefined
| 자동명령도 정의됩니다. ":finish" 명령으로 스크립트 끝에 도달하기 전에 종료됩니다. 2. 사용자가 BNRead 명령을 입력하거나<F19>
키를 누릅니다. BufNetRead()나 BufNetWrite() 함수가 호출됩니다. 3. Vim은 해당 함수를 찾을 수 없으므로 |FuncUndefined
| 자동명령 이벤트가 발생합니다. "BufNet*" 패턴이 호출된 함수와 일치하므로, "source fname" 명령이 수행됩니다. "fname"은 실제 위치에 관계없이 스크립트 이름과 동일합니다. "<sfile>
"을 확장해서 얻은 것이기 때문이죠(|expand()
|를 참고). 4. 스크립트는 다시 로드되고, "s:did_load" 변수가 존재하므로 함수가 정의됩니다. 나중에 정의되는 함수가 |FuncUndefined
| 자동명령의 패턴과 일치해야한다는데 주의하세요. 다른 플러그인이 정의하는 함수가 이 패턴에 일치하지 않는지도 확실히 해야 합니다.
*41.15* 라이브러리 스크립트 만들기 *write-library-script* 몇몇 기능은 여러 곳에서 필요할 겁니다. 이런 내용이 몇 줄 수준을 넘어가면 아예 하나의 스크립트에 넣고 그걸 여러 곳에서 쓰면 좋겠지요. 그런 스크립트를 라이브러리 스크립트(library script)라고 합니다. 이미 로드했을 때 또 로드하는 것만 막는다면, 직접 라이브러리 스크립트를 로드할 수도 있습니다. |exists()
| 함수를 쓰면 됩니다. 예:
if !exists('*MyLibFunction')
runtime library/mylibscript.vim
endif
call MyLibFunction(arg)
여기서는 MyLibFunction()이 'runtimepath' 디렉터리들 중 하나에 있는 "library/mylibscript.vim" 이란 스크립트에 정의되어있다는 걸 알아야 합니다. 좀 더 간단하게 만들기 위해, Vim에서는 자동 로드 기능을 제공합니다. 위 예제는 다음과 같이 바뀝니다:
call mylib#myfunction(arg)
훨씬 간단하죠? Vim은 함수 이름을 인식한 후, 만약 정의되어있지 않다면 'runtimepath'에서 "autoload/mylib.vim" 스크립트를 찾아봅니다. 이 스크립트는 반드시 "mylib#myfunction()" 함수를 정의해야 합니다. mylib.vim 파일에 다른 여러 함수를 넣을 수도 있습니다. 라이브러리 스크립트에 함수를 넣는 것은 자유죠. 하지만 함수 이름에서 '#' 이전 부분이 반드시 스크립트 이름과 일치해야 합니다. 아니면 어떤 스크립트를 로드해야할지 알 수 없으니까요. 만약 당신이 열혈 프로그래머여서 수많은 라이브러리 스크립트를 만들었다면, 하위 디렉터리를 쓰고 싶을 수도 있을 겁니다. 예:
call netlib#ftp#read('somefile')
유닉스라면 사용되는 라이브러리 스크립트는 다음과 같을 겁니다: ~/.vim/autoload/netlib/ftp.vim 함수가 다음과 같이 정의되었다면:
function netlib#ftp#read(fname)
" ftp를 통해 fname 파일을 읽음
endfunction
함수의 이름이 호출할 때와 정확히 일치하게 정의되었습니다. 마지막 '#' 앞의 부분은 하위 디렉터리 및 스크립트 이름과 일치합니다. 변수에도 동일한 방식을 쓸 수 있습니다:
let weekdays = dutch#weekdays
위 예는 "autoload/dutch.vim"을 로드합니다. 여기엔 아마 다음과 같은 내용이 들어있을 겁니다:
let dutch#weekdays = ['zondag', 'maandag', 'dinsdag', 'woensdag',
\ 'donderdag', 'vrijdag', 'zaterdag']
더 관심이 있다면: |
autoload
|.
*41.16* Vim 스크립트 배포하기 *distribute-script* Vim 사용자들은 Vim 웹 사이트 http://www.vim.org 에서 스크립트를 찾을 겁니다. 다른 사람들에게도 유용할만한 것을 만들었다면, 공유하세요! Vim 스크립트는 모든 시스템에서 쓸 수 있습니다. 하지만 tar나 gzip은 없을 수도 있겠지요. 여러 파일을 하나로 묶거나 압축하고 싶다면 집(zip)을 쓰는 것을 추천합니다. 이식성을 최대한 확보하려면 Vim으로 스크립트들을 압축하세요. 빔볼(Vimball) 유틸리티를 쓰면 됩니다. |vimball
|을 참고하세요. 자동 업데이트를 위한 줄 하나를 추가하는 것도 좋겠네요. |glvs-plugins
|를 참고하세요.
다음 장: |usr_42| 새 메뉴 추가하기 저작권: |manual-copyright| 참고 vim:tw=78:ts=8:ft=help:norl:
Generated by vim2html on 2013. 12. 14. (토) 20:47:01 KST