Vim 에디터 — 리눅스 커널 개발을 위한 Vim/Neovim 완전 가이드

리눅스 커널 소스 코드를 효율적으로 탐색·편집하기 위한 Vim/Neovim 완전 가이드입니다. vi에서 시작된 모달 편집 철학, 동사+목적어 문법 체계, 8가지 모드 전환, 레지스터(Register)·매크로(Macro)·텍스트 오브젝트, 윈도우/탭/버퍼(Buffer) 관리, cscope/ctags 기반 커널 소스 탐색, Neovim LSP/Treesitter 연동, vimrc 최적화, 플러그인 생태계, Termdebug GDB 통합까지 실제 커널 개발 업무 기준으로 상세히 설명합니다.

전제 조건: 개발 환경 설정 문서를 먼저 읽으세요. 터미널 기본 조작과 리눅스 기본 명령에 익숙하다고 가정합니다. Vim과 함께 사용하는 Git 워크플로는 Git 가이드를, GDB 디버깅(Debugging)은 GDB 가이드를 참고하세요.
일상 비유: Vim은 텍스트를 위한 언어와 같습니다. 일반 에디터가 "한 글자씩 타이핑"하는 방식이라면, Vim은 "문장 안의 단어를 바꿔라(ciw)", "이 함수 전체를 복사해라(yap)" 같은 문장형 명령으로 편집합니다. 처음에는 단어를 외우는 과정이 필요하지만, 일단 문법을 익히면 생각하는 속도로 코드를 편집할 수 있습니다.

핵심 요약

  • 모달 편집(Modal Editing) — 키보드 입력이 모드에 따라 다른 의미를 가집니다. Normal 모드에서 j는 "아래로 이동", Insert 모드에서는 문자 'j' 입력.
  • 동사+목적어 문법d(삭제) + w(단어) = dw(단어 삭제). 동사와 목적어를 조합해 수백 가지 편집 명령을 만들 수 있습니다.
  • 텍스트 오브젝트iw(단어 안), a}(중괄호 포함), ip(문단 안) 등 구조적 선택 단위. 커널 C 코드에서 함수 본체, 조건문 블록 등을 즉시 선택할 수 있습니다.
  • 레지스터 — 26개 이름 레지스터(a-z), 시스템 클립보드("+), 삭제 이력(1-9) 등 다중 클립보드 시스템.
  • cscope/ctags — 커널 소스 5만+ 파일에서 함수 정의·호출자·호출 대상을 즉시 탐색하는 인덱싱 도구.
  • Neovim — Vim의 현대적 포크. 내장 LSP 클라이언트, Treesitter 구문 분석, Lua 설정 등을 지원합니다.

단계별 이해

  1. 1단계: 생존i로 입력, Esc로 Normal 복귀, :w 저장, :q 종료. 이 네 가지만 알면 Vim에서 빠져나올 수 있습니다.
  2. 2단계: 이동hjkl 기본 이동, w/b 단어 단위 이동, 0/$ 줄 시작/끝, gg/G 파일 시작/끝으로 자유롭게 이동합니다.
  3. 3단계: 편집 문법d(삭제), c(변경), y(복사)를 이동 명령과 결합합니다. dw(단어 삭제), ci"(따옴표 안 내용 변경), yap(문단 복사).
  4. 4단계: 검색과 치환/패턴으로 검색, :%s/old/new/g로 전체 치환. *으로 커서 아래 단어를 즉시 검색합니다.
  5. 5단계: 커널 탐색 — cscope 데이터베이스를 생성하고 :cs find g 함수명으로 정의 위치로 점프합니다. ctags와 함께 사용하면 Ctrl-]/Ctrl-T로 빠르게 탐색할 수 있습니다.
  6. 6단계: 고급 활용 — 매크로, 레지스터, 플러그인, Neovim LSP 등을 조합해 커널 개발에 최적화된 환경을 구축합니다.

Vim 역사와 철학

ed에서 vi로: 라인 편집기의 진화

Vim의 뿌리는 1969년 Ken Thompson이 만든 ed 라인 편집기로 거슬러 올라갑니다. ed는 텔레타이프(teletypewriter) 시대의 산물로, 화면이 없는 환경에서 텍스트를 한 줄씩 편집했습니다. 1976년 Bill Joy가 UC Berkeley에서 ed를 기반으로 ex(extended editor)를 개발하고, 여기에 비주얼 모드를 추가한 것이 vi(visual editor)입니다.

vi는 ADM-3A 터미널에서 개발되었는데, 이 터미널의 키보드에는 hjkl 키에 화살표가 인쇄되어 있었습니다. 오늘날까지 이어지는 hjkl 이동 키의 기원입니다. 또한 ADM-3A의 Esc 키는 현재 Tab 키 위치에 있어 모드 전환이 매우 자연스러웠습니다.

Bram Moolenaar와 Vim의 탄생

1991년 네덜란드 프로그래머 Bram Moolenaar가 Amiga 컴퓨터용으로 vi 클론을 만들면서 Vim(Vi IMproved)이 탄생했습니다. 초기 버전은 "Vi IMitation"이었으나, 다중 실행 취소(undo), 구문 강조, 플러그인 시스템 등 수많은 기능을 추가하면서 "Vi IMproved"로 이름이 바뀌었습니다.

Bram Moolenaar는 2023년 8월 3일 별세했습니다. 30년 이상 Vim을 유지보수하며 오픈소스 세계에 큰 기여를 남겼습니다. 그의 공식 BDFL(Benevolent Dictator For Life) 역할 종료 이후, Vim은 커뮤니티 주도로 유지보수되고 있습니다.

연도이벤트주요 변화
1969ed 탄생 (Ken Thompson)최초의 Unix 텍스트 편집기
1976vi 탄생 (Bill Joy)비주얼 모드, 모달 편집 도입
1988POSIX.2 vi 표준화ex/vi 동작 사양 공식화
1991Vim 1.0 (Bram Moolenaar)Amiga용 vi 클론
1994Vim 3.0다중 윈도우, 다중 버퍼
1996Vim 4.0GUI (gvim)
1998Vim 5.0구문 강조(syntax highlighting)
2001Vim 6.0접기(folding), 다중 언어, 플러그인
2006Vim 7.0탭 페이지(Page), 자동완성, Vimscript 개선
2014Neovim 포크비동기 I/O, Lua, 내장 터미널, LSP
2016Vim 8.0비동기 job, 타이머(Timer), 터미널, packages
2022Vim 9.0Vim9script (컴파일 가능한 스크립트)
2023Bram Moolenaar 별세커뮤니티 주도 유지보수 전환

모달 편집 철학: 왜 모드인가?

모달 편집(modal editing)은 "같은 키가 맥락에 따라 다른 일을 한다"는 원칙입니다. 비모달 편집기(VS Code, Emacs 등)에서 모든 편집 명령은 Ctrl/Alt 수식키(modifier key)와 조합해야 합니다. 이는 두 가지 문제를 만듭니다:

모달 편집의 핵심 통찰은 프로그래머의 작업 시간 대부분이 새로운 코드 입력이 아니라 기존 코드 탐색과 수정에 소비된다는 점입니다. 리눅스 커널처럼 수천만 줄 규모의 코드베이스에서는 이 특성이 더욱 두드러집니다. Vim의 Normal 모드는 이런 "탐색과 수정" 작업에 최적화되어 있습니다.

참고: Linus Torvalds는 커널 개발에 MicroEmacs를 사용하는 것으로 알려져 있지만, 수많은 커널 서브시스템 메인테이너와 기여자들이 Vim을 주 에디터로 사용합니다. 커널 소스 트리에는 scripts/vim-hierarchical-indent 같은 Vim 관련 스크립트도 포함되어 있습니다.

모드 시스템 심층

Vim에는 총 8가지 모드가 있으며, 각 모드는 키 입력의 의미를 완전히 바꿉니다. 대부분의 시간은 Normal 모드에서 보내고, 텍스트 입력이 필요할 때만 Insert 모드로 전환하는 것이 올바른 습관입니다.

Vim 모드 전환 상태 다이어그램 Normal 모드 Insert 모드 Visual 모드 Command-line 모드 Replace 모드 Terminal 모드 Select 모드 Operator-pending 모드 i, a, o, I, A, O Esc / Ctrl-[ v, V, Ctrl-V Esc : / ? / Enter / Esc R Esc :terminal Ctrl-W N gh, gH d, c, y, ... motion / Esc 실선: 모드 진입 | 점선: 모드 복귀 (Esc 또는 동작 완료)

모드 특성 비교

모드진입 키용도상태줄 표시커서 모양
Normal(기본/Esc)이동, 편집 명령, 명령 조합(없음)블록
Inserti, a, o, I, A, O, gi텍스트 입력-- INSERT --세로선
Visualv (문자), V (줄), Ctrl-V (블록)영역 선택 후 명령 적용-- VISUAL --블록(반전)
Command-line: / ? /Ex 명령, 검색하단 명령줄하단 커서
ReplaceR기존 문자를 덮어쓰기-- REPLACE --밑줄
Terminal:terminal내장 터미널 셸Terminal셸 기본
Selectgh, gH, g Ctrl-H선택 후 바로 입력 (MS Word 방식)-- SELECT --블록(반전)
Operator-pendingd, c, y 등 입력 직후동작(motion) 대기 중(없음)블록(변화 없음)

Insert 모드 진입의 다양한 방식

Insert 모드로 진입하는 방법은 여러 가지이며, 각각 커서 위치와 동작이 다릅니다:

동작커널 코드 사용 예
i커서 앞에 삽입변수명 중간에 문자 추가
I줄 첫 번째 비공백 문자 앞에 삽입줄 앞에 static 키워드 추가
a커서 뒤에 삽입단어 끝에 접미사 추가
A줄 끝에 삽입줄 끝에 ; 또는 주석 추가
o아래에 새 줄 열기함수 본문에 새 구문 추가
O위에 새 줄 열기변수 선언 위에 주석 추가
gi마지막 Insert 위치로 돌아가 삽입Normal 모드에서 탐색 후 편집 복귀
gI줄의 1열(컬럼 1)에 삽입들여쓰기 무시하고 줄 맨 앞에 삽입
s커서 아래 문자 삭제 후 삽입한 문자 교체
S / cc줄 전체 삭제 후 삽입줄 내용을 완전히 다시 작성
팁: Insert 모드에서 빠져나올 때 Esc 대신 Ctrl-[을 사용하면 손이 홈 로우(home row)에서 벗어나지 않습니다. 많은 Vim 사용자가 jk 또는 jjEsc에 매핑(Mapping)하기도 합니다: inoremap jk <Esc>

Operator-pending 모드 이해

Normal 모드에서 d, c, y 같은 연산자(operator)를 입력하면 Vim은 즉시 Operator-pending 모드에 들어갑니다. 이 모드에서는 다음 입력이 "어디까지" 연산을 적용할지 결정하는 모션(motion) 또는 텍스트 오브젝트를 기다립니다. 연산자를 두 번 반복하면 현재 줄 전체에 적용됩니다:

" Operator-pending 모드 예시
d       " d 입력 → Operator-pending 모드 진입 (삭제 대기)
dw      " d + w → 단어 하나 삭제 (모드 즉시 종료)
d3w     " d + 3w → 단어 세 개 삭제
dd      " d + d → 현재 줄 전체 삭제
d$      " d + $ → 줄 끝까지 삭제 (D와 동일)
dip     " d + ip → 현재 문단(inner paragraph) 삭제
dat     " d + at → 현재 HTML/XML 태그 포함 삭제

" Operator-pending 모드에서 Esc를 누르면 취소
d → Esc  " 삭제 명령 취소, Normal 모드로 복귀

기본 이동 명령

문자/줄 단위 이동: hjkl

Vim의 가장 기본적인 이동 키는 h(좌), j(하), k(상), l(우)입니다. 화살표 키도 동작하지만, 홈 로우에서 손을 떼지 않는 hjkl 사용을 강력히 권장합니다.

" 기본 이동
h       " 왼쪽 한 칸
j       " 아래로 한 줄
k       " 위로 한 줄
l       " 오른쪽 한 칸
5j      " 아래로 5줄
10k     " 위로 10줄

" 줄 내 이동
0       " 줄 맨 앞 (컬럼 1)
^       " 첫 번째 비공백 문자
$       " 줄 끝
g_      " 마지막 비공백 문자
|       " 컬럼 1로 이동
15|     " 컬럼 15로 이동

단어 단위 이동: w, b, e, W, B, E

단어 단위 이동은 코드 탐색에서 매우 자주 사용됩니다. 소문자(w, b, e)는 Vim이 정의하는 "단어"(알파벳+숫자+밑줄 연속) 단위, 대문자(W, B, E)는 공백으로 구분되는 "WORD" 단위로 이동합니다.

이동 대상예시 (커서 위치 →)
w다음 단어 시작struct |task_struct *p;struct task_struct |*p;
W다음 WORD 시작struct |task_structstruct task_struct |*p;
b이전 단어 시작struct task_struct |*p;struct |task_struct *p;
B이전 WORD 시작공백 기준 이전 WORD 시작으로
e현재/다음 단어 끝|structstruc|t
E현재/다음 WORD 끝공백 기준 WORD 마지막 문자로
ge이전 단어 끝뒤쪽 단어의 마지막 문자로

화면 단위 이동

" 화면(페이지) 스크롤
Ctrl-F  " 한 화면 앞으로 (Forward)
Ctrl-B  " 한 화면 뒤로 (Backward)
Ctrl-D  " 반 화면 앞으로 (Down)
Ctrl-U  " 반 화면 뒤로 (Up)
Ctrl-E  " 화면 한 줄 위로 스크롤 (커서 고정)
Ctrl-Y  " 화면 한 줄 아래로 스크롤 (커서 고정)

" 화면 내 커서 위치
H       " 화면 맨 위 (High)
M       " 화면 중간 (Middle)
L       " 화면 맨 아래 (Low)
zt      " 현재 줄을 화면 맨 위로
zz      " 현재 줄을 화면 중앙으로
zb      " 현재 줄을 화면 맨 아래로

파일 단위 이동

gg      " 파일 첫 줄
G       " 파일 마지막 줄
42G     " 42번째 줄로 이동 (:42와 동일)
42gg    " 42번째 줄로 이동
%       " 대응하는 괄호로 이동 ( ↔ ), [ ↔ ], { ↔ }
{       " 이전 빈 줄 (문단 시작)
}       " 다음 빈 줄 (문단 끝)
(       " 이전 문장 시작
)       " 다음 문장 시작
[[      " 이전 섹션 (함수 시작 {가 1열에 있는 경우)
]]      " 다음 섹션

문자 찾기 이동: f, F, t, T

줄 내에서 특정 문자를 찾아 이동하는 명령은 편집 명령과 결합할 때 매우 강력합니다:

f{char} " 현재 줄에서 앞으로 {char} 위치로 이동
F{char} " 현재 줄에서 뒤로 {char} 위치로 이동
t{char} " {char} 바로 앞까지 이동 (till)
T{char} " 뒤로 {char} 바로 뒤까지 이동
;       " 마지막 f/F/t/T 반복
,       " 마지막 f/F/t/T 반대 방향 반복

" 실전 예시: 커널 코드에서 활용
" 줄: static int __init kernel_init(void *unused)
f(      " '(' 위치로 이동
ct)     " ')' 앞까지 변경 (매개변수 전체 수정)
df;     " ';'까지 삭제
*       " 커서 아래 단어를 앞으로 검색
#       " 커서 아래 단어를 뒤로 검색
g*      " 부분 일치로 앞으로 검색 (단어 경계 무시)
g#      " 부분 일치로 뒤로 검색
n       " 마지막 검색 방향으로 반복
N       " 마지막 검색 반대 방향으로 반복

마크와 점프 리스트

" 마크 설정과 이동
m{a-z}  " 현재 파일 내 마크 설정 (소문자 = 파일 로컬)
m{A-Z}  " 전역 마크 설정 (대문자 = 파일 간 이동 가능)
'{mark} " 마크된 줄의 첫 비공백 문자로 이동
`{mark} " 마크된 정확한 위치(줄+열)로 이동
''      " 직전 위치 줄로 복귀
``      " 직전 위치(줄+열)로 복귀
'.      " 마지막 변경 위치 줄
`.      " 마지막 변경 위치(줄+열)
:marks  " 모든 마크 목록 표시

" 점프 리스트 (파일 간 이동 이력)
Ctrl-O  " 이전 위치로 점프 (Older)
Ctrl-I  " 다음 위치로 점프 (newer, Tab과 동일)
:jumps  " 점프 리스트 표시

" 변경 리스트 (편집 위치 이력)
g;      " 이전 변경 위치로 이동
g,      " 다음 변경 위치로 이동
:changes " 변경 리스트 표시
팁: 커널 소스 탐색 시 mA로 현재 작업 중인 파일에 전역 마크를 찍어두면, 다른 파일을 탐색하다가 'A로 즉시 원래 위치로 돌아올 수 있습니다. Ctrl-O/Ctrl-I는 점프 이력을 따라 앞뒤로 이동하므로, cscope 탐색 후 원래 위치로 돌아갈 때 유용합니다.

Count(횟수)와 이동 명령 결합

거의 모든 이동 명령 앞에 숫자를 붙여 반복 횟수를 지정할 수 있습니다. 이것은 상대 줄 번호(relativenumber)와 함께 사용할 때 특히 강력합니다:

" Count 활용 패턴
5j          " 아래로 5줄 이동
12k         " 위로 12줄
3w          " 단어 3개 앞으로
4b          " 단어 4개 뒤로
2f;         " 두 번째 세미콜론으로 이동
3{          " 세 문단 위로

" Count + 편집 명령
d5j         " 아래 5줄 삭제 (현재 줄 포함 6줄)
c3w         " 다음 3단어를 변경
y2}         " 다음 2문단 복사
>4j         " 4줄 들여쓰기
5dd         " 5줄 삭제 (= d4j와 다름: 현재줄부터 5줄)
3yy         " 3줄 복사
2p          " 2번 붙여넣기 (복사한 내용이 2번 삽입)

" 숫자 vs 이동 명령 반복
3dw         " = dw dw dw (단어 3개 삭제)
d3w         " = d + 3w (커서부터 3단어 끝까지 삭제)
" 결과는 같을 수도 있지만 의미적으로 다릅니다:
" 3dw = "단어 삭제를 3번 반복" → . 으로 반복하면 "단어 1개 삭제"
" d3w = "3단어를 삭제" → . 으로 반복하면 "3단어 삭제"

수평 이동 효율화

줄 내에서의 수평 이동은 Vim 효율성의 핵심입니다. f/t;/,를 조합하면 줄 내 어디든 2~3번의 키 입력으로 도달할 수 있습니다:

" 수평 이동 최적화
" 줄: static int __init kernel_init(void *unused)

" f( → '(' 위치로 이동
" 2f_ → 두 번째 '_' 위치로 이동 (init의 _)
" t) → ')' 직전으로 이동
" F  → 뒤로 공백 찾기

" ; 과 , 로 반복
" fx → 다음 x로 이동
" ;  → 또 다음 x로 (같은 방향)
" ,  → 이전 x로 (반대 방향)

" 실전: 커널 함수 시그니처에서 특정 매개변수로 이동
" int copy_to_user(void __user *to, const void *from, unsigned long n)
f,          " 첫 번째 , 로 이동 (첫 매개변수 뒤)
;           " 두 번째 , 로 이동 (두 번째 매개변수 뒤)
" → 이제 세 번째 매개변수 근처에 있음

" W (WORD 이동)은 복잡한 식별자 건너뛸 때 유용
" __user *to → W → const
" void *from → W → unsigned
" 기호가 많은 C 코드에서 w보다 W가 빠른 경우가 많음
주의: h/l을 연타하여 좌우 이동하는 것은 비효율적입니다. f{char}, t{char}, w/b/e, 0/^/$를 사용하세요. 숙련된 Vim 사용자는 한 줄 내에서 원하는 위치로 2~3번의 키 입력으로 도달합니다.

파일 간 이동: gf와 gd

" gf (go to file) — 커서 아래 파일명으로 이동
" 커널 소스에서 #include <linux/sched.h> 위에 커서를 놓고:
gf          " → include/linux/sched.h 열기
Ctrl-W f    " → 수평 분할로 열기
Ctrl-W gf   " → 새 탭에서 열기

" gf가 찾을 경로 설정 (커널 소스용)
set path+=include/**
set path+=arch/x86/include/**
set path+=arch/arm64/include/**
" suffixesadd로 확장자 자동 추가
set suffixesadd+=.c,.h,.S

" gd (go to definition) — 로컬 변수 정의로 이동
gd          " 현재 함수 스코프에서 변수 정의 위치로 이동
gD          " 파일 전체에서 정의 위치로 이동

" 파일 위치 기반 이동
Ctrl-G      " 현재 파일 정보 (이름, 줄 수, 현재 위치 %)
g Ctrl-G    " 현재 줄/열/단어/바이트 상세 정보
1 Ctrl-G    " 전체 경로 표시

스크롤 제어와 커서 고정

" scrolloff — 커서 주변 최소 여백
set scrolloff=8      " 위아래 최소 8줄 여백 유지
set sidescrolloff=8  " 좌우 최소 8칸 여백

" 스크롤 동기화 (두 윈도우 동시 스크롤)
:set scrollbind      " 현재 윈도우 스크롤 바인딩
:windo set scrollbind  " 모든 윈도우에 스크롤 동기화 설정
:set noscrollbind      " 해제

" 줄 감싸기(wrap) 환경에서 이동
gj          " 화면상 아래 줄 (물리적 줄과 다를 수 있음)
gk          " 화면상 위 줄
g0          " 화면상 줄 시작
g$          " 화면상 줄 끝
g^          " 화면상 줄 첫 비공백 문자

" 숫자 앞에 붙은 실제 줄 번호로 이동
:42         " 42번째 줄로 (= 42G = 42gg)
+           " 다음 줄 첫 비공백 문자
-           " 이전 줄 첫 비공백 문자

이동 명령 실전 조합

" 커널 소스 탐색 시 효율적인 이동 패턴

" 1. 함수 시작/끝 이동 (커널 C 코드에서)
]]          " 다음 함수 시작 ({ 가 1열)
[[          " 이전 함수 시작
][          " 다음 함수 끝 (} 가 1열)
[]          " 이전 함수 끝

" 2. 빈 줄 기반 문단 이동 (함수 간 이동)
}           " 다음 빈 줄
{           " 이전 빈 줄
" → 커널 코드는 함수 사이에 빈 줄이 있으므로 함수 단위 이동에 효과적

" 3. 전처리기 지시문 이동
%           " 대응 #if/#elif/#else/#endif 이동

" 4. 변경 이력 기반 이동 (편집 중 되돌아가기)
g;          " 마지막 편집 위치로
g,          " 반대 방향
`.          " 마지막 변경 정확한 위치
'^          " 마지막 Insert 모드 종료 위치

" 5. 검색과 점프를 결합한 패턴
/\<schedule\>   " schedule 단어 경계 검색
*               " 커서 아래 단어 검색 (자동 단어 경계)
Ctrl-]          " ctags 기반 정의 점프
Ctrl-T          " 태그 스택에서 복귀
Ctrl-O          " 점프 리스트에서 뒤로
참고: set relativenumber를 설정하면 각 줄에 현재 커서로부터의 상대 거리가 표시됩니다. "7줄 아래"에 가고 싶으면 7j를 즉시 입력할 수 있어 이동 효율이 크게 향상됩니다.

편집 명령 문법

Vim 편집 명령의 핵심은 동사(operator) + [횟수(count)] + 목적어(motion/text-object) 구조입니다. 이 문법을 이해하면 개별 명령을 암기하지 않아도 수백 가지 편집 동작을 즉석에서 조합할 수 있습니다.

Vim 편집 명령 문법 구조 동사 (Operator) d c y > < = gq gu gU 삭제/변경/복사/들여쓰기... + [횟수] 1, 2, 3, ... (생략 시 1회) + 목적어 (Motion / Text Object) w b e $ 0 iw ap i} f{char} 단어/줄/문단/괄호 안... = d + 3 + w = d3w (단어 3개 삭제) | c + i + " = ci" (따옴표 안 내용 변경) y + a + p = yap 문단 전체 복사 > + i + } = >i} 중괄호 안 들여쓰기 gU + iw = gUiw 단어 대문자로 변환

주요 연산자(Operator) 목록

연산자동작예시커널 코드 활용
d삭제 (delete)dw, dd, d$불필요한 코드 제거
c변경 (change) — 삭제 후 Insert 진입cw, cc, ci"변수명 리팩토링
y복사 (yank)yw, yy, y$코드 패턴 복사
>들여쓰기 증가>>, >ip조건문 본체 들여쓰기
<들여쓰기 감소<<, <ip들여쓰기 정리
=자동 들여쓰기==, =ip, gg=G파일 전체 재정렬
gq텍스트 포맷팅 (textwidth 기준)gqip, gqq주석 줄바꿈 정리
gu소문자로 변환guw, guu매크로명 소문자화
gU대문자로 변환gUw, gUU상수명 대문자화
g~대소문자 토글g~w, g~~대소문자 전환
!외부 명령으로 필터링!ip sortinclude 목록 정렬

점 명령(dot command): 최강의 반복

.(dot) 명령은 마지막으로 수행한 변경을 반복합니다. 이것은 Vim에서 가장 강력한 단일 명령이라 할 수 있습니다. "한 번 수행하고, 필요한 곳에서 .으로 반복"하는 패턴은 Vim 효율성의 핵심입니다.

" 점 명령 활용 예시

" 1) 여러 줄 끝에 세미콜론 추가
A;     " 줄 끝에 ; 추가 (A = 줄 끝에 Insert)
j.     " 다음 줄에서 같은 동작 반복
j.     " 또 반복

" 2) 커널 코드에서 변수명 일괄 변경
/old_name        " 검색
cwneW_name<Esc>  " 단어를 변경
n.               " 다음 매칭으로 이동 후 반복
n.               " 계속 반복 (원하는 것만 선택적으로)

" 3) 여러 줄 주석 처리 (커널 스타일)
I/* <Esc>A */<Esc>  " 현재 줄을 /* ... */ 로 감싸기
j.                   " 다음 줄에도 반복 (! 이 경우 예상대로 안 될 수 있음)

" 점 명령이 반복하는 것: 마지막 "변경(change)" 단위
" - Insert 모드 진입부터 Normal 복귀까지가 하나의 변경
" - dd, x 같은 Normal 모드 삭제도 변경
" - 점 명령은 "움직임"은 반복하지 않음
참고: 효율적인 점 명령 사용의 핵심은 반복 가능하게 명령을 구성하는 것입니다. 예를 들어 cwdwi보다 점 명령으로 반복하기에 더 적합합니다. cw는 단일 변경 단위이지만, dwidwi...가 별개의 변경이 됩니다.

텍스트 오브젝트

텍스트 오브젝트(text object)는 Vim의 가장 강력한 기능 중 하나입니다. "커서 위치 주변의 구조적 단위"를 선택하는 방법으로, i(inner, 안쪽)와 a(around, 둘레 포함)의 두 가지 변형이 있습니다.

텍스트 오브젝트: inner(i) vs around(a) 단어 (word): hello world iw (inner word) aw (a word) - 공백 포함 따옴표 (quotes): fmt = "hello %s"; i" (따옴표 안) a" (따옴표 포함) 중괄호 (braces): if (cond) { stmt1; stmt2; } i} 또는 iB (중괄호 안 내용만) a} 또는 aB (중괄호 포함) 문단 (paragraph): /* 주석 시작 * 내용 * 끝 */ (빈 줄) ip (빈 줄 사이 내용) ap (다음 빈 줄 포함) 규칙: i = inner (구분자 제외) | a = around (구분자 포함) 커널 코드에서: ci} = 함수 본체 교체, dap = 함수 전체 삭제, yi" = 문자열 복사

텍스트 오브젝트 전체 목록

