Vim 的哲學第四篇姍姍來遲,狗血的緣由我就很少說了,好消息是我將爲這個系列帶來一些動態演示。本來我打算錄視頻的,可是文章都寫了那麼些篇了,如今再錄視頻彷佛晚了些,因此我研究了一下如何錄製高質量的 GIF 動畫(第三方軟件都很差用,最後我仍是用 QuickTime 和一段腳原本完成錄製,挺酷的~)。接下來先奉上第一彈:程序員
上一期的基礎配置我遺漏了一個蠻重要的選項:shiftround
,這個選項真的很貼心很好用,遺憾的是官方文檔對此語焉不詳。我特地 Google 了一下才發現挺少有人解釋這個選項的。如今我也忘記了當初我是怎麼知道它的,而後我發現用文字也還挺難解釋清楚,因此仍是看動畫吧:正則表達式
round 在這裏應該是取整的意思。當你的縮進不成倍時,開啓這個選項將會讓 Vim 自動幫你把周圍的縮進化零爲整,你就不須要手動去填/刪空格了。順便提早講一下,縮進的指令是 <
或 >
鍵,它們支持移動指令(立刻講到),也支持數字前綴。對當前行執行縮進是 <<
或 >>
,也就是連按兩次。咱們在之後會詳細介紹關於縮進的知識。編程
OK,加上它在你的基礎配置裏,咱們開始新的旅程!vim
如今咱們知道使用 Vim 的一個很重要的原則就是遠離鼠標,遠離可視化的定位裝置(固然除了鍵盤之外),緣由我就再也不贅述了。在此原則之下,必然要有一些方式來幫助咱們選中目標而後作出咱們但願的更改,也就是移動和編輯。segmentfault
在常規模式裏有兩個很重要的概念,一個叫作「動做」(Motions),另外一個叫作「操做」(Operators)。動做,是指你能讓光標移動到哪裏,而操做則須要結合動做來決定你能夠對文本作什麼。經過兩者的結合,咱們能夠逐漸體會 Vim 是如何貫徹保持簡單這一原則的。編輯器
hjkl
雖然簡單,可是在不少場合之下它們太沒效率了,對麼?從如今開始我但願你記住:咱們一般不用 hjkl
作大量的光標移動。若是你發現你常常抽瘋似的按這四個鍵在屏幕裏來回移動,那你就已經錯了!這四個鍵真正的主要用處是做爲其餘動做和操做的輔助鍵來使用的,隨着咱們的深刻學習你會愈來愈理解這一點。工具
那麼除此以外咱們還有什麼選擇呢?學習
:help left-right-motions
當咱們以行/段爲單位來審視咱們的文本時,Vim 爲咱們提供了一些橫向移動的快捷動做。動畫
移動指令 | 移動效果 |
---|---|
0 |
移動光標至行首 |
$ |
移動光標至行尾 |
^ |
移動光標至行首的第一個非空白符的字符 |
g_ |
移動光標至行尾的最後一個非空白符的字符 |
f{char} F{char} |
向前(右)或向後(左)移動光標至指定的字符({char} )處,光標停留在該字符之上 |
t{char} T{char} |
向前(右)或向後(左)移動光標至指定的字符({char} )處,光標停留在該字符前/後面 |
經過上面這個表格,咱們能夠獲取到如下信息:ui
這還沒完,對於橫向移動來講,最麻煩的是當「迴繞」(wrap)出現的時候。所謂迴繞是指,當一行(段)的字符數目超過屏幕的可視寬度範圍時,編輯器會將超出的部分自動轉移到下一行來顯示,可是這並不是換行,也就是說沒有插入換行符,只是在顯示上不讓字符超出屏幕的最大寬度範圍而已。
這個特性固然不是 Vim 特有的,幾乎全部的編輯器都支持迴繞,但是 Vim 對待迴繞是和其餘編輯器徹底不一樣的,尤爲是在編輯長文檔時會讓你以爲有些古怪。我不會在本章詳細介紹關於迴繞的一切,由於本系列面向的讀者主要是程序員。對於編寫代碼這樣的工做,只要你遵循良好的編碼規範(你應該這麼作),那就不多會出現應對迴繞的狀況。因此目前爲止你學會上述四個動做指令就足夠了。將來的某一天我會專門寫一篇如何打造專業的 Markdown 編輯功能,在那時候咱們再回過頭來好好談談迴繞。
如今我列出橫向移動時若是遇到迴繞行咱們能夠作什麼,這樣你能夠本身試一下:
移動指令 | 移動效果 |
---|---|
gh gj gk gl |
讓光標在迴繞行內作四方向移動 |
g0 |
移動光標至當前回繞行的行首 |
g$ |
移動光標至當前回繞行的行尾 |
g^ |
移動光標至當前回繞行的行首的第一個非空白符的字符 |
gm |
移動光標至當前回繞行的中間位置(或儘量接近中間的位置) |
g
鍵你或許已經注意到 g
鍵的屢次出現了,彷佛它和其餘指令相互配合能夠產生許多新指令。沒錯,在 Vim 中有那麼幾個「萬能的」指令修飾鍵,g
是其中之一。若是你好奇還有多少指令是用 g
來修飾的,你能夠鍵入 :help g
來查看一個列表。另外你應該知道這份列表實際上是索引文檔的一部分,你能夠時常打開索引(:help index.text
)來考察下本身對 Vim 到底有多熟悉。
:help up-down-motions
縱向移動的指令比較多,我仍是先介紹幾個簡單而且最有用的:
移動指令 | 移動效果 |
---|---|
gg |
讓光標跳轉到文檔的最開始處 |
G |
讓光標跳轉到文檔的最後一行 |
{count}G :{count} |
讓光標跳轉到指定的行號,即 {count} 所表明的行號 |
{count}% |
讓光標跳轉到指定的百分比位置,好比說第一行是 0%,最後一行是 100% 等 |
H |
讓光標移動到當前屏幕的頂部(High Position),不滾屏 |
M |
讓光標移動到當前屏幕的中部(Middle Position),不滾屏 |
L |
讓光標移動到當前屏幕的底部(Low Position),不滾屏 |
{count}
是一個前綴標記,意思是這個指令前面能夠追加數字,好比說若是你鍵入 25G
,那麼光標就會移動到當前文檔的第 25 行去。這個特性很是重要,Vim 的強大和靈活在很大程度上都仰仗相似的前綴特性。
G
和 {count}G
實際上是同一個指令,只是帶上數字前綴與否會產生不一樣的效果,因此我分開寫了。這是第一次,一旦你瞭解了這個特色,之後我就不必分開了。
gg
和 G
是一對經典的搭檔,許多很是有用的操做都是它們倆配合完成的。舉個例子,《Vim 的哲學》全部的文字都是在Vim 裏完成的,每一次到最後我都要把它們複製粘貼到 SegmentFault 博客的發表頁面作最後的檢查而且發佈,如何所有複製過去?有不少辦法,我通常選擇以下兩種:
command + a
,而後 "+y
或者 "*y
gg"+yG
,拆開看:gg
+ "+y
+ G
喂喂,command + a
是全選吧,這是 Vim 的指令嗎?你別忽悠我喔~
我可沒有說你只能用 Vim 的內置指令呀,瞭解你所處的環境,在不影響效率,不打亂節奏的前提下善於利用一切能夠利用的工具,這不正是極客的特質之一嗎?只不過我不是每次都有機會用到 MacVim 的,因此 gg
+ G
的經典組合仍是必需要掌握的。至於 "+y
,這個涉及到寄存器(:help registers
)的知識,咱們稍後就會講到,別急。
:help word-motions
hjkl
是以字符爲單位來移動的,橫向和縱向移動基本上都是以句/段爲單位來使用的,然而有些狀況下咱們須要的是介於兩者之間的移動單位,也就是以詞爲單位。以詞爲單位使得咱們能夠更精確(也是更具語義化)的移動光標,而且要比逐個字符的移動要快得多。
這裏所說的詞特指的是像英語那樣的以空格(有時候也會是別的符號)爲分隔符的詞——很遺憾,中文分詞是很大的挑戰,Vim 沒有內置這一功能。像這樣的詞均可以分出詞頭和詞尾,好比 word 這個詞,詞頭是 w,詞尾是 d。
因而在 Vim 中的詞組移動也按照詞分頭尾的特性分紅了兩組:
移動指令 | 移動效果 |
---|---|
w b |
向前(右)或向後(左)移動光標至下一個詞組的詞頭位置 |
e ge |
向前(右)或向後(左)移動光標至下一個詞組的詞尾位置 |
一些朋友喜歡尋找每個指令對應的含義,這樣有助於形象記憶。事實上,這些含義也不是我編造出來的,內置的文檔裏都有很詳細的描述,好比說上面這幾個分別是:
w
:Words forwardb
:words Backwarde
:forward to the End of wordge
:backward to the End of word你可能在想:我幹嗎要關心詞頭和詞尾,好麻煩啊!這個問題其實無關於 Vim 的哲學,而是語言的哲學。
你看,我們的漢語和英語是徹底不一樣的兩個語種。在漢語裏詞與詞的界限是靠意義來劃分,書寫形式則不過重要。用漢語寫一句話,你能夠在詞組之間添加空格或者不添加空格,基本上不會對讀者產生影響(除了個別會產生歧義的特例)。而英文及其餘相似語言則否則,它們加或不加空格的差異大了去了!一段英文若是沒有空格,那幾乎就是沒法閱讀的。所以,對於母語爲相似語種的人羣來講,空格所劃分出的詞頭與詞尾是天然而然,司空見慣的事情,他們一點也不會以爲奇怪。不幸的是,做爲程序員的咱們也(被迫)得使用英語做爲咱們的主要書寫語言,所以你必須習慣去辨識和使用詞頭與詞尾。一旦你習慣了,你會發現它們很是有用。
爲了讓你看到正確使用詞頭詞尾的效果,我放一張圖給你對比一下差異。在這張圖裏,我分別演示了四種操做:
w
)e
)b
)ge
)每一步操做以後,注意觀察光標停留的位置和最終的效果:
這些結果或許是你指望的,也可能不是,但這沒關係,沒有哪種是絕對正確或錯誤的,重要的是你須要瞭解它們之間的差異,因而你能夠在必要的時候選擇正確的方式。
不過故事還沒完,以上四個指令各自還有一個變體,分別是:W
,B
,E
,gE
。要了解它們的做用,咱們得先聊一下詞的定界符。
對於詞和詞之間,空格是惟一的區隔標準嗎?很顯然不是。像這樣的詞:i_am_a_word
,Vim 會視爲 1 個詞,可是 i-am-a-word
,Vim 則會視爲 7 個詞!這是由於 Vim 容許你爲其指定能夠被視做詞組定界符的字符,因而當 Vim 遇到這些字符的時候,就會認爲是一個詞的結束。默認狀況下,_
不是定界符,因此它會被視做一個詞的組成部分。
然而有些時候咱們但願把這些定界符也看成詞組的一部分,這樣咱們能夠移動的快速一點,這時大寫版本的詞組移動指令就派上用場了,它們永遠都只把空白符(空格、TAB、EOL)視做詞組的定界符。
你會以爲本身定義定界符很酷吧?我會把它放在高級設置那一篇來說。
:help operator
Vim 內置了 15 個編輯指令(還有一些變體),可是通常來講咱們用不到那麼多。在本節咱們來學習其中的五種(共計 10 個):
:help d
若是你把光標對準某個字符,而後按下 d
(delete),你會發現什麼都沒有發生?不要驚訝,編輯操做是要配合移動指令來幹活的,我以前花大力氣介紹一堆移動指令不是漫無目的的不是?
OK,精彩的來了。當你按下 d
,Vim 會說:「好的夥計,你想要刪除對吧?接下來請告訴我你要刪什麼?」
若是你要刪一個詞,按下 dw
,也就是 delete word。
若是你要刪除兩個詞,按下 d2w
或者 2dw
,它們的效果是同樣的,可是它們表明的含義略有差異:
d2w
意思是:刪除 -> 2 個 -> 詞2dw
意思是:2 次 -> 刪除 -> 1 個詞在這個例子裏,兩種操做的結果不會產生歧義,因此你能獲得同樣的效果。可是之後你會發如今某些特定的條件下,數字前綴在不同的地方會產生不同的效果(不僅侷限於刪除操做)。因此,正確的理解操做的含義是有必要的,請記住:
理解操做的含義,而不是背誦操做的順序。就好像你說話說的是你想要表達的意思,而不是字詞的某種排列組合。有些時候你顛倒字詞的順序不會影響你要表達的意思,由於不存在歧義,但有些時候則正好相反,切記切記!
若是我要刪除一整行怎麼辦?簡單:dd
。
那若是我想要從光標的位置開始一直刪除到行結束呢?那還用我教你?d$
!不過 Vim 還有另一個版本等價於 d$
,它是:D
。
哦,那這麼說若是我使用 0d$
或者 0D
,就是和 dd
等價的咯?
啊哈~聰明的童鞋,很抱歉你錯了!可是不怪你,這是一個很重要的區別,咱們來單獨看一下示範:
看明白了嗎?其實差別是很是明顯的,dd
是連同行尾的行結束符(EOL)一塊兒刪除的,因此粘貼的時候也會連着行結束符一塊兒粘貼;而 0D
/0d$
則不會包含行結束符。
你可能還納悶呢,不是演示刪除的嗎?爲何刪掉的東西還能再粘貼回來呢?嗯,可能刪除這個詞不太恰當,若是改叫剪切是否是突然就以爲貼切起來了?
Vim 的刪除操做和咱們常見的剪切很是類似,事實上 Vim 的刪除指的是從你的眼前把目標文字移除到寄存器中,以後你還能夠從寄存器裏把刪除的部分再粘貼回來。Vim 擁有一堆各式各樣的寄存器,擅於使用寄存器可讓你的編輯工做變得異常輕鬆。從此咱們會單獨介紹寄存器的進階使用。
讓我來問你一個問題:若是要刪除一個字符該怎麼辦?你或許已經瞭解到 x
能夠刪除光標所在的那個字符,X
能夠刪除光標左邊的那個字符,可是你是否知道這兩個功能也是從 d
演化出來的呢?試着找找答案吧。
:help c
在你執行完刪除以後,緊接着按下 i
(insert),你就等於在改寫以前刪除的內容了。如何把這兩步簡化成一步?答案就是 c
(change)了。對於 c
,真的沒什麼好講的,你學會了 d
就等於學會了 c
,由於 c
就等於 d
完了緊接着 i
而已。
並且其餘相關聯的操做也是相似的,好比說:
c2w
:改寫兩個詞cl
:改寫光標所在位置的字符,而且 s
等價於 cl
cc
:改寫光標所在的一整行,而且 S
等價於 cc
c^
/c$
:從光標所在位置開始一直改寫到行頭/行尾,而且 C
等價於 c$
s
比較少用,由於一般改一個字符咱們會使用 r
,也就是替換(replace),可是 s
在編輯中文的時候有妙用,容我在這裏賣個關子,等到打造專業 Markdown 編輯器的時候再說(實在是不能都說了,要否則這篇結束不了了)。
S
比 cc
多節省一次按鍵,並且也比較好按(在標準鍵位上),因此推薦用 S
來代替 cc
。
:help y
複製是幾個基礎編輯操做裏怪癖略多的一個。首先是它的命名,y
是 yank 的首字母,可是 yank 又是什麼?它和拷貝(複製)有什麼關係呢?
這也和寄存器有關。你看,Vim 的複製/剪切(刪除)/粘貼操做都是基於它底層的寄存器的,因爲 c
已經被改寫(change)佔用了,而 Vim 的複製實質上是把目標文本拉拽(yank)到寄存器中備用,因此……好了你知道了就是了,咱不解釋那麼多,反正 y
就是複製了,愛咋咋地~
另一個怪癖出在 Y
身上,按照以前刪除和改寫的經驗,你必定會認爲 Y
就等同於 y$
唄,(Vim 亂入:「呵呵,圖樣圖森破!你覺得我會讓你這麼輕易就掌握訣竅嗎,少年?」)但是很不幸你又錯了。這一回,Y
又和 yy
等價了……
我一直都沒鬧清楚爲何到了複製這裏就和刪除/改寫不同了,就連官方的幫助文檔都是這麼說的:
若是你但願
Y
是從光標處複製到行尾(這樣更合乎邏輯,不過不兼容 Vi),你可使用:map Y y$
看起來惟一的緣由就是爲了和老 Vi 兼容,可是咱們徹底不在意這一點!後面的 :map Y y$
是鍵位映射,雖然咱們還沒講到,不過這一句你已經能夠把它放到你的 .vimrc
裏了,重啓 Vim 以後你會發現 Y
的表現和 C
D
它們保持一致了,謝天謝地!
:help p
粘貼就單純多了,只有兩個指令:
操做指令 | 移動效果 |
---|---|
p |
自光標所在位置向右粘貼默認寄存器裏的內容 |
P |
自光標所在位置向左粘貼默認寄存器裏的內容 |
默認寄存器裏的內容取決於你在粘貼前最後的編輯動做,有多是刪除或複製的一段文本,也有多是其餘的。因爲咱們尚未詳細介紹強大的寄存器功能,你或許會偶爾感到有些不便,在這裏我先介紹一個最經常使用的技巧:
有時候,咱們須要完成以下操做:
你看,這其實是要用 A 處的文原本替換 B 處的文本,但因爲默認寄存器只保留了最後一次的複製/刪除(剪切)內容,因此當你完成第 2 步的時候,你在第 1 步準備好的文本已經沒了……大多數人是這麼作的:
實際上,咱們可讓 Vim 不把指定的內容放入默認寄存器,這樣就不會覆蓋預先準備好的內容了,這等同於完全刪除而不是剪切。Vim 的默認寄存器是 ""
(也叫匿名寄存器,:h quotequote
),它保存常規的複製/刪除等操做的內容,Vim 還有一個名字很酷的寄存器叫作:黑洞寄存器(Blackhole Register,:h quote_
),它的按鍵是 "_
。若是你在鍵入任何操做以前先輸入 "_
,操做的結果將不會被任何寄存器保留下來,就好像丟入了一個深淵黑洞,再也回不來了……(好傷感 T_T)
所以,咱們能夠這麼玩:
"_dd
我把這個過程也錄了下來,你能夠對照看看:
我真是愛死這玩意兒了!不過你要知道,就上例而言黑洞寄存器不是惟一的辦法,說不定你更喜歡別的操做組合,好比下面這個:
這一套「組合拳」沒有用黑洞寄存器,它的好處是若是我反悔了,我還能夠撤銷以前的操做把被替換的內容找回來。整個過程的按鍵順序是這樣的:y$
-> gt
-> gP
-> D
。
請容許我用更加具備語義的方式來重複一遍上面的操做:
y$
:從光標所在位置(行首)複製到行尾(不包括換行符)gt
:切換至下一個標籤頁gP
:自光標位置向左粘貼剛纔複製的內容,結束以後把光標向右移動一個字符(這就是 g
的做用,爲了把末尾的 .
保留住。你也可使用 Pl
實現同樣的目標)D
:自光標位置刪除到行尾我但願你理解我這樣重複一遍的緣由,它包含了體現 Vim 哲學的三個側面:
好吧,第三點純粹是我在胡扯,哈哈。
大小寫轉換其實不算什麼大事,原本我也猶豫還要不要介紹一下,可是考慮到這個在編程的時候還挺有用的,因而索性一併說了吧,反正也很少……
操做指令 | 移動效果 |
---|---|
~ |
轉換光標所在字符的大小寫(嚴格來講,這不是一個操做指令) |
g~ |
轉換字符的大小寫(這個才真的是) |
gu |
強制轉換成小寫 |
gU |
強制轉換成大寫 |
解釋一下頭兩個,~
不是操做指令,是由於它沒辦法和移動指令結合,它就只會轉換當前光標所在位置的那個字符。若是你有多個字符須要轉換,你就只能一個一個按過去。g~
纔是轉換大小寫的正式版,它能夠結合移動指令。比方說按下 g~3j
會把往下 3 行的字符大小寫都轉換了(小寫變大寫,大寫變小寫)。不過 Vim 有一個選項叫作 tildeop
(:h tildeop),它默認是關閉的,若是你開啓它,~
就會變成和 g~
同樣了。這選項我記得很熟,由於我常常在團隊裏作重構工做,這種改寫命名的活兒一再重複,我索性就把 ~
變成真正的操做指令了。
另外,毫無心外的,它們幾個都有直接操做當前一整行的快捷版本,分別是:g~~
guu
gUU
。
趣味知識:你知道 ROT13 加密編碼嗎?這多是世界上最簡單的加密手段了,有意思的是 Vim 也內置了 ROT13 編碼/解碼功能。閒來無事的時候能夠拿來逗別人玩哦!切換 ROT13 編碼/解碼的操做指令是:
g?
(:help g?)
操做指令 | 移動效果 |
---|---|
gq |
自動應用排版規則 |
gw |
自動應用排版規則(光標位置不變) |
= |
自動應用縮進規則 |
< > |
手動應用縮進規則(左右兩個方向) |
前面兩個在編寫代碼時不太經常使用,卻是在編寫文檔時能發揮做用,所以它們不是重點,請自行查閱文檔並嘗試。
後面兩個就比較經常使用了,所謂「自動應用縮進規則」,前提是你得有可用的縮進規則。Vim 內置了很是多種語言的縮進規則,那些沒有內置的也基本上均可以在網上找到合適的縮進規則插件。此前咱們也在基礎設置裏打開了 filetype indent on
,因此此時若是你打開一份源碼文件,而後按下 gg=G
,「唰」的一下——整個世界清靜了。
還記得吧?gg
是去文件的最開始處,G
則是去文件的最後一行,因此這條命令的含義是:「從文件的開始處應用自動縮進規則直到文件的最後一行」。固然你能夠沒必要老是對整個文件進行自動縮進,以前咱們提到過的移動指令均可以搭配使用,以後咱們還要介紹更增強大靈活的文本對象選擇指令,搭配上自動縮進那叫一個如虎添翼~
至於 <
和 >
就沒什麼新鮮的了,手動縮進唄!縮進的寬度是由 shiftwidth
指定的,我們上次已經設置過了的有木有?另外它們也有針對當前行的快捷版本,你已經知道了,是吧?
想知道你的 Vim 內置了那些語言的縮進規則?鍵入這條命令:
:e $VIMRUNTIM/indent
你如今能夠嘗試把這些最經常使用的指令應用在你的平常工做裏了,若是你能堅持去尋找最有效率的操做方式,你終將會明白其實根本用不着裝太多的插件。我很樂意進一步幫助你,因此你若是在使用中有任何疑問請不要客氣盡管詢問我,我也喜歡看看有什麼新的挑戰,因此來吧~
其實,第四篇原本想直接講文本對象的,可是我擔憂新手會看不太懂,因而把文本對象一再日後擠。擠到如今才發現,天啊!這篇太長了,實在是不能再繼續下去了。因而,咱們只好對文本對象說拜拜了~我們下期再見!