Class: Tmuxinator::Project

Inherits:
Object
  • Object
show all
Includes:
Deprecations, Hooks::Project, Util
Defined in:
lib/tmuxinator/project.rb

Constant Summary collapse

RBENVRVM_DEP_MSG =
<<-M
DEPRECATION: rbenv/rvm-specific options have been replaced by the
`pre_tab` option and will not be supported in 0.8.0.
M
TABS_DEP_MSG =
<<-M
DEPRECATION: The tabs option has been replaced by the `windows` option
and will not be supported in 0.8.0.
M
CLIARGS_DEP_MSG =
<<-M
DEPRECATION: The `cli_args` option has been replaced by the `tmux_options`
option and will not be supported in 0.8.0.
M
SYNC_DEP_MSG =
<<-M
DEPRECATION: The `synchronize` option's current default behaviour is to
enable pane synchronization before running commands. In a future release,
the default synchronization option will be `after`, i.e. synchronization of
panes will occur after the commands described in each of the panes
have run. At that time, the current behavior will need to be explicitly
enabled, using the `synchronize: before` option. To use this behaviour
now, use the 'synchronize: after' option.
M
PRE_DEP_MSG =
<<-M
DEPRECATION: The `pre` option has been replaced by project hooks (`on_project_start` and
`on_project_restart`) and will be removed in a future release.
M
POST_DEP_MSG =
<<-M
DEPRECATION: The `post` option has been replaced by project hooks (`on_project_stop` and
`on_project_exit`) and will be removed in a future release.
M

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Hooks::Project

hook_on_project_exit, hook_on_project_first_start, hook_on_project_restart, hook_on_project_start, hook_on_project_stop

Methods included from Deprecations

#pre_tab?

Methods included from Util

#exit!, #yes_no

Constructor Details

#initialize(yaml, options = {}) ⇒ Project

Returns a new instance of Project.

[View source]

81
82
83
84
85
86
87
88
89
90
91
92
# File 'lib/tmuxinator/project.rb', line 81

def initialize(yaml, options = {})
  options[:force_attach] = false if options[:force_attach].nil?
  options[:force_detach] = false if options[:force_detach].nil?

  @yaml = yaml

  set_instance_variables_from_options(options)

  validate_options

  extend Tmuxinator::WemuxSupport if wemux?
end

Instance Attribute Details

#custom_nameObject (readonly)

Returns the value of attribute custom_name.


42
43
44
# File 'lib/tmuxinator/project.rb', line 42

def custom_name
  @custom_name
end

#force_attachObject (readonly)

Returns the value of attribute force_attach.


40
41
42
# File 'lib/tmuxinator/project.rb', line 40

def force_attach
  @force_attach
end

#force_detachObject (readonly)

Returns the value of attribute force_detach.


41
42
43
# File 'lib/tmuxinator/project.rb', line 41

def force_detach
  @force_detach
end

#no_pre_windowObject (readonly)

Returns the value of attribute no_pre_window.


43
44
45
# File 'lib/tmuxinator/project.rb', line 43

def no_pre_window
  @no_pre_window
end

#yamlObject (readonly)

Returns the value of attribute yaml.


39
40
41
# File 'lib/tmuxinator/project.rb', line 39

def yaml
  @yaml
end

Class Method Details

.load(path, options = {}) ⇒ Object

[View source]

45
46
47
48
49
50
51
52
53
54
55
56
57
58
# File 'lib/tmuxinator/project.rb', line 45

def self.load(path, options = {})
  yaml = begin
    args = options[:args] || []
    @settings = parse_settings(args)
    @args = args

    content = render_template(path, binding)
    YAML.safe_load(content, aliases: true)
         rescue SyntaxError, StandardError => error
           raise "Failed to parse config file: #{error.message}"
  end

  new(yaml, options)
end

.parse_settings(args) ⇒ Object

[View source]

60
61
62
63
64
65
66
67
68
69
70
# File 'lib/tmuxinator/project.rb', line 60

def self.parse_settings(args)
  settings = args.select { |x| x.match(/.*=.*/) }
  args.reject! { |x| x.match(/.*=.*/) }

  settings.map! do |setting|
    parts = setting.split("=")
    [parts[0], parts[1]]
  end

  Hash[settings]
end

.render_template(template, bndg) ⇒ Object

[View source]

120
121
122
123
# File 'lib/tmuxinator/project.rb', line 120

def self.render_template(template, bndg)
  content = File.read(template)
  bndg.eval(Erubi::Engine.new(content).src)
end

Instance Method Details

#append?Boolean

Returns:

  • (Boolean)
[View source]

153
154
155
# File 'lib/tmuxinator/project.rb', line 153

