Class: Soba::Infrastructure::TmuxClient

Inherits:
Object
  • Object
show all
Defined in:
lib/soba/infrastructure/tmux_client.rb

Instance Method Summary collapse

Instance Method Details

#active_sessionObject



127
128
129
130
131
132
133
134
# File 'lib/soba/infrastructure/tmux_client.rb', line 127

def active_session
  stdout, _stderr, status = execute_tmux_command('display-message', '-p', '#{session_name}')
  return nil unless status.exitstatus == 0

  stdout.strip
rescue Errno::ENOENT
  nil
end

#attach_to_session(session_name) ⇒ Object



276
277
278
279
280
281
# File 'lib/soba/infrastructure/tmux_client.rb', line 276

def attach_to_session(session_name)
  # Use system call to attach to tmux session
  system("tmux", "attach-session", "-t", session_name)
rescue Errno::ENOENT
  false
end

#attach_to_window(window_id) ⇒ Object



269
270
271
272
273
274
# File 'lib/soba/infrastructure/tmux_client.rb', line 269

def attach_to_window(window_id)
  # Use system call to attach to tmux session
  system("tmux", "attach-session", "-t", window_id)
rescue Errno::ENOENT
  false
end

#capture_pane(session_name) ⇒ Object



46
47
48
49
50
51
52
53
# File 'lib/soba/infrastructure/tmux_client.rb', line 46

def capture_pane(session_name)
  stdout, _stderr, status = execute_tmux_command('capture-pane', '-t', session_name, '-p')
  return nil unless status.exitstatus == 0

  stdout
rescue Errno::ENOENT
  nil
end

#capture_pane_continuous(pane_id) ⇒ Object



156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
# File 'lib/soba/infrastructure/tmux_client.rb', line 156

def capture_pane_continuous(pane_id)
  last_content = nil

  loop do
    stdout, _stderr, status = execute_tmux_command('capture-pane', '-t', pane_id, '-p', '-S', '-')
    break unless status.exitstatus == 0

    # Yield only new content
    if last_content.nil?
      # 初回は全体を返す
      yield stdout unless stdout.empty?
      last_content = stdout
    elsif stdout != last_content && stdout.length > last_content.length
      # コンテンツが増えた場合は差分のみを返す
      new_lines = stdout[last_content.length..-1]
      yield new_lines unless new_lines.empty?
      last_content = stdout
    elsif stdout != last_content
      # コンテンツが変わったが短くなった場合(画面クリアなど)は全体を返す
      yield stdout unless stdout.empty?
      last_content = stdout
    end

    sleep 1
  end
rescue Errno::ENOENT
  nil
end

#close_pane(session_name, pane_index) ⇒ Object



109
110
111
112
113
114
# File 'lib/soba/infrastructure/tmux_client.rb', line 109

def close_pane(session_name, pane_index)
  _stdout, _stderr, status = execute_tmux_command('kill-pane', '-t', "#{session_name}.#{pane_index}")
  status.exitstatus == 0
rescue Errno::ENOENT
  false
end

#create_session(session_name) ⇒ Object



9
10
11
12
13
14
# File 'lib/soba/infrastructure/tmux_client.rb', line 9

def create_session(session_name)
  _stdout, _stderr, status = execute_tmux_command('new-session', '-d', '-s', session_name)
  status.exitstatus == 0
rescue Errno::ENOENT
  raise TmuxNotInstalled, 'tmux is not installed or not in PATH'
end

#create_window(session_name, window_name) ⇒ Object



55
56
57
58
59
60
# File 'lib/soba/infrastructure/tmux_client.rb', line 55

def create_window(session_name, window_name)
  _stdout, _stderr, status = execute_tmux_command('new-window', '-t', session_name, '-n', window_name)
  status.exitstatus == 0
rescue Errno::ENOENT
  false
end

#find_pane(session_name) ⇒ Object



146
147
148
149
150
151
152
153
154
# File 'lib/soba/infrastructure/tmux_client.rb', line 146

def find_pane(session_name)
  stdout, _stderr, status = execute_tmux_command('list-panes', '-t', session_name, '-F', '#{pane_id}')
  return nil unless status.exitstatus == 0

  # Return first pane ID
  stdout.lines.first&.strip
rescue Errno::ENOENT
  nil
end

#kill_pane(pane_id) ⇒ Object



239
240
241
242
243
244
# File 'lib/soba/infrastructure/tmux_client.rb', line 239

