I am configuring vimrc file for competitive programming in c++.
autocmd filetype cpp nnoremap <F5> :w <CR>
:!g++ -o %:r % <CR> :!./%:r < input.txt <CR>
The following mapping is when I press F5, it saves, compiles (%:r is filename without .cpp), and runs with input.txt file.
However not every cpp file has input.txt, so I want to pipe input.txt only when input.txt exists in the current directory.
To sum up, what is the best way of checking files during the mapping?
Unless you're working in an environment where gnu make is not available, or poorly configured (mingw), you won't need any Makefile for mono-file projects. They are not mandatory in that case, and IMO more of an hassle than anything else.
:make %<
This way any errors directly go to the quickfix windows. This will improve your productivity to locate errors (:h quickfix). Also, whether your current file is in C, C++, Fortran... or any language recognized by default gnumake configuration, you don't have to specify the compiler you wish to use depending on the current filetype. If you really want to select another compiler for C++ for instance, you can use
:let $CXX ='clang++'
" $CC for C, and so on
And if you want to change your compilation options
:let $CXXFLAGS = '-std=c++17 -Wall -Werror'
" $CFLAGS for C, $LDLIBS, $LDFLAGS for the linker, and so on
Note also that if you have a Makefile, it'll be automatically used.
:!make %< && ./%< indeed is simple enough to chain both steps. Alas, we don't have the direct equivalent with :make. We have to analyse the quickfix list to see if there were any issue
If filter(getqflist(), 'v:val.valid != 0') is not empty we can know whether problems have been detected. But it doesn't tell whether they are warnings or errors. We can have the complete information with the following
" From my build-tools-wrapper plugin
function! lh#btw#build#_get_metrics() abort
let qf = getqflist()
let recognized = filter(qf, 'get(v:val, "valid", 1)')
" TODO: support other locales, see lh#po#context().tranlate()
let errors = filter(copy(recognized), 'v:val.type == "E" || v:val.text =~ "\\v^ *(error|erreur)"')
let warnings = filter(copy(recognized), 'v:val.type == "W" || v:val.text =~ "\\v^ *(warning|attention)"')
let res = { 'all': len(qf), 'errors': len(errors), 'warnings': len(warnings) }
return res
endfunction
From this we can decide to stop just on errors, or on errors and warnings.
With filereadable() we can know whether the input file is here.
It thus becomes:
let exec_line = '!./' . expand('%<') " we could also use the complete path instead
let input = expand('%:p:h')/.'input.txt'
if filereadable(input)
let exec_line .= ' < ' . input
endif
exe exec_line
If you want to redirect the result in a :terminal, this time unfortunately, redirection cannot be used with Vim (it works with nvim though)
The final code (given the previous function to detect errors & warnings) becomes.
function s:build_and_run(file) abort
let tgt = fnamemodify(a:file, ':r')
" to make sure the buffer is saved
exe 'update ' . a:file
exe 'make ' . tgt
if lh#btw#build#_get_metrics().errors
echom "Error detected, execution aborted"
copen
return
endif
let path = fnamemodify(a:file, ':p:h')
let exec_line = '!./' . tgt
let input = path.'/input.txt'
if filereadable(input)
let exec_line .= ' < ' . input
endif
exe exec_line
endfunction
nnoremap µ :<C-U>call <sid>build_and_run(expand('%'))<cr>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With