Module: Mxup::Tmux

Defined in:
lib/mxup/tmux.rb

Overview

Thin wrapper over the tmux(1) CLI. Every method shells out; nothing here knows about mxup's own config. Kept as a module so callers can reference Mxup::Tmux.list_panes(...) without instantiating anything.

Defined Under Namespace

Classes: Error

Class Method Summary collapse

Class Method Details

.break_pane(session, window, pane_index, new_window_name) ⇒ Object

Break a pane out into its own window. Without an explicit -t target, tmux places the new window in the current client's session, which would send it to whichever session the user happens to be attached to when running tests. Always pin the destination to the source session.



89
90
91
92
# File 'lib/mxup/tmux.rb', line 89

def break_pane(session, window, pane_index, new_window_name)
  run!('break-pane', "tmux break-pane -s #{esc(session)}:#{esc(window)}.#{pane_index} " \
       "-t #{esc(session)}: -n #{esc(new_window_name)} -d")
end

.capture_pane(session, target, _lines = nil) ⇒ Object

Capture the pane's entire scrollback (-S - is "start of history") so callers can filter and tail as they see fit. We deliberately don't pass -S -N here: if the interesting output scrolled past the visible window, a lower bound would silently hide it.



56
57
58
# File 'lib/mxup/tmux.rb', line 56

def capture_pane(session, target, _lines = nil)
  `tmux capture-pane -t #{esc(session)}:#{esc(target)} -p -S - 2>/dev/null`
end

.esc(val) ⇒ Object



145
146
147
# File 'lib/mxup/tmux.rb', line 145

def esc(val)
  Shellwords.escape(val.to_s)
end

.has_session?(name) ⇒ Boolean

--- inspection --------------------------------------------------------

Returns:

  • (Boolean)


20
21
22
# File 'lib/mxup/tmux.rb', line 20

def has_session?(name)
  system("tmux has-session -t #{esc(name)} 2>/dev/null")
end

.join_pane(session, src_window, dst_window) ⇒ Object



81
82
83
# File 'lib/mxup/tmux.rb', line 81

def join_pane(session, src_window, dst_window)
  run!('join-pane', "tmux join-pane -s #{esc(session)}:#{esc(src_window)} -t #{esc(session)}:#{esc(dst_window)} -d")
end

.kill_session(name) ⇒ Object



118
119
120
# File 'lib/mxup/tmux.rb', line 118

def kill_session(name)
  system("tmux kill-session -t #{esc(name)}")
end

.kill_window(session, name) ⇒ Object



114
115
116
# File 'lib/mxup/tmux.rb', line 114

def kill_window(session, name)
  system("tmux kill-window -t #{esc(session)}:#{esc(name)}")
end

.list_panes(session) ⇒ Object



36
37
38
39
40
41
42
43
44
45
# File 'lib/mxup/tmux.rb', line 36

def list_panes(session)
  fmt = '#{window_index}|#{window_name}|#{pane_index}|#{pane_pid}|' \
        '#{pane_current_path}|#{pane_current_command}|#{pane_title}'
  `tmux list-panes -t #{esc(session)} -s -F '#{fmt}'`
    .strip.split("\n").map do |line|
      win_idx, name, pane_idx, pid, cwd, fg, title = line.split('|', 7)
      { window_index: win_idx.to_i, name: name, pane_index: pane_idx.to_i,
        pid: pid.to_i, cwd: cwd, fg_cmd: fg, title: title.to_s }
    end
end

.list_windows(session) ⇒ Object



28
29
30
31
32
33
34
# File 'lib/mxup/tmux.rb', line 28

def list_windows(session)
  `tmux list-windows -t #{esc(session)} -F '\#{window_index}|\#{window_name}'`
    .strip.split("\n").map do |line|
      idx, name = line.split('|', 2)
      { index: idx.to_i, name: name }
    end
end

.move_window(session, src_name, dst_index) ⇒ Object



130
131
132
# File 'lib/mxup/tmux.rb', line 130

def move_window(session, src_name, dst_index)
  system("tmux move-window -s #{esc(session)}:#{esc(src_name)} -t #{esc(session)}:#{dst_index} 2>/dev/null")
end

.new_session(name, first_window_name, root) ⇒ Object

--- mutation ----------------------------------------------------------



62
63
64
# File 'lib/mxup/tmux.rb', line 62

def new_session(name, first_window_name, root)
  run!('new-session', "tmux new-session -d -s #{esc(name)} -n #{esc(first_window_name)} -c #{esc(root)}")
end

