Vim激荡30年发展史
作者 |?Joe Nelson
譯者 | 彎月,編輯?| 屠敏
來源 | CSDN(ID:CSDNnews)
導語:眾所周知,Vim 是從 vi 發展出來的一個文本編輯器。其擁有代碼補全、編譯及錯誤跳轉等豐富的功能特性,在程序員群體中廣受歡迎。
本文是作者?Joe Nelson?從頭到尾閱讀 Vim 用戶手冊以及追溯歷史之后的一些心得。希望這些筆記能夠幫助大家發現這款編輯器的核心功能,從而更加熟練地使用各個插件。
如果你想進一步了解Vim,那么我建議你入手一本紙質的用戶手冊和優秀的袖珍參考手冊。我沒有找到官方的Vim紙質手冊,最后只好打印了這個PDF(https://begriffs.com/pdf/vim-user-manual.pdf)。為了方便查看Vim的命令列表,我建議你入手上圖中的《vi and Vim Editors Pocket Reference》。
歷史
Vi的誕生
Vi源自QED編輯器,距今已有五十多年的歷史。其發展歷程如下:
1966年:伯克利分時系統的QED(“Quick EDitor”)
1969年7月:登月(僅供參考)
1969年8月:QED -> AT&T的ed
1976年2月:ed ->瑪麗王后大學的em(“Editor for Mortals”)
1976年:em -> 加州大學伯克利分校的ex (“EXtended”)
1977年10月:ex有了可視化模式,vi
閱讀一下用戶手冊,你就會發現QED和ex之間有很多相似之處。這兩個編輯器在指定和操作行范圍時都采用了類似的語法。
QED、ed和em這類的編輯器都是為硬拷貝終端設計的,這些終端基本上就是帶調制解調器的電動打字機。硬拷貝終端可以將系統輸出打印到紙上。顯然一旦打印完成,就無法更改輸出,因此這種編輯過程需要包含用于更新和手動打印文本范圍的命令。
到1976年的時候,ADM-3A等視頻可視化終端出現了。Ex編輯器添加了一個“開放模式”,允許在可視化終端上進行行內編輯,還有一個可視化模式,可以在支持光標的終端上面利用屏幕進行編輯。這種可視模式(可以通過命令“vi”激活)可以在屏幕上顯示部分文件的最新視圖,同時還保留了屏幕底部的ex命令行。(趣事:在ADM-3A上,h、j、k、l鍵兼作方向鍵,所以vi選擇這幾個鍵作為光標移動只是為了保持一致而已。)
如果你想了解更多關于從ed到ex / vi的發展,可以閱讀Bill Joy的這段采訪(https://begriffs.com/pdf/unix-review-bill-joy.pdf),他在文中談到了ex / vi的創建過程,以及一些令他失望的事情。
傳統的vi實際上只是ex的另一種形式,它們的可執行文件是同一個,根據調用時的可執行文件名來決定啟動ex模式還是vi模式。ex / vi對之前的版本進行了改進,只需很少的系統資源,就可以在有限的帶寬下操作。而且該工具還支持于大多數系統,完全符合POSIX標準。
從vi到vim
作為ed的衍生物,ex / vi編輯器的版權屬于AT&T。如果想在Unix以外的平臺上使用vi,就必須重新編寫不使用任何原始代碼的克隆版本。
克隆版本有很多,下面列出了一部分:
nvi:1980年,4BSD版
calvin:1987年,DOS版
vile:1990年,DOS版
stevie:1987年,Atari ST版
elvis:1990年,Minix和386BSD版
vim:1991年,Amiga版
viper:1995年,Emacs版
elwin:1995年,Windows版
lemmy:2002年,Windows版
下面,我們來重點看一看中間的vim。Bram Moolenaar希望在Amiga上使用vi。于是,他從Atari移植了Stevie,并對其進行了改進。他給自己的這一版起名為“Vi IMitation”。有關完整的第一手資料,請參閱自由軟件雜志對Bram的采訪(https://begriffs.com/pdf/vim-interview.pdf)。
在版本1.22中,Vim被重新命名為“Vi IMproved”,它完全實現并且超越了vi的功能。以下是主流版本及其重要功能的發展歷程:
1991年11月2日,Vim 1.14:首次發布(Fred Fish disk #591)。
1992年,Vim 1.22:移植到Unix。Vim開始與Vi并駕齊驅。
1994年8月12日,Vim 3.0:支持多個緩沖區和窗口。
1996年5月29日,Vim 4.0:圖形用戶界面(主要由Robert Webb提供)。
1998年2月19日,Vim 5.0:語法著色/高亮顯示。
2001年9月26日,Vim 6.0:折疊,插件,垂直分割。
2006年5月8日,Vim 7.0:拼寫檢查,自動補齊,撤消分支,標簽。
2016年9月12日,Vim 8.0:作業,異步I / O,本機包。
有關各個版本的詳細信息,請查看:help vim8。如果想了解未來的計劃,以及已知的bug,請查看:help todo.txt。
受到來自競爭對手NeoVim的壓力,Vim 8.0加入了異步作業的支持,NeoVim的開發人員希望在編輯器中直接運行Web腳本的調試器和REPL。
Vim超級便攜。在漫長的發展過程中,為了支持多種平臺,vim本身不得不保持便攜。它可以在各種平臺上運行,包括OS / 390、Amiga、BeOS和BeBox、Macintosh classic、Atari MiNT、MS-DOS、OS / 2、QNX、RISC-OS、BSD、Linux、OS X、VMS和MS-Windows等。無論哪種計算機都可以使用Vim。
在vi發展歷程的最后一個轉折點上,最原始的ex / vi源代碼最終于2002年在BSD免費軟件許可下發布了。請點擊這里獲取(http://ex-vi.sourceforge.net/)。
下面干貨來了。在深入Vim的使用技巧之前,先讓我們了解一下Vim的組織以及讀取配置文件的方式。
配置層次結構
我曾經錯誤地認為,Vim僅從?/ .vimrc文件中讀取其所有設置和腳本。閱讀各種“dotfiles”的代碼庫更堅定了我的這一看法。通常人們覺得只通過一個.vimrc文件來控制編輯器的各個方面是一種危險的做法。這些龐大的配置文件有時被稱為“vim發行版”。
實際上,Vim的結構非常整潔,.vimrc只是多個配置文件中的其中一個而已。其實,你可以讓Vim告訴你究竟加載了哪些腳本。試試看:任意編輯計算機上的某個源代碼文件。加載后,運行如下命令:
花點時間讀完整個清單。猜猜看這些腳本可能會做些什么,并記下它們所在的目錄。
清單比你預期的要長嗎?如果你安裝了大量插件的話,那么編輯器需要做大量工作。你可以通過以下命令檢查是什么導致編輯器的速度變慢,然后再看看它創建的start.log:
為了比較起見,下面我們看看如果沒有這些配置,Vim的啟動速度有多快:
為了確定啟動時或加載緩沖區時會運行哪些腳本,Vim會遍歷“runtimepath”。該設置是一組以逗號分隔的目錄列表,各個目錄的結構都是一致的。Vim會檢查每個目錄的結構,找到需要運行的腳本,并按照目錄在列表中的順序一一處理。
運行以下命令就可以檢查系統上的runtimepath:
在我的系統上,runtimepath默認包含以下目錄。并非所有這些都必須出現在文件系統中,但如果存在就會被使用。
~/.vim
主目錄,保存個人偏好的文件。
/usr/local/share/vim/vimfiles
系統范圍的Vim目錄,保存由系統管理員決定的文件。
/usr/local/share/vim/vim81
即$VIMRUNTIME,保存與Vim一起分發的文件。
/usr/local/share/vim/vimfiles/after
系統范圍Vim目錄中的“after”目錄。系統管理員可以利用該目錄來覆蓋默認設置,或添加新的設置。
~/.vim/after
主目錄中的“after”目錄??梢岳迷撃夸浻脗€人偏好覆蓋默認設置或系統設置,或添加新的設置。
這些目錄會按照順序處理,所以要說“after”目錄有什么特別的話,那就是它位于列表末尾。實際上“after”并沒有什么特別之處。
在處理每個目錄時,Vim都會查找具有特定名稱的子文件夾。如果想了解更多這方面的信息,請參閱:help runtimepath。下面我們只挑部分進行說明。
plugin/
編輯任何類型的文件都會自動加載的Vim腳本文件,稱為“全局插件”。
autoload/
(不要與“插件”相混淆。)自動加載中的腳本包含僅在其他腳本請求時加載的函數。
ftdetect/
用于檢測文件類型的腳本??梢愿鶕募U展名、位置或內部文件內容決定文件類型。
ftplugin/
編輯已知類型的文件時執行的腳本。
compiler/
定義如何運行各種編譯器或格式化工具,以及如何解析其輸出。可以在多個ftplugins之間共享。且不會自動執行,必須通過 :compiler 調用。
pack/
Vim 8原生軟件包的目錄,它采用了“Pathogen”格式的包管理。原生的包管理系統不需要任何第三方代碼。
最后,通用的編輯器設置都會放到~/.vimrc中。你可以通過它來設置用于覆蓋特定文件類型的默認值。有關.vimrc設置的全面講解,請運行 :options。
第三方插件
在Vim中,插件只是腳本,必須放在runtimepath中的正確位置才能執行。從概念上講,插件的安裝非常簡單:只需下載文件。問題在于,很難刪除或更新某些插件,因為它們的子目錄加入到了runtimepath中,很難判斷哪個插件負責哪些文件。
為了滿足這種需求,網上出現了很多插件管理器。最早在2003年就出現了Vim.org插件倉庫。然而,直到2008年左右,插件管理器的概念才真正流行起來。
這些工具在Vim的runtimepath中添加了單獨的查檢目錄,并會為插件文檔編譯幫助標簽。大多數插件管理器還可以從網上安裝和更新插件代碼,有的還支持并行更新,或者顯示彩色的進度條。
以下是按時間順序整理的插件管理器。我按照每個插件最早和最新版本進行了排序,如果找不到官方的發行版本,則根據最早和最后的提交日期排序。
2006年3月- 2014年7月:Vimball(分發格式和關聯的Vim命令)
2008年10月- 2015年12月:Pathogen(由于原生vim包被棄用)
2009年8月- 2009年12月:Vimana
2009年12月- 2014年12月:VAM
2010年8月 - 2010年12月:Jolt
2010年10月 - 2012年12月:tplugin
2010年10月 - 2014年2月:Vundle(在NeoBundle破解代碼后停止使用)
2012年3月 - 2018年3月:vim-flavor
2012年4月 - 2016年3月:NeoBundle(被棄用,建議使用dein)
2013年1月 - 2017年8月:infect
2013年2月 - 2016年8月:vimogen
2013年10月 - 2015年1月:vim-unbundle
2013年12月 - 2015年7月:Vizardry
2014年2月 - 2018年10月:vim-plug
2015年1月 - 2015年10月:enabler
2015年8月 - 2016年4月:Vizardry 2
2016年1月 - 2018年6月:dein.vim
2016年9月 - 至今:原生Vim 8
2017年2月 - 2018年9月:minpac
2018年3月 - 2018年3月:autopac
2017年2月 - 2018年6月:pack
2017年3月 - 2017年9月:vim-pck
2017年9月 - 2017年9月:vim8-pack
2017年9月 - 2019年5月:volt
2018年9月 - 2019年2月:vim-packager
2019年2月 - 2019年2月:plugpac.vim
首先要注意,這些工具五花八門,其次通常每個工具在活躍大約四年后就會過時。
最穩定的管理插件的方法是使用Vim 8的內置功能,該功能不需要第三方代碼。下面讓我們具體來看看這種方法。
首先在運行時目錄的pack目錄中創建兩個目錄opt和start。
注意占位符 foobar。這個名稱完全取決于你。我們用它對包進行分類。大多數人會把所有的插件都扔進一個無意義的類別中,這樣完全沒問題。你可以選擇自己喜歡的名稱,在本文中我選擇使用 foobar。理論上,你也可以創建多個類別,比如~/.vim/pack/navigation, ~/.vim/pack/linting等。請注意,Vim不會檢測類別之間的重復,如果存在重復,則會加載兩次。
“start”中的包會自動加載。而對于“opt”中的包,只有通過:packadd命令特別請求,Vim才會加載。opt中適合保存不常用的軟件包,以及為保持Vim的快速啟動不必要運行的腳本。請注意,:packadd沒有相反的命令卸載包。
在下述示例子中,我們將添加“ctrlp”模糊查找插件到opt目錄。下載最新版本的命令如下:
????|?tar?zx?-C?~/.vim/pack/foobar/opt
該命令創建了 ~/.vim/pack/foobar/opt/ctrlp.vim-1.79 文件夾,現在這個包可以使用了。我們再次回到vim中,為這個新包創建一個幫助標簽的索引:
該命令會在包的doc目錄中創建了一個名叫”tags“的文件,這樣Vim的內部幫助系統就可以使用它的內容了。(或者你也可以在包加載之后運行一次:helptags ALL,該命令會處理runtimepath下的所有文檔。)
在需要使用包時,只需加載它(Tab自動補齊也可以用于插件名,所以不需要輸入全名):
packadd會把包的根目錄放到runtimepath中,然后運行它的plugin和ftdetect腳本。在加載ctrlp之后,就可以按Ctrl-P來彈出模糊文件查找了。
有些人喜歡將~/.vim目錄放到版本管理中,使用git submodules來管理每個包。而我喜歡簡單地將包從tarball中解壓,然后用自己的代碼庫來管理。如果你使用成熟的包,那么更新不會太頻繁,加上腳本本身也很小,不會把git歷史弄得太亂。
備份和undo
根據不同的用戶設置,Vim可以防止四種類型的丟失:
編輯過程中(兩次保存之間)崩潰。Vim會定期將未保存的修改寫入交換文件來防止這種情況。
使用兩個Vim進程編輯同一個文件,兩個進程互相覆蓋。交換文件也可以防止這種情況。
保存過程中崩潰,即在目標文件已被截斷,新的內容尚未完全寫入時崩潰。Vim可以通過“writebackup”來防止這種情況。為了實現該功能,Vim會首先將內容寫入新的文件,寫入成功后與原始文件交換。但這個功能取決于“backupcopy”設置。
已保存新文件,但想要找回原文件。Vim可以通過在寫入改變后保留原始文件的備份來防止這種情況。
在介紹具體的設置之前,先來放松一下吧!下面是GitHub上人們對于vimrc的一些評論:
“不要創建交換文件。用版本控制管理就好。”
“素人才用備份。高手都用版本控制。”
“用版本控制就好!”
“版本控制都滿天飛了,就不要再用交換文件和備份了。”
“不要寫備份文件,版本控制就是很好的備份?!?/span>
“我其實從來沒用過VIM的備份文件……一直都在用版本控制?!?/span>
“反正大部分東西都保存在版本控制里。”
“禁用備份文件,因為反正你也得用版本控制。”
“版本控制已來到,git拯救全世界。”
“禁用交換文件和備份(永遠使用版本控制!永遠!)”
“關掉備份,我所有東西都用版本控制?!?/span>
上面的評論反映出,大家只了解上述第四種情況(偶爾也會提及第三種情況),這些人傾向于把交換文件也禁用,這會讓Vim無法防止第一種和第二種情況。
為了保證編輯更安全,我建議使用下述配置:
"?(4?seconds)?are?fine
set?swapfile
set?directory^=~/.vim/swap//
"?protect?against?crash-during-write
set?writebackup
"?but?do?not?persist?backup?after?successful?write
set?nobackup
"?use?rename-and-write-new?method?whenever?safe
set?backupcopy=auto
"?patch?required?to?honor?double?slash?at?end
if?has("patch-8.1.0251")
????"?consolidate?the?writebackups?--?not?a?big
????"?deal?either?way,?since?they?usually?get?deleted
????set?backupdir^=~/.vim/backup//
end
"?persist?the?undo?tree?for?each?file
set?undofile
set?undodir^=~/.vim/undo//
這些設置為寫入過程啟用了備份,但在成功寫入后不會保留備份,因為我們有版本控制。注意你需要mkdir ~/.vim/{swap,undodir,backup},否則Vim會使用設置列表中的下一個可用的文件夾。你還應該chmod這些文件夾來保證隱私,因為交換文件和undo歷史可能包含敏感信息。
關于配置中的路徑,需要提及的一點是,它們末尾使用了雙斜線。這樣可以無歧義地表示不同目錄下同名文件的交換文件和備份文件。例如,/foo/bar文件的交換文件會保存在~/.vim/swap/%foo%bar.swp(斜線z轉義成百分號)。Vim有一個bug,對于backupdir不會正確處理雙斜線寫法,該bug直到最近才修復,而上述配置可以防止這個bug。
我們還要求Vim持久保存每個文件的undo文件,這樣在退出Vim并重新編輯文件時依然可以使用undo。雖然有了交換文件,這樣做有點多余,但實際上undo文件是補充性質的,因為它僅在原文件被寫入時才寫入。(如果undo文件寫入太頻繁,那么可能在崩潰后無法匹配磁盤上文件的狀態,所以Vim不這樣做。)
說起undo就不得不提起Vim會維持編輯歷史的整個樹形結構。這意味著你可以做一個修改,undo之后,然后做另一個修改,這時所有三個狀態都可以被恢復。使用:undolist命令可以看到修改的時間和大小,但從該命令的結果很難想象整個樹形結構。你可以遍歷列表中的特定修改,也可以用:earlier和:later命令加上一個時間參數(如5m)或保存次數參數(如3f)在時間軸上移動。但是,遍歷undo樹最好使用插件——如undotree。
啟用這些災難恢復設置可以讓你安心地使用Vim。我曾經在編輯過程中多次保存,或者每次離開電腦時也會保存,但現在我會幾個小時都不保存,因為我知道交換文件在老老實實地干活。
最后幾點:要時刻關注這些災難恢復文件,時間長了它們可能會在.vim文件夾下越積越多,占用大量空間。另外,當磁盤剩余空間很少,卻需要保存大文件時,也許有必要設置nowritebackup,否則Vim必須臨時保存整個文件的副本。默認設置下“backupskip”設置能夠禁用系統臨時目錄下的任何文件的備份。
Vim的“patchmode”與備份有關。你可以在沒有被版本控制管理的目錄下使用該設置。例如,如果你想下載源代碼tar包,做一些修改然后通過郵件列表提交補丁,這一過程中不使用git。只需運行:set patchmod=.orig,那么任何Vim寫入的文件“foo”就會備份成“foo.orig”。然后可以通過命令行比較.orig文件和新文件來創建補丁。
包含和路徑
絕大多數編程需要都允許你在一個文件中包含另一個模塊或文件。Vim通過path、include、suffixesadd和includeexpr配置項來了解如何跟蹤包含文件中的程序標識符。標識符搜索(參見:help include-search)是另一種使用ctags維持系統頭文件的標簽文件的方式。
C程序的默認設置工作得很好。其他語言也同樣支持,但需要一些設置。這些設置超出了本文的范圍,可以參考:help include。
如果一切配置正確,那么你可以在標識符上按 [i 來顯示標識符定義,或者在宏常量上按 [d 顯示宏定義。還有,在文件名上按 gf 可以搜索路徑并跳轉到相應的文件。由于路徑也會影響 :find 命令,一些人傾向于在路徑中添加“**/*”或常用的目錄,把 :find 命令當作簡裝版的模糊查找使用。但這樣做會減慢標識符搜索的速度,因為它需要搜索與標識符搜索無關的目錄。
不污染路徑而實現相同查找功能的方式之一就是建立一個映射。這樣只需按<Leader><space>(通常這兩個鍵就是反斜杠然后空格)然后輸入文件名,再使用Tab或Ctrl-D自動完成來查找文件。
nmap?<Leader><space>?:e?./**/
重申一下:路徑參數是為頭文件準備的。如果你想看更多證據,還可以用:checkpath命令顯示哪些路徑有效。加載一個C文件然后運行:checkpath,它就會顯示那些當前文件包含,卻找不到的文件名。帶感嘆號的 :checkpath! 可以顯示當前文件包含的整個頭文件層次結構。
默認情況下,路徑的值為“.,/usr/include,,”,意思是當前目錄、/usr/include,然后是當前活動緩沖區的所有兄弟文件。目錄指定符和glob非常強大,詳情可以查看:help file-searching。
我還在C ftplugin中(后文會多次提到它),讓路徑搜索包含了當前項目的包含文件,如./src/include或./include。
setlocal?path+=/usr/include
帶數字的 ** (如**3)指定子目錄搜索的深度。最好在這里指定深度,以免標識符搜索鎖死。
如果 :checkpath 指示出項目中找不到的文件,那么也可以考慮將下面這些模式添加到路徑中。當然,這完全取決于你的系統。
更多的系統包含文件:/usr/include/**4,/usr/local/include/**3
Homebrew庫的頭文件:/usr/local/Cellar/**2/include/**2
Macports庫的頭文件:/opt/local/include/**
OpenBSD庫的頭文件:/usr/local/lib/\*/include,/usr/X11R6/include/\*\*3
另請參考::he [,:he gf,:he :find。
編輯-編譯循環
:make 命令會執行用戶選擇的程序來構建項目,然后將輸出收集到quickfix緩沖區中。quickfix記錄中的每一項都記錄了文件名、行號、列號、類型(警告或錯誤)和消息。一種常見的使用方括號命令的映射方式如下,可以在quickfix項目中快速移動:
nmap?]Q?:clast<cr>
nmap?[q?:cprev<cr>
nmap?[Q?:cfirst<cr>
如果在更新程序并重新編譯后,你想知道上次的消息,可以使用 :colder 命令(使用 :cnewer 返回)。如果需要查看有關當前錯誤的更多信息,可以使用 :cc ,然后用 :copen 命令查看完整的quickfix緩沖區。還可以使用 :cile、:caddfile 或 :cexpr 命令,無需運行:make而自行填充quickfix緩沖區。
Vim能夠利用指定的errorformat字符串解析編譯的輸出。errorformat是個類似scanf的轉義序列。例如,Vim的gcc設置($VIMRUNTIME/compiler/gcc.vim)中自帶了errorformat設置,但卻沒有包含clang編譯器的設置。于是我創建了下面的定義:
"
"?It?should?be?possible?to?make?this?work?for?the?combination?of
"?-fno-show-column?and?-fcaret-diagnostics?as?well?with?multiline
"?and?%p,?but?I?was?too?lazy?to?figure?it?out.
"
"?The?%D?and?%X?patterns?are?not?clang?per?se.?They?capture?the
"?directory?change?messages?from?(GNU)?'make?-w'.?I?needed?this
"?for?building?a?project?which?used?recursive?Makefiles.
CompilerSet?errorformat=
????\%f:%l%c:{%*[^}]}{%*[^}]}:\?%trror:\?%m,
????\%f:%l%c:{%*[^}]}{%*[^}]}:\?%tarning:\?%m,
????\%f:%l:%c:\?%trror:\?%m,
????\%f:%l:%c:\?%tarning:\?%m,
????\%f(%l,%c)\?:\?%trror:\?%m,
????\%f(%l,%c)\?:\?%tarning:\?%m,
????\%f\?+%l%c:\?%trror:\?%m,
????\%f\?+%l%c:\?%tarning:\?%m,
????\%f:%l:\?%trror:\?%m,
????\%f:%l:\?%tarning:\?%m,
????\%D%*\\a[%*\\d]:\?Entering\?directory\?%*[`']%f',
????\%D%*\\a:\?Entering\?directory\?%*[`']%f',
????\%X%*\\a[%*\\d]:\?Leaving\?directory\?%*[`']%f',
????\%X%*\\a:\?Leaving\?directory\?%*[`']%f',
????\%DMaking\?%*\\a\?in\?%f
CompilerSet?makeprg=make
要激活該編譯器設置,只需運行 :compiler clang。通常該命令在ftplugin文件中執行。
另一個例子是在文本文件上運行GNU Diction來識別句子中用錯的詞匯和短語。可以創建一個“編譯器”,名為diction.vim:
運行 :compiler diction 之后,可以使用 :make 命令來運行,并填充quickfix。最后,我在.vimrc中添加了一個映射來運行make:
"?GNUism,?for?building?recursively
map?<silent>?<s-F5>?:make?-w<cr><cr><cr>
差異文件和補丁
Vim自帶的比較工具非常強大,但可能有點難用,特別是三方合并視圖。但實際上花點時間學習你就會發現其實挺好用的。要點就是,每個窗口都可以處于或不處于“diff mode”。所有處于diffmode的窗口(用:difft[his]設置)會與所有其他已經處于diffmode的窗口進行比較。
我們從一個簡單的例子開始。首先創建兩個文件:
echo?"goodbye,?world"?>?h2
vim?h1?h2
在vim中運行 :all 命令,將上述參數指定的文件分別放入各自的窗口中。在上方的h1的窗口中運行 :difft。你會看到出現了一個分割線,但沒有檢測到任何差異。用Ctrl-W Ctrl-W移動到下方窗口,然后運行 :difft。這時就會檢測出hello和goodbye之間的差異。在下方窗口中執行 :diffg[et] 可以從上方窗口中拉取“hello”,或者使用 :diffp[ut] 將“goodbye”發送到上方窗口。如果有多個差異塊,那么按 ]c 或 [c 可以在不同的差異塊中移動。
快捷方式之一就是運行 vim -d h1 h2 (或者運行其別名 vimdiff h1 h2),該命令會對所有窗口執行 :difft。此外,還可以先用vim h1僅加載h1,然后執行 :diffsplit h2。記住,所有這些命令實際上都是將文件加載到窗口中并設置diffmode而已。
了解這些基本知識后,我們來學習怎樣把Vim作為git的三方合并工具使用。首先配置git:
git?config?merge.conflictstyle?diff3
git?config?mergetool.prompt?false
現在,當遇到合并沖突時,只需運行git mergetool。該命令會啟動Vim并打開四個窗口。這部分看上去很嚇人,我經常會舉棋不定。
|???????????|????????????|????????????|
|???LOCAL???|????BASE????|???REMOTE???|
+-----------+------------+------------+
|?????????????????????????????????????|
|?????????????????????????????????????|
|?????????????(edit?me)???????????????|
+-------------------------------------+
關鍵在于所有編輯都應該在下方窗口中進行。上方的三個窗口僅用于提供文件差異(local和remote)的上下文,以及每一方在修改之前的樣子(base)。
使用 ]c 命令在下方窗口中移動,針對每個差異塊,可以選擇local、base或remote之一來替換,或者可以自己修改,合并多方的內容。
為了能夠更容易地從上方窗口拉取修改,我在vimrc里設置了一些映射:
map?<Leader>2?:diffget?BASE<CR>
map?<Leader>3?:diffget?REMOTE<CR>
我們已經介紹過了 :diffget,上述綁定會為其傳遞一個參數,即用來識別拉取源的緩沖區名。
合并結束后,執行 :wqa 保存所有窗口并退出。如果你想放棄合并,可以運行 :cq 放棄所有修改,給shell返回一個錯誤代碼。該錯誤代碼會告訴git應當忽略這些修改。
diffget還可以接受范圍。如果想從某個上方窗口拉取所有差異塊,而不想逐個拉取,可以執行 :1,$+1diffget {LOCAL,BASE,REMOTE} ?!?#43;1”是必要的,因為緩沖區的最后一行的“下方”可能存在被刪除的行。
畢竟,三方合并其實很簡單。至少,不需要用Fugitive之類的插件在合并沖突時顯示差異。
最后,8.1.0360版本中包含了xdiff庫,可以直接創建diff文件。這比使用外部程序更有效率,而且可以采用多種diff算法。“patience”算法通??梢陨杀饶J設置更容易閱讀的輸出。在.vimrc中這樣設置:
endif
緩沖區I/O?
看看這是不是很熟悉?你編輯了一個緩沖區,想把它保存成新文件,所以執行了:w newname。再次進行一些編輯后,執行 :w ,但卻保存到了原始文件上。在這種情況下,你真正需要的是 :saveas newname,即寫入新文件,并將緩沖區的文件名改為新文件,方便以后的寫入。此外,:file newname命令可以改變緩沖區文件名,而不會執行實際的寫入。
學習更多有關讀寫命令的知識也很有用。因為r和w都是ex的命令,所以它們都可以接受范圍。下面是一些你不太熟知的使用方法:
:w >> foo
將整個緩沖區追加到文件中
:.w >> foo
將當前行追加到文件中
:$r foo
讀取foo并插入到緩沖區末尾
:0r foo
讀取foo并插入到開頭,已有行向下移動
:.,$w foo
將當前行以及之后的所有行寫入文件
:r !ls
讀取ls輸出到當前光標位置
:w !wc
將緩沖區發送到wc命令然后顯示結果
:.!tr 'A-Za-z' 'N-ZA-Mn-za-m'
為當前行執行ROT-13
:w | so %
連鎖命令:寫入并執行緩沖區
:e!
放棄為保存到修改,重新加載緩沖區
:hide edit foo
編輯foo,如果當前緩沖區被修改過,則隱藏
冷知識:上面的例子中使用一整行來調用 tr 以實現ROT-13加密,但實際上Vim內置了該功能,即 g? 命令。可以將其應用到移動操作,如 g?$。
filetypes
filetypes設置可以根據緩沖區中檢測到到文件類型來改變設置。不過它們并不一定非要自動檢測,我們可以手動啟用它們,實現一些有趣的效果。一個例子就是十六進制編輯。任何文件都可以作為十六進制值查看。GitHub用戶the9ball寫了一個非常聰明的ftplugin腳本,可以將緩沖區傳遞給xxd或傳回,實現十六進制編輯。
為了方便使用,Vim 5版本捆綁了xxd工具。Vim的todo.txt提到,他們想讓二進制文件編輯功能更加順暢,但xxd已經實現了不少功能。?
將下面的代碼放到 ~/.vim/ftplugin/xxd.vim 中。保存到ftplugin中的意思是,每當filetype(即“ft”)變成xxd時,Vim就會執行該腳本。我在腳本中添加了一些簡單的注釋:
????finish
endif
"?don't?insert?a?newline?in?the?final?line?if?it
"?doesn't?already?exist,?and?don't?insert?linebreaks
setlocal?binary?noendofline
silent?%!xxd?-g?1
%s/\r$//e
"?put?the?autocmds?into?a?group?for?easy?removal?later
augroup?ftplugin-xxd
????"?erase?any?existing?autocmds?on?buffer
????autocmd!?*?<buffer>
????"?before?writing,?translate?back?to?binary
????autocmd?BufWritePre?<buffer>?let?b:xxd_cursor?=?getpos('.')
????autocmd?BufWritePre?<buffer>?silent?%!xxd?-r
????"?after?writing,?restore?hex?view?and?mark?unmodified
????autocmd?BufWritePost?<buffer>?silent?%!xxd?-g?1
????autocmd?BufWritePost?<buffer>?%s/\r$//e
????autocmd?BufWritePost?<buffer>?setlocal?nomodified
????autocmd?BufWritePost?<buffer>?call?setpos('.',?b:xxd_cursor)?|?unlet?b:xxd_cursor
????"?update?text?column?after?changing?hex?values
????autocmd?TextChanged,InsertLeave?<buffer>?let?b:xxd_cursor?=?getpos('.')
????autocmd?TextChanged,InsertLeave?<buffer>?silent?%!xxd?-r
????autocmd?TextChanged,InsertLeave?<buffer>?silent?%!xxd?-g?1
????autocmd?TextChanged,InsertLeave?<buffer>?call?setpos('.',?b:xxd_cursor)?|?unlet?b:xxd_cursor
augroup?END
"?when?filetype?is?set?to?no?longer?be?"xxd,"?put?the?binary
"?and?endofline?settings?back?to?what?they?were?before,?remove
"?the?autocmds,?and?replace?buffer?with?its?binary?value
let?b:undo_ftplugin?=?'setl?bin<?eol<?|?execute?"au!?ftplugin-xxd?*?<buffer>"?|?execute?"silent?%!xxd?-r"'
打開一個文件,然后執行 :set ft。記下文件類型。然后執行 :set ft=xxd。Vim就會變成一個十六進制編輯器。要恢復原來的視圖,只需 :set fo=foo,其中foo是原始的文件類型。注意十六進制視圖甚至還有語法高亮,因為Vim默認自帶了 $VIMRUNTIME/syntax/xxd.vim 。
注意這里的“b:undo_ftplugin”非常巧妙,它可以在用戶或ftdetect機制將文件類型切換成其他filetype時,讓filetypes執行一些清理工作。(上面的例子還可以改進一下,因為如果你 :set ft=xxd 然后直接改回去,那么緩沖區會被標記為已修改,即使你沒有進行任何修改。)
ftplugins還可以進一步定義已知的filetype。例如,Vim已經在 $VIMRUNTIME/ftplugin/c.vim 中為C語言包含了非常好的默認設置。我在 ~/.vim/after/ftplugin/c.vim 中添加了額外的選項:
setlocal?cino="Ls,:0,l1,t0,(s,U1,W4"
"?for?quickfix?errorformat
compiler?clang
"?shows?long?build?messages?better
setlocal?ch=2
"?auto-create?folds?per?grammar
setlocal?foldmethod=syntax
setlocal?foldlevel=10
"?local?project?headers
setlocal?path=.,,*/include/**3,./*/include/**3
"?basic?system?headers
setlocal?path+=/usr/include
setlocal?tags=./tags,tags;~
"??????????????????????^?in?working?dir,?or?parents
"????????????????^?sibling?of?open?file
"?the?default?is?menu,preview?but?the?preview?window?is?annoying
setlocal?completeopt=menu
iabbrev?#i?#include
iabbrev?#d?#define
iabbrev?main()?int?main(int?argc,?char?**argv)
"?add?#include?guard
iabbrev?#g?_<c-r>=expand("%:t:r")<cr><esc>VgUV:s/[^A-Z]/_/g<cr>A_H<esc>yypki#ifndef?<esc>j0i#define?<esc>o<cr><cr>#endif<esc>2ki
注意上述腳本使用了“setlocal”而不是“set”。它僅對當前緩沖區生效,而不是對整個Vim進程生效。
該腳本還添加了一些縮寫。例如,我可以輸入 #g 并按回撤,就能自動使用當前文件名添加包含檢測:
#define?_FILENAME_H
/*?<--?cursor?here?*/
#endif
你還可以使用點(“.”)來混合多種filetypes。下面是應用的例子。不同的項目有不同的編碼規范,所以你可以將默認的C設置與特定項目的設置結合起來。OpenBSD的源代碼遵循style(9)格式(https://man.openbsd.org/style.9),所以我們來做一個特殊的openbsd filetype。可以在相關文件上使用 :set ft=c.openbsd 將兩個filetype合并。
要檢測openbsd filetype,可以查看緩沖區的內容,而不僅僅是通過文件擴展名或文件在磁盤上的位置。C文件中包含OpenBSD源代碼的標志就是第一行出現 /* $OpenBSD: 。
創建 ~/.vim/after/ftdetect/openbsd.vim 進行檢測:
????????????????\??if?getline(1)?=~?'OpenBSD;'
????????????????\|???setl?ft=c.openbsd
????????????????\|?endif
augroup?END
OpenBSD的Vim移植已經包含了該filetype的特殊語法:/usr/local/share/vim/vimfiles/syntax/openbsd.vim?;貞浺幌?#xff0c;/usr/local/share/vim/vimfiles目錄位于runtimepath中,用于保存系統管理員提供的文件。該openbsd.vim腳本包含下面的函數:
????setlocal?cindent
????setlocal?cinoptions=(4200,u4200,+0.5s,*500,:0,t0,U4200
????setlocal?indentexpr=IgnoreParenIndent()
????setlocal?indentkeys=0{,0},0),:,0#,!^F,o,O,e
????setlocal?noexpandtab
????setlocal?shiftwidth=8
????setlocal?tabstop=8
????setlocal?textwidth=80
endfun
我們只需在適當時候調用該函數。創建 ~/.vim/after/ftplugin/openbsd.vim:
現在打開任何頂部具有標志性注釋的C文件或頭文件,就會被識別為c.openbsd類型,從而采用style(9)手冊頁中規定的縮進選項。
別忘了鼠標
在此友好地提醒你,盡管我們都喜歡命令行,但實際上Vim也支持鼠標,而且有些任務比鍵盤更方便。由于xterm能夠將鼠標事件轉換為stdin轉義代碼,所以我們甚至可以通過SSH都能支持鼠標事件。
如果想啟用鼠標支持,則需要設置 mouse=n。許多人喜歡設置 mouse=a,因為這樣就可以在所有模式下工作,但我更喜歡只在普通模式下啟用鼠標支持。這樣,在我用鍵盤加點擊的方式在瀏覽器中打開鏈接時,就不會錯誤地創建可視選擇區域。
以下是鼠標可以執行的操作:
打開或關閉折疊(當foldcolumn> 0時)。
選擇標簽(比 gt gt gt gt ...要好用得多)
單擊完成動作,例如 d<點擊>。類似于easymotion插件,但不需要任何插件。
雙擊即可跳轉到幫助主題。
拖動底部的狀態行以更改cmdheight。
拖動窗口邊緣以調整大小。
鼠標滾輪。
其他編輯功能
這部分涉及的內容很雜,但我僅在此介紹一些我學到的技巧。第一個讓我感到震驚的是::set virtualedit=all。它允許你將光標移動到窗口中的任何位置。如果你輸入字符或插入可視塊,Vim會在插入的字符的左側添加所需的空格以保證它們的位置。虛擬編輯模式可以簡化表格數據的編輯。你可以通過 :set virtualedit= 來關閉這個選項。
接下來是一些移動命令。在跳轉到下一段時,我習慣于使用 } ,每次跳轉一個段落。然而, ] 字符可以完成更精準的跳轉:跳轉到下一個函數 ]]、作用域 ]}、圓括號 ‘])’、注釋 ]/、差異塊 ]c。前面提到的 quickfix 映射 ]q 也是這種操作方式之一。
對于大段的跳轉,我曾經嘗試過 1000j 等操作,但實際上只需在普通模式下鍵入百分比,Vim就會跳轉到相應的位置,比如50%。說到滾動百分比,你隨時可以使用CTRL-G查看它。所以現在我采用了 :set noruler 的設置,只在需要了解百分比的時候查看,這樣畫面就不會過于雜亂了。這似乎與色彩斑斕的powerlines的流行趨勢有點背道而馳。
如果想在標簽、文件或文件中跳轉,那么有些命令可以幫助你。比如::ls、:tags、:jumps 和 :marks。在標簽之間跳轉實際上會創建一個棧,你可以按CTRL-T跳到前一個。以前我經常按CTRL-O退出跳轉,但是它不如彈出標簽棧那般直接。
在使用ctags編制索引的項目目錄中,你可以使用 -t 選項在打開編輯器時直接跳到標簽,比如:vim -t main。如果想更靈活地查找標簽文件,那么可以設置 tags 配置變量。請注意如下示例中的分號,有了它Vim就可以從當前目錄向上搜索到主目錄。如此一來,你就可以在項目文件夾外部使用更通用的系統標記文件。
"??????????????????????????^?in?working?dir,?or?parents
"???????????????????^?in?any?subfolder?of?working?dir
"???????????^?sibling?of?open?file
此外,還有一些緩沖區技巧。切換緩沖區的命令 :bu 可以接受緩沖區名稱的片段作為參數,而不僅僅是數字。有時很難記住這些數字,相比之下源文件的名稱更加方便記憶。你也可以使用標記來瀏覽緩沖區。如果使用大寫字母作為標記的名稱,則可以跨緩沖區跳轉到該標記。你還可以在標題中設置標記H,在源文件中設置C,在Makefile中設置M,這樣就可以在緩沖區之間來回跳轉了。
你有沒有遇到過這種情況:復制一個單詞,然后在其他地方刪掉一個單詞,當嘗試粘貼第一個單詞時,卻發現原來復制的單詞已被覆蓋。是不是很氣惱?Vim寄存器不善于處理這種情況。你可以用 :reg 檢查其內容。當你復制文本時,先前的復制就會被輪換到寄存器"0 - "9。因此,"0p 會粘貼倒數第二個復制/刪除。特殊寄存器 "+ 和 "* 可以從系統剪貼板中復制/粘貼,也可以復制/粘貼到系統剪貼板。通常,這兩者的含義相同,除了在一些X11設置中會區分首選和備選。
另一個非常方便的隱藏功能是命令行窗口。它是一個緩沖區,其中包含了你以前運行的命令和搜索。你可以通過 q: 或 q/ 顯示該窗口。在進入該緩沖區后,你可以隨意移動到任何一行,然后按Enter鍵運行該行的命令。然而,你也可以在按Enter鍵之前對行進行編輯。你的更改不會影響該行(僅會將新的命令將添加到列表的底部)。
vim的使用技巧繁多,文本無法詳盡闡述。如果你想了解更多信息,請參閱幫助文檔:views-sessions、viminfo、TOhtml、ins-completion、cmdline-completion、multi-repeat、scroll-cursor、text-objects、grep、netrw-contents。
原文:https://begriffs.com/posts/2019-07-19-history-use-vim.html
本文為 CSDN 翻譯,轉載請注明來源出處。
(*本文為 AI科技大本營轉載文章,轉載請聯系原作者)
社群福利
掃碼添加小助手,回復:大會,加入2019 AI開發者大會福利群,每周一、三、五更新技術福利,還有不定期的抽獎活動~
◆
精彩推薦
◆
60+技術大咖與你相約 2019 AI ProCon!大會早鳥票已售罄,優惠票速搶進行中......2019 AI開發者大會將于9月6日-7日在北京舉行,這一屆AI開發者大會有哪些亮點?一線公司的大牛們都在關注什么?AI行業的風向是什么?2019 AI開發者大會,傾聽大牛分享,聚焦技術實踐,和萬千開發者共成長。
推薦閱讀
自動駕駛激蕩風云錄:來自圈內人的冷眼解讀
不止最佳長論文,騰訊AI在ACL上還有這些NLP成果
認知智能的突圍:NLP、知識圖譜是AI下一個“掘金地”?
5G+AI重新定義生老病死
干貨 | 20個Python教程,掌握時間序列的特征分析(附代碼)
2019 年度程序員吸金榜:你排第幾?
字節跳動入局全網搜索;思科回應中國區裁員;IntelliJ IDEA 新版發布!?| 極客頭條
知名飲料制造商股價暴漲500%驚動FBI,只因在名字中加入了"區塊鏈" ?
總結
以上是生活随笔為你收集整理的Vim激荡30年发展史的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: CTC模型简介
- 下一篇: 计算机历史人物-随笔