IBM®
메인 컨텐츠로 가기
    Korea [국가변경]    이용약관
 
 
   
        제품    서비스 & 솔루션    고객지원 & 다운로드    회원 서비스    
메인 컨텐츠로 가기

한국 developerWorks  >  리눅스  >

Vim 편집기로 스크립트 작성하기, Part 1: 변수, 값 및 표현식

Vimscript의 기본 요소 익히기

developerWorks
문서 옵션
PDF format - Fits A4 and Letter

PDF - Fits A4 and Letter
58KB (18 pages)

Get Adobe® Reader®

JavaScript가 필요한 문서 옵션은 디스플레이되지 않습니다.

영어원문

영어원문


제안 및 의견
피드백

난이도 : 초급

Damian Conway, Dr., CEO and Chief Trainer, Thoughtstream

원문 게재일 : 2009 년 5 월 06 일
번역 게재일 : 2009 년 8 월 18 일

Vimscript는 Vim 편집기를 변경하고 확장하기 위한 메커니즘이다. 스크립트를 사용하면 새 도구를 작성하고 일반 작업을 단순화할 수 있을 뿐만 아니라 편집기의 기존 기능까지도 재설계하여 변경할 수 있습니다. 시리즈의 첫 번째인 이 기사에서는 Vimscript 프로그래밍 언어의 기본 구성 요소인 값, 변수, 표현식, 명령문, 함수 및 명령에 대해 설명합니다. 또한 일련의 간단한 예제를 통해 기능을 직접 구현해 보면서 설명합니다.
Vimscript 및 이 시리즈의 정보
Vimscript는 Vim 편집기를 수정 및 확장할 수 있는 강력한 스크립트 언어이다. 이 언어를 사용하면 새 도구를 작성하고 일반 작업을 단순화할 수 있을 뿐 아니라 편집기의 기존 기능까지도 변경할 수 있다. 이 시리즈 기사에서는 독자가 Vim 편집기에 익숙하다고 간주합니다.

우수한 텍스트 편집기

괜찮은 텍스트 편집기만 있었다면 Emacs는 아주 좋은 운영 체제였을 것이고 이와는 반대로 vi는 괜찮은 운영 체제만 만났다면 정말 좋은 텍스트 편집기였을 것이라는 오래된 농담이 있다. 이 농담은 Emacs가 vi에 비해 항상 우위를 점하고 있었던 전략적 장점인 내장 확장 프로그래밍 언어의 우수성을 반영한다. 실제로 Emacs 사용자가 RSI 유도 제어 코드를 참으면서 Lisp에서 즐겁게 확장을 작성하고 있다는 사실을 보면 내장 확장 언어의 장점이 대단하다는 것을 알 수 있다.

하지만 vi 프로그래머도 이제는 더 이상 Emacs의 내장 스크립트 언어를 부러워하지 않아도 된다. 즐겨 사용하고 있는 vi 편집기도 마찬가지로 스크립트가 지원되며 Emacs보다 훨씬 편리하게 사용할 수 있다.

이 시리즈의 기사에서는 vi 변형 중 오늘날 가장 유명한 프로그램인 Vim 편집기와 Vim에서 제공되는 단순하지만 매우 강력한 스크립트 언어에 대해 살펴본다. 이 첫 번째 기사에서는 Vim 스크립트의 기본 빌딩 블록 즉, 변수, 값, 표현식, 간단한 흐름 제어 등을 살펴본 후 Vim의 수많은 유틸리티 기능 중 일부 기능에 대해 설명한다.

이 기사에서는 독자가 Vim을 이미 가지고 있을 뿐만 아니라 Vim의 대화식 기능에 익숙한 것으로 간주한다. 그렇지 않은 경우에는 Vim의 고유 웹 사이트와 다양한 온라인 리소스를 참조하거나 출판된 책을 구입해서 볼 수 있으며 Vim 내에서 :help를 입력하여 입문자에게 필요한 정보를 얻을 수 있다. 참고자료에서 링크를 볼 수 있다.

별도로 언급하지 않는 한, 이 시리즈의 기사에 포함된 모든 예제에서는 Vim 버전 7.2 이상을 사용하는 것으로 간주한다. 다음과 같은 방법으로 편집기를 호출하여 사용 중인 Vim의 버전을 확인할 수 있다.

vim --version

또는 Vim 자체 내에서 :version을 입력할 수도 있다. 사용 중인 Vim의 버전이 구버전이라면 최신 릴리스로 업그레이드하는 것이 좋다. 이전 버전에서는 앞으로 살펴볼 Vimscript의 많은 기능이 지원되지 않기 때문이다. 참고자료 섹션의 링크를 사용하여 Vim을 다운로드하고 업그레이드할 수 있다.




위로


Vimscript

Vim의 스크립트 언어인 Vimscript는 전형적인 동적 명령 언어이며 대부분의 일반 언어 기능 즉, 변수, 표현식, 제어 구조, 내장 함수, 사용자 정의 함수, 문자열, 고급 데이터 구조(목록 및 사전), 터미널 및 파일 I/O, 정규 표현식 패턴 일치, 예외 및 통합 디버거를 제공한다.

