掌握normal模式下常用操作的文法和概念,這些操作對應的應用場景以及實用技巧。
通過normal模式舉一反三掌握cmd-line和visual的常用文本操作。
文本操作的理想狀态為:一個操作符+一個動作指令。
normal、visual、cmd-line都具有操作文本的基本功能。
normal 操作符+動作指令。
visual 動作指令+操作符。
cmd-line 比對範圍+操作符。
這三個在文本的基礎操作時實作的功能是一樣的。主要圍繞着normal來講,其他的舉一反三即可。
學習新的動作指令及操作符,就像是在學習Vim 的詞彙一樣。如果掌握了這一簡單的文法規則,通過組合,在詞彙量增長時,就能表達更多的想法。是以必須明确每個操作的真實對應的概念,因為這些概念在整個vim都是通用的。
指令一般都是成對出現的,是以可以合起來記,這樣記憶場景量能少一半。例如:
單詞移動,從左向右和從右向左都有對應的動作指令。從左到右:w|W,e|E。從右向左:b|b,ge|gE.....
螢幕滾動, 向前和向後。Ctrl+F|Ctrl+B, Ctrl+U|Ctrl+D
大小寫。gu |gU
指令是象形。了解這個象形,有助于記憶。例如:
符号本身即表示向前還是向後的概念。),},],>
小寫的時候代表 word,大寫的代表WORD。 單詞移動:w(word),W(WORD)
比如在方向前加g,表示實體行和螢幕行。 k(移動到下一個實體行),gk(移動到下一個螢幕行)
大寫代表到行尾。C相當于c$,D相當于d$。
大小寫表示方向的。f{char} 從左向右查找,F{char}從右向左查找。
一個操作符指令被連續調用兩次時,它會作用于目前行。比如:dd:删除目前行,yy:複制目前行,>>:縮進目前行......
由非空白字元組成的連貫的集合,由空白字元分隔。字元不包括關鍵字(iskeyword)。
詳情可以檢視:h word ,:h iskeyword。
由非空白字元組成的連貫的集合,由空白字元分隔。字元包括關鍵字(iskeyword)。
例如:
This is mine/yours
其中的"mine"是word,而"mine/yours"是一個WORD,這裡"mine","/","yours"是三個word。這裡的"/"就是iskeyword。
詳情可以檢視:h WORD,:h iskeyword。
一個句子,以".","!","?"結尾。需要注意的是,是英文狀态中的符号,中文狀态下是無效的,比如"。"。
詳情可以檢視:h sentence。
實際行與螢幕行的差別。
與許多文本編輯器不同,Vim 會區分實際行與螢幕行。當‘wrap’ 設定被啟用時 (預設啟用),每個超出視窗寬度的文本行都會被回繞顯示,以保證沒有文本顯示不出 來。這樣一來,檔案中的一行也許會被顯示為螢幕上的若幹行。要想知道實際行與螢幕行之間的不同,最簡單的方法是啟用‘number’ 設定。
了解實際行與螢幕行間的差别很重要,因為 Vim 提供了不同的動作指令來操作 這兩者。j 和k 指令會根據實際行向下及向上移動,而gj 和gk 則是按螢幕行向下 及向上移動。即隻要有加g就表示的是螢幕行,沒有加g就表示的是實際行。
以空行區分開段落。
詳情可以檢視:h paragraph。
類似于java的程式設計語言的一個方法。
詳情可以檢視:h various-motions裡的]m。
有兩種情況:
1、以一個符号開始,又以這個符号結束的。比如(),[],{},<>,"",''。
2、以标簽開始,又以這個标簽結束的。比如 <aaa> </aaa>
這個主要是文本選擇的時候用的比較多。一般會區分a和i(inner)。a表示整個結構,i表示這個結構内部的文本。
詳情可以檢視:h object-select ,:h tag-blocks。
約定:如果後面需要跟動作指令的用{motion}表示。
詳情可以檢視 :h operator ,:h xx(操作符)。
定義:删除指定的文本并且進入insert模式。
文法:
操作符
實作功能
c{motion}
删除指定動作指令選擇代表的文本并且進入insert模式
cc
删除目前行并且進入insert模式
C
删除到光标到行尾的文本并且進入insert模式。相當于c$
s
删除光标下的字元并且進入insert模式。相當于cl
S
删除目前行。相當于cc。 能不按shift鍵我都不願意用shift。是以一般直接用cc
定義:删除指定的文本。
x
删除光标下的字元
X
删除光标前一個字元
d{motion}
删除指定動作指令代表的文本
dd
删除目前行
D
删除光标到目前行行尾。相當于d$
定義:指定的文本大小寫切換。
~
切換光标下的字元大小寫
~~
切換目前行的大小寫
g~{motion}
切換動作指令代表的文本的大小寫
gu{motion}
把動作指令代表的文本變為小寫
gugu
目前行變為小寫
gU{motiong}
把動作指令代表的文本變為大寫
gUgU
目前行變為大寫
定義:複制文本到寄存器。
y{motion}
複制動作指令代表的文本到寄存器
yy
複制目前行到寄存器
Y
複制目前行到寄存器,相當于yy。能不按shift鍵我都不願意用shift。是以一般直接用yy
定義:把複制的文本粘貼出來。
p
把複制的文本粘貼到光标之後。如果複制的是行,則放在目前行的下面,并且光标停留在複制文本的第一行位置
P(Shift+p)
把複制的文本粘貼到光标之前。如果複制的是行,則放在目前行的上面,并且光标停留在複制文本的下一行位置
gp
gP(Shift+p)
注意事項 :
有加g的操作符,如果是行複制需要注意。有加g最後光标停留在複制的文本的下一行,沒加g最後光标停留在複制的文本第一行。
定義:針對文本進行縮進。縮進針對的是行,是以是文本動作選擇對應的行的縮進。
>{motion}
向右縮進動作指令文本代表的行
>>
向左縮進目前行
<{motion}
向左縮進動作指令文本代表的行
<<
把文本折疊起來。此時操作折疊相當于操作一行。
這個現在暫時不涉及,感覺暫時應用不會太多,如果有用到後續會補充。
用來標明要操作的文本的範圍。
基本所有的動作指令前都可以加上量詞{count}表示重複,就不在一一做說明。
詳情可以檢視 :h motion,:h usr_03.txt,:h xx(動作指令)。
動作指令
動作說明
h
向左移動一個字元
l
向右移動一個字元
j
向下移動一個實際行
gj
向下移動一個螢幕行
k
向上移動一個實際行
gk
向上移動一個螢幕行
注意事項:
g用于差別實際行和螢幕行
移動到實際行的行首
g0
移動到螢幕行的行首
^
移動到實際行的第一個非空白字元
g^
移動到螢幕行的第一個非空白字元
$
移動到實際行的行尾
g$
移動到螢幕行的行尾
gm
移動到螢幕行的中間
+
移動到下一行行首第一個非空白字元
-
移動到上一行行首一個非空白字元
注意事項:
0與^的差別在于非空白字元。
F{char}
從右向左查找字元。光标停留在找到的字元上面。
f{char}
從左向右查找字元。光标停留在找到的字元上面。
T{char}
從左向右查找字元。光标停留在找到的字元後一個字元。
t{char}
從左向右查找字元。光标停留在找到的字元前一個字元。
;
重複上一次的行查找(f,F,t,T)
,
回到上一次的行查找的位置
大寫字母是從右向左,小寫字母是從右向右
f系統是光标停留在查找到的字元上面,t系列是光标停留在查找到的字元臨近一個字元。
G
檔案的最後一行
nG
跳到檔案的第n行。
gg
檔案第一行,相當于1G
n%
跳到檔案的百分之n行。
w
從左向右,移動到下一個單詞詞首
W
從左向右,移動到下一個大單詞詞首。
b
從右向左,如果光标不是在詞首。則移動到本單詞詞首。如果在詞首移動到下一個單詞詞首。
B
從右向左,如果光标不是在詞首。則移動到本單詞詞首。如果在詞首移動到下一個大單詞詞首。
e
從左向右,如果光标不是在詞尾,則移動到本單詞詞尾。如果在詞尾移動到下一個單詞詞尾
E
從左向右,如果光标不是在詞尾,則移動到本單詞詞尾。如果在詞尾移動到下一個大單詞詞尾
ge
從右向左,移動到下一個單詞詞尾
gE
從右向左,移動到下一個大單詞詞尾
注意事項
大寫代表WORD,小寫代表word
)
移動下一個句子句首。
(
移動上一個句子句首。
}
移動下一個段落段首前的空行上
{
移動上一個段落段首前的空行上
]]
移動到下一個以“{"為第一個字元的行首。如果結合操作符,則停留下一個"{"的上一行。
[[
移動到上一個以“{"為第一個字元的行首。如果結合操作符,則停留上一個"{"的那一行
][
移動到下一個以“}"為第一個字元行首。如果結合操作符,則停留下一個"}"上一行
[]
移動到上一個以“}"為第一個字元行首。如果結合操作符,則停留上一個"}"那一行
标記有兩類型的符号'(單引号),`(反撇鍵)。',表示定位到行首。`,表示定位到行首的第一個非空白地方。後面跟的任何指令,兩個類型都有,是以就不一一說明了隻取'來做文法說明。
m{a-zA-Z}
設定标記
'[
跳到最近一次修改或者複制文本的第一行位置
']
跳到最近一次修改或者複制文本的最後一行位置
'>
跳到最近一次visual選擇的最後一行位置
'<
跳到最近一次visual選擇的第一行位置
''(兩個單引号)
跳到最近一次上方位置
'"
跳到檔案上一次打開的最後位置
'^
跳到光标退出插入模式的地方
'.
跳到最後一次檔案修改的地方
]'
跳到上一個小寫字母标記(用:marks檢視)的地方
['
跳到下一個小寫字母标記的地方
注意事項:
還有一些沒有實際意義的,不列出來了。'),'},'(,'{ 相當于(,),{,}。
需要注意的是:配合cmd-line下的:marks。檢視所有标記 。
詳情可以檢視:h marks。
一個跳越,指定的是使用以下指令: "'", "`", "G", "/", "?", "n","N", "%", "(", ")", "[[", "]]", "{", "}", ":s", ":tag", "L", "M", "H" 等指令
Ctrl+o
跳到上一個光标位置
Ctrl+i
跳到下一個光标位置
ng,
向之前n次修改跳越
ng;
向之後n次修改跨越
注意事項:
配合cmd-line下的:jumps,檢視所有跳越。
配合cmd-line下的:changes,檢視所有修改的跳越。
詳情可以檢視:h jumps ,:h changes 。
%
找到比對的([{}])
[(
找到不比對的(
[{
找到上一個不比對的{
]m
跳到下一個方法
]符号( #,*,/)
跳到下一個符号的位置
一旦執行這個動作指令,就相當于執行這個選擇的區域。
a和i的差別。 i:表示的是inner,指的是區域内的文本;a:表示的是a,指的是一個完整的區域文本内容。
後面就隻寫區域名稱,不一一寫出來了,都是成對出現的。
代表的文本區域
句子
段落
]或[
一個"]"塊
)或(
一個"("塊
}或{
一個"{"塊
相當于一個"("塊
相當于一個"{"塊
>或<
一個"<"塊
t
一個tag blocks,相當于<aa></aa>
"
一個"""塊
'
一個"'"塊
詳情可以檢視:h object-select。
目前視窗滾動文本内容顯示。
z{count}CR(Enter鍵)。設定視窗高度。
詳情可以檢視:h scroll.text ,:h xx。
CTRL+E
向下一行。光标一直往上走,直到頂到螢幕第一行,滾動的是内容
CTRL+Y
向上一行.光标一直往下走,直到頂到螢幕最後一行,滾動的是内容
Ctrl+D
向下半屏。光标在螢幕的相對位置不變,滾動的是内容
Ctrl+U
向上半屏。光标在螢幕的相對位置不變,滾動的是内容
CTRL+F
向下一屏。光标一直在第一行,滾動的是内容。
CTRL+B
向上一屏.光标一直在最後一行,滾動的是内容。
說明:螢幕裡的内容不滾動,光标移動。
H
光标定位到目前螢幕最上行
M
光标定位到目前螢幕的中間行
L
光标定位到目前螢幕的最下行
說明:光标在原來的實體行位置不動,螢幕裡的内容滾動最大值一屏。
zt
光标置于螢幕第一行,光标的列還是和移動之前一緻。
z+
光标置于螢幕第一行。相當于z<CR(Enter鍵)>,光标在于行首
zz
光标置于螢幕中文,光标的列還是和移動之前一緻。
z.
光标置于螢幕中文,光标在于行首
zb
光标置于螢幕下方,光标的列還是和移動之前一緻。
z-
光标置于螢幕最後一行,光标在于行首
z後面如果跟的是字母則停留在原來的位置,如果跟的是字元停留在行首
詳情可以檢視,:h z
{count}ctrl+a 加法
{count}ctrl+x 減法
在做一些數字處理和校正時有用。例如
文法:
定義宏:
1、q{name}
2、錄制的指令
3、q
使用宏:
${count}@{name}
這些小技巧都出處于《vim實用技巧》,大家如果有興趣可以進一步閱讀學習。
我們嘗試了3 種不同的方式來删除一個詞:dbx、bdw 以及daw。他們高爾夫數(按鍵操作數)都是3,哪種方式最具重複性?我們針對這三種情況,都使用.指令操作一下。
dbx包含兩步操作:db 指令删除至單詞的開頭,而後x 指令删除一個字元。如果我們跟着執行一次. 指令,它會重複删除一個字元( . = = x )。我不覺得這有什麼價值。
bdw包含兩步。這一次,b 隻是一次普通的移動,而dw 完成修改。此時用. 指令會重複dw,删除從光标位置到下個單詞開頭的内容。不過因為我們剛好已經在行尾了,并沒有“下一個單詞”,是以在這個場景裡. 指令沒什麼用。不過,至少它代表了一個更長點的操作(. = = dw)
daw隻調用一個操作:daw。這個操作不僅僅删除了該單詞,它還會删除一個空格,是以光标最終會停在單詞“is”的最後一個字元上。如果此時我們使用. 指令,它會重複上次删除單詞的指令。這一次, . 指令會做真正有用的事情(. = = daw)。
結論:daw 可以發揮. 指令的最大威力,是以我宣布它是本輪的獲勝者。
如果你發現自己要在幾個地方做同樣的小修改,就可以嘗試構造你的修改,讓它們能夠被. 指令重複執行。要識别出這類機會需要進行一定的實踐,不過一旦你養成了使修改可重複的習慣,那麼你就會從 Vim 這裡得到“獎賞”。
在處理某些特定工作時,使用次數可以使按鍵次數變得最少,不過我們并不是非得這樣不可。我們需要認真考慮次數與重複各自的優缺點。
我們的3 種選擇d2w、2dw 或者dw. 都是3 次按鍵,不過哪一種最好呢?
根據我們的讨論,d2w 和2dw 是相同的,在執行完兩者中的任一個後,我們可以按u 鍵撤銷,這樣兩個被删除的單詞又會回來。或者,我們不是用撤銷,而是用.指令重複執行它,這就會删除後面的兩個單詞。
對于dw. 的情形,按u 或. 的結果會有細微的差别。這裡的修改是dw,即删除一個單詞。是以,如果想恢複這兩個被删除的單詞,必須撤銷兩次,按uu(或者,如果你願意,也可以按2u)。按. 則隻删除後面的一個單詞,而不是兩個。
現在假設我們原本是想删除3 個單詞,而不是2 個。由于判斷出了點差錯,我們執行了d2w 而不是d3w,那接下來怎麼做?我們不能使用. 指令,因為那會總共删除4個單詞。是以,我們或是先撤銷而後修正次數(ud3w),或是繼續删除下一個單詞(dw)。
現在考慮另一種方案,如果我們在第一處地方用的是dw. 指令,那麼我們隻要再多重複一次. 指令就行了。因為我們最初的修改隻是簡單的dw,是以u 指令和. 指令都具有更細的粒度,每次隻作用于一個單詞。
現在假設我們想删除7 個單詞,我們可以運作d7w,或是dw......(即dw 後面跟6 次. 指令)。計算一下按鍵的次數,哪個指令勝出是很顯而易見的。不過你真地确信自己數對了次數嗎?計算次數很是讨厭,是以我甯願按6 次. 指令,也不願意隻為減少按鍵的次數,
而浪費同樣的時間去統計次數。如果我多按了一次. 指令怎麼辦?沒關系,隻要按一次u 鍵就可以回退回來。
在此場景中,使用. 指令的意義不大,我們可以删除一個單詞,然後再用. 指令删除另一個,但随後我們還得切換到插入模式(例如,使用i 或cw)。對我來說這麼做很不順手,我反而更願意用次數。
使用次數的另一個好處是:它保留了一個幹淨、連貫的撤銷曆史記錄。完成這次修改後,我們按一下u 鍵就可以撤銷整個修改。
這條指令會删除此單詞,外加一個空格,是以結果會很幹淨。如果我們用的是diw的話,那删完後就會有兩個連在一起的空格,這或許并不是我們想要的。
ciw 指令隻删除該單詞,而不删除其前後的空白字元,随後它會進入插入模式,這剛好是我們想要的效果。如果用的是caw 的話,那最後兩個單詞就會連在一起,變成“mostadjectives”。雖然這很容易修正,但如果一開始就能避免此問題,那豈不是更好麼。
一般來說,d{motion} 指令和aw、as 和ap 配合起來使用比較好,而c{motion}指令和iw 及類似的文本對象一起用效果會更好。