Source code for prompt.key

"""Key module."""
from curses import ascii  # type: ignore
from collections import namedtuple
from .util import ensure_bytes, ensure_str, int2char


ESCAPE_QUOTE = str.maketrans({
    '"': '\\"',
})

CTRL_KEY = b'\x80\xfc\x04'
META_KEY = b'\x80\xfc\x08'

# :help key-notation
SPECIAL_KEYS = {
    'NUL': 10,
    'BS': b'\x80kb',
    'TAB': 9,
    'S-TAB': b'\x80kB',
    'NL': 10,
    'FE': 12,
    'CR': 13,
    'ESC': 27,
    'SPACE': 32,
    'LT': 60,
    'BSLASH': 92,
    'BAR': 124,
    'DEL': b'\x80kD',
    'CSI': b'\x9B',
    'XCSI': b'\x80\xfdP',
    'UP': b'\x80ku',
    'DOWN': b'\x80kd',
    'LEFT': b'\x80kl',
    'RIGHT': b'\x80kr',
    'S-UP': b'\x80\xfd',
    'S-DOWN': b'\x80\xfd',
    'S-LEFT': b'\x80#4',
    'S-RIGHT': b'\x80%i',
    'C-LEFT': b'\x80\xfdT',
    'C-RIGHT': b'\x80\xfdU',
    'F1': b'\x80k1',
    'F2': b'\x80k2',
    'F3': b'\x80k3',
    'F4': b'\x80k4',
    'F5': b'\x80k5',
    'F6': b'\x80k6',
    'F7': b'\x80k7',
    'F8': b'\x80k8',
    'F9': b'\x80k9',
    'F10': b'\x80k;',
    'F11': b'\x80F1',
    'F12': b'\x80F2',
    'S-F1': b'\x80\xfd\x06',
    'S-F2': b'\x80\xfd\x07',
    'S-F3': b'\x80\xfd\x08',
    'S-F4': b'\x80\xfd\x09',
    'S-F5': b'\x80\xfd\x0A',
    'S-F6': b'\x80\xfd\x0B',
    'S-F7': b'\x80\xfd\x0C',
    'S-F8': b'\x80\xfd\x0D',
    'S-F9': b'\x80\xfd\x0E',
    'S-F10': b'\x80\xfd\x0F',
    'S-F11': b'\x80\xfd\x10',
    'S-F12': b'\x80\xfd\x11',
    'HELP': b'\x80%1',
    'UNDO': b'\x80&8',
    'INSERT': b'\x80kI',
    'HOME': b'\x80kh',
    'END': b'\x80@7',
    'PAGEUP': b'\x80kP',
    'PAGEDOWN': b'\x80kN',
    'KHOME': b'\x80K1',
    'KEND': b'\x80K4',
    'KPAGEUP': b'\x80K3',
    'KPAGEDOWN': b'\x80K5',
    'KPLUS': b'\x80K6',
    'KMINUS': b'\x80K7',
    'KMULTIPLY': b'\x80K9',
    'KDIVIDE': b'\x80K8',
    'KENTER': b'\x80KA',
    'KPOINT': b'\x80KB',
    'K0': b'\x80KC',
    'K1': b'\x80KD',
    'K2': b'\x80KE',
    'K3': b'\x80KF',
    'K4': b'\x80KG',
    'K5': b'\x80KH',
    'K6': b'\x80KI',
    'K7': b'\x80KJ',
    'K8': b'\x80KK',
    'K9': b'\x80KL',
}
SPECIAL_KEYS_REVRESE = {v: k for k, v in SPECIAL_KEYS.items()}

# Add aliases used in Vim. This requires to be AFTER making swap dictionary
SPECIAL_KEYS.update(dict(
    NOP=SPECIAL_KEYS['NUL'],
    RETURN=SPECIAL_KEYS['CR'],
    ENTER=SPECIAL_KEYS['CR'],
    BACKSPACE=SPECIAL_KEYS['BS'],
    DELETE=SPECIAL_KEYS['DEL'],
    INS=SPECIAL_KEYS['INSERT'],
))


KeyBase = namedtuple('KeyBase', ['code', 'char'])


[docs]class Key(KeyBase): """Key class which indicate a single key. Attributes: code (int or bytes): A code of the key. A bytes is used when the key is a special key in Vim (a key which starts from 0x80 in getchar()). char (str): A printable represantation of the key. It might be an empty string when the key is not printable. """ __slots__ = () __cached = {} def __str__(self): return self.char @classmethod
[docs] def represent(cls, nvim, code): """Return a string representation of a Keycode.""" if isinstance(code, int): return int2char(nvim, code) if code in SPECIAL_KEYS_REVRESE: char = SPECIAL_KEYS_REVRESE.get(code) return '<%s>' % char else: return ensure_str(nvim, code)
@classmethod
[docs] def parse(cls, nvim, expr): """Parse a key expression and return a Key instance. It returns a Key instance of a key expression. The instance is cached to individual expression so that the instance is exactly equal when same expression is spcified. Args: expr (int, bytes, or str): A key expression. Example: >>> from unittest.mock import MagicMock >>> nvim = MagicMock() >>> nvim.options = {'encoding': 'utf-8'} >>> Key.parse(nvim, ord('a')) Key(code=97, char='a') >>> Key.parse(nvim, '<Insert>') Key(code=b'\x80kI', char='') Returns: Key: A Key instance. """ if expr not in cls.__cached: code = _resolve(nvim, expr) if isinstance(code, int): char = int2char(nvim, code) elif not code.startswith(b'\x80'): char = ensure_str(nvim, code) else: char = '' cls.__cached[expr] = cls(code, char) return cls.__cached[expr]
def _resolve(nvim, expr): if isinstance(expr, int): return expr elif isinstance(expr, str): return _resolve(nvim, ensure_bytes(nvim, expr)) elif isinstance(expr, bytes): if len(expr) == 1: return ord(expr) elif expr.startswith(b'\x80'): return expr else: raise AttributeError(( '`expr` (%s) requires to be an instance of int|bytes|str but ' '"%s" has specified.' ) % (expr, type(expr))) # Special key if expr.startswith(b'<') or expr.endswith(b'>'): inner = expr[1:-1] code = _resolve_from_special_keys(nvim, inner) if code != inner: return code return expr def _resolve_from_special_keys(nvim, inner): inner_upper = inner.upper() inner_upper_str = ensure_str(nvim, inner_upper) if inner_upper_str in SPECIAL_KEYS: return SPECIAL_KEYS[inner_upper_str] elif inner_upper.startswith(b'C-'): if len(inner) == 3: if inner_upper[-1] in b'@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_?': return ascii.ctrl(inner[-1]) return b''.join([ CTRL_KEY, _resolve_from_special_keys(nvim, inner[2:]), ]) elif inner_upper.startswith(b'M-') or inner_upper.startswith(b'A-'): return b''.join([ META_KEY, _resolve_from_special_keys(nvim, inner[2:]), ]) elif inner_upper == b'LEADER': leader = nvim.vars['mapleader'] leader = ensure_bytes(nvim, leader) return _resolve(nvim, leader) elif inner_upper == b'LOCALLEADER': leader = nvim.vars['maplocalleader'] leader = ensure_bytes(nvim, leader) return _resolve(nvim, leader) return inner