*usr_44.txt* Vim version 7.4 대상. 새로 고침: 2008년 12월 28일
VIM 사용설명서 - Bram Moolenaar 저
정지용 역
나만의 문법 강조 만들기
Vim에는 2백여 가지 파일 형식에 대한 문법 강조 기능이 들어있습니다. 만약
당신이 작성하고 있는 파일이 여기에 포함되지 않는다면, 이번 장을 읽고 해당
파일 형식에 대한 문법 강조 만드는 법을 알아보세요. 상세 설명서의
|:syn-define
|도 참고하세요.
|44.1| 기초 문법 명령
|44.2| 키워드
|44.3| 매치
|44.4| 영역
|44.5| 중첩된 항목
|44.6| 후속 그룹
|44.7| 다른 인자들
|44.8| 클러스터
|44.9| 다른 문법 파일 포함시키기
|44.10| 맞추기
|44.11| 문법 파일 설치하기
|44.12| 어디서나 쓸 수 있는 문법 파일 형식
다음 장: |usr_45| 내 언어 고르기
이전 장: |usr_43| 파일 형식 플러그인 사용하기
차례: |usr_toc|
*44.1* 기초 문법 명령 이미 존재하는 문법 파일로부터 시작하면 많은 시간을 아낄 수 있습니다. $VIMRUNTIME/syntax 디렉터리에서 비슷한 언어의 문법 파일을 찾아보세요. 문법 파일들을 보면 일반적인 문법 파일의 구조도 알 수 있을 겁니다. 문법 파일을 이해하기 위해 아래 내용들을 먼저 공부해 봅시다. 기본적인 인자들부터 시작합시다. 새로운 문법을 정의하기에 앞서, 기존의 정의들을 모두 지워야겠지요:
:syntax clear
최종 문법 파일에서는 필요하지 않지만, 그에 앞서 이것저것 해볼 땐 유용할 겁니다. 이번 장에서는 많이 간소화하여 설명하고 있습니다. 다른 사람들이 사용하는 문법 파일을 쓰려면, 끝까지 모두 읽고 자세한 내용을 알아보세요. 정의한 문법 나열하기 -------------------- 현재 정의된 문법 항목들을 보려면 다음 명령을 사용하세요:
:syntax
이 명령은 어떤 것들이 정의되어있는지 확인할 때도 사용할 수 있습니다. 새로운 문법 파일을 실험할 때는 꽤 유용합니다. 이 명령은 각 항목이 표시되는 색깔도 보여주므로 무엇이 어떤 항목에 해당하는지 더 쉽게 알 수 있습니다. 특정 문법 그룹에 속하는 항목들을 보려면 다음 명령을 사용하세요:
:syntax list
{그룹명}
이 명령은 클러스터(|44.8|에 나옵니다)의 항목들을 볼 때도 쓸 수 있습니다. 이름 앞에 @만 붙이세요. 대소문자 맞추기 --------------- 파스칼 같이 어떤 언어는 대소문자를 구별하지 않고, C 같이 어떤 언어는 대소문자를 구별합니다. 정의하는 언어가 어떤 쪽인지 다음 명령으로 지정해줘야 합니다:
:syntax case match
:syntax case ignore
"match" 인자는 대소문자를 구별한다는 의미입니다. 그러므로 "int"와 "Int", "INT"는 모두 다른 것입니다. 만약 "ignore"를 사용했다면, "Procedure"와 "PROCEDURE", "procedure"는 모두 같습니다. ":syntax case"는 문법 파일의 어디에나 쓸 수 있고, 그 이후에 나오는 문법 정의들에 영향을 줍니다. 대부분의 문법 파일에는 ":syntax case" 명령이 딱 한 번만 나오겠지요. 하지만 만약 어떤 부분은 대소문자를 구별하지 않고, 어떤 부분은 구별하는 독특한 언어에 대해서 작업하고 있다면 파일 여기저기에 ":syntax case"가 등장할 수도 있을 겁니다.
*44.2* 키워드 가장 기본적인 문법 요소는 키워드입니다. 키워드는 아래와 같은 형식으로 정의합니다:
:syntax keyword
{group}
{keyword}
...
{group}
은 문법 그룹의 이름입니다. ":highlight" 명령으로 그룹 별 색깔을 지정하게 됩니다.{keyword}
에는 실제 키워드가 들어갑니다. 예제를 몇 가지 보겠습니다:
:syntax keyword xType int long char
:syntax keyword xStatement if then else endif
위 예에서는 그룹명으로 "xType"과 "xStatement"를 사용하고 있습니다. 관습적으로 각 그룹명에는 대상 언어의 파일 형식 이름을 앞에 붙입니다. 여기서는 x 언어(재미없는 예제(eXample) 언어) 문법을 정의하고 있습니다. 만약 "csh" 스크립트를 위한 문법 파일이었다면 "cshType"을 썼을 것입니다. 즉, 그룹명의 접두어가 'filetype'(파일 형식)의 값과 같습니다. 위 명령들은 "int", "long", "char"가 같은 방식으로, 또 "if", "then", "else", "endif"가 또 다른 방식으로 강조되도록 합니다. 이제 x 그룹명들을 표준 Vim 이름으로 연결만 하면 됩니다. 다음 명령들로 할 수 있습니다:
:highlight link xType Type
:highlight link xStatement Statement
위 명령은 "xType"은 "Type"과 같이 강조하고, "xStatement"는 "Statement"처럼 강조하라는 뜻입니다. 표준 Vim 이름들은 |
group-name
|을 참고하세요. 특이한 키워드 ------------- 키워드에 사용되는 문자는 반드시 'iskeyword' 옵션 값에 있어야 합니다. 다른 문자를 사용한다면 절대 인식이 안 될 겁니다. Vim에서 따로 경고하지도 않고요. x 언어는 키워드에 '-' 문자를 사용합니다. 다음과 같이 하면 됩니다::setlocal iskeyword+=-
:syntax keyword xStatement when-not
":setlocal" 명령은 'iskeyword' 값을 현재 버퍼에서만 바꾸기 위해 사용했습니다. 단, 위와 같이 하면 "w"나 "*" 기능까지 함께 바뀝니다. 만약 이걸 원하지 않는다면, 키워드를 정의하지 말고 매치(다음 절에 바로 나옵니다)를 사용하세요. x 언어는 축약형을 지원합니다. 예를 들어 "next"는 "n"이나 "ne", "nex"로도 쓸 수 있지요. 다음과 같이 정의하면 됩니다:
:syntax keyword xStatement n[ext]
하지만 "nextone"는 키워드로 인식하지 않습니다. 키워드는 언제나 단어 전체만 인식합니다.
*44.3* 매치 이제 더 복잡한 걸 정의해볼까요? 일반적인 식별자를 인식해봅시다. 그러려면 매치 문법 항목을 정의해야 합니다. 다음은 소문자로만 이루어진 단어를 인식하는 예입니다:
:syntax match xIdentifier /\
<\l\+\>
/Note:
키워드는 다른 문법 항목들보다 우선시 됩니다. 따라서 "if", "then" 등은 위의 ":syntax keyword" 명령으로 정의되었으므로 키워드입니다. 비록 xIdentifier의 패턴과 일치하긴 하지만요. 맨 뒷부분은 보통 찾기할 때 쓰던 패턴입니다. //는 패턴을 감싸기 위해 사용한 것입니다(":substitute" 명령에서와 마찬가지로요). 덧셈 기호나 따옴표 같은 다른 문자를 쓸 수도 있습니다. 이제 주석(Comment)을 위한 매치를 정의해봅시다. x 언어에서는 #에서부터 그 줄의 끝까지가 주석입니다:
:syntax match xComment /#.*/
어떤 찾기 패턴이나 쓸 수 있으므로, 매치로 아주 복잡한 것들도 강조할 수 있습니다. 찾기 패턴에 대하여 더 알고 싶다면 |
pattern
|을 참고하세요.
*44.4* 영역(Region) 위 x 언어에서, 문자열은 큰따옴표(")안에 들어갑니다. 문자열을 강조하려면 영역(region)을 정의해야 합니다. 영역의 시작(큰따옴표)과 끝(큰따옴표)이 필요합니다. 다음과 같이 정의하면 됩니다:
:syntax region xString start=/"/ end=/"/
"start"와 "end"는 영역의 시작과 끝을 알기 위해 찾아야하는 패턴을 정의합니다. 그런데 만약 다음과 같은 문자열이 있다면 어떻게 해야 할까요?
"큰따옴표(\")를 포함하고 있는 문자열"
여기서 문제가 생깁니다. 문자열 중간에 있는 큰따옴표가 영역을 끝내버릴 테니까요. 중간의 이스케이프된 큰따옴표는 그냥 넘어가라고 알려줘야 합니다. skip 지시자를 쓰면 됩니다:
:syntax region xString start=/"/ skip=/\\"/ end=/"/
패턴에서 백슬래시는 특수 문자인 관계로 두 개의 백슬래시는 하나의 백슬래시에 대응됩니다. 언제 매치 대신 영역을 써야할까요? 가장 큰 차이는 매치가 하나의 패턴이므로 통째로 일치해야한다는 것입니다. 영역은 "start" 패턴이 일치하면 바로 시작됩니다. "end" 패턴의 일치 여부는 관계없이 말이죠. 따라서 일치 여부가 "end" 패턴에 따라 결정된다면, 영역을 쓰면 안 됩니다. 또, 영역이 정의하기 간단한 경우가 많습니다. 중첩된 항목을 쓰기에도 좋고요. 다음 절에서 더 살펴보겠습니다.
*44.5* 중첩된 항목 다음 주석을 한 번 보세요:%입력 받기 TODO: 공백을 건너뛰기
위 예에서 TODO 부분을 노란색 문자로 보고 싶다고 합시다. 주석이므로 이미 파란 색으로 표시되고 있는 데도요. 이렇게 처리하려면, 다음과 같이 문법 그룹을 정의해야 합니다:
:syntax keyword xTodo TODO contained
:syntax match xComment /%.*/ contains=xTodo
첫 번째 줄에서 "contained" 인자는 이 키워드가 다른 문법 항목 안에만 있을 수 있다고 알려줍니다. 다음 줄에는 "contains=xTodo"가 있지요. 이건 xTodo 문법 항목이 이 안에 있을 수 있다는 말입니다. 그 결과, 주석 부분 전체가 "xComment" 그룹이 되어 파란 색이 됩니다. 그리고 그 안의 TODO는 xTodo로 인식되어 노란색으로 강조됩니다(xTodo가 이렇게 색깔이 지정되었다고 합시다). 재귀 중첩 --------- x 언어는 코드 블록을 중괄호로 정의합니다. 코드 블록 안에는 다른 코드 블록이 올 수 있습니다. 이는 다음과 같이 정의할 수 있습니다:
:syntax region xBlock start=/
{/ end=/}
/ contains=xBlock아래와 같은 내용이 있다고 합시다:
while i < b {
if a {
b = c;
}
}
첫 xBlock이 첫 번째 줄의 { 에서 시작됩니다. 두 번째 줄에는 또 다른 {가 나옵니다. 이미 xBlock 항목 안에 있고, xBlock은 다른 xBlock을 포함할 수 있으므로, 중첩된 xBlock 항목이 여기서 시작됩니다. 따라서 "b = c" 줄은 2단계 xBlock 영역에 있는 거지요. 그 다음 줄에는 }가 나오는데, 영역의 끝(end) 패턴과 일치합니다. 여기서는 중첩된 xBlock이 끝납니다. 이는 }가 중첩된 영역 안에 있고, 따라서 첫 번째 xBlock 영역으로부터 감춰져있기 때문입니다. 그리고 마지막 }가 첫 번째 xBlock 영역을 끝내게 됩니다. 끝을 보존하기 ------------- 다음과 같은 두 문법 항목을 살펴봅시다:
:syntax region xComment start=/%/ end=/$/ contained
:syntax region xPreProc start=/#/ end=/$/ contains=xComment
주석은 %에서 줄의 끝까지 전체라고 정의했습니다. 전처리 지시자(Preprocessor directive)는 #에서 줄의 끝까지 전체입니다. 전처리 지시자가 있는 줄에 주석이 있을 수도 있으므로, 전처리 지시자 정의에 "contains=xComment" 인자가 들어있습니다. 이제 다음 예문이 어떻게 처리되는 지 봅시다:
#define X = Y % 주석 내용
int foo = 1;
아니 그런데 두 번째 줄까지 xPreProc으로 강조되어있을 겁니다. 전처리 지시자는 해당 줄에서 끝나야하는데 말이지요. 이건 "end=/$/"를 썼기 때문입니다. 대체 무엇이 잘못된 걸까요? 문제는 포함된 주석 부분입니다. 주석은 %에서 시작해서 줄의 끝에서 끝납니다. 주석이 끝난 후에 전처리 지시자 문법이 이어집니다. 이건 이미 줄의 끝을 지난 후이므로 다음 줄의 내용으로 이어져버립니다. 이 문제를 피하고 포함된 문법 항목이 필요한 줄의 끝을 먹어버리는 것을 막으려면, "keepend" 인자를 쓰면 됩니다. 다음 예는 중복된 줄의 끝 문제를 수정한 것입니다:
:syntax region xComment start=/%/ end=/$/ contained
:syntax region xPreProc start=/#/ end=/$/ contains=xComment keepend
여러 가지 항목을 포함하기 ------------------------- contains 인자는 모든 항목을 포함할 수 있다고 지정할 수 있습니다. 예를 들어:
:syntax region xList start=/\[/ end=/\]/ contains=ALL
모든 문법 항목이 여기에 포함될 수 있습니다. 그 자신도 포함합니다. 단, 같은 위치에서는 말고요(무한 루프에 빠질 테니까요). 몇몇 그룹은 포함시키지 않고 싶을 수도 있습니다. 따라서 지정한 그룹들을 제외한 모든 그룹을 포함하려면:
:syntax region xList start=/\[/ end=/\]/ contains=ALLBUT,xString
"TOP" 을 쓰면 "contained"가 없는 모든 항목을 포함할 수 있습니다. "CONTAINED"는 "contained"가 있는 모든 항목을 포함할 때 씁니다. 자세한 내용은 |
:syn-contains
|를 참고하세요.
*44.6* 후속 그룹 x 언어에는 다음과 같은 형식의 문장이 있습니다:if (조건문) then
세 가지 항목을 모두 다르게 강조하고 싶다고 합시다. 하지만 "(조건문)"이나 "then"은 다른 곳에서도 나타날 수 있습니다. 이건 또 다르게 강조하고 싶습니다. 그렇다면 다음과 같이 하면 됩니다:
:syntax match xIf /if/ nextgroup=xIfCondition skipwhite
:syntax match xIfCondition /([^)]*)/ contained nextgroup=xThen skipwhite
:syntax match xThen /then/ contained
"nextgroup" 인자로 어떤 항목이 다음에 올지 지정합니다. 꼭 뒤에 해당 항목이 와야 하는 것은 아닙니다. 만약 지정된 항목이 없으면 아무 일도 일어나지 않습니다. 예를 들어 다음 예를 봅시다:
if not (조건문) then
"if"는 xIf와 일치하지만 "not"은 지정된 후속 그룹인 xIfCondition과 일치하지 않습니다. 이러면 그냥 "if"만 강조 됩니다. "skipwhite" 인자는 항목들 사이에 공백이나 탭들이 나타날 수 있음을 지시합니다. 비슷한 것으로 항목들 사이에서 줄이 바뀌어도 되는 "skipnl"이나, 빈 줄이 있어도 되는 "skipempty"가 있습니다. "skipnl"은 빈 줄을 무시하지 않으니 주의하세요. 반드시 다음 줄에 무언가가 와야 합니다.
*44.7* 다른 인자들 매치그룹 -------- 영역을 정의할 때, 전체 영역이 지정된 그룹 이름에 따라 강조됩니다. 예를 들어 괄호 ()로 둘러싸인 내용을 강조하고 싶다면 다음 명령을 쓰면 됩니다:
:syntax region xInside start=/(/ end=/)/
하지만, 여기서 괄호만 다르게 강조하고 싶다면 어떨까요? 복잡한 영역을 여러 개 만들어서 할 수도 있겠지만, 매치그룹(matchgroup)을 사용하면 됩니다. 매치 그룹은 영역의 시작과 끝부분을 다른 그룹(여기서는 xParen 그룹이지요)으로 지정하여 강조하라는 것입니다:
:syntax region xInside matchgroup=xParen start=/(/ end=/)/
"matchgroup" 인자는 그 뒤에 오는 시작 및 끝 패턴에 적용됩니다. 위 예에서는 시작과 끝 모두 xParen으로 강조했지요. 만약 끝은 xParenEnd 그룹을 쓰고 싶다면:
:syntax region xInside matchgroup=xParen start=/(/
\ matchgroup=xParenEnd end=/)/
매치그룹의 부작용은 영역의 시작과 끝부분에 있는 포함된(contained) 항목이 인식되지 않는다는 것입니다. 아래 "투명(transparent)"에서 예제를 볼 수 있습니다. 투명(transparent) ----------------- C 언어 파일에서, "while" 뒤의 () 안 내용과 "for" 뒤의 () 안 내용을 다르게 강조하고 싶다고 합시다. 둘 다 괄호 안에 중첩된 () 들이 있을 수 있고, 이들은 동일하게 강조되어야 합니다. 그리고 () 부분의 강조는 괄호 쌍을 맞춰서 올바른 )에서 끝나야 합니다. 아래와 같이 할 수 있습니다:
:syntax region cWhile matchgroup=cWhile start=/while\s*(/ end=/)/
\ contains=cCondNest
:syntax region cFor matchgroup=cFor start=/for\s*(/ end=/)/
\ contains=cCondNest
:syntax region cCondNest start=/(/ end=/)/ contained transparent
이제 cWhile과 cFor에 다른 강조 방식을 지정하면 되겠지요. 양쪽 모두에 cCondNest가 등장하는데, 자신을 포함하고 있는 항목의 강조를 그대로 사용합니다. "transparent" 인자가 하는 일이 그것입니다. 위 예를 보면 "matchgroup" 인자가 지정하는 그룹과 동일한 그룹을 쓰고 있습니다. 왜 이렇게 정의하는 걸까요? 매치그룹의 부작용으로 영역의 시작 부분으로 인식되는 곳에는 포함된 항목이 인식되지 않습니다. 따라서 위와 같이 쓰면 "while"이나 "for" 바로 뒤에 오는 (는 cCondNest 그룹으로 인식되지 않는 것이지요. 만약 바로 뒤의 (가 cCondNest로 인식될 수 있다면 () 안의 전체 내용이 cCondNest로 인식되어버리고 cWhile이나 cFor 영역은 그 뒤로도 계속 이어져버릴 테지요. 위와 같이 정의하면 cCondNest는 영역의 시작 패턴, 즉 첫 번째 ( 이후부터 인식이 될 수 있습니다. 오프셋 ------ 예를 들어, "if" 뒤에 오는 "("와 ")" 사이의 내용을 영역으로 정의하고 싶다고 합시다. "if"와 "(", ")"는 빼고요. 패턴에 오프셋을 지정해서 하면 됩니다. 예:
:syntax region xCond start=/if\s*(/ms=e+1 end=/)/me=s-1
시작 패턴의 오프셋은 "ms=e+1"입니다. "ms"는 일치 시작(Match Start)를 의미합니다. 패턴 일치의 시작 오프셋을 정의하는 것이지요. 일반적으로는 패턴이 시작되는 곳이 패턴 일치의 시작입니다. "e+1"는 패턴 일치가 실제로 패턴이 일치하는 부분이 끝난 다음, 거기서 한 문자 더 뒤에서 시작한다는 뜻입니다. 끝 패턴의 오프셋은 "me=s-1"입니다. "me"는 일치 끝(Match End)를 의미합니다. "s-1"은 패턴이 실제로 일치하는 부분의 시작 위치에서 한 문자 더 앞으로 간다는 의미입니다. 그 결과는 실제 내용이 다음과 같다고 할 때:
if (foo == bar)
"foo == bar" 부분만이 xCond로 강조됩니다. 오프셋에 대한 더 자세한 내용: |:syn-pattern-offset
|. 한 줄(oneline) -------------- "oneline"인자는 영역이 다음 줄까지 이어지면 안 된다는 것입니다. 예:
:syntax region xIfThen start=/if/ end=/then/ oneline
위 예는 "if"에서 시작하고 "then"에서 끝나는 영역을 정의합니다. 만약 "if" 뒤에 "then"이 없다면, 영역이 인식되지 않습니다.
Note:
"oneline"이 사용되면, 같은 줄에서 끝 패턴이 없는 경우 영역 전체가 인식되지 않습니다. "oneline"이 없다면 끝 패턴이 있는지 확인하지 _않고_ 인식이 시작됩니다. 파일의 나머지 전체에 끝 패턴이 없더라도 말이지요. 이어지는 줄 및 이어지지 않게 하기 --------------------------------- 이제 좀 슬슬 복잡해집니다. 전처리기(preprocessor) 내용을 정의해봅시다. 전처리기 내용은 줄 첫 칸의 #으로 시작해서 줄의 끝까지입니다. \로 끝나는 줄은 다음 줄로 이어지는 줄입니다. 이걸 구현하는 방법은 다음과 같이 문법 항목이 이어지는 줄 패턴을 포함시키는 것입니다:
:syntax region xPreProc start=/^#/ end=/$/ contains=xLineContinue
:syntax match xLineContinue "\\$" contained
여기서 xPreProc은 일반적으로 줄 하나에 대응되지만, 여기에 포함된 xLineContinue라는 그룹이 여러 줄로 확장될 수 있게 만듭니다. 예를 들어 다음 두 줄의 내용 전체가 인식됩니다:
#define SPAM spam spam spam \
bacon and spam
여기서는 이게 우리가 원하는 것이지요. 만약 이렇게 여러 줄이 되는 것을 원하지 않는다면, 포함되는 패턴에 "excludenl"을 추가해서 영역이 한 줄이 되도록 하면 됩니다. 예를 들어, xPreProc 안에서 "end"를 줄의 맨 끝인 경우에만 강조하고 싶다고 합시다. 앞의 xLineContinue처럼 xPreProc이 다음 줄로 계속되는 것을 막으려면, 다음과 같이 "excludenl"을 사용하세요:
:syntax region xPreProc start=/^#/ end=/$/
\ contains=xLineContinue,xPreProcEnd
:syntax match xPreProcEnd excludenl /end$/ contained
:syntax match xLineContinue "\\$" contained
"excludenl"은 패턴 앞에 와야 합니다. "xLineContinue"에는 "excludenl"이 없으므로, 전과 같이 xLineContinue를 포함한 xPreProc은 여러 줄이 됩니다.
*44.8* 클러스터 문법파일을 쓰다보면 가장 먼저 느끼게 되는 것 중 하나는 수많은 문법 그룹을 만들어야만 하게 된다는 점입니다. Vim에서는 여러 문법 그룹을 모아 클러스터(cluster)를 만들 수 있습니다. 예를 들어 for 루프와 if 문, while 루프, 함수를 가진 언어가 있다고 합시다. 각각은 모두 숫자와 식별자라는 동일한 문법 요소들을 포함하고 있습니다. 아래와 같이 정의할 겁니다:
:syntax match xFor /^for.*/ contains=xNumber,xIdent
:syntax match xIf /^if.*/ contains=xNumber,xIdent
:syntax match xWhile /^while.*/ contains=xNumber,xIdent
똑같은 "contains=" 부분을 계속 반복해야하지요. 다른 항목이 추가로 포함되게 하려면, 똑같은 입력을 세 번 해야 합니다. 문법 클러스터는 클러스터 하나가 여러 개의 문법 그룹을 대표하게 함으로써 정의를 더 간단하게 만들어줍니다. 세 그룹이 포함하고 있는 두 항목들의 클러스터를 정의하려면, 다음 명령을 사용하십시오:
:syntax cluster xState contains=xNumber,xIdent
클러스터는 문법 그룹과 동일하게 다른 문법 항목 안에서 쓸 수 있습니다. 클러스터 이름은 @로 시작합니다. 따라서 위의 세 그룹은 다음과 같이 정의할 수 있습니다:
:syntax match xFor /^for.*/ contains=@xState
:syntax match xIf /^if.*/ contains=@xState
:syntax match xWhile /^while.*/ contains=@xState
클러스터에 "add" 인자로 새로운 그룹을 추가할 수도 있습니다:
:syntax cluster xState add=xString
물론 문법그룹을 뺄 수도 있습니다:
:syntax cluster xState remove=xNumber
*44.9* 다른 문법 파일 포함시키기 C++ 언어는 C 언어의 확장입니다. 두 벌의 문법 파일을 쓰고 싶진 않으니 C++ 문법파일은 다음과 같이 C 문법파일을 읽어오도록 합시다:
:runtime! syntax/c.vim
":runtime!" 명령은 'runtimepath'에서 모든 "syntax/c.vim" 파일을 찾습니다. 이로써 C++ 문법의 C 부분은 C 파일에서와 동일하게 정의되었습니다. 만약 c.vim 문법 파일을 바꿨거나, 추가 파일에 몇 가지를 더 넣었다면 이것들도 마찬가지로 반영될 겁니다. C 문법 항목들을 불러온 후 C++에만 있는 것들을 정의해야 합니다. 예를 들어 C에는 없는 키워드들을 추가해봅시다:
:syntax keyword cppStatement new delete this friend using
다른 문법 파일에서와 마찬가지로 동작합니다. 이제 펄(Perl) 언어를 생각해봅시다. 펄 스크립트는 크게 두 부분으로 이루어집니다. POD 형식의 문서 부분과 펄로 쓴 프로그램 부분이요. POD 부분은 "=head"로 시작해서 "=cut"으로 끝납니다. POD 문법만 하나의 파일에 정의하고, 이를 펄 문법 파일에서 사용하고 싶다고 합시다. ":syntax include" 명령은 문법 파일을 읽어다 그 안에서 정의된 항목들을 문법 클러스터에 저장합니다. 펄이라면 다음과 같을 겁니다:
:syntax include @Pod
<sfile>
:p:h/pod.vim:syntax region perlPOD start=/^=head/ end=/^=cut/ contains=@Pod
펄 파일에서 "=head"를 만나면, perlPOD 영역이 시작됩니다. 이 영역에는 @Pod 클러스터가 포함되어있습니다. pod.vim 문법 파일에서 최상위 단계로 정의된 모든 항목이 여기서 인식됩니다. "=cut"이 나오면, 영역은 끝나고 펄 파일에 정의된 항목들이 인식됩니다. ":syntax include" 명령은 영리해서 포함시킨(include) 파일 안의 ":syntax clear" 명령을 알아서 무시합니다. 또한 "contains=ALL" 같은 인자도 포함하는 파일까지 전부 고려해서 해석하는 것이 아니라, 포함시킨 파일의 항목들만 잘 알아서 고려합니다. "
<sfile>
:p:h/" 부분은 현재 파일(<sfile>
)의 이름을 전체 경로(:p)로 확장한 다음, 그 앞부분(:h)만 가져다 쓰는 것입니다. 그 결과는 파일의 디렉터리명이 되지요. 따라서 같은 디렉터리의 pod.vim을 포함시키라는 의미입니다.
*44.10* 맞추기(Synchronizing) 컴파일러는 참 좋겠습니다. 파일의 처음부터 쭉 읽어가면서 그대로 분석하면 되니까요. Vim은 참 힘듭니다. 편집을 시작하는 부분인 파일의 중간에서부터 분석을 해야 하니까요. 그게 어디인지 어떻게 알 수 있을까요? 비밀은 ":syntax sync" 명령입니다. 이 명령은 Vim이 여기가 어디인지 판단하는 방법을 알려줍니다. 예를 들어, 다음 명령은 C 스타일 주석의 시작이나 끝을 파일의 윗부분으로 가면서 찾고, 거기서부터 문법 강조를 하라고 알려줍니다::syntax sync ccomment
이 처리를 몇 가지 인자로 더 조정할 수도 있습니다. "minlines"인자는 몇 줄 이상은 위로 가라고 지정하고, "maxlines"는 몇 줄 까지만 위로 가라고 지정합니다. 예를 들어, 아래 명령은 화면 맨 위로부터 최소 10줄은 살펴보라는 명령입니다:
:syntax sync ccomment minlines=10 maxlines=500
만약 이 범위 안에서 문법 강조를 시작할 부분을 찾지 못하면, 찾을 때까지 위로 위로 갑니다. 하지만 500 줄보다 더 위로는 가지 않습니다. ("maxlines"가 너무 크면 처리 속도가 느려집니다. 하지만 너무 작으면 제대로 해석을 못할 수 있겠지요.) 맞추는 과정을 빠르게 하려면, 어떤 문법 항목은 넘어갈 수 있다고 알려주면 됩니다. 화면에 보여줄 때만 필요한 모든 매치와 영역은 "display" 인자를 주면 됩니다. 기본적으로 찾은 주석은 Comment 문법 그룹의 일부로 색깔이 칠해집니다. 만약 다른 방법으로 칠하고 싶다면, 다른 문법 그룹을 지정할 수 있습니다:
:syntax sync ccomment xAltComment
만약 당신의 프로그래밍 언어에 C 스타일 주석 같은 것이 없다면, 다른 방법으로 맞추어야 합니다. 가장 쉬운 방법은 몇 줄 위로 가서, 거기서부터 분석을 시도해보는 것입니다. 아래 명령은 150줄을 거기서부터 분석하라는 것입니다:
:syntax sync minlines=150
"minlines"가 크면 Vim이 느려질 수 있습니다. 특히 파일에서 위쪽으로 스크롤 할 때요. 마지막으로, 다음 명령으로 찾을 문법 그룹을 지정할 수 있습니다:
:syntax sync match
{sync-group-name}
\ grouphere
{group-name}
{pattern}
이 명령은
{pattern}
패턴을 만나면,{group-name}
에 주어진 문법 그룹이 패턴 바로 뒤에서 시작한다는 의미입니다.{sync-group-name}
은 이 항목에 붙일 이름입니다. 예를 들어, 셸(sh) 스크립트 언어는 "if"로 if 문을 시작하고, "fi"로 끝냅니다:if [ --f file.txt ] ; then
echo "파일이 존재합니다."
fi
이 문법에 대해 "grouphere" 지시자를 사용하려면, 다음 명령을 쓰면 됩니다:
:syntax sync match shIfSync grouphere shIf "\
<if\>
""groupthere" 인자는 그룹이 끝나는 패턴을 알려줍니다. 예를 들어 if/fi 그룹의 끝은 다음과 같습니다:
:syntax sync match shIfSync groupthere NONE "\
<fi\>
"이 예에서 NONE은 어떤 문법 영역 안에 있지 않다는 의미입니다. 특히 if 블록 안에 있지 않다는 것이지요. "grouphere"나 "groupthere" 인자 없이 매치나 영역을 정의할 수도 있습니다. 이 문법 그룹들은 맞추는 동안 그냥 건너뛰게 됩니다. 예를 들어,
{}
안에 있는 모든 것을 건너뛰려면 다음과 같이 하면 됩니다. 일반적으로 다른 맞추기 방법으로 인식될 수 있더라도 무시됩니다:
:syntax sync match xSpecial /
{.*}
/상세 설명서의 맞추기에 대한 더 자세한 내용: |
:syn-sync
|.
*44.11* 문법 파일 설치하기 새로운 문법 파일을 사용할 준비가 되었으면, 'runtimepath'의 "syntax" 디렉터리에 넣으세요. 유닉스라면 "~/.vim/syntax"입니다. 문법 파일의 이름은 반드시 파일 형식(filetype) 이름 뒤에 ".vim"을 붙인 형태여야 합니다. 즉 x 언어라면, 파일의 전체 경로는 다음과 같습니다:~/.vim/syntax/x.vim
또한 파일 형식이 인식되도록 해야 합니다. |43.2|를 참고하세요. 문법 파일이 잘 동작한다면, 다른 사용자들도 쓸 수 있게 하고 싶겠지요. 먼저 다음 절을 읽고 다른 사람들의 환경에서도 잘 동작할지 확인해보세요. 그리고는 Vim 관리자<maintainer@vim.org>
에게 이메일로 보내세요. 파일 형식을 어떻게 인식할 수 있는지도 설명해주십시오. 운이 좋다면 Vim 다음 버전에 포함될 겁니다! 이미 존재하는 문법 파일에 추가하기 ---------------------------------- 지금까지는 새로운 문법 파일을 만드는 것에 대해서만 얘기했습니다. 이미 존재하는 문법 파일이 동작하긴 하지만 몇 가지 빠진 것이 있을 때, 별도의 파일에 항목들을 추가할 수 있습니다. 이를 통해 배포되는 문법 파일을 고치지 않을 수 있고, 또 새로운 버전의 Vim을 깔면서 사라져버리는 걸 막을 수 있습니다. 자신만의 파일에 문법 명령을 쓰세요. 아마 기존의 문법 파일에서 사용하는 그룹 이름들을 써야겠죠. 예를 들어, C 문법 파일에 새로운 변수 타입을 추가하려면:
:syntax keyword cType off_t uint
기존 문법 파일과 동일한 파일명을 사용하세요. 위와 같은 경우라면 "c.vim"입니다. 이 파일을 'runtimepath'의 끝 부분에 가까운 곳에 넣으세요. 기존의 문법 파일이 다음에 로드되도록 말이지요. 유닉스라면 다음 경로일 것입니다:
~/.vim/after/syntax/c.vim
*44.12* 어디서나 쓸 수 있는 문법 파일 형식 모든 Vim 사용자들이 서로 문법 파일을 교환하면 멋지지 않을까요? 이것이 가능하려면 문법 파일을 쓸 때 몇 가지 지침을 따라야 합니다. 이 문법 파일이 무엇을 위한 것이고, 누가 관리하며, 언제 마지막으로 업데이트 되었는지를 설명하는 헤더로 파일을 시작하세요. 변경 사항에 대한 너무 많은 정보는 넣지 마시고요. 몇 사람 읽지 않을 겁니다. 예:
" Vim syntax file
" Language: C
" Maintainer: Bram Moolenaar
<Bram@vim.org>
" Last Change: 2001 Jun 18
" Remark: Included by the C++ syntax.
다른 문법 파일과 동일한 구조로 만드세요. 기존의 문법 파일을 예제 삼아 작업하면 많은 시간을 아낄 수 있을 겁니다. 문법 파일에 좋고, 이해하기 쉬운 이름을 붙이세요. 소문자와 숫자만 사용합니다. 여러 곳에서 사용되는 것이니 너무 길게 만들지 마세요. 문법 파일의 이름, 'filetype', b:current_syntax, 모든 문법 그룹의 접두어(nameType, nameStatement, nameString 등)로 사용됩니다. "b:current_syntax" 확인으로 시작하세요. 만약 이미 정의되어있다면, 'runtimepath'의 앞부분에서 다른 문법 파일이 이미 로드된 겁니다:
if exists("b:current_syntax")
finish
endif
Vim 5.8과 호환 가능하려면 다음과 같이 쓰세요:
if version < 600
syntax clear
elseif exists("b:current_syntax")
finish
endif
끝에서 "b:current_syntax"에 문법의 이름을 할당하세요. 포함된 파일에서도 이걸 한다는 점 잊지 마십시오. 두 파일을 포함시킨다면 "b:current_syntax"를 지워야할 수도 있습니다. Vim 5.x 에서도 문법 파일이 동작하게 하려면, v:version을 확인하는 코드를 넣으세요. yacc.vim에서 어떻게 하는지 확인해보세요. 사용자의 선호에 따른 사항은 아무 것도 넣지 마세요. 'tabstop'이나 'expandtab' 등을 설정하지 마세요. 이런 것은 파일 형식 플러그인에서 합니다. 매핑이나 약어도 넣지 마세요. 키워드 인식에 꼭 필요한 경우에 한해서 'iskeyword' 설정만 넣으세요. 사용자가 선호하는 색을 고를 수 있도록, 모든 종류의 강조 항목에 다른 그룹명을 만들어주세요. 그리고 각각을 표준 강조 그룹들로 연결하세요. 그러면 모든 색 조합(color scheme)에 대해서 잘 동작할 겁니다. 만약 직접 어떤 색을 지정한다면, 어떤 색 조합에서는 이상하게 보일 겁니다. 어떤 사용자는 다른 배경색을 사용하고, 어떤 사용자는 여덟 가지 색밖에 쓰지 못한다는 점도 기억하시고요. 연결할 때는 "hi def link"를 사용하세요. 사용자가 문법 파일이 로드되기 전에 다른 강조 방법을 설정할 수 있도록 말이지요. 예:
hi def link nameString String
hi def link nameNumber Number
hi def link nameCommand Statement
... 등등 ...
맞추기 할 때 사용되지 않는 항목에는 "display" 인자를 넣어서, 위로 스크롤하거나
CTRL-L
을 누를 때 빠르게 동작하도록 만듭시다.
다음 장: |usr_45| 내 언어 고르기 저작권: |manual-copyright| 참고 vim:tw=78:ts=8:ft=help:norl:
Generated by vim2html on 2013. 12. 14. (토) 20:47:01 KST