88from __future__ import annotations
99
1010from dataclasses import dataclass
11- from typing import TYPE_CHECKING , Iterable , NamedTuple
11+ from typing import TYPE_CHECKING , Iterable , Iterator , NamedTuple
1212
1313import rich .repr
1414
@@ -64,7 +64,7 @@ class ActiveBinding(NamedTuple):
6464
6565
6666@rich .repr .auto
67- class _Bindings :
67+ class BindingsMap :
6868 """Manage a set of bindings."""
6969
7070 def __init__ (
@@ -83,14 +83,16 @@ def __init__(
8383 """
8484
8585 def make_bindings (bindings : Iterable [BindingType ]) -> Iterable [Binding ]:
86+ bindings = list (bindings )
8687 for binding in bindings :
8788 # If it's a tuple of length 3, convert into a Binding first
8889 if isinstance (binding , tuple ):
8990 if len (binding ) not in (2 , 3 ):
9091 raise BindingError (
9192 f"BINDINGS must contain a tuple of two or three strings, not { binding !r} "
9293 )
93- binding = Binding (* binding )
94+ # `binding` is a tuple of 2 or 3 values at this point
95+ binding = Binding (* binding ) # type: ignore[reportArgumentType]
9496
9597 # At this point we have a Binding instance, but the key may
9698 # be a list of keys, so now we unroll that single Binding
@@ -112,44 +114,72 @@ def make_bindings(bindings: Iterable[BindingType]) -> Iterable[Binding]:
112114 priority = binding .priority ,
113115 )
114116
115- self .keys : dict [str , Binding ] = (
116- {binding .key : binding for binding in make_bindings (bindings )}
117- if bindings
118- else {}
117+ self .key_to_bindings : dict [str , list [Binding ]] = {}
118+ for binding in make_bindings (bindings or {}):
119+ self .key_to_bindings .setdefault (binding .key , []).append (binding )
120+
121+ def __iter__ (self ) -> Iterator [tuple [str , Binding ]]:
122+ """Iterating produces a sequence of (KEY, BINDING) tuples."""
123+ return iter (
124+ [
125+ (key , binding )
126+ for key , bindings in self .key_to_bindings .items ()
127+ for binding in bindings
128+ ]
119129 )
120130
121- def copy (self ) -> _Bindings :
131+ @classmethod
132+ def from_keys (cls , keys : dict [str , list [Binding ]]) -> BindingsMap :
133+ """Construct a BindingsMap from a dict of keys and bindings.
134+
135+ Args:
136+ keys: A dict that maps a key on to a list of `Binding` objects.
137+
138+ Returns:
139+ New `BindingsMap`
140+ """
141+ bindings = cls ()
142+ bindings .key_to_bindings = keys
143+ return bindings
144+
145+ def copy (self ) -> BindingsMap :
122146 """Return a copy of this instance.
123147
124148 Return:
125149 New bindings object.
126150 """
127- copy = _Bindings ()
128- copy .keys = self .keys .copy ()
151+ copy = BindingsMap ()
152+ copy .key_to_bindings = self .key_to_bindings .copy ()
129153 return copy
130154
131155 def __rich_repr__ (self ) -> rich .repr .Result :
132- yield self .keys
156+ yield self .key_to_bindings
133157
134158 @classmethod
135- def merge (cls , bindings : Iterable [_Bindings ]) -> _Bindings :
136- """Merge a bindings. Subsequent bound keys override initial keys.
159+ def merge (cls , bindings : Iterable [BindingsMap ]) -> BindingsMap :
160+ """Merge a bindings.
137161
138162 Args:
139163 bindings: A number of bindings.
140164
141165 Returns:
142- New bindings .
166+ New `BindingsMap` .
143167 """
144- keys : dict [str , Binding ] = {}
168+ keys : dict [str , list [ Binding ] ] = {}
145169 for _bindings in bindings :
146- keys .update (_bindings .keys )
147- return _Bindings (keys .values ())
170+ for key , key_bindings in _bindings .key_to_bindings .items ():
171+ keys .setdefault (key , []).extend (key_bindings )
172+ return BindingsMap .from_keys (keys )
148173
149174 @property
150175 def shown_keys (self ) -> list [Binding ]:
151176 """A list of bindings for shown keys."""
152- keys = [binding for binding in self .keys .values () if binding .show ]
177+ keys = [
178+ binding
179+ for bindings in self .key_to_bindings .values ()
180+ for binding in bindings
181+ if binding .show
182+ ]
153183 return keys
154184
155185 def bind (
@@ -173,17 +203,19 @@ def bind(
173203 """
174204 all_keys = [key .strip () for key in keys .split ("," )]
175205 for key in all_keys :
176- self .keys [key ] = Binding (
177- key ,
178- action ,
179- description ,
180- show = bool (description and show ),
181- key_display = key_display ,
182- priority = priority ,
206+ self .key_to_bindings .setdefault (key , []).append (
207+ Binding (
208+ key ,
209+ action ,
210+ description ,
211+ show = bool (description and show ),
212+ key_display = key_display ,
213+ priority = priority ,
214+ )
183215 )
184216
185- def get_key (self , key : str ) -> Binding :
186- """Get a binding if it exists .
217+ def get_bindings_for_key (self , key : str ) -> list [ Binding ] :
218+ """Get a list of bindings for a given key .
187219
188220 Args:
189221 key: Key to look up.
@@ -192,9 +224,9 @@ def get_key(self, key: str) -> Binding:
192224 NoBinding: If the binding does not exist.
193225
194226 Returns:
195- A binding object for the key,
227+ A list of bindings associated with the key.
196228 """
197229 try :
198- return self .keys [key ]
230+ return self .key_to_bindings [key ]
199231 except KeyError :
200232 raise NoBinding (f"No binding for { key } " ) from None
0 commit comments