从零开始配置 vim(3)—— 键盘映射进阶
嚴(yán)格意義上來說,快捷鍵的綁定應(yīng)該是鍵盤映射,將某些鍵映射為另一些鍵。
在上篇我們介紹了基本的鍵盤映射操作,知道了如何 :map、:imap、:vmap、:nmap這些命令來映射鍵盤快捷鍵。它們很方便,也很簡單,但是有一個致命的缺點(diǎn)。他們是遞歸的,我們先來討論什么是遞歸
映射的遞歸問題
讓我們先來執(zhí)行下面的命令
:nmap jj J :nmap J j這里我們原本是想 jj來實(shí)現(xiàn) J的功能,更加快速的實(shí)現(xiàn)合并行的功能,但是我們按下之后發(fā)現(xiàn),它只是將光標(biāo)移動到下一行了。這并不是我們想要的。到底發(fā)生什么了呢?
因?yàn)檫@些命令是遞歸的。如何理解遞歸呢?我們以函數(shù)的思想來考慮,每定義一個快捷鍵,就相當(dāng)于定義了一個函數(shù)。并且在新定義的函數(shù)中調(diào)用老函數(shù)。依照這個思路我們來分析一下上述兩個命令產(chǎn)生的結(jié)果:
這些映射產(chǎn)生的偽代碼如下:
void J() {j(); }void jj() {J(); }我們在調(diào)用 jj 這個函數(shù)的時候就相當(dāng)于在調(diào)用 j
有點(diǎn)繞是不是呢。為了講述這個問題,我們再來看這么一個例子
:nmap dd o<esc>kddj我們來分析一下它的本意:
- 首先使用 o在光標(biāo)所在行之下插入一行
- 退回到普通模式,并且讓光標(biāo)向上移動一行
- 刪除光標(biāo)所在行
- 移動到下一行,也就是剛剛的插入行
看起來這個命令的作用是清除本行,但是vim并沒有這么做,只有按下 <C-c>才能停下來,而且vim中多出了許多空白行。
依照上述分析思路,我們可以對這個命令的執(zhí)行結(jié)果寫出如下的偽代碼
從上述的代碼看出這個映射會陷入無限循環(huán),或者叫死遞歸。我們只能使用 <Ctrl +c>來終止。
在正式進(jìn)入下一步之前讓我們先刪除這個映射。我們可以使用 :nunmpa 來刪除一條快捷鍵映射,輸入 :nunmap dd 來終止上述出錯的映射。之前介紹的那些映射命令都有 un 系列的命令,例如 map 對應(yīng) unmap,imap 對應(yīng) iunmap 。
我們從上面的幾個例子應(yīng)該看出來了, 之前介紹的函數(shù)好用是好用,但是會形成遞歸。在上述代碼中還算是比較好找,如果我們配置文件大了,不同插件有自己的映射,而我們也會定義一堆自己的映射,這個時候出問題就難查了。
為了解決這個問題,vim提供了一系列的 nore 開頭的函數(shù)。它相比于之前介紹的函數(shù)來說,是非遞歸的。之前每個命令對應(yīng)的非遞歸版本如下:
- nmap 對應(yīng) nnoremap
- imap 對應(yīng) inorempap
- vmap 對應(yīng) vnoremap
- cmap 對應(yīng) cnoremap
相信各位應(yīng)該看出來了,我們在原來命令的基礎(chǔ)之上添加了 nore 作為非遞歸版本。我們來做一個試驗(yàn)
:nmap x dd :nnoremap \ x我們輸入 \ 發(fā)現(xiàn)它只刪除了一個單詞,即使用 :nnoremap 只保留了 \ 作為 x 操作符的作用,而斬?cái)嗔酥?x 被映射為 dd 的操作。
那么我們何時該使用遞歸版本,何時使用非遞歸版本呢?答案是在任何時候,永遠(yuǎn)使用非遞歸版本,現(xiàn)在就請各位小伙伴忘掉非遞歸版本把。現(xiàn)在多敲幾個單詞將來會省去大量排錯時間。
lua 配置
到此位置我們學(xué)會了怎么使用 :map 系列的命令定義快捷鍵,同時也知道什么是快捷鍵之間的遞歸和非遞歸。可以說掌握了關(guān)于 vimscript 定義快捷鍵的基本方法。那么如何跟 lua 對應(yīng)呢?
neovim 定義了一系列的函數(shù)幫助我們定義、獲取和刪除快捷鍵
- vim.api.nvim_set_keymap: 設(shè)置快捷鍵
- vim.api.nvim_get_keymap: 獲取快捷鍵
- vim.api.nvim_del_keymap: 刪除快捷鍵
我們可以通過幫助文檔查到 vim.api.nvim_set_keymap 的定義如下:
nvim_set_keymap({mode}, {lhs}, {rhs}, {*opts})mode 是一個字符串,對應(yīng)著一個模式,即我們之前說的可視模式、插入模式或者普通模式,下面是各個模式對應(yīng)的字符串名稱 。
| “” | 所有模式 | :map |
| “n” | 普通模式 | :nmap |
| “v” | 可視模式 | :vmap |
| “i” | 插入模式 | :imap |
| “s” | 選擇模式 | :smap |
| “c” | 命令模式 | :cmap |
lhs 對應(yīng)著一個鍵位,即我們想映射的鍵位,如果傳入空字符串,相當(dāng)于通過 :map傳入 <NOP>。表示將要禁用這個鍵
rhs 對應(yīng)著將要執(zhí)行的命令,是 :map 的第二個參數(shù)
opts 代表映射的其他屬性,主要是一個表,你可以暫時理解為一個字典。比如可以使用 noremap 表示禁止遞歸,使用 silent 表示執(zhí)行命令時不回顯內(nèi)容
例如我們在配置文件中定義
vim.api.nvim_set_keymap("n", "<space>", "/nvim<CR>", {noremap = true, silent = false})通過這段代碼,我們將 空格鍵映射為在文件中查找 nvim 字符。因?yàn)?silent 設(shè)置的是 false 因此我們在按下空格鍵之后在vim 的左下角會看到 /nvim 的字樣
我們可以通過設(shè)置 silent = true來取消這個回顯。
映射leader 鍵
常見的映射主要出現(xiàn)在普通模式下,普通模式下的很多按鍵都有其特殊用途,而且還大多挺常用的,想來想去不怎么使用,而且位置好按的也就 <space>、H、L、D 這些了,將他們進(jìn)行映射,映射到常用功能,減輕我們的按鍵負(fù)擔(dān)是再好不過了。但是我們常用功能又那么多,特別是裝了插件之后的。這些鍵完全不夠使用的。
這個時候我們要延續(xù) emacs 或者其他軟件的思路了,一個鍵不夠就兩個鍵,比如使用下面的映射
:noremap -d dd :noremap -c ddO這意味著我們可以使用一個鍵作為前綴,后面接其他字符,將他們作為一個整體來映射一個功能。多按一個鍵而已,比你輸入整個命令要輕松多了。
這就引入一個新的思路了,我們可以統(tǒng)一定義一個鍵作為前綴鍵,后面添加一些字符來整體進(jìn)行映射。當(dāng)然我們可以手工這么做,每次需要多個按鍵的時候的就手動寫上 - 或者其他的。但是后期我發(fā)現(xiàn) - 并不容易按到,我覺得 <space> 或者 ,更容易按到,要進(jìn)行修改,那么修改的量就太大了。
vim 中有一個被稱之為 leader 鍵的東西來解決這個問題,我們可以提前定義一個 leader ,在映射的時候使用 <leader> 來代表對應(yīng)的前綴鍵。例如,先設(shè)置 leader 為 <space> 后面再定義 <leader>d 作為 dd,后面可以很方便的修改 <space>d 這個映射為 ,d,只需要修改 leader 鍵的定義。
可以使用 :let mapleader = <space> 來定義 leader 鍵為空格。這里你可以定義成你喜歡的鍵,我平時喜歡用空格,因?yàn)樗容^大,平時也在大拇指的位置,方便按。
然后我們可以使用 :noremap <leader>d dd 來定義映射
如何在 lua 中定義 leader 鍵呢?
從上面的 vimscript 代碼中可以看到 mapleader 是用 let 關(guān)鍵字來設(shè)置的,一般 let 是用來設(shè)置變量的。lua 中自定義變量可以直接定義,但是 mapleader 明顯是vim 自帶的變量。跟設(shè)置選項(xiàng)類似,neovim 提供了兩種訪問 vim 內(nèi)部變量的方式,一種是使用函數(shù),一種是使用 元訪問器。
跟變量有關(guān)的函數(shù)主要有:
- vim.api.nvim_set_var():設(shè)置全局變量的值
- vim.api.nvim_get_var():獲取全局變量
- vim.api.nvim_del_var():刪除全局變量
當(dāng)然使用元訪問器會更加簡便,對應(yīng)的元訪問器為 vim.g。所以這里我們可以使用 vim.g.mapleader = " " 來設(shè)置
到此我們已經(jīng)學(xué)會了關(guān)于映射的所有初級的內(nèi)容,現(xiàn)在已經(jīng)可以完成大部分的配置工作了。至于在定義快捷鍵的時候是使用 leader鍵還是使用多個普通鍵,看具體場景和各位小伙伴的使用習(xí)慣了。這里我就不給建議了,一切以方便好按為主。
總結(jié)
以上是生活随笔為你收集整理的从零开始配置 vim(3)—— 键盘映射进阶的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: AI笔刷 600+专业ai矢量笔刷套装(
- 下一篇: Socket编程入门(一)实现简单的So