Logo
Published on

Vim 語法系統:動詞 + 量詞 + 名詞

Vim 系列文章

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

前幾篇我們學了移動、編輯和選取,但都是單一指令。這篇要介紹 Vim 最強大的概念:動詞 + 量詞 + 名詞 的組合邏輯,以及文字物件。搞懂這些,你就能用幾個簡單的規則組合出各種操作

Vim 的語法系統

Vim 的指令就像英文一樣,有動詞量詞名詞

[量詞] + 動詞 (Operator) + [量詞] + 名詞 (Motion) = 動作

量詞是選填的,可以放在動詞前面或名詞前面,效果一樣

基本組合(沒有量詞)

  • d(刪除) + w(word) = dw = 刪除一個 word
  • c(修改) + $(到行尾) = c$ = 修改到行尾
  • y(複製) + j(下一行) = yj = 複製這行和下一行

加上量詞

  • d + 2w = d2w = 刪除 2 個 word
  • 3 + dw = 3dw = 刪除 3 個 word(效果同 d3w
  • y + 5j = y5j = 複製往下 5 行

這就是為什麼 Vim 用起來很爽:你不用死背上百個指令,只要記住幾個動詞、名詞,再加上量詞,就能組合出各種操作

常用動詞 (Operators)

動詞作用記憶
d刪除delete
c修改(刪除並進入 Insert Mode)change
y複製yank(Vim 用語)
>增加縮排
<減少縮排
=自動縮排
gu轉小寫
gU轉大寫

常用名詞 (Motions)

名詞範圍
w到下一個 word 開頭
e到當前 word 結尾
b到上一個 word 開頭
$到行尾
0到行首
gg到檔案開頭
G到檔案結尾
j / k下一行 / 上一行

文字物件 (Text Objects)

除了 Motion,Vim 還有一種特別的名詞叫「文字物件」。它可以選取一個完整的結構,像是一個 word、一對引號內的內容、一對括號內的內容等

ia 的差別

  • i = inner(內部),不包含邊界
  • a = around(周圍),包含邊界
say "hello world" here
          ^            ← 游標在引號內

di" → 刪除 hello world(引號內的內容)
da" → 刪除 "hello world"(包含引號)

常用文字物件

文字物件範圍
iw / aw一個 word
i" / a"雙引號內
i' / a'單引號內
i) / a)小括號內(ib / ab 也可以)
i] / a]中括號內
i} / a}大括號內(iB / aB 也可以)
it / atHTML/XML 標籤內
ip / ap段落

文字物件的搜尋規則

游標不一定要在括號或引號內部才能使用文字物件

規則 1:往右搜尋

如果游標不在目標結構內,Vim 會在同一行往右找最近的

let x = foo("hello")
    ^                  ← 游標在 x 上

ci" → 會修改 "hello"(往右找到最近的引號)

規則 2:往外擴展

如果在嵌套結構內,會先選取最內層的,再操作會往外擴展

if (a && (b || c))
          ^         ← 游標在 b 上

di) → 刪除 b || c(最內層的括號)

文字物件範例

ciw  → 修改整個 word
ci"  → 修改引號內的內容
ci(  → 修改括號內的內容
da{  → 刪除大括號(包含括號本身)
yip  → 複製整個段落

dw vs diw vs daw

這三個看起來很像,但結果不同

hello world foo
      ^         ← 游標在 w 上

dw(Motion)

刪除從游標位置到下一個 word 開頭

hello world foo
      ^
dw →
hello foo       ← 刪除 "world "(從 w 到下一個 word)
      ^

diw(Text Object - inner word)

刪除整個 word,不包含空格

hello world foo
      ^
diw →
hello  foo      ← 刪除 "world",保留兩邊空格
      ^

daw(Text Object - around word)

刪除整個 word,包含周圍的空格

hello world foo
      ^
daw →
hello foo       ← 刪除 "world" 加上一邊的空格
      ^

什麼時候用哪個?

  • dw:游標在 word 開頭,想刪到下一個 word
  • diw:想刪掉整個 word,但保留空格(例如要換成別的字)
  • daw:想刪掉整個 word 和空格(例如刪掉一個參數)

搭配 Visual Mode

文字物件和 Visual Mode 搭配很好用

function hello() {
    console.log("hello");
    console.log("world");
}

游標在函數內任意位置

vi{  → 選取大括號內的所有內容
va{  → 選取大括號(包含括號本身)

在 Visual Mode 中也可以用文字物件擴展選取

say "hello world" here
          ^            ← 游標在引號內

按 v 進入 Visual Mode,然後

按 iw → 選取 "world"
按 i" → 選取 "hello world"(引號內全部)
按 a" → 選取 "hello world" 包含引號

組合範例

dw   → 刪除到下一個 word 開頭
de   → 刪除到當前 word 結尾
d$   → 刪除到行尾
d0   → 刪除到行首
dG   → 刪除到檔案結尾

cw   → 修改到 word 結尾,進入 Insert
c$   → 修改到行尾,進入 Insert

yw   → 複製一個 word
y$   → 複製到行尾

量詞讓組合更強大

d2w  → 刪除 2 個 word
c3w  → 修改 3 個 word
y5j  → 複製往下 5gUw  → 把一個 word 轉大寫

3dd vs d3j:看起來像,但不一樣

這是一個容易搞混的地方

3dd = 重複 dd 三次 = 刪除 3 行

1 aaa
2 bbb    ← 游標在這
3 ccc
4 ddd
5 eee

按 3dd

1 aaa
2 eee    ← 刪除了 bbb、ccc、ddd(3 行)

d3j = 刪除到「往下 3 行」的範圍 = 刪除 4 行

1 aaa
2 bbb    ← 游標在這
3 ccc
4 ddd
5 eee
6 fff

按 d3j

1 aaa
2 fff    ← 刪除了 bbb、ccc、ddd、eee(4 行)

差別在於:

  • 3dd:量詞修飾的是 dd(整行操作),所以是「做 3 次刪除整行」
  • d3j3j 是「往下移動 3 行」,所以 d3j 是「刪除從這裡到往下 3 行的範圍」(包含當前行 + 下面 3 行 = 4 行)

動畫展示:組合技

delete some words here
^

按 dw
some words here
^

按 d2w
here
^
change this word
       ^

按 cw
change   word     ← "this" 被刪除,進入 Insert Mode
       ^

輸入 "that"
change that word

本篇重點整理

語法公式
  [量詞] + 動詞 + [量詞] + 名詞 = 動作

  量詞放動詞前或名詞前都可以
    d2w = 2dw = 刪除 2 個 word

常用動詞
  d → 刪除
  c → 修改(刪除 + 進入 Insert)
  y → 複製
  > / < → 增加/減少縮排
  gu / gU → 轉小寫/大寫

常用名詞
  w/e/b → word 相關
  $/0 → 行尾/行首
  gg/G → 檔案開頭/結尾
  j/k → 下一行/上一行

文字物件
  i = inner(內部),a = around(周圍)
  iw/aw → 一個 word
  i"/a" → 引號內
  i)/a) → 括號內
  i}/a} → 大括號內

常用組合
  dw/cw/yw → 操作一個 word
  d$/c$/y$ → 操作到行尾
  ciw/diw → 修改/刪除整個 word
  ci"/di" → 修改/刪除引號內容
  vi{/va{ → 選取大括號內容