Vim 세션 내에서 다음을 입력하여 내장 도움말 시스템을 통해 Vimscript에 대한 Vim의 자체 문서를 볼 수 있다.

:help vim-script-intro

이제 다음으로 넘어가자.

Vim 스크립트 실행하기

Vim 스크립트 명령은 여러 가지 방법으로 실행할 수 있다. 가장 간단한 방법은 스크립트 명령을 파일(일반적으로 .vim 확장자 사용)에 저장한 후 Vim 세션 내에서 다음과 같이 :source 명령을 사용하여 해당 파일을 실행하는 것이다.

:source /full/path/to/the/scriptfile.vim

또는 Vim 명령행에서 콜론 뒤에 스크립트 명령을 직접 입력할 수도 있다. 예를 들면, 다음과 같다.

:call MyBackupFunc(expand('%'), { 'all':1, 'save':'recent'})

하지만 이렇게 직접 입력하는 사람은 거의 없다. 왜냐하면 입력 작업의 양을 줄이는 것이 스크립트를 이용하는 가장 큰 목적이기 때문이다. 따라서 다음과 같이 새 키보드 맵핑을 작성하여 Vim 스크립트를 호출하는 방법이 가장 일반적으로 사용된다.

:nmap ;s :source /full/path/to/the/scriptfile.vim<CR>

:nmap \b :call MyBackupFunc(expand('%'), { 'all': 1 })<CR>

이러한 명령은 대개 홈 디렉토리의 .vimrc 초기화 파일에 저장된다. 따라서 Normal 모드에서 작업 중이라면(즉, 텍스트를 삽입하는 중이 아니라면) 키 시퀀스 ;s가 지정된 스크립트 파일을 실행하고, \b 시퀀스가 MyBackupFunc() 함수(.vimrc에 정의해 놓은 함수)를 호출한다.

이 기사에서 설명하는 모든 Vimscript 예제에서는 다양한 유형의 키 맵핑을 트리거로 사용한다. 후속 기사에서는 두 가지 다른 일반적인 호출 기술 즉, Vim의 명령행에서 스크립트를 콜론 명령으로 실행하는 기술과 편집기 이벤트를 사용하여 스크립트를 자동으로 트리거하는 기술에 대해서도 설명한다.




위로


구문 예제

Vim에는 매우 정교한 구문 강조 기능이 있다. 이 기능은 내장 :syntax enable 명령을 사용하여 설정하고 :syntax off 명령을 사용하여 해제할 수 있다.

구문 강조 기능을 전환할 때마다 10여 개의 문자를 입력하기가 귀찮은 경우에는 다음과 같은 Vimscript 행을 .vimrc 파일에 저장해 놓을 수도 있다.


Listing 1. 구문 강조 기능 전환

function! ToggleSyntax()
   if exists("g:syntax_on")
      syntax off
   else
      syntax enable
   endif
endfunction

nmap <silent>  ;s  :call ToggleSyntax()<CR>

이렇게 하면 Normal 모드에서 ;s 시퀀스를 입력할 때마다 구문 강조 기능이 설정 또는 해제된다. 이 스크립트의 구성 요소를 하나하나 살펴보자.

첫 번째 코드 블록은 인수가 없는 ToggleSyntax()라는 함수를 정의하는 함수 선언이다. 이 사용자 정의 함수에서는 먼저 내장 Vim 함수인 exists()를 호출하여 문자열을 전달한다. exists() 함수는 문자열에 지정된 이름을 가진 변수(이 경우에는 전역 변수 g:syntax_on)가 정의되어 있는지 여부를 확인한다.

정의되어 있으면 if 명령문에서 syntax off;가 실행되고, 그렇지 않은 경우에는 syntax enable이 실행된다. syntax enableg:syntax_on 변수를 정의하지만 syntax off는 이 변수 정의를 취소하기 때문에 ToggleSyntax() 함수를 반복적으로 호출하면 구문 강조 기능의 설정 및 해제 상태가 교대로 전환된다.

이제는 키 시퀀스(이 예제의 경우 ;s)를 설정하여 ToggleSyntax() 함수를 호출하는 작업만 남았다.

nmap <silent> ;s :call ToggleSyntax()<CR>

nmap은 "normal-mode key mapping"의 약어이다. nmap 뒤의 <silent> 옵션은 새로운 ;s 명령이 정상적으로 작업을 수행할 수 있도록 하기 위해 맵핑 중에 실행되는 명령이 화면에 표시되는 것을 차단한다. 마지막으로 다음 명령이 실행된다.

:call ToggleSyntax()<CR>

이는 리턴 값을 무시하려는 경우에 Vimscript에서 함수를 호출하는 방법을 보여 준다.

끝에 있는 <CR><, C, R> 문자로 구성된 리터럴 시퀀스이다. 이 리터럴 시퀀스는 Vimscript에서 리터럴 캐리지 리턴과 같은 것으로 인식된다. 실제로 Vimscript에서는 이처럼 인쇄할 수 없는 다른 여러 가지 문자 표현이 인식된다. 예를 들어, 다음과 같은 키보드 맵핑을 작성하면 스페이스바가 Page Down 키처럼 작동한다. (이 기능은 대부분의 웹 브라우저에서 볼 수 있다.)

