日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

Vim激荡30年发展史

發(fā)布時(shí)間:2023/12/14 编程问答 55 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Vim激荡30年发展史 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.


作者 |?Joe Nelson

譯者 | 彎月,編輯?| 屠敏

來(lái)源 | CSDN(ID:CSDNnews)


導(dǎo)語(yǔ):眾所周知,Vim 是從 vi 發(fā)展出來(lái)的一個(gè)文本編輯器。其擁有代碼補(bǔ)全、編譯及錯(cuò)誤跳轉(zhuǎn)等豐富的功能特性,在程序員群體中廣受歡迎。

本文是作者?Joe Nelson?從頭到尾閱讀 Vim 用戶手冊(cè)以及追溯歷史之后的一些心得。希望這些筆記能夠幫助大家發(fā)現(xiàn)這款編輯器的核心功能,從而更加熟練地使用各個(gè)插件。

如果你想進(jìn)一步了解Vim,那么我建議你入手一本紙質(zhì)的用戶手冊(cè)和優(yōu)秀的袖珍參考手冊(cè)。我沒(méi)有找到官方的Vim紙質(zhì)手冊(cè),最后只好打印了這個(gè)PDF(https://begriffs.com/pdf/vim-user-manual.pdf)。為了方便查看Vim的命令列表,我建議你入手上圖中的《vi and Vim Editors Pocket Reference》。


歷史


Vi的誕生

Vi源自QED編輯器,距今已有五十多年的歷史。其發(fā)展歷程如下:

  • 1966年:伯克利分時(shí)系統(tǒng)的QED(“Quick EDitor”)

  • 1969年7月:登月(僅供參考)

  • 1969年8月:QED -> AT&T的ed

  • 1976年2月:ed ->瑪麗王后大學(xué)的em(“Editor for Mortals”)

  • 1976年:em -> 加州大學(xué)伯克利分校的ex (“EXtended”)

  • 1977年10月:ex有了可視化模式,vi

閱讀一下用戶手冊(cè),你就會(huì)發(fā)現(xiàn)QED和ex之間有很多相似之處。這兩個(gè)編輯器在指定和操作行范圍時(shí)都采用了類似的語(yǔ)法。

QED、ed和em這類的編輯器都是為硬拷貝終端設(shè)計(jì)的,這些終端基本上就是帶調(diào)制解調(diào)器的電動(dòng)打字機(jī)。硬拷貝終端可以將系統(tǒng)輸出打印到紙上。顯然一旦打印完成,就無(wú)法更改輸出,因此這種編輯過(guò)程需要包含用于更新和手動(dòng)打印文本范圍的命令。

到1976年的時(shí)候,ADM-3A等視頻可視化終端出現(xiàn)了。Ex編輯器添加了一個(gè)“開放模式”,允許在可視化終端上進(jìn)行行內(nèi)編輯,還有一個(gè)可視化模式,可以在支持光標(biāo)的終端上面利用屏幕進(jìn)行編輯。這種可視模式(可以通過(guò)命令“vi”激活)可以在屏幕上顯示部分文件的最新視圖,同時(shí)還保留了屏幕底部的ex命令行。(趣事:在ADM-3A上,h、j、k、l鍵兼作方向鍵,所以vi選擇這幾個(gè)鍵作為光標(biāo)移動(dòng)只是為了保持一致而已。)

如果你想了解更多關(guān)于從ed到ex / vi的發(fā)展,可以閱讀Bill Joy的這段采訪(https://begriffs.com/pdf/unix-review-bill-joy.pdf),他在文中談到了ex / vi的創(chuàng)建過(guò)程,以及一些令他失望的事情。

傳統(tǒng)的vi實(shí)際上只是ex的另一種形式,它們的可執(zhí)行文件是同一個(gè),根據(jù)調(diào)用時(shí)的可執(zhí)行文件名來(lái)決定啟動(dòng)ex模式還是vi模式。ex / vi對(duì)之前的版本進(jìn)行了改進(jìn),只需很少的系統(tǒng)資源,就可以在有限的帶寬下操作。而且該工具還支持于大多數(shù)系統(tǒng),完全符合POSIX標(biāo)準(zhǔn)。

從vi到vim

作為ed的衍生物,ex / vi編輯器的版權(quán)屬于AT&T。如果想在Unix以外的平臺(tái)上使用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版

下面,我們來(lái)重點(diǎn)看一看中間的vim。Bram Moolenaar希望在Amiga上使用vi。于是,他從Atari移植了Stevie,并對(duì)其進(jìn)行了改進(jìn)。他給自己的這一版起名為“Vi IMitation”。有關(guān)完整的第一手資料,請(qǐng)參閱自由軟件雜志對(duì)Bram的采訪(https://begriffs.com/pdf/vim-interview.pdf)。

在版本1.22中,Vim被重新命名為“Vi IMproved”,它完全實(shí)現(xiàn)并且超越了vi的功能。以下是主流版本及其重要功能的發(fā)展歷程:

  • 1991年11月2日,Vim 1.14:首次發(fā)布(Fred Fish disk #591)。

  • 1992年,Vim 1.22:移植到Unix。Vim開始與Vi并駕齊驅(qū)。

  • 1994年8月12日,Vim 3.0:支持多個(gè)緩沖區(qū)和窗口。

  • 1996年5月29日,Vim 4.0:圖形用戶界面(主要由Robert Webb提供)。

  • 1998年2月19日,Vim 5.0:語(yǔ)法著色/高亮顯示。

  • 2001年9月26日,Vim 6.0:折疊,插件,垂直分割。

  • 2006年5月8日,Vim 7.0:拼寫檢查,自動(dòng)補(bǔ)齊,撤消分支,標(biāo)簽。

  • 2016年9月12日,Vim 8.0:作業(yè),異步I / O,本機(jī)包。

有關(guān)各個(gè)版本的詳細(xì)信息,請(qǐng)查看:help vim8。如果想了解未來(lái)的計(jì)劃,以及已知的bug,請(qǐng)查看:help todo.txt。

受到來(lái)自競(jìng)爭(zhēng)對(duì)手NeoVim的壓力,Vim 8.0加入了異步作業(yè)的支持,NeoVim的開發(fā)人員希望在編輯器中直接運(yùn)行Web腳本的調(diào)試器和REPL。

Vim超級(jí)便攜。在漫長(zhǎng)的發(fā)展過(guò)程中,為了支持多種平臺(tái),vim本身不得不保持便攜。它可以在各種平臺(tái)上運(yùn)行,包括OS / 390、Amiga、BeOS和BeBox、Macintosh classic、Atari MiNT、MS-DOS、OS / 2、QNX、RISC-OS、BSD、Linux、OS X、VMS和MS-Windows等。無(wú)論哪種計(jì)算機(jī)都可以使用Vim。

在vi發(fā)展歷程的最后一個(gè)轉(zhuǎn)折點(diǎn)上,最原始的ex / vi源代碼最終于2002年在BSD免費(fèi)軟件許可下發(fā)布了。請(qǐng)點(diǎn)擊這里獲取(http://ex-vi.sourceforge.net/)。

下面干貨來(lái)了。在深入Vim的使用技巧之前,先讓我們了解一下Vim的組織以及讀取配置文件的方式。


配置層次結(jié)構(gòu)


我曾經(jīng)錯(cuò)誤地認(rèn)為,Vim僅從?/ .vimrc文件中讀取其所有設(shè)置和腳本。閱讀各種“dotfiles”的代碼庫(kù)更堅(jiān)定了我的這一看法。通常人們覺(jué)得只通過(guò)一個(gè).vimrc文件來(lái)控制編輯器的各個(gè)方面是一種危險(xiǎn)的做法。這些龐大的配置文件有時(shí)被稱為“vim發(fā)行版”。

實(shí)際上,Vim的結(jié)構(gòu)非常整潔,.vimrc只是多個(gè)配置文件中的其中一個(gè)而已。其實(shí),你可以讓Vim告訴你究竟加載了哪些腳本。試試看:任意編輯計(jì)算機(jī)上的某個(gè)源代碼文件。加載后,運(yùn)行如下命令:


:scriptnames?


花點(diǎn)時(shí)間讀完整個(gè)清單。猜猜看這些腳本可能會(huì)做些什么,并記下它們所在的目錄。

清單比你預(yù)期的要長(zhǎng)嗎?如果你安裝了大量插件的話,那么編輯器需要做大量工作。你可以通過(guò)以下命令檢查是什么導(dǎo)致編輯器的速度變慢,然后再看看它創(chuàng)建的start.log:


vim?--startuptime?start.log?name-of-your-fileof-your-file


為了比較起見,下面我們看看如果沒(méi)有這些配置,Vim的啟動(dòng)速度有多快:


vim?--clean?--startuptime?clean.log?name-of-your-fileof-your-file


為了確定啟動(dòng)時(shí)或加載緩沖區(qū)時(shí)會(huì)運(yùn)行哪些腳本,Vim會(huì)遍歷“runtimepath”。該設(shè)置是一組以逗號(hào)分隔的目錄列表,各個(gè)目錄的結(jié)構(gòu)都是一致的。Vim會(huì)檢查每個(gè)目錄的結(jié)構(gòu),找到需要運(yùn)行的腳本,并按照目錄在列表中的順序一一處理。

運(yùn)行以下命令就可以檢查系統(tǒng)上的runtimepath:


:set?runtimepath


在我的系統(tǒng)上,runtimepath默認(rèn)包含以下目錄。并非所有這些都必須出現(xiàn)在文件系統(tǒng)中,但如果存在就會(huì)被使用。

  • ~/.vim

    主目錄,保存?zhèn)€人偏好的文件。

  • /usr/local/share/vim/vimfiles

    系統(tǒng)范圍的Vim目錄,保存由系統(tǒng)管理員決定的文件。

  • /usr/local/share/vim/vim81

    即$VIMRUNTIME,保存與Vim一起分發(fā)的文件。

  • /usr/local/share/vim/vimfiles/after

    系統(tǒng)范圍Vim目錄中的“after”目錄。系統(tǒng)管理員可以利用該目錄來(lái)覆蓋默認(rèn)設(shè)置,或添加新的設(shè)置。

  • ~/.vim/after

    主目錄中的“after”目錄。可以利用該目錄用個(gè)人偏好覆蓋默認(rèn)設(shè)置或系統(tǒng)設(shè)置,或添加新的設(shè)置。

這些目錄會(huì)按照順序處理,所以要說(shuō)“after”目錄有什么特別的話,那就是它位于列表末尾。實(shí)際上“after”并沒(méi)有什么特別之處。

在處理每個(gè)目錄時(shí),Vim都會(huì)查找具有特定名稱的子文件夾。如果想了解更多這方面的信息,請(qǐng)參閱:help runtimepath。下面我們只挑部分進(jìn)行說(shuō)明。

  • plugin/

    編輯任何類型的文件都會(huì)自動(dòng)加載的Vim腳本文件,稱為“全局插件”。

  • autoload/

    (不要與“插件”相混淆。)自動(dòng)加載中的腳本包含僅在其他腳本請(qǐng)求時(shí)加載的函數(shù)。

  • ftdetect/

    用于檢測(cè)文件類型的腳本。可以根據(jù)文件擴(kuò)展名、位置或內(nèi)部文件內(nèi)容決定文件類型。

  • ftplugin/

    編輯已知類型的文件時(shí)執(zhí)行的腳本。

  • compiler/

    定義如何運(yùn)行各種編譯器或格式化工具,以及如何解析其輸出。可以在多個(gè)ftplugins之間共享。且不會(huì)自動(dòng)執(zhí)行,必須通過(guò) :compiler 調(diào)用。

  • pack/

    Vim 8原生軟件包的目錄,它采用了“Pathogen”格式的包管理。原生的包管理系統(tǒng)不需要任何第三方代碼。

最后,通用的編輯器設(shè)置都會(huì)放到~/.vimrc中。你可以通過(guò)它來(lái)設(shè)置用于覆蓋特定文件類型的默認(rèn)值。有關(guān).vimrc設(shè)置的全面講解,請(qǐng)運(yùn)行 :options。


第三方插件


在Vim中,插件只是腳本,必須放在runtimepath中的正確位置才能執(zhí)行。從概念上講,插件的安裝非常簡(jiǎn)單:只需下載文件。問(wèn)題在于,很難刪除或更新某些插件,因?yàn)樗鼈兊淖幽夸浖尤氲搅藃untimepath中,很難判斷哪個(gè)插件負(fù)責(zé)哪些文件。

為了滿足這種需求,網(wǎng)上出現(xiàn)了很多插件管理器。最早在2003年就出現(xiàn)了Vim.org插件倉(cāng)庫(kù)。然而,直到2008年左右,插件管理器的概念才真正流行起來(lái)。

這些工具在Vim的runtimepath中添加了單獨(dú)的查檢目錄,并會(huì)為插件文檔編譯幫助標(biāo)簽。大多數(shù)插件管理器還可以從網(wǎng)上安裝和更新插件代碼,有的還支持并行更新,或者顯示彩色的進(jìn)度條。

以下是按時(shí)間順序整理的插件管理器。我按照每個(gè)插件最早和最新版本進(jìn)行了排序,如果找不到官方的發(fā)行版本,則根據(jù)最早和最后的提交日期排序。

  • 2006年3月- 2014年7月:Vimball(分發(fā)格式和關(guān)聯(lián)的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

  • 首先要注意,這些工具五花八門,其次通常每個(gè)工具在活躍大約四年后就會(huì)過(guò)時(shí)。

    最穩(wěn)定的管理插件的方法是使用Vim 8的內(nèi)置功能,該功能不需要第三方代碼。下面讓我們具體來(lái)看看這種方法。

    首先在運(yùn)行時(shí)目錄的pack目錄中創(chuàng)建兩個(gè)目錄opt和start。


    mkdir?-p?~/.vim/pack/foobar/{opt,start}/foobar/{opt,start}


    注意占位符 foobar。這個(gè)名稱完全取決于你。我們用它對(duì)包進(jìn)行分類。大多數(shù)人會(huì)把所有的插件都扔進(jìn)一個(gè)無(wú)意義的類別中,這樣完全沒(méi)問(wèn)題。你可以選擇自己喜歡的名稱,在本文中我選擇使用 foobar。理論上,你也可以創(chuàng)建多個(gè)類別,比如~/.vim/pack/navigation, ~/.vim/pack/linting等。請(qǐng)注意,Vim不會(huì)檢測(cè)類別之間的重復(fù),如果存在重復(fù),則會(huì)加載兩次。

    “start”中的包會(huì)自動(dòng)加載。而對(duì)于“opt”中的包,只有通過(guò):packadd命令特別請(qǐng)求,Vim才會(huì)加載。opt中適合保存不常用的軟件包,以及為保持Vim的快速啟動(dòng)不必要運(yùn)行的腳本。請(qǐng)注意,:packadd沒(méi)有相反的命令卸載包。

    在下述示例子中,我們將添加“ctrlp”模糊查找插件到opt目錄。下載最新版本的命令如下:


    curl?-L?https://github.com/kien/ctrlp.vim/archive/1.79.tar.gz?\????|?tar?zx?-C?~/.vim/pack/foobar/opt/github.com/kien/ctrlp.vim/archive/1.79.tar.gz?\
    ????|?tar?zx?-C?~/
    .vim/pack/foobar/opt


    該命令創(chuàng)建了 ~/.vim/pack/foobar/opt/ctrlp.vim-1.79 文件夾,現(xiàn)在這個(gè)包可以使用了。我們?cè)俅位氐絭im中,為這個(gè)新包創(chuàng)建一個(gè)幫助標(biāo)簽的索引:


    :helptags?~/.vim/pack/foobar/opt/ctrlp.vim-1.79/doc/.vim/pack/foobar/opt/ctrlp.vim-1.79/doc


    該命令會(huì)在包的doc目錄中創(chuàng)建了一個(gè)名叫”tags“的文件,這樣Vim的內(nèi)部幫助系統(tǒng)就可以使用它的內(nèi)容了。(或者你也可以在包加載之后運(yùn)行一次:helptags ALL,該命令會(huì)處理runtimepath下的所有文檔。)

    在需要使用包時(shí),只需加載它(Tab自動(dòng)補(bǔ)齊也可以用于插件名,所以不需要輸入全名):


    :packadd?ctrlp.vim-1.79ctrlp.vim-1.79


    packadd會(huì)把包的根目錄放到runtimepath中,然后運(yùn)行它的plugin和ftdetect腳本。在加載ctrlp之后,就可以按Ctrl-P來(lái)彈出模糊文件查找了。

    有些人喜歡將~/.vim目錄放到版本管理中,使用git submodules來(lái)管理每個(gè)包。而我喜歡簡(jiǎn)單地將包從tarball中解壓,然后用自己的代碼庫(kù)來(lái)管理。如果你使用成熟的包,那么更新不會(huì)太頻繁,加上腳本本身也很小,不會(huì)把git歷史弄得太亂。


    備份和undo


    根據(jù)不同的用戶設(shè)置,Vim可以防止四種類型的丟失:

  • 編輯過(guò)程中(兩次保存之間)崩潰。Vim會(huì)定期將未保存的修改寫入交換文件來(lái)防止這種情況。

  • 使用兩個(gè)Vim進(jìn)程編輯同一個(gè)文件,兩個(gè)進(jìn)程互相覆蓋。交換文件也可以防止這種情況。

  • 保存過(guò)程中崩潰,即在目標(biāo)文件已被截?cái)?#xff0c;新的內(nèi)容尚未完全寫入時(shí)崩潰。Vim可以通過(guò)“writebackup”來(lái)防止這種情況。為了實(shí)現(xiàn)該功能,Vim會(huì)首先將內(nèi)容寫入新的文件,寫入成功后與原始文件交換。但這個(gè)功能取決于“backupcopy”設(shè)置。

  • 已保存新文件,但想要找回原文件。Vim可以通過(guò)在寫入改變后保留原始文件的備份來(lái)防止這種情況。

  • 在介紹具體的設(shè)置之前,先來(lái)放松一下吧!下面是GitHub上人們對(duì)于vimrc的一些評(píng)論:

    • “不要?jiǎng)?chuàng)建交換文件。用版本控制管理就好。”

    • “素人才用備份。高手都用版本控制。”

    • “用版本控制就好!”

    • “版本控制都滿天飛了,就不要再用交換文件和備份了。”

    • “不要寫備份文件,版本控制就是很好的備份。”

    • “我其實(shí)從來(lái)沒(méi)用過(guò)VIM的備份文件……一直都在用版本控制。”

    • “反正大部分東西都保存在版本控制里。”

    • “禁用備份文件,因?yàn)榉凑阋驳糜冒姹究刂啤!?/span>

    • “版本控制已來(lái)到,git拯救全世界。”

    • “禁用交換文件和備份(永遠(yuǎn)使用版本控制!永遠(yuǎn)!)”

    • “關(guān)掉備份,我所有東西都用版本控制。”

    上面的評(píng)論反映出,大家只了解上述第四種情況(偶爾也會(huì)提及第三種情況),這些人傾向于把交換文件也禁用,這會(huì)讓Vim無(wú)法防止第一種和第二種情況。

    為了保證編輯更安全,我建議使用下述配置:


    "?Protect?changes?between?writes.?Default?values?of"?updatecount?(200?keystrokes)?and?updatetime"?(4?seconds)?are?fineset?swapfileset?directory^=~/.vim/swap//"?protect?against?crash-during-writeset?writebackup"?but?do?not?persist?backup?after?successful?writeset?nobackup"?use?rename-and-write-new?method?whenever?safeset?backupcopy=auto"?patch?required?to?honor?double?slash?at?endif?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?fileset?undofileset?undodir^=~/.vim/undo//
    "?(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//


    這些設(shè)置為寫入過(guò)程啟用了備份,但在成功寫入后不會(huì)保留備份,因?yàn)槲覀冇邪姹究刂啤W⒁饽阈枰猰kdir ~/.vim/{swap,undodir,backup},否則Vim會(huì)使用設(shè)置列表中的下一個(gè)可用的文件夾。你還應(yīng)該chmod這些文件夾來(lái)保證隱私,因?yàn)榻粨Q文件和undo歷史可能包含敏感信息。

    關(guān)于配置中的路徑,需要提及的一點(diǎn)是,它們末尾使用了雙斜線。這樣可以無(wú)歧義地表示不同目錄下同名文件的交換文件和備份文件。例如,/foo/bar文件的交換文件會(huì)保存在~/.vim/swap/%foo%bar.swp(斜線z轉(zhuǎn)義成百分號(hào))。Vim有一個(gè)bug,對(duì)于backupdir不會(huì)正確處理雙斜線寫法,該bug直到最近才修復(fù),而上述配置可以防止這個(gè)bug。

    我們還要求Vim持久保存每個(gè)文件的undo文件,這樣在退出Vim并重新編輯文件時(shí)依然可以使用undo。雖然有了交換文件,這樣做有點(diǎn)多余,但實(shí)際上undo文件是補(bǔ)充性質(zhì)的,因?yàn)樗鼉H在原文件被寫入時(shí)才寫入。(如果undo文件寫入太頻繁,那么可能在崩潰后無(wú)法匹配磁盤上文件的狀態(tài),所以Vim不這樣做。)

    說(shuō)起undo就不得不提起Vim會(huì)維持編輯歷史的整個(gè)樹形結(jié)構(gòu)。這意味著你可以做一個(gè)修改,undo之后,然后做另一個(gè)修改,這時(shí)所有三個(gè)狀態(tài)都可以被恢復(fù)。使用:undolist命令可以看到修改的時(shí)間和大小,但從該命令的結(jié)果很難想象整個(gè)樹形結(jié)構(gòu)。你可以遍歷列表中的特定修改,也可以用:earlier和:later命令加上一個(gè)時(shí)間參數(shù)(如5m)或保存次數(shù)參數(shù)(如3f)在時(shí)間軸上移動(dòng)。但是,遍歷undo樹最好使用插件——如undotree。

    啟用這些災(zāi)難恢復(fù)設(shè)置可以讓你安心地使用Vim。我曾經(jīng)在編輯過(guò)程中多次保存,或者每次離開電腦時(shí)也會(huì)保存,但現(xiàn)在我會(huì)幾個(gè)小時(shí)都不保存,因?yàn)槲抑澜粨Q文件在老老實(shí)實(shí)地干活。

    最后幾點(diǎn):要時(shí)刻關(guān)注這些災(zāi)難恢復(fù)文件,時(shí)間長(zhǎng)了它們可能會(huì)在.vim文件夾下越積越多,占用大量空間。另外,當(dāng)磁盤剩余空間很少,卻需要保存大文件時(shí),也許有必要設(shè)置nowritebackup,否則Vim必須臨時(shí)保存整個(gè)文件的副本。默認(rèn)設(shè)置下“backupskip”設(shè)置能夠禁用系統(tǒng)臨時(shí)目錄下的任何文件的備份。

    Vim的“patchmode”與備份有關(guān)。你可以在沒(méi)有被版本控制管理的目錄下使用該設(shè)置。例如,如果你想下載源代碼tar包,做一些修改然后通過(guò)郵件列表提交補(bǔ)丁,這一過(guò)程中不使用git。只需運(yùn)行:set patchmod=.orig,那么任何Vim寫入的文件“foo”就會(huì)備份成“foo.orig”。然后可以通過(guò)命令行比較.orig文件和新文件來(lái)創(chuàng)建補(bǔ)丁。


    包含和路徑


    絕大多數(shù)編程需要都允許你在一個(gè)文件中包含另一個(gè)模塊或文件。Vim通過(guò)path、include、suffixesadd和includeexpr配置項(xiàng)來(lái)了解如何跟蹤包含文件中的程序標(biāo)識(shí)符。標(biāo)識(shí)符搜索(參見:help include-search)是另一種使用ctags維持系統(tǒng)頭文件的標(biāo)簽文件的方式。

    C程序的默認(rèn)設(shè)置工作得很好。其他語(yǔ)言也同樣支持,但需要一些設(shè)置。這些設(shè)置超出了本文的范圍,可以參考:help include。

    如果一切配置正確,那么你可以在標(biāo)識(shí)符上按 [i 來(lái)顯示標(biāo)識(shí)符定義,或者在宏常量上按 [d 顯示宏定義。還有,在文件名上按 gf 可以搜索路徑并跳轉(zhuǎn)到相應(yīng)的文件。由于路徑也會(huì)影響 :find 命令,一些人傾向于在路徑中添加“**/*”或常用的目錄,把 :find 命令當(dāng)作簡(jiǎn)裝版的模糊查找使用。但這樣做會(huì)減慢標(biāo)識(shí)符搜索的速度,因?yàn)樗枰阉髋c標(biāo)識(shí)符搜索無(wú)關(guān)的目錄。

    不污染路徑而實(shí)現(xiàn)相同查找功能的方式之一就是建立一個(gè)映射。這樣只需按<Leader><space>(通常這兩個(gè)鍵就是反斜杠然后空格)然后輸入文件名,再使用Tab或Ctrl-D自動(dòng)完成來(lái)查找文件。


    "?fuzzy-find?litenmap?<Leader><space>?:e?./**/lite
    nmap?<Leader><space>?:e?./**/


    重申一下:路徑參數(shù)是為頭文件準(zhǔn)備的。如果你想看更多證據(jù),還可以用:checkpath命令顯示哪些路徑有效。加載一個(gè)C文件然后運(yùn)行:checkpath,它就會(huì)顯示那些當(dāng)前文件包含,卻找不到的文件名。帶感嘆號(hào)的 :checkpath! 可以顯示當(dāng)前文件包含的整個(gè)頭文件層次結(jié)構(gòu)。

    默認(rèn)情況下,路徑的值為“.,/usr/include,,”,意思是當(dāng)前目錄、/usr/include,然后是當(dāng)前活動(dòng)緩沖區(qū)的所有兄弟文件。目錄指定符和glob非常強(qiáng)大,詳情可以查看:help file-searching。

    我還在C ftplugin中(后文會(huì)多次提到它),讓路徑搜索包含了當(dāng)前項(xiàng)目的包含文件,如./src/include或./include。


    setlocal?path=.,,*/include/**3,./*/include/**3setlocal?path+=/usr/includepath=.,,*/include/**3,./*/include/**3
    setlocal?path+=/usr/include


    帶數(shù)字的 ** (如**3)指定子目錄搜索的深度。最好在這里指定深度,以免標(biāo)識(shí)符搜索鎖死。

    如果 :checkpath 指示出項(xiàng)目中找不到的文件,那么也可以考慮將下面這些模式添加到路徑中。當(dāng)然,這完全取決于你的系統(tǒng)。

    • 更多的系統(tǒng)包含文件:/usr/include/**4,/usr/local/include/**3

    • Homebrew庫(kù)的頭文件:/usr/local/Cellar/**2/include/**2

    • Macports庫(kù)的頭文件:/opt/local/include/**

    • OpenBSD庫(kù)的頭文件:/usr/local/lib/\*/include,/usr/X11R6/include/\*\*3

    另請(qǐng)參考::he [,:he gf,:he :find。


    編輯-編譯循環(huán)


    :make 命令會(huì)執(zhí)行用戶選擇的程序來(lái)構(gòu)建項(xiàng)目,然后將輸出收集到quickfix緩沖區(qū)中。quickfix記錄中的每一項(xiàng)都記錄了文件名、行號(hào)、列號(hào)、類型(警告或錯(cuò)誤)和消息。一種常見的使用方括號(hào)命令的映射方式如下,可以在quickfix項(xiàng)目中快速移動(dòng):


    "?quickfix?shortcutsnmap?]q?:cnext<cr>nmap?]Q?:clast<cr>nmap?[q?:cprev<cr>nmap?[Q?:cfirst<cr><cr>
    nmap?]Q?:clast<cr>
    nmap?[q?:cprev<cr>
    nmap?[Q?:cfirst<cr>


    如果在更新程序并重新編譯后,你想知道上次的消息,可以使用 :colder 命令(使用 :cnewer 返回)。如果需要查看有關(guān)當(dāng)前錯(cuò)誤的更多信息,可以使用 :cc ,然后用 :copen 命令查看完整的quickfix緩沖區(qū)。還可以使用 :cile、:caddfile 或 :cexpr 命令,無(wú)需運(yùn)行:make而自行填充quickfix緩沖區(qū)。

    Vim能夠利用指定的errorformat字符串解析編譯的輸出。errorformat是個(gè)類似scanf的轉(zhuǎn)義序列。例如,Vim的gcc設(shè)置($VIMRUNTIME/compiler/gcc.vim)中自帶了errorformat設(shè)置,但卻沒(méi)有包含clang編譯器的設(shè)置。于是我創(chuàng)建了下面的定義:


    "?formatting?variations?documented?at"?https://clang.llvm.org/docs/UsersManual.html#formatting-of-diagnostics""?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\?%fCompilerSet?makeprg=make//clang.llvm.org/docs/UsersManual.html#formatting-of-diagnostics
    "
    "
    ?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


    要激活該編譯器設(shè)置,只需運(yùn)行 :compiler clang。通常該命令在ftplugin文件中執(zhí)行。

    另一個(gè)例子是在文本文件上運(yùn)行GNU Diction來(lái)識(shí)別句子中用錯(cuò)的詞匯和短語(yǔ)。可以創(chuàng)建一個(gè)“編譯器”,名為diction.vim:


    CompilerSet?errorformat=%f:%l:\?%mCompilerSet?makeprg=diction\?-s\?%s\?%


    運(yùn)行 :compiler diction 之后,可以使用 :make 命令來(lái)運(yùn)行,并填充quickfix。最后,我在.vimrc中添加了一個(gè)映射來(lái)運(yùn)行make:


    "?real?makemap?<silent>?<F5>?:make<cr><cr><cr>"?GNUism,?for?building?recursivelymap?<silent>?<s-F5>?:make?-w<cr><cr><cr><silent>?<F5>?:make<cr><cr><cr>
    "?GNUism,?for?building?recursively
    map?<silent>?<s-F5>?:make?-w<cr><cr><cr>



    差異文件和補(bǔ)丁


    Vim自帶的比較工具非常強(qiáng)大,但可能有點(diǎn)難用,特別是三方合并視圖。但實(shí)際上花點(diǎn)時(shí)間學(xué)習(xí)你就會(huì)發(fā)現(xiàn)其實(shí)挺好用的。要點(diǎn)就是,每個(gè)窗口都可以處于或不處于“diff mode”。所有處于diffmode的窗口(用:difft[his]設(shè)置)會(huì)與所有其他已經(jīng)處于diffmode的窗口進(jìn)行比較。

    我們從一個(gè)簡(jiǎn)單的例子開始。首先創(chuàng)建兩個(gè)文件:


    echo?"hello,?world"?>?h1echo?"goodbye,?world"?>?h2vim?h1?h2"hello,?world"?>?h1
    echo?"goodbye,?world"?>?h2

    vim?h1?h2


    在vim中運(yùn)行 :all 命令,將上述參數(shù)指定的文件分別放入各自的窗口中。在上方的h1的窗口中運(yùn)行 :difft。你會(huì)看到出現(xiàn)了一個(gè)分割線,但沒(méi)有檢測(cè)到任何差異。用Ctrl-W Ctrl-W移動(dòng)到下方窗口,然后運(yùn)行 :difft。這時(shí)就會(huì)檢測(cè)出hello和goodbye之間的差異。在下方窗口中執(zhí)行 :diffg[et] 可以從上方窗口中拉取“hello”,或者使用 :diffp[ut] 將“goodbye”發(fā)送到上方窗口。如果有多個(gè)差異塊,那么按 ]c 或 [c 可以在不同的差異塊中移動(dòng)。

    快捷方式之一就是運(yùn)行 vim -d h1 h2 (或者運(yùn)行其別名 vimdiff h1 h2),該命令會(huì)對(duì)所有窗口執(zhí)行 :difft。此外,還可以先用vim h1僅加載h1,然后執(zhí)行 :diffsplit h2。記住,所有這些命令實(shí)際上都是將文件加載到窗口中并設(shè)置diffmode而已。

    了解這些基本知識(shí)后,我們來(lái)學(xué)習(xí)怎樣把Vim作為git的三方合并工具使用。首先配置git:


    git?config?merge.tool?vimdiffgit?config?merge.conflictstyle?diff3git?config?mergetool.prompt?false
    git?config?merge.conflictstyle?diff3
    git?config?mergetool.prompt?false


    現(xiàn)在,當(dāng)遇到合并沖突時(shí),只需運(yùn)行g(shù)it mergetool。該命令會(huì)啟動(dòng)Vim并打開四個(gè)窗口。這部分看上去很嚇人,我經(jīng)常會(huì)舉棋不定。


    +-----------+------------+------------+|???????????|????????????|????????????||???????????|????????????|????????????||???LOCAL???|????BASE????|???REMOTE???|+-----------+------------+------------+|?????????????????????????????????????||?????????????????????????????????????||?????????????(edit?me)???????????????|+-------------------------------------+|???????????|????????????|????????????|
    |???????????|????????????|????????????|
    |???LOCAL???|????BASE????|???REMOTE???|
    +-----------+------------+------------+
    |?????????????????????????????????????|
    |?????????????????????????????????????|
    |?????????????(edit?me)???????????????|
    +-------------------------------------+


    關(guān)鍵在于所有編輯都應(yīng)該在下方窗口中進(jìn)行。上方的三個(gè)窗口僅用于提供文件差異(local和remote)的上下文,以及每一方在修改之前的樣子(base)。

    使用 ]c 命令在下方窗口中移動(dòng),針對(duì)每個(gè)差異塊,可以選擇local、base或remote之一來(lái)替換,或者可以自己修改,合并多方的內(nèi)容。

    為了能夠更容易地從上方窗口拉取修改,我在vimrc里設(shè)置了一些映射:


    "?shortcuts?for?3-way?mergemap?<Leader>1?:diffget?LOCAL<CR>map?<Leader>2?:diffget?BASE<CR>map?<Leader>3?:diffget?REMOTE<CR><Leader>1?:diffget?LOCAL<CR>
    map?<Leader>2?:diffget?BASE<CR>
    map?<Leader>3?:diffget?REMOTE<CR>


    我們已經(jīng)介紹過(guò)了 :diffget,上述綁定會(huì)為其傳遞一個(gè)參數(shù),即用來(lái)識(shí)別拉取源的緩沖區(qū)名。

    合并結(jié)束后,執(zhí)行 :wqa 保存所有窗口并退出。如果你想放棄合并,可以運(yùn)行 :cq 放棄所有修改,給shell返回一個(gè)錯(cuò)誤代碼。該錯(cuò)誤代碼會(huì)告訴git應(yīng)當(dāng)忽略這些修改。

    diffget還可以接受范圍。如果想從某個(gè)上方窗口拉取所有差異塊,而不想逐個(gè)拉取,可以執(zhí)行 :1,$+1diffget {LOCAL,BASE,REMOTE} 。“+1”是必要的,因?yàn)榫彌_區(qū)的最后一行的“下方”可能存在被刪除的行。

    畢竟,三方合并其實(shí)很簡(jiǎn)單。至少,不需要用Fugitive之類的插件在合并沖突時(shí)顯示差異。

    最后,8.1.0360版本中包含了xdiff庫(kù),可以直接創(chuàng)建diff文件。這比使用外部程序更有效率,而且可以采用多種diff算法。“patience”算法通常可以生成比默認(rèn)設(shè)置更容易閱讀的輸出。在.vimrc中這樣設(shè)置:


    if?has("patch-8.1.0360")????set?diffopt+=internal,algorithm:patienceendifinternal,algorithm:patience
    endif


    緩沖區(qū)I/O?


    看看這是不是很熟悉?你編輯了一個(gè)緩沖區(qū),想把它保存成新文件,所以執(zhí)行了:w newname。再次進(jìn)行一些編輯后,執(zhí)行 :w ,但卻保存到了原始文件上。在這種情況下,你真正需要的是 :saveas newname,即寫入新文件,并將緩沖區(qū)的文件名改為新文件,方便以后的寫入。此外,:file newname命令可以改變緩沖區(qū)文件名,而不會(huì)執(zhí)行實(shí)際的寫入。

    學(xué)習(xí)更多有關(guān)讀寫命令的知識(shí)也很有用。因?yàn)閞和w都是ex的命令,所以它們都可以接受范圍。下面是一些你不太熟知的使用方法:

    • :w >> foo

      將整個(gè)緩沖區(qū)追加到文件中

    • :.w >> foo

      將當(dāng)前行追加到文件中

    • :$r foo

      讀取foo并插入到緩沖區(qū)末尾

    • :0r foo

      讀取foo并插入到開頭,已有行向下移動(dòng)

    • :.,$w foo

      將當(dāng)前行以及之后的所有行寫入文件

    • :r !ls

      讀取ls輸出到當(dāng)前光標(biāo)位置

    • :w !wc

      將緩沖區(qū)發(fā)送到wc命令然后顯示結(jié)果

    • :.!tr 'A-Za-z' 'N-ZA-Mn-za-m'

      為當(dāng)前行執(zhí)行ROT-13

    • :w | so %

      連鎖命令:寫入并執(zhí)行緩沖區(qū)

    • :e!

      放棄為保存到修改,重新加載緩沖區(qū)

    • :hide edit foo

      編輯foo,如果當(dāng)前緩沖區(qū)被修改過(guò),則隱藏

    冷知識(shí):上面的例子中使用一整行來(lái)調(diào)用 tr 以實(shí)現(xiàn)ROT-13加密,但實(shí)際上Vim內(nèi)置了該功能,即 g? 命令。可以將其應(yīng)用到移動(dòng)操作,如 g?$。


    filetypes


    filetypes設(shè)置可以根據(jù)緩沖區(qū)中檢測(cè)到到文件類型來(lái)改變?cè)O(shè)置。不過(guò)它們并不一定非要自動(dòng)檢測(cè),我們可以手動(dòng)啟用它們,實(shí)現(xiàn)一些有趣的效果。一個(gè)例子就是十六進(jìn)制編輯。任何文件都可以作為十六進(jìn)制值查看。GitHub用戶the9ball寫了一個(gè)非常聰明的ftplugin腳本,可以將緩沖區(qū)傳遞給xxd或傳回,實(shí)現(xiàn)十六進(jìn)制編輯。

    為了方便使用,Vim 5版本捆綁了xxd工具。Vim的todo.txt提到,他們想讓二進(jìn)制文件編輯功能更加順暢,但xxd已經(jīng)實(shí)現(xiàn)了不少功能。?

    將下面的代碼放到 ~/.vim/ftplugin/xxd.vim 中。保存到ftplugin中的意思是,每當(dāng)filetype(即“ft”)變成xxd時(shí),Vim就會(huì)執(zhí)行該腳本。我在腳本中添加了一些簡(jiǎn)單的注釋:


    "?without?the?xxd?command?this?is?all?pointlessif?!executable('xxd')????finishendif"?don't?insert?a?newline?in?the?final?line?if?it"?doesn't?already?exist,?and?don't?insert?linebreakssetlocal?binary?noendoflinesilent?%!xxd?-g?1%s/\r$//e"?put?the?autocmds?into?a?group?for?easy?removal?lateraugroup?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_cursoraugroup?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?valuelet?b:undo_ftplugin?=?'setl?bin<?eol<?|?execute?"au!?ftplugin-xxd?*?<buffer>"?|?execute?"silent?%!xxd?-r"'
    ????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"'


    打開一個(gè)文件,然后執(zhí)行 :set ft。記下文件類型。然后執(zhí)行 :set ft=xxd。Vim就會(huì)變成一個(gè)十六進(jìn)制編輯器。要恢復(fù)原來(lái)的視圖,只需 :set fo=foo,其中foo是原始的文件類型。注意十六進(jìn)制視圖甚至還有語(yǔ)法高亮,因?yàn)閂im默認(rèn)自帶了 $VIMRUNTIME/syntax/xxd.vim 。

    注意這里的“b:undo_ftplugin”非常巧妙,它可以在用戶或ftdetect機(jī)制將文件類型切換成其他filetype時(shí),讓filetypes執(zhí)行一些清理工作。(上面的例子還可以改進(jìn)一下,因?yàn)槿绻?:set ft=xxd 然后直接改回去,那么緩沖區(qū)會(huì)被標(biāo)記為已修改,即使你沒(méi)有進(jìn)行任何修改。)

    ftplugins還可以進(jìn)一步定義已知的filetype。例如,Vim已經(jīng)在 $VIMRUNTIME/ftplugin/c.vim 中為C語(yǔ)言包含了非常好的默認(rèn)設(shè)置。我在 ~/.vim/after/ftplugin/c.vim 中添加了額外的選項(xiàng):


    "?the?smartest?indent?engine?for?Csetlocal?cindent"?my?preferred?"Allman"?style?indentationsetlocal?cino="Ls,:0,l1,t0,(s,U1,W4""?for?quickfix?errorformatcompiler?clang"?shows?long?build?messages?bettersetlocal?ch=2"?auto-create?folds?per?grammarsetlocal?foldmethod=syntaxsetlocal?foldlevel=10"?local?project?headerssetlocal?path=.,,*/include/**3,./*/include/**3"?basic?system?headerssetlocal?path+=/usr/includesetlocal?tags=./tags,tags;~"??????????????????????^?in?working?dir,?or?parents"????????????????^?sibling?of?open?file"?the?default?is?menu,preview?but?the?preview?window?is?annoyingsetlocal?completeopt=menuiabbrev?#i?#includeiabbrev?#d?#defineiabbrev?main()?int?main(int?argc,?char?**argv)"?add?#include?guardiabbrev?#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"Allman"?style?indentation
    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”。它僅對(duì)當(dāng)前緩沖區(qū)生效,而不是對(duì)整個(gè)Vim進(jìn)程生效。

    該腳本還添加了一些縮寫。例如,我可以輸入 #g 并按回撤,就能自動(dòng)使用當(dāng)前文件名添加包含檢測(cè):


    #ifndef?_FILENAME_H#define?_FILENAME_H/*?<--?cursor?here?*/#endif
    #define?_FILENAME_H

    /*?<--?cursor?here?*/

    #endif


    你還可以使用點(diǎn)(“.”)來(lái)混合多種filetypes。下面是應(yīng)用的例子。不同的項(xiàng)目有不同的編碼規(guī)范,所以你可以將默認(rèn)的C設(shè)置與特定項(xiàng)目的設(shè)置結(jié)合起來(lái)。OpenBSD的源代碼遵循style(9)格式(https://man.openbsd.org/style.9),所以我們來(lái)做一個(gè)特殊的openbsd filetype。可以在相關(guān)文件上使用 :set ft=c.openbsd 將兩個(gè)filetype合并。

    要檢測(cè)openbsd filetype,可以查看緩沖區(qū)的內(nèi)容,而不僅僅是通過(guò)文件擴(kuò)展名或文件在磁盤上的位置。C文件中包含OpenBSD源代碼的標(biāo)志就是第一行出現(xiàn) /* $OpenBSD: 。

    創(chuàng)建 ~/.vim/after/ftdetect/openbsd.vim 進(jìn)行檢測(cè):


    augroup?filetypedetect????????au?BufRead,BufNewFile?*.[ch]????????????????\??if?getline(1)?=~?'OpenBSD;'????????????????\|???setl?ft=c.openbsd????????????????\|?endifaugroup?END
    ????????????????\??if?getline(1)?=~?'OpenBSD;'
    ????????????????\|???setl?ft=c.openbsd
    ????????????????\|
    ?endif
    augroup?END


    OpenBSD的Vim移植已經(jīng)包含了該filetype的特殊語(yǔ)法:/usr/local/share/vim/vimfiles/syntax/openbsd.vim。回憶一下,/usr/local/share/vim/vimfiles目錄位于runtimepath中,用于保存系統(tǒng)管理員提供的文件。該openbsd.vim腳本包含下面的函數(shù):


    function!?OpenBSD_Style()????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=80endfun
    ????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


    我們只需在適當(dāng)時(shí)候調(diào)用該函數(shù)。創(chuàng)建 ~/.vim/after/ftplugin/openbsd.vim:


    call?OpenBSD_Style()


    現(xiàn)在打開任何頂部具有標(biāo)志性注釋的C文件或頭文件,就會(huì)被識(shí)別為c.openbsd類型,從而采用style(9)手冊(cè)頁(yè)中規(guī)定的縮進(jìn)選項(xiàng)。


    別忘了鼠標(biāo)


    在此友好地提醒你,盡管我們都喜歡命令行,但實(shí)際上Vim也支持鼠標(biāo),而且有些任務(wù)比鍵盤更方便。由于xterm能夠?qū)⑹髽?biāo)事件轉(zhuǎn)換為stdin轉(zhuǎn)義代碼,所以我們甚至可以通過(guò)SSH都能支持鼠標(biāo)事件。

    如果想啟用鼠標(biāo)支持,則需要設(shè)置 mouse=n。許多人喜歡設(shè)置 mouse=a,因?yàn)檫@樣就可以在所有模式下工作,但我更喜歡只在普通模式下啟用鼠標(biāo)支持。這樣,在我用鍵盤加點(diǎn)擊的方式在瀏覽器中打開鏈接時(shí),就不會(huì)錯(cuò)誤地創(chuàng)建可視選擇區(qū)域。

    以下是鼠標(biāo)可以執(zhí)行的操作:

    • 打開或關(guān)閉折疊(當(dāng)foldcolumn> 0時(shí))。

    • 選擇標(biāo)簽(比 gt gt gt gt ...要好用得多)

    • 單擊完成動(dòng)作,例如 d<點(diǎn)擊>。類似于easymotion插件,但不需要任何插件。

    • 雙擊即可跳轉(zhuǎn)到幫助主題。

    • 拖動(dòng)底部的狀態(tài)行以更改cmdheight。

    • 拖動(dòng)窗口邊緣以調(diào)整大小。

    • 鼠標(biāo)滾輪。


    其他編輯功能


    這部分涉及的內(nèi)容很雜,但我僅在此介紹一些我學(xué)到的技巧。第一個(gè)讓我感到震驚的是::set virtualedit=all。它允許你將光標(biāo)移動(dòng)到窗口中的任何位置。如果你輸入字符或插入可視塊,Vim會(huì)在插入的字符的左側(cè)添加所需的空格以保證它們的位置。虛擬編輯模式可以簡(jiǎn)化表格數(shù)據(jù)的編輯。你可以通過(guò) :set virtualedit= 來(lái)關(guān)閉這個(gè)選項(xiàng)。

    接下來(lái)是一些移動(dòng)命令。在跳轉(zhuǎn)到下一段時(shí),我習(xí)慣于使用 } ,每次跳轉(zhuǎn)一個(gè)段落。然而, ] 字符可以完成更精準(zhǔn)的跳轉(zhuǎn):跳轉(zhuǎn)到下一個(gè)函數(shù) ]]、作用域 ]}、圓括號(hào) ‘])’、注釋 ]/、差異塊 ]c。前面提到的 quickfix 映射 ]q 也是這種操作方式之一。

    對(duì)于大段的跳轉(zhuǎn),我曾經(jīng)嘗試過(guò) 1000j 等操作,但實(shí)際上只需在普通模式下鍵入百分比,Vim就會(huì)跳轉(zhuǎn)到相應(yīng)的位置,比如50%。說(shuō)到滾動(dòng)百分比,你隨時(shí)可以使用CTRL-G查看它。所以現(xiàn)在我采用了 :set noruler 的設(shè)置,只在需要了解百分比的時(shí)候查看,這樣畫面就不會(huì)過(guò)于雜亂了。這似乎與色彩斑斕的powerlines的流行趨勢(shì)有點(diǎn)背道而馳。

    如果想在標(biāo)簽、文件或文件中跳轉(zhuǎn),那么有些命令可以幫助你。比如::ls、:tags、:jumps 和 :marks。在標(biāo)簽之間跳轉(zhuǎn)實(shí)際上會(huì)創(chuàng)建一個(gè)棧,你可以按CTRL-T跳到前一個(gè)。以前我經(jīng)常按CTRL-O退出跳轉(zhuǎn),但是它不如彈出標(biāo)簽棧那般直接。

    在使用ctags編制索引的項(xiàng)目目錄中,你可以使用 -t 選項(xiàng)在打開編輯器時(shí)直接跳到標(biāo)簽,比如:vim -t main。如果想更靈活地查找標(biāo)簽文件,那么可以設(shè)置 tags 配置變量。請(qǐng)注意如下示例中的分號(hào),有了它Vim就可以從當(dāng)前目錄向上搜索到主目錄。如此一來(lái),你就可以在項(xiàng)目文件夾外部使用更通用的系統(tǒng)標(biāo)記文件。


    set?tags=./tags,**5/tags,tags;~"??????????????????????????^?in?working?dir,?or?parents"???????????????????^?in?any?subfolder?of?working?dir"???????????^?sibling?of?open?file
    "??????????????????????????^?in?working?dir,?or?parents
    "
    ???????????????????^?in?any?subfolder?of?working?dir
    "???????????^?sibling?of?open?file


    此外,還有一些緩沖區(qū)技巧。切換緩沖區(qū)的命令 :bu 可以接受緩沖區(qū)名稱的片段作為參數(shù),而不僅僅是數(shù)字。有時(shí)很難記住這些數(shù)字,相比之下源文件的名稱更加方便記憶。你也可以使用標(biāo)記來(lái)瀏覽緩沖區(qū)。如果使用大寫字母作為標(biāo)記的名稱,則可以跨緩沖區(qū)跳轉(zhuǎn)到該標(biāo)記。你還可以在標(biāo)題中設(shè)置標(biāo)記H,在源文件中設(shè)置C,在Makefile中設(shè)置M,這樣就可以在緩沖區(qū)之間來(lái)回跳轉(zhuǎn)了。

    你有沒(méi)有遇到過(guò)這種情況:復(fù)制一個(gè)單詞,然后在其他地方刪掉一個(gè)單詞,當(dāng)嘗試粘貼第一個(gè)單詞時(shí),卻發(fā)現(xiàn)原來(lái)復(fù)制的單詞已被覆蓋。是不是很氣惱?Vim寄存器不善于處理這種情況。你可以用 :reg 檢查其內(nèi)容。當(dāng)你復(fù)制文本時(shí),先前的復(fù)制就會(huì)被輪換到寄存器"0 - "9。因此,"0p 會(huì)粘貼倒數(shù)第二個(gè)復(fù)制/刪除。特殊寄存器 "+ 和 "* 可以從系統(tǒng)剪貼板中復(fù)制/粘貼,也可以復(fù)制/粘貼到系統(tǒng)剪貼板。通常,這兩者的含義相同,除了在一些X11設(shè)置中會(huì)區(qū)分首選和備選。

    另一個(gè)非常方便的隱藏功能是命令行窗口。它是一個(gè)緩沖區(qū),其中包含了你以前運(yùn)行的命令和搜索。你可以通過(guò) q: 或 q/ 顯示該窗口。在進(jìn)入該緩沖區(qū)后,你可以隨意移動(dòng)到任何一行,然后按Enter鍵運(yùn)行該行的命令。然而,你也可以在按Enter鍵之前對(duì)行進(jìn)行編輯。你的更改不會(huì)影響該行(僅會(huì)將新的命令將添加到列表的底部)。

    vim的使用技巧繁多,文本無(wú)法詳盡闡述。如果你想了解更多信息,請(qǐng)參閱幫助文檔: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 翻譯,轉(zhuǎn)載請(qǐng)注明來(lái)源出處。

    (*本文為 AI科技大本營(yíng)轉(zhuǎn)載文章,轉(zhuǎn)載請(qǐng)聯(lián)系原作者)


    社群福利

    掃碼添加小助手,回復(fù):大會(huì),加入2019 AI開發(fā)者大會(huì)福利群,每周一、三、五更新技術(shù)福利,還有不定期的抽獎(jiǎng)活動(dòng)~


    精彩推薦




    60+技術(shù)大咖與你相約 2019 AI ProCon!大會(huì)早鳥票已售罄,優(yōu)惠票速搶進(jìn)行中......2019 AI開發(fā)者大會(huì)將于9月6日-7日在北京舉行,這一屆AI開發(fā)者大會(huì)有哪些亮點(diǎn)?一線公司的大牛們都在關(guān)注什么?AI行業(yè)的風(fēng)向是什么?2019 AI開發(fā)者大會(huì),傾聽大牛分享,聚焦技術(shù)實(shí)踐,和萬(wàn)千開發(fā)者共成長(zhǎng)。


    推薦閱讀

    • 自動(dòng)駕駛激蕩風(fēng)云錄:來(lái)自圈內(nèi)人的冷眼解讀

    • 不止最佳長(zhǎng)論文,騰訊AI在ACL上還有這些NLP成果

    • 認(rèn)知智能的突圍:NLP、知識(shí)圖譜是AI下一個(gè)“掘金地”?

    • 5G+AI重新定義生老病死

    • 干貨 | 20個(gè)Python教程,掌握時(shí)間序列的特征分析(附代碼)

    • 2019 年度程序員吸金榜:你排第幾?

    • 字節(jié)跳動(dòng)入局全網(wǎng)搜索;思科回應(yīng)中國(guó)區(qū)裁員;IntelliJ IDEA 新版發(fā)布!?| 極客頭條

    • 知名飲料制造商股價(jià)暴漲500%驚動(dòng)FBI,只因在名字中加入了"區(qū)塊鏈" ?


    你點(diǎn)的每個(gè)“在看”,我都認(rèn)真當(dāng)成了喜歡


    總結(jié)

    以上是生活随笔為你收集整理的Vim激荡30年发展史的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

    如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。