Vim【编译并】运行快捷键

31

我想在vim中设置一个键盘快捷键,用于编译和运行当前正在编辑的C、C++或Python程序。伪代码如下:

when a shortcut key is pressed:
    if current_extension == 'c' then
        shell: gcc this_filename.c -o this_filename_without_extension
        if retcode == 0 then shell: ./this_filename_without_extension
    else if current_extension == 'cpp' then
        shell: g++ this_filename.cpp -o this_filename_without_extension
    if retcode == 0 then shell: ./this_filename_without_extension
    else if current_extension == 'py' then
        shell: python this_filename.py
    end if
end key

我知道我可能要求有些过分,但如果这是可能的话,我会很喜欢!


你可以在vim中运行终端命令,这是你所询问的吗? - aaronman
3
aaronman:是的,但是每次键入 :!g++ filename.cpp -o filename 然后输入 ./filename 很慢,所以我想将其作为键盘快捷方式。 - user1318194
2
为什么不使用 Makefile - Karoly Horvath
对于提交到编程竞赛的单文件程序,使用makefile肯定是过度设计。 - user1318194
就我个人而言,我建议也使用一个 makefile。如果您不打算将您的 vim 脚本提交到比赛中,那么您可以不提交 makefile,但是 make 更加健壮。 - idoby
在大多数情况下,make 命令可能会过度使用。如果您的编译需要在 shell 脚本中编写超过一两行的代码,那么您可能正在做错什么。我认为,一旦您开始运行 make 文件来创建 Makefile,那么您就已经越过了界限。 - SO_fix_the_vote_sorting_bug
6个回答

43

类似这样的方式可以起作用。只需创建一个文件类型自动命令,将<F4>或其他你想要的按键映射到保存、编译和运行程序上。它使用exec来构建字符串,并使用shellescape来转义文件名。

autocmd filetype python nnoremap <F4> :w <bar> exec '!python '.shellescape('%')<CR>
autocmd filetype c nnoremap <F4> :w <bar> exec '!gcc '.shellescape('%').' -o '.shellescape('%:r').' && ./'.shellescape('%:r')<CR>
autocmd filetype cpp nnoremap <F4> :w <bar> exec '!g++ '.shellescape('%').' -o '.shellescape('%:r').' && ./'.shellescape('%:r')<CR>

%是当前缓冲区文件名。%:r是不带扩展名的缓冲区文件名。


如果你要创建很多这样的文件,最好写一个函数。虽然我认为使用make是正确的方法,因为这种方法只适用于你只需要创建单个源文件的情况。这对于临时文件可能有效,但对于任何项目都不适用。 - FDinoff
我增加的一件事是在最后一个 && 前面添加了 && echo Compilation complete,因为编程竞赛问题无法提示输入,所以我不知道何时编译完成。但再次感谢您提供的出色解决方案! - user1318194
1
这些映射应该是缓冲区本地的。此外,最好使用:make而不是直接调用编译器。 - Luc Hermitte
感谢您提供这个有用的答案。假设我想添加C++ 11标志,我只需将!g++'更改为'g++ -std=c++11'在*(...) '!g++ '.shellescape('%').'(...)*中吗? - Konrad
大多数情况下它都能正常工作,但要注意以下情况:如果在同一个文件夹中存在example.c和example.cpp这两个文件,那么当我编译并运行这两个文件时,它就无法正常工作。 - Zip

8

http://singlecompile.topbug.net 看起来比你想要的更多。为了一个更简单的解决方案,你也可以将以下内容添加到你的vimrc文件中:

au BufEnter *.cpp set makeprg=g++\ -g\ %\ -o\ %< 
au BufEnter *.c set makeprg=gcc\ -g\ %\ -o\ %< 
au BufEnter *.py set makeprg=python\ % 
au BufEnter *.[rR] set makeprg=Rscript\ %
map <F5> :call CompileGcc()<CR>
func! CompileGcc()
    exec "w" 
    silent make
