tmuxinator 3.3.3 → 3.3.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f5ef87c54d6e438767477e9e41cbe4d0b0d802c6aa980d07d1921819a7dc2b81
4
- data.tar.gz: dc974393d493a3ebfd556c87615472acc34f4dc328bb2a31b277ad40a865c8d1
3
+ metadata.gz: e954effc3cf016798540aa6c25ddf5465797a6653fb9400616d288892bde9c9c
4
+ data.tar.gz: b501066e62a1cb5b46d286e86bfc866ebeefbbadf157e588a4934dbdd41eb7d4
5
5
  SHA512:
6
- metadata.gz: dec7830c2331a3fb50a36afd880168ab3ec616900b41ba87c33e610235ee5cb935f794c1738ab5054bc78e3303b3fc078a1a380aed45fd7c9a08870b6a360ca0
7
- data.tar.gz: 182bc06919fe54d090fc455c5f69c5a653115887d495acba204cc2112ffd7379e759d3ed8d40067b8c00be1b20d95468f15a72562728400bfbb806f4a7020ede
6
+ metadata.gz: e29c29f221505b683afb040e11b9e771a3a6b4526e5f716c8c52fe06c27dc642577c6e1f02ff2ead907bcac72ce81b778e65c9763b1adfc0b5d3d4c621b5caad
7
+ data.tar.gz: 4429eb7736ef16c2d78acb2a70c27df6843bd7dcb1d6abef1940e7852735436e6d8d709e5ac8b6de5041968232cd062b727a03dd3f92af9eb31ba7944677c33d
@@ -1,22 +1,17 @@
1
1
  function __fish_tmuxinator_using_command
2
- set cmd (commandline -opc)
3
- if [ (count $cmd) -gt 1 ]
4
- if [ $argv[1] = $cmd[2] ]
5
- return 0
2
+ set cmd (commandline -opc)
3
+ if [ (count $cmd) -gt 1 ]
4
+ if [ $argv[1] = $cmd[2] ]
5
+ return 0
6
+ end
6
7
  end
7
- end
8
- return 1
8
+ return 1
9
9
  end
10
10
 
11
- set __fish_tmuxinator_program_cmd (commandline -o)[1]
12
-
13
- function __fish_tmuxinator_program
14
- eval "$__fish_tmuxinator_program_cmd $argv"
15
- end
16
-
17
- complete -f -c $__fish_tmuxinator_program_cmd -a '(__fish_tmuxinator_program completions start)'
18
- complete -f -c $__fish_tmuxinator_program_cmd -n '__fish_use_subcommand' -x -a "(__fish_tmuxinator_program commands)"
19
- complete -f -c $__fish_tmuxinator_program_cmd -n '__fish_tmuxinator_using_command start' -a "(__fish_tmuxinator_program completions start)"
20
- complete -f -c $__fish_tmuxinator_program_cmd -n '__fish_tmuxinator_using_command open' -a "(__fish_tmuxinator_program completions open)"
21
- complete -f -c $__fish_tmuxinator_program_cmd -n '__fish_tmuxinator_using_command copy' -a "(__fish_tmuxinator_program completions copy)"
22
- complete -f -c $__fish_tmuxinator_program_cmd -n '__fish_tmuxinator_using_command delete' -a "(__fish_tmuxinator_program completions delete)"
11
+ complete --no-files --command tmuxinator --condition __fish_use_subcommand --exclusive --argument "(tmuxinator commands)"
12
+ complete --no-files --command tmuxinator --condition '__fish_tmuxinator_using_command start' --argument "(tmuxinator completions start)"
13
+ complete --no-files --command tmuxinator --condition '__fish_tmuxinator_using_command open' --argument "(tmuxinator completions open)"
14
+ complete --no-files --command tmuxinator --condition '__fish_tmuxinator_using_command edit' --argument "(tmuxinator completions open)"
15
+ complete --no-files --command tmuxinator --condition '__fish_tmuxinator_using_command copy' --argument "(tmuxinator completions copy)"
16
+ complete --no-files --command tmuxinator --condition '__fish_tmuxinator_using_command delete' --argument "(tmuxinator completions delete)"
17
+ complete --no-files --command tmuxinator --condition '__fish_tmuxinator_using_command debug' --argument "(tmuxinator completions start)"
@@ -1,17 +1,19 @@
1
1
  #!<%= ENV["SHELL"] || "/bin/bash" %>
2
2
 
3
- # Clear rbenv variables before starting tmux
4
- unset RBENV_VERSION
5
- unset RBENV_DIR
3
+ <%- if !append? -%>
4
+ # Clear rbenv variables before starting tmux
5
+ unset RBENV_VERSION
6
+ unset RBENV_DIR
6
7
 
7
- <%= tmux %> start-server;
8
+ <%= tmux %> start-server;
9
+ <%- end -%>
8
10
 
9
11
  cd <%= root || "." %>
10
12
 
11
13
  # Run on_project_start command.
12
14
  <%= hook_on_project_start %>
13
15
 
14
- <%- if !tmux_has_session? name -%>
16
+ <%- if append? || !tmux_has_session?(name) -%>
15
17
 
16
18
  # Run pre command.
17
19
  <%= pre %>
@@ -98,7 +100,7 @@ cd <%= root || "." %>
98
100
  <%= hook_on_project_restart %>
99
101
  <%- end -%>
100
102
 
101
- <%- if attach? -%>
103
+ <%- if attach? && !append? -%>
102
104
  if [ -z "$TMUX" ]; then
103
105
  <%= tmux %> -u attach-session -t <%= name %>
104
106
  else
