4

To make it easier to edit my ~/.vimrc file, I set up the following:

:cnoremap ev :<C-u>vsplit $MYVIMRC<cr>

So, to open ~/.vimrc in a split I just enter command mode and enter ev. However, I would like do the same thing entering ev and then <Enter> (just like the other default commands), so I tried this:

:cnoremap ev<cr> :<C-u>vsplit $MYVIMRC<cr>

This gives E492: Not an editor command: ev. Why is this?

This is my full ~/.vimrc:

let mapleader=" "

" Plugins (using 'plugged') ----------- {{{
call plug#begin('~/.vim/plugged')
Plug 'preservim/nerdtree'
Plug 'itchyny/lightline.vim'
call plug#end()
" }}}

" Some basics
    set mouse=a
    set nocompatible
    set laststatus=2
    filetype plugin on
    filetype indent plugin on
    syntax on
" Tabs amount to 4 blank spaces
    set tabstop=4
    set shiftwidth=4
    set expandtab
" Splits open at the the bottom and on the right
    set splitright splitbelow
" All folds are closed upon entering a file
    set foldlevelstart=0 

" Vimscript file settings -------------- {{{    
    augroup filetype_vim
        autocmd!
        autocmd FileType vim setlocal foldmethod=marker
    augroup END
" }}}

" NERDTree ---------------------- {{{
" NERDTree Toggle shortcut remap
    nnoremap <F5> :NERDTreeToggle<CR>

" NERDTress File highlighting
function! NERDTreeHighlightFile(extension, fg, bg, guifg, guibg)
 exec 'autocmd filetype nerdtree highlight ' . a:extension .' ctermbg='. a:bg .' ctermfg='. a:fg .' guibg='. a:guibg .' guifg='. a:guifg
 exec 'autocmd filetype nerdtree syn match ' . a:extension .' #^\s\+.*'. a:extension .'$#'
endfunction

call NERDTreeHighlightFile('cpp', 'green', 'none', 'green', '#151515')
call NERDTreeHighlightFile('h', 'green', 'none', 'green', '#151515')
call NERDTreeHighlightFile('py', 'Red', 'none', '#ffa500', '#151515')
" }}}

" remapping keys for general use
    :nnoremap j <Left>
    :nnoremap m <Down>
    :nnoremap h <Nop>
    :vnoremap j <Left>
    :vnoremap m <Down>
    :vnoremap h <Nop>
    :noremap <Left> <Nop>
    :noremap <Right> <Nop>
    :noremap <Up> <Nop>
    :noremap <Down> <Nop>
    :inoremap <Left> <Nop>
    :inoremap <Right> <Nop>
    :inoremap <Up> <Nop>
    :inoremap <Down> <Nop>
7

I can't reproduce your exact issue. The ev<cr> mapping works fine for me. I don't see the E492: Not an editor command: ev error you're seeing.

But I wanted to write an answer to recommend that you don't use cnoremap for this purpose.

There are many limitations to the way cnoremap works that make it really inconvenient for this purpose:

  • It will expand anywhere in the command line, not only at the beginning of the line, where an Ex command is expected.
  • It will expand not only in command mode (for Ex commands) but for search strings as well (so now /ev will search for vsplit $MYVIMRC instead!)
  • It will "eat" keystrokes and not display them in full trying to match a mapping. Feels funny to start typing and not seeing the characters in feedback as you expect.

There's a way to accomplish something very close to what you want (see solution #3 below.)

But I have two suggestions that accomplish something similar to what you're asking for, not exactly the same, that I think are superior.

1. User-defined Command

You can define user commands in Vim, which is really close to what you seem to want here.

The main constraint is that user commands need to start with a capital letter, so you can't really use ev exactly, you'd have to use EV or Ev instead. (I tend to find the former easier to type, by holding down the shift key while typing the "E" and "V".)

Having said that, it's pretty straightforward to define a user command for this purpose:

command! -bar EV vsplit $MYVIMRC<cr>

Once that's defined, you can use it with:

:EV

One advantage of user commands is that they support a whole range of features, you can have commands that use arguments, ranges, completion (for example, filenames), a second version with a bang (:EV!), etc.

2. Normal mode Mapping

Another option is to use a normal mode mapping. An advantage of that approach is that it's usually quicker to access it than using the command-line. (You need the : to enter command mode, plus the "Enter" at the end. Plus, if you're using user commands, you need uppercase, which means holding the "Shift" key.)

You could simply use <Leader>ev in Normal mode (three keystrokes) to accomplish the same. <Leader> defaults to backslash, but many use , or <Space> to make it even easier to access.

To use a normal mapping with <Leader>, this is all you need:

nnoremap <Leader>ev :vsplit $MYVIMRC<cr>

You could also define a similar xnoremap mapping so it also works in Visual mode, in which case use <c-u> to delete the range first.

Access it with \ev from Normal mode.

3. Command-mode abbreviation

You can use a command-line abbreviation (instead of mapping) to get closer to the intended result of making :ev<cr> work. The abbreviation will not have the unintended side-effect of "eating" keystrokes while finding a match, since it only looks for matches when whole words were typed.

Additionally, by using an <expr> abbreviation, you can only make it trigger for commands (not when typing a search pattern) and only trigger when it's typed as the command.

This is a start:

cabbrev <expr> ev getcmdtype() ==# ':' && getcmdline() ==# 'ev' ? 'vsplit $MYVIMRC' : 'ev'

This checks whether it's in command-line mode (see :help getcmdtype() for more details) and whether the whole command-line matches ev exactly (which means it's the command, since it's the first item) and only then expands it to the vsplit command. Otherwise, expands to ev itself, to keep no changes.

Note that this will produce a slightly weird behavior if you press a key other than "Enter" after :ev. If you press "Space", it will show the expansion with the space at the end. Even more odd if you type some other non-word character like . or #.

There's a way for you to only expand the abbreviation if an "Enter" was typed by looking at getchar(0) from the abbreviation. But it's not enough to test it, you'll have to save it to include it in the abbreviation, since you'll have consumed the character... You'll need a function:

function! EditVimrc()
    if getcmdtype() ==# ':' && getcmdline() ==# 'ev'
        let c = nr2char(getchar(0))
        if c ==# "\r"
            return "vsplit $VIMRC\r"
        endif
        return 'ev' . c
    endif
    return 'ev'
endfunction

cabbrev <expr> ev EditVimrc()

This seems to do exactly what you wanted. It even works if you mistype a character and go back to correct it. It won't work if you have additional spaces in the line though. And the implementation is pretty complex...

So I'd really recommend you go with #1 or #2 instead. But if you want the exact interface you described, this is the closest you'll get.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.