Vimscript 배우기 cho.books
제 10 장 · 원서 §41–56

완전한 플러그인 만들기 — 구문 강조부터 배포까지

이제 모든 것을 모읍니다. Potion 언어용 Vim 플러그인을 처음부터 끝까지 — 파일타입 감지, 구문 강조, 코드 폴딩, 섹션 이동, 자동로드, 문서화, 그리고 세상에 배포하기.

Pathogen syntax foldexpr autoload 배포

Section 01플러그인 구조와 Pathogen

이 장의 플러그인은 Potion 프로그래밍 언어 지원을 추가합니다. Potion은 작은 장난감 언어라 우리 목적에 이상적입니다 — 목표는 Potion을 배우는 것이 아니라, 완전한 Vim 플러그인의 여러 측면을 작은 예제로 짚어보는 것입니다.

바닐라 Vim은 플러그인을 여러 디렉터리로 나눕니다 — colors/, plugin/, ftdetect/, ftplugin/, syntax/, indent/, autoload/, doc/ 등. 각 디렉터리는 정해진 의미를 갖습니다.

개념 Pathogen — 플러그인 관리의 구원

예전엔 플러그인 파일을 디렉터리마다 하나씩 흩어 놓아야 했습니다. Tim Pope의 Pathogen은 이를 단순하게 만듭니다.

~/.vim/bundle/ 안의 각 디렉터리가 runtimepath에 자동 추가됩니다. 즉, 각 플러그인이 자기만의 디렉터리에 통째로 들어갈 수 있습니다 — 업데이트는 옛 디렉터리를 지우고 새 버전으로 바꾸기만 하면 됩니다.

plugin layout — potion 플러그인의 구조
potion/
  README        LICENSE
  doc/        potion.txt
  ftdetect/   potion.vim
  ftplugin/   potion.vim
  syntax/     potion.vim
  autoload/   potion/running.vim

이 구조 그대로 GitHub나 Bitbucket에 올리면, 사용자는 bundle/로 클론하기만 해도 모든 것이 작동합니다.

Section 02파일타입 감지

먼저 Vim이 Potion 파일(.pn)을 알아보게 해야 합니다. ftdetect/potion.vim에 자동명령 하나를 둡니다:

ftdetect/potion.vim — 파일타입 감지
au BufNewFile,BufRead *.pn set filetype=potion

이 자동명령은 .pn 파일의 파일타입을 potion으로 설정합니다. ftdetect/ 안의 파일은 Vim이 자동명령 그룹으로 자동 감싸주므로, 여기서는 augroup을 직접 쓸 필요가 없습니다. 이제 :set filetype?로 확인하면 filetype=potion이 나옵니다.

Section 03구문 강조

syntax/potion.vim에 구문 강조 코드를 둡니다. 파일은 표준 보일러플레이트로 감쌉니다:

syntax/potion.vim — 키워드와 함수 강조
if exists("b:current_syntax")
  finish
endif

syntax keyword potionKeyword  loop times to while if elsif else
syntax keyword potionFunction print join string

highlight link potionKeyword  Keyword
highlight link potionFunction Function

let b:current_syntax = "potion"
개념 구문 강조의 두 단계

syntax keyword(또는 syntax match, syntax region)로 구문의 "덩어리"를 정의한다.

highlight link로 그 덩어리를 공통 강조 그룹(Keyword, Function, Comment 등)에 연결한다.

이렇게 하면 플러그인 저자는 구문을, 색 구성표 저자는 색을 각자 정의할 수 있습니다.

리터럴 키워드가 아닌 것 — 주석, 연산자, 문자열 — 은 정규표현식이 필요합니다. syntax match는 정규표현식 패턴을, syntax region은 시작·끝 패턴을 받습니다:

syntax/potion.vim — match와 region
syntax match  potionComment "\v#.*$"
syntax match  potionOperator "\v\*\="
syntax region potionString  start=/\v"/ skip=/\v\\./ end=/\v"/

highlight link potionComment  Comment
highlight link potionString   String
함정 · 우선순위 나중에 정의된 구문 그룹이 먼저 정의된 것보다 우선합니다. syntax match는 한 줄에 그룹 하나만 받습니다. 작은 그룹을 먼저, 큰 그룹을 나중에 정의하세요 — 그래야 --= 같은 겹치는 패턴이 의도대로 매칭됩니다. regionskip 인자는 \" 같은 문자열 이스케이프를 처리하게 해줍니다.
모션 · 구문 강조의 두 단계 01 / 4
스페이스: 재생 · ← →: 단계 이동

Section 04코드 폴딩과 섹션 이동

Vim은 코드 폴딩 방식을 여섯 가지 지원합니다 — manual, marker, diff, expr, indent. 들여쓰기 기반 폴딩은 거의 공짜입니다. ftplugin/potion/folding.vim에 한 줄이면 됩니다:

ftplugin/potion/folding.vim — indent 폴딩
setlocal foldmethod=indent
setlocal foldignore=

더 정교한 폴딩을 원하면 expr 폴딩으로 커스텀 Vimscript 함수를 씁니다. 각 줄에 "폴드 레벨"이 있고, 함수가 그것을 결정합니다:

ftplugin/potion/folding.vim — expr 폴딩
setlocal foldmethod=expr
setlocal foldexpr=GetPotionFold(v:lnum)

function! GetPotionFold(lnum)
  if getline(a:lnum) =~? '\v^\s*$'
    return '-1'            " 빈 줄 — 폴드 레벨 미정
  endif
  " ... 들여쓰기 레벨을 비교해 폴드 레벨을 정한다 ...
endfunction
직관 · 폴드 레벨 foldexpr 함수는 폴드 레벨을 직접 반환하거나, 특수 문자열을 반환할 수 있습니다 — '-1'(미정, 이웃을 따름), '>1'(레벨 1 폴드를 연다). 같은 레벨의 인접한 줄들이 함께 접힙니다.

섹션 이동

Vim의 섹션 이동 명령([[, ]], [], ][)을 Potion 파일에 맞게 재매핑할 수 있습니다. ftplugin/potion/sections.vim에서 noremap(연산자 대기 모드에서도 작동하도록)으로 매핑하고, 공통 함수를 호출하게 합니다:

ftplugin/potion/sections.vim — 섹션 이동
noremap <script> <buffer> <silent> ]] :call <SID>NextSection(1, 0)<cr>
noremap <script> <buffer> <silent> [[ :call <SID>NextSection(1, 1)<cr>

Section 05자동로드 · 문서화 · 배포

자동로드

autoload는 플러그인 코드의 로딩을 실제로 필요할 때까지 미루는 기법입니다. 사용자가 매번 쓰지 않는 기능은 autoload/ 디렉터리로 옮겨, Vim 시작 시간에 주는 부담을 줄이세요:

autoload/potion/running.vim — 자동로드
function! potion#running#PotionCompileAndRunFile()
  silent !clear
  execute "!" . g:potion_command . " " . bufname("%")
endfunction

함수 이름의 potion#running# 부분이 디렉터리·파일명과 일치합니다. Vim은 potion#running#X 호출을 만나면 autoload/potion/running.vim을 찾아 그때 처음 로드합니다. 매핑·명령은 사용자가 호출해야 하므로 자동로드할 수 없습니다 — 그래서 nnoremapftplugin/에 남깁니다.

문서화

Vim의 도움말 파일은 그저 구문 강조된 텍스트 파일입니다 — doc/potion.txt에 직접 쓸 수 있습니다. 별표로 단어를 감싸면 점프 가능한 태그가 되고, 막대(|)로 감싸면 태그로의 링크가 됩니다:

doc/potion.txt — 도움말 파일
*potion.txt*  functionality for the potion programming language

CONTENTS                                       *PotionContents*
  1. Usage ................ |PotionUsage|
  2. Mappings ............. |PotionMappings|

:Helptags로 도움말 인덱스를 다시 만들면, 사용자는 :help potion.txt로 문서를 열 수 있습니다. 문서는 보통 Introduction, Usage, Mappings, Configuration, License, Bugs, Changelog, Credits 절로 구성합니다.

배포

플러그인을 Vim.org의 스크립트 섹션과 GitHub/Bitbucket 저장소에 올리세요. README에는 무엇을 하는 플러그인인지, 왜 써야 하는지, 라이선스, 전체 문서 링크를 담으세요. 그리고 Twitter, Reddit의 /r/vim, Vim 메일링 리스트에 알리세요.

배려하는 코딩 — 마지막 교훈 모든 엣지 케이스를 잡으려다 일을 못 끝내지 마세요. 대부분의 경우에 동작하고(동작 안 할 때 폭발하지 않으면) 다음으로 넘어가는 편이 보통 낫습니다. 예외는 많은 사람이 쓸 플러그인 — 그때는 시간을 들여 견고하게 만들 가치가 있습니다.

Section 06연습문제와 그다음

  • :help runtimepath를 읽고, Pathogen의 소스 코드를 살펴 무슨 일이 일어나는지 확인하세요.
  • :help syntax, :help syn-match, :help syn-region을 읽으세요.
  • :help foldexpr, :help fold-expr를 읽고, 반환할 수 있는 특수 문자열들을 확인하세요.
  • :help autoload, :help section을 읽으세요.
  • :help help-writing을 읽고, 자신의 vimrc 매핑 하나를 주석으로 완전히 문서화해 보세요.

이제 무엇을 할까

이 책의 모든 예제와 연습을 마쳤다면, 이제 Vimscript의 기초를 단단히 잡았습니다. 더 배울 것은 여전히 많습니다 — 색 구성표, :command 명령, omnicomplete, 컴파일러 지원, 그리고 Python·Ruby·Lua 같은 다른 언어로 Vim을 스크립팅하는 법.

하지만 가장 중요한 마지막 연습은 이것입니다 — 당신이 늘 원했던 무언가를 위한 Vim 플러그인을 직접 쓰고, 세상과 공유하세요.

Happy Vimscripting Steve Losh의 말로 끝맺습니다 — 이 책은 읽기만 하는 책이 아니었습니다. 모든 명령을 직접 손가락으로 입력하며 여기까지 왔다면, 당신은 이제 Vim을 그저 쓰는 사람이 아니라, Vim을 자신의 도구로 빚어내는 사람입니다.