오브젝트inner (i)around (a)설명
wiwaw단어 (word)
WiWaWWORD (공백 구분)
sisas문장 (sentence)
pipap문단 (paragraph)
"i"a"큰따옴표 문자열
'i'a'작은따옴표 문자열
`i`a`백틱 문자열
) / bi) / iba) / ab소괄호 (parentheses)
]i]a]대괄호 (brackets)
} / Bi} / iBa} / aB중괄호 (braces)
titatHTML/XML 태그
>i>a>꺾쇠 괄호 (angle brackets)

커널 코드에서의 실전 활용

" 커널 C 코드 편집 시 텍스트 오브젝트 활용

" 함수 매개변수 수정
ci)     " 소괄호 안 매개변수 전체 교체
" static int __init do_early_param(char *param, char *val, ...)
"                                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^ ci) 범위

" 함수 본체 삭제
di}     " 중괄호 안 내용만 삭제 (중괄호는 유지)
da}     " 중괄호 포함 삭제

" 문자열 복사
yi"     " 큰따옴표 안 문자열 복사
" pr_info("Kernel version: %s\n", version);
"         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ yi" 범위

" 주석 블록 삭제
dap     " 현재 문단(주석 블록) 전체 삭제

" 조건문 안 내용 변경
ci(     " if 조건식 교체
" if (unlikely(atomic_read(&count) > MAX_LIMIT))
"     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ci( 범위

" 구조체 초기화 블록 복사
yi{     " 중괄호 안 내용 복사
" struct file_operations fops = {
"     .open    = my_open,
"     .read    = my_read,
"     .write   = my_write,
"     .release = my_release,
" };
" yi{ → 멤버 초기화 부분만 복사

실행 취소(undo)

Vim의 undo 시스템은 트리 구조입니다. 일반 에디터에서 undo 후 새로운 편집을 하면 redo 이력이 사라지지만, Vim은 모든 분기를 보존합니다:

" 기본 undo/redo
u           " undo (실행 취소)
Ctrl-R      " redo (다시 실행)
U           " 줄 전체를 원래 상태로 복구 (주의: 이것 자체가 변경)

" undo 트리 탐색
g-          " undo 트리에서 이전 상태 (시간순)
g+          " undo 트리에서 다음 상태 (시간순)
:earlier 5m " 5분 전 상태로 (시간 기반 undo!)
:later 5m   " 5분 후 상태로
:earlier 3f " 3번의 파일 저장 전 상태로
:later 2f   " 2번의 파일 저장 후 상태로

" undo 분기 시각화 (undotree 플러그인 또는 :undolist)
:undolist   " undo 분기 목록 표시
:undo 5     " undo 번호 5 상태로 직접 이동

" 영구 undo (파일을 닫았다 열어도 undo 유지)
set undofile                " 영구 undo 활성화
set undodir=~/.vim/undodir  " undo 파일 저장 위치
" mkdir -p ~/.vim/undodir 필요

" undo 설정
set undolevels=1000         " 최대 undo 단계 (기본 1000)
set undoreload=10000        " 버퍼 재로드 시 보존할 줄 수
팁: :earlier 10m은 "10분 전 파일 상태"로 되돌리는 놀라운 기능입니다. 커널 코드를 수정하다 잘못된 방향으로 갔을 때, "30분 전으로 되돌리자"가 가능합니다. undofile을 설정하면 Vim을 종료했다 다시 열어도 이 undo 이력이 유지됩니다.

명령 조합의 수학적 위력

Vim 문법의 진정한 위력은 곱셈 효과에 있습니다. 연산자 10개 × 모션/텍스트 오브젝트 30개 = 300가지 편집 명령을 개별 암기 없이 조합으로 사용할 수 있습니다:

w (단어)iw (inner word)i} (중괄호 안)ap (문단)t; (;까지)$ (줄끝)
d (삭제)dwdiwdi}dapdt;d$
c (변경)cwciwci}capct;c$
y (복사)ywyiwyi}yapyt;y$
> (들여쓰기)>w>iw>i}>ap--
gU (대문자)gUwgUiwgUi}gUapgUt;gU$
= (정렬)=w-=i}=ap--

새로운 연산자를 하나 배우면 기존의 모든 모션/텍스트 오브젝트와 즉시 결합됩니다. 새로운 모션을 하나 배우면 기존의 모든 연산자와 결합됩니다. 이것이 Vim 학습이 지수적으로 효율이 증가하는 이유입니다.

참고: 커널 코드에서 가장 자주 사용하는 조합 TOP 5:
  1. ci{ — 함수/조건문 본체 전체 교체
  2. diw/ciw — 변수명/함수명 삭제/교체
  3. yap — 함수 전체 복사 (빈 줄 사이)
  4. ci" — 문자열 내용 교체 (printk 메시지 등)
  5. da) — 함수 호출 제거 (괄호 포함)

반복 패턴: 점 명령 vs 매크로 vs 치환

방법적합한 경우예시
. (점 명령)같은 편집을 선택적으로 반복ciw새이름<Esc>n. 반복
@{reg} (매크로)복잡한 다단계 편집 반복레코드 → 100@q 일괄 적용
:%s (치환)패턴 기반 일괄 변경:%s/old/new/gc
:g (글로벌)패턴 매칭 줄에 명령 적용:g/pattern/normal @q
:argdo여러 파일에 걸친 변경:argdo %s/old/new/ge

검색과 치환

Vim의 검색과 치환은 정규식 기반으로 매우 강력합니다. 커널 소스처럼 거대한 코드베이스에서 패턴을 찾고 일괄 변경하는 작업에 필수적입니다.

검색/치환 명령 파이프라인 범위 (Range) % . ,$ '<,'> 명령 (Cmd) s / g / v 패턴 (Pattern) 정규식 / 리터럴 치환 (Replace) 대체 문자열 플래그 g c e i I :%s/\vold_func\(([^)]*)\)/new_func(\1)/gc % = 전체 파일 | s = 치환 | \v = very magic | gc = 전역+확인 :g/^\s*\/\//d | :v/TODO\|FIXME/d | :g/pattern/normal @q :g = 패턴 매칭 줄에 실행 | :v = 매칭 안 되는 줄에 실행

기본 검색

" 전방 검색
/pattern        " 아래 방향으로 패턴 검색
?pattern        " 위 방향으로 패턴 검색
n               " 같은 방향으로 다음 매칭
N               " 반대 방향으로 다음 매칭

" 검색 옵션
:set hlsearch   " 검색 결과 하이라이트
:set incsearch  " 타이핑 중 실시간 검색 (incremental)
:set ignorecase " 대소문자 무시
:set smartcase  " 대문자 포함 시 대소문자 구분 (ignorecase 필요)
:nohlsearch     " 하이라이트 임시 해제 (noh)

" 특수 검색
*               " 커서 아래 단어 전방 검색 (\<word\> 자동 적용)
#               " 커서 아래 단어 후방 검색
g*              " 부분 일치 전방 검색 (단어 경계 없음)
/pattern\c      " 이번 검색만 대소문자 무시
/pattern\C      " 이번 검색만 대소문자 구분

치환 명령 (:s)

" 기본 치환 문법
:s/old/new/         " 현재 줄의 첫 번째 매칭만 치환
:s/old/new/g        " 현재 줄의 모든 매칭 치환
:%s/old/new/g       " 파일 전체에서 치환
:%s/old/new/gc      " 파일 전체, 각각 확인(confirm) 후 치환
:5,20s/old/new/g    " 5~20번째 줄에서 치환
:'<,'>s/old/new/g   " 비주얼 선택 범위에서 치환
:.,$s/old/new/g     " 현재 줄부터 파일 끝까지 치환

" 치환 플래그
" g — 줄 내 모든 매칭 (없으면 첫 번째만)
" c — 각 매칭마다 확인 (y/n/a/q/l)
" i — 대소문자 무시
" I — 대소문자 구분
" e — 패턴 미발견 시 에러 무시
" n — 치환하지 않고 매칭 수만 표시

" 커널 코드에서의 치환 예시
:%s/\<printk\>/pr_info/gc          " printk를 pr_info로 변경 (확인)
:%s/\bspin_lock\b/raw_spin_lock/g  " spin_lock → raw_spin_lock
:%s/\v(struct\s+)old_name/\1new_name/g  " 구조체 이름 변경

" 구분자 변경 (경로 등 / 포함 문자열)
:%s#/usr/src/linux#/usr/src/linux-next#g
:%s|old/path|new/path|g

:global과 :vglobal

" :g/pattern/cmd — 패턴 매칭 줄에 명령 실행
:g/DEBUG/d              " DEBUG 포함 줄 전체 삭제
:g/^$/d                 " 빈 줄 전체 삭제
:g/TODO/p               " TODO 포함 줄 출력
:g/EXPORT_SYMBOL/yank A " EXPORT_SYMBOL 줄을 레지스터 a에 누적 복사
:g/pattern/normal @q    " 패턴 매칭 줄에서 매크로 q 실행
:g/^#include/sort       " #include 줄만 정렬

" :v/pattern/cmd — 패턴 매칭 안 되는 줄에 명령 실행 (inverse)
:v/error\|warn/d        " error 또는 warn 없는 줄 삭제
:v/^\s*\*/d             " 주석 블록( * 로 시작)이 아닌 줄 삭제

" :g와 :s 결합
:g/struct file_operations/s/\.read/.read_iter/g
" file_operations 구조체에서만 .read를 .read_iter로 변경
팁: :%s/old/new/gn에서 n 플래그를 사용하면 실제로 치환하지 않고 매칭 수만 확인할 수 있습니다. 대규모 치환 전에 영향 범위를 파악하는 데 유용합니다.

실시간(Real-time) 치환 미리보기 (Neovim)

" Neovim의 inccommand — 치환을 실시간으로 미리보기
set inccommand=split      " 미리보기를 분할 윈도우에 표시
" 또는
set inccommand=nosplit    " 인라인 미리보기만

" :%s/pattern/replacement/g 입력 중에:
" - 매칭 부분이 실시간 하이라이트
" - 치환 결과가 실시간 미리보기
" - split 모드에서는 영향받는 모든 줄이 별도 윈도우에 표시

" Vim 8에서는 이 기능이 없으므로 :%s/old/new/gc를 사용

파일 간 검색/치환 (프로젝트 전체)

" 방법 1: :grep + :cdo
:grep -rn 'old_api_name' drivers/net/
:cdo s/old_api_name/new_api_name/gc | update
" → 모든 매칭 파일에서 확인 후 치환 + 저장

" 방법 2: :args + :argdo
:args `grep -rl 'old_api_name' drivers/net/`
:argdo %s/old_api_name/new_api_name/ge | update

" 방법 3: :bufdo (열린 모든 버퍼)
:bufdo %s/old_api_name/new_api_name/ge | update

" 방법 4: fzf.vim + :Rg
:Rg old_api_name
" → 매칭 파일 목록에서 선택적으로 편집

" 방법 5: Neovim LSP 리네임 (가장 정확)
" 커서를 심볼 위에 놓고:
" :lua vim.lsp.buf.rename()
" → 프로젝트 전체에서 정확한 심볼 이름 변경
" (단순 텍스트 치환이 아닌 AST 기반)

" 방법 6: sed와 연동 (Vim 밖에서)
:!find drivers/net -name '*.c' -exec sed -i 's/old_api/new_api/g' {} +
" → Vim 외부에서 일괄 치환 (주의: undo 불가)

커널 코드 검색 실전 패턴

" 자주 사용하는 커널 코드 검색 패턴

" 1. 함수 정의 찾기 (줄 시작에 반환타입 + 이름)
/\v^\w+\s+\*?\w+\([^)]*\)\s*$
" → 매개변수가 다음 줄에 이어지는 경우는 놓칠 수 있음

" 2. 구조체 정의 찾기
/\vstruct\s+\w+\s*\{
" → struct foo {

" 3. 특정 매크로 사용처 찾기
/\v<DEFINE_MUTEX\(
/\v<DECLARE_WORK\(
/\v<module_init\(

" 4. printk/pr_* 레벨별 찾기
/\v<pr_(emerg|alert|crit|err|warn|notice|info|debug)\(

" 5. TODO/FIXME/HACK 찾기
/\v(TODO|FIXME|HACK|XXX|BUG):?

" 6. #ifdef 블록 찾기
/\v^#if(def|ndef)?\s+CONFIG_
" → CONFIG_ 관련 조건부 컴파일 블록

" 7. EXPORT_SYMBOL 찾기
/\vEXPORT_SYMBOL(_GPL)?\(
" → 모듈에 내보내는 심볼

" 8. 메모리 할당 함수 찾기
/\v<(kmalloc|kzalloc|kcalloc|vmalloc|kvmalloc)\(

" 검색 패턴을 하이라이트에 추가 (match)
:match Search /\v<(BUG_ON|WARN_ON)\(/
:2match Todo /\v<(TODO|FIXME):/
:3match Error /\s\+$/    " trailing whitespace

레지스터 시스템

Vim의 레지스터(register)는 텍스트를 저장하는 다중 슬롯 시스템입니다. 일반 에디터의 단일 클립보드와 달리, Vim은 40개 이상의 레지스터를 제공합니다.

Vim 레지스터 계층 구조 Vim 레지스터 사용자 쓰기 가능 자동 기록 읽기 전용 a-z / A-Z 이름 레지스터 A-Z: 추가모드 + / * 시스템 클립보드 X11 selection "" 무명 (기본 대상) "0 yank (마지막 복사) "1-9 삭제 이력 (큰 삭제) "% 파일명 "/ 검색어 ": ". "# 명령/삽입 /대체파일 특수 레지스터 "_ (블랙홀) "= (수식) "- (작은삭제) 레지스터 사용법 "ay = 레지스터 a에 복사 | "ap = 레지스터 a에서 붙여넣기 | :reg = 전체 목록 Insert/Cmdline 모드: Ctrl-R {reg} = 레지스터 내용 삽입 | Ctrl-R = = 수식 결과 삽입

레지스터 사용법

" 레지스터 지정 방법: "{reg} 접두사
"ayy    " 현재 줄을 레지스터 a에 복사
"Ayy    " 현재 줄을 레지스터 a에 추가(append)
"ap     " 레지스터 a 내용 붙여넣기
"bdiw   " 단어를 삭제하고 레지스터 b에 저장

" 시스템 클립보드
"+y     " 시스템 클립보드에 복사
"+p     " 시스템 클립보드에서 붙여넣기
"*y     " X11 primary selection에 복사 (마우스 드래그 선택)

" 블랙홀 레지스터 (삭제 시 덮어쓰기 방지)
"_dd    " 줄 삭제하되 어떤 레지스터에도 저장하지 않음
"_diw   " 단어 삭제 (무명 레지스터 보존)

" 마지막 yank 레지스터 활용 (핵심 팁!)
yiw     " 단어 복사 → "0에 저장
...     " (다른 곳에서 diw로 삭제 → "" 레지스터가 덮어씀)
"0p     " 원래 복사한 단어 붙여넣기 ("0은 마지막 yank만 보관)

" Insert/Command-line 모드에서 레지스터 삽입
" Ctrl-R a     → 레지스터 a 내용 삽입
" Ctrl-R "     → 무명 레지스터 삽입
" Ctrl-R +     → 클립보드 삽입
" Ctrl-R =     → 수식 결과 삽입 (예: =2+3 → 5)
" Ctrl-R /     → 마지막 검색 패턴 삽입
" Ctrl-R %     → 현재 파일 경로 삽입

" 모든 레지스터 내용 확인
:registers
:reg        " 축약형
:reg ab     " a, b 레지스터만 확인

번호 레지스터(1-9) 이해

줄 단위 이상의 삭제(dd, d} 등)를 수행하면 삭제된 텍스트가 번호 레지스터에 순환 저장됩니다. "1이 가장 최근, "9가 가장 오래된 것입니다:

" 삭제 이력 활용
"1p     " 가장 최근 삭제 내용 붙여넣기
"2p     " 그 전 삭제 내용 붙여넣기
...
"9p     " 9번째 전 삭제 내용

" 이전 삭제 순회 (붙여넣기 후)
"1p     " 최근 삭제 붙여넣기
u.      " undo 후 dot 반복 → "2p 효과
u.      " → "3p 효과
" 이런 식으로 이전 삭제 내용을 순회할 수 있습니다
팁: "_d(블랙홀 삭제)와 "0p(yank 레지스터 붙여넣기)를 습관화하면 "복사한 내용이 삭제로 덮어 씌워지는" 흔한 문제를 피할 수 있습니다. 또는 Neovim에서는 set clipboard=unnamedplus로 시스템 클립보드를 기본 레지스터로 사용하는 방법도 있습니다.

매크로

매크로(macro)는 키 입력 시퀀스를 레지스터에 기록하고 재생하는 기능입니다. 반복적인 편집 작업을 자동화할 때 가장 강력한 도구 중 하나입니다.

매크로 기록/재생 워크플로 1. 기록 시작 q{a-z} 상태줄: recording @a 2. 편집 동작 이동 + 편집 + ... 모든 키 입력 기록 3. 기록 종료 q 레지스터에 저장 4. 재생 @{a-z} / @@ 100@a = 100회 반복 매크로 편집 "ap → 레지스터 내용을 텍스트로 붙여넣기 편집 후 "ayy → 수정된 매크로 다시 저장 재귀 매크로 qaq (레지스터 a 초기화) qa ... @a q (자기 자신 호출 → 에러 시 중단) 커널 코드 실전 예시: EXPORT_SYMBOL → EXPORT_SYMBOL_GPL 변환 qa /EXPORT_SYMBOL(^M ciwEXPORT_SYMBOL_GPL<Esc> n q → 100@a

매크로 기본 사용법

" 매크로 기록
qa          " 레지스터 a에 기록 시작
...         " 편집 동작 수행
q           " 기록 종료

" 매크로 재생
@a          " 레지스터 a의 매크로 1회 실행
@@          " 마지막으로 실행한 매크로 반복
5@a         " 레지스터 a 매크로 5회 반복
100@a       " 100회 반복 (에러 발생 시 자동 중단)

" 비주얼 모드에서 선택한 줄에 매크로 적용
V선택 → :normal @a
" 또는
:'<,'>normal @a

매크로 설계 원칙

안정적으로 반복 가능한 매크로를 만들려면 다음 원칙을 따르세요:

원칙설명예시
시작 위치 통일매크로 시작 시 줄 시작(0 또는 ^)으로 이동qa 0 ... q
상대적 이동 사용10l 대신 f(이나 w 사용f( vs 10l
다음 줄로 이동매크로 끝에 j로 다음 줄 이동... j q
검색 기반 이동고정 위치 대신 패턴 검색/pattern
에러 시 중단100@a는 에러 발생 시 자동 중단모든 줄 처리 후 자동 종료
" 매크로 디버깅 팁
" 1. 먼저 수동으로 한 줄에 적용해보기
" 2. qa로 기록 시작, 한 줄에 적용
" 3. q로 기록 종료
" 4. @a로 다음 줄에서 테스트
" 5. 문제 있으면 "ap로 매크로 내용 확인/편집

" 매크로를 vimrc에 저장 (영구)
let @a = "0f(ci)void\<Esc>j"
" → 레지스터 a에 매크로를 문자열로 저장
" 주의: 특수 키는 이스케이프 시퀀스 사용 (\<Esc>, \<CR> 등)

실전 매크로 예시

" 예시 1: 커널 함수 프로토타입 → 스텁 구현 변환
" 변환 전: int my_read(struct file *filp, char __user *buf, size_t count, loff_t *ppos);
" 변환 후: int my_read(struct file *filp, char __user *buf, size_t count, loff_t *ppos)
"          {
"              return -ENOSYS;
"          }
" 매크로: qa f;s^M{^M^Ireturn -ENOSYS;^M}^M^[j q

" 예시 2: #define 상수 목록에서 enum 생성
" 변환 전: #define FOO_BAR    0x01
" 변환 후: FOO_BAR = 0x01,
" 매크로: qa ^df dwf0i= ^[A,^[$xj q

" 예시 3: CSV를 C 배열 초기화로 변환
" 변환 전: red,255,0,0
" 변환 후: { "red", 255, 0, 0 },
" 매크로: qa I{ "^[f,s", ^[A },^[j q

" 매크로 편집 (레지스터는 텍스트!)
"ap         " 레지스터 a 내용을 텍스트로 붙여넣기
" (텍스트를 편집)
0"ay$       " 수정된 줄을 레지스터 a에 다시 저장
dd          " 임시 줄 삭제
주의: 매크로를 기록할 때는 절대 위치 대신 상대적 이동을 사용하세요. 예를 들어 10l(오른쪽 10칸) 대신 f((다음 괄호로 이동)이나 w(다음 단어)를 사용해야 줄마다 길이가 달라도 매크로가 올바르게 동작합니다. 매크로 시작 시 0이나 ^로 줄 시작 위치를 통일하는 것도 좋은 습관입니다.

윈도우/탭/버퍼

Vim에서 파일 관리는 세 가지 계층 구조로 이루어집니다: 버퍼(Buffer), 윈도우(Window), 탭(Tab Page). 이 관계를 정확히 이해하면 커널 소스처럼 수많은 파일을 효율적으로 다룰 수 있습니다.

버퍼 / 윈도우 / 탭 관계 버퍼 풀 (Buffer Pool) — 메모리에 로드된 파일들 1: sched.c 2: fork.c 3: mm.h 4: signal.c 5: exit.c 6: exec.c 탭 1 (Tab Page) 윈도우 1 버퍼 1: sched.c ┌─────────────┐ │ schedule() │ │ ... │ │ pick_next() │ └─────────────┘ 윈도우 2 버퍼 2: fork.c ┌─────────────┐ │ _do_fork() │ │ ... │ │ copy_mm() │ └─────────────┘ 탭 2 (Tab Page) 윈도우 3 버퍼 4: signal.c 윈도우 4 버퍼 5: exit.c

개념 정리

개념비유설명
버퍼열린 문서파일의 메모리 내 복사본. 화면에 보이지 않아도 존재(hidden buffer)
윈도우창(뷰포트)버퍼를 보여주는 화면 영역. 하나의 버퍼를 여러 윈도우에서 볼 수 있음
탭 페이지가상 데스크톱윈도우 레이아웃의 모음. 탭 ≠ 파일 (흔한 오해)

버퍼 관리

" 버퍼 목록 및 전환
:ls             " 버퍼 목록 표시 (= :buffers)
:b 3            " 버퍼 3으로 전환
:b sched        " 파일명 일부로 전환 (Tab 자동완성)
:bn             " 다음 버퍼 (buffer next)
:bp             " 이전 버퍼 (buffer previous)
:bf             " 첫 버퍼 (buffer first)
:bl             " 마지막 버퍼 (buffer last)
Ctrl-^          " 대체 파일(#) 버퍼로 전환 (= Ctrl-6)
:bd             " 현재 버퍼 삭제 (파일은 유지, 목록에서 제거)
:bd 3 5 7       " 여러 버퍼 한 번에 삭제
:%bd            " 전체 버퍼 삭제
:bufdo %s/old/new/g  " 모든 버퍼에서 치환

윈도우 관리

" 윈도우 분할
:split          " 수평 분할 (= :sp, Ctrl-W s)
:vsplit         " 수직 분할 (= :vs, Ctrl-W v)
:split file.c   " 파일을 수평 분할로 열기
:new            " 새 빈 윈도우 (수평)
:vnew           " 새 빈 윈도우 (수직)

" 윈도우 간 이동
Ctrl-W h        " 왼쪽 윈도우
Ctrl-W j        " 아래 윈도우
Ctrl-W k        " 위 윈도우
Ctrl-W l        " 오른쪽 윈도우
Ctrl-W w        " 다음 윈도우 (순환)
Ctrl-W p        " 이전 윈도우

" 윈도우 이동
Ctrl-W H        " 현재 윈도우를 맨 왼쪽으로
Ctrl-W J        " 현재 윈도우를 맨 아래로
Ctrl-W K        " 현재 윈도우를 맨 위로
Ctrl-W L        " 현재 윈도우를 맨 오른쪽으로
Ctrl-W r        " 윈도우 순환 회전
Ctrl-W T        " 현재 윈도우를 새 탭으로 이동

" 윈도우 크기 조절
Ctrl-W =        " 모든 윈도우 동일 크기
Ctrl-W _        " 현재 윈도우 높이 최대화
Ctrl-W |        " 현재 윈도우 너비 최대화
Ctrl-W 10+      " 높이 10줄 증가
Ctrl-W 5-       " 높이 5줄 감소
Ctrl-W 10>      " 너비 10칸 증가
Ctrl-W 10<      " 너비 10칸 감소

" 윈도우 닫기
:close          " 현재 윈도우 닫기 (= Ctrl-W c)
:only           " 현재 윈도우만 남기고 모두 닫기 (= Ctrl-W o)
:q              " 현재 윈도우 닫기 (마지막이면 Vim 종료)

탭 페이지 관리

" 탭 생성
:tabnew         " 새 탭
:tabnew file.c  " 파일을 새 탭에서 열기
:tabedit file.c " 위와 동일 (= :tabe)

" 탭 전환
gt              " 다음 탭
gT              " 이전 탭
3gt             " 3번 탭으로 이동
:tabn           " 다음 탭 (= :tabnext)
:tabp           " 이전 탭 (= :tabprevious)
:tabfirst       " 첫 번째 탭
:tablast        " 마지막 탭

" 탭 관리
:tabclose       " 현재 탭 닫기 (= :tabc)
:tabonly        " 현재 탭만 남기기 (= :tabo)
:tabmove 0      " 현재 탭을 첫 번째로 이동
:tabmove        " 현재 탭을 마지막으로 이동
:tabdo %s/old/new/g  " 모든 탭에서 치환
팁: 커널 개발 시 추천하는 레이아웃: 탭 = 작업 맥락(예: 네트워킹 수정 탭, 드라이버 수정 탭), 윈도우 = 관련 파일 나란히 보기(예: .c와 .h 좌우 분할). 버퍼 전환(:b 이름)은 파일 간 빠른 점프에 사용합니다.

커널 개발 실전 레이아웃

" 커널 드라이버 개발 시 추천 레이아웃

" 레이아웃 1: 소스/헤더 좌우 분할
:e drivers/net/ethernet/intel/e1000/e1000_main.c
:vs drivers/net/ethernet/intel/e1000/e1000.h
" Ctrl-W = (동일 너비)

" 레이아웃 2: 코드 + 빌드 로그
:e drivers/my_driver.c
:bot split | terminal      " 하단에 터미널 열기
" 터미널에서: make -j$(nproc) drivers/my_driver.ko
" Ctrl-W k로 편집기 복귀

" 레이아웃 3: 탭별 작업 분리
:tabnew                    " 탭 1: 드라이버 소스
:e drivers/my_driver.c
:vs drivers/my_driver.h
:tabnew                    " 탭 2: 커널 코어 참조
:e kernel/sched/core.c
:tabnew                    " 탭 3: 빌드/테스트
:terminal

" 탭 이름 설정 (Neovim)
" Neovim은 탭라인 커스터마이즈 가능

" 빠른 대체 파일 전환
Ctrl-^      " 현재 파일 ↔ 이전 파일 즉시 전환
" 헤더와 소스 파일 사이를 오가는 데 최적

" 인자 목록으로 관련 파일 한번에 열기
:args drivers/my_driver/*.{c,h}
:all        " 모든 arg 파일을 윈도우로 열기
:sall       " 수평 분할로 모든 arg 파일 열기
:vert sall  " 수직 분할로 모든 arg 파일 열기

세션(session) 관리

" 세션 저장 (윈도우 레이아웃, 열린 버퍼 보존)
:mksession ~/sessions/kernel-net.vim    " 세션 저장
:source ~/sessions/kernel-net.vim       " 세션 복원

" Vim 시작 시 세션 로드
$ vim -S ~/sessions/kernel-net.vim

" 세션에 포함되는 항목 설정
set sessionoptions=buffers,curdir,folds,help,tabpages,winsize

" 자동 세션 저장/복원 (Neovim 플러그인 추천)
" auto-session, possession.nvim 등

" 뷰(view) — 단일 윈도우의 설정 저장
:mkview                " 현재 윈도우의 접기/커서 위치 저장
:loadview              " 저장된 뷰 복원
set viewoptions=cursor,folds,slash,unix

비주얼 모드

세 가지 비주얼 모드

모드선택 단위주요 용도
v문자 비주얼문자 단위 자유 선택부분 텍스트 복사/삭제
V줄 비주얼전체 줄 단위 선택줄 그룹 이동/삭제/들여쓰기
Ctrl-V블록 비주얼직사각형 블록 선택다중 줄 동시 편집, 열 삽입

블록 비주얼 모드 실전

블록 비주얼 모드(Ctrl-V)는 여러 줄에 걸쳐 동일한 열 위치를 동시에 편집할 수 있는 독특한 기능입니다:

" 여러 줄 앞에 동시에 텍스트 삽입
" 1. Ctrl-V로 블록 선택 시작
" 2. j/k로 원하는 줄 범위 선택
" 3. I 입력 후 텍스트 입력
" 4. Esc → 모든 선택 줄에 동시 적용

" 예시: 커널 코드에서 여러 변수 선언에 static 추가
" 변환 전:
"   int count;
"   int limit;
"   int flags;
" Ctrl-V → 2j → I → static  → Esc
" 변환 후:
"   static int count;
"   static int limit;
"   static int flags;

" 여러 줄 끝에 동시에 텍스트 추가
" Ctrl-V → 선택 → $ → A → 텍스트 → Esc

" 블록 선택 후 사용 가능한 명령
" I     — 블록 앞에 삽입
" A     — 블록 뒤에 삽입 ($ 사용 시 줄 끝)
" c     — 블록 내용을 변경
" C     — 블록 위치부터 줄 끝까지 변경
" r     — 블록 내용을 한 문자로 교체
" d/x   — 블록 삭제
" >/<   — 들여쓰기
" J     — 선택 줄 합치기

커널 코드에서의 비주얼 모드 실전

" 시나리오 1: 구조체 멤버 초기화 정렬
" 변환 전:
" .open = my_open,
" .read_iter = my_read,
" .write = my_write,
" .release = my_release,
"
" V로 4줄 선택 → :!column -t
" 변환 후 (정렬됨):
" .open      = my_open,
" .read_iter = my_read,
" .write     = my_write,
" .release   = my_release,

" 시나리오 2: switch-case 들여쓰기 수정
" V로 case 블록 선택 → > 또는 < 로 들여쓰기 조정
" gv로 재선택 → 추가 조정 가능

" 시나리오 3: #ifdef 블록 주석 처리
" Ctrl-V로 블록 선택 (줄 시작) → I → // → Esc
" → 모든 줄 앞에 // 추가

" 시나리오 4: 열 삭제 (CSV, 테이블 등)
" Ctrl-V로 특정 열 블록 선택 → d
" → 해당 열만 깔끔하게 삭제

" 시나리오 5: 여러 줄의 할당문에 = 정렬
" V로 할당문 줄들 선택 → :!column -t -s= -o=
" 또는 vim-easy-align 플러그인: vipga=

" 시나리오 6: 함수 매개변수를 한 줄씩 분리
" 긴 함수 호출:
" foo(arg1, arg2, arg3, arg4, arg5);
" f, 위치에서 Ctrl-V → A → Enter → Esc
" 또는 r<CR>로 , 를 줄바꿈으로 교체

비주얼 모드 고급 기법

" 비주얼 재선택
gv          " 마지막 비주얼 선택 영역 재선택
" 유용한 경우: >로 들여쓰기 후 gv로 재선택하여 다시 > 적용

" 비주얼 모드 전환
" v 상태에서 V → 줄 비주얼로 전환
" V 상태에서 Ctrl-V → 블록 비주얼로 전환

" 비주얼 선택 범위 조정
o           " 선택 영역의 반대쪽 끝으로 커서 이동
O           " 블록 비주얼에서 같은 줄의 반대쪽 모서리로

" 비주얼 + 연산자
" 선택 후 d, c, y, >, <, =, gq, U, u, ~ 등 적용 가능

" 비주얼 모드에서 검색/치환
" :'<,'>s/old/new/g  → 선택 범위에서만 치환

" 비주얼 모드에서 정렬
" V 선택 → :sort     → 선택 줄 알파벳 정렬
" V 선택 → :sort n   → 숫자순 정렬
" V 선택 → :sort u   → 중복 제거 정렬

명령줄 모드

범위(Range) 지정

" 범위 지정 문법
:5,20       " 5~20번째 줄
:.          " 현재 줄
:$          " 마지막 줄
:.,$        " 현재 줄 ~ 마지막 줄
:%          " 전체 파일 (= 1,$)
:'<,'>      " 마지막 비주얼 선택 범위
:.+3        " 현재 줄에서 3줄 아래
:.-2,.+2    " 현재 줄 위아래 2줄씩 (5줄)
:/pattern/  " pattern이 나타나는 다음 줄
:?pattern?  " pattern이 나타나는 이전 줄
:/start/,/end/  " start부터 end까지

" 범위 활용 예시
:5,20d          " 5~20줄 삭제
:10,30y         " 10~30줄 복사
:100,200w chunk.c  " 100~200줄을 별도 파일로 저장
:.,.+5s/foo/bar/g  " 현재 줄부터 5줄에서 치환

외부 명령 연동

" 외부 명령 실행
:!ls -la            " 셸 명령 실행 결과 확인
:!make -j$(nproc)   " 커널 빌드

" 외부 명령으로 필터링
:%!sort             " 파일 전체를 sort로 필터링
:5,20!sort          " 5~20줄만 sort
:%!column -t        " 테이블 정렬
:!ip sort           " 현재 문단을 sort (Operator-pending + motion)

" 외부 명령 출력 삽입
:r !date            " 현재 날짜를 커서 아래에 삽입
:r !ls *.c          " C 파일 목록 삽입
:r !git log --oneline -5  " 최근 커밋 5개 삽입
:r filename.txt     " 파일 내용 삽입

" 파일 일부분 저장
:w                  " 전체 저장
:w newfile.c        " 다른 이름으로 저장
:10,50w chunk.c     " 10~50줄만 파일로 저장
:w !sudo tee %      " 읽기 전용 파일에 sudo로 저장

명령줄 이력과 특수 기능

" 명령줄 이력
q:          " Ex 명령 이력 윈도우 (편집 가능!)
q/          " 검색 이력 윈도우
Ctrl-F      " 명령줄에서 이력 윈도우 열기

" arglist (인자 목록)
:args *.c           " C 파일 목록을 arglist에 설정
:argdo %s/old/new/gc | update  " 모든 arglist 파일에서 치환
:argadd file.c      " arglist에 파일 추가
:argdelete file.c   " arglist에서 파일 제거
:next               " 다음 arg 파일
:prev               " 이전 arg 파일

" bufdo, windo, tabdo
:bufdo %s/old/new/ge | update  " 모든 버퍼에서 치환
:windo set number              " 모든 윈도우에 줄번호 표시
:tabdo %s/old/new/g            " 모든 탭에서 치환

" :cdo, :cfdo (quickfix 항목별)
:cdo s/old/new/g | update      " quickfix 매칭마다 치환

명령줄에서의 레지스터와 표현식

" 명령줄에서 레지스터 삽입 (Ctrl-R)
:s/Ctrl-R //new/g       " Ctrl-R / → 마지막 검색 패턴 삽입
:echo Ctrl-R %           " Ctrl-R % → 현재 파일 경로 삽입
:!grep Ctrl-R " %        " Ctrl-R " → 무명 레지스터 내용 삽입

" 표현식 레지스터 (Ctrl-R =)
:echo Ctrl-R =2+3        " → 5 출력
:let x = Ctrl-R =line('$')  " → 마지막 줄 번호

" 명령줄 단축키
Ctrl-B      " 명령줄 맨 앞으로
Ctrl-E      " 명령줄 맨 뒤로
Ctrl-W      " 이전 단어 삭제
Ctrl-U      " 전체 삭제
Ctrl-R Ctrl-W   " 커서 아래 단어 삽입
Ctrl-R Ctrl-A   " 커서 아래 WORD 삽입
Ctrl-R Ctrl-L   " 커서 아래 줄 삽입

" 명령줄 이력 탐색
↑ / ↓       " 이전/다음 명령 (현재 입력으로 필터링)
Ctrl-P/N     " 이전/다음 명령 (필터링 없음)
q:          " 명령 이력 윈도우 (편집 가능!)
q/          " 검색 이력 윈도우

" 명령 출력 캡처
:redir @a | silent execute 'ls' | redir END
" → 버퍼 목록을 레지스터 a에 저장
:put a      " → 레지스터 a 내용 붙여넣기

" 명령 출력을 새 버퍼에서 보기
:enew | put =execute('map')
" → 모든 키 매핑 목록을 새 버퍼에 표시

치환 고급 기법

" 치환 표현식 (\=)
:%s/\d\+/\=submatch(0)+1/g
" → 모든 숫자를 1 증가
" submatch(0) = 전체 매칭, submatch(1) = 첫 번째 그룹

" 줄 번호를 삽입하는 치환
:%s/^/\=line('.').' '/
" → 각 줄 앞에 줄 번호 삽입

" 대소문자 변환 치환
:%s/\<\(\w\)/\u\1/g          " 각 단어 첫 글자 대문자
:%s/\<\(\w\+\)\>/\L\1/g     " 전체 소문자
" \u = 다음 1문자 대문자, \U = 이후 전부 대문자
" \l = 다음 1문자 소문자, \L = 이후 전부 소문자
" \e = 대소문자 변환 종료

" 커널 코드에서의 실전 치환
" printk 레벨 일괄 변경
:%s/\vprintk\(KERN_INFO\s*"([^"]*)"\)/pr_info("\1")/gc

" 구조체 멤버 접근 방식 변경 (-> 를 . 로, 역은 불가)
" obj->member 패턴은 유지하되 특정 변수만 변경
:%s/\vold_ptr->(\w+)/new_struct.\1/gc

" 여러 패턴을 한 번에 치환 (alternation)
:%s/\v(error|err|ERROR)/failure/gc

" 줄 합치기 치환
:%s/\n\s*/ /      " 다음 줄을 현재 줄에 합치기 (공백으로)

" 중복 빈 줄 제거
:%s/\n\{3,}/\r\r/g    " 3개 이상 연속 빈 줄을 2개로

자동완성

Vim은 Insert 모드에서 다양한 자동완성 기능을 내장하고 있습니다. LSP 없이도 키워드, 파일 경로, 태그 기반 완성이 가능합니다.

Insert 모드 자동완성 (Ctrl-X 서브모드) Insert 모드 Ctrl-X 범용: Ctrl-N / Ctrl-P Ctrl-X Ctrl-L 전체 줄 완성 유사 줄 검색 Ctrl-X Ctrl-F 파일 경로 완성 #include 경로 Ctrl-X Ctrl-] 태그 완성 ctags 기반 Ctrl-X Ctrl-K 사전 완성 dictionary 파일 Ctrl-X Ctrl-O 옴니 완성 언어별 지능형 Ctrl-X Ctrl-D 매크로/정의 완성 Ctrl-X Ctrl-I include 파일 완성 Ctrl-X Ctrl-V Vim 명령 완성 Ctrl-X Ctrl-U 사용자 함수 완성 Ctrl-X Ctrl-S 맞춤법 완성 자동완성 팝업 조작 Ctrl-N = 다음 항목 | Ctrl-P = 이전 항목 | Ctrl-Y = 확정 | Ctrl-E = 취소 커널 개발: Ctrl-X Ctrl-] (태그) + Ctrl-X Ctrl-F (헤더 경로) 가장 유용

자동완성 실전 워크플로

커널 코드 작성 시 자동완성은 긴 심볼명, 구조체(Struct) 멤버, 파일 경로를 빠르게 입력하는 데 필수적입니다:

" 시나리오 1: 긴 커널 함수명 입력
" "schedule_timeout_interruptible"을 입력하려면:
" sched → Ctrl-N → 자동완성 목록에서 선택
" 또는 sche → Ctrl-X Ctrl-] (태그 기반 완성)

" 시나리오 2: 헤더 파일 경로 입력
" #include < → Ctrl-X Ctrl-F → linux/ → Ctrl-X Ctrl-F → sched.h
" path에 include 디렉터리가 설정되어 있어야 함

" 시나리오 3: 구조체 멤버 접근
" task->comm → task-> 입력 후 Ctrl-X Ctrl-O (옴니 완성)
" 또는 Ctrl-X Ctrl-] (태그에서 멤버 완성)

" 시나리오 4: 이전에 입력한 줄 반복
" 비슷한 코드 줄을 반복 입력할 때:
" Ctrl-X Ctrl-L → 유사한 줄을 완성

" 시나리오 5: #define 매크로 값 입력
" CONFIG_ → Ctrl-X Ctrl-D → #define 목록에서 선택

" 자동완성 팝업 조작
Ctrl-N      " 다음 항목 선택
Ctrl-P      " 이전 항목 선택
Ctrl-Y      " 현재 선택 항목 확정 (Yes)
Ctrl-E      " 자동완성 취소 (Exit)
" 계속 입력하면 목록이 자동으로 필터링됨

옴니 완성 설정 (C 언어)

" C 파일용 옴니 완성
" Vim 내장 C 옴니 완성은 제한적
" 더 나은 대안: ctags 기반 완성 또는 LSP

" ctags 기반 완성 최적화
set complete=.,w,b,u,t,i
" . = 현재 버퍼
" w = 다른 윈도우의 버퍼
" b = 로드된 다른 버퍼
" u = 언로드된 버퍼
" t = 태그 파일
" i = include된 파일

" include 파일 검색 경로 (Ctrl-X Ctrl-I용)
set path+=include/**
set path+=arch/x86/include/**
set include=^\s*#\s*include
set includeexpr=substitute(v:fname,'\\.','/','g')

" Neovim LSP 자동완성 (nvim-cmp)
-- nvim-cmp 기본 설정
local cmp = require('cmp')
cmp.setup({
  sources = cmp.config.sources({
    { name = 'nvim_lsp' },      -- LSP (clangd) 소스
    { name = 'buffer' },         -- 현재 버퍼 단어
    { name = 'path' },           -- 파일 경로
  }),
  mapping = cmp.mapping.preset.insert({
    ['<C-n>'] = cmp.mapping.select_next_item(),
    ['<C-p>'] = cmp.mapping.select_prev_item(),
    ['<C-y>'] = cmp.mapping.confirm({ select = true }),
    ['<C-e>'] = cmp.mapping.abort(),
    ['<C-Space>'] = cmp.mapping.complete(),
  }),
})

자동완성 사용 예시

" Insert 모드에서:
Ctrl-N          " 키워드 순방향 완성 (현재 파일 + 열린 버퍼)
Ctrl-P          " 키워드 역방향 완성

" Ctrl-X 서브모드 (Insert 모드에서)
Ctrl-X Ctrl-L   " 전체 줄 완성 (유사 줄 검색)
Ctrl-X Ctrl-F   " 파일 경로 완성
Ctrl-X Ctrl-]   " 태그 완성 (ctags 필요)
Ctrl-X Ctrl-K   " 사전 완성
Ctrl-X Ctrl-O   " 옴니 완성 (언어별 지능형 완성)
Ctrl-X Ctrl-D   " #define 매크로 완성
Ctrl-X Ctrl-I   " include 파일의 키워드 완성
Ctrl-X Ctrl-N   " 현재 파일 키워드만 완성
Ctrl-X Ctrl-S   " 맞춤법 교정 완성

" 자동완성 설정
set completeopt=menu,menuone,noselect  " 팝업 메뉴 동작 설정
set complete=.,w,b,u,t                 " 완성 소스 (현재파일,윈도우,버퍼,unloaded,태그)
set pumheight=15                       " 팝업 메뉴 최대 높이

접기 (Folding)

접기(folding)는 코드 블록을 축소하여 파일의 구조를 파악하기 쉽게 만드는 기능입니다. 커널 소스처럼 수천 줄짜리 파일에서 함수 간 이동과 전체 구조 파악에 매우 유용합니다.

코드 접기(Folding) 계층 구조 펼친 상태 1 #include <linux/... 2 #include <linux/... 3 4 static int foo() 5 { 6 int ret; 7 if (cond) { 8 stmt1; 9 stmt2; 10 } 11 return ret; 12 } 13 ... zc 내부 접기 1 #include ... 2 #include ... 3 4 static int foo() 5 { 6 int ret; +-- 4 lines: if--- 11 return ret; zM 전체 접기 1 #include ... 2 #include ... 3 +- 9 lines: foo-- 13 ...

접기 방법(foldmethod)

방법설정기준커널 코드 적합도
manualset foldmethod=manual수동으로 zf 명령낮음 — 수동 작업 필요
indentset foldmethod=indent들여쓰기 수준보통 — 커널 탭 기반 코드에 적당
syntaxset foldmethod=syntax구문 규칙높음 — C 함수/블록 인식
markerset foldmethod=marker{{{/}}} 마커낮음 — 커널 스타일에 부적합
exprset foldmethod=expr사용자 표현식높음 — 커스텀 접기 규칙 가능
diffset foldmethod=diff차이 없는 부분특수 — vimdiff 전용

접기 명령

" 접기/펼치기 기본 명령
zo      " 커서 위치 접기 열기 (open)
zc      " 커서 위치 접기 닫기 (close)
za      " 토글 (열려있으면 닫고, 닫혀있으면 열기)
zO      " 재귀적으로 모두 열기
zC      " 재귀적으로 모두 닫기
zA      " 재귀적 토글

" 전체 접기/펼치기
zR      " 전체 열기 (Reduce folding)
zM      " 전체 닫기 (More folding)
zr      " foldlevel 1 증가 (한 단계 더 열기)
zm      " foldlevel 1 감소 (한 단계 더 닫기)

" 접기 탐색
zj      " 다음 접기로 이동
zk      " 이전 접기로 이동
[z      " 현재 접기 시작으로 이동
]z      " 현재 접기 끝으로 이동

" 수동 접기 만들기
zf{motion}  " motion 범위 접기 생성 (예: zfip = 문단 접기)
zf10j       " 아래 10줄 접기
zd          " 커서 위치 접기 삭제
zE          " 모든 접기 삭제

" 커널 개발 추천 설정
set foldmethod=syntax   " C 구문 기반 접기
set foldlevel=99        " 시작 시 모두 펼쳐진 상태
set foldnestmax=3       " 최대 접기 깊이 제한

커널 소스를 위한 접기 설정

" 커널 C 소스에 최적화된 접기 설정

" 방법 1: syntax 기반 (권장)
autocmd FileType c setlocal foldmethod=syntax foldlevel=99 foldnestmax=4
" foldlevel=99: 시작 시 모두 펼침
" foldnestmax=4: 4단계까지만 접기 (과도한 중첩 방지)

" 방법 2: indent 기반 (빠름)
autocmd FileType c setlocal foldmethod=indent foldlevel=99

" 방법 3: 커스텀 expr (함수 단위만 접기)
function! CFoldExpr(lnum)
  let line = getline(a:lnum)
  " { 로만 구성된 줄 = 함수/블록 시작
  if line =~ '^\s*{$'
    return 'a1'
  elseif line =~ '^\s*}$'
    return 's1'
  endif
  return '='
endfunction
autocmd FileType c setlocal foldmethod=expr foldexpr=CFoldExpr(v:lnum)

" 접기 텍스트 커스터마이즈 (접혀있을 때 표시되는 텍스트)
function! KernelFoldText()
  let line = getline(v:foldstart)
  let lines_count = v:foldend - v:foldstart + 1
  return line . ' ... (' . lines_count . ' lines)'
endfunction
set foldtext=KernelFoldText()

" 유용한 접기 단축키 매핑
nnoremap <Leader>z0 :set foldlevel=0<CR>   " 모두 접기
nnoremap <Leader>z1 :set foldlevel=1<CR>   " 1단계까지 펼치기
nnoremap <Leader>z2 :set foldlevel=2<CR>   " 2단계까지 펼치기
nnoremap <Leader>z9 :set foldlevel=99<CR>  " 모두 펼치기
팁: zM으로 모든 함수를 접으면 파일의 전체 구조(함수 목록)를 한눈에 볼 수 있습니다. 원하는 함수에서 zo로 펼쳐 내용을 확인하세요. tagbar 플러그인과 함께 사용하면 더 편리합니다.

diff 모드

Vim의 diff 모드는 두 개 이상의 파일을 나란히 비교하고 변경 사항을 시각적으로 확인할 수 있게 합니다. Git 머지 충돌 해결이나 커널 패치(Patch) 리뷰에 매우 유용합니다.

vimdiff 3-way 머지 (Git 충돌 해결) LOCAL (내 브랜치) int old_func(void) { my_new_code(); return 0; } :diffget LO (do) BASE (공통 조상) int old_func(void) { original_code(); return 0; } (충돌 기준점) REMOTE (상대 브랜치) int old_func(void) { their_change(); return -1; } :diffget RE (dp) ]c / [c = 다음/이전 변경 | do = diffget | dp = diffput git mergetool --tool=vimdiff 으로 자동 실행

diff 모드 사용법

" diff 모드 시작
$ vimdiff file1.c file2.c           " 셸에서 두 파일 비교
$ vim -d file1.c file2.c            " 위와 동일
$ vimdiff file1.c file2.c file3.c   " 3파일 비교

" Vim 내에서 diff 시작/종료
:diffthis       " 현재 윈도우를 diff에 참여
:diffoff        " 현재 윈도우를 diff에서 제외
:diffoff!       " 모든 윈도우의 diff 해제
:diffupdate     " diff 하이라이트 갱신

" 변경 사항 탐색
]c              " 다음 변경 블록으로 이동
[c              " 이전 변경 블록으로 이동

" 변경 사항 적용
do              " diff obtain — 상대 윈도우의 내용을 현재로 가져오기
dp              " diff put — 현재 내용을 상대 윈도우로 보내기
:diffget        " do와 동일 (범위 지정 가능)
:diffput        " dp와 동일

" Git merge 충돌 해결 설정
" .gitconfig에 추가:
" [merge]
"     tool = vimdiff
" [mergetool "vimdiff"]
"     cmd = vim -d $LOCAL $BASE $REMOTE $MERGED -c '$wincmd w' -c 'wincmd J'

" diff 옵션
set diffopt+=vertical       " 수직 분할 (기본)
set diffopt+=iwhite         " 공백 차이 무시
set diffopt+=algorithm:patience  " patience diff 알고리즘
set diffopt+=indent-heuristic    " 들여쓰기 기반 휴리스틱

패치 리뷰에서의 diff 활용

" 커널 패치 리뷰 시 diff 활용

" 방법 1: 패치 파일을 직접 비교
$ vimdiff before.c after.c

" 방법 2: Git의 특정 커밋 간 비교
:Git diff HEAD~3..HEAD -- %
" → 현재 파일의 최근 3커밋 변경 내용

" 방법 3: 패치 파일을 Vim에서 보기
:e 0001-fix-some-bug.patch
:set filetype=diff
" → diff 구문 강조 자동 적용

" 방법 4: 두 브랜치의 파일 비교
:Gdiffsplit main:%
" → main 브랜치의 같은 파일과 비교

" diff 모드에서 유용한 설정
set diffopt+=context:5        " 변경 주변 5줄 표시 (기본 6)
set diffopt+=foldcolumn:1     " 접기 컬럼 1칸
set diffopt+=followwrap       " 줄바꿈 동기화
set diffopt+=closeoff         " diff 윈도우 닫으면 diff 해제

" 커널 패치 적용 테스트
:!git apply --check 0001-fix-some-bug.patch
" → 적용 가능 여부만 확인 (실제 적용 안 함)

" word diff (단어 단위 변경 표시)
:!git diff --word-diff HEAD~1
" → 줄 내에서 어떤 단어가 변경되었는지 상세 표시
팁: set diffopt+=algorithm:patience를 사용하면 커널 코드의 diff 품질이 크게 향상됩니다. patience 알고리즘은 함수 경계를 더 잘 인식하여 의미 있는 단위로 변경을 그룹화합니다. Vim 8.1+ 또는 Neovim에서 지원됩니다.

Quickfix / Location List

Quickfix는 Vim의 빌드-편집-빌드 사이클을 지원하는 핵심 기능입니다. 컴파일 오류, grep 검색 결과, lint 경고 등을 구조화된 목록으로 관리하며, 항목 간 빠른 이동을 가능하게 합니다. 커널 빌드 오류를 추적하는 데 특히 유용합니다.

Quickfix 워크플로: 빌드 → 수정 → 반복 1. 소스 생성 :make / :grep :vimgrep / :cexpr 2. 목록 확인 :copen / :clist 오류/매칭 목록 3. 탐색/수정 :cn / :cp 다음/이전 항목 4. 일괄 처리 :cdo / :cfdo 매칭별 명령 수정 후 :make 반복 Quickfix List (전역, 1개) :make, :grep, :vimgrep, :cexpr :copen, :cn, :cp, :cfirst, :clast, :cdo 모든 윈도우에서 공유 Location List (윈도우별, 각각) :lmake, :lgrep, :lvimgrep, :lexpr :lopen, :ln, :lp, :lfirst, :llast, :ldo 각 윈도우별 독립

Quickfix vs Location List

Vim은 두 가지 오류/결과 목록을 제공합니다. Quickfix는 전역(모든 윈도우 공유)이고, Location List는 각 윈도우별로 독립적입니다:

기능Quickfix (전역)Location List (윈도우별)
생성:make, :grep, :vimgrep:lmake, :lgrep, :lvimgrep
목록 열기:copen:lopen
목록 닫기:cclose:lclose
다음 항목:cn:ln
이전 항목:cp:lp
첫 항목:cfirst:lfirst
마지막 항목:clast:llast
항목별 명령:cdo:ldo
파일별 명령:cfdo:lfdo
이전 목록:colder:lolder
다음 목록:cnewer:lnewer
범위모든 윈도우에서 공유각 윈도우별 독립

일반적인 사용 패턴:

Quickfix 채우는 다양한 방법

" 1. :make — 빌드 오류
:set makeprg=make\ -j$(nproc)
:make
" → errorformat에 따라 오류 파싱 → quickfix

" 2. :grep — 외부 grep 도구
:set grepprg=rg\ --vimgrep
:grep 'kmalloc' kernel/
" → grepformat에 따라 결과 파싱 → quickfix

" 3. :vimgrep — Vim 내장 grep
:vimgrep /pattern/gj **/*.c
" g = 줄 내 다중 매칭, j = 자동 점프 안 함

" 4. :cexpr — 표현식으로 직접 채우기
:cexpr system('find . -name "*.ko"')
" → 외부 명령 결과를 quickfix에 로드

" 5. :cfile — 파일에서 로드
:cfile /tmp/build-errors.log
" → 파일 내용을 errorformat으로 파싱

" 6. :cgetbuffer — 버퍼에서 로드
:cgetbuffer
" → 현재 버퍼 내용을 quickfix에 로드

" 7. setqflist() — Vimscript에서 프로그래밍적으로
:call setqflist([
  \ {'filename': 'test.c', 'lnum': 10, 'text': 'error here'},
  \ {'filename': 'test.c', 'lnum': 20, 'text': 'warning here'},
  \ ])

Quickfix 사용법

" 커널 빌드와 Quickfix
:set makeprg=make\ -j$(nproc)\ 2>&1  " make 명령 설정
:make                                  " 빌드 실행 → 오류 시 quickfix 채움
:make drivers/net/ethernet/            " 특정 디렉터리만 빌드

" Quickfix 탐색
:copen          " quickfix 윈도우 열기
:cclose         " quickfix 윈도우 닫기
:cn             " 다음 오류/매칭 (cnext)
:cp             " 이전 오류/매칭 (cprevious)
:cfirst         " 첫 번째 항목
:clast          " 마지막 항목
:cc 5           " 5번 항목으로 이동
:clist          " 전체 목록 표시

" Quickfix 히스토리
:colder         " 이전 quickfix 목록
:cnewer         " 다음 quickfix 목록
" (최대 10개의 quickfix 목록 이력 유지)

" grep으로 quickfix 채우기
:grep -rn "schedule_timeout" kernel/   " 외부 grep 사용
:vimgrep /pattern/gj **/*.c           " Vim 내장 grep
" g = 줄 내 다중 매칭, j = 첫 번째로 자동 점프 안 함

" 일괄 수정
:cdo s/old/new/g | update   " 모든 quickfix 항목에서 치환 + 저장
:cfdo %s/old/new/g | update " 모든 quickfix 파일에서 치환 + 저장

" errorformat 설정 (커널 빌드 출력 파싱)
set errorformat=%f:%l:%c:\ %t%*[^:]:%m   " GCC 표준 형식
set errorformat+=%f:%l:\ %m               " 간단한 형식
팁: :grep 대신 :grep!을 사용하면 첫 번째 매칭으로 자동 점프하지 않습니다. grepprgrg --vimgrep(ripgrep)이나 ag --vimgrep(silver searcher)으로 설정하면 대규모 커널 소스에서 검색 속도가 크게 향상됩니다.

플러그인 관리

Vim/Neovim은 플러그인을 통해 기능을 무한히 확장할 수 있습니다. 커널 개발에 유용한 핵심 플러그인들과 관리 도구를 살펴봅니다.

플러그인 관리 방식 비교 Native Packages (Vim 8+) ~/.vim/pack/{name}/start/ ~/.vim/pack/{name}/opt/ start/ = 자동 로드 opt/ = :packadd로 수동 외부 도구 불필요 vim-plug Plug 'tpope/vim-fugitive' Plug 'junegunn/fzf.vim' :PlugInstall, :PlugUpdate 병렬 설치, 지연 로딩 Vim/Neovim 모두 지원 lazy.nvim (Neovim 전용) { "nvim-treesitter/..." } { "neovim/nvim-lspconfig" } :Lazy, 자동 지연 로딩 lockfile, UI 대시보드 Lua 기반 최신 생태계 커널 개발 필수 플러그인 vim-fugitive (Git) | tagbar (코드 구조) fzf.vim (퍼지 검색) | cscope_maps (탐색) ale/nvim-lint (정적 분석) | vim-linux-coding-style nvim-lspconfig + clangd (LSP) | nvim-treesitter 커널 checkpatch.pl 연동: ale_linters 또는 nvim-lint 커스텀 설정

vim-plug 설정 예시

" vim-plug 설치 (Vim)
" curl -fLo ~/.vim/autoload/plug.vim --create-dirs \
"   https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim

" ~/.vimrc에 추가
call plug#begin('~/.vim/plugged')

" 필수 플러그인
Plug 'tpope/vim-fugitive'           " Git 통합
Plug 'tpope/vim-surround'           " 괄호/따옴표 조작
Plug 'tpope/vim-commentary'         " 주석 토글 (gcc, gc{motion})
Plug 'tpope/vim-repeat'             " 플러그인 명령 . 반복 지원
Plug 'preservim/tagbar'             " 코드 구조 사이드바
Plug 'junegunn/fzf', { 'do': { -> fzf#install() } }
Plug 'junegunn/fzf.vim'             " 퍼지 파일/내용 검색
Plug 'airblade/vim-gitgutter'       " Git diff 표시 (줄 단위)
Plug 'dense-analysis/ale'           " 비동기 lint/fix

" 선택 플러그인
Plug 'vim-airline/vim-airline'       " 상태줄
Plug 'preservim/nerdtree'           " 파일 트리
Plug 'ludoviez/vim-linux-coding-style'  " 커널 코딩 스타일

call plug#end()

" 설치: :PlugInstall
" 업데이트: :PlugUpdate
" 삭제: Plug 줄 제거 후 :PlugClean

Native Packages (외부 도구 불필요)

# Vim 8+ Native Package 설치 (vim-plug 없이)

# 디렉터리 구조:
# ~/.vim/pack/{group}/start/{plugin}/   ← 자동 로드
# ~/.vim/pack/{group}/opt/{plugin}/     ← :packadd로 수동 로드

# 예: vim-fugitive 설치
mkdir -p ~/.vim/pack/plugins/start
cd ~/.vim/pack/plugins/start
git clone https://github.com/tpope/vim-fugitive.git

# 예: termdebug는 opt에 이미 설치됨 (Vim 내장)
:packadd termdebug      " 필요할 때만 로드

# 플러그인 업데이트 (수동)
cd ~/.vim/pack/plugins/start/vim-fugitive && git pull

# 플러그인 제거
rm -rf ~/.vim/pack/plugins/start/vim-fugitive

# 도움말 태그 갱신
:helptags ALL

ALE + checkpatch.pl 연동

" ALE 기본 설정 (커널 개발용)
let g:ale_linters = {
\   'c': ['cc', 'clangtidy'],
\   'python': ['pylint'],
\}

let g:ale_fixers = {
\   'c': ['clang-format'],
\   'python': ['black'],
\   '*': ['remove_trailing_lines', 'trim_whitespace'],
\}

" ALE 표시 설정
let g:ale_sign_error = '>>'
let g:ale_sign_warning = '--'
let g:ale_echo_msg_format = '[%linter%] %s [%severity%]'

" ALE 탐색
nmap <Leader>an <Plug>(ale_next_wrap)
nmap <Leader>ap <Plug>(ale_previous_wrap)
nmap <Leader>af <Plug>(ale_fix)

" 커널 C 파일에서 ALE 비활성화 (checkpatch와 충돌 방지)
" 커널은 독자적 코딩 스타일을 사용하므로 일반 lint가 부적합
let g:ale_pattern_options = {
\   '.*/linux.*/.*\.c$': {
\       'ale_linters': ['cc'],
\       'ale_fixers': [],
\   },
\}

" ALE 대신 수동 checkpatch 단축키
augroup KernelCheckpatch
  autocmd!
  autocmd BufRead */linux*/*.c
    \ nnoremap <buffer> <Leader>cp :!scripts/checkpatch.pl --file %<CR>
augroup END

커널 개발 필수 플러그인 상세

플러그인기능핵심 명령
vim-fugitiveGit 통합 (blame, diff, log, status):Git, :Gdiffsplit, :Gblame
fzf.vim퍼지 검색 (파일, 내용, 버퍼, 태그):Files, :Rg, :Buffers, :Tags
tagbar코드 구조 사이드바 (함수, 변수, 매크로):TagbarToggle
vim-surround괄호/따옴표 추가/변경/삭제cs"', ds", ysiw)
vim-commentary주석 토글gcc (줄), gc{motion}
ale비동기 lint + fix (checkpatch 연동 가능):ALELint, :ALEFix
vim-gitgutter줄 단위 Git 변경 표시]c, [c, :GitGutterStageHunk

vimrc 설정

Vim의 동작은 ~/.vimrc(Neovim은 ~/.config/nvim/init.vim 또는 init.lua) 파일로 세밀하게 제어할 수 있습니다. 설정 파일의 로딩 순서와 핵심 옵션들을 살펴봅니다.

Vim 설정 파일 로딩 순서 1. 시스템 vimrc /etc/vim/vimrc /usr/share/vim/... 2. 사용자 vimrc ~/.vimrc ~/.vim/vimrc 3. 플러그인 ~/.vim/plugin/ pack/*/start/*/plugin/ 4. ftplugin filetype 감지 후 ftplugin/c.vim 마지막: after/ 디렉터리 — ~/.vim/after/plugin/, ~/.vim/after/ftplugin/ (플러그인 설정 덮어쓰기용) Neovim: ~/.config/nvim/init.lua → lua/plugins/ → after/plugin/ 핵심 설정 카테고리 일반 옵션 (set ...) 키 매핑 (nnoremap, inoremap) 자동명령 (autocmd, augroup) Leader 키 (let mapleader = " ") 프로젝트별 설정 modeline (파일 내 설정 줄) .exrc / .vimrc (프로젝트 로컬) set exrc / set secure editorconfig (공통 표준)

범용 vimrc 기본 설정

" ============================================================
" 기본 설정
" ============================================================
set nocompatible            " Vi 호환 모드 해제 (Vim 기능 활성화)
filetype plugin indent on   " 파일 타입 감지 + 플러그인 + 들여쓰기
syntax enable               " 구문 강조

" 인코딩
set encoding=utf-8          " 내부 인코딩
set fileencoding=utf-8      " 파일 저장 인코딩
set fileencodings=utf-8,euc-kr,cp949  " 파일 읽기 인코딩 탐지 순서

" 표시
set number                  " 줄 번호 표시
set relativenumber          " 상대 줄 번호 (이동 효율)
set cursorline              " 현재 줄 강조
set showmatch               " 대응 괄호 하이라이트
set signcolumn=yes          " 사인 컬럼 항상 표시 (git, lint)
set scrolloff=8             " 커서 주변 최소 8줄 여백
set sidescrolloff=8         " 좌우 스크롤 여백
set wrap                    " 줄 바꿈 표시
set linebreak               " 단어 단위로 줄 바꿈

" 검색
set hlsearch                " 검색 결과 하이라이트
set incsearch               " 실시간 검색
set ignorecase              " 대소문자 무시
set smartcase               " 대문자 입력 시 구분

" 들여쓰기 (기본값, 커널은 별도 설정)
set autoindent              " 자동 들여쓰기
set smartindent             " 스마트 들여쓰기
set tabstop=4               " 탭 표시 너비
set shiftwidth=4            " 들여쓰기 너비
set expandtab               " 탭을 공백으로 변환

" 동작
set hidden                  " 버퍼 전환 시 저장 강제 안 함
set autoread                " 외부 변경 자동 반영
set backspace=indent,eol,start  " 백스페이스 자유 동작
set mouse=a                 " 마우스 지원
set clipboard=unnamedplus   " 시스템 클립보드 기본 사용
set updatetime=300          " CursorHold 이벤트 지연 (ms)
set timeoutlen=500          " 매핑 대기 시간 (ms)

" 파일
set noswapfile              " 스왑 파일 비활성화
set undofile                " 영구 undo (파일 닫았다 열어도 undo 유지)
set undodir=~/.vim/undodir  " undo 파일 저장 위치
set backup                  " 백업 파일 생성
set backupdir=~/.vim/backup " 백업 파일 위치

" 명령줄
set wildmenu                " 명령줄 자동완성 메뉴
set wildmode=longest:full,full  " 자동완성 모드
set wildignore=*.o,*.ko,*.mod,*.cmd,*.d  " 자동완성 제외 패턴

" 상태줄
set laststatus=2            " 항상 상태줄 표시
set showcmd                 " 입력 중인 명령 표시
set showmode                " 현재 모드 표시

키 매핑 설정

" ============================================================
" 키 매핑
" ============================================================
let mapleader = " "         " Leader 키를 Space로 설정

" 검색 하이라이트 해제
nnoremap <Leader>h :nohlsearch<CR>

" 버퍼 탐색
nnoremap <Leader>bn :bnext<CR>
nnoremap <Leader>bp :bprevious<CR>
nnoremap <Leader>bd :bdelete<CR>
nnoremap <Leader>bl :ls<CR>

" 윈도우 이동 단축키
nnoremap <C-h> <C-w>h
nnoremap <C-j> <C-w>j
nnoremap <C-k> <C-w>k
nnoremap <C-l> <C-w>l

" 비주얼 모드에서 선택 영역 이동
vnoremap J :m '>+1<CR>gv=gv
vnoremap K :m '<-2<CR>gv=gv

" 줄 합치기 시 커서 위치 유지
nnoremap J mzJ`z