def kill_pane(pane_id)
  _stdout, _stderr, status = execute_tmux_command('kill-pane', '-t', pane_id)
  status.exitstatus == 0
rescue Errno::ENOENT
  false
end

#kill_session(session_name) ⇒ Object



16
17
18
19
20
21
# File 'lib/soba/infrastructure/tmux_client.rb', line 16

def kill_session(session_name)
  _stdout, _stderr, status = execute_tmux_command('kill-session', '-t', session_name)
  status.exitstatus == 0
rescue Errno::ENOENT
  raise TmuxNotInstalled, 'tmux is not installed or not in PATH'
end

#kill_window(session_name, window_name) ⇒ Object



254
255
256
257
258
259
260
# File 'lib/soba/infrastructure/tmux_client.rb', line 254

def kill_window(session_name, window_name)
  target = "#{session_name}:#{window_name}"
  _stdout, _stderr, status = execute_tmux_command('kill-window', '-t', target)
  status.exitstatus == 0
rescue Errno::ENOENT
  false
end

#list_panes(session_name, window_name) ⇒ Object



224
225
226
227
228
229
230
231
232
233
234
235
236
237
# File 'lib/soba/infrastructure/tmux_client.rb', line 224

def list_panes(session_name, window_name)
  target = "#{session_name}:#{window_name}"
  stdout, _stderr, status = execute_tmux_command(
    'list-panes', '-t', target, '-F', '#{pane_id}:#{pane_start_time}'
  )
  return [] unless status.exitstatus == 0

  stdout.lines.map do |line|
    parts = line.strip.split(':')
    { id: parts[0], start_time: parts[1].to_i }
  end
rescue Errno::ENOENT
  []
end

#list_sessionsObject



30
31
32
33
34
35
36
37
# File 'lib/soba/infrastructure/tmux_client.rb', line 30

def list_sessions
  stdout, _stderr, status = execute_tmux_command('list-sessions')
  return [] unless status.exitstatus == 0

  parse_session_list(stdout)
rescue Errno::ENOENT
  []
end

#list_soba_sessionsObject



185
186
187
188
189
190
191
192
193
194
195
# File 'lib/soba/infrastructure/tmux_client.rb', line 185

def list_soba_sessions
  sessions = list_sessions

  if ENV['SOBA_TEST_MODE'] == 'true'
    # テストモードの場合は、soba-test-で始まるセッションのみを返す
    sessions.select { |s| s.start_with?('soba-test-') }
  else
    # 通常モードの場合は、soba-で始まるがsoba-test-で始まらないセッションを返す
    sessions.select { |s| s.start_with?('soba-') && !s.start_with?('soba-test-') }
  end
end

#list_windows(session_name) ⇒ Object



69
70
71
72
73
74
75
76
# File 'lib/soba/infrastructure/tmux_client.rb', line 69

def list_windows(session_name)
  stdout, _stderr, status = execute_tmux_command('list-windows', '-t', session_name)
  return [] unless status.exitstatus == 0

  parse_window_list(stdout)
rescue Errno::ENOENT
  []
end

#rename_window(session_name, old_name, new_name) ⇒ Object



78
79
80
81
82
83
# File 'lib/soba/infrastructure/tmux_client.rb', line 78

def rename_window(session_name, old_name, new_name)
  _stdout, _stderr, status = execute_tmux_command('rename-window', '-t', "#{session_name}:#{old_name}", new_name)
  status.exitstatus == 0
rescue Errno::ENOENT
  false
end

#resize_pane(session_name, direction, size) ⇒ Object



100
101
102
103
104
105
106
107
# File 'lib/soba/infrastructure/tmux_client.rb', line 100

def resize_pane(session_name, direction, size)
  direction_flags = { up: '-U', down: '-D', left: '-L', right: '-R' }
  flag = direction_flags[direction]
  _stdout, _stderr, status = execute_tmux_command('resize-pane', '-t', session_name, flag, size.to_s)
  status.exitstatus == 0
rescue Errno::ENOENT
  false
end

#select_layout(session_name, window_name, layout) ⇒ Object



246
247
248
249
250
251
252
# File 'lib/soba/infrastructure/tmux_client.rb', line 246

def select_layout(session_name, window_name, layout)
  target = "#{session_name}:#{window_name}"
  _stdout, _stderr, status = execute_tmux_command('select-layout', '-t', target, layout)
  status.exitstatus == 0
rescue Errno::ENOENT
  false
end

