Vim [编译和]运行快捷方式

Posted

技术标签:

【中文标题】Vim [编译和]运行快捷方式【英文标题】:Vim [compile and] run shortcut 【发布时间】:2013-08-20 05:03:07 【问题描述】:

基本上我想要的是 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: 是的,但是每次输入:!g++ filename.cpp -o filename 然后./filename 都很慢,所以我想把它作为键盘快捷键。 你为什么不使用Makefile @KarolyHorvath 让一个文件程序提交给编程竞赛,makefile 肯定是矫枉过正。 对于它的价值,我也投票支持使用 makefile。如果你没有提交你的 vim 脚本到比赛中,你也可以不提交 makefile,但是 make 更健壮。 【参考方案1】:

这样的事情会奏效。只需创建映射<F4> 的文件类型 autocmd 或您想要保存并编译和运行程序的任何内容。它使用 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 是没有扩展名的缓冲区文件名

【讨论】:

@DuncanNZ 如果您要创建很多这些,您可能需要编写一个函数。尽管我确实认为使用 make 的解决方案是正确的方法,因为这仅在您只需要创建单个源文件时才有效。这可能适用于临时文件,但不适用于任何项目。 我在最后一个&amp;&amp;之前加了一个&amp;&amp; echo Compilation complete ,因为编程竞赛题不能提示输入所以不知道什么时候编译的。但再次感谢您提供的出色解决方案! 这些映射应该是缓冲区本地的。此外,:make 优于直接调用编译器。 感谢这个方便的答案,假设我想添加 C++ 11 标志,我是否应该将 (...) '!g++ '.shellescape('%').' (...) 中的 !g++ ' 更改为 'g++ -std=c++11 '?跨度> 【参考方案2】:

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 的解决方案我已经有了我想要的东西,但这应该作为有用的参考。【参考方案3】:

现在是 2018 年,vim 8 已经发布了 2 年,并附带了所有平均流 Linux 发行版和 Mac OS X。但是很多 vim 教程仍然在教人们十年前的东西。

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

例如,AsyncRun 插件将允许您在后台运行 shell 命令并实时读取 quickfix 窗口的输出。 See the screen capture.

就像在 IDE 中编译程序一样,编译错误将通过 errorformat 匹配并突出显示并成为可选的。您可以在快速修复窗口中导航错误或在编译时继续编辑。

快速设置

将以下行复制并粘贴到您的 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”时:

see the capture here

您将在打开的 quickfix 窗口中看到实时命令输出。

编译并运行单个文件

使用 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 的错误格式匹配,并且会在quickfix 中按原样显示。现在您可以使用 F9 编译您的文件,在 quickfix 窗口中检查编译错误,然后按 F5 运行二进制文件。

构建 C/C++ 项目

无论您使用什么构建工具,make 或 cmake,项目构建都意味着对一组文件进行操作。它需要找到项目根目录。 AsyncRun 使用一种称为根标记的简单方法来识别项目根。项目根被标识为当前文件的最近的祖先目录,其中包含以下目录或文件之一:

let g:asyncrun_rootmarks = ['.svn', '.git', '.root', '_darcs'] 

如果没有父目录包含这些根标记,则将当前文件的目录用作项目根目录。这使我们能够使用&lt;root&gt;$(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)。或者您可以在开头关闭标准输出缓冲区

setbuf(stdout, NULL);

同时,如果您正在编写 C++ 代码,可以将 std::endl 附加到 std::cout 的末尾。它可以强制刷新标准输出缓冲区。如果你是在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,你可以在~/.vim/script/build.sh写一个简单的shell脚本来组合F4和F7:如果CMakeList.txt已经改变,它会更新Makefile,然后执行make。

高级用法

您还可以在您的 dotfiles 存储库中定义 shell 脚本并使用 F3 执行该脚本:

nnoremap <F3> :AsyncRun -cwd=<root> sh /path/to/your/dotfiles/script/build_advanced.sh <cr>

以下 shell 环境变量由 AsyncRun 定义:

$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,当前项目中的符号引用将立即显示在quickfix窗口中。

这个简单的键盘映射在大多数情况下就足够了。您可以改进此脚本以在您的 vimrc 中支持更多文件类型或其他 grep 工具。

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

不再有过时的 vim 教程,尝试新的东西。

【讨论】:

【参考方案4】:

这是我的地图,它确实有效,您可以将其更新为任何语言:

" <!----------------------------" 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

【讨论】:

【参考方案5】:

我知道我是 7 年后来到这里的。我尝试了其他答案的代码,结果不满意,所以我尝试了这个:

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

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

它保存在gist 中,并带有另一个映射来显示输出,也在一个拆分的部分中,但在一个新文件中,因此您可以保存输出。

希望它可以帮助某人。

【讨论】:

【参考方案6】:

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

然后将 run(或您为脚本指定的任何名称)文件移动到 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> 

这里是一些屏幕截图,

Clicking F9 shows me the output screen in a vim buffer,

Clicking F2 shows me the output using valgrind in a vim buffer

你可以对 C++ 做同样的事情,只需在 ~/.vim/after/ftplugin 目录中创建一个 cpp.vim 文件。并制作另一个run 文件(如run_cpp),将其保存在您的用户bin(~/bin) 文件中。做一些自定义键绑定,你就可以开始了。

【讨论】:

以上是关于Vim [编译和]运行快捷方式的主要内容,如果未能解决你的问题,请参考以下文章

VIM:可用的快捷方式?

vim常用快捷键

linux文件查看和vim快捷操作方式

Linux命令行快捷键及vim快捷方式

text Vim快捷方式

markdown Vim快捷方式