在開頭前幾章我們曾講過怎么在Vim里設置選項。 對于布爾選項,我們可以使用`set someoption!`來"切換"選項。 如果我們能給這個命令創建一個映射,那就再好不過了。
執行下面的命令:
~~~
:nnoremap <leader>N :setlocal number!<cr>
~~~
在normal模式中按下`<leader>N`看看。Vim將會在開啟和關閉行號顯示之間切換。 像這樣的"切換"映射是十分方便的,因此我們就不需要兩個獨立的鍵來開/關。
不幸的是,這只對布爾選項起作用。如果我們想要切換一個非布爾選項,還需要做更多的工作。
## 切換選項
從創建一個可以切換選項的函數,以及調用該函數的映射開始吧。 把下面的代碼加入到你的`~/.vimrc`(或一個`~/.vim/plugin/`中的獨立文件,如果你想要的話):
~~~
nnoremap <leader>f :call FoldColumnToggle()<cr>
function! FoldColumnToggle()
echom &foldcolumn
endfunction
~~~
保存并source文件,然后按下`<leader>f`試試看。Vim顯示當前`foldcolumn`選項的值。 如果你不熟悉這個選項,閱讀`:help foldcolumn`再繼續。
讓我們添加真正的切換功能。修改代碼成這樣:
~~~
nnoremap <leader>f :call FoldColumnToggle()<cr>
function! FoldColumnToggle()
if &foldcolumn
setlocal foldcolumn=0
else
setlocal foldcolumn=4
endif
endfunction
~~~
保存并source文件,然后試試看。每次你按下它Vim將顯示或隱藏折疊狀態條(fold column)。
`if`語句判斷`&foldcolumn`是否為真(記住Vim把0看作假而其他數字為真)。 如果是,把它設成0(隱藏它)。否則就設置它為4。就是這么簡單。
你可以使用一個簡單的函數像這樣來切換任何以`0`代表關,以其他數字代表開的選項。
## 切換其他東西
我們的夢想不應止于切換選項。還有一個我們想切換的東西是quickfix窗口。 依然以之前的骨架代碼作為起點。加入下面的代碼到你的文件:
~~~
nnoremap <leader>q :call QuickfixToggle()<cr>
function! QuickfixToggle()
return
endfunction
~~~
這個映射暫時什么都不干。讓我們把它轉變成其他稍微有點用的東西(不過還沒有徹底完成)。 把代碼改成這樣:
~~~
nnoremap <leader>q :call QuickfixToggle()<cr>
function! QuickfixToggle()
copen
endfunction
~~~
保存并source文件。如果現在你試一下這個映射,你就會看到一個空蕩蕩的quickfix窗口。
為了達到實現切換功能的目的,我們將選擇一個既快捷又骯臟的手段:全局變量。 把代碼改成這樣:
~~~
nnoremap <leader>q :call QuickfixToggle()<cr>
function! QuickfixToggle()
if g:quickfix_is_open
cclose
let g:quickfix_is_open = 0
else
copen
let g:quickfix_is_open = 1
endif
endfunction
~~~
我們干的事情十分簡單 —— 每次調用函數時,我們用一個全局變量來儲存quickfix窗口的開關狀態。
保存并source文件,接著執行映射試試看。Vim將抱怨變量尚未定義!那么我們先把變量初始化吧。
~~~
nnoremap <leader>q :call QuickfixToggle()<cr>
let g:quickfix_is_open = 0
function! QuickfixToggle()
if g:quickfix_is_open
cclose
let g:quickfix_is_open = 0
else
copen
let g:quickfix_is_open = 1
endif
endfunction
~~~
保存并source文件,接著試一下映射。成功了!
## 改進
我們的切換函數可以工作,但還留有一些問題。
第一個問題是,假設用戶用`:copen`或`:cclose`手動開關窗口,我們的全局變量將不會刷新。 實際上這不會是個大問題,因為大多數情況下用戶會用這個映射開關窗口,萬一沒有打開,他們也會再按一次。
這又是關于寫Vimscript代碼的重要經驗:如果你試圖處理每一個邊際條件,你將陷在里面,而且不會有任何進展。
在大多數情況下,先推出可工作(而且即使不能工作也不會造成破壞)的代碼然后回過頭改善, 要比耗費許多小時苛求完美好得多。除外你正在開發一個很可能有很多人用到的插件。 在這種情況下它才值得耗費時日來達到無懈可擊的程度,讓用戶滿意并減少bug報告。
## 重新加載窗口/緩沖區
我們的函數的另外一個問題是,當用戶已經打開了quickfix窗口,并執行這個映射時, Vim關閉了窗口,接著把他們彈到上一個分割中,而不是送他們回之前的地方。 如果你僅僅想快速查看一下quickfix窗口然后繼續工作,發生這種事是讓人惱怒的。
為了解決這個問題,我們將引入一種寫Vim插件時非常有用的慣用法。把你的代碼改成這樣:
~~~
nnoremap <leader>q :call QuickfixToggle()<cr>
let g:quickfix_is_open = 0
function! QuickfixToggle()
if g:quickfix_is_open
cclose
let g:quickfix_is_open = 0
execute g:quickfix_return_to_window . "wincmd w"
else
let g:quickfix_return_to_window = winnr()
copen
let g:quickfix_is_open = 1
endif
endfunction
~~~
我們在映射中加入了新的兩行。其中一行(在`else`分支)設置了另一個全局變量,來保存執行`:copen`時的當前窗口序號。
另一行(在`if`分支)執行以那個序號作前綴的`wincmd w`,來告訴Vim跳轉到對應窗口。
我們的解決方法又一次不是無懈可擊的,用戶可能在兩次執行映射之間打開或關閉新的分割。 即使這樣,它還是適合于大多數場合,所以目前這已經夠好的了。
在大多數程序中,這種手工保存全局狀態的伎倆會遭到譴責,但對于一個非常短小的Vimscript函數而言, 它既快捷又骯臟,卻能不辱使命,完成重任。
## 練習
閱讀`:help foldcolumn`.
閱讀`:help winnr()`
閱讀`:help ctrl-w_w`.
閱讀`:help wincmd`.
在需要的地方加上`s:`和`<SID>`來把函數限定在獨自的命名空間中。
- 前言
- 鳴謝
- 預備知識
- 打印信息
- 設置選項
- 基本映射
- 模式映射
- 精確映射
- Leaders
- 編輯你的Vimrc文件
- Abbreviations
- 更多的Mappings
- 鍛煉你的手指
- 本地緩沖區的選項設置和映射
- 自動命令
- 本地緩沖區縮寫
- 自動命令組
- Operator-Pending映射
- 更多Operator-Pending映射
- 狀態條
- 負責任的編碼
- 變量
- 變量作用域
- 條件語句
- 比較
- 函數
- 函數參數
- 數字
- 字符串
- 字符串函數
- Execute命令
- Normal命令
- 執行normal!
- 基本的正則表達式
- 實例研究:Grep 運算符(Operator),第一部分
- 實例研究:Grep運算符(Operator),第二部分
- 實例研究:Grep運算符(Operator),第三部分
- 列表
- 循環
- 字典
- 切換
- 函數式編程
- 路徑
- 創建一個完整的插件
- 舊社會下的插件配置方式
- 新希望:用Pathogen配置插件
- 檢測文件類型
- 基本語法高亮
- 高級語法高亮
- 更高級的語法高亮
- 基本折疊
- 高級折疊
- 段移動原理
- Potion段移動
- 外部命令
- 自動加載
- 文檔
- 發布
- 還剩下什么?