| 70 | |
| 71 | |
| 72 | class Keymap: |
| 73 | def __init__(self, master): |
| 74 | self.executor = commandexecutor.CommandExecutor(master) |
| 75 | self.keys: dict[str, dict[str, Binding]] = defaultdict(dict) |
| 76 | self.bindings = [] |
| 77 | |
| 78 | def _check_contexts(self, contexts): |
| 79 | if not contexts: |
| 80 | raise ValueError("Must specify at least one context.") |
| 81 | for c in contexts: |
| 82 | if c not in Contexts: |
| 83 | raise ValueError("Unsupported context: %s" % c) |
| 84 | |
| 85 | def _on_change(self) -> None: |
| 86 | signals.keybindings_change.send() |
| 87 | self.binding_for_help.cache_clear() |
| 88 | |
| 89 | def add(self, key: str, command: str, contexts: Sequence[str], help="") -> None: |
| 90 | """ |
| 91 | Add a key to the key map. |
| 92 | """ |
| 93 | self._check_contexts(contexts) |
| 94 | |
| 95 | for b in self.bindings: |
| 96 | if b.key == key and b.command.strip() == command.strip(): |
| 97 | b.contexts = sorted(list(set(b.contexts + contexts))) |
| 98 | if help: |
| 99 | b.help = help |
| 100 | self.bind(b) |
| 101 | break |
| 102 | else: |
| 103 | self.remove(key, contexts) |
| 104 | b = Binding(key=key, command=command, contexts=contexts, help=help) |
| 105 | self.bindings.append(b) |
| 106 | self.bind(b) |
| 107 | self._on_change() |
| 108 | |
| 109 | def remove(self, key: str, contexts: Sequence[str]) -> None: |
| 110 | """ |
| 111 | Remove a key from the key map. |
| 112 | """ |
| 113 | self._check_contexts(contexts) |
| 114 | for c in contexts: |
| 115 | b = self.get(c, key) |
| 116 | if b: |
| 117 | self.unbind(b) |
| 118 | b.contexts = [x for x in b.contexts if x != c] |
| 119 | if b.contexts: |
| 120 | self.bindings.append(b) |
| 121 | self.bind(b) |
| 122 | self._on_change() |
| 123 | |
| 124 | def bind(self, binding: Binding) -> None: |
| 125 | for c in binding.contexts: |
| 126 | self.keys[c][binding.keyspec()] = binding |
| 127 | |
| 128 | def unbind(self, binding: Binding) -> None: |
| 129 | """ |
no outgoing calls
searching dependent graphs…