endfunc

HTH


谢谢你的回答。我已经通过@FDinnof的解决方案得到了我想要的,但这应该作为有用的参考。 - user1318194

6

现在是2018年,vim 8已经发布了2年,并且与所有主流Linux发行版和Mac OS X一起发布。但是很多vim教程仍然教授十年前的内容。

您可以使用一些专为Vim 8或NeoVim设计的插件,像Sublime Text或NotePad++一样方便地编译C ++ / Java程序。

例如,AsyncRun插件将允许您在后台运行shell命令并实时从快速修复窗口中读取输出。 查看屏幕截图

就像在IDE中编译程序一样,编译错误将通过错误格式进行匹配,并进行突出显示并变得可选择。您可以在快速修复窗口中导航错误或继续编辑而不编译。

快速设置

将以下行复制并粘贴到您的vimrc中:

Plug 'skywind3000/asyncrun.vim'

" open quickfix window automatically when AsyncRun is executed
" set the quickfix window 6 lines height.
let g:asyncrun_open = 6

" ring the bell to notify you job finished
let g:asyncrun_bell = 1

" F10 to toggle quickfix window
nnoremap <F10> :call asyncrun#quickfix_toggle(6)<cr>

当您在命令行中输入“:AsyncRun echo hello”时:
点击这里查看截图 您将在打开的快速修复窗口中实时查看命令输出。
编译和运行单个文件
使用AsyncRun编译单个文件比Sublime Text的构建系统简单得多。我们可以为此设置F9:
noremap <silent> <F9> :AsyncRun gcc -Wall -O2 "$(VIM_FILEPATH)" -o "$(VIM_FILEDIR)/$(VIM_FILENOEXT)" <cr>

$(..)形式中的宏将被展开为实际的文件名或目录,然后我们就可以使用F5运行可执行文件:

noremap <silent> <F5> :AsyncRun -raw -cwd=$(VIM_FILEDIR) "$(VIM_FILEDIR)/$(VIM_FILENOEXT)" <cr>

双引号用于处理包含空格的路径名。选项-cwd=$(VIM_FILEDIR)表示在文件所在目录中运行文件。使用绝对路径名$(VIM_FILEDIR)/$(VIM_FILENOEXT)是因为Linux需要一个./前缀来运行当前目录下的可执行文件,但Windows不需要。使用二进制文件的绝对路径名可以处理这个跨平台问题。
另一个选项-raw表示输出将不会被vim的errorformat匹配,并且将按其本身显示在快速修复窗口中。现在你可以按F9编译你的文件,在快速修复窗口中检查编译错误,按F5运行二进制文件。
构建C/C++项目
无论你使用什么构建工具,make还是cmake,项目构建都意味着对一组文件进行操作。它需要定位项目根目录。AsyncRun使用一种称为根标记的简单方法来识别项目根目录。项目根目录被识别为包含以下任一目录或文件的当前文件的最近祖先目录之一:
let g:asyncrun_rootmarks = ['.svn', '.git', '.root', '_darcs'] 

如果任何一个父目录中都没有这些根标记,那么当前文件所在的目录将被用作项目根目录。这使我们可以使用<root>$(VIM_ROOT)来表示项目根目录。并且可以设置F7键以构建当前项目:

noremap <silent> <F7> :AsyncRun -cwd=<root> make <cr>

如果您的当前项目没有在任何git或subversion存储库中,该怎么办?如何找到我的项目根目录?解决方案非常简单,只需在项目根目录中放置一个空的.root文件,就可以轻松地定位它。
接下来,设置F8运行当前项目:
noremap <silent> <F8> :AsyncRun -cwd=<root> -raw make run <cr>

该项目将在其根目录中运行。当然,您需要在自己的 makefile 中定义运行规则。然后将 F6 重新映射为测试。
noremap <silent> <F6> :AsyncRun -cwd=<root> -raw make test <cr>

