tmuxinator 0.8.1 → 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/completion/mux.fish +1 -1
- data/completion/tmuxinator.bash +1 -0
- data/completion/tmuxinator.fish +2 -0
- data/completion/tmuxinator.zsh +1 -0
- data/lib/tmuxinator/assets/sample.yml +7 -1
- data/lib/tmuxinator/assets/template.erb +9 -5
- data/lib/tmuxinator/assets/wemux_template.erb +9 -5
- data/lib/tmuxinator/cli.rb +10 -11
- data/lib/tmuxinator/config.rb +5 -5
- data/lib/tmuxinator/pane.rb +0 -4
- data/lib/tmuxinator/project.rb +37 -2
- data/lib/tmuxinator/version.rb +1 -1
- data/lib/tmuxinator/window.rb +27 -10
- data/spec/factories/projects.rb +8 -0
- data/spec/fixtures/noroot.yml +5 -0
- data/spec/fixtures/sample.deprecations.yml +3 -0
- data/spec/lib/tmuxinator/cli_spec.rb +9 -8
- data/spec/lib/tmuxinator/config_spec.rb +10 -10
- data/spec/lib/tmuxinator/project_spec.rb +50 -15
- data/spec/lib/tmuxinator/window_spec.rb +157 -33
- data/spec/matchers/pane_matcher.rb +44 -0
- data/spec/spec_helper.rb +3 -0
- metadata +7 -5
- data/bin/mux +0 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d4701d5ffcbc531c162e257f79c3f3399e106ec4
|
4
|
+
data.tar.gz: 94774dea4bbad7cfbc17f16d9428cd85061e2b5d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fac94b03868d51bfac62e4205b91b4695a97d686d46759a09df7e33144e415b100b9b72984f7c0fd7d0146a27ac21c630d97729769e137047d0253ecf16afaa7
|
7
|
+
data.tar.gz: d9833164fd80716d76f19269f0eb07d424b57e788a2fe70f6c31c2a7f74130285a1a332e2b1401c2026a9a696afaa76cb76db7fa05e4934a7391912170e00e42
|
data/completion/mux.fish
CHANGED
@@ -1 +1 @@
|
|
1
|
-
tmuxinator.fish
|
1
|
+
completion/tmuxinator.fish
|
data/completion/tmuxinator.bash
CHANGED
data/completion/tmuxinator.fish
CHANGED
@@ -20,3 +20,5 @@ complete -f -c $__fish_tmuxinator_program_cmd -n '__fish_tmuxinator_using_comman
|
|
20
20
|
complete -f -c $__fish_tmuxinator_program_cmd -n '__fish_tmuxinator_using_command open' -a "(__fish_tmuxinator_program completions open)"
|
21
21
|
complete -f -c $__fish_tmuxinator_program_cmd -n '__fish_tmuxinator_using_command copy' -a "(__fish_tmuxinator_program completions copy)"
|
22
22
|
complete -f -c $__fish_tmuxinator_program_cmd -n '__fish_tmuxinator_using_command delete' -a "(__fish_tmuxinator_program completions delete)"
|
23
|
+
|
24
|
+
abbr --add mux "tmuxinator"
|
data/completion/tmuxinator.zsh
CHANGED
@@ -19,7 +19,10 @@ root: ~/
|
|
19
19
|
# tmux_command: byobu
|
20
20
|
|
21
21
|
# Specifies (by name or index) which window will be selected on project startup. If not set, the first window is used.
|
22
|
-
# startup_window:
|
22
|
+
# startup_window: editor
|
23
|
+
|
24
|
+
# Specitifes (by index) which pane of the specified window will be selected on project startup. If not set, the first pane is used.
|
25
|
+
# startup_pane: 1
|
23
26
|
|
24
27
|
# Controls whether the tmux session should be attached to automatically. Defaults to true.
|
25
28
|
# attach: false
|
@@ -30,6 +33,9 @@ root: ~/
|
|
30
33
|
windows:
|
31
34
|
- editor:
|
32
35
|
layout: main-vertical
|
36
|
+
# Synchronize all panes of this window, can be enabled before or after the pane commands run.
|
37
|
+
# 'before' represents legacy functionality and will be deprecated in a future release, in favour of 'after'
|
38
|
+
# synchronize: after
|
33
39
|
panes:
|
34
40
|
- vim
|
35
41
|
- guard
|
@@ -34,6 +34,9 @@ unset RBENV_DIR
|
|
34
34
|
<% windows.each do |window| %>
|
35
35
|
|
36
36
|
# Window "<%= window.name %>"
|
37
|
+
<% if window.synchronize_before? %>
|
38
|
+
<%= window.tmux_synchronize_panes %>
|
39
|
+
<% end %>
|
37
40
|
<% unless window.panes? %>
|
38
41
|
<% if window.project.pre_window %>
|
39
42
|
<%= window.tmux_pre_window_command %>
|
@@ -49,12 +52,8 @@ unset RBENV_DIR
|
|
49
52
|
<% if pane.tab.pre %>
|
50
53
|
<%= pane.tmux_pre_command %>
|
51
54
|
<% end %>
|
52
|
-
<%
|
53
|
-
<% pane.commands.each do |command| %>
|
55
|
+
<% pane.commands.each do |command| %>
|
54
56
|
<%= pane.tmux_main_command(command) %>
|
55
|
-
<% end %>
|
56
|
-
<% else %>
|
57
|
-
<%= pane.tmux_main_command(commands.first) %>
|
58
57
|
<% end %>
|
59
58
|
|
60
59
|
<% unless pane.last? %>
|
@@ -66,9 +65,14 @@ unset RBENV_DIR
|
|
66
65
|
<%= window.tmux_layout_command %>
|
67
66
|
<%= window.tmux_select_first_pane %>
|
68
67
|
<% end %>
|
68
|
+
|
69
|
+
<% if window.synchronize_after? %>
|
70
|
+
<%= window.tmux_synchronize_panes %>
|
71
|
+
<% end %>
|
69
72
|
<% end %>
|
70
73
|
|
71
74
|
<%= tmux %> select-window -t <%= startup_window %>
|
75
|
+
<%= tmux %> select-pane -t <%= startup_pane %>
|
72
76
|
<%- end -%>
|
73
77
|
|
74
78
|
<%- if attach? -%>
|
@@ -23,6 +23,9 @@ if [ "$?" -eq 127 ]; then
|
|
23
23
|
<%- windows.each do |window| -%>
|
24
24
|
|
25
25
|
# Window "<%= window.name %>"
|
26
|
+
<% if window.synchronize_before? %>
|
27
|
+
<%= window.tmux_synchronize_panes %>
|
28
|
+
<% end %>
|
26
29
|
<%- unless window.panes? -%>
|
27
30
|
<%= window.tmux_pre_window_command %>
|
28
31
|
<%- window.commands.each do |command| -%>
|
@@ -32,12 +35,8 @@ if [ "$?" -eq 127 ]; then
|
|
32
35
|
<%- window.panes.each do |pane| -%>
|
33
36
|
<%= pane.tmux_pre_window_command %>
|
34
37
|
<%= pane.tmux_pre_command %>
|
35
|
-
|
36
|
-
<%- pane.commands.each do |command| -%>
|
38
|
+
<%- pane.commands.each do |command| -%>
|
37
39
|
<%= pane.tmux_main_command(command) %>
|
38
|
-
<%- end -%>
|
39
|
-
<%- else -%>
|
40
|
-
<%= pane.tmux_main_command(commands.first) %>
|
41
40
|
<%- end -%>
|
42
41
|
|
43
42
|
<%- unless pane.last? -%>
|
@@ -48,9 +47,14 @@ if [ "$?" -eq 127 ]; then
|
|
48
47
|
|
49
48
|
<%= window.tmux_select_first_pane %>
|
50
49
|
<%- end -%>
|
50
|
+
|
51
|
+
<%- if window.synchronize_after? -%>
|
52
|
+
<%= window.tmux_synchronize_panes %>
|
53
|
+
<%- end %>
|
51
54
|
<%- end -%>
|
52
55
|
|
53
56
|
<%= tmux %> select-window -t <%= startup_window %>
|
57
|
+
<%= tmux %> select-pane -t <%= startup_pane %>
|
54
58
|
fi
|
55
59
|
|
56
60
|
wemux attach
|
data/lib/tmuxinator/cli.rb
CHANGED
@@ -12,20 +12,19 @@ module Tmuxinator
|
|
12
12
|
commands: "Lists commands available in tmuxinator",
|
13
13
|
completions: "Used for shell completion",
|
14
14
|
new: "Create a new project file and open it in your editor",
|
15
|
+
edit: "Alias of new",
|
15
16
|
open: "Alias of new",
|
16
|
-
start:
|
17
|
+
start: %w{
|
17
18
|
Start a tmux session using a project's tmuxinator config,
|
18
19
|
with an optional [ALIAS] for project reuse
|
19
|
-
|
20
|
-
stop:
|
21
|
-
Stop a tmux session using a project's tmuxinator config.
|
22
|
-
DESC
|
20
|
+
}.join(" "),
|
21
|
+
stop: "Stop a tmux session using a project's tmuxinator config",
|
23
22
|
local: "Start a tmux session using ./.tmuxinator.yml",
|
24
23
|
debug: "Output the shell commands that are generated by tmuxinator",
|
25
|
-
copy:
|
26
|
-
Copy an existing project to a new project and
|
27
|
-
your editor
|
28
|
-
|
24
|
+
copy: %w{
|
25
|
+
Copy an existing project to a new project and
|
26
|
+
open it in your editor
|
27
|
+
}.join(" "),
|
29
28
|
delete: "Deletes given project",
|
30
29
|
implode: "Deletes all tmuxinator projects",
|
31
30
|
version: "Display installed tmuxinator version",
|
@@ -53,7 +52,7 @@ module Tmuxinator
|
|
53
52
|
desc "completions [arg1 arg2]", COMMANDS[:completions]
|
54
53
|
|
55
54
|
def completions(arg)
|
56
|
-
if %w(start stop open copy delete).include?(arg)
|
55
|
+
if %w(start stop edit open copy delete).include?(arg)
|
57
56
|
configs = Tmuxinator::Config.configs
|
58
57
|
say configs.join("\n")
|
59
58
|
end
|
@@ -81,7 +80,7 @@ module Tmuxinator
|
|
81
80
|
else
|
82
81
|
Tmuxinator::Config.default_project(name)
|
83
82
|
end
|
84
|
-
if File.
|
83
|
+
if File.exist?(path)
|
85
84
|
path
|
86
85
|
else
|
87
86
|
generate_project_file(name, path)
|
data/lib/tmuxinator/config.rb
CHANGED
@@ -5,9 +5,9 @@ module Tmuxinator
|
|
5
5
|
|
6
6
|
class << self
|
7
7
|
def root
|
8
|
-
root_dir = File.expand_path("
|
9
|
-
Dir.mkdir(
|
10
|
-
|
8
|
+
root_dir = File.expand_path("#{ENV['HOME']}/.tmuxinator")
|
9
|
+
Dir.mkdir(root_dir) unless File.directory?(root_dir)
|
10
|
+
root_dir
|
11
11
|
end
|
12
12
|
|
13
13
|
def sample
|
@@ -43,7 +43,7 @@ module Tmuxinator
|
|
43
43
|
end
|
44
44
|
|
45
45
|
def exists?(name)
|
46
|
-
File.
|
46
|
+
File.exist?(project(name))
|
47
47
|
end
|
48
48
|
|
49
49
|
def project_in_root(name)
|
@@ -56,7 +56,7 @@ module Tmuxinator
|
|
56
56
|
end
|
57
57
|
|
58
58
|
def project_in_local
|
59
|
-
[LOCAL_DEFAULT].detect { |f| File.
|
59
|
+
[LOCAL_DEFAULT].detect { |f| File.exist?(f) }
|
60
60
|
end
|
61
61
|
|
62
62
|
def default_project(name)
|
data/lib/tmuxinator/pane.rb
CHANGED
data/lib/tmuxinator/project.rb
CHANGED
@@ -16,6 +16,15 @@ module Tmuxinator
|
|
16
16
|
DEPRECATION: The cli_args option has been replaced by the tmux_options
|
17
17
|
option and will not be supported in 0.8.0.
|
18
18
|
M
|
19
|
+
SYNC_DEP_MSG = <<-M
|
20
|
+
DEPRECATION: The synchronize option's current default behaviour is to
|
21
|
+
enable pane synchronization before running commands. In a future release,
|
22
|
+
the default syncronization option will be `after`, i.e. synchronization of
|
23
|
+
panes will occur after the commands described in each of the panes
|
24
|
+
have run. At that time, the current behavior will need to be explicitly
|
25
|
+
enabled, using the `synchronize: before` option. To use this behaviour
|
26
|
+
now, use the 'synchronize: after' option.
|
27
|
+
M
|
19
28
|
|
20
29
|
attr_reader :yaml
|
21
30
|
attr_reader :force_attach
|
@@ -148,8 +157,11 @@ module Tmuxinator
|
|
148
157
|
end
|
149
158
|
|
150
159
|
def tmux_has_session?(name)
|
151
|
-
|
152
|
-
|
160
|
+
# Redirect stderr to /dev/null in order to prevent "failed to connect
|
161
|
+
# to server: Connection refused" error message and non-zero exit status
|
162
|
+
# if no tmux sessions exist.
|
163
|
+
# Please see issues #402 and #414.
|
164
|
+
sessions = `#{tmux_command} ls 2> /dev/null`
|
153
165
|
!!sessions.match("^#{name}:")
|
154
166
|
end
|
155
167
|
|
@@ -183,10 +195,18 @@ module Tmuxinator
|
|
183
195
|
get_pane_base_index ? get_pane_base_index.to_i : get_base_index.to_i
|
184
196
|
end
|
185
197
|
|
198
|
+
def pane_base_index
|
199
|
+
get_pane_base_index.to_i
|
200
|
+
end
|
201
|
+
|
186
202
|
def startup_window
|
187
203
|
yaml["startup_window"] || base_index
|
188
204
|
end
|
189
205
|
|
206
|
+
def startup_pane
|
207
|
+
yaml["startup_pane"] || pane_base_index
|
208
|
+
end
|
209
|
+
|
190
210
|
def tmux_options?
|
191
211
|
yaml["tmux_options"]
|
192
212
|
end
|
@@ -228,6 +248,7 @@ module Tmuxinator
|
|
228
248
|
deprecations << RBENVRVM_DEP_MSG if yaml["rbenv"] || yaml["rvm"]
|
229
249
|
deprecations << TABS_DEP_MSG if yaml["tabs"]
|
230
250
|
deprecations << CLIARGS_DEP_MSG if yaml["cli_args"]
|
251
|
+
deprecations << SYNC_DEP_MSG if legacy_synchronize?
|
231
252
|
deprecations
|
232
253
|
end
|
233
254
|
|
@@ -271,5 +292,19 @@ module Tmuxinator
|
|
271
292
|
|
272
293
|
options_hash
|
273
294
|
end
|
295
|
+
|
296
|
+
def legacy_synchronize?
|
297
|
+
(synchronize_options & [true, "before"]).any?
|
298
|
+
end
|
299
|
+
|
300
|
+
def synchronize_options
|
301
|
+
window_options.map do |option|
|
302
|
+
option["synchronize"] if option.is_a?(Hash)
|
303
|
+
end
|
304
|
+
end
|
305
|
+
|
306
|
+
def window_options
|
307
|
+
yaml["windows"].map(&:values).flatten
|
308
|
+
end
|
274
309
|
end
|
275
310
|
end
|
data/lib/tmuxinator/version.rb
CHANGED
data/lib/tmuxinator/window.rb
CHANGED
@@ -2,7 +2,7 @@ module Tmuxinator
|
|
2
2
|
class Window
|
3
3
|
include Tmuxinator::Util
|
4
4
|
|
5
|
-
attr_reader :name, :root, :panes, :layout, :commands, :index, :project
|
5
|
+
attr_reader :name, :root, :panes, :layout, :commands, :index, :project, :synchronize
|
6
6
|
|
7
7
|
def initialize(window_yaml, index, project)
|
8
8
|
@name = if !window_yaml.keys.first.nil?
|
@@ -10,6 +10,7 @@ module Tmuxinator
|
|
10
10
|
end
|
11
11
|
@root = nil
|
12
12
|
@panes = []
|
13
|
+
@synchronize = false
|
13
14
|
@layout = nil
|
14
15
|
@pre = nil
|
15
16
|
@project = project
|
@@ -18,6 +19,7 @@ module Tmuxinator
|
|
18
19
|
value = window_yaml.values.first
|
19
20
|
|
20
21
|
if value.is_a?(Hash)
|
22
|
+
@synchronize = value["synchronize"] || false
|
21
23
|
@layout = value["layout"] ? value["layout"].shellescape : nil
|
22
24
|
@pre = value["pre"] if value["pre"]
|
23
25
|
@root = if value["root"]
|
@@ -27,20 +29,23 @@ module Tmuxinator
|
|
27
29
|
end
|
28
30
|
|
29
31
|
@panes = build_panes(value["panes"])
|
30
|
-
else
|
31
|
-
@commands = build_commands(tmux_window_command_prefix, value)
|
32
32
|
end
|
33
|
+
|
34
|
+
@commands = build_commands(tmux_window_command_prefix, value)
|
33
35
|
end
|
34
36
|
|
35
37
|
def build_panes(panes_yml)
|
36
38
|
Array(panes_yml).map.with_index do |pane_yml, index|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
39
|
+
commands = case pane_yml
|
40
|
+
when Hash
|
41
|
+
pane_yml.values.first
|
42
|
+
when Array
|
43
|
+
pane_yml
|
44
|
+
else
|
45
|
+
pane_yml
|
46
|
+
end
|
47
|
+
|
48
|
+
Tmuxinator::Pane.new(index, project, self, *commands)
|
44
49
|
end.flatten
|
45
50
|
end
|
46
51
|
|
@@ -99,6 +104,10 @@ module Tmuxinator
|
|
99
104
|
"#{project.tmux} select-layout -t #{tmux_window_target} tiled"
|
100
105
|
end
|
101
106
|
|
107
|
+
def tmux_synchronize_panes
|
108
|
+
"#{project.tmux} set-window-option -t #{tmux_window_target} synchronize-panes on"
|
109
|
+
end
|
110
|
+
|
102
111
|
def tmux_layout_command
|
103
112
|
"#{project.tmux} select-layout -t #{tmux_window_target} #{layout}"
|
104
113
|
end
|
@@ -106,5 +115,13 @@ module Tmuxinator
|
|
106
115
|
def tmux_select_first_pane
|
107
116
|
"#{project.tmux} select-pane -t #{tmux_window_target}.#{panes.first.index + project.base_index}"
|
108
117
|
end
|
118
|
+
|
119
|
+
def synchronize_before?
|
120
|
+
synchronize == true || synchronize == "before"
|
121
|
+
end
|
122
|
+
|
123
|
+
def synchronize_after?
|
124
|
+
synchronize == "after"
|
125
|
+
end
|
109
126
|
end
|
110
127
|
end
|
data/spec/factories/projects.rb
CHANGED
@@ -73,6 +73,14 @@ FactoryGirl.define do
|
|
73
73
|
initialize_with { Tmuxinator::Project.new(file) }
|
74
74
|
end
|
75
75
|
|
76
|
+
factory :noroot_project, class: Tmuxinator::Project do
|
77
|
+
transient do
|
78
|
+
file { yaml_load("spec/fixtures/noroot.yml") }
|
79
|
+
end
|
80
|
+
|
81
|
+
initialize_with { Tmuxinator::Project.new(file) }
|
82
|
+
end
|
83
|
+
|
76
84
|
factory :nowindows_project, class: Tmuxinator::Project do
|
77
85
|
transient do
|
78
86
|
file { yaml_load("spec/fixtures/nowindows.yml") }
|
@@ -40,6 +40,7 @@ describe Tmuxinator::Cli do
|
|
40
40
|
expected = %w(commands
|
41
41
|
completions
|
42
42
|
new
|
43
|
+
edit
|
43
44
|
open
|
44
45
|
start
|
45
46
|
stop
|
@@ -181,7 +182,7 @@ describe Tmuxinator::Cli do
|
|
181
182
|
|
182
183
|
before do
|
183
184
|
# make sure that no project file exists initially
|
184
|
-
FileUtils.remove_file(path) if File.
|
185
|
+
FileUtils.remove_file(path) if File.exist?(path)
|
185
186
|
expect(File).not_to exist(path)
|
186
187
|
|
187
188
|
# now generate a project file
|
@@ -221,7 +222,7 @@ describe Tmuxinator::Cli do
|
|
221
222
|
|
222
223
|
context "existing project doesn't exist" do
|
223
224
|
before do
|
224
|
-
expect(File).to receive_messages(
|
225
|
+
expect(File).to receive_messages(exist?: false)
|
225
226
|
end
|
226
227
|
|
227
228
|
it "creates a new tmuxinator project file" do
|
@@ -234,8 +235,8 @@ describe Tmuxinator::Cli do
|
|
234
235
|
let(:root_path) { "#{ENV['HOME']}\/\.tmuxinator\/#{name}\.yml" }
|
235
236
|
|
236
237
|
before do
|
237
|
-
allow(File).to receive(:
|
238
|
-
expect(File).to receive(:
|
238
|
+
allow(File).to receive(:exist?).with(anything).and_return(false)
|
239
|
+
expect(File).to receive(:exist?).with(root_path).and_return(true)
|
239
240
|
end
|
240
241
|
|
241
242
|
it "just opens the file" do
|
@@ -252,7 +253,7 @@ describe Tmuxinator::Cli do
|
|
252
253
|
|
253
254
|
context "existing project doesn't exist" do
|
254
255
|
before do
|
255
|
-
allow(File).to receive(:
|
256
|
+
allow(File).to receive(:exist?).at_least(:once) do
|
256
257
|
false
|
257
258
|
end
|
258
259
|
end
|
@@ -266,7 +267,7 @@ describe Tmuxinator::Cli do
|
|
266
267
|
context "files exists" do
|
267
268
|
let(:path) { Tmuxinator::Config::LOCAL_DEFAULT }
|
268
269
|
before do
|
269
|
-
expect(File).to receive(:
|
270
|
+
expect(File).to receive(:exist?).with(path) { true }
|
270
271
|
end
|
271
272
|
|
272
273
|
it "just opens the file" do
|
@@ -489,7 +490,7 @@ describe Tmuxinator::Cli do
|
|
489
490
|
let(:path) { Tmuxinator::Config.default_project(name) }
|
490
491
|
|
491
492
|
after(:each) do
|
492
|
-
FileUtils.remove_file(path) if File.
|
493
|
+
FileUtils.remove_file(path) if File.exist?(path)
|
493
494
|
end
|
494
495
|
|
495
496
|
context "when the project file does not already exist" do
|
@@ -537,7 +538,7 @@ describe Tmuxinator::Cli do
|
|
537
538
|
end
|
538
539
|
|
539
540
|
after(:each) do
|
540
|
-
FileUtils.remove_file(path) if File.
|
541
|
+
FileUtils.remove_file(path) if File.exist?(path)
|
541
542
|
end
|
542
543
|
|
543
544
|
it "should always generate a project file" do
|
@@ -48,8 +48,8 @@ describe Tmuxinator::Config do
|
|
48
48
|
|
49
49
|
context "when the file exists" do
|
50
50
|
before do
|
51
|
-
allow(File).to receive(:
|
52
|
-
allow(File).to receive(:
|
51
|
+
allow(File).to receive(:exist?).with(local_default) { false }
|
52
|
+
allow(File).to receive(:exist?).with(proj_default) { true }
|
53
53
|
end
|
54
54
|
|
55
55
|
it "returns true" do
|
@@ -59,8 +59,8 @@ describe Tmuxinator::Config do
|
|
59
59
|
|
60
60
|
context "when the file doesn't exist" do
|
61
61
|
before do
|
62
|
-
allow(File).to receive(:
|
63
|
-
allow(File).to receive(:
|
62
|
+
allow(File).to receive(:exist?).with(local_default) { false }
|
63
|
+
allow(File).to receive(:exist?).with(proj_default) { false }
|
64
64
|
end
|
65
65
|
|
66
66
|
it "returns true" do
|
@@ -147,7 +147,7 @@ describe Tmuxinator::Config do
|
|
147
147
|
|
148
148
|
describe "#exists?" do
|
149
149
|
before do
|
150
|
-
allow(File).to receive_messages(
|
150
|
+
allow(File).to receive_messages(exist?: true)
|
151
151
|
allow(Tmuxinator::Config).to receive_messages(project: "")
|
152
152
|
end
|
153
153
|
|
@@ -181,7 +181,7 @@ describe Tmuxinator::Config do
|
|
181
181
|
describe "#local?" do
|
182
182
|
it "checks if the given project exists" do
|
183
183
|
path = Tmuxinator::Config::LOCAL_DEFAULT
|
184
|
-
expect(File).to receive(:
|
184
|
+
expect(File).to receive(:exist?).with(path) { true }
|
185
185
|
expect(Tmuxinator::Config.local?).to be_truthy
|
186
186
|
end
|
187
187
|
end
|
@@ -191,7 +191,7 @@ describe Tmuxinator::Config do
|
|
191
191
|
|
192
192
|
context "with a project yml" do
|
193
193
|
it "gets the project as path to the yml file" do
|
194
|
-
expect(File).to receive(:
|
194
|
+
expect(File).to receive(:exist?).with(default) { true }
|
195
195
|
expect(Tmuxinator::Config.project_in_local).to eq default
|
196
196
|
end
|
197
197
|
end
|
@@ -220,7 +220,7 @@ describe Tmuxinator::Config do
|
|
220
220
|
|
221
221
|
context "with a local project, but no project in root" do
|
222
222
|
it "gets the project as path to the yml file" do
|
223
|
-
expect(File).to receive(:
|
223
|
+
expect(File).to receive(:exist?).with(default) { true }
|
224
224
|
expect(Tmuxinator::Config.project("sample")).to eq "./.tmuxinator.yml"
|
225
225
|
end
|
226
226
|
end
|
@@ -253,7 +253,7 @@ describe Tmuxinator::Config do
|
|
253
253
|
|
254
254
|
context "when no project name is provided" do
|
255
255
|
it "should raise if the local project file doesn't exist" do
|
256
|
-
expect(File).to receive(:
|
256
|
+
expect(File).to receive(:exist?).with(default) { false }
|
257
257
|
expect do
|
258
258
|
Tmuxinator::Config.validate
|
259
259
|
end.to raise_error RuntimeError, %r{Project.+doesn't.exist}
|
@@ -262,7 +262,7 @@ describe Tmuxinator::Config do
|
|
262
262
|
it "should load and validate the project" do
|
263
263
|
content = File.read(File.join(path, "sample.yml"))
|
264
264
|
|
265
|
-
expect(File).to receive(:
|
265
|
+
expect(File).to receive(:exist?).with(default).at_least(:once) { true }
|
266
266
|
expect(File).to receive(:read).with(default).and_return(content)
|
267
267
|
|
268
268
|
expect(Tmuxinator::Config.validate).to be_a Tmuxinator::Project
|
@@ -23,6 +23,7 @@ describe Tmuxinator::Project do
|
|
23
23
|
|
24
24
|
let(:wemux_project) { FactoryGirl.build(:wemux_project) }
|
25
25
|
let(:noname_project) { FactoryGirl.build(:noname_project) }
|
26
|
+
let(:noroot_project) { FactoryGirl.build(:noroot_project) }
|
26
27
|
let(:nameless_window_project) do
|
27
28
|
FactoryGirl.build(:nameless_window_project)
|
28
29
|
end
|
@@ -57,24 +58,40 @@ describe Tmuxinator::Project do
|
|
57
58
|
end
|
58
59
|
|
59
60
|
describe "#tmux_has_session?" do
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
call_tmux_ls = receive(:`).with(cmd).at_least(:once).and_return(resp)
|
61
|
+
context "no active sessions" do
|
62
|
+
before do
|
63
|
+
cmd = "#{project.tmux_command} ls 2> /dev/null"
|
64
|
+
resp = ""
|
65
|
+
call_tmux_ls = receive(:`).with(cmd).at_least(:once).and_return(resp)
|
66
66
|
|
67
|
-
|
68
|
-
|
67
|
+
expect(project).to call_tmux_ls
|
68
|
+
end
|
69
69
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
expect(project.tmux_has_session?("quux")).to be false
|
70
|
+
it "should return false if no sessions are found" do
|
71
|
+
expect(project.tmux_has_session?("no server running")).to be false
|
72
|
+
end
|
74
73
|
end
|
75
74
|
|
76
|
-
|
77
|
-
|
75
|
+
context "active sessions" do
|
76
|
+
before do
|
77
|
+
cmd = "#{project.tmux_command} ls 2> /dev/null"
|
78
|
+
resp = ""\
|
79
|
+
"foo: 1 window (created Sun May 25 10:12:00 1986) [0x0] (detached)\n"\
|
80
|
+
"bar: 1 window (created Sat Sept 01 00:00:00 1990) [0x0] (detached)"
|
81
|
+
call_tmux_ls = receive(:`).with(cmd).at_least(:once).and_return(resp)
|
82
|
+
|
83
|
+
expect(project).to call_tmux_ls
|
84
|
+
end
|
85
|
+
|
86
|
+
it "should return true only when `tmux ls` has the named session" do
|
87
|
+
expect(project.tmux_has_session?("foo")).to be true
|
88
|
+
expect(project.tmux_has_session?("bar")).to be true
|
89
|
+
expect(project.tmux_has_session?("quux")).to be false
|
90
|
+
end
|
91
|
+
|
92
|
+
it "should return false if a partial (prefix) match is found" do
|
93
|
+
expect(project.tmux_has_session?("foobar")).to be false
|
94
|
+
end
|
78
95
|
end
|
79
96
|
end
|
80
97
|
|
@@ -107,7 +124,7 @@ describe Tmuxinator::Project do
|
|
107
124
|
|
108
125
|
context "without root" do
|
109
126
|
it "doesn't throw an error" do
|
110
|
-
expect {
|
127
|
+
expect { noroot_project.root }.to_not raise_error
|
111
128
|
end
|
112
129
|
end
|
113
130
|
end
|
@@ -310,6 +327,24 @@ describe Tmuxinator::Project do
|
|
310
327
|
end
|
311
328
|
end
|
312
329
|
|
330
|
+
describe "#startup_pane" do
|
331
|
+
context "startup pane specified" do
|
332
|
+
it "get the startup pane index from project config" do
|
333
|
+
project.yaml["startup_pane"] = 1
|
334
|
+
|
335
|
+
expect(project.startup_pane).to eq(1)
|
336
|
+
end
|
337
|
+
end
|
338
|
+
|
339
|
+
context "startup pane not specified" do
|
340
|
+
it "returns the base pane instead" do
|
341
|
+
allow(project).to receive_messages(pane_base_index: 4)
|
342
|
+
|
343
|
+
expect(project.startup_pane).to eq(4)
|
344
|
+
end
|
345
|
+
end
|
346
|
+
end
|
347
|
+
|
313
348
|
describe "#window" do
|
314
349
|
it "gets the window and index for tmux" do
|
315
350
|
expect(project.window(1)).to eq "sample:1"
|
@@ -4,6 +4,7 @@ describe Tmuxinator::Window do
|
|
4
4
|
let(:project) { double }
|
5
5
|
let(:panes) { ["vim", nil, "top"] }
|
6
6
|
let(:window_name) { "editor" }
|
7
|
+
let(:synchronize) { false }
|
7
8
|
let(:yaml) do
|
8
9
|
{
|
9
10
|
window_name => {
|
@@ -11,6 +12,7 @@ describe Tmuxinator::Window do
|
|
11
12
|
"echo 'I get run in each pane. Before each pane command!'",
|
12
13
|
nil
|
13
14
|
],
|
15
|
+
"synchronize" => synchronize,
|
14
16
|
"layout" => "main-vertical",
|
15
17
|
"panes" => panes
|
16
18
|
}
|
@@ -34,6 +36,25 @@ describe Tmuxinator::Window do
|
|
34
36
|
let(:window) { Tmuxinator::Window.new(yaml, 0, project) }
|
35
37
|
let(:window_root) { Tmuxinator::Window.new(yaml_root, 0, project) }
|
36
38
|
|
39
|
+
shared_context "window command context" do
|
40
|
+
let(:project) { double(:project) }
|
41
|
+
let(:window) { Tmuxinator::Window.new(yaml, 0, project) }
|
42
|
+
let(:root?) { true }
|
43
|
+
let(:root) { "/project/tmuxinator" }
|
44
|
+
|
45
|
+
before do
|
46
|
+
allow(project).to receive_messages(
|
47
|
+
name: "test",
|
48
|
+
tmux: "tmux",
|
49
|
+
root: root,
|
50
|
+
root?: root?,
|
51
|
+
base_index: 1
|
52
|
+
)
|
53
|
+
end
|
54
|
+
|
55
|
+
let(:tmux_part) { project.tmux }
|
56
|
+
end
|
57
|
+
|
37
58
|
before do
|
38
59
|
allow(project).to receive_messages(
|
39
60
|
tmux: "tmux",
|
@@ -65,14 +86,8 @@ describe Tmuxinator::Window do
|
|
65
86
|
end
|
66
87
|
|
67
88
|
describe "#panes" do
|
68
|
-
let(:pane) { double(:pane) }
|
69
|
-
|
70
|
-
before do
|
71
|
-
allow(Tmuxinator::Pane).to receive_messages new: pane
|
72
|
-
end
|
73
|
-
|
74
89
|
context "with a three element Array" do
|
75
|
-
let(:panes) { ["vim",
|
90
|
+
let(:panes) { ["vim", "ls", "top"] }
|
76
91
|
|
77
92
|
it "creates three panes" do
|
78
93
|
expect(Tmuxinator::Pane).to receive(:new).exactly(3).times
|
@@ -80,35 +95,63 @@ describe Tmuxinator::Window do
|
|
80
95
|
end
|
81
96
|
|
82
97
|
it "returns three panes" do
|
83
|
-
expect(window.panes).to
|
98
|
+
expect(window.panes).to all be_a_pane.with(
|
99
|
+
project: project, tab: window
|
100
|
+
)
|
101
|
+
|
102
|
+
expect(window.panes).to match([
|
103
|
+
a_pane.with(index: 0).and_commands("vim"),
|
104
|
+
a_pane.with(index: 1).and_commands("ls"),
|
105
|
+
a_pane.with(index: 2).and_commands("top")
|
106
|
+
])
|
84
107
|
end
|
85
108
|
end
|
86
109
|
|
87
110
|
context "with a String" do
|
88
111
|
let(:panes) { "vim" }
|
89
112
|
|
90
|
-
it "creates one pane" do
|
91
|
-
expect(Tmuxinator::Pane).to receive(:new).once
|
92
|
-
window.panes
|
93
|
-
end
|
94
|
-
|
95
113
|
it "returns one pane in an Array" do
|
96
|
-
expect(window.panes).to
|
114
|
+
expect(window.panes.first).to be_a_pane.
|
115
|
+
with(index: 0).and_commands("vim")
|
97
116
|
end
|
98
117
|
end
|
99
118
|
|
100
119
|
context "with nil" do
|
101
120
|
let(:panes) { nil }
|
102
121
|
|
103
|
-
it "doesn't create any panes" do
|
104
|
-
expect(Tmuxinator::Pane).to_not receive(:new)
|
105
|
-
window.panes
|
106
|
-
end
|
107
|
-
|
108
122
|
it "returns an empty Array" do
|
109
123
|
expect(window.panes).to be_empty
|
110
124
|
end
|
111
125
|
end
|
126
|
+
|
127
|
+
context "nested collections" do
|
128
|
+
let(:command1) { "cd /tmp/" }
|
129
|
+
let(:command2) { "ls" }
|
130
|
+
|
131
|
+
let(:panes) { ["vim", nested_collection] }
|
132
|
+
|
133
|
+
context "with nested hash" do
|
134
|
+
let(:nested_collection) { { pane2: [command1, command2] } }
|
135
|
+
|
136
|
+
it "returns two panes in an Array" do
|
137
|
+
expect(window.panes).to match [
|
138
|
+
a_pane.with(index: 0).and_commands("vim"),
|
139
|
+
a_pane.with(index: 1).and_commands(command1, command2)
|
140
|
+
]
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
context "with nested array" do
|
145
|
+
let(:nested_collection) { [command1, command2] }
|
146
|
+
|
147
|
+
it "returns two panes in an Array" do
|
148
|
+
expect(window.panes).to match [
|
149
|
+
a_pane.with(index: 0).and_commands("vim"),
|
150
|
+
a_pane.with(index: 1).and_commands(command1, command2)
|
151
|
+
]
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
112
155
|
end
|
113
156
|
|
114
157
|
describe "#pre" do
|
@@ -166,6 +209,16 @@ describe Tmuxinator::Window do
|
|
166
209
|
expect(window.commands).to be_empty
|
167
210
|
end
|
168
211
|
end
|
212
|
+
|
213
|
+
context "command is a hash" do
|
214
|
+
before do
|
215
|
+
yaml["editor"] = { "layout" => "main-horizontal", "panes" => [nil] }
|
216
|
+
end
|
217
|
+
|
218
|
+
it "returns an empty array" do
|
219
|
+
expect(window.commands).to be_empty
|
220
|
+
end
|
221
|
+
end
|
169
222
|
end
|
170
223
|
|
171
224
|
describe "#name_options" do
|
@@ -186,23 +239,94 @@ describe Tmuxinator::Window do
|
|
186
239
|
end
|
187
240
|
end
|
188
241
|
|
189
|
-
describe "#
|
190
|
-
|
191
|
-
let(:window) { Tmuxinator::Window.new(yaml, 0, project) }
|
192
|
-
let(:root?) { true }
|
193
|
-
let(:root) { "/project/tmuxinator" }
|
242
|
+
describe "#synchronize_before?" do
|
243
|
+
subject { window.synchronize_before? }
|
194
244
|
|
195
|
-
before do
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
root: root,
|
200
|
-
root?: root?,
|
201
|
-
base_index: 1
|
202
|
-
)
|
245
|
+
context "synchronize is 'before'" do
|
246
|
+
let(:synchronize) { "before" }
|
247
|
+
|
248
|
+
it { is_expected.to be true }
|
203
249
|
end
|
204
250
|
|
205
|
-
|
251
|
+
context "synchronize is true" do
|
252
|
+
let(:synchronize) { true }
|
253
|
+
|
254
|
+
it { is_expected.to be true }
|
255
|
+
end
|
256
|
+
|
257
|
+
context "synchronize is 'after'" do
|
258
|
+
let(:synchronize) { "after" }
|
259
|
+
|
260
|
+
it { is_expected.to be false }
|
261
|
+
end
|
262
|
+
|
263
|
+
context "synchronization disabled" do
|
264
|
+
let(:synchronize) { false }
|
265
|
+
|
266
|
+
it { is_expected.to be false }
|
267
|
+
end
|
268
|
+
|
269
|
+
context "synchronization not specified" do
|
270
|
+
it { is_expected.to be false }
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
274
|
+
describe "#synchronize_after?" do
|
275
|
+
subject { window.synchronize_after? }
|
276
|
+
|
277
|
+
context "synchronization is 'after'" do
|
278
|
+
let(:synchronize) { "after" }
|
279
|
+
|
280
|
+
it { is_expected.to be true }
|
281
|
+
end
|
282
|
+
|
283
|
+
context "synchronization is true" do
|
284
|
+
let(:synchronize) { true }
|
285
|
+
|
286
|
+
it { is_expected.to be false }
|
287
|
+
end
|
288
|
+
|
289
|
+
context "synchronization is 'before'" do
|
290
|
+
let(:synchronize) { "before" }
|
291
|
+
|
292
|
+
it { is_expected.to be false }
|
293
|
+
end
|
294
|
+
|
295
|
+
context "synchronization disabled" do
|
296
|
+
let(:synchronize) { false }
|
297
|
+
|
298
|
+
it { is_expected.to be false }
|
299
|
+
end
|
300
|
+
|
301
|
+
context "synchronization not specified" do
|
302
|
+
it { is_expected.to be false }
|
303
|
+
end
|
304
|
+
end
|
305
|
+
|
306
|
+
describe "#tmux_synchronize_panes" do
|
307
|
+
include_context "window command context"
|
308
|
+
|
309
|
+
let(:window_option_set_part) { "set-window-option" }
|
310
|
+
let(:target_part) { "-t #{window.tmux_window_target}" }
|
311
|
+
let(:synchronize_panes_part) { "synchronize-panes" }
|
312
|
+
|
313
|
+
context "synchronization enabled" do
|
314
|
+
let(:synchronize) { true }
|
315
|
+
let(:enabled) { "on" }
|
316
|
+
|
317
|
+
let(:full_command) do
|
318
|
+
"#{tmux_part} #{window_option_set_part} #{target_part} #{synchronize_panes_part} #{enabled}"
|
319
|
+
end
|
320
|
+
|
321
|
+
it "should set the synchronize-panes window option on" do
|
322
|
+
expect(window.tmux_synchronize_panes).to eq full_command
|
323
|
+
end
|
324
|
+
end
|
325
|
+
end
|
326
|
+
|
327
|
+
describe "#tmux_new_window_command" do
|
328
|
+
include_context "window command context"
|
329
|
+
|
206
330
|
let(:window_part) { "new-window" }
|
207
331
|
let(:name_part) { window.tmux_window_name_option }
|
208
332
|
let(:target_part) { "-t #{window.tmux_window_target}" }
|
@@ -0,0 +1,44 @@
|
|
1
|
+
RSpec::Matchers.alias_matcher :be_a_pane, :a_pane
|
2
|
+
RSpec::Matchers.define :a_pane do
|
3
|
+
attr_reader :commands
|
4
|
+
|
5
|
+
match do
|
6
|
+
result = is_pane
|
7
|
+
|
8
|
+
result && attributes_match if @expected_attrs
|
9
|
+
result &&= commands_match if commands
|
10
|
+
|
11
|
+
result
|
12
|
+
end
|
13
|
+
|
14
|
+
failure_message do |actual|
|
15
|
+
return "Expected #{actual} to be a Tmuxinator::Pane" unless is_pane
|
16
|
+
|
17
|
+
msg = "Actual pane does not match expected"
|
18
|
+
msg << "\n Expected #{@commands} but has #{actual.commands}" if @commands
|
19
|
+
msg << "\n Expected pane to have #{@expected_attrs}" if @expected_attrs
|
20
|
+
end
|
21
|
+
|
22
|
+
chain :with do |attrs|
|
23
|
+
@expected_attrs = attrs
|
24
|
+
end
|
25
|
+
|
26
|
+
chain :with_commands do |*expected|
|
27
|
+
@commands = expected
|
28
|
+
end
|
29
|
+
alias_method :and_commands, :with_commands
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def attributes_match
|
34
|
+
expect(@actual).to have_attributes(@expected_attrs)
|
35
|
+
end
|
36
|
+
|
37
|
+
def commands_match
|
38
|
+
@actual.commands == commands
|
39
|
+
end
|
40
|
+
|
41
|
+
def is_pane
|
42
|
+
@actual.is_a? Tmuxinator::Pane
|
43
|
+
end
|
44
|
+
end
|
data/spec/spec_helper.rb
CHANGED
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: 0.
|
4
|
+
version: 0.9.0
|
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: 2016-
|
12
|
+
date: 2016-10-08 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: thor
|
@@ -64,12 +64,10 @@ email:
|
|
64
64
|
- allen.bargi@gmail.com
|
65
65
|
- chris@chowie.net
|
66
66
|
executables:
|
67
|
-
- mux
|
68
67
|
- tmuxinator
|
69
68
|
extensions: []
|
70
69
|
extra_rdoc_files: []
|
71
70
|
files:
|
72
|
-
- bin/mux
|
73
71
|
- bin/tmuxinator
|
74
72
|
- completion/mux.fish
|
75
73
|
- completion/tmuxinator.bash
|
@@ -92,6 +90,7 @@ files:
|
|
92
90
|
- spec/fixtures/detach.yml
|
93
91
|
- spec/fixtures/nameless_window.yml
|
94
92
|
- spec/fixtures/noname.yml
|
93
|
+
- spec/fixtures/noroot.yml
|
95
94
|
- spec/fixtures/nowindows.yml
|
96
95
|
- spec/fixtures/sample.deprecations.yml
|
97
96
|
- spec/fixtures/sample.yml
|
@@ -105,6 +104,7 @@ files:
|
|
105
104
|
- spec/lib/tmuxinator/util_spec.rb
|
106
105
|
- spec/lib/tmuxinator/wemux_support_spec.rb
|
107
106
|
- spec/lib/tmuxinator/window_spec.rb
|
107
|
+
- spec/matchers/pane_matcher.rb
|
108
108
|
- spec/spec_helper.rb
|
109
109
|
homepage: https://github.com/tmuxinator/tmuxinator
|
110
110
|
licenses:
|
@@ -131,7 +131,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
131
131
|
version: 1.8.23
|
132
132
|
requirements: []
|
133
133
|
rubyforge_project:
|
134
|
-
rubygems_version: 2.
|
134
|
+
rubygems_version: 2.6.6
|
135
135
|
signing_key:
|
136
136
|
specification_version: 4
|
137
137
|
summary: Create and manage complex tmux sessions easily.
|
@@ -140,6 +140,7 @@ test_files:
|
|
140
140
|
- spec/fixtures/detach.yml
|
141
141
|
- spec/fixtures/nameless_window.yml
|
142
142
|
- spec/fixtures/noname.yml
|
143
|
+
- spec/fixtures/noroot.yml
|
143
144
|
- spec/fixtures/nowindows.yml
|
144
145
|
- spec/fixtures/sample.deprecations.yml
|
145
146
|
- spec/fixtures/sample.yml
|
@@ -153,4 +154,5 @@ test_files:
|
|
153
154
|
- spec/lib/tmuxinator/util_spec.rb
|
154
155
|
- spec/lib/tmuxinator/wemux_support_spec.rb
|
155
156
|
- spec/lib/tmuxinator/window_spec.rb
|
157
|
+
- spec/matchers/pane_matcher.rb
|
156
158
|
- spec/spec_helper.rb
|
data/bin/mux
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
tmuxinator
|