def append?
  @append
end

#attach?Boolean

Returns:

  • (Boolean)
[View source]

162
163
164
165
166
167
168
169
170
# File 'lib/tmuxinator/project.rb', line 162

def attach?
  yaml_attach = if yaml["attach"].nil?
                  true
                else
                  yaml["attach"]
                end
  attach = force_attach || !force_detach && yaml_attach
  attach
end

#base_indexObject

[View source]

248
249
250
251
252
# File 'lib/tmuxinator/project.rb', line 248

def base_index
  return last_window_index + 1 if append?

  get_base_index.to_i
end

#cli_args?Boolean

Returns:

  • (Boolean)
[View source]

344
345
346
# File 'lib/tmuxinator/project.rb', line 344

def cli_args?
  yaml["cli_args"]
end

#current_session_nameObject

[View source]

138
139
140
# File 'lib/tmuxinator/project.rb', line 138

def current_session_name
  `[[ -n "${TMUX+set}" ]] && tmux display-message -p "#S"`.strip
end

#deprecation_checksObject

[View source]

306
307
308
309
310
311
312
313
314
315
# File 'lib/tmuxinator/project.rb', line 306

def deprecation_checks
  [
    rvm_or_rbenv?,
    tabs?,
    cli_args?,
    legacy_synchronize?,
    pre?,
    post?
  ]
end

#deprecation_messagesObject

[View source]

317
318
319
320
321
322
323
324
325
326
# File 'lib/tmuxinator/project.rb', line 317

def deprecation_messages
  [
    RBENVRVM_DEP_MSG,
    TABS_DEP_MSG,
    CLIARGS_DEP_MSG,
    SYNC_DEP_MSG,
    PRE_DEP_MSG,
    POST_DEP_MSG
  ]
end

#deprecationsObject

[View source]

298
299
300
301
302
303
304
# File 'lib/tmuxinator/project.rb', line 298

def deprecations
  deprecation_checks.zip(deprecation_messages).
    inject([]) do |deps, (chk, msg)|
    deps << msg if chk
    deps
  end
end

#enable_pane_titles?Boolean

Returns:

  • (Boolean)
[View source]

381
382
383
# File 'lib/tmuxinator/project.rb', line 381

def enable_pane_titles?
  yaml["enable_pane_titles"]
end

#get_base_indexObject

[View source]

360
361
362
# File 'lib/tmuxinator/project.rb', line 360

def get_base_index
  tmux_config["base-index"]
end

#get_pane_base_indexObject

[View source]

356
357
358
# File 'lib/tmuxinator/project.rb', line 356

def get_pane_base_index
  tmux_config["pane-base-index"]
end

#killObject

[View source]

116
117
118
# File 'lib/tmuxinator/project.rb', line 116

def kill
  self.class.render_template(Tmuxinator::Config.stop_template, binding)
end

#last_window_indexObject

[View source]

244
245
246
# File 'lib/tmuxinator/project.rb', line 244

def last_window_index
  `tmux list-windows -F '#I'`.split.last.to_i
end

#nameObject

[View source]

142
143
144
145
146
147
148
149
150
151
# File 'lib/tmuxinator/project.rb', line 142

def name
  name =
    if append?
      current_session_name
    else
      custom_name || yaml["project_name"] || yaml["name"]
    end

  blank?(name) ? nil : name.to_s.shellescape
end

#name?Boolean

Returns:

  • (Boolean)
[View source]

278
279
280
# File 'lib/tmuxinator/project.rb', line 278

def name?
  !name.nil?
end

#pane_base_indexObject

[View source]

254
255
256
# File 'lib/tmuxinator/project.rb', line 254

def pane_base_index
  get_pane_base_index.to_i
end

#pane_title_position_not_valid_warningObject

[View source]

403
404
405
406
407
408
409
# File 'lib/tmuxinator/project.rb', line 403

def pane_title_position_not_valid_warning
  print_warning(
    "The specified pane title position " +
    "\"#{yaml['pane_title_position']}\" is not valid. " +
    "Please choose one of: top, bottom, or off."
  )
end

#pane_titles_not_supported_warningObject

[View source]

411
412
413
414
415
416
417
# File 'lib/tmuxinator/project.rb', line 411

def pane_titles_not_supported_warning
  print_warning(
    "You have enabled pane titles in your configuration, " +
    "but the feature is not supported by your version of tmux.\n" +
    "Please consider upgrading to a version that supports it (tmux >=2.6)."
  )
end

#postObject

[View source]

187
188
189
190
# File 'lib/tmuxinator/project.rb', line 187

def post
  post_config = yaml["post"]
  parsed_parameters(post_config)
end

