tmuxinator 3.3.8 → 3.4.0

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: 52752c91945099b9d08880c8dc405cd68a68497a9be91c3fda3cb6647730fc51
4
- data.tar.gz: 2f05391d0c2fd4a2bddc49740744a8102e67a88dd80fe7cd68ec1d123f678f01
3
+ metadata.gz: 322e55351978533f2dcca159c0f3cdd068baa6ff6a74105c26ef4ff788ecd033
4
+ data.tar.gz: 64aa7c6fb70e76019575e090e6f56b43be3b88e64e6efb3f7a91a94f0ba96963
5
5
  SHA512:
6
- metadata.gz: befa8c88430163f9912bcea81c4dbcbefb6d29d05ce38c9f7799b17d065cc47b70abd08fe7056aa1d3d291152d5c8fedccbe3e458bcb1697b225720b03a44ad8
7
- data.tar.gz: 6b966f0247e33fde76cb856fd4ebaa3840e7da66ef39e4e48f6d710ffe9d6cc7c60beb493e7f573d9d52b48c9213cca6723a3f602dfbfa8f9811c4d629e94503
6
+ metadata.gz: 78c824a5705cc25a9cb7bb013053006c2cdf943b5b158e1431e44b5fe6ddd75db31efca58c6ec5c693f919c2d1b21f4fba7728ed7e260f6452849f4de5d3e5bb
7
+ data.tar.gz: 5530202ccef42f13efce8b6082d1af63d80365896251f018f118437746e01278bf8080634a7996e86c67e1ab8280c7d6be8f4f36e0ca0ea717ccda2b9d0cfabc
@@ -38,7 +38,7 @@ root: ~/
38
38
  # Specifies (by name or index) which window will be selected on project startup. If not set, the first window is used.
39
39
  # startup_window: editor
40
40
 
41
- # Specifies (by index) which pane of the specified window will be selected on project startup. If not set, the first pane is used.
41
+ # Specifies (by index) which pane of the startup window will be selected on project startup. If not set, the first pane is used.
42
42
  # startup_pane: 1
43
43
 
44
44
  # Controls whether the tmux session should be attached to automatically. Defaults to true.
@@ -47,11 +47,17 @@ root: ~/
47
47
  windows:
48
48
  - editor:
49
49
  layout: main-vertical
50
+ panes:
51
+ - editor: vim
52
+ - guard
53
+ # Focus a pane in this window by name or index. Indices are based off zero and automatically adjusted to your pane-base-index.
54
+ # This affects the window's selected pane during setup; `startup_window` and `startup_pane`
55
+ # still control the final window/pane selected when the project starts.
56
+ # focused_pane: editor
57
+
50
58
  # Synchronize all panes of this window, can be enabled before or after the pane commands run.
51
59
  # 'before' represents legacy functionality and will be deprecated in a future release, in favour of 'after'
60
+ #
52
61
  # synchronize: after
53
- panes:
54
- - vim
55
- - guard
56
62
  - server: bundle exec rails s
57
63
  - logs: tail -f log/development.log
@@ -1,5 +1,6 @@
1
1
  #!<%= ENV["SHELL"] || "/bin/bash" %>
2
2
 
3
+
3
4
  <%- if !append? -%>
4
5
  # Clear rbenv variables before starting tmux
5
6
  unset RBENV_VERSION
@@ -85,7 +86,7 @@ cd <%= root || "." %>
85
86
  <% end %>
86
87
 
87
88
  <%= window.tmux_layout_command %>
88
- <%= window.tmux_select_first_pane %>
89
+ <%= window.tmux_focus_pane_command %>
89
90
  <% end %>
90
91
 
91
92
  <% if window.synchronize_after? %>
@@ -94,7 +95,7 @@ cd <%= root || "." %>
94
95
  <% end %>
95
96
 
96
97
  <%= tmux %> select-window -t <%= startup_window %>
97
- <%= tmux %> select-pane -t <%= startup_pane %>
98
+ <%= tmux_startup_pane_command %>
98
99
  <%- else -%>
99
100
  # Run on_project_restart command.
100
101
  <%= hook_on_project_restart %>
@@ -45,7 +45,7 @@ if [ "$?" -eq 127 ]; then
45
45
  <%= window.tmux_layout_command %>
46
46
  <%- end -%>
47
47
 
48
- <%= window.tmux_select_first_pane %>
48
+ <%= window.tmux_focus_pane_command %>
49
49
  <%- end -%>
50
50
 
51
51
  <%- if window.synchronize_after? -%>
@@ -54,7 +54,7 @@ if [ "$?" -eq 127 ]; then
54
54
  <%- end -%>
