Use AHK to detect the "Vim Mode" for GUI windows and configure conditional mappings of keys - vim

The motivating story (TL;DR)
Terminals, due to historical reasons, won't distinguish tab from Ctrl+I. This makes quite a mess when I use Vim: my muscle memory tells me that Ctrl+I shall advance me forward to the next position in the "Jumplist", but some Vim-plugins do remap the tab key to do other good stuffs.
Better defined task
Can I make a "conditional mapping" for active windows from ahk_process gvim.exe when no "special characters" are shown at the bottom left corner of the active Window?
For the window on the left, the Vim session is in "Normal-mode"
For the window on the right, the Vim session is in "Insert-mode".
Other modes do exist, like Visual-mode
At the end of the days, I would like to map the tab key only in "Normal-mode". The best that I can come up with is to OCR a fixed region of pixels at the buttom-left corner, and make it a conditional. Yet, I am inexperienced for getting OCR jobs done in AHK.
Sidenote: I am asking for help from the Vim community the flipside of the same question: https://vi.stackexchange.com/questions/18796/may-i-have-a-vim-session-report-its-mode-in-its-window-title

No need for OCR/AHK hacks.
:nmap (and :nnoremap) maps only in normal mode.
:imap (and :inoremap) maps only in insert mode.
etc.
See :help :map-commands for the variety of different mapping commands for variety of modes.
If a plugin is overwriting a normal-mode mapping for Tab, see who the offender is by using :verbose nmap <Tab> (or its equivalent, :verbose nmap <C-I>), then look at its documentation to see how to rebind it (or in the worst case, eliminate the culprit).

There's no need to grab Vim's current mode to make it available to another application; that would be really cumbersome. If your eventual goal is to send different keys for <Tab> / <C-i> to Vim (e.g. <F13> instead of <Tab>), and you only want Vim to react differently in certain modes (e.g. normal mode), you can just map the other modes to again unify both keys:
:nnoremap <F13> ... " Functionality A
:nnoremap <C-i> ... " Functionality B
:map! <F13> <C-i> " Both keys continue to do the same in insert and command-line mode.

Related

Insert mode default keys in vim

