title: Vim script to compile TeX source and launch PDF only if no errors
body: <p>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.</p>
  
  <p>I know about the Vim-Latex suite, but, if possible, would prefer to avoid using it: it is pretty heavy-weight, hijacks a <em>lot</em> of my keys, and clutters up my vimruntime with a lot of files. </p>
  
  <p>Here is what I have now:</p>
  
  <pre><code>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, &lt;f-args&gt;)
  command! -buffer -nargs=* BuildAndViewTex call BuildTex(1, &lt;f-args&gt;)
  noremap &lt;buffer&gt; &lt;silent&gt; &lt;F9&gt; &lt;Esc&gt;:call BuildTex(0)&lt;CR&gt;
  noremap &lt;buffer&gt; &lt;silent&gt; &lt;S-F9&gt; &lt;Esc&gt;:call BuildTex(1)&lt;CR&gt;
  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 &amp;&amp; v:shell_error == 0
              call ViewTexResults()
          endif
      else
          let b:tex_flavor = 'pdflatex'
          compiler tex
          make %
          if a:view_results &amp;&amp; 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
  </code></pre>
  
  <p>The problem is that <code>v:shell_error</code> 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!</p>
  
  <hr>
  
  <p>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.</p>
  
  <p>Basically, the best solution appears to be to use <a href="http://www.pps.jussieu.fr/~beffara/soft/rubber/" rel="noreferrer">Rubber</a>, 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. </p>
  
  <p>In my own installation, I have lifted the (<em>excellent</em>) "SetLatexEfm()" function from <a href="http://vim-latex.sourceforge.net/" rel="noreferrer">Vim-Latex</a> 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.</p>
  
  <pre><code>    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&gt;&amp;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&lt;%.%#&gt;%.%#,
                      \%C\ \ %m,
                      \%-GSee\ the\ LaTeX%m,
                      \%-GType\ \ H\ &lt;return&gt;%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 &amp;&amp; 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, &lt;f-args&gt;)
      command! -buffer -nargs=* BuildAndViewTex call BuildTex(1, &lt;f-args&gt;)
      noremap &lt;buffer&gt; &lt;silent&gt; &lt;F9&gt; &lt;Esc&gt;:call BuildTex(0)&lt;CR&gt;
      noremap &lt;buffer&gt; &lt;silent&gt; &lt;S-F9&gt; &lt;Esc&gt;:call BuildTex(1)&lt;CR&gt;
  </code></pre>
  
  <hr>
  
  <p>Update: I have packaged and published this as a Vim file-type plugin script, available at: <a href="http://www.vim.org/scripts/script.php?script_id=3230" rel="noreferrer">http://www.vim.org/scripts/script.php?script_id=3230</a>.</p>