#post?Boolean

Returns:

  • (Boolean)
[View source]

352
353
354
# File 'lib/tmuxinator/project.rb', line 352

def post?
  yaml["post"]
end

#preObject

[View source]

157
158
159
160
# File 'lib/tmuxinator/project.rb', line 157

def pre
  pre_config = yaml["pre"]
  parsed_parameters(pre_config)
end

#pre?Boolean

Returns:

  • (Boolean)
[View source]

348
349
350
# File 'lib/tmuxinator/project.rb', line 348

def pre?
  yaml["pre"]
end

#pre_windowObject

[View source]

172
173
174
175
176
177
178
179
180
181
182
183
184
185
# File 'lib/tmuxinator/project.rb', line 172

def pre_window
  return if no_pre_window

  params = if rbenv?
             "rbenv shell #{yaml['rbenv']}"
           elsif rvm?
             "rvm use #{yaml['rvm']}"
           elsif pre_tab?
             yaml["pre_tab"]
           else
             yaml["pre_window"]
           end
  parsed_parameters(params)
end

#rbenv?Boolean

Returns:

  • (Boolean)
[View source]

328
329
330
# File 'lib/tmuxinator/project.rb', line 328

def rbenv?
  yaml["rbenv"]
end

#renderObject

[View source]

112
113
114
# File 'lib/tmuxinator/project.rb', line 112

def render
  self.class.render_template(Tmuxinator::Config.template, binding)
end

#rootObject

[View source]

133
134
135
136
# File 'lib/tmuxinator/project.rb', line 133

def root
  root = yaml["project_root"] || yaml["root"]
  blank?(root) ? nil : File.expand_path(root).shellescape
end

#root?Boolean

Returns:

  • (Boolean)
[View source]

274
275
276
# File 'lib/tmuxinator/project.rb', line 274

def root?
  !root.nil?
end

#rvm?Boolean

Returns:

  • (Boolean)
[View source]

332
333
334
# File 'lib/tmuxinator/project.rb', line 332

def rvm?
  yaml["rvm"]
end

#rvm_or_rbenv?Boolean

Returns:

  • (Boolean)
[View source]

336
337
338
# File 'lib/tmuxinator/project.rb', line 336

def rvm_or_rbenv?
  rvm? || rbenv?
end

#send_keys(cmd, window_index) ⇒ Object

[View source]

290
291
292
293
294
295
296
# File 'lib/tmuxinator/project.rb', line 290

def send_keys(cmd, window_index)
  if cmd.empty?
    ""
  else
    "#{tmux} send-keys -t #{window(window_index)} #{cmd.shellescape} C-m"
  end
end

#send_pane_command(cmd, window_index, _pane_index) ⇒ Object

[View source]

286
287
288
# File 'lib/tmuxinator/project.rb', line 286

def send_pane_command(cmd, window_index, _pane_index)
  send_keys(cmd, window_index)
end

#set_instance_variables_from_options(options) ⇒ Object

[View source]

94
95
96
97
98
99
100
# File 'lib/tmuxinator/project.rb', line 94

def set_instance_variables_from_options(options)
  @custom_name = options[:custom_name]
  @force_attach = options[:force_attach]
  @force_detach = options[:force_detach]
  @append = options[:append]
  @no_pre_window = options[:no_pre_window]
end

#show_tmux_optionsObject

[View source]

364
365
366
367
368
# File 'lib/tmuxinator/project.rb', line 364

def show_tmux_options
  "#{tmux} start-server\\; " \
    "show-option -g base-index\\; " \
    "show-window-option -g pane-base-index\\;"
end

#socketObject

[View source]

218
219
220
221
222
223
224
# File 'lib/tmuxinator/project.rb', line 218

def socket
  if socket_path
    " -S #{socket_path}"
  elsif socket_name
    " -L #{socket_name}"
  end
end

#socket_nameObject

[View source]

226
227
228
# File 'lib/tmuxinator/project.rb', line 226

def socket_name
  yaml["socket_name"]
end

#socket_pathObject

[View source]

230
231
232
# File 'lib/tmuxinator/project.rb', line 230

def socket_path
  yaml["socket_path"]
end

#startup_paneObject

[View source]

262
263
264
# File 'lib/tmuxinator/project.rb', line 262

def startup_pane
  "#{startup_window}.#{yaml['startup_pane'] || pane_base_index}"
end

#startup_windowObject

[View source]

258
259
260
# File 'lib/tmuxinator/project.rb', line 258

def startup_window
  "#{name}:#{yaml['startup_window'] || base_index}"
end

#tabs?Boolean

Returns:

  • (Boolean)
[View source]

340
341
342
# File 'lib/tmuxinator/project.rb', line 340