The following items are useful to me in editing text, and I was wondering if vim had something for this built out of the box (though I didn't see it on the https://vimhelp.org/index.txt.html#index.txt page), or I had to create mappings for it:
Forward-delete a character. This is X in normal mode.
Forward-delete all text to the right of the cursor on the line. This is the inverse of ctrl-u.
Are either of these mappings available? And if not, are there 'standard' mappings for this that are common (for example, how it might be done in another unix program).
Note that this is the keyboard I have -- there is only one delete key (which acts like a normal backspace key) and there is no backspace key:
Note: for forward-delete, I am currently mapping ctrl-d as:
"Ctrl-d to forward-delete when in insert or command mode
noremap! <C-d> <Delete>
However, this interferes with the tab in insert mode (which I don't use) and the help-options in command mode (which I do use!) so I may have to modify this later, or hopefully someone suggests a better solution.
though I didn't see it on the https://vimhelp.org/index.txt.html#index.txt page
If you can't find it in the documentation, then it doesn't exist.
You can use fn+delete for "Forward-delete a character".
"Forward-delete all text to the right of the cursor on the line" is ctrl+k in MacOS, but Vim has its own use for that combo, :help i_ctrl-k so it is up to you to create a mapping for it.
Something like:
inoremap <key> <C-o>ld$

Mapping TO ctrl+[something] doesn't work in gvim

I am using GVim 7.4 and I would like to do some really simple mapping to use CtrlP fuzzy matching of tags when a key combination is used.
I tried 2 approaches and they all seem to fail when vim calls Control + [x] combination. While I do understand that there are restrictions when it comes to mapping Ctrl+[x] codes, I haven't found any information on why ctrl mapping wouldn't work.
noremap \t :CtrlPTag<CR><C-\>w
This one enters CtrlP tag mode but then it doesn't enter word from under the cursor.
noremap \t <C-p><C-\>w
Here we don't even get to CtrlP window (I even omit going into tag mode here for simplicity).
As far as I understand (I'm no CtrlP user), the plugin is triggered via some command / key combination, and then presents interactive selection and filtering. It even has different "source" modes.
Now, this is a pretty heavy integration into Vim, probably using scratch buffers and its own input loop. That's why you cannot simply append keys to the mapping and get them interpreted by the plugin "as typed".
Typically, these plugins offer mode selection and so on via (optional) command arguments. Check the plugin's help, and if you cannot get the plugin into the state you need, best contact the plugin's author and ask for such enhancement.

Mapping Alt-j and Alt-k in vim [duplicate]

I'm running Vim on a gnome terminal. But the alt key mappings are not working. For example:
:imap <A-i> <Esc>
It works fine in GVim. But when I run the same command with Vim in the gnome terminal it does nothing.
I'm using Windows 7, The problem is with the terminal, right?
The problem
There are two ways for a terminal emulator to send an Alt key (usually called a Meta key as actual terminals didn't have Alt). It can either send 8 bit characters and set the high bit when Alt is used, or it can use escape sequences, sending Alt-a as <Esc>a. Vim expects to see the 8 bit encoding rather than the escape sequence.
Some terminal emulators such as xterm can be set to use either mode, but Gnome terminal doesn't offer any such setting. To be honest in these days of Unicode editing, the 8-bit encoding is not such a good idea anyway. But escape sequences are not problem free either; they offer no way of distinguishing between <Esc>j meaning Alt-j vs pressing Esc followed by j.
In earlier terminal use, typing Escj was another way to send a Meta on a keyboard without a Meta key, but this doesn't fit well with vi's use of Esc to leave insert mode.
The solution
It is possible to work around this by configuring vim to map the escape sequences to their Alt combinations.
Add this to your .vimrc:
let c='a'
while c <= 'z'
exec "set <A-".c.">=\e".c
exec "imap \e".c." <A-".c.">"
let c = nr2char(1+char2nr(c))
endw
set timeout ttimeoutlen=50
Alt-letter will now be recognised by vi in a terminal as well as by gvim. The timeout settings are used to work around the ambiguity with escape sequences. Esc and j sent within 50ms will be mapped to <A-j>, greater than 50ms will count as separate keys. That should be enough time to distinguish between Meta encoding and hitting two keys.
If you don't like having timout set, which times out for other mapped key sequences (after a second by default), then you can use ttimeout instead. ttimeout applies only to key codes and not other mappings.
set ttimeout ttimeoutlen=50
For Gnome-terminal, use the following instead:
imap ^[i <Esc>
^[i should be typed by pressing Ctrl-v Alt-i
Attention: You need to yank and put in Vim when you want to copy it elsewhere. If you just copy the mapping in an editor like gedit, the mapping will probably be broken.
EDIT here is an example which makes Alt-k add an empty line above the cursor, and Alt-j add an empty line after the current line.
" Alt-j/k to add a blank line
if has('gui_running')
" the following two lines do not work in vim, but work in Gvim
nnoremap <silent><A-j> :set paste<CR>m`o<Esc>``:set nopaste<CR>
nnoremap <silent><A-k> :set paste<CR>m`O<Esc>``:set nopaste<CR>
else
" these two work in vim
" shrtcut with alt key: press Ctrl-v then Alt-k
" ATTENTION: the following two lines should not be
" edited under other editors like gedit. ^[k and ^[j will be broken!
nnoremap ^[k :set paste<CR>m`O<Esc>``:set nopaste<CR>
nnoremap ^[j :set paste<CR>m`o<Esc>``:set nopaste<CR>
endif
Try
<m-i>
Or, if typing alti inserts a character (like in my case, it inserts a carret: ˆ) just map to that character:
:inoremap ˆ <esc>
Be careful, because this one wouldn't work (at least in my system, MacOS 10.6). The caret waits for a letter, because it's not exactly a caret, it is a circumflex.
It may be that the shortcuts are actually from the Gnome Desktop. Try looking at the Gnome Keyboard Shortcuts tool (System menu, Preferences, Keyboard Shortcuts), which lets you view and modify the shortcuts defined on Gnome Desktop. If the key combination is assigned to a function on Gnome Desktop, then remove it and then that key combo should filter down to Vim properly.
Or you may be right that it is a problem of the terminal. Not all terminals support all key combos. Your problem may be the one described in the Vim help docs at :h map-alt-keys. The docs provide a workaround, but not a very good one.
The same thing happens to me. I searched on Google with "gnome terminal alt key", and found that someone asked almost the same question: "How to disable the alt-hotkey behavior on gnome terminal?" in the first link found. (The second link is just this question)
So, maybe you can try that:
Edit > Keyboard Shortcuts, and uncheck "Enable menu access keys"
Take a look at section 1.10 of http://vimdoc.sourceforge.net/htmldoc/map.html. It seems to indicate that gnome-terminal automatically escapes the Alt modifier, so that it doesn't switch the byte sent in the way that Vim is expecting. The document seems to indicate that there isn't really a way around this except for using a different terminal (such as xterm).
This is certainly frustrating because so far as I can tell Linux machines are also incapable of using the D (Mac's Command or Linux's Super) bindings, so at least as far as the terminal goes, we are limited to Shift and Ctrl modifiers, which is frustrating if we want to ensure that we can use all the commands we use in Gvim on terminal Vim (at least without switching terminals, towards which I'm perhaps overly stubborn - gnome-terminal is just so much prettier). I've been looking for a way around this but have been unable to find anything.

Alt key shortcuts not working on gnome terminal with Vim

I'm running Vim on a gnome terminal. But the alt key mappings are not working. For example:
:imap <A-i> <Esc>
It works fine in GVim. But when I run the same command with Vim in the gnome terminal it does nothing.
I'm using Windows 7, The problem is with the terminal, right?
The problem
There are two ways for a terminal emulator to send an Alt key (usually called a Meta key as actual terminals didn't have Alt). It can either send 8 bit characters and set the high bit when Alt is used, or it can use escape sequences, sending Alt-a as <Esc>a. Vim expects to see the 8 bit encoding rather than the escape sequence.
Some terminal emulators such as xterm can be set to use either mode, but Gnome terminal doesn't offer any such setting. To be honest in these days of Unicode editing, the 8-bit encoding is not such a good idea anyway. But escape sequences are not problem free either; they offer no way of distinguishing between <Esc>j meaning Alt-j vs pressing Esc followed by j.
In earlier terminal use, typing Escj was another way to send a Meta on a keyboard without a Meta key, but this doesn't fit well with vi's use of Esc to leave insert mode.
The solution
It is possible to work around this by configuring vim to map the escape sequences to their Alt combinations.
Add this to your .vimrc:
let c='a'
while c <= 'z'
exec "set <A-".c.">=\e".c
exec "imap \e".c." <A-".c.">"
let c = nr2char(1+char2nr(c))
endw
set timeout ttimeoutlen=50
Alt-letter will now be recognised by vi in a terminal as well as by gvim. The timeout settings are used to work around the ambiguity with escape sequences. Esc and j sent within 50ms will be mapped to <A-j>, greater than 50ms will count as separate keys. That should be enough time to distinguish between Meta encoding and hitting two keys.
If you don't like having timout set, which times out for other mapped key sequences (after a second by default), then you can use ttimeout instead. ttimeout applies only to key codes and not other mappings.
set ttimeout ttimeoutlen=50
For Gnome-terminal, use the following instead:
imap ^[i <Esc>
^[i should be typed by pressing Ctrl-v Alt-i
Attention: You need to yank and put in Vim when you want to copy it elsewhere. If you just copy the mapping in an editor like gedit, the mapping will probably be broken.
EDIT here is an example which makes Alt-k add an empty line above the cursor, and Alt-j add an empty line after the current line.
" Alt-j/k to add a blank line
if has('gui_running')
" the following two lines do not work in vim, but work in Gvim
nnoremap <silent><A-j> :set paste<CR>m`o<Esc>``:set nopaste<CR>
nnoremap <silent><A-k> :set paste<CR>m`O<Esc>``:set nopaste<CR>
else
" these two work in vim
" shrtcut with alt key: press Ctrl-v then Alt-k
" ATTENTION: the following two lines should not be
" edited under other editors like gedit. ^[k and ^[j will be broken!
nnoremap ^[k :set paste<CR>m`O<Esc>``:set nopaste<CR>
nnoremap ^[j :set paste<CR>m`o<Esc>``:set nopaste<CR>
endif
Try
<m-i>
Or, if typing alti inserts a character (like in my case, it inserts a carret: ˆ) just map to that character:
:inoremap ˆ <esc>
Be careful, because this one wouldn't work (at least in my system, MacOS 10.6). The caret waits for a letter, because it's not exactly a caret, it is a circumflex.
It may be that the shortcuts are actually from the Gnome Desktop. Try looking at the Gnome Keyboard Shortcuts tool (System menu, Preferences, Keyboard Shortcuts), which lets you view and modify the shortcuts defined on Gnome Desktop. If the key combination is assigned to a function on Gnome Desktop, then remove it and then that key combo should filter down to Vim properly.
Or you may be right that it is a problem of the terminal. Not all terminals support all key combos. Your problem may be the one described in the Vim help docs at :h map-alt-keys. The docs provide a workaround, but not a very good one.
The same thing happens to me. I searched on Google with "gnome terminal alt key", and found that someone asked almost the same question: "How to disable the alt-hotkey behavior on gnome terminal?" in the first link found. (The second link is just this question)
So, maybe you can try that:
Edit > Keyboard Shortcuts, and uncheck "Enable menu access keys"
Take a look at section 1.10 of http://vimdoc.sourceforge.net/htmldoc/map.html. It seems to indicate that gnome-terminal automatically escapes the Alt modifier, so that it doesn't switch the byte sent in the way that Vim is expecting. The document seems to indicate that there isn't really a way around this except for using a different terminal (such as xterm).
This is certainly frustrating because so far as I can tell Linux machines are also incapable of using the D (Mac's Command or Linux's Super) bindings, so at least as far as the terminal goes, we are limited to Shift and Ctrl modifiers, which is frustrating if we want to ensure that we can use all the commands we use in Gvim on terminal Vim (at least without switching terminals, towards which I'm perhaps overly stubborn - gnome-terminal is just so much prettier). I've been looking for a way around this but have been unable to find anything.

Map shift-tab in vim to inverse tab in Vim

I've done some searching and found a wealth of information on binding keys in vim, but I can't find out, for sure, how to map shift-tab. Or what command I need to map it to for it to "tab backwards".
This is what I have at the moment:
map <S-tab> <S-,><S-,>
Possibly Relevant Information:
I'm running Debian with Terminal 2.22.3. with VIM - Vi IMproved 7.1
Vim already has built-in key commands for insert mode to shift the current line left or right one &shiftwidth. They are (in insert mode):
Ctrl-t : shift right (mnemonic "tab")
Ctrl-d : shift left (mnemonic "de-tab")
If you still want to use shift-tab, this is how you do it:
" for command mode
nnoremap <S-Tab> <<
" for insert mode
inoremap <S-Tab> <C-d>
Debugging why shift-tab in vim isn't performing an inverse tab in Vim
If you placed the code inoremap <S-Tab> <C-d> into your .vimrc and vim still isn't responding in insert mode, then that means your Shift-tab is being intercepted, gobbled, and ignored somewhere in the perilous 4-part journey between your keyboard and vim. You need to figure out where your Shift-Tab is getting silently orphaned.
Four stage journey of Shift+Tab between keyboard and vim
Keyboard -> Operating System
The first step of a keystroke's journey is the operating system that intercepts all keys and stops some of them to perform a behaviors for the Operating system. For example Alt+Tab which often means "change focus of current window to the next". If you send Alt+Tab into vim, vim will not respond because the operating system gobbled it. You have to find this keymapping area on your operating system. Windows, Mac and Linux are all different, and they have different programs that manage which keys are intercepted and which pass through to applications. Find this area and make sure your Shift+tab is set to pass-though to the Terminal you use.
Operating System -> Terminal Application
Step 2 assumes the OS allowed your Shift+Tab to pass through to your terminal Application that has focus. Your terminal application should have a configuration menu option (Most have more than one, that fight each other) under Settings -> shortcuts, or settings -> keymaps. There are hundreds of terminal apps out there and each have different ideologies for which keystrokes to trap and gobble and perform some action native to the app, or which to pass through to the shell. Find this area and make sure your Shift+tab is allowed to pass through and is passing through.
Terminal Application -> your Shell
Step 3 assumes your Terminal application allowed Shift+Tab to pass through to the shell. There is an area that defines which key combos are intercepted to perform an action on the shell, which pass through to the application that is on the front. For me this is inputrc but mac and Windows have different areas. You'll have to find this file and clear out anything that may be gobbling your Shift+Tab and erase that, or add a rule that says pass through.
Shell -> vim
Now we're at the level of Vim where the .vimrc can hear, trap and or rebroadcast the Shift+Tab to the next step in the command chain, and do whatever you want while it does so. Vim has the map keyword that controls this.
There's even a 5th step in the journey if we're talking about Browsers or webpages, who have interpreter engines that allow client side code to remap keys. But that's for a different post.
Debug chain instructions to isolate where your Shift+Tab is orphaned:
Make sure your OS isn't gobbling your Shift+tab and performing no-action. Try a different application like Eclipse, Browser or Notepad, and see if Shift+Tab performs any action. If it does then The OS is likely passing through your Shift+tab unaffected to applications.
Make sure your terminal app can receive and is receiving the Shift+Tab. Verify this by going to settings -> Shortcuts and erase any keymap that has Shift, or Tab in the name, then make a new keymapping that intercepts Shift+tab and performs some simple action like new tab doesn't matter. Save it, put focus in the terminal and press it, if a new tab appears then Terminal can hear and respond.
Make sure your terminal is passing through Shift+Tab to shell. Erase anything smelling of Tab or Shift in Settings -> keymaps and Settings -> Shortcuts. The default action (should be) do nothing and pass through.
Open any other shell program like nano, ed, or emacs. If any of these perform any action when you press Shift+Tab, then it's likely that the terminal is passing through Shift+tab to vim.
At this point we know vi/vim is receiving Shift+Tab, but not responding to it. To isolate the problem, blow away all your vim config files like .vimrc, .profile and anything under .vim. The problem could even be with vim or under /etc You run vanilla vim using vim -u NONE and make sure you're running vim. Vim's default behavior is straight pass through. So if Shift+Tab isn't doing something, then vi is bugged.
Uninstall vim with a blank config files/directories and re-install. If this doesn't work, then your operating system is bugged. Reinstall the operating system. If this doesn't work, throw the computer in the trash.
To test if vim actually gets your shift+tab go into insert mode
ctrl+v then tab and it should create a tab character
ctrl+v then shift+tab and it should create an inverse tab character (which appears as ^[[Z)
If it does not, then vim is not receiving the shift+tab input
In my case it was my terminal.
In the terminal window -> settings -> shortcuts
search shift+tab and unmap
OP's question specifies map, which applies to normal, visual, select, and operator-pending modes (see :h mapmode-nvo). I'm not sure such a mapping makes any sense in operator-pending mode, but it's very useful in visual and select modes. I'll also add forward indentation for completeness:
" both visual and select modes at once. gv means reselect the last selection
vnoremap <Tab> >gv
vnoremap <S-Tab> <gv
" just visual mode. this is unsurprisingly the same as vnoremap
xnoremap <Tab> >gv
xnoremap <S-Tab> <gv
" just select mode. <C-o> leaves select mode for visual mode, where the
" command is performed, and <C-g> reenters select mode from visual mode
snoremap <Tab> <C-o>>gv<C-g>
snoremap <S-Tab> <C-o><gv<C-g>
Please do note that Tab and CTRL-i map to each other in most terminal emulators, so mapping Tab can have unexpected results. See this question for more.
The following can be used with Vim tabs:
map <TAB> <ESC>gt<CR>
map <S-TAB> <ESC>gT<CR>

Resources