Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Map vim keys based on OS?

Tags:

vim

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?

like image 848
Spike Avatar asked Nov 30 '25 15:11

Spike


2 Answers

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>
like image 186
romainl Avatar answered Dec 02 '25 05:12

romainl


In contrast to @romainl's answer, you don't need to dynamically evaluate the operating system during mapping execution (the operating system can hardly change while Vim is running; this is static information). Therefore, it's much easier to define the mappings conditionally:

if has("mac")
    nnoremap <leader>cmd :!open -a terminal<CR>
else
    nnoremap <leader>cmd :!start cmd<CR>
endif

This let's you get rid of the :execute, is a bit faster, and much easier to extend or tweak. (All the other caveats about :noremap and the missing <CR> from @romainl's answer still apply.)

in Lua:

if vim.fn.has("mac") == 1 then
  vim.api.nvim_set_keymap("n", "<leader>cmd", "<cmd>!open -a terminal<cr><esc>", { noremap = true, silent = true })
else
  vim.api.nvim_set_keymap("n", "<leader>cmd", "<cmd>cmd :!start cmd<cr><esc>", { noremap = true, silent = true })
end
like image 21
Ingo Karkat Avatar answered Dec 02 '25 06:12

Ingo Karkat



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!