作者 | 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告诉你究竟加载了哪些脚本。试试看:任意编辑计算机上的某个源代码文件。加载后,运行如下命令:

:scriptnames 

花点时间读完整个清单。猜猜看这些脚本可能会做些什么,并记下它们所在的目录。

清单比你预期的要长吗?如果你安装了大量插件的话,那么编辑器需要做大量工作。你可以通过以下命令检查是什么导致编辑器的速度变慢,然后再看看它创建的start.log:

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

为了比较起见,下面我们看看如果没有这些配置,Vim的启动速度有多快:

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

为了确定启动时或加载缓冲区时会运行哪些脚本,Vim会遍历“runtimepath”。该设置是一组以逗号分隔的目录列表,各个目录的结构都是一致的。Vim会检查每个目录的结构,找到需要运行的脚本,并按照目录在列表中的顺序一一处理。

运行以下命令就可以检查系统上的runtimepath:

:set 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/

    用于检测文件类型的脚本。可以根据文件扩展名、位置或内部文件内容决定文件类型。

  • ftplugin/

    编辑已知类型的文件时执行的脚本。

  • compiler/

    定义如何运行各种编译器或格式化工具,以及如何解析其输出。可以在多个ftplugins之间共享。且不会自动执行,必须通过 :compiler 调用。

  • pack/

    Vim 8原生软件包的目录,它采用了“Pathogen”格式的包管理。原生的包管理系统不需要任何第三方代码。

最后,通用的编辑器设置都会放到~/.vimrc中。你可以通过它来设置用于覆盖特定文件类型的默认值。有关.vimrc设置的全面讲解,请运行 :options。

第三方插件

在Vim中,插件只是脚本,必须放在runtimepath中的正确位置才能执行。从概念上讲,插件的安装非常简单:只需下载文件。问题在于,很难删除或更新某些插件,因为它们的子目录加入到了runtimepath中,很难判断哪个插件负责哪些文件。

为了满足这种需求,网上出现了很多插件管理器。最早在2003年就出现了Vim.org插件仓库。然而,直到2008年左右,插件管理器的概念才真正流行起来。

这些工具在Vim的runtimepath中添加了单独的查检目录,并会为插件文档编译帮助标签。大多数插件管理器还可以从网上安装和更新插件代码,有的还支持并行更新,或者显示彩色的进度条。

以下是按时间顺序整理的插件管理器。我按照每个插件最早和最新版本进行了排序,如果找不到官方的发行版本,则根据最早和最后的提交日期排序。

  1. 2006年3月- 2014年7月:Vimball(分发格式和关联的Vim命令)

  2. 2008年10月- 2015年12月:Pathogen(由于原生vim包被弃用)

  3. 2009年8月- 2009年12月:Vimana

  4. 2009年12月- 2014年12月:VAM

  5. 2010年8月 - 2010年12月:Jolt

  6. 2010年10月 - 2012年12月:tplugin

  7. 2010年10月 - 2014年2月:Vundle(在NeoBundle破解代码后停止使用)

  8. 2012年3月 - 2018年3月:vim-flavor

  9. 2012年4月 - 2016年3月:NeoBundle(被弃用,建议使用dein)

  10. 2013年1月 - 2017年8月:infect

  11. 2013年2月 - 2016年8月:vimogen

  12. 2013年10月 - 2015年1月:vim-unbundle

  13. 2013年12月 - 2015年7月:Vizardry

  14. 2014年2月 - 2018年10月:vim-plug

  15. 2015年1月 - 2015年10月:enabler

  16. 2015年8月 - 2016年4月:Vizardry 2

  17. 2016年1月 - 2018年6月:dein.vim

  18. 2016年9月 - 至今:原生Vim 8

  19. 2017年2月 - 2018年9月:minpac

  20. 2018年3月 - 2018年3月:autopac

  21. 2017年2月 - 2018年6月:pack

  22. 2017年3月 - 2017年9月:vim-pck

  23. 2017年9月 - 2017年9月:vim8-pack

  24. 2017年9月 - 2019年5月:volt

  25. 2018年9月 - 2019年2月:vim-packager

  26. 2019年2月 - 2019年2月:plugpac.vim

首先要注意,这些工具五花八门,其次通常每个工具在活跃大约四年后就会过时。

最稳定的管理插件的方法是使用Vim 8的内置功能,该功能不需要第三方代码。下面让我们具体来看看这种方法。

首先在运行时目录的pack目录中创建两个目录opt和start。

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

注意占位符 foobar。这个名称完全取决于你。我们用它对包进行分类。大多数人会把所有的插件都扔进一个无意义的类别中,这样完全没问题。你可以选择自己喜欢的名称,在本文中我选择使用 foobar。理论上,你也可以创建多个类别,比如~/.vim/pack/navigation, ~/.vim/pack/linting等。请注意,Vim不会检测类别之间的重复,如果存在重复,则会加载两次。

