Over the past 7 years, I have been a member of the #vim channel on
Freenode. Almost every week we get somebody in the channel who wonders
such things as how to map Ctrl-Shift-T differently from Ctrl-T. We
explain that isn't possible. Even in Gvim. Because of vim's input queue
system. People often don't realise that Tab is Ctrl-I, or Enter is
Ctrl-M. We have to explain why it's not possible to map one of these
pairs without breaking the other. It's become apparent to me that the
byte-based model is no longer appropriate if Vim is to remain relevant
and current on newer platforms. It's time we changed the core.
I would like to propose that the base of the input queue be turned into
a queue of structures something like the following form:
struct keypress {
enum {
UNICODE,
SPECIAL,
} type;
int keycode;
int modifiers;
};
In this scheme we can represent any of the keypresses vim can handle:
A = { .type = UNICODE, keycode = 'A', .modifiers = 0 }
<Ctrl-T> = { .type = UNICODE, keycode = 'T', .modifiers = CTRL }
<Enter> = { .type = SPECIAL, keycode = ENTER, .modifiers = 0 }
In this scheme, it is simple to write the code that :map etc.. will use
to recognise the keys and turn them into basic operations. It's also
simple to generate these events from GUI events (such as from GTK or
Win32).
This scheme is also relatively simple to generate from terminal input
bytes, since vim already has this functionality.
Once using this scheme, gvim would now fully support mapping Tab, Ctrl-I
and Ctrl-Shift-I as three separate independent keys. Newer terminals
like xterm already posess ways to encode these three keystrokes
differently at the byte level, and with e.g. libtermkey[*]'s help, vim
could be taught how to recognise all these three too.
In recognition of the non-trivial amount of work required here, and as a
token of my commitment to this issue, I would be happy to donate a
further EUR100 to the vim development sponsorship on top of the EUR20 I
have just sent, if we could come to a mutual agreement on how to proceed
on this front. It would be great if we would be able to make this
feature a reality; I would be happy to consider a further donation at
that point.
I know this isn't the way terminals have worked for the past 20 years.
However, it is the way "terminals", being the units of end-user
interaction, will work for the next 20 years. It would be nice if vim
were able to cope as well with the next 20 as it has with the last.
-----
*: I have also been developing a library, libtermkey, which aims to be
a better way to read key press events from terminals than existing
solutions that are curses or terminfo-based. While it reads the
terminfo database, it also fully understands the extended ways that
xterm et.al. encode modified keypresses, in a way that would be
fully compatible with vim's core, and existing behaviours.
http://www.leonerd.org.uk/code/libtermkey/
-----
--
Paul "LeoNerd" Evans
leo...@leonerd.org.uk
ICQ# 4135350 | Registered Linux# 179460
http://www.leonerd.org.uk/
Hi Bram,
Thanks for the response on this. It looks like then there's three separate
issues here:
1. Making sure that the input queue really can distinguish Ctrl-T from
Ctrl-Shift-T, and also between e.g. Tab and Ctrl-I
2. Getting GUI-based frontends to correctly insert input queue
keypresses
3. Using libtermkey or similar, to recognise keypresses on terminals
I'm actually away until Tuesday now, but when I get back I'll take a
further look into things, mostly in this order. Though, I forsee there's
probably scope for some parallelising between other developers, if
someone else manages to make some headway into one of the bits in the
meantime.
Actually, I've now just remembered why I wanted to change this, why I
thought a byte queue is a really bad idea.
To quote #vim/Freenode just now:
(18:31) <qbit_> can the esc key be compeletly disabled ?
(18:31) <qbit_> I am trying to force myself to use ctrl+c
(18:32) <qbit_> i tried noremap <Esc> <NOP>
(18:32) <LeoNerd> I really would suggest not
(18:32) <LeoNerd> eeeeeverything will break, if you do that
...
(18:34) <LeoNerd> If you map <Esc> away you'll break all the
arrow/function/cursor keys
(18:35) <qbit_> how will that break them ?
(18:35) <LeoNerd> Because of the way terminals work
(18:35) <LeoNerd> E.g. the <Up> arrow key is <Esc>[A
(18:35) <LeoNerd> So if you map <Esc> to something else, the Up arrow
key no longer does that.. and breakage happens
We -really- need to be able to distinguish Escape, the ASCII sequence of
0x1e, from <Esc>, the human-level idea of pressing that piece of
plastic. This would enable us to
:map <Esc> foobarsplot
without affecting any of the other keypress events that start with an
Escape byte, such as all the CSI and SS3s in 7bit legacy mode.
libtermkey already contains all the logic for this; it will only output
a KEYSYM_ESCAPE on a real Escape (by usual prefix and timing analysis),
meaning vim could map that away with confidence of knowing it hadn't
broken CSI/SS3-based keys.
If it will further sway the argument, libtermkey even supports a "render
this struct into a human-readable string" sprintf()-alike operation,
allowing it to produce strings looking like
<Escape>
<Ctrl-Up>
etc...
A little further effort could create a sscanf()-alike to parse these,
giving really easy integration with all of vim's mapping layer.
What thoughts here - can we further consider a keypress-event-structure
queue instead of raw terminal bytes?