:nmap <Space> <PageDown>

Vim에서 :help keycodes를 입력하여 이러한 특수 기호의 전체 목록을 볼 수 있다.

ToggleSyntax()에서도 내장 syntax 명령을 직접 호출할 수 있다. 왜냐하면 Vim의 모든 내장 콜론 명령은 자동으로 Vimscript의 명령문도 되기 때문이다. 예를 들어, Vim에서 작성한 문서에 대해 가운데 맞춤이 적용된 제목을 붙이는 작업을 쉽게 수행하려는 경우 현재 행의 각 단어를 대문자로 변경하고 전체 행을 가운데로 맞춘 후 다음 행으로 이동하는 함수를 다음과 같이 작성하여 사용할 수 있다.


Listing 2. 가운데 맞춤이 적용된 제목 작성하기

function! CapitalizeCenterAndMoveDown()
   s/\<./\u&/g   "Built-in substitution capitalizes each word
   center        "Built-in center command centers entire line
   +1            "Built-in relative motion (+1 line down)
endfunction

nmap <silent>  \C  :call CapitalizeCenterAndMoveDown()<CR>

Vimscript 명령문

이전 예제에서 보았듯이 Vimscript의 모든 명령문은 쉘 스크립트 또는 Python과 같이 줄 바꿈으로 끝난다. 여러 행으로 구성된 하나의 명령문을 실행해야 하는 경우에는 단일 백슬래시를 연결 기호로 사용한다. 두 행의 연결을 의미하는 백슬래시는 행의 끝에 있지 않고 아래 행의 앞쪽에 있다.


Listing 3. 백슬래시로 행 연결하기

call SetName(
\             first_name,
\             middle_initial,
\             family_name
\           )

수직 막대로 명령문을 구별하여 한 행에 둘 이상의 명령문을 입력할 수도 있다.

echo "Starting..." | call Phase(1) | call Phase(2) | echo "Done"

즉, Vimscript의 수직 막대는 대부분의 다른 프로그래밍 언어의 세미콜론과 동등한 기능을 수행한다. 아쉽게도 Vim에서는 세미콜론이 명령의 시작 부분에서 이미 다른 의미(구체적으로 명령의 행 범위의 일부로서 "현재 행에서 ...로"의 의미)로 사용되고 있기 때문에 명령문을 구별하는 데는 사용할 수 없다.

주석

명령문 구별자인 수직 막대는 주석에서도 중요하게 사용된다. Vimscript 주석은 큰따옴표로 시작하고 끝난다. 예를 들면, 다음과 같다.


Listing 4. Vimscript에서 주석 지정하기

if exists("g:syntax_on")
   syntax off      "Not 'syntax clear' (which does something else)
else
   syntax enable   "Not 'syntax on' (which overrides colorscheme)
endif

하지만 아쉽게도 Vimscript 문자열도 큰따옴표로 시작되며 주석보다 우선 순위가 높다. 따라서 다음과 같이 문자열이 예상되는 위치에서는 문자열로 해석되기 때문에 주석을 지정할 수 없다.

echo "> " "Print generic prompt

echo 명령은 하나 이상의 문자열을 기대하므로 이 행은 두 번째 문자열에 닫는 따옴표가 없음을 알리는 오류가 발생한다.

하지만 주석은 언제나 명령문의 시작 부분에 있을 수 있으므로 다음과 같이 수직 막대를 사용하여 새 명령문을 명시적으로 시작한 후 주석을 시작하게 되면 위의 문제점을 해결할 수 있다.

echo "> " |"Print generic prompt

값과 변수

Vimscript에서 변수를 할당하려면 특수 키워드 let을 사용해야 한다.


Listing 5. let 키워드 사용하기

let name = "Damian"

let height = 165

let interests = [ 'Cinema', 'Literature', 'World Domination', 101 ]

let phone     = { 'cell':5551017346, 'home':5558038728, 'work':'?' }

큰따옴표 또는 작은따옴표를 구분 기호로 사용하여 문자열을 지정할 수 있다. 큰따옴표로 묶인 문자열은 "\n"(줄 바꿈), "\t"(탭), "\u263A"(유니코드 웃는 얼굴) 또는 "\<ESC>"(이스케이프 문자)와 같은 특별한 "이스케이프 시퀀스"를 나타낸다. 이에 반해 작은따옴표는 구분 기호 내의 모든 내용을 리터럴 문자로 처리한다. 단, 작은따옴표가 두 번 연달아 있는 경우에는 하나의 리터럴 작은따옴표로 처리된다.

