0
|
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
|