Skip to content

Instantly share code, notes, and snippets.

@choppsv1
Created January 13, 2016 05:48
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save choppsv1/8af4d179794c2603362e to your computer and use it in GitHub Desktop.
Save choppsv1/8af4d179794c2603362e to your computer and use it in GitHub Desktop.
# -*- coding: utf-8 -*-#
# December 31 2015, Christian Hopps <chopps@gmail.com>
#
# Copyright (c) 2015-2016 by Christian E. Hopps.
# All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
from __future__ import absolute_import, division, unicode_literals, print_function, nested_scopes
import argparse
import sys
import uuid
parser = argparse.ArgumentParser("")
parser.add_argument('-i', "--iterm", action="store_true", help='iterm')
args = parser.parse_args()
# Create key sequences for C, M, C-S, M-S, C-M, C-M-S
shiftmap = { chr(x): chr(x).upper() for x in xrange(ord('a'), ord('z') + 1) }
shiftmap['`'] = '~'
shiftmap['1'] = '!'
shiftmap['2'] = '@'
shiftmap['3'] = '#'
shiftmap['4'] = '$'
shiftmap['5'] = '%'
shiftmap['6'] = '^'
shiftmap['7'] = '&'
shiftmap['8'] = '*'
shiftmap['9'] = '('
shiftmap['0'] = ')'
shiftmap['-'] = '_'
shiftmap['='] = '+'
shiftmap['['] = '{'
shiftmap[']'] = '}'
shiftmap['\\'] = '|'
shiftmap[';'] = ':'
shiftmap["'"] = '"'
shiftmap[","] = '<'
shiftmap["."] = '>'
shiftmap["/"] = '?'
# We don't use uppercase alpha for emacs codes so they are
# free to use for movement or function keys
# HELP
# INSERT
# HOME
# END
# PGUP
# PGDN
# RETURN
# BACKSPACE
# TAB
# DELETE
# "Keyboard Map" : {
mapfmt = r''' "0x{ucode:x}-0x{ameta:x}" : {{
"Text" : "[27;{meta:d};{ascii:d}~",
"Action" : 10
}}{comma} '''
elfmt = r' (define-key function-key-map "\e[27;{meta:d};{ascii:d}~" (kbd "{emesc}{emkey}"))'
elfmt2 = r' (define-key function-key-map "\e[27;{meta:d};{ascii:d}~" [{emesc}{emkey}])'
mapfuncfmt = r''' "0x{ucode:x}-0x{ameta:x}" : {{
"Text" : "[1;{meta:d}{echar}",
"Action" : 10
}}{comma} '''
elfuncfmt = r' (define-key function-key-map "\e[1;{meta:d}{echar}" [{emesc}{emkey}])'
mapfunc2fmt = r''' "0x{ucode:x}-0x{ameta:x}" : {{
"Text" : "[{echar};{meta:d}~",
"Action" : 10
}}{comma} '''
elfunc2fmt = r' (define-key function-key-map "\e[{echar};{meta:d}~" [{emesc}{emkey}])'
mapfunckeyfmt = r''' "0x{ucode:x}-0x{ameta:x}" : {{
"Text" : "O{meta:d}{echar}",
"Action" : 10
}}{comma} '''
elfunckeyfmt = r' (define-key function-key-map "\eO{meta:d}{echar}" [{emesc}{emkey}])'
SHIFT = 1
CONTROL = 2
META = 4
# No alpha in unshifted control
nocontrol = set([chr(x) for x in xrange(ord('a'), ord('z') + 1)])
# Add control characters that are normally handled
nocontrol.add('@')
nocontrol.add('[')
nocontrol.add(']')
nocontrol.add('\\')
def lookahead(iterable):
"""Return an element and an indication if it's the last element"""
i = iter(iterable)
last = i.next()
for elm in i:
yield last, False
last = elm
yield last, True
def makeapple (flags):
return (flags << 1) << 16
def xtermmeta (flags):
metamap = {
SHIFT: 2,
META: 3,
META | SHIFT: 4,
CONTROL: 5,
CONTROL | SHIFT: 6,
CONTROL | META: 7,
CONTROL | META | SHIFT: 8
}
return metamap[flags]
def doit (iterm=False, tofile=sys.stdout):
def myprint (out):
tofile.write(out + "\n")
if iterm:
myprint("""{{
"Profiles": [
{{
"Name": "xterm-keymap-profile",
"Guid": "{}",
"Dynamic Profile Parent Name": "default",
"Keyboard Map" : {{""".format(uuid.uuid4()))
# For alpha characters
# for emacs the actual key name is C-a and C-S-a (i.e., not C-A)
# for iterm the unicode is the actual key value (a vs A) also with shift key indicated
# we will use the unshifted key value in our escape value.
# For non-alpha:
# for emacs the actual key names are C-` and C-~ (i.e., shifted value no S- key)
# for iterm the unicode is the actual value as well as above but with the shift key
# indicated.
# we will use the actual value in our escape code as well.
for (val, sval), last in lookahead(shiftmap.items()):
# We do not do META or SHIFT|META here as these are fully supported by prefixing
# the keys with ^[ (or setting the eighth bit).
for meta, lastm in lookahead((CONTROL, CONTROL | META)):
emesc = ""
if meta & CONTROL:
emesc += "C-"
if meta & META:
emesc += "M-"
emkey = val
# if emkey == '"' or emkey == "\\":
# emkey = "\\" + emkey
xmeta = xtermmeta(meta)
# Don't enter codes for normal alpha control character
if meta == CONTROL and val in nocontrol:
pass
else:
# First handle unshifted version
ucode = ord(emkey)
if iterm:
myprint(mapfmt.format(ucode=ord(val),
ameta=makeapple(meta),
meta=xmeta,
ascii=ucode,
comma=","))
else:
if emkey == '"':
emkey = '\\"'
if emkey == '\\':
emkey = '\\\\'
myprint(elfmt.format(meta=xmeta, ascii=ucode, emkey=emkey, emesc=emesc))
# Now handle shifted value
meta |= SHIFT
if val.isalpha():
emkey = sval
emesc += 'S-'
else:
emkey = sval
xmeta = xtermmeta(meta)
if meta == (CONTROL | SHIFT) and sval in nocontrol:
pass
else:
ucode = ord(emkey)
if iterm:
myprint(mapfmt.format(ucode=ord(sval),
ameta=makeapple(meta | SHIFT),
meta=xmeta,
ascii=ucode,
comma=","))
else:
if emkey == '"':
emkey = '\\"'
if emkey == '\\':
emkey = '\\\\'
myprint(elfmt.format(meta=xmeta, ascii=ucode, emkey=emkey, emesc=emesc))
# if last and lastm:
# myprint()
# =========================
# Handle Arrow and HOME/END
# =========================
funcmap = {
"up": ("A", 0xf700, elfuncfmt, mapfuncfmt, 0x200000),
"down": ("B", 0xf701, elfuncfmt, mapfuncfmt, 0x200000),
"left": ("D", 0xf702, elfuncfmt, mapfuncfmt, 0x200000),
"right": ("C", 0xf703, elfuncfmt, mapfuncfmt, 0x200000),
"end": ("F", 0xf72b, elfuncfmt, mapfuncfmt, 0),
"home": ("H", 0xf729, elfuncfmt, mapfuncfmt, 0),
"insert": ("2", 0xF727, elfunc2fmt, mapfunc2fmt, 0),
"delete": ("3", 0xF728, elfunc2fmt, mapfunc2fmt, 0),
"prior": ("5", 0xf72c, elfunc2fmt, mapfunc2fmt, 0),
"next": ("6", 0xf72d, elfunc2fmt, mapfunc2fmt, 0),
"tab": (9, 0x9, elfmt2, mapfmt, 0),
"return": (13, 0xd, elfmt2, mapfmt, 0),
"f1": ("P", 0xf704, elfunckeyfmt, mapfunckeyfmt, 0),
"f2": ("Q", 0xf705, elfunckeyfmt, mapfunckeyfmt, 0),
"f3": ("R", 0xf706, elfunckeyfmt, mapfunckeyfmt, 0),
"f4": ("S", 0xf707, elfunckeyfmt, mapfunckeyfmt, 0),
"f5": ("15", 0xf708, elfunc2fmt, mapfunc2fmt, 0),
"f6": ("17", 0xf709, elfunc2fmt, mapfunc2fmt, 0),
"f7": ("18", 0xf70A, elfunc2fmt, mapfunc2fmt, 0),
"f8": ("19", 0xf70B, elfunc2fmt, mapfunc2fmt, 0),
"f9": ("20", 0xf70C, elfunc2fmt, mapfunc2fmt, 0),
"f10": ("21", 0xf70D, elfunc2fmt, mapfunc2fmt, 0),
"f11": ("23", 0xf70E, elfunc2fmt, mapfunc2fmt, 0),
"f12": ("24", 0xf70F, elfunc2fmt, mapfunc2fmt, 0),
}
for (key, val), last in lookahead(funcmap.items()):
for meta, lastm in lookahead([CONTROL,
SHIFT,
META,
CONTROL | SHIFT,
META | SHIFT,
CONTROL | META,
CONTROL | SHIFT | META]):
echar = val[0]
ucode = val[1]
xmeta = xtermmeta(meta)
if iterm:
# Tab character unicode 19 for backtab (shifted tab)
if ucode == 9 and (meta & SHIFT):
ucode = 0x19
comma = "" if last and lastm else ","
myprint(val[3].format(ucode=ucode,
ameta=makeapple(meta) | val[4],
meta=xmeta,
echar=echar,
ascii=echar,
comma=comma))
else:
emesc = ""
if meta & CONTROL:
emesc += "C-"
if meta & META:
emesc += "M-"
if meta & SHIFT:
emesc += "S-"
ucode = ord(emkey)
myprint(val[2].format(meta=xmeta, ascii=echar, echar=echar, emkey=key, emesc=emesc))
myprint("")
if iterm:
myprint(' }')
myprint(' }')
myprint(' ]')
myprint('}')
else:
myprint("(provide 'iterm-xterm-extra)")
# CSI 27 ; METACODE ; KEYCODE ~
import StringIO
sfile = StringIO.StringIO()
doit(args.iterm, tofile=sfile)
output = sfile.getvalue()
# sys.stdout.write(output)
if args.iterm:
import json
import plistlib
jdict = json.loads(output)
plistlib.writePlist(jdict, sys.stdout)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment