[vim/vim] How do you compile or build projects within Vim? (A look at current methods and potential enhancements) (Discussion #19982)

10 views
Skip to first unread message

rendcrx

unread,
Apr 14, 2026, 10:47:58 PM (2 days ago) Apr 14
to vim/vim, Subscribed

Hi

I’ve been trying to find a good way to compile/build code from inside Vim.

The methods I currently know of are as follows:

  1. Use tmux or Ctrl-z to suspend Vim and perform compilation or building
  2. Use the :! command
  3. Use the :make command
  4. Use the :term command
  5. Use external plugins such as vim-dispatch or asyncrun

However, I believe all these approaches have drawbacks:

Method 1: tmux / Ctrl-z

Cannot make use of quickfix.

Method 2: :! command

Also cannot use quickfix, and produces extra output that clutters the terminal screen. If I run it multiple times, it becomes difficult to locate the output.

I have set these three autocommands to work around this:

set t_te=
autocmd VimLeavePre     * set t_te&
autocmd VimSuspend      * set t_te&
autocmd VimResume       * set t_te=

However, with large amounts of output I can no longer scroll up to review it, nor can I revisit the output later. make and grep suffer from the same issue.

Method 3: :make command

make output is not syntax-highlighted.

You have to manually set the compiler beforehand.

If I just want to compile the current file with gcc, it is cumbersome — I have to manually change makeprg.

When using other build commands, :make is not very intuitive, since it is not actually running the make utility.

Additionally, I have set 'autochdir' for convenience when switching files with :e, which causes :make to run in the current directory. However, many projects have their Makefile in the top-level directory.

(If I avoid 'autochdir' and use :find instead of :e, the completion for :find includes header file directories as well.)

Method 4: :term command

Feels like a very good solution so far, with highlighted output. Although it does not integrate with quickfix natively, I can use :cgetbuf later to make use of quickfix.

Method 5: External plugins (vim-dispatch, AsyncRun, etc.)

In my opinion, plugins are meant to improve or add new features, not completely change the existing workflow. They require learning new uppercase commands.

Moreover, many core features of these plugins rely on integration with external tools like tmux or external terminals.I believe compilation and building should be handled more effectively by Vim itself.

I have some ideas for improvement:

  1. Could we add an option to :!, :make, and :grep to run them via term_start? This would allow asynchronous execution, let us navigate the output using Vim's built-in movement commands, and also provide syntax highlighting. If we automatically run cgetbuf at the end, we could also make use of the quickfix feature.

  2. Add a compile command that executes the following build command through term_start(we can use :compile gcc -g % to compile our file). We could then use cgetbuf to capture the compilation results. This would be very convenient when frequently switching compilers or build commands, as we would no longer need to manually set makeprg. We could also automatically detect the command being run (similar to vim-dispatch) and switch the errorformat accordingly.

  3. In the output window, we could use the g command to repeat the last executed command. The advantage is that the window preserves the previous execution directory and environment. This way, even if we navigate deep into the project directory, pressing g would still run the build from the project root. This idea is inspired by Emacs.

This might also be a good solution for #5411, because we use a separate window to display the original text.And we also don't need 'autochroot' to build project #16070.

Additionally, could term_start support a "non-scrolling" mode? I've noticed that Emacs's compile command does not scroll automatically. I'm not sure if this would be a good idea to adopt in Vim. Perhaps it would be better to avoid auto-scrolling during compilation, but jump straight to the error output if the build fails? But there might be people like me who often run commands like :term go doc strings. This would behave similarly to running go doc strings | less or gcc --help | less in bash. It would be much better if :term could support non-scrolling output.

This is my current idea, and I’m not sure if there’s a better solution.

I'm curious if others have found better workflows, or if there are existing solutions I've missed. Looking forward to hearing your thoughts!


Reply to this email directly, view it on GitHub, or unsubscribe.
You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/repo-discussions/19982@github.com>

Gary Johnson

unread,
Apr 15, 2026, 12:48:20 AM (2 days ago) Apr 15
to reply+ACY5DGHIXF7RJ2ZV7R...@reply.github.com, vim...@googlegroups.com
On 2026-04-14, rendcrx (Vim Github Repository) wrote:
> Hi
>
> I’ve been trying to find a good way to compile/build code from
> inside Vim.

This is what the :make command is intended to do.

> The methods I currently know of are as follows:
>
> 1. Use tmux or Ctrl-z to suspend Vim and perform compilation or building
> 2. Use the :! command
> 3. Use the :make command
> 4. Use the :term command
> 5. Use external plugins such as vim-dispatch or asyncrun
>
> However, I believe all these approaches have drawbacks:

> Method 3: :make command
>
> make output is not syntax-highlighted.

True.

> You have to manually set the compiler beforehand.
>
> If I just want to compile the current file with gcc, it is
> cumbersome — I have to manually change makeprg.

Neither is necessarily true. The default :make command is make, and
make has internal rules to make a specified target from a source
file in various languages. For example, if you edit foo.c to add
a C main(), then save it, you can build foo by executing

:make foo

Even without a Makefile, the make program knows how to compile foo
from foo.c using cc, which on my system is a link to gcc.

> When using other build commands, :make is not very intuitive,
> since it is not actually running the make utility.

By default, it is using make. It is also "intuitive" if you've been
using Unix for a while. If you want :make to run something else,
you can specify it in a filetype plugin, or use a project Makefile
that specifies how to build a given program. There are all sorts of
ways to expand on those ideas to have a :make command that "just
works".

> I'm curious if others have found better workflows, or if there are
> existing solutions I've missed. Looking forward to hearing your
> thoughts!

I have mainly used one of two workflows.

1. Use a Makefile or a 'makeprg' that is smart enough to figure out
what to build from the argument targets I specify. I have
a plugin that configures Vim according to the project Vim starts
in or the file Vim first edits, so selecting a build system for
each project is pretty easy and the build command is always the
same: :make.

2. Run make or a similar command externally using a simple wrapper
script that tees stdour and stderr to a file. This lets me see
the full color output of the build command. If the output
contains an error, I run another wrapper script around Vim that
uses that build output file as an argument to Vim's -q option to
put the errors into the quickfix list.

This method has the feature of allowing the use of less on the
build output to get scrolling and coloring.

Regards,
Gary

vim-dev ML

unread,
Apr 15, 2026, 12:48:54 AM (2 days ago) Apr 15
to vim/vim, vim-dev ML, Your activity

On 2026-04-14, rendcrx (Vim Github Repository) wrote:
> Hi
>
> I’ve been trying to find a good way to compile/build code from
> inside Vim.

This is what the :make command is intended to do.

> The methods I currently know of are as follows:
>
> 1. Use tmux or Ctrl-z to suspend Vim and perform compilation or building

> 2. Use the :! command
> 3. Use the :make command
> 4. Use the :term command
> 5. Use external plugins such as vim-dispatch or asyncrun

>
> However, I believe all these approaches have drawbacks:

> Method 3: :make command
>
> make output is not syntax-highlighted.

True.


> You have to manually set the compiler beforehand.
>
> If I just want to compile the current file with gcc, it is
> cumbersome — I have to manually change makeprg.

Neither is necessarily true. The default :make command is make, and
make has internal rules to make a specified target from a source
file in various languages. For example, if you edit foo.c to add
a C main(), then save it, you can build foo by executing

:make foo

Even without a Makefile, the make program knows how to compile foo
from foo.c using cc, which on my system is a link to gcc.

> When using other build commands, :make is not very intuitive,
> since it is not actually running the make utility.

By default, it is using make. It is also "intuitive" if you've been
using Unix for a while. If you want :make to run something else,
you can specify it in a filetype plugin, or use a project Makefile
that specifies how to build a given program. There are all sorts of
ways to expand on those ideas to have a :make command that "just
works".

> I'm curious if others have found better workflows, or if there are
> existing solutions I've missed. Looking forward to hearing your
> thoughts!

I have mainly used one of two workflows.

1. Use a Makefile or a 'makeprg' that is smart enough to figure out
what to build from the argument targets I specify. I have
a plugin that configures Vim according to the project Vim starts
in or the file Vim first edits, so selecting a build system for
each project is pretty easy and the build command is always the
same: :make.

2. Run make or a similar command externally using a simple wrapper
script that tees stdour and stderr to a file. This lets me see
the full color output of the build command. If the output
contains an error, I run another wrapper script around Vim that
uses that build output file as an argument to Vim's -q option to
put the errors into the quickfix list.

This method has the feature of allowing the use of less on the
build output to get scrolling and coloring.

Regards,
Gary


Reply to this email directly, view it on GitHub.
You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/repo-discussions/19982/comments/16565633@github.com>

rendcrx

unread,
Apr 15, 2026, 1:26:07 AM (2 days ago) Apr 15
to vim/vim, vim-dev ML, Comment

Thank you for mentioning Makefile and the default build workflow.

I do like Makefile, but it has some drawbacks as I mentioned earlier.

When I’m working on small, standalone files, I often need to switch between different commands. For example:

gcc -S to inspect assembly
gcc -O2 / gcc -O0 to compare compiler output
go test -race for testing, go run to execute a Go program

Writing a dedicated Makefile for every such case feels cumbersome.And constantly changing makeprg for these one-off commands is also very tedious.It would be much more convenient if I could run a command directly like this, without leaving Vim, and still capture errors :compile gcc -S -O2 %

Using Makefile requires running make in the right directory, otherwise I have to specify the path manually.If I’m working in a deeply nested directory with autochdir enabled, make becomes inconvenient.

It would be better to have a dedicated compilation window that remembers the environment and the last command.Then I could press a shortcut (like g in Emacs) to re-run the build.That way I could browse files anywhere and just switch to the compilation window to rerun the last build command.

I’m happy to use Makefile for my own C projects.But for other people’s projects or non-C projects, the default make doesn’t feel very convenient.


Reply to this email directly, view it on GitHub.
You are receiving this because you commented.Message ID: <vim/vim/repo-discussions/19982/comments/16565877@github.com>

Gary Johnson

unread,
Apr 15, 2026, 3:23:56 AM (2 days ago) Apr 15
to reply+ACY5DGDKEWKSF3YB3E...@reply.github.com, vim...@googlegroups.com
On 2026-04-14, rendcrx (Vim Github Repository) wrote:
> Thank you for mentioning Makefile and the default build workflow.
>
> I do like Makefile, but it has some drawbacks as I mentioned earlier.
>
> When I’m working on small, standalone files, I often need to switch between
> different commands. For example:
>
> gcc -S to inspect assembly
> gcc -O2 / gcc -O0 to compare compiler output
> go test -race for testing, go run to execute a Go program
>
> Writing a dedicated Makefile for every such case feels cumbersome.And
> constantly changing makeprg for these one-off commands is also very tedious.It
> would be much more convenient if I could run a command directly like this,
> without leaving Vim, and still capture errors :compile gcc -S -O2 %

You could write your own command, e.g. :Compile, that would take as
arguments the compiler command line. Your command could assign the
that command line to 'makeprg' and run :make. It might have to
configure 'efm' as appropriate for each compiler, but something
would have to do that no matter what solution you chose.

If you don't like the idea of using commands that begin with an
upper-case letter, there are solutions for that, too, using command
abbreviations.

> Using Makefile requires running make in the right directory, otherwise I have
> to specify the path manually.If I’m working in a deeply nested directory with
> autochdir enabled, make becomes inconvenient.

You could have your custom build command search for the project root
directory, i.e., the directory in which you would normally execute
your build command. The project root directory is often
identifiable as containing the project's build configuration file
(e.g. Makefile) or revision control system directory (e.g. .git).
Your build command could cd to that directory before executing the
actual build command, using :cd or :lcd within Vim, or using cd in
the shell command that launches the build.

> It would be better to have a dedicated compilation window that remembers the
> environment and the last command.Then I could press a shortcut (like g in
> Emacs) to re-run the build.That way I could browse files anywhere and just
> switch to the compilation window to rerun the last build command.

You could do that, too. You would presumably execute some command
of your own to set up that window, and that command could use :lcd
to set that window's working directory.

Unfortunately, as far as I can tell, Vim does not have window-local
or buffer-local history. To retrieve the last build command, you
might have to search for it, but that's pretty easy. Just type the
start of the command and the <Up> key. See the paragraph
immediately preceding the ":help :history" entry.

Alternatively, you could have your build command cache the last
compile command and use it if you didn't give your command any
arguments, for example.

> I’m happy to use Makefile for my own C projects.But for other
> people’s projects or non-C projects, the default make doesn’t feel
> very convenient.

I've been working on these issues for years and have found solutions
that work well for me. The problems I've run into have usually been
due to my not fully understanding the problem or the behavior
I wanted rather than to any limitation of Vim.

Regards,
Gary

vim-dev ML

unread,
Apr 15, 2026, 3:24:29 AM (2 days ago) Apr 15
to vim/vim, vim-dev ML, Your activity

On 2026-04-14, rendcrx (Vim Github Repository) wrote:
> Thank you for mentioning Makefile and the default build workflow.
>
> I do like Makefile, but it has some drawbacks as I mentioned earlier..


Reply to this email directly, view it on GitHub.
You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/repo-discussions/19982/comments/16567067@github.com>

Char

unread,
Apr 15, 2026, 6:19:59 AM (2 days ago) Apr 15
to vim/vim, vim-dev ML, Comment

Another ideas

  1. make the error line clickable in :term
def TermClick()
	if &buftype == 'terminal'
		const line = getline('.')
		if line =~ '.+:\d+:\d+: error: \d+'
			# TODO: show the error message using popup or text properties
			# open the file at the error line
			ed file
			cursor(lnum, col)
	endif
enddef

nnoremap <LeftMouse> <scriptcmd>TermClick()<CR>
nnoremap <S-CR> <scriptcmd>TermClick()<CR>
  1. use ch_listen to open a port in vim, create a wrapper program that run the compiler and then notify vim that the compile has finished, process the compile error to quickfix, run the wrapper in :term


Reply to this email directly, view it on GitHub.
You are receiving this because you commented.Message ID: <vim/vim/repo-discussions/19982/comments/16568896@github.com>

Mao-Yining

unread,
6:50 AM (3 hours ago) 6:50 AM
to vim/vim, vim-dev ML, Comment

Relate: #18188


Reply to this email directly, view it on GitHub, or unsubscribe.

You are receiving this because you commented.Message ID: <vim/vim/repo-discussions/19982/comments/16601855@github.com>

Mao-Yining

unread,
6:51 AM (3 hours ago) 6:51 AM
to vim/vim, vim-dev ML, Comment

This is an enhancement of using terminal. https://github.com/habamax/.vim/blob/master/autoload/terminal.vim


Reply to this email directly, view it on GitHub.
You are receiving this because you commented.Message ID: <vim/vim/repo-discussions/19982/comments/16601867@github.com>

rendcrx

unread,
7:32 AM (2 hours ago) 7:32 AM
to vim/vim, vim-dev ML, Comment

Thanks for sharing! It’s great to see related work and enhancements around the terminal usage.


Reply to this email directly, view it on GitHub, or unsubscribe.

You are receiving this because you commented.Message ID: <vim/vim/repo-discussions/19982/comments/16602191@github.com>

Reply all
Reply to author
Forward
0 new messages