“start”中的包会自动加载。而对于“opt”中的包,只有通过:packadd命令特别请求,Vim才会加载。opt中适合保存不常用的软件包,以及为保持Vim的快速启动不必要运行的脚本。请注意,:packadd没有相反的命令卸载包。

在下述示例子中,我们将添加“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

该命令创建了 ~/.vim/pack/foobar/opt/ctrlp.vim-1.79 文件夹,现在这个包可以使用了。我们再次回到vim中,为这个新包创建一个帮助标签的索引:

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

该命令会在包的doc目录中创建了一个名叫”tags“的文件,这样Vim的内部帮助系统就可以使用它的内容了。(或者你也可以在包加载之后运行一次:helptags ALL,该命令会处理runtimepath下的所有文档。)

在需要使用包时,只需加载它(Tab自动补齐也可以用于插件名,所以不需要输入全名):

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

packadd会把包的根目录放到runtimepath中,然后运行它的plugin和ftdetect脚本。在加载ctrlp之后,就可以按Ctrl-P来弹出模糊文件查找了。

有些人喜欢将~/.vim目录放到版本管理中,使用git submodules来管理每个包。而我喜欢简单地将包从tarball中解压,然后用自己的代码库来管理。如果你使用成熟的包,那么更新不会太频繁,加上脚本本身也很小,不会把git历史弄得太乱。

备份和undo

根据不同的用户设置,Vim可以防止四种类型的丢失:

  1. 编辑过程中(两次保存之间)崩溃。Vim会定期将未保存的修改写入交换文件来防止这种情况。

  2. 使用两个Vim进程编辑同一个文件,两个进程互相覆盖。交换文件也可以防止这种情况。

  3. 保存过程中崩溃,即在目标文件已被截断,新的内容尚未完全写入时崩溃。Vim可以通过“writebackup”来防止这种情况。为了实现该功能,Vim会首先将内容写入新的文件,写入成功后与原始文件交换。但这个功能取决于“backupcopy”设置。

  4. 已保存新文件,但想要找回原文件。Vim可以通过在写入改变后保留原始文件的备份来防止这种情况。

在介绍具体的设置之前,先来放松一下吧!下面是GitHub上人们对于vimrc的一些评论:

  • “不要创建交换文件。用版本控制管理就好。”

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

  • “用版本控制就好!”

  • “版本控制都满天飞了,就不要再用交换文件和备份了。”

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

  • “我其实从来没用过VIM的备份文件……一直都在用版本控制。”

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

  • “禁用备份文件,因为反正你也得用版本控制。”

  • “版本控制已来到,git拯救全世界。”

  • “禁用交换文件和备份(永远使用版本控制!永远!)”

  • “关掉备份,我所有东西都用版本控制。”

上面的评论反映出,大家只了解上述第四种情况(偶尔也会提及第三种情况),这些人倾向于把交换文件也禁用,这会让Vim无法防止第一种和第二种情况。

为了保证编辑更安全,我建议使用下述配置:

" 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 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//

这些设置为写入过程启用了备份,但在成功写入后不会保留备份,因为我们有版本控制。注意你需要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自动完成来查找文件。

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

重申一下:路径参数是为头文件准备的。如果你想看更多证据,还可以用:checkpath命令显示哪些路径有效。加载一个C文件然后运行:checkpath,它就会显示那些当前文件包含,却找不到的文件名。带感叹号的 :checkpath! 可以显示当前文件包含的整个头文件层次结构。

默认情况下,路径的值为“.,/usr/include,,”,意思是当前目录、/usr/include,然后是当前活动缓冲区的所有兄弟文件。目录指定符和glob非常强大,详情可以查看:help file-searching。

我还在C ftplugin中(后文会多次提到它),让路径搜索包含了当前项目的包含文件,如./src/include或./include。

