仅在没有错误的情况下编译 TeX 源并启动 PDF 的 Vim 脚本

发布于 2024-08-29 19:07:38 字数 6654 浏览 12 评论 0原文

我正在改用 Vim 作为我的 LaTeX 编辑环境。我希望能够从 Vim 中解析源文件,并在编译成功时启动外部查看。

我了解 Vim-Latex 套件,但如果可能的话,我宁愿避免使用它:它相当重量级,劫持了我的很多密钥,并且用很多东西弄乱了我的 vimruntime文件数量。

这是我现在所拥有的:

if exists('b:tex_build_mapped')
    finish
endif
" use maparg or mapcheck to see if key is free
command! -buffer -nargs=* BuildTex call BuildTex(0, <f-args>)
command! -buffer -nargs=* BuildAndViewTex call BuildTex(1, <f-args>)
noremap <buffer> <silent> <F9> <Esc>:call BuildTex(0)<CR>
noremap <buffer> <silent> <S-F9> <Esc>:call BuildTex(1)<CR>
let b:tex_build_mapped = 1

if exists('g:tex_build_loaded')
    finish
endif
let g:tex_build_loaded = 1

function! BuildTex(view_results, ...)
    write
    if filereadable("Makefile")
        " If Makefile is available in current working directory, run 'make' with arguments
        echo "(using Makefile)"
        let l:cmd = "!make ".join(a:000, ' ')
        echo l:cmd
        execute l:cmd
        if a:view_results && v:shell_error == 0
            call ViewTexResults()
        endif
    else
        let b:tex_flavor = 'pdflatex'
        compiler tex
        make %
        if a:view_results && v:shell_error == 0
            call ViewTexResults()
        endif
    endif
endfunction

function! ViewTexResults(...)
    if a:0 == 0
        let l:target = expand("%:p:r") . ".pdf"
    else
        let l:target = a:1
    endif
    if has('mac')
        execute "! open -a Preview ".l:target
    endif
endfunction

问题是,即使存在编译错误,也未设置 v:shell_error 。任何有关如何检测编译是否成功的建议或见解将不胜感激!谢谢!


通过这里给出的答案,再加上对其他方法的一些研究,我认为这个问题已经得到了令人满意的解决。我将解决方案发布在这里,以防其他人感兴趣。

基本上,最好的解决方案似乎是使用 Rubber,它是一个包装器LaTeX,通常“正常工作”,并提供非常干净的输出/错误。如果在系统上找到了 Rubber 并且在当前目录中找不到 Makefile,我下面介绍的解决方案会优先使用 Rubber。如果找到 Makefile,它将使用该文件。如果没有Makefile且未安装Rubber,则使用pdflatex。在所有情况下,如果源代码无法编译,(已过滤和解析的)错误将发送到 QuickFix 缓冲区,并自动打开 QuickFix 窗口。如果编译成功,则会写入一条短信,如果用户请求,则会打开 PDF 进行查看。

