|
| 1 | +"""Internal constants.""" |
| 2 | + |
| 3 | +import io |
| 4 | +import logging |
1 | 5 | import typing as t
|
2 | 6 | from dataclasses import dataclass, field
|
3 | 7 |
|
4 | 8 | from libtmux._internal.dataclasses import SkipDefaultFieldsReprMixin
|
5 | 9 |
|
6 |
| -TerminalFeatures = t.Dict[str, t.List[str]] |
| 10 | +if t.TYPE_CHECKING: |
| 11 | + from typing_extensions import TypeAlias, TypeGuard |
7 | 12 |
|
| 13 | + from libtmux.options import ExplodedComplexUntypedOptionsDict |
8 | 14 |
|
9 | 15 | T = t.TypeVar("T")
|
10 | 16 |
|
| 17 | +TerminalFeatures = t.Dict[str, t.List[str]] |
| 18 | +HookArray: "TypeAlias" = "t.Dict[str, TmuxArray[str]]" |
| 19 | + |
| 20 | +logger = logging.getLogger(__name__) |
| 21 | + |
| 22 | + |
| 23 | +def is_tmux_array_list( |
| 24 | + items: "ExplodedComplexUntypedOptionsDict", |
| 25 | +) -> "TypeGuard[HookArray]": |
| 26 | + return all( |
| 27 | + isinstance( |
| 28 | + v, |
| 29 | + TmuxArray, |
| 30 | + ) |
| 31 | + for k, v in items.items() |
| 32 | + ) |
| 33 | + |
11 | 34 |
|
12 | 35 | class TmuxArray(t.Dict[int, T], t.Generic[T]):
|
13 | 36 | """Support non-sequential indexes without raising IndexError."""
|
@@ -118,7 +141,7 @@ class SessionOptions(
|
118 | 141 | status_right_length: t.Optional[int] = field(default=None)
|
119 | 142 | status_right_style: t.Optional[str] = field(default=None)
|
120 | 143 | status_style: t.Optional[str] = field(default=None)
|
121 |
| - update_environment: t.Optional[t.List[str]] = field(default=None) |
| 144 | + update_environment: TmuxArray[str] = field(default_factory=TmuxArray) |
122 | 145 | visual_activity: t.Optional[t.Literal["on", "off", "both"]] = field(default=None)
|
123 | 146 | visual_bell: t.Optional[t.Literal["on", "off", "both"]] = field(default=None)
|
124 | 147 | visual_silence: t.Optional[t.Literal["on", "off", "both"]] = field(default=None)
|
@@ -240,3 +263,222 @@ def __init__(self, **kwargs: object) -> None:
|
240 | 263 | key_underscored = key.replace("-", "_")
|
241 | 264 | key_asterisk_removed = key_underscored.rstrip("*")
|
242 | 265 | setattr(self, key_asterisk_removed, value)
|
| 266 | + |
| 267 | + |
| 268 | +@dataclass(repr=False) |
| 269 | +class Hooks( |
| 270 | + SkipDefaultFieldsReprMixin, |
| 271 | +): |
| 272 | + """tmux hooks data structure.""" |
| 273 | + |
| 274 | + # --- Tmux normal hooks --- |
| 275 | + # Run when a window has activity. See monitor-activity. |
| 276 | + alert_activity: TmuxArray[str] = field(default_factory=TmuxArray) |
| 277 | + # Run when a window has received a bell. See monitor-bell. |
| 278 | + alert_bell: TmuxArray[str] = field(default_factory=TmuxArray) |
| 279 | + # Run when a window has been silent. See monitor-silence. |
| 280 | + alert_silence: TmuxArray[str] = field(default_factory=TmuxArray) |
| 281 | + # Run when a client becomes the latest active client of its session. |
| 282 | + client_active: TmuxArray[str] = field(default_factory=TmuxArray) |
| 283 | + # Run when a client is attached. |
| 284 | + client_attached: TmuxArray[str] = field(default_factory=TmuxArray) |
| 285 | + # Run when a client is detached. |
| 286 | + client_detached: TmuxArray[str] = field(default_factory=TmuxArray) |
| 287 | + # Run when focus enters a client. |
| 288 | + client_focus_in: TmuxArray[str] = field(default_factory=TmuxArray) |
| 289 | + # Run when focus exits a client. |
| 290 | + client_focus_out: TmuxArray[str] = field(default_factory=TmuxArray) |
| 291 | + # Run when a client is resized. |
| 292 | + client_resized: TmuxArray[str] = field(default_factory=TmuxArray) |
| 293 | + # Run when a client's attached session is changed. |
| 294 | + client_session_changed: TmuxArray[str] = field(default_factory=TmuxArray) |
| 295 | + # Run when the program running in a pane exits, but remain-on-exit is on so the pane |
| 296 | + # has not closed. |
| 297 | + pane_died: TmuxArray[str] = field(default_factory=TmuxArray) |
| 298 | + # Run when the program running in a pane exits. |
| 299 | + pane_exited: TmuxArray[str] = field(default_factory=TmuxArray) |
| 300 | + # Run when the focus enters a pane, if the focus-events option is on. |
| 301 | + pane_focus_in: TmuxArray[str] = field(default_factory=TmuxArray) |
| 302 | + # Run when the focus exits a pane, if the focus-events option is on. |
| 303 | + pane_focus_out: TmuxArray[str] = field(default_factory=TmuxArray) |
| 304 | + # Run when the terminal clipboard is set using the xterm(1) escape sequence. |
| 305 | + pane_set_clipboard: TmuxArray[str] = field(default_factory=TmuxArray) |
| 306 | + # Run when a new session created. |
| 307 | + session_created: TmuxArray[str] = field(default_factory=TmuxArray) |
| 308 | + # Run when a session closed. |
| 309 | + session_closed: TmuxArray[str] = field(default_factory=TmuxArray) |
| 310 | + # Run when a session is renamed. |
| 311 | + session_renamed: TmuxArray[str] = field(default_factory=TmuxArray) |
| 312 | + # Run when a window is linked into a session. |
| 313 | + window_linked: TmuxArray[str] = field(default_factory=TmuxArray) |
| 314 | + # Run when a window is renamed. |
| 315 | + window_renamed: TmuxArray[str] = field(default_factory=TmuxArray) |
| 316 | + # Run when a window is resized. This may be after the client-resized hook is run. |
| 317 | + window_resized: TmuxArray[str] = field(default_factory=TmuxArray) |
| 318 | + # Run when a window is unlinked from a session. |
| 319 | + window_unlinked: TmuxArray[str] = field(default_factory=TmuxArray) |
| 320 | + |
| 321 | + # --- Tmux control mode hooks --- |
| 322 | + # The client has detached. |
| 323 | + client_detached_control: TmuxArray[str] = field(default_factory=TmuxArray) |
| 324 | + # The client is now attached to the session with ID session-id, which is named name. |
| 325 | + client_session_changed_control: TmuxArray[str] = field(default_factory=TmuxArray) |
| 326 | + # An error has happened in a configuration file. |
| 327 | + config_error: TmuxArray[str] = field(default_factory=TmuxArray) |
| 328 | + # The pane has been continued after being paused (if the pause-after flag is set, |
| 329 | + # see refresh-client -A). |
| 330 | + continue_control: TmuxArray[str] = field(default_factory=TmuxArray) |
| 331 | + # The tmux client is exiting immediately, either because it is not attached to any |
| 332 | + # session or an error occurred. |
| 333 | + exit_control: TmuxArray[str] = field(default_factory=TmuxArray) |
| 334 | + # New form of %output sent when the pause-after flag is set. |
| 335 | + extended_output: TmuxArray[str] = field(default_factory=TmuxArray) |
| 336 | + # The layout of a window with ID window-id changed. |
| 337 | + layout_change: TmuxArray[str] = field(default_factory=TmuxArray) |
| 338 | + # A message sent with the display-message command. |
| 339 | + message_control: TmuxArray[str] = field(default_factory=TmuxArray) |
| 340 | + # A window pane produced output. |
| 341 | + output: TmuxArray[str] = field(default_factory=TmuxArray) |
| 342 | + # The pane with ID pane-id has changed mode. |
| 343 | + pane_mode_changed: TmuxArray[str] = field(default_factory=TmuxArray) |
| 344 | + # Paste buffer name has been changed. |
| 345 | + paste_buffer_changed: TmuxArray[str] = field(default_factory=TmuxArray) |
| 346 | + # Paste buffer name has been deleted. |
| 347 | + paste_buffer_deleted: TmuxArray[str] = field(default_factory=TmuxArray) |
| 348 | + # The pane has been paused (if the pause-after flag is set). |
| 349 | + pause_control: TmuxArray[str] = field(default_factory=TmuxArray) |
| 350 | + # The client is now attached to the session with ID session-id, which is named name. |
| 351 | + session_changed_control: TmuxArray[str] = field(default_factory=TmuxArray) |
| 352 | + # The current session was renamed to name. |
| 353 | + session_renamed_control: TmuxArray[str] = field(default_factory=TmuxArray) |
| 354 | + # The session with ID session-id changed its active window to the window with ID |
| 355 | + # window-id. |
| 356 | + session_window_changed: TmuxArray[str] = field(default_factory=TmuxArray) |
| 357 | + # A session was created or destroyed. |
| 358 | + sessions_changed: TmuxArray[str] = field(default_factory=TmuxArray) |
| 359 | + # The value of the format associated with subscription name has changed to value. |
| 360 | + subscription_changed: TmuxArray[str] = field(default_factory=TmuxArray) |
| 361 | + # The window with ID window-id was created but is not linked to the current session. |
| 362 | + unlinked_window_add: TmuxArray[str] = field(default_factory=TmuxArray) |
| 363 | + # The window with ID window-id, which is not linked to the current session, was |
| 364 | + # closed. |
| 365 | + unlinked_window_close: TmuxArray[str] = field(default_factory=TmuxArray) |
| 366 | + # The window with ID window-id, which is not linked to the current session, was |
| 367 | + # renamed. |
| 368 | + unlinked_window_renamed: TmuxArray[str] = field(default_factory=TmuxArray) |
| 369 | + # The window with ID window-id was linked to the current session. |
| 370 | + window_add: TmuxArray[str] = field(default_factory=TmuxArray) |
| 371 | + # The window with ID window-id closed. |
| 372 | + window_close: TmuxArray[str] = field(default_factory=TmuxArray) |
| 373 | + # The layout of a window with ID window-id changed. The new layout is window-layout. |
| 374 | + # The window's visible layout is window-visible-layout and the window flags are |
| 375 | + # window-flags. |
| 376 | + window_layout_changed: TmuxArray[str] = field(default_factory=TmuxArray) |
| 377 | + # The active pane in the window with ID window-id changed to the pane with ID |
| 378 | + # pane-id. |
| 379 | + window_pane_changed: TmuxArray[str] = field(default_factory=TmuxArray) |
| 380 | + # The window with ID window-id was renamed to name. |
| 381 | + window_renamed_control: TmuxArray[str] = field(default_factory=TmuxArray) |
| 382 | + |
| 383 | + # --- After hooks - Run after specific tmux commands complete --- |
| 384 | + # Runs after 'bind-key' completes |
| 385 | + after_bind_key: TmuxArray[str] = field(default_factory=TmuxArray) |
| 386 | + # Runs after 'capture-pane' completes |
| 387 | + after_capture_pane: TmuxArray[str] = field(default_factory=TmuxArray) |
| 388 | + # Runs after 'copy-mode' completes |
| 389 | + after_copy_mode: TmuxArray[str] = field(default_factory=TmuxArray) |
| 390 | + # Runs after 'display-message' completes |
| 391 | + after_display_message: TmuxArray[str] = field(default_factory=TmuxArray) |
| 392 | + # Runs after 'display-panes' completes |
| 393 | + after_display_panes: TmuxArray[str] = field(default_factory=TmuxArray) |
| 394 | + # Runs after 'kill-pane' completes |
| 395 | + after_kill_pane: TmuxArray[str] = field(default_factory=TmuxArray) |
| 396 | + # Runs after 'list-buffers' completes |
| 397 | + after_list_buffers: TmuxArray[str] = field(default_factory=TmuxArray) |
| 398 | + # Runs after 'list-clients' completes |
| 399 | + after_list_clients: TmuxArray[str] = field(default_factory=TmuxArray) |
| 400 | + # Runs after 'list-keys' completes |
| 401 | + after_list_keys: TmuxArray[str] = field(default_factory=TmuxArray) |
| 402 | + # Runs after 'list-panes' completes |
| 403 | + after_list_panes: TmuxArray[str] = field(default_factory=TmuxArray) |
| 404 | + # Runs after 'list-sessions' completes |
| 405 | + after_list_sessions: TmuxArray[str] = field(default_factory=TmuxArray) |
| 406 | + # Runs after 'list-windows' completes |
| 407 | + after_list_windows: TmuxArray[str] = field(default_factory=TmuxArray) |
| 408 | + # Runs after 'load-buffer' completes |
| 409 | + after_load_buffer: TmuxArray[str] = field(default_factory=TmuxArray) |
| 410 | + # Runs after 'lock-server' completes |
| 411 | + after_lock_server: TmuxArray[str] = field(default_factory=TmuxArray) |
| 412 | + # Runs after 'new-session' completes |
| 413 | + after_new_session: TmuxArray[str] = field(default_factory=TmuxArray) |
| 414 | + # Runs after 'new-window' completes |
| 415 | + after_new_window: TmuxArray[str] = field(default_factory=TmuxArray) |
| 416 | + # Runs after 'paste-buffer' completes |
| 417 | + after_paste_buffer: TmuxArray[str] = field(default_factory=TmuxArray) |
| 418 | + # Runs after 'pipe-pane' completes |
| 419 | + after_pipe_pane: TmuxArray[str] = field(default_factory=TmuxArray) |
| 420 | + # Runs after 'queue' command is processed |
| 421 | + after_queue: TmuxArray[str] = field(default_factory=TmuxArray) |
| 422 | + # Runs after 'refresh-client' completes |
| 423 | + after_refresh_client: TmuxArray[str] = field(default_factory=TmuxArray) |
| 424 | + # Runs after 'rename-session' completes |
| 425 | + after_rename_session: TmuxArray[str] = field(default_factory=TmuxArray) |
| 426 | + # Runs after 'rename-window' completes |
| 427 | + after_rename_window: TmuxArray[str] = field(default_factory=TmuxArray) |
| 428 | + # Runs after 'resize-pane' completes |
| 429 | + after_resize_pane: TmuxArray[str] = field(default_factory=TmuxArray) |
| 430 | + # Runs after 'resize-window' completes |
| 431 | + after_resize_window: TmuxArray[str] = field(default_factory=TmuxArray) |
| 432 | + # Runs after 'save-buffer' completes |
| 433 | + after_save_buffer: TmuxArray[str] = field(default_factory=TmuxArray) |
| 434 | + # Runs after 'select-layout' completes |
| 435 | + after_select_layout: TmuxArray[str] = field(default_factory=TmuxArray) |
| 436 | + # Runs after 'select-pane' completes |
| 437 | + after_select_pane: TmuxArray[str] = field(default_factory=TmuxArray) |
| 438 | + # Runs after 'select-window' completes |
| 439 | + after_select_window: TmuxArray[str] = field(default_factory=TmuxArray) |
| 440 | + # Runs after 'send-keys' completes |
| 441 | + after_send_keys: TmuxArray[str] = field(default_factory=TmuxArray) |
| 442 | + # Runs after 'set-buffer' completes |
| 443 | + after_set_buffer: TmuxArray[str] = field(default_factory=TmuxArray) |
| 444 | + # Runs after 'set-environment' completes |
| 445 | + after_set_environment: TmuxArray[str] = field(default_factory=TmuxArray) |
| 446 | + # Runs after 'set-hook' completes |
| 447 | + after_set_hook: TmuxArray[str] = field(default_factory=TmuxArray) |
| 448 | + # Runs after 'set-option' completes |
| 449 | + after_set_option: TmuxArray[str] = field(default_factory=TmuxArray) |
| 450 | + # Runs after 'show-environment' completes |
| 451 | + after_show_environment: TmuxArray[str] = field(default_factory=TmuxArray) |
| 452 | + # Runs after 'show-messages' completes |
| 453 | + after_show_messages: TmuxArray[str] = field(default_factory=TmuxArray) |
| 454 | + # Runs after 'show-options' completes |
| 455 | + after_show_options: TmuxArray[str] = field(default_factory=TmuxArray) |
| 456 | + # Runs after 'split-window' completes |
| 457 | + after_split_window: TmuxArray[str] = field(default_factory=TmuxArray) |
| 458 | + # Runs after 'unbind-key' completes |
| 459 | + after_unbind_key: TmuxArray[str] = field(default_factory=TmuxArray) |
| 460 | + |
| 461 | + @classmethod |
| 462 | + def from_stdout(cls, value: t.List[str]) -> "Hooks": |
| 463 | + from libtmux.options import ( |
| 464 | + explode_arrays, |
| 465 | + explode_complex, |
| 466 | + parse_options_to_dict, |
| 467 | + ) |
| 468 | + |
| 469 | + output_exploded = explode_complex( |
| 470 | + explode_arrays( |
| 471 | + parse_options_to_dict( |
| 472 | + io.StringIO("\n".join(value)), |
| 473 | + ), |
| 474 | + force_array=True, |
| 475 | + ), |
| 476 | + ) |
| 477 | + |
| 478 | + assert is_tmux_array_list(output_exploded) |
| 479 | + |
| 480 | + output_renamed: HookArray = { |
| 481 | + k.lstrip("%").replace("-", "_"): v for k, v in output_exploded.items() |
| 482 | + } |
| 483 | + |
| 484 | + return cls(**output_renamed) |
0 commit comments