|
| 1 | +"""Sparse array for libtmux options and hooks.""" |
| 2 | + |
| 3 | +from __future__ import annotations |
| 4 | + |
| 5 | +import typing as t |
| 6 | + |
| 7 | +if t.TYPE_CHECKING: |
| 8 | + from typing_extensions import TypeAlias, TypeGuard |
| 9 | + |
| 10 | + from libtmux.options import ExplodedComplexUntypedOptionsDict |
| 11 | + |
| 12 | + |
| 13 | +T = t.TypeVar("T") |
| 14 | +HookArray: TypeAlias = "dict[str, SparseArray[str]]" |
| 15 | + |
| 16 | + |
| 17 | +def is_sparse_array_list( |
| 18 | + items: ExplodedComplexUntypedOptionsDict, |
| 19 | +) -> TypeGuard[HookArray]: |
| 20 | + return all( |
| 21 | + isinstance( |
| 22 | + v, |
| 23 | + SparseArray, |
| 24 | + ) |
| 25 | + for k, v in items.items() |
| 26 | + ) |
| 27 | + |
| 28 | + |
| 29 | +class SparseArray(dict[int, T], t.Generic[T]): |
| 30 | + """Support non-sequential indexes while maintaining :class:`list`-like behavior. |
| 31 | +
|
| 32 | + A normal :class:`list` would raise :exc:`IndexError`. |
| 33 | +
|
| 34 | + There are no native sparse arrays in python that contain non-sequential indexes and |
| 35 | + maintain list-like behavior. This is useful for handling libtmux options and hooks: |
| 36 | +
|
| 37 | + ``command-alias[1] split-pane=split-window`` to |
| 38 | + ``{'command-alias[1]': {'split-pane=split-window'}}`` |
| 39 | +
|
| 40 | + :class:`list` would lose indice info, and :class:`dict` would lose list-like |
| 41 | + behavior. |
| 42 | + """ |
| 43 | + |
| 44 | + def add(self, index: int, value: T) -> None: |
| 45 | + self[index] = value |
| 46 | + |
| 47 | + def append(self, value: T) -> None: |
| 48 | + index = max(self.keys()) + 1 |
| 49 | + self[index] = value |
| 50 | + |
| 51 | + def iter_values(self) -> t.Iterator[T]: |
| 52 | + for index in sorted(self.keys()): |
| 53 | + yield self[index] |
| 54 | + |
| 55 | + def as_list(self) -> list[T]: |
| 56 | + return [self[index] for index in sorted(self.keys())] |
0 commit comments