vim에서 컬러 스킴을 변경하고 싶을 때에 :colorscheme
명령을 사용한다. (줄여서 :color
로 쓸 수 있다.) 이 명령으로 컬러 스킴을 변경할 때에는 테마 이름을 알아야 하는데, 사실 몰라도 상관없다. :color<space>
한 후에 탭 키를 누르면 테마 이름은 자동완성되기 때문이다. 근데 이것보다도 키 하나만 눌러서 다른 테마로 적용되는 것을 보면서 전환하도록 하는 것이 더 편리할 것 같다. 이 기능을 vim 안에서 어떻게 만들면 좋을지 알아보자.
전체 컬러 목록 만들기
컬러 스킴을 전환하려면 사용 가능한 전체 컬러의 이름 목록이 있어야 할 것이다. 이 목록을 만드는 작업부터 시작해보자.
let s:colorNameList = []
func! s:GetThemeNames()
let s:colorNameList = []
let s:paths = split(glob("$VIMRUNTIME/colors/*.vim"), '\n')
for s:path in s:paths
let s:name = fnamemodify(s:path, ":r:t")
call add(s:colorNameList, s:name)
endfor
endfunc
s:colorNameList
는 컬러스킴의 이름을 담아두는 리스트이다. 기본적인 컬러테마는 vim 설치위치 내에 colors 디렉토리 아래에 vim 파일로 정리되어 있다. glob()
함수를 사용해서 이 파일들의 경로를 얻은 다음, 개행문자로 분리하여 파일 경로의 리스트를 얻는다. 각각의 경로는 fnamemodify()
함수에 ":r:t"
옵션을 사용하여 확장자를 제외한 파일의 이름만 추출한다. 이 때, :t
옵션은 경로의 최하위 컴포넌트 (이름.확장자)를 의미하며, :r
옵션은 확장자를 제외하는 것을 말한다.
이렇게 얻은 이름들을 하나씩 s:colorNameList
에 추가한다. 리스트에 추가하는 함수는 add()
인데, 명령줄 상태에서 함수만 호출해야하므로 call
명령을 쓰고 있음에 유의하자.
현재 스킴 파악하기
다음 컬러스킴으로 변경하기 위해서는 현재 컬러스킴이 이름 목록에서 몇 번째인지를 알아내야 한다. 이 동작을 s:GetCurrentThemeIndex()
함수에서 정의하고 있다. 현재 컬러 스킴의 이름은 :colorscheme
명령을 실행하여 알 수 있는데, g:colors_name
에도 이 값이 기록되어 있기 때문에 그대로 사용하면 되겠다.
func s:GetCurrentThemeIndex()
let s:name = g:colors_name
return index(s:colorNameList, s:name)
endfunc
최종적으로 index()
함수를 사용해서 리스트에서 몇 번째 이름의 컬러스킴을 사용하는지 알아낼 수 있고 이 값을 리턴하면 된다.
컬러 변경 동작 구현하기
이번엔 컬러스킴을 변경하는 함수를 작성할 차례이다. 여기서도 역시 :colorscheme
명령을 사용할 것이다. 주의할 점은 :colorscheme
명령은 그 자체로도 Ex명령이지만, 이 경우에 컬러스킴 이름이 변수에 있기 때문에 colorscheme s:nname
이라고 하면 "s:nname"
이라는 컬러를 사용하려고 시도하기 때문에 동작하지 않는다. 따라서 :execute
명령을 사용해서 컬러를 전환한다. 이 명령에서 인자로 받는 명령은 문자열 형태로 전달해주면 되기 때문에 변수의 내용을 사용할 수 있다.
이제 다음 번 컬러로의 변환하는 방법은 간단하다. 현재 컬러의 다음 인덱스를 구해서 해당 위치의 컬러 이름으로 ":colorscheme 다음색이름"
명령을 실행하면 된다.
s:ChangeToNexTheme()
함수가 이를 수행한다. 먼저 s:colorNameList 의 원소의 개수를 구해서 2개미만이면 s:GetThemeList()
를 호출해서 리스트를 구성한다. 그리고 다음 인덱스를 계산한 후, 그에 해당하는 이름을 얻는다.
func! s:ChangeToNextTheme()
if len(s:colorNameList) < 2
call s:GetThemeNames()
endif
let s:cid = (s:GetCurrentThemeIndex() + 1) % len(s:colorNameList)
let s:nname = s:colorNameList[s:cid]
execute "colorscheme " . s:nname
endfunc
끝으로 s:ChangeToNextTheme()
함수를 실행해주는 명령을 등록하고, F10 키를 여기에 맵핑하자.
command! -nargs=0 ColorNext call s:ChangeToNextTheme()
nnoremap <F10> :ColorNext<cr>
함수, 명령 설명
이 글에서 사용한 함수나 명령에 대한 간략한 설명을 따로 정리했다. 자세한 내용은 vim의 :help
명령을 사용해서 확인하자.
glob({expr} [, {nosuf} [, {list} [, {alllinks}]]])
:{expr}
에 정의된 파일 와일드 카드를 확장한다. 결과는 NL로 이어진 하나의 문자열이므로split()
을 통해 분리하여 써야 한다.split({expr} [, {pattern} [, {keepemtpy}]])
:{expr}
을 분리하여 리스트를 만든다. 패턴이 생략되면 공백문자를 기준으로 자른다.{keepemtpy}
옵션은 빈 문자열을 리스트에 포함시킬 것인지 여부를 결정한다.:let words = split(getline('.'), '\W\+')
는 현재 줄에서 단어들로만 구성된 리스트를 만들어낸다.fnamemodify({fname}, {mods})
:{fname}
으로 주어진 파일 이름을 변형한다.index({list}, {expr} [, {start} [, {ic}]])
:
리스트에서 표현식 값이 등장하는 첫 인덱스를 구한다. 존재하지 않으면 -1이 리턴된다.len({expr})
:
길이를 구한다. 문자열, 리스트, 사전 등을 인자로 받을 수 있다.:[range]call {name}([arguments])
:
함수에 인자를 전달하여 호출한다. 범위가 주어지면 범위 내의 각 라인에 대해 반복실행한다. 만약 함수가 범위를 다룰 수 있다면a:firstline
,a:lastline
이 인자로 전달된다.aList[idx]
와 같은 식으로 리스트에서 인자를 통해 값을 얻을 수 있다. 비슷한 동작으로get({list}, {index})
가 있다. (List 도움말 참조)execute({command} [, {silent}])
:
문자열값을 받아 Ex명령으로 실행하고 그 결과를 문자열로 리턴한다. 리스트를 전달하는 경우, 명령이 하나씩 순차적으로 실행된다.{silent}
옵션을 주면 명령을 수행하는 동안 메시지를 출력하지 않는다.:silent
나:silent!
로 줄 수 있다.:execute {expr1} ..
: Ex 명령을 실행한다.:colorscheme [{name}]
: 주어진 이름으로 컬러 스킴을 변경한다. 이름이 주어지지 않으면 현재 컬러스킴 이름을 표시한다. 현재 컬러스킴 이름은g:colors_name
이다.
부록 2 – 파일이름 수정자
fnamemodify()
함수에서 사용할 수 있는 파일 이름 수정자는 아래와 같다. 이 수정자는 expand()
함수에서도 같이 사용할 수 있다.
:p
– 파일 이름을 절대 경로로 변경:8
– 파일 경로를 8.3 포맷으로 변경:~
– 홈 디렉토리로부터의 상대경로로 변경:.
– 현재 디렉토리로부터의 상대경로로 변경:h
– 파일 이름 앞까지의 경로:t
– 경로의 마지막 컴포넌트:r
– 확장자를 없앤다.:e
– 확장자:s?pat?sub
– 처음 등장하는 pat을 sub로 치환한다.:gs?pat?sub
– 모든 pat을 sub로 치환한다.:S
– 쉘 명령에 적용할 수 있도록 특수 문자를 이스케이프처리한다.