Vimscript의 값은 대개 다음 세 유형 중 하나이다.

  • 스칼라: 문자열이나 숫자와 같은 단일 값이다. 예: "Damian" 또는 165
  • 목록: 대괄호로 구분되고 0부터 시작하는 묵시적 정수 인덱스를 사용하는 정렬된 값 시퀀스이다. 예: ['Cinema', 'Literature', 'World Domination', 101]
  • 사전: 중괄호로 구분되고 명시적 문자열 키를 사용하는 정렬되지 않은 값 세트이다. 예: {'cell':5551017346, 'home':5558038728, 'work':'?'}

목록이나 사전에 있는 값이 모두 같은 유형일 필요는 없으므로 문자열, 숫자, 중첩 목록 및 사전 등의 유형을 원하는 대로 혼합해서 사용할 수 있다.

값과는 달리 변수에는 기본 유형이 없다. 대신 처음에 할당된 값의 유형을 따른다. 따라서 앞 예제에서 nameheight 변수는 이제 스칼라 유형(즉, 문자열이나 숫자만 저장할 수 있음)이고, interests는 목록 변수(즉, 목록만 저장할 수 있음)이고, phone은 사전 변수(사전만 저장할 수 있음)이다. 한번 할당된 변수 유형은 런타임 동안 영구적으로 엄격하게 유지된다.

let interests = 'unknown' " Error: variable type mismatch

기본적으로 변수의 범위는 변수가 처음 할당된 함수로 한정된다. 그렇지 않고 첫 번째 할당이 함수 외부에서 발생한 변수는 전역 변수이다. 하지만 표 1에서 설명하는 다양한 접두어를 사용하여 변수를 명시적으로 다른 범위에 속한 변수로 선언할 수도 있다.


표 1. Vimscript 변수 범위
접두어 의미
g: varname 변수가 전역 변수이다.
s: varname 변수가 현재 스크립트 파일에 한정되는 로컬 변수이다.
w: varname 변수가 현재 편집기 창에 한정되는 로컬 변수이다.
t: varname 변수가 현재 편집기 탭에 한정되는 로컬 변수이다.
b: varname 변수가 현재 편집기 버퍼에 한정되는 로컬 변수이다.
l: varname 변수가 현재 함수에 한정되는 로컬 변수이다.
a: varname 변수가 현재 함수의 매개변수이다.
v: varname 변수가 Vim에 미리 정의되어 있는 변수이다.

스크립트에서 다른 유형의 값 컨테이너에 액세스하는 데 사용할 수 있는 의사 변수도 Vim에서 제공된다. 표 2에서는 이러한 변수에 대한 요약 정보를 제공한다.


표 2. Vimscript 의사 변수
접두어 의미
& varname Vim 옵션(정의된 경우 로컬, 그렇지 않은 경우 전역)
&l: varname 로컬 Vim 옵션
&g: varname 전역 Vim 옵션
@ varname Vim 레지스터
$ varname 환경 변수

"option" 의사 변수는 매우 유용하게 사용할 수 있다. 예를 들어, 다음과 같이 두 개의 키 맵을 설정하여 현재 탭 간격을 늘이거나 줄일 수 있다.

nmap <silent> ]] :let &tabstop += 1<CR>

