Repository: kovidgoyal/kitty

Issue:
terminfo: Include function and editing keys with modifiers
Both the xterm and vte terminfo entries declare extended capabilities for keys with modifiers. Kitty already sends the same CSI/SS3 sequences as those emulators; maybe you’d want to follow suit with terminfo, too?

```
key   unmod  S+   A+    A+S+  C+    C+S+  C+A+  C+A+S+
------------------------------------------------------
↑     kcuu1  kUP* kUP3  kUP4  kUP5  kUP6  kUP7
↓     kcud1  kDN* kDN3  kDN4  kDN5  kDN6  kDN7
→     kcuf1  kRIT kRIT3 kRIT4 kRIT5 kRIT6 kRIT7
←     kcub1  kLFT kLFT3 kLFT4 kLFT5 kLFT6 kLFT7
End   kend   kEND kEND3 kEND4 kEND5 kEND6 kEND7
Home  khome  kHOM kHOM3 kHOM4 kHOM5 kHOM6 kHOM7
Ins   kich1  kIC  kIC3  kIC4  kIC5  kIC6  kIC7
Del   kdch1  kDC  kDC3  kDC4  kDC5  kDC6  kDC7
PgUp  kpp    kPRV kPRV3 kPRV4 kPRV5 kPRV6 kPRV7
PgDn  knp    kNXT kNXT3 kNXT4 kNXT5 kNXT6 kNXT7
F1    kf1    kf13 kf49  kf61  kf25  kf37
F2    kf2    kf14 kf50  kf62  kf26  kf38
F3    kf3    kf15 kf51  kf63  kf27  kf39
F4    kf4    kf16 kf52        kf28  kf40
F5    kf5    kf17 kf53        kf29  kf41
F6    kf6    kf18 kf54        kf30  kf42
F7    kf7    kf19 kf55        kf31  kf43
F8    kf8    kf20 kf56        kf32  kf44
F9    kf9    kf21 kf57        kf33  kf45
F10   kf10   kf22 kf58        kf34  kf46
F11   kf11   kf23 kf59        kf35  kf47
F12   kf12   kf24 kf60        kf36  kf48

* kUP and kDN are also aliased kri and kind respectively
```

I can prepare a merge request if you want.


Discussion:
Sure, send a PR. You want to make the changes in terminfo.py and then run ./build-terminfo. 

Resolving patch:
diff --git a/kitty/keys.py b/kitty/keys.py
--- a/kitty/keys.py
+++ b/kitty/keys.py
@@ -6,20 +6,10 @@
 
 from . import fast_data_types as defines
 from .key_encoding import KEY_MAP
-from .terminfo import key_as_bytes
+from .terminfo import key_as_bytes, modify_key_bytes
 from .utils import base64_encode
 
 
-def modify_key_bytes(keybytes, amt):
-    ans = bytearray(keybytes)
-    amt = str(amt).encode('ascii')
-    if ans[-1] == ord('~'):
-        return bytes(ans[:-1] + bytearray(b';' + amt + b'~'))
-    if ans[1] == ord('O'):
-        return bytes(ans[:1] + bytearray(b'[1;' + amt) + ans[-1:])
-    raise ValueError('Unknown key type in key: {!r}'.format(keybytes))
-
-
 def modify_complex_key(name, amt):
     if not isinstance(name, bytes):
         name = key_as_bytes(name)
diff --git a/kitty/terminfo.py b/kitty/terminfo.py
--- a/kitty/terminfo.py
+++ b/kitty/terminfo.py
@@ -6,6 +6,22 @@
 from binascii import hexlify, unhexlify
 
 
+def modify_key_bytes(keybytes, amt):
+    if amt == 0:
+        return keybytes
+    ans = bytearray(keybytes)
+    amt = str(amt).encode('ascii')
+    if ans[-1] == ord('~'):
+        return bytes(ans[:-1] + bytearray(b';' + amt + b'~'))
+    if ans[1] == ord('O'):
+        return bytes(ans[:1] + bytearray(b'[1;' + amt) + ans[-1:])
+    raise ValueError('Unknown key type in key: {!r}'.format(keybytes))
+
+
+def encode_keystring(keybytes):
+    return keybytes.decode('ascii').replace('\033', r'\E')
+
+
 names = 'xterm-kitty', 'KovIdTTY'
 
 termcap_aliases = {
@@ -156,54 +172,8 @@
     # 'invis': r'\E[8m',
     # Backspace
     'kbs': r'\177',
-    # Left
-    'kcub1': r'\EOD',
-    # Down
-    'kcud1': r'\EOB',
-    # Right
-    'kcuf1': r'\EOC',
-    # Up
-    'kcuu1': r'\EOA',
-    # Function keys
-    'kf1': r'\EOP',
-    'kf2': r'\EOQ',
-    'kf3': r'\EOR',
-    'kf4': r'\EOS',
-    'kf5': r'\E[15~',
-    'kf6': r'\E[17~',
-    'kf7': r'\E[18~',
-    'kf8': r'\E[19~',
-    'kf9': r'\E[20~',
-    'kf10': r'\E[21~',
-    'kf11': r'\E[23~',
-    'kf12': r'\E[24~',
-    'kf13': r'\E[1;2P',
-    'kf14': r'\E[1;2Q',
-    'kf15': r'\E[1;2R',
-    'kf16': r'\E[1;2S',
-    'kf17': r'\E[15;2~',
-    'kf18': r'\E[17;2~',
-    'kf19': r'\E[18;2~',
-    'kf20': r'\E[19;2~',
-    'kf21': r'\E[20;2~',
-    'kf22': r'\E[21;2~',
-    'kf23': r'\E[23;2~',
-    'kf24': r'\E[24;2~',
-    'kf25': r'\E[1;5P',
-    # Home
-    'khome': r'\EOH',
-    # End
-    'kend': r'\EOF',
-    # Insert character key
-    'kich1': r'\E[2~',
-    # Delete character key
-    'kdch1': r'\E[3~',
     # Mouse event has occurred
     'kmous': r'\E[M',
-    # Page down
-    'knp': r'\E[6~',
-    # Page up
-    'kpp': r'\E[5~',
     # Scroll backwards (reverse index)
     'kri': r'\E[1;2A',
     # scroll forwards (index)
@@ -270,11 +240,6 @@
     # Select alternate charset
     'smacs': r'\E(0',
     'rmacs': r'\E(B',
-    # Shifted keys
-    'kRIT': r'\E[1;2C',
-    'kLFT': r'\E[1;2D',
-    'kEND': r'\E[1;2F',
-    'kHOM': r'\E[1;2H',
     # Special keys
     'khlp': r'',
     'kund': r'',
@@ -298,6 +263,27 @@
     # 'rs2': r'\E[!p\E[?3;4l\E[4l\E>',
 }
 
