vim过滤器和标准输出/标准输出

vim过滤器和标准输出/标准输出,vim,Vim,当我使用:%!要通过筛选器运行文件内容,筛选器失败(它返回的代码不是0)并向stderr打印错误消息,我将用此错误消息替换我的文件。如果过滤器返回指示错误和/或忽略过滤器程序写入stderr的输出的状态代码,是否有方法告诉vim跳过过滤 在某些情况下,您希望用过滤器的输出替换文件,但这种行为通常是错误的。当然,我可以用一个键取消过滤,但这不是最优的 在编写自定义vim脚本进行过滤时,我也遇到了类似的问题。我有一个脚本,它用system()调用一个过滤器程序,并用它的输出替换缓冲区中的文件,但似乎

当我使用:%!要通过筛选器运行文件内容,筛选器失败(它返回的代码不是0)并向stderr打印错误消息,我将用此错误消息替换我的文件。如果过滤器返回指示错误和/或忽略过滤器程序写入stderr的输出的状态代码,是否有方法告诉vim跳过过滤

在某些情况下,您希望用过滤器的输出替换文件,但这种行为通常是错误的。当然,我可以用一个键取消过滤,但这不是最优的


在编写自定义vim脚本进行过滤时,我也遇到了类似的问题。我有一个脚本,它用system()调用一个过滤器程序,并用它的输出替换缓冲区中的文件,但似乎没有办法检测system()返回的行是否写入到stdout或stderr。有没有办法在vim脚本中区分它们?

您可以使用Python来区分stdout和stderr:

python import vim, subprocess
python b=vim.current.buffer
python line=vim.current.range.start
python p=subprocess.Popen(["command", "argument", ...], stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=True)
python returncode=p.poll()
python if not returncode: b.append(("STDOUT:\n"+p.stdout.read()+"\nSTDERR:\n"+p.stderr.read()).split("\n"), line)

:!{cmd}
使用shell执行
{cmd}
,并设置
v:shell\u错误

如果您碰巧设置了映射以调用过滤器,则可以执行以下操作:

function! UndoIfShellError()
    if v:shell_error
        undo
    endif
endfuntion

nmap <leader>filter :%!/path/to/filter<CR>:call UndoIfShellError()<CR>
函数!UndoIfShellError()
如果v:shell\u错误
解开
恩迪夫
终末功能
nmap筛选器:%/路径/to/filter:调用UndoIfShellError()

另一种方法是运行filter命令,例如修改磁盘上的文件

例如,对于gofmt(www.golang.org),我有以下映射:

map <f9> :w<CR>:silent !gofmt -w=true %<CR>:e<CR>
imap <f9> <ESC>:w<CR>:silent !gofmt -w=true %<CR>:e<CR>
地图:w:安静!gofmt-w=真%:e 女:安静!gofmt-w=真%:e 说明: :w-保存文件 :静音-避免在结束时按enter键 %-将文件传递给gofmt -w=true-告诉gofmt写回文件
:e-告诉vim重新加载修改过的文件

这就是我最后要做的:

function MakeItAFunction(line1, line2, args)
  let l:results=system() " call filter via system or systemlist
  if v:shell_error
    "no changes were ever actually made!
    echom "Error with etc etc"
    echom results
  endif
  "process results if anything needed?

  " delete lines but don't put in register:
  execute a:line1.",".a:line2." normal \"_dd"
  call append(a:line1-1, l:result)  " add lines
  call cursor(a:line1, 1)  " back to starting place
  " echom any messages
endfunction
command -range <command keys> MakeItAFunction(<line1>,<line2>,<q-args>) 
"                                         or <f-args>, etc.
函数MakeItAFunction(第1行、第2行、参数)
让l:results=system()“通过system或systemlist调用筛选器
如果v:shell\u错误
“事实上没有任何改变!
echom“etc错误”
回波结果
恩迪夫
“如果需要,处理结果如何?
“删除行但不放入寄存器:
执行a:line1.,.a:line2.“正常\”\u dd
调用append(a:line1-1,l:result)“添加行
将光标(a:line1,1)“调回起始位置
“回传任何信息
端功能
命令范围函数(,,)
”“或者,等等。
您可以在以下位置查看我的完整代码:


它很复杂,但可以工作,而且在使用时相当透明和优雅。希望这能有所帮助!

在Vim 7中添加了新的自动命令事件:
ShellCmdPost
ShellFilterPost

augroup FILTER_ERROR
  au!
  autocmd ShellFilterPost * if v:shell_error | undo | endif
augroup END

当然!对于每一种“真正的”脚本语言来说,这根本不是问题。我没有考虑过这一点。尽管我更喜欢vim脚本,如果可能的话,因为我不想对脚本有太多的依赖性。我只知道另一种方法:使用
system()
,将stderr重定向到临时文件(或
/dev/null
),将stdout保存到某个变量,并使用
v:shell_error
确定在覆盖缓冲区之前命令是否失败。注意,
:!
过滤器将«!»替换为以前的命令,«%»替换为当前文件名,而
系统()
则不会。这是一个很好的技巧:)。实际上,我希望为:!{cmd}语法,我刚刚忽略了它。但是我越来越担心没有这样的替代语法存在。