mirror of https://github.com/python/cpython
115 lines
3.7 KiB
Python
115 lines
3.7 KiB
Python
# Copyright 2000-2004 Michael Hudson-Doyle <micahel@gmail.com>
|
|
#
|
|
# 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.
|
|
|
|
# (naming modules after builtin functions is not such a hot idea...)
|
|
|
|
# an KeyTrans instance translates Event objects into Command objects
|
|
|
|
# hmm, at what level do we want [C-i] and [tab] to be equivalent?
|
|
# [meta-a] and [esc a]? obviously, these are going to be equivalent
|
|
# for the UnixConsole, but should they be for PygameConsole?
|
|
|
|
# it would in any situation seem to be a bad idea to bind, say, [tab]
|
|
# and [C-i] to *different* things... but should binding one bind the
|
|
# other?
|
|
|
|
# executive, temporary decision: [tab] and [C-i] are distinct, but
|
|
# [meta-key] is identified with [esc key]. We demand that any console
|
|
# class does quite a lot towards emulating a unix terminal.
|
|
|
|
from __future__ import annotations
|
|
|
|
from abc import ABC, abstractmethod
|
|
import unicodedata
|
|
from collections import deque
|
|
|
|
|
|
# types
|
|
if False:
|
|
from .types import EventTuple
|
|
|
|
|
|
class InputTranslator(ABC):
|
|
@abstractmethod
|
|
def push(self, evt: EventTuple) -> None:
|
|
pass
|
|
|
|
@abstractmethod
|
|
def get(self) -> EventTuple | None:
|
|
return None
|
|
|
|
@abstractmethod
|
|
def empty(self) -> bool:
|
|
return True
|
|
|
|
|
|
class KeymapTranslator(InputTranslator):
|
|
def __init__(self, keymap, verbose=False, invalid_cls=None, character_cls=None):
|
|
self.verbose = verbose
|
|
from .keymap import compile_keymap, parse_keys
|
|
|
|
self.keymap = keymap
|
|
self.invalid_cls = invalid_cls
|
|
self.character_cls = character_cls
|
|
d = {}
|
|
for keyspec, command in keymap:
|
|
keyseq = tuple(parse_keys(keyspec))
|
|
d[keyseq] = command
|
|
if self.verbose:
|
|
print(d)
|
|
self.k = self.ck = compile_keymap(d, ())
|
|
self.results = deque()
|
|
self.stack = []
|
|
|
|
def push(self, evt):
|
|
if self.verbose:
|
|
print("pushed", evt.data, end="")
|
|
key = evt.data
|
|
d = self.k.get(key)
|
|
if isinstance(d, dict):
|
|
if self.verbose:
|
|
print("transition")
|
|
self.stack.append(key)
|
|
self.k = d
|
|
else:
|
|
if d is None:
|
|
if self.verbose:
|
|
print("invalid")
|
|
if self.stack or len(key) > 1 or unicodedata.category(key) == "C":
|
|
self.results.append((self.invalid_cls, self.stack + [key]))
|
|
else:
|
|
# small optimization:
|
|
self.k[key] = self.character_cls
|
|
self.results.append((self.character_cls, [key]))
|
|
else:
|
|
if self.verbose:
|
|
print("matched", d)
|
|
self.results.append((d, self.stack + [key]))
|
|
self.stack = []
|
|
self.k = self.ck
|
|
|
|
def get(self):
|
|
if self.results:
|
|
return self.results.popleft()
|
|
else:
|
|
return None
|
|
|
|
def empty(self) -> bool:
|
|
return not self.results
|