Neomake快速入门
先说说我想解决的问题是什么。
其实主要还是想提升Linux下编程的效率。因为我的工作环境是Ubuntu (Linux) + gcc +Makefile,所以之前一直以来都是Vim编辑文件,terminal底下编译(make)调试(gdb)。因为要从文件编辑器(Vim)跳出到终端,实际上是做了两次上下文切换,没有IDE那样可以very focus。于是想到,可否通过Vim(或者NeoVim)插件来做到编译和调试。结论是可以的,只是工作的不是那么完美。
Vim GDB也有很多选择,目前我选择的是sakhnik/nvim-gdb。使用体验还不错,配合nvim,可以做到vim下进行GDB调试。虽然有bug,但还好不太影响正常使用。
Vim make相信其实也是比较普通的需求了,可以通过:make + makeprg参数来解决。缺点是不能异步执行,如果是一个大项目,就只能在那儿等着了。后来翻到"著名"的Neomake。网上搜了一下,发现虽然口碑不错,但真正用的人似乎不多,所以我用了个双引号。尤其用Neomake来编译C程序的就更凤毛麟角了。
因为没有什么快速入门的文档,所以打算写这篇博文,一方面记录自己的心得,便于以后回顾,另一方面帮助需要的人。
简单介绍
按照作者的说法,Neomake的诞生只是为了证明Vim的老牌lint插件syntastic可以异步执行。
Its origin is a proof-of-concept for Syntastic to be asynchronous.
不过现在的Neomake能做的显然比syntax checking(也就是所谓的linting)要多。起码我想用的功能就不是syntax checking。从我对Neomake文档的理解,起码有以下两大功能:
1.Syntax checking,支持各种语言,前提是指定对应的lint工具
2.automake,自动编译,其实还是目标做lint工具
3.异步make,用quickfix,location window过滤错误信息
前两者对我毫无吸引力,因为自动编译耗时耗资源,没什么意义。况且因为工作语言是C语言,这种强类型语言,编译设置太麻烦,所以没有lint的价值。
为什么要用第3条,原因是,平时用vim或nvim编辑文件,然后切到终端编译调试,上下文切换太多,感觉没有达到极致效率。当然如果能用上IDE,这一切都不是问题。IDE需要的是图形界面,工作环境里没有,否则用VS code或JetBrain家族的CLion也是个不错的选择
Neomake使用简单介绍
Neomake提供了2个基本命令来执行所谓的make操作。
第一种就是最简单默认的Neomake,它是针对文件的。主要的用途就是做lint检查,也就是很多IDE都提供的语法检查或者叫Instant build。
另一种是Neomake!,带个感叹号,它是针对项目的,即当前的工作目录(也可以通过maker的选项设置)。主要用途就是是编译,在设置好的目录下进行编译。所有的设置都是由maker提供,后文详述。
Neomake还提供了其他的功能,例如NeomakeSh来运行shell命令等等,本文不作讨论。
下面是运行Neomake做lint的界面:
automake
Neomake的automake和IDE的syntax checking非常相似。可以自动触发语法检查。但是对于C/C++这种强类型语言似乎不太友好。因为要特别的去设置头文件搜索路径,否则永远都是头文件找不到的错误。不过看起来这个功能还是挺高级的,而且似乎并不很影响性能。试试:help neomake-automake
了解更多详细内容。
简单来说automake可以根据不同的模式设置编译延时时间,例如:
" When writing a buffer.
call neomake#configure#automake('w')
" When writing a buffer, and on normal mode changes (after 750ms).
call neomake#configure#automake('nw', 750)
" When reading a buffer (after 1s), and when writing.
call neomake#configure#automake('rw', 1000)
这样设置的目的是为了少做无意义的编译。当然你还可以在vimrc中做更多的酷炫的配置。
但其实我想分享的是disable automake的方法,可能因为它对C/C++不太友好吧。有两种方法可以禁止:
- :NeomakeDisable/Toggle
- call neomake#configure#disable_automake()
第二种方法是从Neomake源码中搜索出来的,不知道效果如何。
Maker
maker定义了Neomake如何来lint或compile。其实就是定义了具体的命令行,以及需要识别的error format。
A maker is an object that tells Neomake how to run a job for you.
通常使用maker的方法是调用下面的Vim命令
:Neomake <maker name>
当不传入maker name的时候,Neomake会使用系统默认的maker,即makeprg maker。看,按照作者的推荐,如果只是想异步地执行make,只要配置好makeprg和error format就已经可以用了。
If you just want an easy way to run |:make| asynchronously, you're all set.
Just set your |'makeprg'| and |'errorformat'| as usual, and run |:Neomake!|.
自定义maker
定义一个最简单的maker如下:
let g:neomake_make_maker = {
\ 'exe': 'make',
\ 'args': ['--build'],
\ 'errorformat': '%f:%l:%c: %m',
\ }
" Use the maker like this:
:Neomake! make
这几个参数还是比较直接的。具体也可以参考:help neomake
。对于makeprg maker,exe就是make,args就是makeprg指定的参数,而error format就是Vim默认的error format,可以通过命令set efm=xxx
来设置。error format那些神奇的占位符可以参考:help errorformat
帮助文档。
*注:当前目录下的.clang文件会影响到clang maker的参数。例如在.clang里写上
{
"c": "c11",
"cpp": "c++1z",
"objc": "c11",
"objcpp": "c++1z",
}
Neomake的参数会多出下面这一溜:
Define a new filetype or project-level maker
g:neomake_<name>_maker
g:neomake_<ft>_<name>_maker
" <ft> = filetype, 例如定义一个C语言的maker
g:neomake_c_clang_maker = {...}
Configure properties for a maker
g:neomake_<name>_<property>
g:neomake_<ft>_<name>_<property>
b:neomake_<name>_<property>
b:neomake_<ft>_<name>_<property>
" g: 表示全局变量, b: 表示buffer级变量
where <property> is one of 'exe', 'args', 'errorformat', 'buffer_output', 'remove_invalid_entries', 'append_file', or 'supports_stdin'
.
Eabled makers
" This setting will tell Neomake which makers to use by default for the given filetype `<ft>` (when called without a maker as an argument,
g:neomake_enabled_makers
b:neomake_enabled_makers
" This setting will tell Neomake which makers to use by default for the given filetype `<ft>` (when called without a maker as an argument,
g:neomake_<ft>_enabled_makers
b:neomake_<ft>_enabled_makers
通过查看上面的全局变量可以了解到当前使能的一些maker。所有语言相关的maker都定义在neomake/neomake/autoload/neomake/makers/
中。例如neomake/neomake/autoload/neomake/makers/ft/c.vim
, neomake/neomake/autoload/neomake/makers/ft/cpp.vim
等等。
当用户调用Neomake并传入maker名时,则直接使用对应的maker来编译。如果未传入maker名,则根据以上使能的maker来调用对应的maker编译。
当使用:Neomake
时:
- 首先尝试使用
g:neomake_<ft>_enabled_makers
,<ft>
为当前buffer的filetype - 若当前没有为
<ft>
定义的maker,则使用g:neomake_enabled_makers
定义的maker
当使用:Neomake!
时,则直接使用g:neomake_enabled_makers
定义的maker
有用的配置
Neomake的所有信息都在它的帮助文档里,用:help neomake
命令来查看吧。也可以看网页版。
下面摘录一些我通读过neomake帮助文档后,认为有用的一些信息,以飨读者。
neomake-args-file
When running a maker on a file with |:Neomake|, you may want to control where in the args
list the file's path will appear. To do this, insert '%t' in the args
list and use append_file=0
:
let g:neomake_c_lint_maker = {
\ 'exe': 'lint',
\ 'args': ['%t', '--option', 'x'],
\ 'append_file': 0,
\ 'errorformat': '%f:%l:%c: %m',
\ }
This will cause "lint /path/to/file.c --option x" to be run instead of
"lint --option x /path/to/file.c".
%t
gets replaced with the absolute path to the file (handling any temporary
file).
neomake-makers-remove_invalid_entries
Default: 0
This option filters invalid entries from makers from the location/quickfix
list (i.e. entries that do not match the |'errorformat'|, and that would show
up with a ||
prefix in the location/quickfix list):
let g:neomake_ft_maker_remove_invalid_entries = 1
NOTE: the default for this is 0, because unhandled/unexpected output might be
useful, e.g. when the program displays some error.
Makers should handle this properly through |errorformat|, e.g. by using '%-G'(see |efm-ignore| and |neomake-faq-errorformat|).
* 注: 事实证明有时候啥都没有,是因为error format没设置好
neomake-makers-cwd
The working directory of a maker defaults to the current working directory of the make run (|getcwd()|).
The cwd
property overrides this, and gets expanded in the context of the current buffer. Special buffers (like fugitive blobs) get handled for values starting with %:
(typically used in this context), falling back to |expand()|. See |filename-modifiers|.
Example: change to the buffer's directory:
let g:neomake_my_example_maker = {
\ 'exe': 'pwd',
\ 'cwd': '%:p:h'
\ }
neomake_open_list
g:neomake_open_list
This setting will open the |location-list| or |quickfix| list (depending on whether it is operating on a file) when adding entries. A value of 2 will preserve the cursor position when the |location-list| or |quickfix| window is opened. Defaults to 0.
* 解释:当有新entry来的时候是否打开quickfix或location-list,取值为2时,表示打开,并且能keep原先的位置。显然选2比较好
neomake_virtualtext_current_error
g:neomake_virtualtext_current_error
选择是否显示virtual text,显然要显示啊,多么酷炫。
neomake log
g:neomake_verbose
0 - Errors only
1 - Quiet message
2 - Loud message (may log multiple messages at once, making the screen shift momentarily)
3 - Debug information (all messages). This will also add time information to messages.
这个在使用Neomake时毫无反应时,特别有用。开启log再用:messages
命令查看就可以知道发生了什么。
总结
Neomake是个异步编译不错的选择,可以考虑取代vim自带的make + makeprg,并做到异步编译。不过前提是还是要好好了解一下error format,这其中对错误处的跳转和显示有着决定性的作用。