" 검색 시 화면 중앙 유지
nnoremap n nzzzv
nnoremap N Nzzzv

" 시스템 클립보드 복사/붙여넣기
nnoremap <Leader>y "+y
vnoremap <Leader>y "+y
nnoremap <Leader>p "+p

" 현재 파일 경로 복사
nnoremap <Leader>fp :let @+=expand('%:p')<CR>:echo expand('%:p')<CR>

" 빠른 저장/종료
nnoremap <Leader>w :w<CR>
nnoremap <Leader>q :q<CR>
nnoremap <Leader>x :x<CR>

파일타입별 설정 (ftplugin)

" 파일타입별 설정은 ftplugin 디렉터리에 저장합니다
" ~/.vim/ftplugin/c.vim (C 파일 설정)
setlocal tabstop=8
setlocal shiftwidth=8
setlocal noexpandtab
setlocal textwidth=80
setlocal colorcolumn=81
setlocal cindent
setlocal cinoptions=:0,l1,t0,g0,(0
" c.vim은 C 파일을 열 때만 자동으로 로드됩니다

" ~/.vim/ftplugin/python.vim (Python 파일 설정)
setlocal tabstop=4
setlocal shiftwidth=4
setlocal expandtab
setlocal textwidth=79
setlocal colorcolumn=80

" ~/.vim/ftplugin/make.vim (Makefile 설정)
setlocal noexpandtab
setlocal tabstop=8
setlocal shiftwidth=8

" ~/.vim/ftplugin/gitcommit.vim (Git 커밋 메시지)
setlocal textwidth=72
setlocal spell spelllang=en
setlocal colorcolumn=51,73

" ~/.vim/ftplugin/kconfig.vim (Kconfig 파일)
setlocal tabstop=8
setlocal shiftwidth=8
setlocal noexpandtab

" after 디렉터리로 플러그인 설정 덮어쓰기
" ~/.vim/after/ftplugin/c.vim
" → 플러그인이 설정한 C 파일 옵션을 재정의
setlocal formatoptions-=o    " 'o' 키로 주석 자동 계속 비활성화

상태줄 커스터마이즈

" 커스텀 상태줄 (statusline)
" 플러그인 없이 정보가 풍부한 상태줄 만들기
set laststatus=2
set statusline=
set statusline+=%#PmenuSel#
set statusline+=\ %{toupper(mode())}    " 현재 모드
set statusline+=\ %#LineNr#
set statusline+=\ %f                     " 파일 경로
set statusline+=%m                       " 수정 표시 [+]
set statusline+=%r                       " 읽기 전용 표시
set statusline+=%=                       " 오른쪽 정렬
set statusline+=\ %{&filetype}           " 파일 타입
set statusline+=\ %{&fileencoding?&fileencoding:&encoding}  " 인코딩
set statusline+=\ %{&fileformat}         " 줄바꿈 형식 (unix/dos)
set statusline+=\ %l:%c                  " 줄:열
set statusline+=\ %p%%                   " 파일 위치 %
set statusline+=\ [%L]                   " 전체 줄 수

" 모드별 상태줄 색상 변경
autocmd InsertEnter * hi StatusLine guibg=#005f00 ctermbg=22
autocmd InsertLeave * hi StatusLine guibg=#1c1c1c ctermbg=234

" 또는 vim-airline / lualine.nvim 플러그인 사용 (더 예쁨)
" vim-airline 기본 설정:
let g:airline#extensions#tabline#enabled = 1   " 탭라인 표시
let g:airline#extensions#branch#enabled = 1    " Git 브랜치 표시
let g:airline_powerline_fonts = 1              " Powerline 폰트

색상 스킴과 터미널 설정

" 색상 스킴 설정
set background=dark
colorscheme desert          " Vim 내장 색상 스킴

" 추천 색상 스킴 (플러그인 설치 필요)
" gruvbox — 따뜻한 레트로 스타일
" tokyonight — 모던 다크 스타일
" catppuccin — 파스텔 톤
" everforest — 눈에 편한 녹색 톤
" kanagawa — 일본 화풍 스타일 (Neovim)

" True Color (24비트 색상) 활성화
if has('termguicolors')
  set termguicolors
  " tmux 환경에서 필요한 설정
  let &t_8f = "\<Esc>[38;2;%lu;%lu;%lum"
  let &t_8b = "\<Esc>[48;2;%lu;%lu;%lum"
endif

" 커서 모양 변경 (터미널별)
" Insert 모드: 세로선 | Normal 모드: 블록 | Replace 모드: 밑줄
let &t_SI = "\<Esc>[6 q"    " Insert: 세로선
let &t_SR = "\<Esc>[4 q"    " Replace: 밑줄
let &t_EI = "\<Esc>[2 q"    " Normal: 블록

" 투명 배경 (터미널 배경 사용)
highlight Normal guibg=NONE ctermbg=NONE
highlight EndOfBuffer guibg=NONE ctermbg=NONE

자동명령(autocommand)

" ============================================================
" 자동명령
" ============================================================
augroup MyAutoCmd
  autocmd!
  " 파일 열 때 마지막 편집 위치로 복귀
  autocmd BufReadPost *
    \ if line("'\"") > 0 && line("'\"") <= line("$") |
    \   exe "normal! g`\"" |
    \ endif

  " 저장 시 trailing whitespace 제거
  autocmd BufWritePre *.c,*.h,*.py :%s/\s\+$//e

  " 파일 타입별 설정
  autocmd FileType c setlocal tabstop=8 shiftwidth=8 noexpandtab
  autocmd FileType python setlocal tabstop=4 shiftwidth=4 expandtab
  autocmd FileType make setlocal noexpandtab

  " 커널 소스 자동 인식
  autocmd BufRead,BufNewFile */linux*/*.c setlocal tabstop=8 shiftwidth=8 noexpandtab
  autocmd BufRead,BufNewFile */linux*/*.h setlocal tabstop=8 shiftwidth=8 noexpandtab

  " quickfix 자동 열기
  autocmd QuickFixCmdPost [^l]* cwindow
  autocmd QuickFixCmdPost l* lwindow
augroup END
참고: modeline을 사용하면 파일 단위로 설정을 지정할 수 있습니다. 파일의 첫 줄이나 마지막 줄에 /* vim: set ts=8 sw=8 noet: */를 추가하면 해당 파일을 열 때 자동으로 적용됩니다. 커널 소스에도 일부 파일에 modeline이 포함되어 있습니다.

커널 개발 특화 설정

리눅스 커널은 독자적인 코딩 스타일(Documentation/process/coding-style.rst)을 사용합니다. Vim을 커널 개발에 최적화하려면 들여쓰기 설정, cscope/ctags 연동, checkpatch 통합 등이 필요합니다.

cscope / ctags 커널 소스 탐색 워크플로 1. 데이터베이스 생성 make cscope make tags cscope.out + tags 파일 생성 2. Vim에서 로드 :cs add cscope.out set tags=./tags;/ 자동 로드 설정 가능 3. 소스 탐색 Ctrl-] (정의로 점프) :cs find g/s/c/... Ctrl-T (이전으로 복귀) cscope 검색 유형 (:cs find {type} {symbol}) s = 심볼 사용처 | g = 정의 위치 c = 호출하는 곳 | d = 호출되는 함수 t = 텍스트 검색 | e = egrep 패턴 f = 파일 찾기 | i = #include하는 파일 ctags 정의 위치 점프 (Ctrl-] / Ctrl-T) 빠른 생성, 가벼움, 태그 완성 지원 cscope 호출자/피호출자/참조 등 양방향 탐색 데이터베이스 큼, 전체 관계 분석 가능

