Usr_10
Nvim 的 :help
頁面,是使用 tree-sitter-vimdoc 解析器,從 原始碼 產生 的。
VIM 使用者手冊 - 作者:Bram Moolenaar
進行大型變更
在第 4 章中,說明了幾種進行小變更的方法。本章將探討如何進行重複或影響大量文字的變更。可使用「視覺模式」對文字區塊進行各種操作。若要進行非常複雜的操作,則可使用外部程式。
錄製與播放命令
「.」命令會重複上一個變更。但若想執行比單一變更更複雜的操作,該怎麼辦?這時就可使用命令錄製功能。總共有三個步驟
1. 「q{暫存器}」命令開始將按鍵錄製到名為 {暫存器}
的暫存器中。暫存器名稱必須是 a 到 z 之間的字母。2. 輸入你的命令。3. 若要結束錄製,請按下 q(不帶任何額外字元)。
現在你可以輸入「@{暫存器}」命令來執行巨集。
看看如何在實務上使用這些命令。假設你有一份類似如下的檔名清單
stdio.h
fcntl.h
unistd.h
stdlib.h
而你想要的是下列形式
#include "stdio.h"
#include "fcntl.h"
#include "unistd.h"
#include "stdlib.h"
首先將游標移到第一行的第一個字元。接著執行以下命令
qa 開始在暫存器 a 中錄製巨集。^ 移至行首。i#include "<Esc> 在行首插入字串 #include " 。$ 移至行尾。a"<Esc> 在行尾附加雙引號 (") 字元。j 移至下一行。q 停止錄製巨集。
現在你已經完成一次操作,可以輸入「@a」命令重複變更三次。「@a」命令可以帶有一個數字,表示巨集將會執行多少次。在這個例子中,你可以輸入
3@a
移動並執行
你可能想在不同的位置變更行。只要將游標移至每個位置,然後使用「@a」命令即可。如果你已經執行過一次,可以使用「@@」再次執行。這更容易輸入。如果現在使用「@b」執行暫存器 b,則下一個「@@」將會使用暫存器 b。如果將播放方法與使用「.」比較,會發現有幾個差異。首先,「.」只能重複一個變更。如上述範例所示,「@a」可以執行數個變更,還可以移動。其次,「.」只能記住最後一個變更。執行暫存器可讓你進行任何變更,然後仍然使用「@a」重播已錄製的命令。最後,你可以使用 26 個不同的暫存器。因此你可以記住 26 個不同的命令序列來執行。
使用暫存器
用於錄製的暫存器與用於複製和刪除命令的暫存器相同。這可讓你將錄製與其他命令混合,以操作暫存器。假設你已在暫存器 n 中錄製了一些命令。當你使用「@n」執行這些命令時,你會發現做錯了某些事。你可以嘗試再次錄製,但可能會犯另一個錯誤。因此,請使用這個技巧
G 移至檔案結尾。o<Esc> 建立一個空行。"np 放入來自 n 暫存器的文字。你現在會看到你輸入的命令會以文字形式顯示在檔案中。{編輯}
變更錯誤的命令。這就像編輯文字一樣。0 移至行首。"ny$ 將已更正的命令複製到暫存器 n 中。dd 刪除臨時行。
現在你可以使用「@n」執行已更正的命令。(如果錄製的命令包含換行符號,請調整範例中的最後兩個項目,以包含所有行。)
附加至暫存器
到目前為止,我們使用小寫字母作為暫存器名稱。若要附加至暫存器,請使用大寫字母。假設你已錄製將某個單字變更至暫存器 c 的命令。它可以正常運作,但你想要新增搜尋下一個要變更的單字的功能。這可以使用以下方式完成
qC/word<Enter>q
你從「qC」開始,它會錄製到 c 暫存器並附加。因此,寫入大寫暫存器名稱表示附加至具有相同字母但為小寫的暫存器。
這適用於錄製、複製和刪除命令。例如,你想要將一系列行收集到 a 暫存器中。使用以下命令複製第一行
"ayy
現在移至第二行,然後輸入
"Ayy
針對所有行重複此命令。現在 a 暫存器包含所有這些行,順序為你複製它們的順序。
「:substitute」命令可讓你對整行範圍執行字串取代。此命令的一般形式如下
:[range]substitute/from/to/[flags]
此命令會將 [範圍] 中指定的行中「from」字串變更為「to」字串。例如,你可以使用以下命令將所有行的「Professor」變更為「Teacher」
:%substitute/Professor/Teacher/
注意:「:substitute」命令幾乎永遠不會完整拼寫出來。大多數情況下,人們會使用縮寫版本「:s」。從這裡開始將會使用縮寫版本。
命令前的「%」表示命令會作用於所有行。如果沒有範圍,「:s」只會作用於目前行。有關範圍的詳細資訊,請參閱下一節
10.3。
依預設,「:substitute」命令只會變更每一行的第一個出現項目。例如,先前的命令會將行
Professor Smith criticized Professor Johnson today.
變更為
Teacher Smith criticized Professor Johnson today.
若要變更行中的每個出現項目,你需要新增 g(全域)旗標。此命令
:%s/Professor/Teacher/g
會產生(從原始行開始)
Teacher Smith criticized Teacher Johnson today.
其他旗標包括 p (print),這會讓「:substitute」命令列印出最後一個變更的行。c (confirm) 旗標會告知「:substitute」在執行每個取代之前,要求你進行確認。輸入以下內容
:%s/Professor/Teacher/c
Vim 會尋找「Professor」的第一個出現項目,並顯示它即將變更的文字。你會收到以下提示
replace with Teacher (y/n/a/q/l/^E/^Y)?
此時,你必須輸入下列其中一個答案
y 是;進行此變更。n 否;跳過此相符項目。a 全部;進行此變更,並在無需進一步確認的情況下進行所有其餘變更。q 退出;不再進行任何變更。l 最後一個;進行此變更,然後退出。CTRL-E
將文字向上捲動一行。CTRL-Y
將文字向下捲動一行。
替換命令的「from」部分實際上是一個模式。與搜尋命令所使用的類型相同。例如,此命令只會在「the」出現在行首時取代它
:s/^the/these/
如果你的「from」或「to」部分包含斜線,你需要在斜線前面加上反斜線。更簡單的方法是使用斜線以外的另一個字元。例如加號
:s+one/two+one or two+
「:substitute」命令,以及許多其他 : 命令,都可以套用至選取的行。這稱為範圍。範圍的簡單形式是
{數字}
,
{數字}
。例如
:1,5s/this/that/g
在第 1 到第 5 行執行替換命令。包含第 5 行。範圍一律放在命令之前。
可以使用單一數字來處理一個特定行
:54s/President/Fool/
當你未指定範圍時,某些命令會作用於整個檔案。若要讓它們作用於目前行,請使用「.」位址。「:write」命令就是如此。如果沒有範圍,它會寫入整個檔案。若要讓它只將目前行寫入檔案
:.write otherfile
第一行始終是數字 1。那麼最後一行呢?「$」字元用於此目的。例如,若要在從游標到結尾的行中進行替換
:.,$s/yes/no/
我們之前使用的「%」範圍,實際上是表示「1,$」(從第一行到最後一行)的簡短方式。
在範圍中使用模式
假設你正在編輯一本書中的章節,並想將所有出現的「grey」取代為「gray」。但只在這個章節中,而不是在下一個章節中。你知道只有章節邊界的第一欄才會有「Chapter」這個字。那麼此命令會有效
:?^Chapter?,/^Chapter/s=grey=gray=g
你可以看到搜尋模式使用了兩次。第一個「?^Chapter?」會尋找目前位置上方符合此模式的行。因此,「?模式?」範圍會用於向後搜尋。同樣地,「/^Chapter/」則用於向前搜尋下一個章節的開頭。為了避免與斜線混淆,這裡在替換命令中使用「=」字元。斜線或其他字元也會有效。
新增和減少
上述命令中存在一個小錯誤:如果下一個章節的標題包含「grey」,它也會被取代。也許這正是你想要的,但如果你不想要呢?那麼你可以指定一個位移。若要搜尋模式,然後使用其上方的行
/Chapter/-1
你可以使用任何數字來取代 1。若要處理符合項目下方的第二行
/Chapter/+2
位移也可以與範圍中的其他項目一起使用。看看這個
:.+3,$-5
這會指定範圍,該範圍從游標下方三行開始,到檔案中最後一行前的五行結束。
使用標記
你可以使用標記,而不是找出特定位置的行號、記住它們,然後在範圍中輸入它們。如第 3 章所述放置標記。例如,使用「mt」標記區域的頂部,並使用「mb」標記底部。然後你可以使用此範圍來指定標記之間的行(包括包含標記的行)
:'t,'b
視覺模式和範圍
你可以使用「視覺模式」選取文字。如果你接著按下「:」啟動冒號命令,你會看到這個
:'<,'>
現在你可以輸入命令,它會套用至視覺選取的行範圍。
注意:當使用「視覺模式」選取部分行,或使用 CTRL-V
選取文字區塊時,冒號命令仍然會套用至整行。這可能會在未來版本的 Vim 中變更。
「'<」和「'>」實際上是標記,位於視覺選取的開頭和結尾。標記會保留在其位置,直到進行另一個視覺選取。因此,你可以使用「''<」命令跳至視覺區域開始的位置。你可以將標記與其他項目混合使用
:'>,$
這會處理從視覺區域結尾到檔案結尾的行。
行數
當你知道要變更多少行時,你可以輸入數字,然後輸入「:」。例如,當你輸入「5:」時,你會得到
:.,.+4
現在你可以輸入你要使用的命令。它會使用範圍「.」(目前行)到「.+4」(向下四行)。因此,它會涵蓋五行。
「:global」指令是 Vim 中較強大的功能之一。它允許您尋找符合模式的字串,並在那裡執行指令。其一般形式為:
:[range]global/{pattern}/{command}
這類似於「:substitute」指令。但是,它不是用其他文字取代匹配到的文字,而是執行
{command}
指令。
注意: 「:global」執行的指令必須以冒號開頭。一般的模式指令不能直接使用。您可以使用
:normal 指令來達成此目的。
假設您想將「foobar」變更為「barfoo」,但僅限於 C++ 風格的註解中。這些註解以「//」開頭。請使用以下指令:
:g+//+s/foobar/barfoo/g
此指令以「:g」開頭。這與「:s」是「:substitute」的縮寫一樣,「:g」是「:global」的縮寫。接著是模式,以加號字元括住。由於我們要尋找的模式包含斜線,因此使用加號字元來分隔模式。接下來是將「foobar」變更為「barfoo」的 substitute 指令。global 指令的預設範圍是整個檔案。因此,在此範例中沒有指定範圍。這與「:substitute」不同,後者若無指定範圍則會在一行上運作。此指令並非完美,因為它也會匹配到一行中間出現「//」的行,且取代動作也會在「//」之前發生。
與「:substitute」相同,可以使用任何模式。當您稍後學習更複雜的模式時,您也可以在此處使用它們。
使用 CTRL-V
您可以開始選取文字的矩形區域。有些指令會對文字區塊執行特殊操作。
在可視區塊模式中使用「$」指令有一些特殊之處。當最後使用的移動指令是「$」時,可視選取範圍中的所有行都會延伸至行尾,即使游標所在的行較短。這會持續有效,直到您使用水平移動游標的移動指令為止。因此,使用「j」會保持有效,「h」則會停止。
插入文字
指令「I{字串}<Esc>」會在每行中,可視區塊的左側插入文字 {字串}
。首先,按下 CTRL-V
進入可視區塊模式。現在,移動游標來定義您的區塊。接下來,輸入 I 進入插入模式,然後輸入要插入的文字。在您輸入時,文字只會出現在第一行。在您按下 <Esc>
結束插入後,文字會神奇地插入到可視選取範圍中包含的其餘行中。範例:
include one
include two
include three
include four
將游標移動到「one」的「o」並按下
CTRL-V
。使用「3j」將游標向下移動到「four」。您現在擁有一個跨越四行的區塊選取範圍。現在輸入:
Imain.<Esc>
結果如下:
include main.one
include main.two
include main.three
include main.four
如果區塊跨越未延伸到區塊中的短行,則不會在該行中插入文字。例如,選取一個可視區塊,其中包含此文字的第一行和最後一行中的「long」一詞,因此在第二行中沒有選取任何文字:
This is a long line
short
Any other long line
^^^^ 選取的區塊
現在使用指令「Ivery <Esc>
」。結果如下:
This is a very long line
short
Any other very long line
在短行中沒有插入文字。
如果您插入的字串包含換行符號,則「I」的行為就像一般的插入指令一樣,只會影響區塊的第一行。
「A」指令的作用方式相同,不同之處在於它會附加在區塊的右側之後。它也會在短行中插入文字。因此,您可以選擇是否要將文字附加到短行中。「A」有一個特殊情況:選取一個可視區塊,然後使用「$」使區塊延伸至每行的末尾。現在使用「A」會將文字附加到每行的末尾。使用上面的相同範例,然後輸入「$A XXX<Esc>」,您會得到以下結果:
This is a long line XXX
short XXX
Any other long line XXX
這確實需要使用「$」指令。Vim 會記住它已被使用。透過使用其他移動指令將游標移動到最長行的末尾來進行相同的選取,將不會產生相同的結果。
變更文字
可視區塊「c」指令會刪除區塊,然後將您帶入插入模式,讓您可以輸入字串。字串會插入到區塊中的每一行。從上面相同的「long」字詞的選取範圍開始,然後輸入「c_LONG_<Esc>」,您會得到:
This is a _LONG_ line
short
Any other _LONG_ line
就像「I」一樣,短行不會變更。此外,您無法在新文字中輸入換行符號。
「C」指令會刪除從區塊左邊緣到行尾的文字。然後,它會將您置於插入模式,讓您可以輸入一個字串,該字串會附加到每行的末尾。再次從相同的文字開始,然後輸入「Cnew text<Esc>」,您會得到:
This is a new text
short
Any other new text
請注意,即使僅選取了「long」這個字詞,它之後的文字也會被刪除。因此,可視區塊左邊緣的位置才是真正重要的。同樣,不延伸到區塊中的短行會被排除在外。
其他變更區塊中字元的指令
~ 交換大小寫 (a -> A 和 A -> a) U 設為大寫 (a -> A 和 A -> A) u 設為小寫 (a -> a 和 A -> a)
以一個字元填滿
若要以一個字元填滿整個區塊,請使用「r」指令。再次從上面相同的範例文字開始,然後輸入「rx」:
This is a xxxx line
short
Any other xxxx line
位移
指令「>」會將選取的文字向右移動一個位移量,並插入空白。此位移的起點是可視區塊的左邊緣。再次使用相同的範例,「>」會產生以下結果:
This is a long line
short
Any other long line
位移量是透過
'shiftwidth' 選項指定的。若要將其變更為使用 4 個空格:
:set shiftwidth=4
「<」指令會移除區塊左邊緣的一個位移量空白。此指令受限於現有文字量;因此,如果可用的空白少於一個位移量,則會移除其所能移除的空白。
合併行
「J」指令會將所有選取的行合併為一行。因此,它會移除換行符號。實際上,換行符號、開頭的空白和結尾的空白會被一個空格取代。行尾後會使用兩個空格(可以使用
'joinspaces' 選項變更)。讓我們使用我們現在非常熟悉的範例。使用「J」指令的結果如下:
This is a long line short Any other long line
「J」指令不需要區塊式的選取。它以完全相同的方式與「v」和「V」選取一起運作。
如果您不希望變更空白,請使用「gJ」指令。
當您撰寫電子郵件訊息時,您可能想要包含另一個檔案。可以使用「:read {filename}
」指令來完成此操作。檔案的文字會放置在游標行之下。從以下文字開始:
Hi John,
Here is the diff that fixes the bug
Bye, Pierre.
將游標移動到第二行,然後輸入:
:read patch
名為「patch」的檔案會被插入,結果如下:
Hi John,
Here is the diff that fixes the bug
2c2
< for (i = 0; i <= length; ++i)
---
> for (i = 0; i < length; ++i)
Bye, Pierre.
「:read」指令接受範圍。檔案會放置在此範圍的最後一個行號之下。因此,「:$r patch」會將「patch」檔案附加到檔案的末尾。如果您想在第一行上方讀取檔案,該怎麼辦?這可以使用行號零來完成。此行並不存在,您將在使用大多數指令時收到錯誤訊息。但此指令是被允許的:
:0read patch
「patch」檔案會放置在檔案的第一行上方。
寫入一系列行
若要將一系列行寫入檔案,可以使用「:write」指令。不指定範圍的話,它會寫入整個檔案。指定範圍則只會寫入指定的行:
:.,$write tempo
這會將從游標到檔案末尾的行寫入「tempo」檔案。如果此檔案已存在,您會收到錯誤訊息。Vim 會保護您免於意外覆寫現有檔案。如果您知道自己在做什麼,並想要覆寫檔案,請附加 !:
:.,$write! tempo
小心:! 必須緊接在「:write」指令之後,沒有空格。否則它會變成篩選指令,這將在本章稍後說明。
附加到檔案
本章的第一節說明了如何將多行收集到暫存器中。也可以執行相同的操作來將多行收集到檔案中。使用此指令寫入第一行:
:.write collection
現在將游標移動到您要收集的第二行,然後輸入:
:.write >>collection
「>>」會告訴 Vim,「收集」檔案不是要寫入為新檔案,而是必須將該行附加到末尾。您可以根據需要重複此操作多次。
當您輸入純文字時,如果每行的長度自動修剪以適合視窗會很方便。若要在插入文字時執行此操作,請設定
'textwidth' 選項:
:set textwidth=78
您可能還記得,在 vimrc 檔案範例中,此指令用於每個文字檔案。因此,如果您使用的是該 vimrc 檔案,則您已經在使用它。若要檢查
'textwidth' 的目前值:
:set textwidth
現在,行會斷開以僅佔用最多 78 個字元。但是,當您在一行的中間插入文字,或刪除一些字詞時,行會變得太長或太短。Vim 不會自動重新格式化文字。若要告訴 Vim 格式化目前的段落:
gqap
這從 "gq" 命令開始,它是一個運算符。後面是 "ap",這是文字物件,代表「一個段落」。段落之間由空行分隔。
注意: 包含空白字元的空白行,並**不**分隔段落。這點很難注意到!
你可以使用任何移動或文字物件來代替 "ap"。如果你的段落分隔正確,你可以使用這個命令來格式化整個檔案
gggqG
"gg" 會將你帶到第一行,"gq" 是格式化運算符,而 "G" 是跳到最後一行的移動命令。
如果你的段落沒有明確定義,你可以只格式化你手動選擇的行。將游標移動到你想格式化的第一行。從 "gqj" 命令開始。這會格式化目前行和下面的一行。如果第一行很短,下一行的文字會附加到第一行。如果第一行太長,文字會移動到下一行。游標會移動到第二行。現在你可以使用 "." 來重複該命令。持續執行直到你到達想要格式化的文字結尾。
你有一些文字,其中章節標題是小寫的。你想要將 "section" 這個詞全部變成大寫。使用 "gU" 運算符來完成。將游標放在第一欄開始
gUw
section header ----> SECTION header
SECTION header ----> section header
你也可以使用 "g~" 來交換大小寫。所有這些都是運算符,因此它們可以與任何移動命令、文字物件和可視模式一起使用。要使運算符作用於行,你將其重複使用。刪除運算符是 "d",因此要刪除一行,你使用 "dd"。同樣地,"gugu" 會將整行變為小寫。這可以縮短為 "guu"。"gUgU" 縮短為 "gUU",而 "g~g~" 縮短為 "g~~"。範例
g~~
Some GIRLS have Fun ----> sOME girls HAVE fUN
Vim 有一組非常強大的命令,它可以做任何事。但可能仍然有一些外部命令可以做得更好或更快。命令 "!{motion}{program}" 取得一段文字,並透過外部程式過濾它。換句話說,它會執行由
{program}
代表的系統命令,並將由
{motion}
代表的文字區塊作為輸入。然後這個命令的輸出會取代選取的區塊。如果你不熟悉 Unix 過濾器,這很難總結,請看一個範例。sort 命令會排序檔案。如果你執行以下命令,未排序的檔案 input.txt 將會被排序並寫入到 output.txt。這在 Unix 和 Windows 上都適用。
sort <input.txt >output.txt
現在在 Vim 中做同樣的事情。你想要排序檔案的第 1 到 5 行。你先將游標放在第 1 行。接著執行以下命令
!5G
"!" 告訴 Vim 你正在執行篩選操作。Vim 編輯器預期後接一個移動命令,表示要篩選檔案的哪一部分。"5G" 命令告訴 Vim 移動到第 5 行,因此它現在知道要篩選第 1 行(目前行)到第 5 行。為了準備篩選,游標會移到螢幕底部,並顯示 ! 提示符。你現在可以輸入篩選程式的名稱,在這個例子中是 "sort"。因此,你的完整命令如下
!5Gsort<Enter>
結果是 sort 程式在頭 5 行執行。程式的輸出會取代這些行。
line 55 line 11 line 33 line 22 line 11 --> line 33 line 22 line 44 line 44 line 55 last line last line
"!!" 命令會透過篩選器篩選目前行。在 Unix 中,"date" 命令會印出目前的時間和日期。"!!date<Enter>" 會將目前行替換為 "date" 的輸出。這對於在檔案中新增時間戳記很有用。
當它不工作時
啟動 shell、將文字傳送給它並捕捉輸出,需要 Vim 確切知道 shell 的運作方式。當你篩選時遇到問題,請檢查這些選項的值
在 Unix 上這幾乎不是問題,因為有兩種 shell:「sh」類型和「csh」類型。Vim 會檢查
'shell' 選項,並根據它是否在
'shell' 中看到 "csh" 來自動設定相關選項。然而,在 MS-Windows 上,有許多不同的 shell,你可能必須調整選項才能使篩選功能正常工作。請查看選項的說明以取得更多資訊。
讀取命令輸出
若要將目前目錄的內容讀取到檔案中,請使用此命令
在 Unix 上
:read !ls
在 MS-Windows 上
:read !dir
"ls" 或 "dir" 命令的輸出會被擷取並插入到文字中,游標下方。這與讀取檔案類似,只是使用 "!" 來告訴 Vim 後面接著一個命令。命令可以有參數。而且可以使用範圍來告訴 Vim 應該將這些行放在哪裡
:0read !date -u
這會在檔案頂部插入 UTC 格式的目前時間和日期。(嗯,如果你有一個接受 "-u" 參數的 date 命令。)請注意與使用 "!!date" 的不同之處:那會取代一行,而 ":read !date" 會插入一行。
將文字寫入命令
Unix 命令 "wc" 會計算單字。要計算目前檔案中的單字
:write !wc
這與之前的寫入命令相同,但不是檔案名稱,而是使用 "!" 字元和外部命令的名稱。寫入的文字將會當作其標準輸入傳遞給指定的命令。輸出看起來像這樣
"wc" 命令並不囉嗦。這表示你有 4 行、47 個單字和 249 個字元。
小心這個錯誤
:write! wc
這將會在目前目錄中強制寫入檔案 "wc"。這裡空白很重要!
重繪螢幕
如果外部命令產生錯誤訊息,螢幕顯示可能會混亂。Vim 非常有效率,只會重繪它知道需要重繪的螢幕部分。但是它無法知道另一個程式寫了什麼。要告訴 Vim 重繪螢幕
CTRL-L