guns / vim-sexp

Precision Editing for S-expressions
MIT License
612 stars 33 forks source link

Do the <M-S-hjkl> mappings work natively on Mac? #18

Open gsavovski opened 8 years ago

gsavovski commented 8 years ago

Thanks for the great plugin.

I'm trying hard to get it to work on a mac in iterm2 with vim version 7.4.488. My .vimrc is pretty big so I'm eliminating any possible conflicts by using a plain vanila .vimrc with nothing in it but your plugin. None of the <M mappings work for me.

Am I missing some obvious thing specific to Mac. Anybody else has/had this issue?

I saw that there is a possibility to set your own mappings using the 'sexp-explicit-mappings' directive. But I like the default mappings so want to stick to those..

Thanks

justinmk commented 8 years ago

Mapping meta does not work in the terminal because Vim cannot tell the difference between meta and esc. Neovim in the terminal works fine with meta maps (as well as <esc> map), but it means that the opposite issue occurs: https://github.com/neovim/neovim/issues/2017

guns commented 8 years ago

Hi @gsavovski,

I make Meta mappings work in my terminal by explicitly setting them on startup.

For example, if your terminal sends \033a when you press <M-a> on your keyboard, you can teach vim this keystroke with:

execute "set <M-a>=\<Esc>a"

Therefore, to make <M-…> work in a terminal that sends \033:

" Timeout quickly on key codes to differentiate from normal <Esc>
set ttimeout ttimeoutlen=0

" Special named keys that cause problems when used literally
let namedkeys = { ' ': 'Space', '\': 'Bslash', '|': 'Bar', '<': 'lt' }

" Map Alt + ASCII printable chars
for n in range(0x20, 0x7e)
    let char = nr2char(n)
    let key  = char

    if has_key(namedkeys, char)
        let char = namedkeys[char]
        let key  = '<' . char . '>'
    endif

    " Escaped Meta (i.e. not 8-bit mode)
    "  * Esc-[ is the CSI prefix (Control Sequence Introducer)
    "  * Esc-O is the SS3 prefix (Single Shift Select of G3 Character Set)
    if char !=# '[' && char !=# 'O'
        try
            execute 'set <M-' . char . ">=\<Esc>" . key
        catch
        endtry
    endif
endfor

unlet namedkeys n key char

I've always meant to package this bit of vim voodoo into a plugin, but never got around to it. In fact, when I originally wrote vim-sexp, I had been using this setup for so long that I forgot that <M-…> does not work in vim by default.

Hence https://github.com/tpope/vim-sexp-mappings-for-regular-people

:)

EDIT (removed discussion of binding <4-…>, simplified code example):

@justinmk just brought up a good point: this will cause <Esc> to behave badly unless we set ttimeout and ttimeoutlen:

set ttimeout ttimeoutlen=0

Setting ttimeout{,len} can have its own issues, but I don't find it to be a problem in practice. You can increase ttimeoutlen if you are using Vim over an especially slow network connection.

In truth, none of this has personally never bothered me as I use <C-c> in place of <Esc>.

Hope this helps!

Thanks for the reminder @justinmk!

guns commented 8 years ago

@justinmk

Oops, forgot about that side effect. Appending my first post now...

guns commented 8 years ago

Okay, I've updated my comment with discussion of ttimeout{,len}.

It's surely a hack, but a serviceable one nonetheless.

guns commented 8 years ago

@gsavovski

One more thing I forgot to mention: Meta mappings work by default in vim if you can configure your terminal to set the 8th bit on meta/alt.

Most terminals can be configured to do this, but often do not do it by default. I imagine iTerm has this option as well. IIRC, the default OS X Terminal also has this option.

gsavovski commented 8 years ago

Hi @justinmk

Thanks for the kind explanation.

I actually remembered that I never could use the Alt/Meta and Command keys since I didn’t have any mappings for them in vimrc.

Hi @guns,

Thanks for opening this new door :-)

I already had set in my iTerm2 that the left option acts as (+Esc). When I press any of the combination in terminal by catting the output I get:

-=} cat ^[H^[J^[K^[L

So I tried explicitly setting:

execute "set =^[J" execute "set =^[K" execute "set =^[H" execute "set =^[L”

The ASCII code for ‘^[‘ is ‘\094 \091’.

Then I just put your code snippet in my vimrc and it really worked. I can not even believe it :-) Thank you so much!

Few more questions bug me..

Why did the explicit setting I mention above didn’t work? Out of curiosity looking at your modifiers.vim code you have a lots of mappings using the arrow keys, what do you accomplish doing that? And lastly what do you mean by rarely used <4-…> mapping to be used as super? Is that the actual number 4?

Thanks again this is great :-)

By the way the plugin already worked great in MacVim but I'm much more of terminal user, therefore this is super exciting..

guns commented 8 years ago

I already had set in my iTerm2 that the left option acts as (+Esc).

Sounds like turning this off might enable meta-sets-top-bit mode, which is really the best no-hack option if you just want to enable meta keys in your terminal. You should give it a try!

So I tried explicitly setting:

execute "set =^[J" execute "set =^[K" execute "set =^[H" execute "set =^[L”

The ASCII code for ‘^[‘ is ‘\094 \091’. … Why did the explicit setting I mention above didn’t work?

The ^[ is actually the standard printable representation for ASCII 033 (Escape). This character can be produced with the Escape key, or by pressing <C-[>. Actually, the first 32 unprintable ASCII control characters can be produced by pressing Ctrl and the corresponding ASCII equivalent 0x40 up the table:

Ctrl + @ (0x40) -> 0x00 (Other keys can send NUL; I like Ctrl + Space)
Ctrl + A (0x41) -> 0x01
Ctrl + B (0x42) -> 0x02
Ctrl + C (0x43) -> 0x03
…
Ctrl + _ (0x5f) -> 0x1f

Of course, we don't type Ctrl-Shift-C when we want to send interrupt through a terminal, so the actual key sent is upper cased when the Control modifier is active. This convenience is why Ctrl-a and Ctrl-Shift-a are indistinguishable in most terminal programs. Lots of people find this annoying, but I personally enjoy this feature.

Here is the relevant bit in vim:

https://github.com/vim/vim/blob/60cce2f/src/ascii.h#L40-L41

#define Ctrl_chr(x) (TOUPPER_ASC(x) ^ 0x40) /* '?' -> DEL, '@' -> ^@, etc. */
#define Meta(x)     ((x) | 0x80)

Here we can also see why configuring your terminal to set the top bit when Meta is active makes Meta mappings work out of the box.

Then I just put your code snippet in my vimrc and it really worked. I can not even believe it :-)

I've edited my comment again to remove an errant debugging echo string(), so you may want to remove that from your vimrc.

You've convinced me to start work on packaging this up as a plugin since it does not seem to be common knowledge. It is in the documentation, but it's easy to miss: :help :set-termcap.

Out of curiosity looking at your modifiers.vim code you have a lots of mappings using the arrow keys, what do you accomplish doing that?

It allows mapping Modifier + Arrow keys. I don't ever find myself touching the arrow keys, so it was mostly a way to experiment with setting key codes.

And lastly what do you mean by rarely used <4-…> mapping to be used as super? Is that the actual number 4?

Yes, the <4- modifier is actually a mouse event modifier, but you can bind it in regular mappings. For example, I configure my terminal to send \033\007… when I type Super + …, then I map this sequence to produce the corresponding <4-…>:

execute "map  <special> \<Esc>\<C-g>… <4-…>"
execute "map! <special> \<Esc>\<C-g>… <4-…>"

Now this <4-…> key can represent Super + … in your vimrc:

map <4-s> :<C-u>update<CR>

This is my favorite Vim hack, but it has the nasty side effect of borking <Esc>, which is why I retracted my discussion of it.

Like I mentioned before, I use Ctrl-C as my escape anyway, so this does not bother me.

I suspect there is a way to abuse the termcap key code system to accomplish the same without this downside. If I find one I'll report back here.

By the way the plugin already worked great in MacVim but I'm much more of terminal user, therefore this is super exciting..

Good to know. I compile vim with --disable-gui, so I am very unfamiliar with GUI vim builds.