Vim 是 Linux 系統上的最著名的文本/代碼編輯器,也是早年的 Vi 編輯器的加強版。一直以來,Vim 普遍被推崇為類 Vi 編輯器中最好的一個,其擁有代碼補全、編譯及錯誤跳轉等諸多豐富的功能,接下來,本文將與大家分享一些 Vim 使用上的一些實用技巧,希望對技術路上的程式設計師們有所裨益。
作者 | Hillel Wayne
譯者 | 彎月,責編 | 屠敏
以下為譯文:
我使用Vim已經8年多了,但至今仍然可以發現新的技巧。通常這算得上是一件好事。然而,在我看來,不斷發現新事物也許是失敗之處,因為你很難知道Vim還有哪些功能。
雖然人們經常談論模態編輯或文本對象的美感,但我認為這並不是Vim的本質。Vim是一個拼湊而成的子系統,每一部分都塞滿了各種特殊用途的工具。僅在普通模式下就有超過一百種不同的鍵盤輸入命令。密集的功能成就了Vim的實用性。如果你想「顯示與關鍵字匹配的所有標籤」,那麼只需輸入「g]」,所以人人都喜歡使用這個工具。
由於這個系統很難發現新東西,我們必須依靠閱讀指南來尋找。然而,有關Vim的文檔並不多。入門文章的話,我們只有ciw等幾篇(https://wikileaks.org/ciav7p1/cms/page_3375350.html),還有深入到子系統中的專家文章。但沒有人真正談論那些有特殊目的的技巧,所以人們只好在過去的6年中不斷探索偶遇新功能。
我希望通過這篇文章向大家介紹一些Vim使用上的小技巧。這些技巧都很淺顯,我鼓勵你了解更多有趣的技巧。這些技巧之間沒有關聯性。總的來說,它們能給予你很多幫助。
組織形式
Vim的用戶大致可以分為兩類。第一類人是純粹主義者,他們喜歡Vim的小巧和無處不在。在不熟悉的計算機上使用Vi(例如在ssh期間)時,他們傾向於保持最低限度的配置。另外一類人是形式主義者,他們喜歡在Vim中安裝各種插件、函數以及和自定義的鍵盤映射,目的只是為了假裝他們正在使用Emacs。如果你拿走這些形式主義者的vimrc,他們就會感到非常無助。
而我本人可能更加偏向於形式主義者。根據是否涉及向基本的Vim添加映射或設置,我可以將Vim的使用技巧分成兩個部分。
純粹主義者的Vim
我使用標準Vim幫助中的表示形式來書寫模態命令,即<cr>表示按下回車鍵。在需要使用:h獲取幫助的時候,例如:h E676,我會將幫助的字符串寫到括號中。
其他普通命令
":,@:
": 是保存最後執行的命令的寄存器。可以使用":p將該寄存器的內容顯示到當前緩衝區中。@:返回最後一條命令。
"=
這個是「表達式」寄存器。可以在該寄存器中輸入任何vimL表達式,並使用ctrl-R等進行粘貼。例如,輸入"=strftime("%c")<cr>p可以粘貼當前時間戳。
mA,'A
m{letter}在當前光標位置設置標記。之後可以用'{letter}跳轉到標記所在行。小寫字母的標記僅限於當前緩衝區,所以可以用它們進行導航。大寫字母是全局的:如果你當前不在標記A指明的文件中,那麼'A將會跳轉到那個文件。利用:marks:命令可以查看設置過的所有標記。
ctrl-A和ctrl-X
增加或減少當前行內、當前光標所在位置之後的下一個數字。該命令將會跳轉到數字,所以可以在任何地方使用。例如,10c-A要比wwwwwciw20容易得多。
q:
打開之前輸入過的命令的歷史窗口。該窗口可以像任何Vim文本窗口一樣進行操作,但對其進行修改則不會被保存。但按下<CR>可以運行修改後的命令。該功能可以十分方便快捷地修改並重新運行命令,或者搜索舊命令以便重新使用。
q/,q?
與q:相同,不過打開的是搜索歷史。
ctrl-I,ctrl-O
跳轉到跳轉列表中的下一個或上一個位置。常用於翻看一個東西後跳轉回原位置。閱讀幫助文件時非常有用。
宏
關於宏的進階內容參考這篇文章(https://www.hillelwayne.com/post/vim-macro-trickz/)。
可視模式
gv
選擇前一個可視範圍。
v_o
跳轉到可視塊的另一端。當你發現可視塊開始處少了一行時非常有用。在塊模式下,該命令會跳轉到對角位置;使用v_O可以跳轉到水平方向的另一端。
g ctrl-A / ctrl-X
在可視模式下,ctrl-A僅增加每一行的第一個數字。相反,g ctrl-A會對每個匹配的行進行增一的操作。用表格解釋可能更方便:
operators: v,V,c-v(:h o_v)
你肯定知道可視模式中,v是按照字符選擇,V是按照行選擇,ctrl-V是按照塊選擇。但這三個命令也可以作為移動操作符使用,按照相應的選擇方式執行移動操作。例如,如果有下面的文本:
abcabcabc
如果將光標放在第一行的b上然後輸入d2j,就會刪除所有三行。因為j是按行移動。如果按d<c-V>2j,就可以將移動轉變為塊移動,從而僅刪除中間的b的一列。
/regex/{n}
移動到匹配行下方的第n行,如果n是負數則向上移動。它還可以讓移動變成按行移動。所以如果要刪除當前位置到第一個正則匹配所在行的所有內容,可以使用d/regex//0。
ex命令
ex命令是在命令模式下輸入的東西,如:s。除了替換之外,ex還有許多有用的用法。下面所有例子都需要指定範圍,如%。
:g/regex/ex
僅在匹配regex的行上運行ex命令。例如,使用g/regex/d刪除所有匹配regex的行。v類似於g,不過它會在所有不匹配regex的行上運行命令。
與norm和friends結合使用更強大。
:norm {Vim}
該命令可以在指定範圍內的每一行運行{Vim}的命令。例如,g/regex/norm f dw會刪除匹配regex的每一行的第一個空格之後的第一個詞。這個方法通常比宏更簡單。
norm遵循所有鍵盤映射。比如你在插入模式下映射jk為<esc>,那麼norm I jk$diw將在行首插入一個空格,然後退出插入模式,然後刪除行的最後一個詞。我非常喜歡這個功能,但如果你希望不使用鍵盤映射,那麼可以執行norm!。
:co .
將範圍複製到當前行。也可以指定任意地點,如+3或'a.mv等。
:y {reg}
將範圍複製到寄存器{reg}。如果{reg}是大寫字母,則追加到已有的寄存器。即,如果執行
let@a = '' | %g/regex/y A
則複製整個文件中所有匹配regex的行到寄存器a。這個方法可以從文件中提取文本並複製到系統剪貼板(使用let @+ = @a)。
:windo {ex}
在所有窗口上運行ex。:windo $能將所有窗口滾動到最底部。也可以使用bufdo,cdo,tabdo等。
該命令非常適合與g、s一起使用。如果要替換所有AA為BB,但希望在每次替換前進行確認,那麼可以使用vimgrep AA將所有匹配加載到quickfix中,然後使用cdo s/AA/BB來查找替換所有匹配。
形式主義者的Vim
有一些命令需要永久存儲,或者需要改變Vim會話。理論上作為純粹主義者你也可以鍵入這些命令,但有些命令已經超出了純粹主義者的理想範圍。
這裡我只想寫一些不常見的東西。比如很多人都會把H映射到^,這個我就不需要再提了。我也不需要介紹vim-sensible或vim-surround,這裡只介紹更加不為人知的插件。
如果你經常修改vimrc,可以添加一個命令來做這件事:
command! Vimrc :vs $MYVIMRC
設置
我將所有的設置、鍵盤映射和函數都放在一個vimrc文件中。拆成多個文件後,我很難找到想找的東西。
許多設置實際上並不是Vim的「技巧」。最好去讀一下vim-sensible(https://github.com/tpope/vim-sensible),裡面介紹了適合vimrc的幾乎所有好東西。
set lazyredrew
不要在宏的執行過程中重繪屏幕。可以讓宏更快一些。
set smartcase/ignorecase
兩者全啟用後,搜索關鍵字中不含大寫字母則進行不區分大小寫搜索,包含大寫字母則進行區分大小寫搜索。
set undofile
支持永久的撤銷,即使重新啟動Vim也可以撤銷。與undotree插件一起使用非常好。
set foldcolumn={n}
使得摺疊在側邊欄中可見。n越大,以可視形式顯示的摺疊就越多,以數字形式顯示的摺疊越少。
set suffixesadd={str}
gf通常是「跳轉到光標所在處的文件」,但字符串中必須包含文件擴展名。suffixesadd可以同時檢查suffix指定的擴展名。例如,如果設置suffixesadd=.md,那麼在字符串「foo」下按gf會查找文件foo.md。
set inccommand=nosplit
該命令僅限Neovim。inccommand能夠實時顯示ex命令會造成的改變。現在它僅支持s,但即使如此,這個功能也極其有用。例如,輸入:s/regex會高亮顯示所有匹配regex的文本。如果繼續輸入/change,則會顯示出所有匹配替換成change的樣子。該功能適用於所有曾澤表達式屬性,甚至包括後向引用和分組。
set statusline (:h statusline)
設置每個窗口底部的欄中顯示什麼東西。與其他設置相比,這裡指定的格式非常複雜詳盡,要想完全解釋清楚需要寫一整篇文章。這裡僅從技巧的角度介紹幾點。首先,Vim的默認statusline為
:set statusline=%<%f\ %h%m%r%=%-14.(%l,%c%V%)\ %P
最容易替換的就是%p,它顯示當前位置在文件中的百分比。statusline格式中的%{exp()}為輸出exp()的結果。所以對於Markdown文件,我們可以這樣寫:
:set statusline=%<%f\ %h%m%r%=%-14.(%l,%c%V%)\ %{wordcount()[\"words\"]}
即可將百分比替換成文檔的單詞數。
還可以set tabline。如果你不使用標籤頁,那麼可以將tabline改成「全局的statusline」。比如:
set tabline=%{strftime('%c')}
這樣可以永遠在頂端顯示日期。
鍵盤映射
我設置了許多鍵盤映射。
Vim中許多快捷鍵都是無用的。s佔了整個鍵,它的功能只不過是cl。U跟u一樣,只不過它把撤銷當做新的修改,從功能上來說完全沒有用。Q跟gQ一樣。Z僅用於ZZ和ZQ。Vim手冊還推薦把_,,等綁定到自定義命令上,因為「你幾乎永遠不會用到它們」。然而,與節省幾次擊鍵相比,我更願意增加全新的功能。我的一些鍵盤映射包括:
nnoremap Q @@
不要進入ex模式,而是重複上一次運行的宏。
nnoremap s "_d
使s(以及ss和S的相應映射)表現得像d一樣,但不會將刪除的文本放到寄存器中。在需要刪除東西又不想搞亂匿名寄存器時很有用。
nnoremap <c-j> <c-w>j
將窗口移動到下方。還有對應於h,k,l的映射。使得移動窗口更容易。
nnoremap <leader>e :exe getline(line('.'))<cr>
將當前行當做命令執行。在試驗東西時通常比q:更方便。
特殊參數(:h map-arguments)
命令 map <buffer> lhs rhs 可以讓映射僅對該緩衝區生效。與自動命令結合作為臨時快捷鍵使用非常方便,也可以在函數中定義映射時使用。緩衝區映射比全局映射優先級更高,也就是說你可以用特殊用途的命令來覆蓋通用的命令。
每次使用map <expr> {lhs},{expr}都會對{expr}求值,然後用返回值作為實際的映射。一個簡單的例子就是條件映射。如:
nnoremap <expr> k (v:count == 0 ? 'gk' : 'k')nnoremap <expr> j (v:count == 0 ? 'gj' : 'j')
將映射j和k為在折行內部移動,但如果設置了count,則按照正常的規則移動。這樣j和k可以在很長的段落內部移動,同時不會改變10j等命令的行為。
如果要用映射來啟動ex命令,那麼<silent>非常方便。
inoremap
使用inoremap可以定義插入模式下的鍵盤映射。映射在插入模式下觸發,所以inoremap ;a aaaa將輸入'aaaa'而不是輸入';a'。如果想進行一般模式下的動作,可以使用<c-O>。即,
inoremap ;1 <c-o>ma
那麼輸入;1將會在輸入的地方設置'a的標記。
我喜歡使用分號作為imap的leader鍵,因為分號後面除了空格和換行之外幾乎不會接任何其他字符。
autocmd
自動命令非常適合在配置中使用。通常寫成如下形式:
augroup {name} autocmd! " Prevents duplicate autocommands au {events} {file regex} {command}augroup END
這樣,一旦在匹配{file regex}的文件中發生任何{events},{command}就會執行。事件可以用:h event列出。例如,如果這樣寫:
augroup every autocmd! au InsertEnter * set norelativenumber au InsertLeave * set relativenumberaugroup END
那麼Vim僅在插入模式下禁用relativenumber。
命令au {event} <buffer> {ex}可以僅在當前緩衝區中應用自動命令。有時候我會利用該命令為某個文件添加臨時的事件處理。
BufNewFile,BufRead
BufNewFile在創建新文件時觸發,而BufRead在第一次打開緩衝區時觸發。這兩個命令通常用於給特定文件類型添加設置和映射。我用到的一個例子是
augroup md autocmd! au BufNewFile,BufRead *.md syntax keyword todo TODO au BufNewFile,BufRead *.md inoremap <buffer> ;` ```<cr><cr>```<Up><Up>augroup END
這段代碼的意思是,僅對於markdown文件高亮顯示TODO,在插入模式下輸入;`可以添加代碼符號。
自動命令還可以完成更複雜的事情。例如,給BufWriteCmd添加au可以重載標準的保存操作,從而添加自定義的邏輯。這些內容已經超出了「Vim技巧」的範圍,已經屬於「黑科技」了。
插件
許多人都知道vim-surround、NERDtree等流行插件。下面是一些我認為非常有用的、不那麼流行的插件。
Undotree
大多數文本編輯器的撤銷都是線性的。如果你做了修改A,然後將其撤銷,再做修改B,那麼A就永久丟失了。而Vim會保存整個撤銷樹。u只能撤銷到當前分支的前一個狀態。g-能恢復到按時間排序的前一個版本。用:undolist精靈可以查看所有的撤銷葉節點。
但輸出格式不太容易閱讀。最好是能看到實際的樹狀結構。這就是Undotree的功能,它會為撤銷樹生成漂亮的ASCII表示形式,以便閱讀。
vim.swap
該插件可以交換兩個參數的位置,因此只需幾個鍵即可將(a, f(b, c))改成(f(b, c), a)。我經常需要做這種編輯,所以這個插件能極大地提高我的生活水準。
Neoterm
該插件為neo/vim內嵌的終端提供了高階API。比如,:T {text}可以將{text}發送到終端。更方便使用REPL。
" TODO {{{
還有許多東西太複雜太長了,比如編寫函數,或者編寫語法系統,就不在此一一介紹了。而且還有許多我不知道的東西。下面是我打算繼續學習的內容:
預覽,quickfix,窗口列表
有時我需要使用的工具用到了這些功能,但我不知道應該怎樣手動操作。我希望給我的TLA+插件(https://github.com/hwayne/tla.vim/)添加quickfix的功能。我還希望在預覽窗口中添加輔助信息和回調命令。這些功能很難在IDE中找到。
Neovim API
Neovim有豐富的API,可以將外部程序與Vim結合。所以可以使用Python腳本發送命令給Neovim實例,或者通過伺服器來控制。我見過一些非常酷的概念演示,可以根據瀏覽器中的內容實現自動完成。看起來非常有意思!
文本對象
從來沒有自己定義過。
----
不管怎樣,以上是我介紹的一些不為人知的Vim功能。希望對你有所幫助!
原文:https://www.hillelwayne.com/post/intermediate-vim/
本文為 CSDN 翻譯,轉載請註明來源出處。
【END】
「2019以太坊技術及應用大會」門票驚喜大促!
V神攜手眾多海內外知名區塊鏈專家來北京啦!自6月14日起,大會隆重推出618特惠票,低至399元,感恩新老用戶,驚喜不斷,虛左以待!掃碼了解詳情。