如何在Vim中为命令创建别名?

175

Vim是我编程时首选的文本编辑器,因此我经常遇到一个非常烦人的问题。

经常情况下,当我需要快速保存缓冲区并继续进行其他杂项任务时,我会执行典型的

:w

然而,我总是——似乎超过50%的时间——将:w大写。自然地,Vim会报错因为W是无效的命令:

E492: Not an editor command: W

我的问题是如何在Vim中创建别名Colon命令。特别是,你能否举例说明如何将W别名为w

我知道可以通过映射键来执行某些命令,但这不是我想要的。


1
可能是Can I (re-) map commands in vim?的重复问题。 - Chris Morgan
1
为了避免 :W,您可以映射一个键来执行保存操作。如果您习惯于使用 Ctrl-s 进行保存,则可以使用 $VIM/mswin.vim 中的以下映射: " Use CTRL-S for saving, also in Insert mode noremap <C-S> :update<CR> vnoremap <C-S> <C-C>:update<CR> inoremap <C-S> <C-O>:update<CR> - mMontu
Vi Stack exchange 上的类似问题:https://vi.stackexchange.com/q/2665/7244 - Flimm
8个回答

151

要保持自动完成不变,请尝试使用

cnoreabbrev W w

这将会在命令行中用w替换W,但只有在它前后都不是单词字符的情况下才会进行替换。因此:W<CR>将被替换为:w<CR>,但:Write则不会受影响。(请注意,这会影响与之匹配的所有命令,包括您可能意想不到的命令。例如,命令:saveas W Z将被替换为:saveas w Z,因此请小心使用。)

更新

以下是我现在会如何写:

cnoreabbrev <expr> W ((getcmdtype() is# ':' && getcmdline() is# 'W')?('w'):('W'))

作为一个函数:

fun! SetupCommandAlias(from, to)
  exec 'cnoreabbrev <expr> '.a:from
        \ .' ((getcmdtype() is# ":" && getcmdline() is# "'.a:from.'")'
        \ .'? ("'.a:to.'") : ("'.a:from.'"))'
endfun
call SetupCommandAlias("W","w")

通过检查命令类型是否为:以及命令是否为W,因此它比仅使用cnoreabbrev W w更安全。


3
这个答案对我来说是最安全和最可靠的。 - Sean
2
如果您使用推荐的解决方案,请注意下面两个命令都将像较低的那个一样工作,这可能会根据实际缓冲区内容和VIM设置呈现出意外的结果::%s/W/foo/g<CR> :%s/w/foo/g<CR> - cprn
2
实际上,这意味着W将被替换任何地方在命令栏中,包括例如在搜索中,因此s/W foo/bar/g将被转换为s/w foo/bar/g。这可能会很快变得烦人。请参阅我的答案以获取全面的解决方案。 - airstrike
4
当然,这个想法太糟糕了。你绝对不能、永远不要、绝对不要这样做。 - Chris Morgan
4
:cnoreabbrev <expr> W getcmdtype()==':'&&getcmdline()=~#'^W'?'w':'W' - kev
显示剩余12条评论

117

通过进一步搜索,我发现有人几乎问了与我相同的问题

:command <AliasName> <string of command to be aliased>

请注意,正如Richo指出的那样,用户命令必须以大写字母开头才能奏效。


6
使用":command"命令是一个好的解决方案。":cnoreabbrev"命令无法理解"cmd1|cmd2",而":command"命令可以。 - Pavel Strakhov
1
一个更清晰的写法是::command AliasName 要被别名的命令字符串 - Alec Jacobson
9
这不会处理/转发任何“命令”参数,比如“-nargs”,“-complete”等。 - blueyed
:Q!:W!怎么样? - Jacklynn
13
只是非常字面意思:将 :command W w 放入 .vimrc 文件中。 - isomorphismes

28

我发现将;键映射为:会是一个更好的解决方案,而且可以让你在输入其他命令时更高效。

nnoremap ; :
vnoremap ; :

3
这是 Vim 的最佳技巧。我现在用惯了它,每当遇到正常行为时,我都需要尝试几次来重新训练我的思维。 - airstrike
2
这不是问题的答案。 - Flimm
11
不,但它可以解决原帖作者的问题。 - Dessa Simpson
4
当使用tf时,通常可以使用;跳转到下一个出现位置。即使将分号映射为冒号,也可以安全地将其反向映射。不会出现别名循环。nnoremap : ; - Luc

12
最好的解决方案是编写自定义函数来处理仅在命令栏开头发生的缩写。
为此,请将以下内容添加到您的vimrc文件或任何其他地方。
" cabs - less stupidity                                                      {{{
fu! Single_quote(str)
  return "'" . substitute(copy(a:str), "'", "''", 'g') . "'"
endfu
fu! Cabbrev(key, value)
  exe printf('cabbrev <expr> %s (getcmdtype() == ":" && getcmdpos() <= %d) ? %s : %s',
    \ a:key, 1+len(a:key), Single_quote(a:value), Single_quote(a:key))
endfu
"}}}

 

" use this custom function for cabbrevations. This makes sure that they only
" apply in the beginning of a command. Else we might end up with stuff like
"   :%s/\vfoo/\v/\vbar/
" if we happen to move backwards in the pattern.

" For example:
call Cabbrev('W', 'w')

以下是我从源材料中找到的一些有用的缩写:

call Cabbrev('/',   '/\v')
call Cabbrev('?',   '?\v')

call Cabbrev('s/',  's/\v')
call Cabbrev('%s/', '%s/\v')

call Cabbrev('s#',  's#\v')
call Cabbrev('%s#', '%s#\v')

call Cabbrev('s@',  's@\v')
call Cabbrev('%s@', '%s@\v')

call Cabbrev('s!',  's!\v')
call Cabbrev('%s!', '%s!\v')

call Cabbrev('s%',  's%\v')
call Cabbrev('%s%', '%s%\v')

call Cabbrev("'<,'>s/", "'<,'>s/\v")
call Cabbrev("'<,'>s#", "'<,'>s#\v")
call Cabbrev("'<,'>s@", "'<,'>s@\v")
call Cabbrev("'<,'>s!", "'<,'>s!\v")

2
有一个内置函数 string(),它与你的 Single_quote() 做的事情是一样的。 - ZyX

7

假设您想在gvim中为tabnew命令添加别名。您只需在.vimrc文件中键入以下命令(如果不在主文件夹中,则创建一个):

cabbrev t tabnew

3
这将导致类似于 :saveas t example 的命令被替换为 :saveas tabnew example - Flimm

5
也许你想将功能键(F1..F12)之一映射到:w?那么请将以下内容放入您的.vimrc文件中:
noremap  <f1> :w<return>
inoremap <f1> <c-o>:w<return>

(在插入模式下按ctrl-o可以暂时切换到正常模式)。


5

最安全和最简单的方法是使用像cmdalias.vim这样的插件,或者像我最近更新的vim-alias一样的插件,它们考虑了以下因素:

  • 前面的空格或修饰符,如:sil(ent)(!):redi(r),
  • 范围修饰符,如'<,'>表示当前可视选择区域,
  • 转义特殊字符,如引号,并且
  • 检查所选别名是否为有效的命令行缩写。

1

我认为 @ZyX 的回答很好,但如果你正在使用更新版本的 Neovim(0.5+),你可能想要使用 Lua 定义这个函数。以下是一种可能的方法:

function _G.abbreviate_or_noop(input, output)
  local cmdtype = vim.fn.getcmdtype()
  local cmdline = vim.fn.getcmdline()

  if (cmdtype == ":" and cmdline == input) then 
    return output
  else
    return input
  end
end

function SetupCommandAlias(input, output)
  vim.api.nvim_command("cabbrev <expr> " .. input .. " " .. "v:lua.abbreviate_or_noop('" .. input .. "', '" .. output .. "')")
end

然后,你需要从call SetupCommandAlias("pg", "postgres://")中删除call,并像这样使用函数:SetupCommandAlias("pg", "postgres://")
注意:如果在.vim文件而不是.lua文件中使用它,则需要在函数调用前加上lua前缀,即lua SetupCommandAlias("pg", "postgres://")

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