如果您使用cmake,F4可以映射到更新您的Makefile:

nnoremap <silent> <F4> :AsyncRun -cwd=<root> cmake . <cr>

由于c运行时的实现,如果进程正在运行的是非tty环境,则stdout中的所有数据都会被缓冲,直到进程退出。因此,如果您想看到实时输出,必须在printf语句之后加上fflush(stdout),或者在开头关闭stdout缓冲区。
setbuf(stdout, NULL);

同时,如果您编写C++代码,可以在std::cout的末尾添加std::endl。这会强制刷新stdout缓冲区。如果您在Windows上开发,则AsyncRun可以为子进程打开一个新的cmd窗口:

nnoremap <silent> <F5> :AsyncRun -cwd=$(VIM_FILEDIR) -mode=4 "$(VIM_FILEDIR)/$(VIM_FILENOEXT)" <cr>
nnoremap <silent> <F8> :AsyncRun -cwd=<root> -mode=4 make run <cr>

在Windows操作系统上使用选项-mode=4将打开一个新的提示窗口来运行命令,就像在Visual Studio中运行命令行程序一样。最后,我们有以下按键映射:
  • F4:用cmake更新Makefile。
  • F5:运行单个文件
  • F6:运行项目测试
  • F7:构建项目
  • F8:运行项目
  • F9:编译单个文件
  • F10:切换快速修复窗口
这更像是NotePad++和GEdit中的构建系统。如果您经常使用cmake,则可以编写一个简单的shell脚本,位于~/.vim/script/build.sh,将F4和F7组合在一起:如果CMakeList.txt已更改,则会更新Makefile,然后执行make。 高级用法 您还可以在dotfiles存储库中定义shell脚本,并使用F3执行该脚本:
nnoremap <F3> :AsyncRun -cwd=<root> sh /path/to/your/dotfiles/script/build_advanced.sh <cr>

以下是AsyncRun定义的shell环境变量:

$VIM_FILEPATH  - File name of current buffer with full path
$VIM_FILENAME  - File name of current buffer without path
$VIM_FILEDIR   - Full path of current buffer without the file name
$VIM_FILEEXT   - File extension of current buffer
$VIM_FILENOEXT - File name of current buffer without path and extension
$VIM_CWD       - Current directory
$VIM_RELDIR    - File path relativize to current directory
$VIM_RELNAME   - File name relativize to current directory 
$VIM_ROOT      - Project root directory
$VIM_CWORD     - Current word under cursor
$VIM_CFILE     - Current filename under cursor
$VIM_GUI       - Is running under gui ?
$VIM_VERSION   - Value of v:version
$VIM_COLUMNS   - How many columns in vim's screen
$VIM_LINES     - How many lines in vim's screen
$VIM_SVRNAME   - Value of v:servername for +clientserver usage

所有上述环境变量都可以在您的 build_advanced.sh 中使用。使用外部 shell 脚本文件可以执行比单个命令更复杂的工作。 Grep 符号 有时,如果您的远程 Linux 箱没有良好设置的环境,则 grep 是在源代码中搜索符号定义和引用的最便宜的方法。现在我们将使用 F2 在光标下搜索关键字。
if has('win32') || has('win64')
    noremap <F2> :AsyncRun! -cwd=<root> grep -n -s -R <C-R><C-W> --include='*.h' --include='*.c*' '<root>' <cr>
else
    noremap <F2> :AsyncRun! -cwd=<root> findstr /n /s /C:"<C-R><C-W>" "\%CD\%\*.h" "\%CD\%\*.c*" <cr>
endif

以上脚本将在你的项目根目录中运行grep或findstr,并仅在.c.cpp.h文件中查找符号。现在我们移动光标并按F2键,当前项目中的符号引用将立即显示在快速修复窗口中。

这个简单的按键映射对于大多数情况已经足够了。你可以改进这个脚本以支持更多的文件类型或其他grep工具,在你的vimrc中。

