Usr_40

Nvim :help 頁面,由 產生,來源為 原始碼,使用 tree-sitter-vimdoc 解析器。


VIM 使用者手冊 - 作者:Bram Moolenaar
建立新的指令
Vim 是一個可擴展的編輯器。您可以將常用的指令序列轉換為新的指令,或重新定義現有的指令。自動指令可以讓您自動執行指令。
40.1 按鍵對應 40.2 定義命令列指令 40.3 自動指令
下一章:usr_41.txt 撰寫 Vim 腳本 前一章:usr_32.txt 復原樹 目錄:usr_toc.txt

按鍵對應

簡單的對應在 05.3 節中解釋過。原理是將一個按鍵序列轉換為另一個按鍵序列。這是一個簡單但功能強大的機制。最簡單的形式是將一個按鍵對應到一個按鍵序列。由於功能鍵,除了 <F1> 之外,在 Vim 中沒有預定義的含義,因此這些是進行對應的好選擇。範例:
:map <F2> GoDate: <Esc>:read !date<CR>kJ
這顯示了如何使用三種模式。使用 "G" 跳到最後一行後,"o" 指令會開啟新的一行並啟動插入模式。文字 "Date: " 被插入,而 <Esc> 會讓您離開插入模式。請注意在 <> 內使用特殊按鍵。這稱為角括號表示法。您是輸入這些個別的字元,而不是按下按鍵本身。這使得對應更易於閱讀,並且您可以複製貼上文字而不會出現問題。":" 字元將 Vim 帶到命令列。":read !date" 指令會讀取 "date" 指令的輸出並將其附加到當前行的下方。需要 <CR> 來執行 ":read" 指令。在執行此點時,文字看起來像這樣:
Date
Fri Jun 15 12:54:34 CEST 2001
現在 "kJ" 會將游標向上移動並將行合併在一起。若要決定要使用哪個按鍵或哪些按鍵進行對應,請參閱 map-which-keys

對應和模式

