Search code examples
vim

Map vim keys based on OS?


Background


I use VIM on both MacOS and windows, with just one vimrc setting

And here's one of my lightweight key mapping binding:

map <leader>cmd:exec has("mac")==1? ':!open -a terminal' : ':!start cmd'

Problem


However, it doesn't work as I thought: press <leader>cmd and without pressing an enter key then cmd or terminal pops out.

What actually happens is the whole mapping string

exec has("mac")==1? ':!open -a terminal' : ':!start cmd' just appears in ex mode and waits for me to press the enter key to run itself.

As I know, if you use exe "your cmd string" in your mapping key setting , you don't need put a <cr> after it, but why when you are using the triple operator, it behaves differently?

Try


So I put into the string, like

map <leader>cmd:exec has("mac")==1? ':!open -a terminal<cr>' : ':!start cmd<cr>'

vim tells me:

enter image description here

(Also, I cannot figure out why <cr>in `` should be escaped, isn't that a string in `` remains the original meaning?)

So I modified as following:

map <leader>cmd:exec has("mac")==1? ':!open -a terminal\<cr\>' : ':!start cmd\<cr\>'

But it just appears in ex mode again.

Any solutions, please?


Solution

  • First, let's clean up your command a little:

    :nnoremap <leader>cmd :execute has("mac") ? "!open -a terminal" : "!start cmd"
    
    • nnoremap is better than map because it ensures non-recursiveness and it's explicit about the mode it's made for,
    • no need to test the value of has(),
    • shortened names are cool for typing in the command-line but they are useless in script,
    • the : are redundant.

    But none of that will actually fix your problem, namely that you don't have a carriage return at the end of your mapping:

    :nnoremap <leader>cmd :execute has("mac") ? "!open -a terminal" : "!start cmd"<CR>
    

    And you still have to press <CR> a second time anyway to get back to Vim so…

    :nnoremap <leader>cmd :execute has("mac") ? "!open -a terminal" : "!start cmd"<CR><CR>
    

    Now, here is a pretty clean and generic way to do things differently depending on the platform:

    " Define a global variable containing the current environment's name
    " if it hasn't been already defined.
    if !exists('g:env')
        if has('win64') || has('win32') || has('win16')
            let g:env = 'WINDOWS'
        else
            let g:env = toupper(substitute(system('uname'), '\n', '', ''))
        endif
    endif
    

    and a reworked version of your mapping, with an added path for the open command which you may or may not need depending on your use case:

    nnoremap <leader>cmd :execute g:env == "DARWIN" ? "!open -a terminal ." : "!start cmd"<CR><CR>
    

    and an alternative approach, just for the fun of it:

    let cmds = { "DARWIN": "!open -a terminal .", "WINDOWS": "!start cmd" }
    nnoremap <leader>cmd :execute cmds[g:env]<CR><CR>