setlocal path=.,,*/include/**3,./*/include/**3setlocal path+=/usr/includepath=.,,*/include/**3,./*/include/**3setlocal 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项目中快速移动:

" 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 返回)。如果需要查看有关当前错误的更多信息,可以使用 :cc ,然后用 :copen 命令查看完整的quickfix缓冲区。还可以使用 :cile、:caddfile 或 :cexpr 命令,无需运行:make而自行填充quickfix缓冲区。

Vim能够利用指定的errorformat字符串解析编译的输出。errorformat是个类似scanf的转义序列。例如,Vim的gcc设置($VIMRUNTIME/compiler/gcc.vim)中自带了errorformat设置,但却没有包含clang编译器的设置。于是我创建了下面的定义:

" 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

要激活该编译器设置,只需运行 :compiler clang。通常该命令在ftplugin文件中执行。

另一个例子是在文本文件上运行GNU Diction来识别句子中用错的词汇和短语。可以创建一个“编译器”,名为diction.vim:

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

运行 :compiler diction 之后,可以使用 :make 命令来运行,并填充quickfix。最后,我在.vimrc中添加了一个映射来运行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 recursivelymap <silent> <s-F5> :make -w<cr><cr><cr>

差异文件和补丁

Vim自带的比较工具非常强大,但可能有点难用,特别是三方合并视图。但实际上花点时间学习你就会发现其实挺好用的。要点就是,每个窗口都可以处于或不处于“diff mode”。所有处于diffmode的窗口(用:difft[his]设置)会与所有其他已经处于diffmode的窗口进行比较。

我们从一个简单的例子开始。首先创建两个文件:

echo "hello, world" > h1echo "goodbye, world" > h2vim h1 h2"hello, world" > h1echo "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.tool vimdiffgit config merge.conflictstyle diff3git config mergetool.prompt falsegit config merge.conflictstyle diff3git config mergetool.prompt false

现在,当遇到合并冲突时,只需运行git mergetool。该命令会启动Vim并打开四个窗口。这部分看上去很吓人,我经常会举棋不定。

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

关键在于所有编辑都应该在下方窗口中进行。上方的三个窗口仅用于提供文件差异(local和remote)的上下文,以及每一方在修改之前的样子(base)。

使用 ]c 命令在下方窗口中移动,针对每个差异块,可以选择local、base或remote之一来替换,或者可以自己修改,合并多方的内容。

为了能够更容易地从上方窗口拉取修改,我在vimrc里设置了一些映射:

" 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>

我们已经介绍过了 :diffget,上述绑定会为其传递一个参数,即用来识别拉取源的缓冲区名。

合并结束后,执行 :wqa 保存所有窗口并退出。如果你想放弃合并,可以运行 :cq 放弃所有修改,给shell返回一个错误代码。该错误代码会告诉git应当忽略这些修改。

diffget还可以接受范围。如果想从某个上方窗口拉取所有差异块,而不想逐个拉取,可以执行 :1,$+1diffget {LOCAL,BASE,REMOTE} 。“+1”是必要的,因为缓冲区的最后一行的“下方”可能存在被删除的行。

毕竟,三方合并其实很简单。至少,不需要用Fugitive之类的插件在合并冲突时显示差异。

最后,8.1.0360版本中包含了xdiff库,可以直接创建diff文件。这比使用外部程序更有效率,而且可以采用多种diff算法。“patience”算法通常可以生成比默认设置更容易阅读的输出。在.vimrc中这样设置:

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

缓冲区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就会执行该脚本。我在脚本中添加了一些简单的注释:

" 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"'    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"'

打开一个文件,然后执行 :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 中添加了额外的选项:

" 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 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/include

setlocal tags=./tags,tags;~"                      ^ in working dir, or parents"                ^ sibling of open file

" the default is menu,preview but the preview window is annoyingsetlocal completeopt=menu

iabbrev #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

注意上述脚本使用了“setlocal”而不是“set”。它仅对当前缓冲区生效,而不是对整个Vim进程生效。

该脚本还添加了一些缩写。例如,我可以输入 #g 并按回撤,就能自动使用当前文件名添加包含检测:

#ifndef _FILENAME_H#define _FILENAME_H/* <-- cursor here */#endif#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 进行检测:

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

OpenBSD的Vim移植已经包含了该filetype的特殊语法:/usr/local/share/vim/vimfiles/syntax/openbsd.vim。回忆一下,/usr/local/share/vim/vimfiles目录位于runtimepath中,用于保存系统管理员提供的文件。该openbsd.vim脚本包含下面的函数:

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=80endfun

我们只需在适当时候调用该函数。创建 ~/.vim/after/ftplugin/openbsd.vim:

call OpenBSD_Style()

现在打开任何顶部具有标志性注释的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就可以从当前目录向上搜索到主目录。如此一来,你就可以在项目文件夹外部使用更通用的系统标记文件。

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

