vim
一直是程序員之間比較有爭議的一個話題。有人認爲她是編輯器之神,有人則認爲她古老過時,遠遠不如IDE,或是以當紅小生vscode
爲代表的圖形化文本編輯器。無論愛恨,我們的開發工作,大到遠程登錄服務器coding
,修改config
文件,小到git commit message
,或多或少總要接觸她。
爲什麼要寫這篇blog呢,是因爲我發現各大平臺充斥的vim
教程類blog其實很不友好,大多數是命令的堆砌,很少有對思想的解讀。由此造成的結果,很多工程師對vim
總是敬畏三分,或者就算部分人可以使用vim
,也只是以自己的固有思維,結合vim的命令操作,並沒有真正掌握vim
的精髓。所以我嘗試用自己的方式,試圖幫助大家系統地建立起vim
的知識系統。
文章主要結構如下:
- 首先介紹
vim
編輯器最簡單、基本的操作,讓你快速入門,在遇到vim
時,可以不至於驚慌,從容完成任務。如果想到某些操作,比如常用的複製、粘貼之類,可以到第二部分查找對應高階操作,循序漸進使用vim
。 - 接下來,將會介紹
vim
一系列高級操作,將我們的效率最大化。注:這部分信息量較大,建議您在閱讀部分內容後,快速瀏覽不熟悉的命令,做到心中有數;隨後可前進到第三部分;回過頭來,再循序漸進,邊學邊練。 - 最後,試圖講述
vim
思想的精髓,既讓我們真正對vim
的操作融會貫通,又讓我們可以在使用其他IDE/編輯器時應用這些思想,甚至在我們自己設計、實現功能、組件時,都能進行應用。這纔算真正掌握了神器vim
。
模式
vim有三個模式,分別爲普通(正常)模式、插入模式以及命令模式。
- 普通模式:一般用於瀏覽文件,也包括一些複製、粘貼、除等操作。
- 插入模式:主要用來輸入、修改、刪除字符,此時的操作,除了不能用鼠標外,與我們日常在編輯器中操作無異。普通模式下,通過
i
等命令進入插入模式。 - 命令模式:用以執行一些輸入並執行一些vim或插件提供的指令。在普通模式下通過輸入
:
後,可以發現,屏幕的右下角會出現:
,此時便進入了命令模式。本文中使用:
開頭的命令,便可視爲輸入:
進入命令模式後,輸入後面的字符,執行命令。
很多人對於vim
的第一點疑惑,便來源於此。我們習慣了圖形化編輯器下,始終處於插入狀態。然而在vim
中,大多數情況下,我們會處於正常模式。只有當需要輸入字符時,進入插入模式;當需要使用命令時,進入到命令模式。在插入和命令模式下,輸入Esc
便可返回正常模式。一張圖概括如下:
注:後文講解,如無特殊說明,均爲普通模式下操作。
基本操作
如果不追求效率,只想完成修改文件的任務,並保存退出,只需要掌握以下三個命令:
- 移動:
h j k l
最簡單的移動,相當於鍵盤上面的方向鍵,分別對應左下上右。 - 進入插入模式:
i
- 保存退出:
ZZ
(注意區分大小寫)
流程如下:
- 普通模式下,通過
h j k l
移動到想要修改的位置 - 輸入
i
進入插入模式,此時通過輸入(字母、數字、符號),刪除(Backspace)等,完成基本修改操作 -
Esc
回到普通模式,ZZ
,保存修改並退出。
進階:命令形式
普通模式下,vim
的命令主要分爲以下三種:
-
動作,用以移動光標,或者定義操作的範圍;比如:
-
h
:定義操作範圍爲一格,單獨使用時,向左移動光標一格。 -
w
:定義操作範圍爲一個單詞,移動光標到下一個單詞首部。
-
-
操作,這種命令需要在後面接表示操作範圍的指令;
-
d
,刪除,後接表示一個單詞操作範圍的w
,即dw
時,表示刪除到當前詞尾。 -
c
,修改,後接表示一個單詞操作範圍的w
,即cw
時,表示修改當前單詞。(編輯器行爲表現爲,刪除到當前詞尾,同時進入插入模式)。
-
-
命令,直接執行的命令,其中一部分,在執行命令後,直接進入編輯模式;比如:
-
D
,刪除至行末。 -
I
,到行首進入插入模式。
我們的使用方式主要也是三種:命令、動作、操作+動作。
此外,在動作類的命令前,加上number
爲可選項,可實現重複n次
的效果:
-
[number] + h/j/k/l
向左/下/上/右
移動number
個字符。比如,'2j',向下移動光標2個字符。依舊使用
d
和w
來舉例,d
是刪除,w
是單詞,dw
代表刪除一個單詞,d2w
代表刪除兩個單詞。後面的命令,大多都可應用此種形式組合使用,大家多注意,養成這種操作 + [次數] + 範圍
的思維模式,舉一反三,便可發揮最大功效。
移動進階
單詞級別的移動
這裏有僅大小寫不同的兩組命令,兩組命令的功能,是相同的:跳轉光標到對應位置。但是對應的單位不同,分別爲word
和string
。具體區別是:
-
string
僅以空格分開; -
word
以字母數字以外的字符分開。
以這個字符串爲例:hello world-hehe111 abcde
-
word
有5個,分別爲hello
world
,-
,hehe111
和abcde
。 -
string
有3個,分別爲hello
world-hehe111
和abcde
。
兩組命令如下:(跳轉光標至)
-
w
下一個單詞開頭 -
e
當前或下一個單詞結尾 -
b
當前或上一個單詞開頭 -
ge
上一個單詞的結尾 -
W
下一個字符串的開頭 -
E
當前或下一個字符串結尾 -
B
當前或上一個字符串的開頭 -
GE
上一個字符串的結尾
舉個例子,當光標位於hehe111
的第一個字符h
時,前後的單詞/字符串信息如下:
前一個 | 當前 | 後一個 | |
---|---|---|---|
單詞 | - | hehe111 | abcde |
字符串 | hello | world-hehe111 | abcde |
那麼以上各個敲擊以上各個命令的結果,便一目瞭然(加粗字表示命令運行後光標位置):
句子,段落級別的移動
-
0
移動到當前行行首 -
^
移動到當前行的第一個非空字符 -
$
移動到當前行尾 -
(
跳轉到當前或前一個句子的開頭 -
)
跳轉到當前或下一個句子的結尾 -
{
跳轉到當前或前一個段落的開頭 -
}
跳轉到當前或下一個段落的結尾
- 這裏段落很容易理解,是以空行分隔開的。句子麻煩些,是按照句號來算的。
- 記得在這些命令前添加
d
試一下效果吧,掌握操作+範圍
這種命令形式吧。
頁面級別的移動
按行移動光標
-
gg
移動到文本第一行行首 -
G
移動到文本末行行首 -
[n] + %
:按百分比近似定位到某行,該行位於整個文件的n%
處 -
[n] + gg/G
跳轉到第n
行,常用。
要想用好上述幾個命令,有兩個簡單的建議:
- 結合命令:
ctrl-g
。該命令的作用是顯示當前行的位置信息(第幾行,相對整個文本行數的百分比)。 - 在命令模式下輸入以下命令,或在
~/.vimrc
中添加如下代碼片段
set nu " 顯示行號
set cursorline " 高亮光標所在行</pre>
顯示頁面內移動光標
-
H
:屏幕頂部行首 -
M
:屏幕中央行首 -
L
:屏幕底部行首
滾動與翻頁
-
ctrl-d/u
:前進/後退半頁 -
ctrl-f/b
:前進/後退整頁 -
ctrl+e
:上滾一行 -
ctrl+y
:下滾一行 -
zt
:使光標所在位置移動到屏幕的頂部(所有內容做位移) -
zz
:使光標所在位置移動到屏幕的中央(所有內容做位移) -
zb
:使光標所在位置移動到屏幕的頂部(所有內容做位移)
匹配
-
f+單個字符
:在本行內向右移動到指定字符 -
F+單個字符
:在本行內向左移動到指定字符 -
t+單個字符
:在本行內向右移動到指定字符的前一個字符 -
T+單個字符
:在本行內向左移動到指定字符的前一個字符 -
%
: 在“( )”、“[ ]”、“{ }”類符號的首尾間切換 -
*
和#
: 匹配光標當前所在的單詞,移動光標到下一個(或上一個)匹配單詞(*是下一個,#是上一個)。
Mark
-
m+[a~z]
:在當前光標做標記,如ma -
'+[mark]
:光標返回指定標記所在的行,如'a
,則光標返回到標記a
所在行首 - "`"+[mark]:光標返回指定標記
-
ctrl+o
:跳轉回光標前一個位置 -
ctrl+i
:跳轉回較新的光標位置 - 建議結合命令模式下如下兩個命令,可獲得更好體驗:
-
:marks
:顯示全部mark -
:delmarks [mark]
:刪除指定mark
-
編輯進階
進入插入模式
在不同位置進入插入模式
-
i
:在光標前插入字符 -
I
:在行首插入字符 -
a
:在光標後插入字符 -
A
:在行尾插入字符 -
o
:在光標下發插入空行 -
O
:在光標上方插入空行
使用修改命令進入插入模式
-
c
:修改,後面需要接範圍 -
c+w
:刪除光標位置單詞,並進入插入模式 -
c+l / s
:刪除光標位置字符,並進入插入模式 -
c+c / S
:刪除光標所在行,並進入插入模式 -
c+$ / C
:刪除光標位置到行尾的字符,並進入插入模式
-
r
: 替換當前字符。 -
R
:(進入replace模式)持續替換光標所在字符,直到使用ESC退出替換模式。
刪除
-
x
: 刪除當前位置或下一個位置的字符。 -
d
:刪除,屬於動作指令,後面需要加操作類指令。比如如下命令:-
de
:刪除到當前單詞結尾。 -
dw
:刪除到下一個單詞開始。 - 注意,此處與
de
的區別在於,dw
會刪除兩個單詞之間的空格。 -
daw
:刪除一個單詞,包含單詞的邊界(空格)。 -
d0
:刪除至行首。 -
d$ / D
:刪除至行尾。
-
-
da[
:刪除[ ]整個塊,包含符號本身; -
di[
:刪除[ ]塊,不包含符號本身; -
da/di +
' " { ( 等,也與接[類似,刪除整個區塊。唯一需要注意的,"和'僅僅在行內。 -
dt[x]
:在本行,刪除到[x]。比如,dt"
刪除到雙引號,dtf
,刪除到字母f
。d/foo
:在全文, 刪除到 “foo” 。
剪切
剪切操作其實就是我們之前講的刪除。也就是d
。刪除的內容,默認會存放到剪切板中。也就相當於進行了剪切。
進階操作符
從這裏大家可以看出,i
和a
的作用比較特殊,代表與區塊相關的某種操作。區別就在於,i
不包含區塊邊界符號。a
包含。這兩個操作符很重要,在後面的複製操作中還會用到。此外還有t
,/
。此外,i
和a
還可以接t
,此時t
表示一對xml標籤。i
:區塊,不包含邊界。a
:區塊,包含邊界。t
:"to",本行到哪裏。/
:接匹配,全文到哪裏。
粘貼
-
p
:粘貼到光標後,或下一行。 -
P
:粘貼到光標前,或前一行。
爲什麼會有光標前後或上下一行兩種情況呢?是因爲我們複製或剪切的內容有可能是字符串或者整行:
- 當複製內容爲字符串時,粘貼到光標前/後。
- 當複製內容爲整行時,粘貼到上/下一行。
複製
-
y
,複製,屬於操作,後面需要接動作來標識複製的範圍。比如:-
yw
:複製到當前單詞結尾。 -
ye
:從當前位置複製到本單詞的最後一個字符。 -
y$
:複製到當前行尾。 -
yy
或Y
:複製當前行。 -
nyy
:複製從光標所在行起的n行,注意n在最前面。
-
0y$
: 命令意味着:
-
0
→ 先到行頭 -
y
→ 從這裏開始拷貝 -
$
→ 拷貝到本行最後一個字符
當然也可以結合我們剛剛介紹的進階操作符來進行操作:
-
yi"
:複製兩個引號之間 -
yit
:複製兩個xml標籤之間 -
y/[x]
:複製到x。
剪切板
vim 有 12 個剪切板,分別是 0、1、2、...、9、a、“、+。:reg
:查看各個剪切板裏的內容。y
,p
默認使用 "剪切板中的內容。
"[n]y
:複製到剪切板n
中。"[n]p
:粘貼剪切板n
中的內容。
查看是否支持系統剪切板:
vim --version | grep "clipboard"
觀看輸出中,clipboard
前面是+
還是-
。若是-
,則說明不支持系統剪切板。
+
號剪切板比較特殊,是系統剪切板,用於與系統其他應用互動:
-
"+y
,將內容複製到系統剪切板,ctrl+v
將其粘貼到其他應用中,比如vs code
。 -
"+p
,將其他應用中複製的內容,粘貼到vim中。
可視模式
v
:進入可視模式。V
:進入行選擇模式。Crtl + v
:進入塊選擇模式。
進入可視模式後,可以通過之前的移動操作,來進行選擇。比如:hjkl
:前後左右選擇。$
:選擇到行尾。i"
:選擇兩個引號之間。
選擇後,可以使用
-
d
進行刪除/剪切, -
y
進行復制。 - 還可以使用以下很有意思的命令:
-
gU
:變大寫。 -
gu
:變小寫。 -
J
:把所有的行連接起來(變成一行)。 -
<
或>
:左右縮進。 -
=
:自動縮進 。
-
格式化
=
:調整格式化縮進。gg=G
:全文代碼格式化。
-
gg
,到文章開頭 -
=
,調整格式 -
G
,到文章結尾。
自動補全
編輯模式下Ctrl + n/p
出現提示,此時會出現補全的選項。按住Ctrl
不放,用n
和p
來遍歷提示選項,到達期待的選項後,無需其他操作,繼續輸入即可。
撤銷
-
u
:撤銷前一個動作 -
U
:撤銷當前行的一系列動作 -
CTRL-R
:Redo,意思就是我又不想撤銷了。
查找替換
-
/
: 查找,此時Terminal左下角會出現/
,在後面輸入想要查找的內容,回車即可。 -
?
:反向查找,同樣道理,左下角會出現?
。 -
/[search]\c
:忽略大小寫。比如:/test\c
,查找test,忽略大小寫 -
n
: 下一個匹配 -
N
: 前一個匹配
命令模式下:
-
s/old/new/
:用new
替換old
-
s/old/new/g
:全局替換 -
set hlsearch
:高亮搜索結果
宏錄製
qa
操作序列 q
, @a
, @@
-
qa
把你的操作記錄在寄存器a。
- 於是
@a
會replay被錄製的宏。 -
@@
是一個快捷鍵用來replay最新錄製的宏。
命令
-
:w
:保存修改 -
:wq
:保存修改並退出 -
ZZ
:保存修改並退出 -
q!
:不保存修改,強制退出 -
e!
:不保存修改,強制重新打開當前文件
大家可以看到,
!
的作用便在於,強制。除此以外,他還有另一個很強勢的功能,就是執行shell命令。具體信息,大家可以詳細閱讀下一節。
-
.
:重複執行前一個命令。這個命令很靈活、實用,建議多多嘗試。 -
:help [command]
:查看某命令的help此外,在命令行中執行如下命令,便可進入vim的教程。
vimtutor
外部命令
這是vim
的一個很神奇的功能,在編輯的時候可以與外部文本互動,甚至執行一些shell
命令。
-
:w [file-name]
:將當前內容輸出到指定文件中 -
:r [file-name]
:將另外一個文件的內容輸出到當前位置 -
:e filename
:vim下打開指定文本
-
ctrl+w, s
:水平拆分窗口 -
ctrl+w, v
:垂直拆分窗口 -
ctrl+w, ARROW(h,j,k,l或方向鍵)
:在窗口間切換光標。 -
ctrl+w, w
:在窗口間切換光標。 -
:qa
:關閉所有窗口。
-
:saveas
:另存爲。 -
:n/bn/bp
:在打開的多個文件間切換。
-
:![command]
:vim下執行某shell命令。 - 比如,
:!ls
,便會暫時切換到shell下,輸出當前目錄的文件名。此時輸入回車,便可退回當前vim
編輯的文件中。
如果你覺得這種輸入命令的方式還不夠過癮,vim
還提供了保留當前工作現場,直接進入shell的方式。這種命令一個典型的工作場景是,如我們編輯了一個文件,但是發現無法保存(沒有寫權限),此時可以先進入到shell下,執行類似chmod u+w [filename]
,的命令,爲當前用戶獲取該文件的寫權限,然後再回到 vim 保存剛剛的修改。 有如下兩種方法:
-
:shell
或:sh
,當退出當前 shell 時(比如exit
),就會回到 vim。 -
ctr-z
進入 shell,fg
退回 vim。
Config
這部分主要是一些vim的config。可以直接命令模式輸入,也可以保存到~/.vimrc
中,便可每次打開vim
自動應用。(其中一些命令是互相沖突的,請自行選擇有用的命令)。
syntax on # 開啓語法高亮
set nu[mber] # 顯示行號
set nonu[mber] # 隱藏行號
set cursorline # 高亮當前行
set ruler # 顯示光標位置信息
set noruler # 隱藏光標位置信息
set hlsearch # 高亮匹配
set nohlsearch # 取消高亮匹配
nohlsearch # 臨時取消高亮(只取消一次查詢的高亮)
set incsearch # 在輸入字符串過程中顯示匹配點
set nowrapscan # 找到文尾後停止查找
set wrapscan # 恢復爲到文尾後自動從頭開始
set ic/ignorecase) # 忽略大小寫
set noic/noignorecase # 區分大小寫</pre>
VIM思想
這部分主要是一些我在使用vim
過程中的一些思考和感悟,試圖盡力闡述出來。如果大家能有一些思考和收穫,說明我的思考是有意義的。如果大家有不同見解,十分歡迎拍磚交流。
Why Normal
- 爲什麼vim下,要放棄人們習慣的插入模式,使用命令模式呢?仔細想一想,其實原因很簡單:在沒有鼠標的年代,人們只能依靠鍵盤來移動光標,修改文本。
- 爲什麼現在有了鼠標,我們還要用正常模式呢?
- 工作內容覆蓋。我們每個人都認爲,工程師的工作,是寫代碼。然而,其實我們主要的工作,是讀,或者說,理解代碼。經調查,工程師日常工作中,讀:寫代碼的比例,爲10:1(參考《Clean Code》一書)。所以默認的普通模式,主要滿足佔比重更大的”讀“;遇到需要修改的時候,再進入編輯模式。
- 大量快捷鍵。相信每個人,都最起碼知道一組快捷鍵:
ctrl-c/v
,也就是我們熟悉的copy & paste. 如果你平時注重效率,養成了快捷鍵的習慣,還可能知道一些諸如ctrl-a/x/s/w
等。不知你注意到沒有,如剛剛列舉的很多快捷鍵,都由特殊的命令符+字母
構成。因爲在此時,鍵盤上的大多數按鍵,都是可以輸入到文本中的字符。而在vim
的正常模式下面,無法直接向文件中輸入這些字符,相當於不用按ctrl
等特殊的命令符,直接可以把這些按鍵,用作命令的快捷鍵。
合理的快捷鍵
- vim中的快捷鍵,佈局非常合理。根據使用頻繁程度,調整距離手邊的距離。比如,最基礎的移動操作,放在手邊的
HJKL
。雖然移動將手移動到鍵盤上的方向鍵,並未真正的浪費多少時間,但是其對思維的打斷,其實非常影響效率。 - 快捷鍵的設置,也是非常合理,結合了單詞的意義、讀音,非常便於記憶。比如:
-
d
delete -
c
change -
w
word -
e
end -
b
back -
I
edit -
f
find -
r
replace
-
精細化,多維度命令
快捷鍵應有盡有,各個維度移動,都切合使用者思維,幾乎可以做到”指哪兒打哪兒“。
比如,移動、刪除、複製、等等操作,都可以結合精細化的位置,根據符合人類思維的不同維度,進行操作。
比如,字符,單詞,行,文章,屏幕,匹配(位置、文字、符號),以及類似書籤的Mark等。
原子、組合命令
vim
的大部分快捷鍵,都是院子操作,並通過與範圍結合,排列組合,靈活多變,完成各種強大的功能。這也與unix
的主要思想契合:每個命令做好,且只做好同一件事。
與此同時,通過用數字和宏,代替無意義的重複。
此外,對一些常用操作,提供了現成的宏,方便操作。比如,dd
,是刪除整行,同時也可以直接用D
來完成。I
,A
等,也是類似道理。
外部命令
類似棧的思路,可以放下當前操作,保存現場,然後進入另一個操作。當操作完成後,回到當前現場。
思維模式
vim
的快捷鍵,或者說命令,不僅很符合我們的思維,而且還能在很大程度上擴展我們的思維。
拿編輯代碼時最多的操作,移動光標來說。原來我們的移動,基本就是通過鍵盤的方向鍵,上下左右,或者通過鼠標,移動到想要去的位置。而在vim中,你會發現,光標除了上下左右,還可以移動到詞首,詞尾,句首,句尾,行首,行尾,頁面首部,頁面中部,頁面尾部,文檔首部,文檔尾部,文檔任意一行,甚至還可以移動到某個指定字母,某個tag,匹配大、中、小括號。度過最初的不適應後,通過刻意練習和日常使用,肌肉形成記憶,便無需刻意回想是用什麼命令,而是潛意識完成操作。掌握了這些命令後,當你使用原來的編輯器時,也會去尋找這些快捷鍵。這就不僅僅是使用vim時候提供效率了,而是通過提高編輯操作的意識、思想,提高了整體的工作效率。
使用vim一段時間後,我在其他工具中進行編輯時,編再也無法忍受,一個一個自讀地移動光標。於是也會主動去找單詞、行級別移動的快捷鍵。
- Mac系統
-
cmd + ←/→
移動到:當前行首/尾部 -
alt + ←/→
移動到:當前單詞首/尾部
-
- iterm:
-
ctrl + f/b
前進/後退一個字符 -
Esc + f/b
前進/後退一個單詞 -
ctrl + a/e
行首/行尾 -
ctrl + h/d
刪除光標前/後一個字符 -
ctrl + w
刪除光標前一個單詞 -
ctrl + k/u
刪除光標前/後所有內容 -
ctrl + y
粘貼之前刪除的內容
-
後記
這篇文章到這裏也就結束了,洋洋灑灑寫了這麼多,一次讀下來就接受,很難;僅僅通過閱讀就掌握,更難。想要真正用熟vim
,掌握思想,需要後續更多思考、實踐。但是相信我,這些付出,一定是值得的。因爲它不僅能讓你掌握一個開發利器,更能帶給你很有價值的思想。