@@ -15,25 +15,26 @@ module Tmuxinator
15
15
  COMMANDS = {
16
16
  commands: "Lists commands available in tmuxinator",
17
17
  completions: "Used for shell completion",
18
- new: "Create a new project file and open it in your editor",
18
+ copy: %w{
19
+ Copy an existing project to a new project and
20
+ open it in your editor
21
+ }.join(" "),
22
+ debug: "Output the shell commands that are generated by tmuxinator",
23
+ delete: "Deletes given project",
24
+ doctor: "Look for problems in your configuration",
19
25
  edit: "Alias of new",
26
+ implode: "Deletes all tmuxinator projects",
27
+ local: "Start a tmux session using ./.tmuxinator.y[a]ml",
28
+ list: "Lists all tmuxinator projects",
29
+ new: "Create a new project file and open it in your editor",
20
30
  open: "Alias of new",
21
31
  start: %w{
22
32
  Start a tmux session using a project's name (with an optional [ALIAS]
23
33
  for project reuse) or a path to a project config file (via the -p flag)
24
34
  }.join(" "),
25
35
  stop: "Stop a tmux session using a project's tmuxinator config",
26
- local: "Start a tmux session using ./.tmuxinator.y[a]ml",
27
- debug: "Output the shell commands that are generated by tmuxinator",
28
- copy: %w{
29
- Copy an existing project to a new project and
30
- open it in your editor
31
- }.join(" "),
32
- delete: "Deletes given project",
33
- implode: "Deletes all tmuxinator projects",
36
+ stop_all: "Stop all tmux sessions which are using tmuxinator projects",
34
37
  version: "Display installed tmuxinator version",
35
- doctor: "Look for problems in your configuration",
36
- list: "Lists all tmuxinator projects"
37
38
  }.freeze
38
39
 
39
40
  # For future reference: due to how tmuxinator currently consumes
@@ -93,7 +94,7 @@ module Tmuxinator
93
94
 
94
95
  no_commands do
95
96
  def new_project(name)
96
- project_file = find_project_file(name, options[:local])
97
+ project_file = find_project_file(name, local: options[:local])
97
98
  Kernel.system("$EDITOR #{project_file}") || doctor
98
99
  end
99
100
 
@@ -145,14 +146,14 @@ module Tmuxinator
145
146
  end
146
147
  }
147
148
 
148
- path = config_path(name, options[:local])
149
+ path = config_path(name, local: options[:local])
149
150
  File.open(path, "w") do |f|
150
151
  f.write(YAML.dump(yaml))
151
152
  end
152
153
  end
153
154
 
154
- def find_project_file(name, local = false)
155
- path = config_path(name, local)
155
+ def find_project_file(name, local: false)
156
+ path = config_path(name, local: local)
156
157
  if File.exist?(path)
157
158
  path
158
159
  else
@@ -160,7 +161,7 @@ module Tmuxinator
160
161
  end
161
162
  end
162
163
 
163
- def config_path(name, local = false)
164
+ def config_path(name, local: false)
164
165
  if local
165
166
  Tmuxinator::Config::LOCAL_DEFAULTS[0]
166
167
  else
@@ -176,28 +177,22 @@ module Tmuxinator
176
177
  end
177
178
 
178
179
  def create_project(project_options = {})
