173

Vim is my preferred text editor when I program, and thus I always run into a particularly annoying issue.

Frequently, when I quickly need to save the buffer and continue on to some other miscellaneous task, I do the typical

:w

However, I always — what seems to be like more than 50% of the time — manage to capitalize that :w. Naturally, Vim yells at me because W is an invalid command:

E492: Not an editor command: W

My question is how can one alias colon-commands in Vim. Particularly, could you exemplify how to alias W to w.

I am aware of the process to map keys to certain commands, but that is not what I’m looking for.

ib.
  • 27,830
  • 11
  • 80
  • 100
Sean
  • 5,233
  • 5
  • 22
  • 26
  • 1
    possible duplicate of [Can I (re-) map commands in vim?](http://stackoverflow.com/questions/117150/can-i-re-map-commands-in-vim) – Chris Morgan May 23 '12 at 13:46
  • 1
    To avoid `:W` you could a map a key to perform the saving. If you are used to some program that saves with Ctrl-s, there are these mappings from $VIM/mswin.vim: `" Use CTRL-S for saving, also in Insert mode` `noremap :update` `vnoremap :update` `inoremap :update` – mMontu May 23 '12 at 14:21
  • Similar question on Vi Stack exchange: https://vi.stackexchange.com/q/2665/7244 – Flimm Aug 24 '18 at 12:23

8 Answers8

149

To leave completion untouched, try using

cnoreabbrev W w

It will replace W in command line with w, but only if it is neither followed nor preceded by word character, so :W<CR> will be replaced with :w<CR>, but :Write won’t. (Note that this affects any commands that match, including ones that you might not expect. For example, the command :saveas W Z will be replaced by :saveas w Z, so be careful with this.)

Update

Here is how I would write it now:

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

As a function:

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

This checks that the command type is : and the command is W, so it’s safer than just cnoreabbrev W w.

ib.
  • 27,830
  • 11
  • 80
  • 100
ZyX
  • 52,536
  • 7
  • 114
  • 135
  • 3
    This answer is the safest and most reliable for me. – Sean Oct 13 '10 at 01:57
  • 2
    Actually, this would mean W will be replaced *anywhere* in the command bar, including, for example, in searches, so s/W foo/bar/g would be turned into s/w foo/bar/g. this can get annoying really fast. see my answer for a comprehensive solution. – airstrike May 22 '12 at 19:17
  • 4
    Absolutely; this is a *horrible* idea. You should *never*, **ever**, ***ever*** do this. – Chris Morgan May 23 '12 at 13:43
  • 2
    If you use the recommended solution, please, be aware both of the two below commands will work as the lower one which may present an unexpected result depending on the actual buffer content and VIM settings: `:%s/W/foo/g` `:%s/w/foo/g` – cprn Apr 25 '12 at 02:00
  • 4
    `:cnoreabbrev W getcmdtype()==':'&&getcmdline()=~#'^W'?'w':'W'` – kev Jul 27 '12 at 13:32
  • 1
    @kev Strange manner of saying “you’ve case problems here”. And an abbreviation with another problem: type `:W W` But thanks, that was fixed. // I had two errors: `getcmdline() is# 'w'` which is impossible and swapped `'w'` and `'W'` in result. – ZyX Jul 27 '12 at 14:02
  • @kev By the way, using regular expressions where they are not needed is bad for performance. This is not perl, `getcmdline()[0] is# 'W'` serves the same purpose. – ZyX Jul 27 '12 at 14:05
  • @CelsoDantas Replace `cnoreabbrev` with `cnoremap` then. With `getcmdtype() …` condition it should not be too intrusive. Or define two abbreviations. – ZyX Apr 26 '13 at 19:44
  • @CelsoDantas Altering a file with some kind of custom command and then immediately quitting is dangerous anyway.... – Kyle Strand Jun 20 '13 at 18:02
  • 1
    You can use [cmdalias](http://www.vim.org/scripts/script.php?script_id=746) to simplify this to `:Alias W w` and it will do the getcmdline magic for you. – idbrii Dec 13 '13 at 19:09
  • With regards to your edit, my only regret is that the ```fun!``` had to eventually come to an end. – Seldom 'Where's Monica' Needy Mar 20 '15 at 22:22
  • I have a command without any arguments and to get it expanded I need to hit `space`. Is it possible to avoid this? – user14416 Jun 06 '16 at 16:32
  • @user14416 To *verify* that it gets expanded, not to *get* it expanded. `` expands abbreviations just as good as a space, just you are unable to check or react if expansion is unwanted. – ZyX Jun 07 '16 at 00:57
  • @ZyX Sorry, I have not fully understand your answer. I am using `:Alias q qall`. And `:q` only does `q`, but not `:qall`, am I missing something? – user14416 Jun 07 '16 at 10:12
  • @user14416 What is “:Alias”? If I use `cnoreabbrev q ((getcmdtype() is# ':' && getcmdline() is# 'q')?('echo 1'):('echo 2'))` I get `1` when typing `:q` as expected. If I use `call SetupCommandAlias('q', 'echo 1')` (with function from my answer) I get `1` as expected. If you got buggy command somewhere not from this answer why are you asking here? – ZyX Jun 07 '16 at 14:35
  • @ZyX The reason was that I had mapping `cmap `. Why it is not supposed to work when I have this kind of mapping? – user14416 Jun 10 '16 at 15:00
  • @user14416 `cmap ` is not interferring with such aliases. What is is `cnoremap`. Check what is the difference in the help. – ZyX Jun 10 '16 at 18:16
115

With supplementary searching, I've found that someone asked nearly the same question as I.

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

will do the trick.

Please be aware that, as Richo points out, the user command must begin with a capital letter.

Community
  • 1
  • 1
Sean
  • 5,233
  • 5
  • 22
  • 26
27

I find that mapping the ; key to : would be a better solution, and would make you more productive for typing other commands.

nnoremap ; :
vnoremap ; :
fent
  • 17,861
  • 15
  • 87
  • 91
  • 2
    This is the single best tip for vim. I'm so used to it now that every time I encounter the normal behavior, it takes me a few tried to get my mind retrained. – airstrike Feb 24 '15 at 19:58
  • 2
    This is not an answer to the question. – Flimm Aug 24 '18 at 11:45
  • 10
    @Flimm No, but it makes OP's issue go away. – Dessa Simpson Sep 24 '18 at 18:02
  • 3
    When using `t` or `f`, one can usually use `;` to go to the next occurrence. One can safely map that the other way, even if you mapped semicolon to colon. There won't be an alias loop. `nnoremap : ;` – Luc Mar 05 '19 at 08:27
12

The best solution involves writing a custom function for handling abbreviations that only take place in the beginning of the command bar.

For this, add the following your vimrc file or anywhere else.

" 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')

A few useful abbreviations from the source material where I found this stuff:

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")
jdhao
  • 24,001
  • 18
  • 134
  • 273
airstrike
  • 2,270
  • 1
  • 25
  • 26
  • 2
    There is a built-in function `string()` that does the same thing as yours `Single_quote()`. – ZyX May 23 '12 at 00:57
7

Suppose you want to add alias for tabnew command in gvim. you can simply type the following command in your .vimrc file (if not in home folder than create one)

cabbrev t tabnew
Andrew Barber
  • 39,603
  • 20
  • 94
  • 123
Sandip
  • 79
  • 1
  • 1
  • 3
    This will cause a command like `:saveas t example` to be replaced with `:saveas tabnew example` – Flimm Aug 24 '18 at 11:56
5

Safest and easiest is a plugin such as cmdalias.vim or my recent update vim-alias of it that take into account

  • preceding blanks or modifiers such as :sil(ent)(!) or :redi(r),
  • range modifiers such as '<,'> for the current visual selection,
  • escape special characters such as quotes, and
  • check if the chosen alias is a valid command line abbreviation.
Enno
  • 238
  • 2
  • 6
5

Maybe you would like to map one of your function keys (F1..F12) to :w ? Then put this into your .vimrc:

noremap  <f1> :w<return>
inoremap <f1> <c-o>:w<return>

(ctrl-o in insert mode switches temporarily to normal mode).

Benoit
  • 76,634
  • 23
  • 210
  • 236
1

I think @ZyX's answer is great, but if you're using a newer version of neovim (0.5+), you might want to define the function using lua instead. Here's one way you could do it:

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

Then, you'd drop the call from call SetupCommandAlias("pg", "postgres://") and just use the function like this: SetupCommandAlias("pg", "postgres://").

n.b. If using it from a .vim file instead of a .lua file, you'd need to prefix the function call with lua, i.e. lua SetupCommandAlias("pg", "postgres://").

achalk
  • 3,229
  • 3
  • 17
  • 37