src/extras/pyrepl/keymap.py
changeset 0 ca70ae20a155
equal deleted inserted replaced
-1:000000000000 0:ca70ae20a155
       
     1 #   Copyright 2000-2004 Michael Hudson mwh@python.net
       
     2 #
       
     3 #                        All Rights Reserved
       
     4 #
       
     5 # Portions Copyright (c) 2005 Nokia Corporation 
       
     6 #
       
     7 # Permission to use, copy, modify, and distribute this software and
       
     8 # its documentation for any purpose is hereby granted without fee,
       
     9 # provided that the above copyright notice appear in all copies and
       
    10 # that both that copyright notice and this permission notice appear in
       
    11 # supporting documentation.
       
    12 #
       
    13 # THE AUTHOR MICHAEL HUDSON DISCLAIMS ALL WARRANTIES WITH REGARD TO
       
    14 # THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
       
    15 # AND FITNESS, IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL,
       
    16 # INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
       
    17 # RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
       
    18 # CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
       
    19 # CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
       
    20 
       
    21 """
       
    22 functions for parsing keyspecs
       
    23 
       
    24 Support for turning keyspecs into appropriate sequences.
       
    25 
       
    26 pyrepl uses it's own bastardized keyspec format, which is meant to be
       
    27 a strict superset of readline's \"KEYSEQ\" format (which is to say
       
    28 that if you can come up with a spec readline accepts that this
       
    29 doesn't, you've found a bug and should tell me about it).
       
    30 
       
    31 Note that this is the `\\C-o' style of readline keyspec, not the
       
    32 `Control-o' sort.
       
    33 
       
    34 A keyspec is a string representing a sequence of keypresses that can
       
    35 be bound to a command.
       
    36 
       
    37 All characters other than the backslash represent themselves.  In the
       
    38 traditional manner, a backslash introduces a escape sequence.
       
    39 
       
    40 The extension to readline is that the sequence \\<KEY> denotes the
       
    41 sequence of charaters produced by hitting KEY.
       
    42 
       
    43 Examples:
       
    44 
       
    45 `a'     - what you get when you hit the `a' key
       
    46 `\\EOA'  - Escape - O - A (up, on my terminal)
       
    47 `\\<UP>' - the up arrow key
       
    48 `\\<up>' - ditto (keynames are case insensitive)
       
    49 `\\C-o', `\\c-o'  - control-o
       
    50 `\\M-.'  - meta-period
       
    51 `\\E.'   - ditto (that's how meta works for pyrepl)
       
    52 `\\<tab>', `\\<TAB>', `\\t', `\\011', '\\x09', '\\X09', '\\C-i', '\\C-I'
       
    53    - all of these are the tab character.  Can you think of any more?
       
    54 """
       
    55 
       
    56 import ascii
       
    57 
       
    58 _escapes = {
       
    59     '\\':'\\',
       
    60     "'":"'",
       
    61     '"':'"',
       
    62     'a':'\a',
       
    63     'b':'\h',
       
    64     'e':'\033',
       
    65     'f':'\f',
       
    66     'n':'\n',
       
    67     'r':'\r',
       
    68     't':'\t',
       
    69     'v':'\v'
       
    70     }
       
    71 
       
    72 _keynames = {
       
    73     'backspace': 'backspace',
       
    74     'delete':    'delete',
       
    75     'down':      'down',
       
    76     'end':       'end',
       
    77     'enter':     '\r',
       
    78     'escape':    '\033',
       
    79     'f1' : 'f1',   'f2' : 'f2',   'f3' : 'f3',   'f4' : 'f4',
       
    80     'f5' : 'f5',   'f6' : 'f6',   'f7' : 'f7',   'f8' : 'f8',
       
    81     'f9' : 'f9',   'f10': 'f10',  'f11': 'f11',  'f12': 'f12',
       
    82     'f13': 'f13',  'f14': 'f14',  'f15': 'f15',  'f16': 'f16',
       
    83     'f17': 'f17',  'f18': 'f18',  'f19': 'f19',  'f20': 'f20',
       
    84     'home':      'home',
       
    85     'insert':    'insert',
       
    86     'left':      'left',
       
    87     'page down': 'page down',
       
    88     'page up':   'page up',
       
    89 #    'return':    'enter',
       
    90     'return':    '\r',
       
    91     'right':     'right',
       
    92     'space':     ' ',
       
    93     'tab':       '\t',
       
    94     'up':        'up',
       
    95     }
       
    96 
       
    97 class KeySpecError(Exception):
       
    98     pass
       
    99 
       
   100 def _parse_key1(key, s):
       
   101     ctrl = 0
       
   102     meta = 0
       
   103     ret = ''
       
   104     while not ret and s < len(key):
       
   105         if key[s] == '\\':
       
   106             c = key[s+1].lower()
       
   107             if _escapes.has_key(c):
       
   108                 ret = _escapes[c]
       
   109                 s += 2
       
   110             elif c == "c":
       
   111                 if key[s + 2] != '-':
       
   112                     raise KeySpecError, \
       
   113                               "\\C must be followed by `-' (char %d of %s)"%(
       
   114                         s + 2, repr(key))
       
   115                 if ctrl:
       
   116                     raise KeySpecError, "doubled \\C- (char %d of %s)"%(
       
   117                         s + 1, repr(key))
       
   118                 ctrl = 1
       
   119                 s += 3
       
   120             elif c == "m":
       
   121                 if key[s + 2] != '-':
       
   122                     raise KeySpecError, \
       
   123                               "\\M must be followed by `-' (char %d of %s)"%(
       
   124                         s + 2, repr(key))
       
   125                 if meta:
       
   126                     raise KeySpecError, "doubled \\M- (char %d of %s)"%(
       
   127                         s + 1, repr(key))
       
   128                 meta = 1
       
   129                 s += 3
       
   130             elif c.isdigit():
       
   131                 n = key[s+1:s+4]
       
   132                 ret = chr(int(n, 8))
       
   133                 s += 4
       
   134             elif c == 'x':
       
   135                 n = key[s+2:s+4]
       
   136                 ret = chr(int(n, 16))
       
   137                 s += 4
       
   138             elif c == '<':
       
   139                 t = key.find('>', s)
       
   140                 if t == -1:
       
   141                     raise KeySpecError, \
       
   142                               "unterminated \\< starting at char %d of %s"%(
       
   143                         s + 1, repr(key))                        
       
   144                 ret = key[s+2:t].lower()
       
   145                 if ret not in _keynames:
       
   146                     raise KeySpecError, \
       
   147                               "unrecognised keyname `%s' at char %d of %s"%(
       
   148                         ret, s + 2, repr(key))
       
   149                 ret = _keynames[ret]
       
   150                 s = t + 1
       
   151             else:
       
   152                 raise KeySpecError, \
       
   153                           "unknown backslash escape %s at char %d of %s"%(
       
   154                     `c`, s + 2, repr(key))
       
   155         else:
       
   156             ret = key[s]
       
   157             s += 1
       
   158     if ctrl:
       
   159         if len(ret) > 1:
       
   160             raise KeySpecError, "\\C- must be followed by a character"
       
   161         ret = ascii.ctrl(ret)
       
   162     if meta:
       
   163         ret = ['\033', ret]
       
   164     else:
       
   165         ret = [ret]
       
   166     return ret, s
       
   167 
       
   168 def parse_keys(key):
       
   169     s = 0
       
   170     r = []
       
   171     while s < len(key):
       
   172         k, s = _parse_key1(key, s)
       
   173         r.extend(k)
       
   174     return r
       
   175 
       
   176 def compile_keymap(keymap, empty=''):
       
   177     r = {}
       
   178     for key, value in keymap.items():
       
   179         r.setdefault(key[0], {})[key[1:]] = value
       
   180     for key, value in r.items():
       
   181         if empty in value:
       
   182             if len(value) <> 1:
       
   183                 raise KeySpecError, \
       
   184                       "key definitions for %s clash"%(value.values(),)
       
   185             else:
       
   186                 r[key] = value[empty]
       
   187         else:
       
   188             r[key] = compile_keymap(value, empty)
       
   189     return r