Vim script to compile TeX source and launch PDF only if no errors - latex

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.

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?

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

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

Related

Linux expect newline

I have a custom package I want to install automatically in my docker using expect.
The first thing the package asks me to do is press Enter to continue, then it prints another 2 empty lines then it waits for an input.
My expect script :
#!/usr/bin/expect -f
set timeout -1
spawn ./install
expect "\n"
send -- "\n"
But as you can see in the image, it just runs the installer and exits.
I tried removing the expect "\n" so only send -- "\n" will execute but now even the install message doesn't appear (tried with set timeout 1000 before send and it also didn't work)
Any ideas?
P.S : This is a link to the package if anyone wants to have a go at it:
https://www.bayometric.com/downloads/digital-persona/DP_UareU_Linux223_20140429.2.zip
(the installer is inside DP-UareU-RTE-2.2.3-1.20140429_1533.tar.gz)
expect "\n" match a linefeed exactly, I think this is not what your program is sending.
To wait for a Shell prompt you can use expect "%" or expect "*" to match anything.
If you need to make sure you're dealing with the right prompt you may be able to use something like expect "*Linux Installation*".
Also don't send \n but \r for the enter key :
#!/usr/bin/expect
spawn ./install
expect "*Linux Installation*"
send "\r"
expect eof
Note that the default flag is -gl for glob pattern matching but you can also use the -re flag for regular expression matching.

dxl CreateProcess failed for system cmd instruction

I want to call run a file called csvplot.vbs (from this site) to turn a .csv file I have written using dxl (has 5 columns, each with a heading and then just numerical data) into a graph (stored as .png).
I have run the following instruction directly through cmd with success:
#echo off
cscript //nologo C:\Users\Administrator\csvplot.vbs C:\PROGRA~1\IBM\Rational\DOORS\9.6\lib\dxl\addins\Verification\Statistics\statGenTest_Top_Level.csv C:\PROGRA~1\IBM\Rational\DOORS\9.6\lib\dxl\addins\Verification\Statistics\statGenTest_Top_Level.png 800 600 1 3 1 4 1 5
pause
This produces the desired .png file.
What I want, however, is to be able to execute this through DOORS, so that whenever the script that generates the raw data is run, it also produces a graph.
What I have is this as my test case:
string echostr = "#echo off"
string commands = "cscript //nologo C:\\Users\\Administrator\\csvplot.vbs C:\\PROGRA~1\\IBM\\Rational\\DOORS\\9.6\\lib\\dxl\\addins\\Verification\\Statistics\\statGenTest_Top_Level.csv C:\\PROGRA~1\\IBM\\Rational\\DOORS\\9.6\\lib\\dxl\\addins\\Verification\\Statistics\\statGenTest_Top_Level.png 800 600 1 3 1 4 1 5"
system("cmd /c start #echo off") // doesn't recognise echo command
system("cmd /c start " commands "")
I get an error:
"Windows cannot find '#echo'. Make sure you typed the name correctly,
and then try again."
I am at a loss on how to get the script to run though cmd from dxl, and I would appreciate any help. I've only had one previous foray into system() prompts through dxl, and it was only to open a .pdf. In the meantime I will keep trying to work this out. Please let me know if I can provide any further information.
Edit: Further Information
#echo: I removed the # to see how it operates, it brings up a blank
cmd window and performs no further action. In order to even run the things in the points below, I left the # off.
I deleted "/c start" from the second system() line: this opens one command line with the usual white text at the top, and a second over the top that is completely blank.
I changed the first line as follows, and commented out the second:
system("cmd /c start echo off" "\n" commands "")
--- this got a similar result to the second dot-point, but only with one cmd window, the black (no text one)
If I don't include the "\n" marker then I get a cmd window with text of "off" commands (where commands is the defined string above).
If I only have the system("cmd /c start " commands "") line, and not the echo line, then a cmd window briefly flashes and disappears and no further results demonstrating the success of the script appear.
So my issue is this: I know this script works when run directly through command line, the problem I have is that I cannot now run it through dxl.
I have developed a solid work-around that does exactly what I need.
The issue was that the input I had dxl writing was not going through command line correctly.
Knowing that the script ran from cmd correctly and, in turn, that the script executed from a batch file correctly, and that I could run the batch file from dxl, my solution was as follows:
Define the paths in dxl using the format C:\PROGRA~1\PATHNAME\
Using the Stream write() command to write the instructions directly
to a .bat file
Then using the system() command to run the .bat file
I have included some of my code, so that maybe it might help someone attempting to do the same thing. (I'll gladly take any advice on better programming conventions.)
// functions used: genFileName(), assume if a variable is not declared here, it was declared under my globals
// genFileName() returns a string of the file name, replacing any " " with "_" so cmd doesn't cry when I run it
string basename = genFileName()
string fcsv = basename ".csv"
string csvPath = "blahblahthefilepath" fcsv
if(fileExists_(csvPath)) isFile = true
Stream fOut = append(csvPath)
// === if file does not exist, create, give column names
if( !isFile){
fOut << "Date and Time,count1,count2,count3,count4" "\n"
}
else ack ("File name exists, append stats to file?" // may not be necessary
// === print to file ===
fOut << datetime "," ctot "," ctc "," cti "," ctnc "\n"
// ===== Create Batch file to run grapher ===
string columnsToPlot = "1 3 1 4 1 5" // ==> may develop this to allow user to choose
string graphDim = "800 600" // ==> px dim, may develop for user choice
string fbat = basename ".bat"
string batPath = "blahblahthefilepath"
Stream batOut = write(batPath fbat)
batOut << "#echo off" "\n"
batOut << "title Batch file to plot statistics for " fcsv "\n"
batOut << "cscript //nologo " batPath "csvplot.vbs " batPath fcsv " " batPath basename ".png " graphDim " " columnsToPlot ""
system("cmd /c start " batPath fbat "")
// some infoBox feedback DB to tell the user that the files were created
Good luck to anyone else who is attempting something similar, and I hope this is of use to someone.
Does running the dxl script without the # in front of the echo command work?

Show status of Rails test in status bar of Vim?

Is there any known Vim plugin that would show if a Rails test is passing or failing by showing a green or red bar respectively. I personally find Growl notifications quite distracting and am looking for an alternative but can't find anything. Something similar to this is what I'm aiming for..
EDIT: I'm using MacVim on OSX 10.6
Have you tried Autotest::Screen?
I'm not a ruby person, but the trick was not so complicated.
All you need to do is to detect file changes under the specific project directory, run your test then bring test result back to Screen's status bar.
This is what I have in my $VIMFILES/ftplugin/python/tests.vim file:
function! RunAllTests(args)
silent ! echo -e "\033[1;36mRunning all unit tests\033[0m"
if filereadable(getcwd() . "/runtests.py")
set makeprg=./runtests.py\ --with-machineout
elseif filereadable(getcwd() . "/manage.py") && filereadable(getcwd() . "/settings.py")
set makeprg=./manage.py\ test\ --with-machineout\ --verbosity=0
else
set makeprg=nosetests\ --with-machineout
endif
exec "make! " . a:args
endfunction
function! JumpToError()
if getqflist() != []
for error in getqflist()
if error['valid']
break
endif
endfor
let error_message = substitute(error['text'], '^ *', '', 'g')
silent cc!
if error['bufnr'] != 0
exec ":sbuffer " . error['bufnr']
endif
call RedBar()
echo error_message
else
call GreenBar()
echo "All tests passed"
endif
endfunction
function! RedBar()
hi RedBar ctermfg=white ctermbg=red guibg=red
echohl RedBar
echon repeat(" ",&columns - 1)
echohl None
endfunction
function! GreenBar()
hi GreenBar ctermfg=white ctermbg=green guibg=green
echohl GreenBar
echon repeat(" ",&columns - 1)
echohl None
endfunction
let maplocalleader=","
nmap <LocalLeader>a :call RunAllTests("--machine-out")<cr>:redraw<cr>:call JumpToError()<cr>
nmap <LocalLeader>A :call RunAllTests("")<cr>
I originally based it off Gary's .vimrc, and then moved some things around and recoded the method that shows the bars.
I'm sure it can be easily adapted to ruby and whatever it uses for test running (I'm not a ruby guy). This currently uses nosetests for running python tests.
Hope that helps!

Getting return status AND program output

I need to use Lua to run a binary program that may write something in its stdout and also returns a status code (also known as "exit status").
I searched the web and couldn't find something that does what I need. However I found out that in Lua:
os.execute() returns the status code
io.popen() returns a file handler that can be used to read process output
However I need both. Writing a wrapper function that runs both functions behind the scene is not an option because of process overhead and possibly changes in result on consecutive runs. I need to write a function like this:
function run(binpath)
...
return output,exitcode
end
Does anyone has an idea how this problem can be solved?
PS. the target system rung Linux.
With Lua 5.2 I can do the following and it works
-- This will open the file
local file = io.popen('dmesg')
-- This will read all of the output, as always
local output = file:read('*all')
-- This will get a table with some return stuff
-- rc[1] will be true, false or nil
-- rc[3] will be the signal
local rc = {file:close()}
I hope this helps!
I can't use Lua 5.2, I use this helper function.
function execute_command(command)
local tmpfile = '/tmp/lua_execute_tmp_file'
local exit = os.execute(command .. ' > ' .. tmpfile .. ' 2> ' .. tmpfile .. '.err')
local stdout_file = io.open(tmpfile)
local stdout = stdout_file:read("*all")
local stderr_file = io.open(tmpfile .. '.err')
local stderr = stderr_file:read("*all")
stdout_file:close()
stderr_file:close()
return exit, stdout, stderr
end
This is how I do it.
local process = io.popen('command; echo $?') -- echo return code of last run command
local lastline
for line in process:lines() do
lastline = line
end
print(lastline) -- the return code is the last line of output
If the last line has fixed length you can read it directly using file:seek("end", -offset), offset should be the length of the last line in bytes.
This functionality is provided in C by pclose.
Upon successful return, pclose() shall return the termination status
of the command language interpreter.
The interpreter returns the termination status of its child.
But Lua doesn't do this right (io.close always returns true). I haven't dug into these threads but some people are complaining about this brain damage.
http://lua-users.org/lists/lua-l/2004-05/msg00005.html
http://lua-users.org/lists/lua-l/2011-02/msg00387.html
If you're running this code on Win32 or in a POSIX environment, you could try this Lua extension: http://code.google.com/p/lua-ex-api/
Alternatively, you could write a small shell script (assuming bash or similar is available) that:
executes the correct executable, capturing the exit code into a shell variable,
prints a newline and terminal character/string onto standard out
prints the shell variables value (the exit code) onto standard out
Then, capture all the output of io.popen and parse backward.
Full disclosure: I'm not a Lua developer.
yes , your are right that os.execute() has returns and it's very simple if you understand how to run your command with and with out lua
you also may want to know how many variables it returns , and it might take a while , but i think you can try
local a, b, c, d, e=os.execute(-what ever your command is-)
for my example a is an first returned argument , b is the second returned argument , and etc.. i think i answered your question right, based off of what you are asking.

Anyone written a script that turns gcov branch coverage output into compiler warning format?

Most code editors know how to take a reasonably standard format of file_path, line_number, message_text and hyperlink into code. They know how because that's how they display compiler warnings. Visual Studio does it, and Source Insight is trivial to configure for that. If I have to I will write the script myself, but maybe someone has done so already.
Looking for a script that takes gcc's gcov branch coverage output and translates it into that format.
You can process the output of gcov with a simple awk script:
/.*:.*:/ {
split($2,tokens,":");
LINE=tokens[1];
}
/#####:/ { print FILENAME ":" LINE ": warning: Line not executed"}
/branch/ {
if ($4 == 0)
print FILENAME ":" LINE ": warning: " $0;
}
Run with awk -f warn.awk main.c.gcov
and it will convert the output to:
main.c.gcov:35: warning: branch 0 taken 0 (fallthrough)
main.c.gcov:37: warning: Line not executed

Resources