Skip to content

Commit ce8fbac

Browse files
committed
feat(internal[constants]): Add Hooks data structure
1 parent e62484d commit ce8fbac

File tree

1 file changed

+235
-21
lines changed

1 file changed

+235
-21
lines changed

src/libtmux/_internal/constants.py

Lines changed: 235 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,25 @@
1+
"""Internal constants."""
2+
13
from __future__ import annotations
24

5+
import io
6+
import logging
37
import typing as t
48
from dataclasses import dataclass, field
59

610
from libtmux._internal.dataclasses import SkipDefaultFieldsReprMixin
11+
from libtmux._internal.sparse_array import SparseArray, is_sparse_array_list
712

8-
TerminalFeatures = dict[str, list[str]]
13+
if t.TYPE_CHECKING:
14+
from typing_extensions import TypeAlias
915

1016

1117
T = t.TypeVar("T")
1218

19+
TerminalFeatures = dict[str, list[str]]
20+
HookArray: TypeAlias = "dict[str, SparseArray[str]]"
1321

14-
class TmuxArray(dict[int, T], t.Generic[T]):
15-
"""Support non-sequential indexes without raising IndexError."""
16-
17-
def add(self, index: int, value: T) -> None:
18-
self[index] = value
19-
20-
def append(self, value: T) -> None:
21-
index = max(self.keys()) + 1
22-
self[index] = value
23-
24-
def iter_values(self) -> t.Iterator[T]:
25-
for index in sorted(self.keys()):
26-
yield self[index]
27-
28-
def as_list(self) -> list[T]:
29-
return [self[index] for index in sorted(self.keys())]
22+
logger = logging.getLogger(__name__)
3023

3124

