DEV Community

woland
woland

Posted on • Edited on

Run any code from Vim

Do you want to quickly run and test any code without leaving Vim and without even opening a :terminal?

There is a very simple way of achieving this. All we need is a runner function, a run command and a mapping to call it.

Here is an example:

function! CodeRunner()
    if &ft ==# 'python'
        execute 'RPy'
    endif
endfunction
Enter fullscreen mode Exit fullscreen mode

and the run command:

command! RPy :!python3 %
Enter fullscreen mode Exit fullscreen mode

and the mapping:

nnoremap <F12> :call CodeRunner()<CR>
Enter fullscreen mode Exit fullscreen mode

I prefer to wrap the mapping and the run command inside an augroup:

augroup code_runner
    au!
    au FileType python command! RPy :!python3 %
    nnoremap <F12> :call CodeRunner()<CR>
augroup end
Enter fullscreen mode Exit fullscreen mode

Place the above snippets in your .vimrc and source it. Now, every time you’re inside a python file, you can just press F12 to run the code and see its output.

You can add other languages to the function and to the augroup too:

"===[ Execute From Vim ]==="
function! CodeRunner()
   if &ft ==# 'python'
     execute 'RPy'
   elseif &ft ==# 'sh'
     execute 'RB'
   elseif &ft ==# 'javascript'
     execute 'RJs'
   elseif &ft ==# 'php'
     execute 'RPHP'
   elseif &ft ==# 'go'
     execute 'RGo'
   endif
endfunction
Enter fullscreen mode Exit fullscreen mode

And the corresponding augroup:

augroup code_runner
    au!
    au FileType python command! RPy :!python3 %
    au FileType sh command! RB :!bash %
    au FileType javascript command! RJs :!node %
    au FileType go command! RGo :!go run %
    au FileType php command! RPHP :!php %
    nnoremap <F12> :call CodeRunner()<CR>
augroup end
Enter fullscreen mode Exit fullscreen mode

With this, you can run bash, python, javascript, golang and php with the F12 key.

A better idea

While the above script works, it goes without saying that it's a mess, we can improve it by using a dictionary and checking the ft variable against it.

function! CodeRunner()
    silent !clear

    let file_commands = {
                \ 'python': 'python3',
                \ 'sh': 'bash',
                \ 'javascript': 'node',
                \ 'go': 'go run',
                \ 'php': 'php',
                \ 'perl': 'perl'
                \ }

    let ft = &filetype

    if has_key(file_commands, ft)
        let cmd = file_commands[ft]
        execute '!' . cmd . ' %'
    else
        echo "No run command defined for filetype: " . ft
    endif
endfunction

command! CodeRunner call CodeRunner()
nnoremap <F12> :CodeRunner<CR>
Enter fullscreen mode Exit fullscreen mode

A similar method can be used for compiled languages, such as C.

Example:

function! CompileAndRun()
  let current_file = expand('%')
  let file_name = fnamemodify(current_file, ':t:r')
  let compile_cmd = 'gcc ' . current_file . ' -o ' . file_name . ' && ./' . file_name
  execute '!'.compile_cmd
endfunction

Enter fullscreen mode Exit fullscreen mode

The function above, will compile the C code inside the current buffer using gcc, and then execute the binary output.

Naturally, we need a corresponding mapping:

nnoremap <F8> :call CompileAndRun()<CR>
Enter fullscreen mode Exit fullscreen mode

More arguments can be added to the compile _cmd to serve your needs.

Update

You can improve the CodeRunner and CompileAndRun functions by converting them to vim9script.

vim9script

def CodeRunner()
    var file_commands = {
        'python': 'python3',
        'sh': 'bash',
        'javascript': 'node',
        'go': 'go run',
        'php': 'php',
        'perl': 'perl',
    }
    # filetype variable
    var ft = &filetype

    # check if ft is found in the dict
    if has_key(file_commands, ft)
        var cmd = file_commands[ft]
        silent !clear
        execute '!' .. cmd .. ' %' 
    else
        echo "No run command defined for filetype: " .. ft
    endif
enddef

# compile the functions 
defcompile

command! CodeRunner call CodeRunner()
nnoremap <F12> :CodeRunner<CR>
Enter fullscreen mode Exit fullscreen mode

We can also improve our CompileAndRun function to display the compiler errors, which is something that we could've done in legacy vim9script as well, but oh well ...

# Compile and Run with Error Split
def CompileAndRun()
    var current_file = expand('%')
    var file_name = fnamemodify(current_file, ':t:r')
    var compile_cmd = 'gcc ' .. current_file .. ' -o ' .. file_name

    # Compile
    var compile_result = systemlist(compile_cmd)

    # If there are errors
    if v:shell_error != 0
        botright new +setlocal\ buftype=nofile\ bufhidden=wipe\ noswapfile
        file CompileErrors
        call setline(1, compile_result)
        return
    endif

    # Run binary
    execute 'terminal ./' .. file_name
enddef

defcompile
Enter fullscreen mode Exit fullscreen mode

I think the comments are enough to let you know what is going on. It simply compiles and executes the binary while showing the results in a split, and if there are errors, it simply displays them in a split.

You can also set this up to automatically close the error split when you switch back to your code.

# Global variable to set if error split is closed automatically
g:compile_err_auto_close = 0

# Augroup to automatically close the error split
if g:compile_err_auto_close == 1
    augroup AutoCloseCompileErrors
        autocmd!
        autocmd! BufEnter * if bufname('CompileErrors') != '' | execute 'bdelete! CompileErrors' | endif
    augroup END
endif
Enter fullscreen mode Exit fullscreen mode

Note

You only need to write defcompile once at the end of your script to compile all the functions in that file. So if you plan to put these functions in one vimscript file, you only need one defcompile entry for both of them.

Note Two

For a more complete and in depth script with an improved CompileAndRun function in Vim9Script, see my other article. How to write vim9script or How to Compile and Run C code from Vim

Top comments (0)