这就是在Vim 8或NeoVim中构建/运行C/C++项目的实用方法。就像Sublime Text的构建系统和NotePad++的NppExec一样。

不再使用过时的vim教程,尝试一些新的东西吧。


3

我知道我来晚了7年。我尝试了其他答案的代码,但结果并没有令我满意,所以我尝试了这个:

autocmd filetype cpp nnoremap <F9> :w<bar>term ++shell g++ %:p -o %:p:r && %:p:r<CR>

当按下 F9 键时,它(与上面所有答案一样)会编译并执行当前文件。输出结果显示在一个 :terminal 区域中的分屏中。您可以更改 g++ 命令以运行其他语言代码。

enter image description here

这个程序被保存在gist中,另一个映射用于显示输出结果,同样在一个分割的部分中,但是在一个新文件中,所以你可以保存输出结果。

希望能对某些人有所帮助。


1
这是我的地图,它真的可以工作,您可以将其更新为任何语言:
" <!----------------------------" gcc compile C files----------------------------------------!>
autocmd filetype c nnoremap <Leader>c :w <CR>:!gcc % -o %:r && ./%:r<CR>

" <!----------------------------" java compile files----------------------------------------!>
autocmd filetype java nnoremap <Leader>c :w <CR>:!javac % :r&& java %:r<CR>
" <!----------------------------" php compile files----------------------------------------!>
autocmd filetype php nnoremap <Leader>c :w <CR>:!clear && php  %<CR>

如果您正在vim中编辑文件,请在正常模式下执行以下操作:

<leader>c

0

我也想找捷径。但出于某种原因,我不想使用autocmd。我使用了bash脚本。我已经在使用一个bash脚本来编译和运行我的C/C++代码。所以,我想,为什么不使用一个bash脚本,并将其用于C和C++的fltplugin文件中。我制作了两个单独的bash脚本。一个用于C,另一个用于C++。这是C的脚本(对于C++,它也类似,只需将编译器更改为clang++/g++,将std=c2x更改为std=c++20,将$filename.c更改为$filename.cpp),

filename=$1
compiler=clang-10
if [ $2 ]; then
    if [ $2 == "-v" ]; then
        FLAGS="-g -Wall -Wextra -pedantic -std=c2x"
    fi
else
    FLAGS="-Wall -Wextra -pedantic -std=c2x"
fi

$compiler $FLAGS $filename.c -o $filename -lm

return_value=$?

if [ $return_value -eq 0]; then
    if [ $2 ]; then
        if [ $2 == "-v" ]; then
            valgrind ./$filename
            rm $filename
        fi
    else
        ./$filename
        echo
        echo "[process exited $return_value]"
        rm $filename
    fi
fi

将其保存为run。我将其设置为所有用户可执行。

$ chmod 777 run

我把它移动到了我的用户bin目录。

$ mv run ~/bin

如果您没有用户bin目录,请创建一个。进入您的主目录,然后创建一个名为bin的目录。
$ cd ~
$ mkdir bin

然后将运行文件(或您为脚本指定的任何名称)移动到bin目录中。
$ mv run ~/bin

我们继续吧。 然后我在~/.vim/after/ftplugin目录下创建了一个c.vim文件,并向c.vim文件中添加了两个新的按键映射。

nnoremap <F9> :vertical bo term run %:r<CR>

# This will run my code with valgrind
nnoremap <F2> :vertical bo term run %:r -v<CR> 

这里是一些屏幕截图,

点击F9会在vim缓冲区中显示输出屏幕,

点击F2会在vim缓冲区中使用valgrind显示输出

对于C++也可以采用同样的方法,只需在~/.vim/after/ftplugin目录下创建一个cpp.vim文件。然后再创建另一个run文件(如run_cpp),将其保存到用户的bin~/bin)文件夹中。进行一些自定义按键绑定,就可以使用了。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接