def tabs?
  yaml["tabs"]
end

#tmuxObject

[View source]

192
193
194
# File 'lib/tmuxinator/project.rb', line 192

def tmux
  "#{tmux_command}#{tmux_options}#{socket}"
end

#tmux_commandObject

[View source]

196
197
198
# File 'lib/tmuxinator/project.rb', line 196

def tmux_command
  yaml["tmux_command"] || "tmux"
end

#tmux_has_session?(name) ⇒ Boolean

Returns:

  • (Boolean)
[View source]

200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
# File 'lib/tmuxinator/project.rb', line 200

def tmux_has_session?(name)
  return false unless name

  # Redirect stderr to /dev/null in order to prevent "failed to connect
  # to server: Connection refused" error message and non-zero exit status
  # if no tmux sessions exist.
  # Please see issues #402 and #414.
  sessions = `#{tmux} ls 2> /dev/null`

  # Remove any escape sequences added by `shellescape` in Project#name.
  # Escapes can result in: "ArgumentError: invalid multibyte character"
  # when attempting to match `name` against `sessions`.
  # Please see issue #564.
  unescaped_name = name.shellsplit.join("")

  !(sessions !~ /^#{unescaped_name}:/)
end

#tmux_kill_session_commandObject

[View source]

377
378
379
# File 'lib/tmuxinator/project.rb', line 377

def tmux_kill_session_command
  "#{tmux} kill-session -t #{name}"
end

#tmux_new_session_commandObject

[View source]

370
371
372
373
374
375
# File 'lib/tmuxinator/project.rb', line 370

def tmux_new_session_command
  return if append?

  window = windows.first.tmux_window_name_option
  "#{tmux} new-session -d -s #{name} #{window}"
end

#tmux_optionsObject

[View source]

234
235
236
237
238
239
240
241
242
# File 'lib/tmuxinator/project.rb', line 234

def tmux_options
  if cli_args?
    " #{yaml['cli_args'].to_s.strip}"
  elsif tmux_options?
    " #{yaml['tmux_options'].to_s.strip}"
  else
    ""
  end
end

#tmux_options?Boolean

Returns:

  • (Boolean)
[View source]

266
267
268
# File 'lib/tmuxinator/project.rb', line 266

def tmux_options?
  yaml["tmux_options"]
end

#tmux_set_pane_title_format(tmux_window_target) ⇒ Object

[View source]

394
395
396
397
398
399
400
401
# File 'lib/tmuxinator/project.rb', line 394

def tmux_set_pane_title_format(tmux_window_target)
  command = set_window_option(tmux_window_target)
  if pane_title_format?
    "#{command} pane-border-format \"#{yaml['pane_title_format']}\""
  else
    "#{command} pane-border-format \"\#{pane_index}: \#{pane_title}\""
  end
end

#tmux_set_pane_title_position(tmux_window_target) ⇒ Object

[View source]

385
386
387
388
389
390
391
392
# File 'lib/tmuxinator/project.rb', line 385

def tmux_set_pane_title_position(tmux_window_target)
  command = set_window_option(tmux_window_target)
  if pane_title_position? && pane_title_position_valid?
    "#{command} pane-border-status #{yaml['pane_title_position']}"
  else
    "#{command} pane-border-status top"
  end
end

#validate!Object

[View source]

72
73
74
75
76
77
78
79
# File 'lib/tmuxinator/project.rb', line 72

def validate!
  raise "Your project file should include some windows." \
    unless windows?
  raise "Your project file didn't specify a 'project_name'" \
    unless name?

  self
end

#validate_optionsObject

[View source]

102
103
104
105
106
107
108
109
110
# File 'lib/tmuxinator/project.rb', line 102

def validate_options
  if @force_attach && @force_detach
    raise "Cannot force_attach and force_detach at the same time"
  end

  if append? && !tmux_has_session?(name)
    raise "Cannot append to a session that does not exist"
  end
end

#window(index) ⇒ Object

[View source]

282
283
284
# File 'lib/tmuxinator/project.rb', line 282

def window(index)
  append? ? ":#{index}" : "#{name}:#{index}"
end

#windowsObject

[View source]

125
126
127
128
129
130
131
# File 'lib/tmuxinator/project.rb', line 125

def windows
  windows_yml = yaml["tabs"] || yaml["windows"]

  @windows ||= (windows_yml || {}).map.with_index do |window_yml, index|
    Tmuxinator::Window.new(window_yml, index, self)
  end
end

#windows?Boolean

Returns:

  • (Boolean)
[View source]

270
271
272
# File 'lib/tmuxinator/project.rb', line 270

def windows?
  windows.any?
end