teamocil 0.3.9 → 0.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/README.md +28 -15
- data/Rakefile +4 -4
- data/examples/four-splits.yml +8 -0
- data/examples/one-and-three-splits.yml +8 -0
- data/examples/six-splits.yml +10 -0
- data/examples/two-horizontal-splits.yml +6 -0
- data/examples/two-vertical-splits.yml +6 -0
- data/lib/teamocil.rb +5 -4
- data/lib/teamocil/cli.rb +10 -12
- data/lib/teamocil/layout.rb +12 -14
- data/lib/teamocil/layout/session.rb +4 -6
- data/lib/teamocil/layout/split.rb +5 -8
- data/lib/teamocil/layout/window.rb +15 -7
- data/spec/cli_spec.rb +24 -31
- data/spec/layout_spec.rb +74 -63
- data/spec/mock/cli.rb +10 -13
- data/spec/mock/layout.rb +2 -6
- data/spec/spec_helper.rb +2 -4
- data/teamocil.gemspec +1 -1
- metadata +14 -30
- data/examples/simple-four-splits.yml +0 -13
- data/examples/simple-one-and-three-splits.yml +0 -12
- data/examples/simple-six-splits.yml +0 -18
- data/examples/simple-two-horitonzal-splits.yml +0 -6
- data/examples/simple-two-vertical-splits.yml +0 -6
checksums.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
ZjUwMTBkNTkxZjQyMjMzOTU5ZTAxYmZlN2EyZDdlMDNjZmFmOWRlZQ==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
NTY4Nzc1ZjAzMjQzZTYyOTA4NzhhNWRiNjI4NGE0NzFiNWZhYzMzOQ==
|
7
|
+
!binary "U0hBNTEy":
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
ZmVhMDQ3ZWUyOGZkOGYwZWE4NzdlNTcwZjkxNTZmYmEyZjYwMDg1MzBjYjEz
|
10
|
+
MjU5MGEwZmQ5YWM0ZjI0MzJmMDFjYzM3Y2M1ZjVhMGJmMTQxNjk1ZWE5N2Q3
|
11
|
+
ZjZhZjExNDEyZDlhOWQ2MmRjMmQxMDJlYWQ3M2UyYWE4MWMxNjY=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
NTZjOWZmMTFmMDNkNGFkNGFhZjkxNTdlZjY2MDhiNWYyNWI0ZGYwOTExMTgx
|
14
|
+
MzY4NjYyYWVhOWZlNGViMmQ3ZGJjMWZiY2QyZTliMjExOTdhMjQxNDcxMGM2
|
15
|
+
ZDgwY2EzOTcwNzE5NzdlZDgxNWQ4Yjg0YjU2M2IyNzNiY2IyZWE=
|
data/README.md
CHANGED
@@ -51,9 +51,25 @@ If you are not using a top-level `session` key, then the first key of your layou
|
|
51
51
|
* `root` (the directory in which every split will be created)
|
52
52
|
* `filters` (a hash of `before` and `after` commands to run for each split)
|
53
53
|
* `clear` (whether or not to prepend a `clear` command before the `before` filters list)
|
54
|
-
* `layout` (a layout name or serialized string supported by `tmux
|
54
|
+
* `layout` (a layout name or serialized string supported by the `tmux select-layout` command)
|
55
55
|
* `splits` (an array of split items)
|
56
|
-
* `options` (a hash of tmux options, see `man tmux` for a list)
|
56
|
+
* `options` (a hash of `tmux` options, see `man tmux` for a list)
|
57
|
+
|
58
|
+
#### Notes
|
59
|
+
|
60
|
+
If you want to use a custom value for the `layout` key, running this command will give you the layout of the current window:
|
61
|
+
|
62
|
+
```bash
|
63
|
+
$ tmux list-windows -F "#{window_active} #{window_layout}" | grep "^1" | cut -d " " -f 2
|
64
|
+
```
|
65
|
+
|
66
|
+
You can then use the value as a string, like so:
|
67
|
+
|
68
|
+
```yaml
|
69
|
+
- name: "a-window-with-weird-layout"
|
70
|
+
layout: "4d71,204x51,0,0{101x51,0,0,114,102x51,102,0[102x10,102,0,118,102x40,102,11,115]}"
|
71
|
+
splits: …
|
72
|
+
```
|
57
73
|
|
58
74
|
#### Example
|
59
75
|
|
@@ -66,15 +82,17 @@ windows:
|
|
66
82
|
root: "~/Projects/foo-www"
|
67
83
|
filters:
|
68
84
|
before:
|
69
|
-
- "echo 'Let’s use ruby-1.9.
|
70
|
-
- "
|
85
|
+
- "echo 'Let’s use ruby-1.9.3 for each split in this window.'"
|
86
|
+
- "rbenv local 1.9.3-p374"
|
71
87
|
splits:
|
72
88
|
[splits list]
|
73
89
|
- name: "my-second-window"
|
90
|
+
layout: tiled
|
74
91
|
root: "~/Projects/foo-api"
|
75
92
|
splits:
|
76
93
|
[splits list]
|
77
94
|
- name: "my-third-window"
|
95
|
+
layout: main-vertical
|
78
96
|
root: "~/Projects/foo-daemons"
|
79
97
|
splits:
|
80
98
|
[splits list]
|
@@ -82,7 +100,7 @@ windows:
|
|
82
100
|
|
83
101
|
### Splits
|
84
102
|
|
85
|
-
Every window must define an array of splits that will be created within it. A vertical or horizontal split will be created, depending on whether the `width` or `height` parameter is used.
|
103
|
+
Every window must define an array of splits that will be created within it. A vertical or horizontal split will be created, depending on whether the `width` or `height` parameter is used. If a `layout` option is used for the window, the `width` and `height` attributes won’t have any effect.
|
86
104
|
|
87
105
|
#### Item keys
|
88
106
|
|
@@ -98,18 +116,17 @@ Every window must define an array of splits that will be created within it. A ve
|
|
98
116
|
windows:
|
99
117
|
- name: "my-first-window"
|
100
118
|
root: "~/Projects/foo-www"
|
119
|
+
layout: even-vertical
|
101
120
|
filters:
|
102
|
-
before: "
|
121
|
+
before: "rbenv local 2.0.0-p0"
|
103
122
|
after: "echo 'I am done initializing this split.'"
|
104
123
|
splits:
|
105
124
|
- cmd: "git status"
|
106
125
|
- cmd: "bundle exec rails server --port 4000"
|
107
126
|
focus: true
|
108
|
-
width: 50
|
109
127
|
- cmd:
|
110
128
|
- "sudo service memcached start"
|
111
129
|
- "sudo service mongodb start"
|
112
|
-
height: 50
|
113
130
|
```
|
114
131
|
|
115
132
|
## Layout examples
|
@@ -124,10 +141,10 @@ See more example files in the `examples` directory.
|
|
124
141
|
windows:
|
125
142
|
- name: "sample-two-splits"
|
126
143
|
root: "~/Code/sample/www"
|
144
|
+
layout: even-horizontal
|
127
145
|
splits:
|
128
146
|
- cmd: ["pwd", "ls -la"]
|
129
147
|
- cmd: "rails server --port 3000"
|
130
|
-
width: 50
|
131
148
|
```
|
132
149
|
|
133
150
|
|
@@ -153,16 +170,12 @@ windows:
|
|
153
170
|
windows:
|
154
171
|
- name: "sample-four-splits"
|
155
172
|
root: "~/Code/sample/www"
|
173
|
+
layout: tiled
|
156
174
|
splits:
|
157
175
|
- cmd: "pwd"
|
158
176
|
- cmd: "pwd"
|
159
|
-
width: 50
|
160
177
|
- cmd: "pwd"
|
161
|
-
height: 50
|
162
|
-
target: "bottom-right"
|
163
178
|
- cmd: "pwd"
|
164
|
-
height: 50
|
165
|
-
target: "bottom-left"
|
166
179
|
```
|
167
180
|
|
168
181
|
#### Result of `$ teamocil sample-2`
|
@@ -226,4 +239,4 @@ Take a look at the `spec` folder before you do, and make sure `bundle exec rake
|
|
226
239
|
|
227
240
|
## License
|
228
241
|
|
229
|
-
Teamocil is © 2011-
|
242
|
+
Teamocil is © 2011-2013 [Rémi Prévost](http://exomel.com) and may be freely distributed under the [MIT license](https://github.com/remiprev/teamocil/blob/master/LICENSE). See the `LICENSE` file.
|
data/Rakefile
CHANGED
@@ -7,13 +7,13 @@ require "rspec/core/rake_task"
|
|
7
7
|
task :default => :spec
|
8
8
|
|
9
9
|
desc "Run all specs"
|
10
|
-
RSpec::Core::RakeTask.new(:spec) do |task|
|
10
|
+
RSpec::Core::RakeTask.new(:spec) do |task|
|
11
11
|
task.pattern = "spec/**/*_spec.rb"
|
12
12
|
task.rspec_opts = "--colour --format=documentation"
|
13
|
-
end
|
13
|
+
end
|
14
14
|
|
15
15
|
desc "Generate YARD Documentation"
|
16
|
-
YARD::Rake::YardocTask.new do |task|
|
16
|
+
YARD::Rake::YardocTask.new do |task|
|
17
17
|
task.options = [
|
18
18
|
"-o", File.expand_path("../doc", __FILE__),
|
19
19
|
"--readme=README.md",
|
@@ -25,4 +25,4 @@ YARD::Rake::YardocTask.new do |task| # {{{
|
|
25
25
|
"--title=Teamocil",
|
26
26
|
]
|
27
27
|
task.files = ["lib/**/*.rb"]
|
28
|
-
end
|
28
|
+
end
|
data/lib/teamocil.rb
CHANGED
data/lib/teamocil/cli.rb
CHANGED
@@ -5,14 +5,13 @@ require 'erb'
|
|
5
5
|
module Teamocil
|
6
6
|
# This class handles interaction with the `tmux` utility.
|
7
7
|
class CLI
|
8
|
-
|
9
8
|
attr_accessor :layout, :layouts
|
10
9
|
|
11
10
|
# Initialize a new run of `tmux`
|
12
11
|
#
|
13
12
|
# @param argv [Hash] the command line parameters hash (usually `ARGV`).
|
14
13
|
# @param env [Hash] the environment variables hash (usually `ENV`).
|
15
|
-
def initialize(argv, env)
|
14
|
+
def initialize(argv, env)
|
16
15
|
parse_options! argv
|
17
16
|
layout_path = env["TEAMOCIL_PATH"] || File.join("#{env["HOME"]}", ".teamocil")
|
18
17
|
|
@@ -44,10 +43,10 @@ module Teamocil
|
|
44
43
|
end
|
45
44
|
@layout.execute_commands(@layout.generate_commands)
|
46
45
|
end
|
47
|
-
end
|
46
|
+
end
|
48
47
|
|
49
48
|
# Parse the command line options
|
50
|
-
def parse_options!(args)
|
49
|
+
def parse_options!(args)
|
51
50
|
@options = {}
|
52
51
|
opts = ::OptionParser.new do |opts|
|
53
52
|
opts.banner = "Usage: teamocil [options] <layout>
|
@@ -76,28 +75,27 @@ module Teamocil
|
|
76
75
|
|
77
76
|
end
|
78
77
|
opts.parse! args
|
79
|
-
end
|
78
|
+
end
|
80
79
|
|
81
80
|
# Return an array of available layouts
|
82
81
|
#
|
83
82
|
# @param path [String] the path used to look for layouts
|
84
|
-
def get_layouts(path)
|
83
|
+
def get_layouts(path)
|
85
84
|
Dir.glob(File.join(path, "*.yml")).map { |file| File.basename(file).gsub(/\..+$/, "") }.sort
|
86
|
-
end
|
85
|
+
end
|
87
86
|
|
88
87
|
# Print each layout on a single line
|
89
|
-
def print_layouts
|
88
|
+
def print_layouts
|
90
89
|
STDOUT.puts @layouts.join("\n")
|
91
90
|
exit 0
|
92
|
-
end
|
91
|
+
end
|
93
92
|
|
94
93
|
# Print an error message and exit the utility
|
95
94
|
#
|
96
95
|
# @param msg [Mixed] something to print before exiting.
|
97
|
-
def bail(msg)
|
96
|
+
def bail(msg)
|
98
97
|
STDERR.puts "[teamocil] #{msg}"
|
99
98
|
exit 1
|
100
|
-
end
|
101
|
-
|
99
|
+
end
|
102
100
|
end
|
103
101
|
end
|
data/lib/teamocil/layout.rb
CHANGED
@@ -1,46 +1,44 @@
|
|
1
|
-
|
1
|
+
require "teamocil/layout/session"
|
2
|
+
require "teamocil/layout/window"
|
3
|
+
require "teamocil/layout/split"
|
2
4
|
|
5
|
+
module Teamocil
|
3
6
|
# This class act as a wrapper around a tmux YAML layout file
|
4
7
|
class Layout
|
5
|
-
autoload :Session, "teamocil/layout/session"
|
6
|
-
autoload :Window, "teamocil/layout/window"
|
7
|
-
autoload :Split, "teamocil/layout/split"
|
8
|
-
|
9
8
|
attr_reader :session
|
10
9
|
|
11
10
|
# Initialize a new layout from a hash
|
12
11
|
#
|
13
12
|
# @param layout [Hash] the parsed layout
|
14
13
|
# @param options [Hash] some options
|
15
|
-
def initialize(layout, options={})
|
14
|
+
def initialize(layout, options={})
|
16
15
|
@layout = layout
|
17
16
|
@options = options
|
18
|
-
end
|
17
|
+
end
|
19
18
|
|
20
19
|
# Generate tmux commands based on the data found in the layout file
|
21
20
|
#
|
22
21
|
# @return [Array] an array of shell commands to send
|
23
|
-
def generate_commands
|
22
|
+
def generate_commands
|
24
23
|
@session.generate_commands
|
25
|
-
end
|
24
|
+
end
|
26
25
|
|
27
26
|
# Compile the layout into objects
|
28
27
|
#
|
29
28
|
# @return [Session]
|
30
|
-
def compile!
|
29
|
+
def compile!
|
31
30
|
if @layout["session"].nil?
|
32
31
|
@session = Session.new @options, "windows" => @layout["windows"]
|
33
32
|
else
|
34
33
|
@session = Session.new @options, @layout["session"]
|
35
34
|
end
|
36
|
-
end
|
35
|
+
end
|
37
36
|
|
38
37
|
# Execute each command in the shell
|
39
38
|
#
|
40
39
|
# @param commands [Array] an array of complete commands to send to the shell
|
41
|
-
def execute_commands(commands)
|
40
|
+
def execute_commands(commands)
|
42
41
|
`#{commands.join("; ")}`
|
43
|
-
end
|
44
|
-
|
42
|
+
end
|
45
43
|
end
|
46
44
|
end
|
@@ -1,6 +1,5 @@
|
|
1
1
|
module Teamocil
|
2
2
|
class Layout
|
3
|
-
|
4
3
|
# This class represents a session within tmux
|
5
4
|
class Session
|
6
5
|
attr_reader :options, :windows, :name
|
@@ -9,22 +8,21 @@ module Teamocil
|
|
9
8
|
#
|
10
9
|
# @param options [Hash] the options, mostly passed by the CLI
|
11
10
|
# @param attrs [Hash] the session data from the layout file
|
12
|
-
def initialize(options, attrs={})
|
11
|
+
def initialize(options, attrs={})
|
13
12
|
raise Teamocil::Error::LayoutError.new("You must specify a `windows` or `session` key for your layout.") unless attrs["windows"]
|
14
13
|
@name = attrs["name"] || "teamocil-session"
|
15
14
|
@windows = attrs["windows"].each_with_index.map { |window, window_index| Window.new(self, window_index, window) }
|
16
15
|
@options = options
|
17
|
-
end
|
16
|
+
end
|
18
17
|
|
19
18
|
# Generate commands to send to tmux
|
20
19
|
#
|
21
20
|
# @return [Array]
|
22
|
-
def generate_commands
|
21
|
+
def generate_commands
|
23
22
|
commands = []
|
24
23
|
commands << "tmux rename-session \"#{@name}\"" unless @name.nil?
|
25
24
|
commands << @windows.map(&:generate_commands)
|
26
|
-
end
|
27
|
-
|
25
|
+
end
|
28
26
|
end
|
29
27
|
end
|
30
28
|
end
|
@@ -1,6 +1,5 @@
|
|
1
1
|
module Teamocil
|
2
2
|
class Layout
|
3
|
-
|
4
3
|
# This class represents a split within a tmux window
|
5
4
|
class Split
|
6
5
|
attr_reader :width, :height, :cmd, :index, :target, :focus
|
@@ -10,7 +9,7 @@ module Teamocil
|
|
10
9
|
# @param session [Session] the window where the split is initialized
|
11
10
|
# @param index [Fixnnum] the split index
|
12
11
|
# @param attrs [Hash] the split data from the layout file
|
13
|
-
def initialize(window, index, attrs={})
|
12
|
+
def initialize(window, index, attrs={})
|
14
13
|
raise Teamocil::Error::LayoutError.new("You cannot have empty splits") if attrs.nil?
|
15
14
|
@height = attrs["height"]
|
16
15
|
@width = attrs["width"]
|
@@ -20,17 +19,17 @@ module Teamocil
|
|
20
19
|
|
21
20
|
@window = window
|
22
21
|
@index = index
|
23
|
-
end
|
22
|
+
end
|
24
23
|
|
25
24
|
# Generate commands to send to tmux
|
26
25
|
#
|
27
26
|
# @return [Array]
|
28
|
-
def generate_commands
|
27
|
+
def generate_commands
|
29
28
|
commands = []
|
30
29
|
|
31
30
|
# Is it a vertical or horizontal split?
|
32
31
|
init_command = ""
|
33
|
-
unless @index ==
|
32
|
+
unless @index == @window.pane_base_index
|
34
33
|
if !@width.nil?
|
35
34
|
init_command = "tmux split-window -h -p #{@width}"
|
36
35
|
elsif !@height.nil?
|
@@ -56,9 +55,7 @@ module Teamocil
|
|
56
55
|
commands << "tmux send-keys -t #{@index} Enter"
|
57
56
|
|
58
57
|
commands
|
59
|
-
end
|
60
|
-
|
58
|
+
end
|
61
59
|
end
|
62
|
-
|
63
60
|
end
|
64
61
|
end
|
@@ -1,6 +1,5 @@
|
|
1
1
|
module Teamocil
|
2
2
|
class Layout
|
3
|
-
|
4
3
|
# This class represents a window within tmux
|
5
4
|
class Window
|
6
5
|
attr_reader :filters, :root, :splits, :options, :index, :name, :clear, :layout
|
@@ -10,7 +9,7 @@ module Teamocil
|
|
10
9
|
# @param session [Session] the session where the window is initialized
|
11
10
|
# @param index [Fixnnum] the window index
|
12
11
|
# @param attrs [Hash] the window data from the layout file
|
13
|
-
def initialize(session, index, attrs={})
|
12
|
+
def initialize(session, index, attrs={})
|
14
13
|
@name = attrs["name"] || "teamocil-window-#{index+1}"
|
15
14
|
@root = attrs["root"] || "."
|
16
15
|
@clear = attrs["clear"] == true ? "clear" : nil
|
@@ -19,7 +18,7 @@ module Teamocil
|
|
19
18
|
|
20
19
|
@splits = attrs["splits"] || []
|
21
20
|
raise Teamocil::Error::LayoutError.new("You must specify a `splits` key for every window.") if @splits.empty?
|
22
|
-
@splits = @splits.each_with_index.map { |split, split_index| Split.new(self, split_index, split) }
|
21
|
+
@splits = @splits.each_with_index.map { |split, split_index| Split.new(self, split_index + pane_base_index, split) }
|
23
22
|
|
24
23
|
@filters = attrs["filters"] || {}
|
25
24
|
@filters["before"] ||= []
|
@@ -27,12 +26,12 @@ module Teamocil
|
|
27
26
|
|
28
27
|
@index = index
|
29
28
|
@session = session
|
30
|
-
end
|
29
|
+
end
|
31
30
|
|
32
31
|
# Generate commands to send to tmux
|
33
32
|
#
|
34
33
|
# @return [Array]
|
35
|
-
def generate_commands
|
34
|
+
def generate_commands
|
36
35
|
commands = []
|
37
36
|
|
38
37
|
if @session.options.include?(:here) and @index == 0
|
@@ -53,9 +52,18 @@ module Teamocil
|
|
53
52
|
commands << "tmux select-pane -t #{@splits.map(&:focus).index(true) || 0}"
|
54
53
|
|
55
54
|
commands
|
56
|
-
end
|
55
|
+
end
|
57
56
|
|
57
|
+
# Return the `pane-base-index` option for the current window
|
58
|
+
#
|
59
|
+
# @return [Fixnum]
|
60
|
+
def pane_base_index
|
61
|
+
@pane_base_index ||= begin
|
62
|
+
global_setting = `tmux show-window-option -g pane-base-index`.split(/\s/).last
|
63
|
+
local_setting = `tmux show-window-option pane-base-index`.split(/\s/).last
|
64
|
+
(local_setting || global_setting || "0").to_i
|
65
|
+
end
|
66
|
+
end
|
58
67
|
end
|
59
|
-
|
60
68
|
end
|
61
69
|
end
|
data/spec/cli_spec.rb
CHANGED
@@ -2,77 +2,70 @@
|
|
2
2
|
require File.join(File.dirname(__FILE__), "spec_helper.rb")
|
3
3
|
|
4
4
|
describe Teamocil::CLI do
|
5
|
-
|
6
5
|
context "executing" do
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
end # }}}
|
6
|
+
before do
|
7
|
+
@fake_env = { "TMUX" => 1, "HOME" => File.join(File.dirname(__FILE__), "fixtures") }
|
8
|
+
end
|
11
9
|
|
12
10
|
context "not in tmux" do
|
13
|
-
|
14
|
-
it "should allow editing" do # {{{
|
11
|
+
it "should allow editing" do
|
15
12
|
FileUtils.stub(:touch)
|
16
13
|
Kernel.should_receive(:system).with("$EDITOR #{File.join(@fake_env["HOME"], ".teamocil", "my-layout.yml").inspect}")
|
17
14
|
Teamocil::CLI.new(["--edit", "my-layout"], @fake_env)
|
18
|
-
end
|
19
|
-
|
15
|
+
end
|
20
16
|
end
|
21
17
|
|
22
18
|
context "in tmux" do
|
23
|
-
|
24
|
-
before :each do # {{{
|
19
|
+
before :each do
|
25
20
|
Teamocil::CLI.messages = []
|
26
|
-
end
|
21
|
+
end
|
27
22
|
|
28
|
-
it "creates a layout from a name" do
|
23
|
+
it "creates a layout from a name" do
|
29
24
|
@cli = Teamocil::CLI.new(["sample"], @fake_env)
|
30
25
|
@cli.layout.session.name.should == "sample"
|
31
26
|
@cli.layout.session.windows.length.should == 2
|
32
27
|
@cli.layout.session.windows.first.name.should == "foo"
|
33
28
|
@cli.layout.session.windows.last.name.should == "bar"
|
34
|
-
end
|
29
|
+
end
|
35
30
|
|
36
|
-
it "fails to create a layout from a layout that doesn’t exist" do
|
37
|
-
|
31
|
+
it "fails to create a layout from a layout that doesn’t exist" do
|
32
|
+
expect { @cli = Teamocil::CLI.new(["i-do-not-exist"], @fake_env) }.to raise_error SystemExit
|
38
33
|
Teamocil::CLI.messages.should include("There is no file \"#{File.join(File.dirname(__FILE__), "fixtures", ".teamocil", "i-do-not-exist.yml")}\"")
|
39
|
-
end
|
34
|
+
end
|
40
35
|
|
41
|
-
it "creates a layout from a specific file" do
|
36
|
+
it "creates a layout from a specific file" do
|
42
37
|
@cli = Teamocil::CLI.new(["--layout", "./spec/fixtures/.teamocil/sample.yml"], @fake_env)
|
43
38
|
@cli.layout.session.name.should == "sample"
|
44
39
|
@cli.layout.session.windows.length.should == 2
|
45
40
|
@cli.layout.session.windows.first.name.should == "foo"
|
46
41
|
@cli.layout.session.windows.last.name.should == "bar"
|
47
|
-
end
|
42
|
+
end
|
48
43
|
|
49
|
-
it "fails to create a layout from a file that doesn’t exist" do
|
50
|
-
|
44
|
+
it "fails to create a layout from a file that doesn’t exist" do
|
45
|
+
expect { @cli = Teamocil::CLI.new(["--layout", "./spec/fixtures/.teamocil/i-do-not-exist.yml"], @fake_env) }.to raise_error SystemExit
|
51
46
|
Teamocil::CLI.messages.should include("There is no file \"./spec/fixtures/.teamocil/i-do-not-exist.yml\"")
|
52
|
-
end
|
47
|
+
end
|
53
48
|
|
54
|
-
it "lists available layouts" do
|
49
|
+
it "lists available layouts" do
|
55
50
|
@cli = Teamocil::CLI.new(["--list"], @fake_env)
|
56
51
|
@cli.layouts.should == ["sample", "sample-2"]
|
57
|
-
end
|
52
|
+
end
|
58
53
|
|
59
|
-
it "should show the content" do
|
54
|
+
it "should show the content" do
|
60
55
|
FileUtils.stub(:touch)
|
61
56
|
Kernel.should_receive(:system).with("cat #{File.join(@fake_env["HOME"], ".teamocil", "sample.yml").inspect}")
|
62
57
|
Teamocil::CLI.new(["--show", "sample"], @fake_env)
|
63
|
-
end
|
58
|
+
end
|
64
59
|
|
65
|
-
it "looks only in the $TEAMOCIL_PATH environment variable for layouts" do
|
60
|
+
it "looks only in the $TEAMOCIL_PATH environment variable for layouts" do
|
66
61
|
@fake_env = { "TMUX" => 1, "HOME" => File.join(File.dirname(__FILE__), "fixtures"), "TEAMOCIL_PATH" => File.join(File.dirname(__FILE__), "fixtures/.my-fancy-layouts-directory") }
|
67
62
|
|
68
63
|
@cli = Teamocil::CLI.new(["sample-3"], @fake_env)
|
69
64
|
@cli.layout.session.name.should == "sample-3"
|
70
65
|
|
71
|
-
|
66
|
+
expect { @cli = Teamocil::CLI.new(["sample"], @fake_env) }.to raise_error SystemExit
|
72
67
|
Teamocil::CLI.messages.should include("There is no file \"#{@fake_env["TEAMOCIL_PATH"]}/sample.yml\"")
|
73
|
-
end
|
74
|
-
|
68
|
+
end
|
75
69
|
end
|
76
|
-
|
77
70
|
end
|
78
71
|
end
|
data/spec/layout_spec.rb
CHANGED
@@ -2,149 +2,149 @@
|
|
2
2
|
require File.join(File.dirname(__FILE__), "spec_helper.rb")
|
3
3
|
|
4
4
|
describe Teamocil::Layout do
|
5
|
+
let(:window_pane_base_index) { 0 }
|
6
|
+
before { Teamocil::Layout::Window.any_instance.stub(:pane_base_index).and_return(window_pane_base_index) }
|
5
7
|
|
6
8
|
context "compiling" do
|
7
|
-
before do
|
9
|
+
before do
|
8
10
|
@layout = Teamocil::Layout.new(layouts["two-windows"], {})
|
9
|
-
end
|
11
|
+
end
|
10
12
|
|
11
|
-
describe "handles bad layouts" do
|
12
|
-
it "does not compile without windows" do
|
13
|
+
describe "handles bad layouts" do
|
14
|
+
it "does not compile without windows" do
|
13
15
|
@layout = Teamocil::Layout.new({ "name" => "foo" }, {})
|
14
|
-
|
15
|
-
end
|
16
|
+
expect { @layout.compile! }.to raise_error Teamocil::Error::LayoutError
|
17
|
+
end
|
16
18
|
|
17
|
-
it "does not compile without splits" do
|
19
|
+
it "does not compile without splits" do
|
18
20
|
@layout = Teamocil::Layout.new({ "windows" => [{ "name" => "foo" }] }, {})
|
19
|
-
|
20
|
-
end
|
21
|
+
expect { @layout.compile! }.to raise_error Teamocil::Error::LayoutError
|
22
|
+
end
|
21
23
|
|
22
|
-
it "does not compile with empty splits" do
|
24
|
+
it "does not compile with empty splits" do
|
23
25
|
@layout = Teamocil::Layout.new({ "windows" => [{ "name" => "foo", "splits" => [nil, nil] }] }, {})
|
24
|
-
|
25
|
-
end
|
26
|
-
end
|
26
|
+
expect { @layout.compile! }.to raise_error Teamocil::Error::LayoutError
|
27
|
+
end
|
28
|
+
end
|
27
29
|
|
28
|
-
describe "windows" do
|
29
|
-
it "creates windows" do
|
30
|
+
describe "windows" do
|
31
|
+
it "creates windows" do
|
30
32
|
session = @layout.compile!
|
31
33
|
session.windows.each do |window|
|
32
34
|
window.should be_an_instance_of Teamocil::Layout::Window
|
33
35
|
end
|
34
|
-
end
|
36
|
+
end
|
35
37
|
|
36
|
-
it "creates windows with names" do
|
38
|
+
it "creates windows with names" do
|
37
39
|
session = @layout.compile!
|
38
40
|
session.windows[0].name.should == "foo"
|
39
41
|
session.windows[1].name.should == "bar"
|
40
|
-
end
|
42
|
+
end
|
41
43
|
|
42
|
-
it "creates windows with root paths" do
|
44
|
+
it "creates windows with root paths" do
|
43
45
|
session = @layout.compile!
|
44
46
|
session.windows[0].root.should == "/foo"
|
45
47
|
session.windows[1].root.should == "/bar"
|
46
|
-
end
|
48
|
+
end
|
47
49
|
|
48
|
-
it "creates windows with clear option" do
|
50
|
+
it "creates windows with clear option" do
|
49
51
|
session = @layout.compile!
|
50
52
|
session.windows[0].clear.should == "clear"
|
51
53
|
session.windows[1].clear.should be_nil
|
52
|
-
end
|
54
|
+
end
|
53
55
|
|
54
|
-
it "creates windows with layout option" do
|
56
|
+
it "creates windows with layout option" do
|
55
57
|
session = @layout.compile!
|
56
58
|
session.windows[0].layout.should == "tiled"
|
57
59
|
session.windows[1].layout.should be_nil
|
58
|
-
end
|
59
|
-
end
|
60
|
+
end
|
61
|
+
end
|
60
62
|
|
61
|
-
describe "splits" do
|
62
|
-
it "creates splits" do
|
63
|
+
describe "splits" do
|
64
|
+
it "creates splits" do
|
63
65
|
session = @layout.compile!
|
64
66
|
session.windows.first.splits.each do |split|
|
65
67
|
split.should be_an_instance_of Teamocil::Layout::Split
|
66
68
|
end
|
67
|
-
end
|
69
|
+
end
|
68
70
|
|
69
|
-
it "creates splits with dimensions" do
|
71
|
+
it "creates splits with dimensions" do
|
70
72
|
session = @layout.compile!
|
71
73
|
session.windows.first.splits[0].width.should == nil
|
72
74
|
session.windows.first.splits[1].width.should == 50
|
73
|
-
end
|
75
|
+
end
|
74
76
|
|
75
|
-
it "creates splits with commands specified in strings" do
|
77
|
+
it "creates splits with commands specified in strings" do
|
76
78
|
session = @layout.compile!
|
77
79
|
session.windows.first.splits[0].cmd.should == "echo 'foo'"
|
78
|
-
end
|
80
|
+
end
|
79
81
|
|
80
|
-
it "creates splits with commands specified in an array" do
|
82
|
+
it "creates splits with commands specified in an array" do
|
81
83
|
session = @layout.compile!
|
82
84
|
session.windows.last.splits[0].cmd.length.should == 2
|
83
85
|
session.windows.last.splits[0].cmd.first.should == "echo 'bar'"
|
84
86
|
session.windows.last.splits[0].cmd.last.should == "echo 'bar in an array'"
|
85
|
-
end
|
87
|
+
end
|
86
88
|
|
87
|
-
it "handles focused splits" do
|
89
|
+
it "handles focused splits" do
|
88
90
|
session = @layout.compile!
|
89
91
|
session.windows.last.splits[1].focus.should be_true
|
90
92
|
session.windows.last.splits[0].focus.should be_false
|
91
|
-
end
|
92
|
-
end
|
93
|
+
end
|
94
|
+
end
|
93
95
|
|
94
|
-
describe "filters" do
|
95
|
-
it "creates windows with before filters" do
|
96
|
+
describe "filters" do
|
97
|
+
it "creates windows with before filters" do
|
96
98
|
layout = Teamocil::Layout.new(layouts["two-windows-with-filters"], {})
|
97
99
|
session = layout.compile!
|
98
100
|
session.windows.first.filters["before"].length.should == 2
|
99
101
|
session.windows.first.filters["before"].first.should == "echo first before filter"
|
100
102
|
session.windows.first.filters["before"].last.should == "echo second before filter"
|
101
|
-
end
|
103
|
+
end
|
102
104
|
|
103
|
-
it "creates windows with after filters" do
|
105
|
+
it "creates windows with after filters" do
|
104
106
|
layout = Teamocil::Layout.new(layouts["two-windows-with-filters"], {})
|
105
107
|
session = layout.compile!
|
106
108
|
session.windows.first.filters["after"].length.should == 2
|
107
109
|
session.windows.first.filters["after"].first.should == "echo first after filter"
|
108
110
|
session.windows.first.filters["after"].last.should == "echo second after filter"
|
109
|
-
end
|
111
|
+
end
|
110
112
|
|
111
|
-
it "should handle blank filters" do
|
113
|
+
it "should handle blank filters" do
|
112
114
|
session = @layout.compile!
|
113
115
|
session.windows.first.filters.should have_key "after"
|
114
116
|
session.windows.first.filters.should have_key "before"
|
115
117
|
session.windows.first.filters["after"].should be_empty
|
116
118
|
session.windows.first.filters["before"].should be_empty
|
117
|
-
end
|
118
|
-
end
|
119
|
+
end
|
120
|
+
end
|
119
121
|
|
120
|
-
describe "targets" do
|
121
|
-
it "should handle splits without a target" do
|
122
|
+
describe "targets" do
|
123
|
+
it "should handle splits without a target" do
|
122
124
|
session = @layout.compile!
|
123
125
|
session.windows.last.splits.last.target.should == nil
|
124
|
-
end
|
126
|
+
end
|
125
127
|
|
126
|
-
it "should handle splits with a target" do
|
128
|
+
it "should handle splits with a target" do
|
127
129
|
session = @layout.compile!
|
128
130
|
session.windows.last.splits.first.target.should == "bottom-right"
|
129
|
-
end
|
130
|
-
end
|
131
|
+
end
|
132
|
+
end
|
131
133
|
|
132
|
-
describe "sessions" do
|
133
|
-
it "should handle windows within a session" do
|
134
|
+
describe "sessions" do
|
135
|
+
it "should handle windows within a session" do
|
134
136
|
layout = Teamocil::Layout.new(layouts["three-windows-within-a-session"], {})
|
135
137
|
session = layout.compile!
|
136
138
|
session.windows.length.should == 3
|
137
139
|
session.name.should == "my awesome session"
|
138
|
-
end
|
139
|
-
end
|
140
|
+
end
|
141
|
+
end
|
140
142
|
end
|
141
143
|
|
142
144
|
context "generating commands" do
|
143
|
-
before
|
144
|
-
@layout = Teamocil::Layout.new(layouts["two-windows"], {})
|
145
|
-
end # }}}
|
145
|
+
before { @layout = Teamocil::Layout.new(layouts["two-windows"], {}) }
|
146
146
|
|
147
|
-
it "should generate split commands" do
|
147
|
+
it "should generate split commands" do
|
148
148
|
session = @layout.compile!
|
149
149
|
commands = session.windows.last.splits[0].generate_commands
|
150
150
|
commands.length.should == 2
|
@@ -156,14 +156,25 @@ describe Teamocil::Layout do
|
|
156
156
|
commands.length.should == 2
|
157
157
|
commands.first.should == "tmux send-keys -t 0 \"export TEAMOCIL=1 && cd \"/foo\" && clear && echo 'foo'\""
|
158
158
|
commands.last.should == "tmux send-keys -t 0 Enter"
|
159
|
-
end
|
159
|
+
end
|
160
160
|
|
161
|
-
it "should generate window commands" do
|
161
|
+
it "should generate window commands" do
|
162
162
|
session = @layout.compile!
|
163
163
|
commands = session.windows.last.generate_commands
|
164
164
|
commands.first.should == "tmux new-window -n \"bar\""
|
165
165
|
commands.last.should == "tmux select-pane -t 1"
|
166
|
-
end
|
167
|
-
|
166
|
+
end
|
167
|
+
|
168
|
+
context "with custom pane-base-index option" do
|
169
|
+
let(:window_pane_base_index) { 2 }
|
168
170
|
|
171
|
+
it "should generate split commands" do
|
172
|
+
session = @layout.compile!
|
173
|
+
commands = session.windows.last.splits[0].generate_commands
|
174
|
+
commands.length.should == 2
|
175
|
+
commands.first.should == "tmux send-keys -t 2 \"export TEAMOCIL=1 && cd \"/bar\" && echo 'bar' && echo 'bar in an array'\""
|
176
|
+
commands.last.should == "tmux send-keys -t 2 Enter"
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
169
180
|
end
|
data/spec/mock/cli.rb
CHANGED
@@ -1,36 +1,33 @@
|
|
1
1
|
module Teamocil
|
2
2
|
module Mock
|
3
3
|
module CLI
|
4
|
-
|
5
|
-
def self.included(base) # {{{
|
4
|
+
def self.included(base)
|
6
5
|
base.class_eval do
|
7
6
|
|
8
7
|
# Return all messages
|
9
|
-
def self.messages
|
8
|
+
def self.messages
|
10
9
|
@@messages
|
11
|
-
end
|
10
|
+
end
|
12
11
|
|
13
12
|
# Change messages
|
14
|
-
def self.messages=(messages)
|
13
|
+
def self.messages=(messages)
|
15
14
|
@@messages = messages
|
16
|
-
end
|
15
|
+
end
|
17
16
|
|
18
17
|
# Do not print anything
|
19
|
-
def print_layouts
|
18
|
+
def print_layouts
|
20
19
|
# Nothing
|
21
|
-
end
|
20
|
+
end
|
22
21
|
|
23
22
|
# Print an error message and exit the utility
|
24
23
|
#
|
25
24
|
# @param msg [Mixed] something to print before exiting.
|
26
|
-
def bail(msg)
|
25
|
+
def bail(msg)
|
27
26
|
Teamocil::CLI.messages << msg
|
28
27
|
exit 1
|
29
|
-
end
|
30
|
-
|
28
|
+
end
|
31
29
|
end
|
32
|
-
end
|
33
|
-
|
30
|
+
end
|
34
31
|
end
|
35
32
|
end
|
36
33
|
end
|
data/spec/mock/layout.rb
CHANGED
@@ -1,18 +1,14 @@
|
|
1
1
|
module Teamocil
|
2
2
|
module Mock
|
3
3
|
module Layout
|
4
|
-
|
5
|
-
def self.included(base) # {{{
|
4
|
+
def self.included(base)
|
6
5
|
base.class_eval do
|
7
|
-
|
8
6
|
# Do not execute anything
|
9
7
|
def execute_commands(commands)
|
10
8
|
# Nothing
|
11
9
|
end
|
12
|
-
|
13
10
|
end
|
14
|
-
end
|
15
|
-
|
11
|
+
end
|
16
12
|
end
|
17
13
|
end
|
18
14
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -6,12 +6,10 @@ require File.join(File.dirname(__FILE__), "./mock/layout.rb")
|
|
6
6
|
require File.join(File.dirname(__FILE__), "./mock/cli.rb")
|
7
7
|
|
8
8
|
module Helpers
|
9
|
-
|
10
|
-
def layouts # {{{
|
9
|
+
def layouts
|
11
10
|
return @@examples if defined?(@@examples)
|
12
11
|
@@examples = YAML.load_file(File.join(File.dirname(__FILE__), "fixtures/layouts.yml"))
|
13
|
-
end
|
14
|
-
|
12
|
+
end
|
15
13
|
end
|
16
14
|
|
17
15
|
RSpec.configure do |c|
|
data/teamocil.gemspec
CHANGED
@@ -23,7 +23,7 @@ spec = Gem::Specification.new do |s|
|
|
23
23
|
|
24
24
|
# Dependencies
|
25
25
|
s.add_development_dependency "rake"
|
26
|
-
s.add_development_dependency "rspec"
|
26
|
+
s.add_development_dependency "rspec", '~> 2.13'
|
27
27
|
s.add_development_dependency "yard"
|
28
28
|
s.add_development_dependency "maruku"
|
29
29
|
end
|
metadata
CHANGED
@@ -1,20 +1,18 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: teamocil
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
5
|
-
prerelease:
|
4
|
+
version: '0.4'
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- Rémi Prévost
|
9
8
|
autorequire:
|
10
9
|
bindir: bin
|
11
10
|
cert_chain: []
|
12
|
-
date:
|
11
|
+
date: 2013-03-03 00:00:00.000000000 Z
|
13
12
|
dependencies:
|
14
13
|
- !ruby/object:Gem::Dependency
|
15
14
|
name: rake
|
16
15
|
requirement: !ruby/object:Gem::Requirement
|
17
|
-
none: false
|
18
16
|
requirements:
|
19
17
|
- - ! '>='
|
20
18
|
- !ruby/object:Gem::Version
|
@@ -22,7 +20,6 @@ dependencies:
|
|
22
20
|
type: :development
|
23
21
|
prerelease: false
|
24
22
|
version_requirements: !ruby/object:Gem::Requirement
|
25
|
-
none: false
|
26
23
|
requirements:
|
27
24
|
- - ! '>='
|
28
25
|
- !ruby/object:Gem::Version
|
@@ -30,23 +27,20 @@ dependencies:
|
|
30
27
|
- !ruby/object:Gem::Dependency
|
31
28
|
name: rspec
|
32
29
|
requirement: !ruby/object:Gem::Requirement
|
33
|
-
none: false
|
34
30
|
requirements:
|
35
|
-
- -
|
31
|
+
- - ~>
|
36
32
|
- !ruby/object:Gem::Version
|
37
|
-
version: '
|
33
|
+
version: '2.13'
|
38
34
|
type: :development
|
39
35
|
prerelease: false
|
40
36
|
version_requirements: !ruby/object:Gem::Requirement
|
41
|
-
none: false
|
42
37
|
requirements:
|
43
|
-
- -
|
38
|
+
- - ~>
|
44
39
|
- !ruby/object:Gem::Version
|
45
|
-
version: '
|
40
|
+
version: '2.13'
|
46
41
|
- !ruby/object:Gem::Dependency
|
47
42
|
name: yard
|
48
43
|
requirement: !ruby/object:Gem::Requirement
|
49
|
-
none: false
|
50
44
|
requirements:
|
51
45
|
- - ! '>='
|
52
46
|
- !ruby/object:Gem::Version
|
@@ -54,7 +48,6 @@ dependencies:
|
|
54
48
|
type: :development
|
55
49
|
prerelease: false
|
56
50
|
version_requirements: !ruby/object:Gem::Requirement
|
57
|
-
none: false
|
58
51
|
requirements:
|
59
52
|
- - ! '>='
|
60
53
|
- !ruby/object:Gem::Version
|
@@ -62,7 +55,6 @@ dependencies:
|
|
62
55
|
- !ruby/object:Gem::Dependency
|
63
56
|
name: maruku
|
64
57
|
requirement: !ruby/object:Gem::Requirement
|
65
|
-
none: false
|
66
58
|
requirements:
|
67
59
|
- - ! '>='
|
68
60
|
- !ruby/object:Gem::Version
|
@@ -70,7 +62,6 @@ dependencies:
|
|
70
62
|
type: :development
|
71
63
|
prerelease: false
|
72
64
|
version_requirements: !ruby/object:Gem::Requirement
|
73
|
-
none: false
|
74
65
|
requirements:
|
75
66
|
- - ! '>='
|
76
67
|
- !ruby/object:Gem::Version
|
@@ -90,11 +81,11 @@ files:
|
|
90
81
|
- README.md
|
91
82
|
- Rakefile
|
92
83
|
- bin/teamocil
|
93
|
-
- examples/
|
94
|
-
- examples/
|
95
|
-
- examples/
|
96
|
-
- examples/
|
97
|
-
- examples/
|
84
|
+
- examples/four-splits.yml
|
85
|
+
- examples/one-and-three-splits.yml
|
86
|
+
- examples/six-splits.yml
|
87
|
+
- examples/two-horizontal-splits.yml
|
88
|
+
- examples/two-vertical-splits.yml
|
98
89
|
- lib/teamocil.rb
|
99
90
|
- lib/teamocil/cli.rb
|
100
91
|
- lib/teamocil/error.rb
|
@@ -115,33 +106,26 @@ files:
|
|
115
106
|
homepage: http://remiprev.github.com/teamocil
|
116
107
|
licenses:
|
117
108
|
- MIT
|
109
|
+
metadata: {}
|
118
110
|
post_install_message:
|
119
111
|
rdoc_options: []
|
120
112
|
require_paths:
|
121
113
|
- lib
|
122
114
|
required_ruby_version: !ruby/object:Gem::Requirement
|
123
|
-
none: false
|
124
115
|
requirements:
|
125
116
|
- - ! '>='
|
126
117
|
- !ruby/object:Gem::Version
|
127
118
|
version: '0'
|
128
|
-
segments:
|
129
|
-
- 0
|
130
|
-
hash: -2208554246657691212
|
131
119
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
132
|
-
none: false
|
133
120
|
requirements:
|
134
121
|
- - ! '>='
|
135
122
|
- !ruby/object:Gem::Version
|
136
123
|
version: '0'
|
137
|
-
segments:
|
138
|
-
- 0
|
139
|
-
hash: -2208554246657691212
|
140
124
|
requirements: []
|
141
125
|
rubyforge_project:
|
142
|
-
rubygems_version:
|
126
|
+
rubygems_version: 2.0.0
|
143
127
|
signing_key:
|
144
|
-
specification_version:
|
128
|
+
specification_version: 4
|
145
129
|
summary: Easy window and split layouts for tmux
|
146
130
|
test_files:
|
147
131
|
- spec/cli_spec.rb
|
@@ -1,13 +0,0 @@
|
|
1
|
-
windows:
|
2
|
-
- name: simple-four-splits
|
3
|
-
splits:
|
4
|
-
- cmd: "echo 'first split'"
|
5
|
-
- cmd: "echo 'second split'"
|
6
|
-
width: 50
|
7
|
-
focus: true
|
8
|
-
- cmd: "echo 'fourth split'"
|
9
|
-
height: 50
|
10
|
-
target: bottom-right
|
11
|
-
- cmd: "echo 'third split'"
|
12
|
-
height: 50
|
13
|
-
target: bottom-left
|
@@ -1,12 +0,0 @@
|
|
1
|
-
windows:
|
2
|
-
- name: simple-one-and-three-splits
|
3
|
-
splits:
|
4
|
-
- cmd: "echo 'first split'"
|
5
|
-
- cmd: "echo 'second split'"
|
6
|
-
width: 50
|
7
|
-
- cmd: "echo 'third split'"
|
8
|
-
height: 66
|
9
|
-
target: bottom-right
|
10
|
-
- cmd: "echo 'fourth split'"
|
11
|
-
height: 50
|
12
|
-
target: bottom-right
|
@@ -1,18 +0,0 @@
|
|
1
|
-
windows:
|
2
|
-
- name: simple-six-splits
|
3
|
-
splits:
|
4
|
-
- cmd: "echo 'first split'"
|
5
|
-
- cmd: "echo 'second split'"
|
6
|
-
width: 50
|
7
|
-
- cmd: "echo 'fourth split'"
|
8
|
-
height: 66
|
9
|
-
target: bottom-right
|
10
|
-
- cmd: "echo 'third split'"
|
11
|
-
height: 66
|
12
|
-
target: bottom-left
|
13
|
-
- cmd: "echo 'sixth split'"
|
14
|
-
height: 50
|
15
|
-
target: bottom-right
|
16
|
-
- cmd: "echo 'fifth split'"
|
17
|
-
height: 50
|
18
|
-
target: bottom-left
|