커널 코딩 스타일 설정

" 리눅스 커널 코딩 스타일 (탭 8, 탭 문자 사용)
" ~/.vim/after/ftplugin/c.vim 또는 autocmd로 적용
augroup LinuxKernelStyle
  autocmd!
  " 커널 소스 디렉터리 감지
  autocmd BufRead,BufNewFile */linux*/*.{c,h}
    \ setlocal tabstop=8 |
    \ setlocal shiftwidth=8 |
    \ setlocal softtabstop=8 |
    \ setlocal noexpandtab |
    \ setlocal textwidth=80 |
    \ setlocal colorcolumn=81 |
    \ setlocal cindent |
    \ setlocal cinoptions=:0,l1,t0,g0,(0 |
    \ setlocal formatoptions=tcqlron
augroup END

" cinoptions 설명:
"  :0  — case 레이블 들여쓰기 없음
"  l1  — case 안 중괄호 정렬
"  t0  — 함수 반환형 들여쓰기 없음
"  g0  — C++ public/private 들여쓰기 없음
"  (0  — 괄호 안 연속줄 괄호에 정렬

cscope 데이터베이스 생성과 사용

# 커널 소스 디렉터리에서 cscope 데이터베이스 생성
cd /usr/src/linux

# 방법 1: make 타깃 사용 (권장)
make cscope
make tags

# 방법 2: 수동 생성 (아키텍처 제한)
find . -name "*.c" -o -name "*.h" -o -name "*.S" \
    -not -path "./arch/*" -o \
    -path "./arch/x86/*" -o \
    -path "./arch/arm64/*" | \
    sort > cscope.files
cscope -b -q -k    # -b: build only, -q: inverted index, -k: kernel mode

# ctags 생성 (Universal Ctags 권장)
ctags -R --fields=+lS --extras=+q \
    --exclude=.git --exclude=Documentation \
    --languages=C,C++ .
" vimrc에 cscope 자동 로드 설정
if has("cscope")
  set cscopetag           " :tag 명령이 cscope도 검색
  set csto=0              " cscope를 ctags보다 먼저 검색
  set cscopeverbose       " 데이터베이스 추가 시 메시지 표시

  " cscope 데이터베이스 자동 로드
  if filereadable("cscope.out")
    cs add cscope.out
  elseif $CSCOPE_DB != ""
    cs add $CSCOPE_DB
  endif

  " cscope 단축키 설정
  " s: 심볼 참조   g: 정의    c: 호출하는 곳
  " t: 텍스트 검색  e: egrep   f: 파일 찾기
  " i: include     d: 호출되는 함수
  nmap <C-\>s :cs find s <C-R>=expand("<cword>")<CR><CR>
  nmap <C-\>g :cs find g <C-R>=expand("<cword>")<CR><CR>
  nmap <C-\>c :cs find c <C-R>=expand("<cword>")<CR><CR>
  nmap <C-\>t :cs find t <C-R>=expand("<cword>")<CR><CR>
  nmap <C-\>e :cs find e <C-R>=expand("<cword>")<CR><CR>
  nmap <C-\>f :cs find f <C-R>=expand("<cfile>")<CR><CR>
  nmap <C-\>i :cs find i ^<C-R>=expand("<cfile>")<CR>$<CR>
  nmap <C-\>d :cs find d <C-R>=expand("<cword>")<CR><CR>

  " 결과를 수평 분할로 열기
  nmap <C-\><C-\>s :scs find s <C-R>=expand("<cword>")<CR><CR>
  nmap <C-\><C-\>g :scs find g <C-R>=expand("<cword>")<CR><CR>
endif

" checkpatch.pl 통합
" ALE 설정
let g:ale_linters = {
\   'c': ['checkpatch', 'cc'],
\}
" 또는 직접 명령
command! CheckPatch !scripts/checkpatch.pl --file %

" scripts/Lindent (커널 들여쓰기 도구) 연동
command! Lindent !scripts/Lindent %
주의: 커널 소스에서 expandtab을 사용하면 안 됩니다! 커널 코딩 스타일은 실제 탭 문자(tabstop=8)를 요구합니다. :set list로 탭이 제대로 표시되는지 확인하세요. 일부 파일(Kconfig, Python 스크립트 등)은 다른 규칙을 사용하므로 filetype별 설정이 필요합니다.

EditorConfig와 커널

" EditorConfig는 에디터 간 공통 설정 포맷
" 리눅스 커널 소스 루트의 .editorconfig:
" [*]
" charset = utf-8
" end_of_line = lf
" trim_trailing_whitespace = true
" insert_final_newline = true
"
" [*.{c,h}]
" indent_style = tab
" indent_size = 8
" tab_width = 8
"
" [*.py]
" indent_style = space
" indent_size = 4
"
" [Kconfig*]
" indent_style = tab
" indent_size = 8

" Vim에서 EditorConfig 사용
" 방법 1: editorconfig-vim 플러그인
Plug 'editorconfig/editorconfig-vim'

" 방법 2: Neovim 0.9+ 내장 지원
" init.lua에서:
vim.g.editorconfig = true    -- 기본 활성화됨

" EditorConfig가 있으면 vimrc의 파일타입별 설정을
" 덮어쓸 수 있으므로, 우선순위에 주의하세요

커널 코드 스니펫과 템플릿

" 자주 사용하는 커널 코드 패턴을 약어(abbreviation)로 등록

" C 파일용 약어 (~/.vim/ftplugin/c.vim에 추가)
" 모듈 초기화/정리 템플릿
iabbrev modinit static int __init mymod_init(void)<CR>{<CR><Tab>return 0;<CR>}<CR>module_init(mymod_init);
iabbrev modexit static void __exit mymod_exit(void)<CR>{<CR><CR>}<CR>module_exit(mymod_exit);

" 에러 처리 패턴
iabbrev errgoto if (ret) {<CR><Tab>goto err;<CR>}
iabbrev erret if (ret)<CR><Tab>return ret;

" printk 패턴
iabbrev pri pr_info("%s: \n", __func__);
iabbrev pre pr_err("%s: error %d\n", __func__, ret);
iabbrev prd pr_debug("%s: \n", __func__);

" 뮤텍스 패턴
iabbrev mutlock mutex_lock(&lock);<CR>/* critical section */<CR>mutex_unlock(&lock);

" 약어 vs 매핑 차이:
" 약어(iabbrev): 단어 입력 후 공백/Enter 시 확장
" 매핑(inoremap): 키 입력 즉시 실행
" 약어가 코드 템플릿에 더 자연스러움

" UltiSnips/LuaSnip (더 강력한 스니펫 시스템)
" Tab으로 확장, 플레이스홀더 이동, 동적 내용
" 예: for<Tab> → 전체 for 루프 생성, Tab으로 변수명/조건/본문 이동

공백/탭 관리와 시각화

" 보이지 않는 문자 표시
set list                    " 불가시 문자 표시 활성화
set listchars=tab:»·        " 탭: » 로 시작, · 로 채움
set listchars+=trail:·      " 줄 끝 공백: ·
set listchars+=eol:¬        " 줄 끝: ¬
set listchars+=extends:→    " 오른쪽 잘림 표시
set listchars+=precedes:←   " 왼쪽 잘림 표시
set listchars+=nbsp:+       " 비분리 공백: +
set listchars+=space:·      " 일반 공백: · (너무 많으면 지저분)

" trailing whitespace 제거
" 저장 시 자동 제거 (커널 코딩 스타일 준수)
autocmd BufWritePre *.c,*.h :%s/\s\+$//e

" 수동 제거
:%s/\s\+$//e

" 탭↔공백 변환
:set expandtab | retab     " 탭 → 공백
:set noexpandtab | retab!  " 공백 → 탭

" 혼합 들여쓰기 찾기 (커널에서 중요)
/\v^(\t* +\t)              " 탭 후 공백 후 탭 = 잘못된 들여쓰기
/\v^ +\t                   " 공백으로 시작 후 탭 = 잘못된 들여쓰기

" 올바른 커널 들여쓰기: 탭만 사용 (8칸 정지점)
" 80칸 안에서의 정렬은 탭+공백 조합 허용

checkpatch.pl 통합 상세

" checkpatch.pl을 ALE linter로 설정
" ~/.vim/ale_linters/c/checkpatch.vim 생성:
call ale#linter#Define('c', {
\   'name': 'checkpatch',
\   'executable': 'perl',
\   'command': 'perl scripts/checkpatch.pl --no-tree --file %s',
\   'callback': 'ale_linters#c#checkpatch#Handle',
\   'lint_file': 1,
\})

function! ale_linters#c#checkpatch#Handle(buffer, lines) abort
  let l:output = []
  for l:line in a:lines
    let l:match = matchlist(l:line,
      \ '\v^(WARNING|ERROR|CHECK):\s*(.*)')
    if !empty(l:match)
      call add(l:output, {
      \   'type': l:match[1] ==# 'ERROR' ? 'E' : 'W',
      \   'text': l:match[2],
      \})
    endif
  endfor
  return l:output
endfunction

" 수동 checkpatch 실행
:!scripts/checkpatch.pl --file %
" → 현재 파일의 코딩 스타일 위반 사항 표시

" 커밋에 대해 checkpatch 실행
:!git diff HEAD~1 | scripts/checkpatch.pl -
" → 마지막 커밋의 패치에 대해 검사

" format-patch 결과에 대해 실행
:!scripts/checkpatch.pl 0001-*.patch

clangd와 compile_commands.json

# 커널 소스에서 compile_commands.json 생성
# (clangd, ccls 등 LSP 서버가 이 파일을 읽어 코드 분석)

# 방법 1: 커널 내장 스크립트 (권장)
make defconfig              # 또는 원하는 config
make -j$(nproc)             # 전체 빌드 (컴파일 명령 기록)
scripts/clang-tools/gen_compile_commands.py
# → compile_commands.json 생성됨

# 방법 2: bear 사용
bear -- make -j$(nproc)
# → compile_commands.json 자동 생성

# 방법 3: compiledb 사용
compiledb make -j$(nproc)

# .clangd 설정 파일 (커널 소스 루트에 생성)
# CompileFlags:
#   Add:
#     - -Wno-unknown-warning-option
#     - -Wno-unused-command-line-argument
#   Remove:
#     - -mno-fp-ret-in-387
#     - -mpreferred-stack-boundary=*
#     - -mindirect-branch=*
#     - -mindirect-branch-register
#     - -fno-allow-store-data-races
#     - -fconserve-stack
" Neovim에서 clangd 상태 확인
:LspInfo                " LSP 서버 상태 확인
:LspLog                 " LSP 로그 확인
:checkhealth lsp        " LSP 건강 검사

" clangd가 제공하는 기능 (커널 코드에서)
" gd          — 정의로 점프 (매크로 전개까지 추적)
" gr          — 참조 목록 (cscope보다 정확)
" K           — 함수 시그니처, 문서 호버
" leader+rn   — 심볼 이름 변경 (프로젝트 전체)
" leader+ca   — 코드 액션 (include 추가, 타입 캐스트 등)
" [d ]d       — 진단(오류/경고) 간 이동

" 진단(diagnostic) 설정
vim.diagnostic.config({
  virtual_text = true,       -- 인라인 진단 텍스트
  signs = true,              -- 사인 컬럼 아이콘
  underline = true,          -- 밑줄
  update_in_insert = false,  -- Insert 모드에서 업데이트 안 함
  severity_sort = true,      -- 심각도 순 정렬
})
주의: 커널 소스에서 clangd를 사용하려면 반드시 compile_commands.json이 필요합니다. 이 파일이 없으면 clangd가 include 경로를 찾지 못해 대부분의 헤더에서 오류가 발생합니다. 또한 커널은 GCC 전용 확장을 많이 사용하므로 .clangd 파일에서 일부 경고를 억제해야 합니다.

정규식

Vim의 정규식은 PCRE(Perl Compatible Regular Expressions)와 문법이 다릅니다. 특히 메타문자의 이스케이프 규칙이 다르므로 주의가 필요합니다. \v(very magic) 모드를 사용하면 PCRE에 가까운 문법을 사용할 수 있습니다.

매직 레벨 비교

