2

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

0

3 Answers 3

8

(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.

3
  • 2
    I wish every new vim user would get to read this!
    – Biggybi
    Jan 16, 2021 at 12:55
  • Thanks @Biggybi :) Jan 16, 2021 at 13:33
  • 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
3

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:

Integrated Terminal in Vim

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

Vim Terminal P1

The output will be:

Vim Terminal P2

3
  • Or you could use tmux, or you could ctrl-z to move your vim instance to the background while you compile and then fg to bring it back to the foreground, or...
    – scohe001
    Jan 7, 2021 at 17:46
  • @scohe001 I've added an alternative solution, maybe that's what the OP is asking for.
    – Rohan Bari
    Jan 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
2

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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.