此外,还有一些缓冲区技巧。切换缓冲区的命令 :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年发展史相关推荐

  1. Vim 激荡 30 年发展史

    众所周知,Vim 是从 vi 发展出来的一个文本编辑器.其拥有代码补全.编译及错误跳转等丰富的功能特性,在程序员群体中广受欢迎. 本文是作者 Joe Nelson 从头到尾阅读 Vim 用户手册以及追 ...

  2. 开源激荡 30 年:从免费社区到价值数十亿美元公司

    开源起始于边缘活动,活跃于社区,30 年来一路进化,无数的企业在开源项目的基础上拔地而起,今天,开源商业已经迎来了最好的发展机会. 演讲 | Peter Levine,A16Z Partner Edi ...

  3. 手机基带芯片激荡 30 年!

    [CSDN编者按]从1G到5G,芯片一直是经久不衰的话题.而回望2G-3G时代手机芯片竞争之激烈,绝不亚于今日"5G芯片之战".从摩托罗拉.爱立信.诺基亚和西门子的群雄争霸,到诺基 ...

  4. 开发小程序遇协同、平台兼容难题,该如何破局?

    受访者 | 薛端阳 采访者 | 伍杏玲 出品 | CSDN(ID:CSDNnews) 2017 年 1 月 9 日,微信小程序诞生,自此我们从移动互联网时代踏入小程序时代.目前各大巨头公司正在紧锣密鼓 ...

  5. 汲取 IE6、IE8 消亡的经验,如何“杀死” IE11?

    我们大家熟悉的 IE 浏览器经过更新换代,目前已经更新到 IE11,而程序员多年唠叨的"IE 必须死"如今似乎要成为现实了.本文将回顾 IE6 和 IE8 消亡的历史,预测如何更好 ...

  6. 即使在微软 Azure 上,Linux 也大有一统天下之势!

    [CSDN编者按]微软曾一度视Linux之父Linus为眼中钉,Linux从一开始就开源.而微软在开源的大门外徘徊多年,终于叩开开源之门.但是即便如此,微软的Azure相比Linux仍有一定的差距!今 ...

  7. Linux命令技巧之30个必会的命令技巧

    在Unix/Linux下,高效工作方式不是操作图形页面,而是命令行操作,命令行意味着更容易自动化.使用过Linux系统的朋友应该都知道它的命令行强大之处.本文讲述了Linux下的查找,删除,打包,解压 ...

  8. php vimrc配置文件,vim技巧:我的 .vimrc 配置文件,详解每一个配置项的作用

    下面是我的 .vimrc 配置文件,每一个配置项都添加了注释说明,详解每一个配置项的作用,以便确认为什么要添加这个配置项. " 使用vim的modeline来设置当前文件的textwidth ...

  9. vim 安装_vim实战:插件安装(Vundle,NerdTree)

    一:插件管理器Vundle 1.简介 Vundle是vim的一个插件管理器, 同时它本身也是vim的一个插件.插件管理器用于方便.快速的安装.删除.Vim更新插件.vim Vundle插件官方地址:h ...

  10. 分享我的vim配置文件

    1.vim的配置文件/ect/vimrc vim支持自己设计风格和功能,他的配置文件为/ect/vimrc,可以根据自己的编码风格设计vim,包括快捷键,高亮,对齐方式等等,我把我配置的vim分享一下 ...

最新文章

  1. 开发笔记13 | 部署 Node.js 应用程序到云 ECS
  2. vue router按需加载
  3. VS C++调用python进行画图matplotlib
  4. how to learn
  5. ios中播放gif动画
  6. 和功率的计算公式_电机功率计算公式是什么?
  7. Python 让所有奇数都在偶数前面,而且奇数升序排列,偶数降序排序
  8. vuex Payload 荷载
  9. C风格字符串与C++风格字符串
  10. 【Python 标准库学习】数据科学计算库 — math
  11. 【AI视野·今日NLP 自然语言处理论文速览 第二十四期】Thu, 30 Sep 2021
  12. “现男友”来了!荣耀手机正式官宣
  13. PeekMessage抓取消息,如何把每个消息都获取到呢?
  14. 各种无线传输协议汇总(一)- Bluetooth
  15. php 499状态如何处理,HTTP499状态码 nginx下499错误及其解决方法
  16. win11蓝牙无法使用 Windows11蓝牙无法使用的解决方法
  17. 【往届会议已EI检索】第六届管理工程、软件工程与服务科学国际会议
  18. 人头识别与计数_目标检测之人头检测(HaarLike Adaboost)---高密度环境下行人检测和统计...
  19. python画一个点_python中画散点图
  20. 思岚科技 引领高性价比激光雷达未来

热门文章

  1. 上海蓝光集团公司信息化建设规划方案
  2. swift 极光推送
  3. 微信小程序教程、微信小程序开发资源下载汇总
  4. 乐优商城(04)--商品规格
  5. taobao API open淘宝 错误码一览表
  6. 基于opencv,C++实现中值滤波器
  7. W5500以太网控制器芯片(五):实现FTP客户端
  8. c语言程序设计教程二进制是什么,C语言程序设计教程
  9. php仿u8系统模板_用友U8:项目型制造企业的信息化模板
  10. 利用哈夫曼编码英文字母表