레벨접두사의미() 그루핑+ 반복| 분기
magic (기본)\m일부 메타문자 활성\(\)\+\|
nomagic\M대부분 리터럴\(\)\+\|
very magic\v대부분 메타문자 활성()+|
very nomagic\V거의 모든 것이 리터럴\(\)\+\|

Vim 정규식 원자(atom) 목록

패턴의미PCRE 동등물
.줄바꿈 제외 임의 문자.
\s, \S공백 / 비공백\s, \S
\d, \D숫자 / 비숫자\d, \D
\w, \W단어문자 / 비단어문자\w, \W
\a, \A알파벳 / 비알파벳[a-zA-Z]
\l, \u소문자 / 대문자[a-z], [A-Z]
\<, \>단어 시작 / 끝 경계\b
\zs, \ze매칭 시작/끝 위치 설정\K (시작만)
\{n,m}n~m회 반복{n,m}
\{-}비탐욕(non-greedy) 반복*?
\@=전방탐색 (lookahead)(?=...)
\@!부정 전방탐색(?!...)
\@<=후방탐색 (lookbehind)(?<=...)
\@<!부정 후방탐색(?<!...)
\1~\9역참조(Dereference)\1~\9
\n줄바꿈 문자\n

전방/후방 탐색 (Lookaround)

Vim의 lookaround 문법은 PCRE와 다릅니다. 순서가 반대이므로 주의가 필요합니다:

기능PCRE 문법Vim 문법 (magic)Vim 문법 (\v)
긍정 전방탐색(?=pattern)pattern\@=(pattern)@=
부정 전방탐색(?!pattern)pattern\@!(pattern)@!
긍정 후방탐색(?<=pattern)pattern\@<=(pattern)@<=
부정 후방탐색(?<!pattern)pattern\@<!(pattern)@<!
" Vim에서 lookaround는 패턴 뒤에 @= 등을 붙임 (PCRE와 반대 순서!)

" 예시: "int" 다음에 오는 식별자만 매칭 (int 자체는 매칭에 포함 안 함)
/\vint\s+\zs\w+
" \zs가 더 간단한 대안 (매칭 시작점 설정)

