Logo
Published on

Vim 效率倍增:重複與自動化

Vim 系列文章

  1. Vim 生存指南:模式與移動
  2. Vim 編輯基礎:輸入、刪除和複製貼上
  3. Vim 選取模式:三種 Visual Mode
  4. Vim 語法系統:動詞 + 量詞 + 名詞
  5. Vim 搜尋技巧:快速定位與取代
  6. Vim 效率倍增:重複與自動化 (本篇)
  7. Vim 客製化:設定與 IDE 整合

前幾篇學會了 Vim 的核心操作。但高手和新手的差距,在於如何避免重複。這篇要學習三個實用技巧:. 指令、寄存器、和巨集

最強大的按鍵:.

. 是 Vim 中最強大的按鍵。它的功能是:重複上一個編輯動作

什麼是「上一個動作」?

任何改變文字的操作都算,例如:

  • dw(刪除 word)
  • ciw + 輸入新內容 + Esc
  • x(刪除字元)
  • >>(增加縮排)

不算的:純移動(如 wj)、搜尋(如 /pattern

基本用法

foo bar foo bar foo
^(游標在第一個 foo 的 f 上)

按 ciw,輸入 baz,按 Esc

baz bar foo bar foo
  ^                  ← foo 變成 baz

按 w w 移動到下一個 foo

baz bar foo bar foo
        ^(游標在第二個 foo 的 f 上)

.

baz bar baz bar foo
          ^          ← 第二個 foo 也變成 baz

搜尋 + . 的組合技

這招很實用,用三行內容更能展示跨行搜尋的威力

1 foo bar baz
  ^(游標在第 1 行的 foo)
2 foo bar baz
3 foo bar baz

/foo Enter

1 foo bar baz
2 foo bar baz
  ^              ← 跳到第 2 行的 foo(搜尋會往下找下一個匹配)
3 foo bar baz

按 ciw,輸入 hello,按 Esc

1 foo bar baz
2 hello bar baz
      ^          ← 第 2 行的 foo 變成 hello
3 foo bar baz

按 n

1 foo bar baz
2 hello bar baz
3 foo bar baz
  ^              ← 跳到第 3 行的 foo

.

1 foo bar baz
2 hello bar baz
3 hello bar baz
      ^          ← 第 3 行的 foo 也變成 hello

按 n

1 foo bar baz
  ^              ← wrap around 回到第 1 行的 foo
2 hello bar baz
3 hello bar baz

.

1 hello bar baz
      ^          ← 全部都變成 hello 了
2 hello bar baz
3 hello bar baz

更快的方式:用 * 搜尋當前單字

把游標放在 foo 上
1. *(搜尋 foo,跳到下一個)
2. ciw 改成 baz,Esc
3. n.n.n.(跳 +++...

為什麼 . 這麼強大?

因為 Vim 的設計哲學是

效率 = 精準的動作 × 重複的次數
  1. 做一次正確的編輯(用最精準的指令)
  2. 移動到下一個位置
  3. . 重複

你只需要「想」一次,剩下的都是機械式的 n.n.n.

寄存器:多個剪貼簿

Vim 有多個「剪貼簿」,稱為寄存器 (registers)

操作語法

使用寄存器的語法是 "寄存器名稱 + 動作

"a + yy  → 複製整行到寄存器 a
"a + p   → 從寄存器 a 貼上
"+ + y   → 複製到系統剪貼簿

" 是告訴 Vim「接下來要指定寄存器」,後面接寄存器名稱,再接你要做的動作

預設寄存器

當你用 dy 時,內容會存到「預設寄存器」(""

問題是:如果你先 yiw 複製一個 word,然後 dw 刪除另一個 word,想貼上一開始複製的內容… 糟糕!預設寄存器已經被覆蓋了

"0 寄存器:解決方案

"0 永遠存放最近 yank 的內容,不會被 delete 覆蓋

important text delete
^(游標在 important 的 i 上)

按 yiw 複製 "important"

important text delete
^"important" 已複製到 "" 和 "0

按 w w 移動到 "delete"

important text delete
               ^(游標在 delete 的 d 上)

按 dw 刪除 "delete"

important text
               ^"delete" 存到 "",覆蓋了 "important"

按 p

important text delete
               ^         ← 貼出 "delete"(錯誤!)

按 u 復原

important text
               ^

按 "0p

important text important
               ^         ← 貼出 "important"(正確!"0 沒被覆蓋)

每次都要按 "0p 有點麻煩,可以在 vimrc 中設定快捷鍵來簡化這個操作,請參考設定與 IDE 整合中的推薦設定

"1"9:刪除歷史

Vim 會自動幫你保留最近 9 次的刪除紀錄

  • "1:最近一次刪除的內容(整行或多字元的刪除)
  • "2:前一次的刪除
  • "3"9:依序更早的刪除

每次新的刪除會把舊內容往後推("1"2"3…)

第一行
^(游標在第一行)
第二行
第三行

按 dd 刪除第一行 → "1 = "第一行"

第二行
^
第三行

按 dd 刪除第二行 → "1 = "第二行","2 = "第一行"

第三行
^

按 "2p

第三行
第一行
^         ← 從 "2 找回最早刪除的 "第一行"

不小心刪錯了?用 :reg 1 2 3 看看刪除歷史,從裡面找回來就好

"-:小刪除寄存器

不是所有刪除都會進入 "1"9,規則很簡單

  • 少於一行的刪除(如 dwxdl)→ 存到 "-
  • 整行的刪除(如 dddj)→ 存到 "1

所以 "- 就是專門放「小刪除」的地方,用 "-p 可以貼上最近一次的小刪除

具名寄存器:a-z

你可以用 "a"z 存放不同內容

"ayiw   → 複製 word 到寄存器 a
"byy    → 複製整行到寄存器 b
"ap     → 從寄存器 a 貼上
"bp     → 從寄存器 b 貼上

查看所有寄存器

:reg        → 顯示所有寄存器內容
:reg a      → 只顯示寄存器 a

理解 :reg 輸出

Type Name Content
  c  ""   hello
  c  "0   hello
  l  "1   first line^J
  l  "2   second line^J
  c  "-   world
  c  "a   foo
  c  "b   bar
  c  "+   clipboard text
  c  "/   search_pattern
  c  ".   last insert
  c  ":   %s/old/new/g
  c  "%   index.mdx

執行 :reg 後會看到三欄

欄位說明
Typel 代表整行模式 (linewise),c 代表字元模式 (characterwise)
Name寄存器名稱(如 """0"a
Content寄存器中的內容,^J 代表換行符

Type 決定了貼上時的行為:l 會貼成獨立的一行,c 會貼在游標位置

系統剪貼簿

寄存器說明
"+系統剪貼簿
"*選取剪貼簿(Linux X11 環境)
"+y     → 複製到系統剪貼簿(可以貼到其他程式)
"+p     → 從系統剪貼簿貼上

唯讀寄存器

這幾個寄存器由 Vim 自動維護,你只能讀取不能寫入

寄存器說明
".最近插入的文字
":最近執行的命令列指令
"%目前檔案名稱
"/最近的搜尋 pattern

例如忘記剛才搜尋了什麼,用 "/p 就能把搜尋的 pattern 貼出來看

巨集:錄製你的動作

. 不夠用的時候,巨集就派上用場了。巨集可以錄製一連串的動作,然後重複播放

操作語法

q + 寄存器名稱  → 開始錄製(例如 qa)
q              → 停止錄製
@ + 寄存器名稱  → 播放(例如 @a)
@@             → 重複上次播放
數字 + @a      → 播放多次(例如 10@a)

q 是錄製的開關,@ 是播放。寄存器名稱可以用 az

基本操作

指令動作
qa開始錄製到寄存器 a
q停止錄製
@a播放寄存器 a 的巨集
@@重複上次播放的巨集
10@a播放 10 次

這裡用 a 當例子,實際上可以用 az 任一個寄存器

實戰範例:將每行轉成 JSON 格式

1 apple
  ^(游標在第 1)
2 banana
3 cherry

按 qa 開始錄製到寄存器 a

1 apple
  ^              ← 左下角顯示 recording @a
2 banana
3 cherry

I" Esc(行首加 "
1 "apple
   ^
2 banana
3 cherry

A", Esc(行尾加 ",
1 "apple",
         ^
2 banana
3 cherry

按 j 和 0(移到下一行的最前面)

1 "apple",
2 banana
  ^
3 cherry

按 q(停止錄製)

1 "apple",
2 banana
  ^              ← 錄製完成
3 cherry

2@a(對剩下 2 行執行巨集)

1 "apple",
2 "banana",
3 "cherry",
4
  ^              ← 全部完成

巨集設計原則

  1. 從行首開始:先按 0^,確保位置一致
  2. 移動到下一行:巨集最後加 j,這樣可以連續執行
  3. 測試一次:錄完後先執行一次 @a,確認正確
  4. 用數字前綴100@a 比按 100 次 @a

另一個範例:產生序號

1 item
  ^(游標在第 1)

按 i1. Esc(在第一行前面加 "1. "
1 1. item
    ^

按 qa 開始錄製到寄存器 a

1 1. item
    ^            ← 左下角顯示 recording @a

按 yy(複製整行)

1 1. item
    ^            ← 已複製 "1. item"

按 p(貼上到下一行)

1 1. item
2 1. item
  ^              ← 貼上的新行


Ctrl+a(數字加 1
1 1. item
2 2. item
  ^1 變成 2

按 q(停止錄製)

1 1. item
3 2. item
  ^              ← 錄製完成

按 @a(執行一次巨集)

1 1. item
2 2. item
4 3. item
  ^              ← 數字加 1 後複製貼上

5@a(執行 5 次產生更多行)

1 1. item
2 2. item
3 3. item
4 4. item
5 5. item
6 6. item
7 7. item
8 8. item
  ^              ← 可繼續執行產生更多
...

其他效率技巧

Ctrl+a / Ctrl+x:數字加減

count = 10
        ^

Ctrl+a → count = 11+1Ctrl+x → count = 10-15 Ctrl+a → count = 15+5

游標不需要在數字上,Vim 會自動找到該行第一個數字

本篇重點整理

. 指令
  - 重複上一個編輯動作
  - 搭配 n 使用:n.n.n.

寄存器
  "" → 預設寄存器
  "0 → 最近 yank(不被 delete 覆蓋)
  "1-"9 → 刪除歷史
  "- → 小刪除(少於一行)
  "a-z → 具名寄存器
  "+ → 系統剪貼簿
  ". ": "% "/ → 唯讀寄存器
  :reg → 查看所有

巨集
  qa → 開始錄製到 a
  q → 停止
  @a → 播放
  @@ → 重複播放
  10@a → 播放 10
其他
  Ctrl+a / Ctrl+x → 數字加減

圖片來源:AI 產生