效率至上--一文帶你真正走進vim

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便可返回正常模式。一張圖概括如下:

注:後文講解,如無特殊說明,均爲普通模式下操作。

基本操作

如果不追求效率,只想完成修改文件的任務,並保存退出,只需要掌握以下三個命令:

  1. 移動:h j k l 最簡單的移動,相當於鍵盤上面的方向鍵,分別對應左下上右。
  2. 進入插入模式:i
  3. 保存退出:ZZ(注意區分大小寫)

流程如下:

  1. 普通模式下,通過h j k l 移動到想要修改的位置
  2. 輸入i進入插入模式,此時通過輸入(字母、數字、符號),刪除(Backspace)等,完成基本修改操作
  3. Esc回到普通模式,ZZ,保存修改並退出。

進階:命令形式

普通模式下,vim的命令主要分爲以下三種:

  1. 動作,用以移動光標,或者定義操作的範圍;比如:

    1. h:定義操作範圍爲一格,單獨使用時,向左移動光標一格。
    2. w:定義操作範圍爲一個單詞,移動光標到下一個單詞首部。
  2. 操作,這種命令需要在後面接表示操作範圍的指令;

    1. d,刪除,後接表示一個單詞操作範圍的w,即dw時,表示刪除到當前詞尾。
    2. c,修改,後接表示一個單詞操作範圍的w,即cw時,表示修改當前單詞。(編輯器行爲表現爲,刪除到當前詞尾,同時進入插入模式)。
  3. 命令,直接執行的命令,其中一部分,在執行命令後,直接進入編輯模式;比如:

    1. D,刪除至行末。
    2. I,到行首進入插入模式。
      我們的使用方式主要也是三種:命令、動作、操作+動作。
      此外,在動作類的命令前,加上number爲可選項,可實現重複n次的效果:
  4. [number] + h/j/k/l左/下/上/右移動number個字符。比如,'2j',向下移動光標2個字符。

  5. 依舊使用dw來舉例,d是刪除,w是單詞,dw代表刪除一個單詞,d2w代表刪除兩個單詞。後面的命令,大多都可應用此種形式組合使用,大家多注意,養成這種操作 + [次數] + 範圍的思維模式,舉一反三,便可發揮最大功效。

移動進階

單詞級別的移動

這裏有僅大小寫不同的兩組命令,兩組命令的功能,是相同的:跳轉光標到對應位置。但是對應的單位不同,分別爲wordstring。具體區別是:

  • string僅以空格分開;
  • word字母數字以外的字符分開。

以這個字符串爲例:hello world-hehe111 abcde

  • word有5個,分別爲hello world, -, hehe111abcde
  • string有3個,分別爲hello world-hehe111abcde

兩組命令如下:(跳轉光標至)

  • 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行,常用。

要想用好上述幾個命令,有兩個簡單的建議:

  1. 結合命令:ctrl-g。該命令的作用是顯示當前行的位置信息(第幾行,相對整個文本行數的百分比)。
  2. 在命令模式下輸入以下命令,或在~/.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,刪除到字母fd/foo:在全文, 刪除到 “foo” 。

剪切

剪切操作其實就是我們之前講的刪除。也就是d。刪除的內容,默認會存放到剪切板中。也就相當於進行了剪切。

進階操作符

從這裏大家可以看出,ia的作用比較特殊,代表與區塊相關的某種操作。區別就在於,i不包含區塊邊界符號。a包含。這兩個操作符很重要,在後面的複製操作中還會用到。此外還有t/。此外,ia還可以接t,此時t表示一對xml標籤。i:區塊,不包含邊界。a:區塊,包含邊界。t:"to",本行到哪裏。/:接匹配,全文到哪裏。

粘貼

  • p:粘貼到光標後,或下一行。
  • P:粘貼到光標前,或前一行。

爲什麼會有光標前後或上下一行兩種情況呢?是因爲我們複製或剪切的內容有可能是字符串或者整行:

  • 當複製內容爲字符串時,粘貼到光標前/後。
  • 當複製內容爲整行時,粘貼到上/下一行。

複製

  • y,複製,屬於操作,後面需要接動作來標識複製的範圍。比如:
    • yw:複製到當前單詞結尾。
    • ye:從當前位置複製到本單詞的最後一個字符。
    • y$:複製到當前行尾。
    • yyY:複製當前行。
    • nyy:複製從光標所在行起的n行,注意n在最前面。

0y$: 命令意味着:

  • 0 → 先到行頭
  • y → 從這裏開始拷貝
  • $ → 拷貝到本行最後一個字符

當然也可以結合我們剛剛介紹的進階操作符來進行操作:

  • yi":複製兩個引號之間
  • yit:複製兩個xml標籤之間
  • y/[x]:複製到x。

剪切板

vim 有 12 個剪切板,分別是 0、1、2、...、9、a、“、+。:reg:查看各個剪切板裏的內容。yp默認使用 "剪切板中的內容。
"[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不放,用np來遍歷提示選項,到達期待的選項後,無需其他操作,繼續輸入即可。

撤銷

  • 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 保存剛剛的修改。 有如下兩種方法:

  1. :shell:sh,當退出當前 shell 時(比如exit),就會回到 vim。
  2. 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下,要放棄人們習慣的插入模式,使用命令模式呢?仔細想一想,其實原因很簡單:在沒有鼠標的年代,人們只能依靠鍵盤來移動光標,修改文本。
  • 爲什麼現在有了鼠標,我們還要用正常模式呢?
    1. 工作內容覆蓋。我們每個人都認爲,工程師的工作,是寫代碼。然而,其實我們主要的工作,是讀,或者說,理解代碼。經調查,工程師日常工作中,讀:寫代碼的比例,爲10:1(參考《Clean Code》一書)。所以默認的普通模式,主要滿足佔比重更大的”讀“;遇到需要修改的時候,再進入編輯模式。
    2. 大量快捷鍵。相信每個人,都最起碼知道一組快捷鍵: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來完成。IA等,也是類似道理。

外部命令

類似棧的思路,可以放下當前操作,保存現場,然後進入另一個操作。當操作完成後,回到當前現場。

思維模式

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,掌握思想,需要後續更多思考、實踐。但是相信我,這些付出,一定是值得的。因爲它不僅能讓你掌握一個開發利器,更能帶給你很有價值的思想。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章