":map" 指令定義了普通模式下按鍵的重新對應。您也可以為其他模式定義對應。例如,":imap" 適用於插入模式。您可以使用它在游標下方插入日期:
:imap <F2> <CR>Date: <Esc>:read !date<CR>kJ
它看起來很像普通模式中 <F2> 的對應,只是開始的地方不同。普通模式的 <F2> 對應仍然存在。因此,您可以為每個模式以不同的方式對應相同的按鍵。請注意,儘管此對應從插入模式開始,但它在普通模式中結束。如果您希望它繼續在插入模式中,請在對應中附加一個 "a"。
以下是 map 指令及其作用模式的概述:
:map 普通、視覺和待運算子模式 :vmap 視覺模式 :nmap 普通模式 :omap 待運算子模式 :map! 插入和命令列模式 :imap 插入模式 :cmap 命令列模式
待運算子模式是指在您輸入運算子字元(例如 "d" 或 "y")時,您需要輸入移動指令或文字物件。因此,當您輸入 "dw" 時,"w" 會在待運算子模式下輸入。
假設您想要定義 <F7>,以便指令 d<F7> 刪除一個 C 程式區塊(以大括號 {} 括住的文字)。同樣地,y<F7> 會將程式區塊複製到未命名的暫存器中。因此,您需要做的是定義 <F7> 以選取目前的程式區塊。您可以使用以下指令來執行此操作:
:omap <F7> a{
這會導致 <F7> 在待運算子模式下執行選取區塊 "a{",就像您輸入的一樣。如果您覺得在鍵盤上輸入 { 有點困難,此對應會很有用。

列出對應

若要查看目前已定義的對應,請使用不帶參數的 ":map"。或包含它們作用模式的變體。輸出可能如下所示:
_g :call MyGrep(1)<CR>
v <F2> :s/^/> /<CR>:noh<CR>``
n <F2> :.,$s/^/> /<CR>:noh<CR>``
<xHome> <Home> <xEnd> <End>
清單的第一欄顯示對應生效的模式。普通模式為 "n",插入模式為 "i" 等。空白用於使用 ":map" 定義的對應,因此在普通和視覺模式下都有效。列出對應的一個有用的目的是檢查 <> 表單中的特殊按鍵是否已被識別(這僅在支援色彩時有效)。例如,當 <Esc> 以彩色顯示時,它代表跳脫字元。當它與其他文字的顏色相同時,它是五個字元。

重新對應

將檢查對應的結果中是否有其他對應。例如,上面的 <F2> 對應可以縮短為:
:map <F2> G<F3>
:imap <F2> <Esc><F3>
:map <F3>  oDate: <Esc>:read !date<CR>kJ
對於普通模式,<F2> 對應到最後一行,然後像按下 <F3> 一樣。在插入模式中,<F2> 會使用 <Esc> 停止插入模式,然後也使用 <F3>。然後 <F3> 對應到執行實際工作。
假設您很少使用 Ex 模式,並且想要使用 "Q" 指令來格式化文字(這在舊版的 Vim 中是如此)。這個對應可以做到這一點:
:map Q gq
但是,在極少數情況下,您仍然需要使用 Ex 模式。讓我們將 "gQ" 對應到 Q,以便您仍然可以進入 Ex 模式:
:map gQ Q
現在發生的是,當您輸入 "gQ" 時,它會對應到 "Q"。到目前為止還不錯。但是,"Q" 又對應到 "gq",因此輸入 "gQ" 會導致 "gq",而您根本無法進入 Ex 模式。若要避免按鍵再次對應,請使用 ":noremap" 指令:
:noremap gQ Q
現在,Vim 知道不應檢查 "Q" 是否有適用於它的對應。每個模式都有類似的指令:
:noremap 普通、視覺和待運算子模式 :vnoremap 視覺模式 :nnoremap 普通模式 :onoremap 待運算子模式 :noremap! 插入和命令列模式 :inoremap 插入模式 :cnoremap 命令列模式

遞迴對應

當對應觸發自身時,它將永遠執行。這可以用來無限次重複動作。例如,您有一個檔案清單,其中第一行包含版本號碼。您可以使用 vim *.txt 編輯這些檔案。您現在正在編輯第一個檔案。定義以下對應:
:map ,, :s/5.1/5.2/<CR>:wnext<CR>,,
現在您輸入 ",,"。這會觸發對應。它會將第一行中的 "5.1" 取代為 "5.2"。然後它會執行 ":wnext" 以寫入檔案並編輯下一個檔案。對應以 ",," 結尾。這會再次觸發相同的對應,因此執行取代等操作。這會一直持續到發生錯誤為止。在這種情況下,可能是取代指令找不到 "5.1" 匹配項的檔案。然後您可以進行變更以插入 "5.1",並再次輸入 ",," 繼續。或者,":wnext" 失敗,因為您位於清單中的最後一個檔案。當對應在中間遇到錯誤時,將會捨棄對應的其餘部分。CTRL-C 會中斷對應(在 MS-Windows 上為 CTRL-Break)。
刪除對應
若要移除對應,請使用 ":unmap" 指令。同樣,取消對應適用的模式取決於所使用的指令:
:unmap 普通、視覺和待運算子模式 :vunmap 視覺模式 :nunmap 普通模式 :ounmap 待運算子模式 :unmap! 插入和命令列模式 :iunmap 插入模式 :cunmap 命令列模式
有一個技巧可以定義在普通和待運算子模式下有效,但在視覺模式下無效的對應。首先為所有三種模式定義它,然後在視覺模式下刪除它:
:map <C-A> /---><CR>
:vunmap <C-A>
請注意,五個字元 "<C-A>" 代表單個按鍵 CTRL-A
若要移除所有對應,請使用 :mapclear 指令。您現在可以猜測不同模式的變化。請小心使用此指令,它無法復原。

特殊字元

":map" 指令後面可以跟隨另一個指令。| 字元會分隔兩個指令。這也表示 | 字元不能在 map 指令內使用。若要包含一個字元,請使用 <Bar>(五個字元)。範例:
:map <F8> :write <Bar> !checkin %:S<CR>
":unmap" 指令也適用於相同的問題,此外,您還必須注意結尾的空白。以下兩個指令不同:
:unmap a | unmap b
:unmap a| unmap b
第一個指令會嘗試取消對應 "a ",並帶有結尾空格。
在對應中使用空格時,請使用 <Space>(七個字元):
:map <Space> W
這會使空格鍵向前移動以空白分隔的單字。
不可能在對應之後直接放置註解,因為 " 字元被視為對應的一部分。您可以使用 |",這會使用註解啟動新的空指令。範例:
:map <Space> W|     " Use spacebar to move forward a word

對應和縮寫

縮寫很像插入模式對應。引數的處理方式相同。主要差異在於觸發方式。縮寫是藉由在單字後輸入非單字字元來觸發。對應是在輸入最後一個字元時觸發。另一個差異是,您為縮寫輸入的字元會在您輸入時插入到文字中。當縮寫被觸發時,這些字元會被刪除,並替換為縮寫產生的內容。在輸入對應的字元時,在您輸入觸發它的最後一個字元之前,不會插入任何內容。如果設定了 'showcmd' 選項,則輸入的字元會顯示在 Vim 視窗的最後一行。當對應不明確時,會出現例外情況。假設您已完成兩個對應:
:imap aa foo
:imap aaa bar
現在,當您輸入 "aa" 時,Vim 不知道它應該套用第一個還是第二個對應。它會等待輸入另一個字元。如果它是 "a",則會套用第二個對應並產生 "bar"。例如,如果它是空格,則會套用第一個對應,產生 "foo",然後插入空格。

此外...

<script> 關鍵字可用於使對應成為腳本的本機。請參閱 :map-<script>
<buffer> 關鍵字可用於使對應成為特定緩衝區的本機。請參閱 :map-<buffer>
<unique> 關鍵字可用於使在已存在對應時定義新對應失敗。否則,新對應會直接覆寫舊對應。請參閱 :map-<unique>
若要讓按鍵不執行任何操作,請將其對應至 <Nop>(五個字元)。這會讓 <F7> 按鍵完全不執行任何操作。
:map <F7> <Nop>| map! <F7> <Nop>
<Nop> 後面必須沒有空格。

40.2 定義命令列指令

Vim 編輯器允許您定義自己的指令。您執行這些指令的方式,與執行其他命令列模式指令相同。若要定義指令,請使用 ":command" 指令,如下所示:
:command DeleteFirst 1delete
現在,當您執行指令 ":DeleteFirst" 時,Vim 會執行 ":1delete",這會刪除第一行。
注意: 使用者自訂的指令必須以大寫字母開頭。您不能使用 ":Next"。底線不能使用!您可以使用數字,但不建議這樣做。
若要列出使用者定義的指令,請執行以下指令:
:command
如同內建指令一樣,使用者定義的指令也可以縮寫。您只需輸入足夠的字符,以將該指令與其他指令區分開來。可以使用命令列補全功能來取得完整名稱。

參數數量

使用者定義的指令可以接受一系列參數。參數數量必須由 -nargs 選項指定。例如,範例指令 :DeleteFirst 不接受任何參數,因此您可以將其定義如下:
:command -nargs=0 DeleteFirst 1delete
但是,由於零參數是預設值,因此您不需要新增 "-nargs=0"。-nargs 的其他值如下:
-nargs=0 沒有參數 -nargs=1 一個參數 -nargs=* 任意數量的參數 -nargs=? 零個或一個參數 -nargs=+ 一個或多個參數

使用參數

在指令定義內,參數由 <args> 關鍵字表示。例如:
:command -nargs=+ Say :echo "<args>"
現在,當您輸入:
:Say Hello World
Vim 會回顯 "Hello World"。但是,如果您新增雙引號,它將無法運作。例如:
:Say he said "hello"
若要將特殊字元轉換為字串,並正確逸出以用作表達式,請使用 "<q-args>"
:command -nargs=+ Say :echo <q-args>
現在,上述的 ":Say" 指令將導致執行以下內容:
:echo "he said \"hello\""
<f-args> 關鍵字包含與 <args> 關鍵字相同的資訊,但格式適合用作函數呼叫參數。例如:
:command -nargs=* DoIt :call AFunction(<f-args>)
:DoIt a b c
執行以下指令:
:call AFunction("a", "b", "c")

行範圍

某些指令會將範圍作為其參數。若要告知 Vim 您正在定義這樣的指令,您需要指定 -range 選項。此選項的值如下:
-range 允許範圍;預設值為目前行。-range=% 允許範圍;預設值為整個檔案。-range={count} 允許範圍;其中的最後一個數字會當作單一數字使用,其預設值為 {count}
當指定範圍時,關鍵字 <line1><line2> 會取得範圍中第一行和最後一行的值。例如,以下指令定義 SaveIt 指令,該指令會將指定的範圍寫入檔案 "save_file":
:command -range=% SaveIt :<line1>,<line2>write! save_file

其他選項

其他一些選項和關鍵字如下:
-count={number} 指令可以接受計數,其預設值為 {number}。產生的計數可以透過 <count> 關鍵字使用。-bang 您可以使用 !。如果存在,使用 <bang> 將會產生 !。-register 您可以指定暫存器。(預設值為未命名的暫存器。)暫存器規範可用作 <reg> (又名 <register>)。-complete={type} 使用的命令列補全類型。有關可能的值列表,請參閱 :command-completion。-bar 指令後面可以接 | 和另一個指令,或 " 和註解。-buffer 該指令僅適用於目前的緩衝區。
最後,您有 <lt> 關鍵字。它代表字元 <。使用它來逸出上述 <> 項目的特殊含義。

重新定義和刪除

若要重新定義相同的指令,請使用 ! 參數:
:command -nargs=+ Say :echo "<args>"
:command! -nargs=+ Say :echo <q-args>
若要刪除使用者指令,請使用 ":delcommand"。它接受一個參數,即指令的名稱。例如:
:delcommand SaveIt
若要刪除所有使用者指令:
:comclear
小心,此操作無法還原!
有關此內容的更多詳細資訊,請參閱參考手冊:user-commands

40.3 自動指令

自動指令是指在回應某些事件(例如讀取或寫入檔案或緩衝區變更)時自動執行的指令。透過使用自動指令,您可以訓練 Vim 編輯壓縮檔案,例如在 gzip 外掛程式中使用的那樣。自動指令非常強大。請小心使用它們,它們會幫助您避免輸入許多指令。粗心使用它們會造成很多麻煩。
假設您想要在每次寫入檔案時,替換檔案末尾的日期戳記。首先,您需要定義一個函數:
:function DateInsert()
:  $delete
:  read !date
:endfunction
您希望在每次將緩衝區寫入檔案之前呼叫此函數。這將使其發生:
:autocmd BufWritePre *  call DateInsert()
"BufWritePre" 是觸發此自動指令的事件:在(pre)將緩衝區寫入檔案之前。 "*" 是與檔案名稱匹配的模式。在本例中,它會比對所有檔案。啟用此指令後,當您執行 ":write" 時,Vim 會檢查是否有任何匹配的 BufWritePre 自動指令,並執行它們,然後執行 ":write"。 :autocmd 指令的一般形式如下:
:autocmd [group] {events} {file-pattern} [++nested] {command}
\[group] 名稱是選用的。它用於管理和呼叫指令(稍後將詳細說明)。 {events} 參數是觸發指令的事件列表(以逗號分隔)。 {file-pattern} 是檔案名稱,通常包含萬用字元。例如,使用 "*.txt" 會使自動指令適用於所有名稱以 ".txt" 結尾的檔案。選用的 \[++nested] 旗標允許自動指令的巢狀(請參閱下文),最後,{command} 是要執行的指令。
當新增自動指令時,現有的自動指令會保留。若要避免多次新增自動指令,您應該使用此形式:
:augroup updateDate
:  autocmd!
:  autocmd BufWritePre *  call DateInsert()
:augroup END
這會在定義新的自動指令之前,先使用 :autocmd! 刪除先前定義的任何自動指令。稍後將說明群組。

事件

其中一個最有用的事件是 BufReadPost。它會在編輯新檔案之後觸發。它通常用於設定選項值。例如,您知道 "*.gsm" 檔案是 GNU 組合語言。若要讓語法檔案正確,請定義此自動指令:
:autocmd BufReadPost *.gsm  set filetype=asm
如果 Vim 能夠偵測檔案類型,它會為您設定 'filetype' 選項。這會觸發 Filetype 事件。當編輯特定類型的檔案時,請使用它來執行某些動作。例如,若要載入文字檔案的縮寫清單:
:autocmd Filetype text  source ~/.config/nvim/abbrevs.vim
在開始編輯新檔案時,您可以讓 Vim 插入骨架:
:autocmd BufNewFile *.[ch]  0read ~/skeletons/skel.c
有關事件的完整列表,請參閱 autocmd-events

模式

{file-pattern} 參數實際上可以是檔案模式的逗號分隔清單。例如:*.c,*.h 比對以 ".c" 和 ".h" 結尾的檔案。可以使用常用的檔案萬用字元。以下是常用萬用字元的摘要:
* 比對任意字元任意次數 ? 比對任意字元一次 \[abc] 比對字元 a、b 或 c . 比對點 a{b,c} 比對 "ab" 和 "ac"
當模式包含斜線 (/) 時,Vim 會比較目錄名稱。沒有斜線時,只會使用檔案名稱的最後一部分。例如,"*.txt" 會比對 "/home/biep/readme.txt"。模式 "/home/biep/*" 也會比對它。但是 "home/foo/*.txt" 不會比對。當包含斜線時,Vim 會將模式與檔案的完整路徑 ("/home/biep/readme.txt") 和相對路徑(例如 "biep/readme.txt")進行比對。
注意: 當在將反斜線用作檔案分隔符號的系統(例如 MS-Windows)上工作時,您仍然在自動指令中使用正斜線。這使得編寫模式更容易,因為反斜線具有特殊含義。這也使得自動指令具有可攜性。

刪除

若要刪除自動指令,請使用定義它時使用的相同指令,但省略結尾的 {command} 並使用 !。例如:
:autocmd! FileWritePre *
這將刪除使用 "*" 模式的 "FileWritePre" 事件的所有自動指令。

列出

若要列出所有目前已定義的自動指令,請使用:
:autocmd
列表可能非常長,尤其是在使用檔案類型偵測時。若要僅列出部分指令,請指定群組、事件和/或模式。例如,若要列出所有 BufNewFile 自動指令:
:autocmd BufNewFile
若要列出模式 "*.c" 的所有自動指令:
:autocmd * *.c
使用 "*" 作為事件會列出所有事件。若要列出 cprograms 群組的所有自動指令:
:autocmd cprograms

群組

定義自動指令時使用的 {group} 項目會將相關的自動指令分組在一起。例如,這可用於刪除特定群組中的所有自動指令。當為特定群組定義多個自動指令時,請使用 ":augroup" 指令。例如,讓我們為 C 程式定義自動指令:
:augroup cprograms
:  autocmd BufReadPost *.c,*.h :set sw=4 sts=4
:  autocmd BufReadPost *.cpp   :set sw=3 sts=3
:augroup END
這與執行以下操作相同:
:autocmd cprograms BufReadPost *.c,*.h :set sw=4 sts=4
:autocmd cprograms BufReadPost *.cpp   :set sw=3 sts=3
若要刪除 "cprograms" 群組中的所有自動指令:
:autocmd! cprograms

巢狀

一般而言,由於自動指令事件而執行的指令不會觸發任何新事件。如果因為 FileChangedShell 事件而讀取檔案,則不會觸發會設定語法的自動指令。若要觸發事件,請新增 "++nested" 旗標:
:autocmd FileChangedShell * ++nested  edit

執行自動指令

可以透過假裝已發生事件來觸發自動指令。這對於讓一個自動指令觸發另一個自動指令很有用。例如:
:autocmd BufReadPost *.new  execute "doautocmd BufReadPost " . expand("<afile>:r")
這定義了一個在編輯新檔案時觸發的自動指令。檔案名稱必須以 ".new" 結尾。 ":execute" 指令使用表達式評估來形成新指令並執行它。當編輯檔案 "tryout.c.new" 時,執行的指令會是:
:doautocmd BufReadPost tryout.c
expand() 函數會採用 "<afile>" 參數,該參數代表執行自動指令的檔案名稱,並採用檔案名稱的根目錄,其帶有 ":r"。
":doautocmd" 在目前緩衝區上執行。":doautoall" 指令的作用與 "doautocmd" 相同,但它會在所有緩衝區上執行。

使用一般模式指令

自動指令執行的指令是命令列指令。如果您想要使用一般模式指令,可以使用 ":normal" 指令。例如:
:autocmd BufReadPost *.log normal G
這會使游標在您開始編輯時跳到 *.log 檔案的最後一行。使用 ":normal" 指令有點棘手。首先,請確保其參數是完整的指令,包括所有參數。當您使用 "i" 進入插入模式時,也必須有一個 <Esc> 才能再次離開插入模式。如果您使用 "/" 開始搜尋模式,則必須有一個 <CR> 才能執行它。":normal" 指令會將其後的所有文字當作指令使用。因此,後面不能接 | 和另一個指令。若要解決此問題,請將 ":normal" 指令放入 ":execute" 指令中。這也可以方便地傳遞不可列印的字元。例如:
:autocmd BufReadPost *.chg execute "normal ONew entry:\<Esc>" |
        \ 1read !date
這也顯示了使用反斜線將長指令分成多行的用法。這可以用於 Vim 指令碼中(而不是在命令列中)。
當您希望自動指令執行複雜的操作時,其中涉及在檔案中跳動,然後返回原始位置,您可能想要還原檔案的視圖。有關範例,請參閱 restore-position

忽略事件

有時,您不希望觸發自動指令。'eventignore' 選項包含將完全忽略的事件列表。例如,以下指令會導致忽略進入和離開視窗的事件:
:set eventignore=WinEnter,WinLeave
若要忽略所有事件,請使用以下指令:
:set eventignore=all
若要將其設定回正常行為,請將 'eventignore' 設定為空白:
:set eventignore=
下一章:usr_41.txt 編寫 Vim 腳本
版權:請參閱 manual-copyright vim:tw=78:ts=8:noet:ft=help:norl
主要
指令索引
快速參考