tmuxinator 0.5.0 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/.rspec +1 -1
- data/.travis.yml +5 -0
- data/CHANGELOG.md +36 -0
- data/CONTRIBUTING.md +29 -0
- data/Gemfile +4 -7
- data/{LICENSE.txt → LICENSE} +1 -1
- data/README.md +128 -118
- data/Rakefile +1 -72
- data/bin/mux +8 -3
- data/bin/tmuxinator +8 -3
- data/completion/tmuxinator.bash +18 -0
- data/completion/tmuxinator.zsh +20 -0
- data/lib/tmuxinator.rb +13 -7
- data/lib/tmuxinator/assets/sample.yml +24 -11
- data/lib/tmuxinator/assets/template.erb +47 -0
- data/lib/tmuxinator/cli.rb +121 -145
- data/lib/tmuxinator/config.rb +92 -0
- data/lib/tmuxinator/deprecations.rb +19 -0
- data/lib/tmuxinator/pane.rb +36 -0
- data/lib/tmuxinator/project.rb +142 -0
- data/lib/tmuxinator/util.rb +14 -0
- data/lib/tmuxinator/version.rb +3 -0
- data/lib/tmuxinator/window.rb +76 -0
- data/spec/factories/projects.rb +17 -0
- data/spec/fixtures/sample.deprecations.yml +35 -0
- data/spec/fixtures/sample.yml +35 -0
- data/spec/lib/tmuxinator/cli_spec.rb +230 -0
- data/spec/lib/tmuxinator/config_spec.rb +121 -0
- data/spec/lib/tmuxinator/pane_spec.rb +7 -0
- data/spec/lib/tmuxinator/project_spec.rb +209 -0
- data/spec/lib/tmuxinator/util_spec.rb +4 -0
- data/spec/lib/tmuxinator/window_spec.rb +62 -0
- data/spec/spec_helper.rb +28 -8
- data/tmuxinator.gemspec +47 -65
- metadata +186 -62
- data/.document +0 -5
- data/Gemfile.lock +0 -28
- data/TODO +0 -4
- data/VERSION +0 -1
- data/bin/tmuxinator_completion +0 -29
- data/lib/tmuxinator/assets/tmux_config.tmux +0 -37
- data/lib/tmuxinator/config_writer.rb +0 -104
- data/lib/tmuxinator/helper.rb +0 -19
- data/spec/tmuxinator_spec.rb +0 -54
@@ -0,0 +1,92 @@
|
|
1
|
+
require "pry"
|
2
|
+
module Tmuxinator
|
3
|
+
class Config
|
4
|
+
class << self
|
5
|
+
def root
|
6
|
+
Dir.mkdir("#{ENV["HOME"]}/.tmuxinator") unless File.directory?(File.expand_path("~/.tmuxinator"))
|
7
|
+
"#{ENV["HOME"]}/.tmuxinator"
|
8
|
+
end
|
9
|
+
|
10
|
+
def sample
|
11
|
+
"#{File.dirname(__FILE__)}/assets/sample.yml"
|
12
|
+
end
|
13
|
+
|
14
|
+
def default
|
15
|
+
"#{ENV["HOME"]}/.tmuxinator/default.yml"
|
16
|
+
end
|
17
|
+
|
18
|
+
def default?
|
19
|
+
exists?(default)
|
20
|
+
end
|
21
|
+
|
22
|
+
def installed?
|
23
|
+
Kernel.system("which tmux > /dev/null")
|
24
|
+
end
|
25
|
+
|
26
|
+
def editor?
|
27
|
+
!ENV["EDITOR"].nil? && !ENV["EDITOR"].empty?
|
28
|
+
end
|
29
|
+
|
30
|
+
def shell?
|
31
|
+
!ENV["SHELL"].nil? && !ENV["SHELL"].empty?
|
32
|
+
end
|
33
|
+
|
34
|
+
def exists?(name)
|
35
|
+
unless project(name).nil?
|
36
|
+
File.exists?(project(name))
|
37
|
+
else
|
38
|
+
false
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def project(name)
|
43
|
+
projects = Dir.glob("#{root}/**/*.yml")
|
44
|
+
projects.select { |project| project =~ /#{name}.yml/ }.first
|
45
|
+
end
|
46
|
+
|
47
|
+
def template
|
48
|
+
"#{File.dirname(__FILE__)}/assets/template.erb"
|
49
|
+
end
|
50
|
+
|
51
|
+
def configs
|
52
|
+
Dir["#{Tmuxinator::Config.root}/*.yml"].sort.map do |path|
|
53
|
+
path.gsub("#{Tmuxinator::Config.root}/", "").gsub(".yml", "")
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def validate(name)
|
58
|
+
unless Tmuxinator::Config.exists?(name)
|
59
|
+
puts "Project #{name} doesn't exist."
|
60
|
+
exit!
|
61
|
+
end
|
62
|
+
|
63
|
+
config_path = Tmuxinator::Config.project(name)
|
64
|
+
|
65
|
+
yaml = begin
|
66
|
+
YAML.load(File.read(config_path))
|
67
|
+
rescue SyntaxError, StandardError
|
68
|
+
puts "Failed to parse config file. Please check your formatting."
|
69
|
+
exit!
|
70
|
+
end
|
71
|
+
|
72
|
+
project = Tmuxinator::Project.new(yaml)
|
73
|
+
|
74
|
+
unless project.windows?
|
75
|
+
puts "Your project file should include some windows."
|
76
|
+
exit!
|
77
|
+
end
|
78
|
+
|
79
|
+
unless project.root?
|
80
|
+
puts "Your project file didn't specify a 'project_root'"
|
81
|
+
exit!
|
82
|
+
end
|
83
|
+
|
84
|
+
unless project.name?
|
85
|
+
puts "Your project file didn't specify a 'project_name'"
|
86
|
+
end
|
87
|
+
|
88
|
+
project
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Tmuxinator
|
2
|
+
module Deprecations
|
3
|
+
def rvm?
|
4
|
+
yaml["rvm"].present?
|
5
|
+
end
|
6
|
+
|
7
|
+
def rbenv?
|
8
|
+
yaml["rbenv"].present?
|
9
|
+
end
|
10
|
+
|
11
|
+
def pre_tab?
|
12
|
+
yaml["pre_tab"].present?
|
13
|
+
end
|
14
|
+
|
15
|
+
def cli_args?
|
16
|
+
yaml["cli_args"].present?
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Tmuxinator
|
2
|
+
class Pane
|
3
|
+
attr_reader :command, :project, :index, :project, :tab
|
4
|
+
|
5
|
+
def initialize(command, index, project, tab)
|
6
|
+
@command = command
|
7
|
+
@index = index
|
8
|
+
@project = project
|
9
|
+
@tab = tab
|
10
|
+
end
|
11
|
+
|
12
|
+
def tmux_window_and_pane_target
|
13
|
+
"#{project.name}:#{tab.index + project.base_index}.#{index + project.base_index}"
|
14
|
+
end
|
15
|
+
|
16
|
+
def tmux_pre_command
|
17
|
+
tab.pre.present? ? "#{project.tmux} send-keys -t #{tmux_window_and_pane_target} #{tab.pre.shellescape} C-m" : ""
|
18
|
+
end
|
19
|
+
|
20
|
+
def tmux_pre_window_command
|
21
|
+
project.pre_window.present? ? "#{project.tmux} send-keys -t #{tmux_window_and_pane_target} #{project.pre_window.shellescape} C-m" : ""
|
22
|
+
end
|
23
|
+
|
24
|
+
def tmux_main_command
|
25
|
+
command.present? ? "#{project.tmux} send-keys -t #{project.name}:#{tab.index + project.base_index}.#{index + tab.project.base_index} #{command.shellescape} C-m" : ""
|
26
|
+
end
|
27
|
+
|
28
|
+
def tmux_split_command
|
29
|
+
"#{project.tmux} splitw -t #{tab.tmux_window_target}"
|
30
|
+
end
|
31
|
+
|
32
|
+
def last?
|
33
|
+
index == tab.panes.length - 1
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,142 @@
|
|
1
|
+
module Tmuxinator
|
2
|
+
class Project
|
3
|
+
include Tmuxinator::Util
|
4
|
+
include Tmuxinator::Deprecations
|
5
|
+
|
6
|
+
attr_reader :yaml
|
7
|
+
|
8
|
+
def initialize(yaml)
|
9
|
+
@yaml = yaml
|
10
|
+
end
|
11
|
+
|
12
|
+
def render
|
13
|
+
template = File.read(Tmuxinator::Config.template)
|
14
|
+
Erubis::Eruby.new(template).result(binding)
|
15
|
+
end
|
16
|
+
|
17
|
+
def windows
|
18
|
+
windows_yml =
|
19
|
+
if yaml["tabs"].present?
|
20
|
+
yaml["tabs"]
|
21
|
+
else
|
22
|
+
yaml["windows"]
|
23
|
+
end
|
24
|
+
|
25
|
+
@windows ||= windows_yml.map.with_index do |window_yml, index|
|
26
|
+
Tmuxinator::Window.new(window_yml, index, self)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def root
|
31
|
+
if yaml["project_root"].present?
|
32
|
+
yaml["project_root"]
|
33
|
+
else
|
34
|
+
yaml["root"]
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def name
|
39
|
+
if yaml["project_name"].present?
|
40
|
+
yaml["project_name"].shellescape
|
41
|
+
else
|
42
|
+
yaml["name"].shellescape
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def pre
|
47
|
+
yaml["pre"]
|
48
|
+
end
|
49
|
+
|
50
|
+
def pre_window
|
51
|
+
if rbenv?
|
52
|
+
"rbenv shell #{yaml["rbenv"]}"
|
53
|
+
elsif rvm?
|
54
|
+
"rvm use #{yaml["rvm"]}"
|
55
|
+
elsif pre_tab?
|
56
|
+
yaml["pre_tab"]
|
57
|
+
else
|
58
|
+
yaml["pre_window"]
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def tmux
|
63
|
+
"tmux#{tmux_options}#{socket}"
|
64
|
+
end
|
65
|
+
|
66
|
+
def socket
|
67
|
+
if socket_path.present?
|
68
|
+
" -S #{socket_path}"
|
69
|
+
elsif socket_name.present?
|
70
|
+
" -L #{socket_name}"
|
71
|
+
else
|
72
|
+
nil
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def socket_name
|
77
|
+
yaml["socket_name"]
|
78
|
+
end
|
79
|
+
|
80
|
+
def socket_path
|
81
|
+
yaml["socket_path"]
|
82
|
+
end
|
83
|
+
|
84
|
+
def tmux_options
|
85
|
+
if cli_args?
|
86
|
+
" #{yaml["cli_args"].strip}"
|
87
|
+
elsif tmux_options?
|
88
|
+
" #{yaml["tmux_options"].strip}"
|
89
|
+
else
|
90
|
+
""
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def base_index
|
95
|
+
`tmux start-server\\\; show-window-options -g | grep pane-base-index`.split(/\s/).last.to_i
|
96
|
+
end
|
97
|
+
|
98
|
+
def tmux_options?
|
99
|
+
yaml["tmux_options"].present?
|
100
|
+
end
|
101
|
+
|
102
|
+
def windows?
|
103
|
+
windows.any?
|
104
|
+
end
|
105
|
+
|
106
|
+
def root?
|
107
|
+
root.present?
|
108
|
+
end
|
109
|
+
|
110
|
+
def name?
|
111
|
+
name.present?
|
112
|
+
end
|
113
|
+
|
114
|
+
def window(i)
|
115
|
+
"#{name}:#{i}"
|
116
|
+
end
|
117
|
+
|
118
|
+
def send_pane_command(cmd, window_index, pane_index)
|
119
|
+
if cmd.blank?
|
120
|
+
""
|
121
|
+
else
|
122
|
+
"#{tmux} send-keys -t #{window(window_index)} #{cmd.shellescape} C-m"
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
def send_keys(cmd, window_index)
|
127
|
+
if cmd.blank?
|
128
|
+
""
|
129
|
+
else
|
130
|
+
"#{tmux} send-keys -t #{window(window_index)} #{cmd.shellescape} C-m"
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
def deprecations
|
135
|
+
deprecations = []
|
136
|
+
deprecations << "DEPRECATION: rbenv/rvm specific options have been replaced by the pre_tab option and will not be supported in 0.8.0." if yaml["rbenv"] || yaml["rvm"]
|
137
|
+
deprecations << "DEPRECATION: The tabs option has been replaced by the window option and will not be supported in 0.8.0." if yaml["tabs"].present?
|
138
|
+
deprecations << "DEPRECATION: The cli_args option has been replaced by the tmux_options option and will not be supported in 0.8.0." if yaml["cli_args"].present?
|
139
|
+
deprecations
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
module Tmuxinator
|
2
|
+
class Window
|
3
|
+
attr_reader :name, :panes, :layout, :command, :index, :project
|
4
|
+
|
5
|
+
def initialize(window_yaml, index, project)
|
6
|
+
@name = window_yaml.keys.first.present? ? window_yaml.keys.first.shellescape : nil
|
7
|
+
@panes = []
|
8
|
+
@layout = nil
|
9
|
+
@pre = nil
|
10
|
+
@command = nil
|
11
|
+
@project = project
|
12
|
+
@index = index
|
13
|
+
|
14
|
+
value = window_yaml.values.first
|
15
|
+
|
16
|
+
if value.is_a?(Hash)
|
17
|
+
@layout = value["layout"].present? ? value["layout"].shellescape : nil
|
18
|
+
@pre = value["pre"] if value["pre"].present?
|
19
|
+
|
20
|
+
@panes = build_panes(value["panes"])
|
21
|
+
else
|
22
|
+
@command = value
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def build_panes(pane_yml)
|
27
|
+
if pane_yml.is_a?(Array)
|
28
|
+
pane_yml.map.with_index do |pane_cmd, index|
|
29
|
+
Tmuxinator::Pane.new(pane_cmd, index, project, self)
|
30
|
+
end
|
31
|
+
else
|
32
|
+
Tmuxinator::Pane.new(pane_yml, index, project, self)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def pre
|
37
|
+
if @pre.present?
|
38
|
+
if @pre.is_a?(Array)
|
39
|
+
@pre.join(" && ")
|
40
|
+
elsif @pre.is_a?(String)
|
41
|
+
@pre
|
42
|
+
end
|
43
|
+
else
|
44
|
+
""
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def panes?
|
49
|
+
panes.any?
|
50
|
+
end
|
51
|
+
|
52
|
+
def tmux_window_target
|
53
|
+
"#{project.name}:#{index + project.base_index}"
|
54
|
+
end
|
55
|
+
|
56
|
+
def tmux_pre_window_command
|
57
|
+
project.pre_window.present? ? "#{project.tmux} send-keys -t #{tmux_window_target} #{project.pre_window.shellescape} C-m" : ""
|
58
|
+
end
|
59
|
+
|
60
|
+
def tmux_main_command
|
61
|
+
command.present? ? "#{project.tmux} send-keys -t #{project.name}:#{index + project.base_index} #{command.shellescape} C-m" : ""
|
62
|
+
end
|
63
|
+
|
64
|
+
def tmux_new_window_command
|
65
|
+
"#{project.tmux} new-window -t #{tmux_window_target} -n #{name}"
|
66
|
+
end
|
67
|
+
|
68
|
+
def tmux_layout_command
|
69
|
+
"#{project.tmux} select-layout -t #{tmux_window_target} #{layout}"
|
70
|
+
end
|
71
|
+
|
72
|
+
def tmux_select_first_pane
|
73
|
+
"#{project.tmux} select-pane -t #{tmux_window_target}.#{panes.first.index + project.base_index}"
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
FactoryGirl.define do
|
2
|
+
factory :project, :class => Tmuxinator::Project do
|
3
|
+
ignore do
|
4
|
+
file { YAML.load(File.read("#{File.expand_path("spec/fixtures/sample.yml")}")) }
|
5
|
+
end
|
6
|
+
|
7
|
+
initialize_with { Tmuxinator::Project.new(file) }
|
8
|
+
end
|
9
|
+
|
10
|
+
factory :project_with_deprecations, :class => Tmuxinator::Project do
|
11
|
+
ignore do
|
12
|
+
file { YAML.load(File.read("#{File.expand_path("spec/fixtures/sample.deprecations.yml")}")) }
|
13
|
+
end
|
14
|
+
|
15
|
+
initialize_with { Tmuxinator::Project.new(file) }
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# ~/.tmuxinator/sample.deprecations.yml
|
2
|
+
# you can make as many tabs as you wish...
|
3
|
+
|
4
|
+
project_name: sample
|
5
|
+
project_root: ~/test
|
6
|
+
socket_name: foo # Remove to use default socket
|
7
|
+
pre: sudo /etc/rc.d/mysqld start # Runs before everything
|
8
|
+
rbenv: 2.0.0-p247
|
9
|
+
cli_args: -v -2 # Pass arguments to tmux
|
10
|
+
tabs:
|
11
|
+
- editor:
|
12
|
+
pre:
|
13
|
+
- echo "I get run in each pane, before each pane command!"
|
14
|
+
-
|
15
|
+
layout: main-vertical
|
16
|
+
panes:
|
17
|
+
- vim
|
18
|
+
- #empty, will just run plain bash
|
19
|
+
- top
|
20
|
+
- shell: git pull
|
21
|
+
- guard:
|
22
|
+
layout: tiled
|
23
|
+
pre:
|
24
|
+
- echo "I get run in each pane."
|
25
|
+
- echo "Before each pane command!"
|
26
|
+
panes:
|
27
|
+
-
|
28
|
+
- #empty, will just run plain bash
|
29
|
+
-
|
30
|
+
- database: bundle exec rails db
|
31
|
+
- server: bundle exec rails s
|
32
|
+
- logs: tail -f log/development.log
|
33
|
+
- console: bundle exec rails c
|
34
|
+
- capistrano:
|
35
|
+
- server: ssh user@example.com
|