@@ -252,7 +252,71 @@ def __init__(self, **kwargs: object) -> None:
252252class Hooks (
253253 SkipDefaultFieldsReprMixin ,
254254):
255- """tmux hooks data structure."""
255+ """tmux hooks data structure.
256+
257+ Parses tmux hook output into typed :class:`SparseArray` fields, preserving
258+ array indices for hooks that can have multiple commands at different indices.
259+
260+ Examples
261+ --------
262+ Parse raw tmux hook output:
263+
264+ >>> from libtmux._internal.constants import Hooks
265+
266+ >>> raw = [
267+ ... "session-renamed[0] set-option -g status-left-style bg=red",
268+ ... "session-renamed[1] display-message 'session renamed'",
269+ ... ]
270+ >>> hooks = Hooks.from_stdout(raw)
271+
272+ Access individual hook commands by index:
273+
274+ >>> hooks.session_renamed[0]
275+ 'set-option -g status-left-style bg=red'
276+ >>> hooks.session_renamed[1]
277+ "display-message 'session renamed'"
278+
279+ Get all commands as a list (sorted by index):
280+
281+ >>> hooks.session_renamed.as_list()
282+ ['set-option -g status-left-style bg=red', "display-message 'session renamed'"]
283+
284+ Sparse indices are preserved (gaps in index numbers):
285+
286+ >>> raw_sparse = [
287+ ... "pane-focus-in[0] refresh-client",
288+ ... "pane-focus-in[5] display-message 'focus'",
289+ ... ]
290+ >>> hooks_sparse = Hooks.from_stdout(raw_sparse)
291+ >>> 0 in hooks_sparse.pane_focus_in
292+ True
293+ >>> 5 in hooks_sparse.pane_focus_in
294+ True
295+ >>> 3 in hooks_sparse.pane_focus_in
296+ False
297+ >>> sorted(hooks_sparse.pane_focus_in.keys())
298+ [0, 5]
299+
300+ Iterate over values in index order:
301+
302+ >>> for cmd in hooks_sparse.pane_focus_in.iter_values():
303+ ... print(cmd)
304+ refresh-client
305+ display-message 'focus'
306+
307+ Multiple hook types in one parse:
308+
309+ >>> raw_multi = [
310+ ... "after-new-window[0] select-pane -t 0",
311+ ... "after-new-window[1] send-keys 'clear' Enter",
312+ ... "window-renamed[0] refresh-client -S",
313+ ... ]
314+ >>> hooks_multi = Hooks.from_stdout(raw_multi)
315+ >>> len(hooks_multi.after_new_window)
316+ 2
317+ >>> len(hooks_multi.window_renamed)
318+ 1
319+ """
256320
257321 # --- Tmux normal hooks ---
258322 # Run when a window has activity. See monitor-activity.
@@ -453,6 +517,54 @@ class Hooks(
453517
454518 @classmethod
455519 def from_stdout (cls , value : list [str ]) -> Hooks :
520+ """Parse raw tmux hook output into a Hooks instance.
521+
522+ The parsing pipeline:
523+
524+ 1. ``parse_options_to_dict()`` - Parse "key value" lines into dict
525+ 2. ``explode_arrays(force_array=True)`` - Extract array indices into SparseArray
526+ 3. ``explode_complex()`` - Handle complex option types
527+ 4. Rename keys: ``session-renamed`` → ``session_renamed``
528+
529+ Parameters
530+ ----------
531+ value : list[str]
532+ Raw tmux output lines from ``show-hooks`` command.
533+
534+ Returns
535+ -------
536+ Hooks
537+ Parsed hooks with SparseArray fields for each hook type.
538+
539+ Examples
540+ --------
541+ Basic parsing:
542+
543+ >>> from libtmux._internal.constants import Hooks
544+
545+ >>> raw = ["session-renamed[0] display-message 'renamed'"]
546+ >>> hooks = Hooks.from_stdout(raw)
547+ >>> hooks.session_renamed[0]
548+ "display-message 'renamed'"
549+
550+ The pipeline preserves sparse indices:
551+
552+ >>> raw = [
553+ ... "after-select-window[0] refresh-client",
554+ ... "after-select-window[10] display-message 'selected'",
555+ ... ]
556+ >>> hooks = Hooks.from_stdout(raw)
557+ >>> sorted(hooks.after_select_window.keys())
558+ [0, 10]
559+
560+ Empty input returns empty SparseArrays:
561+
562+ >>> hooks_empty = Hooks.from_stdout([])
563+ >>> len(hooks_empty.session_renamed)
564+ 0
565+ >>> hooks_empty.session_renamed.as_list()
566+ []
567+ """
456568 from libtmux .options import (
457569 explode_arrays ,
458570 explode_complex ,
0 commit comments