" 예시: static이 앞에 없는 함수 정의 찾기
/\v(static\s+)@<!int\s+\w+\s*\(
" → "int func(" 만 매칭 (static int func는 제외)

" 예시: EXPORT_SYMBOL 뒤에 _GPL이 없는 경우 찾기
/\vEXPORT_SYMBOL\((_GPL)@!
" → EXPORT_SYMBOL(foo)는 매칭, EXPORT_SYMBOL_GPL(foo)는 미매칭

" 예시: 세미콜론으로 끝나지 않는 줄 (주석 제외) 찾기
/\v^[^/]*[^;/\s]$
" 또는 더 정확하게:
/\v^(\s*\/[/*])@!.*[^;\s{}]$

여러 줄에 걸친 검색

" Vim에서 \n은 줄바꿈 문자를 의미합니다
" 여러 줄에 걸친 패턴 검색:

" 빈 줄 연속 찾기
/\n\n\n
" → 빈 줄이 2개 이상 연속되는 곳

" 함수 시그니처가 여러 줄에 걸친 경우 찾기
/\vint\s+\w+\(\_s*   " \_s = 줄바꿈 포함 공백
" \_.  = 줄바꿈 포함 임의 문자
" \_s  = 줄바꿈 포함 공백
" \_^  = 줄 시작
" \_$  = 줄 끝

" 여러 줄 주석 블록 찾기
/\/\*\_.\{-}\*\/
" \_. = 줄바꿈 포함 임의 문자
" \{-} = 비탐욕 반복

" 여러 줄 치환 (줄바꿈 제거)
:%s/\n\s*\*/  */g
" → 여러 줄 주석을 한 줄로 합치기

" 줄바꿈을 치환 결과에 삽입 (\r 사용!)
" 검색에서는 \n, 치환에서는 \r
:%s/;\s*/;\r/g
" → 세미콜론마다 줄바꿈 삽입

커널 코드 정규식 실전 예시

" very magic 모드 사용 (\v)
/\v(struct|union)\s+\w+\s*\{
" → struct 또는 union 정의 시작점 검색

" 함수 정의 찾기 (C 스타일)
/\v^\w+\s+\*?\w+\([^)]*\)\s*\{
" → 줄 시작에 반환타입 + 함수명 + 매개변수 + { 패턴

" printk 포맷 문자열에서 %d, %s, %x 등 찾기
/\v\%[-+0 #]*\d*\.?\d*[diouxXeEfFgGaAcspn]

" #include 경로 중 linux/ 하위만 찾기
/\v#include\s*[<"]linux\/[^>"]+[>"]

" 비탐욕 매칭으로 주석 내용만 추출
/\/\*\zs.\{-}\ze\*\//
" \zs: 매칭 시작점, \ze: 매칭 끝점
" .\{-}: 비탐욕 반복 (최소 매칭)

" 매칭 위치를 이동하지 않는 패턴 (assertion)
" BUG_ON 뒤에 조건이 없는 경우 찾기
/\vBUG_ON\(\s*\)
" WARN_ON 사용처 중 unlikely가 없는 경우
/\vWARN_ON\(.*\)\@<!unlikely

" 치환에서 캡처 그룹 활용
:%s/\v(\w+)_read\(([^)]*)\)/\1_read_iter(\2, \&iov)/g
" old_read(args) → old_read_iter(args, &iov)

" 대소문자 변환 치환
:%s/\v<(\w)/\u\1/g     " 각 단어 첫 글자 대문자
:%s/\v<(\w+)>/\L\1/g   " 전체 소문자
:%s/\v<(\w+)>/\U\1/g   " 전체 대문자

치환에서의 특수 문자

" 치환 대체 문자열에서의 특수 문자

" & — 전체 매칭 내용
:%s/foo/(&)/g           " foo → (foo)
:%s/\w\+/[&]/g          " 모든 단어를 [] 로 감싸기

" \0 — & 와 동일 (전체 매칭)
" \1, \2, ... — 캡처 그룹 역참조
:%s/\v(\w+)\s+(\w+)/\2 \1/g    " 두 단어 순서 뒤바꾸기

" \r — 줄바꿈 삽입 (치환에서는 \n이 아닌 \r!)
:%s/;/;\r/g             " 세미콜론 뒤에 줄바꿈

" \t — 탭 문자
" \\ — 리터럴 백슬래시

" \u, \U — 다음 문자/이후 전부 대문자
" \l, \L — 다음 문자/이후 전부 소문자
" \e, \E — 대소문자 변환 종료

" 커널 코드 실전: 변수명을 camelCase에서 snake_case로
:%s/\v([a-z])([A-Z])/\1_\l\2/g
" readPage → read_page
" writeSector → write_sector

" 수식을 사용한 동적 치환 (\=)
:%s/version \d\+/\='version ' . (submatch(0) =~ '\d\+' ? str2nr(matchstr(submatch(0), '\d\+')) + 1 : 0)/g
" → "version 3" → "version 4" (버전 번호 자동 증가)

" 줄 번호를 삽입하는 치환 (카운터 패턴)
let n = [0]
:%s/^/\=map(n, 'v:val + 1')[0] . '. '/
" → 1. 첫째 줄
" → 2. 둘째 줄
" → 3. 셋째 줄

Vimscript 기초

Vimscript(Vim script, VimL)은 Vim의 내장 스크립트 언어입니다. vimrc 설정에서부터 플러그인 개발까지 사용됩니다. Vim 9.0부터는 컴파일 가능한 Vim9script가 도입되었고, Neovim은 Lua를 기본 확장 언어로 사용합니다.

기본 문법

" 변수 스코프
let g:my_var = 1        " 전역 변수
let b:my_var = 1        " 버퍼 로컬
let w:my_var = 1        " 윈도우 로컬
let t:my_var = 1        " 탭 로컬
let s:my_var = 1        " 스크립트 로컬
let l:my_var = 1        " 함수 로컬 (함수 내에서만)
let v:count             " Vim 내장 변수 (읽기 전용)

" 환경 변수, 옵션, 레지스터 접근
echo $HOME              " 환경 변수
echo &tabstop           " 옵션 값 읽기
let &tabstop = 8        " 옵션 값 설정
echo @a                 " 레지스터 a 읽기
let @a = "text"         " 레지스터 a 설정

" 조건문
if v:version >= 800
  echo "Vim 8 이상"
elseif has('nvim')
  echo "Neovim"
else
  echo "구버전 Vim"
endif

" 반복문
for item in ['a', 'b', 'c']
  echo item
endfor

let i = 0
while i < 10
  let i += 1
endwhile

" 리스트와 딕셔너리
let mylist = [1, 2, 3, 'four']
let mydict = {'name': 'kernel', 'version': 6}
echo mylist[0]            " 1
echo mydict['name']       " kernel
echo mydict.name          " kernel (축약)

" 문자열 연결
let result = 'Hello' . ' ' . 'World'   " 마침표로 연결
echo "Line: " . line('.')               " 현재 줄 번호

함수 정의

" 함수 정의 (대문자로 시작하거나 s: 접두사)
function! KernelGrep(pattern) abort
  execute 'grep -rn ' . shellescape(a:pattern) . ' .'
  copen
endfunction

" 사용: :call KernelGrep('schedule_timeout')
" 또는 command 정의:
command! -nargs=1 KGrep call KernelGrep(<q-args>)
" 사용: :KGrep schedule_timeout

" 줄 번호를 표시하는 상태줄 함수
function! StatusLineInfo() abort
  return ' ' . expand('%:t') . ' [' . line('.') . '/' . line('$') . ']'
endfunction
set statusline=%{StatusLineInfo()}

" 현재 함수명을 표시하는 함수 (ctags 기반)
function! CurrentFunction() abort
  let l:funcname = tagbar#currenttag('%s', '', 'f')
  return l:funcname !=# '' ? l:funcname : '---'
endfunction

Vim9script 소개

" Vim9script (Vim 9.0+, 기존 Vimscript보다 빠름)
vim9script

# 주석은 # 사용 (기존 " 대신)
var name: string = "kernel"
var count: number = 42

# 타입 추론 가능
var items = ['a', 'b', 'c']

# 함수 정의 (간결한 문법)
def KernelGrep(pattern: string)
  execute $'grep -rn {shellescape(pattern)} .'
  copen
enddef

# 람다
var Double = (n: number): number => n * 2

autoload와 플러그인 구조

" autoload 함수 (지연 로딩)
" ~/.vim/autoload/kernel.vim 파일에:
function! kernel#grep(pattern) abort
  execute 'grep! -rn ' . shellescape(a:pattern) . ' .'
  copen
endfunction

" 사용: :call kernel#grep('schedule_timeout')
" → kernel.vim은 첫 호출 시에만 로드됨

" 플러그인 디렉터리 구조
" ~/.vim/
" ├── autoload/         ← 지연 로딩 함수
" │   └── kernel.vim
" ├── plugin/           ← 시작 시 자동 로드
" │   └── settings.vim
" ├── ftplugin/         ← 파일타입별 설정
" │   ├── c.vim         ← C 파일 설정
" │   └── python.vim    ← Python 파일 설정
" ├── after/            ← 플러그인 설정 덮어쓰기
" │   └── ftplugin/
" │       └── c.vim     ← C 파일 설정 (우선순위 높음)
" ├── syntax/           ← 커스텀 구문 정의
" ├── indent/           ← 커스텀 들여쓰기 규칙
" ├── colors/           ← 색상 스킴
" └── spell/            ← 맞춤법 사전

실전 Vimscript 예시

" 1. 현재 함수명을 상태줄에 표시 (ctags 기반)
function! CurrentCFunction() abort
  let l:line = line('.')
  let l:col = col('.')
  " 위로 이동하며 함수 정의 줄 찾기
  while l:line > 0
    let l:text = getline(l:line)
    if l:text =~ '^\w.*('
      " 함수명 추출
      let l:match = matchstr(l:text, '\w\+\ze\s*(')
      if !empty(l:match)
        return l:match
      endif
    endif
    let l:line -= 1
  endwhile
  return '---'
endfunction

" 2. trailing whitespace 하이라이트
highlight ExtraWhitespace ctermbg=red guibg=red
match ExtraWhitespace /\s\+$/
autocmd InsertEnter * match ExtraWhitespace /\s\+\%#\@<!$/
autocmd InsertLeave * match ExtraWhitespace /\s\+$/

" 3. 커밋 메시지 작성 도우미
autocmd FileType gitcommit setlocal textwidth=72
autocmd FileType gitcommit setlocal spell spelllang=en
autocmd FileType gitcommit setlocal colorcolumn=51,73
" 50문자 제목 / 72문자 본문 가이드라인

" 4. 80컬럼 초과 줄 강조
set colorcolumn=81
highlight ColorColumn ctermbg=darkgray guibg=#2d2d2d

" 5. 토글 함수
function! ToggleNumbers() abort
  if &relativenumber
    set norelativenumber number
  else
    set relativenumber
  endif
endfunction
nnoremap <Leader>n :call ToggleNumbers()<CR>
참고: Neovim에서는 Vimscript 대신 Lua를 사용하는 것이 권장됩니다. Lua는 실행 속도가 빠르고(LuaJIT), Neovim API에 직접 접근할 수 있습니다. 기존 Vimscript도 호환되므로 점진적으로 마이그레이션하면 됩니다.

터미널 통합

Vim 8.1+ / Neovim은 내장 터미널을 제공합니다. 에디터를 떠나지 않고 셸 명령을 실행하고, 커널 빌드 로그를 확인하고, git 작업을 수행할 수 있습니다.

터미널 기본 사용법

" 터미널 열기
:terminal               " 현재 윈도우에 터미널 열기
:terminal bash           " bash 셸로 터미널 열기
:vert terminal           " 수직 분할로 터미널 열기
:bot terminal            " 하단에 터미널 열기
:tab terminal            " 새 탭에 터미널 열기
:terminal ++rows=15      " 높이 15줄로 터미널 열기 (Vim)
:terminal ++curwin       " 현재 윈도우를 터미널로 변환

" 터미널 모드 전환 (Vim)
" Terminal-Job 모드: 기본 — 터미널에 키 입력 전달
" Terminal-Normal 모드: Ctrl-W N — Vim Normal 모드처럼 스크롤/복사
" Terminal-Normal에서 복귀: i 또는 a

" Neovim 터미널 모드 전환
" Terminal 모드 → Normal: Ctrl-\ Ctrl-N (또는 커스텀 매핑)
" Normal → Terminal: i

" 터미널 매핑 (Neovim)
tnoremap <Esc> <C-\><C-n>           " Esc로 Normal 복귀
tnoremap <C-h> <C-\><C-n><C-w>h     " 터미널에서 윈도우 이동
tnoremap <C-j> <C-\><C-n><C-w>j
tnoremap <C-k> <C-\><C-n><C-w>k
tnoremap <C-l> <C-\><C-n><C-w>l

커널 개발에서의 활용

" 하단 터미널에서 커널 빌드
:bot terminal ++rows=15
make -j$(nproc) 2>&1 | tee build.log

" 빌드 결과를 Vim으로 가져오기
:cfile build.log          " build.log를 quickfix로 파싱
:copen                    " quickfix 열기

" 터미널에서 make 후 자동으로 quickfix 채우기
" Vim의 :make가 이 역할을 하므로 보통 :make를 권장
:set makeprg=make\ -j$(nproc)
:make

" QEMU 실행과 GDB 디버깅을 나란히
:vert terminal
" (왼쪽 터미널에서 QEMU 실행)
" (오른쪽 편집기에서 :Termdebug vmlinux)
팁: Vim의 :terminal에서 Ctrl-W "를 누르면 Vim 레지스터 내용을 터미널에 붙여넣을 수 있습니다. 레지스터에 복사한 경로나 명령을 터미널에 직접 전달할 때 유용합니다.

터미널과 버퍼 간 데이터 전송

" 버퍼에서 터미널로 텍스트 전송

" Vim의 term_sendkeys()
" 현재 줄을 터미널로 전송하는 매핑
nnoremap <Leader>ts :call term_sendkeys(bufnr('%'), getline('.') . "\n")<CR>

" 비주얼 선택 영역을 터미널로 전송
vnoremap <Leader>ts :call SendToTerminal()<CR>
function! SendToTerminal() range
  let lines = getline(a:firstline, a:lastline)
  let term_buf = term_list()[0]  " 첫 번째 터미널 버퍼
  for line in lines
    call term_sendkeys(term_buf, line . "\n")
  endfor
endfunction

" Neovim에서의 터미널 제어
" vim.fn.chansend(job_id, data)
-- Neovim Lua: 터미널에 명령 전송
local function send_to_terminal(cmd)
  local terminals = vim.fn.getbufinfo({ buftype = 'terminal' })
  if #terminals > 0 then
    local chan = vim.b[terminals[1].bufnr].terminal_job_id
    vim.fn.chansend(chan, cmd .. '\n')
  end
end

vim.keymap.set('n', '<leader>tm', function()
  send_to_terminal('make -j$(nproc)')
end, { desc = "터미널에서 make 실행" })

터미널 기반 개발 워크플로

" 추천 터미널 레이아웃 1: 편집 + 빌드
" ┌──────────────────────────────┐
" │        소스 코드 편집         │
" │       (Normal buffer)        │
" ├──────────────────────────────┤
" │     터미널 (빌드/테스트)      │
" │       :bot terminal          │
" └──────────────────────────────┘

" 추천 터미널 레이아웃 2: 편집 + 로그 모니터링
" ┌─────────────────┬────────────┐
" │   소스 코드 편집  │   dmesg    │
" │   (main buffer)  │  (terminal)│
" │                  │ tail -f    │
" └─────────────────┴────────────┘

" 터미널에서 REPL 활용 (스크립트 테스트)
:terminal python3       " Python REPL
:terminal bash          " Bash 셸

" tmux와의 차이점:
" - Vim 터미널: 에디터와 직접 통합 (레지스터, 복사/붙여넣기)
" - tmux: 세션 유지, 더 유연한 레이아웃, 에디터 독립적
" - 둘을 함께 사용하는 것이 가장 효과적:
"   tmux 세션 안에서 Vim 실행, 필요시 Vim 터미널도 활용

" Vim 터미널에서 현재 편집 파일 참조
" Terminal-Normal 모드 (Ctrl-W N) → 텍스트 선택 → 복사
" → 편집 윈도우로 이동 → 붙여넣기
" 또는 Ctrl-W " {reg} 로 레지스터 내용을 터미널에 전달

Neovim 차이점

Neovim은 2014년 Vim에서 포크된 프로젝트로, 비동기 I/O, 내장 LSP 클라이언트, Treesitter 구문 분석, Lua 확장 등 현대적 기능을 제공합니다. 커널 개발에서 Neovim의 LSP + clangd 조합은 대규모 C 코드베이스 탐색에 매우 효과적입니다.

Vim vs Neovim 아키텍처 비교 Vim Vimscript / Vim9 if_python / if_lua 동기 이벤트 루프 (Vim 8+: 부분 비동기) job/channel (8.0+) terminal (8.1+) syntax highlighting (정규식 기반) ALE / coc.nvim (외부 LSP) packages (native), vim-plug Neovim Lua (LuaJIT) Vimscript 호환 libuv 비동기 이벤트 루프 내장 LSP 클라이언트 RPC API (msgpack) Treesitter (AST 기반 구문 분석) floating window / virtual text lazy.nvim, packer.nvim

Neovim LSP + clangd 설정 (커널 개발용)

" ~/.config/nvim/init.lua (Neovim Lua 설정)

-- lazy.nvim 플러그인 관리자 설정 (bootstrap)
local lazypath = vim.fn.stdpath("data") .. "/lazy/lazy.nvim"
if not vim.loop.fs_stat(lazypath) then
  vim.fn.system({
    "git", "clone", "--filter=blob:none",
    "https://github.com/folke/lazy.nvim.git",
    "--branch=stable", lazypath,
  })
end
vim.opt.rtp:prepend(lazypath)

require("lazy").setup({
  -- LSP 설정
  {
    "neovim/nvim-lspconfig",
    config = function()
      local lspconfig = require("lspconfig")

      -- clangd 설정 (커널 개발용)
      lspconfig.clangd.setup({
        cmd = {
          "clangd",
          "--background-index",
          "--clang-tidy",
          "--header-insertion=never",
          "--completion-style=detailed",
          "--function-arg-placeholders=false",
          "-j=4",
        },
        -- compile_commands.json이 필요
        -- 생성: scripts/clang-tools/gen_compile_commands.py
        root_dir = lspconfig.util.root_pattern(
          "compile_commands.json",
          ".clangd",
          "Makefile"
        ),
        on_attach = function(client, bufnr)
          local opts = { buffer = bufnr }
          vim.keymap.set("n", "gd", vim.lsp.buf.definition, opts)
          vim.keymap.set("n", "gr", vim.lsp.buf.references, opts)
          vim.keymap.set("n", "K", vim.lsp.buf.hover, opts)
          vim.keymap.set("n", "<leader>rn", vim.lsp.buf.rename, opts)
          vim.keymap.set("n", "<leader>ca", vim.lsp.buf.code_action, opts)
          vim.keymap.set("n", "[d", vim.diagnostic.goto_prev, opts)
          vim.keymap.set("n", "]d", vim.diagnostic.goto_next, opts)
        end,
      })
    end,
  },

  -- Treesitter (구문 분석)
  {
    "nvim-treesitter/nvim-treesitter",
    build = ":TSUpdate",
    config = function()
      require("nvim-treesitter.configs").setup({
        ensure_installed = { "c", "lua", "vim", "vimdoc", "make", "bash" },
        highlight = { enable = true },
        indent = { enable = true },
        textobjects = { enable = true },
      })
    end,
  },

  -- 자동완성
  {
    "hrsh7th/nvim-cmp",
    dependencies = {
      "hrsh7th/cmp-nvim-lsp",
      "hrsh7th/cmp-buffer",
      "hrsh7th/cmp-path",
    },
  },
})
# 커널 소스에서 compile_commands.json 생성
cd /usr/src/linux
make defconfig
make -j$(nproc)
scripts/clang-tools/gen_compile_commands.py

# 생성된 compile_commands.json을 clangd가 읽어
# 정확한 코드 분석/완성/진단을 제공합니다

Treesitter vs 정규식 하이라이팅

항목정규식 (Vim syntax)Treesitter (Neovim)
분석 방식줄 단위 정규식 매칭AST (추상 구문 트리) 구축
정확도복잡한 구문에서 오류 가능파서 기반으로 정확
성능큰 파일에서 느려질 수 있음증분 파싱으로 빠름
커널 C 코드매크로 내 코드 하이라이트 부정확매크로도 정확히 파싱
추가 기능없음텍스트 오브젝트, 선택적 접기, 범위 쿼리
참고: Vim에서 Neovim으로 마이그레이션할 때, 기존 ~/.vimrc는 대부분 호환됩니다. Neovim은 ~/.config/nvim/init.vim 또는 init.lua를 사용합니다. :checkhealth 명령으로 환경 상태를 확인할 수 있습니다.

Neovim Lua 설정 기초

-- ~/.config/nvim/init.lua 기본 설정

-- 기본 옵션 (vim.opt = set 동등)
vim.opt.number = true
vim.opt.relativenumber = true
vim.opt.tabstop = 8
vim.opt.shiftwidth = 8
vim.opt.expandtab = false         -- 커널 개발: 탭 사용
vim.opt.signcolumn = "yes"
vim.opt.scrolloff = 8
vim.opt.updatetime = 300
vim.opt.undofile = true
vim.opt.clipboard = "unnamedplus"
vim.opt.termguicolors = true
vim.opt.splitbelow = true
vim.opt.splitright = true

-- 전역 변수 (vim.g = let g:)
vim.g.mapleader = " "
vim.g.maplocalleader = ","

-- 키 매핑 (vim.keymap.set)
vim.keymap.set("n", "<leader>h", ":nohlsearch<CR>", { desc = "검색 하이라이트 해제" })
vim.keymap.set("n", "<C-h>", "<C-w>h", { desc = "왼쪽 윈도우" })
vim.keymap.set("n", "<C-j>", "<C-w>j", { desc = "아래 윈도우" })
vim.keymap.set("n", "<C-k>", "<C-w>k", { desc = "위 윈도우" })
vim.keymap.set("n", "<C-l>", "<C-w>l", { desc = "오른쪽 윈도우" })
vim.keymap.set("v", "J", ":m '>+1<CR>gv=gv", { desc = "선택 줄 아래로" })
vim.keymap.set("v", "K", ":m '<-2<CR>gv=gv", { desc = "선택 줄 위로" })
vim.keymap.set("n", "n", "nzzzv", { desc = "검색 다음 (중앙)" })
vim.keymap.set("n", "N", "Nzzzv", { desc = "검색 이전 (중앙)" })

-- 자동명령 (vim.api.nvim_create_autocmd)
local augroup = vim.api.nvim_create_augroup("KernelDev", { clear = true })

vim.api.nvim_create_autocmd("FileType", {
  group = augroup,
  pattern = "c",
  callback = function()
    vim.opt_local.tabstop = 8
    vim.opt_local.shiftwidth = 8
    vim.opt_local.expandtab = false
    vim.opt_local.textwidth = 80
    vim.opt_local.colorcolumn = "81"
  end,
})

vim.api.nvim_create_autocmd("BufReadPost", {
  group = augroup,
  callback = function()
    -- 마지막 편집 위치 복원
    local mark = vim.api.nvim_buf_get_mark(0, '"')
    if mark[1] > 0 and mark[1] <= vim.api.nvim_buf_line_count(0) then
      vim.api.nvim_win_set_cursor(0, mark)
    end
  end,
})

Neovim 고유 기능

-- Floating window (떠 있는 창)
-- LSP hover, 자동완성 문서, 알림 등에 사용
local buf = vim.api.nvim_create_buf(false, true)
vim.api.nvim_buf_set_lines(buf, 0, -1, false, {"Hello", "World"})
vim.api.nvim_open_win(buf, false, {
  relative = "cursor",
  width = 30,
  height = 5,
  row = 1,
  col = 0,
  style = "minimal",
  border = "rounded",
})

-- Virtual text (가상 텍스트 — 버퍼 변경 없이 텍스트 표시)
-- LSP 진단, Git blame 인라인, 타입 힌트 등에 사용
local ns = vim.api.nvim_create_namespace("my_namespace")
vim.api.nvim_buf_set_extmark(0, ns, 0, 0, {
  virt_text = {{"← 여기에 주의", "Comment"}},
  virt_text_pos = "eol",
})

-- 알림 (vim.notify)
vim.notify("빌드 완료!", vim.log.levels.INFO)
-- nvim-notify 플러그인으로 더 예쁜 알림 가능

-- 비동기 작업 (vim.loop / libuv)
-- Neovim은 libuv 기반으로 파일 I/O, 프로세스, 타이머 등을 비동기 처리
local timer = vim.loop.new_timer()
timer:start(5000, 0, vim.schedule_wrap(function()
  vim.notify("5초 경과!", vim.log.levels.WARN)
  timer:stop()
end))

Vim에서 Neovim으로 마이그레이션

항목VimNeovim
설정 파일~/.vimrc~/.config/nvim/init.lua
데이터 디렉터리~/.vim/~/.local/share/nvim/
상태 디렉터리~/.vim/~/.local/state/nvim/
캐시(Cache) 디렉터리~/.vim/~/.cache/nvim/
확장 언어Vimscript / Vim9Lua (+ Vimscript 호환)
플러그인 관리vim-plug, packageslazy.nvim, packer.nvim
LSPcoc.nvim (외부)내장 vim.lsp
구문 분석정규식 (syntax)Treesitter
건강 검사없음:checkhealth
Esc 대안Ctrl-[터미널: Ctrl-\ Ctrl-N
# Vim 설정을 Neovim에서 사용하기 (과도기용)
mkdir -p ~/.config/nvim
echo 'source ~/.vimrc' > ~/.config/nvim/init.vim
# → 기존 .vimrc를 그대로 사용

# Neovim 환경 확인
nvim +checkhealth

성능 최적화

시작 시간 분석

# Vim 시작 시간 측정
vim --startuptime /tmp/vim-startup.log +q
# 각 플러그인/스크립트의 로딩 시간이 기록됨

# Neovim 시작 시간
nvim --startuptime /tmp/nvim-startup.log +q

# 로그 분석 (느린 항목 찾기)
sort -k2 -n /tmp/vim-startup.log | tail -20

성능 최적화 팁

" 대용량 파일 처리 (커널 소스에서 가끔 발생)
" 1. 구문 강조 비활성화
:syntax off

" 2. 접기 비활성화
:set nofoldenable

" 3. 지연 재그리기
:set lazyredraw         " 매크로 실행 중 화면 갱신 안 함

" 4. 정규식 엔진 선택
:set regexpengine=1     " 구형 NFA 엔진 (일부 패턴에서 더 빠름)

" 5. 대용량 파일 자동 최적화 패턴
augroup LargeFile
  autocmd!
  autocmd BufReadPre * if getfsize(expand("%")) > 10 * 1024 * 1024
    \ | setlocal noswapfile bufhidden=unload undolevels=-1
    \ | syntax clear
    \ | setlocal nofoldenable
    \ | endif
augroup END

" 6. 플러그인 지연 로딩 (vim-plug)
Plug 'preservim/tagbar', { 'on': 'TagbarToggle' }  " 명령 사용 시 로드
Plug 'fatih/vim-go', { 'for': 'go' }               " Go 파일에서만 로드

" 7. matchparen 비활성화 (괄호 하이라이트)
" 큰 파일에서 성능 저하 원인이 될 수 있음
let g:loaded_matchparen = 1
" 또는: :NoMatchParen

" 8. 줄 번호 관련 최적화
" relativenumber는 매 커서 이동마다 재계산
" 필요 없으면: set norelativenumber

" 9. cursorline/cursorcolumn
" 활성화 시 성능 영향, 필요시만 사용
:set nocursorcolumn
팁: 커널의 drivers/gpu/drm/amd/display/dc/dml/ 같은 자동 생성 코드 파일은 수만 줄에 달합니다. 이런 파일을 열 때 vim -u NONE으로 설정 없이 열거나, :syntax off를 먼저 실행하면 훨씬 빠릅니다.

성능 프로파일링(Profiling) 실전

# 시작 시간 상세 분석
vim --startuptime /tmp/vim-startup.log +q

# 상위 20개 느린 항목 확인
sort -k2 -n /tmp/vim-startup.log | tail -20

# 플러그인 없이 시작 (문제 분리)
vim -u NONE           # 설정 없이 시작
vim --noplugin        # 플러그인 없이 시작
vim -u ~/.vim/minimal.vim  # 최소 설정으로 시작
" Vim 내부 프로파일링
:profile start /tmp/vim-profile.log
:profile func *
:profile file *
" (느린 동작 수행)
:profile stop
" → /tmp/vim-profile.log에서 함수별 실행 시간 확인

" Neovim 프로파일링
:lua require('plenary.profile').start('/tmp/nvim-profile.log')
" (느린 동작 수행)
:lua require('plenary.profile').stop()

" 구문 강조 성능 확인
:syntime on
" (파일 스크롤 등)
:syntime report
" → 느린 구문 규칙 확인
:syntime off

" 자동명령 성능 영향 확인
:verbose autocmd BufWritePre
" → 파일 저장 시 실행되는 자동명령 목록

대용량 파일 처리 전략

" 대용량 파일 최적화 자동설정 (개선된 버전)
augroup LargeFile
  autocmd!
  autocmd BufReadPre * call LargeFileCheck()
augroup END

function! LargeFileCheck() abort
  let l:size = getfsize(expand('%'))
  if l:size > 2 * 1024 * 1024       " 2MB 이상
    " 기본 최적화
    setlocal noswapfile
    setlocal bufhidden=unload
    setlocal undolevels=-1
    setlocal nofoldenable
    setlocal lazyredraw
    " 구문 강조 제한
    if l:size > 10 * 1024 * 1024    " 10MB 이상
      syntax clear
      setlocal nolist
      setlocal nocursorline
      setlocal nocursorcolumn
      echo "Large file mode: syntax highlighting disabled"
    else
      " 최대 구문 분석 줄 수 제한
      setlocal synmaxcol=200
      echo "Large file mode: synmaxcol=200"
    endif
  endif
endfunction

" 대용량 파일의 특정 부분만 편집
" 줄 범위 지정 읽기 (셸에서)
$ sed -n '1000,2000p' huge_file.c | vim -
" → 1000~2000줄만 편집

" 또는 Vim에서 특정 줄로 직접 이동
$ vim +1000 huge_file.c
" → 1000번째 줄에서 시작

원격 서버에서의 성능

" SSH 환경에서 Vim 최적화
set nottyfast          " 느린 연결에서 터미널 최적화
set lazyredraw         " 매크로 실행 중 화면 갱신 안 함
set norelativenumber   " 상대 번호 비활성화 (매 이동시 재계산)
set nocursorline       " 커서 줄 하이라이트 비활성화

" 클립보드 문제 (SSH에서 X11 포워딩 없이)
" 방법 1: OSC 52 지원 터미널 사용
" 방법 2: :w !xclip -selection clipboard (X11 포워딩 시)
" 방법 3: Neovim의 OSC 52 기본 지원 활용

" tmux 연동 최적화
set ttimeoutlen=10     " Esc 키 지연 최소화 (tmux 환경)
" tmux.conf에도 추가: set -sg escape-time 10

" 256색 터미널 설정
set t_Co=256
" 또는 True Color (24비트)
set termguicolors
" tmux에서: set -g default-terminal "tmux-256color"

커널 소스 탐색 실전

cscope 탐색 실전 워크플로

" 예시: schedule() 함수 분석

" 1. 정의 위치 찾기
:cs find g schedule
" → kernel/sched/core.c의 schedule() 함수 정의로 점프

" 2. schedule()을 호출하는 곳 찾기
:cs find c schedule
" → 수백 개의 호출 위치 목록 (quickfix)

" 3. schedule()이 호출하는 함수들 찾기
:cs find d schedule
" → __schedule(), preempt_disable() 등

" 4. 심볼(변수/함수) 사용처 찾기
:cs find s rcu_read_lock
" → rcu_read_lock이 사용되는 모든 위치

" 5. 문자열 검색
:cs find t "out of memory"
" → OOM 관련 메시지 위치

" 6. 파일 찾기
:cs find f sched.h
" → include/linux/sched.h, kernel/sched/sched.h 등

" 7. 이 파일을 include하는 파일 찾기
:cs find i sched.h
" → #include <linux/sched.h>가 있는 모든 파일

ctags 탐색

" ctags 기본 탐색
Ctrl-]          " 커서 아래 심볼의 정의로 점프
Ctrl-T          " 이전 위치로 복귀 (스택)
Ctrl-W ]        " 정의를 수평 분할로 열기
Ctrl-W }        " 정의를 미리보기 윈도우로 열기

" 태그 검색
:tag func_name  " 이름으로 태그 검색
:ts func_name   " 동명 태그 목록 표시 (tselect)
:tn             " 다음 태그 (동명이 여러 개일 때)
:tp             " 이전 태그
g]              " 커서 아래 심볼의 태그 목록 표시
g Ctrl-]        " 태그가 1개면 점프, 여러 개면 목록

" 태그 스택
:tags           " 태그 점프 이력 (스택) 표시
Ctrl-T          " 스택에서 한 단계 되돌아가기
:pop            " Ctrl-T와 동일

" 실전 예시: 커널 구조체 분석
" task_struct 위에 커서를 놓고 Ctrl-] → 구조체 정의로 점프
" mm_struct 멤버를 보고 Ctrl-] → mm_struct 정의로 점프
" Ctrl-T → task_struct로 복귀
" Ctrl-T → 원래 위치로 복귀

커널 소스 검색

" grepprg을 ripgrep으로 설정 (커널 소스에서 매우 빠름)
set grepprg=rg\ --vimgrep\ --no-heading\ --smart-case
set grepformat=%f:%l:%c:%m

" 검색 예시
:grep 'DEFINE_MUTEX' kernel/          " 특정 디렉터리에서 검색
:grep 'MODULE_LICENSE' drivers/       " 드라이버 모듈 라이선스 검색
:grep '\bkzalloc\b' mm/              " mm 디렉터리에서 kzalloc 검색

" fzf.vim을 사용한 퍼지 검색 (매우 빠름)
:Files          " 파일명 퍼지 검색
:Rg pattern     " 내용 검색 (ripgrep 기반)
:Buffers        " 열린 버퍼 검색
:Tags           " ctags 태그 검색
:BLines         " 현재 버퍼 줄 검색
팁: 커널 소스 탐색 시 :grep:cs find를 상황에 맞게 사용하세요. 정확한 심볼 관계(호출자/피호출자)는 cscope가 강력하고, 텍스트 패턴 검색은 ripgrep이 빠릅니다. Neovim + clangd(LSP)를 사용하면 gd(정의), gr(참조), K(문서) 등으로 더욱 정확한 탐색이 가능합니다.

커널 서브시스템 탐색 실전 시나리오

실제 커널 개발에서 코드를 추적하는 전형적인 시나리오들을 살펴봅니다:

" 시나리오 1: 시스템 콜 추적 (read → vfs → ext4)
" 1. 시스템 콜 정의 찾기
:cs find g __x64_sys_read
" → fs/read_write.c

" 2. vfs_read 호출 추적
:cs find g vfs_read
" → 실제 VFS 계층

" 3. file_operations의 .read 콜백 확인
:cs find s file_operations
" → 수많은 파일시스템에서의 구현

" 4. ext4의 read 구현 찾기
:grep 'ext4.*read_iter' fs/ext4/
" → fs/ext4/file.c

" 시나리오 2: 드라이버 probe 함수 추적
" 1. 특정 드라이버의 probe 찾기
:cs find g e1000_probe
" → drivers/net/ethernet/intel/e1000/e1000_main.c

" 2. 이 probe를 호출하는 프레임워크 찾기
:cs find c e1000_probe
" → PCI 프레임워크에서 호출

" 3. pci_driver 구조체 확인
:tag pci_driver
" → include/linux/pci.h

" 시나리오 3: 커널 설정 옵션 추적
" Kconfig 심볼에서 코드까지:
:grep -rn 'CONFIG_PREEMPT_RT' .
" → kernel/Kconfig.preempt, include/linux/preempt.h
" → #ifdef CONFIG_PREEMPT_RT 사용처 확인

커널 빌드와 quickfix

" Vim에서 커널 빌드
:set makeprg=make\ -j$(nproc)\ 2>&1

" 전체 빌드
:make

" 특정 모듈만 빌드
:make M=drivers/net/ethernet/intel/e1000/

" 빌드 오류 탐색
:copen          " quickfix 열기 (오류 목록)
:cn             " 다음 오류
:cp             " 이전 오류

" 특정 타깃 빌드
:make menuconfig    " 메뉴 설정 (터미널에서)
:make dtbs          " 디바이스 트리 빌드
:make headers       " 헤더 설치 준비

" LLVM/Clang으로 빌드 (정적 분석에 유리)
:set makeprg=make\ LLVM=1\ -j$(nproc)\ 2>&1

" 빌드 에러를 파일로 저장 후 quickfix로 로드
:!make -j$(nproc) 2>&1 | tee /tmp/build.log
:cfile /tmp/build.log

태그 활용 고급 기법

" 태그 파일 우선순위 설정
set tags=./tags;/          " 현재 디렉터리부터 루트까지 tags 파일 검색
set tags+=~/.vim/tags/linux   " 커널 전용 태그 파일

" Universal Ctags 커널 최적화 옵션
" ~/.ctags.d/kernel.ctags 파일:
" --langdef=Kconfig
" --langmap=Kconfig:(Kconfig*)
" --regex-Kconfig=/^(menu)?config[ \t]+([A-Za-z0-9_]+)/\2/c,config/
" --regex-Kconfig=/^(choice)/\1/h,choice/
" --exclude=.git
" --exclude=Documentation
" --fields=+lS
" --extras=+q

" 태그 기반 자동완성 (Insert 모드)
" 함수명 일부 입력 후 Ctrl-X Ctrl-] → 태그에서 완성
" spin_lo → Ctrl-X Ctrl-] → spin_lock, spin_lock_irq, ...

" 프리뷰 윈도우로 정의 확인 (점프 없이)
Ctrl-W }        " 커서 아래 태그를 프리뷰 윈도우에 표시
:ptag func_name  " 프리뷰 윈도우에 태그 표시
:pclose          " 프리뷰 윈도우 닫기
Ctrl-W z         " 프리뷰 윈도우 닫기
참고: 대규모 커널 소스에서의 탐색 도구 비교:
  • cscope — 양방향 관계 분석(호출자/피호출자), C 전용, 데이터베이스 큼
  • ctags — 빠른 정의 점프, 다중 언어 지원, 가벼움
  • clangd (LSP) — 가장 정확, 타입 인식, 리팩토링 지원, compile_commands.json 필요
  • ripgrep (:grep) — 텍스트 패턴 검색에 최적, 설정 없이 사용 가능
커널 개발에서는 ctags + cscope 조합이 가장 전통적이며, Neovim 사용자는 clangd(LSP) + ripgrep 조합을 추천합니다.

Git 통합

커널 개발에서 Git은 필수 도구이며, Vim에서 Git을 직접 사용할 수 있으면 워크플로 효율이 크게 향상됩니다. vim-fugitive 플러그인은 Vim에서 가장 강력한 Git 통합을 제공합니다.

vim-fugitive Git 통합 워크플로 :Git (status) s = stage u = unstage :Gdiffsplit staged vs working do/dp 변경 적용 :Git commit 커밋 메시지 편집 :wq로 커밋 완료 :Git log Enter = 커밋 상세 O = 새 탭에 열기 :Git blame 각 줄의 커밋 정보 표시 Enter = 해당 커밋 상세, ~ = 이전 커밋 blame gitgutter / gitsigns 줄 단위 +/-/~ 표시 (사인 컬럼) ]c/[c 네비, <leader>hs stage hunk 머지 충돌 해결 :Gdiffsplit! (3-way) :diffget //2 or //3 커널 패치 제출 워크플로 (Vim 내에서) 편집 → :Git diff (확인) → :Git add % (스테이징) → :Git commit → :Git format-patch → :Git send-email 상세 Git 워크플로: Git 가이드 참조

vim-fugitive 핵심 명령

" vim-fugitive 기본 명령
:Git                " git status (인터랙티브 — s/u/=/-/cc 등)
:Git diff           " git diff
:Git add %          " 현재 파일 스테이징
:Git commit         " 커밋 메시지 편집기 열기
:Git push           " push
:Git pull           " pull
:Git fetch          " fetch
:Git log            " 커밋 로그 (Enter로 상세 보기)
:Git log --oneline  " 한 줄 로그
:Git stash          " stash
:Git stash pop      " stash pop

" diff 보기
:Gdiffsplit         " 현재 파일의 인덱스 버전과 비교 (2분할)
:Gdiffsplit HEAD~3  " 3커밋 전과 비교
:Gdiffsplit!        " 머지 충돌 시 3-way diff

" blame
:Git blame          " 줄별 blame 표시
" blame 윈도우에서:
"   Enter = 해당 커밋 상세
"   o     = 새 윈도우에서 열기
"   ~     = 이전 커밋의 blame (시간 여행)
"   P     = 부모 커밋의 blame

" 파일 이력 탐색
:Gclog              " 현재 파일의 커밋 이력 (quickfix)
:0Gclog             " 현재 파일의 모든 버전 (revision별)

" 임의 git 명령
:Git format-patch -1 HEAD         " 마지막 커밋을 패치로
:Git send-email --to=... *.patch  " 패치 메일 전송

" Gbrowse (GitHub/GitLab 연동)
:GBrowse            " 현재 파일을 브라우저에서 열기
:'<,'>GBrowse       " 선택 범위의 permalink 생성

머지 충돌 해결 워크플로

" Git 머지 충돌 발생 시

" 방법 1: vimdiff (3-way merge)
" .gitconfig에 설정:
" [merge]
"   tool = vimdiff
"   conflictstyle = diff3
" [mergetool "vimdiff"]
"   cmd = vim -d $LOCAL $BASE $REMOTE $MERGED -c 'wincmd J'

$ git mergetool
" → vimdiff가 4개 윈도우로 열림
" LOCAL (내 브랜치) | BASE (공통 조상) | REMOTE (상대 브랜치)
"                    MERGED (결과 파일)

" 충돌 해결 명령
]c              " 다음 충돌 블록으로 이동
[c              " 이전 충돌 블록으로 이동
:diffget LO     " LOCAL(내 것) 선택
:diffget RE     " REMOTE(상대 것) 선택
:diffget BA     " BASE(공통 조상) 선택

" 방법 2: fugitive의 :Gdiffsplit!
:Gdiffsplit!
" → 3-way diff가 자동으로 열림
" //2 = LOCAL, //3 = REMOTE
:diffget //2    " LOCAL 선택 (내 변경)
:diffget //3    " REMOTE 선택 (상대 변경)

" 충돌 마커 검색
/<<<<<<<
" → 충돌 시작 위치
/=======
" → 경계
/>>>>>>>
" → 충돌 끝

" 수동 충돌 해결 후
:w              " 저장
:!git add %     " 해결된 파일 스테이징

Git 이력 탐색

" fugitive로 파일 이력 탐색
:Gclog              " 현재 파일의 커밋 이력 (quickfix)
:Gclog --all        " 모든 브랜치에서 파일 이력
:Gclog -20          " 최근 20개 커밋만
:0Gclog             " 모든 리비전의 파일 내용 (quickfix)

" quickfix에서 커밋 탐색
:copen              " 커밋 목록 열기
Enter               " 해당 커밋의 파일 내용 보기
:cn :cp             " 다음/이전 커밋

" 특정 함수의 변경 이력 (Git log -L)
:Git log -L :func_name:file.c
" → func_name 함수의 모든 변경 이력

" blame 시간 여행
:Git blame
" blame 윈도우에서:
" Enter → 커밋 상세
" ~ → 해당 줄의 이전 커밋 blame (시간 여행!)
" P → 부모 커밋 blame
" o → 새 윈도우에서 열기
" O → 새 탭에서 열기

" 특정 커밋 시점의 파일 보기
:Gedit HEAD~5:%     " 5커밋 전의 현재 파일
:Gedit main:path/to/file.c  " main 브랜치의 파일
:Gsplit HEAD~3:%    " 수평 분할로 비교

gitgutter/gitsigns 활용

" vim-gitgutter 핵심 기능
]c                  " 다음 변경 hunk로 이동
[c                  " 이전 변경 hunk로 이동
<Leader>hs          " hunk stage (git add 부분)
<Leader>hu          " hunk undo (변경 되돌리기)
<Leader>hp          " hunk preview (변경 내용 미리보기)

" Neovim gitsigns.nvim 설정
require('gitsigns').setup({
  signs = {
    add          = { text = '+' },
    change       = { text = '~' },
    delete       = { text = '_' },
    topdelete    = { text = '-' },
    changedelete = { text = '~' },
  },
  on_attach = function(bufnr)
    local gs = package.loaded.gitsigns
    vim.keymap.set('n', ']c', gs.next_hunk, { buffer = bufnr })
    vim.keymap.set('n', '[c', gs.prev_hunk, { buffer = bufnr })
    vim.keymap.set('n', '<leader>hs', gs.stage_hunk, { buffer = bufnr })
    vim.keymap.set('n', '<leader>hu', gs.undo_stage_hunk, { buffer = bufnr })
    vim.keymap.set('n', '<leader>hp', gs.preview_hunk, { buffer = bufnr })
    vim.keymap.set('n', '<leader>hb', gs.blame_line, { buffer = bufnr })
  end,
})

디버깅 지원

Vim 8.1+에 내장된 Termdebug 플러그인은 GDB를 Vim 내에서 통합 실행하는 3-pane 디버거 인터페이스를 제공합니다. 커널 디버깅 시 QEMU + GDB stub과 연결하여 소스 레벨 디버깅이 가능합니다.

Termdebug GDB 3-Pane 레이아웃 소스 윈도우 (Source) 42 static int __init kernel_init(void *unused) 43 { ● 44 int ret; 45 → 46 ret = kernel_init_freeable(); 47 async_synchronize_full(); 48 free_initmem(); 49 mark_readonly(); ● = 중단점 (breakpoint) → = 현재 실행 위치 (PC) GDB 윈도우 (gdb) break kernel_init Breakpoint 1 at 0xffffffff (gdb) continue Hit Breakpoint 1, kernel_init (gdb) print ret 프로그램 출력 [ 0.000000] Linux version 6.x [ 0.100000] Booting kernel... Termdebug Vim 명령 :Break = 중단점 설정 | :Clear = 중단점 해제 :Step = step into | :Over = step over :Continue = 실행 재개 | :Stop = 정지 K = 커서 아래 변수 evaluate | :Evaluate expr

Termdebug 설정과 사용법

" Termdebug 활성화 (Vim 8.1+ 내장)
:packadd termdebug

" 일반 프로그램 디버깅
:Termdebug ./my_program

" 커널 디버깅 (QEMU GDB stub 연결)
" 1. QEMU를 GDB stub과 함께 실행 (별도 터미널)
"    qemu-system-x86_64 -kernel bzImage -s -S ...
"    (-s = GDB 포트 1234, -S = 시작 시 정지)

" 2. Vim에서 Termdebug 시작
:Termdebug vmlinux
" GDB 윈도우에서:
"    target remote :1234
"    break start_kernel
"    continue

" Termdebug Vim 명령
:Break              " 커서 위치에 중단점 설정
:Clear              " 커서 위치의 중단점 해제
:Step               " step into (함수 안으로)
:Over               " step over (함수 건너뛰기)
:Finish             " step out (현재 함수 완료까지)
:Continue           " 실행 재개
:Stop               " 실행 중지 (Ctrl-C)
:Run                " 프로그램 시작/재시작
:Evaluate expr      " 표현식 평가
K                   " 커서 아래 변수 값 표시 (Normal 모드)

" Termdebug 설정
let g:termdebug_wide = 1           " 수직 분할 레이아웃
let g:termdebug_popup = 0          " 팝업 대신 윈도우 사용
let g:termdebugger = 'gdb-multiarch'  " 크로스 디버깅 시

" 커널 디버깅 편의 매핑
nnoremap <F5>  :Continue<CR>
nnoremap <F9>  :Break<CR>
nnoremap <F10> :Over<CR>
nnoremap <F11> :Step<CR>
nnoremap <F12> :Finish<CR>
참고: 커널 디버깅의 상세한 GDB 사용법은 GDB 가이드를, QEMU 기반 커널 디버깅 환경 설정은 디버깅 가이드를 참조하세요. Neovim에서는 nvim-dap 플러그인이 DAP(Debug Adapter Protocol) 기반의 더 현대적인 디버깅 환경을 제공합니다.

QEMU + Termdebug 커널 디버깅 실전

# 1. 디버깅 가능한 커널 빌드
cd /usr/src/linux
make defconfig
scripts/config --enable CONFIG_DEBUG_INFO
scripts/config --enable CONFIG_GDB_SCRIPTS
scripts/config --enable CONFIG_DEBUG_KERNEL
scripts/config --disable CONFIG_RANDOMIZE_BASE  # KASLR 비활성화
make -j$(nproc)

# 2. QEMU 실행 (GDB stub 포함)
qemu-system-x86_64 \
    -kernel arch/x86/boot/bzImage \
    -append "root=/dev/sda console=ttyS0 nokaslr" \
    -drive file=rootfs.img,format=raw \
    -nographic \
    -s -S
# -s: GDB 서버 포트 1234
# -S: 시작 시 정지 (GDB 연결 대기)
" 3. Vim에서 Termdebug로 커널 디버깅
:packadd termdebug
let g:termdebug_wide = 1    " 수직 레이아웃

" vmlinux (디버그 심볼 포함)으로 Termdebug 시작
:Termdebug vmlinux

" GDB 윈도우에서 QEMU에 연결
" (gdb) target remote :1234
" (gdb) lx-dmesg                    " 커널 dmesg 출력 (GDB scripts)
" (gdb) lx-lsmod                    " 로드된 모듈 목록
" (gdb) lx-ps                       " 프로세스 목록
" (gdb) lx-symbols                  " 모듈 심볼 로드
" (gdb) break start_kernel
" (gdb) continue

" 소스 윈도우에서 디버깅
:Break          " 커서 위치에 중단점 (줄에 >> 마커)
:Step           " 한 줄 실행 (함수 진입)
:Over           " 한 줄 실행 (함수 건너뜀)
:Finish         " 현재 함수 끝까지 실행
:Continue       " 실행 재개

" K로 변수 값 확인 (커서를 변수 위에 놓고)
" Normal 모드에서 K → GDB evaluate 결과 표시

" 커널 구조체 멤버 확인
" GDB 윈도우에서:
" (gdb) p *current                  " 현재 task_struct
" (gdb) p current->comm             " 프로세스 이름
" (gdb) p current->mm->pgd          " 페이지 디렉터리

Neovim nvim-dap 대안

-- Neovim에서는 nvim-dap 플러그인으로 더 현대적인 디버깅 UI 가능
-- lazy.nvim 설정:
{
  "mfussenegger/nvim-dap",
  dependencies = {
    "rcarriga/nvim-dap-ui",       -- 디버그 UI
    "theHamsta/nvim-dap-virtual-text",  -- 변수 값 인라인 표시
  },
  config = function()
    local dap = require("dap")

    -- GDB 어댑터 설정 (커널용)
    dap.adapters.cppdbg = {
      id = "cppdbg",
      type = "executable",
      command = "gdb",
      args = { "-i", "dap" },  -- GDB 14+ DAP 모드
    }

    -- 커널 디버깅 설정
    dap.configurations.c = {
      {
        name = "Kernel Debug (QEMU)",
        type = "cppdbg",
        request = "launch",
        program = "${workspaceFolder}/vmlinux",
        miDebuggerServerAddress = "localhost:1234",
        cwd = "${workspaceFolder}",
        stopAtEntry = true,
      },
    }
  end,
}

-- 디버깅 키매핑
vim.keymap.set("n", "<F5>", require("dap").continue)
vim.keymap.set("n", "<F9>", require("dap").toggle_breakpoint)
vim.keymap.set("n", "<F10>", require("dap").step_over)
vim.keymap.set("n", "<F11>", require("dap").step_into)
vim.keymap.set("n", "<F12>", require("dap").step_out)

고급 편집 패턴

블록 삽입과 일괄 편집

" 블록 비주얼 + 삽입: 여러 줄 앞에 동시 삽입
" Ctrl-V → 줄 선택 → I → 텍스트 → Esc

" 블록 비주얼 + 추가: 여러 줄 끝에 동시 추가
" Ctrl-V → $ → 줄 선택 → A → 텍스트 → Esc

" :normal 명령으로 다중 줄 편집
:'<,'>normal I//        " 선택 줄 앞에 // 추가 (주석)
:'<,'>normal A;          " 선택 줄 끝에 ; 추가
:'<,'>normal @q          " 선택 줄에 매크로 q 적용
:10,30normal dd          " 10~30줄 각각 삭제 (주의: 줄 번호 변화)

" 수식 레지스터 (Ctrl-R =)
" Insert 모드에서:
Ctrl-R =42*3             " → 126 삽입
Ctrl-R =line('.')        " → 현재 줄 번호 삽입
Ctrl-R =strftime('%Y-%m-%d')  " → 현재 날짜 삽입
Ctrl-R =system('whoami')      " → 사용자명 삽입

숫자 증감과 순번 생성

" 숫자 증가/감소
Ctrl-A          " 커서 아래/뒤의 숫자를 1 증가
Ctrl-X          " 커서 아래/뒤의 숫자를 1 감소
5 Ctrl-A        " 5 증가
10 Ctrl-X       " 10 감소

" 비주얼 블록 + g Ctrl-A: 순차 번호 생성
" 0
" 0
" 0
" 0
" → Ctrl-V로 4줄의 0을 블록 선택 → g Ctrl-A
" 1
" 2
" 3
" 4

" 실전: 배열 인덱스 자동 생성
" arr[0] = val0;
" arr[0] = val0;
" arr[0] = val0;
" → 블록 선택 후 g Ctrl-A → arr[1], arr[2], arr[3] ...

수식 레지스터 ("=)

수식 레지스터(Ctrl-R =)는 Insert 모드와 Command-line 모드에서 Vimscript 표현식을 평가하여 결과를 삽입합니다. 간단한 계산부터 복잡한 동적 텍스트 생성까지 가능합니다:

" Insert 모드에서 Ctrl-R = 사용 예시:

" 계산
Ctrl-R =2**10           " → 1024
Ctrl-R =0xff            " → 255
Ctrl-R =4096/8          " → 512

" 현재 파일 정보
Ctrl-R =expand('%:t')   " → 파일명만 (test.c)
Ctrl-R =expand('%:p')   " → 전체 경로
Ctrl-R =expand('%:e')   " → 확장자 (c)
Ctrl-R =line('.')       " → 현재 줄 번호
Ctrl-R =col('.')        " → 현재 열 번호

" 날짜/시간
Ctrl-R =strftime('%Y-%m-%d %H:%M:%S')   " → 2026-03-16 14:30:00

" 시스템 정보
Ctrl-R =system('hostname')         " → 호스트명
Ctrl-R =system('git rev-parse --short HEAD')  " → 커밋 해시

" UUID 생성 (Linux)
Ctrl-R =system('cat /proc/sys/kernel/random/uuid')[:-2]

" 반복 문자열
Ctrl-R =repeat('=', 72)     " → ====... (72개)
Ctrl-R =repeat("\t", 3)     " → 탭 3개

" 조건식
Ctrl-R =&expandtab ? "spaces" : "tabs"

" Command-line 모드에서도 사용 가능
:echo Ctrl-R =&tabstop       " → 8 (현재 tabstop 값)

정렬과 포맷팅

" 정렬
:sort               " 줄 알파벳순 정렬
:sort!              " 역순 정렬
:sort n             " 숫자순 정렬
:sort u             " 중복 제거 후 정렬
:sort /pattern/     " 패턴 이후 부분으로 정렬
:'<,'>sort           " 선택 범위만 정렬

" 커널 코드 정렬 예시
" #include 정렬
:g/^#include/sort

" 외부 명령으로 포맷팅
:%!column -t        " 테이블 정렬
:!ip column -t      " 현재 문단 테이블 정렬
:%!indent           " C 코드 자동 들여쓰기 (GNU indent)

" Vim 내장 정렬
:set textwidth=80   " 텍스트 너비 80
gqip                " 현재 문단을 textwidth에 맞춰 리포맷
gqq                 " 현재 줄 리포맷

" 공백/탭 변환
:retab!             " 현재 tabstop 설정에 따라 탭↔공백 변환
:set expandtab | retab  " 탭 → 공백
:set noexpandtab | retab! " 공백 → 탭

다중 커서 대안

" Vim에는 VS Code 같은 다중 커서가 없지만, 더 강력한 대안이 있습니다:

" 1. 점 명령 (.) — 가장 기본적인 반복
/old_name           " 검색
cgnnew_name<Esc>    " cgn = 다음 매칭을 변경 (gn = 검색 매칭 선택)
.                   " 반복 (다음 매칭 자동 변경)
.                   " 계속 반복
n                   " 건너뛰기 (변경하지 않을 곳)
.                   " 다시 변경

" 2. :s 치환 — 일괄 변경
:%s/old/new/gc      " 전체 확인 후 치환

" 3. :g/pattern/normal — 패턴 매칭 줄에 명령 적용
:g/TODO/normal A // DONE

" 4. 매크로 — 복잡한 반복 편집
qa ... q → :%normal @a

" 5. :argdo, :bufdo, :cdo — 파일 간 일괄 적용
:args **/*.c
:argdo %s/old_api/new_api/ge | update

surrond 조작 (vim-surround)

" vim-surround 플러그인 핵심 명령
" (tpope/vim-surround 설치 필요)

" 추가 (ys = you surround)
ysiw"       " 단어를 큰따옴표로 감싸기: word → "word"
ysiw)       " 단어를 괄호로 감싸기: word → (word)
ysiw(       " 단어를 괄호+공백으로: word → ( word )
yss"        " 줄 전체를 큰따옴표로 감싸기
ys$)        " 줄 끝까지 괄호로 감싸기

" 변경 (cs = change surround)
cs"'        " 큰따옴표를 작은따옴표로: "text" → 'text'
cs)}        " 괄호를 중괄호로: (text) → {text}
cs'<q>      " 작은따옴표를 태그로: 'text' → <q>text</q>
cst"        " 태그를 큰따옴표로: <p>text</p> → "text"

" 삭제 (ds = delete surround)
ds"         " 큰따옴표 제거: "text" → text
ds)         " 괄호 제거: (text) → text
dst         " 태그 제거: <p>text</p> → text

" 비주얼 모드에서 (S = surround)
" v로 선택 → S" → 선택 영역을 큰따옴표로 감싸기
" V로 줄 선택 → S{ → 줄을 중괄호 블록으로 감싸기

" 커널 코드 실전 예시
" 1. 조건문 추가: stmt → if (cond) { stmt; }
" yss) → (stmt) → I if cond <Esc> → A { <Enter> } <Esc>

" 2. 함수 호출 감싸기: expr → WARN_ON(expr)
" ysiw) → (expr) → i WARN_ON <Esc>

" 3. 매크로 래핑: value → unlikely(value)
" ysiw) → (value) → i unlikely <Esc>

마크 활용 실전 패턴

" 전역 마크로 작업 컨텍스트 저장
mA          " 현재 작업 중인 주 파일에 마크 A
mB          " 참조 중인 헤더 파일에 마크 B
mC          " 테스트/빌드 설정 파일에 마크 C
'A          " 언제든 주 파일로 복귀
'B          " 헤더 파일로 복귀

" 로컬 마크로 함수 내 탐색
ma          " 함수 시작에 마크
mb          " 디버깅 중인 줄에 마크
'a          " 함수 시작으로 복귀
'b          " 디버깅 줄로 복귀

" 특수 마크
'.          " 마지막 변경 줄 (매우 유용!)
'"          " 마지막으로 나간 줄 (파일 재열기 시)
'^          " 마지막 Insert 모드 종료 위치

" 마크 간 범위 지정
:'a,'b d    " 마크 a~b 사이 삭제
:'a,'b y    " 마크 a~b 사이 복사
:'a,'b s/old/new/g  " 마크 a~b 사이 치환

Ex 명령 고급 기법

" 줄 이동
:5m 20      " 5번 줄을 20번 줄 뒤로 이동
:5,10m 30   " 5~10번 줄을 30번 줄 뒤로 이동
:m 0        " 현재 줄을 파일 맨 위로

" 줄 복사
:5t 20      " 5번 줄을 20번 줄 뒤에 복사 (= :5copy 20)
:5,10t $    " 5~10번 줄을 파일 끝에 복사
:t.         " 현재 줄 아래에 복사 (= yyp)

" 줄 합치기
:5,10join   " 5~10번 줄 합치기 (공백으로)
:5,10join!  " 공백 없이 합치기

" 텍스트 센터링/정렬
:center 80  " 현재 줄을 80칸 중앙 정렬
:right 80   " 오른쪽 정렬
:left 4     " 왼쪽 들여쓰기 4칸

" 줄 번호 제어
:set number relativenumber    " 절대+상대 번호 혼합
" → 현재 줄: 절대 번호, 나머지: 상대 번호

" 커서 아래 단어를 명령줄에 삽입
" : 입력 후 Ctrl-R Ctrl-W → 커서 아래 단어 삽입
:help Ctrl-R Ctrl-W     " 예: Ctrl-R Ctrl-W 삽입 후 Enter

" 마지막 Ex 명령 반복
@:          " 마지막 : 명령 반복
@@          " 다시 반복

" 셸 명령 결과를 변수로
:let output = system('git branch --show-current')
:echo output

팁과 안티패턴

흔한 안티패턴 (피해야 할 습관)

안티패턴문제점올바른 방법
화살표 키 사용홈 로우에서 손이 벗어남hjkl 사용 (초기 불편함 감수)
Insert 모드에 머무르기Vim의 강점을 전혀 활용하지 못함편집 완료 즉시 Esc로 Normal 복귀
jjjjjj로 이동비효율적, 정확하지 않음5j, /pattern, {/} 사용
x 연타로 삭제반복 삭제에 비효율적dw, d3w, dt; 등 모션 활용
방향키+Delete로 편집일반 에디터 습관, 느림텍스트 오브젝트 (ciw, da}) 활용
수동으로 같은 작업 반복시간 낭비, 실수 가능. (dot), 매크로, :%s 사용
마우스로 선택+복사레지스터/텍스트 오브젝트 미활용yiw, yap, "ay 사용
:wq 후 다시 열기버퍼를 활용하지 않음:e 파일, :b 버퍼, Ctrl-^ 대체 파일
플러그인 과다 설치시작 시간 증가, 충돌필요한 것만, 내장 기능 먼저 확인
설정만 만지작거리기"vimrc 함정" — 실제 작업 안 함최소 설정으로 시작, 필요할 때 추가

Vim 사고방식 전환

Vim을 효과적으로 사용하려면 "타이핑 도구"에서 "편집 언어"로 사고방식을 전환해야 합니다:

일반 에디터 사고Vim 사고
"이 단어를 지우려면 더블클릭해서 선택 후 Delete"diw — "단어 안(inner word)을 삭제(delete)"
"이 줄을 복사하려면 Home, Shift+End, Ctrl+C"yy — "줄을 복사(yank)"
"이 함수 본문을 전부 선택하려면 마우스로..."vi{ — "중괄호 안(inner brace)을 선택(visual)"
"이 변수명을 10곳에서 바꾸려면 하나씩..."cgn새이름<Esc>.으로 반복
"이 줄들의 순서를 바꾸려면 잘라서 붙여넣고..."ddp — "줄 삭제 후 아래에 붙여넣기"
"이 파일의 다른 곳을 보려면 스크롤 바를..."/함수명 또는 Ctrl-] — 의미 기반 탐색

핵심 원칙은 "반복하지 마라(Don't Repeat Yourself)"입니다. 같은 편집을 두 번 이상 수동으로 하고 있다면, ., 매크로, :%s, :g 중 하나로 자동화할 수 있습니다.

효과적인 편집 전략

" 전략 1: "의도를 명확하게" — 명확한 텍스트 오브젝트 사용
" 나쁨: dw dw dw (단어 3개를 하나씩 삭제)
" 좋음: d3w (단어 3개 한번에 삭제)
" 더 좋음: d/pattern (패턴까지 삭제 — 정확한 위치 지정)

" 전략 2: "변경을 최소화" — 점 명령 활용
" 나쁨: 검색 → 수정 → 검색 → 수정 → ...
" 좋음: /old → cgnnew<Esc> → . → . → . (필요한 곳만 점으로 반복)

" 전략 3: "구조를 이용" — 텍스트 오브젝트와 모션
" 함수 전체 복사: yap (문단 = 빈줄 사이)
" 조건문 본체 수정: ci{ (중괄호 안 변경)
" 문자열 교체: ci" (따옴표 안 변경)

" 전략 4: "범위를 활용" — 시각적 확인
" :5,20s/old/new/gc → 5~20줄에서만 확인 치환
" V선택 → :sort → 선택 줄만 정렬
" :'<,'>normal @q → 선택 줄에만 매크로 적용

" 전략 5: "되돌리기에 자신감" — undo tree 활용
" u → undo, Ctrl-R → redo
" :earlier 5m → 5분 전으로 (실험적 편집에 안전)
" :later 3f → 3번의 저장 후 시점으로

커널 개발자의 하루: Vim 워크플로 예시

" 1. 아침: 작업 시작
$ cd /usr/src/linux
$ vim -S ~/.vim/sessions/kernel.vim   " 어제 세션 복원

" 2. 코드 탐색: 새로운 버그 리포트 분석
/WARN_ON_ONCE                         " 경고 메시지 검색
:cs find g suspicious_function        " 정의로 이동
:cs find c suspicious_function        " 호출자 목록 확인
Ctrl-O                                 " 원래 위치로 복귀

" 3. 코드 수정
mA                                     " 현재 위치에 전역 마크
ciw                                    " 변수명 변경
/관련_함수                             " 관련 함수로 이동
.                                      " 같은 변경 반복

" 4. 빌드 및 검증
:make                                  " 빌드
:copen                                 " 오류 확인
:cn :cp                                " 오류 순회 및 수정
:!scripts/checkpatch.pl --file %       " 코딩 스타일 검사

" 5. 커밋
:Git diff                              " 변경 사항 확인
:Git add %                             " 현재 파일 스테이징
:Git commit                            " 커밋 메시지 작성

" 6. 패치 생성
:Git format-patch -1 HEAD              " 패치 파일 생성
:!scripts/checkpatch.pl 0001-*.patch   " 패치 검사

" 7. 세션 저장
:mksession! ~/.vim/sessions/kernel.vim " 세션 저장
:qa                                    " 종료

효율적인 습관

습관효과예시
반복 가능하게 편집. 명령으로 반복 가능ciw > dwi, A; > $a;
텍스트 오브젝트 우선커서 위치 무관 정확한 선택ci} 커서가 어디든 중괄호 안 변경
검색으로 이동jjjjj보다 빠르고 정확/func_name, *, f(
Count 활용반복 동작 한 번에3dw, 5j, 10@a
:h (도움말) 활용Vim 내장 문서가 가장 정확:h text-objects, :h registers
undo 분기 활용u 후 편집해도 이전 분기 보존:earlier 5m (5분 전으로), g+/g-

자주 빠지는 함정들

" 함정 1: "완벽한 vimrc" 추구
" → 해결: 최소 설정으로 시작, 불편할 때만 추가
" → 한 주에 하나의 새 기능/설정만 추가하는 규칙

" 함정 2: 플러그인 수집가
" → 해결: 내장 기능으로 먼저 시도, 정말 필요할 때만 설치
" → 설치 전 :help로 내장 대안 확인

" 함정 3: "Vim으로 IDE를 만들자"
" → 해결: Vim은 텍스트 편집 도구. 파일 관리는 셸, 빌드는 make
" → 필요한 기능만 선택적으로 추가

" 함정 4: 모든 것을 매핑하려는 욕구
" → 해결: 기본 키를 먼저 충분히 익히기
" → 매핑은 정말 자주 사용하는 것만

" 함정 5: Normal 모드를 두려워하기
" → 해결: Insert 모드를 "일시적 상태"로 인식
" → 키 입력이 끝나면 즉시 Esc로 Normal 복귀

" 함정 6: Esc 키에 대한 불만
" → 해결: Ctrl-[ (동일한 기능, 홈로우)
" → 또는 CapsLock을 Ctrl이나 Esc에 매핑 (OS 레벨)
" → inoremap jk <Esc> 같은 커스텀 매핑

일상적으로 시간을 절약하는 명령들

" 이 명령들을 자연스럽게 사용하면 하루에 수십 분을 절약합니다:

" 1. cgn — 검색 매칭을 변경하고 . 으로 반복
/old_name
cgnnew_name<Esc>
.   " 다음 매칭 자동 변경
n   " 건너뛰기 (변경 안 함)
.   " 또 변경

" 2. * 과 cgn 결합 — 커서 아래 단어를 빠르게 리네임
*           " 커서 아래 단어 검색
cgn새이름<Esc>  " 변경
.           " 다음 것도 변경

" 3. Ctrl-^ — 두 파일 간 빠른 전환
" .c 파일과 .h 파일 사이를 즉시 전환

" 4. gi — 마지막 Insert 위치에서 편집 재개
" Normal 모드에서 이동 후, gi로 마지막 편집 위치 복귀

" 5. gv — 마지막 비주얼 선택 재활성화
" 들여쓰기 후 다시 선택하여 추가 들여쓰기

" 6. Ctrl-O (Insert 모드) — Normal 명령 1회 실행
" Insert 모드에서 Ctrl-O dd → 줄 삭제 후 Insert 유지

" 7. :earlier 5m — 5분 전 상태로 (시간 여행 undo)

" 8. gf — 커서 아래 파일명으로 이동
" #include <linux/sched.h> 위에서 gf

" 9. q: — 명령 이력 윈도우 (편집 가능!)
" 이전 명령을 수정해서 다시 실행

" 10. :w !diff % - — 저장 전 변경사항 확인

학습 경로 권장

팁: Vim 학습의 핵심은 "점진적 확장"입니다. 한 번에 모든 것을 외우려 하지 마세요.
  1. 1주차: hjkl, i/Esc, :w/:q, dd/yy/p
  2. 2주차: w/b/e, 0/$, f/t, /검색, . 반복
  3. 3주차: 텍스트 오브젝트 (ciw, di}, yap), 비주얼 모드
  4. 4주차: 레지스터, 매크로, 윈도우 분할
  5. 이후: cscope/ctags, 플러그인, vimrc 커스터마이징
vimtutor 명령으로 시작하면 30분 내에 기본을 익힐 수 있습니다.

단축키 치트시트

이 치트시트는 커널 개발에서 자주 사용되는 Vim 단축키를 모드별로 정리한 빠른 참조표입니다. 모든 명령을 외울 필요는 없으며, 자주 사용하는 것부터 점진적으로 습관화하는 것을 권장합니다.

팁: 이 치트시트를 인쇄하거나 :help quickref를 참조하세요. Vim 내장 도움말은 가장 정확하고 완전한 참조입니다. 특정 키가 무엇을 하는지 알고 싶으면 :help CTRL-W, :help text-objects 같이 검색할 수 있습니다.

Normal 모드

카테고리동작
이동h j k l좌 하 상 우
w W b B e E단어/WORD 단위 이동
0 ^ $ g_줄 시작/첫비공백/끝/마지막비공백
gg G파일 시작/끝
{ } ( )문단/문장 이동
Ctrl-D Ctrl-U반 화면 아래/위
H M L화면 상/중/하
편집x X문자 삭제 (뒤/앞)
dd D줄 삭제 / 줄 끝까지 삭제
yy Y줄 복사
p P뒤/앞에 붙여넣기
u Ctrl-R실행 취소 / 다시 실행
.마지막 변경 반복
~대소문자 토글
검색/ ?전방/후방 검색
n N다음/이전 매칭
* #커서 단어 전방/후방 검색
f F t T줄 내 문자 찾기
% 대응 괄호 이동
마크/점프m{a-zA-Z}마크 설정
'{mark} `{mark}마크로 이동 (줄/정확한 위치)
Ctrl-O Ctrl-I이전/다음 점프 위치
g; g,이전/다음 변경 위치

Insert 모드

동작
Ctrl-[ / EscNormal 모드로 복귀
Ctrl-W이전 단어 삭제
Ctrl-U줄 시작까지 삭제
Ctrl-H이전 문자 삭제 (Backspace)
Ctrl-T현재 줄 들여쓰기 증가
Ctrl-D현재 줄 들여쓰기 감소
Ctrl-N키워드 자동완성 (다음)
Ctrl-P키워드 자동완성 (이전)
Ctrl-R {reg}레지스터 내용 삽입
Ctrl-R =수식 결과 삽입
Ctrl-O {cmd}Normal 명령 1회 실행 후 Insert 복귀
Ctrl-V {char}리터럴 문자 삽입 (특수문자)
Ctrl-A이전 Insert의 텍스트 반복 삽입
Ctrl-X Ctrl-L줄 단위 완성
Ctrl-X Ctrl-F파일 경로 완성
Ctrl-X Ctrl-]태그 완성
Ctrl-X Ctrl-O옴니 완성

Visual 모드

동작
v V Ctrl-V문자/줄/블록 비주얼 시작
gv마지막 비주얼 영역 재선택
o선택 영역 반대쪽 끝으로
O블록 비주얼: 같은 줄 반대 모서리
d / x선택 영역 삭제
y선택 영역 복사
c선택 영역 변경
I (블록)블록 앞에 삽입
A (블록)블록 뒤에 삽입
r{char}선택 영역을 문자로 교체
> < =들여쓰기 증가/감소/자동
U u ~대문자/소문자/토글
J선택 줄 합치기
: (범위)선택 범위에 Ex 명령 적용

Command-line 모드

명령동작
:w :q :wq :x :q!저장/종료/강제종료
:e file파일 열기
:b {name/num}버퍼 전환
:sp :vs수평/수직 분할
:%s/old/new/gc전체 파일 확인 치환
:g/pattern/cmd패턴 매칭 줄에 명령
:v/pattern/cmd비매칭 줄에 명령
:make빌드 (quickfix 채움)
:grep pattern검색 (quickfix 채움)
:cn :cp :copenquickfix 탐색/열기
:reg레지스터 목록
:marks마크 목록
:h topic도움말
:!cmd외부 명령 실행
:r !cmd명령 출력 삽입
:earlier :later시간 기반 undo/redo

연산자 + 모션/텍스트 오브젝트 조합

조합동작용도
dw단어 삭제커서~단어 끝 삭제
diw단어(inner) 삭제커서 위치 무관 단어 전체 삭제
daw단어(around) 삭제단어 + 주변 공백 삭제
ci"따옴표 안 변경문자열 내용 교체
ca}중괄호 포함 변경코드 블록 교체
yip문단 복사함수/주석 블록 복사
=ip문단 자동 들여쓰기코드 블록 정렬
>i}중괄호 안 들여쓰기if/for 본체 들여쓰기
gUiw단어 대문자 변환상수명 변환
gqip문단 포맷팅주석 줄바꿈 정리
df;; 까지 삭제구문 뒷부분 삭제
ct)) 앞까지 변경함수 매개변수 수정
y/pattern패턴까지 복사특정 위치까지 복사
d/pattern패턴까지 삭제특정 위치까지 삭제
gg=G파일 전체 자동 들여쓰기전체 코드 재정렬

윈도우/탭/버퍼 명령

카테고리키/명령동작
윈도우 분할Ctrl-W s / :sp수평 분할
Ctrl-W v / :vs수직 분할
Ctrl-W c / :close현재 윈도우 닫기
Ctrl-W o / :only다른 윈도우 모두 닫기
Ctrl-W T현재 윈도우를 새 탭으로
윈도우 이동Ctrl-W h/j/k/l좌/하/상/우 윈도우로 이동
Ctrl-W w다음 윈도우 (순환)
Ctrl-W H/J/K/L윈도우 재배치 (끝으로)
Ctrl-W r/R윈도우 순환 회전
Ctrl-W =모든 윈도우 동일 크기
:tabnew / :tabe새 탭
gt / gT다음/이전 탭
NgtN번 탭으로
:tabclose / :tabo탭 닫기 / 다른 탭 닫기
버퍼:ls / :buffers버퍼 목록
:b N / :b name버퍼 전환
:bn / :bp다음/이전 버퍼
:bd버퍼 삭제
Ctrl-^대체 파일 전환

cscope/ctags 명령

명령동작
Ctrl-]ctags: 정의로 점프
Ctrl-Tctags: 이전 위치로 복귀 (스택)
Ctrl-W ]ctags: 정의를 수평 분할로
g]ctags: 동명 태그 목록
g Ctrl-]ctags: 1개면 점프, 여러 개면 목록
:cs find s {sym}cscope: 심볼 사용처
:cs find g {sym}cscope: 정의 위치
:cs find c {sym}cscope: 호출하는 곳
:cs find d {sym}cscope: 호출되는 함수
:cs find t {text}cscope: 텍스트 검색
:cs find e {pat}cscope: egrep 패턴 검색
:cs find f {file}cscope: 파일 찾기
:cs find i {file}cscope: include하는 파일

기타 유용한 명령

명령동작
ga커서 아래 문자의 ASCII/유니코드 값 표시
g8커서 아래 문자의 UTF-8 바이트 표시
g Ctrl-G현재 위치 상세 정보 (줄/열/단어/바이트)
Ctrl-A / Ctrl-X숫자 증가/감소
g Ctrl-A (Visual)순차 번호 생성
gf커서 아래 파일명으로 이동
gd / gD로컬/글로벌 변수 정의로 이동
gv마지막 비주얼 선택 재활성화
gi마지막 Insert 위치에서 삽입 재개
Ctrl-G파일 정보 표시
ZZ저장 후 종료 (= :wq)
ZQ저장 없이 종료 (= :q!)
q:명령 이력 윈도우
q/검색 이력 윈도우
:earlier NmN분 전 상태로 undo
:later NmN분 후 상태로 redo
:r !cmd외부 명령 출력 삽입
:%!cmd파일 전체를 외부 명령으로 필터링
:set list보이지 않는 문자 표시 (탭, 줄끝 공백)
:set spell맞춤법 검사 활성화
]s / [s다음/이전 맞춤법 오류
z=맞춤법 교정 제안 목록

머지 충돌 해결

커널 개발에서 다수의 브랜치를 관리하면 머지 충돌(merge conflict)은 일상적입니다. Vim/Neovim의 diff 모드fugitive 플러그인은 충돌을 시각적으로 비교하고 해결하는 강력한 도구를 제공합니다.

3-Way Merge 레이아웃 (vimdiff3) LOCAL (현재 브랜치) static int init_fn(void) { pr_info("v2\n"); return 0; } BASE (공통 조상) static int init_fn(void) { pr_info("v1\n"); return 0; } REMOTE (머지 대상) static int init_fn(void) { pr_info("v3\n"); return 0; } MERGED (최종 결과 — 편집 대상) static int init_fn(void) { pr_info("v2\n"); /* LOCAL 선택: :diffget LO */ return 0; } :diffget LO|BA|RE — 해당 윈도우에서 변경 가져오기 | :diffput — 현재 변경 보내기

vimdiff 기본 사용법

# 두 파일 비교
vim -d file_old.c file_new.c
vimdiff file_old.c file_new.c      # 동일

# 3-way diff (수평 분할)
vim -d file1.c file2.c file3.c

# 이미 Vim 내에서 diff 시작
# :diffsplit other_file.c      — 수평 분할 diff
# :vert diffsplit other_file.c — 수직 분할 diff

Git mergetool 설정

# Git에서 vimdiff를 mergetool로 설정
git config --global merge.tool vimdiff
git config --global mergetool.vimdiff.layout "(LOCAL,BASE,REMOTE)/MERGED"
git config --global mergetool.keepBackup false

# 충돌 발생 시 실행
git mergetool
# → Vim이 4분할로 열림: 상단 LOCAL|BASE|REMOTE, 하단 MERGED

# Neovim diffview.nvim 대안
# :DiffviewOpen — 변경 파일 목록 + diff 뷰
# :DiffviewFileHistory — 파일별 커밋 이력

diff 명령 요약

명령설명
]c다음 변경 영역(hunk)으로 점프
[c이전 변경 영역으로 점프
dodiffget — 상대 윈도우에서 변경 가져오기 (diff obtain)
dpdiffput — 현재 변경을 상대 윈도우로 보내기
:diffget LO3-way에서 LOCAL 버전 선택
:diffget BA3-way에서 BASE 버전 선택
:diffget RE3-way에서 REMOTE 버전 선택
:diffupdate직접 편집 후 diff 하이라이트 갱신
zo / zc변경 없는 접힌 영역 열기/닫기

conflict markers 처리

" conflict markers 검색
/<<<<<<<\|=======\|>>>>>>>

" fugitive 머지 충돌 해결
:Gvdiffsplit!          " 3-way vertical diff (LOCAL | MERGED | REMOTE)
:Gdiffsplit!           " 3-way horizontal diff

" MERGED 윈도우에서 선택:
:diffget //2           " LOCAL(왼쪽) 버전 선택
:diffget //3           " REMOTE(오른쪽) 버전 선택

" 모든 충돌 해결 후 저장하고 다음 파일로
:wqa                   " 모든 윈도우 저장 후 종료

" conflict markers 자동 점프 매핑
nnoremap <leader>cn /<<<<<<<<CR>   " 다음 충돌
nnoremap <leader>cp ?<<<<<<<<CR>   " 이전 충돌
팁: 커널 서브시스템별 브랜치를 머지할 때 충돌이 많으면, git mergetool 대신 :Gvdiffsplit!(fugitive)을 사용하세요. 충돌 파일 목록에서 하나씩 열어 :diffget //2 또는 //3으로 빠르게 해결할 수 있습니다. Neovim 사용자는 diffview.nvim이 더 편리합니다.

원격 편집 (netrw/scp)

Vim에 내장된 netrw 플러그인은 SCP, SFTP, rsync 등 다양한 프로토콜을 통해 원격 서버의 파일을 로컬 Vim에서 직접 편집할 수 있게 합니다. 커널 빌드 서버나 임베디드 타겟 장비의 파일을 로컬 개발 환경에서 수정할 때 유용합니다.

netrw 원격 파일 접근 흐름 Vim Editor :edit scp://... netrw Plugin 프로토콜 파싱 프로토콜 선택 scp:// → scp sftp:// → sftp rsync:// → rsync Remote Server 파일 read/write 파일 내용 → 로컬 버퍼 갱신 (임시 파일 기반) :w 저장 시 임시 파일 → 프로토콜 전송 → 원격 서버 덮어쓰기 SSH 키 인증 설정 필수 (비밀번호 프롬프트 회피) 디렉토리 URL: vim scp://host//path/ → 원격 디렉토리 탐색 가능

netrw 기본 사용법

# SCP를 통한 원격 파일 편집
vim scp://user@buildserver//home/user/kernel/drivers/net/e1000.c
# 주의: 호스트 뒤 // = 절대 경로, / = 상대 경로(홈 기준)

# SFTP 프로토콜 사용
vim sftp://user@host/relative/path/file.c

# rsync 프로토콜 (빠른 전송)
vim rsync://user@host//absolute/path/file.c

# Vim 내에서 원격 파일 열기
# :e scp://buildserver//var/log/dmesg
# :split scp://target//proc/config.gz

원격 디렉토리 탐색

" 원격 디렉토리 탐색 (끝에 / 필수)
:Explore scp://buildserver//home/user/linux/
:Sexplore scp://host//path/     " 수평 분할로 열기
:Vexplore scp://host//path/     " 수직 분할로 열기

" 로컬 netrw 디렉토리 탐색 명령 (동일하게 동작)
:Explore           " 현재 디렉토리
:Explore .         " 현재 작업 디렉토리
:Lexplore          " 왼쪽 사이드바로 파일 탐색기

" netrw 디렉토리 뷰 조작
i                  " 목록 스타일 순환 (thin/long/wide/tree)
s                  " 정렬 순서 변경 (name/time/size)
d                  " 새 디렉토리 생성
%                  " 새 파일 생성
R                  " 이름 변경
D                  " 삭제

netrw 설정 (.vimrc)

" netrw 기본 설정
let g:netrw_liststyle = 3       " 트리 형태 표시
let g:netrw_browse_split = 4    " 이전 윈도우에서 파일 열기
let g:netrw_winsize = 25        " 탐색기 너비 25%
let g:netrw_banner = 0          " 상단 배너 숨기기
let g:netrw_altv = 1            " 수직 분할 시 오른쪽에 열기

" 원격 편집 관련 설정
let g:netrw_scp_cmd = "scp -q"              " scp 조용한 모드
let g:netrw_sftp_cmd = "sftp"                " sftp 명령
let g:netrw_rsync_cmd = "rsync -e ssh"       " rsync SSH 터널
let g:netrw_silent = 1                        " 전송 메시지 숨기기

" SSH 설정 (~/.ssh/config 활용 권장)
" Host buildserver
"   HostName 192.168.1.100
"   User developer
"   IdentityFile ~/.ssh/id_ed25519
"   ControlMaster auto
"   ControlPath /tmp/ssh-%r@%h:%p
"   ControlPersist 600

" 위 설정 후: vim scp://buildserver//path/file.c

프로토콜 비교

프로토콜URL 형식특징권장 용도
scp://scp://host//path단순·안정적, 파일 단위 전송단일 파일 편집
sftp://sftp://host/path디렉토리 탐색 지원, 방화벽(Firewall) 친화적원격 디렉토리 탐색
rsync://rsync://host//path차분 전송, 대용량 파일에 효율적대용량 로그 파일
ftp://ftp://host/path레거시, 비암호화사용 비권장
주의: netrw 원격 편집은 파일을 임시 디렉토리에 다운로드한 뒤 편집하고, 저장 시 다시 업로드하는 방식입니다. 네트워크 끊김 시 변경이 유실될 수 있으므로, 대규모 편집 작업에는 sshfs로 원격 파일시스템(Filesystem)을 마운트(Mount)하거나 rsync 동기화를 사용하세요.
커널 개발 팁: 빌드 서버에서 원격 컴파일 결과를 확인할 때, :e scp://buildserver//home/user/build.log로 로그를 즉시 열어볼 수 있습니다. SSH ControlMaster를 설정하면 연결 재사용으로 속도가 크게 향상됩니다.

Termdebug 고급

기본 Termdebug 섹션에서 다룬 기초 사용법을 넘어, 조건부 중단점, 워치포인트, 멀티스레드, 원격 디버깅, 커널 모듈(Kernel Module) 심볼 로드 등 고급 기법을 설명합니다.

조건부 중단점과 워치포인트

" 조건부 중단점: 특정 조건일 때만 정지
:Break                          " 커서 위치에 중단점 설정
" GDB 윈도우에서 조건 추가:
"   condition 1 count > 100
"   condition 2 ptr == NULL
"   condition 3 strcmp(name, "eth0") == 0

" Vim에서 직접 GDB 명령 전송
:call TermDebugSendCommand('break drivers/net/e1000/e1000_main.c:542 if adapter->num_rx_queues > 4')

" 워치포인트: 변수 값 변경 시 정지
:call TermDebugSendCommand('watch skb->len')             " write 워치포인트
:call TermDebugSendCommand('rwatch skb->data')            " read 워치포인트
:call TermDebugSendCommand('awatch netdev->flags')         " access 워치포인트 (read+write)

" 중단점 목록과 관리
:call TermDebugSendCommand('info breakpoints')
:call TermDebugSendCommand('delete 3')                   " 3번 중단점 삭제
:call TermDebugSendCommand('disable 1')                  " 1번 중단점 비활성화

멀티스레드 디버깅

" 커널 워커 스레드, kthread 디버깅
:call TermDebugSendCommand('info threads')               " 모든 스레드 목록
:call TermDebugSendCommand('thread 3')                   " 3번 스레드로 전환
:call TermDebugSendCommand('thread apply all bt')        " 모든 스레드 backtrace
:call TermDebugSendCommand('thread apply 1 3 5 print $rip')  " 특정 스레드들의 PC

" 스레드 스케줄링 제어
:call TermDebugSendCommand('set scheduler-locking on')   " 현재 스레드만 실행
:call TermDebugSendCommand('set scheduler-locking off')  " 모든 스레드 실행 (기본)
:call TermDebugSendCommand('set scheduler-locking step') " step 시 현재만 실행

원격 디버깅과 커널 모듈

" QEMU + GDB stub 원격 디버깅
:Termdebug vmlinux
" GDB 윈도우에서:
"   target remote :1234
"   break start_kernel
"   continue

" gdbserver를 사용한 원격 타겟 디버깅
" 타겟에서: gdbserver :2345 ./my_daemon
:call TermDebugSendCommand('target remote 192.168.1.50:2345')

" 커널 모듈 심볼 로드 (GDB python 확장)
:call TermDebugSendCommand('source scripts/gdb/vmlinux-gdb.py')
:call TermDebugSendCommand('lx-symbols')                " 로드된 모든 모듈 심볼 자동 로드

" 수동 모듈 심볼 로드
" 모듈 .text 섹션 주소 확인:
"   cat /sys/module/e1000/sections/.text → 0xffffffffa0000000
:call TermDebugSendCommand('add-symbol-file drivers/net/ethernet/intel/e1000/e1000.ko 0xffffffffa0000000')

" 커널 데이터 구조 탐색 (lx- 명령)
:call TermDebugSendCommand('lx-dmesg')                  " 커널 로그
:call TermDebugSendCommand('lx-lsmod')                  " 로드된 모듈 목록
:call TermDebugSendCommand('lx-ps')                     " 프로세스 목록
팁: Termdebug 고급 기능을 효과적으로 사용하려면, GDB 윈도우에서 직접 명령을 입력하는 것과 :call TermDebugSendCommand()를 적절히 혼용하세요. 자주 쓰는 명령은 nnoremap으로 매핑하면 편리합니다. 예: nnoremap <leader>dt :call TermDebugSendCommand('info threads')<CR>

Kbuild/Kconfig 문법 강조

리눅스 커널의 빌드 시스템(Build System)은 Kbuild(Makefile 기반)와 Kconfig(설정 언어)로 구성됩니다. Vim에서 이들 파일을 효과적으로 편집하기 위한 문법 강조, 탐색, 빌드 연동 설정을 다룹니다.

Kconfig 파일 편집

" Kconfig 파일 syntax highlighting 설정
" Vim은 Kconfig 파일을 자동 인식하지 못하므로 수동 설정 필요
autocmd BufRead,BufNewFile Kconfig*,Config.in set filetype=kconfig
autocmd BufRead,BufNewFile Kconfig* setlocal tabstop=8 noexpandtab

" Kconfig 키워드 하이라이트 (커스텀 syntax 파일이 없을 때)
autocmd FileType kconfig syntax keyword kconfigKeyword config menuconfig
  \ menu endmenu choice endchoice source
autocmd FileType kconfig syntax keyword kconfigType bool tristate string int hex
autocmd FileType kconfig syntax keyword kconfigProp default depends select
  \ imply range help prompt visible

" Kconfig에서 CONFIG_ 심볼 검색 매핑
nnoremap <leader>kc :grep -r "config \<lt>" . --include="Kconfig*"<CR>:copen<CR>

Makefile 탐색과 빌드 연동

" Makefile에서 include 파일 추적
" gf — 커서 아래 파일명으로 점프
" gF — 줄 번호 포함 점프 (filename:42)
set path+=include,arch/x86/include,arch/arm64/include
set suffixesadd+=.h,.c,.S

" :make으로 커널 빌드 → quickfix에 오류 로드
set makeprg=make\ -C\ /path/to/linux\ -j$(nproc)\ 2>&1
:make drivers/net/e1000/      " 특정 디렉토리만 빌드
:copen                         " quickfix에서 오류 목록 확인
:cn                            " 다음 오류로 점프
:cp                            " 이전 오류로 점프
:cc 3                          " 3번 오류로 점프

" 커널 빌드 전용 errorformat 설정
set errorformat=%f:%l:%c:\ %m,%f:%l:\ %m

.config 편집 팁

작업Vim 명령설명
CONFIG_ 검색/CONFIG_E1000특정 설정 찾기
활성화 토글:s/# CONFIG_\(.*\) is not set/CONFIG_\1=m/비활성 → 모듈 전환
전체 모듈 확인:vimgrep /=m$/ .config | copen모듈로 설정된 항목 목록
비활성 항목 수:%s/is not set//gn비활성 설정 개수 확인 (n=카운트)
주석 제거:g/^#/d모든 주석 줄 삭제
참고: .config 파일을 직접 편집하는 것보다 make menuconfig, make nconfig, 또는 scripts/config --enable CONFIG_XXX를 사용하는 것이 안전합니다. Vim에서 직접 편집 시 의존성이 깨질 수 있으며, 편집 후 반드시 make olddefconfig를 실행하여 의존성을 재검증하세요.

플러그인 진단

플러그인 수가 늘어나면 Vim 시작 시간이 느려지고, 설정 충돌이 발생할 수 있습니다. 문제의 원인을 체계적으로 추적하고 성능을 최적화하는 진단 기법을 소개합니다.

로드된 스크립트 확인

" 현재 로드된 모든 스크립트 파일 목록
:scriptnames
" 출력 예시:
"   1: /usr/share/vim/vim91/filetype.vim
"   2: ~/.vimrc
"   3: ~/.vim/pack/plugins/start/fugitive/plugin/fugitive.vim
"  ...

" 특정 옵션이 어디서 설정되었는지 추적
:verbose set tabstop?
" → tabstop=8
"       Last set from ~/.vimrc line 42

:verbose set filetype?
:verbose map <leader>ff
" → 어떤 플러그인/설정 파일이 해당 매핑을 정의했는지 표시

시작 시간 프로파일링

# Vim 시작 시간 측정
vim --startuptime startup.log +q
# startup.log 상위 항목이 느린 플러그인

# Neovim 시작 시간 (더 상세)
nvim --startuptime startup.log +q

# 결과 분석: 시간순 정렬
sort -k2 -n startup.log | tail -20

# 특정 플러그인 없이 시작 (문제 격리)
vim -u NONE                   # 설정 없이 시작
vim -u NORC                   # vimrc 없이, 플러그인은 로드
vim --noplugin                # 플러그인 없이 시작
vim -u ~/.vimrc-minimal       # 최소 설정으로 시작

런타임 프로파일링

" 함수/파일 수준 프로파일링
:profile start profile.log
:profile func *                " 모든 함수 프로파일링
:profile file *                " 모든 스크립트 파일 프로파일링

" 작업 수행 후 종료 → profile.log 분석
:qa

" profile.log에서 느린 함수 확인:
" FUNCTION  airline#check_mode()
"     Called 1523 times
"     Total time:   0.892345
"     Self time:    0.234567

진단 명령 요약

명령용도비고
:scriptnames로드된 스크립트 목록번호순 출력
:verbose set opt?옵션 설정 출처 추적파일명+줄 번호 표시
:verbose map key키 매핑 출처 추적충돌 매핑 식별
vim --startuptime시작 시간 측정플러그인별 시간
:profile런타임 성능 프로파일함수/파일 단위
:messages이전 메시지/오류 확인플러그인 오류 추적
:checkhealthNeovim 환경 진단의존성/설정 검증
:echo has('feature')Vim 기능 지원 확인python3, lua 등
주의: :profile은 Vim의 +profile 기능이 컴파일에 포함되어야 합니다. :echo has('profile')로 확인하세요. Neovim은 기본 내장되어 있습니다.
팁: Vim 시작이 500ms 이상 걸린다면 개선이 필요합니다. --startuptime에서 100ms 이상 소요되는 플러그인을 식별하고, 지연(Latency) 로드(lazy loading)를 적용하세요. vim-plugPlug 'plugin', {'on': 'Command'}lazy.nvimevent/cmd/ft 트리거를 활용합니다.

tmux/zellij 통합 워크플로

커널 개발에서는 편집, 빌드, 테스트, 로그 모니터링을 동시에 수행해야 합니다. tmux와 Vim을 통합하면 하나의 터미널 세션에서 모든 작업을 효율적으로 관리할 수 있습니다.

tmux + Vim 커널 개발 워크플로 [session: kernel-dev] [window: 0:edit] [panes: 3] Pane 0: Vim Editor (65%) 42 static int __init e1000_init(void) 43 { 44 return pci_register_driver(&e1000_drv); 45 } ~ ~ ~ ~ ~ buffers: e1000_main.c | e1000.h | Makefile Ctrl+h/j/k/l → tmux pane 간 이동 (vim-tmux-navigator) Pane 1: Build (20%) $ make -j$(nproc) M=drivers/net CC drivers/net/e1000/e1000_main.o LD drivers/net/e1000/e1000.ko Build complete. 0 errors. Pane 2: Git/Log (15%) $ git log --oneline -5 a1b2c3d fix: e1000 rx buf e4f5g6h refactor: cleanup $ tail -f /var/log/kern.log

tmux 커널 개발 세션 구성

#!/bin/bash
# kernel-tmux.sh — 커널 개발용 tmux 세션

SESSION="kernel-dev"
KERNEL_DIR="$HOME/linux"

tmux new-session -d -s $SESSION -c $KERNEL_DIR

# 메인 윈도우: 편집 + 빌드 + 로그
tmux rename-window -t $SESSION:0 "edit"
tmux send-keys -t $SESSION:0 "vim ." C-m
tmux split-window -h -t $SESSION:0 -p 35 -c $KERNEL_DIR
tmux split-window -v -t $SESSION:0.1 -p 50 -c $KERNEL_DIR
tmux send-keys -t $SESSION:0.2 "git log --oneline -20" C-m

# 두 번째 윈도우: QEMU + GDB
tmux new-window -t $SESSION:1 -n "debug" -c $KERNEL_DIR
tmux send-keys -t $SESSION:1 "# qemu-system-x86_64 -kernel arch/x86/boot/bzImage -s -S ..." C-m

# 세 번째 윈도우: 문서/레퍼런스
tmux new-window -t $SESSION:2 -n "docs" -c $KERNEL_DIR/Documentation

tmux select-window -t $SESSION:0
tmux select-pane -t 0
tmux attach -t $SESSION

vim-tmux-navigator 통합 이동

" vim-tmux-navigator: Vim 윈도우와 tmux pane 간 통합 이동
" 설치: Plug 'christoomey/vim-tmux-navigator'

" Ctrl+h/j/k/l로 Vim 윈도우 ↔ tmux pane 자유 이동
" Vim 분할 윈도우에서 끝에 도달하면 자동으로 tmux pane으로 전환

" tmux.conf에 추가 (tmux 측 키 바인딩)
" is_vim="ps -o state= -o comm= -t '#{pane_tty}' \
"     | grep -iqE '^[^TXZ ]+ +(\\S+\\/)?g?(view|n?vim?x?)(diff)?$'"
" bind-key -n C-h if-shell "$is_vim" "send-keys C-h"  "select-pane -L"
" bind-key -n C-j if-shell "$is_vim" "send-keys C-j"  "select-pane -D"
" bind-key -n C-k if-shell "$is_vim" "send-keys C-k"  "select-pane -U"
" bind-key -n C-l if-shell "$is_vim" "send-keys C-l"  "select-pane -R"

" Vim에서 tmux pane으로 명령 전송
" 설치: Plug 'preservim/vimux'
nnoremap <leader>mb :VimuxRunCommand("make -j$(nproc) M=drivers/net")<CR>
nnoremap <leader>ml :VimuxRunLastCommand<CR>
nnoremap <leader>mi :VimuxInspectRunner<CR>

tmux vs zellij 비교

기능tmuxzellij
설정 언어자체 문법 (tmux.conf)KDL (zellij/config.kdl)
레이아웃 저장수동 스크립트내장 레이아웃 파일 지원
플러그인TPM (tmux plugin manager)WASM 플러그인
Vim 통합vim-tmux-navigator (성숙)zellij-nav.vim (초기)
성능경량, 안정적Rust 기반, 빠른 렌더링
러닝 커브높음 (키 바인딩 학습)낮음 (UI 힌트 제공)
커널 개발 추천권장 (생태계 성숙)대안 (기능 발전 중)
주의: tmux의 C-l(화면 클리어)이 vim-tmux-navigator와 충돌합니다. tmux.confbind C-l send-keys 'C-l'을 추가하거나, prefix + C-l로 클리어를 바인딩하세요.
팁: 커널 개발 시 tmux 세션을 tmux-resurrecttmux-continuum 플러그인으로 자동 저장/복원하면, 시스템 재부팅 후에도 작업 환경을 즉시 복구할 수 있습니다. 빌드 서버에 SSH 접속하는 경우 tmux attach로 이전 세션에 바로 복귀합니다.

LSP 고급 기능

Language Server Protocol(LSP)은 편집기와 언어 분석 서버 간의 표준 프로토콜입니다. 커널 개발에서는 clangd를 LSP 서버로 사용하여 코드 탐색, 자동완성, 리팩토링을 수행합니다. 여기서는 커널 소스에 맞는 LSP 고급 설정을 다룹니다.

LSP 클라이언트-서버 아키텍처 (커널 개발) LSP 클라이언트 coc.nvim nvim-lspconfig vim-lsp JSON-RPC stdio/TCP clangd 서버 인덱싱 엔진 clang-tidy 통합 background indexer compile_commands.json 빌드 플래그, 인클루드 경로 커널 소스 파일 *.c, *.h (5만+ 파일) LSP 기능 (요청/응답) textDocument/definition 정의로 점프 textDocument/completion 자동완성 textDocument/references 참조 목록 textDocument/codeAction quick fix / 리팩토링 textDocument/inlayHint 타입/파라미터 힌트 workspace/symbol 프로젝트 심볼 검색

clangd 커널 설정

# compile_commands.json 생성 (커널 빌드 후)
# 먼저 커널 빌드 수행 (컴파일 명령 기록 필요)
make -j$(nproc) defconfig
make -j$(nproc)

# Python 스크립트로 compile_commands.json 생성
python3 scripts/clang-tools/gen_compile_commands.py

# 또는 Bear 사용 (빌드 과정 기록)
bear -- make -j$(nproc)

# clangd 실행 옵션 (.clangd 파일 또는 커맨드라인)
# 커널 루트에 .clangd 파일 생성:
cat <<'EOF' > .clangd
CompileFlags:
  Add:
    - -Wno-unknown-warning-option
    - -Wno-unused-command-line-argument
  Remove:
    - -mpreferred-stack-boundary*
    - -mindirect-branch*
    - -fno-allow-store-data-races
    - -fconserve-stack
Index:
  Background: Build
Diagnostics:
  ClangTidy:
    Add:
      - bugprone-*
      - performance-*
    Remove:
      - bugprone-easily-swappable-parameters
EOF

LSP 고급 기능 활용

" === nvim-lspconfig 기본 설정 (init.lua) ===
" require('lspconfig').clangd.setup{
"   cmd = {
"     'clangd',
"     '--background-index',         -- 백그라운드 인덱싱
"     '--clang-tidy',               -- clang-tidy 진단 통합
"     '--header-insertion=never',   -- 자동 헤더 삽입 비활성
"     '--completion-style=detailed', -- 상세 자동완성
"     '-j=4',                       -- 인덱싱 스레드 수
"   },
"   root_dir = require('lspconfig.util').root_pattern('compile_commands.json'),
" }

" Code Action (quick fix, 리팩토링)
nnoremap <leader>ca :lua vim.lsp.buf.code_action()<CR>
" → 미사용 변수 제거, 누락된 include 추가, 함수 추출 등

" Inlay Hints (Neovim 0.10+)
" vim.lsp.inlay_hint.enable(true)  -- 전역 활성화
" 코드에 파라미터 이름과 반환 타입이 인라인 표시됨:
"   alloc_skb(/*size=*/1500, /*priority=*/GFP_KERNEL)
"   int ret = register_netdev(dev)  →  int ret

" Workspace Symbols (프로젝트 전체 심볼 검색)
nnoremap <leader>ws :lua vim.lsp.buf.workspace_symbol()<CR>
" → 'netdev' 입력 시 관련 구조체, 함수, 매크로 전체 목록

" Document Symbols (현재 파일 심볼 개요)
nnoremap <leader>ds :lua vim.lsp.buf.document_symbol()<CR>
" → 현재 파일의 함수, 구조체, 매크로 트리 구조 표시

" Rename Symbol (프로젝트 전체 이름 변경)
nnoremap <leader>rn :lua vim.lsp.buf.rename()<CR>
" → 커서 아래 심볼의 모든 참조를 일괄 변경

coc.nvim vs nvim-lspconfig 비교

기능coc.nvimnvim-lspconfig
대상 에디터Vim 8+ / NeovimNeovim 전용
설정 언어JSON (coc-settings.json)Lua (init.lua)
의존성Node.js 필수Neovim 내장 LSP
자동완성내장 (VS Code 스타일)nvim-cmp 별도 설치
확장 생태계coc extensions (npm)Neovim 플러그인
성능Node.js 프로세스(Process) 상주네이티브, 경량
설정 복잡도낮음 (out-of-box)중간 (수동 구성 필요)
커널 개발 추천Vim 사용자Neovim 사용자 권장

LSP 서버 성능 튜닝

" clangd 성능 최적화 (대규모 커널 소스)
" .clangd 파일 또는 lspconfig cmd 옵션:
"   --background-index       : 백그라운드에서 전체 프로젝트 인덱싱
"   --pch-storage=memory     : PCH를 메모리에 캐싱 (SSD 부하 감소)
"   --log=error              : 로그 레벨 최소화
"   -j=4                     : 동시 인덱싱 스레드 (CPU 코어에 맞춤)
"   --malloc-trim            : 유휴 시 메모리 반환

" 인덱스 캐시 위치 (재시작 시 빠른 로드)
"   --compile-commands-dir=/path/to/kernel

" Neovim LSP 클라이언트 측 최적화
" vim.lsp.handlers['textDocument/publishDiagnostics'] =
"   vim.lsp.with(vim.lsp.diagnostic.on_publish_diagnostics, {
"     update_in_insert = false,   -- Insert 모드에서 진단 갱신 비활성
"     severity_sort = true,        -- 심각도순 정렬
"   })

" coc.nvim 성능 설정 (coc-settings.json)
" {
"   "clangd.arguments": [
"     "--background-index",
"     "--clang-tidy",
"     "--pch-storage=memory",
"     "-j=4"
"   ],
"   "diagnostic.refreshOnInsertMode": false
" }
주의: 커널 소스 전체(5만+ 파일)를 clangd로 인덱싱하면 초기 인덱싱에 수 GB 메모리수십 분이 소요됩니다. --background-index를 사용하면 인덱스가 .cache/clangd/index/에 캐싱되어 이후 시작이 빨라집니다. 메모리가 부족하면 -j 값을 줄이세요.
팁: compile_commands.json이 없으면 clangd가 커널 헤더를 찾지 못해 대부분의 LSP 기능이 동작하지 않습니다. 커널 빌드 후 반드시 scripts/clang-tools/gen_compile_commands.py를 실행하세요. 빌드 설정이 바뀌면(defconfig → allmodconfig 등) 재생성이 필요합니다.
자료설명URL/명령
Vim 내장 도움말가장 정확하고 완전한 참조:help, :help quickref
vimtutor30분 대화형 입문 튜토리얼셸에서 vimtutor
Vim 공식 사이트Vim 다운로드, 뉴스, 공식 안내Vim — the ubiquitous text editor (vim.org)
Vim 공식 문서Vim 온라인 레퍼런스 매뉴얼Vim help files (vimhelp.org)
Vim FAQ자주 묻는 질문과 답변 모음Vim FAQ (vimhelp.org)
Vim Tips Wiki커뮤니티 기반 Vim 팁 1,600여 개Vim Tips Wiki (vim.fandom.com)
VimcastsDrew Neil의 Vim 동영상 튜토리얼 시리즈Vimcasts — Free screencasts about Vim (vimcasts.org)
Practical VimDrew Neil 저. 실전 팁 모음서적 (2판, 2015)
Learn Vimscript the Hard WaySteve Losh의 Vimscript 프로그래밍 학습서Learn Vimscript the Hard Way (stevelosh.com)
Learn Vim the Smart Way현대적 Vim 학습 가이드Learn-Vim (github.com/iggredible)
Neovim 공식 사이트Neovim 프로젝트 홈페이지 및 다운로드Neovim — hyperextensible Vim-based text editor (neovim.io)
Neovim 공식 문서Neovim 사용자 가이드, API, Lua 레퍼런스Neovim Documentation (neovim.io)
Neovim LSP 가이드Neovim 내장 LSP 클라이언트 설정 가이드LSP — Neovim docs (neovim.io)
vim-galoreVim 지식 모음집 — 초보부터 고급까지vim-galore (github.com/mhinz)
Vim AwesomeVim 플러그인 검색 및 인기 순위 사이트Vim Awesome (vimawesome.com)
vim-plug경량 Vim 플러그인 매니저vim-plug — Minimalist Plugin Manager (github.com/junegunn)
lazy.nvimNeovim용 현대적 플러그인 매니저lazy.nvim — Modern plugin manager (github.com/folke)
ctags (Universal Ctags)소스 코드 태그 생성 — 심볼 점프에 필수Universal Ctags (ctags.io)
cscopeC 코드 탐색 도구 — 호출자/피호출자 추적Cscope (cscope.sourceforge.net)
커널 cscope/ctags 가이드커널 소스에서 cscope/ctags 활용법커널 Makefile: make cscope, make tags
clangd LSP 서버C/C++ 코드 분석 — 커널에서도 활용 가능clangd — C/C++ language server (clangd.llvm.org)
커널 편집 가이드커널 문서에 포함된 에디터 설정 및 코딩 스타일Linux kernel coding style (kernel.org)
Vim과 커널 개발 (LWN)커널 개발자의 에디터 활용 사례Kernel development articles (lwn.net)
Vim GitHub 저장소Vim 소스 코드 및 이슈 트래커vim/vim — Vim source code (github.com)
Neovim GitHub 저장소Neovim 소스 코드 및 개발 현황neovim/neovim — Neovim source (github.com)
" Vim의 내장 도움말은 매우 상세하고 체계적입니다

" 도움말 열기
:help                    " 기본 도움말
:help topic              " 특정 주제 검색
:help :command           " Ex 명령 도움말 (: 접두사)
:help 'option'           " 옵션 도움말 (작은따옴표)
:help function()         " 함수 도움말 (괄호)
:help ctrl-w             " Normal 모드 키 도움말
:help i_ctrl-n           " Insert 모드 키 (i_ 접두사)
:help v_d                " Visual 모드 키 (v_ 접두사)
:help c_ctrl-r           " Command-line 모드 키 (c_ 접두사)
:help t_ctrl-w           " Terminal 모드 키 (t_ 접두사)

" 도움말 탐색
Ctrl-]                   " 도움말 태그로 점프 (하이라이트된 키워드)
Ctrl-T                   " 이전으로 복귀
Ctrl-O                   " 점프 리스트에서 뒤로

" 유용한 도움말 주제
:help quickref           " 빠른 참조 (전체 요약)
:help text-objects       " 텍스트 오브젝트 상세
:help pattern            " 정규식 상세
:help registers          " 레지스터 상세
:help motion             " 이동 명령 상세
:help usr_01             " 사용자 매뉴얼 첫 장
:help tips               " 팁 모음
:help index              " 모든 명령의 알파벳순 색인

" 도움말 검색 (주제를 모를 때)
:helpgrep pattern        " 도움말 내용 검색
:helpclose               " 도움말 윈도우 닫기
" Tab으로 자동완성: :help tex → Tab → text-objects 등

" Neovim 추가 도움말
:help lsp                " LSP 클라이언트
:help treesitter         " Treesitter 구문 분석
:help lua-guide          " Lua 확장 가이드
:checkhealth             " 환경 진단
팁: Vim을 처음 배울 때 가장 중요한 습관은 "모르면 :help를 치자"입니다. 예를 들어 "비주얼 블록에서 뭘 할 수 있지?"라면 :help visual-operators, "이 옵션은 뭐지?"라면 :help 'tabstop'을 입력하세요. 웹 검색보다 정확하고 Vim 버전에 맞는 정보를 얻을 수 있습니다.