Mercurial > core / lisp/lib/cli/tools/tmux.lisp
changeset 698: |
96958d3eb5b0 |
parent: |
7046f3bdb668
|
author: |
Richard Westhaver <ellis@rwest.io> |
date: |
Fri, 04 Oct 2024 22:04:59 -0400 |
permissions: |
-rw-r--r-- |
description: |
fixes |
1 ;;; cli/tmux.lisp --- Tmux Tools 3 ;; Control Tmux from Lisp 7 ;; ref: https://github.com/tmux/tmux/wiki/Getting-Started#getting-started 9 ;; By default tmux tries to open a TTY and errors when it can't, so normally 10 ;; you'd want to use SPAWN-TMUX to allocate a terminal first. 12 ;; There is however a control-mode available which provides a text-based 13 ;; channel without needing a TTY. 15 ;; To use this mode call RUN-TMUX with the "-C" arg. 17 ;; ref: https://github.com/tmux/tmux/wiki/Control-Mode#control-mode 20 (in-package :cli/tools/tmux) 22 (deferror simple-tmux-error (simple-error error) ()) 24 (defun simple-tmux-error (fmt &rest args) 25 (error 'simple-tmux-error :format-arguments args :format-control fmt)) 27 (defparameter *tmux-user-config-path* (merge-pathnames ".tmux.conf" (user-homedir-pathname))) 28 (defparameter *tmux-system-config-path* (merge-pathnames "tmux.conf" "/etc/")) 30 (defparameter *tmux* (find-exe "tmux")) 32 (defparameter *default-tmux-tmpdir* (pathname (format nil "/tmp/tmux-~A/" (sb-posix:getuid)))) 33 (defparameter *default-tmux-socket* (merge-pathnames "default" *default-tmux-tmpdir*)) 36 (defun run-tmux (&rest args) 37 (let ((proc (sb-ext:run-program *tmux* (or args nil) :output :stream))) 38 (with-open-stream (s (sb-ext:process-output proc)) 39 (loop for l = (read-line s nil nil) 42 (if (eq 0 (sb-ext:process-exit-code proc)) 44 (simple-tmux-error "tmux command failed: ~A ~A" args)))) 46 (defun spawn-tmux (&rest args) 47 (run-terminal (append (list "-e" "tmux") args))) 49 ;;; Session > Window > Pane 50 (defstruct tmux-session 53 (windows nil :type list)) 55 (defstruct tmux-window 58 (panes nil :type list) 66 (defstruct tmux-controller 67 (input nil :type (or null sb-sys:fd-stream)) 68 (output nil :type (or null sb-sys:fd-stream)) 69 (silent nil :type boolean)) 71 (defun run-tmux-controller (&rest args) 72 (sb-ext:run-program *tmux* (or args nil) :output :stream :input :stream)) 74 (defun init-tmux-controller (ctrl &rest args) 77 (if (tmux-controller-silent ctrl) "-CC" "-C") 79 (setf (tmux-controller-output ctrl) (sb-ext:process-output proc) 80 (tmux-controller-input ctrl) (sb-ext:process-input proc)) 83 (defun write-tmux-line (ctrl string) 84 (write-line string (tmux-controller-input ctrl))) 86 (defun read-tmux-line (ctrl) 87 (read-line (tmux-controller-output ctrl))) 89 (defstruct tmux-command name flags args) 91 (defun parse-tmux-command (str) 92 "Parse a single TMUX-COMMAND from a string." 93 (let ((words (split-sequence #\space str))) 94 ;; TODO 2024-08-06: parse for real 95 (make-tmux-command :name (car words) :args (cdr words)))) 97 (defcfg tmux-config () 98 ((commands :initform nil) 99 (server-options :type hash-table) 100 (session-options :type hash-table) 101 (window-options :type hash-table) 102 (keys :type hash-table)) 103 (:documentation "A CFG object containing the parsed content of a tmux configuration file.")) 105 (defmethod make-cfg ((obj (eql :tmux)) &key commands server session window keys) 106 (let ((cfg (make-instance 'tmux-config))) 107 (when commands (setf (slot-value cfg 'commands) commands)) 108 (when server (setf (slot-value cfg 'server-options) server)) 109 (when session (setf (slot-value cfg 'session-options) session)) 110 (when window (setf (slot-value cfg 'window-options) window)) 111 (when keys (setf (slot-value cfg 'keys) keys)) 114 (defmethod find-cfg ((obj (eql :tmux)) &key system user) 115 "Find a tmux configuration and load it. 117 When SYSTEM is non-nil, skip check for user config. 119 When USER is non-nil it should be the name of a user whose cfg will be loaded 120 from /home/USER/.tmux.conf." 122 (system (probe-file *tmux-system-config-path*)) 123 (user (probe-file (format nil "/home/~A/.tmux.conf" user))) 124 (t (or (probe-file *tmux-user-config-path*) (probe-file *tmux-system-config-path*))))) 125 (obj (make-cfg :tmux :commands nil))) 126 (with-open-file (file path) 127 (with-output-to-string (str) 128 (loop for l = (read-line file nil nil) 130 unless (or (zerop (length l)) (equal (char l 0) #\#)) 131 do (push (parse-tmux-command l) (slot-value obj 'commands))))) 134 ;; (describe (find-cfg :tmux)) 137 (defun format-tmux-string (dst fmt &rest args) 138 (apply #'format dst fmt (mapcar (lambda (a) (format nil "#{~A}" a)) args))) 140 (defvar *tmux-var-table* (make-hash-table)) 142 (defmacro tmux-format (dst fmt &rest args) 143 "Format a tmux string, replacing symbols in ARGS that match a member of 144 *TMUX-VARIABLES* with their corresponding lower-case name." 145 `(format-tmux-string ,dst ,fmt 146 ,@(mapcar (lambda (a) 147 (gethash (symbolicate a) *tmux-var-table* a)) 150 (declaim ((vector symbol) *tmux-variables*)) 151 (defvar *tmux-variables* 152 #(active-window-index ;; Index of active window in session 153 alternate-on ;; 1 if pane is in alternate screen 154 alternate-saved-x ;; Saved cursor X in alternate screen 155 alternate-saved-y ;; Saved cursor Y in alternate screen 156 buffer-created ;; Time buffer created 157 buffer-name ;; Name of buffer 158 buffer-sample ;; Sample of start of buffer 159 buffer-size ;; Size of the specified buffer in bytes 160 client-activity ;; Time client last had activity 161 client-cell-height ;; Height of each client cell in pixels 162 client-cell-width ;; Width of each client cell in pixels 163 client-control-mode ;; 1 if client is in control mode 164 client-created ;; Time client created 165 client-discarded ;; Bytes discarded when client behind 166 client-flags ;; List of client flags 167 client-height ;; Height of client 168 client-key-table ;; Current key table 169 client-last-session ;; Name of the client's last session 170 client-name ;; Name of client 171 client-pid ;; PID of client process 172 client-prefix ;; 1 if prefix key has been pressed 173 client-readonly ;; 1 if client is read-only 174 client-session ;; Name of the client's session 175 client-termfeatures ;; Terminal features of client, if any 176 client-termname ;; Terminal name of client 177 client-termtype ;; Terminal type of client, if available 178 client-tty ;; Pseudo terminal of client 179 client-uid ;; UID of client process 180 client-user ;; User of client process 181 client-utf8 ;; 1 if client supports UTF-8 182 client-width ;; Width of client 183 client-written ;; Bytes written to client 184 command ;; Name of command in use, if any 185 command-list-alias ;; Command alias if listing commands 186 command-list-name ;; Command name if listing commands 187 command-list-usage ;; Command usage if listing commands 188 config-files ;; List of configuration files loaded 189 copy-cursor-line ;; Line the cursor is on in copy mode 190 copy-cursor-word ;; Word under cursor in copy mode 191 copy-cursor-x ;; Cursor X position in copy mode 192 copy-cursor-y ;; Cursor Y position in copy mode 193 current-file ;; Current configuration file 194 cursor-character ;; Character at cursor in pane 195 cursor-flag ;; Pane cursor flag 196 cursor-x ;; Cursor X position in pane 197 cursor-y ;; Cursor Y position in pane 198 history-bytes ;; Number of bytes in window history 199 history-limit ;; Maximum window history lines 200 history-size ;; Size of history in lines 201 hook ;; Name of running hook, if any 202 hook-client ;; Name of client where hook was run, if any 203 hook-pane ;; ID of pane where hook was run, if any 204 hook-session ;; ID of session where hook was run, if any 205 hook-session-name ;; Name of session where hook was run, if any 206 hook-window ;; ID of window where hook was run, if any 207 hook-window-name ;; Name of window where hook was run, if any 208 host ;; H Hostname of local host 209 host-short ;; h Hostname of local host (no domain name) 210 insert-flag ;; Pane insert flag 211 keypad-cursor-flag ;; Pane keypad cursor flag 212 keypad-flag ;; Pane keypad flag 213 last-window-index ;; Index of last window in session 214 line ;; Line number in the list 215 mouse-all-flag ;; Pane mouse all flag 216 mouse-any-flag ;; Pane mouse any flag 217 mouse-button-flag ;; Pane mouse button flag 218 mouse-hyperlink ;; Hyperlink under mouse, if any 219 mouse-line ;; Line under mouse, if any 220 mouse-sgr-flag ;; Pane mouse SGR flag 221 mouse-standard-flag ;; Pane mouse standard flag 222 mouse-status-line ;; Status line on which mouse event took place 223 mouse-status-range ;; Range type or argument of mouse event on status line 224 mouse-utf8-flag ;; Pane mouse UTF-8 flag 225 mouse-word ;; Word under mouse, if any 226 mouse-x ;; Mouse X position, if any 227 mouse-y ;; Mouse Y position, if any 228 next-session-id ;; Unique session ID for next new session 229 origin-flag ;; Pane origin flag 230 pane-active ;; 1 if active pane 231 pane-at-bottom ;; 1 if pane is at the bottom of window 232 pane-at-left ;; 1 if pane is at the left of window 233 pane-at-right ;; 1 if pane is at the right of window 234 pane-at-top ;; 1 if pane is at the top of window 235 pane-bg ;; Pane background colour 236 pane-bottom ;; Bottom of pane 237 pane-current-command ;; Current command if available 238 pane-current-path ;; Current path if available 239 pane-dead ;; 1 if pane is dead 240 pane-dead-signal ;; Exit signal of process in dead pane 241 pane-dead-status ;; Exit status of process in dead pane 242 pane-dead-time ;; Exit time of process in dead pane 243 pane-fg ;; Pane foreground colour 244 pane-format ;; 1 if format is for a pane 245 pane-height ;; Height of pane 246 pane-id ;; D Unique pane ID 247 pane-in-mode ;; 1 if pane is in a mode 248 pane-index ;; P Index of pane 249 pane-input-off ;; 1 if input to pane is disabled 250 pane-last ;; 1 if last pane 251 pane-left ;; Left of pane 252 pane-marked ;; 1 if this is the marked pane 253 pane-marked-set ;; 1 if a marked pane is set 254 pane-mode ;; Name of pane mode, if any 255 pane-path ;; Path of pane (can be set by application) 256 pane-pid ;; PID of first process in pane 257 pane-pipe ;; 1 if pane is being piped 258 pane-right ;; Right of pane 259 pane-search-string ;; Last search string in copy mode 260 pane-start-command ;; Command pane started with 261 pane-start-path ;; Path pane started with 262 pane-synchronized ;; 1 if pane is synchronized 263 pane-tabs ;; Pane tab positions 264 pane-title ;; T Title of pane (can be set by application) 265 pane-top ;; Top of pane 266 pane-tty ;; Pseudo terminal of pane 267 pane-unseen-changes ;; 1 if there were changes in pane while in mode 268 pane-width ;; Width of pane 270 rectangle-toggle ;; 1 if rectangle selection is activated 271 scroll-position ;; Scroll position in copy mode 272 scroll-region-lower ;; Bottom of scroll region in pane 273 scroll-region-upper ;; Top of scroll region in pane 274 search-match ;; Search match if any 275 search-present ;; 1 if search started in copy mode 276 selection-active ;; 1 if selection started and changes with the cursor in copy mode 277 selection-end-x ;; X position of the end of the selection 278 selection-end-y ;; Y position of the end of the selection 279 selection-present ;; 1 if selection started in copy mode 280 selection-start-x ;; X position of the start of the selection 281 selection-start-y ;; Y position of the start of the selection 282 server-sessions ;; Number of sessions 283 session-activity ;; Time of session last activity 284 session-alerts ;; List of window indexes with alerts 285 session-attached ;; Number of clients session is attached to 286 session-attached-list ;; List of clients session is attached to 287 session-created ;; Time session created 288 session-format ;; 1 if format is for a session 289 session-group ;; Name of session group 290 session-group-attached ;; Number of clients sessions in group are attached to 291 session-group-attached-list ;; List of clients sessions in group are attached to 292 session-group-list ;; List of sessions in group 293 session-group-many-attached ;; 1 if multiple clients attached to sessions in group 294 session-group-size ;; Size of session group 295 session-grouped ;; 1 if session in a group 296 session-id ;; Unique session ID 297 session-last-attached ;; Time session last attached 298 session-many-attached ;; 1 if multiple clients attached 299 session-marked ;; 1 if this session contains the marked pane 300 session-name ;; S Name of session 301 session-path ;; Working directory of session 302 session-stack ;; Window indexes in most recent order 303 session-windows ;; Number of windows in session 304 socket-path ;; Server socket path 305 start-time ;; Server start time 308 version ;; Server version 309 window-active ;; 1 if window active 310 window-active-clients ;; Number of clients viewing this window 311 window-active-clients-list ;; List of clients viewing this window 312 window-active-sessions ;; Number of sessions on which this window is active 313 window-active-sessions-list ;; List of sessions on which this window is active 314 window-activity ;; Time of window last activity 315 window-activity-flag ;; 1 if window has activity 316 window-bell-flag ;; 1 if window has bell 317 window-bigger ;; 1 if window is larger than client 318 window-cell-height ;; Height of each cell in pixels 319 window-cell-width ;; Width of each cell in pixels 320 window-end-flag ;; 1 if window has the highest index 321 window-flags ;; F Window flags with # escaped as ## 322 window-format ;; 1 if format is for a window 323 window-height ;; Height of window 324 window-id ;; Unique window ID 325 window-index ;; I Index of window 326 window-last-flag ;; 1 if window is the last used 327 window-layout ;; Window layout description, ignoring zoomed window panes 328 window-linked ;; 1 if window is linked across sessions 329 window-linked-sessions ;; Number of sessions this window is linked to 330 window-linked-sessions-list ;; List of sessions this window is linked to 331 window-marked-flag ;; 1 if window contains the marked pane 332 window-name ;; W Name of window 333 window-offset-x ;; X offset into window if larger than client 334 window-offset-y ;; Y offset into window if larger than client 335 window-panes ;; Number of panes in window 336 window-raw-flags ;; Window flags with nothing escaped 337 window-silence-flag ;; 1 if window has silence alert 338 window-stack-index ;; Index in session most recent stack 339 window-start-flag ;; 1 if window has the lowest index 340 window-visible-layout ;; Window layout description, respecting zoomed window panes 341 window-width ;; Width of window 342 window-zoomed-flag ;; 1 if window is zoomed 343 wrap-flag ;; Pane wrap flag 345 popup-centre-x Centered in the client 346 popup-centre-y ;; entered in the client 347 popup-height ;; eight of menu or popup 348 popup-mouse-bottom ;; ottom of at the mouse 349 popup-mouse-centre-x ;; orizontal centre at the mouse 350 popup-mouse-centre-y ;; ertical centre at the mouse 351 popup-mouse-top ;; op at the mouse 352 popup-mouse-x ;; ouse X position 353 popup-mouse-y ;; ouse Y position 354 popup-pane-bottom ;; ottom of the pane 355 popup-pane-left ;; eft of the pane 356 popup-pane-right ;; ight of the pane 357 popup-pane-top ;; op of the pane 358 popup-status-line-y ;; bove or below the status line 359 popup-width ;; idth of menu or popup 360 popup-window-status-line-x ;; t the window position in status line 361 popup-window-status-line-y ;; t the status line showing the window 364 (defvar *tmux-variable-names* 366 (loop for v across *tmux-variables* 367 collect (string-downcase (substitute #\_ #\- (symbol-name v))))