在我自己的安装中,我从 中提升了(优秀)“SetLatexEfm()”函数Vim-Latex 解析和过滤 tex 构建输出。但是,如果找不到此函数,则下面的函数默认设置一种错误消息格式,该格式足以在 QuickFix 窗口中识别并突出显示错误,尽管有很多繁琐的内容。

    function! BuildTex(view_results, ...)

        " record position
        let save_cursor = getpos(".")

        " save work
        silent write

        " From: http://stackoverflow.com/questions/2679475/vim-script-to-compile-tex-source-and-launch-pdf-only-if-no-errors
        " If your shell is bash, you can use the ${PIPESTATUS} array variable to get
        " the correct exit code (borrowed from this answer to another question).
        silent setlocal shell=bash
        silent setlocal shellpipe=2>&1\ \|\ tee\ %s;exit\ \${PIPESTATUS[0]}

        let success = 1
        if filereadable("Makefile")
            " If Makefile is available in current working directory, run 'make' with arguments
            echon "compiling using Makefile ..."
            let l:makecmd = "make\\ ".join(a:000, '\\ ')
            silent execute "setlocal makeprg=" . l:makecmd
            try
                " This function is defined in the Vim-Latex package, 
                " and provides excellent parsing and filtering of the error messages
                " when running latex outside of the Rubber wrapper.
                call s:SetLatexEfm()
            catch /E117/
                set errorformat=%E!\ LaTeX\ %trror:\ %m,
                    \%E!\ %m,
                    \%+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%#,
                    \%+O(%f)%r,
                    \%+P(%f%r,
                    \%+P\ %\\=(%f%r,
                    \%+P%*[^()](%f%r,
                    \%+P[%\\d%[^()]%#(%f%r,
                    \%+Q)%r,
                    \%+Q%*[^()])%r,
                    \%+Q[%\\d%*[^()])%r
            endtry
            silent make
        else
            let l:special_tex_compiler = "rubber"
            if executable(l:special_tex_compiler)
                echon "compiling with Rubber ..."
                silent execute "setlocal makeprg=" . l:special_tex_compiler . "\\ -dfs\\ %"
                setlocal errorformat=%f:%l:\ %m
                silent make %
            else
                echon "compiling ..."
                let b:tex_flavor = 'pdflatex'
                compiler tex
                silent make %
            endif
        endif

        " set/report compile status
        if v:shell_error
            let l:success = 0
            " let l:wheight = winheight(bufnr("%")) / 2
            " execute "copen ".l:wheight
            copen
        else
            let l:success = 1
            cclose
            redraw
            echon "successfully compiled"
        endif

        " view results if successful compile
        if l:success && a:view_results
            call ViewTexResults()
        endif

        " restore position
        call setpos('.', save_cursor)

    endfunction

    function! ViewTexResults(...)
        if a:0 == 0
            let l:target = expand("%:p:r") . ".pdf"
        else
            let l:target = a:1
        endif
        if has('mac')
            silent execute "! open -a Preview ".l:target
            " obviously, you will need to write specific commands for other systems
            " left as an exercise for the reader ...
        endif
    endfunction

    command! -buffer -nargs=* BuildTex call BuildTex(0, <f-args>)
    command! -buffer -nargs=* BuildAndViewTex call BuildTex(1, <f-args>)
    noremap <buffer> <silent> <F9> <Esc>:call BuildTex(0)<CR>
    noremap <buffer> <silent> <S-F9> <Esc>:call BuildTex(1)<CR>

更新:我已将其打包并发布为 Vim 文件类型插件脚本,可在以下位置获取: http://www.vim.org/scripts/script.php?script_id=3230

I am switching to using Vim for for my LaTeX editing environment. I would like to be able to tex the source file from within Vim, and launch an external viewing if the compile was successful.

I know about the Vim-Latex suite, but, if possible, would prefer to avoid using it: it is pretty heavy-weight, hijacks a lot of my keys, and clutters up my vimruntime with a lot of files.

Here is what I have now:

if exists('b:tex_build_mapped')
    finish
endif
" use maparg or mapcheck to see if key is free
command! -buffer -nargs=* BuildTex call BuildTex(0, <f-args>)
command! -buffer -nargs=* BuildAndViewTex call BuildTex(1, <f-args>)
noremap <buffer> <silent> <F9> <Esc>:call BuildTex(0)<CR>
noremap <buffer> <silent> <S-F9> <Esc>:call BuildTex(1)<CR>
let b:tex_build_mapped = 1

if exists('g:tex_build_loaded')
    finish
endif
let g:tex_build_loaded = 1

function! BuildTex(view_results, ...)
    write
    if filereadable("Makefile")
        " If Makefile is available in current working directory, run 'make' with arguments
        echo "(using Makefile)"
        let l:cmd = "!make ".join(a:000, ' ')
        echo l:cmd
        execute l:cmd
        if a:view_results && v:shell_error == 0
            call ViewTexResults()
        endif
    else
        let b:tex_flavor = 'pdflatex'
        compiler tex
        make %
        if a:view_results && v:shell_error == 0
            call ViewTexResults()
        endif
    endif
endfunction

function! ViewTexResults(...)
    if a:0 == 0
        let l:target = expand("%:p:r") . ".pdf"
    else
        let l:target = a:1
    endif
    if has('mac')
        execute "! open -a Preview ".l:target
    endif
endfunction

The problem is that v:shell_error is not set, even if there are compile errors. Any suggestions or insight on how to detect whether a compile was successful or not would be greatly appreciated! Thanks!


Between the answers given here, plus some study of other approaches, I think that this has been satisfactorily solved. I am posting the solution here in case anyone else is interested.

Basically, the best solution appears to be to use Rubber, a wrapper around LaTeX, that generally "just works", and provides very clean output/errors. The solution I present below preferentially uses Rubber if it is found on the system and no Makefile is found in the current directory. If a Makefile is found, it uses that instead. If there is no Makefile and Rubber is not installed, it uses pdflatex. In all cases, if the source fails to compile, the (filtered and parsed) errors are sent to the QuickFix buffer and the QuickFix window is automatically opened. If it compiles successfully, a short message is written, and if the user requested it, the PDF will be opened for viewing.

In my own installation, I have lifted the (excellent) "SetLatexEfm()" function from Vim-Latex to parse and filter the tex build output. If this function is not found, however, the function below defaults to setting an error message format that works fine enough for the errors to be identified and highlighted in the QuickFix window, albeit with lots of crud.

    function! BuildTex(view_results, ...)

        " record position
        let save_cursor = getpos(".")

        " save work
        silent write

        " From: http://stackoverflow.com/questions/2679475/vim-script-to-compile-tex-source-and-launch-pdf-only-if-no-errors
        " If your shell is bash, you can use the ${PIPESTATUS} array variable to get
        " the correct exit code (borrowed from this answer to another question).
        silent setlocal shell=bash
        silent setlocal shellpipe=2>&1\ \|\ tee\ %s;exit\ \${PIPESTATUS[0]}

        let success = 1
        if filereadable("Makefile")
            " If Makefile is available in current working directory, run 'make' with arguments
            echon "compiling using Makefile ..."
            let l:makecmd = "make\\ ".join(a:000, '\\ ')
            silent execute "setlocal makeprg=" . l:makecmd
            try
                " This function is defined in the Vim-Latex package, 
                " and provides excellent parsing and filtering of the error messages
                " when running latex outside of the Rubber wrapper.
                call s:SetLatexEfm()
            catch /E117/
                set errorformat=%E!\ LaTeX\ %trror:\ %m,
                    \%E!\ %m,
                    \%+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%#,
                    \%+O(%f)%r,
                    \%+P(%f%r,
                    \%+P\ %\\=(%f%r,
                    \%+P%*[^()](%f%r,
                    \%+P[%\\d%[^()]%#(%f%r,
                    \%+Q)%r,
                    \%+Q%*[^()])%r,
                    \%+Q[%\\d%*[^()])%r
            endtry
            silent make
        else
            let l:special_tex_compiler = "rubber"
            if executable(l:special_tex_compiler)
                echon "compiling with Rubber ..."
                silent execute "setlocal makeprg=" . l:special_tex_compiler . "\\ -dfs\\ %"
                setlocal errorformat=%f:%l:\ %m
                silent make %
            else
                echon "compiling ..."
                let b:tex_flavor = 'pdflatex'
                compiler tex
                silent make %
            endif
        endif

        " set/report compile status
        if v:shell_error
            let l:success = 0
            " let l:wheight = winheight(bufnr("%")) / 2
            " execute "copen ".l:wheight
            copen
        else
            let l:success = 1
            cclose
            redraw
            echon "successfully compiled"
        endif

        " view results if successful compile
        if l:success && a:view_results
            call ViewTexResults()
        endif

        " restore position
        call setpos('.', save_cursor)

    endfunction

    function! ViewTexResults(...)
        if a:0 == 0
            let l:target = expand("%:p:r") . ".pdf"
        else
            let l:target = a:1
        endif
        if has('mac')
            silent execute "! open -a Preview ".l:target
            " obviously, you will need to write specific commands for other systems
            " left as an exercise for the reader ...
        endif
    endfunction

    command! -buffer -nargs=* BuildTex call BuildTex(0, <f-args>)
    command! -buffer -nargs=* BuildAndViewTex call BuildTex(1, <f-args>)
    noremap <buffer> <silent> <F9> <Esc>:call BuildTex(0)<CR>
    noremap <buffer> <silent> <S-F9> <Esc>:call BuildTex(1)<CR>

Update: I have packaged and published this as a Vim file-type plugin script, available at: http://www.vim.org/scripts/script.php?script_id=3230.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

思念满溢 2024-09-05 19:07:38

假设您陷入了 else-theres-no-makefile 部分,问题可能出在 shellpipe 变量上。

在我的系统(Ubuntu)上,shellpipe=2>&1| tee 和内置 make 调用在失败时不会设置 v:shell_error

| 的返回状态tee 可能是 v:shell_error 设置的内容。

如果您的 shell 是 bash,则可以使用 ${PIPESTATUS} 数组变量来获取正确的退出代码(借自 这个答案到另一个问题)。

:set shellpipe=2>&1\ \|\ tee\ %s;exit\ \${PIPESTATUS[0]}

否则,您可以尝试:

:set shellpipe=\>
:make %

这会在失败时设置 v:shell_error ,但我不确定这是否会扰乱 go-to-error-line-number 功能(如果有)。

要查看变量设置的内容:

:set shellpipe?

Assuming you're falling into the else-theres-no-makefile section, the issue may be with the shellpipe variable.

On my system (Ubuntu), shellpipe=2>&1| tee and the built-in make call doesn't set v:shell_error if it fails.

The return status of | tee might be what v:shell_error is getting set to.

If your shell is bash, you can use the ${PIPESTATUS} array variable to get the correct exit code (borrowed from this answer to another question).

:set shellpipe=2>&1\ \|\ tee\ %s;exit\ \${PIPESTATUS[0]}

Otherwise, you can try:

:set shellpipe=\>
:make %

This sets v:shell_error when it fails but I'm not sure if that will mess with the go-to-error-line-number functionality, if there is any.

To see what the variable is set to:

:set shellpipe?
烟雨凡馨 2024-09-05 19:07:38

我知道它与 vim 无关,但我认为 latexmk 完成工作。

这是一个脚本(用 perl 编写),用于编译 Latex 文件并更新 pdf。最有用的未来是自动更新。一旦你保存文件,“latexmk”就会编译它,如果你的 pdf 查看器支持它,视图就会更新。

latexmk -pdf -pvc

I know it's not related to vim, but I think that latexmk does the job.

It's a script (written in perl) which compile the latex file and update the pdf. The most useful future is the auto-update one. As soon as you save your file, 'latexmk' compile it, and if your pdf viewer supports it, the view is updated.

latexmk -pdf -pvc

薆情海 2024-09-05 19:07:38

如果 Latexmk 使用“-halt-on-error”选项(或在不间断模式下)运行 Latex,编译将停止而不会暂停输入。

If latexmk runs latex with the '-halt-on-error' option (or in nonstop mode), compilation will cease without pausing for input.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文