1. 程式人生 > 程式設計 >一開始看不上親密接觸後又讓你欲罷不能的 vim

一開始看不上親密接觸後又讓你欲罷不能的 vim

每一個程式設計師都有一款心心念叨的程式碼編輯器。而在眾多妖顏魅惑的編輯器偶像團體前,vim 就像個不加粉飾的農村姑娘,咋一看是那樣樸實無華,難有傾心。但只要走近一點,來個親密接觸,又會被她的似水柔情所俘獲。

這是青筆親身經歷。公司從騰訊空降技術總監,時不時會分享一些他的開發經驗。其中說到編輯器,他的臉上總會泛起淡淡桃花,“我就喜歡 vim!” 。儘管我們曾一致向他引薦當下紅極一時集美貌與才華於一身的 vscode 。他卻依然心有所屬,不為所動。

多少次,我百思不得其解,這個“相貌平平”的 vim 到底有何魔力,能牢牢扣住總監的心絃。在我真正走進她的內心世界,多次晝夜相伴之後。我最終也被她的清新脫俗所拜倒。

接下來就讓我們一起來瞭解這名神祕的“女子”。

1. vim 歷史

以下是網上找到一張圖。如對 vim 發展歷史感興趣可以參考維基百科的詞條 Vim (text editor)

en.wikipedia.org/wiki/Vim_(t…

早期的 UNIX 作業系統上標準的編輯器是 ed ,這是一個面向行的編輯器,只能顯示當前編輯的行。1976年,Bill Joy 在 ed 上做了擴充套件,使之支援了全屏顯示和編輯,命名為 ex 。ex 是 ed 的超集,通過在 ex 輸入命令 vi 來啟動擴充套件功能。後來這個命令由於經常使用而被獨立出來,也就是現在 Linux (包括 macOS)系統預裝的命令列編輯器 vi 。直到 1991 年,Bram Moolenaar 才開發了 vi 的改進版(improvement),命名為 VIM (寓意:Vi IMprovement),同年 11 月 4 日釋出了 vim 的第一個版本。下圖來自 vim.org 網站首頁的一條新聞,也就是去年的11月4日迎來了 vim 的27歲生日。

2. 內建教程

vim 自帶了一個面向初學者開發的教程。可通過獨立的子程式 vimtutor 啟動教程。

如果系統已經安裝了 vim (macOS 預裝了 vim,部分 linux 發行版可能需要手動安裝),可以在直接在終端輸入 vimtutor 命令開啟教程。

vimtutor
複製程式碼

教程實際也是使用 vim 開啟系統上的一個文字檔案。檔案通常在 /usr/share/vim/vim80/tutor/ 目錄下。當然,這個目錄下有很多個檔案,分別是不同語言版本的教程。使用 vimtutor 的不同之處是能自動開啟使用者所在地的語言版本。

上面看到的簡體中文版實際就是檔案 /usr/share/vim/vim80/tutor/tutor.zh_cn.utf-8

因此,你可以將其複製一份放到當前目錄下,再使用 vim 開啟,用做練習,隨意編輯檔案內容。

cp /usr/share/vim/vim80/tutor/tutor.zh_cn.utf-8 ./
vim tutor.zh_cn.utf-8
複製程式碼

但如果按照教程來練習,個人用起來感覺最舒服的方式是:同時開啟兩個終端,一個使用 vimtutor 開啟本地教程用於閱覽,另一個將教程的英文版複製到當前目錄,然後使用 vim 在複製的英文版上進行編輯練習(因為我們實際應用場景大多是英文環境,包括編輯程式碼和配置檔案)。

複製英文版教程並進行編輯練習:

cp /usr/share/vim/vim80/tutor/tutor ./
vim tutor
複製程式碼

3. 工作模式

對於習慣了 GUI 編輯器的開發者,剛開始接觸 vim ,很容易被 vim 的工作模式搞得一頭霧水,而且很可能就此放棄使用 vim 。但如果熟悉了,就覺得一切也理所當然。按照維基百科的介紹,vim 一共有 12 種不同模式,但只有 6 種基本模式,剩下 6 種都是基本模式的變種。但實際我們最常接觸的只有兩類模式:命令模式和編輯模式。其中命令模式又細分為,Normal(預設)模式和 Cmdline (底部命令列) 模式。而 Normal 模式是啟動 vim 進入的預設模式,並且在任何模式下,按 ESC (鍵盤左上角)鍵都將回到這個模式,作用類似於手機的 Home 鍵。因此,當你開始迷糊自己當前處在什麼模式下,最好的辦法就是按 ESC 回到 Normal 模式。我們使用 vim 的絕大多數命令都在 Normal 模式執行,而 Cmdline(命令列)模式通常只在退出和儲存時使用,因此,如不特別說明,當我們在說命令模式時,就是指代 Normal 模式。此外,本文將 Cmdline 統稱為命令列模式

下圖是三種模式的切換方式:

如果要簡單概括這三種模式的使用場景,那就是:在命令模式中執行遊標導航,複製貼上刪除,撤銷重做,以及查詢替換等操作;在編輯模式下,編寫程式碼或進行創作;在命令列模式模式下,輸入 q 退出,輸入w儲存,輸入wq儲存並退出。當然這裡還不是全部的操作,但是熟悉這些操作,基本就能將 vim 應用得得心應手了。

4. 命令模式

4.1 移動遊標

要使用 vim 熟練地進行程式碼和文字的編輯,首先需要能夠精確快速的將遊標定位到要編輯的地方。最基本的就是上下左右移動遊標了。這些操作當然可以使用鍵盤的四個方向鍵來完成。但是更常用的是使用字母輸入區域連續的四個按鍵 h,j,k,l 。事實上,當你按照標準的手勢接觸鍵盤,你右手的食指觸碰的就是 j 鍵(按鍵有個凸起)。因此,在你保持正常輸入的手勢下,順其自然地敲動食指,就能將遊標往下移動一行,而緊挨著的中指接觸的是 k 鍵,進行的是和 j 相反的操作,即將遊標向上移動一行,你會發現這樣用起來相當順手,這就理解為什麼要使用字母按鍵來進行遊標移動操作了。當然這是在命令模式下,按下字母鍵不會作為輸入而改變編輯內容。

  • h: 向左移動一個字元
  • l: 向右移動一個字元
  • j: 向下移動一行
  • k: 向上移動一行

雖然可以使用 j,k 左右兩邊的 h 和 l 來實現向左和向右移動遊標。這在一次只移動一個字元時,也還是很方便。但是如果向左或向右移動一個字(word,英文中空格和標點符號隔開的單詞,標點符號也算一個字)時,還使用 h 和 l 就顯得有些笨拙了。向左(向後)和向右(向前)移動一個字的方法如下:

  • w: 向右或向前移動一個字,遊標定位在字的首字元
  • b: 向左或向後移動一個字,遊標定位在字的首字元

命令模式按下字母w將遊標向右移動一個字:

命令模式按下字母b將遊標向左移動一個字:

如果想近一步擴大遊標單次移動的範圍,就要用到按句子和段落來前後移動來。兩對圓括弧()分別將遊標向後和向前移動一個句子,對應兩個花括弧{}分別將遊標向後和向前移動一個段落。

  • ( : 向後移動一個句子,遊標定位在句子開始
  • ) : 向前移動一個句子,遊標定位在句子開始
  • { : 向後移動一個段落,遊標定位在段落開始
  • } : 向前移動一個段落,遊標定位在段落開始

命令模式按下)(向前和向後移動一個句子:

命令模式按下}{向前和向後移動一個段落:

此外你還可以在螢幕所見範圍內進行快速移動遊標。分別使用大寫的HML。但是這三種定位並不是很精確,通常用做快速大範圍級定位,然後再使用前面的命令進行更加精確的定位。讀者可以自行嘗試。

  • H: 將遊標定位到螢幕頂部一行的最左端
  • M: 將遊標定位到螢幕中間一行
  • L: 將遊標定位到螢幕的底部一行

還有一種我們非常熟悉的應用場景,就是在除錯程式丟擲異常時,通常會顯示出異常產生的行號,這時就需要根據行號快速將遊標定位到指定的位置。這個命令相比前面的命令要複雜一點,它需要先輸入代表行號的一個或多個數字,然後按下大寫字母G來完成。例如將遊標定位到檔案的第 80 行。需要先在命令模式輸入80,然後快速按下大寫字母G

4.2 刪除/撤銷

將刪除和撤銷兩個操作放在一起講,其中一個原因是考慮到可能因為還不知道如何撤銷刪除操作,而害怕嘗試刪除操作的心理(儘管我們已經事先做了備份,但這應該是一種普通的心理和人性害怕失去是同樣的道理)。我們知道可以撤銷,因此在刪除這件事上就有了“安全感”。也許你不是這麼認為,但是這樣還是能幫助我們更好的練習和記憶(我們可以迴圈往復地練習刪除撤銷)。

和移動遊標一樣,刪除也可以按不同粒度進行。如刪除單個字元,字,行,句子,段落以及螢幕首尾。刪除操作由用字母d加上表示刪除範圍的識別符號構成。同時刪除具體範圍還受遊標當前所在的具體位置決定。例如刪除字使用字母組合dw,如果游標出現在單詞hello的第二個字元e上,此時在命令模式連續按下dw,將刪除從e開始之後的整個字,但是會e前面的h不會刪除,刪除後的結果就是還剩一個字母h。其他粒度的刪除,也遵循相似的規律。

刪除單個字元

刪除單個字元有兩者方法。兩者等效,都是刪除遊標所在的字元,但是使用x更簡單一些,因為只需要輸入一個字母。

  • x
  • dl

刪除字

  • dw: 從遊標所在位置開始,刪除到字的末尾(包含遊標所在位置的字元)
  • db: 與dw相反方向刪除,即刪除遊標所在位置前面的字元(不包含遊標所在位置的字元)

刪除行

  • dd: 刪除遊標所在的行
  • 3dd: 刪除從遊標開始的3行,當然這是一個例子,可以更改前面的數字刪除任意數量的行

一次刪除3行:

刪除到行尾

如果你不想刪除整行。而是從遊標開始到行尾的字元,可以直接使用一個大寫D來實現。

使用大寫字母D刪除從遊標位置開始到行尾的字元:

從行首刪除

與刪除到行尾對應的是使用d0從行首刪除:

d^與d$

如果對正則表示式熟悉,應該很容易猜到兩者的含義。也是刪除行首和行尾,但是與前面的 d0D 所不同的是不刪除行首和行尾的空格。

  • d^: 刪除遊標前面知道行首的字元,包含行首的空格
  • d$: 刪除從遊標開始知道行尾的字元,包含行尾的空格

刪除句子和段落

熟悉了前面的刪除,以下刪除句子和段落也可以以此類推。唯一區別僅僅在於刪除粒度不同。

  • d(: 從句首刪除
  • d): 刪除到句尾
  • d{: 從段首刪除
  • d}: 刪除到段尾

替換:刪除並輸入

說到刪除不得不再提到替換。因為兩者就像兄弟一般,實際替換像刪除的哥哥,只比弟弟刪除多做了一步。就是在刪除後緊接著進入編輯模式,在已刪除的地方進行編輯。這兩個組合動作不就是替換嘛。需要注意的還有,替換會改變當前工作模式,也就是進入下文會講到的編輯模式,因此在完成輸入後,需要按ESC重新回到命令模式。與刪除d對應的替換命令是c。因此將刪除裡講到的所有操作中的d換成c就是對應的替換操作,當然執行後的細節會有些差異。但是這種差異在你親自試過後就很容易理解,因此不作過多論述。讀者可以自行嘗試。

撤銷操作

撤銷操作很簡單,命令模式下,按下小寫字母u(undo)即可。可以連續按多次,以快速撤銷多個歷史操作。

4.3 複製/貼上

複製 y

複製使用字母y。它的用法和刪除操作d很像,不同的是y是複製而不是刪除。

下面以複製行為例。其他粒度的複製類比刪除操作d即可。

dd刪除當前行對應的複製當前行操作是yy,當然也可以複製多行,和刪除多行格式相似。在yy前加上要複製的行數。例如3yy複製從遊標所在行開始的三行。

  • yy: 複製遊標所在的行
  • 3yy: 複製從遊標開始的3行,這裡只是例子,實際可以是任意行數

貼上 p

貼上命令就簡單多了。它沒有沒有那麼多的粒度區分,而只需要將已經複製的內容插入到當前遊標之後或者之前的位置。分別使用小寫的p和大寫的P

  • p: 將複製的內容插入到遊標之後
  • P: 將複製的內容插入到遊標之前

4.4 查詢字串

在命令模式輸入斜槓/(向下查詢)或問號?(向上查詢),遊標定位到終端底部一行,輸入要查詢的字串,再按回車(RETURN),首先定位到一個找到的字串,接著按小寫的n查詢下一個,按大寫的N查詢前一個。

5. 進入編輯模式

上面介紹的所有命令都是在命令模式下進行的。如果要開始編寫程式碼或其他文字編輯工作,就需要從命令列模式進入編輯模式,否則輸入的字母(除能進入編輯模式的字母外)都將視作命令,而不會編輯到到檔案中。

進入編輯模式有以下6種方式,區別僅在於進入編輯模式後,遊標所在的位置的不同。

  • i: 在當前遊標前插入
  • I: 在行首插入
  • a: 在當前遊標後追加
  • A: 在行尾追加
  • o: 在當前遊標所在行之新增新行
  • O: 在當前遊標所在行之新增新行

6. 命令列模式

在命令模式下,按下冒號:,在終端底部出現輸入行,表示當前是命令列模式。在命令列模式可以使用 vim 自身支援的很多命令。如前文已經談到的最常用用到的就是退出和儲存。

  • :q + RETURN: 退出編輯
  • :w + RETURN: 儲存不退出
  • :wq + RETURN: 儲存並退出

常用的還有對 vim 進行設定。以下命令都需要在輸入完後按下回車鍵 RETURN 才能執行(和在終端執行命令方式差不多)。

  • :syntax on: 開啟語法高亮
  • :set number: 顯示行號
  • :set tabstop=4: 設定 Tab 鍵寬度
  • :set expandtab: 使用空格替代 Tab
  • :set softtabstop=4: 設定軟體 Tab (自動Tab)寬度為 4
  • :set shiftwidth=4: 設定自動縮排寬度為 4
  • :set autoindent: 開啟自動縮排,通常用於編寫程式

當然以上對 vim 的設定僅僅影響當前開啟的 vim 。要想每次開啟都使用同樣的設定,需要將設定命令統一儲存到 vim 的配置檔案 ~/.vimrc 中,方法下一節將講到。

此外,可以使用help來獲取幫助。例如輸入:help user-manual + RETURN 檢視使用者手冊。

如果說以上都是 vim 內部執行命令,那麼在命令列模式下,實際也可以呼叫外部 shell 的命令。方法是以感嘆號!開頭,標識感嘆號!之後的命令是一條外部 shell 的命令。如檢視當前目錄下的檔案,:!ls -l。第一次按下 RETURN 會隱藏當前編輯區域,顯示終端介面,第二次按下 RETURN 再次回到 vim 編輯介面。

7. 配置檔案

前文已經講到如果想要每次開啟 vim 都使用同樣和介面和設定,需要將設定命令統一儲存在配置檔案中。在 linux 系統(含macOS)推薦儲存的路徑是 ~/.vimrc。如果從來沒有設定過,這個檔案可能還不存在。這就需要我們先建立一個。為了不覆蓋已有的,我們可以使用如下命令:

test -f ~/.vimrc || vim ~/.vimrc
複製程式碼

這條 shell 命令會先判斷 ~/.vimrc 是否存在,不存在就新建一個空白的配置檔案,並使用 vim 開啟這個新建的配置檔案。

可選的方式也可以複製一份系統的上的配置檔案,然後在此基礎上新增和覆蓋預設配置。

test -f ~/.vimrc || cp /usr/share/vim/vimrc ~/.vimrc
複製程式碼

以下是青筆自己的配置檔案參考:

colorscheme default     " 設定顏色主題
syntax on               " 語法高亮
filetype on             " 檢測檔案的型別
set number              " 顯示行號

set ruler               " 在編輯過程中,在右下角顯示遊標位置的狀態行
set laststatus=2        " 顯示狀態列 (預設值為 1,無法顯示狀態列)
set statusline=\ %<%F[%1*%M%*%n%R%H]%=\ %y\ %0(%{&fileformat}\ %{&encoding}\ %c:%l/%L%)                        " 設定在狀態行顯示的資訊

set tabstop=4           " Tab鍵的寬度
set expandtab           " 使用空格替換Tab
set softtabstop=4
set shiftwidth=4        " 統一縮排為4

set autoindent          " vim使用自動對齊,也就是把當前行的對齊格式應用到下一行(自動縮排)
set cindent             " (cindent是特別針對 C語言語法自動縮排)
set smartindent         " 依據上面的對齊格式,智慧的選擇對齊方式,對於類似C語言編寫上有用

set scrolloff=3         " 遊標移動到buffer的頂部和底部時保持3行距離

set incsearch           " 輸入搜尋內容時就顯示搜尋結果
set hlsearch            " 搜尋時高亮顯示被找到的文字

set foldmethod=indent   " 設定縮排摺疊
set foldlevel=99        " 設定摺疊層數
nnoremap <space> @=((foldclosed(line('.')) < 0) ? 'zc' : 'zo')<CR>
                        " 用空格鍵來開關摺疊

" 自動跳轉到上次退出的位置
if has("autocmd")
    au BufReadPost * if line("'\"") > 1 && line("'\"") <= line("$") | exe "normal! g'\"" | endif
endif
複製程式碼

關於配置檔案的更多資訊,也可通過 vim 內建的幫助手冊來檢視。與配置相關的手冊為 usr_05 。在命令列模式輸入:help usr_05(冒號用於進入命令列模式,輸入後回車)即可開啟該手冊。

總結

本文從 vim 的發展歷史開始,以具備能夠熟練駕馭 vim 編輯器來滿足日常程式碼編寫的基本操作為主線,講解了在 vim 中進行模式切換,遊標導航,刪除,撤銷,替換,複製,貼上,插入文字,以及配置編輯器外觀設定等必要技能。當然,vim 支援的操作和命令還有很多。本文只介紹到基礎部分。但是卻是我們進行程式碼編輯最常用到的功能。熟練掌握後,可以進一步檢視手冊學習更多的技巧。檢視手冊方式上文已經給出。即在命令列模式輸入::help user-manual 再回車。

如果本文能讓你重新認識了 vim 這位眉目清新又純潔善良的“姑娘”,並且從此對她有了一份獨有的愛意,算是本文創作的最大收穫所在^^。


本文首發公號