mirror of
				https://github.com/python/cpython.git
				synced 2025-11-04 03:44:55 +00:00 
			
		
		
		
	Co-authored-by: Łukasz Langa <lukasz@langa.pl> Co-authored-by: Marta Gómez Macías <mgmacias@google.com> Co-authored-by: Lysandros Nikolaou <lisandrosnik@gmail.com> Co-authored-by: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Co-authored-by: Jelle Zijlstra <jelle.zijlstra@gmail.com>
		
			
				
	
	
		
			215 lines
		
	
	
	
		
			6.1 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			215 lines
		
	
	
	
		
			6.1 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
#   Copyright 2000-2008 Michael Hudson-Doyle <micahel@gmail.com>
 | 
						|
#                       Armin Rigo
 | 
						|
#
 | 
						|
#                        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.
 | 
						|
 | 
						|
"""
 | 
						|
functions for parsing keyspecs
 | 
						|
 | 
						|
Support for turning keyspecs into appropriate sequences.
 | 
						|
 | 
						|
pyrepl uses it's own bastardized keyspec format, which is meant to be
 | 
						|
a strict superset of readline's \"KEYSEQ\" format (which is to say
 | 
						|
that if you can come up with a spec readline accepts that this
 | 
						|
doesn't, you've found a bug and should tell me about it).
 | 
						|
 | 
						|
Note that this is the `\\C-o' style of readline keyspec, not the
 | 
						|
`Control-o' sort.
 | 
						|
 | 
						|
A keyspec is a string representing a sequence of keypresses that can
 | 
						|
be bound to a command.
 | 
						|
 | 
						|
All characters other than the backslash represent themselves.  In the
 | 
						|
traditional manner, a backslash introduces a escape sequence.
 | 
						|
 | 
						|
The extension to readline is that the sequence \\<KEY> denotes the
 | 
						|
sequence of charaters produced by hitting KEY.
 | 
						|
 | 
						|
Examples:
 | 
						|
 | 
						|
`a'     - what you get when you hit the `a' key
 | 
						|
`\\EOA'  - Escape - O - A (up, on my terminal)
 | 
						|
`\\<UP>' - the up arrow key
 | 
						|
`\\<up>' - ditto (keynames are case insensitive)
 | 
						|
`\\C-o', `\\c-o'  - control-o
 | 
						|
`\\M-.'  - meta-period
 | 
						|
`\\E.'   - ditto (that's how meta works for pyrepl)
 | 
						|
`\\<tab>', `\\<TAB>', `\\t', `\\011', '\\x09', '\\X09', '\\C-i', '\\C-I'
 | 
						|
   - all of these are the tab character.  Can you think of any more?
 | 
						|
"""
 | 
						|
 | 
						|
_escapes = {
 | 
						|
    "\\": "\\",
 | 
						|
    "'": "'",
 | 
						|
    '"': '"',
 | 
						|
    "a": "\a",
 | 
						|
    "b": "\b",
 | 
						|
    "e": "\033",
 | 
						|
    "f": "\f",
 | 
						|
    "n": "\n",
 | 
						|
    "r": "\r",
 | 
						|
    "t": "\t",
 | 
						|
    "v": "\v",
 | 
						|
}
 | 
						|
 | 
						|
_keynames = {
 | 
						|
    "backspace": "backspace",
 | 
						|
    "delete": "delete",
 | 
						|
    "down": "down",
 | 
						|
    "end": "end",
 | 
						|
    "enter": "\r",
 | 
						|
    "escape": "\033",
 | 
						|
    "f1": "f1",
 | 
						|
    "f2": "f2",
 | 
						|
    "f3": "f3",
 | 
						|
    "f4": "f4",
 | 
						|
    "f5": "f5",
 | 
						|
    "f6": "f6",
 | 
						|
    "f7": "f7",
 | 
						|
    "f8": "f8",
 | 
						|
    "f9": "f9",
 | 
						|
    "f10": "f10",
 | 
						|
    "f11": "f11",
 | 
						|
    "f12": "f12",
 | 
						|
    "f13": "f13",
 | 
						|
    "f14": "f14",
 | 
						|
    "f15": "f15",
 | 
						|
    "f16": "f16",
 | 
						|
    "f17": "f17",
 | 
						|
    "f18": "f18",
 | 
						|
    "f19": "f19",
 | 
						|
    "f20": "f20",
 | 
						|
    "home": "home",
 | 
						|
    "insert": "insert",
 | 
						|
    "left": "left",
 | 
						|
    "page down": "page down",
 | 
						|
    "page up": "page up",
 | 
						|
    "return": "\r",
 | 
						|
    "right": "right",
 | 
						|
    "space": " ",
 | 
						|
    "tab": "\t",
 | 
						|
    "up": "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 c in _escapes:
 | 
						|
                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))
 | 
						|
                    )
 | 
						|
                ret = key[s + 2 : t].lower()
 | 
						|
                if ret not in _keynames:
 | 
						|
                    raise KeySpecError(
 | 
						|
                        "unrecognised keyname `%s' at char %d of %s"
 | 
						|
                        % (ret, s + 2, repr(key))
 | 
						|
                    )
 | 
						|
                ret = _keynames[ret]
 | 
						|
                s = t + 1
 | 
						|
            else:
 | 
						|
                raise KeySpecError(
 | 
						|
                    "unknown backslash escape %s at char %d of %s"
 | 
						|
                    % (repr(c), s + 2, repr(key))
 | 
						|
                )
 | 
						|
        else:
 | 
						|
            ret = key[s]
 | 
						|
            s += 1
 | 
						|
    if ctrl:
 | 
						|
        if len(ret) > 1:
 | 
						|
            raise KeySpecError("\\C- must be followed by a character")
 | 
						|
        ret = chr(ord(ret) & 0x1F)  # curses.ascii.ctrl()
 | 
						|
    if meta:
 | 
						|
        ret = ["\033", ret]
 | 
						|
    else:
 | 
						|
        ret = [ret]
 | 
						|
    return ret, s
 | 
						|
 | 
						|
 | 
						|
def parse_keys(key):
 | 
						|
    s = 0
 | 
						|
    r = []
 | 
						|
    while s < len(key):
 | 
						|
        k, s = _parse_key1(key, s)
 | 
						|
        r.extend(k)
 | 
						|
    return r
 | 
						|
 | 
						|
 | 
						|
def compile_keymap(keymap, empty=b""):
 | 
						|
    r = {}
 | 
						|
    for key, value in keymap.items():
 | 
						|
        if isinstance(key, bytes):
 | 
						|
            first = key[:1]
 | 
						|
        else:
 | 
						|
            first = key[0]
 | 
						|
        r.setdefault(first, {})[key[1:]] = value
 | 
						|
    for key, value in r.items():
 | 
						|
        if empty in value:
 | 
						|
            if len(value) != 1:
 | 
						|
                raise KeySpecError("key definitions for %s clash" % (value.values(),))
 | 
						|
            else:
 | 
						|
                r[key] = value[empty]
 | 
						|
        else:
 | 
						|
            r[key] = compile_keymap(value, empty)
 | 
						|
    return r
 |