I want to be able to compile c++ code from within vim without running a new terminal and calling the compiler. **How can I do it ** please
3 Answers
(I've answered this question so many times that I've lost the count, and impossible to find a complete answer in the lot... I'll bookmark this answer this time...)
Everything starts with Vim quickfix feature which permits to integrate compilation, and to navigate error messages. The starting point is :make
. To navigate error messages, you'll need , :copen
, :cc
, :cnext
, :cclose
... In short, read documentation about Vim :h quickfix
feature.
Using :!g++ whatever
, or :!make sometarget
, instead of :make sometarget
(without the bang !
) is what we used to do 20ish years ago with vi -- we had no choice at the time. Vim integrates call to compilation chains. Let's take advantage of it (in particular in C++ where we can have an extremely long compiler output when templates are involved or when no matching operator<<
is found)
Now. There are several use cases:
1. Mono-file projects, everywhere but on MinGW
This is the main use case for users doing competitive programming.
TL;DR: To compile foo.cpp
, (or even foo.c
or foo.f95
...) we just need to type :make %<
. And... that's all!.
No need to write a Makefile
, nor to tweak 'makeprg'
option. Thanks to gnumake default configuration, there is nothing more to do.
Actually, a little bit more is required to pass options. It can be done separately through the main following gnumake options:
$CC
,$CXX
compiler for C, or C++ (respectively)$CFLAGS
,$CXXFLAG
,$FFLAGS
flags used to compile C, C++ and Fortran programs$LDFLAGS
flags to pass to the linker$LDLIBS
libraries to link the executable with
e.g.
:let $CXX = 'clang++'
:let $CXXFLAGS = '-std=c++17 -O2 -Wall -Wextra -Ipath/to/my/boost/include`
:let $LDFLAGS = '-Lpath/to/my/boost/lib'
:let $LDLIBS = '-lboost_datetime'
Notes:
Everything is with the expectation current working directory is the one where the current file is stored. Some minor tweaking may be required otherwise.
These variables could also be exported in our shell, where we could compile with
make foo
.Actually, everything I've just said works in the console as well, and that's what this Vim solution relies upon to work. For instance:
CXX='clang++' CXXFLAGS='-std=c++17 -Wall -Wextra -g -fno-omit-frame-pointer -fsanitize=address,undefined' make foo
My unpackaged/confidential
PkgConfig
plugin can automatically set these variables from the pkg config files present on our system. It's in two parts: the autoload plugin, the plugin, and the library plugin it depends upon.Some other non gnu-systems may not support this solution either.
I have hinted it: any language for which gnumake has default implicit variables and implicit rules is supported with this approach. I'm using it for C and C++, IIRC I've also used it for Fortran but I'm not sure, and it seems that Pascal compilers compatible with
pc
are supported as well (let me know if you have more info).
To execute?
" (with the expectation current working directory is the one where the file is stored)
"
" either
:!./%<
" or (which is not compatible with redirections with non-neo Vim at this time)
:term ./%<
Need for mappings?
" Compile
nnoremap <silent> <F7> :<c-u>make %<<cr>
" Execute
nnoremap <silent> <c-f5> :<c-u>term ./%<<cr>
Chain compilation with execution
This one is a little bit more complex. We can trick Vim with :make %< && ./%<
which will also send the execution results into the quickfix window. I've never tested it with interactive programs (i.e. that read from stdin
/std::cin
).
For a better integrated solution, a little bit of vim scripting is required. See this related answer on SO: https://stackoverflow.com/a/56991040/15934
2. Mono-file projects, on MinGW
Sorry the previous solution won't work with MinGW. The problem is that gnumake default configuration isn't shipped with MinGW packets.
Unless we patch gnumake configuration, we have to set 'makeprg'
. A typical setting is
" To compile the current file:
" Define the following once
:let &makeprg = 'g++ -Wall -Wextra -std=c++17 % -o %<.exe'
" Then compile with
:make
" Or to compile the specified target -- '$*' has a special meaning for :make
:let &makeprg = 'g++ -Wall -Wextra -std=c++17 $* -o $*.exe'
:make foo " foo.cpp -> foo
:make %< " current
Note: This solution would also work elsewhere. Yet, I wouldn't bother to change 'makeprg'
if I can avoid it. As we would have to play with ftplugins/autocommands if we want to have a 'makeprg'
for C and another for C++. i.e. if we want to work with mono-file projects in C and in C++.
Of course, with other programming languages we will have to set (locally!) this option, with :compiler
command typically. BTW, to use VisualStudio/MSDev compiler, 'makeprg'
(and the error format: 'errorformat'
) can be configured with :compiler msdev
.
3. Multi-file projects
In this use case we usually have a Makefile
(traditional solution), in which case we have to type :make target
as we would have type make target
in the console.
When there is a Makefile
, it takes the priority over gnumake default settings (i.e. solution 1), as it does in the console.
4. CMake based projects
More is required. At least :let &l:makeprg = 'cd buildir && make $*'
-- this shouldn't be set in buffers that don't belong to the project...
I'm maintaining a plugin, build-tools-wrapper that tries to autodetect the out-of-source build directory -- which is typical of CMake -- with various heuristics. It even permits to swap the build dir -- the idea is to model build modes (debug, release, release with debug info, sanitized...)
Background compilation
With :make
, the compilation is modal: it blocks vim user interface until it's finished. On mono-file projects this is not a problem, on real projects it can quickly become really annoying.
Since the late versions of Vim7.4-2xxx, we can finally compile in background an see the quickfix window being populated with messages as the compilation is going on. Alas, there is no standard/vanilla command in Vim to do that. It has to be done through vim scripting.
In other words, background compilation requires the support of plugins. Several exist. Again there is my build-tools-wrapper plugin. There is also AsynchRun (IIRC) and many more.
-
2
-
-
Don't hesitate to improve it (technical issues, English...). There is also more to say on related topics: compiler plugins, project settings... Jan 16, 2021 at 13:37
Vim is a text editor, not a shell. Although, Vim 8.1 has a built-in terminal that can be opened with the :term
command. It would look something like this:
To exit, you can use exit
command to quit from the integrated shell.
There's an alternative way and probably it's what you're looking for. You need to hit Esc and type the following:
! g++ -std=c++14 -o hello hello.cpp && ./hello
The output will be:
-
Or you could use
tmux
, or you couldctrl-z
to move yourvim
instance to the background while you compile and thenfg
to bring it back to the foreground, or...– scohe001Jan 7, 2021 at 17:46 -
@scohe001 I've added an alternative solution, maybe that's what the OP is asking for.– Rohan BariJan 7, 2021 at 17:48
-
Or we could use
:make
that Vim has been supporting for ages. No need to quit Vim to compile C++, on the contrary, it's much more ergonomic to scroll the error messages within the quickfix windows. Jan 7, 2021 at 20:27
If you use Makefiles, you can just call :make
from inside vim. If not, the behavior of the :make
can be customized by setting makeprg
.
You can also customize the way the output is interpreted in case there are errors, e.g., to Automatically open quickfix window on make. The entire system is fairly flexible, although in some cases creating a custom key-binding to make a call via the shell (using :!<cmd>
) is easier. You can always go back to the output using CTRL-Z
and return to vim using fg
.