在控制台Vim中使用CMD映射

fir*_*dev 2 macos vim terminal keyboard-shortcuts iterm2

有没有办法Cmd在终端中使用Vim键?MacVim做的事情.

我已经重新映射Cmd+S到iTerm2发送Esc:w!<CR>以保存Vim中的文件,但这听起来有点弱.

有了iTerm和Vim的所有功能,应该有一些方法可以做到这一点吗?

ech*_*son 5

这是可能的,但它需要一些做,并有一些缺点.

第一组问题是:

  • 虽然iTerm2可以接受Command密钥并将它们传递给终端程序,但它只能通过将它们重新映射为其他东西来实现,例如Left Option或者Control密钥.
  • 它只能重新映射每个修饰键无处不在iTerm2; 你不能仅仅针对某些窗口,窗格或配置文件.
  • 为了在终端中重新映射修改器,您需要允许iTerm2在"安全和隐私"偏好设置面板中控制计算机; 当您配置iTerm2进行重新映射时,它会自动指向您.
  • 一旦你重新映射Left Command,像Left Command+ N打开一个新窗口的东西不再有效.幸运的是,您可以重新映射Left Option到Mac Left Command并且仍然可以将Right Command密钥用于Mac快捷方式; 或者你可以切换你的Command和Option钥匙.

剩下的问题是Vim没有开箱即用的配置类似Mac的键绑定.幸运的是,MacVim在/Applications/MacVim.app/Contents/Resources/vim/runtime/macmap.vim中包含了大部分Mac风格的GUI绑定,而不是在Interface Builder或Objective-C源代码中对它们进行编码; 所以我能够复制它们并使它们适应终端.这是一个与MacVim(和其他Mac应用程序)非常接近的配置:

" Keybindings for terminal vim; mostly to emulate MacVim (somewhat)

" See /Applications/MacVim.app/Contents/Resources/vim/runtime/macmap.vim (from
" which many of the following are taken); and menu.vim in the same directory.

" Since these all have native (Cmd-modified) versions in MacVim, don't bother
" defining them there.

" A utility function to help cover our bases when mapping.
"
" Example of use:
"   call NvicoMapMeta('n', ':new<CR>', 1)
" is equivalent to:
"   exec "set <M-n>=\<Esc>n"
"   nnoremap <special> <Esc>n :new<CR>
"   vnoremap <special> <Esc>n <Esc><Esc>ngv
"   inoremap <special> <Esc>n <C-o><Esc>n
"   cnoremap <special> <Esc>n <C-c><Esc>n
"   onoremap <special> <Esc>n <Esc><Esc>n
function! NvicoMapMeta(key, cmd, add_gv)
    " TODO: Make this detect whether key is something that has a Meta
    " equivalent.
    let l:keycode = "<M-" . a:key . ">"

    let l:set_line = "set " . l:keycode . "=\<Esc>" . a:key

    let l:nmap_line = 'nmap <silent> <special> ' . l:keycode . ' ' . a:cmd
    let l:vnoremap_line = 'vnoremap <silent> <special> ' . l:keycode . ' <Esc>' . l:keycode
    if(a:add_gv)
        let l:vnoremap_line.='gv'
    endif
    let l:inoremap_line = 'inoremap <silent> <special> ' . l:keycode . ' <C-o>' . l:keycode
    let l:cnoremap_line = 'cnoremap <special> ' . l:keycode . ' <C-c>' . l:keycode
    let l:onoremap_line = 'onoremap <silent> <special> ' . l:keycode . ' <Esc>' . l:keycode

    exec l:set_line
    exec l:nmap_line
    exec l:vnoremap_line
    exec l:inoremap_line
    exec l:cnoremap_line
    exec l:onoremap_line
endfunction

" I can't think of a good function to assign to Meta+n, since in MacVim Cmd+N
" opens a whole new editing session.

" Meta+Shift+N
" No equivalent to this in standard MacVim. Here " it just opens a window on a
" new buffer.
call NvicoMapMeta('N', ':new<CR>', 1)

" Meta+o
" Open netrw file browser
call NvicoMapMeta('o', ':split %:p:h<CR>', 1)

" Meta+w
" Close window
call NvicoMapMeta('w', ':confirm close<CR>', 1)

" Meta+s
" Save buffer
call NvicoMapMeta('s', ':confirm w<CR>', 1)

" Meta+Shift+S
" Save as
" TODO: This is silent, so you can't tell it's waiting for input. If anyone can
" fix this, please do!
call NvicoMapMeta('S', ':confirm saveas ', 1)

" Meta+z
" Undo
call NvicoMapMeta('z', 'u', 1)

" Meta+Shift+Z
" Redo
call NvicoMapMeta('Z', '<C-r>', 1)

" Meta+x
" Cut to system clipboard (requires register +")
exec "set <M-x>=\<Esc>x"
vnoremap <special> <M-x> "+x

" Meta+c
" Copy to system clipboard (requires register +")
exec "set <M-c>=\<Esc>c"
vnoremap <special> <M-c> "+y

" Meta+v
" Paste from system clipboard (requires register +")
exec "set <M-v>=\<Esc>v"
nnoremap <silent> <special> <M-v> "+gP
cnoremap <special> <M-v> <C-r>+
execute 'vnoremap <silent> <script> <special> <M-v>' paste#paste_cmd['v']
execute 'inoremap <silent> <script> <special> <M-v>' paste#paste_cmd['i']

" Meta+a
" Select all
call NvicoMapMeta('a', ':if &slm != ""<Bar>exe ":norm gggH<C-o>G"<Bar> else<Bar>exe ":norm ggVG"<Bar>endif<CR>', 0)

" Meta+f
" Find regexp. NOTE: MacVim's Cmd+f does a non-regexp search.
call NvicoMapMeta('f', '/', 0)

" Meta+g
" Find again
call NvicoMapMeta('g', 'n', 0)

" Meta+Shift+G
" Find again, reverse direction
call NvicoMapMeta('G', 'N', 0)

" Meta+q
" Quit Vim
" Not quite identical to MacVim default (which is actually coded in the app
" itself rather than in macmap.vim)
call NvicoMapMeta('q', ':confirm qa<CR>', 0)

" Meta+Shift+{
" Switch tab left
call NvicoMapMeta('{', ':tabN<CR>', 0)

" Meta+Shift+}
" Switch tab right
call NvicoMapMeta('}', ':tabn<CR>', 0)

" Meta+t
" Create new tab
call NvicoMapMeta('t', ':tabnew<CR>', 0)

" Meta+Shift+T
" Open netrw file browser in new tab
call NvicoMapMeta('T', ':tab split %:p:h<CR>', 0)

" Meta+b
" Call :make
call NvicoMapMeta('b', ':make<CR>', 1)

" Meta+l
" Open error list
call NvicoMapMeta('l', ':cl<CR>', 1)

" TODO: We need to configure iTerm2 to be able to send Cmd+Ctrl+arrow keys, so
" we can duplicate the :cnext/:cprevious/:colder/:cnewer bindings to those keys
" in MacVim.
" There may be a few others I've missed, too.
Run Code Online (Sandbox Code Playgroud)

该NvicoMakeMeta函数将传递给它的密钥的元密钥修改版本映射到指定的操作; "元键"这里只是Command- 或 - 修改的通用术语Option.名称中的"Nvico"表示它以普通,可视,插入,命令和操作员暂挂模式映射的事实.

由于Vim在解释ESC字符时的工作方式(这是所有元键序列的开头以及箭头键等),如果我们只是映射ESC加上另一个字符的序列,我们最终会得到Vim等待明显的接收到实际Esc按键后的时间量(例如,表示希望返回正常模式).我的函数避免这种情况的方法是使用:set设置密钥代码(请参阅参考资料:help :set-termcap).

总而言之,这是您需要做的:

  • 将上面的代码放在.vimrc中.
  • 转到一般的iTerm2首选项(iTerm菜单> Preferences…); 去Keys标签; 重新映射您想要用作修饰符的任何键,以便它发送Left Option或Right Option.按照建议允许iTerm2控制计算机; 它应该为您打开正确的首选项窗格.您必须先单击锁定并输入密码,然后才能选中iTerm旁边的复选框.
  • 打开您要使用的终端配置文件的首选项(例如,Preferences在Profiles选项卡中的应用程序范围的对话框中打开它),并确保+Esc选中Left option key acts as(和/或Right option key acts as,取决于您是否已创建元键)发送Right Option).