src/extras/pyrepl/socket_keymap.py
changeset 0 ca70ae20a155
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/extras/pyrepl/socket_keymap.py	Tue Feb 16 10:07:05 2010 +0530
@@ -0,0 +1,250 @@
+#   Copyright 2000-2004 Michael Hudson mwh@python.net
+#
+# Portions Copyright (c) 2005 Nokia Corporation 
+#
+#                        All Rights Reserved
+#
+#
+# Permission to use, copy, modify, and distribute this software and
+# its documentation for any purpose is hereby granted without fee,
+# provided that the above copyright notice appear in all copies and
+# that both that copyright notice and this permission notice appear in
+# supporting documentation.
+#
+# THE AUTHOR MICHAEL HUDSON DISCLAIMS ALL WARRANTIES WITH REGARD TO
+# THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS, IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL,
+# INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+# RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
+# CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+# CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+# keyspec parsing for a pygame console.  currently this is simply copy
+# n' change from the unix (ie. trad terminal) variant; probably some
+# refactoring will happen when I work out how it will work best.
+
+# A key is represented as *either*
+
+# a) a (keycode, meta, ctrl) sequence (used for special keys such as
+# f1, the up arrow key, etc)
+# b) a (unichar, meta, ctrl) sequence (used for printable chars)
+
+# Because we allow keystokes like '\\C-xu', I'll use the same trick as
+# the unix keymap module uses.
+
+# '\\C-a' --> (K_a, 0, 1)
+
+# XXX it's actually possible to test this module, so it should have a
+# XXX test suite.
+
+import ascii
+
+_escapes = {
+    '\\': K_BACKSLASH,
+    "'" : K_QUOTE,
+    '"' : K_QUOTEDBL,
+#    'a' : '\a',
+    'b' : K_BACKSLASH,
+    'e' : K_ESCAPE,
+#    'f' : '\f',
+    'n' : K_RETURN,
+    'r' : K_RETURN,
+    't' : K_TAB,
+#    'v' : '\v'
+    }
+
+_keynames = {
+    'backspace' : K_BACKSPACE,
+    'delete'    : K_DELETE,
+    'down'      : K_DOWN,
+    'end'       : K_END,
+    'enter'     : K_KP_ENTER,
+    'escape'    : K_ESCAPE,
+    'f1' : K_F1, 'f2' : K_F2, 'f3' : K_F3, 'f4' : K_F4,
+    'f5' : K_F5, 'f6' : K_F6, 'f7' : K_F7, 'f8' : K_F8,
+    'f9' : K_F9, 'f10': K_F10,'f11': K_F11,'f12': K_F12,
+    'f13': K_F13,'f14': K_F14,'f15': K_F15,
+    'home'   : K_HOME,
+    'insert' : K_INSERT,
+    'left'   : K_LEFT,
+    'pgdown' : K_PAGEDOWN, 'page down' : K_PAGEDOWN,
+    'pgup'   : K_PAGEUP,   'page up'   : K_PAGEUP,
+    'return' : K_RETURN,
+    'right'  : K_RIGHT,
+    'space'  : K_SPACE,
+    'tab'    : K_TAB,
+    'up'     : K_UP,
+    }
+
+class KeySpecError(Exception):
+    pass
+
+def _parse_key1(key, s):
+    ctrl = 0
+    meta = 0
+    ret = ''
+    while not ret and s < len(key):
+        if key[s] == '\\':
+            c = key[s+1].lower()
+            if _escapes.has_key(c):
+                ret = _escapes[c]
+                s += 2
+            elif c == "c":
+                if key[s + 2] != '-':
+                    raise KeySpecError, \
+                              "\\C must be followed by `-' (char %d of %s)"%(
+                        s + 2, repr(key))
+                if ctrl:
+                    raise KeySpecError, "doubled \\C- (char %d of %s)"%(
+                        s + 1, repr(key))
+                ctrl = 1
+                s += 3
+            elif c == "m":
+                if key[s + 2] != '-':
+                    raise KeySpecError, \
+                              "\\M must be followed by `-' (char %d of %s)"%(
+                        s + 2, repr(key))
+                if meta:
+                    raise KeySpecError, "doubled \\M- (char %d of %s)"%(
+                        s + 1, repr(key))
+                meta = 1
+                s += 3
+            elif c.isdigit():
+                n = key[s+1:s+4]
+                ret = chr(int(n, 8))
+                s += 4
+            elif c == 'x':
+                n = key[s+2:s+4]
+                ret = chr(int(n, 16))
+                s += 4
+            elif c == '<':
+                t = key.find('>', s)
+                if t == -1:
+                    raise KeySpecError, \
+                              "unterminated \\< starting at char %d of %s"%(
+                        s + 1, repr(key))
+                try:
+                    ret = _keynames[key[s+2:t].lower()]
+                    s = t + 1
+                except KeyError:
+                    raise KeySpecError, \
+                              "unrecognised keyname `%s' at char %d of %s"%(
+                        key[s+2:t], s + 2, repr(key))
+                if ret is None:
+                    return None, s
+            else:
+                raise KeySpecError, \
+                          "unknown backslash escape %s at char %d of %s"%(
+                    `c`, s + 2, repr(key))
+        else:
+            if ctrl:
+                ret = unicode(ascii.ctrl(key[s]))
+            else:
+                ret = unicode(key[s])
+            s += 1
+    return (ret, meta, ctrl), s
+
+def parse_keys(key):
+    s = 0
+    r = []
+    while s < len(key):
+        k, s = _parse_key1(key, s)
+        if k is None:
+            return None
+        r.append(k)
+    return tuple(r)
+
+def _compile_keymap(keymap):
+    r = {}
+    for key, value in keymap.items():
+        r.setdefault(key[0], {})[key[1:]] = value
+    for key, value in r.items():
+        if value.has_key(()):
+            if len(value) <> 1:
+                raise KeySpecError, \
+                          "key definitions for %s clash"%(value.values(),)
+            else:
+                r[key] = value[()]
+        else:
+            r[key] = _compile_keymap(value)
+    return r
+
+def compile_keymap(keymap):
+    r = {}
+    for key, value in keymap:
+        k = parse_keys(key)
+        if value is None and r.has_key(k):
+            del r[k]
+        if k is not None:
+            r[k] = value
+    return _compile_keymap(r)
+
+def keyname(key):
+    longest_match = ''
+    longest_match_name = ''
+    for name, keyseq in keyset.items():
+        if keyseq and key.startswith(keyseq) and \
+               len(keyseq) > len(longest_match):
+            longest_match = keyseq
+            longest_match_name = name
+    if len(longest_match) > 0:
+        return longest_match_name, len(longest_match)
+    else:
+        return None, 0
+
+_unescapes = {'\r':'\\r', '\n':'\\n', '\177':'^?'}
+
+#for k,v in _escapes.items():
+#    _unescapes[v] = k
+
+def unparse_key(keyseq):
+    if not keyseq:
+        return ''
+    name, s = keyname(keyseq)
+    if name:
+        if name <> 'escape' or s == len(keyseq):
+            return '\\<' + name + '>' + unparse_key(keyseq[s:])
+        else:
+            return '\\M-' + unparse_key(keyseq[1:])
+    else:
+        c = keyseq[0]
+        r = keyseq[1:]
+        if c == '\\':
+            p = '\\\\'
+        elif _unescapes.has_key(c):
+            p = _unescapes[c]
+        elif ord(c) < ord(' '):
+            p = '\\C-%s'%(chr(ord(c)+96),)
+        elif ord(' ') <= ord(c) <= ord('~'):
+            p = c
+        else:
+            p = '\\%03o'%(ord(c),)
+        return p + unparse_key(r)
+
+def _unparse_keyf(keyseq):
+    if not keyseq:
+        return []
+    name, s = keyname(keyseq)
+    if name:
+        if name <> 'escape' or s == len(keyseq):
+            return [name] + _unparse_keyf(keyseq[s:])
+        else:
+            rest = _unparse_keyf(keyseq[1:])
+            return ['M-'+rest[0]] + rest[1:]
+    else:
+        c = keyseq[0]
+        r = keyseq[1:]
+        if c == '\\':
+            p = '\\'
+        elif _unescapes.has_key(c):
+            p = _unescapes[c]
+        elif ord(c) < ord(' '):
+            p = 'C-%s'%(chr(ord(c)+96),)
+        elif ord(' ') <= ord(c) <= ord('~'):
+            p = c
+        else:
+            p = '\\%03o'%(ord(c),)
+        return [p] + _unparse_keyf(r)
+
+def unparse_keyf(keyseq):
+    return " ".join(_unparse_keyf(keyseq))