3225
@dataclass(repr=False)
@@ -35,7 +28,7 @@ class ServerOptions(
3528
):
3629
backspace: str | None = field(default=None)
3730
buffer_limit: int | None = field(default=None)
38-
command_alias: TmuxArray[str] = field(default_factory=TmuxArray)
31+
command_alias: SparseArray[str] = field(default_factory=SparseArray)
3932
default_terminal: str | None = field(default=None)
4033
copy_command: str | None = field(default=None)
4134
escape_time: int | None = field(default=None)
@@ -49,8 +42,8 @@ class ServerOptions(
4942
prompt_history_limit: int | None = field(default=None)
5043
set_clipboard: t.Literal["on", "external", "off"] | None = field(default=None)
5144
terminal_features: TerminalFeatures = field(default_factory=dict)
52-
terminal_overrides: TmuxArray[str] = field(default_factory=TmuxArray)
53-
user_keys: TmuxArray[str] = field(default_factory=TmuxArray)
45+
terminal_overrides: SparseArray[str] = field(default_factory=SparseArray)
46+
user_keys: SparseArray[str] = field(default_factory=SparseArray)
5447

5548
def __init__(self, **kwargs: object) -> None:
5649
# Convert hyphenated keys to underscored attribute names and assign values
@@ -121,7 +114,7 @@ class SessionOptions(
121114
status_right_length: int | None = field(default=None)
122115
status_right_style: str | None = field(default=None)
123116
status_style: str | None = field(default=None)
124-
update_environment: list[str] | None = field(default=None)
117+
update_environment: SparseArray[str] = field(default_factory=SparseArray)
125118
visual_activity: t.Literal["on", "off", "both"] | None = field(default=None)
126119
visual_bell: t.Literal["on", "off", "both"] | None = field(default=None)
127120
visual_silence: t.Literal["on", "off", "both"] | None = field(default=None)
@@ -245,3 +238,224 @@ def __init__(self, **kwargs: object) -> None:
245238
key_underscored = key.replace("-", "_")
246239
key_asterisk_removed = key_underscored.rstrip("*")
247240
setattr(self, key_asterisk_removed, value)
241+
242+
243+
@dataclass(repr=False)
244+
class Hooks(
245+
SkipDefaultFieldsReprMixin,
246+
):
247+
"""tmux hooks data structure."""
248+
249+
# --- Tmux normal hooks ---
250+
# Run when a window has activity. See monitor-activity.
251+
alert_activity: SparseArray[str] = field(default_factory=SparseArray)
252+
# Run when a window has received a bell. See monitor-bell.
253+
alert_bell: SparseArray[str] = field(default_factory=SparseArray)
254+
# Run when a window has been silent. See monitor-silence.
255+
alert_silence: SparseArray[str] = field(default_factory=SparseArray)
256+
# Run when a client becomes the latest active client of its session.
257+
client_active: SparseArray[str] = field(default_factory=SparseArray)
258+
# Run when a client is attached.
259+
client_attached: SparseArray[str] = field(default_factory=SparseArray)
260+
# Run when a client is detached.
261+
client_detached: SparseArray[str] = field(default_factory=SparseArray)
262+
# Run when focus enters a client.
263+
client_focus_in: SparseArray[str] = field(default_factory=SparseArray)
264+
# Run when focus exits a client.
265+
client_focus_out: SparseArray[str] = field(default_factory=SparseArray)
266+
# Run when a client is resized.
267+
client_resized: SparseArray[str] = field(default_factory=SparseArray)
268+
# Run when a client's attached session is changed.
269+
client_session_changed: SparseArray[str] = field(default_factory=SparseArray)
270+
# Run when the program running in a pane exits, but remain-on-exit is on so the pane
271+
# has not closed.
272+
pane_died: SparseArray[str] = field(default_factory=SparseArray)
273+
# Run when the program running in a pane exits.
274+
pane_exited: SparseArray[str] = field(default_factory=SparseArray)
275+
# Run when the focus enters a pane, if the focus-events option is on.
276+
pane_focus_in: SparseArray[str] = field(default_factory=SparseArray)
277+
# Run when the focus exits a pane, if the focus-events option is on.
278+
pane_focus_out: SparseArray[str] = field(default_factory=SparseArray)
279+
# Run when the terminal clipboard is set using the xterm(1) escape sequence.
280+
pane_set_clipboard: SparseArray[str] = field(default_factory=SparseArray)
281+
# Run when a new session created.
282+
session_created: SparseArray[str] = field(default_factory=SparseArray)
283+
# Run when a session closed.
284+
session_closed: SparseArray[str] = field(default_factory=SparseArray)
285+
# Run when a session is renamed.
286+
session_renamed: SparseArray[str] = field(default_factory=SparseArray)
287+
# Run when a window is linked into a session.
288+
window_linked: SparseArray[str] = field(default_factory=SparseArray)
289+
# Run when a window is renamed.
290+
window_renamed: SparseArray[str] = field(default_factory=SparseArray)
291+
# Run when a window is resized. This may be after the client-resized hook is run.
292+
window_resized: SparseArray[str] = field(default_factory=SparseArray)
293+
# Run when a window is unlinked from a session.
294+
window_unlinked: SparseArray[str] = field(default_factory=SparseArray)
295+
296+
# --- Tmux control mode hooks ---
297+
# The client has detached.
298+
client_detached_control: SparseArray[str] = field(default_factory=SparseArray)
299+
# The client is now attached to the session with ID session-id, which is named name.
300+
client_session_changed_control: SparseArray[str] = field(
301+
default_factory=SparseArray,
302+
)
303+
# An error has happened in a configuration file.
304+
config_error: SparseArray[str] = field(default_factory=SparseArray)
305+
# The pane has been continued after being paused (if the pause-after flag is set,
306+
# see refresh-client -A).
307+
continue_control: SparseArray[str] = field(default_factory=SparseArray)
308+
# The tmux client is exiting immediately, either because it is not attached to any
309+
# session or an error occurred.
310+
exit_control: SparseArray[str] = field(default_factory=SparseArray)
311+
# New form of %output sent when the pause-after flag is set.
312+
extended_output: SparseArray[str] = field(default_factory=SparseArray)
313+
# The layout of a window with ID window-id changed.
314+
layout_change: SparseArray[str] = field(default_factory=SparseArray)
315+
# A message sent with the display-message command.
316+
message_control: SparseArray[str] = field(default_factory=SparseArray)
317+
# A window pane produced output.
318+
output: SparseArray[str] = field(default_factory=SparseArray)
319+
# The pane with ID pane-id has changed mode.
320+
pane_mode_changed: SparseArray[str] = field(default_factory=SparseArray)
321+
# Paste buffer name has been changed.
322+
paste_buffer_changed: SparseArray[str] = field(default_factory=SparseArray)
323+
# Paste buffer name has been deleted.
324+
paste_buffer_deleted: SparseArray[str] = field(default_factory=SparseArray)
325+
# The pane has been paused (if the pause-after flag is set).
326+
pause_control: SparseArray[str] = field(default_factory=SparseArray)
327+
# The client is now attached to the session with ID session-id, which is named name.
328+
session_changed_control: SparseArray[str] = field(default_factory=SparseArray)
329+
# The current session was renamed to name.
330+
session_renamed_control: SparseArray[str] = field(default_factory=SparseArray)
331+
# The session with ID session-id changed its active window to the window with ID
332+
# window-id.
333+
session_window_changed: SparseArray[str] = field(default_factory=SparseArray)
334+
# A session was created or destroyed.
335+
sessions_changed: SparseArray[str] = field(default_factory=SparseArray)
336+
# The value of the format associated with subscription name has changed to value.
337+
subscription_changed: SparseArray[str] = field(default_factory=SparseArray)
338+
# The window with ID window-id was created but is not linked to the current session.
339+
unlinked_window_add: SparseArray[str] = field(default_factory=SparseArray)
340+
# The window with ID window-id, which is not linked to the current session, was
341+
# closed.
342+
unlinked_window_close: SparseArray[str] = field(default_factory=SparseArray)
343+
# The window with ID window-id, which is not linked to the current session, was
344+
# renamed.
345+
unlinked_window_renamed: SparseArray[str] = field(default_factory=SparseArray)
346+
# The window with ID window-id was linked to the current session.
347+
window_add: SparseArray[str] = field(default_factory=SparseArray)
348+
# The window with ID window-id closed.
349+
window_close: SparseArray[str] = field(default_factory=SparseArray)
350+
# The layout of a window with ID window-id changed. The new layout is window-layout.
351+
# The window's visible layout is window-visible-layout and the window flags are
352+
# window-flags.
353+
window_layout_changed: SparseArray[str] = field(default_factory=SparseArray)
354+
# The active pane in the window with ID window-id changed to the pane with ID
355+
# pane-id.
356+
window_pane_changed: SparseArray[str] = field(default_factory=SparseArray)
357+
# The window with ID window-id was renamed to name.
358+
window_renamed_control: SparseArray[str] = field(default_factory=SparseArray)
359+
360+
# --- After hooks - Run after specific tmux commands complete ---
361+
# Runs after 'bind-key' completes
362+
after_bind_key: SparseArray[str] = field(default_factory=SparseArray)
363+
# Runs after 'capture-pane' completes
364+
after_capture_pane: SparseArray[str] = field(default_factory=SparseArray)
365+
# Runs after 'copy-mode' completes
366+
after_copy_mode: SparseArray[str] = field(default_factory=SparseArray)
367+
# Runs after 'display-message' completes
368+
after_display_message: SparseArray[str] = field(default_factory=SparseArray)
369+
# Runs after 'display-panes' completes
370+
after_display_panes: SparseArray[str] = field(default_factory=SparseArray)
371+
# Runs after 'kill-pane' completes
372+
after_kill_pane: SparseArray[str] = field(default_factory=SparseArray)
373+
# Runs after 'list-buffers' completes
374+
after_list_buffers: SparseArray[str] = field(default_factory=SparseArray)
375+
# Runs after 'list-clients' completes
376+
after_list_clients: SparseArray[str] = field(default_factory=SparseArray)
377+
# Runs after 'list-keys' completes
378+
after_list_keys: SparseArray[str] = field(default_factory=SparseArray)
379+
# Runs after 'list-panes' completes
380+
after_list_panes: SparseArray[str] = field(default_factory=SparseArray)
381+
# Runs after 'list-sessions' completes
382+
after_list_sessions: SparseArray[str] = field(default_factory=SparseArray)
383+
# Runs after 'list-windows' completes
384+
after_list_windows: SparseArray[str] = field(default_factory=SparseArray)
385+
# Runs after 'load-buffer' completes
386+
after_load_buffer: SparseArray[str] = field(default_factory=SparseArray)
387+
# Runs after 'lock-server' completes
388+
after_lock_server: SparseArray[str] = field(default_factory=SparseArray)
389+
# Runs after 'new-session' completes
390+
after_new_session: SparseArray[str] = field(default_factory=SparseArray)
391+
# Runs after 'new-window' completes
392+
after_new_window: SparseArray[str] = field(default_factory=SparseArray)
393+
# Runs after 'paste-buffer' completes
394+
after_paste_buffer: SparseArray[str] = field(default_factory=SparseArray)
395+
# Runs after 'pipe-pane' completes
396+
after_pipe_pane: SparseArray[str] = field(default_factory=SparseArray)
397+
# Runs after 'queue' command is processed
398+
after_queue: SparseArray[str] = field(default_factory=SparseArray)
399+
# Runs after 'refresh-client' completes
400+
after_refresh_client: SparseArray[str] = field(default_factory=SparseArray)
401+
# Runs after 'rename-session' completes
402+
after_rename_session: SparseArray[str] = field(default_factory=SparseArray)
403+
# Runs after 'rename-window' completes
404+
after_rename_window: SparseArray[str] = field(default_factory=SparseArray)
405+
# Runs after 'resize-pane' completes
406+
after_resize_pane: SparseArray[str] = field(default_factory=SparseArray)
407+
# Runs after 'resize-window' completes
408+
after_resize_window: SparseArray[str] = field(default_factory=SparseArray)
409+
# Runs after 'save-buffer' completes
410+
after_save_buffer: SparseArray[str] = field(default_factory=SparseArray)
411+
# Runs after 'select-layout' completes
412+
after_select_layout: SparseArray[str] = field(default_factory=SparseArray)
413+
# Runs after 'select-pane' completes
414+
after_select_pane: SparseArray[str] = field(default_factory=SparseArray)
415+
# Runs after 'select-window' completes
416+
after_select_window: SparseArray[str] = field(default_factory=SparseArray)
417+
# Runs after 'send-keys' completes
418+
after_send_keys: SparseArray[str] = field(default_factory=SparseArray)
419+
# Runs after 'set-buffer' completes
420+
after_set_buffer: SparseArray[str] = field(default_factory=SparseArray)
421+
# Runs after 'set-environment' completes
422+
after_set_environment: SparseArray[str] = field(default_factory=SparseArray)
423+
# Runs after 'set-hook' completes
424+
after_set_hook: SparseArray[str] = field(default_factory=SparseArray)
425+
# Runs after 'set-option' completes
426+
after_set_option: SparseArray[str] = field(default_factory=SparseArray)
427+
# Runs after 'show-environment' completes
428+
after_show_environment: SparseArray[str] = field(default_factory=SparseArray)
429+
# Runs after 'show-messages' completes
430+
after_show_messages: SparseArray[str] = field(default_factory=SparseArray)
431+
# Runs after 'show-options' completes
432+
after_show_options: SparseArray[str] = field(default_factory=SparseArray)
433+
# Runs after 'split-window' completes
434+
after_split_window: SparseArray[str] = field(default_factory=SparseArray)
435+
# Runs after 'unbind-key' completes
436+
after_unbind_key: SparseArray[str] = field(default_factory=SparseArray)
437+
438+
@classmethod
439+
def from_stdout(cls, value: list[str]) -> Hooks:
440+
from libtmux.options import (
441+
explode_arrays,
442+
explode_complex,
443+
parse_options_to_dict,
444+
)
445+
446+
output_exploded = explode_complex(
447+
explode_arrays(
448+
parse_options_to_dict(
449+
io.StringIO("\n".join(value)),
450+
),
451+
force_array=True,
452+
),
453+
)
454+
455+
assert is_sparse_array_list(output_exploded)
456+
457+
output_renamed: HookArray = {
458+
k.lstrip("%").replace("-", "_"): v for k, v in output_exploded.items()
459+
}
460+
461+
return cls(**output_renamed)

0 commit comments

Comments
 (0)