55
55
 
56
56
  <%= tmux %> select-window -t <%= startup_window %>
57
- <%= tmux %> select-pane -t <%= startup_pane %>
57
+ <%= tmux_startup_pane_command %>
58
58
  fi
59
59
 
60
60
  wemux attach
@@ -22,13 +22,13 @@ module Tmuxinator
22
22
  debug: "Output the shell commands that are generated by tmuxinator",
23
23
  delete: "Deletes given project",
24
24
  doctor: "Look for problems in your configuration",
25
- edit: "Alias of new",
25
+ edit: "Edit an existing project file in your editor",
26
26
  help: "Shows help for a specific command",
27
27
  implode: "Deletes all tmuxinator projects",
28
28
  local: "Start a tmux session using ./.tmuxinator.y[a]ml",
29
29
  list: "Lists all tmuxinator projects",
30
30
  new: "Create a new project file and open it in your editor",
31
- open: "Alias of new",
31
+ open: "Create or open a project file in your editor",
32
32
  start: %w{
33
33
  Start a tmux session using a project's name (with an optional [ALIAS]
34
34
  for project reuse) or a path to a project config file (via the -p flag)
@@ -76,14 +76,10 @@ module Tmuxinator
76
76
  end
77
77
 
78
78
  desc "new [PROJECT] [SESSION]", COMMANDS[:new]
79
- map "open" => :new
80
- map "edit" => :new
81
- map "o" => :new
82
- map "e" => :new
83
79
  map "n" => :new
84
80
  method_option :local, type: :boolean,
85
81
  aliases: ["-l"],
86
- desc: "Create local project file at ./.tmuxinator.yml"
82
+ desc: "Use local project file at ./.tmuxinator.yml"
87
83
  method_option :help, type: :boolean,
88
84
  aliases: ["-h"],
89
85
  desc: "Display usage information"
@@ -101,6 +97,49 @@ module Tmuxinator
101
97
  end
102
98
  end
103
99
 
100
+ desc "open [PROJECT]", COMMANDS[:open]
101
+ map "o" => :open
102
+ method_option :local, type: :boolean,
103
+ aliases: ["-l"],
104
+ desc: "Use local project file at ./.tmuxinator.yml"
105
+ method_option :help, type: :boolean,
106
+ aliases: ["-h"],
107
+ desc: "Display usage information"
108
+
109
+ def open(name = nil)
110
+ if options[:help] || name.nil?
111
+ invoke :help, ["open"]
112
+ return
113
+ end
114
+
115
+ new_project(name)
116
+ end
117
+
118
+ desc "edit [PROJECT]", COMMANDS[:edit]
119
+ map "e" => :edit
120
+ method_option :local, type: :boolean,
121
+ aliases: ["-l"],
122
+ desc: "Use local project file at ./.tmuxinator.yml"
123
+ method_option :help, type: :boolean,
124
+ aliases: ["-h"],
125
+ desc: "Display usage information"
126
+
127
+ def edit(name = nil)
128
+ if options[:help] || (name.nil? && !options[:local])
129
+ invoke :help, ["edit"]
130
+ return
131
+ end
132
+
133
+ path = config_path(name, local: options[:local])
134
+ if File.exist?(path)
135
+ Kernel.system("$EDITOR #{path}") || doctor
136
+ elsif options[:local] && name.nil?
137
+ exit! Tmuxinator::Config::NO_LOCAL_FILE_MSG
138
+ else
139
+ exit! "Project #{name} doesn't exist!"
140
+ end
141
+ end
142
+
104
143
  no_commands do
105
144
  def new_project(name)
106
145
  project_file = find_project_file(name, local: options[:local])
@@ -155,7 +155,7 @@ module Tmuxinator
155
155
  custom_name || yaml["project_name"] || yaml["name"]
156
156
  end
157
157
 
158
- blank?(name) ? nil : name.to_s.shellescape
158
+ blank?(name) ? nil : name.to_s.tr(".:", "__").shellescape
159
159
  end
160
160
 
161
161
  def append?
@@ -200,6 +200,10 @@ module Tmuxinator
200
200
  yaml["tmux_command"] || "tmux"
201
201
  end
202
202
 
203
+ def tmux_startup_pane_command
204
+ "#{tmux} select-pane -t #{startup_pane}"
205
+ end
206
+
203
207
  def tmux_has_session?(name)
204
208
  return false unless name
205
209
 
@@ -263,7 +267,14 @@ module Tmuxinator
263
267
  end
264
268
 
265
269
  def startup_pane
