爲了能像 Gilles Castel 那樣記筆記(用 Vim + snippets 居然可以在課堂上同步記數學筆記😱),我又又又又一次撿起了 Vim 。感覺 Vim 之所以這麼難學,是因爲沒一篇教程寫對路了的😒。
我的最初想法是重現一個新手可能會遇到的問題,尋找原生的解決方案 & 更優雅的方案。不過回頭看,更加深切地感受到,這麼多年過去,Vim & Emacs 的對手(各種 IDE )換了一波又一波,它倆卻還屹立在那,不得不說其設計中有很多先進那個時代、即使在今天仍不過時的思想(當然,也必然存在很多缺陷。當我們認識了這些缺陷,並知道原生如何解決、以及如何更好地解決,使用插件🧩就更加得當)。就算從程序設計的角度,Vim 仍然值得深入學習。
Unix 上最早的文本編輯器是 ed ,而後經過幾代的改進,包括 em (editor for mortals) ,en ,最終到 ex 。再之後纔是 vi ,Vim (vi improved) 。這段有趣而沒用的歷史,我一定要告訴你。
1. 打開 & 退出
- 用 Vim 打開某份文件:
vim a.txt
; - MD,怎麼卡死了!
<ctrl-w>
也退不出去🤯! - 哦,要按
i
(insert) 進入「編輯模式🖋[1]」纔可以寫字😒; - MD,
ctrl-s
怎麼保存不了🤯! - 哦,要按
<esc>
退出「編輯模式🖋」,進入到「思考模式🤔[2]」才能執行命令; - 保存 & 退出:
:wq
(其實是進入了「命令行模式」);
這麼會有這麼反人性的設計😒。改下配置 vim ~/.vimrc
(即,告訴 Vim 你的使用習慣)。當然,你可以改成熟知的 ctrl + s
( Vim 習慣寫成 c-s
),但爲了符合 Vim 的用鍵傳統,還是用單字母吧:
" Save & quit
map Q :q<CR>
map S :w<CR>
現在你可以用 S
保存,按 Q
退出了。
但是,要是在新文件裏寫了點什麼,怎麼保存呢?:w <new_filename>
。
map S :write %<CR> " % :活動緩衝區的完整文件路徑。
其實,這樣寫也不準確。改變原生鍵位最好準確指定起效的模式,要是能給出原生鍵位的替代方案就更好了。比如,幸運的是,不會和 surround.vim 的 S 命令衝突。
" Save & quit
nnoremap S :w %<CR> " ⚠️ 幸運的是,不會和 surround.vim 的 S 命令(只在 visual mode 下起效)衝突。
nnoremap Q :q<CR>
聽人說,Vim 的強大來自「多模式」?呵呵,你也可以將「多模式」理解爲「反人性」(例如:同一鍵位在不同的模式下作用可能天差地別。詳見下表)。∴別再說什麼「「多模式」是 Vim 之髓」了😓。我認爲,Vim 的強大源於它既提供了粗粒度的操作,又提供了細粒度的操作。強大的工具其實莫不如是,比如 PS 。後面再詳細解釋。
no. | 操作 | 思考模式🤔 | 可視模式🌈 | 編輯模式🖋 |
---|---|---|---|---|
::no.:: |
i & a
|
進入「編輯模式🖋」插入 (insert / append) | (或「思考模式🤔」下 <operator> 後)i<text-object> (inner / inside) & a<text-object> (arround / all) |
|
::no.:: | o |
進入「編輯模式🖋」並插入新行 | 在高亮選區兩端來回切換 | |
::no.:: | c-r |
重做 (redo) |
<c-r><register> 意爲插入寄存器 register 的內容 |
2. 怎麼對調 caps lock
和 esc
?
你聽到的第一個建議,大概率是改鍵:將 <caps lock>
改爲 <esc>
。這件事在 MacOS 上原生自帶:
Windows 上也不難,下一個 PowerToys ,用裏面的 Keyboard Manager 就搞定。
最麻煩的是 Linux ,折騰了很久都沒有成功😓。
3.界面太醜怎麼辦🥴🤢🤮?
Vim 最爲人詬病的就是「記憶負擔太重」。比如:在不同的模式之間切換,完全憑腦力。再比如什麼 marks 啦、buffers 啦、tabs 啦……原廠的統統不顯示。
最簡單的改造方式是高亮顯示當前行 / 列:
set cursorline
set cursorcolumn
可以裝個叫 vim-airline
的美化插件😍,至少不同模式的顏色不同。但你得先裝個管理插件的插件 vim-plug
。
裝好以後在 ~/.vimrc
裏寫:
call plug#begin()
" ...
call plug#end()
現在你終於可以裝 vim-airline
了:
call plug#begin()
Plug 'vim-airline/vim-airline'
Plug 'vim-airline/vim-airline-themes'
call plug#end()
let g:airline_theme = 'simple'
airline
除了好看,一定要把 Smarter tab line 打開:
let g:airline#extensions#tabline#enabled = 1
現在,當你打開多個文件 vim *.md
,就可以用 :bprevious
& :bnext
來回切換緩衝區 (buffers) 了。太蠢了是不🤢🤮?還是改個快捷鍵吧。這是從 Tim Pope 的 unimpaired.vim
那抄來的:
" 快速遍歷 Vim 列表
" ⚠️ 加 ! 後可以強制切換緩衝區。
nnoremap <silent> [b :bprevious!<CR>
nnoremap <silent> ]b :bnext!<CR>
nnoremap <silent> [B :bfirst!<CR>
nnoremap <silent> ]B :blast!<CR>
你最好再裝個 Powerline
。
但還是沒有解決 marks、buffers、tabs 的提示問題啊?那還得靠插件:
4. 怎麼在窗口間切換啊?
當你裝插件上癮以後,比如 NERDTree
、Undo Tree
,會遇到一個問題,怎麼切換到新開的窗口上呢?
原生命令是:
命令 | 效果 |
---|---|
<C-w>w |
在窗口間循環切換 🔁 |
<C-w>h |
切換到左邊的窗口 ⬅️ |
<C-w>j |
切換到下邊的窗口 ⬇️ |
<C-w>k |
切換到上邊的窗口 ⬆️ |
<C-w>l |
切換到下邊的窗口 ⬇️ |
<C-w>c |
關閉活動窗口 ❌ |
但還是喜歡 @TheCW 的改法:
" ===
" === Window management
" ===
" Set <LEADER> as <SPACE>
let mapleader="\<space>"
" Use <space> + new arrow keys for moving the cursor around windows
map <LEADER>w <C-w>w
map <LEADER>k <C-w>k
map <LEADER>j <C-w>j
map <LEADER>h <C-w>h
map <LEADER>l <C-w>l
5. 最糾結的改鍵😵
排名 | 原生鍵位 | 想改成 | 改鍵原因 | 得失 |
---|---|---|---|---|
::no.:: | : |
; |
需要頻繁進入 Ex mode 。 |
; & , 是一對操作。 |
::no.:: |
<Leader> [3] 用哪個?缺省是 \ 。 |
6. 常用連招
從按鍵效率來說,當然是「單鍵」>「雙擊單鍵」>「易記的鍵組合」>……:
no. | 快捷鍵 | 效果 | 初始模式 | 切入模式 |
---|---|---|---|---|
::no.:: | A |
(等效於 $a )在行尾添加文本。 |
normal mode🤔 | insert mode🖋 |
::no.:: | I |
(等效於 ^i )在行首添加文本。 |
normal mode🤔 | insert mode🖋 |
::no.:: | o |
(等效於 A<CR> )在當前行下方新起一行。 |
normal mode🤔 | insert mode🖋 |
::no.:: | O |
(等效於 ko )在當前行上方插入一行。 |
normal mode🤔 | insert mode🖋 |
::no.:: | C |
(等效於 c$ ) |
normal mode🤔 | insert mode🖋 |
::no.:: | s |
(等效於 cl )刪掉光標下的字符,然後進入插入模式。即,替換光標下的字符。 |
normal mode🤔 | insert mode🖋 |
::no.:: | S |
(等效於 ^c ) |
normal mode🤔 | insert mode🖋 |
::no.:: | dd |
刪除當前行。 | normal mode🤔 | normal mode🤔 |
::no.:: | >> |
增加當前行縮進。 | normal mode🤔 | normal mode🤔 |
::no.:: | << |
減少當前行縮進。 | normal mode🤔 | normal mode🤔 |
::no.:: | ea |
在單詞結尾 (end) 添加文本。 | normal mode🤔 | insert mode🖋 |
::no.:: | gf |
go to file. | normal mode🤔 | normal mode🤔 |
::no.:: | xp |
調換光標之後的兩個字符。 | normal mode🤔 | normal mode🤔 |
::no.:: | gUU |
將當前行整行轉換爲大寫。 | normal mode🤔 | normal mode🤔 |
::no.:: | yyp |
複製當前行,並粘貼在下方。 | normal mode🤔 | normal mode🤔 |
::no.:: | daw |
delete a word. | normal mode🤔 | normal mode🤔 |
::no.:: | vit |
visully select inside the tag. | ||
::no.:: | gv |
重選上次的高亮選區。 | ||
::no.:: | f,dt. |
刪除句子的後半段。 | normal mode🤔 | normal mode🤔 |
::no.:: | qaq |
清空寄存器 a 。 |
||
::no.:: | gUaw |
將當前單詞轉換爲大寫。 | normal mode🤔 | normal mode🤔 |
7. 成對操作🎎
我覺得,學 Vim 完全可以比照學二外的方式——比較 & 聯繫。
no. | 快捷鍵 | 效果 | 初始模式 | 切入模式 |
---|---|---|---|---|
::no.:: |
a v.s. A
|
從後方 (append) 插入。 | normal mode🤔 | insert mode🖋 |
::no.:: |
i v.s. I
|
從前方插入 (insert) 。 | normal mode🤔 | insert mode🖋 |
::no.:: |
n v.s. N
|
下一處 (next) 。 | ||
::no.:: |
o v.s. O
|
插入新行。 | normal mode🤔 | insert mode🖋 |
::no.:: |
v v.s. V
|
進入 Visual mode 。 | ||
::no.:: |
f<char> / F<char> v.s. t<char> / T<char>
|
正向 / 反向行內查找 <char> ,區別在於包不包括 char ( till 不包括)。 |
normal mode🤔 | normal mode🤔 |
::no.:: |
; v.s. ,
|
繼續尋找與調轉方向搜尋前次搜尋的位置。 | normal mode🤔 | normal mode🤔 |
::no.:: |
/<pattern><CR> v.s. ?<pattern><CR>
|
文內查找。 | normal mode🤔 | comand mode🎛 |
::no.:: |
<C-a> v.s. <C-x>
|
對當前行其後最近的數字執行加 / 減操作。 | normal mode🤔 | normal mode🤔 |
::no.:: |
. v.s. u
|
重複與撤回。 | normal mode🤔 | normal mode🤔 |
::no.:: | normal mode🤔下的命令 v.s. Ex 命令 | ⚠️:normal mode🤔下的命令適合在本地操作,而 Ex 命令則可遠距離操作。比如: p v.s. :t (copy To) 。 |
||
::no.:: |
<c-]> v.s. :tag <keyword>
|
跳轉到 tag 到定義處。 | ||
::no.:: |
mm v.s. ``m` |
標記位置📌 m / 跳轉到標記📌 m 。 |
||
::no.:: | (in visual mode)o v.s. (in normal mode)%
|
在高亮選區兩端來回切換 v.s. 在括號(([{ v.s. }]) )間跳轉 。 |
||
::no.:: |
<c-o> v.s. <c-i>
|
在跳轉列表 (:jumps ) 中後退 / 前進(📢注意:低頭看一下,在 qwerty 鍵盤⌨️中,i o 是緊挨在一起的)。 |
重複類型 | 重複操作符 | 回退操作符 |
---|---|---|
文本編輯重複 | . |
u |
行內查找重複 | ; |
, |
全文查找重複 | n |
N |
文本替換重複 | & |
u |
宏重複 | @<register> |
u |
8. 插件🧩
no. | 插件名 | 用途 | 原生解決方案 |
---|---|---|---|
::no.:: | NERDTree |
文件管理 |
netrw ,標配插件,across NETwork Read and Write files 的意思。 |
::no.:: | coc-yank |
可視複製 | |
::no.:: | vim-peekaboo |
寄存器增強。(Peekaboo extends " and @ in normal mode and <CTRL-R> in insert mode so you can see the contents of the registers.) |
|
::no.:: | Undo Tree |
||
::no.:: | vim-devicons |
美化插件:添加圖標 | |
::no.:: | vim-table-mode |
美化插件:增強表格 | |
::no.:: | vim-signature |
增強書籤📌 | marks |
::no.:: | vim-gitgutter |
在行號欄顯示修改狀態(必須置於 Git 看管之下)。 | (changs list):changes & g; , g,
|
::no.:: | vim-surround |
增強 text object ,實現快速環繞編輯。請和 wildfire.vim 配合食用。 |
|
::no.:: | wildfire.vim |
按下 <enter> 就能智能選中最近的文本塊。請和 vim-surround 配合食用。 |
|
::no.:: | vim-expand-region |
快速擴大 / 縮小選中區域 | |
::no.:: | vim-commentary |
註釋 | |
::no.:: | NERD Commenter |
註釋 | |
::no.:: | coc.nvim |
補全🤖️ | |
::no.:: | ctrlp.vim |
||
::no.:: | Tagbar |
tag 導航 | |
::no.:: | vim-markdown |
MarkDown 增強 | |
::no.:: | markdown-preview.nvim |
MarkDown 預覽 |
從 @theCW 那抄來的超有用的 MarkDown 速記技巧:
動作 | 用途 |
---|---|
,b |
加粗 |
,d |
code |
,c |
代碼塊 |
,p |
圖片🎨 |
,a |
鏈接🔗 |
,f |
跳轉到下一定位點 <++>
|
autocmd Filetype markdown inoremap ,f <Esc>/<++><CR>:nohlsearch<CR>c4l
autocmd Filetype markdown inoremap ,n ---<Enter><Enter>
autocmd Filetype markdown inoremap ,b **** <++><Esc>F*hi
autocmd Filetype markdown inoremap ,s ~~~~ <++><Esc>F~hi
autocmd Filetype markdown inoremap ,i ** <++><Esc>F*I
autocmd Filetype markdown inoremap ,d `` <++><Esc>F`i
autocmd Filetype markdown inoremap ,c ~~~<Enter><++><Enter>~~~<Enter><Enter><++><Esc>4kA
autocmd Filetype markdown inoremap ,h ====<Space><++><Esc>F=hi
autocmd Filetype markdown inoremap ,p [圖片上傳失敗...(image-cd8e3a-1663893306192)] <++><Esc>F[a
autocmd Filetype markdown inoremap ,a [](<++>) <++><Esc>F[a
autocmd Filetype markdown inoremap ,1 #<Space><Enter><++><Esc>kA
autocmd Filetype markdown inoremap ,2 ##<Space><Enter><++><Esc>kA
autocmd Filetype markdown inoremap ,3 ###<Space><Enter><++><Esc>kA
autocmd Filetype markdown inoremap ,4 ####<Space><Enter><++><Esc>kA
autocmd Filetype markdown inoremap ,l --------<Enter>
Ref📚
- 很喜歡 B 站 Up 主 @TheCW 的配置,謄抄了不少;