.new_window(session, name, root) ⇒ Object



66
67
68
# File 'lib/mxup/tmux.rb', line 66

def new_window(session, name, root)
  run!('new-window', "tmux new-window -t #{esc(session)} -n #{esc(name)} -c #{esc(root)}")
end

.pane_count(session, window) ⇒ Object



47
48
49
50
# File 'lib/mxup/tmux.rb', line 47

def pane_count(session, window)
  `tmux list-panes -t #{esc(session)}:#{esc(window)} 2>/dev/null`
    .strip.split("\n").size
end

.pane_target(window, pane_index) ⇒ Object

Format a pane target string "window.index" for use in other tmux calls.



141
142
143
# File 'lib/mxup/tmux.rb', line 141

def pane_target(window, pane_index)
  "#{window}.#{pane_index}"
end

.rename_window(session, old_name, new_name) ⇒ Object



94
95
96
# File 'lib/mxup/tmux.rb', line 94

def rename_window(session, old_name, new_name)
  run!('rename-window', "tmux rename-window -t #{esc(session)}:#{esc(old_name)} #{esc(new_name)}")
end

.run!(action, cmd) ⇒ Object

Run a tmux command that must succeed. Captures stdout+stderr so a failure can be re-raised with tmux's own message; returns true on success. Best-effort calls (predicates, teardown, reordering) keep using system() directly and stay tolerant of failure.

Raises:



153
154
155
156
157
158
# File 'lib/mxup/tmux.rb', line 153

def run!(action, cmd)
  output = `#{cmd} 2>&1`.strip
  return true if $?.success?

  raise Error, "tmux #{action} failed#{output.empty? ? '' : ": #{output}"}"
end

.select_layout(session, window, layout) ⇒ Object



77
78
79
# File 'lib/mxup/tmux.rb', line 77

def select_layout(session, window, layout)
  run!('select-layout', "tmux select-layout -t #{esc(session)}:#{esc(window)} #{esc(layout)}")
end

.send_interrupt(session, target) ⇒ Object



126
127
128
# File 'lib/mxup/tmux.rb', line 126

def send_interrupt(session, target)
  run!('send-keys', "tmux send-keys -t #{esc(session)}:#{esc(target)} C-c")
end

.send_keys(session, target, keys) ⇒ Object



122
123
124
# File 'lib/mxup/tmux.rb', line 122

def send_keys(session, target, keys)
  run!('send-keys', "tmux send-keys -t #{esc(session)}:#{esc(target)} #{esc(keys)} Enter")
end

.session_created(name) ⇒ Object



24
25
26
# File 'lib/mxup/tmux.rb', line 24

def session_created(name)
  `tmux display-message -t #{esc(name)} -p '\#{session_created}'`.strip
end

.set_environment(session, key, value) ⇒ Object



102
103
104
# File 'lib/mxup/tmux.rb', line 102

def set_environment(session, key, value)
  run!('set-environment', "tmux set-environment -t #{esc(session)} #{esc(key)} #{esc(value)}")
end

.set_pane_title(session, window, pane_index, title) ⇒ Object



98
99
100
# File 'lib/mxup/tmux.rb', line 98

def set_pane_title(session, window, pane_index, title)
  run!('select-pane', "tmux select-pane -t #{esc(session)}:#{esc(window)}.#{pane_index} -T #{esc(title)}")
end

.show_environment(session, key) ⇒ Object

Returns the value stored in the session's environment, or nil if unset (tmux prefixes unset entries with '-').



108
109
110
111
112
# File 'lib/mxup/tmux.rb', line 108

def show_environment(session, key)
  out = `tmux show-environment -t #{esc(session)} #{esc(key)} 2>/dev/null`.strip
  return nil if out.empty? || out.start_with?('-')
  out.split('=', 2).last
end

.split_window(session, window, root, target_pane: nil) ⇒ Object



70
71
72
73
74
75
# File 'lib/mxup/tmux.rb', line 70

def split_window(session, window, root, target_pane: nil)
  target = target_pane \
    ? "#{esc(session)}:#{esc(window)}.#{target_pane}" \
    : "#{esc(session)}:#{esc(window)}"
  run!('split-window', "tmux split-window -t #{target} -c #{esc(root)} -d")
end

.swap_window(session, idx_a, idx_b) ⇒ Object



134
135
136
# File 'lib/mxup/tmux.rb', line 134

def swap_window(session, idx_a, idx_b)
  system("tmux swap-window -s #{esc(session)}:#{idx_a} -t #{esc(session)}:#{idx_b}")
end