179
- # Strings provided to --attach are coerced into booleans by Thor.
180
- # "f" and "false" will result in `:attach` being `false` and any other
181
- # string or the empty flag will result in `:attach` being `true`.
182
- # If the flag is not present, `:attach` will be `nil`.
183
- attach = detach = false
184
- attach = true if project_options[:attach] == true
185
- detach = true if project_options[:attach] == false
186
-
187
- options = {
180
+ Tmuxinator::Config.validate(project_create_options(project_options))
181
+ rescue StandardError => e
182
+ exit! e.message
183
+ end
184
+
185
+ def project_create_options(project_options)
186
+ {
188
187
  args: project_options[:args],
189
188
  custom_name: project_options[:custom_name],
190
- force_attach: attach,
191
- force_detach: detach,
189
+ force_attach: project_options[:attach] == true,
190
+ force_detach: project_options[:attach] == false,
192
191
  name: project_options[:name],
193
- project_config: project_options[:project_config]
192
+ project_config: project_options[:project_config],
193
+ append: project_options[:append],
194
+ no_pre_window: project_options[:no_pre_window],
194
195
  }
195
-
196
- begin
197
- Tmuxinator::Config.validate(options)
198
- rescue StandardError => e
199
- exit! e.message
200
- end
201
196
  end
202
197
 
203
198
  def render_project(project)
@@ -221,12 +216,31 @@ module Tmuxinator
221
216
  def show_continuation_prompt
222
217
  say
223
218
  print "Press ENTER to continue."
224
- STDIN.getc
219
+ $stdin.getc
225
220
  end
226
221
 
227
222
  def kill_project(project)
228
223
  Kernel.exec(project.kill)
229
224
  end
225
+
226
+ def start_params(name = nil, *args)
227
+ # project-config takes precedence over a named project in the case that
228
+ # both are provided.
229
+ if options["project-config"]
230
+ args.unshift name if name
231
+ name = nil
232
+ end
233
+
234
+ {
235
+ args: args,
236
+ attach: options[:attach],
237
+ custom_name: options[:name],
238
+ name: name,
239
+ project_config: options["project-config"],
240
+ append: options["append"],
241
+ no_pre_window: options["no-pre-window"],
242
+ }
243
+ end
230
244
  end
231
245
 
232
246
  desc "start [PROJECT] [ARGS]", COMMANDS[:start]
@@ -240,22 +254,13 @@ module Tmuxinator
240
254
  desc: "Path to project config file"
241
255
  method_option "suppress-tmux-version-warning",
242
256
  desc: "Don't show a warning for unsupported tmux versions"
243
-
257
+ method_option :append, type: :boolean,
258
+ desc: "Appends the project windows and panes in " \
259
+ "the current session"
260
+ method_option "no-pre-window", type: :boolean, default: false,
261
+ desc: "Skip pre_window commands"
244
262
  def start(name = nil, *args)
245
- # project-config takes precedence over a named project in the case that
246
- # both are provided.
247
- if options["project-config"]
248
- args.unshift name if name
249
- name = nil
250
- end
251
-
252
- params = {
253
- args: args,
254
- attach: options[:attach],
255
- custom_name: options[:name],
256
- name: name,
257
- project_config: options["project-config"]
258
- }
263
+ params = start_params(name, *args)
259
264
 
260
265
  show_version_warning if version_warning?(
261
266
  options["suppress-tmux-version-warning"]
@@ -291,6 +296,26 @@ module Tmuxinator
291
296
  kill_project(project)
292
297
  end
293
298
 
299
+ desc "stop-all", COMMANDS[:stop_all]
300
+ method_option :noconfirm, type: :boolean,
301
+ default: false,
302
+ aliases: "-y",
303
+ desc: "Skip confirmation"
304
+ def stop_all
305
+ # We only need to stop active projects
306
+ configs = Tmuxinator::Config.configs(active: true)
307
+
308
+ unless options[:noconfirm]
309
+ say "Stop all active projects:\n\n", :yellow
310
+ say configs.join("\n")
311
+ say "\n"
312
+
313
+ return unless yes?("Are you sure? (n/y)")
314
+ end
315
+
316
+ Project.stop_all
317
+ end
318
+
294
319
  desc "local", COMMANDS[:local]
295
320
  map "." => :local
296
321
  method_option "suppress-tmux-version-warning",
@@ -312,24 +337,16 @@ module Tmuxinator
312
337
  desc: "Give the session a different name"
313
338
  method_option "project-config", aliases: "-p",
314
339
  desc: "Path to project config file"
315
-
340
+ method_option :append, type: :boolean,
341
+ desc: "Appends the project windows and panes in " \
342
+ "the current session"
343
+ method_option "no-pre-window", type: :boolean, default: false,
344
+ desc: "Skip pre_window commands"
316
345
  def debug(name = nil, *args)
317
- # project-config takes precedence over a named project in the case that
318
- # both are provided.
319
- if options["project-config"]
320
- args.unshift name if name
321
- name = nil
322
- end
323
-
324
- params = {
325
- args: args,
326
- attach: options[:attach],
327
- custom_name: options[:name],
328
- name: name,
329
- project_config: options["project-config"]
330
- }
346
+ params = start_params(name, *args)
331
347
 
332
348
  project = create_project(params)
349
+
333
350
  say project.render
334
351
  end
335
352
 
@@ -439,7 +456,7 @@ module Tmuxinator
439
456
  Tmuxinator::Cli.new.local
440
457
  elsif name && !Tmuxinator::Cli::RESERVED_COMMANDS.include?(name) &&
441
458
  Tmuxinator::Config.exist?(name: name)
442
- Tmuxinator::Cli.new.start(name, *args.drop(1))
459
+ Tmuxinator::Cli.start([:start, *args])
443
460
  else
444
461
  Tmuxinator::Cli.start(args)
445
462
  end
@@ -20,7 +20,7 @@ module Tmuxinator
20
20
  end
21
21
 
22
22
  def home
23
- ENV["HOME"] + "/.tmuxinator"
23
+ "#{ENV['HOME']}/.tmuxinator"
24
24
  end
25
25
 
26
26
  def home?
@@ -31,7 +31,9 @@ module Tmuxinator
31
31
  # a custom value. (e.g. if $XDG_CONFIG_HOME is set to ~/my-config, the
32
32
  # return value will be ~/my-config/tmuxinator)
33
33
  def xdg
34
- XDG["CONFIG"].to_s + "/tmuxinator"
34
+ xdg_config_directory = ENV.fetch("XDG_CONFIG_HOME", "~/.config")
35
+ config_home = File.expand_path(xdg_config_directory)
36
+ File.join(config_home, "tmuxinator")
35
37
  end
36
38
 
37
39
  def xdg?
@@ -131,7 +133,7 @@ module Tmuxinator
131
133
  `tmux list-sessions -F "#S"`.split("\n")
132
134
  end
133
135
 
134
- # Sorted list of all project .yml file basenames, including duplicates
136
+ # Sorted list of all project file basenames, including duplicates.
135
137
  #
136
138
  # @param active filter configs by active project sessions
137
139
  # @return [Array<String>] list of project names
@@ -147,6 +149,16 @@ module Tmuxinator
147
149
  configs
148
150
  end
149
151
 
152
+ # List the names of all config files relative to the config directory.
153
+ #
154
+ # If sub-folders are used, those are part of the name too.
155
+ #
156
+ # Example:
157
+ # $CONFIG_DIR/project.yml -> project
158
+ # $CONFIG_DIR/sub/project.yml -> sub/project
159
+ # $HOME_CONFIG_DIR/project.yml -> project
160
+ #
161
+ # @return [Array<String] a list of config file names
150
162
  def config_file_basenames
151
163
  directories.flat_map do |directory|
152
164
  Dir["#{directory}/**/*.yml"].map do |path|
@@ -193,7 +205,7 @@ module Tmuxinator
193
205
  name = options[:name]
194
206
  options[:force_attach] ||= false
195
207
  options[:force_detach] ||= false
196
- project_config = options.fetch(:project_config) { false }
208
+ project_config = options.fetch(:project_config, false)
197
209
  project_file = if valid_project_config?(project_config)
198
210
  project_config
199
211
  elsif valid_local_project?(name)
@@ -36,36 +36,49 @@ module Tmuxinator
36
36
  `on_project_exit`) and will be removed in a future release.
37
37
  M
38
38
 
39
- attr_reader :yaml
40
- attr_reader :force_attach
41
- attr_reader :force_detach
42
- attr_reader :custom_name
43
-
44
- def self.load(path, options = {})
45
- yaml = begin
46
- args = options[:args] || []
47
- @settings = parse_settings(args)
48
- @args = args
49
-
50
- content = render_template(path, binding)
51
- YAML.safe_load(content, aliases: true)
52
- rescue SyntaxError, StandardError => error
53
- raise "Failed to parse config file: #{error.message}"
39
+ attr_reader :yaml, :force_attach, :force_detach, :custom_name,
40
+ :no_pre_window
41
+
42
+ class << self
43
+ include Tmuxinator::Util
44
+
45
+ def load(path, options = {})
46
+ yaml = begin
47
+ args = options[:args] || []
48
+ @settings = parse_settings(args)
49
+ @args = args
50
+
51
+ content = render_template(path, binding)
52
+ YAML.safe_load(content, aliases: true)
53
+ rescue SyntaxError, StandardError => e
54
+ raise "Failed to parse config file: #{e.message}"
55
+ end
56
+
57
+ new(yaml, options)
54
58
  end
55
59
 
56
- new(yaml, options)
57
- end
60
+ def parse_settings(args)
61
+ settings = args.select { |x| x.match(/.*=.*/) }
62
+ args.reject! { |x| x.match(/.*=.*/) }
63
+
64
+ settings.map! do |setting|
65
+ parts = setting.split("=", 2)
66
+ [parts[0], parts[1]]
67
+ end
58
68
 
59
- def self.parse_settings(args)
60
- settings = args.select { |x| x.match(/.*=.*/) }
61
- args.reject! { |x| x.match(/.*=.*/) }
69
+ Hash[settings]
70
+ end
62
71
 
63
- settings.map! do |setting|
64
- parts = setting.split("=")
65
- [parts[0], parts[1]]
72
+ def active
73
+ Tmuxinator::Config.configs(active: true).
74
+ map { |config| Config.validate(name: config) }
66
75
  end
67
76
 
68
- Hash[settings]
77
+ def stop_all
78
+ active.
79
+ sort_by { |project| project.name == current_session_name ? 1 : 0 }.
80
+ each { |project| Kernel.system(project.kill) }
81
+ end
69
82
  end
70
83
 
71
84
  def validate!
@@ -83,15 +96,29 @@ module Tmuxinator
83
96
 
84
97
  @yaml = yaml
85
98
 
86
- @custom_name = options[:custom_name]
99
+ set_instance_variables_from_options(options)
87
100
 
101
+ validate_options
102
+
103
+ extend Tmuxinator::WemuxSupport if wemux?
104
+ end
105
+
106
+ def set_instance_variables_from_options(options)
107
+ @custom_name = options[:custom_name]
88
108
  @force_attach = options[:force_attach]
89
109
  @force_detach = options[:force_detach]
110
+ @append = options[:append]
111
+ @no_pre_window = options[:no_pre_window]
112
+ end
90
113
 
91
- raise "Cannot force_attach and force_detach at the same time" \
92
- if @force_attach && @force_detach
114
+ def validate_options
115
+ if @force_attach && @force_detach
116
+ raise "Cannot force_attach and force_detach at the same time"
117
+ end
93
118
 
94
- extend Tmuxinator::WemuxSupport if wemux?
119
+ if append? && !tmux_has_session?(name)
120
+ raise "Cannot append to a session that does not exist"
121
+ end
95
122
  end
96
123
 
97
124
  def render
@@ -121,26 +148,33 @@ module Tmuxinator
121
148
  end
122
149
 
123
150
  def name
124
- name = custom_name || yaml["project_name"] || yaml["name"]
151
+ name =
152
+ if append?
153
+ current_session_name
154
+ else
155
+ custom_name || yaml["project_name"] || yaml["name"]
156
+ end
157
+
125
158
  blank?(name) ? nil : name.to_s.shellescape
126
159
  end
127
160
 
161
+ def append?
162
+ @append
163
+ end
164
+
128
165
  def pre
129
166
  pre_config = yaml["pre"]
130
167
  parsed_parameters(pre_config)
131
168
  end
132
169
 
133
170
  def attach?
134
- yaml_attach = if yaml["attach"].nil?
135
- true
136
- else
137
- yaml["attach"]
138
- end
139
- attach = force_attach || !force_detach && yaml_attach
140
- attach
171
+ yaml_attach = yaml["attach"].nil? || yaml["attach"]
172
+ force_attach || !force_detach && yaml_attach
141
173
  end
142
174
 
143
175
  def pre_window
176
+ return if no_pre_window
177
+
144
178
  params = if rbenv?
145
179
  "rbenv shell #{yaml['rbenv']}"
146
180
  elsif rvm?
@@ -167,6 +201,8 @@ module Tmuxinator
167
201
  end
168
202
 
169
203
  def tmux_has_session?(name)
204
+ return false unless name
205
+
170
206
  # Redirect stderr to /dev/null in order to prevent "failed to connect
171
207
  # to server: Connection refused" error message and non-zero exit status
172
208
  # if no tmux sessions exist.
@@ -208,7 +244,13 @@ module Tmuxinator
208
244
  end
209
245
  end
210
246
 
247
+ def last_window_index
248
+ `tmux list-windows -F '#I'`.split.last.to_i
249
+ end
250
+
211
251
  def base_index
252
+ return last_window_index + 1 if append?
253
+
212
254
  get_base_index.to_i
213
255
  end
214
256
 
@@ -241,7 +283,7 @@ module Tmuxinator
241
283
  end
242
284
 
243
285
  def window(index)
244
- "#{name}:#{index}"
286
+ append? ? ":#{index}" : "#{name}:#{index}"
245
287
  end
246
288
 
247
289
  def send_pane_command(cmd, window_index, _pane_index)
@@ -329,6 +371,8 @@ module Tmuxinator
329
371
  end
330
372
 
331
373
  def tmux_new_session_command
374
+ return if append?
375
+
332
376
  window = windows.first.tmux_window_name_option
333
377
  "#{tmux} new-session -d -s #{name} #{window}"
334
378
  end
@@ -361,17 +405,16 @@ module Tmuxinator
361
405
 
362
406
  def pane_title_position_not_valid_warning
363
407
  print_warning(
364
- "The specified pane title position " +
365
- "\"#{yaml['pane_title_position']}\" is not valid. " +
366
- "Please choose one of: top, bottom, or off."
408
+ "The specified pane title position '#{yaml['pane_title_position']}' " \
409
+ "is not valid. Please choose one of: top, bottom, or off."
367
410
  )
368
411
  end
369
412
 
370
413
  def pane_titles_not_supported_warning
371
414
  print_warning(
372
- "You have enabled pane titles in your configuration, " +
373
- "but the feature is not supported by your version of tmux.\n" +
374
- "Please consider upgrading to a version that supports it (tmux >=2.6)."
415
+ "You have enabled pane titles in your configuration, but the " \
416
+ "feature is not supported by your version of tmux.\nPlease consider " \
417
+ "upgrading to a version that supports it (tmux >=2.6)."
375
418
  )
376
419
  end
377
420
 
@@ -12,5 +12,9 @@ module Tmuxinator
12
12
  def yes_no(condition)
13
13
  condition ? say("Yes", :green) : say("No", :red)
14
14
  end
15
+
16
+ def current_session_name
17
+ `[[ -n "${TMUX+set}" ]] && tmux display-message -p "#S"`.strip
18
+ end
15
19
  end
16
20
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Tmuxinator
4
- VERSION = "3.3.3"
4
+ VERSION = "3.3.5"
5
5
  end
@@ -29,7 +29,7 @@ module Tmuxinator
29
29
  end
30
30
 
31
31
  def layout
32
- yaml["layout"] ? yaml["layout"].shellescape : nil
32
+ yaml["layout"]&.shellescape
33
33
  end
34
34
 
35
35
  def synchronize
@@ -139,7 +139,7 @@ module Tmuxinator
139
139
  end
140
140
 
141
141
  def synchronize_before?
142
- synchronize == true || synchronize == "before"
142
+ [true, "before"].include?(synchronize)
143
143
  end
144
144
 
145
145
  def synchronize_after?
data/lib/tmuxinator.rb CHANGED
@@ -5,7 +5,6 @@ require "fileutils"
5
5
  require "shellwords"
6
6
  require "thor"
7
7
  require "thor/version"
8
- require "xdg"
9
8
  require "yaml"
10
9
 
11
10
  module Tmuxinator
@@ -115,4 +115,12 @@ FactoryBot.define do
115
115
 
116
116
  initialize_with { Tmuxinator::Project.load(file) }
117
117
  end
118
+
119
+ factory :project_with_append, class: Tmuxinator::Project do
120
+ transient do
121
+ file { "spec/fixtures/sample.yml" }
122
+ end
123
+
124
+ initialize_with { Tmuxinator::Project.load(file, append: true) }
125
+ end
118
126
  end
@@ -99,11 +99,18 @@ describe Tmuxinator::Cli do
99
99
  end
100
100
 
101
101
  it "should call #start" do
102
- instance = instance_double(cli)
103
- expect(cli).to receive(:new).and_return(instance)
104
- expect(instance).to receive(:start).with(*args)
102
+ expect(cli).to receive(:start).with([:start, *args])
105
103
  subject
106
104
  end
105
+
106
+ context "and the append option is passed" do
107
+ let(:args) { ["sample", "--append"] }
108
+
109
+ it "should call #start" do
110
+ expect(cli).to receive(:start).with([:start, *args])
111
+ subject
112
+ end
113
+ end
107
114
  end
108
115
 
109
116
  context "a thor command" do
@@ -191,21 +198,24 @@ describe Tmuxinator::Cli do
191
198
 
192
199
  it "lists the commands" do
193
200
  out, _err = capture_io { cli.start }
194
- expected = %w(commands
195
- completions
196
- new
197
- edit
198
- open
199
- start
200
- stop
201
- local
202
- debug
203
- copy
204
- delete
205
- implode
206
- version
207
- doctor
208
- list)
201
+ expected = %w(
202
+ commands
203
+ completions
204
+ copy
205
+ debug
206
+ delete
207
+ doctor
208
+ edit
209
+ implode
210
+ local
211
+ list
212
+ new
213
+ open
214
+ start
215
+ stop
216
+ stop_all
217
+ version
218
+ )
209
219
  expect(out).to eq "#{expected.join("\n")}\n"
210
220
  end
211
221
  end
@@ -372,6 +382,20 @@ describe Tmuxinator::Cli do
372
382
  end
373
383
  end
374
384
 
385
+ describe "#stop_all" do
386
+ before do
387
+ allow(Tmuxinator::Config).to receive_messages(validate: project)
388
+ allow(Tmuxinator::Config).to receive_messages(version: 1.9)
389
+ end
390
+
391
+ it "stops all projects" do
392
+ ARGV.replace(["stop-all", "--noconfirm"])
393
+ out, err = capture_io { cli.start }
394
+ expect(err).to eq ""
395
+ expect(out).to eq ""
396
+ end
397
+ end
398
+
375
399
  describe "#local" do
376
400
  before do
377
401
  allow(Tmuxinator::Config).to receive_messages(validate: project)
@@ -900,7 +924,7 @@ describe Tmuxinator::Cli do
900
924
  end
901
925
 
902
926
  it "should generate a project file" do
903
- new_path = described_class.new.find_project_file(name, false)
927
+ new_path = described_class.new.find_project_file(name, local: false)
904
928
  expect(new_path).to eq path
905
929
  expect(File).to exist new_path
906
930
  end
@@ -922,7 +946,7 @@ describe Tmuxinator::Cli do
922
946
  end
923
947
 
924
948
  it "should _not_ generate a new project file" do
925
- new_path = described_class.new.find_project_file(name, false)
949
+ new_path = described_class.new.find_project_file(name, local: false)
926
950
  expect(new_path).to eq path
927
951
  expect(File).to exist new_path
928
952
  expect(File.read(new_path)).to match %r{#{extra}}
@@ -55,7 +55,8 @@ describe Tmuxinator::Config do
55
55
 
56
56
  Dir.mktmpdir do |dir|
57
57
  config_parent = "#{dir}/non_existent_parent/s"
58
- allow(XDG).to receive(:[]).with("CONFIG").and_return config_parent
58
+ allow(ENV).to receive(:fetch).with("XDG_CONFIG_HOME", "~/.config").
59
+ and_return config_parent
59
60
  expect(described_class.directory).
60
61
  to eq "#{config_parent}/tmuxinator"
61
62
  expect(File.directory?("#{config_parent}/tmuxinator")).to be true
@@ -69,7 +70,6 @@ describe Tmuxinator::Config do
69
70
  it "is $TMUXINATOR_CONFIG" do
70
71
  allow(ENV).to receive(:[]).with("TMUXINATOR_CONFIG").
71
72
  and_return "expected"
72
- # allow(XDG).to receive(:[]).with("CONFIG").and_return "expected"
73
73
  allow(File).to receive(:directory?).and_return true
74
74
  expect(described_class.environment).to eq "expected"
75
75
  end
@@ -87,7 +87,6 @@ describe Tmuxinator::Config do
87
87
 
88
88
  context "environment variable $TMUXINATOR_CONFIG is set and empty" do
89
89
  it "is an empty string" do
90
- allow(XDG).to receive(:[]).with("CONFIG").and_return ""
91
90
  allow(ENV).to receive(:[]).with("TMUXINATOR_CONFIG").and_return ""
92
91
  expect(described_class.environment).to eq ""
93
92
  end
@@ -136,7 +135,7 @@ describe Tmuxinator::Config do
136
135
 
137
136
  describe "#xdg" do
138
137
  it "is $XDG_CONFIG_HOME/tmuxinator" do
139
- expect(described_class.xdg).to eq "#{XDG['CONFIG_HOME']}/tmuxinator"
138
+ expect(described_class.xdg).to eq File.expand_path("~/.config/tmuxinator")
140
139
  end
141
140
  end
142
141
 
@@ -181,7 +180,8 @@ describe Tmuxinator::Config do
181
180
 
182
181
  before do
183
182
  expect(Tmuxinator::Doctor).to receive(:installed?).and_return(true)
184
- allow_any_instance_of(Kernel).to receive(:`).with(/tmux\s\-V/).
183
+ allow_any_instance_of(Kernel).to receive(:`).
184
+ with(/tmux\s-V/).
185
185
  and_return("tmux #{version}")
186
186
  end
187
187
 
@@ -37,6 +37,10 @@ describe Tmuxinator::Project do
37
37
  FactoryBot.build(:project_with_alias)
38
38
  end
39
39
 
40
+ let(:project_with_append) do
41
+ FactoryBot.build(:project_with_append)
42
+ end
43
+
40
44
  it "should include Hooks" do
41
45
  expect(project).to be_kind_of(Tmuxinator::Hooks::Project)
42
46
  end
@@ -46,6 +50,21 @@ describe Tmuxinator::Project do
46
50
  it "creates an instance" do
47
51
  expect(project).to be_a(Tmuxinator::Project)
48
52
  end
53
+
54
+ it "validates append outside a current session" do
55
+ Tmuxinator::Project.any_instance.stub(tmux_has_session?: false)
56
+ expect { project_with_append }.to(
57
+ raise_error(
58
+ RuntimeError,
59
+ "Cannot append to a session that does not exist"
60
+ )
61
+ )
62
+ end
63
+
64
+ it "validates append within a current session" do
65
+ Tmuxinator::Project.any_instance.stub(tmux_has_session?: true)
66
+ expect { project_with_append }.to_not raise_error
67
+ end
49
68
  end
50
69
  end
51
70
 
@@ -262,6 +281,16 @@ describe Tmuxinator::Project do
262
281
  end
263
282
  end
264
283
  end
284
+
285
+ context "no_pre_window option is true" do
286
+ before do
287
+ allow(project).to receive_messages(no_pre_window: true)
288
+ end
289
+
290
+ it "returns nil" do
291
+ expect(pre_window).to be_nil
292
+ end
293
+ end
265
294
  end
266
295
 
267
296
  describe "#socket" do
@@ -368,6 +397,17 @@ describe Tmuxinator::Project do
368
397
  expect(project.base_index).to eq 0
369
398
  end
370
399
  end
400
+
401
+ context "with append set" do
402
+ before do
403
+ Tmuxinator::Project.any_instance.stub(tmux_has_session?: true)
404
+ project_with_append.stub(last_window_index: 3)
405
+ end
406
+
407
+ it "defaults to the next window index" do
408
+ expect(project_with_append.base_index).to eq 4
409
+ end
410
+ end
371
411
  end
372
412
 
373
413
  describe "#startup_window" do
@@ -410,6 +450,13 @@ describe Tmuxinator::Project do
410
450
  it "gets the window and index for tmux" do
411
451
  expect(project.window(1)).to eq "sample:1"
412
452
  end
453
+
454
+ context "with append set" do
455
+ it "excludes the session name" do
456
+ Tmuxinator::Project.any_instance.stub(tmux_has_session?: true)
457
+ expect(project_with_append.window(1)).to eq ":1"
458
+ end
459
+ end
413
460
  end
414
461
 
415
462
  describe "#name?" do
@@ -576,6 +623,13 @@ describe Tmuxinator::Project do
576
623
  expect(project.tmux_new_session_command).to eq command
577
624
  end
578
625
  end
626
+
627
+ context "with append set" do
628
+ it "returns nothing" do
629
+ Tmuxinator::Project.any_instance.stub(tmux_has_session?: true)
630
+ expect(project_with_append.tmux_new_session_command).to be_nil
631
+ end
632
+ end
579
633
  end
580
634
 
581
635
  describe "tmux_kill_session_command" do
@@ -611,7 +665,7 @@ describe Tmuxinator::Project do
611
665
  end
612
666
 
613
667
  describe "::parse_settings" do
614
- let(:args) { ["one", "two=three"] }
668
+ let(:args) { ["one", "two=three", "four=five=six"] }
615
669
 
616
670
  it "returns settings in a hash" do
617
671
  expect(described_class.parse_settings(args)["two"]).to eq("three")
@@ -621,6 +675,10 @@ describe Tmuxinator::Project do
621
675
  described_class.parse_settings(args)
622
676
  expect(args).to eq(["one"])
623
677
  end
678
+
679
+ it "handles equals signs in values" do
680
+ expect(described_class.parse_settings(args)["four"]).to eq("five=six")
681
+ end
624
682
  end
625
683
 
626
684
  describe "#validate!" do
@@ -12,7 +12,7 @@ describe Tmuxinator::WemuxSupport do
12
12
  it "renders the template" do
13
13
  expect(File).to receive(:read).at_least(:once) { "wemux ls 2>/dev/null" }
14
14
 
15
- expect(instance.render).to match %r{wemux.ls.2>\/dev\/null}
15
+ expect(instance.render).to match %r{wemux.ls.2>/dev/null}
16
16
  end
17
17
  end
18
18
 
data/spec/spec_helper.rb CHANGED
@@ -1,16 +1,20 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "coveralls"
4
3
  require "pry"
5
4
  require "simplecov"
6
- require "xdg"
7
5
 
8
- formatters = [
9
- SimpleCov::Formatter::HTMLFormatter,
10
- Coveralls::SimpleCov::Formatter
11
- ]
12
- SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter.new(formatters)
13
6
  SimpleCov.start do
7
+ if ENV["CI"]
8
+ formatter SimpleCov::Formatter::SimpleFormatter
9
+ else
10
+ formatter SimpleCov::Formatter::MultiFormatter.new(
11
+ [
12
+ SimpleCov::Formatter::HTMLFormatter,
13
+ SimpleCov::Formatter::SimpleFormatter,
14
+ ]
15
+ )
16
+ end
17
+
14
18
  add_filter "vendor/cache"
15
19
  end
16
20
 
@@ -54,11 +58,11 @@ def tmux_config(options = {})
54
58
  "bell-on-alert off",
55
59
  ]
56
60
 
57
- if base_index = options.fetch(:base_index) { 1 }
61
+ if base_index = options.fetch(:base_index, 1)
58
62
  standard_options << "base-index #{base_index}"
59
63
  end
60
64
 
61
- if pane_base_index = options.fetch(:pane_base_index) { 1 }
65
+ if pane_base_index = options.fetch(:pane_base_index, 1)
62
66
  standard_options << "pane-base-index #{pane_base_index}"
63
67
  end
64
68
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tmuxinator
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.3.3
4
+ version: 3.3.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Allen Bargi
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2024-11-19 00:00:00.000000000 Z
12
+ date: 2025-08-02 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: erubi
@@ -17,62 +17,42 @@ dependencies:
17
17
  requirements:
18
18
  - - "~>"
19
19
  - !ruby/object:Gem::Version
20
- version: '1.7'
20
+ version: '1.13'
21
21
  type: :runtime
22
22
  prerelease: false
23
23
  version_requirements: !ruby/object:Gem::Requirement
24
24
  requirements:
25
25
  - - "~>"
26
26
  - !ruby/object:Gem::Version
27
- version: '1.7'
27
+ version: '1.13'
28
28
  - !ruby/object:Gem::Dependency
29
29
  name: thor
30
30
  requirement: !ruby/object:Gem::Requirement
31
31
  requirements:
32
32
  - - "~>"
33
33
  - !ruby/object:Gem::Version
34
- version: 1.3.0
34
+ version: 1.4.0
35
35
  type: :runtime
36
36
  prerelease: false
37
37
  version_requirements: !ruby/object:Gem::Requirement
38
38
  requirements:
39
39
  - - "~>"
40
40
  - !ruby/object:Gem::Version
41
- version: 1.3.0
41
+ version: 1.4.0
42
42
  - !ruby/object:Gem::Dependency
43
- name: xdg
43
+ name: amazing_print
44
44
  requirement: !ruby/object:Gem::Requirement
45
45
  requirements:
46
46
  - - "~>"
47
47
  - !ruby/object:Gem::Version
48
- version: '2.2'
49
- - - ">="
50
- - !ruby/object:Gem::Version
51
- version: 2.2.5
52
- type: :runtime
53
- prerelease: false
54
- version_requirements: !ruby/object:Gem::Requirement
55
- requirements:
56
- - - "~>"
57
- - !ruby/object:Gem::Version
58
- version: '2.2'
59
- - - ">="
60
- - !ruby/object:Gem::Version
61
- version: 2.2.5
62
- - !ruby/object:Gem::Dependency
63
- name: awesome_print
64
- requirement: !ruby/object:Gem::Requirement
65
- requirements:
66
- - - "~>"
67
- - !ruby/object:Gem::Version
68
- version: '1.2'
48
+ version: '1.8'
69
49
  type: :development
70
50
  prerelease: false
71
51
  version_requirements: !ruby/object:Gem::Requirement
72
52
  requirements:
73
53
  - - "~>"
74
54
  - !ruby/object:Gem::Version
75
- version: '1.2'
55
+ version: '1.8'
76
56
  - !ruby/object:Gem::Dependency
77
57
  name: bundler
78
58
  requirement: !ruby/object:Gem::Requirement
@@ -87,118 +67,90 @@ dependencies:
87
67
  - - ">="
88
68
  - !ruby/object:Gem::Version
89
69
  version: '1.3'
90
- - !ruby/object:Gem::Dependency
91
- name: coveralls
92
- requirement: !ruby/object:Gem::Requirement
93
- requirements:
94
- - - "~>"
95
- - !ruby/object:Gem::Version
96
- version: '0.8'
97
- type: :development
98
- prerelease: false
99
- version_requirements: !ruby/object:Gem::Requirement
100
- requirements:
101
- - - "~>"
102
- - !ruby/object:Gem::Version
103
- version: '0.8'
104
70
  - !ruby/object:Gem::Dependency
105
71
  name: factory_bot
106
72
  requirement: !ruby/object:Gem::Requirement
107
73
  requirements:
108
74
  - - "~>"
109
75
  - !ruby/object:Gem::Version
110
- version: '4.8'
76
+ version: '6.5'
111
77
  type: :development
112
78
  prerelease: false
113
79
  version_requirements: !ruby/object:Gem::Requirement
114
80
  requirements:
115
81
  - - "~>"
116
82
  - !ruby/object:Gem::Version
117
- version: '4.8'
83
+ version: '6.5'
118
84
  - !ruby/object:Gem::Dependency
119
85
  name: pry
120
86
  requirement: !ruby/object:Gem::Requirement
121
87
  requirements:
122
88
  - - "~>"
123
89
  - !ruby/object:Gem::Version
124
- version: '0.10'
90
+ version: '0.15'
125
91
  type: :development
126
92
  prerelease: false
127
93
  version_requirements: !ruby/object:Gem::Requirement
128
94
  requirements:
129
95
  - - "~>"
130
96
  - !ruby/object:Gem::Version
131
- version: '0.10'
97
+ version: '0.15'
132
98
  - !ruby/object:Gem::Dependency
133
99
  name: rake
134
100
  requirement: !ruby/object:Gem::Requirement
135
101
  requirements:
136
102
  - - "~>"
137
103
  - !ruby/object:Gem::Version
138
- version: 12.3.3
104
+ version: '13.3'
139
105
  type: :development
140
106
  prerelease: false
141
107
  version_requirements: !ruby/object:Gem::Requirement
142
108
  requirements:
143
109
  - - "~>"
144
110
  - !ruby/object:Gem::Version
145
- version: 12.3.3
111
+ version: '13.3'
146
112
  - !ruby/object:Gem::Dependency
147
113
  name: rspec
148
114
  requirement: !ruby/object:Gem::Requirement
149
115
  requirements:
150
116
  - - "~>"
151
117
  - !ruby/object:Gem::Version
152
- version: '3.3'
118
+ version: '3.13'
153
119
  type: :development
154
120
  prerelease: false
155
121
  version_requirements: !ruby/object:Gem::Requirement
156
122
  requirements:
157
123
  - - "~>"
158
124
  - !ruby/object:Gem::Version
159
- version: '3.3'
125
+ version: '3.13'
160
126
  - !ruby/object:Gem::Dependency
161
127
  name: rubocop
162
128
  requirement: !ruby/object:Gem::Requirement
163
129
  requirements:
164
130
  - - "~>"
165
131
  - !ruby/object:Gem::Version
166
- version: 0.61.1
132
+ version: '1.79'
167
133
  type: :development
168
134
  prerelease: false
169
135
  version_requirements: !ruby/object:Gem::Requirement
170
136
  requirements:
171
137
  - - "~>"
172
138
  - !ruby/object:Gem::Version
173
- version: 0.61.1
139
+ version: '1.79'
174
140
  - !ruby/object:Gem::Dependency
175
141
  name: simplecov
176
142
  requirement: !ruby/object:Gem::Requirement
177
143
  requirements:
178
144
  - - "~>"
179
145
  - !ruby/object:Gem::Version
180
- version: '0.16'
146
+ version: '0.22'
181
147
  type: :development
182
148
  prerelease: false
183
149
  version_requirements: !ruby/object:Gem::Requirement
184
150
  requirements:
185
151
  - - "~>"
186
152
  - !ruby/object:Gem::Version
187
- version: '0.16'
188
- - !ruby/object:Gem::Dependency
189
- name: unicode-display_width
190
- requirement: !ruby/object:Gem::Requirement
191
- requirements:
192
- - - "~>"
193
- - !ruby/object:Gem::Version
194
- version: '1.3'
195
- type: :development
196
- prerelease: false
197
- version_requirements: !ruby/object:Gem::Requirement
198
- requirements:
199
- - - "~>"
200
- - !ruby/object:Gem::Version
201
- version: '1.3'
153
+ version: '0.22'
202
154
  description: Create and manage complex tmux sessions easily.
203
155
  email:
204
156
  - allen.bargi@gmail.com
@@ -281,14 +233,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
281
233
  requirements:
282
234
  - - ">="
283
235
  - !ruby/object:Gem::Version
284
- version: 2.6.7
236
+ version: '2.7'
285
237
  required_rubygems_version: !ruby/object:Gem::Requirement
286
238
  requirements:
287
239
  - - ">="
288
240
  - !ruby/object:Gem::Version
289
- version: 1.8.23
241
+ version: '2.7'
290
242
  requirements: []
291
- rubygems_version: 3.5.11
243
+ rubygems_version: 3.5.22
292
244
  signing_key:
293
245
  specification_version: 4
294
246
  summary: Create and manage complex tmux sessions easily.