快速修复
Nvim 的 :help
页面,由 生成 自 源代码 使用 tree-sitter-vimdoc 解析器。
Vim 有一种特殊模式,可以加快编辑-编译-编辑循环。这灵感来自 Amiga 上 Manx 的 Aztec C 编译器的快速修复选项。其思路是将编译器的错误消息保存到一个文件中,并使用 Vim 一次性跳到错误位置。您可以检查每个问题并修复它,而无需记住所有错误消息。
在 Vim 中,快速修复命令更广泛地用于查找文件中的位置列表。例如,
:vimgrep 查找模式匹配。您可以在脚本中使用
getqflist() 函数访问这些位置。因此,您可以做更多事情,而不仅仅是编辑/编译/修复循环!
如果您有一个包含错误消息的文件,您可以使用以下命令启动 Vim:
vim -q filename
在 Vim 内部,运行命令并处理输出的简单方法是使用
:make 命令(见下文)。
快速修复 ID 每个快速修复列表都有一个唯一的标识符,称为快速修复 ID,并且此编号在 Vim 会话中不会更改。可以使用
getqflist() 函数获取分配给列表的标识符。还有一个快速修复列表编号,它在向快速修复堆栈添加超过十个列表时可能会更改。
位置列表 E776 位置列表是一个窗口本地快速修复列表。在执行
:lvimgrep
、
:lgrep
、
:lhelpgrep
、
:lmake
等命令后,您将获得一个位置列表,而不是像相应的
:vimgrep
、
:grep
、
:helpgrep
、
:make
那样创建快速修复列表。
位置列表文件窗口位置列表与窗口关联,每个窗口都可以有单独的位置列表。位置列表只能与一个窗口关联。位置列表独立于快速修复列表。
当包含位置列表的窗口被分割时,新窗口将获得位置列表的副本。当不再有任何对位置列表的引用时,位置列表将被销毁。
可以使用以下快速修复命令。位置列表命令类似于快速修复命令,将快速修复命令中的 'c' 前缀替换为 'l'。
:cc :cc[!] [nr] 显示错误 [nr]。如果省略 [nr],则会再次显示相同的 :[nr]cc[!] 错误。如果省略 [!],则在跳转到另一个缓冲区时不起作用,当前缓冲区已被更改,只有该缓冲区的窗口,并且
'hidden' 和
'autowrite' 都已关闭。当使用 [!] 跳转到另一个缓冲区时,对当前缓冲区的任何更改都会丢失,除非设置了
'hidden' 或该缓冲区还有另一个窗口。在跳转到缓冲区时,会遵守
'switchbuf' 设置。当在快速修复窗口中使用时,可以使用行号,包括 "." 代表当前行,"$" 代表最后一行。
:ll :ll[!] [nr] 与 ":cc" 相同,只是使用 :[nr]ll[!] 当前窗口的位置列表,而不是快速修复列表。
:lne :lnext ]l :[count]lne[xt][!] 与 ":cnext" 相同,只是使用当前窗口的位置列表,而不是快速修复列表。
:cabo :cabove :[count]cabo[ve] 转到当前缓冲区中当前行上方的 [count] 个错误。如果省略 [count],则使用 1。如果没有错误,则会显示一条错误消息。假定快速修复列表中的条目按其缓冲区编号和行号排序。如果同一行有多个错误,则只使用第一个条目。如果 [count] 超过当前行上方的条目数,则选择文件中第一个错误。
:lab :labove :[count]lab[ove] 与 ":cabove" 相同,只是使用当前窗口的位置列表,而不是快速修复列表。
:cbel :cbelow :[count]cbel[ow] 转到当前缓冲区中当前行下方的 [count] 个错误。如果省略 [count],则使用 1。如果没有错误,则会显示一条错误消息。假定快速修复列表中的条目按其缓冲区编号和行号排序。如果同一行有多个错误,则只使用第一个条目。如果 [count] 超过当前行下方的条目数,则选择文件中最后一个错误。
:lbel :lbelow :[count]lbel[ow] 与 ":cbelow" 相同,只是使用当前窗口的位置列表,而不是快速修复列表。
:cbe :cbefore :[count]cbe[fore] 转到当前缓冲区中当前光标位置之前的 [count] 个错误。如果省略 [count],则使用 1。如果没有错误,则会显示一条错误消息。假定快速修复列表中的条目按其缓冲区、行和列号排序。如果 [count] 超过当前位置之前的条目数,则选择文件中第一个错误。
:lbe :lbefore :[count]lbe[fore] 与 ":cbefore" 相同,只是使用当前窗口的位置列表,而不是快速修复列表。
:caf :cafter :[count]caf[ter] 转到当前缓冲区中当前光标位置之后的 [count] 个错误。如果省略 [count],则使用 1。如果没有错误,则会显示一条错误消息。假定快速修复列表中的条目按其缓冲区、行和列号排序。如果 [count] 超过当前位置之后的条目数,则选择文件中最后一个错误。
:laf :lafter :[count]laf[ter] 与 ":cafter" 相同,只是使用当前窗口的位置列表,而不是快速修复列表。
:lrewind :lr [L :lr[ewind][!] [nr] 与 ":crewind" 相同,只是使用当前窗口的位置列表,而不是快速修复列表。
:llast :lla ]L :lla[st][!] [nr] 与 ":clast" 相同,只是使用当前窗口的位置列表,而不是快速修复列表。
:cq :cquit :cq[uit][!] :{N}cq[uit][!] :cq[uit][!]
{N}
以错误代码
{N}
退出 Vim。
{N}
默认值为 1。在从另一个程序调用 Vim 时很有用:例如,编译器将不再编译同一个文件,
git commit
将中止提交过程,
fc
(bash 和 zsh 等 shell 的内置命令)将不会执行命令,等等。
{N}
也可以为零,在这种情况下,Vim 将正常退出。
注意: 文件中的所有更改都会丢失。它的工作原理类似于 ":qall!"
:qall,只是 Nvim 退出非零或 [count]。
:lf :lfi :lfile :lf[ile][!] [errorfile] 与 ":cfile" 相同,只是使用当前窗口的位置列表,而不是快速修复列表。您不能使用 -q 命令行选项来设置位置列表。
:lg[etfile] [errorfile]
:lg :lge :lgetfile 与 ":cgetfile" 相同,只是使用当前窗口的位置列表,而不是快速修复列表。
:laddf :laddfile :laddf[ile] [errorfile] 与 ":caddfile" 相同,只是使用当前窗口的位置列表,而不是快速修复列表。
:cb :cbuffer E681 :[range]cb[uffer][!] [bufnr] 从当前缓冲区读取错误列表。当给出 [bufnr] 时,它必须是已加载缓冲区的编号。然后该缓冲区将被用于当前缓冲区。可以为要使用的行指定范围。否则,将使用缓冲区中的所有行。参见
:cc 获取 [!] 的信息。
:lb :lbuffer :[range]lb[uffer][!] [bufnr] 与 ":cbuffer" 相同,只是使用当前窗口的位置列表,而不是快速修复列表。
:cgetb :cgetbuffer :[range]cgetb[uffer] [bufnr] 从当前缓冲区读取错误列表。就像 ":cbuffer" 一样,但不会跳转到第一个错误。
:lgetb :lgetbuffer :[range]lgetb[uffer] [bufnr] 与 ":cgetbuffer" 相同,只是使用当前窗口的位置列表,而不是快速修复列表。
:cad :cadd :caddbuffer :[range]cad[dbuffer] [bufnr] 从当前缓冲区读取错误列表并将错误添加到当前快速修复列表。如果不存在快速修复列表,则创建一个新列表。否则,与 ":cbuffer" 相同。
:laddb :laddbuffer :[range]laddb[uffer] [bufnr] 与 ":caddbuffer" 相同,只是使用当前窗口的位置列表,而不是快速修复列表。
:cex :cexpr E777 :cex[pr][!]
{expr}
使用
{expr}
的结果创建快速修复列表并跳转到第一个错误。如果
{expr}
是一个字符串,则使用
'errorformat' 的全局值处理字符串中的每行换行符终止的行,并将结果添加到快速修复列表。如果
{expr}
是一个列表,则处理列表中的每个字符串项并将其添加到快速修复列表。列表中的非字符串项将被忽略。参见
:cc 获取 [!] 的信息。示例
:cexpr system('grep -n xyz *')
:cexpr getline(1, '$')
:cadde :caddexpr :cadde[xpr]
{expr}
评估
{expr}
并将生成的线条添加到当前快速修复列表。如果不存在快速修复列表,则创建一个新列表。当前光标位置不会改变。参见
:cexpr 获取更多信息。示例
:g/mypattern/caddexpr expand("%") .. ":" .. line(".") .. ":" .. getline(".")
:cl :clist :cl[ist] [from] [, [to]] 列出所有有效的
quickfix-valid 错误。如果给出数字 [from] 和/或 [to],则列出相应的错误范围。负数从最后一个错误开始倒数,-1 表示最后一个错误。可以使用
:filter 命令仅显示与提供的模式匹配的快速修复条目。模式与条目的文件名、模块名、模式和文本匹配。
:cl[ist] +{count} 列出当前和接下来的 {count}
个有效错误。这类似于 ":clist from from+count",其中 "from" 是当前错误位置。
:cl[ist]! [from] [, [to]] 列出所有错误。
:cl[ist]! +{count} 列出当前和接下来的
{count}
行错误。这对于查看当前行之后的未识别行很有用。例如,如果 ":clist" 显示
8384 testje.java:252: error: cannot find symbol
然后使用 ":cl! +3" 显示原因
8384 testje.java:252: error: cannot find symbol
8385: ZexitCode = Fmainx();
8386: ^
8387: symbol: method Fmainx()
:lli[st] [from] [, [to]]
:lli :llist 与 ":clist" 相同,只是使用当前窗口的位置列表,而不是快速修复列表。
:lli[st]! [from] [, [to]] 列出当前窗口位置列表中的所有条目。
如果您插入或删除行,大多数情况下仍然可以找到正确的错误位置,因为使用了隐藏标记。有时,当标记因某种原因被删除时,会显示消息“行已更改”,以警告您错误位置可能不正确。如果您退出 Vim 并重新启动,则标记会丢失,并且错误位置可能不再正确。
QuickFixCmdPost-example 当
'encoding' 与语言环境不同时,错误消息的编码可能与 Vim 使用的编码不同。要转换消息,可以使用以下代码
function QfMakeConv()
let qflist = getqflist()
for i in qflist
let i.text = iconv(i.text, "cp936", "utf-8")
endfor
call setqflist(qflist)
endfunction
au QuickfixCmdPost make call QfMakeConv()
另一种选择是使用
'makeencoding'。
quickfix-title 每个快速修复和位置列表都有一个标题。默认情况下,标题设置为创建列表的命令。可以使用
getqflist() 和
getloclist() 函数分别获取快速修复和位置列表的标题。可以使用
setqflist() 和
setloclist() 函数分别修改快速修复和位置列表的标题。示例
call setqflist([], 'a', {'title' : 'Cmd output'})
echo getqflist({'title' : 1})
call setloclist(3, [], 'a', {'title' : 'Cmd output'})
echo getloclist(3, {'title' : 1})
quickfix-index 当您使用任何快速修复命令(例如
:cc、
:cnext、
:cprev 等)跳转到快速修复/位置列表条目时,该条目将成为当前选择的条目。可以使用 getqflist()/getloclist() 函数获取快速修复/位置列表中当前选择的条目的索引。示例
echo getqflist({'idx' : 0}).idx
echo getqflist({'id' : qfid, 'idx' : 0}).idx
echo getloclist(2, {'idx' : 0}).idx
对于新的快速修复列表,将选择第一个条目,索引为 1。可以使用 setqflist() 函数将任何快速修复/位置列表中的任何条目设置为当前选择的条目。示例
call setqflist([], 'a', {'idx' : 12})
call setqflist([], 'a', {'id' : qfid, 'idx' : 7})
call setloclist(1, [], 'a', {'idx' : 7})
quickfix-context 任何 Vim 类型都可以与快速修复或位置列表关联为上下文。可以使用
setqflist() 和
setloclist() 函数分别将上下文与快速修复和位置列表关联。可以使用
getqflist() 和
getloclist() 函数分别检索快速修复和位置列表的上下文。这对处理多个快速修复/位置列表的 Vim 插件很有用。示例
let somectx = {'name' : 'Vim', 'type' : 'Editor'}
call setqflist([], 'a', {'context' : somectx})
echo getqflist({'context' : 1})
let newctx = ['red', 'green', 'blue']
call setloclist(2, [], 'a', {'id' : qfid, 'context' : newctx})
echo getloclist(2, {'id' : qfid, 'context' : 1})
quickfix-parse 您可以使用
'errorformat' 解析行列表,而无需使用
getqflist() 函数创建或修改快速修复列表。示例
echo getqflist({'lines' : ["F1:10:Line10", "F2:20:Line20"]})
echo getqflist({'lines' : systemlist('grep -Hn quickfix *')})
这将返回一个字典,其中 "items" 键包含从行解析的快速修复条目列表。以下显示了如何使用自定义
'errorformat' 来解析行而不修改
'errorformat' 选项。
echo getqflist({'efm' : '%f#%l#%m', 'lines' : ['F1#10#Line']})
在快速修复或位置列表中的所有缓冲区中执行命令:
:cdo:cdo[!]
{cmd}
在快速修复列表中的每个有效条目中执行
{cmd}
。它就像这样操作
:cfirst
:{cmd}
:cnext
:{cmd}
etc.
当当前文件不能
放弃 并且没有 [!] 时,命令将失败。当跳转到下一个条目失败时,执行将停止。最后一个缓冲区(或出现错误的地方)将成为当前缓冲区。
{cmd}
可以包含 '|' 来连接多个命令。
只使用快速修复列表中的有效条目。可以使用范围来选择条目,例如
:10,$cdo cmd
跳过条目 1 到 9。
:cfdo :cfdo[!]
{cmd}
在快速修复列表中的每个文件中执行
{cmd}
。它就像这样操作
:cfirst
:{cmd}
:cnfile
:{cmd}
etc.
否则,它的工作方式与 :cdo
相同。
:ldo :ld[o][!]
{cmd}
在当前窗口的位置列表中的每个有效条目中执行
{cmd}
。它就像这样操作
:lfirst
:{cmd}
:lnext
:{cmd}
etc.
只使用位置列表中的有效条目。否则,它的工作方式与 :cdo
相同。
:lfdo :lfdo[!]
{cmd}
在当前窗口的位置列表中的每个文件中执行
{cmd}
。它就像这样操作
:lfirst
:{cmd}
:lnfile
:{cmd}
etc.
否则,它的工作方式与 :ldo
相同。
过滤快速修复或位置列表:
cfilter-plugin :Cfilter :Lfilter 如果快速修复列表中包含太多条目,可以使用 cfilter 插件来减少条目数量。使用以下命令加载插件
packadd cfilter
然后可以使用以下命令过滤快速修复/位置列表
:Cfilter[!] /{pat}/
:Lfilter[!] /{pat}/
该
:Cfilter 命令从当前快速修复列表中与
{pat}
匹配的条目创建一个新的快速修复列表。
{pat}
是 Vim
正则表达式 模式。文件名和条目的文本都与
{pat}
匹配。如果提供可选的 !,则使用与
{pat}
不匹配的条目。模式可以使用以下字符中的一个可选地括起来:'、"、/。如果模式为空,则使用最后使用的搜索模式。
这些命令不会修改当前快速修复/位置列表,因此可以使用
:colder/|:lolder| 命令返回到未过滤的列表。
当给出 [height] 时,窗口的高度将变为该高度(如果存在空间)。当省略 [height] 时,窗口的高度将变为十行。
如果已存在快速修复窗口,则该窗口将变为当前窗口。无法打开第二个快速修复窗口。如果给出 [height],则现有窗口将调整其大小以匹配该高度。
quickfix-buffer 该窗口将包含一个特殊缓冲区,其
'buftype' 等于 "quickfix"。不要更改它!该窗口将设置 w:quickfix_title 变量,该变量将指示生成快速修复列表的命令。如果
'statusline' 的值调整得当,则可以使用它来组成自定义状态行。每当此缓冲区被快速修复命令或函数修改时,
b:changedtick 变量将递增。可以通过传递 "qfbufnr" 项目来使用 getqflist() 和 getloclist() 函数获取此缓冲区的编号。对于位置列表,此缓冲区将在删除位置列表时被清空。
:lop :lopen :lop[en] [height] 打开一个窗口以显示当前窗口的位置列表。仅在当前窗口的位置列表存在时才有效。您可以同时打开多个位置窗口。否则,它与 ":copen" 的行为相同。
:cw :cwindow :cw[indow] [height] 当存在识别出的错误时,打开快速修复窗口。如果窗口已打开且不存在识别出的错误,则关闭窗口。
:lw :lwindow :lw[indow] [height] 与 ":cwindow" 相同,但使用显示当前窗口的位置列表的窗口。
:cbo :cbottom :cbo[ttom] 将光标放在快速修复窗口的最后一行并滚动以使其可见。这对于异步回调添加错误时很有用。如果有很多更新,请仅偶尔调用它以避免大量重绘。
在快速修复窗口中,每行代表一个错误。行号等于错误号。当前条目将使用 QuickFixLine 高亮显示。可以根据自己的喜好更改它,例如
:hi QuickFixLine ctermbg=Yellow guibg=Yellow
可以使用 ":.cc" 跳转到光标下的错误。按下
<Enter>
键或双击鼠标在行上具有相同的效果。包含错误的文件将在快速修复窗口上面的窗口中打开。如果该文件已经有一个窗口,则使用该窗口。如果所用窗口中的缓冲区已更改,并且错误位于另一个文件中,则跳转到错误将失败。首先,您必须确保窗口包含一个可以放弃的缓冲区。
当从快速修复窗口中选择文件时,将使用以下步骤来查找用于编辑该文件的窗口
1. 如果当前选项卡页中存在显示所选文件的窗口(从快速修复窗口之前的窗口开始),则使用该窗口。 2. 如果上述步骤失败,并且
'switchbuf' 包含 "usetab" 并且任何一个选项卡页中存在显示所选文件的窗口(从第一个选项卡页开始),则使用该窗口。 3. 如果上述步骤失败,则使用当前选项卡页中显示未设置
'buftype' 的缓冲区的窗口(从快速修复窗口之前的窗口开始)。 4. 如果上述步骤失败,并且
'switchbuf' 包含 "uselast",则使用上次访问的窗口。 5. 如果上述步骤失败,则使用快速修复窗口之前的窗口。如果没有先前的窗口,则使用快速修复窗口之后的窗口。 6. 如果上述步骤失败,则使用快速修复窗口上方的新水平分割窗口。
当快速修复窗口已填充时,将触发两个自动命令事件。首先,
'filetype' 选项将设置为 "qf",这将触发 FileType 事件(另见
qf.vim)。然后,将使用 "quickfix" 作为缓冲区名称触发 BufReadPost 事件。这可用于对列出的错误执行某些操作。例如
au BufReadPost quickfix setlocal modifiable
\ | silent exe 'g/^/s//\=line(".") .. " "/'
\ | setlocal nomodifiable
这将把行号添加到每行的前面。请注意在 ":s" 命令的替换字符串中使用 "\=",它用于计算表达式。BufWinEnter 事件也将被触发,同样使用 "quickfix" 作为缓冲区名称。
注意: 当添加到现有快速修复列表时,不会触发自动命令。
注意: 在快速修复窗口中进行更改不会影响错误列表。
'modifiable' 已关闭以避免进行更改。如果您仍然删除或插入行,则文本和错误号之间的关系将被弄乱。如果您真的想这样做,可以将快速修复窗口的内容写入文件并使用 ":cfile" 来解析它并将其用作新的错误列表。
location-list-window 位置列表窗口显示位置列表中的条目。当打开位置列表窗口时,它将在当前窗口下方创建并显示当前窗口的位置列表。位置列表窗口类似于快速修复窗口,不同之处在于您可以同时打开多个位置列表窗口。当在此窗口中使用位置列表命令时,将使用显示的位置列表。
当从位置列表窗口中选择文件时,将使用以下步骤来查找用于编辑该文件的窗口
1. 如果当前选项卡页中存在与位置列表关联的非快速修复窗口,则使用该窗口。 2. 如果上述步骤失败,并且该文件已经在当前选项卡页中的另一个窗口中打开,则使用该窗口。 3. 如果上述步骤失败,并且
'switchbuf' 包含 "usetab" 并且该文件在任何一个选项卡页中的窗口中打开,则使用该窗口。 4. 如果上述步骤失败,则使用当前选项卡页中显示未设置
'buftype' 的缓冲区的窗口。 5. 如果上述步骤失败,则在新的窗口中编辑该文件。
在所有上述情况下,如果所选窗口的位置列表尚未设置,则将其设置为位置列表窗口中显示的位置列表。
getqflist-examples 可以使用
getqflist() 和
getloclist() 函数分别获取快速修复和位置列表的各种属性。以下是使用这些函数的一些示例。
" get the title of the current quickfix list
:echo getqflist({'title' : 0}).title
" get the identifier of the current quickfix list
:let qfid = getqflist({'id' : 0}).id
" get the identifier of the fourth quickfix list in the stack
:let qfid = getqflist({'nr' : 4, 'id' : 0}).id
" check whether a quickfix list with a specific identifier exists
:if getqflist({'id' : qfid}).id == qfid
" get the index of the current quickfix list in the stack
:let qfnum = getqflist({'nr' : 0}).nr
" get the items of a quickfix list specified by an identifier
:echo getqflist({'id' : qfid, 'items' : 0}).items
" get the number of entries in a quickfix list specified by an id
:echo getqflist({'id' : qfid, 'size' : 0}).size
" get the context of the third quickfix list in the stack
:echo getqflist({'nr' : 3, 'context' : 0}).context
" get the number of quickfix lists in the stack
:echo getqflist({'nr' : '$'}).nr
" get the number of times the current quickfix list is changed
:echo getqflist({'changedtick' : 0}).changedtick
" get the current entry in a quickfix list specified by an identifier
:echo getqflist({'id' : qfid, 'idx' : 0}).idx
" get all the quickfix list attributes using an identifier
:echo getqflist({'id' : qfid, 'all' : 0})
" parse text from a List of lines and return a quickfix list
:let myList = ["a.java:10:L10", "b.java:20:L20"]
:echo getqflist({'lines' : myList}).items
" parse text using a custom 'efm' and return a quickfix list
:echo getqflist({'lines' : ['a.c#10#Line 10'], 'efm':'%f#%l#%m'}).items
" get the quickfix list window id
:echo getqflist({'winid' : 0}).winid
" get the quickfix list window buffer number
:echo getqflist({'qfbufnr' : 0}).qfbufnr
" get the context of the current location list
:echo getloclist(0, {'context' : 0}).context
" get the location list window id of the third window
:echo getloclist(3, {'winid' : 0}).winid
" get the location list window buffer number of the third window
:echo getloclist(3, {'qfbufnr' : 0}).qfbufnr
" get the file window id of a location list window (winnr: 4)
:echo getloclist(4, {'filewinid' : 0}).filewinid
setqflist-examples 可以使用
setqflist() 和
setloclist() 函数分别设置快速修复和位置列表的各种属性。以下是使用这些函数的一些示例。
" create an empty quickfix list with a title and a context
:let t = 'Search results'
:let c = {'cmd' : 'grep'}
:call setqflist([], ' ', {'title' : t, 'context' : c})
" set the title of the current quickfix list
:call setqflist([], 'a', {'title' : 'Mytitle'})
" change the current entry in the list specified by an identifier
:call setqflist([], 'a', {'id' : qfid, 'idx' : 10})
" set the context of a quickfix list specified by an identifier
:call setqflist([], 'a', {'id' : qfid, 'context' : {'val' : 100}})
" create a new quickfix list from a command output
:call setqflist([], ' ', {'lines' : systemlist('grep -Hn main *.c')})
" parse text using a custom efm and add to a particular quickfix list
:call setqflist([], 'a', {'id' : qfid,
\ 'lines' : ["a.c#10#L10", "b.c#20#L20"], 'efm':'%f#%l#%m'})
" add items to the quickfix list specified by an identifier
:let newItems = [{'filename' : 'a.txt', 'lnum' : 10, 'text' : "Apple"},
\ {'filename' : 'b.txt', 'lnum' : 20, 'text' : "Orange"}]
:call setqflist([], 'a', {'id' : qfid, 'items' : newItems})
" empty a quickfix list specified by an identifier
:call setqflist([], 'r', {'id' : qfid, 'items' : []})
" free all the quickfix lists in the stack
:call setqflist([], 'f')
" set the title of the fourth quickfix list
:call setqflist([], 'a', {'nr' : 4, 'title' : 'SomeTitle'})
" create a new quickfix list at the end of the stack
:call setqflist([], ' ', {'nr' : '$',
\ 'lines' : systemlist('grep -Hn class *.java')})
" create a new location list from a command output
:call setloclist(0, [], ' ', {'lines' : systemlist('grep -Hn main *.c')})
" replace the location list entries for the third window
:call setloclist(3, [], 'r', {'items' : newItems})
到目前为止,我们假设只有一个错误列表。实际上,最后使用的十个列表会被记住。当开始一个新的列表时,之前的列表会被自动保留。可以使用两个命令来访问旧的错误列表。它们将其中一个现有错误列表设置为当前列表。
:colder :col E380 :col[der] [count] 转到更旧的错误列表。如果给定了 [count],则执行 [count] 次。如果已经在最旧的错误列表中,则会显示一条错误消息。
:lolder :lol :lol[der] [count] 与
:colder
相同,但使用当前窗口的位置列表,而不是快速修复列表。
:cnewer :cnew E381 :cnew[er] [count] 转到更新的错误列表。如果给定了 [count],则执行 [count] 次。如果已经在最新的错误列表中,则会显示一条错误消息。
:lnewer :lnew :lnew[er] [count] 与
:cnewer
相同,但使用当前窗口的位置列表,而不是快速修复列表。
:chistory :chi :[count]chi[story] 显示错误列表列表。当前列表用“>”标记。输出类似于
error list 1 of 3; 43 errors :make
> error list 2 of 3; 0 errors :helpgrep tag
error list 3 of 3; 15 errors :grep ex_help *.c
如果给定了 [count],则将第 [count] 个快速修复列表设置为当前列表。例如
" Make the 4th quickfix list current
:4chistory
添加新的错误列表时,它将成为当前列表。
如果使用过 ":colder",并且使用 ":make" 或 ":grep" 添加新的错误列表,则会覆盖一个更新的列表。这在使用 ":grep"
grep 浏览时特别有用。如果你想保留最近的错误列表,请先使用 ":cnewer 99"。
要获取快速修复和位置列表堆栈中的列表数量,可以使用
getqflist() 和
getloclist() 函数,分别将列表编号设置为特殊值“$”。例如
echo getqflist({'nr' : '$'}).nr
echo getloclist(3, {'nr' : '$'}).nr
要获取堆栈中当前列表的编号
echo getqflist({'nr' : 0}).nr
:lmak :lmake :lmak[e][!] [arguments] 与 ":make" 相同,但使用当前窗口的位置列表,而不是快速修复列表。
":make" 命令执行使用
'makeprg' 选项给定的命令。这是通过将命令传递给使用
'shell' 选项给定的 shell 来完成的。这几乎与在命令行中输入
":!{makeprg} [arguments] {shellpipe}
{errorfile}
" 相同。
{makeprg}
是使用
'makeprg' 选项给定的字符串。可以使用任何命令,而不只是“make”。字符“%”和“#”在命令行上按常规方式展开。可以使用 "%<" 插入当前文件名(不带扩展名),或使用 "#<" 插入备用文件名(不带扩展名),例如
:set makeprg=make\ #<.o
[arguments] 是在 ":make" 后输入的任何内容。
{shellpipe}
是
'shellpipe' 选项。
{errorfile}
是
'makeef' 选项,其中“##”被替换以使其唯一。
如果命令需要在其参数后添加一些额外的字符,则可以使用占位符 “$*” 来表示
{makeprg}
中的参数列表。然后 “$*” 将被替换为所有参数。例如
:set makeprg=latex\ \\\\nonstopmode\ \\\\input\\{$*}
或者更简单点
:let &mp = 'latex \\nonstopmode \\input\{$*}'
“$*” 可以多次出现,例如
:set makeprg=gcc\ -o\ $*\ $*
对于 Win32,
'shellpipe' 选项默认为“2>&1| tee”。这意味着编译器的输出被保存到一个文件中,而不是直接显示在屏幕上。对于 Unix,使用的是“| tee”。编译器输出会同时显示在屏幕上并保存到一个文件中。默认情况下,根据所使用的 shell,“|& tee” 或“2>&1| tee” 会被使用,因此 stderr 输出会被包含在内。
如果
'shellpipe' 为空,则会省略
{errorfile}
部分。这对那些自己写入错误文件的编译器很有用。
可能是
'encoding' 设置为与构建程序生成的错误消息不同的编码。此示例显示了如何在 Vim 读取错误消息后修复此问题
function QfMakeConv()
let qflist = getqflist()
for i in qflist
let i.text = iconv(i.text, "cp936", "utf-8")
endfor
call setqflist(qflist)
endfunction
au QuickfixCmdPost make call QfMakeConv()
(由 Faque Cheng 提供的示例)另一种选择是使用
'makeencoding'。
Vim 有两种方法可以查找模式匹配:内部和外部。内部 grep 的优点是它适用于所有系统,并且使用强大的 Vim 搜索模式。如果 Vim grep 不能满足你的需求,可以使用外部 grep 程序。
内部方法会更慢,因为文件被读取到内存中。优点是
行分隔符和编码会自动识别,就像正在编辑文件一样。
使用 Vim 搜索模式。可以使用多行模式。
为了能够做到这一点,Vim 会像编辑一样加载每个文件。如果文件中没有匹配项,则会再次清除关联的缓冲区。
'hidden' 选项在这里被忽略,以避免在搜索许多文件时耗尽内存或文件描述符。但是,当使用
:hide 命令修饰符时,缓冲区会保持加载状态。这使得在相同文件中的后续搜索速度快得多。
请注意,可以使用
:copen(或
:lopen 用于
:lgrep)打开包含以链接形式显示的搜索结果的缓冲区。可以使用
:silent 命令抑制默认的全屏 grep 输出。":grep!" 形式的
:grep 命令不会自动跳转到第一个匹配项。这些命令可以组合起来创建一个 NewGrep 命令
command! -nargs=+ NewGrep execute 'silent grep! <args>' | copen 42
5.1 使用 Vim 的内部 grep
标志:'g' 如果没有 'g' 标志,则每行仅添加一次。使用 'g' 时,会添加每个匹配项。
'j' 如果没有 'j' 标志,则 Vim 会跳转到第一个匹配项。使用 'j' 时,只更新快速修复列表。使用 [!] 时,当前缓冲区中的任何更改都会被放弃。
'f' 当指定 'f' 标志时,将使用模糊字符串匹配来查找匹配行。在这种情况下,
{pattern}
被视为一个文字字符串,而不是一个正则表达式。有关模糊匹配字符串的更多信息,请参见
fuzzy-matching。
:{count}vim[grep] ... 如果在命令之前放置一个数字,则将其用作要查找的最大匹配项数量。使用 ":1vimgrep pattern file" 只查找第一个匹配项。如果只想检查是否匹配并快速退出,则很有用。
每隔一秒或更长时间,就会显示正在搜索的文件名,以便让你了解进度。例如
:vimgrep /an error/ *.c
:vimgrep /\<FileName\>/ *.h include/*
:vimgrep /myfunc/ **/*.c
:vim[grep][!]
{pattern}
{file}
... 与上面类似,但不要用非 ID 字符括起来模式,而是使用空格分隔的模式。模式必须以 ID 字符开头。例如
:vimgrep Error *.c
:lv :lvimgrep :lv[imgrep][!] /{pattern}/[g][j][f]
{file}
... :lv[imgrep][!]
{pattern}
{file}
... 与 ":vimgrep" 相同,但使用当前窗口的位置列表,而不是快速修复列表。
:vimgrepa :vimgrepadd :vimgrepa[dd][!] /{pattern}/[g][j][f]
{file}
... :vimgrepa[dd][!]
{pattern}
{file}
... 与 ":vimgrep" 相同,但不是创建新的错误列表,而是将匹配项追加到当前列表中。
:lvimgrepa :lvimgrepadd :lvimgrepa[dd][!] /{pattern}/[g][j][f]
{file}
... :lvimgrepa[dd][!]
{pattern}
{file}
... 与 ":vimgrepadd" 相同,但使用当前窗口的位置列表,而不是快速修复列表。
5.2 外部 grep
Vim 可以以类似于其编译器集成(参见上面的
:make)的方式与 "grep" 和类似 grep 的程序(如 GNU id-utils)交互。
(Unix 小知识:Unix "grep" 命令的名称来源于 ":g/re/p",其中 "re" 代表正则表达式。)
:lgr :lgrep :lgr[ep][!] [arguments] 与 ":grep" 相同,但使用当前窗口的位置列表,而不是快速修复列表。
:grepa :grepadd :grepa[dd][!] [arguments] 与 ":grep" 相同,但不是创建新的错误列表,而是将匹配项追加到当前列表。示例
:call setqflist([])
:bufdo grepadd! something %
第一个命令创建一个新的错误列表,该列表为空。第二个命令对每个列出的缓冲区执行 "grepadd"。请注意使用 ! 以避免 ":grepadd" 跳到第一个错误,这在使用
:bufdo 时是不允许的。使用参数列表并避免无匹配文件错误的示例
:silent argdo try
\ | grepadd! something %
\ | catch /E480:/
\ | endtry"
:lgrepa :lgrepadd :lgrepa[dd][!] [arguments] 与 ":grepadd" 相同,但使用当前窗口的位置列表,而不是快速修复列表。
5.3 设置外部 grep
如果安装了标准的 "grep" 程序,:grep 命令可能会使用默认设置正常工作。语法与标准命令非常相似
:grep foo *.c
将在所有扩展名为 .c 的文件中搜索子字符串 "foo"。:grep 的参数将直接传递给 "grep" 程序,因此可以使用 "grep" 支持的任何选项。
a) 使用的程序不是 "grep" b) 需要使用完整路径调用 grep c) 想要自动传递其他选项(例如,不区分大小写的搜索。)
解析结果后,Vim 将加载第一个包含匹配项的文件,并跳转到相应的行,就像它在
快速修复 模式下跳转到编译器错误一样。然后,可以使用
:cnext、
:clist 等命令查看其他匹配项。
5.4 使用 :grep 与 id-utils
可以使用以下方法设置 :grep 以与 GNU id-utils 协同工作
:set grepprg=lid\ -Rgrep\ -s
:set grepformat=%f:%l:%m
然后
:grep (regexp)
将按预期工作。(前提是您记得先执行 mkid :)
5.5 使用 :vimgrep 或 :grep 浏览源代码
使用 Vim 保留的错误列表堆栈,可以浏览文件以查找函数及其调用的函数。例如,假设需要在 read_file() 函数中添加一个参数。输入以下命令
:vimgrep /\<read_file\>/ *.c
使用 ":cn" 遍历匹配项列表并添加参数。在一个位置,需要从更高级别的函数 msg() 获取新参数,并且需要更改该函数。因此,使用
:vimgrep /\<msg\>/ *.c
在更改 msg() 函数时,发现另一个函数需要从更高级别的函数获取参数。可以再次使用 ":vimgrep" 查找这些函数。完成一个函数后,可以使用
:colder
返回到上一个函数。
这就像浏览树一样:":vimgrep" 向下深入一层,创建一个分支列表。":colder" 返回到上一级。可以混合使用 ":vimgrep" 和 "colder" 以树状方式浏览所有位置。如果始终如一地这样做,就可以找到所有位置,而无需记录 "待办事项" 列表。
:comp :compiler E666 :comp[iler][!]
{name}
设置选项以使用编译器
{name}
。不带 "!" 会为当前缓冲区设置选项。带 "!" 会设置全局选项。如果您在 "file.foo" 中使用 ":compiler foo",然后在另一个缓冲区中使用 ":compiler! bar",Vim 将在 "file.foo" 中继续使用 "foo"。
"compiler" 目录中的 Vim 插件将设置选项以使用所选编译器。对于
:compiler
,将设置局部选项;对于
:compiler!
,将设置全局选项。
current_compiler为了支持较旧的 Vim 版本,插件始终使用 "current_compiler",而不是 "b:current_compiler"。该命令的实际执行过程如下
删除 "current_compiler" 和 "b:current_compiler" 变量。
定义 "CompilerSet" 用户命令。带 "!" 时执行 ":set";不带 "!" 时执行 ":setlocal"。
执行 ":runtime! compiler/{name}.{vim,lua}"。插件应该使用 "CompilerSet" 设置选项,并将 "current_compiler" 变量设置为编译器名称。
删除 "CompilerSet" 用户命令。
将 "b:current_compiler" 设置为 "current_compiler" 的值。
不带 "!" 时,将恢复 "current_compiler" 的旧值。
使用 g/b:`c_cppcheck_params` 设置 cppcheck 参数。默认情况下,全局设置包括
--verbose
:启用详细输出。
--force
:强制检查所有配置。
--inline-suppr
:允许内联抑制。
--enable=...
:启用特定检查,例如警告、样式、性能、可移植性、信息和缺少的包含文件。
-j
:如果可用,将利用多个处理器,由 getconf
命令(如果可用)确定(需要省略 unusedFunction 检查)
对于 C++ 文件 (filetype == 'cpp'
),将添加 --language=c++
选项以确保 Cppcheck 将该文件视为 C++ 文件。
如果当前目录中存在 compile_commands.json,则将其作为
--project
参数添加到命令行。否则,默认情况下,将 &path 中的目录作为包含目录传递。这些目录可以通过 g/b:`c_cppcheck_includes` 作为
-I
标志列表设置。Tim Pope 的 vim-apathy 插件 [0] 可以扩展 &path。要将 git 仓库中的文件夹也追加到其中,可以使用
let &l:path = join(systemlist('git ls-tree -d --name-only -r HEAD'), ',')
[0]
https://github.com/tpope/vim-apathy
.NET CLI 编译器默认输出错误和警告。可以通过将 g:dotnet_errors_only 变量设置为
v:true 来限制输出,仅包含错误。
每个错误和警告中都包含关联的项目名称。要隐藏项目名称,请将 g:dotnet_show_project_file 变量设置为
v:false。
示例:限制输出,仅显示错误,并隐藏项目名称
let dotnet_errors_only = v:true
let dotnet_show_project_file = v:false
compiler dotnet
您可以为 GCC 编译器设置一个变量
g:compiler_gcc_ignore_unmatched_lines 忽略与为 GCC 定义的任何模式不匹配的行。如果从 make 运行的命令的输出生成误报,这将非常有用。
可以通过设置 g:javac_makeprg_params 变量将常用编译器选项添加到
'makeprg' 中。例如
let g:javac_makeprg_params = "-Xlint:all -encoding utf-8"
由于默认的 make 程序为 "make",因此 make 的编译器插件 :compiler make 将重置
'makeprg' 和
'errorformat' 选项为默认值,并取消设置可能由之前的编译器插件设置的任何变量。
GROFF 编译器插件使用 mom 宏集(在 groff_mom 手册页中记录)作为输入,并期望将输出文件类型扩展名传递给 make,例如::make html 或 :make pdf。
可以通过在
b:groff_compiler_args
或
g:groff_compiler_args
中设置其他参数传递给 groff。传递给 groff 的
language
参数使用
'spelllang' 设置;可以通过设置
b:groff_compiler_lang
覆盖它。默认编码为
UTF-8
,可以通过设置
b:groff_compiler_encoding
或
g:groff_compiler_encoding
更改。
Pandoc 编译器插件期望将输出文件类型扩展名传递给 make,例如::make html 或 :make pdf。
可以将其他参数传递给 pandoc
将它们追加到 make 中,例如 :make html --self-contained
。
或在 b:pandoc_compiler_args
或 g:pandoc_compiler_args
中设置它们。
--from
参数是使用缓冲区文件类型的合理猜测;可以通过设置
b:pandoc_compiler_from
覆盖它。
--metadata lang
参数使用
'spelllang' 设置;如果假设
--from=markdown
并且标题标题或 YAML 块中没有设置标题,则使用文件名(不带扩展名)作为标题。
Perl 编译器插件实际上并不进行编译,而是调用 Perl 的内部语法检查功能,并解析输出以查找可能的错误,以便在快速修复模式下进行更正。
无论文件中是否存在 "no warnings" 或 "$^W = 0",都会强制显示警告。要禁用此功能,请将 g:perl_compiler_force_warnings 设置为零值。例如
let g:perl_compiler_force_warnings = 0
使用该框架运行测试时,Vim 会解析可能的错误,并在快速修复模式下向您显示这些错误。
不幸的是,没有标准的方法来运行测试。alltests.py 脚本似乎被广泛使用,仅此而已。因此,
'makeprg' 选项的有用值如下:setlocal makeprg=./alltests.py " 运行测试套件 setlocal makeprg=python\ %:S " 运行单个测试用例
TeX 的分发编译器(位于 $VIMRUNTIME/compiler/tex.vim)如果可能的话,会使用 make 命令。如果编译器在当前目录中找到名为 "Makefile" 或 "makefile" 的文件,它会假设您希望使用 make 处理您的
*TeX
文件,并且 makefile 会执行正确的操作。在这种情况下,编译器会为
*TeX
输出设置
'errorformat' 并保持
'makeprg' 不变。如果既没有找到 "Makefile" 也没有找到 "makefile",编译器将不会使用 make。您可以通过定义 b:tex_ignore_makefile 或 g:tex_ignore_makefile 变量来强制编译器忽略 makefile(只检查是否存在)。
如果编译器选择不使用 make,它需要选择一个合适的程序来处理您的输入。如果 b:tex_flavor 或 g:tex_flavor(按此优先级)变量存在,它将定义 TeX 风格供 :make 使用(实际上,这是执行命令的名称),如果这两个变量都不存在,它将默认设置为 "latex"。例如,当编辑从 mypaper.tex(用 AMS-TeX 编写的)中 \input 的 chapter2.tex 时
:let b:tex_flavor = 'amstex'
:compiler tex
[正在编辑...]
:make mypaper
请注意,您必须指定要处理的文件名作为参数(以便在编辑 \input 或 \include 的文件时处理正确文件;欢迎提供便携式解决方案以用 % 替换无参数)。这与 make 的语义不一致,您需要指定目标,而不是源,但您可以指定没有 ".tex" 扩展名的文件名,并将此解释为 "根据编译器生成 filename.dvi 或 filename.pdf 或 filename.some_result_extension"。
注意:tex 命令行语法被设置为可用于 MikTeX(由 Srinath Avadhanula 建议)和 teTeX(由 Artem Chuprina 检查)。来自
errorformat-LaTeX 的建议过于复杂,无法使其在不同的 shell 和操作系统上正常工作,而且也不允许使用其他可用的 TeX 选项(如果有)。如果您的 TeX 不支持 "-interaction=nonstopmode",请以其他方式报告从命令行表达 \nonstopmode 的方法。
Vim 包含一个针对 Typst 文件的编译器插件。该编译器由 Typst 文件类型插件
ft-typst-plugin 在 Typst 缓冲区中自动启用。运行
:make 来编译当前 Typst 文件。
g:typst_cmd 默认情况下,Vim 将使用 "typst" 作为运行 Typst 编译器的命令。可以通过设置
g:typst_cmd 变量来更改此设置。
let g:typst_cmd = "/path/to/other/command"
在
'errorformat' 中的每个条目都是一个类似 scanf 的字符串,它描述了格式。首先,您需要了解 scanf 的工作原理。请查看您的 C 编译器的文档。在下面,您将找到 Vim 理解的 % 项目。其他项目无效。
在
'errorformat' 中的特殊字符是逗号和反斜杠。请参阅
efm-entries 以了解如何处理它们。请注意,一个字面意义上的 "%" 将由 "%%" 匹配,因此不会用反斜杠转义它。请记住,在
:make
和
:grep
输出中,所有 NUL 字符都被替换为 SOH(0x01)。
注意:默认情况下,将忽略大小写之间的区别。如果您想匹配大小写,请在模式中添加 "\C"
/\C。
Vim 将读取任何长度的行的,但只使用前 4095 个字节,其余的将被忽略。项目长度只能为 1023 个字节。
基本项目
%f 文件名(找到一个字符串) %b 缓冲区编号(找到一个数字) %o 模块名(找到一个字符串) %l 行号(找到一个数字) %e 结束行号(找到一个数字) %c 列号(找到一个数字,代表错误的字符列,字节索引,<tab>
为 1 个字符列) %v 虚拟列号(找到一个数字,代表错误的屏幕列(1 个 <tab>
== 8 个屏幕列)) %k 结束列号(找到一个数字,代表错误的字符列,字节索引,或者如果与 %v 一起使用,则代表错误的屏幕结束列) %t 错误类型(找到一个单字符): e - 错误消息 w - 警告消息 i - 信息消息 n - 注意消息 %n 错误编号(找到一个数字) %m 错误消息(找到一个字符串) %r 匹配单行文件消息的“其余部分” %O/P/Q %p 指针行(找到一系列 '-'、'.'、' ' 或制表符,并使用其长度作为列号) %*{conv} 任何 scanf 不可分配的转换 %% 单个 '%' 字符 %s 搜索文本(找到一个字符串)
"%f" 转换可能取决于当前的
'isfname' 设置。"~/" 将扩展为家目录,环境变量将被扩展。
"%f" 和 "%m" 转换必须检测字符串的结束位置。这通常通过匹配后续字符和项目来实现。当没有后续字符时,将匹配该行的其余部分。如果 "%f" 后面跟着 '%' 或反斜杠,它将寻找
'isfname' 字符的序列。
在 Windows 上,即使使用 "%f:",也会在 "%f" 中包含一个前导的 "C:"。这意味着一个仅为单个字母的文件名将不会被检测到。
"%b" 转换用于解析缓冲区编号。这对于引用暂存缓冲区或没有名称的缓冲区中的行很有用。如果不存在具有匹配编号的缓冲区,则该行将用作非错误行。
"%p" 转换通常后面跟着一个 "^"。它用于那些输出类似于以下行的编译器
^
或
---------^
来指示错误的列。这将用于多行错误消息。请参阅
errorformat-javac 以获取一个有用的示例。
"%s" 转换指定要搜索的文本,以定位错误行。该文本用作字面字符串。锚点 "^" 和 "$" 将被添加到该文本中,以准确地定位与搜索文本完全匹配的错误行,并且该文本将以 "\V" 原子为前缀,使其成为“非常不神奇的”。"%s" 转换可用于定位错误输出中没有行号的行。例如,"grep" shell 命令的输出。当模式存在时,行号将不会被使用。
"%o" 转换指定 quickfix 条目中的模块名。如果存在,它将在 quickfix 错误窗口中使用,而不是文件名。模块名仅用于显示目的,文件名在跳转到文件时使用。
更改目录
以下大写转换字符指定特殊格式字符串的类型。最多只能有一个作为前缀出现在单个逗号分隔的格式模式的开头。某些编译器会生成包含目录名的消息,这些目录名必须附加到由 %f 读取的每个文件名之前(例如:GNU make)。以下代码可用于扫描这些目录名;它们将存储在一个内部目录堆栈中。
E379%D "进入目录" 格式字符串;预期后面跟着一个找到目录名的 %f %X "离开目录" 格式字符串;预期后面跟着 %f
在定义 "进入目录" 或 "离开目录" 格式时,"%D" 或 "%X" 必须出现在该子字符串的开头。Vim 会跟踪目录更改,并将当前目录附加到每个包含相对路径的错误文件。请参阅
quickfix-directory-stack 以获取详细信息、提示和限制。
可以读取那些生成多行消息的程序的输出,即跨越多行的错误字符串。可能的首码有: %E 多行错误消息的开头 %W 多行警告消息的开头 %I 多行信息消息的开头 %N 多行注意消息的开头 %A 多行消息的开头(类型未指定) %> 对于下一行,再次使用当前模式
efm-%> %C 多行消息的延续 %Z 多行消息的结尾 这些可以与 '+' 和 '-' 一起使用,请参阅下面的
efm-ignore。
在模式中使用 "\n" 不会起作用,无法匹配多行消息。
示例:您的编译器恰好以以下格式写出错误(前导行号不是实际输出的一部分)
1 错误 275
2 行 42
3 列 3
4 ' ' 预期在 '--' 之后
相应的错误格式字符串必须如下所示
:set efm=%EError\ %n,%Cline\ %l,%Ccolumn\ %c,%Z%m
为该错误生成的
:clist 错误消息是
1:42 列 3 错误 275: ' ' 预期在 '--' 之后
另一个示例:假设一个 Python 解释器会生成以下错误消息(行号不是实际输出的一部分)
1 ============================================================== 2 FAIL: testGetTypeIdCachesResult (dbfacadeTest.DjsDBFacadeTest) 3 -------------------------------------------------------------- 4 Traceback (most recent call last): 5 File "unittests/dbfacadeTest.py", line 89, in testFoo 6 self.assertEquals(34, dtid) 7 File "/usr/lib/python3.8/unittest.py", line 286, in 8 failUnlessEqual 9 raise self.failureException, \ 10 AssertionError: 34 != 33 11 12 -------------------------------------------------------------- 13 Ran 27 tests in 0.063s
假设您希望
:clist 仅写入该消息的相关信息,即:5 unittests/dbfacadeTest.py:89: AssertionError: 34 != 33
那么错误格式字符串可以定义如下
:set efm=%C\ %.%#,%A\ \ File\ \"%f\"\\,\ line\ %l%.%#,%Z%[%^\ ]%\\@=%m
请注意,这里的 %C 字符串位于 %A 之前:由于表达式 ' %.%#'(代表正则表达式 ' .* ')与以空格开头且后面跟着任何字符直到行尾的每一行匹配,因此它也会隐藏第 7 行,否则第 7 行会触发单独的错误消息。错误格式字符串总是逐个模式地解析,直到出现第一个匹配项为止。
efm-%>%> 项目可用于避免尝试在
'errorformat' 中出现的更早的模式。这对于匹配几乎所有内容的模式很有用。例如,如果错误看起来像这样
可以用以下方法找到它
:set efm=xxx,%E%>Error in line %l of %f:,%Z%m
其中 "xxx" 具有也匹配第二行的模式。
重要提示:没有关于之前匹配了 errorformat 的哪个部分的记忆;错误文件中的每一行都会对 errorformat 行进行全新的运行。例如,如果有人有
setlocal efm=aa,bb,cc,dd,ee
其中 aa、bb 等是 errorformat 字符串。错误文件中的每一行都会与模式 aa 匹配,然后与 bb 匹配,然后与 cc 匹配,依此类推。仅仅因为 cc 匹配了上一个错误行,_并不_意味着 dd 会在当前行上首先被尝试,即使 cc 和 dd 是多行 errorformat 字符串。
这些首码在文件名被给出一次且多个消息引用该文件名时很有用。 %O 单行文件消息:覆盖匹配的部分 %P 单行文件消息:将文件 %f 推入堆栈 %Q 单行文件消息:从堆栈中弹出最后一个文件
示例:假设一个编译器生成以下错误日志文件(没有前导行号)
1 [a1.tt] 2 (1,17) 错误: ';' 丢失 3 (21,2) 警告: 未定义变量 'z' 4 (67,3) 错误: 在字符串结束之前遇到文件结束 5 6 [a2.tt] 7 8 [a3.tt] 9 NEW 编译器 v1.1 10 (2,2) 警告: 未定义变量 'x' 11 (67,3) 警告: 's' 已定义
该日志文件为每个包含在 [...] 中的文件列出了多条消息,这些消息通过类似于以下的错误格式被正确解析
:set efm=%+P[%f],(%l\\,%c)%*[\ ]%t%*[^:]:\ %m,%-Q
调用
:clist 会用正确的文件名相应地写入它们。
2 a1.tt:1 col 17 error: ';' missing 3 a1.tt:21 col 2 warning: variable 'z' not defined 4 a1.tt:67 col 3 error: end of file found before string ended 8 a3.tt:2 col 2 warning: variable 'x' not defined 9 a3.tt:67 col 3 warning: 's' already defined
与所有与整行匹配的其他前缀不同,%P、%Q 和 %O 可用于匹配同一行中的多个模式。因此,可以解析像下面这行这样的嵌套文件。
{"file1" {"file2" error1} error2 {"file3" error3 {"file4" error4 error5}}}
代码 '+' 或 '-' 可以与上面的大写代码组合使用;在这种情况下,它们必须放在字母前面,例如 '%+A' 或 '%-G':%- 不包含匹配的多行在任何输出中 %+ 包含整个匹配行在 %m 错误字符串中
只有一个前缀仅在与 '+' 或 '-' 组合使用时才有用,即 %G。它会解析包含通用信息的行,例如编译器版本字符串或其他可以跳过的标头。%-G 忽略此消息 %+G 通用消息
模式匹配
scanf() 样式的 "%*[]" 符号是为了向后兼容 Vim 的先前版本而支持的。但是,也可以在格式字符串中指定(几乎)任何 Vim 支持的正则表达式。由于正则表达式语言的元字符可能是普通匹配字符串或文件名的部分(因此在内部必须进行转义),因此元符号必须使用前导 '%' 编写:%\ 单个 '\' 字符。请注意,这在 ":set errorformat=" 定义中必须进行转义("%\\")。%. 单个 '.' 字符。%# 单个 "*"(!) 字符。%^ 单个 '^' 字符。请注意,这没有用,模式已经匹配行首。%$ 单个 '$' 字符。请注意,这没有用,模式已经匹配行尾。%[ 单个 '[' 字符用于 [] 字符范围。%~ 单个 '~' 字符。在表达式中使用字符类时(有关概述,请参见
/\i),包含 "\+" 量词的项可以用 scanf() 的 "%*" 符号编写。例如:"%\\d%\\+" ("\d\+", "任何数字") 等效于 "%*\\d"。重要说明:\(...\) 子匹配的分组不能用于格式规范,因为它是为内部转换保留的。
为了能够检测来自多个编译器的输出,可以在
'errorformat' 中放置多个格式模式,用逗号隔开(注意:逗号后的空格将被忽略)。第一个完全匹配的模式将被使用。如果找不到匹配项,将使用最后一个匹配项中的匹配部分,尽管文件名将被删除,错误消息将被设置为整个消息。如果存在一个可能匹配来自多个编译器的输出的模式(但不正确),请将其放在一个限制性更强的模式之后。
要在模式中包含逗号,请在其前面加上反斜杠(在 ":set" 命令中,您需要输入两个)。要包含反斜杠本身,请给出两个反斜杠(在 ":set" 命令中,您需要输入四个)。您还需要在 ":set" 之前的空格前加上反斜杠。
如果一行没有完全匹配
'errorformat' 中的某个条目,则整行将被放入错误消息中,并且该条目将被标记为“无效”。这些行将使用 ":cn" 和 ":cp" 命令跳过(除非根本没有有效行)。您可以使用 ":cl!" 显示所有错误消息。
如果错误格式不包含文件名,Vim 无法切换到正确文件。您将不得不手动执行此操作。
例如,Amiga Aztec 编译器输出的格式为
filename>linenumber:columnnumber:errortype:errornumber:errormessage
filename 检测到错误的文件名 linenumber 检测到错误的行号 columnnumber 检测到错误的列号 errortype 错误类型,通常为单个 'E' 或 'W' errornumber 错误编号(用于在手册中查找)errormessage 错误描述
以下是产生单行错误输出的 C 编译器的一些示例:%f:%l:\ %t%*[^0123456789]%n:\ %m 用于 Manx/Aztec C 错误消息(scanf() 不理解 [0-9]) %f\ %l\ %t%*[^0-9]%n:\ %m 用于 SAS C \"%f\"\\,%*[^0-9]%l:\ %m 用于通用 C 编译器 %f:%l:\ %m 用于 GCC %f:%l:\ %m,%Dgmake[%*\\d]:\ Entering\ directory\%f', %Dgmake[%*\\d]:\ Leaving\ directory\%f' 用于带有 gmake 的 GCC(连接这些行!) %f(%l)\ :\ %*[^:]:\ %m 旧 SCO C 编译器(pre-OS5) %f(%l)\ :\ %t%*[^0-9]%n:\ %m 同上,带有错误类型和编号 %f:%l:\ %m,In\ file\ included\ from\ %f:%l:,\^I\^Ifrom\ %f:%l%m 用于 GCC,包含一些额外内容
请注意空格和双引号前面的反斜杠。这是 :set 命令所需的。逗号前面有两个反斜杠,一个用于 :set 命令,另一个用于避免将逗号识别为错误格式的分隔符。
过滤消息
如果您有一个编译器产生不适合格式字符串的错误消息,则可以编写一个程序将错误消息转换为这种格式。您可以通过更改
'makeprg' 选项,将此程序与 ":make" 命令一起使用。例如
:set mp=make\ \\\|&\ error_filter
管道字符之前的反斜杠是必需的,以避免将其识别为命令分隔符。set 命令之前每个空格前的反斜杠也是必需的。
Quickfix 维护一个堆栈,用于保存从 make 输出中解析的所有已使用目录。对于 GNU-make,这很简单,因为它始终打印其进入和离开的所有目录的绝对路径。无论这是通过 makefile 中的
'cd' 命令完成的,还是通过参数 "-C dir"(在读取 makefile 之前更改为目录)完成的。使用开关 "-w" 强制 GNU-make 在处理之前和之后打印出工作目录可能很有用。
如果您不使用 GNU-make,则维护正确的目录会更加复杂。例如,AIX-make 不会打印任何关于其工作目录的信息。然后,您需要增强 makefile。在 LessTif 的 makefile 中,有一个命令会回显 "Making {target}
in {dir}
"。这里特殊的问题是,它不会打印有关离开目录的信息,也不会打印绝对路径。
为了解决相对路径和缺少“离开目录”消息的问题,Vim 使用以下算法
1) 检查给定目录是否为当前目录的子目录。如果为真,则将其存储为当前目录。2) 如果它不是当前目录的子目录,请尝试它是否是其中一个上级目录的子目录。3) 如果仍然找不到该目录,则假定它是 Vim 的当前目录的子目录。
此外,还会检查每个文件,以查看它是否确实存在于已识别的目录中。如果没有,则在目录堆栈中的所有其他目录(而不是目录子树!)中搜索。如果仍然找不到,则假定它位于 Vim 的当前目录中。
此算法存在限制。这些示例假设 make 只打印有关以 "Making all in dir" 形式进入目录的信息。
1) 假设您有以下目录和文件:./dir1 ./dir1/file1.c ./file1.c
如果 make 在当前目录之前处理目录 "./dir1",并且在文件 "./file1.c" 中存在错误,则您将最终使用 Vim 加载文件 "./dir1/file.c"。
这只能通过“离开目录”消息来解决。
2) 假设您有以下目录和文件:./dir1 ./dir1/dir2 ./dir2
您将获得以下内容
Make 输出 Vim 解释的目录 ------------------------ ---------------------------- Making all in dir1 ./dir1 Making all in dir2 ./dir1/dir2 Making all in dir2 ./dir1/dir2
这可以通过在“进入目录”消息中打印绝对目录或打印“离开目录”消息来解决。
为了避免此问题,请确保打印绝对目录名和“离开目录”消息。
Makefile 示例
Unix: libs: for dn in $(LIBDIRS); do \ (cd $$dn; echo "Entering dir '$$(pwd)'"; make); \ echo "Leaving dir"; \ done
将 %DEntering\ dir\ '%f',%XLeaving\ dir 添加到您的
'errorformat' 以处理上述输出。
请注意,Vim 不会检查“离开目录”消息中的目录名是否为当前目录。这就是为什么您只需使用消息“Leaving dir”的原因。
下面显示了与生成的消息匹配的
'errorformat' 字符串。以下行可以放在用户的
init.vim 中以覆盖 Vim 识别的默认格式,或者参见
:set+= 如何将此格式额外安装到默认格式中。
:set efm=%A%f:%l:%c:%*\\d:%*\\d:,
\%C%*\\s%trror:%m,
\%+C%*[^:]%trror:%m,
\%C%*\\s%tarning:%m,
\%C%m
Jikes(TM) 在使用选项 "+E" 调用时会生成单行错误消息,并且可以使用以下内容进行匹配
:setl efm=%f:%l:%v:%*\\d:%*\\d:%*\\s%m
errorformat-javac 据报道,此
'errorformat' 适用于 javac,javac 会输出包含 "^" 的行以指示错误的列
:setl efm=%A%f:%l:\ %m,%-Z%p^,%-C%.%#
或
:setl efm=%A%f:%l:\ %m,%+Z%p^,%+C%.%#,%-G%.%#
以下是 Michael F. Lamb 为 Unix 提供的替代方案,它会先过滤错误
:setl errorformat=%Z%f:%l:\ %m,%A%p^,%-G%*[^sl]%.%#
:setl makeprg=javac\ %:S\ 2>&1\ \\\|\ vim-javac-filter
您需要将以下内容放在路径中的某个位置(例如,在 ~/bin 中)的 "vim-javac-filter" 中,并将其设为可执行文件
#!/bin/sed -f
/\^$/s/\t/\ /g;/:[0-9]\+:/{h;d};/^[ \t]*\^/G;
用英语来说,该 sed 脚本
将单个制表符更改为单个空格,并且
将包含文件名、行号、错误消息的行移动到指针行的正下方。这样,它们之间未使用的错误文本就不会破坏 vim 对“多行消息”的理解,也不会强迫我们将其作为“多行消息的延续”包含在内。
errorformat-ant 对于 ant (
https://jakarta.apache.org/),上述 errorformat 必须进行修改以尊重每个 javac 输出行前面的 [javac]
:set efm=%A\ %#[javac]\ %f:%l:\ %m,%-Z\ %#[javac]\ %p^,%-C%.%#
还可以配置
'errorformat' 以处理 ant 以及 javac 或 jikes。如果您使用的是 jikes,则应告诉 ant 使用 jikes 的 +E 命令行开关,这将强制 jikes 生成单行错误消息。这就是下面 build.xml 文件的第二行所做的
<property name = "build.compiler" value = "jikes"/>
<property name = "build.compiler.emacs" value = "true"/>
处理 ant 与 javac 和 jikes 结合使用的
'errorformat' 为
:set efm=\ %#[javac]\ %#%f:%l:%c:%*\\d:%*\\d:\ %t%[%^:]%#:%m,
\%A\ %#[javac]\ %f:%l:\ %m,%-Z\ %#[javac]\ %p^,%-C%.%#
errorformat-LaTeX 以下是
'errorformat' 字符串的示例,该字符串可以为 (La)TeX 排版系统指定,该系统会跨多行显示错误消息。":clist" 和 ":cc" 等命令的输出会将多行显示在一行中,前导空格将被删除。应该很容易将上述 LaTeX errorformat 调整为任何由多行错误组成的编译器输出。
这些命令可以放在
vimrc 文件或其他 Vim 脚本文件中,例如,一个仅在编辑 LaTeX 源代码时加载的包含 LaTeX 相关内容的脚本。确保复制示例中的所有行(以给定的顺序),然后删除注释行。对于某些行开头的“\”符号,请参阅
行延续。
首先准备
'makeprg',以便 LaTeX 可以报告多个错误;当第一个错误发生时不要停止。
:set makeprg=latex\ \\\\nonstopmode\ \\\\input\\{$*}
多行错误消息的开始
:set efm=%E!\ LaTeX\ %trror:\ %m,
\%E!\ %m,
多行警告消息的开始;前两个还包括行号。一些正则表达式的含义
“%.%#" (".*") 匹配一个(可能为空)字符串
“%*\\d" ("\d\+") 匹配一个数字
\%+WLaTeX\ %.%#Warning:\ %.%#line\ %l%.%#,
\%+W%.%#\ at\ lines\ %l--%*\\d,
\%WLaTeX\ %.%#Warning:\ %m,
错误/警告消息的可能延续;第一个也包括行号
\%Cl.%l\ %m,
\%+C\ \ %m.,
\%+C%.%#-%.%#,
\%+C%.%#[]%.%#,
\%+C[]%.%#,
\%+C%.%#%[{}\\]%.%#,
\%+C<%.%#>%.%#,
\%C\ \ %m,
与以下模式匹配的行不包含任何重要信息;不要将它们包含在消息中
\%-GSee\ the\ LaTeX%m,
\%-GType\ \ H\ <return>%m,
\%-G\ ...%.%#,
\%-G%.%#\ (C)\ %.%#,
\%-G(see\ the\ transcript%.%#),
通常,将任何空行或仅包含空格的行从显示中排除
\%-G\\s%#,
LaTeX 输出日志未指定每行错误源文件的名称;相反,它们是在全局范围内给出的,用括号括起来。以下模式试图匹配这些名称并将它们存储在一个内部堆栈中。这些模式可能扫描同一输入行(一个接一个),尾随的“%r”转换表示在下一轮解析中将被解析的行的“剩余部分”,直到到达行尾。
重读用 '('...')' 括起来的的文件名;不要将其推入堆栈,因为该文件显然不包含任何错误
\%+O(%f)%r,
将文件名推入堆栈。名称在 '(' 后给出
\%+P(%f%r,
\%+P\ %\\=(%f%r,
\%+P%*[^()](%f%r,
\%+P[%\\d%[^()]%#(%f%r,
当扫描到 ')' 时弹出最后一个存储的文件名
\%+Q)%r,
\%+Q%*[^()])%r,
\%+Q[%\\d%*[^()])%r
请注意,在某些情况下,LaTeX 输出日志中的文件名无法正确解析。解析器可能被不平衡的括号弄乱了。上面的示例只尝试捕获最相关的用例。您可以根据自己的需要自定义给定的设置,例如,所有恼人的“Overfull ...”警告都可以从识别为错误中排除。作为对过滤 LaTeX 编译器输出的替代方案,还可以直接读取 [La]TeX 编译器生成的
*.log
文件。这包含了更多关于可能的错误原因的有用信息。但是,为了正确解析如此复杂的文件,应该使用外部过滤器。请参阅上述说明,了解如何使 Vim 了解此类过滤器。
快速修复窗口和位置列表窗口中显示的行的默认格式为
<filename>|<lnum> col <col>|<text>
每行显示的值对应于
getqflist() 函数返回的“bufnr”、“lnum”、“col”和“text”字段。
对于某些快速修复/位置列表,显示的文本需要自定义。例如,如果快速修复条目中仅存在文件名,则文件名后的两个“|”字段分隔符字符是不需要的。另一个用例是自定义文件名显示的路径。默认情况下,对于不在当前目录树下的文件,将显示完整路径(可能太长)。文件路径可能需要简化为一个公共父目录。
可以通过将
'quickfixtextfunc' 选项设置为 Vim 函数来自定义显示的文本。此函数将使用字典参数调用,应返回一个要显示在快速修复或位置列表窗口中的字符串列表。字典参数将具有以下字段
quickfix 当调用快速修复列表时设置为 1,当调用位置列表时设置为 0。 winid 对于位置列表,设置为具有位置列表的窗口的 id。对于快速修复列表,设置为 0。可以在 getloclist() 中使用以获取位置列表条目。 id 快速修复或位置列表标识符 start_idx 需要返回文本的第一个条目的索引 end_idx 需要返回文本的最后一个条目的索引
该函数应为每个从 start_idx 到 end_idx 的条目返回一行要显示在快速修复窗口中的文本。该函数可以使用
getqflist() 函数并指定快速修复列表标识符“id”来获取有关条目的信息。对于位置列表,可以使用“winid”参数使用 getloclist() 函数。如果返回一个空列表,则使用默认格式显示所有条目。如果返回的列表中的项目为空字符串,则使用默认格式显示相应的条目。
下面的示例在快速修复窗口中显示旧文件列表 (
v:oldfiles)。由于与每个条目关联的没有行号、列号和错误文本信息,因此
'quickfixtextfunc' 函数仅返回文件名。示例
" create a quickfix list from v:oldfiles
call setqflist([], ' ', {'lines' : v:oldfiles, 'efm' : '%f',
\ 'quickfixtextfunc' : 'QfOldFiles'})
func QfOldFiles(info)
" get information about a range of quickfix entries
let items = getqflist({'id' : a:info.id, 'items' : 1}).items
let l = []
for idx in range(a:info.start_idx - 1, a:info.end_idx - 1)
" use the simplified file name
call add(l, fnamemodify(bufname(items[idx].bufnr), ':p:.'))
endfor
return l
endfunc