1+ """Internal constants."""
2+
13from __future__ import annotations
24
5+ import io
6+ import logging
37import typing as t
48from dataclasses import dataclass , field
59
610from 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
1117T = 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