Skip to content

Commit c9bc8d0

Browse files
committed
feat(internal[constants]): Add Hooks data structure
1 parent 31826e3 commit c9bc8d0

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,30 +1,23 @@
1+
"""Internal constants."""
2+
3+
import io
4+
import logging
15
import typing as t
26
from dataclasses import dataclass, field
37

48
from libtmux._internal.dataclasses import SkipDefaultFieldsReprMixin
9+
from libtmux._internal.sparse_array import SparseArray, is_sparse_array_list
510

6-
TerminalFeatures = t.Dict[str, t.List[str]]
11+
if t.TYPE_CHECKING:
12+
from typing_extensions import TypeAlias
713

814

915
T = t.TypeVar("T")
1016

17+
TerminalFeatures = t.Dict[str, t.List[str]]
18+
HookArray: "TypeAlias" = "t.Dict[str, SparseArray[str]]"
1119

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

2922

3023
@dataclass(repr=False)
@@ -33,7 +26,7 @@ class ServerOptions(
3326
):
3427
backspace: t.Optional[str] = field(default=None)
3528
buffer_limit: t.Optional[int] = field(default=None)
36-
command_alias: TmuxArray[str] = field(default_factory=TmuxArray)
29+
command_alias: SparseArray[str] = field(default_factory=SparseArray)
3730
default_terminal: t.Optional[str] = field(default=None)
3831
copy_command: t.Optional[str] = field(default=None)
3932
escape_time: t.Optional[int] = field(default=None)
@@ -47,8 +40,8 @@ class ServerOptions(
4740
prompt_history_limit: t.Optional[int] = field(default=None)
4841
set_clipboard: t.Optional[t.Literal["on", "external", "off"]] = field(default=None)
4942
terminal_features: TerminalFeatures = field(default_factory=dict)
50-
terminal_overrides: TmuxArray[str] = field(default_factory=TmuxArray)
51-
user_keys: TmuxArray[str] = field(default_factory=TmuxArray)
43+
terminal_overrides: SparseArray[str] = field(default_factory=SparseArray)
44+
user_keys: SparseArray[str] = field(default_factory=SparseArray)
5245

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

0 commit comments

Comments
 (0)