From 8db62e1d87e45c162566e5d04c51feea9950360d Mon Sep 17 00:00:00 2001 From: Christian Heller Date: Thu, 11 Sep 2025 19:27:35 +0200 Subject: [PATCH] Also treat Channel.user_ids as (completable) set rather than sorted. --- ircplom/client.py | 62 ++++++++++++++++++++++++++----------------- ircplom/client_tui.py | 4 +-- 2 files changed, 40 insertions(+), 26 deletions(-) diff --git a/ircplom/client.py b/ircplom/client.py index 3b71158..6f324a6 100644 --- a/ircplom/client.py +++ b/ircplom/client.py @@ -148,11 +148,27 @@ class _CompletableStringsSet(_CompletableStringsCollection, Set): def _copy_collected(self) -> set[str]: return set(self._collected) - def add(self, item: str) -> None: + def _on_set(self, m_name: str, item: str, on_complete: bool, exists: bool + ) -> None: + method = getattr(self._collected, m_name) + if on_complete: + assert self._completed is not None + assert (item in self._completed) == exists + assert (item in self._collected) == exists + method(item) + self.complete() + else: + assert self._completed is None + assert (item in self._collected) == exists + method(item) + + def completable_add(self, item: str, on_complete: bool) -> None: 'Put item into collection.' - assert self._completed is None - assert item not in self._collected - self._collected.add(item) + self._on_set('add', item, on_complete, False) + + def completable_remove(self, item: str, on_complete: bool) -> None: + 'Remove item from collection.' + self._on_set('remove', item, on_complete, True) def intersection(self, *others: Iterable[str]) -> set[str]: 'Compare self to other set(s).' @@ -177,13 +193,6 @@ class _CompletableStringsOrdered(_Clearable, _CompletableStringsCollection): if complete: self.complete() - def remove(self, value: str, complete=False) -> None: - 'Remove value from list.' - assert value in self._collected - self._collected = tuple(x for x in self._collected if x != value) - if complete: - self.complete() - def clear(self) -> None: self._completed = None self._collected = tuple() @@ -196,7 +205,7 @@ class IntoUpdateValueMixin(AutoAttrMixin): 'Return non-updating copy of self.' if isinstance(self, _Dict): return None - if isinstance(self, _CompletableStringsOrdered): + if isinstance(self, _CompletableStringsCollection): return self._completed if isinstance(self, _CompletableTopic): return Topic(*self._completed) @@ -265,8 +274,13 @@ class _UpdatingCompletable(_UpdatingMixin, _Completable): self._on_update() -class _UpdatingCompletableStringsOrdered(_UpdatingCompletable, - _CompletableStringsOrdered): +class _UpdatingCompletableStringsSet( + _UpdatingCompletable, _CompletableStringsSet): + pass + + +class _UpdatingCompletableStringsOrdered( + _UpdatingCompletable, _CompletableStringsOrdered): pass @@ -392,7 +406,7 @@ class _CompletableTopic(_Completable): class _Channel: - user_ids: _CompletableStringsOrdered + user_ids: _CompletableStringsSet topic: _CompletableTopic def __init__(self, @@ -411,17 +425,17 @@ class _Channel: for item in items: n_u_h = NickUserHost(item.lstrip(self._get_membership_prefixes())) user_id = self._userid_for_nickuserhost(n_u_h, create_if_none=True) - self.user_ids.append(user_id, complete=False) + self.user_ids.completable_add(user_id, on_complete=False) - def append_user(self, user: '_User') -> None: - 'To .user_ids append user.nickname and declare .user_ids complete.' + def add_user(self, user: '_User') -> None: + 'To .user_ids add user.nickname, keep .user_ids declared complete.' user_id = self._userid_for_nickuserhost(user, create_if_none=True, updating=True) - self.user_ids.append(user_id, complete=True) + self.user_ids.completable_add(user_id, on_complete=True) def remove_user(self, user: '_User') -> None: - 'From .user_ids remove .nickname and declare .user_ids complete.' - self.user_ids.remove(user.id_, complete=True) + 'From .user_ids remove .nickname, keep .user_ids declared complete.' + self.user_ids.completable_remove(user.id_, on_complete=True) self.purge_users() @@ -511,7 +525,7 @@ class _UpdatingCompletableTopic(_UpdatingCompletable, _CompletableTopic): class _UpdatingChannel(_UpdatingMixin, _Channel): - user_ids: _UpdatingCompletableStringsOrdered + user_ids: _UpdatingCompletableStringsSet topic: _UpdatingCompletableTopic @@ -686,7 +700,7 @@ class _CapsManager(_Clearable): if verb in {'LS', 'LIST'}: target = getattr(self, f'_{verb.lower()}') for item in items: - target.add(item) + target.completable_add(item, False) if complete: target.complete() if target is self._ls: @@ -856,7 +870,7 @@ class Client(ABC, ClientQueueMixin): else: self.caps.end_negotiation() elif ret['_verb'] == 'JOIN' and ret['joiner'] != self.db.users['me']: - self.db.channels[ret['channel']].append_user(ret['joiner']) + self.db.channels[ret['channel']].add_user(ret['joiner']) elif ret['_verb'] == 'NICK': user_id = self.db.users.id_for_nickuserhost(ret['named'], updating=True) diff --git a/ircplom/client_tui.py b/ircplom/client_tui.py index 77c66e7..e9e0234 100644 --- a/ircplom/client_tui.py +++ b/ircplom/client_tui.py @@ -216,7 +216,7 @@ class _QueryWindow(_ChatWindow): class _UpdatingChannel(_UpdatingNode): - user_ids: tuple[str, ...] = tuple() + user_ids: set[str] topic: Topic = Topic() log_scopes = {'': LogScope.CHAT} @@ -232,7 +232,7 @@ class _UpdatingChannel(_UpdatingNode): assert update.key == 'user_ids' if update.value is None: return None - assert isinstance(update.value, tuple) + assert isinstance(update.value, set) d = {'NUHS:joining': tuple(id_ for id_ in update.value if id_ not in self.user_ids) } if self.user_ids else {'NICKS:residents': tuple(update.value)} -- 2.30.2