+string_capabilities.update({
+    'kf{}'.format(offset + n):
+        encode_keystring(modify_key_bytes(b'\033' + value, mod))
+    for offset, mod in {0: 0, 12: 2, 24: 5, 36: 6, 48: 3, 60: 4}.items()
+    for n, value in zip(range(1, 13),
+                        b'OP OQ OR OS [15~ [17~ [18~ [19~ [20~ [21~ [23~ [24~'.split())
+    if offset + n < 64
+})
+
+string_capabilities.update({
+    name.format(unmod=unmod, key=key):
+        encode_keystring(modify_key_bytes(b'\033' + value, mod))
+    for unmod, key, value in zip(
+        'cuu1 cud1 cuf1 cub1 end home ich1 dch1 pp  np'.split(),
+        'UP   DN   RIT  LFT  END HOM  IC   DC   PRV NXT'.split(),
+        b'OA  OB   OC   OD   OF  OH   [2~  [3~  [5~ [6~'.split())
+    for name, mod in {
+        'k{unmod}': 0, 'k{key}': 2, 'k{key}3': 3, 'k{key}4': 4,
+        'k{key}5': 5, 'k{key}6': 6, 'k{key}7': 7}.items()
+})
+
 termcap_aliases.update({
     'ac': 'acsc',
     'bl': 'bel',
@@ -346,31 +332,6 @@
     'kd': 'kcud1',
     'kr': 'kcuf1',
     'ku': 'kcuu1',
-    'k1': 'kf1',
-    'k2': 'kf2',
-    'k3': 'kf3',
-    'k4': 'kf4',
-    'k5': 'kf5',
-    'k6': 'kf6',
-    'k7': 'kf7',
-    'k8': 'kf8',
-    'k9': 'kf9',
-    'k;': 'kf10',
-    'F1': 'kf11',
-    'F2': 'kf12',
-    'F3': 'kf13',
-    'F4': 'kf14',
-    'F5': 'kf15',
-    'F6': 'kf16',
-    'F7': 'kf17',
-    'F8': 'kf18',
-    'F9': 'kf19',
-    'FA': 'kf20',
-    'FB': 'kf21',
-    'FC': 'kf22',
-    'FD': 'kf23',
-    'FE': 'kf24',
-    'FF': 'kf25',
     'kh': 'khome',
     '@7': 'kend',
     'kI': 'kich1',
@@ -410,8 +371,12 @@
     'ks': 'smkx',
     'ke': 'rmkx',
     '#2': 'kHOM',
+    '#3': 'kIC',
     '#4': 'kLFT',
+    '*4': 'kDC',
     '*7': 'kEND',
+    '%c': 'kNXT',
+    '%e': 'kPRV',
     '%i': 'kRIT',
     '%1': 'khlp',
     '&8': 'kund',
@@ -433,10 +398,22 @@
     # 'r2': 'rs2',
 })
 
+termcap_aliases.update({
+    tc: 'kf{}'.format(n)
+    for n, tc in enumerate(
+        'k1 k2 k3 k4 k5 k6 k7 k8 k9 k; F1 F2 F3 F4 F5 F6 F7 F8 F9 FA '
+        'FB FC FD FE FF FG FH FI FJ FK FL FM FN FO FP FQ FR FS FT FU '
+        'FV FW FX FY FZ Fa Fb Fc Fd Fe Ff Fg Fh Fi Fj Fk Fl Fm Fn Fo '
+        'Fp Fq Fr'.split(), 1)})
+
 queryable_capabilities = numeric_capabilities.copy()
 queryable_capabilities.update(string_capabilities)
 extra = (bool_capabilities | numeric_capabilities.keys() | string_capabilities.keys()) - set(termcap_aliases.values())
-no_termcap_for = frozenset('Su Tc setrgbf setrgbb fullkbd'.split())
+no_termcap_for = frozenset(
+    'Su Tc setrgbf setrgbb fullkbd kUP kDN'.split() + [
+        'k{}{}'.format(key, mod)
+        for key in 'UP DN RIT LFT END HOM IC DC PRV NXT'.split()
+        for mod in range(3, 8)])
 if extra - no_termcap_for:
     raise Exception('Termcap aliases not complete, missing: {}'.format(extra - no_termcap_for))
 del extra


Test patch: