Using a simple vimscript, we can easily compile and run any C code without leaving Vim! We will use vim9script for this.
Functions in vim9script are defined with the def keyword. Since Vim uses an algol-like syntax, enddef will denote the end of that function.
We will define several variables with the var keyword as vim9script doesn't use let.
vim9script def CompileAndRun() var current_file = expand('%') var file_name = fnamemodify(current_file, ':t:r')
As with every vim9script, we start by telling vim that this is in fact vim9script!.
Then we get the name of the current file using expand(), then using the fnamemodifty() and :t:t we will strip away the path and the extension, keeping only the basename.
Typically expand('%') returns filename.extension if you directly open a file with vim filename.extension but if the file was opened from a relative path, such as vim ~/some/path/filename.extension it would also return the path, so we have to clean it up.
We need another variable to store our compile command, A simple gcc compile command should do for starters:
var compile_cmd = 'gcc ' .. current_file .. ' -o ' .. file_name
The .. is how we concatinate strings, similar to php and some other languages. You may have also noticed that we are using the extracted file name for our output binary.
After that we can simply compile and execute the output binary.
var compile_result = systemlist(compile_cmd) execute 'terminal ./' .. file_name
So together:
def CompileAndRun() var current_file = expand('%') var file_name = fnamemodify(current_file, ':t:r') var compile_cmd = 'gcc ' .. current_file .. ' -o ' .. file_name var compile_result = systemlist(compile_cmd) execute 'terminal ./' .. file_name enddef defcompile
The defcompile command in the end does exactly what it says, which is compiling our functions.
Since vim9script functions are scoped differently than legacy vimscript, we need to define a command to access them.
command! CompileAndRun call CompileAndRun()
Now we can run :CompileAndRun.
Ok that's fine but what about errors? Warnings? WE NEED THOSE! So let's make it better.
Let us add a condition that checks for v:shell_error, see :h v:shell_error.
if v:shell_error != 0 || !empty(compile_result) botright new +setlocal\ buftype=nofile\ noswapfile\ bufhidden=wipe call setline(1, compile_result) return endif
Here is a summary, botright new creates a horizontal split, the setlocal options ensure that it's a scratch buffer, systemlist() executes the binary while handling newlines properly, setline() puts the results at the first line of the split buffer.
Let's also add -Wall to our compile command and create an unused variable in our simple test C code.
var compile_cmd = 'gcc -Wall ' .. current_file .. ' -o ' .. file_name
Not bad ey? We can still make it better!
Another perk of using systemlist() is that it already captured 2>&1 aka stdout and stderr for us so we don't need to perform a redirection.
Can we add syntax highlighting to the warnings and errors? Yes! Just add set filetype=c before call setline().
Can we auto-close this split? Yes! But it needs a bit more code.
We will give a name to our split buffer, then using an augroup auto close it on leave.
vim9script def CompileAndRun() var current_file = expand('%') var file_name = fnamemodify(current_file, ':t:r')
This ensures that when we change focus from a buffer with the name CompileErrors, the buffer with be deleted and therefor the split will be closed.
However, there may be times when we'd want to keep the split open as we work, so we can create a boolean to set it to 1 or 0 at runtime.
var compile_cmd = 'gcc ' .. current_file .. ' -o ' .. file_name
And we'll modify the augroup and format it nicely:
var compile_result = systemlist(compile_cmd) execute 'terminal ./' .. file_name
We will be applying the same checks and niceties to the run command as well. So instead of simply running the binary, we can do the following:
def CompileAndRun() var current_file = expand('%') var file_name = fnamemodify(current_file, ':t:r') var compile_cmd = 'gcc ' .. current_file .. ' -o ' .. file_name var compile_result = systemlist(compile_cmd) execute 'terminal ./' .. file_name enddef defcompile
You may have noticed that this time we are also determining the size of the split.
Let us add the last touch, which is the ability to define the compile flags at runtime. For that we will have to add a condition to check for a g:custom_compile_flag and initiate it to be empty at first since vim9script is picky with empty variables.
command! CompileAndRun call CompileAndRun()
And of course, we have to also modify our compile_cmd:
if v:shell_error != 0 || !empty(compile_result) botright new +setlocal\ buftype=nofile\ noswapfile\ bufhidden=wipe call setline(1, compile_result) return endif
Now we can change our compile flags at run time, just don't forget to add a space in the end!
Let's test:
As you can see, we can change our compile flags at runtime without any issues!
The whole script is as follows:
var compile_cmd = 'gcc -Wall ' .. current_file .. ' -o ' .. file_name
We've also added the F8 keybinding to access our command more easily.
Vim already has the compiler and make commands that you should add to your arsenal. This article only tries to teach some vim9script and some of the things that you can very easily achieve with it.
I hope that you enjoyed this article and if you did, leave a comment or a reaction.
The above is the detailed content of How to write vimript or How to Compile and Run C code from Vim. For more information, please follow other related articles on the PHP Chinese website!