'TUI adaptions to Client.'
# built-ins
from getpass import getuser
-from typing import Any, Callable, Optional, Self, Sequence
+from typing import Any, Callable, Optional, Sequence
# ourselves
from ircplom.tui_base import (BaseTui, PromptWidget, TuiEvent, Window,
CMD_SHORTCUTS)
class _Update:
+ old_value: Any
+ results: list[tuple[LogScope, Any]]
- def __init__(self, path: tuple[str, ...], value: Any, force_log=False
- ) -> None:
- self.path = path
+ def __init__(self, path: tuple[str, ...], value: Any) -> None:
+ self.full_path = path
+ self.rel_path = self.full_path[:]
self.value = value
- self.force_log = force_log
+ self.old_value = None
+ self.force_log = False
+ self.results = []
@property
def key(self) -> str:
'Name of item or attribute to be processed.'
- return self.path[0]
+ return self.rel_path[0]
- def decremented(self) -> Self:
- 'Create copy with .path reduced by leftmost step.'
- return self.__class__(self.path[1:], self.value, self.force_log)
+ def decrement_path(self) -> None:
+ 'Remove first element from .rel_path.'
+ self.rel_path = self.rel_path[1:]
class _UpdatingNode(AutoAttrMixin):
scopes = c.log_scopes | scopes
return scopes.get(key, scopes[''])
- def recursive_set_and_report_change(
- self, update: _Update) -> tuple[tuple[LogScope, Any], ...]:
- 'Apply update, return if that actually made a difference.'
+ def recursive_set_and_report_change(self, update: _Update) -> None:
+ 'Apply update, and, if it makes a difference, add to its .results.'
update.force_log = update.force_log or (not self._is_set(update.key))
node = self._get(update.key)
- if len(update.path) > 1:
- return node.recursive_set_and_report_change(update.decremented())
- return self._focused_set_and_report_change(node, update)
-
- def _focused_set_and_report_change(
- self, old_value: '_UpdatingNode', update: _Update
- ) -> tuple[tuple[LogScope, Any], ...]:
- scope = self._scope(update.key)
- do_report = update.force_log
- if update.value is None:
- if self._is_set(update.key):
- self._unset(update.key)
+ if len(update.rel_path) > 1:
+ update.decrement_path()
+ node.recursive_set_and_report_change(update)
+ else:
+ update.old_value = node
+ scope = self._scope(update.key)
+ do_report = update.force_log
+ if update.value is None:
+ if self._is_set(update.key):
+ self._unset(update.key)
+ do_report |= True
+ elif update.old_value != update.value:
+ self._set(update.key, update.value)
do_report |= True
- elif old_value != update.value:
- self._set(update.key, update.value)
- do_report |= True
- return ((scope, update.value),) if do_report else tuple()
+ if do_report:
+ update.results += [(scope, update.value)]
def _get(self, key: str) -> Any:
return getattr(self, key)
log_scopes = {'': LogScope.CHAT}
user_ids: set[str]
- def _focused_set_and_report_change(
- self, old_value: '_UpdatingNode', update: _Update
- ) -> tuple[tuple[LogScope, Any], ...]:
+ def recursive_set_and_report_change(self, update: _Update) -> None:
+ super().recursive_set_and_report_change(update)
if update.key == 'user_ids':
- assert isinstance(update.value, set)
- d = ({'NUHS:joining': tuple(sorted(id_ for id_ in update.value
- if id_ not in self.user_ids))}
- if self.user_ids
- else {'NICKS:residents': tuple(sorted(update.value))})
- if super()._focused_set_and_report_change(old_value, update):
- return ((self._scope(update.key), d),)
- return tuple()
- return super()._focused_set_and_report_change(old_value, update)
+ if update.old_value:
+ d = {'NUHS:joining': tuple(sorted(
+ id_ for id_ in update.value
+ if id_ not in update.old_value))}
+ else:
+ d = {'NICKS:residents': tuple(sorted(update.value))}
+ update.results = [(self._scope(update.key), d)]
class _UpdatingUser(_UpdatingNode, User):
log_scopes = {'exit_msg': LogScope.USER}
prev_nick = '?'
- def _focused_set_and_report_change(
- self, old_value: '_UpdatingNode', update: _Update
- ) -> tuple[tuple[LogScope, Any], ...]:
- assert isinstance(update.value, str)
- if (result := super()._focused_set_and_report_change(old_value,
- update)):
- if update.key not in {'nick', 'exit_msg'}:
- return result
+ def recursive_set_and_report_change(self, update: _Update) -> None:
+ super().recursive_set_and_report_change(update)
+ if update.key in {'nick', 'exit_msg'}:
msg = 'RAW:'
if update.key == 'nick':
- if self.prev_nick != '?':
- result = result + (
- (LogScope.USER,
- msg + f'{self.prev} renames {update.value}'),)
- return result
- if update.key == 'exit_msg' and update.value:
- msg += f'{self} '
- msg += 'quits' if update.value[0] == 'Q' else 'parts'
- if len(update.value) > 1:
- msg += ': ' + update.value[1:]
- return ((self._scope(update.key), msg),)
- return tuple()
+ self.prev_nick = update.old_value
+ if update.old_value != '?':
+ update.results += [
+ (LogScope.USER,
+ msg + f'{self.prev} renames {update.value}')]
+ elif update.key == 'exit_msg':
+ update.results.clear()
+ if update.value:
+ msg += f'{self} '
+ msg += 'quits' if update.value[0] == 'Q' else 'parts'
+ if len(update.value) > 1:
+ msg += ': ' + update.value[1:]
+ update.results += [(self._scope(update.key), msg)]
@property
def prev(self) -> str:
'Return .nickuserhost with .prev_nick as .nick.'
return str(NickUserHost(self.prev_nick, self.user, self.host))
- def __setattr__(self, key: str, value) -> None:
- if key == 'nick':
- self.prev_nick = self.nick
- super().__setattr__(key, value)
-
class _UpdatingServerCapability(_UpdatingNode, ServerCapability):
pass
def update_db(self, update: _Update) -> bool:
'Apply update to .db, and if changing anything, log and trigger.'
- result = self.db.recursive_set_and_report_change(update)
- if not result:
+ self.db.recursive_set_and_report_change(update)
+ if not update.results:
return False
- for t in result:
+ for t in update.results:
scope, value = t
- log_path = ':'.join(update.path)
+ log_path = ':'.join(update.full_path)
log_kwargs: dict[str, Any] = {'scope': scope}
if scope in {LogScope.CHAT, LogScope.USER}:
- log_kwargs |= {'target': update.path[1]}
+ log_kwargs |= {'target': update.full_path[1]}
if value is None:
self.log(f'{log_path} cleared', **log_kwargs)
elif isinstance(value, Topic):