#select_pane(session_name, pane_index) ⇒ Object



93
94
95
96
97
98
# File 'lib/soba/infrastructure/tmux_client.rb', line 93

def select_pane(session_name, pane_index)
  _stdout, _stderr, status = execute_tmux_command('select-pane', '-t', "#{session_name}.#{pane_index}")
  status.exitstatus == 0
rescue Errno::ENOENT
  false
end

#send_keys(session_name, command) ⇒ Object



39
40
41
42
43
44
# File 'lib/soba/infrastructure/tmux_client.rb', line 39

def send_keys(session_name, command)
  _stdout, _stderr, status = execute_tmux_command('send-keys', '-t', session_name, command, 'Enter')
  status.exitstatus == 0
rescue Errno::ENOENT
  false
end

#session_attached?(session_name) ⇒ Boolean

Returns:

  • (Boolean)


136
137
138
139
140
141
142
143
144
# File 'lib/soba/infrastructure/tmux_client.rb', line 136

def session_attached?(session_name)
  stdout, _stderr, status = execute_tmux_command('list-sessions', '-F', '#{session_name}: #{session_attached}',
'-f', "#{session_name}==#{session_name}")
  return false unless status.exitstatus == 0

  parse_attached_status(stdout)
rescue Errno::ENOENT
  false
end

#session_exists?(session_name) ⇒ Boolean

Returns:

  • (Boolean)


23
24
25
26
27
28
# File 'lib/soba/infrastructure/tmux_client.rb', line 23

def session_exists?(session_name)
  _stdout, _stderr, status = execute_tmux_command('has-session', '-t', session_name)
  status.exitstatus == 0
rescue Errno::ENOENT
  false
end

#session_info(session_name) ⇒ Object



116
117
118
119
120
121
122
123
124
125
# File 'lib/soba/infrastructure/tmux_client.rb', line 116

def session_info(session_name)
  format_string = '#{session_name}: #{session_windows} windows ' \
                  '(created #{session_created_string}) [#{session_width}x#{session_height}]'
  stdout, _stderr, status = execute_tmux_command('list-sessions', '-F', format_string)
  return nil unless status.exitstatus == 0

  parse_session_info(stdout, session_name)
rescue Errno::ENOENT
  nil
end

#split_pane(session_name, direction) ⇒ Object



85
86
87
88
89
90
91
# File 'lib/soba/infrastructure/tmux_client.rb', line 85

def split_pane(session_name, direction)
  flag = direction == :horizontal ? '-h' : '-v'
  _stdout, _stderr, status = execute_tmux_command('split-window', '-t', session_name, flag)
  status.exitstatus == 0
rescue Errno::ENOENT
  false
end

#split_window(session_name:, window_name:, vertical: true) ⇒ Object



204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
# File 'lib/soba/infrastructure/tmux_client.rb', line 204

def split_window(session_name:, window_name:, vertical: true)
  flag = vertical ? '-v' : '-h'
  target = "#{session_name}:#{window_name}"
  command_args = ['split-window', '-t', target, flag, '-P', '-F', '#{pane_id}']
  stdout, stderr, status = execute_tmux_command(*command_args)

  if status.exitstatus == 0
    stdout.strip
  else
    error_details = {
      stderr: stderr,
      command: ['tmux'] + command_args,
      exit_status: status.exitstatus,
    }
    [nil, error_details]
  end
rescue Errno::ENOENT
  nil
end

#switch_window(session_name, window_name) ⇒ Object



62
63
64
65
66
67
# File 'lib/soba/infrastructure/tmux_client.rb', line 62

def switch_window(session_name, window_name)
  _stdout, _stderr, status = execute_tmux_command('select-window', '-t', "#{session_name}:#{window_name}")
  status.exitstatus == 0
rescue Errno::ENOENT
  false
end

#tmux_installed?Boolean

Returns:

  • (Boolean)


262
263
264
265
266
267
# File 'lib/soba/infrastructure/tmux_client.rb', line 262

def tmux_installed?
  _stdout, _stderr, _status = execute_tmux_command('list-sessions')
  true
rescue Errno::ENOENT
  false
end

#window_exists?(session_name, window_name) ⇒ Boolean

Returns:

  • (Boolean)


197
198
199
200
201
202
# File 'lib/soba/infrastructure/tmux_client.rb', line 197

def window_exists?(session_name, window_name)
  windows = list_windows(session_name)
  windows.include?(window_name)
rescue Errno::ENOENT
  false
end