nmap <silent> [[ :let &tabstop -= &tabstop > 1 ? 1 : 0<CR>

표현식

이전 예제의 [[ 키 맵핑에서는 다음과 같은 C 형태의 "삼원식"이 포함된 표현식을 사용하고 있다.

&tabstop > 1 ? 1 : 0

이 표현식은 키 맵이 현재 탭 간격을 정상적인 최소값인 1 이하로 축소하게 되는 것을 막아 준다. 이 예제에서 보여 주는 것처럼 Vimscript의 표현식은 대부분의 다른 현대 스크립트 언어에서 사용하는 것과 동일한 기본 연산자로 구성되어 있으며 일반적으로 동일한 구문을 사용한다. 표 3에서는 사용할 수 있는 연산자를 우선 순위별로 구분하여 보여 준다.


표 3. Vimscript 연산자 우선 순위 표
연산 연산자 구문
할당
숫자 더하기 및 할당
숫자 빼기 및 할당
문자열 연결 및 할당
let var = expr
let var += expr
let var -= expr
let var .= expr
삼원식 연산자 bool ? expr-if-true : expr-if-false
논리적 OR bool || bool
논리적 AND bool && bool
숫자 또는 문자열 같음
숫자 또는 문자열 같지 않음
숫자 또는 문자열 보다 큼
숫자 또는 문자열 크거나 같음
숫자 또는 문자열 보다 작음
숫자 또는 문자열 작거나 같음
expr == expr
expr != expr
expr > expr
expr >= expr
expr < expr
expr <= expr
숫자 더하기
숫자 빼기
문자열 연결
num + num
num - num
str . str
숫자 곱하기
숫자 나누기
숫자 나머지
num * num
num / num
num % num
숫자로 변환
숫자 부정
논리적 NOT
+ num
- num
! bool
괄호 우선 순위 ( expr )

논리적 연산자 주의 사항

C에서와 마찬가지로 Vimscript에서도 숫자 값 0만 부울 컨텍스트에서 false로 간주되며 0이 아닌 숫자 값(음수 포함)은 모두 true로 간주된다. 하지만 모든 논리적 및 비교 연산자는 true의 경우 값 1을 리턴한다.

문자열이 부울 값으로 사용될 경우에는 먼저 문자열이 정수로 변환된 다음 참(0이 아닌 값) 또는 거짓(0)으로 평가된다. 그리고 비어 있지 않은 문자열을 포함한 대부분의 문자열이 false로 평가된다. 아래 Listing에서는 빈 문자열을 테스트할 때 발생하는 전형적인 실수를 보여 준다.


Listing 6. 잘못된 빈 문자열 테스트

let result_string = GetResult();

if !result_string
   echo "No result"
endif

이 테스트는 result_string에 빈 문자열이 할당된 경우 올바르게 작동하기는 하지만 result_string"I am NOT an empty string"과 같은 문자열이 있는 경우에도 "No result"가 표시되는 문제점을 가지고 있다. 이 문제점은 문자열이 숫자(0)으로 변환된 후 부울 값(false)으로 변환되기 때문에 발생한다.

올바른 해결 방법은 다음과 같이 적합한 내장 함수를 사용하여 문자열이 비어 있는지 여부를 명시적으로 테스트하는 것이다.


Listing 7. 빈 문자열 테스트 방법

if empty(result_string)
   echo "No result"
endif

비교 연산자 주의 사항

Vimscript에서 비교 연산자는 피연산자가 모두 문자열이 아닌 경우에는 항상 숫자 비교를 수행한다. 특히, 피연산자 중 하나만 문자열이고 다른 하나는 숫자인 경우에는 문자열을 숫자로 변환한 후 두 피연산자의 숫자 값을 비교한다. 이로 인해 다음과 같은 민감한 오류가 발생할 수 있다.

let ident = 'Vim'

if ident == 0 "Always true (string 'Vim' converted to number 0)

이러한 경우에는 다음과 같이 강력한 해결 방법을 사용할 수 있다.

if ident == '0'   "Uses string equality if ident contains string
                  "but numeric equality if ident contains number

문자열 비교는 대개 Vim ignorecase 옵션의 로컬 설정을 따르지만 명시적으로 문자열 비교 연산자에 대/소문자 구분(# 추가) 또는 대/소문자 구분 안함(? 추가)을 표시할 수 있다.


Listing 8. 문자열 비교 연산자의 대/소문자 구분 여부 지정하기

if name ==? 'Batman'         |"Equality always case insensitive
   echo "I'm Batman"
elseif name <# 'ee cummings' |"Less-than always case sensitive
   echo "the sky was can dy lu minous"
endif

"명시적으로 대/소문자 구분 여부가 지정된" 연산자를 사용하면 사용자의 변형된 옵션 설정에 영향을 받지 않고 스크립트를 안정적으로 실행할 수 있으므로 모든 문자열 비교에 이러한 연산자를 사용하는 것이 좋다.

산술 연산자 주의 사항

산술 표현식을 사용할 경우에는 Vim 버전 7.2까지 정수 산술 연산만 지원되었다는 점에 유의해야 한다. 이전 버전에서는 다음과 같이 코드를 잘못 작성하는 경우가 자주 발생했었다.


Listing 9. 정수 산술 연산 문제점

"Step through each file...
for filenum in range(filecount)
   " Show progress...
   echo (filenum / filecount * 100) . '% done'

   " Make progress...
   call process_file(filenum)
endfor

filenum은 항상 filecount보다 작기 때문에 정수 나누기 filenum/filecount의 결과는 항상 0이 된다. 따라서 루프의 반복마다 다음과 같은 결과가 표시된다.

Now 0% done

Vim 버전 7.2에서도 피연산자 중 하나가 명시적으로 부동소수점 숫자인 경우에는 부동소수점 산술 연산만 수행된다.

let filecount = 234

echo filecount/100   |" echoes 2
echo filecount/100.0 |" echoes 2.34




위로


또 다른 변환 예제

앞에서 살펴본 구문 변환 스크립트를 사용하면 다른 유용한 도구도 쉽게 작성할 수 있다. 예를 들어, 오타나 실수가 자주 발생하는 단어 집합이 있을 경우 텍스트를 검토할 때 Vim의 일치 메커니즘을 활성화하여 문제가 있는 단어를 강조 표시하는 스크립트를 .vimrc에 추가할 수 있다.

예를 들어, 다음과 같은 단락을 Vim에 표시하는 키 맵핑(;p로 지정)을 작성할 수 있다.

It's easy to adapt the syntax-toggling script shown earlier to create other useful tools. For example, if there is a set of words that you frequently misspell or misapply, you could add a script to your .vimrc to activate Vim's match mechanism and highlight problematic words when you're proofreading text.

해당 스크립트의 내용은 다음과 같다.


Listing 10. 실수가 잦은 단어 강조 표시하기

"Create a text highlighting style that always stands out...
highlight STANDOUT term=bold cterm=bold gui=bold

"List of troublesome words...
let s:words = [
             \ "it's",  "its",
             \ "your",  "you're",
             \ "were",  "we're",   "where",
             \ "their", "they're", "there",
             \ "to",    "too",     "two"
             \ ]

"Build a Vim command to match troublesome words...
let s:words_matcher
\ = 'match STANDOUT /\c\<\(' . join(s:words, '\|') . '\)\>/'

"Toggle word checking on or off...
function! WordCheck ()
   "Toggle the flag (or set it if it doesn't yet exist)...
   let w:check_words = exists('w:check_words') ? !w:check_words : 1

   "Turn match mechanism on/off, according to new state of flag...
   if w:check_words
      exec s:words_matcher
   else
      match none
   endif
endfunction

"Use ;p to toggle checking...
nmap <silent> ;p :call WordCheck()<CR>

이 스크립트에서는 w:check_words 변수를 부울 플래그로 사용하여 단어 검사를 설정 또는 해제한다. WordCheck() 함수의 첫 번째 행에서는 플래그가 있는지 여부를 확인하여 플래그가 있으면 할당 명령에 의해 변수의 부울 값이 전환된다.

let w:check_words = exists('w:check_words') ? !w:check_words : 1

w:check_words라는 변수가 아직 없으면 해당 변수가 생성되면서 값 1이 할당된다.

let w:check_words = exists('w:check_words') ? !w:check_words : 1

w: 접두어는 해당 플래그 변수가 항상 현재 창에 대한 로컬 변수임을 의미한다. 따라서 이 접두어를 사용하면 편집기 창별로 개별적으로 단어 검사를 전환할 수 있다. (이는 match 명령의 동작에도 동일하게 적용되므로 명령의 효과가 항상 현재 창의 로컬에만 반영된다.)

단어 검사는 Vim의 match 명령을 설정하여 사용할 수 있다. match를 실행하려면 먼저 텍스트 강조 표시 스펙(이 예제의 경우, STANDOUT)을 제공한 후 강조 표시할 텍스트를 지정하는 정규 표현식을 제공해야 한다. 이 경우에는 스크립트의 s:words 목록 변수에 지정된 모든 단어에 대해 OR 연산(즉, join(s:words, '\|'))을 수행하여 정규 표현식이 생성된다. 그런 다음 대/소문자를 구분하지 않고 전체 단어가 일치하는 경우만 검색되도록 하기 위해 해당 대체 집합이 대/소문자를 구분하지 않는 단어 경계로 묶인다(\c\<\(...\)\>).

그런 다음 WordCheck() 함수는 결과 문자열을 Vim 명령으로 변환한 후 실행하여(exec s:words_matcher) 일치 기능을 설정한다. w:check_words가 해제된 경우에는 이 함수에서 match none 명령이 대신 수행되면서 특수 일치가 비활성화된다.




위로


Insert 모드에서 스크립트 작업하기

Vim 스크립트 작업은 Normal 모드로 제한되어 있지 않다. imap 또는 iabbrev 명령을 사용하여 텍스트를 삽입할 때 사용할 수 있는 키 맵핑이나 약어를 설정할 수 있다. 예를 들면, 다음과 같다.

imap <silent> <C-D><C-D> <C-R>=strftime("%e %b %Y")<CR>

imap <silent> <C-T><C-T> <C-R>=strftime("%l:%M %p")<CR>

.vimrc에 이러한 맵핑이 있을 경우에는 Insert 모드에서 Ctrl-D를 두 번 누르면 Vim의 내장 strftime() 함수가 호출되면서 결과 날짜가 삽입되며 이와 비슷하게 Ctrl-T를 두 번 누르면 현재 시간이 삽입된다.

이와 같은 방법으로 삽입 맵 또는 약어 기능을 사용하여 스크립트로 작성할 수 있는 모든 작업을 수행할 수 있다. 초기 <C-R>=(후속 계산 결과를 삽입하도록 지정)과 마지막 <CR>(이전 표현식을 실제로 계산하도록 지정) 사이에 적절한 Vimscript 표현식 또는 함수 호출을 넣기만 하면 된다. 여기에서는 <C-R>(Ctrl-R을 의미하는 Vim의 약어)과 <CR>(캐리지 리턴을 의미하는 Vim의 약어)이 다르다는 점에 유의해야 한다.

예를 들어, 다음과 같이 Vim의 내장 함수 getcwd()를 사용하여 현재 작업 디렉토리에 대한 약어를 작성할 수 있다.

iabbrev <silent> CWD <C-R>=getcwd()<CR>

또는 다음과 같이 텍스트 삽입 중에 Ctrl-C를 입력하여 호출할 수 있는 간단한 계산기를 포함시킬 수도 있다.

imap <silent> <C-C> <C-R>=string(eval(input("Calculate: ")))<CR>

해당 표현식은 다음과 같다.

string( eval( input("Calculate: ") ) )

먼저 내장 input() 함수를 호출하여 사용자에게 계산을 입력하도록 요청한다. 그러면 input()이 사용자의 입력을 문자열로 리턴한다. 그런 다음 입력 문자열이 내장 eval() 함수로 전달된다. 이 함수는 문자열을 Vimscript 표현식으로 계산하여 결과를 리턴한다. 그런 다음 내장 string() 함수가 숫자 결과를 문자열로 다시 변환한다. 마지막으로 키 맵핑의 <C-R>= 시퀀스가 최종 문자열을 삽입하게 된다.

복잡한 Insert 모드 스크립트

삽입 맵핑에는 이전 예제보다 훨씬 더 복잡한 스크립트가 사용될 수 있다. 그러한 경우에는 일반적으로 키 맵핑에서 호출할 수 있는 사용자 정의 함수를 사용하여 코드를 수정하는 것이 좋다.

예를 들어, 삽입 중에 수행되는 Ctrl-Y의 동작을 변경할 수 있다. 일반적으로 Insert 모드에서 Ctrl-Y는 "수직 복사"를 수행한다. 즉, 커서 바로 위에 있는 줄에서 같은 열에 있는 문자를 복사한다. 예를 들어, 다음과 같은 상황에서 Ctrl-Y는 커서 위치에 "m"을 삽입한다.

Glib jocks quiz nymph to vex dwarf
Glib jocks quiz ny_

하지만 수직 복사를 수행할 때 삽입 지점 위로 올라가면서 중간에 끼어 있는 빈 행을 무시하고 비어 있지 않은 첫 번째 행의 같은 열에 있는 문자를 복사할 수도 있다. 예를 들어, 다음과 같은 상황에서 Ctrl-Y는 바로 앞 행이 비어 있음에도 불구하고 "m"을 삽입한다.

Glib jocks quiz nymph to vex dwarf

Glib jocks quiz ny_

.vimrc 파일에 다음 코드를 삽입하여 이 동작을 수행할 수 있다.


Listing 11. 빈 행을 무시하도록 수정된 수직 복사

"Locate and return character "above" current cursor position...
function! LookUpwards()
   "Locate current column and preceding line from which to copy...
   let column_num      = virtcol('.')
   let target_pattern  = '\%' . column_num . 'v.'
   let target_line_num = search(target_pattern . '*\S', 'bnW')

   "If target line found, return vertically copied character...
if !target_line_num return "" else return matchstr(getline(target_line_num), target_pattern) endif endfunction "Reimplement CTRL-Y within insert mode...
imap <silent> <C-Y> <C-R><C-R>=LookUpwards()<CR>

먼저 LookUpwards() 함수가 내장 virtcol() 함수를 사용하여 현재 삽입 지점이 있는 온스크린 열(또는 "가상 열")을 결정한다. '.' 인수는 현재 커서 위치의 열 번호를 지정한다.

let column_num = virtcol('.')

그런 다음 LookUpwards()는 내장 search() 함수를 사용하여 커서 위치에서부터 역방향으로 파일을 검색한다.

let target_pattern = '\%' . column_num . 'v.'
let target_line_num = search(target_pattern . '*\S', 'bnW')

search() 함수는 특수 대상 패턴(즉, \%column_numv.*\S)을 사용하여 커서 열(\%column_numv) 이후(.*)로 공백이 아닌 문자를 가지고 있는 가장 가까운 행(\S)을 찾는다. search() 함수의 두 번째 인수는 구성 문자열 bnW이다. 이 구성 문자열은 함수에게 역방향으로(backwards) 검색하지만 커서를 이동하지 말고(not) 검색도 랩핑(wrap)하지 않도록 지시한다. 검색이 성공하면 search() 함수는 해당하는 선행 행의 행 번호를 리턴하며 그렇지 않고 실패한 경우에는 0을 리턴한다.

그런 다음 if 명령문에서 삽입 위치에 복사할 문자(있는 경우)를 결정한다. 적합한 선행 행이 없는 경우에는 target_line_num에 0이 할당되므로 첫 번째 return 명령문이 실행되면서 "삽입하지 않음"을 나타내는 빈 문자열이 리턴된다.

하지만 적합한 선행 행이 있는 경우에는 두 번째 return 명령문이 실행된다. 이 명령문은 먼저 현재 편집기 버퍼에서 해당 선행 행의 사본을 가져온다.

return matchstr(getline(target_line_num), target_pattern)

그런 다음 이전 search() 호출과 일치하는 한 문자로 된 문자열을 찾아서 리턴한다.

return matchstr(getline(target_line_num), target_pattern)

이 새로운 수직 복사 동작을 LookUpwards() 내에 구현한 후에는 다음과 같이 imap을 사용하여 Insert 모드의 표준 Ctrl-Y 명령을 재정의하기만 하면 된다.

imap <silent> <C-Y> <C-R><C-R>=LookUpwards()<CR>

앞의 모든 예제에서는 <C-R>=을 사용하여 Vimscript 함수를 호출했지만 이 예제에서는 <C-R><C-R>=을 대신 사용한다. Ctrl-R을 한 번만 사용할 경우에는 후속 표현식의 결과가 직접 입력된 것처럼 삽입된다. 즉, 결과에 들어 있는 모든 특수 문자의 의미와 동작이 그대로 유지된다. 이에 반해 Ctrl-R을 두 번 사용하면 후속 처리가 필요 없는 약어 텍스트로 결과가 삽입된다.

이 예제의 경우에는 커서 위의 텍스트를 정확히 복사하는 것이 목적이므로 약어 삽입이 적합한 방법이다. 키 맵핑에서 <C-R>=을 사용했다면 이전 행의 리터럴 이스케이프 문자를 복사하는 동작이 해당 문자를 입력하는 동작과 같은 기능을 수행하게 되므로 그 즉시 편집기가 Insert 모드에서 빠져나가게 된다.

Vim의 내장 함수에 대한 정보 보기

지금까지의 예제를 보면서 알 수 있듯이 Vimscript의 우수성의 원천은 200개 이상의 내장 함수에 있다고 해도 과언이 아니다. 다음 명령을 입력하여 이들 함수에 대한 자세한 정보를 확인할 수 있다.

:help functions

또는 다음 명령을 실행하여 범주별로 분류된 유용한 목록에 액세스할 수도 있다.

:help function-list




위로


후속 기사 소개

Vimscript는 Vim 편집기를 변경하고 확장하기 위한 메커니즘이다. 스크립트를 사용하면 새로운 도구(예: 오류 단어 강조 표시기)를 작성하고 일반 작업(탭 간격 변경, 시간 및 날짜 정보 삽입, 구문 강조 표시 기능 전환 등)을 단순하게 만들 수 있으며 기존 편집기 기능을 완전히 재설계(예: Ctrl-Y의 "선행 행 복사" 동작 개선)할 수도 있다.

대부분의 경우 새 언어를 가장 쉽게 배우는 방법은 예제를 활용하는 것이다. Vim Tips wiki에는 샘플 Vimscript가 지속적으로 제공되고 있으며 이들 스크립트의 대부분이 유용한 도구이기도 하므로 적극 활용하면 좋은 효과를 얻을 수 있을 것이다. 수준 높은 Vim 스크립트 예제를 원하는 경우에는 Vim 스크립트 아카이브에 들어 있는 2000개 이상의 큰 프로젝트를 살펴볼 수 있다. 두 항목 모두 아래 참고자료 섹션에 해당 링크가 소개되어 있다.

Perl, Python, Ruby, PHP, Lua, Awk, Tcl 등의 쉘 언어에 익숙하다면 한편으로는 Vimscript가 매우 익숙하면서도(일반적인 방법론 및 개념의 경우) 다른 한편으로는 아주 큰 이질감을 느낄 수도 있다(특정 구문 표현법의 경우). 이러한 부조화를 극복하고 Vimscript를 자신의 것으로 만들려면 언어를 학습하고 실습하는 데 시간과 노력을 투자해야 한다. 어느 정도 익숙해진 후에는 현재 Vim의 작동 방식 중에서 개인적으로 마음에 들지 않는 부분을 본인에게 맞도록 스크립트를 작성하여 솔루션을 개선시킬 수도 있을 것이다.

이 기사에서는 Vimscript의 기본적인 변수, 값, 표현식 및 함수에 대해서만 설명했다. 이러한 몇 가지 구성 요소만으로 생성할 수 있는 "향상된 솔루션"은 아무래도 제한적일 수밖에 없다. 따라서 이후 기사에서는 데이터 구조, 흐름 제어, 사용자 정의 명령, 이벤트 구동 스크립트, Vim 모듈 빌드 및 다른 스크립트 언어를 이용한 Vim 확장과 같은 고급 Vimscript 도구 및 기술에 대해 살펴볼 것이다. 특히, 이 시리즈의 다음 기사에서는 Vimscript의 사용자 정의 함수의 기능과 이러한 함수를 사용하여 Vim을 개선할 수 있는 여러 가지 방법에 중점을 두고 설명한다.



참고자료

교육

제품 및 기술 얻기

토론
  • 사용자의 개인 프로파일과 사용자 정의 홈 페이지가 제공되는 My developerWorks community에서는 관심을 가지고 있는 developerWorks의 여러 주제를 추적할 수 있으며 다른 developerWorks 사용자들과 의견을 나눌 수도 있다.


필자소개

author photo - damian conway

Damian Conway는 오스트레일리아의 Monash University에서 교류 교수로서 컴퓨터 과학을 강의하고 있으며 국제 IT 교육 회사인 Thoughtstream의 CEO이다. 25년 이상 동안 날마다 vi를 사용하고 있으며 아마도 그는 평생 동안 이 중독에서 벗어나지 못할 것이다.




기사에 대한 평가


보다 나은 서비스를 제공하기 위함이오니 잠시 짬을 내어 이 양식을 제출하여 주십시오.



 


 


 


이 문서 북마킹 하기

mar.gar.in mar.gar.in naver naver eolin eolin del.icio.us del.icio.us





위로


developerWorks 콘텐트를 다른 사이트에 전재하기:
developerWorks 콘텐트에 대한 저작권은 IBM에 있습니다. IBM의 서면 허가나 원본 저자의 허락이 없이는 전재를 금합니다. 저희 콘텐트를 전재하시려면 IBM developerWorks 담당자 에게 문의하십시오.
    IBM 소개 개인정보 보호정책 문의