266
- "#{startup_window}.#{yaml['startup_pane'] || pane_base_index}"
270
+ pane =
271
+ if blank?(yaml["startup_pane"])
272
+ pane_base_index
273
+ else
274
+ yaml["startup_pane"]
275
+ end
276
+
277
+ "#{startup_window}.#{pane}"
267
278
  end
268
279
 
269
280
  def tmux_options?
@@ -3,6 +3,7 @@
3
3
  module Tmuxinator
4
4
  module TmuxVersion
5
5
  SUPPORTED_TMUX_VERSIONS = [
6
+ "3.6b",
6
7
  "3.6a",
7
8
  3.6,
8
9
  "3.5a",
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Tmuxinator
4
- VERSION = "3.3.8"
4
+ VERSION = "3.4.0"
5
5
  end
@@ -17,7 +17,7 @@ module Tmuxinator
17
17
  end
18
18
 
19
19
  def panes
20
- build_panes(yaml["panes"]) || []
20
+ @panes ||= build_panes(yaml["panes"]) || []
21
21
  end
22
22
 
23
23
  def _hashed?
@@ -126,6 +126,10 @@ module Tmuxinator
126
126
  "#{project.tmux} select-layout -t #{tmux_window_target} tiled"
127
127
  end
128
128
 
129
+ def tmux_focus_pane_command
130
+ "#{project.tmux} select-pane -t #{focused_pane}"
131
+ end
132
+
129
133
  def tmux_synchronize_panes
130
134
  "#{project.tmux} set-window-option -t #{tmux_window_target} synchronize-panes on"
131
135
  end
@@ -134,10 +138,6 @@ module Tmuxinator
134
138
  "#{project.tmux} select-layout -t #{tmux_window_target} #{layout}"
135
139
  end
136
140
 
137
- def tmux_select_first_pane
138
- "#{project.tmux} select-pane -t #{tmux_window_target}.#{panes.first.index + project.pane_base_index}"
139
- end
140
-
141
141
  def synchronize_before?
142
142
  [true, "before"].include?(synchronize)
143
143
  end
@@ -145,5 +145,39 @@ module Tmuxinator
145
145
  def synchronize_after?
146
146
  synchronize == "after"
147
147
  end
148
+
149
+ private
150
+
151
+ def focused_pane
152
+ "#{tmux_window_target}.#{tmux_pane_index}"
153
+ end
154
+
155
+ def tmux_pane_index
156
+ # Adjust for tmux pane base index
157
+ pane_index + project.pane_base_index
158
+ end
159
+
160
+ def pane_index
161
+ focused_pane_config = yaml["focused_pane"]
162
+ # Select the first pane if the user hasn't set focused_pane
163
+ return 0 unless focused_pane_config
164
+
165
+ # The user may provide the focused pane index.
166
+ if integer?(focused_pane_config)
167
+ idx = Integer(focused_pane_config)
168
+ return idx if idx >= 0 && panes[idx]
169
+
170
+ return 0
171
+ end
172
+
173
+ # If no pane with the given name is found fall back to the first pane
174
+ panes.index { |pane| pane.title == focused_pane_config.to_s.shellescape } || 0
175
+ end
176
+
177
+ def integer?(str)
178
+ !!Integer(str)
179
+ rescue StandardError
180
+ false
181
+ end
148
182
  end
149
183
  end
@@ -0,0 +1,14 @@
1
+ name: basic
2
+ root: /workspace/basic
3
+ tmux_options: -L interface
4
+ pre_window: bundle exec ruby -v
5
+ windows:
6
+ - editor:
7
+ root: app
8
+ panes:
9
+ - bundle exec vim
10
+ - bundle exec rake test
11
+ - shell:
12
+ root: .
13
+ panes:
14
+ - bin/setup
@@ -0,0 +1,11 @@
1
+ name: pane-titles
2
+ root: /workspace/pane-titles
3
+ tmux_options: -L interface
4
+ enable_pane_titles: true
5
+ pane_title_position: bottom
6
+ pane_title_format: "[ #T ]"
7
+ windows:
8
+ - editor:
9
+ panes:
10
+ - editor: bundle exec vim
11
+ - logs: tail -f log/test.log
@@ -0,0 +1,5 @@
1
+ name: home.arpa:lab
2
+ windows:
3
+ - main:
4
+ panes:
5
+ - echo ok
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+
5
+ describe "tmuxinator debug snapshots" do
6
+ let(:cli) { Tmuxinator::Cli }
7
+ let(:snapshot_root) { File.expand_path("../snapshots/debug", __dir__) }
8
+
9
+ around do |example|
10
+ original_argv = ARGV.dup
11
+ example.run
12
+ ARGV.replace(original_argv)
13
+ end
14
+
15
+ before do
16
+ allow_any_instance_of(Tmuxinator::Project).
17
+ to receive(:extract_tmux_config).
18
+ and_return({ "base-index" => "0", "pane-base-index" => "0" })
19
+ allow_any_instance_of(Tmuxinator::Project).
20
+ to receive(:tmux_has_session?).
21
+ and_return(false)
22
+ allow(ENV).to receive(:[]).and_call_original
23
+ allow(ENV).to receive(:[]).with("SHELL").and_return("/bin/bash")
24
+ end
25
+
26
+ {
27
+ "1.6" => 1.6,
28
+ "1.8" => 1.8,
29
+ "2.6" => 2.6,
30
+ }.each do |version_label, version|
31
+ {
32
+ "basic" => "spec/fixtures/interface/basic.yml",
33
+ "pane_titles" => "spec/fixtures/interface/pane_titles.yml",
34
+ }.each do |fixture_name, fixture_path|
35
+ it "matches #{fixture_name} output for tmux #{version_label}" do
36
+ allow(Tmuxinator::Config).to receive(:version).and_return(version)
37
+
38
+ ARGV.replace(["debug", "--project-config=#{fixture_path}"])
39
+ output, _err = capture_io { cli.start }
40
+
41
+ snapshot_path = File.join(
42
+ snapshot_root,
43
+ version_label,
44
+ "#{fixture_name}.sh"
45
+ )
46
+
47
+ expect(output).to eq("#{File.read(snapshot_path)}\n")
48
+ end
49
+ end
50
+ end
51
+
52
+ it "normalizes tmux session targets for project names with separators" do
53
+ allow(Tmuxinator::Config).to receive(:version).and_return(2.6)
54
+
55
+ ARGV.replace([
56
+ "debug",
57
+ "--project-config=spec/fixtures/interface/session_name.yml"
58
+ ])
59
+ output, _err = capture_io { cli.start }
60
+
61
+ snapshot_path = File.join(snapshot_root, "2.6", "session_name.sh")
62
+
63
+ expect(output).to eq("#{File.read(snapshot_path)}\n")
64
+ end
65
+ end
@@ -517,6 +517,16 @@ describe Tmuxinator::Cli do
517
517
  let(:name) { "test" }
518
518
  let(:path) { Tmuxinator::Config.default_project(name) }
519
519
 
520
+ context "with --help flag" do
521
+ it "shows help instead of editing a project" do
522
+ ARGV.replace(["edit", "--help"])
523
+ out, _err = capture_io { cli.start }
524
+ expect(out).to include("edit [PROJECT]")
525
+ expect(out).to include("Use local project file at ./.tmuxinator.yml")
526
+ expect(out).to include("Options:")
527
+ end
528
+ end
529
+
520
530
  context "when the project file _does_ already exist" do
521
531
  let(:extra) { " - extra: echo 'foobar'" }
522
532
 
@@ -545,6 +555,84 @@ describe Tmuxinator::Cli do
545
555
  expect(File.read(path)).to match %r{#{extra}}
546
556
  end
547
557
  end
558
+
559
+ context "when the project file does not already exist" do
560
+ let(:path) { File.join(Dir.tmpdir, "#{name}.yml") }
561
+
562
+ before do
563
+ allow(Tmuxinator::Config).to receive(:default_project).and_call_original
564
+ allow(Tmuxinator::Config).to receive(:default_project).
565
+ with(name).and_return(path)
566
+ ARGV.replace(["edit", name])
567
+ allow(File).to receive(:exist?).with(anything).and_call_original
568
+ allow(File).to receive(:exist?).with(path).and_return(false)
569
+ end
570
+
571
+ it "exits instead of generating a project file" do
572
+ expect { cli.start }.to raise_error(SystemExit)
573
+ expect(File).not_to exist(path)
574
+ end
575
+ end
576
+ end
577
+
578
+ describe "#open" do
579
+ let(:file) { StringIO.new }
580
+ let(:name) { "test" }
581
+ let(:path) { Tmuxinator::Config.default_project(name) }
582
+
583
+ context "with --help flag" do
584
+ it "shows help instead of opening a project" do
585
+ ARGV.replace(["open", "--help"])
586
+ out, _err = capture_io { cli.start }
587
+ expect(out).to include("open [PROJECT]")
588
+ expect(out).to include("Use local project file at ./.tmuxinator.yml")
589
+ expect(out).to include("Options:")
590
+ end
591
+ end
592
+
593
+ context "when the project file does not already exist" do
594
+ before do
595
+ ARGV.replace(["open", name])
596
+ allow(File).to receive(:open) { |&block| block.yield file }
597
+ allow(File).to receive(:exist?).with(anything).and_return(false)
598
+ end
599
+
600
+ it "creates and opens a tmuxinator project file" do
601
+ expect(Kernel).to receive(:system).with(%r{#{path}})
602
+
603
+ capture_io { cli.start }
604
+
605
+ expect(file.string).to_not be_empty
606
+ end
607
+ end
608
+
609
+ context "when the project file already exists" do
610
+ let(:extra) { " - extra: echo 'foobar'" }
611
+ let(:path) { File.join(Dir.tmpdir, "#{name}.yml") }
612
+
613
+ before do
614
+ allow(Tmuxinator::Config).to receive(:default_project).and_call_original
615
+ allow(Tmuxinator::Config).to receive(:default_project).
616
+ with(name).and_return(path)
617
+ FileUtils.remove_file(path) if File.exist?(path)
618
+ expect(described_class.new.generate_project_file(name, path)).to eq path
619
+
620
+ File.open(path, "w") do |f|
621
+ f.write(extra)
622
+ f.flush
623
+ end
624
+
625
+ ARGV.replace(["open", name])
626
+ end
627
+
628
+ it "opens the existing project file without replacing it" do
629
+ expect(Kernel).to receive(:system).with(%r{#{path}})
630
+
631
+ capture_io { cli.start }
632
+
633
+ expect(File.read(path)).to match %r{#{extra}}
634
+ end
635
+ end
548
636
  end
549
637
 
550
638
  describe "#new" do
@@ -560,6 +648,7 @@ describe Tmuxinator::Cli do
560
648
  ARGV.replace(["new", "--help"])
561
649
  out, _err = capture_io { cli.start }
562
650
  expect(out).to include("new [PROJECT]")
651
+ expect(out).to include("Use local project file at ./.tmuxinator.yml")
563
652
  expect(out).to include("Options:")
564
653
  end
565
654
  end
@@ -628,27 +717,34 @@ describe Tmuxinator::Cli do
628
717
 
629
718
  # this command variant only works for tmux version 1.6 and up.
630
719
  context "from a session" do
631
- context "with tmux >= 1.6", if: Tmuxinator::Config.version >= 1.6 do
720
+ context "with tmux >= 1.6" do
721
+ let(:success_status) { instance_double(Process::Status, success?: true) }
722
+ let(:failure_status) { instance_double(Process::Status, success?: false) }
723
+
632
724
  before do
633
- # Necessary to make `Doctor.installed?` work in specs
634
- allow(Tmuxinator::Doctor).to receive(:installed?).and_return(true)
725
+ allow(Tmuxinator::Config).to receive(:version).and_return(1.6)
635
726
  end
636
727
 
637
728
  context "session exists" do
638
- before(:all) do
639
- # Can't add variables through `let` in `before :all`.
640
- @session = "for-testing-tmuxinator"
641
- # Pass the -d option, so that the session is not attached.
642
- Kernel.system "tmux new-session -d -s #{@session}"
643
- end
644
-
645
729
  before do
646
- ARGV.replace ["new", name, @session]
647
- end
648
-
649
- after(:all) do
650
- puts @session
651
- Kernel.system "tmux kill-session -t #{@session}"
730
+ ARGV.replace ["new", name, "existing-session"]
731
+ allow(Open3).to receive(:capture3).and_return(
732
+ [
733
+ "editor tiled 1 /workspace/app\n",
734
+ "",
735
+ success_status,
736
+ ],
737
+ [
738
+ "editor /workspace/app\n",
739
+ "",
740
+ success_status,
741
+ ],
742
+ [
743
+ "default-path \"/workspace/app\"\n",
744
+ "",
745
+ success_status,
746
+ ]
747
+ )
652
748
  end
653
749
 
654
750
  it "creates a project file" do
@@ -662,6 +758,11 @@ describe Tmuxinator::Cli do
662
758
  context "session doesn't exist" do
663
759
  before do
664
760
  ARGV.replace ["new", name, "sessiondoesnotexist"]
761
+ allow(Open3).to receive(:capture3).and_return(
762
+ ["", "", failure_status],
763
+ ["", "", failure_status],
764
+ ["", "", failure_status]
765
+ )
665
766
  end
666
767
 
667
768
  it "fails" do
@@ -205,6 +205,7 @@ describe Tmuxinator::Config do
205
205
  "v3.5" => 3.5,
206
206
  "v3.6" => 3.6,
207
207
  "v3.6a" => 3.6,
208
+ "v3.6b" => 3.6,
208
209
  "v3.12.0" => 3.12,
209
210
  "v3.12.5" => 3.12
210
211
  }.freeze