src/extras/pyrepl/socket_keymap.py
changeset 0 ca70ae20a155
equal deleted inserted replaced
-1:000000000000 0:ca70ae20a155
       
     1 #   Copyright 2000-2004 Michael Hudson mwh@python.net
       
     2 #
       
     3 # Portions Copyright (c) 2005 Nokia Corporation 
       
     4 #
       
     5 #                        All Rights Reserved
       
     6 #
       
     7 #
       
     8 # Permission to use, copy, modify, and distribute this software and
       
     9 # its documentation for any purpose is hereby granted without fee,
       
    10 # provided that the above copyright notice appear in all copies and
       
    11 # that both that copyright notice and this permission notice appear in
       
    12 # supporting documentation.
       
    13 #
       
    14 # THE AUTHOR MICHAEL HUDSON DISCLAIMS ALL WARRANTIES WITH REGARD TO
       
    15 # THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
       
    16 # AND FITNESS, IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL,
       
    17 # INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
       
    18 # RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
       
    19 # CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
       
    20 # CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
       
    21 
       
    22 # keyspec parsing for a pygame console.  currently this is simply copy
       
    23 # n' change from the unix (ie. trad terminal) variant; probably some
       
    24 # refactoring will happen when I work out how it will work best.
       
    25 
       
    26 # A key is represented as *either*
       
    27 
       
    28 # a) a (keycode, meta, ctrl) sequence (used for special keys such as
       
    29 # f1, the up arrow key, etc)
       
    30 # b) a (unichar, meta, ctrl) sequence (used for printable chars)
       
    31 
       
    32 # Because we allow keystokes like '\\C-xu', I'll use the same trick as
       
    33 # the unix keymap module uses.
       
    34 
       
    35 # '\\C-a' --> (K_a, 0, 1)
       
    36 
       
    37 # XXX it's actually possible to test this module, so it should have a
       
    38 # XXX test suite.
       
    39 
       
    40 import ascii
       
    41 
       
    42 _escapes = {
       
    43     '\\': K_BACKSLASH,
       
    44     "'" : K_QUOTE,
       
    45     '"' : K_QUOTEDBL,
       
    46 #    'a' : '\a',
       
    47     'b' : K_BACKSLASH,
       
    48     'e' : K_ESCAPE,
       
    49 #    'f' : '\f',
       
    50     'n' : K_RETURN,
       
    51     'r' : K_RETURN,
       
    52     't' : K_TAB,
       
    53 #    'v' : '\v'
       
    54     }
       
    55 
       
    56 _keynames = {
       
    57     'backspace' : K_BACKSPACE,
       
    58     'delete'    : K_DELETE,
       
    59     'down'      : K_DOWN,
       
    60     'end'       : K_END,
       
    61     'enter'     : K_KP_ENTER,
       
    62     'escape'    : K_ESCAPE,
       
    63     'f1' : K_F1, 'f2' : K_F2, 'f3' : K_F3, 'f4' : K_F4,
       
    64     'f5' : K_F5, 'f6' : K_F6, 'f7' : K_F7, 'f8' : K_F8,
       
    65     'f9' : K_F9, 'f10': K_F10,'f11': K_F11,'f12': K_F12,
       
    66     'f13': K_F13,'f14': K_F14,'f15': K_F15,
       
    67     'home'   : K_HOME,
       
    68     'insert' : K_INSERT,
       
    69     'left'   : K_LEFT,
       
    70     'pgdown' : K_PAGEDOWN, 'page down' : K_PAGEDOWN,
       
    71     'pgup'   : K_PAGEUP,   'page up'   : K_PAGEUP,
       
    72     'return' : K_RETURN,
       
    73     'right'  : K_RIGHT,
       
    74     'space'  : K_SPACE,
       
    75     'tab'    : K_TAB,
       
    76     'up'     : K_UP,
       
    77     }
       
    78 
       
    79 class KeySpecError(Exception):
       
    80     pass
       
    81 
       
    82 def _parse_key1(key, s):
       
    83     ctrl = 0
       
    84     meta = 0
       
    85     ret = ''
       
    86     while not ret and s < len(key):
       
    87         if key[s] == '\\':
       
    88             c = key[s+1].lower()
       
    89             if _escapes.has_key(c):
       
    90                 ret = _escapes[c]
       
    91                 s += 2
       
    92             elif c == "c":
       
    93                 if key[s + 2] != '-':
       
    94                     raise KeySpecError, \
       
    95                               "\\C must be followed by `-' (char %d of %s)"%(
       
    96                         s + 2, repr(key))
       
    97                 if ctrl:
       
    98                     raise KeySpecError, "doubled \\C- (char %d of %s)"%(
       
    99                         s + 1, repr(key))
       
   100                 ctrl = 1
       
   101                 s += 3
       
   102             elif c == "m":
       
   103                 if key[s + 2] != '-':
       
   104                     raise KeySpecError, \
       
   105                               "\\M must be followed by `-' (char %d of %s)"%(
       
   106                         s + 2, repr(key))
       
   107                 if meta:
       
   108                     raise KeySpecError, "doubled \\M- (char %d of %s)"%(
       
   109                         s + 1, repr(key))
       
   110                 meta = 1
       
   111                 s += 3
       
   112             elif c.isdigit():
       
   113                 n = key[s+1:s+4]
       
   114                 ret = chr(int(n, 8))
       
   115                 s += 4
       
   116             elif c == 'x':
       
   117                 n = key[s+2:s+4]
       
   118                 ret = chr(int(n, 16))
       
   119                 s += 4
       
   120             elif c == '<':
       
   121                 t = key.find('>', s)
       
   122                 if t == -1:
       
   123                     raise KeySpecError, \
       
   124                               "unterminated \\< starting at char %d of %s"%(
       
   125                         s + 1, repr(key))
       
   126                 try:
       
   127                     ret = _keynames[key[s+2:t].lower()]
       
   128                     s = t + 1
       
   129                 except KeyError:
       
   130                     raise KeySpecError, \
       
   131                               "unrecognised keyname `%s' at char %d of %s"%(
       
   132                         key[s+2:t], s + 2, repr(key))
       
   133                 if ret is None:
       
   134                     return None, s
       
   135             else:
       
   136                 raise KeySpecError, \
       
   137                           "unknown backslash escape %s at char %d of %s"%(
       
   138                     `c`, s + 2, repr(key))
       
   139         else:
       
   140             if ctrl:
       
   141                 ret = unicode(ascii.ctrl(key[s]))
       
   142             else:
       
   143                 ret = unicode(key[s])
       
   144             s += 1
       
   145     return (ret, meta, ctrl), s
       
   146 
       
   147 def parse_keys(key):
       
   148     s = 0
       
   149     r = []
       
   150     while s < len(key):
       
   151         k, s = _parse_key1(key, s)
       
   152         if k is None:
       
   153             return None
       
   154         r.append(k)
       
   155     return tuple(r)
       
   156 
       
   157 def _compile_keymap(keymap):
       
   158     r = {}
       
   159     for key, value in keymap.items():
       
   160         r.setdefault(key[0], {})[key[1:]] = value
       
   161     for key, value in r.items():
       
   162         if value.has_key(()):
       
   163             if len(value) <> 1:
       
   164                 raise KeySpecError, \
       
   165                           "key definitions for %s clash"%(value.values(),)
       
   166             else:
       
   167                 r[key] = value[()]
       
   168         else:
       
   169             r[key] = _compile_keymap(value)
       
   170     return r
       
   171 
       
   172 def compile_keymap(keymap):
       
   173     r = {}
       
   174     for key, value in keymap:
       
   175         k = parse_keys(key)
       
   176         if value is None and r.has_key(k):
       
   177             del r[k]
       
   178         if k is not None:
       
   179             r[k] = value
       
   180     return _compile_keymap(r)
       
   181 
       
   182 def keyname(key):
       
   183     longest_match = ''
       
   184     longest_match_name = ''
       
   185     for name, keyseq in keyset.items():
       
   186         if keyseq and key.startswith(keyseq) and \
       
   187                len(keyseq) > len(longest_match):
       
   188             longest_match = keyseq
       
   189             longest_match_name = name
       
   190     if len(longest_match) > 0:
       
   191         return longest_match_name, len(longest_match)
       
   192     else:
       
   193         return None, 0
       
   194 
       
   195 _unescapes = {'\r':'\\r', '\n':'\\n', '\177':'^?'}
       
   196 
       
   197 #for k,v in _escapes.items():
       
   198 #    _unescapes[v] = k
       
   199 
       
   200 def unparse_key(keyseq):
       
   201     if not keyseq:
       
   202         return ''
       
   203     name, s = keyname(keyseq)
       
   204     if name:
       
   205         if name <> 'escape' or s == len(keyseq):
       
   206             return '\\<' + name + '>' + unparse_key(keyseq[s:])
       
   207         else:
       
   208             return '\\M-' + unparse_key(keyseq[1:])
       
   209     else:
       
   210         c = keyseq[0]
       
   211         r = keyseq[1:]
       
   212         if c == '\\':
       
   213             p = '\\\\'
       
   214         elif _unescapes.has_key(c):
       
   215             p = _unescapes[c]
       
   216         elif ord(c) < ord(' '):
       
   217             p = '\\C-%s'%(chr(ord(c)+96),)
       
   218         elif ord(' ') <= ord(c) <= ord('~'):
       
   219             p = c
       
   220         else:
       
   221             p = '\\%03o'%(ord(c),)
       
   222         return p + unparse_key(r)
       
   223 
       
   224 def _unparse_keyf(keyseq):
       
   225     if not keyseq:
       
   226         return []
       
   227     name, s = keyname(keyseq)
       
   228     if name:
       
   229         if name <> 'escape' or s == len(keyseq):
       
   230             return [name] + _unparse_keyf(keyseq[s:])
       
   231         else:
       
   232             rest = _unparse_keyf(keyseq[1:])
       
   233             return ['M-'+rest[0]] + rest[1:]
       
   234     else:
       
   235         c = keyseq[0]
       
   236         r = keyseq[1:]
       
   237         if c == '\\':
       
   238             p = '\\'
       
   239         elif _unescapes.has_key(c):
       
   240             p = _unescapes[c]
       
   241         elif ord(c) < ord(' '):
       
   242             p = 'C-%s'%(chr(ord(c)+96),)
       
   243         elif ord(' ') <= ord(c) <= ord('~'):
       
   244             p = c
       
   245         else:
       
   246             p = '\\%03o'%(ord(c),)
       
   247         return [p] + _unparse_keyf(r)
       
   248 
       
   249 def unparse_keyf(keyseq):
       
   250     return " ".join(_unparse_keyf(keyseq))